From 32b94555c3cc2c1e73b7b42af7487f96602a4531 Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Fri, 26 Feb 2021 22:36:26 +0100 Subject: [PATCH] Remove 3rdparty taglib --- .circleci/config.yml | 3 + .github/workflows/ccpp.yml | 12 + 3rdparty/README.md | 30 - 3rdparty/taglib/CMakeLists.txt | 402 ---- 3rdparty/taglib/COPYING | 502 ---- 3rdparty/taglib/ConfigureChecks.cmake | 216 -- 3rdparty/taglib/README.md | 26 - 3rdparty/taglib/ape/ape-tag-format.txt | 170 -- 3rdparty/taglib/ape/apefile.cpp | 291 --- 3rdparty/taglib/ape/apefile.h | 214 -- 3rdparty/taglib/ape/apefooter.cpp | 215 -- 3rdparty/taglib/ape/apefooter.h | 175 -- 3rdparty/taglib/ape/apeitem.cpp | 271 --- 3rdparty/taglib/ape/apeitem.h | 207 -- 3rdparty/taglib/ape/apeproperties.cpp | 227 -- 3rdparty/taglib/ape/apeproperties.h | 118 - 3rdparty/taglib/ape/apetag.cpp | 519 ----- 3rdparty/taglib/ape/apetag.h | 216 -- 3rdparty/taglib/asf/asfattribute.cpp | 328 --- 3rdparty/taglib/asf/asfattribute.h | 200 -- 3rdparty/taglib/asf/asffile.cpp | 661 ------ 3rdparty/taglib/asf/asffile.h | 118 - 3rdparty/taglib/asf/asfpicture.cpp | 168 -- 3rdparty/taglib/asf/asfpicture.h | 218 -- 3rdparty/taglib/asf/asfproperties.cpp | 160 -- 3rdparty/taglib/asf/asfproperties.h | 168 -- 3rdparty/taglib/asf/asftag.cpp | 522 ----- 3rdparty/taglib/asf/asftag.h | 201 -- 3rdparty/taglib/asf/asfutils.h | 100 - 3rdparty/taglib/audioproperties.cpp | 53 - 3rdparty/taglib/audioproperties.h | 120 - 3rdparty/taglib/dsdiff/dsdiffdiintag.cpp | 147 -- 3rdparty/taglib/dsdiff/dsdiffdiintag.h | 154 -- 3rdparty/taglib/dsdiff/dsdifffile.cpp | 919 -------- 3rdparty/taglib/dsdiff/dsdifffile.h | 265 --- 3rdparty/taglib/dsdiff/dsdiffproperties.cpp | 96 - 3rdparty/taglib/dsdiff/dsdiffproperties.h | 78 - 3rdparty/taglib/dsf/dsffile.cpp | 228 -- 3rdparty/taglib/dsf/dsffile.h | 125 - 3rdparty/taglib/dsf/dsfproperties.cpp | 135 -- 3rdparty/taglib/dsf/dsfproperties.h | 94 - 3rdparty/taglib/fileref.cpp | 424 ---- 3rdparty/taglib/fileref.h | 283 --- 3rdparty/taglib/flac/flacfile.cpp | 535 ----- 3rdparty/taglib/flac/flacfile.h | 278 --- 3rdparty/taglib/flac/flacmetadatablock.cpp | 39 - 3rdparty/taglib/flac/flacmetadatablock.h | 74 - 3rdparty/taglib/flac/flacpicture.cpp | 194 -- 3rdparty/taglib/flac/flacpicture.h | 203 -- 3rdparty/taglib/flac/flacproperties.cpp | 146 -- 3rdparty/taglib/flac/flacproperties.h | 111 - .../taglib/flac/flacunknownmetadatablock.cpp | 68 - .../taglib/flac/flacunknownmetadatablock.h | 80 - 3rdparty/taglib/it/itfile.cpp | 312 --- 3rdparty/taglib/it/itfile.h | 91 - 3rdparty/taglib/it/itproperties.cpp | 216 -- 3rdparty/taglib/it/itproperties.h | 107 - 3rdparty/taglib/mod/modfile.cpp | 173 -- 3rdparty/taglib/mod/modfile.h | 95 - 3rdparty/taglib/mod/modfilebase.cpp | 122 - 3rdparty/taglib/mod/modfilebase.h | 65 - 3rdparty/taglib/mod/modfileprivate.h | 66 - 3rdparty/taglib/mod/modproperties.cpp | 94 - 3rdparty/taglib/mod/modproperties.h | 66 - 3rdparty/taglib/mod/modtag.cpp | 162 -- 3rdparty/taglib/mod/modtag.h | 186 -- 3rdparty/taglib/mp4/mp4atom.cpp | 181 -- 3rdparty/taglib/mp4/mp4atom.h | 111 - 3rdparty/taglib/mp4/mp4coverart.cpp | 85 - 3rdparty/taglib/mp4/mp4coverart.h | 83 - 3rdparty/taglib/mp4/mp4file.cpp | 156 -- 3rdparty/taglib/mp4/mp4file.h | 120 - 3rdparty/taglib/mp4/mp4item.cpp | 215 -- 3rdparty/taglib/mp4/mp4item.h | 109 - 3rdparty/taglib/mp4/mp4properties.cpp | 227 -- 3rdparty/taglib/mp4/mp4properties.h | 108 - 3rdparty/taglib/mp4/mp4tag.cpp | 1060 --------- 3rdparty/taglib/mp4/mp4tag.h | 146 -- 3rdparty/taglib/mpc/mpcfile.cpp | 300 --- 3rdparty/taglib/mpc/mpcfile.h | 212 -- 3rdparty/taglib/mpc/mpcproperties.cpp | 353 --- 3rdparty/taglib/mpc/mpcproperties.h | 132 -- 3rdparty/taglib/mpeg/id3v1/id3v1genres.cpp | 268 --- 3rdparty/taglib/mpeg/id3v1/id3v1genres.h | 64 - 3rdparty/taglib/mpeg/id3v1/id3v1tag.cpp | 247 -- 3rdparty/taglib/mpeg/id3v1/id3v1tag.h | 154 -- .../id3v2/frames/attachedpictureframe.cpp | 207 -- .../mpeg/id3v2/frames/attachedpictureframe.h | 226 -- .../taglib/mpeg/id3v2/frames/chapterframe.cpp | 293 --- .../taglib/mpeg/id3v2/frames/chapterframe.h | 233 -- .../mpeg/id3v2/frames/commentsframe.cpp | 182 -- .../taglib/mpeg/id3v2/frames/commentsframe.h | 176 -- .../id3v2/frames/eventtimingcodesframe.cpp | 126 - .../mpeg/id3v2/frames/eventtimingcodesframe.h | 185 -- .../frames/generalencapsulatedobjectframe.cpp | 168 -- .../frames/generalencapsulatedobjectframe.h | 179 -- .../mpeg/id3v2/frames/ownershipframe.cpp | 153 -- .../taglib/mpeg/id3v2/frames/ownershipframe.h | 150 -- .../taglib/mpeg/id3v2/frames/podcastframe.cpp | 70 - .../taglib/mpeg/id3v2/frames/podcastframe.h | 81 - .../mpeg/id3v2/frames/popularimeterframe.cpp | 124 - .../mpeg/id3v2/frames/popularimeterframe.h | 133 -- .../taglib/mpeg/id3v2/frames/privateframe.cpp | 115 - .../taglib/mpeg/id3v2/frames/privateframe.h | 112 - .../mpeg/id3v2/frames/relativevolumeframe.cpp | 172 -- .../mpeg/id3v2/frames/relativevolumeframe.h | 215 -- .../id3v2/frames/synchronizedlyricsframe.cpp | 223 -- .../id3v2/frames/synchronizedlyricsframe.h | 227 -- .../id3v2/frames/tableofcontentsframe.cpp | 329 --- .../mpeg/id3v2/frames/tableofcontentsframe.h | 242 -- .../id3v2/frames/textidentificationframe.cpp | 415 ---- .../id3v2/frames/textidentificationframe.h | 296 --- .../frames/uniquefileidentifierframe.cpp | 136 -- .../id3v2/frames/uniquefileidentifierframe.h | 119 - .../taglib/mpeg/id3v2/frames/unknownframe.cpp | 85 - .../taglib/mpeg/id3v2/frames/unknownframe.h | 81 - .../frames/unsynchronizedlyricsframe.cpp | 185 -- .../id3v2/frames/unsynchronizedlyricsframe.h | 175 -- .../taglib/mpeg/id3v2/frames/urllinkframe.cpp | 226 -- .../taglib/mpeg/id3v2/frames/urllinkframe.h | 187 -- 3rdparty/taglib/mpeg/id3v2/id3v2.2.0.txt | 1660 -------------- 3rdparty/taglib/mpeg/id3v2/id3v2.3.0.txt | 2022 ----------------- .../taglib/mpeg/id3v2/id3v2.4.0-frames.txt | 1734 -------------- .../taglib/mpeg/id3v2/id3v2.4.0-structure.txt | 733 ------ 3rdparty/taglib/mpeg/id3v2/id3v2.h | 27 - .../taglib/mpeg/id3v2/id3v2extendedheader.cpp | 63 - .../taglib/mpeg/id3v2/id3v2extendedheader.h | 92 - 3rdparty/taglib/mpeg/id3v2/id3v2footer.cpp | 48 - 3rdparty/taglib/mpeg/id3v2/id3v2footer.h | 81 - 3rdparty/taglib/mpeg/id3v2/id3v2frame.cpp | 733 ------ 3rdparty/taglib/mpeg/id3v2/id3v2frame.h | 439 ---- .../taglib/mpeg/id3v2/id3v2framefactory.cpp | 518 ----- .../taglib/mpeg/id3v2/id3v2framefactory.h | 130 -- 3rdparty/taglib/mpeg/id3v2/id3v2header.cpp | 217 -- 3rdparty/taglib/mpeg/id3v2/id3v2header.h | 165 -- 3rdparty/taglib/mpeg/id3v2/id3v2synchdata.cpp | 100 - 3rdparty/taglib/mpeg/id3v2/id3v2synchdata.h | 67 - 3rdparty/taglib/mpeg/id3v2/id3v2tag.cpp | 920 -------- 3rdparty/taglib/mpeg/id3v2/id3v2tag.h | 331 --- 3rdparty/taglib/mpeg/mpegfile.cpp | 532 ----- 3rdparty/taglib/mpeg/mpegfile.h | 305 --- 3rdparty/taglib/mpeg/mpegheader.cpp | 298 --- 3rdparty/taglib/mpeg/mpegheader.h | 172 -- 3rdparty/taglib/mpeg/mpegproperties.cpp | 193 -- 3rdparty/taglib/mpeg/mpegproperties.h | 134 -- 3rdparty/taglib/mpeg/mpegutils.h | 61 - 3rdparty/taglib/mpeg/xingheader.cpp | 124 - 3rdparty/taglib/mpeg/xingheader.h | 118 - 3rdparty/taglib/ogg/flac/oggflacfile.cpp | 299 --- 3rdparty/taglib/ogg/flac/oggflacfile.h | 151 -- 3rdparty/taglib/ogg/oggfile.cpp | 304 --- 3rdparty/taglib/ogg/oggfile.h | 118 - 3rdparty/taglib/ogg/oggpage.cpp | 332 --- 3rdparty/taglib/ogg/oggpage.h | 207 -- 3rdparty/taglib/ogg/oggpageheader.cpp | 293 --- 3rdparty/taglib/ogg/oggpageheader.h | 217 -- 3rdparty/taglib/ogg/opus/opusfile.cpp | 134 -- 3rdparty/taglib/ogg/opus/opusfile.h | 122 - 3rdparty/taglib/ogg/opus/opusproperties.cpp | 169 -- 3rdparty/taglib/ogg/opus/opusproperties.h | 117 - 3rdparty/taglib/ogg/speex/speexfile.cpp | 124 - 3rdparty/taglib/ogg/speex/speexfile.h | 122 - 3rdparty/taglib/ogg/speex/speexproperties.cpp | 190 -- 3rdparty/taglib/ogg/speex/speexproperties.h | 108 - 3rdparty/taglib/ogg/vorbis/vorbisfile.cpp | 133 -- 3rdparty/taglib/ogg/vorbis/vorbisfile.h | 115 - .../taglib/ogg/vorbis/vorbisproperties.cpp | 205 -- 3rdparty/taglib/ogg/vorbis/vorbisproperties.h | 121 - 3rdparty/taglib/ogg/xiphcomment.cpp | 515 ----- 3rdparty/taglib/ogg/xiphcomment.h | 253 --- 3rdparty/taglib/riff/aiff/aifffile.cpp | 150 -- 3rdparty/taglib/riff/aiff/aifffile.h | 141 -- 3rdparty/taglib/riff/aiff/aiffproperties.cpp | 159 -- 3rdparty/taglib/riff/aiff/aiffproperties.h | 130 -- 3rdparty/taglib/riff/rifffile.cpp | 382 ---- 3rdparty/taglib/riff/rifffile.h | 157 -- 3rdparty/taglib/riff/riffutils.h | 60 - 3rdparty/taglib/riff/wav/infotag.cpp | 234 -- 3rdparty/taglib/riff/wav/infotag.h | 162 -- 3rdparty/taglib/riff/wav/wavfile.cpp | 237 -- 3rdparty/taglib/riff/wav/wavfile.h | 193 -- 3rdparty/taglib/riff/wav/wavproperties.cpp | 173 -- 3rdparty/taglib/riff/wav/wavproperties.h | 121 - 3rdparty/taglib/s3m/s3mfile.cpp | 229 -- 3rdparty/taglib/s3m/s3mfile.h | 95 - 3rdparty/taglib/s3m/s3mproperties.cpp | 182 -- 3rdparty/taglib/s3m/s3mproperties.h | 96 - 3rdparty/taglib/tag.cpp | 195 -- 3rdparty/taglib/tag.h | 196 -- 3rdparty/taglib/taglib-config.h.cmake | 35 - 3rdparty/taglib/taglib_export.h | 43 - 3rdparty/taglib/tagunion.cpp | 251 -- 3rdparty/taglib/tagunion.h | 109 - 3rdparty/taglib/tagutils.cpp | 108 - 3rdparty/taglib/tagutils.h | 54 - 3rdparty/taglib/toolkit/taglib.cpp | 49 - 3rdparty/taglib/toolkit/taglib.h | 182 -- 3rdparty/taglib/toolkit/tbytevector.cpp | 898 -------- 3rdparty/taglib/toolkit/tbytevector.h | 558 ----- 3rdparty/taglib/toolkit/tbytevectorlist.cpp | 82 - 3rdparty/taglib/toolkit/tbytevectorlist.h | 80 - 3rdparty/taglib/toolkit/tbytevectorstream.cpp | 154 -- 3rdparty/taglib/toolkit/tbytevectorstream.h | 137 -- 3rdparty/taglib/toolkit/tdebug.cpp | 63 - 3rdparty/taglib/toolkit/tdebug.h | 72 - 3rdparty/taglib/toolkit/tdebuglistener.cpp | 80 - 3rdparty/taglib/toolkit/tdebuglistener.h | 72 - 3rdparty/taglib/toolkit/tfile.cpp | 311 --- 3rdparty/taglib/toolkit/tfile.h | 293 --- 3rdparty/taglib/toolkit/tfilestream.cpp | 496 ---- 3rdparty/taglib/toolkit/tfilestream.h | 151 -- 3rdparty/taglib/toolkit/tiostream.cpp | 95 - 3rdparty/taglib/toolkit/tiostream.h | 163 -- 3rdparty/taglib/toolkit/tlist.h | 261 --- 3rdparty/taglib/toolkit/tlist.tcc | 324 --- 3rdparty/taglib/toolkit/tmap.h | 202 -- 3rdparty/taglib/toolkit/tmap.tcc | 171 -- 3rdparty/taglib/toolkit/tpicture.cpp | 179 -- 3rdparty/taglib/toolkit/tpicture.h | 157 -- 3rdparty/taglib/toolkit/tpicturemap.cpp | 75 - 3rdparty/taglib/toolkit/tpicturemap.h | 89 - 3rdparty/taglib/toolkit/tpropertymap.cpp | 169 -- 3rdparty/taglib/toolkit/tpropertymap.h | 222 -- 3rdparty/taglib/toolkit/trefcounter.cpp | 95 - 3rdparty/taglib/toolkit/trefcounter.h | 64 - 3rdparty/taglib/toolkit/tstring.cpp | 693 ------ 3rdparty/taglib/toolkit/tstring.h | 521 ----- 3rdparty/taglib/toolkit/tstringhandler.cpp | 30 - 3rdparty/taglib/toolkit/tstringhandler.h | 72 - 3rdparty/taglib/toolkit/tstringlist.cpp | 115 - 3rdparty/taglib/toolkit/tstringlist.h | 108 - 3rdparty/taglib/toolkit/tutils.h | 218 -- 3rdparty/taglib/toolkit/tzlib.cpp | 101 - 3rdparty/taglib/toolkit/tzlib.h | 56 - 3rdparty/taglib/trueaudio/trueaudiofile.cpp | 287 --- 3rdparty/taglib/trueaudio/trueaudiofile.h | 226 -- .../taglib/trueaudio/trueaudioproperties.cpp | 151 -- .../taglib/trueaudio/trueaudioproperties.h | 114 - 3rdparty/taglib/wavpack/wavpackfile.cpp | 269 --- 3rdparty/taglib/wavpack/wavpackfile.h | 212 -- 3rdparty/taglib/wavpack/wavpackproperties.cpp | 211 -- 3rdparty/taglib/wavpack/wavpackproperties.h | 126 - 3rdparty/taglib/xm/xmfile.cpp | 597 ----- 3rdparty/taglib/xm/xmfile.h | 95 - 3rdparty/taglib/xm/xmproperties.cpp | 162 -- 3rdparty/taglib/xm/xmproperties.h | 89 - 3rdparty/utf8-cpp/CMakeLists.txt | 1 - 3rdparty/utf8-cpp/checked.h | 327 --- 3rdparty/utf8-cpp/core.h | 332 --- CMakeLists.txt | 35 +- README.md | 1 + debian/copyright | 176 -- ext/libstrawberry-tagreader/tagreader.h | 4 - src/config.h.in | 1 - src/core/song.cpp | 4 - tests/taglib/CMakeLists.txt | 91 - tests/taglib/data/005411.id3 | Bin 38402 -> 0 bytes tests/taglib/data/64bit.mp4 | Bin 85 -> 0 bytes tests/taglib/data/alaw.aifc | Bin 1890 -> 0 bytes tests/taglib/data/alaw.wav | Bin 56858 -> 0 bytes tests/taglib/data/ape-id3v1.mp3 | Bin 8419 -> 0 bytes tests/taglib/data/ape-id3v2.mp3 | Bin 9341 -> 0 bytes tests/taglib/data/ape.mp3 | Bin 8291 -> 0 bytes tests/taglib/data/bladeenc.mp3 | Bin 28422 -> 0 bytes tests/taglib/data/blank_video.m4v | Bin 15018 -> 0 bytes tests/taglib/data/broken-tenc.id3 | Bin 400 -> 0 bytes tests/taglib/data/changed.mod | Bin 3132 -> 0 bytes tests/taglib/data/changed.s3m | Bin 544 -> 0 bytes tests/taglib/data/changed.xm | Bin 5471 -> 0 bytes tests/taglib/data/click.mpc | Bin 1588 -> 0 bytes tests/taglib/data/click.wv | Bin 3176 -> 0 bytes tests/taglib/data/compressed_id3_frame.mp3 | Bin 5000 -> 0 bytes .../data/correctness_gain_silent_output.opus | Bin 35506 -> 0 bytes tests/taglib/data/covr-junk.m4a | Bin 5108 -> 0 bytes tests/taglib/data/duplicate_id3v2.aiff | Bin 8124 -> 0 bytes tests/taglib/data/duplicate_id3v2.mp3 | Bin 10138 -> 0 bytes tests/taglib/data/duplicate_tags.wav | Bin 17052 -> 0 bytes tests/taglib/data/empty-seektable.flac | Bin 4608 -> 0 bytes tests/taglib/data/empty.aiff | Bin 5936 -> 0 bytes tests/taglib/data/empty.dsf | Bin 92 -> 0 bytes tests/taglib/data/empty.ogg | Bin 4328 -> 0 bytes tests/taglib/data/empty.spx | Bin 24301 -> 0 bytes tests/taglib/data/empty.tta | Bin 79538 -> 0 bytes tests/taglib/data/empty.wav | Bin 14744 -> 0 bytes tests/taglib/data/empty10ms.dff | Bin 7186 -> 0 bytes tests/taglib/data/empty10ms.dsf | Bin 8284 -> 0 bytes tests/taglib/data/empty_alac.m4a | Bin 5380 -> 0 bytes tests/taglib/data/empty_flac.oga | Bin 9113 -> 0 bytes tests/taglib/data/empty_vorbis.oga | Bin 4328 -> 0 bytes tests/taglib/data/excessive_alloc.aif | Bin 2170 -> 0 bytes tests/taglib/data/excessive_alloc.mp3 | Bin 925 -> 0 bytes tests/taglib/data/float64.wav | Bin 68584 -> 0 bytes tests/taglib/data/four_channels.wv | Bin 53520 -> 0 bytes tests/taglib/data/garbage.mp3 | Bin 8190 -> 0 bytes tests/taglib/data/gnre.m4a | Bin 5026 -> 0 bytes tests/taglib/data/has-tags.m4a | Bin 5108 -> 0 bytes tests/taglib/data/id3v22-tda.mp3 | Bin 4096 -> 0 bytes tests/taglib/data/ilst-is-last.m4a | Bin 32768 -> 0 bytes tests/taglib/data/infloop.m4a | Bin 53192 -> 0 bytes tests/taglib/data/infloop.mpc | Bin 434 -> 0 bytes tests/taglib/data/infloop.wav | Bin 14272 -> 0 bytes tests/taglib/data/infloop.wv | Bin 2462 -> 0 bytes tests/taglib/data/invalid-frames1.mp3 | Bin 8164 -> 0 bytes tests/taglib/data/invalid-frames2.mp3 | Bin 7898 -> 0 bytes tests/taglib/data/invalid-frames3.mp3 | Bin 8192 -> 0 bytes tests/taglib/data/lame_cbr.mp3 | Bin 4096 -> 0 bytes tests/taglib/data/lame_vbr.mp3 | Bin 4096 -> 0 bytes tests/taglib/data/longloop.ape | Bin 184 -> 0 bytes tests/taglib/data/lossless.wma | Bin 99013 -> 0 bytes tests/taglib/data/lowercase-fields.ogg | Bin 4477 -> 0 bytes tests/taglib/data/mac-390-hdr.ape | Bin 128 -> 0 bytes tests/taglib/data/mac-396.ape | Bin 104 -> 0 bytes tests/taglib/data/mac-399-id3v2.ape | Bin 89155 -> 0 bytes tests/taglib/data/mac-399-tagged.ape | Bin 91591 -> 0 bytes tests/taglib/data/mac-399.ape | Bin 85212 -> 0 bytes tests/taglib/data/matroska.mka | Bin 4707 -> 0 bytes tests/taglib/data/mpeg2.mp3 | Bin 16384 -> 0 bytes tests/taglib/data/multiple-vc.flac | Bin 4754 -> 0 bytes tests/taglib/data/no-extension | Bin 256 -> 0 bytes tests/taglib/data/no-tags.3g2 | Bin 68335 -> 0 bytes tests/taglib/data/no-tags.flac | Bin 4692 -> 0 bytes tests/taglib/data/no-tags.m4a | Bin 2898 -> 0 bytes tests/taglib/data/no_length.wv | Bin 532 -> 0 bytes tests/taglib/data/noise.aif | Bin 4400 -> 0 bytes tests/taglib/data/noise_odd.aif | Bin 4399 -> 0 bytes tests/taglib/data/pcm_with_fact_chunk.wav | Bin 14756 -> 0 bytes tests/taglib/data/rare_frames.mp3 | Bin 8320 -> 0 bytes tests/taglib/data/segfault.aif | Bin 31 -> 0 bytes tests/taglib/data/segfault.mpc | Bin 19 -> 0 bytes tests/taglib/data/segfault.oga | Bin 120 -> 0 bytes tests/taglib/data/segfault.wav | Bin 30 -> 0 bytes tests/taglib/data/segfault2.mpc | 1 - tests/taglib/data/silence-1.wma | Bin 35416 -> 0 bytes tests/taglib/data/silence-44-s.flac | Bin 50904 -> 0 bytes tests/taglib/data/sinewave.flac | Bin 64567 -> 0 bytes tests/taglib/data/stripped.xm | Bin 602 -> 0 bytes tests/taglib/data/sv4_header.mpc | Bin 128 -> 0 bytes tests/taglib/data/sv5_header.mpc | Bin 128 -> 0 bytes tests/taglib/data/sv8_header.mpc | Bin 114 -> 0 bytes tests/taglib/data/tagged.tta | Bin 81819 -> 0 bytes tests/taglib/data/tagged.wv | Bin 76627 -> 0 bytes tests/taglib/data/test.it | Bin 644 -> 0 bytes tests/taglib/data/test.mod | Bin 3132 -> 0 bytes tests/taglib/data/test.ogg | Bin 4408 -> 0 bytes tests/taglib/data/test.s3m | Bin 544 -> 0 bytes tests/taglib/data/test.xm | Bin 5471 -> 0 bytes tests/taglib/data/toc_many_children.mp3 | Bin 11525 -> 0 bytes tests/taglib/data/unsupported-extension.xx | Bin 256 -> 0 bytes tests/taglib/data/unsynch.id3 | Bin 320 -> 0 bytes tests/taglib/data/w000.mp3 | Bin 512 -> 0 bytes tests/taglib/data/xing.mp3 | Bin 8208 -> 0 bytes tests/taglib/data/zero-length-mdat.m4a | Bin 4517 -> 0 bytes tests/taglib/data/zero-size-chunk.wav | Bin 1024 -> 0 bytes tests/taglib/data/zero-sized-padding.flac | Bin 4692 -> 0 bytes tests/taglib/data/zerodiv.ape | Bin 946 -> 0 bytes tests/taglib/data/zerodiv.mpc | Bin 405 -> 0 bytes tests/taglib/main.cpp | 88 - tests/taglib/test_aiff.cpp | 130 -- tests/taglib/test_ape.cpp | 175 -- tests/taglib/test_apetag.cpp | 145 -- tests/taglib/test_asf.cpp | 306 --- tests/taglib/test_bytevector.cpp | 579 ----- tests/taglib/test_bytevectorlist.cpp | 58 - tests/taglib/test_bytevectorstream.cpp | 121 - tests/taglib/test_dsdiff.cpp | 108 - tests/taglib/test_dsf.cpp | 68 - tests/taglib/test_file.cpp | 147 -- tests/taglib/test_fileref.cpp | 305 --- tests/taglib/test_flac.cpp | 514 ----- tests/taglib/test_flacpicture.cpp | 72 - .../taglib/test_flacunknownmetadatablock.cpp | 60 - tests/taglib/test_id3v1.cpp | 69 - tests/taglib/test_id3v2.cpp | 1285 ----------- tests/taglib/test_info.cpp | 72 - tests/taglib/test_it.cpp | 136 -- tests/taglib/test_list.cpp | 72 - tests/taglib/test_map.cpp | 68 - tests/taglib/test_mod.cpp | 126 - tests/taglib/test_mp4.cpp | 439 ---- tests/taglib/test_mp4coverart.cpp | 70 - tests/taglib/test_mp4item.cpp | 59 - tests/taglib/test_mpc.cpp | 173 -- tests/taglib/test_mpeg.cpp | 392 ---- tests/taglib/test_ogg.cpp | 218 -- tests/taglib/test_oggflac.cpp | 117 - tests/taglib/test_opus.cpp | 131 -- tests/taglib/test_propertymap.cpp | 98 - tests/taglib/test_riff.cpp | 288 --- tests/taglib/test_s3m.cpp | 124 - tests/taglib/test_speex.cpp | 98 - tests/taglib/test_string.cpp | 362 --- tests/taglib/test_synchdata.cpp | 115 - tests/taglib/test_trueaudio.cpp | 119 - tests/taglib/test_wav.cpp | 295 --- tests/taglib/test_wavpack.cpp | 143 -- tests/taglib/test_xiphcomment.cpp | 209 -- tests/taglib/test_xm.cpp | 214 -- tests/taglib/utils.h | 140 -- 398 files changed, 25 insertions(+), 63930 deletions(-) delete mode 100644 3rdparty/taglib/CMakeLists.txt delete mode 100644 3rdparty/taglib/COPYING delete mode 100644 3rdparty/taglib/ConfigureChecks.cmake delete mode 100644 3rdparty/taglib/README.md delete mode 100644 3rdparty/taglib/ape/ape-tag-format.txt delete mode 100644 3rdparty/taglib/ape/apefile.cpp delete mode 100644 3rdparty/taglib/ape/apefile.h delete mode 100644 3rdparty/taglib/ape/apefooter.cpp delete mode 100644 3rdparty/taglib/ape/apefooter.h delete mode 100644 3rdparty/taglib/ape/apeitem.cpp delete mode 100644 3rdparty/taglib/ape/apeitem.h delete mode 100644 3rdparty/taglib/ape/apeproperties.cpp delete mode 100644 3rdparty/taglib/ape/apeproperties.h delete mode 100644 3rdparty/taglib/ape/apetag.cpp delete mode 100644 3rdparty/taglib/ape/apetag.h delete mode 100644 3rdparty/taglib/asf/asfattribute.cpp delete mode 100644 3rdparty/taglib/asf/asfattribute.h delete mode 100644 3rdparty/taglib/asf/asffile.cpp delete mode 100644 3rdparty/taglib/asf/asffile.h delete mode 100644 3rdparty/taglib/asf/asfpicture.cpp delete mode 100644 3rdparty/taglib/asf/asfpicture.h delete mode 100644 3rdparty/taglib/asf/asfproperties.cpp delete mode 100644 3rdparty/taglib/asf/asfproperties.h delete mode 100644 3rdparty/taglib/asf/asftag.cpp delete mode 100644 3rdparty/taglib/asf/asftag.h delete mode 100644 3rdparty/taglib/asf/asfutils.h delete mode 100644 3rdparty/taglib/audioproperties.cpp delete mode 100644 3rdparty/taglib/audioproperties.h delete mode 100644 3rdparty/taglib/dsdiff/dsdiffdiintag.cpp delete mode 100644 3rdparty/taglib/dsdiff/dsdiffdiintag.h delete mode 100644 3rdparty/taglib/dsdiff/dsdifffile.cpp delete mode 100644 3rdparty/taglib/dsdiff/dsdifffile.h delete mode 100644 3rdparty/taglib/dsdiff/dsdiffproperties.cpp delete mode 100644 3rdparty/taglib/dsdiff/dsdiffproperties.h delete mode 100644 3rdparty/taglib/dsf/dsffile.cpp delete mode 100644 3rdparty/taglib/dsf/dsffile.h delete mode 100644 3rdparty/taglib/dsf/dsfproperties.cpp delete mode 100644 3rdparty/taglib/dsf/dsfproperties.h delete mode 100644 3rdparty/taglib/fileref.cpp delete mode 100644 3rdparty/taglib/fileref.h delete mode 100644 3rdparty/taglib/flac/flacfile.cpp delete mode 100644 3rdparty/taglib/flac/flacfile.h delete mode 100644 3rdparty/taglib/flac/flacmetadatablock.cpp delete mode 100644 3rdparty/taglib/flac/flacmetadatablock.h delete mode 100644 3rdparty/taglib/flac/flacpicture.cpp delete mode 100644 3rdparty/taglib/flac/flacpicture.h delete mode 100644 3rdparty/taglib/flac/flacproperties.cpp delete mode 100644 3rdparty/taglib/flac/flacproperties.h delete mode 100644 3rdparty/taglib/flac/flacunknownmetadatablock.cpp delete mode 100644 3rdparty/taglib/flac/flacunknownmetadatablock.h delete mode 100644 3rdparty/taglib/it/itfile.cpp delete mode 100644 3rdparty/taglib/it/itfile.h delete mode 100644 3rdparty/taglib/it/itproperties.cpp delete mode 100644 3rdparty/taglib/it/itproperties.h delete mode 100644 3rdparty/taglib/mod/modfile.cpp delete mode 100644 3rdparty/taglib/mod/modfile.h delete mode 100644 3rdparty/taglib/mod/modfilebase.cpp delete mode 100644 3rdparty/taglib/mod/modfilebase.h delete mode 100644 3rdparty/taglib/mod/modfileprivate.h delete mode 100644 3rdparty/taglib/mod/modproperties.cpp delete mode 100644 3rdparty/taglib/mod/modproperties.h delete mode 100644 3rdparty/taglib/mod/modtag.cpp delete mode 100644 3rdparty/taglib/mod/modtag.h delete mode 100644 3rdparty/taglib/mp4/mp4atom.cpp delete mode 100644 3rdparty/taglib/mp4/mp4atom.h delete mode 100644 3rdparty/taglib/mp4/mp4coverart.cpp delete mode 100644 3rdparty/taglib/mp4/mp4coverart.h delete mode 100644 3rdparty/taglib/mp4/mp4file.cpp delete mode 100644 3rdparty/taglib/mp4/mp4file.h delete mode 100644 3rdparty/taglib/mp4/mp4item.cpp delete mode 100644 3rdparty/taglib/mp4/mp4item.h delete mode 100644 3rdparty/taglib/mp4/mp4properties.cpp delete mode 100644 3rdparty/taglib/mp4/mp4properties.h delete mode 100644 3rdparty/taglib/mp4/mp4tag.cpp delete mode 100644 3rdparty/taglib/mp4/mp4tag.h delete mode 100644 3rdparty/taglib/mpc/mpcfile.cpp delete mode 100644 3rdparty/taglib/mpc/mpcfile.h delete mode 100644 3rdparty/taglib/mpc/mpcproperties.cpp delete mode 100644 3rdparty/taglib/mpc/mpcproperties.h delete mode 100644 3rdparty/taglib/mpeg/id3v1/id3v1genres.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v1/id3v1genres.h delete mode 100644 3rdparty/taglib/mpeg/id3v1/id3v1tag.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v1/id3v1tag.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/attachedpictureframe.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/chapterframe.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/chapterframe.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/commentsframe.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/commentsframe.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/eventtimingcodesframe.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/ownershipframe.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/ownershipframe.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/podcastframe.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/podcastframe.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/popularimeterframe.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/popularimeterframe.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/privateframe.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/privateframe.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/relativevolumeframe.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/unknownframe.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/unknownframe.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/urllinkframe.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/frames/urllinkframe.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/id3v2.2.0.txt delete mode 100644 3rdparty/taglib/mpeg/id3v2/id3v2.3.0.txt delete mode 100644 3rdparty/taglib/mpeg/id3v2/id3v2.4.0-frames.txt delete mode 100644 3rdparty/taglib/mpeg/id3v2/id3v2.4.0-structure.txt delete mode 100644 3rdparty/taglib/mpeg/id3v2/id3v2.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/id3v2extendedheader.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/id3v2extendedheader.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/id3v2footer.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/id3v2footer.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/id3v2frame.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/id3v2frame.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/id3v2framefactory.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/id3v2framefactory.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/id3v2header.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/id3v2header.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/id3v2synchdata.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/id3v2synchdata.h delete mode 100644 3rdparty/taglib/mpeg/id3v2/id3v2tag.cpp delete mode 100644 3rdparty/taglib/mpeg/id3v2/id3v2tag.h delete mode 100644 3rdparty/taglib/mpeg/mpegfile.cpp delete mode 100644 3rdparty/taglib/mpeg/mpegfile.h delete mode 100644 3rdparty/taglib/mpeg/mpegheader.cpp delete mode 100644 3rdparty/taglib/mpeg/mpegheader.h delete mode 100644 3rdparty/taglib/mpeg/mpegproperties.cpp delete mode 100644 3rdparty/taglib/mpeg/mpegproperties.h delete mode 100644 3rdparty/taglib/mpeg/mpegutils.h delete mode 100644 3rdparty/taglib/mpeg/xingheader.cpp delete mode 100644 3rdparty/taglib/mpeg/xingheader.h delete mode 100644 3rdparty/taglib/ogg/flac/oggflacfile.cpp delete mode 100644 3rdparty/taglib/ogg/flac/oggflacfile.h delete mode 100644 3rdparty/taglib/ogg/oggfile.cpp delete mode 100644 3rdparty/taglib/ogg/oggfile.h delete mode 100644 3rdparty/taglib/ogg/oggpage.cpp delete mode 100644 3rdparty/taglib/ogg/oggpage.h delete mode 100644 3rdparty/taglib/ogg/oggpageheader.cpp delete mode 100644 3rdparty/taglib/ogg/oggpageheader.h delete mode 100644 3rdparty/taglib/ogg/opus/opusfile.cpp delete mode 100644 3rdparty/taglib/ogg/opus/opusfile.h delete mode 100644 3rdparty/taglib/ogg/opus/opusproperties.cpp delete mode 100644 3rdparty/taglib/ogg/opus/opusproperties.h delete mode 100644 3rdparty/taglib/ogg/speex/speexfile.cpp delete mode 100644 3rdparty/taglib/ogg/speex/speexfile.h delete mode 100644 3rdparty/taglib/ogg/speex/speexproperties.cpp delete mode 100644 3rdparty/taglib/ogg/speex/speexproperties.h delete mode 100644 3rdparty/taglib/ogg/vorbis/vorbisfile.cpp delete mode 100644 3rdparty/taglib/ogg/vorbis/vorbisfile.h delete mode 100644 3rdparty/taglib/ogg/vorbis/vorbisproperties.cpp delete mode 100644 3rdparty/taglib/ogg/vorbis/vorbisproperties.h delete mode 100644 3rdparty/taglib/ogg/xiphcomment.cpp delete mode 100644 3rdparty/taglib/ogg/xiphcomment.h delete mode 100644 3rdparty/taglib/riff/aiff/aifffile.cpp delete mode 100644 3rdparty/taglib/riff/aiff/aifffile.h delete mode 100644 3rdparty/taglib/riff/aiff/aiffproperties.cpp delete mode 100644 3rdparty/taglib/riff/aiff/aiffproperties.h delete mode 100644 3rdparty/taglib/riff/rifffile.cpp delete mode 100644 3rdparty/taglib/riff/rifffile.h delete mode 100644 3rdparty/taglib/riff/riffutils.h delete mode 100644 3rdparty/taglib/riff/wav/infotag.cpp delete mode 100644 3rdparty/taglib/riff/wav/infotag.h delete mode 100644 3rdparty/taglib/riff/wav/wavfile.cpp delete mode 100644 3rdparty/taglib/riff/wav/wavfile.h delete mode 100644 3rdparty/taglib/riff/wav/wavproperties.cpp delete mode 100644 3rdparty/taglib/riff/wav/wavproperties.h delete mode 100644 3rdparty/taglib/s3m/s3mfile.cpp delete mode 100644 3rdparty/taglib/s3m/s3mfile.h delete mode 100644 3rdparty/taglib/s3m/s3mproperties.cpp delete mode 100644 3rdparty/taglib/s3m/s3mproperties.h delete mode 100644 3rdparty/taglib/tag.cpp delete mode 100644 3rdparty/taglib/tag.h delete mode 100644 3rdparty/taglib/taglib-config.h.cmake delete mode 100644 3rdparty/taglib/taglib_export.h delete mode 100644 3rdparty/taglib/tagunion.cpp delete mode 100644 3rdparty/taglib/tagunion.h delete mode 100644 3rdparty/taglib/tagutils.cpp delete mode 100644 3rdparty/taglib/tagutils.h delete mode 100644 3rdparty/taglib/toolkit/taglib.cpp delete mode 100644 3rdparty/taglib/toolkit/taglib.h delete mode 100644 3rdparty/taglib/toolkit/tbytevector.cpp delete mode 100644 3rdparty/taglib/toolkit/tbytevector.h delete mode 100644 3rdparty/taglib/toolkit/tbytevectorlist.cpp delete mode 100644 3rdparty/taglib/toolkit/tbytevectorlist.h delete mode 100644 3rdparty/taglib/toolkit/tbytevectorstream.cpp delete mode 100644 3rdparty/taglib/toolkit/tbytevectorstream.h delete mode 100644 3rdparty/taglib/toolkit/tdebug.cpp delete mode 100644 3rdparty/taglib/toolkit/tdebug.h delete mode 100644 3rdparty/taglib/toolkit/tdebuglistener.cpp delete mode 100644 3rdparty/taglib/toolkit/tdebuglistener.h delete mode 100644 3rdparty/taglib/toolkit/tfile.cpp delete mode 100644 3rdparty/taglib/toolkit/tfile.h delete mode 100644 3rdparty/taglib/toolkit/tfilestream.cpp delete mode 100644 3rdparty/taglib/toolkit/tfilestream.h delete mode 100644 3rdparty/taglib/toolkit/tiostream.cpp delete mode 100644 3rdparty/taglib/toolkit/tiostream.h delete mode 100644 3rdparty/taglib/toolkit/tlist.h delete mode 100644 3rdparty/taglib/toolkit/tlist.tcc delete mode 100644 3rdparty/taglib/toolkit/tmap.h delete mode 100644 3rdparty/taglib/toolkit/tmap.tcc delete mode 100644 3rdparty/taglib/toolkit/tpicture.cpp delete mode 100644 3rdparty/taglib/toolkit/tpicture.h delete mode 100644 3rdparty/taglib/toolkit/tpicturemap.cpp delete mode 100644 3rdparty/taglib/toolkit/tpicturemap.h delete mode 100644 3rdparty/taglib/toolkit/tpropertymap.cpp delete mode 100644 3rdparty/taglib/toolkit/tpropertymap.h delete mode 100644 3rdparty/taglib/toolkit/trefcounter.cpp delete mode 100644 3rdparty/taglib/toolkit/trefcounter.h delete mode 100644 3rdparty/taglib/toolkit/tstring.cpp delete mode 100644 3rdparty/taglib/toolkit/tstring.h delete mode 100644 3rdparty/taglib/toolkit/tstringhandler.cpp delete mode 100644 3rdparty/taglib/toolkit/tstringhandler.h delete mode 100644 3rdparty/taglib/toolkit/tstringlist.cpp delete mode 100644 3rdparty/taglib/toolkit/tstringlist.h delete mode 100644 3rdparty/taglib/toolkit/tutils.h delete mode 100644 3rdparty/taglib/toolkit/tzlib.cpp delete mode 100644 3rdparty/taglib/toolkit/tzlib.h delete mode 100644 3rdparty/taglib/trueaudio/trueaudiofile.cpp delete mode 100644 3rdparty/taglib/trueaudio/trueaudiofile.h delete mode 100644 3rdparty/taglib/trueaudio/trueaudioproperties.cpp delete mode 100644 3rdparty/taglib/trueaudio/trueaudioproperties.h delete mode 100644 3rdparty/taglib/wavpack/wavpackfile.cpp delete mode 100644 3rdparty/taglib/wavpack/wavpackfile.h delete mode 100644 3rdparty/taglib/wavpack/wavpackproperties.cpp delete mode 100644 3rdparty/taglib/wavpack/wavpackproperties.h delete mode 100644 3rdparty/taglib/xm/xmfile.cpp delete mode 100644 3rdparty/taglib/xm/xmfile.h delete mode 100644 3rdparty/taglib/xm/xmproperties.cpp delete mode 100644 3rdparty/taglib/xm/xmproperties.h delete mode 100644 3rdparty/utf8-cpp/CMakeLists.txt delete mode 100644 3rdparty/utf8-cpp/checked.h delete mode 100644 3rdparty/utf8-cpp/core.h delete mode 100644 tests/taglib/CMakeLists.txt delete mode 100644 tests/taglib/data/005411.id3 delete mode 100644 tests/taglib/data/64bit.mp4 delete mode 100644 tests/taglib/data/alaw.aifc delete mode 100644 tests/taglib/data/alaw.wav delete mode 100644 tests/taglib/data/ape-id3v1.mp3 delete mode 100644 tests/taglib/data/ape-id3v2.mp3 delete mode 100644 tests/taglib/data/ape.mp3 delete mode 100644 tests/taglib/data/bladeenc.mp3 delete mode 100644 tests/taglib/data/blank_video.m4v delete mode 100644 tests/taglib/data/broken-tenc.id3 delete mode 100644 tests/taglib/data/changed.mod delete mode 100644 tests/taglib/data/changed.s3m delete mode 100644 tests/taglib/data/changed.xm delete mode 100644 tests/taglib/data/click.mpc delete mode 100644 tests/taglib/data/click.wv delete mode 100644 tests/taglib/data/compressed_id3_frame.mp3 delete mode 100644 tests/taglib/data/correctness_gain_silent_output.opus delete mode 100644 tests/taglib/data/covr-junk.m4a delete mode 100644 tests/taglib/data/duplicate_id3v2.aiff delete mode 100644 tests/taglib/data/duplicate_id3v2.mp3 delete mode 100644 tests/taglib/data/duplicate_tags.wav delete mode 100644 tests/taglib/data/empty-seektable.flac delete mode 100644 tests/taglib/data/empty.aiff delete mode 100644 tests/taglib/data/empty.dsf delete mode 100644 tests/taglib/data/empty.ogg delete mode 100644 tests/taglib/data/empty.spx delete mode 100644 tests/taglib/data/empty.tta delete mode 100644 tests/taglib/data/empty.wav delete mode 100644 tests/taglib/data/empty10ms.dff delete mode 100644 tests/taglib/data/empty10ms.dsf delete mode 100644 tests/taglib/data/empty_alac.m4a delete mode 100644 tests/taglib/data/empty_flac.oga delete mode 100644 tests/taglib/data/empty_vorbis.oga delete mode 100644 tests/taglib/data/excessive_alloc.aif delete mode 100644 tests/taglib/data/excessive_alloc.mp3 delete mode 100644 tests/taglib/data/float64.wav delete mode 100644 tests/taglib/data/four_channels.wv delete mode 100644 tests/taglib/data/garbage.mp3 delete mode 100644 tests/taglib/data/gnre.m4a delete mode 100644 tests/taglib/data/has-tags.m4a delete mode 100644 tests/taglib/data/id3v22-tda.mp3 delete mode 100644 tests/taglib/data/ilst-is-last.m4a delete mode 100644 tests/taglib/data/infloop.m4a delete mode 100644 tests/taglib/data/infloop.mpc delete mode 100644 tests/taglib/data/infloop.wav delete mode 100644 tests/taglib/data/infloop.wv delete mode 100644 tests/taglib/data/invalid-frames1.mp3 delete mode 100644 tests/taglib/data/invalid-frames2.mp3 delete mode 100644 tests/taglib/data/invalid-frames3.mp3 delete mode 100644 tests/taglib/data/lame_cbr.mp3 delete mode 100644 tests/taglib/data/lame_vbr.mp3 delete mode 100644 tests/taglib/data/longloop.ape delete mode 100644 tests/taglib/data/lossless.wma delete mode 100644 tests/taglib/data/lowercase-fields.ogg delete mode 100644 tests/taglib/data/mac-390-hdr.ape delete mode 100644 tests/taglib/data/mac-396.ape delete mode 100644 tests/taglib/data/mac-399-id3v2.ape delete mode 100644 tests/taglib/data/mac-399-tagged.ape delete mode 100644 tests/taglib/data/mac-399.ape delete mode 100644 tests/taglib/data/matroska.mka delete mode 100644 tests/taglib/data/mpeg2.mp3 delete mode 100644 tests/taglib/data/multiple-vc.flac delete mode 100644 tests/taglib/data/no-extension delete mode 100644 tests/taglib/data/no-tags.3g2 delete mode 100644 tests/taglib/data/no-tags.flac delete mode 100644 tests/taglib/data/no-tags.m4a delete mode 100644 tests/taglib/data/no_length.wv delete mode 100644 tests/taglib/data/noise.aif delete mode 100644 tests/taglib/data/noise_odd.aif delete mode 100644 tests/taglib/data/pcm_with_fact_chunk.wav delete mode 100644 tests/taglib/data/rare_frames.mp3 delete mode 100644 tests/taglib/data/segfault.aif delete mode 100644 tests/taglib/data/segfault.mpc delete mode 100644 tests/taglib/data/segfault.oga delete mode 100644 tests/taglib/data/segfault.wav delete mode 100644 tests/taglib/data/segfault2.mpc delete mode 100644 tests/taglib/data/silence-1.wma delete mode 100644 tests/taglib/data/silence-44-s.flac delete mode 100644 tests/taglib/data/sinewave.flac delete mode 100644 tests/taglib/data/stripped.xm delete mode 100644 tests/taglib/data/sv4_header.mpc delete mode 100644 tests/taglib/data/sv5_header.mpc delete mode 100644 tests/taglib/data/sv8_header.mpc delete mode 100644 tests/taglib/data/tagged.tta delete mode 100644 tests/taglib/data/tagged.wv delete mode 100644 tests/taglib/data/test.it delete mode 100644 tests/taglib/data/test.mod delete mode 100644 tests/taglib/data/test.ogg delete mode 100644 tests/taglib/data/test.s3m delete mode 100644 tests/taglib/data/test.xm delete mode 100644 tests/taglib/data/toc_many_children.mp3 delete mode 100644 tests/taglib/data/unsupported-extension.xx delete mode 100644 tests/taglib/data/unsynch.id3 delete mode 100644 tests/taglib/data/w000.mp3 delete mode 100644 tests/taglib/data/xing.mp3 delete mode 100644 tests/taglib/data/zero-length-mdat.m4a delete mode 100644 tests/taglib/data/zero-size-chunk.wav delete mode 100644 tests/taglib/data/zero-sized-padding.flac delete mode 100644 tests/taglib/data/zerodiv.ape delete mode 100644 tests/taglib/data/zerodiv.mpc delete mode 100644 tests/taglib/main.cpp delete mode 100644 tests/taglib/test_aiff.cpp delete mode 100644 tests/taglib/test_ape.cpp delete mode 100644 tests/taglib/test_apetag.cpp delete mode 100644 tests/taglib/test_asf.cpp delete mode 100644 tests/taglib/test_bytevector.cpp delete mode 100644 tests/taglib/test_bytevectorlist.cpp delete mode 100644 tests/taglib/test_bytevectorstream.cpp delete mode 100644 tests/taglib/test_dsdiff.cpp delete mode 100644 tests/taglib/test_dsf.cpp delete mode 100644 tests/taglib/test_file.cpp delete mode 100644 tests/taglib/test_fileref.cpp delete mode 100644 tests/taglib/test_flac.cpp delete mode 100644 tests/taglib/test_flacpicture.cpp delete mode 100644 tests/taglib/test_flacunknownmetadatablock.cpp delete mode 100644 tests/taglib/test_id3v1.cpp delete mode 100644 tests/taglib/test_id3v2.cpp delete mode 100644 tests/taglib/test_info.cpp delete mode 100644 tests/taglib/test_it.cpp delete mode 100644 tests/taglib/test_list.cpp delete mode 100644 tests/taglib/test_map.cpp delete mode 100644 tests/taglib/test_mod.cpp delete mode 100644 tests/taglib/test_mp4.cpp delete mode 100644 tests/taglib/test_mp4coverart.cpp delete mode 100644 tests/taglib/test_mp4item.cpp delete mode 100644 tests/taglib/test_mpc.cpp delete mode 100644 tests/taglib/test_mpeg.cpp delete mode 100644 tests/taglib/test_ogg.cpp delete mode 100644 tests/taglib/test_oggflac.cpp delete mode 100644 tests/taglib/test_opus.cpp delete mode 100644 tests/taglib/test_propertymap.cpp delete mode 100644 tests/taglib/test_riff.cpp delete mode 100644 tests/taglib/test_s3m.cpp delete mode 100644 tests/taglib/test_speex.cpp delete mode 100644 tests/taglib/test_string.cpp delete mode 100644 tests/taglib/test_synchdata.cpp delete mode 100644 tests/taglib/test_trueaudio.cpp delete mode 100644 tests/taglib/test_wav.cpp delete mode 100644 tests/taglib/test_wavpack.cpp delete mode 100644 tests/taglib/test_xiphcomment.cpp delete mode 100644 tests/taglib/test_xm.cpp delete mode 100644 tests/taglib/utils.h diff --git a/.circleci/config.yml b/.circleci/config.yml index f086e329..52451b7e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -80,6 +80,7 @@ commands: libpulse-devel gstreamer-devel gstreamer-plugins-base-devel + taglib-devel vlc-devel libQt5Core-devel libQt5Gui-devel @@ -183,6 +184,7 @@ commands: libgnutls28-dev libasound2-dev libpulse-dev + libtag1-dev qtbase5-dev qtbase5-dev-tools qtbase5-private-dev @@ -234,6 +236,7 @@ commands: libgnutls28-dev libasound2-dev libpulse-dev + libtag1-dev qtbase5-dev qtbase5-dev-tools qtbase5-private-dev diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index e410ea68..0f65e6e4 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -39,6 +39,7 @@ jobs: gstreamer-devel gstreamer-plugins-base-devel vlc-devel + taglib-devel libQt5Core-devel libQt5Gui-devel libQt5Widgets-devel @@ -107,6 +108,7 @@ jobs: gstreamer-devel gstreamer-plugins-base-devel vlc-devel + taglib-devel libQt5Core-devel libQt5Gui-devel libQt5Widgets-devel @@ -183,6 +185,7 @@ jobs: gstreamer-devel gstreamer-plugins-base-devel vlc-devel + taglib-devel libQt5Core-devel libQt5Gui-devel libQt5Widgets-devel @@ -260,6 +263,7 @@ jobs: gstreamer-devel gstreamer-plugins-base-devel vlc-devel + taglib-devel qt6-core-devel qt6-gui-devel qt6-widgets-devel @@ -338,6 +342,7 @@ jobs: #gstreamer-devel #gstreamer-plugins-base-devel #vlc-devel + #taglib-devel #libQt5Core-devel #libQt5Gui-devel #libQt5Widgets-devel @@ -418,6 +423,7 @@ jobs: #gstreamer-devel #gstreamer-plugins-base-devel #vlc-devel + #taglib-devel #qt6-core-devel #qt6-gui-devel #qt6-widgets-devel @@ -675,6 +681,7 @@ jobs: libmtp-devel libjpeg-devel cairo-devel + taglib-devel dbus-x11 xorg-x11-server-Xvfb xorg-x11-xauth @@ -737,6 +744,7 @@ jobs: libgnutls28-dev libasound2-dev libpulse-dev + libtag1-dev qtbase5-dev qtbase5-dev-tools qtbase5-private-dev @@ -794,6 +802,7 @@ jobs: libgnutls28-dev libasound2-dev libpulse-dev + libtag1-dev qtbase5-dev qtbase5-dev-tools qtbase5-private-dev @@ -855,6 +864,7 @@ jobs: libgnutls28-dev libasound2-dev libpulse-dev + libtag1-dev qtbase5-dev qtbase5-dev-tools qtbase5-private-dev @@ -917,6 +927,7 @@ jobs: libgnutls28-dev libasound2-dev libpulse-dev + libtag1-dev qtbase5-dev qtbase5-dev-tools qtbase5-private-dev @@ -979,6 +990,7 @@ jobs: libgnutls28-dev libasound2-dev libpulse-dev + libtag1-dev qtbase5-dev qtbase5-dev-tools qtbase5-private-dev diff --git a/3rdparty/README.md b/3rdparty/README.md index 6d088aa7..4df50c54 100644 --- a/3rdparty/README.md +++ b/3rdparty/README.md @@ -12,36 +12,6 @@ It is included here because it is not packed by distros and is also used on macO URL: https://github.com/itay-grudev/SingleApplication -taglib ------- - -TagLib is a library for reading and editing the meta-data of several popular audio formats. It is also used -by Strawberry to identify audio files. It is important that it is kept up-to-date for Strawberry to function -correctly. - -It is kept in 3rdparty because there currently is no official release of TagLib with the features and bugfixes -that are in the official repository. And also because some distros use older, or unpatched versions. - -There is a bug in the latest version (1.11.1) corrupting Ogg files, -see: https://github.com/taglib/taglib/issues/864 -If you decide to use the systems taglib, make sure it has been patched with the following commit: -https://github.com/taglib/taglib/commit/9336c82da3a04552168f208cd7a5fa4646701ea4 - -The current taglib in 3rdparty also has the following features: -- Audio file detection by content. -- DSF and DSDIFF support - -URL: https://github.com/taglib/taglib - - -utf8-cpp --------- - -This is 2 header files used by taglib, but kept in a separate directory because it is maintained by others. - -URL: http://utfcpp.sourceforge.net/ - - SPMediaKeyTap ------------- diff --git a/3rdparty/taglib/CMakeLists.txt b/3rdparty/taglib/CMakeLists.txt deleted file mode 100644 index e79148ec..00000000 --- a/3rdparty/taglib/CMakeLists.txt +++ /dev/null @@ -1,402 +0,0 @@ -cmake_minimum_required(VERSION 3.0) - -set(TAGLIB_SOVERSION_CURRENT 17) -set(TAGLIB_SOVERSION_REVISION 0) -set(TAGLIB_SOVERSION_AGE 16) - -math(EXPR TAGLIB_SOVERSION_MAJOR "${TAGLIB_SOVERSION_CURRENT} - ${TAGLIB_SOVERSION_AGE}") -math(EXPR TAGLIB_SOVERSION_MINOR "${TAGLIB_SOVERSION_AGE}") -math(EXPR TAGLIB_SOVERSION_PATCH "${TAGLIB_SOVERSION_REVISION}") - -include(ConfigureChecks.cmake) -set(TESTS_DIR "${CMAKE_SOURCE_DIR}/tests/taglib/") -configure_file(taglib-config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/taglib-config.h") - -add_definitions(-DHAVE_CONFIG_H) -add_definitions(-DTAGLIB_STATIC) - -set(tag_HDRS - tag.h - fileref.h - audioproperties.h - taglib_export.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 - toolkit/tiostream.h - toolkit/tfile.h - toolkit/tfilestream.h - toolkit/tmap.h - toolkit/tmap.tcc - toolkit/tpicture.h - toolkit/tpicturemap.h - toolkit/tpropertymap.h - toolkit/trefcounter.h - toolkit/tdebuglistener.h - mpeg/mpegfile.h - mpeg/mpegproperties.h - mpeg/mpegheader.h - mpeg/xingheader.h - mpeg/id3v1/id3v1tag.h - mpeg/id3v1/id3v1genres.h - mpeg/id3v2/id3v2.h - mpeg/id3v2/id3v2extendedheader.h - mpeg/id3v2/id3v2frame.h - mpeg/id3v2/id3v2header.h - mpeg/id3v2/id3v2synchdata.h - mpeg/id3v2/id3v2footer.h - mpeg/id3v2/id3v2framefactory.h - mpeg/id3v2/id3v2tag.h - mpeg/id3v2/frames/attachedpictureframe.h - mpeg/id3v2/frames/commentsframe.h - mpeg/id3v2/frames/eventtimingcodesframe.h - mpeg/id3v2/frames/generalencapsulatedobjectframe.h - mpeg/id3v2/frames/ownershipframe.h - mpeg/id3v2/frames/popularimeterframe.h - mpeg/id3v2/frames/privateframe.h - mpeg/id3v2/frames/relativevolumeframe.h - mpeg/id3v2/frames/synchronizedlyricsframe.h - mpeg/id3v2/frames/textidentificationframe.h - mpeg/id3v2/frames/uniquefileidentifierframe.h - mpeg/id3v2/frames/unknownframe.h - mpeg/id3v2/frames/unsynchronizedlyricsframe.h - mpeg/id3v2/frames/urllinkframe.h - mpeg/id3v2/frames/chapterframe.h - mpeg/id3v2/frames/tableofcontentsframe.h - mpeg/id3v2/frames/podcastframe.h - ogg/oggfile.h - ogg/oggpage.h - ogg/oggpageheader.h - ogg/xiphcomment.h - ogg/vorbis/vorbisfile.h - ogg/vorbis/vorbisproperties.h - ogg/flac/oggflacfile.h - ogg/speex/speexfile.h - ogg/speex/speexproperties.h - ogg/opus/opusfile.h - ogg/opus/opusproperties.h - flac/flacfile.h - flac/flacpicture.h - flac/flacproperties.h - flac/flacmetadatablock.h - ape/apefile.h - ape/apeproperties.h - ape/apetag.h - ape/apefooter.h - ape/apeitem.h - mpc/mpcfile.h - mpc/mpcproperties.h - wavpack/wavpackfile.h - wavpack/wavpackproperties.h - trueaudio/trueaudiofile.h - trueaudio/trueaudioproperties.h - riff/rifffile.h - riff/aiff/aifffile.h - riff/aiff/aiffproperties.h - riff/wav/wavfile.h - riff/wav/wavproperties.h - riff/wav/infotag.h - asf/asffile.h - asf/asfproperties.h - asf/asftag.h - asf/asfattribute.h - asf/asfpicture.h - mp4/mp4file.h - mp4/mp4atom.h - mp4/mp4tag.h - mp4/mp4item.h - mp4/mp4properties.h - mp4/mp4coverart.h - mod/modfilebase.h - mod/modfile.h - mod/modtag.h - mod/modproperties.h - it/itfile.h - it/itproperties.h - s3m/s3mfile.h - s3m/s3mproperties.h - xm/xmfile.h - xm/xmproperties.h - dsf/dsffile.h - dsf/dsfproperties.h - dsdiff/dsdifffile.h - dsdiff/dsdiffproperties.h - dsdiff/dsdiffdiintag.h -) - -set(mpeg_SRCS - mpeg/mpegfile.cpp - mpeg/mpegproperties.cpp - mpeg/mpegheader.cpp - mpeg/xingheader.cpp -) - -set(id3v1_SRCS - mpeg/id3v1/id3v1tag.cpp - mpeg/id3v1/id3v1genres.cpp -) - -set(id3v2_SRCS - mpeg/id3v2/id3v2framefactory.cpp - mpeg/id3v2/id3v2synchdata.cpp - mpeg/id3v2/id3v2tag.cpp - mpeg/id3v2/id3v2header.cpp - mpeg/id3v2/id3v2frame.cpp - mpeg/id3v2/id3v2footer.cpp - mpeg/id3v2/id3v2extendedheader.cpp - ) - -set(frames_SRCS - mpeg/id3v2/frames/attachedpictureframe.cpp - mpeg/id3v2/frames/commentsframe.cpp - mpeg/id3v2/frames/eventtimingcodesframe.cpp - mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp - mpeg/id3v2/frames/ownershipframe.cpp - mpeg/id3v2/frames/popularimeterframe.cpp - mpeg/id3v2/frames/privateframe.cpp - mpeg/id3v2/frames/relativevolumeframe.cpp - mpeg/id3v2/frames/synchronizedlyricsframe.cpp - mpeg/id3v2/frames/textidentificationframe.cpp - mpeg/id3v2/frames/uniquefileidentifierframe.cpp - mpeg/id3v2/frames/unknownframe.cpp - mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp - mpeg/id3v2/frames/urllinkframe.cpp - mpeg/id3v2/frames/chapterframe.cpp - mpeg/id3v2/frames/tableofcontentsframe.cpp - mpeg/id3v2/frames/podcastframe.cpp -) - -set(ogg_SRCS - ogg/oggfile.cpp - ogg/oggpage.cpp - ogg/oggpageheader.cpp - ogg/xiphcomment.cpp -) - -set(vorbis_SRCS - ogg/vorbis/vorbisfile.cpp - ogg/vorbis/vorbisproperties.cpp -) - -set(flacs_SRCS - flac/flacfile.cpp - flac/flacpicture.cpp - flac/flacproperties.cpp - flac/flacmetadatablock.cpp - flac/flacunknownmetadatablock.cpp -) - -set(oggflacs_SRCS - ogg/flac/oggflacfile.cpp -) - -set(mpc_SRCS - mpc/mpcfile.cpp - mpc/mpcproperties.cpp -) - -set(mp4_SRCS - mp4/mp4file.cpp - mp4/mp4atom.cpp - mp4/mp4tag.cpp - mp4/mp4item.cpp - mp4/mp4properties.cpp - mp4/mp4coverart.cpp -) - -set(ape_SRCS - ape/apetag.cpp - ape/apefooter.cpp - ape/apeitem.cpp - ape/apefile.cpp - ape/apeproperties.cpp -) - -set(wavpack_SRCS - wavpack/wavpackfile.cpp - wavpack/wavpackproperties.cpp -) - -set(speex_SRCS - ogg/speex/speexfile.cpp - ogg/speex/speexproperties.cpp -) - -set(opus_SRCS - ogg/opus/opusfile.cpp - ogg/opus/opusproperties.cpp -) - -set(trueaudio_SRCS - trueaudio/trueaudiofile.cpp - trueaudio/trueaudioproperties.cpp -) - -set(asf_SRCS - asf/asftag.cpp - asf/asffile.cpp - asf/asfproperties.cpp - asf/asfattribute.cpp - asf/asfpicture.cpp -) - -set(riff_SRCS - riff/rifffile.cpp -) - -set(aiff_SRCS - riff/aiff/aifffile.cpp - riff/aiff/aiffproperties.cpp -) - -set(wav_SRCS - riff/wav/wavfile.cpp - riff/wav/wavproperties.cpp - riff/wav/infotag.cpp -) - -set(mod_SRCS - mod/modfilebase.cpp - mod/modfile.cpp - mod/modtag.cpp - mod/modproperties.cpp -) - -set(s3m_SRCS - s3m/s3mfile.cpp - s3m/s3mproperties.cpp -) - -set(it_SRCS - it/itfile.cpp - it/itproperties.cpp -) - -set(xm_SRCS - xm/xmfile.cpp - xm/xmproperties.cpp -) - -set(dsf_SRCS - dsf/dsffile.cpp - dsf/dsfproperties.cpp -) - -set(dsdiff_SRCS - dsdiff/dsdifffile.cpp - dsdiff/dsdiffproperties.cpp - dsdiff/dsdiffdiintag.cpp -) - -set(toolkit_SRCS - toolkit/taglib.cpp - toolkit/tstring.cpp - toolkit/tstringlist.cpp - toolkit/tstringhandler.cpp - toolkit/tbytevector.cpp - toolkit/tbytevectorlist.cpp - toolkit/tbytevectorstream.cpp - toolkit/tiostream.cpp - toolkit/tfile.cpp - toolkit/tfilestream.cpp - toolkit/tdebug.cpp - toolkit/tpicture.cpp - toolkit/tpicturemap.cpp - toolkit/tpropertymap.cpp - toolkit/trefcounter.cpp - toolkit/tdebuglistener.cpp - toolkit/tzlib.cpp -) - -set(tag_LIB_SRCS - ${mpeg_SRCS} - ${id3v1_SRCS} - ${id3v2_SRCS} - ${frames_SRCS} - ${ogg_SRCS} - ${vorbis_SRCS} - ${oggflacs_SRCS} - ${mpc_SRCS} - ${ape_SRCS} - ${toolkit_SRCS} - ${flacs_SRCS} - ${wavpack_SRCS} - ${speex_SRCS} - ${trueaudio_SRCS} - ${riff_SRCS} - ${aiff_SRCS} ${wav_SRCS} - ${asf_SRCS} - ${mp4_SRCS} - ${mod_SRCS} - ${s3m_SRCS} - ${it_SRCS} - ${xm_SRCS} - ${opus_SRCS} - ${dsf_SRCS} - ${dsdiff_SRCS} - ${zlib_SRCS} - tag.cpp - tagunion.cpp - fileref.cpp - audioproperties.cpp - tagutils.cpp -) - -add_library(tag STATIC ${tag_LIB_SRCS} ${tag_HDRS}) - -target_include_directories(tag PRIVATE - ${ZLIB_INCLUDE_DIR} - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/toolkit - ${CMAKE_CURRENT_SOURCE_DIR}/asf - ${CMAKE_CURRENT_SOURCE_DIR}/mpeg - ${CMAKE_CURRENT_SOURCE_DIR}/ogg - ${CMAKE_CURRENT_SOURCE_DIR}/ogg/flac - ${CMAKE_CURRENT_SOURCE_DIR}/flac - ${CMAKE_CURRENT_SOURCE_DIR}/mpc - ${CMAKE_CURRENT_SOURCE_DIR}/mp4 - ${CMAKE_CURRENT_SOURCE_DIR}/ogg/vorbis - ${CMAKE_CURRENT_SOURCE_DIR}/ogg/speex - ${CMAKE_CURRENT_SOURCE_DIR}/ogg/opus - ${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2 - ${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2/frames - ${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v1 - ${CMAKE_CURRENT_SOURCE_DIR}/ape - ${CMAKE_CURRENT_SOURCE_DIR}/wavpack - ${CMAKE_CURRENT_SOURCE_DIR}/trueaudio - ${CMAKE_CURRENT_SOURCE_DIR}/riff - ${CMAKE_CURRENT_SOURCE_DIR}/riff/aiff - ${CMAKE_CURRENT_SOURCE_DIR}/riff/wav - ${CMAKE_CURRENT_SOURCE_DIR}/mod - ${CMAKE_CURRENT_SOURCE_DIR}/s3m - ${CMAKE_CURRENT_SOURCE_DIR}/it - ${CMAKE_CURRENT_SOURCE_DIR}/xm - ${CMAKE_CURRENT_SOURCE_DIR}/dsf - ${CMAKE_CURRENT_SOURCE_DIR}/dsdiff - ${CMAKE_SOURCE_DIR}/3rdparty -) - -target_link_libraries(tag PRIVATE ${ZLIB_LIBRARIES}) - -set_target_properties(tag PROPERTIES - VERSION ${TAGLIB_SOVERSION_MAJOR}.${TAGLIB_SOVERSION_MINOR}.${TAGLIB_SOVERSION_PATCH} - SOVERSION ${TAGLIB_SOVERSION_MAJOR} - DEFINE_SYMBOL MAKE_TAGLIB_LIB - LINK_INTERFACE_LIBRARIES "" -) - -foreach(header ${tag_HDRS}) - get_filename_component(header_name ${header} NAME) - configure_file( - "${header}" - "${CMAKE_CURRENT_BINARY_DIR}/headers/taglib/${header_name}" - COPYONLY - ) -endforeach() diff --git a/3rdparty/taglib/COPYING b/3rdparty/taglib/COPYING deleted file mode 100644 index 4362b491..00000000 --- a/3rdparty/taglib/COPYING +++ /dev/null @@ -1,502 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - 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 - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/3rdparty/taglib/ConfigureChecks.cmake b/3rdparty/taglib/ConfigureChecks.cmake deleted file mode 100644 index 1ac4e62a..00000000 --- a/3rdparty/taglib/ConfigureChecks.cmake +++ /dev/null @@ -1,216 +0,0 @@ -include(CheckLibraryExists) -include(CheckTypeSize) -include(CheckCXXCompilerFlag) -include(CheckCXXSourceCompiles) - -# Check if the size of numeric types are suitable. - -check_type_size("short" SIZEOF_SHORT) -if(NOT SIZEOF_SHORT EQUAL 2) - message(FATAL_ERROR "TagLib requires that short is 16-bit wide.") -endif() - -check_type_size("int" SIZEOF_INT) -if(NOT SIZEOF_INT EQUAL 4) - message(FATAL_ERROR "TagLib requires that int is 32-bit wide.") -endif() - -check_type_size("long long" SIZEOF_LONGLONG) -if(NOT SIZEOF_LONGLONG EQUAL 8) - message(FATAL_ERROR "TagLib requires that long long is 64-bit wide.") -endif() - -check_type_size("wchar_t" SIZEOF_WCHAR_T) -if(SIZEOF_WCHAR_T LESS 2) - message(FATAL_ERROR "TagLib requires that wchar_t is sufficient to store a UTF-16 char.") -endif() - -check_type_size("float" SIZEOF_FLOAT) -if(NOT SIZEOF_FLOAT EQUAL 4) - message(FATAL_ERROR "TagLib requires that float is 32-bit wide.") -endif() - -check_type_size("double" SIZEOF_DOUBLE) -if(NOT SIZEOF_DOUBLE EQUAL 8) - message(FATAL_ERROR "TagLib requires that double is 64-bit wide.") -endif() - -# Determine which kind of atomic operations your compiler supports. - -check_cxx_source_compiles(" - #include - int main() { - std::atomic_int x(1); - ++x; - --x; - return 0; - } -" HAVE_STD_ATOMIC) - -if(NOT HAVE_STD_ATOMIC) - check_cxx_source_compiles(" - int main() { - volatile int x; - __sync_add_and_fetch(&x, 1); - int y = __sync_sub_and_fetch(&x, 1); - return 0; - } - " HAVE_GCC_ATOMIC) - - if(NOT HAVE_GCC_ATOMIC) - check_cxx_source_compiles(" - #include - int main() { - volatile int32_t x; - OSAtomicIncrement32Barrier(&x); - int32_t y = OSAtomicDecrement32Barrier(&x); - return 0; - } - " HAVE_MAC_ATOMIC) - - if(NOT HAVE_MAC_ATOMIC) - check_cxx_source_compiles(" - #include - int main() { - volatile LONG x; - InterlockedIncrement(&x); - LONG y = InterlockedDecrement(&x); - return 0; - } - " HAVE_WIN_ATOMIC) - - if(NOT HAVE_WIN_ATOMIC) - check_cxx_source_compiles(" - #include - int main() { - volatile int x; - __sync_add_and_fetch(&x, 1); - int y = __sync_sub_and_fetch(&x, 1); - return 0; - } - " HAVE_IA64_ATOMIC) - endif() - endif() - endif() -endif() - -# Determine which kind of byte swap functions your compiler supports. - -check_cxx_source_compiles(" - int main() { - __builtin_bswap16(0); - __builtin_bswap32(0); - __builtin_bswap64(0); - return 0; - } -" HAVE_GCC_BYTESWAP) - -if(NOT HAVE_GCC_BYTESWAP) - check_cxx_source_compiles(" - #include - int main() { - __bswap_16(0); - __bswap_32(0); - __bswap_64(0); - return 0; - } - " HAVE_GLIBC_BYTESWAP) - - if(NOT HAVE_GLIBC_BYTESWAP) - check_cxx_source_compiles(" - #include - int main() { - _byteswap_ushort(0); - _byteswap_ulong(0); - _byteswap_uint64(0); - return 0; - } - " HAVE_MSC_BYTESWAP) - - if(NOT HAVE_MSC_BYTESWAP) - check_cxx_source_compiles(" - #include - int main() { - OSSwapInt16(0); - OSSwapInt32(0); - OSSwapInt64(0); - return 0; - } - " HAVE_MAC_BYTESWAP) - - if(NOT HAVE_MAC_BYTESWAP) - check_cxx_source_compiles(" - #include - int main() { - swap16(0); - swap32(0); - swap64(0); - return 0; - } - " HAVE_OPENBSD_BYTESWAP) - endif() - endif() - endif() -endif() - -# Determine whether your compiler supports some safer version of vsprintf. - -check_cxx_source_compiles(" - #include - #include - int main() { - char buf[20]; - va_list args; - vsnprintf(buf, 20, \"%d\", args); - return 0; - } -" HAVE_VSNPRINTF) - -if(NOT HAVE_VSNPRINTF) - check_cxx_source_compiles(" - #include - #include - int main() { - char buf[20]; - va_list args; - vsprintf_s(buf, \"%d\", args); - return 0; - } - " HAVE_VSPRINTF_S) -endif() - -# Determine whether your compiler supports ISO _strdup. - -check_cxx_source_compiles(" - #include - int main() { - _strdup(0); - return 0; - } -" HAVE_ISO_STRDUP) - -# Determine whether zlib is installed. - -if(NOT ZLIB_SOURCE) - find_package(ZLIB) - if(ZLIB_FOUND) - set(HAVE_ZLIB 1) - else() - set(HAVE_ZLIB 0) - endif() -endif() - -# Determine whether CppUnit is installed. - -if(BUILD_TESTS AND NOT BUILD_SHARED_LIBS) - find_package(CppUnit) - if(NOT CppUnit_FOUND) - message(STATUS "CppUnit not found, disabling tests.") - set(BUILD_TESTS OFF) - endif() -endif() - -# Detect WinRT mode -if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") - set(PLATFORM WINRT 1) -endif() diff --git a/3rdparty/taglib/README.md b/3rdparty/taglib/README.md deleted file mode 100644 index 5f087995..00000000 --- a/3rdparty/taglib/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# TagLib - -[![Build Status](https://travis-ci.org/taglib/taglib.svg?branch=master)](https://travis-ci.org/taglib/taglib) - -### TagLib Audio Metadata Library - -http://taglib.org/ - -TagLib is a library for reading and editing the metadata of several -popular audio formats. Currently it supports both ID3v1 and [ID3v2][] -for MP3 files, [Ogg Vorbis][] comments and ID3 tags -in [FLAC][], MPC, Speex, WavPack, TrueAudio, WAV, AIFF, MP4, APE, -DSF, DFF, and ASF files. - -TagLib is distributed under the [GNU Lesser General Public License][] -(LGPL) and [Mozilla Public License][] (MPL). Essentially that means that -it may be used in proprietary applications, but if changes are made to -TagLib they must be contributed back to the project. Please review the -licenses if you are considering using TagLib in your project. - - [ID3v2]: http://www.id3.org - [Ogg Vorbis]: http://vorbis.com/ - [FLAC]: https://xiph.org/flac/ - [GNU Lesser General Public License]: http://www.gnu.org/licenses/lgpl.html - [Mozilla Public License]: http://www.mozilla.org/MPL/MPL-1.1.html - diff --git a/3rdparty/taglib/ape/ape-tag-format.txt b/3rdparty/taglib/ape/ape-tag-format.txt deleted file mode 100644 index 21ff1c86..00000000 --- a/3rdparty/taglib/ape/ape-tag-format.txt +++ /dev/null @@ -1,170 +0,0 @@ -================================================================================ -= APE Tag Specification, Version 2.000 -================================================================================ - -Original Content (C) 2004, Frank Klemm -Formatting / Editing (C) 2004, Scott Wheeler - -================================================================================ -= Contents -================================================================================ - -1 - APE Tag General Structure -2 - APE Tag Header / Footer Format -3 - APE Tag Flags -4 - APE Tag Item Format -5 - APE Tag Item Supported Keys -6 - APE Tag Item Content -7 - Data Types -7.1 - Data Types / UTF-8 -7.2 - Data Types / Dates -7.3 - Data Types / Timestamps - -================================================================================ -= 1 - APE Tag General Structure -================================================================================ - -Member of Basic Components of SV8 Stream Note: - -It is strongly recommended that the data size be stored in the tags. The size -should normally be in the roughly one kilobyte, never more than 8 kilobytes. - -Larger data should be stored externally using link entries. Linked data is much -easier to process by normal programs, so for instance JPEG data should not be -included inside the audio file. - -APE Tag Version 2.000 (with header, recommended): - -/================================\ -| APE Tag Header | 32 bytes | -|-------------------|------------| -| APE Tag Item 1 | > 10 bytes | -| APE Tag Item 2 | > 10 bytes | -| APE Tag Item n-1 | > 10 bytes | -| APE Tag Item n | > 10 bytes | -|-------------------|------------| -| APE Tag Footer | 32 bytes | -\================================/ - - -APE tag items should be sorted ascending by size. When streaming, parts of the -APE tag may be dropped to reduce the danger of drop outs between tracks. This -is not required, but is strongly recommended. It would be desirable for the i -tems to be sorted by importance / size, but this is not feasible. This -convention should only be broken when adding less important small items and it -is not desirable to rewrite the entire tag. An APE tag at the end of a file -(the recommended location) must have at least a footer; an APE tag at the -beginning of a file (strongly discouraged) must have at least a header. - -APE Tag Version 1.000 (without header, deprecated) - -/================================\ -| APE Tag Item 1 | > 10 bytes | -| APE Tag Item 2 | > 10 bytes | -| APE Tag Item n-1 | > 10 bytes | -| APE Tag Item n | > 10 bytes | -|-------------------|------------| -| APE Tag Footer | 32 bytes | -\================================/ - -================================================================================ -= 2 - APE Tag Header / Footer Format -================================================================================ - -Contains number, length and attributes of all tag items - -Header and Footer are different in 1 bit in the Tags Flags to distinguish -between them. - -Member of APE Tag 2.0 - -/===========================================================================\ -| Preamble | 8 bytes | { 'A', 'P', 'E', 'T', 'A', 'G', 'E', 'X' } | -|----------------|---------|------------------------------------------------| -| Version Number | 4 bytes | 1000 = Version 1.000, 2000 = Version 2.000 | -|----------------|---------|------------------------------------------------| -| Tag Size | 4 bytes | Tag size in bytes including footer and all tag | -| | | items excluding the header (for 1.000 | -| | | compatibility) | -|----------------|---------|------------------------------------------------| -| Item Count | 4 bytes | Number of items in the tag | -|----------------|---------|------------------------------------------------| -| Tag Flags | 4 bytes | Global flags | -|----------------|---------|------------------------------------------------| -| Reserved | 8 bytes | Must be zeroed | -\===========================================================================/ - -================================================================================ -= 3 - APE Tag Flags -================================================================================ - -The general flag structure for either items or headers / footers is the same. -Bits 31, 30 and 29 are specific to headers / footers, whereas 2 through 0 are -item specific. - -Note: APE Tags from Version 1.0 do not use any of the following. All flags in -that version are zeroed and ignored when reading. - -/=================================================================\ -| Contains Header | Bit 31 | 1 - has header | 0 - no header | -|-----------------|-------------|---------------------------------| -| Contains Footer | Bit 30 | 1 - has footer | 0 - no footer | -|-----------------|-------------|---------------------------------| -| Is Header | Bit 29 | 1 - is header | 0 - is footer | -|-----------------|-------------|---------------------------------| -| Undefined | Bits 28 - 3 | Undefined, must be zeroed | -|-----------------|-------------|---------------------------------| -| Encoding | Bits 2 - 1 | 00 - UTF-8 | -| | | 01 - Binary Data * | -| | | 10 - External Reference ** | -| | | 11 - Reserved | -|-----------------|-------------|---------------------------------| -| Read Only | Bit 0 | 1 - read only | 0 - read/write | -\=================================================================/ - - (*) Should be ignored by tools for editing text values -(**) Allowed external reference formats: - - http://host/directory/filename.ext - - ftp://host/directory/filename.ext - - filename.ext - - /directory/filename.ext - - DRIVE:/directory/filename.ext - - Note: External references are also UTF-8 encoded. - -================================================================================ -= 4 - APE Tag Item Format -================================================================================ - -APE Tag Items are stored as key-value pairs. APE Tags Item Key are case -sensitive, however it is illegal to use keys which only differ in case and -it is recommended that tag reading not be case sensitive. - -Every key can only occur (at most) once. It is not possible to repeat a key -to signify updated contents. - -Tags can be partially or completely repeated in the streaming format. This -makes it possible to display an artist and / or title if it was missed at the -beginning of the stream. It is recommended that the important information like -artist, album and title should occur approximately every 2 minutes in the -stream and again 5 to 10 seconds before the end. However, care should be tak -en not to replicate this information too often or during passages with high -bitrate demands to avoid unnecessary drop-outs. - -/==============================================================================\ -| Content Size | 4 bytes | Length of the value in bytes | -|----------------|---------------|---------------------------------------------| -| Flags | 4 bytes | Item flags | -|----------------|---------------|---------------------------------------------| -| Key | 2 - 255 bytes | Item key | -|----------------|---------------|---------------------------------------------| -| Key Terminator | 1 byte | Null byte that indicates the end of the key | -|----------------|---------------|---------------------------------------------| -| Value | variable | Content (formatted according to the flags) | -\==============================================================================/ - -================================================================================ - -Sections 5 - 7 haven't yet been converted from: - -http://www.personal.uni-jena.de/~pfk/mpp/sv8/apetag.html diff --git a/3rdparty/taglib/ape/apefile.cpp b/3rdparty/taglib/ape/apefile.cpp deleted file mode 100644 index 2b0095fb..00000000 --- a/3rdparty/taglib/ape/apefile.cpp +++ /dev/null @@ -1,291 +0,0 @@ -/*************************************************************************** - copyright : (C) 2010 by Alex Novichkov - email : novichko@atnet.ru - - copyright : (C) 2006 by Lukáš Lalinský - email : lalinsky@gmail.com - (original WavPack implementation) - - copyright : (C) 2004 by Allan Sandfeld Jensen - email : kde@carewolf.org - (original MPC implementation) - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "tbytevector.h" -#include "tstring.h" -#include "tdebug.h" -#include "tagunion.h" -#include "id3v1tag.h" -#include "id3v2header.h" -#include "tpropertymap.h" -#include "tagutils.h" - -#include "apefile.h" -#include "apetag.h" -#include "apefooter.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { -enum { ApeAPEIndex = 0, - ApeID3v1Index = 1 }; -} - -class APE::File::FilePrivate { - public: - FilePrivate() : APELocation(-1), - APESize(0), - ID3v1Location(-1), - ID3v2Location(-1), - ID3v2Size(0) {} - - long long APELocation; - long long APESize; - - long long ID3v1Location; - - std::unique_ptr ID3v2Header; - long long ID3v2Location; - long long ID3v2Size; - - DoubleTagUnion tag; - - std::unique_ptr properties; -}; - -//////////////////////////////////////////////////////////////////////////////// -// static members -//////////////////////////////////////////////////////////////////////////////// - -bool APE::File::isSupported(IOStream *stream) { - // An APE file has an ID "MAC " somewhere. An ID3v2 tag may precede. - - const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true); - return (buffer.find("MAC ") == 0); - -} - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -APE::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::File(file), d(new FilePrivate()) { - - if (isOpen()) - read(readProperties); - -} - -APE::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::File(stream), d(new FilePrivate()) { - - if (isOpen()) - read(readProperties); - -} - -APE::File::~File() { - delete d; -} - -Strawberry_TagLib::TagLib::Tag *APE::File::tag() const { - return &d->tag; -} - -PropertyMap APE::File::setProperties(const PropertyMap &properties) { - - if (ID3v1Tag()) - ID3v1Tag()->setProperties(properties); - - return APETag(true)->setProperties(properties); - -} - -APE::AudioProperties *APE::File::audioProperties() const { - return d->properties.get(); -} - -bool APE::File::save() { - - if (readOnly()) { - debug("APE::File::save() -- File is read only."); - return false; - } - - // Update ID3v1 tag - - if (ID3v1Tag() && !ID3v1Tag()->isEmpty()) { - - // ID3v1 tag is not empty. Update the old one or create a new one. - - if (d->ID3v1Location >= 0) { - seek(d->ID3v1Location); - } - else { - seek(0, End); - d->ID3v1Location = tell(); - } - - writeBlock(ID3v1Tag()->render()); - } - else { - - // ID3v1 tag is empty. Remove the old one. - - if (d->ID3v1Location >= 0) { - truncate(d->ID3v1Location); - d->ID3v1Location = -1; - } - } - - // Update APE tag - - if (APETag() && !APETag()->isEmpty()) { - - // APE tag is not empty. Update the old one or create a new one. - - if (d->APELocation < 0) { - if (d->ID3v1Location >= 0) - d->APELocation = d->ID3v1Location; - else - d->APELocation = length(); - } - - const ByteVector data = APETag()->render(); - insert(data, d->APELocation, d->APESize); - - if (d->ID3v1Location >= 0) - d->ID3v1Location += (static_cast(data.size()) - d->APESize); - - d->APESize = data.size(); - } - else { - - // APE tag is empty. Remove the old one. - - if (d->APELocation >= 0) { - removeBlock(d->APELocation, d->APESize); - - if (d->ID3v1Location >= 0) - d->ID3v1Location -= d->APESize; - - d->APELocation = -1; - d->APESize = 0; - } - } - - return true; - -} - -ID3v1::Tag *APE::File::ID3v1Tag(bool create) { - return d->tag.access(ApeID3v1Index, create); -} - -APE::Tag *APE::File::APETag(bool create) { - return d->tag.access(ApeAPEIndex, create); -} - -void APE::File::strip(int tags) { - - if (tags & ID3v1) - d->tag.set(ApeID3v1Index, nullptr); - - if (tags & APE) - d->tag.set(ApeAPEIndex, nullptr); - - if (!ID3v1Tag()) - APETag(true); - -} - -bool APE::File::hasAPETag() const { - return (d->APELocation >= 0); -} - -bool APE::File::hasID3v1Tag() const { - return (d->ID3v1Location >= 0); -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void APE::File::read(bool readProperties) { - - // Look for an ID3v2 tag - - d->ID3v2Location = Utils::findID3v2(this); - - if (d->ID3v2Location >= 0) { - seek(d->ID3v2Location); - d->ID3v2Header.reset(new ID3v2::Header(readBlock(ID3v2::Header::size()))); - d->ID3v2Size = d->ID3v2Header->completeTagSize(); - } - - // Look for an ID3v1 tag - - d->ID3v1Location = Utils::findID3v1(this); - - if (d->ID3v1Location >= 0) - d->tag.set(ApeID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); - - // Look for an APE tag - - d->APELocation = Utils::findAPE(this, d->ID3v1Location); - - if (d->APELocation >= 0) { - d->tag.set(ApeAPEIndex, new APE::Tag(this, d->APELocation)); - d->APESize = APETag()->footer()->completeTagSize(); - d->APELocation = d->APELocation + APE::Footer::size() - d->APESize; - } - - if (d->ID3v1Location < 0) - APETag(true); - - // Look for APE audio properties - - if (readProperties) { - - long long streamLength; - - if (d->APELocation >= 0) - streamLength = d->APELocation; - else if (d->ID3v1Location >= 0) - streamLength = d->ID3v1Location; - else - streamLength = length(); - - if (d->ID3v2Location >= 0) { - seek(d->ID3v2Location + d->ID3v2Size); - streamLength -= (d->ID3v2Location + d->ID3v2Size); - } - else { - seek(0); - } - - d->properties.reset(new AudioProperties(this, streamLength)); - } - -} diff --git a/3rdparty/taglib/ape/apefile.h b/3rdparty/taglib/ape/apefile.h deleted file mode 100644 index 8064b314..00000000 --- a/3rdparty/taglib/ape/apefile.h +++ /dev/null @@ -1,214 +0,0 @@ -/*************************************************************************** - copyright : (C) 2010 by Alex Novichkov - email : novichko@atnet.ru - - copyright : (C) 2006 by Lukáš Lalinský - email : lalinsky@gmail.com - (original WavPack implementation) - - copyright : (C) 2004 by Allan Sandfeld Jensen - email : kde@carewolf.org - (original MPC implementation) - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_APEFILE_H -#define TAGLIB_APEFILE_H - -#include "tfile.h" -#include "taglib_export.h" -#include "apeproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -class Tag; - -namespace ID3v1 { -class Tag; -} -namespace APE { -class Tag; -} - -//! An implementation of APE metadata - -/*! - * This is implementation of APE metadata. - * - * This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream properties from the file. - * - */ - -namespace APE { - -//! An implementation of TagLib::File with APE specific methods - -/*! - * This implements and provides an interface for APE files to the - * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing - * the abstract TagLib::File API as well as providing some additional information specific to APE files. - * - */ - -class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File { - public: - /*! - * This set of flags is used for various operations and is suitable for being OR-ed together. - */ - enum TagTypes { - //! Empty set. Matches no tag types. - NoTags = 0x0000, - //! Matches ID3v1 tags. - ID3v1 = 0x0001, - //! Matches APE tags. - APE = 0x0002, - //! Matches all tag types. - AllTags = 0xffff - }; - - /*! - * Constructs an APE file from \a file. - * If \a readProperties is true the file's audio properties will also be read. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Constructs an APE file from \a stream. - * If \a readProperties is true the file's audio properties will also be read. - * - * \note TagLib will *not* take ownership of the stream, the caller is responsible for deleting it after the File object. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Destroys this instance of the File. - */ - ~File() override; - - /*! - * Returns the Tag for this file. This will be an APE tag, an ID3v1 tag or a combination of the two. - */ - Strawberry_TagLib::TagLib::Tag *tag() const override; - - /*! - * Implements the unified property interface -- import function. - * Creates an APEv2 tag if necessary. - * A potentially existing ID3v1 tag will be updated as well. - */ - PropertyMap setProperties(const PropertyMap&) override; - - /*! - * Returns the APE::AudioProperties for this file. - * If no audio properties were read then this will return a null pointer. - */ - AudioProperties *audioProperties() const override; - - /*! - * Saves the file. - * - * \note According to the official Monkey's Audio SDK, an APE file - * can only have either ID3V1 or APE tags, so a parameter is used here. - */ - bool save() override; - - /*! - * Returns a pointer to the ID3v1 tag of the file. - * - * If \a create is false (the default) this may return a null pointer if there is no valid ID3v1 tag. - * If \a create is true it will create an ID3v1 tag if one does not exist and returns a valid pointer. - * - * \note This may return a valid pointer regardless of whether or not the file on disk has an ID3v1 tag. - * Use hasID3v1Tag() to check if the file on disk actually has an ID3v1 tag. - * - * \note The Tag is still owned by the MPEG::File and should not be deleted by the user. - * It will be deleted when the file (object) is destroyed. - * - * \see hasID3v1Tag() - */ - ID3v1::Tag *ID3v1Tag(bool create = false); - - /*! - * Returns a pointer to the APE tag of the file. - * - * If \a create is false (the default) this may return a null pointer if there is no valid APE tag. - * If \a create is true it will create an APE tag if one does not exist and returns a valid pointer. - * - * \note This may return a valid pointer regardless of whether or not the file on disk has an APE tag. - * Use hasAPETag() to check if the file on disk actually has an APE tag. - * - * \note The Tag is still owned by the MPEG::File and should not be deleted by the user. - * It will be deleted when the file (object) is destroyed. - * - * \see hasAPETag() - */ - APE::Tag *APETag(bool create = false); - - /*! - * This will remove the tags that match the OR-ed together TagTypes from the file. - * By default it removes all tags. - * - * \note This will also invalidate pointers to the tags as their memory will be freed. - * \note In order to make the removal permanent save() still needs to be called - */ - void strip(int tags = AllTags); - - /*! - * Returns whether or not the file on disk actually has an APE tag. - * - * \see APETag() - */ - bool hasAPETag() const; - - /*! - * Returns whether or not the file on disk actually has an ID3v1 tag. - * - * \see ID3v1Tag() - */ - bool hasID3v1Tag() const; - - /*! - * Returns whether or not the given \a stream can be opened as an APE 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); - - class FilePrivate; - FilePrivate *d; -}; -} // namespace APE -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/ape/apefooter.cpp b/3rdparty/taglib/ape/apefooter.cpp deleted file mode 100644 index 50fccd53..00000000 --- a/3rdparty/taglib/ape/apefooter.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/*************************************************************************** - copyright : (C) 2004 by Allan Sandfeld Jensen - (C) 2002 - 2008 by Scott Wheeler (id3v2header.cpp) - email : kde@carewolf.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 -#include - -#include "tstring.h" -#include "tdebug.h" - -#include "apefooter.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace APE; - -class APE::Footer::FooterPrivate { - public: - FooterPrivate() : version(0), - footerPresent(true), - headerPresent(false), - isHeader(false), - itemCount(0), - tagSize(0) {} - - unsigned int version; - - bool footerPresent; - bool headerPresent; - - bool isHeader; - - unsigned int itemCount; - unsigned int tagSize; -}; - -//////////////////////////////////////////////////////////////////////////////// -// static members -//////////////////////////////////////////////////////////////////////////////// - -unsigned int APE::Footer::size() { - return 32; -} - -ByteVector APE::Footer::fileIdentifier() { - return ByteVector("APETAGEX"); -} - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -APE::Footer::Footer() : d(new FooterPrivate()) { -} - -APE::Footer::Footer(const ByteVector &data) : d(new FooterPrivate()) { - parse(data); -} - -APE::Footer::~Footer() { - delete d; -} - -unsigned int APE::Footer::version() const { - return d->version; -} - -bool APE::Footer::headerPresent() const { - return d->headerPresent; -} - -bool APE::Footer::footerPresent() const { - return d->footerPresent; -} - -bool APE::Footer::isHeader() const { - return d->isHeader; -} - -void APE::Footer::setHeaderPresent(bool b) const { - d->headerPresent = b; -} - -unsigned int APE::Footer::itemCount() const { - return d->itemCount; -} - -void APE::Footer::setItemCount(unsigned int s) { - d->itemCount = s; -} - -unsigned int APE::Footer::tagSize() const { - return d->tagSize; -} - -unsigned int APE::Footer::completeTagSize() const { - if (d->headerPresent) - return d->tagSize + size(); - else - return d->tagSize; -} - -void APE::Footer::setTagSize(unsigned int s) { - d->tagSize = s; -} - -void APE::Footer::setData(const ByteVector &data) { - parse(data); -} - -ByteVector APE::Footer::renderFooter() const { - return render(false); -} - -ByteVector APE::Footer::renderHeader() const { - - if (!d->headerPresent) - return ByteVector(); - else - return render(true); - -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void APE::Footer::parse(const ByteVector &data) { - - if (data.size() < size()) - return; - - // The first eight bytes, data[0..7], are the File Identifier, "APETAGEX". - - // Read the version number - - d->version = data.toUInt32LE(8); - - // Read the tag size - - d->tagSize = data.toUInt32LE(12); - - // Read the item count - - d->itemCount = data.toUInt32LE(16); - - // Read the flags - - std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt32LE(20))); - - d->headerPresent = flags[31]; - d->footerPresent = !flags[30]; - d->isHeader = flags[29]; - -} - -ByteVector APE::Footer::render(bool isHeader) const { - - ByteVector v; - - // add the file identifier -- "APETAGEX" - - v.append(fileIdentifier()); - - // add the version number -- we always render a 2.000 tag regardless of what - // the tag originally was. - - v.append(ByteVector::fromUInt32LE(2000)); - - // add the tag size - - v.append(ByteVector::fromUInt32LE(d->tagSize)); - - // add the item count - - v.append(ByteVector::fromUInt32LE(d->itemCount)); - - // render and add the flags - - std::bitset<32> flags; - - flags[31] = d->headerPresent; - flags[30] = false; // footer is always present - flags[29] = isHeader; - - v.append(ByteVector::fromUInt32LE(flags.to_ulong())); - - // add the reserved 64bit - - v.append(ByteVector::fromUInt64BE(0)); - - return v; - -} diff --git a/3rdparty/taglib/ape/apefooter.h b/3rdparty/taglib/ape/apefooter.h deleted file mode 100644 index 6e39900b..00000000 --- a/3rdparty/taglib/ape/apefooter.h +++ /dev/null @@ -1,175 +0,0 @@ -/*************************************************************************** - copyright : (C) 2004 by Allan Sandfeld Jensen - email : kde@carewolf.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_APEFOOTER_H -#define TAGLIB_APEFOOTER_H - -#include "tbytevector.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -namespace APE { - -//! An implementation of APE footers - -/*! - * This class implements APE footers (and headers). - * It attempts to follow, both semantically and programmatically, - * the structure specified in the APE v2.0 standard. - * The API is based on the properties of APE footer and headers specified there. - * - */ - -class TAGLIB_EXPORT Footer { - public: - /*! - * Constructs an empty APE footer. - */ - explicit Footer(); - - /*! - * Constructs an APE footer based on \a data. parse() is called immediately. - */ - explicit Footer(const ByteVector &data); - - /*! - * Destroys the footer. - */ - virtual ~Footer(); - - /*! - * Returns the version number. (Note: This is the 1000 or 2000.) - */ - unsigned int version() const; - - /*! - * Returns true if a header is present in the tag. - */ - bool headerPresent() const; - - /*! - * Returns true if a footer is present in the tag. - */ - bool footerPresent() const; - - /*! - * Returns true this is actually the header. - */ - bool isHeader() const; - - /*! - * Sets whether the header should be rendered or not - */ - void setHeaderPresent(bool b) const; - - /*! - * Returns the number of items in the tag. - */ - unsigned int itemCount() const; - - /*! - * Set the item count to \a s. - * \see itemCount() - */ - void setItemCount(unsigned int s); - - /*! - * Returns the tag size in bytes. - * This is the size of the frame content and footer. - * The size of the \e entire tag will be this plus the header size, if present. - * - * \see completeTagSize() - */ - unsigned int tagSize() const; - - /*! - * Returns the tag size, including if present, the header - * size. - * - * \see tagSize() - */ - unsigned int completeTagSize() const; - - /*! - * Set the tag size to \a s. - * \see tagSize() - */ - void setTagSize(unsigned int s); - - /*! - * Returns the size of the footer. Presently this is always 32 bytes. - */ - static unsigned int size(); - - /*! - * Returns the string used to identify an APE tag inside of a file. - * Presently this is always "APETAGEX". - */ - static ByteVector fileIdentifier(); - - /*! - * Sets the data that will be used as the footer. 32 bytes, - * starting from \a data will be used. - */ - void setData(const ByteVector &data); - - /*! - * Renders the footer back to binary format. - */ - ByteVector renderFooter() const; - - /*! - * Renders the header corresponding to the footer. - * If headerPresent is set to false, it returns an empty ByteVector. - */ - ByteVector renderHeader() const; - - protected: - /*! - * Called by setData() to parse the footer data. - * It makes this information available through the public API. - */ - void parse(const ByteVector &data); - - /*! - * Called by renderFooter and renderHeader - */ - ByteVector render(bool isHeader) const; - - private: - Footer(const Footer &); - Footer &operator=(const Footer &); - - class FooterPrivate; - FooterPrivate *d; -}; - -} // namespace APE -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/ape/apeitem.cpp b/3rdparty/taglib/ape/apeitem.cpp deleted file mode 100644 index 682c3ccf..00000000 --- a/3rdparty/taglib/ape/apeitem.cpp +++ /dev/null @@ -1,271 +0,0 @@ -/*************************************************************************** - copyright : (C) 2004 by Allan Sandfeld Jensen - email : kde@carewolf.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 - -#include "tbytevectorlist.h" -#include "tdebug.h" - -#include "apeitem.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace APE; - -struct ItemData { - ItemData() : type(Item::Text), readOnly(false) {} - - Item::ItemTypes type; - String key; - ByteVector value; - StringList text; - bool readOnly; -}; - -class APE::Item::ItemPrivate { - public: - ItemPrivate() : data(new ItemData()) {} - - std::shared_ptr data; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -APE::Item::Item() : d(new ItemPrivate()) {} - -APE::Item::Item(const String &key, const String &value) : d(new ItemPrivate()) { - d->data->key = key; - d->data->text.append(value); -} - -APE::Item::Item(const String &key, const StringList &values) : d(new ItemPrivate()) { - d->data->key = key; - d->data->text = values; -} - -APE::Item::Item(const String &key, const ByteVector &value, bool binary) : d(new ItemPrivate()) { - d->data->key = key; - if (binary) { - d->data->type = Binary; - d->data->value = value; - } - else { - d->data->text.append(value); - } - -} - -APE::Item::Item(const Item &item) : d(new ItemPrivate(*item.d)) {} - -APE::Item::~Item() { - delete d; -} - -Item &APE::Item::operator=(const Item &item) { - - Item(item).swap(*this); - return *this; - -} - -void APE::Item::swap(Item &item) { - - using std::swap; - - swap(d, item.d); - -} - -void APE::Item::setReadOnly(bool readOnly) { - d->data->readOnly = readOnly; -} - -bool APE::Item::isReadOnly() const { - return d->data->readOnly; -} - -void APE::Item::setType(APE::Item::ItemTypes val) { - d->data->type = val; -} - -APE::Item::ItemTypes APE::Item::type() const { - return d->data->type; -} - -String APE::Item::key() const { - return d->data->key; -} - -ByteVector APE::Item::binaryData() const { - return d->data->value; -} - -void APE::Item::setBinaryData(const ByteVector &value) { - d->data->type = Binary; - d->data->value = value; - d->data->text.clear(); -} - -void APE::Item::setKey(const String &key) { - d->data->key = key; -} - -void APE::Item::setValue(const String &value) { - d->data->type = Text; - d->data->text = value; - d->data->value.clear(); -} - -void APE::Item::setValues(const StringList &value) { - d->data->type = Text; - d->data->text = value; - d->data->value.clear(); -} - -void APE::Item::appendValue(const String &value) { - d->data->type = Text; - d->data->text.append(value); - d->data->value.clear(); -} - -void APE::Item::appendValues(const StringList &values) { - d->data->type = Text; - d->data->text.append(values); - d->data->value.clear(); -} - -int APE::Item::size() const { - size_t result = 8 + d->data->key.size() + 1; - switch (d->data->type) { - case Text: - if (!d->data->text.isEmpty()) { - StringList::ConstIterator it = d->data->text.begin(); - - result += it->data(String::UTF8).size(); - it++; - for (; it != d->data->text.end(); ++it) - result += 1 + it->data(String::UTF8).size(); - } - break; - - case Binary: - case Locator: - result += d->data->value.size(); - break; - } - return result; - -} - -StringList APE::Item::values() const { - return d->data->text; -} - -String APE::Item::toString() const { - if (d->data->type == Text && !isEmpty()) - return d->data->text.front(); - else - return String(); - -} - -bool APE::Item::isEmpty() const { - switch (d->data->type) { - case Text: - if (d->data->text.isEmpty()) - return true; - if (d->data->text.size() == 1 && d->data->text.front().isEmpty()) - return true; - return false; - case Binary: - case Locator: - return d->data->value.isEmpty(); - default: - return false; - } - -} - -void APE::Item::parse(const ByteVector &data) { - - // 11 bytes is the minimum size for an APE item - - if (data.size() < 11) { - debug("APE::Item::parse() -- no data in item"); - return; - } - - const unsigned int valueLength = data.toUInt32LE(0); - const unsigned int flags = data.toUInt32LE(4); - - // An item key can contain ASCII characters from 0x20 up to 0x7E, not UTF-8. - // We assume that the validity of the given key has been checked. - - d->data->key = String(&data[8], String::Latin1); - - const ByteVector value = data.mid(8 + d->data->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); - else - d->data->value = value; -} - -ByteVector APE::Item::render() const { - - ByteVector data; - unsigned int flags = ((d->data->readOnly) ? 1 : 0) | (d->data->type << 1); - ByteVector value; - - if (isEmpty()) - return data; - - if (d->data->type == Text) { - StringList::ConstIterator it = d->data->text.begin(); - - value.append(it->data(String::UTF8)); - it++; - for (; it != d->data->text.end(); ++it) { - value.append('\0'); - value.append(it->data(String::UTF8)); - } - d->data->value = value; - } - else - value.append(d->data->value); - - data.append(ByteVector::fromUInt32LE(value.size())); - data.append(ByteVector::fromUInt32LE(flags)); - data.append(d->data->key.data(String::Latin1)); - data.append(ByteVector('\0')); - data.append(value); - - return data; - -} diff --git a/3rdparty/taglib/ape/apeitem.h b/3rdparty/taglib/ape/apeitem.h deleted file mode 100644 index 75a5bf00..00000000 --- a/3rdparty/taglib/ape/apeitem.h +++ /dev/null @@ -1,207 +0,0 @@ -/*************************************************************************** - copyright : (C) 2004 by Allan Sandfeld Jensen - email : kde@carewolf.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_APEITEM_H -#define TAGLIB_APEITEM_H - -#include "tbytevector.h" -#include "tstring.h" -#include "tstringlist.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace APE { - -//! An implementation of APE-items - -/*! - * This class provides the features of items in the APEv2 standard. - */ -class TAGLIB_EXPORT Item { - public: - /*! - * Enum of types an Item can have. The value of 3 is reserved. - */ - enum ItemTypes { - //! Item contains text information coded in UTF-8 - Text = 0, - //! Item contains binary information - Binary = 1, - //! Item is a locator of external stored information - Locator = 2 - }; - /*! - * Constructs an empty item. - */ - explicit Item(); - - /*! - * Constructs a text item with \a key and \a values. - */ - explicit Item(const String &key, const String &values); - - /*! - * Constructs a text item with \a key and \a values. - */ - explicit Item(const String &key, const StringList &values); - - /*! - * Constructs an item with \a key and \a value. - * If \a binary is true a Binary item will be created, otherwise \a value will be interpreted as text - */ - explicit Item(const String &key, const ByteVector &value, bool binary); - - /*! - * Construct an item as a copy of \a item. - */ - Item(const Item &item); - - /*! - * Destroys the item. - */ - virtual ~Item(); - - /*! - * Copies the contents of \a item into this item. - */ - Item &operator=(const Item &item); - - /*! - * Exchanges the content of this item by the content of \a item. - */ - void swap(Item &item); - - /*! - * Returns the key. - */ - String key() const; - - /*! - * Returns the binary value. - * If the item type is not \a Binary, always returns an empty ByteVector. - */ - ByteVector binaryData() const; - - /*! - * Set the binary value to \a value - * The item's type will also be set to \a Binary - */ - void setBinaryData(const ByteVector &value); - - /*! - * Sets the key for the item to \a key. - */ - void setKey(const String &key); - - /*! - * Sets the text value of the item to \a value and clears any previous contents. - * - * \see toString() - */ - void setValue(const String &value); - - /*! - * Sets the text value of the item to the list of values in \a value and clears any previous contents. - * - * \see toStringList() - */ - void setValues(const StringList &values); - - /*! - * Appends \a value to create (or extend) the current list of text values. - * - * \see toString() - */ - void appendValue(const String &value); - - /*! - * Appends \a values to extend the current list of text values. - * - * \see toStringList() - */ - void appendValues(const StringList &values); - - /*! - * Returns the size of the full item. - */ - int size() const; - - /*! - * Returns the value as a single string. In case of multiple strings, the first is returned. - * If the data type is not \a Text, always returns an empty String. - */ - String toString() const; - - /*! - * Returns the list of text values. If the data type is not \a Text, always returns an empty StringList. - */ - StringList values() const; - - /*! - * Render the item to a ByteVector. - */ - ByteVector render() const; - - /*! - * Parse the item from the ByteVector \a data. - */ - void parse(const ByteVector &data); - - /*! - * Set the item to read-only. - */ - void setReadOnly(bool readOnly); - - /*! - * Return true if the item is read-only. - */ - bool isReadOnly() const; - - /*! - * Sets the type of the item to \a type. - * - * \see ItemTypes - */ - void setType(ItemTypes type); - - /*! - * Returns the type of the item. - */ - ItemTypes type() const; - - /*! - * Returns if the item has any real content. - */ - bool isEmpty() const; - - private: - class ItemPrivate; - ItemPrivate *d; -}; -} // namespace APE -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/ape/apeproperties.cpp b/3rdparty/taglib/ape/apeproperties.cpp deleted file mode 100644 index 3ab702e8..00000000 --- a/3rdparty/taglib/ape/apeproperties.cpp +++ /dev/null @@ -1,227 +0,0 @@ -/*************************************************************************** - copyright : (C) 2010 by Alex Novichkov - email : novichko@atnet.ru - - copyright : (C) 2006 by Lukáš Lalinský - email : lalinsky@gmail.com - (original WavPack implementation) - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tstring.h" -#include "tdebug.h" - -#include "id3v2tag.h" -#include "apeproperties.h" -#include "apefile.h" -#include "apetag.h" -#include "apefooter.h" - -using namespace Strawberry_TagLib::TagLib; - -class APE::AudioProperties::AudioPropertiesPrivate { - public: - explicit AudioPropertiesPrivate() : length(0), - bitrate(0), - sampleRate(0), - channels(0), - version(0), - bitsPerSample(0), - sampleFrames(0) {} - - int length; - int bitrate; - int sampleRate; - int channels; - int version; - int bitsPerSample; - unsigned int sampleFrames; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -APE::AudioProperties::AudioProperties(File *file, long long streamLength, ReadStyle) : Strawberry_TagLib::TagLib::AudioProperties(), d(new AudioPropertiesPrivate()) { - read(file, streamLength); -} - -APE::AudioProperties::~AudioProperties() { - delete d; -} - -int APE::AudioProperties::lengthInSeconds() const { - return d->length / 1000; -} - -int APE::AudioProperties::lengthInMilliseconds() const { - return d->length; -} - -int APE::AudioProperties::bitrate() const { - return d->bitrate; -} - -int APE::AudioProperties::sampleRate() const { - return d->sampleRate; -} - -int APE::AudioProperties::channels() const { - return d->channels; -} - -int APE::AudioProperties::version() const { - return d->version; -} - -int APE::AudioProperties::bitsPerSample() const { - return d->bitsPerSample; -} - -unsigned int APE::AudioProperties::sampleFrames() const { - return d->sampleFrames; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -namespace { -int headerVersion(const ByteVector &header) { - if (header.size() < 6 || !header.startsWith("MAC ")) - return -1; - - return header.toUInt16LE(4); -} -} // namespace - -void APE::AudioProperties::read(File *file, long long streamLength) { - - // First, we assume that the file pointer is set at the first descriptor. - long long offset = file->tell(); - int version = headerVersion(file->readBlock(6)); - - // Next, we look for the descriptor. - if (version < 0) { - offset = file->find("MAC ", offset); - file->seek(offset); - version = headerVersion(file->readBlock(6)); - } - - if (version < 0) { - debug("APE::AudioProperties::read() -- APE descriptor not found"); - return; - } - - d->version = version; - - if (d->version >= 3980) - analyzeCurrent(file); - else - analyzeOld(file); - - if (d->sampleFrames > 0 && d->sampleRate > 0) { - const double length = d->sampleFrames * 1000.0 / d->sampleRate; - d->length = static_cast(length + 0.5); - d->bitrate = static_cast(streamLength * 8.0 / length + 0.5); - } - -} - -void APE::AudioProperties::analyzeCurrent(File *file) { - - // Read the descriptor - file->seek(2, File::Current); - const ByteVector descriptor = file->readBlock(44); - if (descriptor.size() < 44) { - debug("APE::AudioProperties::analyzeCurrent() -- descriptor is too short."); - return; - } - - const unsigned int descriptorBytes = descriptor.toUInt32LE(0); - - if ((descriptorBytes - 52) > 0) - file->seek(descriptorBytes - 52, File::Current); - - // Read the header - const ByteVector header = file->readBlock(24); - if (header.size() < 24) { - debug("APE::AudioProperties::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); - - const unsigned int totalFrames = header.toUInt32LE(12); - if (totalFrames == 0) - return; - - const unsigned int blocksPerFrame = header.toUInt32LE(4); - const unsigned int finalFrameBlocks = header.toUInt32LE(8); - d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks; - -} - -void APE::AudioProperties::analyzeOld(File *file) { - - const ByteVector header = file->readBlock(26); - if (header.size() < 26) { - debug("APE::AudioProperties::analyzeOld() -- MAC header is too short."); - return; - } - - const unsigned int totalFrames = header.toUInt32LE(18); - - // Fail on 0 length APE files (catches non-finalized APE files) - if (totalFrames == 0) - return; - - const short compressionLevel = header.toUInt32LE(0); - unsigned int blocksPerFrame; - if (d->version >= 3950) - blocksPerFrame = 73728 * 4; - else if (d->version >= 3900 || (d->version >= 3800 && compressionLevel == 4000)) - blocksPerFrame = 73728; - else - blocksPerFrame = 9216; - - // Get the APE info - d->channels = header.toUInt16LE(4); - d->sampleRate = header.toUInt32LE(6); - - const unsigned int finalFrameBlocks = header.toUInt32LE(22); - d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks; - - // Get the bit depth from the RIFF-fmt chunk. - file->seek(16, File::Current); - const ByteVector fmt = file->readBlock(28); - if (fmt.size() < 28 || !fmt.startsWith("WAVEfmt ")) { - debug("APE::AudioProperties::analyzeOld() -- fmt header is too short."); - return; - } - - d->bitsPerSample = fmt.toUInt16LE(26); - -} diff --git a/3rdparty/taglib/ape/apeproperties.h b/3rdparty/taglib/ape/apeproperties.h deleted file mode 100644 index 02f2a77e..00000000 --- a/3rdparty/taglib/ape/apeproperties.h +++ /dev/null @@ -1,118 +0,0 @@ -/*************************************************************************** - copyright : (C) 2010 by Alex Novichkov - email : novichko@atnet.ru - - copyright : (C) 2006 by Lukáš Lalinský - email : lalinsky@gmail.com - (original WavPack implementation) - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_APEPROPERTIES_H -#define TAGLIB_APEPROPERTIES_H - -#include "taglib_export.h" -#include "audioproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace APE { - -class File; - -//! An implementation of audio property reading for APE - -/*! - * This reads the data from an APE stream found in the AudioProperties API. - */ - -class TAGLIB_EXPORT AudioProperties : public Strawberry_TagLib::TagLib::AudioProperties { - public: - - /*! - * Create an instance of APE::AudioProperties with the data read from the APE::File \a file. - */ - explicit AudioProperties(File *file, long long streamLength, ReadStyle style = Average); - - /*! - * Destroys this APE::AudioProperties instance. - */ - ~AudioProperties() override; - - /*! - * Returns the length of the file in seconds. The length is rounded down to the nearest whole second. - * - * \see lengthInMilliseconds() - */ - int lengthInSeconds() const override; - - /*! - * Returns the length of the file in milliseconds. - * - * \see lengthInSeconds() - */ - int lengthInMilliseconds() const override; - - /*! - * Returns the average bit rate of the file in kb/s. - */ - int bitrate() const override; - - /*! - * Returns the sample rate in Hz. - */ - int sampleRate() const override; - - /*! - * Returns the number of audio channels. - */ - int channels() const override; - - /*! - * Returns the number of bits per audio sample. - */ - int bitsPerSample() const; - - /*! - * Returns the total number of audio samples in file. - */ - unsigned int sampleFrames() const; - - /*! - * Returns APE version. - */ - int version() const; - - private: - void read(File *file, long long streamLength); - - void analyzeCurrent(File *file); - void analyzeOld(File *file); - - class AudioPropertiesPrivate; - AudioPropertiesPrivate *d; -}; -} // namespace APE -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/ape/apetag.cpp b/3rdparty/taglib/ape/apetag.cpp deleted file mode 100644 index 0b43a0b6..00000000 --- a/3rdparty/taglib/ape/apetag.cpp +++ /dev/null @@ -1,519 +0,0 @@ -/*************************************************************************** - copyright : (C) 2004 by Allan Sandfeld Jensen - email : kde@carewolf.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/ * - ***************************************************************************/ - -#if defined(__SUNPRO_CC) && (__SUNPRO_CC < 0x5130) -// Sun Studio finds multiple specializations of Map because -// it considers specializations with and without class types -// to be different; this define forces Map to use only the -// specialization with the class keyword. -# define WANT_CLASS_INSTANTIATION_OF_MAP (1) -#endif - -#include "tfile.h" -#include "tstring.h" -#include "tmap.h" -#include "tpicturemap.h" -#include "tpropertymap.h" -#include "tdebug.h" -#include "tutils.h" - -#include "apetag.h" -#include "apefooter.h" -#include "apeitem.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace APE; - -namespace { -const unsigned int MinKeyLength = 2; -const unsigned int MaxKeyLength = 255; - -bool isKeyValid(const ByteVector &key) { - - const char *invalidKeys[] = { "ID3", "TAG", "OGGS", "MP+", nullptr }; - - // only allow printable ASCII including space (32..126) - - for (ByteVector::ConstIterator it = key.begin(); it != key.end(); ++it) { - const int c = static_cast(*it); - if (c < 32 || c > 126) - return false; - } - - const String upperKey = String(key).upper(); - for (size_t i = 0; invalidKeys[i] != nullptr; ++i) { - if (upperKey == invalidKeys[i]) - return false; - } - - return true; -} -} // namespace - -class APE::Tag::TagPrivate { - public: - TagPrivate() : file(nullptr), footerLocation(0) {} - - File *file; - long long footerLocation; - - Footer footer; - ItemListMap itemListMap; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public methods -//////////////////////////////////////////////////////////////////////////////// - -APE::Tag::Tag() : d(new TagPrivate()) {} - -APE::Tag::Tag(Strawberry_TagLib::TagLib::File *file, long long footerLocation) : d(new TagPrivate()) { - - d->file = file; - d->footerLocation = footerLocation; - - read(); -} - -APE::Tag::~Tag() { - delete d; -} - -ByteVector APE::Tag::fileIdentifier() { - return ByteVector::fromCString("APETAGEX"); -} - -String APE::Tag::title() const { - - if (d->itemListMap["TITLE"].isEmpty()) - return String(); - return d->itemListMap["TITLE"].values().toString(); - -} - -String APE::Tag::artist() const { - - if (d->itemListMap["ARTIST"].isEmpty()) - return String(); - return d->itemListMap["ARTIST"].values().toString(); - -} - -String APE::Tag::album() const { - - if (d->itemListMap["ALBUM"].isEmpty()) - return String(); - return d->itemListMap["ALBUM"].values().toString(); - -} - -String APE::Tag::comment() const { - - if (d->itemListMap["COMMENT"].isEmpty()) - return String(); - return d->itemListMap["COMMENT"].values().toString(); - -} - -String APE::Tag::genre() const { - - if (d->itemListMap["GENRE"].isEmpty()) - return String(); - return d->itemListMap["GENRE"].values().toString(); - -} - -unsigned int APE::Tag::year() const { - - if (d->itemListMap["YEAR"].isEmpty()) - return 0; - return d->itemListMap["YEAR"].toString().toInt(); - -} - -unsigned int APE::Tag::track() const { - - if (d->itemListMap["TRACK"].isEmpty()) - return 0; - return d->itemListMap["TRACK"].toString().toInt(); - -} - -Strawberry_TagLib::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); -} - -void APE::Tag::setArtist(const String &s) { - addValue("ARTIST", s, true); -} - -void APE::Tag::setAlbum(const String &s) { - addValue("ALBUM", s, true); -} - -void APE::Tag::setComment(const String &s) { - addValue("COMMENT", s, true); -} - -void APE::Tag::setGenre(const String &s) { - addValue("GENRE", s, true); -} - -void APE::Tag::setYear(unsigned int i) { - - if (i == 0) - removeItem("YEAR"); - else - addValue("YEAR", String::number(i), true); - -} - -void APE::Tag::setTrack(unsigned int i) { - - if (i == 0) - removeItem("TRACK"); - else - addValue("TRACK", String::number(i), true); - -} - -void APE::Tag::setPictures(const PictureMap &l) { - - removeItem(FRONT_COVER); - removeItem(BACK_COVER); - - for (PictureMap::ConstIterator pictureMapIt = l.begin(); pictureMapIt != l.end(); ++pictureMapIt) { - Picture::Type type = pictureMapIt->first; - if (Picture::FrontCover != type && Picture::BackCover != type) { - std::cout << "APE: Trying to add a picture with wrong type" << std::endl; - continue; - } - - const char *id; - switch (type) { - case Picture::FrontCover: - id = FRONT_COVER; - break; - case Picture::BackCover: - id = BACK_COVER; - break; - default: - id = FRONT_COVER; - break; - } - - PictureList list = pictureMapIt->second; - for (PictureList::ConstIterator pictureListIt = list.begin(); pictureListIt != list.end(); ++pictureListIt) { - Picture picture = *pictureListIt; - if (d->itemListMap.contains(id)) { - std::cout << "APE: Already added a picture of type " - << id - << " '" - << picture.description() - << "' " - << "and next are being ignored" - << std::endl; - break; - } - - ByteVector data = picture.description().data(String::Latin1).append('\0').append(picture.data()); - - Item item; - item.setKey(id); - item.setType(Item::Binary); - item.setBinaryData(data); - setItem(item.key(), item); - } - } - -} - -namespace { -// conversions of tag keys between what we use in PropertyMap and what's usual -// for APE tags -// usual, APE -const char *keyConversions[][2] = { { "TRACKNUMBER", "TRACK" }, - { "DATE", "YEAR" }, - { "ALBUMARTIST", "ALBUM ARTIST" }, - { "DISCNUMBER", "DISC" }, - { "REMIXER", "MIXARTIST" } }; -const size_t keyConversionsSize = sizeof(keyConversions) / sizeof(keyConversions[0]); -} // namespace - -PropertyMap APE::Tag::properties() const { - - PropertyMap properties; - ItemListMap::ConstIterator it = itemListMap().begin(); - for (; it != itemListMap().end(); ++it) { - String tagName = it->first.upper(); - // if the item is Binary or Locator, or if the key is an invalid string, - // add to unsupportedData - if (it->second.type() != Item::Text || tagName.isEmpty()) { - properties.unsupportedData().append(it->first); - } - else { - // Some tags need to be handled specially - for (size_t i = 0; i < keyConversionsSize; ++i) { - if (tagName == keyConversions[i][1]) - tagName = keyConversions[i][0]; - } - properties[tagName].append(it->second.values()); - } - } - return properties; - -} - -void APE::Tag::removeUnsupportedProperties(const StringList &properties) { - - StringList::ConstIterator it = properties.begin(); - for (; it != properties.end(); ++it) - removeItem(*it); - -} - -PropertyMap APE::Tag::setProperties(const PropertyMap &origProps) { - - PropertyMap properties(origProps); // make a local copy that can be modified - - // see comment in properties() - for (size_t i = 0; i < keyConversionsSize; ++i) - if (properties.contains(keyConversions[i][0])) { - properties.insert(keyConversions[i][1], properties[keyConversions[i][0]]); - properties.erase(keyConversions[i][0]); - } - - // first check if tags need to be removed completely - StringList toRemove; - ItemListMap::ConstIterator remIt = itemListMap().begin(); - for (; remIt != itemListMap().end(); ++remIt) { - String key = remIt->first.upper(); - // only remove if a) key is valid, b) type is text, c) key not contained in new properties - if (!key.isEmpty() && remIt->second.type() == APE::Item::Text && !properties.contains(key)) - toRemove.append(remIt->first); - } - - for (StringList::ConstIterator removeIt = toRemove.begin(); removeIt != toRemove.end(); removeIt++) - removeItem(*removeIt); - - // now sync in the "forward direction" - PropertyMap::ConstIterator it = properties.begin(); - PropertyMap invalid; - for (; it != properties.end(); ++it) { - const String &tagName = it->first; - if (!checkKey(tagName)) - invalid.insert(it->first, it->second); - else if (!(itemListMap().contains(tagName)) || !(itemListMap()[tagName].values() == it->second)) { - if (it->second.isEmpty()) - removeItem(tagName); - else { - StringList::ConstIterator valueIt = it->second.begin(); - addValue(tagName, *valueIt, true); - ++valueIt; - for (; valueIt != it->second.end(); ++valueIt) - addValue(tagName, *valueIt, false); - } - } - } - return invalid; -} - -bool APE::Tag::checkKey(const String &key) { - - if (key.size() < MinKeyLength || key.size() > MaxKeyLength) - return false; - - return isKeyValid(key.data(String::UTF8)); - -} - -APE::Footer *APE::Tag::footer() const { - return &d->footer; -} - -const APE::ItemListMap &APE::Tag::itemListMap() const { - return d->itemListMap; -} - -void APE::Tag::removeItem(const String &key) { - d->itemListMap.erase(key.upper()); -} - -void APE::Tag::addValue(const String &key, const String &value, bool replace) { - - if (replace) - removeItem(key); - - if (value.isEmpty()) - return; - - // Text items may contain more than one value. - // Binary or locator items may have only one value, hence always replaced. - - ItemListMap::Iterator it = d->itemListMap.find(key.upper()); - - if (it != d->itemListMap.end() && it->second.type() == Item::Text) - it->second.appendValue(value); - else - setItem(key, Item(key, value)); - -} - -void APE::Tag::setData(const String &key, const ByteVector &value) { - - removeItem(key); - - if (value.isEmpty()) - return; - - setItem(key, Item(key, value, true)); - -} - -void APE::Tag::setItem(const String &key, const Item &item) { - - if (!checkKey(key)) { - debug("APE::Tag::setItem() - Couldn't set an item due to an invalid key."); - return; - } - - d->itemListMap[key.upper()] = item; - -} - -bool APE::Tag::isEmpty() const { - return d->itemListMap.isEmpty(); -} - -//////////////////////////////////////////////////////////////////////////////// -// protected methods -//////////////////////////////////////////////////////////////////////////////// - -void APE::Tag::read() { - - if (d->file && d->file->isValid()) { - - d->file->seek(d->footerLocation); - d->footer.setData(d->file->readBlock(Footer::size())); - - if (d->footer.tagSize() <= Footer::size() || - d->footer.tagSize() > static_cast(d->file->length())) - return; - - d->file->seek(d->footerLocation + Footer::size() - d->footer.tagSize()); - parse(d->file->readBlock(d->footer.tagSize() - Footer::size())); - } - -} - -ByteVector APE::Tag::render() const { - - ByteVector data; - unsigned int itemCount = 0; - - for (ItemListMap::ConstIterator it = d->itemListMap.begin(); it != d->itemListMap.end(); ++it) { - data.append(it->second.render()); - itemCount++; - } - - d->footer.setItemCount(itemCount); - d->footer.setTagSize(data.size() + Footer::size()); - d->footer.setHeaderPresent(true); - - return d->footer.renderHeader() + data + d->footer.renderFooter(); - -} - -void APE::Tag::parse(const ByteVector &data) { - - // 11 bytes is the minimum size for an APE item - - if (data.size() < 11) - return; - - size_t 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()) { - 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); - - if (keyLength >= MinKeyLength && keyLength <= MaxKeyLength && isKeyValid(data.mid(pos + 8, keyLength))) { - APE::Item item; - item.parse(data.mid(pos)); - - d->itemListMap.insert(item.key().upper(), item); - } - else { - debug("APE::Tag::parse() - Skipped an item due to an invalid key."); - } - - pos += keyLength + valLegnth + 9; - } - -} diff --git a/3rdparty/taglib/ape/apetag.h b/3rdparty/taglib/ape/apetag.h deleted file mode 100644 index 713e04e2..00000000 --- a/3rdparty/taglib/ape/apetag.h +++ /dev/null @@ -1,216 +0,0 @@ -/*************************************************************************** - copyright : (C) 2004 by Allan Sandfeld Jensen - email : kde@carewolf.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_APETAG_H -#define TAGLIB_APETAG_H - -#include "tag.h" -#include "tbytevector.h" -#include "tmap.h" -#include "tstring.h" -#include "taglib_export.h" - -#include "apeitem.h" - -#define FRONT_COVER "COVER ART (FRONT)" -#define BACK_COVER "COVER ART (BACK)" - -namespace Strawberry_TagLib { -namespace TagLib { - -class File; - -//! An implementation of the APE tagging format - -namespace APE { - -class Footer; - -/*! - * A mapping between a list of item names, or keys, and the associated item. - * - * \see APE::Tag::itemListMap() - */ -typedef Map ItemListMap; - - -//! An APE tag implementation - -class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag { - public: - /*! - * Create an APE tag with default values. - */ - explicit Tag(); - - /*! - * Create an APE tag and parse the data in \a file with APE footer at - * \a tagOffset. - */ - explicit Tag(Strawberry_TagLib::TagLib::File *file, long long footerLocation); - - /*! - * Destroys this Tag instance. - */ - ~Tag() override; - - /*! - * Renders the in memory values to a ByteVector suitable for writing to the file. - */ - ByteVector render() const; - - /*! - * Returns the string "APETAGEX" suitable for usage in locating the tag in a file. - */ - static ByteVector fileIdentifier(); - - // Reimplementations. - - String title() const override; - String artist() const override; - String album() const override; - String comment() const override; - String genre() const override; - unsigned int year() const override; - unsigned int track() const override; - - /** - * @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 - */ - PictureMap pictures() const override; - - void setTitle(const String &s) override; - void setArtist(const String &s) override; - void setAlbum(const String &s) override; - void setComment(const String &s) override; - void setGenre(const String &s) override; - void setYear(unsigned int i) override; - void setTrack(unsigned int i) override; - void setPictures(const PictureMap &l) override; - - /*! - * Implements the unified tag dictionary interface -- export function. - * APE tags are perfectly compatible with the dictionary interface because they - * support both arbitrary tag names and multiple values. - * Currently only APE items of type *Text* are handled by the dictionary interface; all *Binary* - * and *Locator* items will be put into the unsupportedData list and can be - * deleted on request using removeUnsupportedProperties(). - * The same happens to Text items if their key is invalid for PropertyMap (which should actually never happen). - * - * The only conversion done by this export function is to rename the APE tags - * TRACK to TRACKNUMBER, YEAR to DATE, and ALBUM ARTIST to ALBUMARTIST, - * respectively, in order to be compliant with the names used in other formats. - */ - PropertyMap properties() const override; - - void removeUnsupportedProperties(const StringList &properties) override; - - /*! - * Implements the unified tag dictionary interface -- import function. - * The same comments as for the export function apply; additionally note that the APE tag - * specification requires keys to have between 2 and 16 printable ASCII characters - * with the exception of the fixed strings "ID3", "TAG", "OGGS", and "MP+". - */ - PropertyMap setProperties(const PropertyMap &) override; - - /*! - * Check if the given String is a valid APE tag key. - */ - static bool checkKey(const String &); - - /*! - * Returns a pointer to the tag's footer. - */ - Footer *footer() const; - - /*! - * Returns a reference to the item list map. - * This is an ItemListMap of all of the items in the tag. - * - * This is the most powerful structure for accessing the items of the tag. - * - * APE tags are case-insensitive, all keys in this map have been converted - * to upper case. - * - * \warning You should not modify this data structure directly, instead - * use setItem() and removeItem(). - */ - const ItemListMap &itemListMap() const; - - /*! - * Removes the \a key item from the tag - */ - void removeItem(const String &key); - - /*! - * Adds to the text item specified by \a key the data \a value. - * If \a replace is true, then all of the other values on the same key will be removed first. - * If a binary item exists for \a key it will be removed first. - */ - void addValue(const String &key, const String &value, bool replace = true); - - /*! - * Set the binary data for the key specified by \a item to \a value - * This will convert the item to type \a Binary if it isn't already and all of the other values on the same key will be removed. - */ - void setData(const String &key, const ByteVector &value); - - /*! - * Sets the \a key item to the value of \a item. If an item with the \a key is already present, it will be replaced. - */ - void setItem(const String &key, const Item &item); - - /*! - * Returns true if the tag does not contain any data. - */ - bool isEmpty() const override; - - protected: - /*! - * Reads from the file specified in the constructor. - */ - void read(); - - /*! - * Parses the body of the tag in \a data. - */ - void parse(const ByteVector &data); - - private: - Tag(const Tag &); - Tag &operator=(const Tag &); - - class TagPrivate; - TagPrivate *d; -}; -} // namespace APE -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/asf/asfattribute.cpp b/3rdparty/taglib/asf/asfattribute.cpp deleted file mode 100644 index 36b77b76..00000000 --- a/3rdparty/taglib/asf/asfattribute.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/************************************************************************** - copyright : (C) 2005-2007 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "taglib.h" -#include "tdebug.h" - -#include "asfattribute.h" -#include "asffile.h" -#include "asfutils.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { -struct AttributeData { - explicit 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; -}; -} // namespace - -class ASF::Attribute::AttributePrivate { - public: - AttributePrivate() : data(new AttributeData()) { - data->pictureValue = ASF::Picture::fromInvalid(); - } - - std::shared_ptr data; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -ASF::Attribute::Attribute() : d(new AttributePrivate()) { - d->data->type = UnicodeType; -} - -ASF::Attribute::Attribute(const ASF::Attribute &other) : d(new AttributePrivate(*other.d)) {} - -ASF::Attribute::Attribute(const String &value) : d(new AttributePrivate()) { - d->data->type = UnicodeType; - d->data->stringValue = value; -} - -ASF::Attribute::Attribute(const ByteVector &value) : d(new AttributePrivate()) { - d->data->type = BytesType; - d->data->byteVectorValue = value; -} - -ASF::Attribute::Attribute(const ASF::Picture &value) : d(new AttributePrivate()) { - d->data->type = BytesType; - d->data->pictureValue = value; -} - -ASF::Attribute::Attribute(unsigned int value) : d(new AttributePrivate()) { - d->data->type = DWordType; - d->data->numericValue = value; -} - -ASF::Attribute::Attribute(unsigned long long value) : d(new AttributePrivate()) { - d->data->type = QWordType; - d->data->numericValue = value; -} - -ASF::Attribute::Attribute(unsigned short value) : d(new AttributePrivate()) { - d->data->type = WordType; - d->data->numericValue = value; -} - -ASF::Attribute::Attribute(bool value) : d(new AttributePrivate()) { - d->data->type = BoolType; - d->data->numericValue = value; -} - -ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &other) { - Attribute(other).swap(*this); - return *this; -} - -void ASF::Attribute::swap(Attribute &other) { - using std::swap; - - swap(d, other.d); -} - -ASF::Attribute::~Attribute() { - delete d; -} - -ASF::Attribute::AttributeTypes ASF::Attribute::type() const { - return d->data->type; -} - -String ASF::Attribute::toString() const { - return d->data->stringValue; -} - -ByteVector ASF::Attribute::toByteVector() const { - if (d->data->pictureValue.isValid()) - return d->data->pictureValue.render(); - - return d->data->byteVectorValue; -} - -unsigned short ASF::Attribute::toBool() const { - return d->data->numericValue ? 1 : 0; -} - -unsigned short ASF::Attribute::toUShort() const { - return static_cast(d->data->numericValue); -} - -unsigned int ASF::Attribute::toUInt() const { - return static_cast(d->data->numericValue); -} - -unsigned long long ASF::Attribute::toULongLong() const { - return static_cast(d->data->numericValue); -} - -ASF::Picture ASF::Attribute::toPicture() const { - return d->data->pictureValue; -} - -String ASF::Attribute::parse(ASF::File &f, int kind) { - - unsigned int size, nameLength; - String name; - d->data->pictureValue = Picture::fromInvalid(); - // extended content descriptor - if (kind == 0) { - nameLength = readWORD(&f); - name = readString(&f, nameLength); - d->data->type = ASF::Attribute::AttributeTypes(readWORD(&f)); - size = readWORD(&f); - } - // metadata & metadata library - else { - int temp = readWORD(&f); - // metadata library - if (kind == 2) { - d->data->language = temp; - } - d->data->stream = readWORD(&f); - nameLength = readWORD(&f); - d->data->type = ASF::Attribute::AttributeTypes(readWORD(&f)); - size = readDWORD(&f); - name = readString(&f, nameLength); - } - - if (kind != 2 && size > 65535) { - debug("ASF::Attribute::parse() -- Value larger than 64kB"); - } - - switch (d->data->type) { - case WordType: - d->data->numericValue = readWORD(&f); - break; - - case BoolType: - if (kind == 0) { - d->data->numericValue = (readDWORD(&f) != 0); - } - else { - d->data->numericValue = (readWORD(&f) != 0); - } - break; - - case DWordType: - d->data->numericValue = readDWORD(&f); - break; - - case QWordType: - d->data->numericValue = readQWORD(&f); - break; - - case UnicodeType: - d->data->stringValue = readString(&f, size); - break; - - case BytesType: - case GuidType: - d->data->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(); - } - } - - return name; - -} - -int ASF::Attribute::dataSize() const { - - switch (d->data->type) { - case WordType: - return 2; - case BoolType: - return 4; - case DWordType: - return 4; - case QWordType: - return 5; - case UnicodeType: - return static_cast(d->data->stringValue.size() * 2 + 2); - case BytesType: - if (d->data->pictureValue.isValid()) - return d->data->pictureValue.dataSize(); - break; - case GuidType: - return static_cast(d->data->byteVectorValue.size()); - } - return 0; - -} - -ByteVector ASF::Attribute::render(const String &name, int kind) const { - - ByteVector data; - - switch (d->data->type) { - case WordType: - data.append(ByteVector::fromUInt16LE(toUShort())); - break; - - case BoolType: - if (kind == 0) { - data.append(ByteVector::fromUInt32LE(toBool())); - } - else { - data.append(ByteVector::fromUInt16LE(toBool())); - } - break; - - case DWordType: - data.append(ByteVector::fromUInt32LE(toUInt())); - break; - - case QWordType: - data.append(ByteVector::fromUInt64LE(toULongLong())); - break; - - case UnicodeType: - data.append(renderString(d->data->stringValue)); - break; - - case BytesType: - if (d->data->pictureValue.isValid()) { - data.append(d->data->pictureValue.render()); - break; - } - break; - case GuidType: - data.append(d->data->byteVectorValue); - break; - } - - if (kind == 0) { - data = renderString(name, true) + - ByteVector::fromUInt16LE((int)d->data->type) + - ByteVector::fromUInt16LE(data.size()) + - 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(static_cast(d->data->type)) + - ByteVector::fromUInt32LE(data.size()) + - nameData + - data; - } - - return data; - -} - -int ASF::Attribute::language() const { - return d->data->language; -} - -void ASF::Attribute::setLanguage(int value) { - d->data->language = value; -} - -int ASF::Attribute::stream() const { - return d->data->stream; -} - -void ASF::Attribute::setStream(int value) { - d->data->stream = value; -} diff --git a/3rdparty/taglib/asf/asfattribute.h b/3rdparty/taglib/asf/asfattribute.h deleted file mode 100644 index 2da2f590..00000000 --- a/3rdparty/taglib/asf/asfattribute.h +++ /dev/null @@ -1,200 +0,0 @@ -/************************************************************************** - copyright : (C) 2005-2007 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_ASFATTRIBUTE_H -#define TAGLIB_ASFATTRIBUTE_H - -#include "tstring.h" -#include "tbytevector.h" -#include "taglib_export.h" -#include "asfpicture.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ASF { - -class File; -class Picture; - -class TAGLIB_EXPORT Attribute { - public: - /*! - * Enum of types an Attribute can have. - */ - enum AttributeTypes { - UnicodeType = 0, - BytesType = 1, - BoolType = 2, - DWordType = 3, - QWordType = 4, - WordType = 5, - GuidType = 6 - }; - - /*! - * Constructs an empty attribute. - */ - Attribute(); - - /*! - * Constructs an attribute with \a key and a UnicodeType \a value. - */ - Attribute(const String &value); - - /*! - * Constructs an attribute with \a key and a BytesType \a value. - */ - Attribute(const ByteVector &value); - - /*! - * Constructs an attribute with \a key and a Picture \a value. - * - * This attribute is compatible with the ID3 frame, APIC. The ID3 specification for the APIC frame stipulates that, - * while there may be any number of APIC frames associated with a file, - * only one may be of type 1 and only one may be of type 2. - * - * The specification also states that the description of the picture can be no longer than 64 characters, but can be empty. - * WM/Picture attributes added with TagLib::ASF are not automatically validated to conform to ID3 specifications. - * You must add code in your application to perform validations if you want to maintain complete compatibility with ID3. - */ - Attribute(const Picture &value); - - /*! - * Constructs an attribute with \a key and a DWordType \a value. - */ - Attribute(unsigned int value); - - /*! - * Constructs an attribute with \a key and a QWordType \a value. - */ - Attribute(unsigned long long value); - - /*! - * Constructs an attribute with \a key and a WordType \a value. - */ - Attribute(unsigned short value); - - /*! - * Constructs an attribute with \a key and a BoolType \a value. - */ - Attribute(bool value); - - /*! - * Construct an attribute as a copy of \a other. - */ - Attribute(const Attribute &other); - - /*! - * Copies the contents of \a other into this item. - */ - Attribute &operator=(const Attribute &other); - - /*! - * Exchanges the content of the Attribute by the content of \a other. - */ - void swap(Attribute &other); - - /*! - * Destroys the attribute. - */ - virtual ~Attribute(); - - /*! - * Returns type of the value. - */ - AttributeTypes type() const; - - /*! - * Returns the BoolType \a value. - */ - unsigned short toBool() const; - - /*! - * Returns the WordType \a value. - */ - unsigned short toUShort() const; - - /*! - * Returns the DWordType \a value. - */ - unsigned int toUInt() const; - - /*! - * Returns the QWordType \a value. - */ - unsigned long long toULongLong() const; - - /*! - * Returns the UnicodeType \a value. - */ - String toString() const; - - /*! - * Returns the BytesType \a value. - */ - ByteVector toByteVector() const; - - /*! - * Returns the Picture \a value. - */ - Picture toPicture() const; - - /*! - * Returns the language number, or 0 is no stream number was set. - */ - int language() const; - - /*! - * Sets the language number. - */ - void setLanguage(int value); - - /*! - * Returns the stream number, or 0 is no stream number was set. - */ - int stream() const; - - /*! - * Sets the stream number. - */ - void setStream(int value); - - //! 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; - AttributePrivate *d; -}; -} // namespace ASF -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/asf/asffile.cpp b/3rdparty/taglib/asf/asffile.cpp deleted file mode 100644 index 68751078..00000000 --- a/3rdparty/taglib/asf/asffile.cpp +++ /dev/null @@ -1,661 +0,0 @@ -/************************************************************************** - copyright : (C) 2005-2007 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "tdebug.h" -#include "tbytevectorlist.h" -#include "tpropertymap.h" -#include "tstring.h" -#include "tagutils.h" - -#include "asffile.h" -#include "asftag.h" -#include "asfproperties.h" -#include "asfutils.h" - -using namespace Strawberry_TagLib::TagLib; - -class ASF::File::FilePrivate { - public: - class BaseObject; - class UnknownObject; - class FilePropertiesObject; - class StreamPropertiesObject; - class ContentDescriptionObject; - class ExtendedContentDescriptionObject; - class HeaderExtensionObject; - class CodecListObject; - class MetadataObject; - class MetadataLibraryObject; - - typedef List> ObjectList; - typedef ObjectList::ConstIterator ObjectConstIterator; - - FilePrivate() : headerSize(0) {} - - unsigned long long headerSize; - - std::unique_ptr tag; - std::unique_ptr properties; - - ObjectList objects; - - std::shared_ptr contentDescriptionObject; - std::shared_ptr extendedContentDescriptionObject; - std::shared_ptr headerExtensionObject; - std::shared_ptr metadataObject; - std::shared_ptr metadataLibraryObject; -}; - -namespace { -const ByteVector headerGuid("\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16); -const ByteVector filePropertiesGuid("\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65", 16); -const ByteVector streamPropertiesGuid("\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65", 16); -const ByteVector contentDescriptionGuid("\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16); -const ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50", 16); -const ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16); -const ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16); -const ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16); -const ByteVector codecListGuid("\x40\x52\xd1\x86\x1d\x31\xd0\x11\xa3\xa4\x00\xa0\xc9\x03\x48\xf6", 16); -const ByteVector contentEncryptionGuid("\xFB\xB3\x11\x22\x23\xBD\xD2\x11\xB4\xB7\x00\xA0\xC9\x55\xFC\x6E", 16); -const ByteVector extendedContentEncryptionGuid("\x14\xE6\x8A\x29\x22\x26 \x17\x4C\xB9\x35\xDA\xE0\x7E\xE9\x28\x9C", 16); -const ByteVector advancedContentEncryptionGuid("\xB6\x9B\x07\x7A\xA4\xDA\x12\x4E\xA5\xCA\x91\xD3\x8D\xC1\x1A\x8D", 16); -} // namespace - -class ASF::File::FilePrivate::BaseObject { - public: - ByteVector data; - virtual ~BaseObject() {} - virtual ByteVector guid() const = 0; - virtual void parse(ASF::File *file, unsigned int size); - virtual ByteVector render(ASF::File *file); -}; - -class ASF::File::FilePrivate::UnknownObject : public ASF::File::FilePrivate::BaseObject { - ByteVector myGuid; - - public: - explicit UnknownObject(const ByteVector &guid); - ByteVector guid() const override; -}; - -class ASF::File::FilePrivate::FilePropertiesObject : public ASF::File::FilePrivate::BaseObject { - public: - ByteVector guid() const override; - void parse(ASF::File *file, unsigned int size) override; -}; - -class ASF::File::FilePrivate::StreamPropertiesObject : public ASF::File::FilePrivate::BaseObject { - public: - ByteVector guid() const override; - void parse(ASF::File *file, unsigned int size) override; -}; - -class ASF::File::FilePrivate::ContentDescriptionObject : public ASF::File::FilePrivate::BaseObject { - public: - ByteVector guid() const override; - void parse(ASF::File *file, unsigned int size) override; - ByteVector render(ASF::File *file) override; -}; - -class ASF::File::FilePrivate::ExtendedContentDescriptionObject : public ASF::File::FilePrivate::BaseObject { - public: - ByteVectorList attributeData; - ByteVector guid() const override; - void parse(ASF::File *file, unsigned int size) override; - ByteVector render(ASF::File *file) override; -}; - -class ASF::File::FilePrivate::MetadataObject : public ASF::File::FilePrivate::BaseObject { - public: - ByteVectorList attributeData; - ByteVector guid() const override; - void parse(ASF::File *file, unsigned int size) override; - ByteVector render(ASF::File *file) override; -}; - -class ASF::File::FilePrivate::MetadataLibraryObject : public ASF::File::FilePrivate::BaseObject { - public: - ByteVectorList attributeData; - ByteVector guid() const override; - void parse(ASF::File *file, unsigned int size) override; - ByteVector render(ASF::File *file) override; -}; - -class ASF::File::FilePrivate::HeaderExtensionObject : public ASF::File::FilePrivate::BaseObject { - public: - ObjectList objects; - HeaderExtensionObject(); - ByteVector guid() const override; - void parse(ASF::File *file, unsigned int size) override; - ByteVector render(ASF::File *file) override; -}; - -class ASF::File::FilePrivate::CodecListObject : public ASF::File::FilePrivate::BaseObject { - public: - ByteVector guid() const override; - void parse(ASF::File *file, unsigned int size) override; - - private: - enum CodecType { - Video = 0x0001, - Audio = 0x0002, - Unknown = 0xFFFF - }; -}; - -void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, unsigned int size) { - - data.clear(); - if (size > 24 && static_cast(size) <= file->length()) - data = file->readBlock(size - 24); - else - data = ByteVector(); - -} - -ByteVector ASF::File::FilePrivate::BaseObject::render(ASF::File * /*file*/) { - return guid() + ByteVector::fromUInt64LE(data.size() + 24) + data; -} - -ASF::File::FilePrivate::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid) {} - -ByteVector ASF::File::FilePrivate::UnknownObject::guid() const { - return myGuid; -} - -ByteVector ASF::File::FilePrivate::FilePropertiesObject::guid() const { - return filePropertiesGuid; -} - -void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, unsigned int size) { - - BaseObject::parse(file, size); - if (data.size() < 64) { - debug("ASF::File::FilePrivate::FilePropertiesObject::parse() -- data is too short."); - return; - } - - const long long duration = data.toInt64LE(40); - const long long preroll = data.toInt64LE(56); - file->d->properties->setLengthInMilliseconds(static_cast(duration / 10000.0 - preroll + 0.5)); - -} - -ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const { - return streamPropertiesGuid; -} - -void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, unsigned int size) { - - BaseObject::parse(file, size); - if (data.size() < 70) { - debug("ASF::File::FilePrivate::StreamPropertiesObject::parse() -- data is too short."); - 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(data.toUInt32LE(62) * 8.0 / 1000.0 + 0.5)); - file->d->properties->setBitsPerSample(data.toUInt16LE(68)); - -} - -ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const { - return contentDescriptionGuid; -} - -void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/) { - - const int titleLength = readWORD(file); - const int artistLength = readWORD(file); - const int copyrightLength = readWORD(file); - const int commentLength = readWORD(file); - const int ratingLength = readWORD(file); - file->d->tag->setTitle(readString(file, titleLength)); - file->d->tag->setArtist(readString(file, artistLength)); - file->d->tag->setCopyright(readString(file, copyrightLength)); - file->d->tag->setComment(readString(file, commentLength)); - file->d->tag->setRating(readString(file, ratingLength)); - -} - -ByteVector ASF::File::FilePrivate::ContentDescriptionObject::render(ASF::File *file) { - - const ByteVector v1 = renderString(file->d->tag->title()); - const ByteVector v2 = renderString(file->d->tag->artist()); - const ByteVector v3 = renderString(file->d->tag->copyright()); - 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(v1); - data.append(v2); - data.append(v3); - data.append(v4); - data.append(v5); - return BaseObject::render(file); - -} - -ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::guid() const { - return extendedContentDescriptionGuid; -} - -void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/) { - - int count = readWORD(file); - while (count--) { - ASF::Attribute attribute; - String name = attribute.parse(*file); - file->d->tag->addAttribute(name, attribute); - } - -} - -ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::render(ASF::File *file) { - - data.clear(); - data.append(ByteVector::fromUInt16LE(attributeData.size())); - data.append(attributeData.toByteVector("")); - return BaseObject::render(file); - -} - -ByteVector ASF::File::FilePrivate::MetadataObject::guid() const { - return metadataGuid; -} - -void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, unsigned int /*size*/) { - - int count = readWORD(file); - while (count--) { - ASF::Attribute attribute; - String name = attribute.parse(*file, 1); - file->d->tag->addAttribute(name, attribute); - } - -} - -ByteVector ASF::File::FilePrivate::MetadataObject::render(ASF::File *file) { - - data.clear(); - data.append(ByteVector::fromUInt16LE(attributeData.size())); - data.append(attributeData.toByteVector("")); - return BaseObject::render(file); - -} - -ByteVector ASF::File::FilePrivate::MetadataLibraryObject::guid() const { - return metadataLibraryGuid; -} - -void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, unsigned int /*size*/) { - - int count = readWORD(file); - while (count--) { - ASF::Attribute attribute; - String name = attribute.parse(*file, 2); - file->d->tag->addAttribute(name, attribute); - } - -} - -ByteVector ASF::File::FilePrivate::MetadataLibraryObject::render(ASF::File *file) { - - data.clear(); - data.append(ByteVector::fromUInt16LE(attributeData.size())); - data.append(attributeData.toByteVector("")); - return BaseObject::render(file); - -} - -ASF::File::FilePrivate::HeaderExtensionObject::HeaderExtensionObject() { - objects.setAutoDelete(true); -} - -ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const { - return headerExtensionGuid; -} - -void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsigned int /*size*/) { - - file->seek(18, File::Current); - long long dataSize = readDWORD(file); - long long dataPos = 0; - while (dataPos < dataSize) { - ByteVector guid = file->readBlock(16); - if (guid.size() != 16) { - file->setValid(false); - break; - } - bool ok; - long long size = readQWORD(file, &ok); - if (!ok) { - file->setValid(false); - break; - } - std::shared_ptr obj; - if (guid == metadataGuid) { - file->d->metadataObject.reset(new MetadataObject()); - obj = file->d->metadataObject; - } - else if (guid == metadataLibraryGuid) { - file->d->metadataLibraryObject.reset(new MetadataLibraryObject()); - obj = file->d->metadataLibraryObject; - } - else { - obj.reset(new UnknownObject(guid)); - } - obj->parse(file, static_cast(size)); - objects.append(obj); - dataPos += size; - } - -} - -ByteVector ASF::File::FilePrivate::HeaderExtensionObject::render(ASF::File *file) { - - data.clear(); - for (ObjectConstIterator it = objects.begin(); it != objects.end(); ++it) { - data.append((*it)->render(file)); - } - data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt32LE(data.size()) + data; - return BaseObject::render(file); - -} - -ByteVector ASF::File::FilePrivate::CodecListObject::guid() const { - return codecListGuid; -} - -void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned int size) { - - BaseObject::parse(file, size); - if (data.size() <= 20) { - debug("ASF::File::FilePrivate::CodecListObject::parse() -- data is too short."); - return; - } - - unsigned int pos = 16; - - const int count = data.toUInt32LE(pos); - pos += 4; - - for (int i = 0; i < count; ++i) { - - if (pos >= data.size()) - break; - - const CodecType type = static_cast(data.toUInt16LE(pos)); - pos += 2; - - int nameLength = data.toUInt16LE(pos); - pos += 2; - - const unsigned int namePos = pos; - pos += nameLength * 2; - - const int descLength = data.toUInt16LE(pos); - pos += 2; - - const unsigned int descPos = pos; - pos += descLength * 2; - - const int infoLength = data.toUInt16LE(pos); - pos += 2 + infoLength * 2; - - if (type == CodecListObject::Audio) { - // First audio codec found. - - const String name(data.mid(namePos, nameLength * 2), String::UTF16LE); - file->d->properties->setCodecName(name.stripWhiteSpace()); - - const String desc(data.mid(descPos, descLength * 2), String::UTF16LE); - file->d->properties->setCodecDescription(desc.stripWhiteSpace()); - - break; - } - } - -} - -//////////////////////////////////////////////////////////////////////////////// -// static members -//////////////////////////////////////////////////////////////////////////////// - -bool ASF::File::isSupported(IOStream *stream) { - - // An ASF file has to start with the designated GUID. - - const ByteVector id = Utils::readHeader(stream, 16, false); - return (id == headerGuid); - -} - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -ASF::File::File(FileName file, bool, AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::File(file), d(new FilePrivate()) { - if (isOpen()) - read(); -} - -ASF::File::File(IOStream *stream, bool, AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::File(stream), d(new FilePrivate()) { - if (isOpen()) - read(); -} - -ASF::File::~File() { - delete d; -} - -ASF::Tag *ASF::File::tag() const { - return d->tag.get(); -} - -ASF::AudioProperties *ASF::File::audioProperties() const { - return d->properties.get(); -} - -bool ASF::File::save() { - - if (readOnly()) { - debug("ASF::File::save() -- File is read only."); - return false; - } - - if (!isValid()) { - debug("ASF::File::save() -- Trying to save invalid file."); - return false; - } - - if (!d->contentDescriptionObject) { - d->contentDescriptionObject.reset(new FilePrivate::ContentDescriptionObject()); - d->objects.append(d->contentDescriptionObject); - } - if (!d->extendedContentDescriptionObject) { - d->extendedContentDescriptionObject.reset(new FilePrivate::ExtendedContentDescriptionObject()); - d->objects.append(d->extendedContentDescriptionObject); - } - if (!d->headerExtensionObject) { - d->headerExtensionObject.reset(new FilePrivate::HeaderExtensionObject()); - d->objects.append(d->headerExtensionObject); - } - if (!d->metadataObject) { - d->metadataObject.reset(new FilePrivate::MetadataObject()); - d->headerExtensionObject->objects.append(d->metadataObject); - } - if (!d->metadataLibraryObject) { - d->metadataLibraryObject.reset(new FilePrivate::MetadataLibraryObject()); - d->headerExtensionObject->objects.append(d->metadataLibraryObject); - } - - d->extendedContentDescriptionObject->attributeData.clear(); - d->metadataObject->attributeData.clear(); - d->metadataLibraryObject->attributeData.clear(); - - const AttributeListMap allAttributes = d->tag->attributeListMap(); - - for (AttributeListMap::ConstIterator it = allAttributes.begin(); it != allAttributes.end(); ++it) { - - const String &name = it->first; - const AttributeList &attributes = it->second; - - bool inExtendedContentDescriptionObject = false; - bool inMetadataObject = false; - - for (AttributeList::ConstIterator jt = attributes.begin(); jt != attributes.end(); ++jt) { - - const Attribute &attribute = *jt; - const bool largeValue = (attribute.dataSize() > 65535); - const bool guid = (attribute.type() == Attribute::GuidType); - - if (!inExtendedContentDescriptionObject && !guid && !largeValue && attribute.language() == 0 && attribute.stream() == 0) { - d->extendedContentDescriptionObject->attributeData.append(attribute.render(name)); - inExtendedContentDescriptionObject = true; - } - else if (!inMetadataObject && !guid && !largeValue && attribute.language() == 0 && attribute.stream() != 0) { - d->metadataObject->attributeData.append(attribute.render(name, 1)); - inMetadataObject = true; - } - else { - d->metadataLibraryObject->attributeData.append(attribute.render(name, 2)); - } - } - } - - ByteVector data; - for (FilePrivate::ObjectConstIterator 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("\x01\x02", 2)); - - insert(data, 30, static_cast(d->headerSize - 30)); - - d->headerSize = data.size() + 30; - - return true; - -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void ASF::File::read() { - - if (!isValid()) - return; - - if (readBlock(16) != headerGuid) { - debug("ASF::File::read(): Not an ASF file."); - setValid(false); - return; - } - - d->tag.reset(new ASF::Tag()); - d->properties.reset(new ASF::AudioProperties()); - - bool ok; - d->headerSize = readQWORD(this, &ok); - if (!ok) { - setValid(false); - return; - } - int numObjects = readDWORD(this, &ok); - if (!ok) { - setValid(false); - return; - } - seek(2, Current); - - std::shared_ptr filePropertiesObject; - std::shared_ptr streamPropertiesObject; - for (int i = 0; i < numObjects; i++) { - const ByteVector guid = readBlock(16); - if (guid.size() != 16) { - setValid(false); - break; - } - long size = static_cast(readQWORD(this, &ok)); - if (!ok) { - setValid(false); - break; - } - std::shared_ptr obj; - if (guid == filePropertiesGuid) { - filePropertiesObject.reset(new FilePrivate::FilePropertiesObject()); - obj = filePropertiesObject; - } - else if (guid == streamPropertiesGuid) { - streamPropertiesObject.reset(new FilePrivate::StreamPropertiesObject()); - obj = streamPropertiesObject; - } - else if (guid == contentDescriptionGuid) { - d->contentDescriptionObject.reset(new FilePrivate::ContentDescriptionObject()); - obj = d->contentDescriptionObject; - } - else if (guid == extendedContentDescriptionGuid) { - d->extendedContentDescriptionObject.reset(new FilePrivate::ExtendedContentDescriptionObject()); - obj = d->extendedContentDescriptionObject; - } - else if (guid == headerExtensionGuid) { - d->headerExtensionObject.reset(new FilePrivate::HeaderExtensionObject()); - obj = d->headerExtensionObject; - } - else if (guid == codecListGuid) { - obj.reset(new FilePrivate::CodecListObject()); - } - else { - if (guid == contentEncryptionGuid || - guid == extendedContentEncryptionGuid || - guid == advancedContentEncryptionGuid) { - d->properties->setEncrypted(true); - } - obj.reset(new FilePrivate::UnknownObject(guid)); - } - obj->parse(this, size); - d->objects.append(obj); - } - - if (!filePropertiesObject || !streamPropertiesObject) { - debug("ASF::File::read(): Missing mandatory header objects."); - setValid(false); - return; - } - -} diff --git a/3rdparty/taglib/asf/asffile.h b/3rdparty/taglib/asf/asffile.h deleted file mode 100644 index 0c41924f..00000000 --- a/3rdparty/taglib/asf/asffile.h +++ /dev/null @@ -1,118 +0,0 @@ -/************************************************************************** - copyright : (C) 2005-2007 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_ASFFILE_H -#define TAGLIB_ASFFILE_H - -#include "tag.h" -#include "tfile.h" -#include "taglib_export.h" -#include "asfproperties.h" -#include "asftag.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -//! An implementation of ASF (WMA) metadata -namespace ASF { - -/*! - * This implements and provides an interface for ASF files to the - * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing - * the abstract TagLib::File API as well as providing some additional - * information specific to ASF files. - */ -class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File { - public: - /*! - * 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. - */ - explicit File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * 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 - * read. - * - * \note TagLib will *not* take ownership of the stream, the caller is - * responsible for deleting it after the File object. - */ - explicit File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Destroys this instance of the File. - */ - ~File() override; - - /*! - * Returns a pointer to the ASF tag of the file. - * - * ASF::Tag implements the tag interface, so this serves as the - * reimplementation of TagLib::File::tag(). - * - * \note The Tag is still owned by the ASF::File and should not be - * deleted by the user. It will be deleted when the file (object) is - * destroyed. - */ - Tag *tag() const override; - - /*! - * Returns the ASF audio properties for this file. - */ - AudioProperties *audioProperties() const override; - - /*! - * Save the file. - * - * This returns true if the save was successful. - */ - bool save() override; - - /*! - * Returns whether or not the given \a stream can be opened as an ASF file. - * - * \note This method is designed to do a quick check. The result may not necessarily be correct. - */ - static bool isSupported(IOStream *stream); - - private: - void read(); - - class FilePrivate; - FilePrivate *d; -}; - -} // namespace ASF - -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/asf/asfpicture.cpp b/3rdparty/taglib/asf/asfpicture.cpp deleted file mode 100644 index 2dfa31c9..00000000 --- a/3rdparty/taglib/asf/asfpicture.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/************************************************************************** - copyright : (C) 2010 by Anton Sergunov - email : setosha@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "taglib.h" -#include "tdebug.h" - -#include "asfattribute.h" -#include "asffile.h" -#include "asfpicture.h" -#include "asfutils.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { -struct PictureData { - bool valid; - ASF::Picture::Type type; - String mimeType; - String description; - ByteVector picture; -}; -} // namespace - -class ASF::Picture::PicturePrivate { - public: - explicit PicturePrivate() : data(new PictureData()) {} - - std::shared_ptr data; -}; - -//////////////////////////////////////////////////////////////////////////////// -// Picture class members -//////////////////////////////////////////////////////////////////////////////// - -ASF::Picture::Picture() : d(new PicturePrivate()) { - d->data->valid = true; -} - -ASF::Picture::Picture(const Picture& other) : d(new PicturePrivate(*other.d)) {} - -ASF::Picture::~Picture() { - delete d; -} - -bool ASF::Picture::isValid() const { - return d->data->valid; -} - -String ASF::Picture::mimeType() const { - return d->data->mimeType; -} - -void ASF::Picture::setMimeType(const String& value) { - d->data->mimeType = value; -} - -ASF::Picture::Type ASF::Picture::type() const { - return d->data->type; -} - -void ASF::Picture::setType(const ASF::Picture::Type &t) { - d->data->type = t; -} - -String ASF::Picture::description() const { - return d->data->description; -} - -void ASF::Picture::setDescription(const String& desc) { - d->data->description = desc; -} - -ByteVector ASF::Picture::picture() const { - return d->data->picture; -} - -void ASF::Picture::setPicture(const ByteVector &p) { - d->data->picture = p; -} - -int ASF::Picture::dataSize() const { - return 9 + (d->data->mimeType.length() + d->data->description.length()) * 2 + d->data->picture.size(); -} - -ASF::Picture& ASF::Picture::operator=(const ASF::Picture& other) { - Picture(other).swap(*this); - return *this; -} - -void ASF::Picture::swap(Picture& other) { - using std::swap; - - swap(d, other.d); -} - -ByteVector ASF::Picture::render() const { - - if (!isValid()) - return ByteVector(); - - return ByteVector(static_cast(d->data->type)) + ByteVector::fromUInt32LE(d->data->picture.size()) + renderString(d->data->mimeType) + renderString(d->data->description) + d->data->picture; - -} - -void ASF::Picture::parse(const ByteVector &bytes) { - - d->data->valid = false; - if (bytes.size() < 9) - return; - size_t pos = 0; - d->data->type = static_cast(bytes[0]); - ++pos; - const unsigned int dataLen = bytes.toUInt32LE(pos); - pos += 4; - - const ByteVector nullStringTerminator(2, 0); - - size_t endPos = bytes.find(nullStringTerminator, pos, 2); - if (endPos == ByteVector::npos()) - return; - d->data->mimeType = String(bytes.mid(pos, endPos - pos), String::UTF16LE); - pos = endPos + 2; - - endPos = bytes.find(nullStringTerminator, pos, 2); - if (endPos == ByteVector::npos()) - return; - d->data->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; - -} - -ASF::Picture ASF::Picture::fromInvalid() { - - Picture ret; - ret.d->data->valid = false; - return ret; - -} diff --git a/3rdparty/taglib/asf/asfpicture.h b/3rdparty/taglib/asf/asfpicture.h deleted file mode 100644 index c22ae179..00000000 --- a/3rdparty/taglib/asf/asfpicture.h +++ /dev/null @@ -1,218 +0,0 @@ -/************************************************************************** - copyright : (C) 2010 by Anton Sergunov - email : setosha@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef ASFPICTURE_H -#define ASFPICTURE_H - -#include "tstring.h" -#include "tbytevector.h" -#include "taglib_export.h" -#include "attachedpictureframe.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ASF { -class Attribute; - -//! An ASF attached picture interface implementation - -/*! - * This is an implementation of ASF attached pictures interface. - * Pictures may be included in attributes, one per WM/Picture attribute (but there may be multiple WM/Picture attribute in a single tag). - * These pictures are usually in either JPEG or PNG format. - * \see Attribute::toPicture() - * \see Attribute::Attribute(const Picture& picture) - */ -class TAGLIB_EXPORT Picture { - public: - /*! - * This describes the function or content of the picture. - */ - enum Type { - //! A type not enumerated below - Other = 0x00, - //! 32x32 PNG image that should be used as the file icon - FileIcon = 0x01, - //! File icon of a different size or format - OtherFileIcon = 0x02, - //! Front cover image of the album - FrontCover = 0x03, - //! Back cover image of the album - BackCover = 0x04, - //! Inside leaflet page of the album - LeafletPage = 0x05, - //! Image from the album itself - Media = 0x06, - //! Picture of the lead artist or soloist - LeadArtist = 0x07, - //! Picture of the artist or performer - Artist = 0x08, - //! Picture of the conductor - Conductor = 0x09, - //! Picture of the band or orchestra - Band = 0x0A, - //! Picture of the composer - Composer = 0x0B, - //! Picture of the lyricist or text writer - Lyricist = 0x0C, - //! Picture of the recording location or studio - RecordingLocation = 0x0D, - //! Picture of the artists during recording - DuringRecording = 0x0E, - //! Picture of the artists during performance - DuringPerformance = 0x0F, - //! Picture from a movie or video related to the track - MovieScreenCapture = 0x10, - //! Picture of a large, coloured fish - ColouredFish = 0x11, - //! Illustration related to the track - Illustration = 0x12, - //! Logo of the band or performer - BandLogo = 0x13, - //! Logo of the publisher (record company) - PublisherLogo = 0x14 - }; - - /*! - * Constructs an empty picture. - */ - explicit Picture(); - - /*! - * Construct an picture as a copy of \a other. - */ - Picture(const Picture& other); - - /*! - * Destroys the picture. - */ - virtual ~Picture(); - - /*! - * Copies the contents of \a other into this picture. - */ - Picture& operator=(const Picture& other); - - /*! - * Exchanges the content of the Picture by the content of \a other. - */ - void swap(Picture& other); - - /*! - * Returns true if Picture stores valid picture - */ - bool isValid() const; - - /*! - * Returns the mime type of the image. This should in most cases be "image/png" or "image/jpeg". - * \see setMimeType(const String &) - * \see picture() - * \see setPicture(const ByteArray&) - */ - String mimeType() const; - - /*! - * Sets the mime type of the image. This should in most cases be "image/png" or "image/jpeg". - * \see setMimeType(const String &) - * \see picture() - * \see setPicture(const ByteArray&) - */ - void setMimeType(const String& value); - - /*! - * Returns the type of the image. - * - * \see Type - * \see setType() - */ - Type type() const; - - /*! - * Sets the type for the image. - * - * \see Type - * \see type() - */ - void setType(const ASF::Picture::Type& t); - - /*! - * Returns a text description of the image. - * - * \see setDescription() - */ - String description() const; - - /*! - * Sets a textual description of the image to \a desc. - * - * \see description() - */ - void setDescription(const String& desc); - - /*! - * Returns the image data as a ByteVector. - * - * \note ByteVector has a data() method that returns a const char * which should make it easy to export this data to external programs. - * - * \see setPicture() - * \see mimeType() - */ - ByteVector picture() const; - - /*! - * Sets the image data to \a p. - * \a p should be of the type specified in this frame's mime-type specification. - * - * \see picture() - * \see mimeType() - * \see setMimeType() - */ - void setPicture(const ByteVector& p); - - /*! - * Returns picture as binary raw data \a value - */ - ByteVector render() const; - - /*! - * Returns picture as binary raw data \a value - */ - int dataSize() const; - - private: - friend class Attribute; - - void parse(const ByteVector&); - static Picture fromInvalid(); - - private: - class PicturePrivate; - PicturePrivate* d; -}; -} // namespace ASF -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif // ASFPICTURE_H diff --git a/3rdparty/taglib/asf/asfproperties.cpp b/3rdparty/taglib/asf/asfproperties.cpp deleted file mode 100644 index a91b46ad..00000000 --- a/3rdparty/taglib/asf/asfproperties.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/************************************************************************** - copyright : (C) 2005-2007 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tdebug.h" -#include "tstring.h" -#include "asfproperties.h" - -using namespace Strawberry_TagLib::TagLib; - -class ASF::AudioProperties::AudioPropertiesPrivate { - public: - explicit AudioPropertiesPrivate() : length(0), - bitrate(0), - sampleRate(0), - channels(0), - bitsPerSample(0), - codec(ASF::AudioProperties::Unknown), - encrypted(false) {} - - int length; - int bitrate; - int sampleRate; - int channels; - int bitsPerSample; - ASF::AudioProperties::Codec codec; - String codecName; - String codecDescription; - bool encrypted; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -ASF::AudioProperties::AudioProperties() : Strawberry_TagLib::TagLib::AudioProperties(), d(new AudioPropertiesPrivate()) { -} - -ASF::AudioProperties::~AudioProperties() { - delete d; -} - -int ASF::AudioProperties::lengthInSeconds() const { - return d->length / 1000; -} - -int ASF::AudioProperties::lengthInMilliseconds() const { - return d->length; -} - -int ASF::AudioProperties::bitrate() const { - return d->bitrate; -} - -int ASF::AudioProperties::sampleRate() const { - return d->sampleRate; -} - -int ASF::AudioProperties::channels() const { - return d->channels; -} - -int ASF::AudioProperties::bitsPerSample() const { - return d->bitsPerSample; -} - -ASF::AudioProperties::Codec ASF::AudioProperties::codec() const { - return d->codec; -} - -String ASF::AudioProperties::codecName() const { - return d->codecName; -} - -String ASF::AudioProperties::codecDescription() const { - return d->codecDescription; -} - -bool ASF::AudioProperties::isEncrypted() const { - return d->encrypted; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void ASF::AudioProperties::setLengthInMilliseconds(int value) { - d->length = value; -} - -void ASF::AudioProperties::setBitrate(int value) { - d->bitrate = value; -} - -void ASF::AudioProperties::setSampleRate(int value) { - d->sampleRate = value; -} - -void ASF::AudioProperties::setChannels(int value) { - d->channels = value; -} - -void ASF::AudioProperties::setBitsPerSample(int value) { - d->bitsPerSample = value; -} - -void ASF::AudioProperties::setCodec(int value) { - - switch (value) { - case 0x0160: - d->codec = WMA1; - break; - case 0x0161: - d->codec = WMA2; - break; - case 0x0162: - d->codec = WMA9Pro; - break; - case 0x0163: - d->codec = WMA9Lossless; - break; - default: - d->codec = Unknown; - break; - } - -} - -void ASF::AudioProperties::setCodecName(const String &value) { - d->codecName = value; -} - -void ASF::AudioProperties::setCodecDescription(const String &value) { - d->codecDescription = value; -} - -void ASF::AudioProperties::setEncrypted(bool value) { - d->encrypted = value; -} diff --git a/3rdparty/taglib/asf/asfproperties.h b/3rdparty/taglib/asf/asfproperties.h deleted file mode 100644 index ab1e351b..00000000 --- a/3rdparty/taglib/asf/asfproperties.h +++ /dev/null @@ -1,168 +0,0 @@ -/************************************************************************** - copyright : (C) 2005-2007 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_ASFPROPERTIES_H -#define TAGLIB_ASFPROPERTIES_H - -#include "audioproperties.h" -#include "tstring.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ASF { - -//! An implementation of ASF audio properties -class TAGLIB_EXPORT AudioProperties : public Strawberry_TagLib::TagLib::AudioProperties { - friend class File; - - public: - /*! - * Audio codec types can be used in ASF file. - */ - enum Codec { - /*! - * Couldn't detect the codec. - */ - Unknown = 0, - - /*! - * Windows Media Audio 1 - */ - WMA1, - - /*! - * Windows Media Audio 2 or above - */ - WMA2, - - /*! - * Windows Media Audio 9 Professional - */ - WMA9Pro, - - /*! - * Windows Media Audio 9 Lossless - */ - WMA9Lossless, - }; - - /*! - * Creates an instance of ASF::Properties. - */ - explicit AudioProperties(); - - /*! - * Destroys this ASF::AudioProperties instance. - */ - ~AudioProperties() override; - - /*! - * Returns the length of the file in seconds. The length is rounded down to - * the nearest whole second. - * - * \see lengthInMilliseconds() - */ - int lengthInSeconds() const override; - - /*! - * Returns the length of the file in milliseconds. - * - * \see lengthInSeconds() - */ - int lengthInMilliseconds() const override; - - /*! - * Returns the average bit rate of the file in kb/s. - */ - int bitrate() const override; - - /*! - * Returns the sample rate in Hz. - */ - int sampleRate() const override; - - /*! - * Returns the number of audio channels. - */ - int channels() const override; - - /*! - * Returns the number of bits per audio sample. - */ - int bitsPerSample() const; - - /*! - * Returns the codec used in the file. - * - * \see codecName() - * \see codecDescription() - */ - Codec codec() const; - - /*! - * Returns the concrete codec name, for example "Windows Media Audio 9.1" used in the file if available, otherwise an empty string. - * - * \see codec() - * \see codecDescription() - */ - String codecName() const; - - /*! - * Returns the codec description, typically contains the encoder settings, - * for example "VBR Quality 50, 44kHz, stereo 1-pass VBR" if available, otherwise an empty string. - * - * \see codec() - * \see codecName() - */ - String codecDescription() const; - - /*! - * Returns whether or not the file is encrypted. - */ - bool isEncrypted() const; - - private: - void setLengthInMilliseconds(int value); - void setBitrate(int value); - void setSampleRate(int value); - void setChannels(int value); - void setBitsPerSample(int value); - void setCodec(int value); - void setCodecName(const String &value); - void setCodecDescription(const String &value); - void setEncrypted(bool value); - - private: - class AudioPropertiesPrivate; - AudioPropertiesPrivate *d; -}; - -} // namespace ASF - -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/asf/asftag.cpp b/3rdparty/taglib/asf/asftag.cpp deleted file mode 100644 index f007e5be..00000000 --- a/3rdparty/taglib/asf/asftag.cpp +++ /dev/null @@ -1,522 +0,0 @@ -/************************************************************************** - copyright : (C) 2005-2007 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tpicturemap.h" -#include "tpropertymap.h" -#include "asftag.h" - -using namespace Strawberry_TagLib::TagLib; - -class ASF::Tag::TagPrivate { - public: - String title; - String artist; - String copyright; - String comment; - String rating; - AttributeListMap attributeListMap; -}; - -ASF::Tag::Tag() : d(new TagPrivate()) {} - -ASF::Tag::~Tag() { - delete d; -} - -String ASF::Tag::title() const { - return d->title; -} - -String ASF::Tag::artist() const { - return d->artist; -} - -String ASF::Tag::album() const { - - if (d->attributeListMap.contains("WM/AlbumTitle")) - return d->attributeListMap["WM/AlbumTitle"][0].toString(); - return String(); - -} - -String ASF::Tag::copyright() const { - return d->copyright; -} - -String ASF::Tag::comment() const { - return d->comment; -} - -String ASF::Tag::rating() const { - return d->rating; -} - -unsigned int ASF::Tag::year() const { - - if (d->attributeListMap.contains("WM/Year")) - return d->attributeListMap["WM/Year"][0].toString().toInt(); - return 0; - -} - -unsigned int ASF::Tag::track() const { - - if (d->attributeListMap.contains("WM/TrackNumber")) { - const ASF::Attribute attr = d->attributeListMap["WM/TrackNumber"][0]; - if (attr.type() == ASF::Attribute::DWordType) - return attr.toUInt(); - else - return attr.toString().toInt(); - } - if (d->attributeListMap.contains("WM/Track")) - return d->attributeListMap["WM/Track"][0].toUInt(); - return 0; - -} - -String ASF::Tag::genre() const { - - if (d->attributeListMap.contains("WM/Genre")) - return d->attributeListMap["WM/Genre"][0].toString(); - return String(); - -} - -PictureMap ASF::Tag::pictures() const { - - PictureMap map; - if (d->attributeListMap.contains("WM/Picture")) { - AttributeList list = d->attributeListMap["WM/Picture"]; - for (AttributeList::ConstIterator it = list.begin(); it != list.end(); ++it) { - ASF::Picture asfPicture = (*it).toPicture(); - TagLib::Picture::Type type; - switch (asfPicture.type()) { - case ASF::Picture::FileIcon: - type = TagLib::Picture::FileIcon; - break; - case ASF::Picture::OtherFileIcon: - type = TagLib::Picture::OtherFileIcon; - break; - case ASF::Picture::FrontCover: - type = TagLib::Picture::FrontCover; - break; - case ASF::Picture::BackCover: - type = TagLib::Picture::BackCover; - break; - case ASF::Picture::LeafletPage: - type = TagLib::Picture::LeafletPage; - break; - case ASF::Picture::Media: - type = TagLib::Picture::Media; - break; - case ASF::Picture::LeadArtist: - type = TagLib::Picture::LeadArtist; - break; - case ASF::Picture::Artist: - type = TagLib::Picture::Artist; - break; - case ASF::Picture::Conductor: - type = TagLib::Picture::Conductor; - break; - case ASF::Picture::Band: - type = TagLib::Picture::Band; - break; - case ASF::Picture::Composer: - type = TagLib::Picture::Composer; - break; - case ASF::Picture::Lyricist: - type = TagLib::Picture::Lyricist; - break; - case ASF::Picture::RecordingLocation: - type = TagLib::Picture::RecordingLocation; - break; - case ASF::Picture::DuringRecording: - type = TagLib::Picture::DuringRecording; - break; - case ASF::Picture::DuringPerformance: - type = TagLib::Picture::DuringPerformance; - break; - case ASF::Picture::MovieScreenCapture: - type = TagLib::Picture::MovieScreenCapture; - break; - case ASF::Picture::ColouredFish: - type = TagLib::Picture::ColouredFish; - break; - case ASF::Picture::Illustration: - type = TagLib::Picture::Illustration; - break; - case ASF::Picture::BandLogo: - type = TagLib::Picture::BandLogo; - break; - case ASF::Picture::PublisherLogo: - type = TagLib::Picture::PublisherLogo; - break; - default: - type = TagLib::Picture::Other; - break; - } - TagLib::Picture picture(asfPicture.picture(), type, asfPicture.mimeType(), asfPicture.description()); - map.insert(picture); - } - } - return PictureMap(map); - -} - -void ASF::Tag::setTitle(const String &value) { - d->title = value; -} - -void ASF::Tag::setArtist(const String &value) { - d->artist = value; -} - -void ASF::Tag::setCopyright(const String &value) { - d->copyright = value; -} - -void ASF::Tag::setComment(const String &value) { - d->comment = value; -} - -void ASF::Tag::setRating(const String &value) { - d->rating = value; -} - -void ASF::Tag::setAlbum(const String &value) { - setAttribute("WM/AlbumTitle", value); -} - -void ASF::Tag::setGenre(const String &value) { - setAttribute("WM/Genre", value); -} - -void ASF::Tag::setYear(unsigned int value) { - setAttribute("WM/Year", String::number(value)); -} - -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)); - } - } - -} - -const ASF::AttributeListMap &ASF::Tag::attributeListMap() const { - return d->attributeListMap; -} - -bool ASF::Tag::contains(const String &key) const { - return d->attributeListMap.contains(key); -} - -void ASF::Tag::removeItem(const String &key) { - d->attributeListMap.erase(key); -} - -ASF::AttributeList ASF::Tag::attribute(const String &name) const { - return d->attributeListMap[name]; -} - -void ASF::Tag::setAttribute(const String &name, const Attribute &attribute) { - - AttributeList value; - value.append(attribute); - d->attributeListMap.insert(name, value); - -} - -void ASF::Tag::setAttribute(const String &name, const AttributeList &values) { - d->attributeListMap.insert(name, values); -} - -void ASF::Tag::addAttribute(const String &name, const Attribute &attribute) { - - if (d->attributeListMap.contains(name)) { - d->attributeListMap[name].append(attribute); - } - else { - setAttribute(name, attribute); - } - -} - -bool ASF::Tag::isEmpty() const { - - return Strawberry_TagLib::TagLib::Tag::isEmpty() && - copyright().isEmpty() && - rating().isEmpty() && - d->attributeListMap.isEmpty(); - -} - -namespace { -const char *keyTranslation[][2] = { - { "WM/AlbumTitle", "ALBUM" }, - { "WM/AlbumArtist", "ALBUMARTIST" }, - { "WM/Composer", "COMPOSER" }, - { "WM/Writer", "WRITER" }, - { "WM/Conductor", "CONDUCTOR" }, - { "WM/ModifiedBy", "REMIXER" }, - { "WM/Year", "DATE" }, - { "WM/OriginalReleaseYear", "ORIGINALDATE" }, - { "WM/Producer", "PRODUCER" }, - { "WM/ContentGroupDescription", "GROUPING" }, - { "WM/SubTitle", "SUBTITLE" }, - { "WM/SetSubTitle", "DISCSUBTITLE" }, - { "WM/TrackNumber", "TRACKNUMBER" }, - { "WM/PartOfSet", "DISCNUMBER" }, - { "WM/Genre", "GENRE" }, - { "WM/BeatsPerMinute", "BPM" }, - { "WM/Mood", "MOOD" }, - { "WM/ISRC", "ISRC" }, - { "WM/Lyrics", "LYRICS" }, - { "WM/Media", "MEDIA" }, - { "WM/Publisher", "LABEL" }, - { "WM/CatalogNo", "CATALOGNUMBER" }, - { "WM/Barcode", "BARCODE" }, - { "WM/EncodedBy", "ENCODEDBY" }, - { "WM/AlbumSortOrder", "ALBUMSORT" }, - { "WM/AlbumArtistSortOrder", "ALBUMARTISTSORT" }, - { "WM/ArtistSortOrder", "ARTISTSORT" }, - { "WM/TitleSortOrder", "TITLESORT" }, - { "WM/Script", "SCRIPT" }, - { "WM/Language", "LANGUAGE" }, - { "MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID" }, - { "MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID" }, - { "MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID" }, - { "MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" }, - { "MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" }, - { "MusicBrainz/Work Id", "MUSICBRAINZ_WORKID" }, - { "MusicIP/PUID", "MUSICIP_PUID" }, - { "Acoustid/Id", "ACOUSTID_ID" }, - { "Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT" }, -}; -const size_t keyTranslationSize = sizeof(keyTranslation) / sizeof(keyTranslation[0]); - -String translateKey(const String &key) { - - for (size_t i = 0; i < keyTranslationSize; ++i) { - if (key == keyTranslation[i][0]) - return keyTranslation[i][1]; - } - - return String(); - -} -} // namespace - -PropertyMap ASF::Tag::properties() const { - - PropertyMap props; - - if (!d->title.isEmpty()) { - props["TITLE"] = d->title; - } - if (!d->artist.isEmpty()) { - props["ARTIST"] = d->artist; - } - if (!d->copyright.isEmpty()) { - props["COPYRIGHT"] = d->copyright; - } - if (!d->comment.isEmpty()) { - props["COMMENT"] = d->comment; - } - - ASF::AttributeListMap::ConstIterator it = d->attributeListMap.begin(); - for (; it != d->attributeListMap.end(); ++it) { - const String key = translateKey(it->first); - if (!key.isEmpty()) { - AttributeList::ConstIterator it2 = it->second.begin(); - for (; it2 != it->second.end(); ++it2) { - if (key == "TRACKNUMBER") { - if (it2->type() == ASF::Attribute::DWordType) - props.insert(key, String::number(it2->toUInt())); - else - props.insert(key, it2->toString()); - } - else { - props.insert(key, it2->toString()); - } - } - } - else { - props.unsupportedData().append(it->first); - } - } - return props; - -} - -void ASF::Tag::removeUnsupportedProperties(const StringList &props) { - - StringList::ConstIterator it = props.begin(); - for (; it != props.end(); ++it) - d->attributeListMap.erase(*it); - -} - -PropertyMap ASF::Tag::setProperties(const PropertyMap &props) { - - static Map reverseKeyMap; - if (reverseKeyMap.isEmpty()) { - int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]); - for (int i = 0; i < numKeys; i++) { - reverseKeyMap[keyTranslation[i][1]] = keyTranslation[i][0]; - } - } - - PropertyMap origProps = properties(); - PropertyMap::ConstIterator it = origProps.begin(); - for (; it != origProps.end(); ++it) { - if (!props.contains(it->first) || props[it->first].isEmpty()) { - if (it->first == "TITLE") { - d->title.clear(); - } - else if (it->first == "ARTIST") { - d->artist.clear(); - } - else if (it->first == "COMMENT") { - d->comment.clear(); - } - else if (it->first == "COPYRIGHT") { - d->copyright.clear(); - } - else { - d->attributeListMap.erase(reverseKeyMap[it->first]); - } - } - } - - PropertyMap ignoredProps; - it = props.begin(); - for (; it != props.end(); ++it) { - if (reverseKeyMap.contains(it->first)) { - String name = reverseKeyMap[it->first]; - removeItem(name); - StringList::ConstIterator it2 = it->second.begin(); - for (; it2 != it->second.end(); ++it2) { - addAttribute(name, *it2); - } - } - else if (it->first == "TITLE") { - d->title = it->second.toString(); - } - else if (it->first == "ARTIST") { - d->artist = it->second.toString(); - } - else if (it->first == "COMMENT") { - d->comment = it->second.toString(); - } - else if (it->first == "COPYRIGHT") { - d->copyright = it->second.toString(); - } - else { - ignoredProps.insert(it->first, it->second); - } - } - - return ignoredProps; - -} diff --git a/3rdparty/taglib/asf/asftag.h b/3rdparty/taglib/asf/asftag.h deleted file mode 100644 index 328cf21a..00000000 --- a/3rdparty/taglib/asf/asftag.h +++ /dev/null @@ -1,201 +0,0 @@ -/************************************************************************** - copyright : (C) 2005-2007 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_ASFTAG_H -#define TAGLIB_ASFTAG_H - -#include "tag.h" -#include "tlist.h" -#include "tmap.h" -#include "taglib_export.h" -#include "asfattribute.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -namespace ASF { - -typedef List AttributeList; -typedef Map AttributeListMap; - -class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag { - - friend class File; - - public: - explicit Tag(); - - ~Tag() override; - - /*! - * Returns the track name. - */ - String title() const override; - - /*! - * Returns the artist name. - */ - String artist() const override; - - /*! - * Returns the album name; if no album name is present in the tag String::null will be returned. - */ - String album() const override; - - /*! - * Returns the track comment. - */ - String comment() const override; - - /*! - * Returns the genre name; if no genre is present in the tag String::null will be returned. - */ - String genre() const override; - - /*! - * Returns the rating. - */ - virtual String rating() const; - - /*! - * Returns the genre name; if no genre is present in the tag String::null will be returned. - */ - virtual String copyright() const; - - /*! - * Returns the year; if there is no year set, this will return 0. - */ - unsigned int year() const override; - - /*! - * Returns the track number; if there is no track number set, this will return 0. - */ - unsigned int track() const override; - - PictureMap pictures() const override; - - /*! - * Sets the title to \a s. - */ - void setTitle(const String &value) override; - - /*! - * Sets the artist to \a s. - */ - void setArtist(const String &value) override; - - /*! - * Sets the album to \a s. If \a s is String::null then this value will be cleared. - */ - void setAlbum(const String &value) override; - - /*! - * Sets the comment to \a s. - */ - void setComment(const String &value) override; - - /*! - * Sets the rating to \a s. - */ - virtual void setRating(const String &value); - - /*! - * Sets the copyright to \a s. - */ - virtual void setCopyright(const String &value); - - /*! - * Sets the genre to \a s. - */ - void setGenre(const String &value) override; - - /*! - * Sets the year to \a i. If \a s is 0 then this value will be cleared. - */ - void setYear(unsigned int value) override; - - /*! - * Sets the track to \a i. If \a s is 0 then this value will be cleared. - */ - void setTrack(unsigned int value) override; - - void setPictures(const PictureMap&) override; - - /*! - * Returns true if the tag does not contain any data. - * This should be reimplemented in subclasses that provide more than the basic tagging abilities in this class. - */ - bool isEmpty() const override; - - /*! - * Returns a reference to the item list map. This is an AttributeListMap of all of the items in the tag. - */ - const AttributeListMap &attributeListMap() const; - - /*! - * \return True if a value for \a attribute is currently set. - */ - bool contains(const String &key) const; - - /*! - * Removes the \a key attribute from the tag - */ - void removeItem(const String &key); - - /*! - * \return The list of values for the key \a name, or an empty list if no values have been set. - */ - AttributeList attribute(const String &name) const; - - /*! - * Sets the \a key attribute to the value of \a attribute. If an attribute with the \a key is already present, it will be replaced. - */ - void setAttribute(const String &name, const Attribute &attribute); - - /*! - * Sets multiple \a values to the key \a name. - */ - void setAttribute(const String &name, const AttributeList &values); - - /*! - * Sets the \a key attribute to the value of \a attribute. If an attribute - * with the \a key is already present, it will be added to the list. - */ - void addAttribute(const String &name, const Attribute &attribute); - - PropertyMap properties() const override; - void removeUnsupportedProperties(const StringList &props) override; - PropertyMap setProperties(const PropertyMap &props) override; - - private: - class TagPrivate; - TagPrivate *d; -}; - -} // namespace ASF -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/asf/asfutils.h b/3rdparty/taglib/asf/asfutils.h deleted file mode 100644 index 7369aed2..00000000 --- a/3rdparty/taglib/asf/asfutils.h +++ /dev/null @@ -1,100 +0,0 @@ -/*************************************************************************** - copyright : (C) 2015 by Tsuda Kageyu - email : tsuda.kageyu@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_ASFUTILS_H -#define TAGLIB_ASFUTILS_H - -// THIS FILE IS NOT A PART OF THE TAGLIB API - -#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ASF { -namespace { - -inline unsigned short readWORD(File *file, bool *ok = nullptr) { - const ByteVector v = file->readBlock(2); - if (v.size() != 2) { - if (ok) *ok = false; - return 0; - } - if (ok) *ok = true; - return v.toUInt16LE(0); -} - -inline unsigned int readDWORD(File *file, bool *ok = nullptr) { - const ByteVector v = file->readBlock(4); - if (v.size() != 4) { - if (ok) *ok = false; - return 0; - } - if (ok) *ok = true; - return v.toUInt32LE(0); -} - -inline long long readQWORD(File *file, bool *ok = nullptr) { - const ByteVector v = file->readBlock(8); - if (v.size() != 8) { - if (ok) *ok = false; - return 0; - } - if (ok) *ok = true; - return v.toInt64LE(0); -} - -inline String readString(File *file, int length) { - - ByteVector data = file->readBlock(length); - size_t size = data.size(); - while (size >= 2) { - if (data[size - 1] != '\0' || data[size - 2] != '\0') { - break; - } - size -= 2; - } - if (size != data.size()) { - data.resize(size); - } - return String(data, String::UTF16LE); - -} - -inline ByteVector renderString(const String &str, bool includeLength = false) { - ByteVector data = str.data(String::UTF16LE) + ByteVector::fromUInt16LE(0); - if (includeLength) { - data = ByteVector::fromUInt16LE(data.size()) + data; - } - return data; -} - -} // namespace -} // namespace ASF -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif - -#endif diff --git a/3rdparty/taglib/audioproperties.cpp b/3rdparty/taglib/audioproperties.cpp deleted file mode 100644 index 3a9575bd..00000000 --- a/3rdparty/taglib/audioproperties.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tstringlist.h" - -#include "audioproperties.h" - -using namespace Strawberry_TagLib::TagLib; - -//////////////////////////////////////////////////////////////////////////////// -// public methods -//////////////////////////////////////////////////////////////////////////////// - -AudioProperties::~AudioProperties() {} - - -String AudioProperties::toString() const { - - StringList desc; - desc.append("Audio"); - desc.append(String::number(lengthInSeconds()) + " seconds"); - desc.append(String::number(bitrate()) + " kbps"); - return desc.toString(", "); - -} - -//////////////////////////////////////////////////////////////////////////////// -// protected methods -//////////////////////////////////////////////////////////////////////////////// - -AudioProperties::AudioProperties() : d(nullptr) {} diff --git a/3rdparty/taglib/audioproperties.h b/3rdparty/taglib/audioproperties.h deleted file mode 100644 index 0db61f75..00000000 --- a/3rdparty/taglib/audioproperties.h +++ /dev/null @@ -1,120 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_AUDIOPROPERTIES_H -#define TAGLIB_AUDIOPROPERTIES_H - -#include "taglib_export.h" -#include "tstring.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -//! A simple, abstract interface to common audio properties - -/*! - * The values here are common to most audio formats. - * For more specific, codec dependent values, please see see the subclasses APIs. - * This is meant to compliment the TagLib::File and TagLib::Tag APIs in providing a simple - * interface that is sufficient for most applications. - */ - -class TAGLIB_EXPORT AudioProperties { - public: - /*! - * Reading audio properties from a file can sometimes be very time consuming - * and for the most accurate results can often involve reading the entire file. - * Because in many situations speed is critical or the accuracy of the values - * is not particularly important this allows the level of desired accuracy to be set. - */ - enum ReadStyle { - //! Read as little of the file as possible - Fast, - //! Read more of the file and make better values guesses - Average, - //! Read as much of the file as needed to report accurate values - Accurate - }; - - /*! - * Destroys this AudioProperties instance. - */ - virtual ~AudioProperties(); - - /*! - * Returns the length of the file in seconds. The length is rounded down to the nearest whole second. - * - * \see lengthInMilliseconds() - */ - virtual int lengthInSeconds() const = 0; - - /*! - * Returns the length of the file in milliseconds. - * - * \see lengthInSeconds() - */ - virtual int lengthInMilliseconds() const = 0; - - /*! - * Returns the most appropriate bit rate for the file in kb/s. For constant bitrate formats this is simply the bitrate of the file. - * For variable bitrate formats this is either the average or nominal bitrate. - */ - virtual int bitrate() const = 0; - - /*! - * Returns the sample rate in Hz. - */ - virtual int sampleRate() const = 0; - - /*! - * Returns the number of audio channels. - */ - virtual int channels() const = 0; - - /*! - * Returns description of the audio file. - */ - virtual String toString() const; - - protected: - /*! - * Construct an audio properties instance. - * This is protected as this class should not be instantiated directly, - * but should be instantiated via its subclasses and can be fetched from the FileRef or File APIs. - */ - explicit AudioProperties(); - - private: - AudioProperties(const AudioProperties&); - AudioProperties &operator=(const AudioProperties&); - - class AudioPropertiesPrivate; - AudioPropertiesPrivate *d; -}; - -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/dsdiff/dsdiffdiintag.cpp b/3rdparty/taglib/dsdiff/dsdiffdiintag.cpp deleted file mode 100644 index 7943c407..00000000 --- a/3rdparty/taglib/dsdiff/dsdiffdiintag.cpp +++ /dev/null @@ -1,147 +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 Strawberry_TagLib::TagLib; -using namespace DSDIFF::DIIN; - -class DSDIFF::DIIN::Tag::TagPrivate { - public: - TagPrivate() { - } - - String title; - String artist; -}; - -DSDIFF::DIIN::Tag::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&) {} - -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; - -} diff --git a/3rdparty/taglib/dsdiff/dsdiffdiintag.h b/3rdparty/taglib/dsdiff/dsdiffdiintag.h deleted file mode 100644 index 066f75a2..00000000 --- a/3rdparty/taglib/dsdiff/dsdiffdiintag.h +++ /dev/null @@ -1,154 +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 Strawberry_TagLib { -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 Strawberry_TagLib::TagLib::Tag { - public: - explicit Tag(); - ~Tag() override; - - /*! - * Returns the track name; if no track name is present in the tag String() will be returned. - */ - String title() const override; - - /*! - * Returns the artist name; if no artist name is present in the tag String() will be returned. - */ - String artist() const override; - - /*! - * Not supported. Therefore always returns String(). - */ - String album() const override; - - /*! - * Not supported. Therefore always returns String(). - */ - String comment() const override; - - /*! - * Not supported. Therefore always returns String(). - */ - String genre() const override; - - /*! - * Not supported. Therefore always returns 0. - */ - unsigned int year() const override; - - /*! - * Not supported. Therefore always returns 0. - */ - unsigned int track() const override; - - /*! - * Not supported. Therefore always returns an empty list. - */ - PictureMap pictures() const override; - - /*! - * Sets the title to \a title. If \a title is String() then this value will be cleared. - */ - void setTitle(const String &title) override; - - /*! - * Sets the artist to \a artist. If \a artist is String() then this value will be cleared. - */ - void setArtist(const String &artist) override; - - /*! - * Not supported and therefore ignored. - */ - void setAlbum(const String &album) override; - - /*! - * Not supported and therefore ignored. - */ - void setComment(const String &comment) override; - - /*! - * Not supported and therefore ignored. - */ - void setGenre(const String &genre) override; - - /*! - * Not supported and therefore ignored. - */ - void setYear(unsigned int year) override; - - /*! - * Not supported and therefore ignored. - */ - void setTrack(unsigned int track) override; - - /*! - * Not supported and therefore ignored. - */ - void setPictures(const PictureMap&) override; - - /*! - * Implements the unified property interface -- export function. - * Since the DIIN tag is very limited, the exported map is as well. - */ - PropertyMap properties() const override; - - /*! - * Implements the unified property interface -- import function. - * Because of the limitations of the DIIN file tag, any tags besides - * TITLE and ARTIST, will be returned. - * Additionally, if the map contains tags with multiple values, - * all but the first will be contained in the returned map of unsupported properties. - */ - PropertyMap setProperties(const PropertyMap &) override; - - private: - Tag(const Tag &); - Tag &operator=(const Tag &); - - class TagPrivate; - TagPrivate *d; -}; -} // namespace DIIN -} // namespace DSDIFF -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/dsdiff/dsdifffile.cpp b/3rdparty/taglib/dsdiff/dsdifffile.cpp deleted file mode 100644 index 1b6a8e1a..00000000 --- a/3rdparty/taglib/dsdiff/dsdifffile.cpp +++ /dev/null @@ -1,919 +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 -#include - -#include "tstring.h" -#include "tbytevector.h" -#include "tdebug.h" -#include "id3v2tag.h" -#include "tstringlist.h" -#include "tpropertymap.h" -#include "tagutils.h" - -#include "tagunion.h" -#include "dsdifffile.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { -struct Chunk64 { - ByteVector name; - unsigned long long offset; - unsigned long long size; - char padding; -}; - -typedef std::vector ChunkList; - -int chunkIndex(const ChunkList &chunks, const ByteVector &id) { - - for (unsigned long int i = 0; i < chunks.size(); i++) { - if (chunks[i].name == id) - return i; - } - - return -1; - -} - -bool isValidChunkID(const ByteVector &name) { - - if (name.size() != 4) - return false; - - for (int i = 0; i < 4; i++) { - if (name[i] < 32) - return false; - } - - return true; - -} - -enum { - ID3v2Index = 0, - DIINIndex = 1 -}; -enum { - PROPChunk = 0, - DIINChunk = 1 -}; -} // namespace - -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; - ChunkList chunks; - std::array childChunks; - std::array childChunkIndex; - /* - * Two possibilities can be found: ID3V2 chunk inside PROP chunk or at root level - */ - bool isID3InPropChunk; - /* - * ID3 chunks are present. This is then the index of the one in PROP chunk that - * will be removed upon next save to remove duplicates. - */ - int duplicateID3V2chunkIndex; - - std::unique_ptr 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) : Strawberry_TagLib::TagLib::File(file) { - - d = new FilePrivate; - d->endianness = BigEndian; - if (isOpen()) - read(readProperties, propertiesStyle); - -} - -DSDIFF::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle propertiesStyle) : Strawberry_TagLib::TagLib::File(stream) { - - d = new FilePrivate; - d->endianness = BigEndian; - if (isOpen()) - read(readProperties, propertiesStyle); - -} - -DSDIFF::File::~File() { - delete d; -} - -Strawberry_TagLib::TagLib::Tag *DSDIFF::File::tag() const { - return &d->tag; -} - -ID3v2::Tag *DSDIFF::File::ID3v2Tag(bool create) const { - return d->tag.access(ID3v2Index, create); -} - -bool DSDIFF::File::hasID3v2Tag() const { - return d->hasID3v2; -} - -DSDIFF::DIIN::Tag *DSDIFF::File::DIINTag(bool create) const { - return d->tag.access(DIINIndex, create); -} - -bool DSDIFF::File::hasDIINTag() const { - return d->hasDiin; -} - -PropertyMap DSDIFF::File::properties() const { - - if (d->hasID3v2) - return d->tag.access(ID3v2Index, false)->properties(); - - return PropertyMap(); - -} - -void DSDIFF::File::removeUnsupportedProperties(const StringList &properties) { - - if (d->hasID3v2) - d->tag.access(ID3v2Index, false)->removeUnsupportedProperties(properties); - - if (d->hasDiin) - d->tag.access(DIINIndex, false)->removeUnsupportedProperties(properties); - -} - -PropertyMap DSDIFF::File::setProperties(const PropertyMap &properties) { - return d->tag.access(ID3v2Index, true)->setProperties(properties); -} - -DSDIFF::AudioProperties *DSDIFF::File::audioProperties() const { - return d->properties.get(); -} - -bool DSDIFF::File::save() { - return save(AllTags); -} - -bool DSDIFF::File::save(TagTypes tags, StripTags, ID3v2::Version version) { - - if (readOnly()) { - debug("DSDIFF::File::save() -- File is read only."); - return false; - } - - if (!isValid()) { - debug("DSDIFF::File::save() -- Trying to save invalid file."); - return false; - } - - //if(strip == StripOthers || strip == StripAll) - //File::strip(static_cast(AllTags & ~tags)); - - // First: save ID3V2 chunk - - ID3v2::Tag *id3v2Tag = d->tag.access(ID3v2Index, false); - - if (tags & ID3v2 && id3v2Tag) { - if (d->isID3InPropChunk) { - if (id3v2Tag && !id3v2Tag->isEmpty()) { - setChildChunkData(d->id3v2TagChunkID, id3v2Tag->render(version), PROPChunk); - d->hasID3v2 = true; - } - else { - // Empty tag: remove it - setChildChunkData(d->id3v2TagChunkID, ByteVector(), PROPChunk); - d->hasID3v2 = false; - } - } - else { - if (id3v2Tag && !id3v2Tag->isEmpty()) { - setRootChunkData(d->id3v2TagChunkID, id3v2Tag->render(version)); - d->hasID3v2 = true; - } - else { - // Empty tag: remove it - setRootChunkData(d->id3v2TagChunkID, ByteVector()); - d->hasID3v2 = false; - } - } - } - - // Second: save the DIIN chunk - - DSDIFF::DIIN::Tag *diinTag = d->tag.access(DIINIndex, false); - - if (tags & DIIN && diinTag) { - 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; - -} - -void DSDIFF::File::strip(TagTypes tags) { - - if (tags & ID3v2) { - removeRootChunk("ID3 "); - removeRootChunk("id3 "); - d->hasID3v2 = false; - d->tag.set(ID3v2Index, new ID3v2::Tag); - - /// TODO: needs to also account for ID3v2 tags under the PROP chunk - } - if (tags & DIIN) { - removeRootChunk("DITI"); - removeRootChunk("DIAR"); - d->hasDiin = false; - d->tag.set(DIINIndex, new DIIN::Tag); - } - -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void DSDIFF::File::removeRootChunk(unsigned int i) { - - unsigned long long chunkSize = d->chunks[i].size + d->chunks[i].padding + 12; - - d->size -= chunkSize; - 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, chunkSize); - - // 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); - -} - -void DSDIFF::File::removeRootChunk(const ByteVector &id) { - - int i = chunkIndex(d->chunks, id); - - if (i >= 0) - removeRootChunk(i); - -} - -void DSDIFF::File::setRootChunkData(unsigned int i, const ByteVector &data) { - - if (data.isEmpty()) { - removeRootChunk(i); - return; - } - - // Non null data: update chunk - // First we update the global size - - d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding); - 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; - } - - int i = chunkIndex(d->chunks, name); - - if (i >= 0) { - setRootChunkData(i, data); - return; - } - - // Couldn't find an existing chunk, so let's create a new one. - i = 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(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::removeChildChunk(unsigned int i, unsigned int childChunkNum) { - - ChunkList &childChunks = d->childChunks[childChunkNum]; - - // Update global size - - unsigned long long removedChunkTotalSize = childChunks[i].size + childChunks[i].padding + 12; - d->size -= removedChunkTotalSize; - 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); - -} - -void DSDIFF::File::setChildChunkData(unsigned int i, const ByteVector &data, unsigned int childChunkNum) { - - ChunkList &childChunks = d->childChunks[childChunkNum]; - - if (data.isEmpty()) { - removeChildChunk(i, childChunkNum); - return; - } - - // Non null data: update chunk - // First we update the global size - - d->size += ((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding); - - 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) { - - ChunkList &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(d->chunks.size())) - nextRootChunkIdx = d->chunks[d->childChunkIndex[childChunkNum] + 1].offset - 12; - - writeChunk(name, data, offset, - std::max(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); - -} - -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(startingChunk)) { - ChunkList &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(startingChunk)) { - ChunkList &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(tell()) + chunkSize > - static_cast(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); - } - - // For DSD uncompressed - unsigned long long lengthDSDSamplesTimeChannels = 0; - // For computing bitrate - unsigned long long audioDataSizeinBytes = 0; - // For DST compressed frames - unsigned long dstNumFrames = 0; - // For DST compressed frames - unsigned short dstFrameRate = 0; - - for (unsigned int i = 0; i < d->chunks.size(); i++) { - if (d->chunks[i].name == "DSD ") { - lengthDSDSamplesTimeChannels = d->chunks[i].size * 8; - audioDataSizeinBytes = d->chunks[i].size; - } - else if (d->chunks[i].name == "DST ") { - // Now decode the chunks inside the DST chunk to read the DST Frame Information one - long long dstChunkEnd = d->chunks[i].offset + d->chunks[i].size; - seek(d->chunks[i].offset); - - audioDataSizeinBytes = d->chunks[i].size; - - while (tell() + 12 <= dstChunkEnd) { - ByteVector dstChunkName = readBlock(4); - long long dstChunkSize = 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(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); - // Found the wanted one, no need to look at the others - break; - } - - 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; - // +4 to remove the 'SND ' marker at beginning of 'PROP' chunk - seek(d->chunks[i].offset + 4); - while (tell() + 12 <= propChunkEnd) { - ByteVector propChunkName = readBlock(4); - long long propChunkSize = 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(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(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; - // ID3V2 tag has already been found at root level - continue; - } - d->id3v2TagChunkID = d->childChunks[PROPChunk][i].name; - d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->childChunks[PROPChunk][i].offset)); - d->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(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(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(DIINIndex, false)->setArtist(artistStr); - } - } - } - } - - if (readProperties) { - if (lengthDSDSamplesTimeChannels == 0) { - // DST compressed signal : need to compute length of DSD uncompressed frames - if (dstFrameRate > 0) - lengthDSDSamplesTimeChannels = static_cast(dstNumFrames) * static_cast(sampleRate) / static_cast(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(ID3v2Index, true); - // By default, ID3 chunk is at root level - d->isID3InPropChunk = false; - d->hasID3v2 = false; - } - -} - -void DSDIFF::File::writeChunk(const ByteVector &name, const ByteVector &data, unsigned long long offset, unsigned long replace, unsigned int leadingPadding) { - - ByteVector combined; - if (leadingPadding) - combined.append(ByteVector(leadingPadding, '\x00')); - - combined.append(name); - 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); - -} diff --git a/3rdparty/taglib/dsdiff/dsdifffile.h b/3rdparty/taglib/dsdiff/dsdifffile.h deleted file mode 100644 index c1a5bf00..00000000 --- a/3rdparty/taglib/dsdiff/dsdifffile.h +++ /dev/null @@ -1,265 +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 Strawberry_TagLib { -namespace TagLib { - -//! An implementation of DSDIFF metadata - -/*! - * This is implementation of DSDIFF metadata. - * - * This supports an ID3v2 tag as well as reading stream from the ID3 RIFF - * chunk as well as properties from the file. - * Description of the DSDIFF format is available at http://dsd-guide.com/sites/default/files/white-papers/DSDIFF_1.5_Spec.pdf - * DSDIFF standard does not explicitly specify the ID3V2 chunk - * It can be found at the root level, but also sometimes inside the PROP chunk. - * In addition, title and artist info are stored as part of the standard - */ - -namespace DSDIFF { - -//! An implementation of TagLib::File with DSDIFF specific methods - -/*! - * This implements and provides an interface for DSDIFF files to the - * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing the - * abstract TagLib::File API as well as providing some additional information specific to DSDIFF files. - */ - -class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File { - public: - /*! - * This set of flags is used for various operations and is suitable for - * being OR-ed together. - */ - enum TagTypes { - //! Empty set. Matches no tag types. - NoTags = 0x0000, - //! Matches DIIN tags. - DIIN = 0x0002, - //! Matches ID3v1 tags. - ID3v2 = 0x0002, - //! Matches all tag types. - AllTags = 0xffff - }; - - /*! - * 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. - */ - explicit 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. - */ - explicit File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Destroys this instance of the File. - */ - ~File() override; - - /*! - * Returns a pointer to a tag that is the union of the ID3v2 and DIIN tags. - * The ID3v2 tag is given priority in reading the information -- if requested information exists in both the ID3v2 tag and the ID3v1 tag, - * the information from the ID3v2 tag will be returned. - * - * If you would like more granular control over the content of the tags, with the concession of generality, use the tag-type specific calls. - * - * \note As this tag is not implemented as an ID3v2 tag or a DIIN tag, - * but a union of the two this pointer may not be cast to the specific tag types. - * - * \see ID3v2Tag() - * \see DIINTag() - */ - Tag *tag() const override; - - /*! - * Returns the ID3V2 Tag for this file. - * - * \note This always returns a valid pointer regardless of whether or not the file on disk has an ID3v2 tag. - * Use hasID3v2Tag() to check if the file on disk actually has an ID3v2 tag. - * - * \see hasID3v2Tag() - */ - ID3v2::Tag *ID3v2Tag(bool create = false) const; - - /*! - * Returns the DSDIFF DIIN Tag for this file - * - */ - DSDIFF::DIIN::Tag *DIINTag(bool create = false) const; - - /*! - * Implements the unified property interface -- export function. - * This method forwards to ID3v2::Tag::properties(). - */ - PropertyMap properties() const override; - - void removeUnsupportedProperties(const StringList &properties) override; - - /*! - * Implements the unified property interface -- import function. - * This method forwards to ID3v2::Tag::setProperties(). - */ - PropertyMap setProperties(const PropertyMap &properties) override; - - /*! - * Returns the AIFF::AudioProperties for this file. - * If no audio properties were read then this will return a null pointer. - */ - AudioProperties *audioProperties() const override; - - /*! - * Save the file. If at least one tag -- ID3v1 or DIIN -- exists this will duplicate its content into the other tag. - * This returns true if saving was successful. - * - * If neither exists or if both tags are empty, this will strip the tags from the file. - * - * This is the same as calling save(AllTags); - * - * If you would like more granular control over the content of the tags, - * with the concession of generality, use paramaterized save call below. - * - * \see save(int tags) - */ - bool save() override; - - /*! - * Save the file. If \a strip is specified, - * it is possible to choose if tags not specified in \a tags should be stripped from the file or retained. - * With \a version, it is possible to specify whether ID3v2.4 or ID3v2.3 should be used. - */ - bool save(TagTypes tags, StripTags strip = StripOthers, ID3v2::Version version = ID3v2::v4); - - /*! - * This will strip the tags that match the OR-ed together TagTypes from the file. - * By default it strips all tags. It returns true if the tags are successfully stripped. - * - * \note This will update the file immediately. - */ - void strip(TagTypes tags = AllTags); - - /*! - * Returns whether or not the file on disk actually has an ID3v2 tag. - * - * \see ID3v2Tag() - */ - bool hasID3v2Tag() const; - - /*! - * Returns whether or not the file on disk actually has the DSDIFF title and artist tags. - * - * \see DIINTag() - */ - bool hasDIINTag() const; - - /*! - * Returns whether or not the given \a stream can be opened as a DSDIFF file. - * - * \note This method is designed to do a quick check. The result may not necessarily be correct. - */ - static bool isSupported(IOStream *stream); - - protected: - enum Endianness { - BigEndian, - LittleEndian - }; - - File(FileName file, Endianness endianness); - File(IOStream *stream, Endianness endianness); - - private: - File(const File &); - File &operator=(const File &); - - void removeRootChunk(const ByteVector &id); - void removeRootChunk(unsigned int i); - void removeChildChunk(unsigned int i, unsigned int childChunkNum); - - /*! - * Sets the data for the the specified chunk at root level to \a data. - * - * \warning This will update the file immediately. - */ - void setRootChunkData(unsigned int i, const ByteVector &data); - - /*! - * Sets the data for the root-level chunk \a name to \a data. - * If a root-level chunk with the given name already exists it will be overwritten, otherwise it will be created after the existing chunks. - * - * \warning This will update the file immediately. - */ - void setRootChunkData(const ByteVector &name, const ByteVector &data); - - /*! - * Sets the data for the the specified child chunk to \a data. - * - * If data is null, then remove the chunk - * - * \warning This will update the file immediately. - */ - void setChildChunkData(unsigned int i, const ByteVector &data, unsigned int childChunkNum); - - /*! - * Sets the data for the child chunk \a name to \a data. - * If a chunk with the given name already exists it will be overwritten, otherwise it will be created after the existing chunks inside child chunk. - * - * If data is null, then remove the chunks with \a name name - * - * \warning This will update the file immediately. - */ - void setChildChunkData(const ByteVector &name, const ByteVector &data, unsigned int childChunkNum); - - void updateRootChunksStructure(unsigned int startingChunk); - - void read(bool readProperties, 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; -}; -} // namespace DSDIFF -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/dsdiff/dsdiffproperties.cpp b/3rdparty/taglib/dsdiff/dsdiffproperties.cpp deleted file mode 100644 index 90409730..00000000 --- a/3rdparty/taglib/dsdiff/dsdiffproperties.cpp +++ /dev/null @@ -1,96 +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 Strawberry_TagLib::TagLib; - -class DSDIFF::AudioProperties::AudioPropertiesPrivate { - public: - explicit 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) : Strawberry_TagLib::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((d->sampleCount * 1000.0) / d->sampleRate + 0.5) : 0; - -} - -DSDIFF::AudioProperties::~AudioProperties() { - delete d; -} - -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; -} diff --git a/3rdparty/taglib/dsdiff/dsdiffproperties.h b/3rdparty/taglib/dsdiff/dsdiffproperties.h deleted file mode 100644 index 48c76201..00000000 --- a/3rdparty/taglib/dsdiff/dsdiffproperties.h +++ /dev/null @@ -1,78 +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 Strawberry_TagLib { -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 Strawberry_TagLib::TagLib::AudioProperties { - public: - /*! - * Create an instance of DSDIFF::AudioProperties with the data read from the ByteVector \a data. - */ - explicit AudioProperties(const unsigned int sampleRate, const unsigned short channels, const unsigned long long samplesCount, const int bitrate, ReadStyle); - - /*! - * Destroys this DSDIFF::AudioProperties instance. - */ - ~AudioProperties() override; - - // Reimplementations. - - int lengthInSeconds() const override; - int lengthInMilliseconds() const override; - int bitrate() const override; - int sampleRate() const override; - int channels() const override; - - int bitsPerSample() const; - long long sampleCount() const; - - private: - AudioProperties(const AudioProperties &); - AudioProperties &operator=(const AudioProperties &); - - class AudioPropertiesPrivate; - AudioPropertiesPrivate *d; -}; - -} // namespace DSDIFF -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/dsf/dsffile.cpp b/3rdparty/taglib/dsf/dsffile.cpp deleted file mode 100644 index 82c3ea53..00000000 --- a/3rdparty/taglib/dsf/dsffile.cpp +++ /dev/null @@ -1,228 +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 - -#include "tbytevector.h" -#include "tdebug.h" -#include "id3v2tag.h" -#include "tstringlist.h" -#include "tpropertymap.h" -#include "tagutils.h" - -#include "dsffile.h" - -using namespace Strawberry_TagLib::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; - - std::unique_ptr properties; - std::unique_ptr 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) : Strawberry_TagLib::TagLib::File(file), d(new FilePrivate()) { - - if (isOpen()) - read(readProperties, propertiesStyle); - -} - -DSF::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle propertiesStyle) : Strawberry_TagLib::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(oldTagSize)); - } - - return true; - -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - - -void DSF::File::read(bool, 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, wrong chunk size"); - 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 wrong length"); - 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(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)); - -} diff --git a/3rdparty/taglib/dsf/dsffile.h b/3rdparty/taglib/dsf/dsffile.h deleted file mode 100644 index 129b4fdb..00000000 --- a/3rdparty/taglib/dsf/dsffile.h +++ /dev/null @@ -1,125 +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 Strawberry_TagLib { -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 Strawberry_TagLib::TagLib::File with DSF specific methods - -/*! - * This implements and provides an interface for DSF files to the - * Strawberry_TagLib::TagLib::Tag and Strawberry_TagLib::TagLib::AudioProperties interfaces by way of implementing - * the abstract Strawberry_TagLib::TagLib::File API as well as providing some additional information specific to DSF files. - * - */ - -class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File { - public: - /*! - * Constructs 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. - */ - explicit File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Constructs 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. - */ - explicit File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Destroys this instance of the File. - */ - ~File() override; - - /*! - * Returns the Tag for this file. - */ - ID3v2::Tag *tag() const override; - - /*! - * Implements the unified property interface -- export function. - * This method forwards to ID3v2::Tag::properties(). - */ - PropertyMap properties() const override; - - /*! - * Implements the unified property interface -- import function. - * This method forwards to ID3v2::Tag::setProperties(). - */ - PropertyMap setProperties(const PropertyMap &) override; - - /*! - * Returns the DSF::AudioProperties for this file. - * If no audio properties were read then this will return a null pointer. - */ - AudioProperties *audioProperties() const override; - - /*! - * Saves the file. - */ - bool save() override; - - /*! - * Returns whether or not the given \a stream can be opened as a DSF file. - * - * \note This method is designed to do a quick check. - * The result may not necessarily be correct. - */ - static bool isSupported(IOStream *stream); - - private: - File(const File &); - File &operator=(const File &); - - void read(bool readProperties, AudioProperties::ReadStyle propertiesStyle); - - class FilePrivate; - FilePrivate *d; -}; -} // namespace DSF -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/dsf/dsfproperties.cpp b/3rdparty/taglib/dsf/dsfproperties.cpp deleted file mode 100644 index a8e9f5eb..00000000 --- a/3rdparty/taglib/dsf/dsfproperties.cpp +++ /dev/null @@ -1,135 +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 Strawberry_TagLib::TagLib; - -class DSF::AudioProperties::AudioPropertiesPrivate { - public: - explicit AudioPropertiesPrivate() : 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) : Strawberry_TagLib::TagLib::AudioProperties(), d(new AudioPropertiesPrivate()) { - read(data); -} - -DSF::AudioProperties::~AudioProperties() { - delete d; -} - -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((d->samplingFrequency * d->bitsPerSample * d->channelNum) / 1000.0 + 0.5); - d->length = d->samplingFrequency > 0 ? static_cast(d->sampleCount * 1000.0 / d->samplingFrequency + 0.5) : 0; -} diff --git a/3rdparty/taglib/dsf/dsfproperties.h b/3rdparty/taglib/dsf/dsfproperties.h deleted file mode 100644 index b23c78c2..00000000 --- a/3rdparty/taglib/dsf/dsfproperties.h +++ /dev/null @@ -1,94 +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 Strawberry_TagLib { -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 Strawberry_TagLib::TagLib::AudioProperties { - public: - /*! - * Create an instance of DSF::AudioProperties with the data read from the ByteVector \a data. - */ - explicit AudioProperties(const ByteVector &data, ReadStyle); - - /*! - * Destroys this DSF::AudioProperties instance. - */ - ~AudioProperties() override; - - // Reimplementations. - - int lengthInSeconds() const override; - int lengthInMilliseconds() const override; - int bitrate() const override; - int sampleRate() const override; - int channels() const override; - - 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 AudioPropertiesPrivate; - AudioPropertiesPrivate *d; -}; -} // namespace DSF -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/fileref.cpp b/3rdparty/taglib/fileref.cpp deleted file mode 100644 index 6923d535..00000000 --- a/3rdparty/taglib/fileref.cpp +++ /dev/null @@ -1,424 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - - copyright : (C) 2010 by Alex Novichkov - email : novichko@atnet.ru - (added APE file support) - ***************************************************************************/ - -/*************************************************************************** - * 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 - -#include "tfile.h" -#include "tfilestream.h" -#include "tstring.h" -#include "tdebug.h" - -#include "fileref.h" -#include "asffile.h" -#include "mpegfile.h" -#include "vorbisfile.h" -#include "flacfile.h" -#include "oggflacfile.h" -#include "mpcfile.h" -#include "mp4file.h" -#include "wavpackfile.h" -#include "speexfile.h" -#include "opusfile.h" -#include "trueaudiofile.h" -#include "aifffile.h" -#include "wavfile.h" -#include "apefile.h" -#include "modfile.h" -#include "s3mfile.h" -#include "itfile.h" -#include "xmfile.h" -#include "dsffile.h" -#include "dsdifffile.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { -typedef List ResolverList; -ResolverList fileTypeResolvers; - -// Detect the file type by user-defined resolvers. - -File *detectByResolvers(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) { - - ResolverList::ConstIterator it = fileTypeResolvers.begin(); - for (; it != fileTypeResolvers.end(); ++it) { - File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle); - if (file) - return file; - } - - return nullptr; - -} - -// Detect the file type based on the file extension. - -File *detectByExtension(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) { - -#ifdef _WIN32 - const String s(stream->name().wstr()); -#else - const String s(stream->name()); -#endif - - String ext; - const size_t pos = s.rfind("."); - if (pos != String::npos()) - ext = s.substr(pos + 1).upper(); - - // If this list is updated, the method defaultFileExtensions() should also be - // updated. However at some point that list should be created at the same time - // that a default file type resolver is created. - - if (ext.isEmpty()) - return nullptr; - - // .oga can be any audio in the Ogg container. So leave it to content-based detection. - - if (ext == "MP3") - return new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle); - if (ext == "OGG") - return new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle); - if (ext == "FLAC") - return new FLAC::File(stream, readAudioProperties, audioPropertiesStyle); - if (ext == "MPC") - return new MPC::File(stream, readAudioProperties, audioPropertiesStyle); - if (ext == "WV") - return new WavPack::File(stream, readAudioProperties, audioPropertiesStyle); - if (ext == "SPX") - return new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle); - if (ext == "OPUS") - return new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle); - if (ext == "TTA") - return new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle); - if (ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V") - return new MP4::File(stream, readAudioProperties, audioPropertiesStyle); - if (ext == "WMA" || ext == "ASF") - return new ASF::File(stream, readAudioProperties, audioPropertiesStyle); - if (ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC") - return new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle); - if (ext == "WAV") - return new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle); - if (ext == "APE") - return new APE::File(stream, readAudioProperties, audioPropertiesStyle); - // module, nst and wow are possible but uncommon extensions - if (ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW") - return new Mod::File(stream, readAudioProperties, audioPropertiesStyle); - if (ext == "S3M") - return new S3M::File(stream, readAudioProperties, audioPropertiesStyle); - if (ext == "IT") - return new IT::File(stream, readAudioProperties, audioPropertiesStyle); - if (ext == "XM") - return new XM::File(stream, readAudioProperties, audioPropertiesStyle); - if (ext == "DFF" || ext == "DSDIFF") - return new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle); - if (ext == "DSF") - return new DSF::File(stream, readAudioProperties, audioPropertiesStyle); - - return nullptr; - -} - -// Detect the file type based on the actual content of the stream. - -File *detectByContent(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) { - - File *file = nullptr; - - if (MPEG::File::isSupported(stream)) - file = new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle); - else if (Ogg::Vorbis::File::isSupported(stream)) - file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle); - else if (Ogg::FLAC::File::isSupported(stream)) - file = new Ogg::FLAC::File(stream, readAudioProperties, audioPropertiesStyle); - else if (FLAC::File::isSupported(stream)) - file = new FLAC::File(stream, readAudioProperties, audioPropertiesStyle); - else if (MPC::File::isSupported(stream)) - file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle); - else if (WavPack::File::isSupported(stream)) - file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle); - else if (Ogg::Speex::File::isSupported(stream)) - file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle); - else if (Ogg::Opus::File::isSupported(stream)) - file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle); - else if (TrueAudio::File::isSupported(stream)) - file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle); - else if (MP4::File::isSupported(stream)) - file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle); - else if (ASF::File::isSupported(stream)) - file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle); - else if (RIFF::AIFF::File::isSupported(stream)) - file = new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle); - else if (RIFF::WAV::File::isSupported(stream)) - 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. - - if (file) { - if (file->isValid()) - return file; - else - delete file; - } - - return nullptr; - -} - -struct FileRefData { - FileRefData() : file(nullptr), stream(nullptr) {} - - ~FileRefData() { - delete file; - delete stream; - } - - File *file; - IOStream *stream; -}; - -} // namespace - -class FileRef::FileRefPrivate { - public: - FileRefPrivate() : data(new FileRefData()) {} - - std::shared_ptr data; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -FileRef::FileRef() : d(new FileRefPrivate()) {} - -FileRef::FileRef(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) : d(new FileRefPrivate()) { - parse(fileName, readAudioProperties, audioPropertiesStyle); -} - -FileRef::FileRef(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) : d(new FileRefPrivate()) { - parse(stream, readAudioProperties, audioPropertiesStyle); -} - -FileRef::FileRef(File *file) : d(new FileRefPrivate()) { - d->data->file = file; -} - -FileRef::FileRef(const FileRef &ref) : d(new FileRefPrivate(*ref.d)) {} - -FileRef::~FileRef() { - delete d; -} - -Tag *FileRef::tag() const { - - if (isNull()) { - debug("FileRef::tag() - Called without a valid file."); - return nullptr; - } - 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); -} - -AudioProperties *FileRef::audioProperties() const { - - if (isNull()) { - debug("FileRef::audioProperties() - Called without a valid file."); - return nullptr; - } - return d->data->file->audioProperties(); - -} - -File *FileRef::file() const { - return d->data->file; -} - -bool FileRef::save() { - - if (isNull()) { - debug("FileRef::save() - Called without a valid file."); - return false; - } - return d->data->file->save(); - -} - -const FileRef::FileTypeResolver *FileRef::addFileTypeResolver(const FileRef::FileTypeResolver *resolver) { // static - fileTypeResolvers.prepend(resolver); - return resolver; -} - -StringList FileRef::defaultFileExtensions() { - - StringList l; - - l.append("ogg"); - l.append("flac"); - l.append("oga"); - l.append("mp3"); - l.append("mpc"); - l.append("wv"); - l.append("spx"); - l.append("tta"); - l.append("m4a"); - l.append("m4r"); - l.append("m4b"); - l.append("m4p"); - l.append("3g2"); - l.append("mp4"); - l.append("m4v"); - l.append("wma"); - l.append("asf"); - l.append("aif"); - l.append("aiff"); - l.append("wav"); - l.append("ape"); - l.append("mod"); - l.append("module"); // alias for "mod" - l.append("nst"); // alias for "mod" - l.append("wow"); // alias for "mod" - l.append("s3m"); - l.append("it"); - l.append("xm"); - l.append("dsf"); - l.append("dff"); - l.append("dsdiff"); // alias for "dff" - - return l; - -} - -bool FileRef::isValid() const { - return (d->data->file && d->data->file->isValid()); -} - -bool FileRef::isNull() const { - return (!d->data->file || !d->data->file->isValid()); -} - -FileRef &FileRef::operator=(const FileRef &ref) { - FileRef(ref).swap(*this); - return *this; -} - -void FileRef::swap(FileRef &ref) { - using std::swap; - - swap(d, ref.d); -} - -bool FileRef::operator==(const FileRef &ref) const { - return (ref.d->data == d->data); -} - -bool FileRef::operator!=(const FileRef &ref) const { - return (ref.d->data != d->data); -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void FileRef::parse(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) { - - // Try user-defined resolvers. - - d->data->file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle); - if (d->data->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) - 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) - return; - - // Stream have to be closed here if failed to resolve file types. - - delete d->data->stream; - d->data->stream = nullptr; -} - -void FileRef::parse(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) { - - // User-defined resolvers won't work with a stream. - - // Try to resolve file types based on the file extension. - - d->data->file = detectByExtension(stream, readAudioProperties, audioPropertiesStyle); - if (d->data->file) - return; - - // At last, try to resolve file types based on the actual content of the file. - - d->data->file = detectByContent(stream, readAudioProperties, audioPropertiesStyle); - -} diff --git a/3rdparty/taglib/fileref.h b/3rdparty/taglib/fileref.h deleted file mode 100644 index 47be18cf..00000000 --- a/3rdparty/taglib/fileref.h +++ /dev/null @@ -1,283 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_FILEREF_H -#define TAGLIB_FILEREF_H - -#include "tfile.h" -#include "tstringlist.h" - -#include "taglib_export.h" -#include "tpropertymap.h" -#include "audioproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -class Tag; - -//! This class provides a simple abstraction for creating and handling files - -/*! - * FileRef exists to provide a minimal, generic and value-based wrapper around a File. - * It is lightweight and implicitly shared, and as such suitable for pass-by-value use. - * This hides some of the uglier details of TagLib::File and the non-generic portions of the concrete file implementations. - * - * This class is useful in a "simple usage" situation where it is desirable - * to be able to get and set some of the tag information that is similar across file types. - * - * Also note that it is probably a good idea to plug this into your mime - * type system rather than using the constructor that accepts a file name using the FileTypeResolver. - * - * \see FileTypeResolver - * \see addFileTypeResolver() - */ - -class TAGLIB_EXPORT FileRef { - public: - //! A class for pluggable file type resolution. - - /*! - * This class is used to add extend TagLib's very basic file name based file type resolution. - * - * This can be accomplished with: - * - * \code - * - * class MyFileTypeResolver : FileTypeResolver - * { - * Strawberry_TagLib::TagLib::File *createFile(Strawberry_TagLib::TagLib::FileName *fileName, bool, AudioProperties::ReadStyle) const - * { - * if(someCheckForAnMP3File(fileName)) - * return new Strawberry_TagLib::TagLib::MPEG::File(fileName); - * return 0; - * } - * } - * - * FileRef::addFileTypeResolver(new MyFileTypeResolver); - * - * \endcode - * - * Naturally a less contrived example would be slightly more complex. - * This can be used to plug in mime-type detection systems or to add new file types to TagLib. - */ - - class TAGLIB_EXPORT FileTypeResolver { - 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 return a valid File object; if not it should return 0. - * - * \note The created file is then owned by the FileRef and should not be deleted. - * Deletion will happen automatically when the FileRef passes out of scope. - */ - virtual File *createFile(FileName fileName, bool readAudioProperties = true, AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average) const = 0; - }; - - /*! - * Creates a null FileRef. - */ - explicit FileRef(); - - /*! - * Create a FileRef from \a fileName. - * 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. - * - * Also see the note in the class documentation about why you may not want to - * use this method in your application. - */ - explicit FileRef(FileName fileName, bool readAudioProperties = true, AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average); - - /*! - * Construct a FileRef from an opened \a IOStream. - * 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. - * - * Also see the note in the class documentation about why you may not want to use this method in your application. - * - * \note TagLib will *not* take ownership of the stream, the caller is responsible for deleting it after the File object. - */ - explicit FileRef(IOStream *stream, bool readAudioProperties = true, AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average); - - /*! - * Construct a FileRef using \a file. - * The FileRef now takes ownership of the pointer and will delete the File when it passes out of scope. - */ - explicit FileRef(File *file); - - /*! - * Make a copy of \a ref. - */ - FileRef(const FileRef &ref); - - /*! - * Destroys this FileRef instance. - */ - virtual ~FileRef(); - - /*! - * Returns a pointer to represented file's tag. - * - * \warning This pointer will become invalid when this FileRef and all - * copies pass out of scope. - * - * \warning Do not cast it to any subclasses of \class Tag. - * Use tag returning methods of appropriate subclasses of \class File instead. - * - * \see File::tag() - */ - 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. - */ - AudioProperties *audioProperties() const; - - /*! - * Returns a pointer to the file represented by this handler class. - * - * As a general rule this call should be avoided since if you need to work - * with file objects directly, you are probably better served instantiating - * the File subclasses (i.e. MPEG::File) manually and working with their APIs. - * - * This handle exists to provide a minimal, generic and value-based - * wrapper around a File. Accessing the file directly generally indicates - * a moving away from this simplicity (and into things beyond the scope of - * FileRef). - * - * \warning This pointer will become invalid when this FileRef and all - * copies pass out of scope. - */ - File *file() const; - - /*! - * Saves the file. Returns true on success. - */ - bool save(); - - /*! - * Adds a FileTypeResolver to the list of those used by TagLib. - * Each additional FileTypeResolver is added to the front of a list of resolvers that are tried. - * If the FileTypeResolver returns zero the next resolver is tried. - * - * Returns a pointer to the added resolver (the same one that's passed in -- - * this is mostly so that static initializers have something to use for assignment). - * - * \see FileTypeResolver - */ - static const FileTypeResolver *addFileTypeResolver(const FileTypeResolver *resolver); - - /*! - * As is mentioned elsewhere in this class's documentation, the default file - * type resolution code provided by TagLib only works by comparing file extensions. - * - * This method returns the list of file extensions that are used by default. - * - * The extensions are all returned in lowercase, though the comparison used - * by TagLib for resolution is case-insensitive. - * - * \note This does not account for any additional file type resolvers that - * are plugged in. Also note that this is not intended to replace a proper - * mime-type resolution system, but is just here for reference. - * - * \see FileTypeResolver - */ - 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. - */ - bool isNull() const; - - /*! - * Assign the file pointed to by \a ref to this FileRef. - */ - FileRef &operator=(const FileRef &ref); - - /*! - * Exchanges the content of the FileRef by the content of \a ref. - */ - void swap(FileRef &ref); - - /*! - * Returns true if this FileRef and \a ref point to the same File object. - */ - bool operator==(const FileRef &ref) const; - - /*! - * Returns true if this FileRef and \a ref do not point to the same File object. - */ - bool operator!=(const FileRef &ref) const; - - private: - void parse(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle); - void parse(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle); - - class FileRefPrivate; - FileRefPrivate *d; -}; - -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif // TAGLIB_FILEREF_H diff --git a/3rdparty/taglib/flac/flacfile.cpp b/3rdparty/taglib/flac/flacfile.cpp deleted file mode 100644 index 6c419416..00000000 --- a/3rdparty/taglib/flac/flacfile.cpp +++ /dev/null @@ -1,535 +0,0 @@ -/*************************************************************************** - copyright : (C) 2003-2004 by Allan Sandfeld Jensen - email : kde@carewolf.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 - -#include "tbytevector.h" -#include "tstring.h" -#include "tlist.h" -#include "tdebug.h" -#include "tagunion.h" -#include "tpropertymap.h" -#include "tagutils.h" - -#include "id3v2header.h" -#include "id3v2tag.h" -#include "id3v1tag.h" -#include "xiphcomment.h" - -#include "flacpicture.h" -#include "flacfile.h" -#include "flacmetadatablock.h" -#include "flacunknownmetadatablock.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { -typedef List> 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 char LastBlockFlag = '\x80'; -} // namespace - -namespace Strawberry_TagLib { -namespace TagLib { -namespace FLAC { -// Enables BlockList::find() to take raw pointers. - -bool operator==(std::shared_ptr lhs, MetadataBlock *rhs); -bool operator==(std::shared_ptr lhs, MetadataBlock *rhs) { - return lhs.get() == rhs; -} -} // namespace FLAC -} // namespace TagLib -} // namespace Strawberry_TagLib - -class FLAC::File::FilePrivate { - public: - explicit FilePrivate(const ID3v2::FrameFactory *frameFactory) : ID3v2FrameFactory(ID3v2::FrameFactory::instance()), - ID3v2Location(-1), - ID3v2OriginalSize(0), - ID3v1Location(-1), - flacStart(0), - streamStart(0), - scanned(false) { - - if (frameFactory) - ID3v2FrameFactory = frameFactory; - - } - - const ID3v2::FrameFactory *ID3v2FrameFactory; - long long ID3v2Location; - long long ID3v2OriginalSize; - - long long ID3v1Location; - - TripleTagUnion tag; - - std::unique_ptr properties; - ByteVector xiphCommentData; - BlockList blocks; - - long long flacStart; - long long streamStart; - bool scanned; -}; - -//////////////////////////////////////////////////////////////////////////////// -// static members -//////////////////////////////////////////////////////////////////////////////// - -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()); - -} - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -FLAC::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle, ID3v2::FrameFactory *frameFactory) : Strawberry_TagLib::TagLib::File(file), d(new FilePrivate(frameFactory)) { - - if (isOpen()) - read(readProperties); - -} - -FLAC::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle, ID3v2::FrameFactory *frameFactory) : Strawberry_TagLib::TagLib::File(stream), d(new FilePrivate(frameFactory)) { - - if (isOpen()) - read(readProperties); - -} - -FLAC::File::~File() { - delete d; -} - -Strawberry_TagLib::TagLib::Tag *FLAC::File::tag() const { - return &d->tag; -} - -PropertyMap FLAC::File::setProperties(const PropertyMap &properties) { - return xiphComment(true)->setProperties(properties); -} - -FLAC::AudioProperties *FLAC::File::audioProperties() const { - return d->properties.get(); -} - -bool FLAC::File::save() { - - if (readOnly()) { - debug("FLAC::File::save() - Cannot save to a read only file."); - return false; - } - - if (!isValid()) { - debug("FLAC::File::save() -- Trying to save invalid file."); - return false; - } - - // Create new vorbis comments - if (!hasXiphComment()) - Tag::duplicate(&d->tag, xiphComment(true), false); - - d->xiphCommentData = xiphComment()->render(false); - - // Replace metadata blocks - - for (BlockIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) { - if ((*it)->code() == MetadataBlock::VorbisComment) { - // Set the new Vorbis Comment block - d->blocks.erase(it); - break; - } - } - - d->blocks.append(std::shared_ptr(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()); - blockHeader[0] = (*it)->code(); - data.append(blockHeader); - data.append(blockData); - } - - // Compute the amount of padding, and append that to data. - - long long originalLength = d->streamStart - d->flacStart; - long long paddingLength = originalLength - data.size() - 4; - - if (paddingLength <= 0) { - paddingLength = MinPaddingLength; - } - else { - // Padding won't increase beyond 1% of the file size or 1MB. - - long long threshold = length() / 100; - threshold = std::max(threshold, MinPaddingLength); - threshold = std::min(threshold, MaxPaddingLegnth); - - if (paddingLength > threshold) - paddingLength = MinPaddingLength; - } - - ByteVector paddingHeader = ByteVector::fromUInt32BE(paddingLength); - paddingHeader[0] = static_cast(MetadataBlock::Padding | LastBlockFlag); - data.append(paddingHeader); - data.resize(static_cast(data.size() + paddingLength)); - - // Write the data to the file - - insert(data, d->flacStart, originalLength); - - d->streamStart += (static_cast(data.size()) - originalLength); - - if (d->ID3v1Location >= 0) - d->ID3v1Location += (static_cast(data.size()) - originalLength); - - // Update ID3 tags - - if (ID3v2Tag() && !ID3v2Tag()->isEmpty()) { - - // ID3v2 tag is not empty. Update the old one or create a new one. - - if (d->ID3v2Location < 0) - d->ID3v2Location = 0; - - data = ID3v2Tag()->render(); - insert(data, d->ID3v2Location, d->ID3v2OriginalSize); - - d->flacStart += (static_cast(data.size()) - d->ID3v2OriginalSize); - d->streamStart += (static_cast(data.size()) - d->ID3v2OriginalSize); - - if (d->ID3v1Location >= 0) - d->ID3v1Location += (static_cast(data.size()) - d->ID3v2OriginalSize); - - d->ID3v2OriginalSize = data.size(); - } - else { - - // ID3v2 tag is empty. Remove the old one. - - if (d->ID3v2Location >= 0) { - removeBlock(d->ID3v2Location, d->ID3v2OriginalSize); - - d->flacStart -= d->ID3v2OriginalSize; - d->streamStart -= d->ID3v2OriginalSize; - - if (d->ID3v1Location >= 0) - d->ID3v1Location -= d->ID3v2OriginalSize; - - d->ID3v2Location = -1; - d->ID3v2OriginalSize = 0; - } - } - - if (ID3v1Tag() && !ID3v1Tag()->isEmpty()) { - - // ID3v1 tag is not empty. Update the old one or create a new one. - - if (d->ID3v1Location >= 0) { - seek(d->ID3v1Location); - } - else { - seek(0, End); - d->ID3v1Location = tell(); - } - - writeBlock(ID3v1Tag()->render()); - } - else { - - // ID3v1 tag is empty. Remove the old one. - - if (d->ID3v1Location >= 0) { - truncate(d->ID3v1Location); - d->ID3v1Location = -1; - } - } - - return true; - -} - -ID3v2::Tag *FLAC::File::ID3v2Tag(bool create) { - return d->tag.access(FlacID3v2Index, create); -} - -ID3v1::Tag *FLAC::File::ID3v1Tag(bool create) { - return d->tag.access(FlacID3v1Index, create); -} - -Ogg::XiphComment *FLAC::File::xiphComment(bool create) { - return d->tag.access(FlacXiphIndex, create); -} - -List FLAC::File::pictureList() { - - List pictures; - for (BlockConstIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) { - Picture *picture = dynamic_cast(it->get()); - if (picture) { - pictures.append(picture); - } - } - return pictures; - -} - -void FLAC::File::addPicture(Picture *picture) { - d->blocks.append(std::shared_ptr(picture)); -} - -void FLAC::File::removePicture(Picture *picture) { - - BlockIterator it = d->blocks.find(picture); - if (it != d->blocks.end()) - d->blocks.erase(it); - -} - -void FLAC::File::removePictures() { - - for (BlockIterator it = d->blocks.begin(); it != d->blocks.end();) { - if (dynamic_cast(it->get())) { - it = d->blocks.erase(it); - } - else { - ++it; - } - } - -} - -void FLAC::File::strip(int tags) { - - if (tags & ID3v1) - d->tag.set(FlacID3v1Index, nullptr); - - if (tags & ID3v2) - d->tag.set(FlacID3v2Index, nullptr); - - if (tags & XiphComment) { - xiphComment()->removeAllFields(); - xiphComment()->removeAllPictures(); - } - -} - -bool FLAC::File::hasXiphComment() const { - return !d->xiphCommentData.isEmpty(); -} - -bool FLAC::File::hasID3v1Tag() const { - return (d->ID3v1Location >= 0); -} - -bool FLAC::File::hasID3v2Tag() const { - return (d->ID3v2Location >= 0); -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void FLAC::File::read(bool readProperties) { - - // Look for an ID3v2 tag - - d->ID3v2Location = Utils::findID3v2(this); - - if (d->ID3v2Location >= 0) { - d->tag.set(FlacID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory)); - d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize(); - } - - // Look for an ID3v1 tag - - d->ID3v1Location = Utils::findID3v1(this); - - if (d->ID3v1Location >= 0) - d->tag.set(FlacID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); - - // Look for FLAC metadata, including vorbis comments - - scan(); - - if (!isValid()) - return; - - if (!d->xiphCommentData.isEmpty()) - d->tag.set(FlacXiphIndex, new Ogg::XiphComment(d->xiphCommentData)); - else - d->tag.set(FlacXiphIndex, new Ogg::XiphComment()); - - if (readProperties) { - - // First block should be the stream_info metadata - - const ByteVector infoData = d->blocks.front()->render(); - - long long streamLength; - - if (d->ID3v1Location >= 0) - streamLength = d->ID3v1Location - d->streamStart; - else - streamLength = length() - d->streamStart; - - d->properties.reset(new AudioProperties(infoData, streamLength)); - } - -} - -void FLAC::File::scan() { - - // Scan the metadata pages - - if (d->scanned) - return; - - if (!isValid()) - return; - - long long nextBlockOffset; - - if (d->ID3v2Location >= 0) - nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize); - else - nextBlockOffset = find("fLaC"); - - if (nextBlockOffset < 0) { - debug("FLAC::File::scan() -- FLAC stream not found"); - setValid(false); - return; - } - - nextBlockOffset += 4; - d->flacStart = nextBlockOffset; - - while (true) { - - seek(nextBlockOffset); - const ByteVector header = readBlock(4); - - // Header format (from spec): - // <1> Last-metadata-block flag - // <7> BLOCK_TYPE - // 0 : STREAMINFO - // 1 : PADDING - // .. - // 4 : VORBIS_COMMENT - // .. - // 6 : PICTURE - // .. - // <24> Length of metadata to follow - - const char blockType = header[0] & ~LastBlockFlag; - const bool isLastBlock = (header[0] & LastBlockFlag) != 0; - const size_t blockLength = header.toUInt24BE(1); - - // First block should be the stream_info metadata - - if (d->blocks.isEmpty() && blockType != MetadataBlock::StreamInfo) { - debug("FLAC::File::scan() -- First block should be the stream_info metadata"); - setValid(false); - return; - } - - if (blockLength == 0 && blockType != MetadataBlock::Padding && blockType != MetadataBlock::SeekTable) { - debug("FLAC::File::scan() -- Zero-sized metadata block found"); - setValid(false); - return; - } - - const ByteVector data = readBlock(blockLength); - if (data.size() != blockLength) { - debug("FLAC::File::scan() -- Failed to read a metadata block"); - setValid(false); - return; - } - - std::shared_ptr block; - - // Found the vorbis-comment - if (blockType == MetadataBlock::VorbisComment) { - if (d->xiphCommentData.isEmpty()) { - d->xiphCommentData = data; - block.reset(new UnknownMetadataBlock(MetadataBlock::VorbisComment, data)); - } - else { - debug("FLAC::File::scan() -- multiple Vorbis Comment blocks found, discarding"); - } - } - else if (blockType == MetadataBlock::Picture) { - std::shared_ptr picture(new FLAC::Picture()); - if (picture->parse(data)) { - block = picture; - } - else { - debug("FLAC::File::scan() -- invalid picture found, discarding"); - } - } - else if (blockType == MetadataBlock::Padding) { - // Skip all padding blocks. - } - else { - block.reset(new UnknownMetadataBlock(blockType, data)); - } - - if (block) - d->blocks.append(block); - - nextBlockOffset += blockLength + 4; - - if (isLastBlock) - break; - } - - // End of metadata, now comes the datastream - - d->streamStart = nextBlockOffset; - - d->scanned = true; - -} diff --git a/3rdparty/taglib/flac/flacfile.h b/3rdparty/taglib/flac/flacfile.h deleted file mode 100644 index 66c9df59..00000000 --- a/3rdparty/taglib/flac/flacfile.h +++ /dev/null @@ -1,278 +0,0 @@ -/*************************************************************************** - copyright : (C) 2003 by Allan Sandfeld Jensen - email : kde@carewolf.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_FLACFILE_H -#define TAGLIB_FLACFILE_H - -#include - -#include "taglib_export.h" -#include "tfile.h" -#include "tlist.h" -#include "tag.h" - -#include "flacpicture.h" -#include "flacproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -class Tag; -namespace ID3v2 { -class FrameFactory; -class Tag; -} // namespace ID3v2 -namespace ID3v1 { -class Tag; -} -namespace Ogg { -class XiphComment; -} - -//! An implementation of FLAC metadata - -/*! - * This is implementation of FLAC metadata for non-Ogg FLAC files. At some - * point when Ogg / FLAC is more common there will be a similar implementation - * under the Ogg hierarchy. - * - * This supports ID3v1, ID3v2 and Xiph style comments as well as reading stream - * properties from the file. - */ - -namespace FLAC { - -//! An implementation of TagLib::File with FLAC specific methods - -/*! - * This implements and provides an interface for FLAC 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 FLAC files. - * - */ - -class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File { - public: - /*! - * This set of flags is used for various operations and is suitable for - * being OR-ed together. - */ - enum TagTypes { - //! Empty set. Matches no tag types. - NoTags = 0x0000, - //! Matches Vorbis comments. - XiphComment = 0x0001, - //! Matches ID3v1 tags. - ID3v1 = 0x0002, - //! Matches ID3v2 tags. - ID3v2 = 0x0004, - //! Matches all tag types. - AllTags = 0xffff - }; - - /*! - * Constructs an FLAC file from \a file. - * If \a readProperties is true the file's audio properties will also be read. - * - * If this file contains and ID3v2 tag the frames will be created using \a frameFactory. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average, ID3v2::FrameFactory *frameFactory = nullptr); - - /*! - * Constructs a FLAC file from \a stream. If \a readProperties is true the file's audio properties will also be read. - * - * \note TagLib will *not* take ownership of the stream, the caller is responsible for deleting it after the File object. - * - * If this file contains and ID3v2 tag the frames will be created using \a frameFactory. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - // BIC: merge with the above constructor - explicit File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average, ID3v2::FrameFactory *frameFactory = nullptr); - - /*! - * Destroys this instance of the File. - */ - ~File() override; - - /*! - * Returns the Tag for this file. This will be a union of XiphComment, ID3v1 and ID3v2 tags. - * - * \see ID3v2Tag() - * \see ID3v1Tag() - * \see XiphComment() - */ - Strawberry_TagLib::TagLib::Tag *tag() const override; - - /*! - * Implements the unified property interface -- import function. - * This always creates a Xiph comment, if none exists. The return value relates to the Xiph comment only. - * Ignores any changes to ID3v1 or ID3v2 comments since they are not allowed in the FLAC specification. - */ - PropertyMap setProperties(const PropertyMap &) override; - - /*! - * Returns the FLAC::AudioProperties for this file. If no audio properties were read then this will return a null pointer. - */ - AudioProperties *audioProperties() const override; - - /*! - * Save the file. This will primarily save the XiphComment, but will also keep any old ID3-tags up to date. - * If the file has no XiphComment, one will be constructed from the ID3-tags. - * - * This returns true if the save was successful. - */ - bool save() override; - - /*! - * Returns a pointer to the ID3v2 tag of the file. - * - * If \a create is false (the default) this returns a null pointer - * if there is no valid ID3v2 tag. - * If \a create is true it will create an ID3v2 tag if one does not exist and returns a valid pointer. - * - * \note This may return a valid pointer regardless of whether or not the file on disk has an ID3v2 tag. - * Use hasID3v2Tag() to check if the file on disk actually has an ID3v2 tag. - * - * \note The Tag is still owned by the MPEG::File and should not be - * deleted by the user. It will be deleted when the file (object) is destroyed. - * - * \see hasID3v2Tag() - */ - ID3v2::Tag *ID3v2Tag(bool create = false); - - /*! - * Returns a pointer to the ID3v1 tag of the file. - * - * If \a create is false (the default) this returns a null pointer if there is no valid APE tag. - * If \a create is true it will create an APE tag if one does not exist and returns a valid pointer. - * - * \note This may return a valid pointer regardless of whether or not the file on disk has an ID3v1 tag. - * Use hasID3v1Tag() to check if the file on disk actually has an ID3v1 tag. - * - * \note The Tag is still owned by the MPEG::File and should not be deleted by the user. - * It will be deleted when the file (object) is destroyed. - * - * \see hasID3v1Tag() - */ - ID3v1::Tag *ID3v1Tag(bool create = false); - - /*! - * Returns a pointer to the XiphComment for the file. - * - * If \a create is false (the default) this returns a null pointer if there is no valid XiphComment. - * If \a create is true it will create a XiphComment if one does not exist and returns a valid pointer. - * - * \note This may return a valid pointer regardless of whether or not the file on disk has a XiphComment. - * Use hasXiphComment() to check if the file on disk actually has a XiphComment. - * - * \note The Tag is still owned by the FLAC::File and should not be deleted by the user. - * It will be deleted when the file (object) is destroyed. - * - * \see hasXiphComment() - */ - Ogg::XiphComment *xiphComment(bool create = false); - - /*! - * Returns a list of pictures attached to the FLAC file. - */ - List pictureList(); - - /*! - * Removes an attached picture. The picture's memory will be freed. - */ - void removePicture(Picture *picture); - - /*! - * Remove all attached images. - */ - void removePictures(); - - /*! - * Add a new picture to the file. - * The file takes ownership of the picture and will handle freeing its memory. - * - * \note The file will be saved only after calling save(). - */ - void addPicture(Picture *picture); - - /*! - * This will remove the tags that match the OR-ed together TagTypes from the file. - * By default it removes all tags. - * - * \warning This will also invalidate pointers to the tags as their memory will be freed. - * - * \note In order to make the removal permanent save() still needs to be called. - * - * \note This won't remove the Vorbis comment block completely. - * The vendor ID will be preserved. - */ - void strip(int tags = AllTags); - - /*! - * Returns whether or not the file on disk actually has a XiphComment. - * - * \see xiphComment() - */ - bool hasXiphComment() const; - - /*! - * Returns whether or not the file on disk actually has an ID3v1 tag. - * - * \see ID3v1Tag() - */ - bool hasID3v1Tag() const; - - /*! - * Returns whether or not the file on disk actually has an ID3v2 tag. - * - * \see ID3v2Tag() - */ - bool hasID3v2Tag() const; - - /*! - * Returns whether or not the given \a stream can be opened as a FLAC 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); - void scan(); - - class FilePrivate; - FilePrivate *d; -}; -} // namespace FLAC -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/flac/flacmetadatablock.cpp b/3rdparty/taglib/flac/flacmetadatablock.cpp deleted file mode 100644 index c4138bf1..00000000 --- a/3rdparty/taglib/flac/flacmetadatablock.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/************************************************************************** - copyright : (C) 2010 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "taglib.h" -#include "tdebug.h" -#include "flacmetadatablock.h" - -using namespace Strawberry_TagLib::TagLib; - -class FLAC::MetadataBlock::MetadataBlockPrivate { - public: - MetadataBlockPrivate() {} -}; - -FLAC::MetadataBlock::MetadataBlock() : d(nullptr) {} - -FLAC::MetadataBlock::~MetadataBlock() {} diff --git a/3rdparty/taglib/flac/flacmetadatablock.h b/3rdparty/taglib/flac/flacmetadatablock.h deleted file mode 100644 index ba3dbd7a..00000000 --- a/3rdparty/taglib/flac/flacmetadatablock.h +++ /dev/null @@ -1,74 +0,0 @@ -/************************************************************************** - copyright : (C) 2010 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_FLACMETADATABLOCK_H -#define TAGLIB_FLACMETADATABLOCK_H - -#include "tlist.h" -#include "tbytevector.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace FLAC { - -class TAGLIB_EXPORT MetadataBlock { - public: - explicit MetadataBlock(); - virtual ~MetadataBlock(); - - enum BlockType { - StreamInfo = 0, - Padding, - Application, - SeekTable, - VorbisComment, - CueSheet, - Picture - }; - - /*! - * Returns the FLAC metadata block type. - */ - virtual int code() const = 0; - - /*! - * Render the content of the block. - */ - virtual ByteVector render() const = 0; - - private: - MetadataBlock(const MetadataBlock &item); - MetadataBlock &operator=(const MetadataBlock &item); - - class MetadataBlockPrivate; - MetadataBlockPrivate *d; -}; - -} // namespace FLAC -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/flac/flacpicture.cpp b/3rdparty/taglib/flac/flacpicture.cpp deleted file mode 100644 index 8593efbd..00000000 --- a/3rdparty/taglib/flac/flacpicture.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/************************************************************************** - copyright : (C) 2010 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "taglib.h" -#include "tdebug.h" -#include "flacpicture.h" - -using namespace Strawberry_TagLib::TagLib; - -class FLAC::Picture::PicturePrivate { - public: - explicit PicturePrivate() : type(FLAC::Picture::Other), - width(0), - height(0), - colorDepth(0), - numColors(0) { - } - - Type type; - String mimeType; - String description; - int width; - int height; - int colorDepth; - int numColors; - ByteVector data; -}; - -FLAC::Picture::Picture() : d(new PicturePrivate()) { -} - -FLAC::Picture::Picture(const ByteVector &data) : d(new PicturePrivate()) { - parse(data); -} - -FLAC::Picture::~Picture() { - delete d; -} - -int FLAC::Picture::code() const { - return FLAC::MetadataBlock::Picture; -} - -bool FLAC::Picture::parse(const ByteVector &data) { - - if (data.size() < 32) { - debug("A picture block must contain at least 5 bytes."); - return false; - } - - size_t pos = 0; - d->type = FLAC::Picture::Type(data.toUInt32BE(pos)); - pos += 4; - const unsigned int mimeTypeLength = data.toUInt32BE(pos); - pos += 4; - if (pos + mimeTypeLength + 24 > data.size()) { - debug("Invalid picture block."); - return false; - } - d->mimeType = String(data.mid(pos, mimeTypeLength), String::UTF8); - pos += mimeTypeLength; - const unsigned int descriptionLength = data.toUInt32BE(pos); - pos += 4; - if (pos + descriptionLength + 20 > data.size()) { - debug("Invalid picture block."); - return false; - } - d->description = String(data.mid(pos, descriptionLength), String::UTF8); - pos += descriptionLength; - d->width = data.toUInt32BE(pos); - pos += 4; - d->height = data.toUInt32BE(pos); - pos += 4; - d->colorDepth = data.toUInt32BE(pos); - pos += 4; - d->numColors = data.toUInt32BE(pos); - pos += 4; - const unsigned int dataLength = data.toUInt32BE(pos); - pos += 4; - if (pos + dataLength > data.size()) { - debug("Invalid picture block."); - return false; - } - d->data = data.mid(pos, dataLength); - - return true; - -} - -ByteVector FLAC::Picture::render() const { - - ByteVector result; - result.append(ByteVector::fromUInt32BE(d->type)); - ByteVector mimeTypeData = d->mimeType.data(String::UTF8); - result.append(ByteVector::fromUInt32BE(mimeTypeData.size())); - result.append(mimeTypeData); - ByteVector descriptionData = d->description.data(String::UTF8); - result.append(ByteVector::fromUInt32BE(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(d->data); - return result; - -} - -FLAC::Picture::Type FLAC::Picture::type() const { - return d->type; -} - -void FLAC::Picture::setType(FLAC::Picture::Type type) { - d->type = type; -} - -String FLAC::Picture::mimeType() const { - return d->mimeType; -} - -void FLAC::Picture::setMimeType(const String &mimeType) { - d->mimeType = mimeType; -} - -String FLAC::Picture::description() const { - return d->description; -} - -void FLAC::Picture::setDescription(const String &description) { - d->description = description; -} - -int FLAC::Picture::width() const { - return d->width; -} - -void FLAC::Picture::setWidth(int width) { - d->width = width; -} - -int FLAC::Picture::height() const { - return d->height; -} - -void FLAC::Picture::setHeight(int height) { - d->height = height; -} - -int FLAC::Picture::colorDepth() const { - return d->colorDepth; -} - -void FLAC::Picture::setColorDepth(int colorDepth) { - d->colorDepth = colorDepth; -} - -int FLAC::Picture::numColors() const { - return d->numColors; -} - -void FLAC::Picture::setNumColors(int numColors) { - d->numColors = numColors; -} - -ByteVector FLAC::Picture::data() const { - return d->data; -} - -void FLAC::Picture::setData(const ByteVector &data) { - d->data = data; -} diff --git a/3rdparty/taglib/flac/flacpicture.h b/3rdparty/taglib/flac/flacpicture.h deleted file mode 100644 index 2fc84a87..00000000 --- a/3rdparty/taglib/flac/flacpicture.h +++ /dev/null @@ -1,203 +0,0 @@ -/************************************************************************** - copyright : (C) 2010 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_FLACPICTURE_H -#define TAGLIB_FLACPICTURE_H - -#include "tlist.h" -#include "tstring.h" -#include "tbytevector.h" -#include "taglib_export.h" -#include "flacmetadatablock.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -namespace FLAC { - -class TAGLIB_EXPORT Picture : public MetadataBlock { - public: - /*! - * This describes the function or content of the picture. - */ - enum Type { - //! A type not enumerated below - Other = 0x00, - //! 32x32 PNG image that should be used as the file icon - FileIcon = 0x01, - //! File icon of a different size or format - OtherFileIcon = 0x02, - //! Front cover image of the album - FrontCover = 0x03, - //! Back cover image of the album - BackCover = 0x04, - //! Inside leaflet page of the album - LeafletPage = 0x05, - //! Image from the album itself - Media = 0x06, - //! Picture of the lead artist or soloist - LeadArtist = 0x07, - //! Picture of the artist or performer - Artist = 0x08, - //! Picture of the conductor - Conductor = 0x09, - //! Picture of the band or orchestra - Band = 0x0A, - //! Picture of the composer - Composer = 0x0B, - //! Picture of the lyricist or text writer - Lyricist = 0x0C, - //! Picture of the recording location or studio - RecordingLocation = 0x0D, - //! Picture of the artists during recording - DuringRecording = 0x0E, - //! Picture of the artists during performance - DuringPerformance = 0x0F, - //! Picture from a movie or video related to the track - MovieScreenCapture = 0x10, - //! Picture of a large, coloured fish - ColouredFish = 0x11, - //! Illustration related to the track - Illustration = 0x12, - //! Logo of the band or performer - BandLogo = 0x13, - //! Logo of the publisher (record company) - PublisherLogo = 0x14 - }; - - explicit Picture(); - explicit Picture(const ByteVector &data); - ~Picture() override; - - /*! - * Returns the type of the image. - */ - Type type() const; - - /*! - * Sets the type of the image. - */ - void setType(Type type); - - /*! - * Returns the mime type of the image. This should in most cases be "image/png" or "image/jpeg". - */ - String mimeType() const; - - /*! - * Sets the mime type of the image. This should in most cases be "image/png" or "image/jpeg". - */ - void setMimeType(const String &m); - - /*! - * Returns a text description of the image. - */ - - String description() const; - - /*! - * Sets a textual description of the image to \a desc. - */ - - void setDescription(const String &desc); - - /*! - * Returns the width of the image. - */ - int width() const; - - /*! - * Sets the width of the image. - */ - void setWidth(int w); - - /*! - * Returns the height of the image. - */ - int height() const; - - /*! - * Sets the height of the image. - */ - void setHeight(int h); - - /*! - * Returns the color depth (in bits-per-pixel) of the image. - */ - int colorDepth() const; - - /*! - * Sets the color depth (in bits-per-pixel) of the image. - */ - void setColorDepth(int depth); - - /*! - * Returns the number of colors used on the image.. - */ - int numColors() const; - - /*! - * Sets the number of colors used on the image (for indexed images). - */ - void setNumColors(int numColors); - - /*! - * Returns the image data. - */ - ByteVector data() const; - - /*! - * Sets the image data. - */ - void setData(const ByteVector &data); - - /*! - * Returns the FLAC metadata block type. - */ - int code() const override; - - /*! - * Render the content to the FLAC picture block format. - */ - ByteVector render() const override; - - /*! - * Parse the picture data in the FLAC picture block format. - */ - bool parse(const ByteVector &rawData); - - private: - Picture(const Picture &item); - Picture &operator=(const Picture &item); - - class PicturePrivate; - PicturePrivate *d; -}; - -} // namespace FLAC -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/flac/flacproperties.cpp b/3rdparty/taglib/flac/flacproperties.cpp deleted file mode 100644 index b91ba685..00000000 --- a/3rdparty/taglib/flac/flacproperties.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/*************************************************************************** - copyright : (C) 2003 by Allan Sandfeld Jensen - email : kde@carewolf.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 "flacproperties.h" -#include "flacfile.h" - -using namespace Strawberry_TagLib::TagLib; - -class FLAC::AudioProperties::AudioPropertiesPrivate { - public: - explicit AudioPropertiesPrivate() : length(0), - bitrate(0), - sampleRate(0), - bitsPerSample(0), - channels(0), - sampleFrames(0) {} - - int length; - int bitrate; - int sampleRate; - int bitsPerSample; - int channels; - unsigned long long sampleFrames; - ByteVector signature; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -FLAC::AudioProperties::AudioProperties(const ByteVector &data, long long streamLength, ReadStyle) : Strawberry_TagLib::TagLib::AudioProperties(), d(new AudioPropertiesPrivate()) { - read(data, streamLength); -} - -FLAC::AudioProperties::~AudioProperties() { - delete d; -} - -int FLAC::AudioProperties::lengthInSeconds() const { - return d->length / 1000; -} - -int FLAC::AudioProperties::lengthInMilliseconds() const { - return d->length; -} - -int FLAC::AudioProperties::bitrate() const { - return d->bitrate; -} - -int FLAC::AudioProperties::sampleRate() const { - return d->sampleRate; -} - -int FLAC::AudioProperties::bitsPerSample() const { - return d->bitsPerSample; -} - -int FLAC::AudioProperties::channels() const { - return d->channels; -} - -unsigned long long FLAC::AudioProperties::sampleFrames() const { - return d->sampleFrames; -} - -ByteVector FLAC::AudioProperties::signature() const { - return d->signature; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void FLAC::AudioProperties::read(const ByteVector &data, long long streamLength) { - - if (data.size() < 18) { - debug("FLAC::AudioProperties::read() - FLAC properties must contain at least 18 bytes."); - return; - } - - size_t pos = 0; - - // Minimum block size (in samples) - pos += 2; - - // Maximum block size (in samples) - pos += 2; - - // Minimum frame size (in bytes) - pos += 3; - - // Maximum frame size (in bytes) - pos += 3; - - const unsigned int flags = data.toUInt32BE(pos); - pos += 4; - - d->sampleRate = flags >> 12; - d->channels = ((flags >> 9) & 7) + 1; - d->bitsPerSample = ((flags >> 4) & 31) + 1; - - // The last 4 bits are the most significant 4 bits for the 36 bit - // stream length in samples. (Audio files measured in days) - - const unsigned long long hi = flags & 0xf; - const unsigned long long lo = data.toUInt32BE(pos); - pos += 4; - - d->sampleFrames = (hi << 32) | lo; - - if (d->sampleFrames > 0 && d->sampleRate > 0) { - const double length = d->sampleFrames * 1000.0 / d->sampleRate; - d->length = static_cast(length + 0.5); - d->bitrate = static_cast(streamLength * 8.0 / length + 0.5); - } - - if (data.size() >= pos + 16) - d->signature = data.mid(pos, 16); - -} diff --git a/3rdparty/taglib/flac/flacproperties.h b/3rdparty/taglib/flac/flacproperties.h deleted file mode 100644 index 1cc19dfc..00000000 --- a/3rdparty/taglib/flac/flacproperties.h +++ /dev/null @@ -1,111 +0,0 @@ -/*************************************************************************** - copyright : (C) 2003 by Allan Sandfeld Jensen - email : kde@carewolf.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_FLACPROPERTIES_H -#define TAGLIB_FLACPROPERTIES_H - -#include "taglib_export.h" -#include "audioproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -namespace FLAC { - -class File; - -//! An implementation of audio property reading for FLAC - -/*! - * This reads the data from an FLAC stream found in the AudioProperties API. - */ - -class TAGLIB_EXPORT AudioProperties : public Strawberry_TagLib::TagLib::AudioProperties { - public: - /*! - * Create an instance of FLAC::AudioProperties with the data read from the ByteVector \a data. - */ - explicit AudioProperties(const ByteVector &data, long long streamLength, ReadStyle style = Average); - - /*! - * Destroys this FLAC::AudioProperties instance. - */ - ~AudioProperties() override; - - /*! - * Returns the length of the file in seconds. The length is rounded down to the nearest whole second. - * - * \see lengthInMilliseconds() - */ - int lengthInSeconds() const override; - - /*! - * Returns the length of the file in milliseconds. - * - * \see lengthInSeconds() - */ - int lengthInMilliseconds() const override; - - /*! - * Returns the average bit rate of the file in kb/s. - */ - int bitrate() const override; - - /*! - * Returns the sample rate in Hz. - */ - int sampleRate() const override; - - /*! - * Returns the number of audio channels. - */ - int channels() const override; - - /*! - * Returns the number of bits per audio sample as read from the FLAC identification header. - */ - int bitsPerSample() const; - - /*! - * Return the number of sample frames. - */ - unsigned long long sampleFrames() const; - - /*! - * Returns the MD5 signature of the uncompressed audio stream as read from the stream info header. - */ - ByteVector signature() const; - - private: - void read(const ByteVector &data, long long streamLength); - - class AudioPropertiesPrivate; - AudioPropertiesPrivate *d; -}; -} // namespace FLAC -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/flac/flacunknownmetadatablock.cpp b/3rdparty/taglib/flac/flacunknownmetadatablock.cpp deleted file mode 100644 index 5ef473ff..00000000 --- a/3rdparty/taglib/flac/flacunknownmetadatablock.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/************************************************************************** - copyright : (C) 2010 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "taglib.h" -#include "tdebug.h" -#include "tstring.h" -#include "flacunknownmetadatablock.h" - -using namespace Strawberry_TagLib::TagLib; - -class FLAC::UnknownMetadataBlock::UnknownMetadataBlockPrivate { - public: - explicit UnknownMetadataBlockPrivate() : code(0) {} - - int code; - ByteVector data; -}; - -FLAC::UnknownMetadataBlock::UnknownMetadataBlock(int code, const ByteVector &data) : d(new UnknownMetadataBlockPrivate()) { - d->code = code; - d->data = data; -} - -FLAC::UnknownMetadataBlock::~UnknownMetadataBlock() { - delete d; -} - -int FLAC::UnknownMetadataBlock::code() const { - return d->code; -} - -void FLAC::UnknownMetadataBlock::setCode(int code) { - d->code = code; -} - -ByteVector FLAC::UnknownMetadataBlock::data() const { - return d->data; -} - -void FLAC::UnknownMetadataBlock::setData(const ByteVector &data) { - d->data = data; -} - -ByteVector FLAC::UnknownMetadataBlock::render() const { - return d->data; -} diff --git a/3rdparty/taglib/flac/flacunknownmetadatablock.h b/3rdparty/taglib/flac/flacunknownmetadatablock.h deleted file mode 100644 index c23afc22..00000000 --- a/3rdparty/taglib/flac/flacunknownmetadatablock.h +++ /dev/null @@ -1,80 +0,0 @@ -/************************************************************************** - copyright : (C) 2010 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_FLACUNKNOWNMETADATABLOCK_H -#define TAGLIB_FLACUNKNOWNMETADATABLOCK_H - -#include "tlist.h" -#include "tbytevector.h" -#include "taglib_export.h" -#include "flacmetadatablock.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace FLAC { - -class TAGLIB_EXPORT UnknownMetadataBlock : public MetadataBlock { - public: - explicit UnknownMetadataBlock(int code, const ByteVector &data); - ~UnknownMetadataBlock() override; - - /*! - * Returns the FLAC metadata block type. - */ - int code() const override; - - /*! - * Sets the FLAC metadata block type. - */ - void setCode(int code); - - /*! - * Returns the FLAC metadata block type. - */ - ByteVector data() const; - - /*! - * Sets the FLAC metadata block type. - */ - void setData(const ByteVector &data); - - /*! - * Render the content of the block. - */ - ByteVector render() const override; - - private: - explicit UnknownMetadataBlock(const MetadataBlock &item); - UnknownMetadataBlock &operator=(const MetadataBlock &item); - - class UnknownMetadataBlockPrivate; - UnknownMetadataBlockPrivate *d; -}; - -} // namespace FLAC -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/it/itfile.cpp b/3rdparty/taglib/it/itfile.cpp deleted file mode 100644 index 38dfcc84..00000000 --- a/3rdparty/taglib/it/itfile.cpp +++ /dev/null @@ -1,312 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - - -#include "tstringlist.h" -#include "itfile.h" -#include "tdebug.h" -#include "modfileprivate.h" -#include "tpropertymap.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace IT; - -class IT::File::FilePrivate { - public: - explicit FilePrivate(AudioProperties::ReadStyle propertiesStyle) : properties(propertiesStyle) {} - - Mod::Tag tag; - IT::AudioProperties properties; -}; - -IT::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(file), d(new FilePrivate(propertiesStyle)) { - - if (isOpen()) - read(readProperties); - -} - -IT::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(stream), d(new FilePrivate(propertiesStyle)) { - - if (isOpen()) - read(readProperties); - -} - -IT::File::~File() { - delete d; -} - -Mod::Tag *IT::File::tag() const { - return &d->tag; -} - -IT::AudioProperties *IT::File::audioProperties() const { - return &d->properties; -} - -bool IT::File::save() { - - if (readOnly()) { - debug("IT::File::save() - Cannot save to a read only file."); - return false; - } - seek(4); - writeString(d->tag.title(), 25); - writeByte(0); - - seek(2, Current); - - unsigned short length = 0; - unsigned short instrumentCount = 0; - unsigned short sampleCount = 0; - - if (!readU16L(length) || !readU16L(instrumentCount) || !readU16L(sampleCount)) - return false; - - seek(15, Current); - - // write comment as instrument and sample names: - StringList lines = d->tag.comment().split("\n"); - for (unsigned short i = 0; i < instrumentCount; ++i) { - seek(192L + length + (static_cast(i) << 2)); - unsigned int instrumentOffset = 0; - if (!readU32L(instrumentOffset)) - return false; - - seek(instrumentOffset + 32); - - if (i < lines.size()) - writeString(lines[i], 25); - else - writeString(String(), 25); - writeByte(0); - } - - for (unsigned short i = 0; i < sampleCount; ++i) { - seek(192L + length + (static_cast(instrumentCount) << 2) + (static_cast(i) << 2)); - unsigned int sampleOffset = 0; - if (!readU32L(sampleOffset)) - return false; - - seek(sampleOffset + 20); - - if (static_cast(i + instrumentCount) < lines.size()) - writeString(lines[i + instrumentCount], 25); - else - writeString(String(), 25); - writeByte(0); - } - - // write rest as message: - StringList messageLines; - for (unsigned int i = instrumentCount + sampleCount; i < lines.size(); ++i) - messageLines.append(lines[i]); - ByteVector message = messageLines.toString("\r").data(String::Latin1); - - // it's actually not really stated if the message needs a - // terminating NUL but it does not hurt to add one: - if (message.size() > 7999) - message.resize(7999); - message.append(static_cast(0)); - - unsigned short special = 0; - unsigned short messageLength = 0; - unsigned int messageOffset = 0; - - seek(46); - if (!readU16L(special)) - return false; - - unsigned int fileSize = File::length(); - if (special & AudioProperties::MessageAttached) { - seek(54); - if (!readU16L(messageLength) || !readU32L(messageOffset)) - return false; - - if (messageLength == 0) - messageOffset = fileSize; - } - else { - messageOffset = fileSize; - seek(46); - writeU16L(special | 0x1); - } - - if (messageOffset + messageLength >= fileSize) { - // append new message - seek(54); - writeU16L(message.size()); - writeU32L(messageOffset); - seek(messageOffset); - writeBlock(message); - truncate(messageOffset + message.size()); - } - else { - // Only overwrite existing message. - // I'd need to parse (understand!) the whole file for more. - // Although I could just move the message to the end of file - // and let the existing one be, but that would waste space. - message.resize(messageLength, 0); - seek(messageOffset); - writeBlock(message); - } - return true; -} - -void IT::File::read(bool) { - if (!isOpen()) - return; - - seek(0); - READ_ASSERT(readBlock(4) == "IMPM"); - READ_STRING(d->tag.setTitle, 26); - - seek(2, Current); - - READ_U16L_AS(length); - READ_U16L_AS(instrumentCount); - READ_U16L_AS(sampleCount); - - d->properties.setInstrumentCount(instrumentCount); - d->properties.setSampleCount(sampleCount); - READ_U16L(d->properties.setPatternCount); - READ_U16L(d->properties.setVersion); - READ_U16L(d->properties.setCompatibleVersion); - READ_U16L(d->properties.setFlags); - READ_U16L_AS(special); - d->properties.setSpecial(special); - READ_BYTE(d->properties.setGlobalVolume); - READ_BYTE(d->properties.setMixVolume); - READ_BYTE(d->properties.setBpmSpeed); - READ_BYTE(d->properties.setTempo); - READ_BYTE(d->properties.setPanningSeparation); - READ_BYTE(d->properties.setPitchWheelDepth); - - // IT supports some kind of comment tag. Still, the - // sample/instrument names are abused as comments so - // I just add all together. - String message; - if (special & AudioProperties::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(static_cast(0)); - if (index != ByteVector::npos()) - messageBytes.resize(index, 0); - messageBytes.replace('\r', '\n'); - message = messageBytes; - } - - seek(64); - - ByteVector pannings = readBlock(64); - ByteVector volumes = readBlock(64); - READ_ASSERT(pannings.size() == 64 && volumes.size() == 64); - int channels = 0; - for (int i = 0; i < 64; ++i) { - // Strictly speaking an IT file has always 64 channels, but - // I don't count disabled and muted channels. - // But this always gives 64 channels for all my files anyway. - // Strangely VLC does report other values. I wonder how VLC - // gets it's values. - if (static_cast(pannings[i]) < 128 && volumes[i] > 0) - ++channels; - } - d->properties.setChannels(channels); - - // real length might be shorter because of skips and terminator - unsigned short realLength = 0; - for (unsigned short i = 0; i < length; ++i) { - READ_BYTE_AS(order); - if (order == 255) break; - if (order != 254) ++realLength; - } - d->properties.setLengthInPatterns(realLength); - - StringList comment; - // Note: I found files that have nil characters somewhere - // in the instrument/sample names and more characters - // afterwards. The spec does not mention such a case. - // Currently I just discard anything after a nil, but - // e.g. VLC seems to interpret a nil as a space. I - // don't know what is the proper behaviour. - for (unsigned short i = 0; i < instrumentCount; ++i) { - seek(192L + length + (static_cast(i) << 2)); - READ_U32L_AS(instrumentOffset); - seek(instrumentOffset); - - ByteVector instrumentMagic = readBlock(4); - READ_ASSERT(instrumentMagic == "IMPI"); - - READ_STRING_AS(dosFileName, 13); - - seek(15, Current); - - READ_STRING_AS(instrumentName, 26); - comment.append(instrumentName); - } - - for (unsigned short i = 0; i < sampleCount; ++i) { - seek(192L + length + (static_cast(instrumentCount) << 2) + (static_cast(i) << 2)); - READ_U32L_AS(sampleOffset); - - seek(sampleOffset); - - ByteVector sampleMagic = readBlock(4); - READ_ASSERT(sampleMagic == "IMPS"); - - READ_STRING_AS(dosFileName, 13); - READ_BYTE_AS(globalVolume); - READ_BYTE_AS(sampleFlags); - READ_BYTE_AS(sampleVolume); - READ_STRING_AS(sampleName, 26); - /* - READ_BYTE_AS(sampleCvt); - READ_BYTE_AS(samplePanning); - READ_U32L_AS(sampleLength); - READ_U32L_AS(loopStart); - READ_U32L_AS(loopStop); - READ_U32L_AS(c5speed); - READ_U32L_AS(sustainLoopStart); - READ_U32L_AS(sustainLoopEnd); - READ_U32L_AS(sampleDataOffset); - READ_BYTE_AS(vibratoSpeed); - READ_BYTE_AS(vibratoDepth); - READ_BYTE_AS(vibratoRate); - READ_BYTE_AS(vibratoType); - */ - - comment.append(sampleName); - } - - if (message.size() > 0) - comment.append(message); - d->tag.setComment(comment.toString("\n")); - d->tag.setTrackerName("Impulse Tracker"); - -} diff --git a/3rdparty/taglib/it/itfile.h b/3rdparty/taglib/it/itfile.h deleted file mode 100644 index b4eaac26..00000000 --- a/3rdparty/taglib/it/itfile.h +++ /dev/null @@ -1,91 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * - * MA 02110-1301 USA * - ***************************************************************************/ - -#ifndef TAGLIB_ITFILE_H -#define TAGLIB_ITFILE_H - -#include "tfile.h" -#include "audioproperties.h" -#include "taglib_export.h" -#include "modfilebase.h" -#include "modtag.h" -#include "itproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace IT { - -class TAGLIB_EXPORT File : public Mod::FileBase { - public: - /*! - * Constructs a Impulse Tracker file from \a file. - * - * \note In the current implementation, both \a readProperties and \a propertiesStyle are ignored. - * The audio properties are always read. - */ - explicit File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Constructs a Impulse Tracker file from \a stream. - * - * \note In the current implementation, both \a readProperties and \a propertiesStyle are ignored. - * The audio properties are always read. - * - * \note TagLib will *not* take ownership of the stream, the caller is responsible for deleting it after the File object. - */ - explicit File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Destroys this instance of the File. - */ - ~File() override; - - Mod::Tag *tag() const override; - - /*! - * Returns the IT::AudioProperties for this file. If no audio properties - * were read then this will return a null pointer. - */ - IT::AudioProperties *audioProperties() const override; - - /*! - * Save the file. - * This is the same as calling save(AllTags); - * - * \note Saving Impulse Tracker tags is not supported. - */ - bool save() override; - - private: - File(const File&); - File &operator=(const File&); - - void read(bool readProperties); - - class FilePrivate; - FilePrivate *d; -}; - -} // namespace IT -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/it/itproperties.cpp b/3rdparty/taglib/it/itproperties.cpp deleted file mode 100644 index 19636775..00000000 --- a/3rdparty/taglib/it/itproperties.cpp +++ /dev/null @@ -1,216 +0,0 @@ -/*************************************************************************** - copyright :(C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - - -#include "itproperties.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace IT; - -class IT::AudioProperties::AudioPropertiesPrivate { - public: - explicit AudioPropertiesPrivate() : channels(0), - lengthInPatterns(0), - instrumentCount(0), - sampleCount(0), - patternCount(0), - version(0), - compatibleVersion(0), - flags(0), - special(0), - globalVolume(0), - mixVolume(0), - tempo(0), - bpmSpeed(0), - panningSeparation(0), - pitchWheelDepth(0) { - } - - int channels; - unsigned short lengthInPatterns; - unsigned short instrumentCount; - unsigned short sampleCount; - unsigned short patternCount; - unsigned short version; - unsigned short compatibleVersion; - unsigned short flags; - unsigned short special; - unsigned char globalVolume; - unsigned char mixVolume; - unsigned char tempo; - unsigned char bpmSpeed; - unsigned char panningSeparation; - unsigned char pitchWheelDepth; -}; - -IT::AudioProperties::AudioProperties(AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::AudioProperties(), d(new AudioPropertiesPrivate()) {} - -IT::AudioProperties::~AudioProperties() { - delete d; -} - -int IT::AudioProperties::lengthInSeconds() const { - return 0; -} - -int IT::AudioProperties::lengthInMilliseconds() const { - return 0; -} - -int IT::AudioProperties::bitrate() const { - return 0; -} - -int IT::AudioProperties::sampleRate() const { - return 0; -} - -int IT::AudioProperties::channels() const { - return d->channels; -} - -unsigned short IT::AudioProperties::lengthInPatterns() const { - return d->lengthInPatterns; -} - -bool IT::AudioProperties::stereo() const { - return d->flags & Stereo; -} - -unsigned short IT::AudioProperties::instrumentCount() const { - return d->instrumentCount; -} - -unsigned short IT::AudioProperties::sampleCount() const { - return d->sampleCount; -} - -unsigned short IT::AudioProperties::patternCount() const { - return d->patternCount; -} - -unsigned short IT::AudioProperties::version() const { - return d->version; -} - -unsigned short IT::AudioProperties::compatibleVersion() const { - return d->compatibleVersion; -} - -unsigned short IT::AudioProperties::flags() const { - return d->flags; -} - -unsigned short IT::AudioProperties::special() const { - return d->special; -} - -unsigned char IT::AudioProperties::globalVolume() const { - return d->globalVolume; -} - -unsigned char IT::AudioProperties::mixVolume() const { - return d->mixVolume; -} - -unsigned char IT::AudioProperties::tempo() const { - return d->tempo; -} - -unsigned char IT::AudioProperties::bpmSpeed() const { - return d->bpmSpeed; -} - -unsigned char IT::AudioProperties::panningSeparation() const { - return d->panningSeparation; -} - -unsigned char IT::AudioProperties::pitchWheelDepth() const { - return d->pitchWheelDepth; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void IT::AudioProperties::setChannels(int channels) { - d->channels = channels; -} - -void IT::AudioProperties::setLengthInPatterns(unsigned short lengthInPatterns) { - d->lengthInPatterns = lengthInPatterns; -} - -void IT::AudioProperties::setInstrumentCount(unsigned short instrumentCount) { - d->instrumentCount = instrumentCount; -} - -void IT::AudioProperties::setSampleCount(unsigned short sampleCount) { - d->sampleCount = sampleCount; -} - -void IT::AudioProperties::setPatternCount(unsigned short patternCount) { - d->patternCount = patternCount; -} - -void IT::AudioProperties::setFlags(unsigned short flags) { - d->flags = flags; -} - -void IT::AudioProperties::setSpecial(unsigned short special) { - d->special = special; -} - -void IT::AudioProperties::setCompatibleVersion(unsigned short compatibleVersion) { - d->compatibleVersion = compatibleVersion; -} - -void IT::AudioProperties::setVersion(unsigned short version) { - d->version = version; -} - -void IT::AudioProperties::setGlobalVolume(unsigned char globalVolume) { - d->globalVolume = globalVolume; -} - -void IT::AudioProperties::setMixVolume(unsigned char mixVolume) { - d->mixVolume = mixVolume; -} - -void IT::AudioProperties::setTempo(unsigned char tempo) { - d->tempo = tempo; -} - -void IT::AudioProperties::setBpmSpeed(unsigned char bpmSpeed) { - d->bpmSpeed = bpmSpeed; -} - -void IT::AudioProperties::setPanningSeparation(unsigned char panningSeparation) { - d->panningSeparation = panningSeparation; -} - -void IT::AudioProperties::setPitchWheelDepth(unsigned char pitchWheelDepth) { - d->pitchWheelDepth = pitchWheelDepth; -} diff --git a/3rdparty/taglib/it/itproperties.h b/3rdparty/taglib/it/itproperties.h deleted file mode 100644 index 9b8044de..00000000 --- a/3rdparty/taglib/it/itproperties.h +++ /dev/null @@ -1,107 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_ITPROPERTIES_H -#define TAGLIB_ITPROPERTIES_H - -#include "taglib.h" -#include "audioproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace IT { -class TAGLIB_EXPORT AudioProperties : public Strawberry_TagLib::TagLib::AudioProperties { - friend class File; - - public: - /*! Flag bits. */ - enum { - Stereo = 1, - Vol0MixOptimizations = 2, - UseInstruments = 4, - LinearSlides = 8, - OldEffects = 16, - LinkEffects = 32, - UseMidiPitchController = 64, - RequestEmbeddedMidiConf = 128 - }; - - /*! Special bits. */ - enum { - MessageAttached = 1, - MidiConfEmbedded = 8 - }; - - explicit AudioProperties(AudioProperties::ReadStyle); - ~AudioProperties() override; - - int lengthInSeconds() const override; - int lengthInMilliseconds() const override; - int bitrate() const override; - int sampleRate() const override; - int channels() const override; - - unsigned short lengthInPatterns() const; - bool stereo() const; - unsigned short instrumentCount() const; - unsigned short sampleCount() const; - unsigned short patternCount() const; - unsigned short version() const; - unsigned short compatibleVersion() const; - unsigned short flags() const; - unsigned short special() const; - unsigned char globalVolume() const; - unsigned char mixVolume() const; - unsigned char tempo() const; - unsigned char bpmSpeed() const; - unsigned char panningSeparation() const; - unsigned char pitchWheelDepth() const; - - private: - void setChannels(int channels); - void setLengthInPatterns(unsigned short lengthInPatterns); - void setInstrumentCount(unsigned short instrumentCount); - void setSampleCount(unsigned short sampleCount); - void setPatternCount(unsigned short patternCount); - void setVersion(unsigned short version); - void setCompatibleVersion(unsigned short compatibleVersion); - void setFlags(unsigned short flags); - void setSpecial(unsigned short special); - void setGlobalVolume(unsigned char globalVolume); - void setMixVolume(unsigned char mixVolume); - void setTempo(unsigned char tempo); - void setBpmSpeed(unsigned char bpmSpeed); - void setPanningSeparation(unsigned char panningSeparation); - void setPitchWheelDepth(unsigned char pitchWheelDepth); - - private: - class AudioPropertiesPrivate; - AudioPropertiesPrivate *d; -}; -} // namespace IT -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mod/modfile.cpp b/3rdparty/taglib/mod/modfile.cpp deleted file mode 100644 index e02e459f..00000000 --- a/3rdparty/taglib/mod/modfile.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - - -#include "modfile.h" -#include "tstringlist.h" -#include "tdebug.h" -#include "modfileprivate.h" -#include "tpropertymap.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace Mod; - -class Mod::File::FilePrivate { - public: - explicit FilePrivate(AudioProperties::ReadStyle propertiesStyle) : properties(propertiesStyle) {} - - Mod::Tag tag; - Mod::AudioProperties properties; -}; - -Mod::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(file), d(new FilePrivate(propertiesStyle)) { - - if (isOpen()) - read(readProperties); - -} - -Mod::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(stream), d(new FilePrivate(propertiesStyle)) { - - if (isOpen()) - read(readProperties); - -} - -Mod::File::~File() { - delete d; -} - -Mod::Tag *Mod::File::tag() const { - return &d->tag; -} - -Mod::AudioProperties *Mod::File::audioProperties() const { - return &d->properties; -} - -bool Mod::File::save() { - - if (readOnly()) { - debug("Mod::File::save() - Cannot save to a read only file."); - return false; - } - seek(0); - writeString(d->tag.title(), 20); - StringList lines = d->tag.comment().split("\n"); - size_t n = std::min(lines.size(), d->properties.instrumentCount()); - for (size_t i = 0; i < n; ++i) { - writeString(lines[i], 22); - seek(8, Current); - } - - for (size_t i = n; i < d->properties.instrumentCount(); ++i) { - writeString(String(), 22); - seek(8, Current); - } - return true; - -} - -void Mod::File::read(bool) { - - if (!isOpen()) - return; - - seek(1080); - ByteVector modId = readBlock(4); - READ_ASSERT(modId.size() == 4); - - int channels = 4; - unsigned int instruments = 31; - if (modId == "M.K." || modId == "M!K!" || modId == "M&K!" || modId == "N.T.") { - d->tag.setTrackerName("ProTracker"); - channels = 4; - } - else if (modId.startsWith("FLT") || modId.startsWith("TDZ")) { - d->tag.setTrackerName("StarTrekker"); - char digit = modId[3]; - READ_ASSERT(digit >= '0' && digit <= '9'); - channels = digit - '0'; - } - else if (modId.endsWith("CHN")) { - d->tag.setTrackerName("StarTrekker"); - char digit = modId[0]; - READ_ASSERT(digit >= '0' && digit <= '9'); - channels = digit - '0'; - } - else if (modId == "CD81" || modId == "OKTA") { - d->tag.setTrackerName("Atari Oktalyzer"); - channels = 8; - } - else if (modId.endsWith("CH") || modId.endsWith("CN")) { - d->tag.setTrackerName("TakeTracker"); - char digit = modId[0]; - READ_ASSERT(digit >= '0' && digit <= '9'); - channels = (digit - '0') * 10; - digit = modId[1]; - READ_ASSERT(digit >= '0' && digit <= '9'); - channels += digit - '0'; - } - else { - // Not sure if this is correct. I'd need a file - // created with NoiseTracker to check this. - d->tag.setTrackerName("NoiseTracker"); // probably - channels = 4; - instruments = 15; - } - d->properties.setChannels(channels); - d->properties.setInstrumentCount(instruments); - - seek(0); - READ_STRING(d->tag.setTitle, 20); - - StringList comment; - for (unsigned int i = 0; i < instruments; ++i) { - READ_STRING_AS(instrumentName, 22); - // value in words, * 2 (<< 1) for bytes: - READ_U16B_AS(sampleLength); - - READ_BYTE_AS(fineTuneByte); - int fineTune = fineTuneByte & 0xF; - // > 7 means negative value - if (fineTune > 7) fineTune -= 16; - - READ_BYTE_AS(volume); - if (volume > 64) volume = 64; - // volume in decibels: 20 * log10(volume / 64) - - // value in words, * 2 (<< 1) for bytes: - READ_U16B_AS(repeatStart); - // value in words, * 2 (<< 1) for bytes: - READ_U16B_AS(repatLength); - - comment.append(instrumentName); - } - - READ_BYTE(d->properties.setLengthInPatterns); - - d->tag.setComment(comment.toString("\n")); - -} diff --git a/3rdparty/taglib/mod/modfile.h b/3rdparty/taglib/mod/modfile.h deleted file mode 100644 index 0d55d278..00000000 --- a/3rdparty/taglib/mod/modfile.h +++ /dev/null @@ -1,95 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_MODFILE_H -#define TAGLIB_MODFILE_H - -#include "tfile.h" -#include "audioproperties.h" -#include "taglib_export.h" -#include "modfilebase.h" -#include "modtag.h" -#include "modproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace Mod { - -class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::Mod::FileBase { - public: - /*! - * Constructs a Protracker file from \a file. - * - * \note In the current implementation, both \a readProperties and \a propertiesStyle are ignored. - * The audio properties are always read. - */ - explicit File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Constructs a Protracker file from \a stream. - * - * \note In the current implementation, both \a readProperties and \a propertiesStyle are ignored. - * The audio properties are always read. - * - * \note TagLib will *not* take ownership of the stream, the caller is - * responsible for deleting it after the File object. - */ - explicit File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Destroys this instance of the File. - */ - ~File() override; - - Mod::Tag *tag() const override; - - /*! - * Returns the Mod::AudioProperties for this file. If no audio properties were read then this will return a null pointer. - */ - Mod::AudioProperties *audioProperties() const override; - - /*! - * Save the file. - * This is the same as calling save(AllTags); - * - * \note Saving Protracker tags is not supported. - */ - bool save() override; - - private: - File(const File &); - File &operator=(const File &); - - void read(bool readProperties); - - class FilePrivate; - FilePrivate *d; -}; - -} // namespace Mod -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mod/modfilebase.cpp b/3rdparty/taglib/mod/modfilebase.cpp deleted file mode 100644 index 4643fb8f..00000000 --- a/3rdparty/taglib/mod/modfilebase.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - - -#include "tdebug.h" -#include "modfilebase.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace Mod; - -Mod::FileBase::FileBase(FileName file) : Strawberry_TagLib::TagLib::File(file) {} - -Mod::FileBase::FileBase(IOStream *stream) : Strawberry_TagLib::TagLib::File(stream) {} - -void Mod::FileBase::writeString(const String &s, unsigned int size, char padding) { - ByteVector data(s.data(String::Latin1)); - data.resize(size, padding); - writeBlock(data); -} - -bool Mod::FileBase::readString(String &s, unsigned int size) { - - ByteVector data(readBlock(size)); - if (data.size() < size) return false; - const size_t index = data.find(static_cast(0)); - if (index != ByteVector::npos()) { - data.resize(index); - } - data.replace('\xff', ' '); - - s = data; - return true; - -} - -void Mod::FileBase::writeByte(unsigned char _byte) { - ByteVector data(1, _byte); - writeBlock(data); -} - -void Mod::FileBase::writeU16L(unsigned short number) { - writeBlock(ByteVector::fromUInt16LE(number)); -} - -void Mod::FileBase::writeU32L(unsigned int number) { - writeBlock(ByteVector::fromUInt32LE(number)); -} - -void Mod::FileBase::writeU16B(unsigned short number) { - writeBlock(ByteVector::fromUInt16BE(number)); -} - -void Mod::FileBase::writeU32B(unsigned int number) { - writeBlock(ByteVector::fromUInt32BE(number)); -} - -bool Mod::FileBase::readByte(unsigned char &_byte) { - - ByteVector data(readBlock(1)); - if (data.size() < 1) return false; - _byte = data[0]; - return true; - -} - -bool Mod::FileBase::readU16L(unsigned short &number) { - - ByteVector data(readBlock(2)); - if (data.size() < 2) return false; - number = data.toUInt16LE(0); - return true; - -} - -bool Mod::FileBase::readU32L(unsigned int &number) { - - ByteVector data(readBlock(4)); - if (data.size() < 4) return false; - number = data.toUInt32LE(0); - return true; - -} - -bool Mod::FileBase::readU16B(unsigned short &number) { - - ByteVector data(readBlock(2)); - if (data.size() < 2) return false; - number = data.toUInt16BE(0); - return true; - -} - -bool Mod::FileBase::readU32B(unsigned int &number) { - - ByteVector data(readBlock(4)); - if (data.size() < 4) return false; - number = data.toUInt32BE(0); - return true; - -} diff --git a/3rdparty/taglib/mod/modfilebase.h b/3rdparty/taglib/mod/modfilebase.h deleted file mode 100644 index b1e7512c..00000000 --- a/3rdparty/taglib/mod/modfilebase.h +++ /dev/null @@ -1,65 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_MODFILEBASE_H -#define TAGLIB_MODFILEBASE_H - -#include "taglib.h" -#include "tfile.h" -#include "tstring.h" -#include "tlist.h" -#include "taglib_export.h" - -#include - -namespace Strawberry_TagLib { -namespace TagLib { -namespace Mod { - -class TAGLIB_EXPORT FileBase : public Strawberry_TagLib::TagLib::File { - protected: - explicit FileBase(FileName file); - explicit FileBase(IOStream *stream); - - void writeString(const String &s, unsigned int size, char padding = 0); - void writeByte(unsigned char byte); - void writeU16L(unsigned short number); - void writeU32L(unsigned int number); - void writeU16B(unsigned short number); - void writeU32B(unsigned int number); - - bool readString(String &s, unsigned int size); - bool readByte(unsigned char &_byte); - bool readU16L(unsigned short &number); - bool readU32L(unsigned int &number); - bool readU16B(unsigned short &number); - bool readU32B(unsigned int &number); -}; - -} // namespace Mod -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mod/modfileprivate.h b/3rdparty/taglib/mod/modfileprivate.h deleted file mode 100644 index 459ddf8f..00000000 --- a/3rdparty/taglib/mod/modfileprivate.h +++ /dev/null @@ -1,66 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * - * MA 02110-1301 USA * - ***************************************************************************/ - -#ifndef TAGLIB_MODFILEPRIVATE_H -#define TAGLIB_MODFILEPRIVATE_H - -// some helper-macros only used internally by (s3m|it|xm)file.cpp -#define READ_ASSERT(cond) \ - if (!(cond)) { \ - setValid(false); \ - return; \ - } - -#define READ(setter, type, read) \ - { \ - type number; \ - READ_ASSERT(read(number)); \ - setter(number); \ - } - -#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_U16B(setter) READ(setter, unsigned short, readU16B) -#define READ_U32B(setter) READ(setter, unsigned int, readU32B) - -#define READ_STRING(setter, size) \ - { \ - String s; \ - READ_ASSERT(readString(s, size)); \ - setter(s); \ - } - -#define READ_AS(type, name, read) \ - type name = 0; \ - READ_ASSERT(read(name)); - -#define READ_BYTE_AS(name) READ_AS(unsigned char, name, readByte) -#define READ_U16L_AS(name) READ_AS(unsigned short, name, readU16L) -#define READ_U32L_AS(name) READ_AS(unsigned int, 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_STRING_AS(name, size) \ - String name; \ - READ_ASSERT(readString(name, size)); - -#endif diff --git a/3rdparty/taglib/mod/modproperties.cpp b/3rdparty/taglib/mod/modproperties.cpp deleted file mode 100644 index 24caf261..00000000 --- a/3rdparty/taglib/mod/modproperties.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - - -#include "modproperties.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace Mod; - -class Mod::AudioProperties::AudioPropertiesPrivate { - public: - explicit AudioPropertiesPrivate() : channels(0), instrumentCount(0), lengthInPatterns(0) {} - - int channels; - unsigned int instrumentCount; - unsigned char lengthInPatterns; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -Mod::AudioProperties::AudioProperties(AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::AudioProperties(), d(new AudioPropertiesPrivate()) { -} - -Mod::AudioProperties::~AudioProperties() { - delete d; -} - -int Mod::AudioProperties::lengthInSeconds() const { - return 0; -} - -int Mod::AudioProperties::lengthInMilliseconds() const { - return 0; -} - -int Mod::AudioProperties::bitrate() const { - return 0; -} - -int Mod::AudioProperties::sampleRate() const { - return 0; -} - -int Mod::AudioProperties::channels() const { - return d->channels; -} - -unsigned int Mod::AudioProperties::instrumentCount() const { - return d->instrumentCount; -} - -unsigned char Mod::AudioProperties::lengthInPatterns() const { - return d->lengthInPatterns; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void Mod::AudioProperties::setChannels(int channels) { - d->channels = channels; -} - -void Mod::AudioProperties::setInstrumentCount(unsigned int instrumentCount) { - d->instrumentCount = instrumentCount; -} - -void Mod::AudioProperties::setLengthInPatterns(unsigned char lengthInPatterns) { - d->lengthInPatterns = lengthInPatterns; -} diff --git a/3rdparty/taglib/mod/modproperties.h b/3rdparty/taglib/mod/modproperties.h deleted file mode 100644 index 91ca355b..00000000 --- a/3rdparty/taglib/mod/modproperties.h +++ /dev/null @@ -1,66 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_MODPROPERTIES_H -#define TAGLIB_MODPROPERTIES_H - -#include "taglib.h" -#include "audioproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace Mod { - -class TAGLIB_EXPORT AudioProperties : public Strawberry_TagLib::TagLib::AudioProperties { - friend class File; - - public: - explicit AudioProperties(AudioProperties::ReadStyle propertiesStyle); - ~AudioProperties() override; - - int lengthInSeconds() const override; - int lengthInMilliseconds() const override; - int bitrate() const override; - int sampleRate() const override; - int channels() const override; - - unsigned int instrumentCount() const; - unsigned char lengthInPatterns() const; - - private: - void setChannels(int channels); - - void setInstrumentCount(unsigned int instrumentCount); - void setLengthInPatterns(unsigned char lengthInPatterns); - - class AudioPropertiesPrivate; - AudioPropertiesPrivate *d; -}; - -} // namespace Mod -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mod/modtag.cpp b/3rdparty/taglib/mod/modtag.cpp deleted file mode 100644 index 0f7c58c3..00000000 --- a/3rdparty/taglib/mod/modtag.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - - -#include "modtag.h" -#include "tstringlist.h" -#include "tpropertymap.h" -#include "tpicturemap.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace Mod; - -class Mod::Tag::TagPrivate { - public: - explicit TagPrivate() {} - - String title; - String comment; - String trackerName; -}; - -Mod::Tag::Tag() : d(new TagPrivate()) {} - -Mod::Tag::~Tag() { - delete d; -} - -String Mod::Tag::title() const { - return d->title; -} - -String Mod::Tag::artist() const { - return String(); -} - -String Mod::Tag::album() const { - return String(); -} - -String Mod::Tag::comment() const { - return d->comment; -} - -String Mod::Tag::genre() const { - return String(); -} - -unsigned int Mod::Tag::year() const { - return 0; -} - -unsigned int Mod::Tag::track() const { - return 0; -} - -Strawberry_TagLib::TagLib::PictureMap Mod::Tag::pictures() const { - return PictureMap(); -} - -String Mod::Tag::trackerName() const { - return d->trackerName; -} - -void Mod::Tag::setTitle(const String &title) { - d->title = title; -} - -void Mod::Tag::setArtist(const String &) { -} - -void Mod::Tag::setAlbum(const String &) { -} - -void Mod::Tag::setComment(const String &comment) { - d->comment = comment; -} - -void Mod::Tag::setGenre(const String &) { -} - -void Mod::Tag::setYear(unsigned int) { -} - -void Mod::Tag::setTrack(unsigned int) { -} - -void Mod::Tag::setPictures(const PictureMap&) {} - -void Mod::Tag::setTrackerName(const String &trackerName) { - d->trackerName = trackerName; -} - -PropertyMap Mod::Tag::properties() const { - - PropertyMap properties; - properties["TITLE"] = d->title; - properties["COMMENT"] = d->comment; - if (!(d->trackerName.isEmpty())) - properties["TRACKERNAME"] = d->trackerName; - return properties; - -} - -PropertyMap Mod::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.clear(); - - if (properties.contains("COMMENT")) { - d->comment = properties["COMMENT"].front(); - oneValueSet.append("COMMENT"); - } - else - d->comment.clear(); - - if (properties.contains("TRACKERNAME")) { - d->trackerName = properties["TRACKERNAME"].front(); - oneValueSet.append("TRACKERNAME"); - } - else - d->trackerName.clear(); - - // for each tag that has been set above, remove the first entry in the corresponding - // value list. The others will be returned as unsupported by this format. - for (StringList::ConstIterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) { - if (properties[*it].size() == 1) - properties.erase(*it); - else - properties[*it].erase(properties[*it].begin()); - } - return properties; - -} diff --git a/3rdparty/taglib/mod/modtag.h b/3rdparty/taglib/mod/modtag.h deleted file mode 100644 index 067c60af..00000000 --- a/3rdparty/taglib/mod/modtag.h +++ /dev/null @@ -1,186 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_MODTAG_H -#define TAGLIB_MODTAG_H - -#include "tag.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace Mod { - -/*! - * Tags for module files (Mod, S3M, IT, XM). - * - * Note that only the \a title is supported as such by most module file formats. - * Except for XM files the \a trackerName is derived from the file format or the flavour of the file format. - * For XM files it is stored in the file. - * - * The \a comment tag is not strictly supported by module files, - * but it is common practice to abuse instrument/sample/pattern names as multiline comments. - * TagLib does so as well. - * - */ -class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag { - public: - explicit Tag(); - ~Tag() override; - - /*! - * Returns the track name; if no track name is present in the tag String::null will be returned. - */ - String title() const override; - - /*! - * Not supported by module files. Therefore always returns String::null. - */ - String artist() const override; - - /*! - * Not supported by module files. Therefore always returns String::null. - */ - String album() const override; - - /*! - * Returns the track comment derived from the instrument/sample/pattern - * names; if no comment is present in the tag String::null will be returned. - */ - String comment() const override; - - /*! - * Not supported by module files. Therefore always returns String::null. - */ - String genre() const override; - - /*! - * Not supported by module files. Therefore always returns 0. - */ - unsigned int year() const override; - - /*! - * Not supported by module files. Therefore always returns 0. - */ - unsigned int track() const override; - - PictureMap pictures() const override; - - /*! - * 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 - * (Mod, S3M, IT) this is derived from the file type or the flavour of the file type. - * Therefore only XM files might have an empty (String::null) tracker name. - */ - String trackerName() const; - - /*! - * Sets the title to \a title. - * If \a title is String::null then this value will be cleared. - * - * The length limits per file type are (1 character = 1 byte): - * Mod 20 characters, S3M 27 characters, IT 25 characters and XM 20 characters. - */ - void setTitle(const String &title) override; - - /*! - * Not supported by module files and therefore ignored. - */ - void setArtist(const String &artist) override; - - /*! - * Not supported by module files and therefore ignored. - */ - void setAlbum(const String &album) override; - - /*! - * Sets the comment to \a comment. - * If \a comment is String::null then this value will be cleared. - * - * Note that module file formats don't actually support a comment tag. - * Instead the names of instruments/patterns/samples are abused as a multiline comment. - * Because of this the number of lines in a module file is fixed to the number of instruments/patterns/samples. - * - * Also note that the instrument/pattern/sample name length is limited an thus the line length in comments are limited. - * Too big comments will be truncated. - * - * The line length limits per file type are (1 character = 1 byte): - * Mod 22 characters, S3M 27 characters, IT 25 characters and XM 22 characters. - */ - void setComment(const String &comment) override; - - /*! - * Not supported by module files and therefore ignored. - */ - void setGenre(const String &genre) override; - - /*! - * Not supported by module files and therefore ignored. - */ - void setYear(unsigned int year) override; - - /*! - * Not supported by module files and therefore ignored. - */ - void setTrack(unsigned int track) override; - - void setPictures(const PictureMap &l) override; - - /*! - * Sets the tracker name to \a trackerName. - * If \a trackerName is String::null then this value will be cleared. - * - * Note that only XM files support this tag. - * Setting the tracker name for other module file formats will be ignored. - * - * The length of this tag is limited to 20 characters (1 character = 1 byte). - */ - void setTrackerName(const String &trackerName); - - /*! - * Implements the unified property interface -- export function. - * Since the module tag is very limited, the exported map is as well. - */ - PropertyMap properties() const override; - - /*! - * Implements the unified property interface -- import function. - * Because of the limitations of the module file tag, any tags besides COMMENT, TITLE and, if it is an XM file, TRACKERNAME, will be returned. - * Additionally, if the map contains tags with multiple values, all but the first will be contained in the returned map of unsupported properties. - */ - PropertyMap setProperties(const PropertyMap &) override; - - private: - Tag(const Tag&); - Tag &operator=(const Tag&); - - class TagPrivate; - TagPrivate *d; -}; - -} // namespace Mod -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mp4/mp4atom.cpp b/3rdparty/taglib/mp4/mp4atom.cpp deleted file mode 100644 index de2c70eb..00000000 --- a/3rdparty/taglib/mp4/mp4atom.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/************************************************************************** - copyright : (C) 2007 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "tdebug.h" -#include "tstring.h" -#include "mp4atom.h" - -using namespace Strawberry_TagLib::TagLib; - -const char *MP4::Atom::containers[11] = { "moov", "udta", "mdia", "meta", "ilst", "stbl", "minf", "moof", "traf", "trak", "stsd" }; - -MP4::Atom::Atom(File *file) { - - children.setAutoDelete(true); - - offset = file->tell(); - ByteVector header = file->readBlock(8); - if (header.size() != 8) { - // The atom header must be 8 bytes long, otherwise there is either - // trailing garbage or the file is truncated - debug("MP4: Couldn't read 8 bytes of data for atom header"); - length = 0; - file->seek(0, File::End); - return; - } - - length = header.toUInt32BE(0); - - if (length == 0) { - // The last atom which extends to the end of the file. - length = file->length() - offset; - } - else if (length == 1) { - // The atom has a 64-bit length. - length = file->readBlock(8).toInt64BE(0); - } - - if (length < 8) { - debug("MP4: Invalid atom size"); - length = 0; - file->seek(0, File::End); - return; - } - - name = header.mid(4, 4); - - for (int i = 0; i < numContainers; i++) { - if (name == containers[i]) { - if (name == "meta") { - file->seek(4, File::Current); - } - else if (name == "stsd") { - file->seek(8, File::Current); - } - while (file->tell() < offset + length) { - MP4::Atom *child = new MP4::Atom(file); - children.append(child); - if (child->length == 0) - return; - } - return; - } - } - - file->seek(offset + length); - -} - -MP4::Atom::~Atom() {} - -MP4::Atom *MP4::Atom::find(const char *name1, const char *name2, const char *name3, const char *name4) { - - if (!name1) { - return this; - } - for (AtomList::ConstIterator it = children.begin(); it != children.end(); ++it) { - if ((*it)->name == name1) { - return (*it)->find(name2, name3, name4); - } - } - return nullptr; - -} - -MP4::AtomList MP4::Atom::findall(const char *_name, bool recursive) { - - MP4::AtomList result; - for (AtomList::ConstIterator it = children.begin(); it != children.end(); ++it) { - if ((*it)->name == _name) { - result.append(*it); - } - if (recursive) { - result.append((*it)->findall(_name, recursive)); - } - } - return result; - -} - -bool MP4::Atom::path(MP4::AtomList &path, const char *name1, const char *name2, const char *name3) { - - path.append(this); - if (!name1) { - return true; - } - for (AtomList::ConstIterator it = children.begin(); it != children.end(); ++it) { - if ((*it)->name == name1) { - return (*it)->path(path, name2, name3); - } - } - return false; - -} - -MP4::Atoms::Atoms(File *file) { - - atoms.setAutoDelete(true); - - file->seek(0, File::End); - long long end = file->tell(); - file->seek(0); - while (file->tell() + 8 <= end) { - MP4::Atom *atom = new MP4::Atom(file); - atoms.append(atom); - if (atom->length == 0) - break; - } - -} - -MP4::Atoms::~Atoms() {} - -MP4::Atom *MP4::Atoms::find(const char *name1, const char *name2, const char *name3, const char *name4) { - - for (AtomList::ConstIterator it = atoms.begin(); it != atoms.end(); ++it) { - if ((*it)->name == name1) { - return (*it)->find(name2, name3, name4); - } - } - return nullptr; - -} - -MP4::AtomList MP4::Atoms::path(const char *name1, const char *name2, const char *name3, const char *name4) { - - MP4::AtomList path; - for (AtomList::ConstIterator it = atoms.begin(); it != atoms.end(); ++it) { - if ((*it)->name == name1) { - if (!(*it)->path(path, name2, name3, name4)) { - path.clear(); - } - return path; - } - } - return path; - -} diff --git a/3rdparty/taglib/mp4/mp4atom.h b/3rdparty/taglib/mp4/mp4atom.h deleted file mode 100644 index 33624178..00000000 --- a/3rdparty/taglib/mp4/mp4atom.h +++ /dev/null @@ -1,111 +0,0 @@ -/************************************************************************** - copyright : (C) 2007,2011 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -// This file is not part of the public API! - -#ifndef DO_NOT_DOCUMENT - -# ifndef TAGLIB_MP4ATOM_H -# define TAGLIB_MP4ATOM_H - -# include "tfile.h" -# include "tlist.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace MP4 { - -class Atom; -typedef Strawberry_TagLib::TagLib::List AtomList; - -enum AtomDataType { - TypeImplicit = 0, // for use with tags for which no type needs to be indicated because only one type is allowed - TypeUTF8 = 1, // without any count or null terminator - TypeUTF16 = 2, // also known as UTF-16BE - TypeSJIS = 3, // deprecated unless it is needed for special Japanese characters - TypeHTML = 6, // the HTML file header specifies which HTML version - TypeXML = 7, // the XML header must identify the DTD or schemas - TypeUUID = 8, // also known as GUID; stored as 16 bytes in binary (valid as an ID) - TypeISRC = 9, // stored as UTF-8 text (valid as an ID) - TypeMI3P = 10, // stored as UTF-8 text (valid as an ID) - TypeGIF = 12, // (deprecated) a GIF image - TypeJPEG = 13, // a JPEG image - TypePNG = 14, // a PNG image - TypeURL = 15, // absolute, in UTF-8 characters - TypeDuration = 16, // in milliseconds, 32-bit integer - TypeDateTime = 17, // in UTC, counting seconds since midnight, January 1, 1904; 32 or 64-bits - TypeGenred = 18, // a list of enumerated values - TypeInteger = 21, // a signed big-endian integer with length one of { 1,2,3,4,8 } bytes - TypeRIAAPA = 24, // RIAA parental advisory; { -1=no, 1=yes, 0=unspecified }, 8-bit integer - TypeUPC = 25, // Universal Product Code, in text UTF-8 format (valid as an ID) - TypeBMP = 27, // Windows bitmap image - TypeUndefined = 255 // undefined -}; - -struct AtomData { - explicit AtomData(AtomDataType _type, ByteVector _data) : type(_type), locale(0), data(_data) {} - AtomDataType type; - int locale; - ByteVector data; -}; - -typedef Strawberry_TagLib::TagLib::List AtomDataList; - -class Atom { - public: - explicit Atom(File *file); - ~Atom(); - - Atom *find(const char *name1, const char *name2 = nullptr, const char *name3 = nullptr, const char *name4 = nullptr); - bool path(AtomList &path, const char *name1, const char *name2 = nullptr, const char *name3 = nullptr); - AtomList findall(const char *name, bool recursive = false); - long long offset; - long long length; - Strawberry_TagLib::TagLib::ByteVector name; - AtomList children; - - private: - static const int numContainers = 11; - static const char *containers[11]; -}; - -//! Root-level atoms -class Atoms { - public: - explicit Atoms(File *file); - ~Atoms(); - - Atom *find(const char *name1, const char *name2 = nullptr, const char *name3 = nullptr, const char *name4 = nullptr); - AtomList path(const char *name1, const char *name2 = nullptr, const char *name3 = nullptr, const char *name4 = nullptr); - AtomList atoms; -}; - -} // namespace MP4 -} // namespace TagLib -} // namespace Strawberry_TagLib - -# endif - -#endif diff --git a/3rdparty/taglib/mp4/mp4coverart.cpp b/3rdparty/taglib/mp4/mp4coverart.cpp deleted file mode 100644 index 4d9e4c85..00000000 --- a/3rdparty/taglib/mp4/mp4coverart.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/************************************************************************** - copyright : (C) 2009 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "taglib.h" -#include "tdebug.h" -#include "mp4coverart.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { - -struct CoverArtData { - MP4::CoverArt::Format format; - ByteVector data; -}; - -} // namespace - -class MP4::CoverArt::CoverArtPrivate { - public: - explicit CoverArtPrivate(Format f, const ByteVector &v) : data(new CoverArtData()) { - data->format = f; - data->data = v; - } - - std::shared_ptr data; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -MP4::CoverArt::CoverArt(Format format, const ByteVector &data) : d(new CoverArtPrivate(format, data)) {} - -MP4::CoverArt::CoverArt(const CoverArt &item) : d(new CoverArtPrivate(*item.d)) {} - -MP4::CoverArt & -MP4::CoverArt::operator=(const CoverArt &item) { - CoverArt(item).swap(*this); - return *this; -} - -void MP4::CoverArt::swap(CoverArt &item) { - using std::swap; - - swap(d, item.d); -} - -MP4::CoverArt::~CoverArt() { - delete d; -} - -MP4::CoverArt::Format -MP4::CoverArt::format() const { - return d->data->format; -} - -ByteVector -MP4::CoverArt::data() const { - return d->data->data; -} diff --git a/3rdparty/taglib/mp4/mp4coverart.h b/3rdparty/taglib/mp4/mp4coverart.h deleted file mode 100644 index d15765d0..00000000 --- a/3rdparty/taglib/mp4/mp4coverart.h +++ /dev/null @@ -1,83 +0,0 @@ -/************************************************************************** - copyright : (C) 2009 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_MP4COVERART_H -#define TAGLIB_MP4COVERART_H - -#include "tlist.h" -#include "tbytevector.h" -#include "taglib_export.h" -#include "mp4atom.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace MP4 { - -class TAGLIB_EXPORT CoverArt { - public: - /*! - * This describes the image type. - */ - enum Format { - JPEG = TypeJPEG, - PNG = TypePNG, - BMP = TypeBMP, - GIF = TypeGIF, - Unknown = TypeImplicit, - }; - - explicit CoverArt(Format format, const ByteVector &data); - ~CoverArt(); - - CoverArt(const CoverArt &item); - - /*! - * Copies the contents of \a item into this CoverArt. - */ - CoverArt &operator=(const CoverArt &item); - - /*! - * Exchanges the content of the CoverArt by the content of \a item. - */ - void swap(CoverArt &item); - - //! Format of the image - Format format() const; - - //! The image data - ByteVector data() const; - - private: - class CoverArtPrivate; - CoverArtPrivate *d; -}; - -typedef List CoverArtList; - -} // namespace MP4 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mp4/mp4file.cpp b/3rdparty/taglib/mp4/mp4file.cpp deleted file mode 100644 index 7cd45da5..00000000 --- a/3rdparty/taglib/mp4/mp4file.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/************************************************************************** - copyright : (C) 2007 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tdebug.h" -#include "tstring.h" -#include "tpropertymap.h" -#include "tagutils.h" - -#include "mp4atom.h" -#include "mp4tag.h" -#include "mp4file.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { -bool checkValid(const MP4::AtomList &list) { - - for (MP4::AtomList::ConstIterator it = list.begin(); it != list.end(); ++it) { - - if ((*it)->length == 0) - return false; - - if (!checkValid((*it)->children)) - return false; - } - - return true; - -} -} // namespace - -class MP4::File::FilePrivate { - public: - explicit FilePrivate() : tag(nullptr), atoms(nullptr), properties(nullptr) {} - - ~FilePrivate() { - delete atoms; - delete tag; - delete properties; - } - - MP4::Tag *tag; - MP4::Atoms *atoms; - MP4::AudioProperties *properties; -}; - -//////////////////////////////////////////////////////////////////////////////// -// static members -//////////////////////////////////////////////////////////////////////////////// - -bool MP4::File::isSupported(IOStream *stream) { - - // An MP4 file has to have an "ftyp" box first. - - const ByteVector id = Utils::readHeader(stream, 8, false); - return id.containsAt("ftyp", 4); - -} - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::File(file), d(new FilePrivate()) { - - if (isOpen()) - read(readProperties); - -} - -MP4::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::File(stream), d(new FilePrivate()) { - - if (isOpen()) - read(readProperties); - -} - -MP4::File::~File() { - delete d; -} - -MP4::Tag * -MP4::File::tag() const { - return d->tag; -} - -MP4::AudioProperties * -MP4::File::audioProperties() const { - return d->properties; -} - -void MP4::File::read(bool readProperties) { - - if (!isValid()) - return; - - d->atoms = new Atoms(this); - if (!checkValid(d->atoms->atoms)) { - setValid(false); - return; - } - - // must have a moov atom, otherwise consider it invalid - if (!d->atoms->find("moov")) { - setValid(false); - return; - } - - d->tag = new Tag(this, d->atoms); - if (readProperties) { - d->properties = new AudioProperties(this, d->atoms); - } - -} - -bool MP4::File::save() { - - if (readOnly()) { - debug("MP4::File::save() -- File is read only."); - return false; - } - - if (!isValid()) { - debug("MP4::File::save() -- Trying to save invalid file."); - return false; - } - - return d->tag->save(); - -} - -bool MP4::File::hasMP4Tag() const { - return (d->atoms->find("moov", "udta", "meta", "ilst") != nullptr); -} diff --git a/3rdparty/taglib/mp4/mp4file.h b/3rdparty/taglib/mp4/mp4file.h deleted file mode 100644 index a02e34cf..00000000 --- a/3rdparty/taglib/mp4/mp4file.h +++ /dev/null @@ -1,120 +0,0 @@ -/************************************************************************** - copyright : (C) 2007 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_MP4FILE_H -#define TAGLIB_MP4FILE_H - -#include "tag.h" -#include "tfile.h" -#include "taglib_export.h" -#include "mp4properties.h" -#include "mp4tag.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -//! An implementation of MP4 (AAC, ALAC, ...) metadata -namespace MP4 { - -class Atoms; - -/*! - * This implements and provides an interface for MP4 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 MP4 files. - */ -class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File { - public: - /*! - * Constructs an MP4 file from \a file. - * If \a readProperties is true the file's audio properties will also be read. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(FileName file, bool readProperties = true, AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average); - - /*! - * Constructs an MP4 file from \a stream. - * If \a readProperties is true the file's audio properties will also be read. - * - * \note TagLib will *not* take ownership of the stream, the caller is - * responsible for deleting it after the File object. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average); - - /*! - * Destroys this instance of the File. - */ - ~File() override; - - /*! - * Returns a pointer to the MP4 tag of the file. - * - * MP4::Tag implements the tag interface, so this serves as the reimplementation of TagLib::File::tag(). - * - * \note The Tag is still owned by the MP4::File and should not be deleted by the user. - * It will be deleted when the file (object) is destroyed. - */ - Tag *tag() const override; - - /*! - * Returns the MP4 audio properties for this file. - */ - AudioProperties *audioProperties() const override; - - /*! - * Save the file. - * - * This returns true if the save was successful. - */ - bool save() override; - - /*! - * Returns whether or not the file on disk actually has an MP4 tag, or the file has a Metadata Item List (ilst) atom. - */ - bool hasMP4Tag() const; - - /*! - * Returns whether or not the given \a stream can be opened as an ASF file. - * - * \note This method is designed to do a quick check. The result may not necessarily be correct. - */ - static bool isSupported(IOStream *stream); - - private: - void read(bool readProperties); - - class FilePrivate; - FilePrivate *d; -}; - -} // namespace MP4 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mp4/mp4item.cpp b/3rdparty/taglib/mp4/mp4item.cpp deleted file mode 100644 index 751cfb1b..00000000 --- a/3rdparty/taglib/mp4/mp4item.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/************************************************************************** - copyright : (C) 2007 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "taglib.h" -#include "tdebug.h" -#include "mp4item.h" -#include "tutils.h" - -using namespace Strawberry_TagLib::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; -}; -} // namespace - -class MP4::Item::ItemPrivate { - public: - explicit ItemPrivate() : data(new ItemData()) { - data->valid = true; - data->atomDataType = MP4::TypeUndefined; - data->type = MP4::Item::TypeUndefined_; - } - - std::shared_ptr data; -}; - -MP4::Item::Item() : d(new ItemPrivate()) { - d->data->valid = false; -} - -MP4::Item::Item(const Item &item) : d(new ItemPrivate(*item.d)) {} - -MP4::Item &MP4::Item::operator=(const Item &item) { - Item(item).swap(*this); - return *this; -} - -void MP4::Item::swap(Item &item) { - using std::swap; - - swap(d, item.d); -} - -MP4::Item::~Item() { - delete d; -} - -MP4::Item::Item(bool value) : d(new ItemPrivate()) { - d->data->m_bool = value; - d->data->type = TypeBool; -} - -MP4::Item::Item(int value) : d(new ItemPrivate()) { - d->data->m_int = value; - d->data->type = TypeInt; -} - -MP4::Item::Item(unsigned char value) : d(new ItemPrivate()) { - d->data->m_byte = value; - d->data->type = TypeByte; -} - -MP4::Item::Item(unsigned int value) : d(new ItemPrivate()) { - d->data->m_uint = value; - d->data->type = TypeUInt; -} - -MP4::Item::Item(long long value) : d(new ItemPrivate()) { - d->data->m_longlong = value; - d->data->type = TypeLongLong; -} - -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; -} - -MP4::Item::Item(const ByteVectorList &value) : d(new ItemPrivate()) { - d->data->m_byteVectorList = value; - d->data->type = TypeByteVectorList; -} - -MP4::Item::Item(const StringList &value) : d(new ItemPrivate()) { - d->data->m_stringList = value; - d->data->type = TypeStringList; -} - -MP4::Item::Item(const MP4::CoverArtList &value) : d(new ItemPrivate()) { - d->data->m_coverArtList = value; - d->data->type = TypeCoverArtList; -} - -void MP4::Item::setAtomDataType(MP4::AtomDataType type) { - d->data->atomDataType = type; -} - -MP4::AtomDataType MP4::Item::atomDataType() const { - return d->data->atomDataType; -} - -bool MP4::Item::toBool() const { - return d->data->m_bool; -} - -int MP4::Item::toInt() const { - return d->data->m_int; -} - -unsigned char MP4::Item::toByte() const { - return d->data->m_byte; -} - -unsigned int MP4::Item::toUInt() const { - return d->data->m_uint; -} - -long long MP4::Item::toLongLong() const { - return d->data->m_longlong; -} - -MP4::Item::IntPair MP4::Item::toIntPair() const { - return d->data->m_intPair; -} - -StringList MP4::Item::toStringList() const { - return d->data->m_stringList; -} - -ByteVectorList MP4::Item::toByteVectorList() const { - return d->data->m_byteVectorList; -} - -MP4::CoverArtList MP4::Item::toCoverArtList() const { - return d->data->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(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(d->data->m_coverArtList[i].data().size()))); - } - return desc.toString(", "); - case TypeUndefined_: - return "[unknown]"; - } - return String(); - -} diff --git a/3rdparty/taglib/mp4/mp4item.h b/3rdparty/taglib/mp4/mp4item.h deleted file mode 100644 index c2c580dd..00000000 --- a/3rdparty/taglib/mp4/mp4item.h +++ /dev/null @@ -1,109 +0,0 @@ -/************************************************************************** - copyright : (C) 2007 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_MP4ITEM_H -#define TAGLIB_MP4ITEM_H - -#include "tstringlist.h" -#include "mp4coverart.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace MP4 { - -class TAGLIB_EXPORT Item { - public: - struct IntPair { - int first, second; - }; - - enum ItemType { - TypeUndefined_ = 0, - TypeBool, - TypeInt, - TypeIntPair, - TypeByte, - TypeUInt, - TypeLongLong, - TypeStringList, - TypeByteVectorList, - TypeCoverArtList, - }; - - explicit Item(); - Item(const Item &item); - - /*! - * Copies the contents of \a item into this Item. - */ - Item &operator=(const Item &item); - - /*! - * Exchanges the content of the Item by the content of \a item. - */ - void swap(Item &item); - - ~Item(); - - Item(int value); - Item(unsigned char value); - Item(unsigned int value); - Item(long long value); - Item(bool value); - Item(int first, int second); - Item(const StringList &value); - Item(const ByteVectorList &value); - Item(const CoverArtList &value); - - void setAtomDataType(AtomDataType type); - AtomDataType atomDataType() const; - - int toInt() const; - unsigned char toByte() const; - unsigned int toUInt() const; - long long toLongLong() const; - bool toBool() const; - IntPair toIntPair() const; - StringList toStringList() const; - ByteVectorList toByteVectorList() const; - CoverArtList toCoverArtList() const; - - ItemType type() const; - - bool isValid() const; - - String toString() const; - - private: - class ItemPrivate; - ItemPrivate *d; -}; - -} // namespace MP4 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mp4/mp4properties.cpp b/3rdparty/taglib/mp4/mp4properties.cpp deleted file mode 100644 index 1a55eabc..00000000 --- a/3rdparty/taglib/mp4/mp4properties.cpp +++ /dev/null @@ -1,227 +0,0 @@ -/************************************************************************** - copyright : (C) 2007 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tdebug.h" -#include "tstring.h" -#include "mp4file.h" -#include "mp4atom.h" -#include "mp4properties.h" - -using namespace Strawberry_TagLib::TagLib; - -class MP4::AudioProperties::AudioPropertiesPrivate { - public: - explicit AudioPropertiesPrivate() : length(0), - bitrate(0), - sampleRate(0), - channels(0), - bitsPerSample(0), - encrypted(false), - codec(MP4::AudioProperties::Unknown) {} - - int length; - int bitrate; - int sampleRate; - int channels; - int bitsPerSample; - bool encrypted; - Codec codec; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -MP4::AudioProperties::AudioProperties(File *file, MP4::Atoms *atoms, ReadStyle) : Strawberry_TagLib::TagLib::AudioProperties(), d(new AudioPropertiesPrivate()) { - read(file, atoms); -} - -MP4::AudioProperties::~AudioProperties() { - delete d; -} - -int MP4::AudioProperties::channels() const { - return d->channels; -} - -int MP4::AudioProperties::sampleRate() const { - return d->sampleRate; -} - -int MP4::AudioProperties::lengthInSeconds() const { - return d->length / 1000; -} - -int MP4::AudioProperties::lengthInMilliseconds() const { - return d->length; -} - -int MP4::AudioProperties::bitrate() const { - return d->bitrate; -} - -int MP4::AudioProperties::bitsPerSample() const { - return d->bitsPerSample; -} - -bool MP4::AudioProperties::isEncrypted() const { - return d->encrypted; -} - -MP4::AudioProperties::Codec -MP4::AudioProperties::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(lengthInSeconds()) + " seconds"); - desc.append(String::number(bitrate()) + " kbps"); - return desc.toString(", "); - -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void MP4::AudioProperties::read(File *file, Atoms *atoms) { - - MP4::Atom *moov = atoms->find("moov"); - if (!moov) { - debug("MP4: Atom 'moov' not found"); - return; - } - - MP4::Atom *trak = nullptr; - ByteVector data; - - const MP4::AtomList trakList = moov->findall("trak"); - for (MP4::AtomList::ConstIterator it = trakList.begin(); it != trakList.end(); ++it) { - trak = *it; - MP4::Atom *hdlr = trak->find("mdia", "hdlr"); - if (!hdlr) { - debug("MP4: Atom 'trak.mdia.hdlr' not found"); - return; - } - file->seek(hdlr->offset); - data = file->readBlock(hdlr->length); - if (data.containsAt("soun", 16)) { - break; - } - trak = nullptr; - } - if (!trak) { - debug("MP4: No audio tracks"); - return; - } - - MP4::Atom *mdhd = trak->find("mdia", "mdhd"); - if (!mdhd) { - debug("MP4: Atom 'trak.mdia.mdhd' not found"); - return; - } - - file->seek(mdhd->offset); - data = file->readBlock(mdhd->length); - - const unsigned int version = data[8]; - long long unit; - long long length; - if (version == 1) { - if (data.size() < 36 + 8) { - debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected"); - return; - } - unit = data.toUInt32BE(28); - length = data.toInt64BE(32); - } - 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); - } - if (unit > 0 && length > 0) - d->length = static_cast(length * 1000.0 / unit + 0.5); - - MP4::Atom *atom = trak->find("mdia", "minf", "stbl", "stsd"); - if (!atom) { - return; - } - - file->seek(atom->offset); - 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); - if (data.containsAt("esds", 56) && data[64] == 0x03) { - unsigned int pos = 65; - if (data.containsAt("\x80\x80\x80", pos)) { - pos += 3; - } - pos += 4; - if (data[pos] == 0x04) { - pos += 1; - if (data.containsAt("\x80\x80\x80", pos)) { - pos += 3; - } - pos += 10; - d->bitrate = static_cast((data.toUInt32BE(pos) + 500) / 1000.0 + 0.5); - } - } - } - else if (data.containsAt("alac", 20)) { - if (atom->length == 88 && data.containsAt("alac", 56)) { - d->codec = ALAC; - d->bitsPerSample = data.at(69); - d->channels = data.at(73); - d->bitrate = static_cast(data.toUInt32BE(80) / 1000.0 + 0.5); - d->sampleRate = data.toUInt32BE(84); - } - } - - MP4::Atom *drms = atom->find("drms"); - if (drms) { - d->encrypted = true; - } - -} diff --git a/3rdparty/taglib/mp4/mp4properties.h b/3rdparty/taglib/mp4/mp4properties.h deleted file mode 100644 index 5f885ffc..00000000 --- a/3rdparty/taglib/mp4/mp4properties.h +++ /dev/null @@ -1,108 +0,0 @@ -/************************************************************************** - copyright : (C) 2007 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_MP4PROPERTIES_H -#define TAGLIB_MP4PROPERTIES_H - -#include "taglib_export.h" -#include "audioproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace MP4 { - -class Atoms; -class File; - -//! An implementation of MP4 audio properties -class TAGLIB_EXPORT AudioProperties : public Strawberry_TagLib::TagLib::AudioProperties { - public: - enum Codec { - Unknown = 0, - AAC, - ALAC - }; - - explicit AudioProperties(File *file, Atoms *atoms, ReadStyle style = Average); - ~AudioProperties() override; - - /*! - * Returns the length of the file in seconds. The length is rounded down to the nearest whole second. - * - * \see lengthInMilliseconds() - */ - int lengthInSeconds() const override; - - /*! - * Returns the length of the file in milliseconds. - * - * \see lengthInSeconds() - */ - int lengthInMilliseconds() const override; - - /*! - * Returns the average bit rate of the file in kb/s. - */ - int bitrate() const override; - - /*! - * Returns the sample rate in Hz. - */ - int sampleRate() const override; - - /*! - * Returns the number of audio channels. - */ - int channels() const override; - - /*! - * Returns the number of bits per audio sample. - */ - virtual int bitsPerSample() const; - - /*! - * Returns whether or not the file is encrypted. - */ - bool isEncrypted() const; - - /*! - * Returns the codec used in the file. - */ - Codec codec() const; - - String toString() const override; - - private: - void read(File *file, Atoms *atoms); - - class AudioPropertiesPrivate; - AudioPropertiesPrivate *d; -}; - -} // namespace MP4 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mp4/mp4tag.cpp b/3rdparty/taglib/mp4/mp4tag.cpp deleted file mode 100644 index bff8229a..00000000 --- a/3rdparty/taglib/mp4/mp4tag.cpp +++ /dev/null @@ -1,1060 +0,0 @@ -/************************************************************************** - copyright : (C) 2007,2011 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tdebug.h" -#include "tstring.h" -#include "tpicturemap.h" -#include "tpropertymap.h" -#include "mp4atom.h" -#include "mp4tag.h" -#include "id3v1genres.h" - -using namespace Strawberry_TagLib::TagLib; - -class MP4::Tag::TagPrivate { - public: - explicit TagPrivate() : file(nullptr), atoms(nullptr) {} - - Strawberry_TagLib::TagLib::File *file; - Atoms *atoms; - ItemMap items; -}; - -MP4::Tag::Tag() : d(new TagPrivate()) {} - -MP4::Tag::Tag(Strawberry_TagLib::TagLib::File *file, MP4::Atoms *atoms) : d(new TagPrivate()) { - - d->file = file; - d->atoms = atoms; - - MP4::Atom *ilst = atoms->find("moov", "udta", "meta", "ilst"); - if (!ilst) { - //debug("Atom moov.udta.meta.ilst not found."); - return; - } - - for (AtomList::ConstIterator it = ilst->children.begin(); it != ilst->children.end(); ++it) { - MP4::Atom *atom = *it; - file->seek(atom->offset + 8); - if (atom->name == "----") { - parseFreeForm(atom); - } - else if (atom->name == "trkn" || atom->name == "disk") { - parseIntPair(atom); - } - else if (atom->name == "cpil" || atom->name == "pgap" || atom->name == "pcst" || - atom->name == "hdvd" || atom->name == "shwm") { - parseBool(atom); - } - 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, static_cast((val.data.toUInt16LE(0)))); - } - } - } - else if (atom->name == "tvsn" || atom->name == "tves" || atom->name == "cnID" || - atom->name == "sfID" || atom->name == "atID" || atom->name == "geID" || - atom->name == "cmID") { - parseUInt(atom); - } - else if (atom->name == "plID") { - parseLongLong(atom); - } - else if (atom->name == "stik" || atom->name == "rtng" || atom->name == "akID") { - parseByte(atom); - } - else if (atom->name == "gnre") { - parseGnre(atom); - } - else if (atom->name == "covr") { - parseCovr(atom); - } - else if (atom->name == "purl" || atom->name == "egid") { - parseText(atom, -1); - } - else { - parseText(atom); - } - } -} - -MP4::Tag::~Tag() { - delete d; -} - -MP4::AtomDataList MP4::Tag::parseData2(const MP4::Atom *atom, int expectedFlags, bool freeForm) { - - AtomDataList result; - ByteVector data = d->file->readBlock(atom->length - 8); - int i = 0; - size_t pos = 0; - while (pos < data.size()) { - const int length = static_cast(data.toUInt32BE(pos)); - if (length < 12) { - debug("MP4: Too short atom"); - return result; - } - - const ByteVector name = data.mid(pos + 4, 4); - const int flags = static_cast(data.toUInt32BE(pos + 8)); - if (freeForm && i < 2) { - if (i == 0 && name != "mean") { - debug("MP4: Unexpected atom \"" + name + "\", expecting \"mean\""); - return result; - } - else if (i == 1 && name != "name") { - debug("MP4: Unexpected atom \"" + name + "\", expecting \"name\""); - return result; - } - result.append(AtomData(AtomDataType(flags), data.mid(pos + 12, length - 12))); - } - else { - if (name != "data") { - debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\""); - return result; - } - if (expectedFlags == -1 || flags == expectedFlags) { - result.append(AtomData(AtomDataType(flags), data.mid(pos + 16, length - 16))); - } - } - pos += length; - i++; - } - return result; - -} - -ByteVectorList MP4::Tag::parseData(const MP4::Atom *atom, int expectedFlags, bool freeForm) { - - AtomDataList data = parseData2(atom, expectedFlags, freeForm); - ByteVectorList result; - for (AtomDataList::ConstIterator it = data.begin(); it != data.end(); ++it) { - result.append(it->data); - } - return result; - -} - -void MP4::Tag::parseInt(const MP4::Atom *atom) { - - ByteVectorList data = parseData(atom); - if (!data.isEmpty()) { - addItem(atom->name, (int)data[0].toInt16BE(0)); - } - -} - -void MP4::Tag::parseUInt(const MP4::Atom *atom) { - - ByteVectorList data = parseData(atom); - if (!data.isEmpty()) { - addItem(atom->name, data[0].toUInt32BE(0)); - } - -} - -void MP4::Tag::parseLongLong(const MP4::Atom *atom) { - - ByteVectorList data = parseData(atom); - if (!data.isEmpty()) { - addItem(atom->name, data[0].toInt64BE(0)); - } - -} - -void MP4::Tag::parseByte(const MP4::Atom *atom) { - - ByteVectorList data = parseData(atom); - if (!data.isEmpty()) { - addItem(atom->name, static_cast(data[0].at(0))); - } - -} - -void MP4::Tag::parseGnre(const MP4::Atom *atom) { - - ByteVectorList data = parseData(atom); - if (!data.isEmpty()) { - int idx = static_cast(data[0].toInt16BE(0)); - if (idx > 0) { - addItem("\251gen", StringList(ID3v1::genre(idx - 1))); - } - } - -} - -void 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); - addItem(atom->name, MP4::Item(a, b)); - } - -} - -void MP4::Tag::parseBool(const MP4::Atom *atom) { - - ByteVectorList data = parseData(atom); - if (!data.isEmpty()) { - bool value = data[0].size() ? data[0][0] != '\0' : false; - addItem(atom->name, value); - } - -} - -void MP4::Tag::parseText(const MP4::Atom *atom, int expectedFlags) { - - ByteVectorList data = parseData(atom, expectedFlags); - if (!data.isEmpty()) { - StringList value; - for (ByteVectorList::ConstIterator it = data.begin(); it != data.end(); ++it) { - value.append(String(*it, String::UTF8)); - } - addItem(atom->name, value); - } - -} - -void MP4::Tag::parseFreeForm(const MP4::Atom *atom) { - - AtomDataList data = parseData2(atom, -1, true); - if (data.size() > 2) { - AtomDataList::ConstIterator itBegin = data.begin(); - - String name = "----:"; - name += String((itBegin++)->data, String::UTF8); // data[0].data - name += ':'; - name += String((itBegin++)->data, String::UTF8); // data[1].data - - AtomDataType type = itBegin->type; // data[2].type - - for (AtomDataList::ConstIterator it = itBegin; it != data.end(); ++it) { - if (it->type != type) { - debug("MP4: We currently don't support values with multiple types"); - break; - } - } - if (type == TypeUTF8) { - StringList value; - for (AtomDataList::ConstIterator it = itBegin; it != data.end(); ++it) { - value.append(String(it->data, String::UTF8)); - } - Item item(value); - item.setAtomDataType(type); - addItem(name, item); - } - else { - ByteVectorList value; - for (AtomDataList::ConstIterator it = itBegin; it != data.end(); ++it) { - value.append(it->data); - } - Item item(value); - item.setAtomDataType(type); - addItem(name, item); - } - } - -} - -void MP4::Tag::parseCovr(const MP4::Atom *atom) { - - MP4::CoverArtList value; - ByteVector data = d->file->readBlock(atom->length - 8); - unsigned int pos = 0; - while (pos < data.size()) { - const int length = static_cast(data.toUInt32BE(pos)); - if (length < 12) { - debug("MP4: Too short atom"); - break; - ; - } - - const ByteVector name = data.mid(pos + 4, 4); - const int flags = static_cast(data.toUInt32BE(pos + 8)); - if (name != "data") { - debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\""); - break; - } - if (flags == TypeJPEG || flags == TypePNG || flags == TypeBMP || - flags == TypeGIF || flags == TypeImplicit) { - value.append(MP4::CoverArt(MP4::CoverArt::Format(flags), - data.mid(pos + 16, length - 16))); - } - else { - debug("MP4: Unknown covr format " + String::number(flags)); - } - pos += length; - } - if (!value.isEmpty()) - addItem(atom->name, value); - -} - -ByteVector MP4::Tag::padIlst(const ByteVector &data, int length) const { - - if (length == -1) { - length = ((data.size() + 1023) & ~1023) - data.size(); - } - return renderAtom("free", ByteVector(length, '\1')); - -} - -ByteVector MP4::Tag::renderAtom(const ByteVector &name, const ByteVector &data) const { - return ByteVector::fromUInt32BE(data.size() + 8) + name + data; -} - -ByteVector MP4::Tag::renderData(const ByteVector &name, int flags, const ByteVectorList &data) const { - - ByteVector result; - for (ByteVectorList::ConstIterator it = data.begin(); it != data.end(); ++it) { - result.append(renderAtom("data", ByteVector::fromUInt32BE(flags) + ByteVector(4, '\0') + *it)); - } - return renderAtom(name, result); - -} - -ByteVector MP4::Tag::renderBool(const ByteVector &name, const MP4::Item &item) const { - - ByteVectorList data; - data.append(ByteVector(1, item.toBool() ? '\1' : '\0')); - return renderData(name, TypeInteger, data); - -} - -ByteVector MP4::Tag::renderInt(const ByteVector &name, const MP4::Item &item) const { - - ByteVectorList data; - data.append(ByteVector::fromUInt16BE(item.toInt())); - return renderData(name, TypeInteger, data); - -} - -ByteVector MP4::Tag::renderUInt(const ByteVector &name, const MP4::Item &item) const { - - ByteVectorList data; - data.append(ByteVector::fromUInt32BE(item.toUInt())); - return renderData(name, TypeInteger, data); - -} - -ByteVector MP4::Tag::renderLongLong(const ByteVector &name, const MP4::Item &item) const { - - ByteVectorList data; - data.append(ByteVector::fromUInt64BE(item.toLongLong())); - return renderData(name, TypeInteger, data); - -} - -ByteVector MP4::Tag::renderByte(const ByteVector &name, const MP4::Item &item) const { - - ByteVectorList data; - data.append(ByteVector(1, item.toByte())); - return renderData(name, TypeInteger, data); - -} - -ByteVector 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(2, '\0')); - return renderData(name, TypeImplicit, data); - -} - -ByteVector MP4::Tag::renderIntPairNoTrailing(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)); - return renderData(name, TypeImplicit, data); - -} - -ByteVector MP4::Tag::renderText(const ByteVector &name, const MP4::Item &item, int flags) const { - - ByteVectorList data; - StringList value = item.toStringList(); - for (StringList::ConstIterator it = value.begin(); it != value.end(); ++it) { - data.append(it->data(String::UTF8)); - } - return renderData(name, flags, data); - -} - -ByteVector 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()) + ByteVector(4, '\0') + it->data())); - } - return renderAtom(name, data); - -} - -ByteVector MP4::Tag::renderFreeForm(const String &name, const MP4::Item &item) const { - - StringList header = StringList::split(name, ":"); - if (header.size() != 3) { - debug("MP4: Invalid free-form item name \"" + name + "\""); - return ByteVector(); - } - ByteVector data; - data.append(renderAtom("mean", ByteVector::fromUInt32BE(0) + header[1].data(String::UTF8))); - data.append(renderAtom("name", ByteVector::fromUInt32BE(0) + header[2].data(String::UTF8))); - AtomDataType type = item.atomDataType(); - if (type == TypeUndefined) { - if (!item.toStringList().isEmpty()) { - type = TypeUTF8; - } - else { - type = TypeImplicit; - } - } - if (type == TypeUTF8) { - 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))); - } - } - 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)); - } - } - return renderAtom("----", data); - -} - -bool MP4::Tag::save() { - - ByteVector data; - for (MP4::ItemMap::ConstIterator it = d->items.begin(); it != d->items.end(); ++it) { - const String name = it->first; - if (name.startsWith("----")) { - data.append(renderFreeForm(name, it->second)); - } - else if (name == "trkn") { - data.append(renderIntPair(name.data(String::Latin1), it->second)); - } - else if (name == "disk") { - data.append(renderIntPairNoTrailing(name.data(String::Latin1), it->second)); - } - else if (name == "cpil" || name == "pgap" || name == "pcst" || name == "hdvd" || - name == "shwm") { - data.append(renderBool(name.data(String::Latin1), it->second)); - } - 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") { - data.append(renderUInt(name.data(String::Latin1), it->second)); - } - else if (name == "plID") { - data.append(renderLongLong(name.data(String::Latin1), it->second)); - } - else if (name == "stik" || name == "rtng" || name == "akID") { - data.append(renderByte(name.data(String::Latin1), it->second)); - } - else if (name == "covr") { - data.append(renderCovr(name.data(String::Latin1), it->second)); - } - else if (name == "purl" || name == "egid") { - data.append(renderText(name.data(String::Latin1), it->second, TypeImplicit)); - } - else if (name.size() == 4) { - data.append(renderText(name.data(String::Latin1), it->second)); - } - else { - debug("MP4: Unknown item name \"" + name + "\""); - } - } - data = renderAtom("ilst", data); - - AtomList path = d->atoms->path("moov", "udta", "meta", "ilst"); - if (path.size() == 4) { - saveExisting(data, path); - } - else { - saveNew(data); - } - - return true; - -} - -void MP4::Tag::updateParents(const AtomList &path, long delta, int ignore) { - - if (static_cast(path.size()) <= ignore) - return; - - AtomList::ConstIterator itEnd = path.end(); - std::advance(itEnd, 0 - ignore); - - for (AtomList::ConstIterator it = path.begin(); it != itEnd; ++it) { - d->file->seek((*it)->offset); - long size = d->file->readBlock(4).toUInt32BE(0); - // 64-bit - if (size == 1) { - d->file->seek(4, File::Current); // Skip name - long long longSize = d->file->readBlock(8).toInt64BE(0); - // Seek the offset of the 64-bit size - d->file->seek((*it)->offset + 8); - d->file->writeBlock(ByteVector::fromUInt64BE(longSize + delta)); - } - // 32-bit - else { - d->file->seek((*it)->offset); - d->file->writeBlock(ByteVector::fromUInt32BE(size + delta)); - } - } -} - -void MP4::Tag::updateOffsets(long delta, long long offset) { - - MP4::Atom *moov = d->atoms->find("moov"); - if (moov) { - MP4::AtomList stco = moov->findall("stco", true); - for (MP4::AtomList::ConstIterator it = stco.begin(); it != stco.end(); ++it) { - MP4::Atom *atom = *it; - if (atom->offset > offset) { - atom->offset += delta; - } - d->file->seek(atom->offset + 12); - ByteVector data = d->file->readBlock(atom->length - 12); - unsigned int count = data.toUInt32BE(0); - d->file->seek(atom->offset + 16); - size_t pos = 4; - while (count--) { - long o = data.toUInt32BE(pos); - if (o > offset) { - o += delta; - } - d->file->writeBlock(ByteVector::fromUInt32BE(o)); - pos += 4; - } - } - - MP4::AtomList co64 = moov->findall("co64", true); - for (MP4::AtomList::ConstIterator it = co64.begin(); it != co64.end(); ++it) { - MP4::Atom *atom = *it; - if (atom->offset > offset) { - atom->offset += delta; - } - d->file->seek(atom->offset + 12); - ByteVector data = d->file->readBlock(atom->length - 12); - unsigned int count = data.toUInt32BE(0); - d->file->seek(atom->offset + 16); - size_t pos = 4; - while (count--) { - long long o = data.toInt64BE(pos); - if (o > offset) { - o += delta; - } - d->file->writeBlock(ByteVector::fromUInt64BE(o)); - pos += 8; - } - } - } - - MP4::Atom *moof = d->atoms->find("moof"); - if (moof) { - MP4::AtomList tfhd = moof->findall("tfhd", true); - for (MP4::AtomList::ConstIterator it = tfhd.begin(); it != tfhd.end(); ++it) { - MP4::Atom *atom = *it; - if (atom->offset > offset) { - atom->offset += delta; - } - d->file->seek(atom->offset + 9); - ByteVector data = d->file->readBlock(atom->length - 9); - const unsigned int flags = data.toUInt24BE(0); - if (flags & 1) { - long long o = data.toInt64BE(7); - if (o > offset) { - o += delta; - } - d->file->seek(atom->offset + 16); - d->file->writeBlock(ByteVector::fromUInt64BE(o)); - } - } - } - -} - -void MP4::Tag::saveNew(ByteVector data) { - - data = renderAtom("meta", ByteVector(4, '\0') + renderAtom("hdlr", ByteVector(8, '\0') + ByteVector("mdirappl") + ByteVector(9, '\0')) + data + padIlst(data)); - - AtomList path = d->atoms->path("moov", "udta"); - if (path.size() != 2) { - path = d->atoms->path("moov"); - data = renderAtom("udta", data); - } - - long long offset = path.back()->offset + 8; - d->file->insert(data, offset, 0); - - updateParents(path, data.size()); - updateOffsets(data.size(), offset); - - // Insert the newly created atoms into the tree to keep it up-to-date. - - d->file->seek(offset); - path.back()->children.prepend(new Atom(d->file)); - -} - -void 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; - - MP4::Atom *meta = *(--it); - AtomList::ConstIterator index = meta->children.find(ilst); - - // check if there is an atom before 'ilst', and possibly use it as padding - if (index != meta->children.begin()) { - AtomList::ConstIterator prevIndex = index; - prevIndex--; - MP4::Atom *prev = *prevIndex; - if (prev->name == "free") { - offset = prev->offset; - length += prev->length; - } - } - // check if there is an atom after 'ilst', and possibly use it as padding - AtomList::ConstIterator nextIndex = index; - nextIndex++; - if (nextIndex != meta->children.end()) { - MP4::Atom *next = *nextIndex; - if (next->name == "free") { - length += next->length; - } - } - - long delta = data.size() - length; - if (delta > 0 || (delta < 0 && delta > -8)) { - data.append(padIlst(data)); - delta = data.size() - length; - } - else if (delta < 0) { - data.append(padIlst(data, 0 - delta - 8)); - delta = 0; - } - - d->file->insert(data, offset, length); - - if (delta) { - updateParents(path, delta, 1); - updateOffsets(delta, offset); - } - -} - -String MP4::Tag::title() const { - if (d->items.contains("\251nam")) - return d->items["\251nam"].toStringList().toString(", "); - return String(); -} - -String MP4::Tag::artist() const { - if (d->items.contains("\251ART")) - return d->items["\251ART"].toStringList().toString(", "); - return String(); -} - -String MP4::Tag::album() const { - if (d->items.contains("\251alb")) - return d->items["\251alb"].toStringList().toString(", "); - return String(); -} - -String MP4::Tag::comment() const { - if (d->items.contains("\251cmt")) - return d->items["\251cmt"].toStringList().toString(", "); - return String(); -} - -String MP4::Tag::genre() const { - if (d->items.contains("\251gen")) - return d->items["\251gen"].toStringList().toString(", "); - return String(); -} - -unsigned int MP4::Tag::year() const { - if (d->items.contains("\251day")) - return d->items["\251day"].toStringList().toString().toInt(); - return 0; -} - -unsigned int MP4::Tag::track() const { - if (d->items.contains("trkn")) - return d->items["trkn"].toIntPair().first; - 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) { - d->items["\251nam"] = StringList(value); -} - -void MP4::Tag::setArtist(const String &value) { - d->items["\251ART"] = StringList(value); -} - -void MP4::Tag::setAlbum(const String &value) { - d->items["\251alb"] = StringList(value); -} - -void MP4::Tag::setComment(const String &value) { - d->items["\251cmt"] = StringList(value); -} - -void MP4::Tag::setGenre(const String &value) { - d->items["\251gen"] = StringList(value); -} - -void MP4::Tag::setYear(unsigned int value) { - - if (value == 0) { - d->items.erase("\251day"); - } - else { - d->items["\251day"] = StringList(String::number(value)); - } - -} - -void MP4::Tag::setTrack(unsigned int value) { - - if (value == 0) { - d->items.erase("trkn"); - } - else { - 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); - } - } - d->items["covr"] = list; - -} - -bool MP4::Tag::isEmpty() const { - return d->items.isEmpty(); -} - -const MP4::ItemMap &MP4::Tag::itemMap() const { - return d->items; -} - -MP4::Item MP4::Tag::item(const String &key) const { - return d->items[key]; -} - -void MP4::Tag::setItem(const String &key, const Item &value) { - d->items[key] = value; -} - -void MP4::Tag::removeItem(const String &key) { - d->items.erase(key); -} - -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] = { - { "\251nam", "TITLE" }, - { "\251ART", "ARTIST" }, - { "\251alb", "ALBUM" }, - { "\251cmt", "COMMENT" }, - { "\251gen", "GENRE" }, - { "\251day", "DATE" }, - { "\251wrt", "COMPOSER" }, - { "\251grp", "GROUPING" }, - { "aART", "ALBUMARTIST" }, - { "trkn", "TRACKNUMBER" }, - { "disk", "DISCNUMBER" }, - { "cpil", "COMPILATION" }, - { "tmpo", "BPM" }, - { "cprt", "COPYRIGHT" }, - { "\251lyr", "LYRICS" }, - { "\251too", "ENCODEDBY" }, - { "soal", "ALBUMSORT" }, - { "soaa", "ALBUMARTISTSORT" }, - { "soar", "ARTISTSORT" }, - { "sonm", "TITLESORT" }, - { "soco", "COMPOSERSORT" }, - { "sosn", "SHOWSORT" }, - { "shwm", "SHOWWORKMOVEMENT" }, - { "\251wrk", "WORK" }, - { "\251mvn", "MOVEMENTNAME" }, - { "\251mvi", "MOVEMENTNUMBER" }, - { "\251mvc", "MOVEMENTCOUNT" }, - { "----:com.apple.iTunes:MusicBrainz Track Id", "MUSICBRAINZ_TRACKID" }, - { "----:com.apple.iTunes:MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" }, - { "----:com.apple.iTunes:MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" }, - { "----:com.apple.iTunes:MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" }, - { "----:com.apple.iTunes:MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" }, - { "----:com.apple.iTunes:MusicBrainz Work Id", "MUSICBRAINZ_WORKID" }, - { "----:com.apple.iTunes:ASIN", "ASIN" }, - { "----:com.apple.iTunes:LABEL", "LABEL" }, - { "----:com.apple.iTunes:LYRICIST", "LYRICIST" }, - { "----:com.apple.iTunes:CONDUCTOR", "CONDUCTOR" }, - { "----:com.apple.iTunes:REMIXER", "REMIXER" }, - { "----:com.apple.iTunes:ENGINEER", "ENGINEER" }, - { "----:com.apple.iTunes:PRODUCER", "PRODUCER" }, - { "----:com.apple.iTunes:DJMIXER", "DJMIXER" }, - { "----:com.apple.iTunes:MIXER", "MIXER" }, - { "----:com.apple.iTunes:SUBTITLE", "SUBTITLE" }, - { "----:com.apple.iTunes:DISCSUBTITLE", "DISCSUBTITLE" }, - { "----:com.apple.iTunes:MOOD", "MOOD" }, - { "----:com.apple.iTunes:ISRC", "ISRC" }, - { "----:com.apple.iTunes:CATALOGNUMBER", "CATALOGNUMBER" }, - { "----:com.apple.iTunes:BARCODE", "BARCODE" }, - { "----:com.apple.iTunes:SCRIPT", "SCRIPT" }, - { "----:com.apple.iTunes:LANGUAGE", "LANGUAGE" }, - { "----:com.apple.iTunes:LICENSE", "LICENSE" }, - { "----:com.apple.iTunes:MEDIA", "MEDIA" }, -}; -const size_t keyTranslationSize = sizeof(keyTranslation) / sizeof(keyTranslation[0]); - -String translateKey(const String &key) { - - for (size_t i = 0; i < keyTranslationSize; ++i) { - if (key == keyTranslation[i][0]) - return keyTranslation[i][1]; - } - - return String(); - -} -} // namespace - -PropertyMap MP4::Tag::properties() const { - - PropertyMap props; - for (MP4::ItemMap::ConstIterator it = d->items.begin(); it != d->items.end(); ++it) { - const String key = translateKey(it->first); - if (!key.isEmpty()) { - if (key == "TRACKNUMBER" || key == "DISCNUMBER") { - MP4::Item::IntPair ip = it->second.toIntPair(); - String value = String::number(ip.first); - if (ip.second) { - value += "/" + String::number(ip.second); - } - props[key] = value; - } - else if (key == "BPM" || key == "MOVEMENTNUMBER" || key == "MOVEMENTCOUNT") { - props[key] = String::number(it->second.toInt()); - } - else if (key == "COMPILATION" || key == "SHOWWORKMOVEMENT") { - props[key] = String::number(it->second.toBool()); - } - else { - props[key] = it->second.toStringList(); - } - } - else { - props.unsupportedData().append(it->first); - } - } - return props; - -} - -void MP4::Tag::removeUnsupportedProperties(const StringList &props) { - - for (StringList::ConstIterator it = props.begin(); it != props.end(); ++it) - d->items.erase(*it); - -} - -PropertyMap MP4::Tag::setProperties(const PropertyMap &props) { - - static Map reverseKeyMap; - if (reverseKeyMap.isEmpty()) { - int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]); - for (int i = 0; i < numKeys; i++) { - reverseKeyMap[keyTranslation[i][1]] = keyTranslation[i][0]; - } - } - - PropertyMap origProps = properties(); - for (PropertyMap::ConstIterator it = origProps.begin(); it != origProps.end(); ++it) { - if (!props.contains(it->first) || props[it->first].isEmpty()) { - d->items.erase(reverseKeyMap[it->first]); - } - } - - PropertyMap ignoredProps; - for (PropertyMap::ConstIterator it = props.begin(); it != props.end(); ++it) { - if (reverseKeyMap.contains(it->first)) { - String name = reverseKeyMap[it->first]; - if ((it->first == "TRACKNUMBER" || it->first == "DISCNUMBER") && !it->second.isEmpty()) { - StringList parts = StringList::split(it->second.front(), "/"); - if (!parts.isEmpty()) { - int first = parts[0].toInt(); - int second = 0; - if (parts.size() > 1) { - second = parts[1].toInt(); - } - d->items[name] = MP4::Item(first, second); - } - } - else if ((it->first == "BPM" || it->first == "MOVEMENTNUMBER" || it->first == "MOVEMENTCOUNT") && !it->second.isEmpty()) { - int value = it->second.front().toInt(); - d->items[name] = MP4::Item(value); - } - else if ((it->first == "COMPILATION" || it->first == "SHOWWORKMOVEMENT") && !it->second.isEmpty()) { - bool value = (it->second.front().toInt() != 0); - d->items[name] = MP4::Item(value); - } - else { - d->items[name] = it->second; - } - } - else { - ignoredProps.insert(it->first, it->second); - } - } - - return ignoredProps; - -} - -void MP4::Tag::addItem(const String &name, const Item &value) { - - if (!d->items.contains(name)) { - d->items.insert(name, value); - } - else { - debug("MP4: Ignoring duplicate atom \"" + name + "\""); - } - -} diff --git a/3rdparty/taglib/mp4/mp4tag.h b/3rdparty/taglib/mp4/mp4tag.h deleted file mode 100644 index cf1c063b..00000000 --- a/3rdparty/taglib/mp4/mp4tag.h +++ /dev/null @@ -1,146 +0,0 @@ -/************************************************************************** - copyright : (C) 2007,2011 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_MP4TAG_H -#define TAGLIB_MP4TAG_H - -#include "tag.h" -#include "tbytevectorlist.h" -#include "tfile.h" -#include "tmap.h" -#include "tstringlist.h" -#include "taglib_export.h" -#include "mp4atom.h" -#include "mp4item.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace MP4 { - -typedef Strawberry_TagLib::TagLib::Map ItemMap; -typedef Strawberry_TagLib::TagLib::Map ItemListMap; - -class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag { - public: - explicit Tag(); - explicit Tag(Strawberry_TagLib::TagLib::File *file, Atoms *atoms); - ~Tag() override; - bool save(); - - String title() const override; - String artist() const override; - String album() const override; - String comment() const override; - String genre() const override; - unsigned int year() const override; - unsigned int track() const override; - PictureMap pictures() const override; - - void setTitle(const String &value) override; - void setArtist(const String &value) override; - void setAlbum(const String &value) override; - void setComment(const String &value) override; - void setGenre(const String &value) override; - void setYear(unsigned int value) override; - void setTrack(unsigned int value) override; - void setPictures(const PictureMap &l) override; - - bool isEmpty() const override; - /*! - * Returns a string-keyed map of the MP4::Items for this tag. - */ - const ItemMap &itemMap() const; - - /*! - * \return The item, if any, corresponding to \a key. - */ - Item item(const String &key) const; - - /*! - * Sets the value of \a key to \a value, overwriting any previous value. - */ - void setItem(const String &key, const Item &value); - - /*! - * Removes the entry with \a key from the tag, or does nothing if it does not exist. - */ - void removeItem(const String &key); - - /*! - * \return True if the tag contains an entry for \a key. - */ - bool contains(const String &key) const; - - String toString() const override; - - PropertyMap properties() const override; - void removeUnsupportedProperties(const StringList &props) override; - PropertyMap setProperties(const PropertyMap &props) override; - - private: - AtomDataList parseData2(const Atom *atom, int expectedFlags = -1, bool freeForm = false); - ByteVectorList parseData(const Atom *atom, int expectedFlags = -1, bool freeForm = false); - void parseText(const Atom *atom, int expectedFlags = 1); - void parseFreeForm(const Atom *atom); - void parseInt(const Atom *atom); - void parseByte(const Atom *atom); - void parseUInt(const Atom *atom); - void parseLongLong(const Atom *atom); - void parseGnre(const Atom *atom); - void parseIntPair(const Atom *atom); - void parseBool(const Atom *atom); - void parseCovr(const Atom *atom); - - ByteVector padIlst(const ByteVector &data, int length = -1) const; - ByteVector renderAtom(const ByteVector &name, const ByteVector &data) const; - ByteVector renderData(const ByteVector &name, int flags, const ByteVectorList &data) const; - ByteVector renderText(const ByteVector &name, const Item &item, int flags = TypeUTF8) const; - ByteVector renderFreeForm(const String &name, const Item &item) const; - ByteVector renderBool(const ByteVector &name, const Item &item) const; - ByteVector renderInt(const ByteVector &name, const Item &item) const; - ByteVector renderByte(const ByteVector &name, const Item &item) const; - ByteVector renderUInt(const ByteVector &name, const Item &item) const; - ByteVector renderLongLong(const ByteVector &name, const Item &item) const; - ByteVector renderIntPair(const ByteVector &name, const Item &item) const; - ByteVector renderIntPairNoTrailing(const ByteVector &name, const Item &item) const; - 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 saveNew(ByteVector data); - void saveExisting(ByteVector data, const AtomList &path); - - void addItem(const String &name, const Item &value); - - class TagPrivate; - TagPrivate *d; -}; - -} // namespace MP4 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpc/mpcfile.cpp b/3rdparty/taglib/mpc/mpcfile.cpp deleted file mode 100644 index d1fd6b77..00000000 --- a/3rdparty/taglib/mpc/mpcfile.cpp +++ /dev/null @@ -1,300 +0,0 @@ -/*************************************************************************** - copyright : (C) 2004 by Allan Sandfeld Jensen - email : kde@carewolf.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 - -#include "tbytevector.h" -#include "tstring.h" -#include "tagunion.h" -#include "tdebug.h" -#include "tpropertymap.h" -#include "tagutils.h" - -#include "mpcfile.h" -#include "id3v1tag.h" -#include "id3v2header.h" -#include "apetag.h" -#include "apefooter.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { -enum { MPCAPEIndex = 0, - MPCID3v1Index = 1 }; -} - -class MPC::File::FilePrivate { - public: - explicit FilePrivate() : APELocation(-1), - APESize(0), - ID3v1Location(-1), - ID3v2Location(-1), - ID3v2Size(0) {} - - long long APELocation; - long long APESize; - - long long ID3v1Location; - - std::unique_ptr ID3v2Header; - long long ID3v2Location; - long long ID3v2Size; - - DoubleTagUnion tag; - - std::unique_ptr properties; -}; - -//////////////////////////////////////////////////////////////////////////////// -// static members -//////////////////////////////////////////////////////////////////////////////// - -bool MPC::File::isSupported(IOStream *stream) { - - // A newer MPC file has to start with "MPCK" or "MP+", but older files don't - // have keys to do a quick check. - - const ByteVector id = Utils::readHeader(stream, 4, false); - return (id == "MPCK" || id.startsWith("MP+")); - -} - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -MPC::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::File(file), d(new FilePrivate()) { - - if (isOpen()) - read(readProperties); - -} - -MPC::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::File(stream), d(new FilePrivate()) { - - if (isOpen()) - read(readProperties); - -} - -MPC::File::~File() { - delete d; -} - -Strawberry_TagLib::TagLib::Tag *MPC::File::tag() const { - return &d->tag; -} - -PropertyMap MPC::File::setProperties(const PropertyMap &properties) { - if (ID3v1Tag()) - ID3v1Tag()->setProperties(properties); - - return APETag(true)->setProperties(properties); -} - -MPC::AudioProperties *MPC::File::audioProperties() const { - return d->properties.get(); -} - -bool MPC::File::save() { - - if (readOnly()) { - debug("MPC::File::save() -- File is read only."); - return false; - } - - // Possibly strip ID3v2 tag - - if (!d->ID3v2Header && d->ID3v2Location >= 0) { - removeBlock(d->ID3v2Location, d->ID3v2Size); - - if (d->APELocation >= 0) - d->APELocation -= d->ID3v2Size; - - if (d->ID3v1Location >= 0) - d->ID3v1Location -= d->ID3v2Size; - - d->ID3v2Location = -1; - d->ID3v2Size = 0; - } - - // Update ID3v1 tag - - if (ID3v1Tag() && !ID3v1Tag()->isEmpty()) { - - // ID3v1 tag is not empty. Update the old one or create a new one. - - if (d->ID3v1Location >= 0) { - seek(d->ID3v1Location); - } - else { - seek(0, End); - d->ID3v1Location = tell(); - } - - writeBlock(ID3v1Tag()->render()); - } - else { - - // ID3v1 tag is empty. Remove the old one. - - if (d->ID3v1Location >= 0) { - truncate(d->ID3v1Location); - d->ID3v1Location = -1; - } - } - - // Update APE tag - - if (APETag() && !APETag()->isEmpty()) { - - // APE tag is not empty. Update the old one or create a new one. - - if (d->APELocation < 0) { - if (d->ID3v1Location >= 0) - d->APELocation = d->ID3v1Location; - else - d->APELocation = length(); - } - - const ByteVector data = APETag()->render(); - insert(data, d->APELocation, d->APESize); - - if (d->ID3v1Location >= 0) - d->ID3v1Location += (static_cast(data.size()) - d->APESize); - - d->APESize = data.size(); - } - else { - - // APE tag is empty. Remove the old one. - - if (d->APELocation >= 0) { - removeBlock(d->APELocation, d->APESize); - - if (d->ID3v1Location >= 0) - d->ID3v1Location -= d->APESize; - - d->APELocation = -1; - d->APESize = 0; - } - } - - return true; - -} - -ID3v1::Tag *MPC::File::ID3v1Tag(bool create) { - return d->tag.access(MPCID3v1Index, create); -} - -APE::Tag *MPC::File::APETag(bool create) { - return d->tag.access(MPCAPEIndex, create); -} - -void MPC::File::strip(int tags) { - - if (tags & ID3v1) - d->tag.set(MPCID3v1Index, nullptr); - - if (tags & APE) - d->tag.set(MPCAPEIndex, nullptr); - - if (!ID3v1Tag()) - APETag(true); - - if (tags & ID3v2) - d->ID3v2Header.reset(); -} - -bool MPC::File::hasID3v1Tag() const { - return (d->ID3v1Location >= 0); -} - -bool MPC::File::hasAPETag() const { - return (d->APELocation >= 0); -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void MPC::File::read(bool readProperties) { - - // Look for an ID3v2 tag - - d->ID3v2Location = Utils::findID3v2(this); - - if (d->ID3v2Location >= 0) { - seek(d->ID3v2Location); - d->ID3v2Header.reset(new ID3v2::Header(readBlock(ID3v2::Header::size()))); - d->ID3v2Size = d->ID3v2Header->completeTagSize(); - } - - // Look for an ID3v1 tag - - d->ID3v1Location = Utils::findID3v1(this); - - if (d->ID3v1Location >= 0) - d->tag.set(MPCID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); - - // Look for an APE tag - - d->APELocation = Utils::findAPE(this, d->ID3v1Location); - - if (d->APELocation >= 0) { - d->tag.set(MPCAPEIndex, new APE::Tag(this, d->APELocation)); - d->APESize = APETag()->footer()->completeTagSize(); - d->APELocation = d->APELocation + APE::Footer::size() - d->APESize; - } - - if (d->ID3v1Location < 0) - APETag(true); - - // Look for MPC metadata - - if (readProperties) { - - long long streamLength; - - if (d->APELocation >= 0) - streamLength = d->APELocation; - else if (d->ID3v1Location >= 0) - streamLength = d->ID3v1Location; - else - streamLength = length(); - - if (d->ID3v2Location >= 0) { - seek(d->ID3v2Location + d->ID3v2Size); - streamLength -= (d->ID3v2Location + d->ID3v2Size); - } - else { - seek(0); - } - - d->properties.reset(new AudioProperties(this, streamLength)); - } - -} diff --git a/3rdparty/taglib/mpc/mpcfile.h b/3rdparty/taglib/mpc/mpcfile.h deleted file mode 100644 index 7b36c50e..00000000 --- a/3rdparty/taglib/mpc/mpcfile.h +++ /dev/null @@ -1,212 +0,0 @@ -/*************************************************************************** - copyright : (C) 2004 by Allan Sandfeld Jensen - email : kde@carewolf.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_MPCFILE_H -#define TAGLIB_MPCFILE_H - -#include "taglib_export.h" -#include "tfile.h" -#include "tag.h" - -#include "mpcproperties.h" - -#include "tlist.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -class Tag; - -namespace ID3v1 { -class Tag; -} -namespace APE { -class Tag; -} - -//! An implementation of MPC metadata - -/*! - * This is implementation of MPC metadata. - * - * This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream properties from the file. - * ID3v2 tags are invalid in MPC-files, but will be skipped and ignored. - * - */ - -namespace MPC { - -//! An implementation of TagLib::File with MPC specific methods - -/*! - * This implements and provides an interface for MPC 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 MPC files. - * The only invalid tag combination supported is an ID3v1 tag after an APE tag. - */ - -class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File { - public: - /*! - * This set of flags is used for various operations and is suitable for being OR-ed together. - */ - enum TagTypes { - //! Empty set. Matches no tag types. - NoTags = 0x0000, - //! Matches ID3v1 tags. - ID3v1 = 0x0001, - //! Matches ID3v2 tags. - ID3v2 = 0x0002, - //! Matches APE tags. - APE = 0x0004, - //! Matches all tag types. - AllTags = 0xffff - }; - - /*! - * Constructs an MPC file from \a file. - * If \a readProperties is true the file's audio properties will also be read. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Constructs an MPC file from \a stream. - * If \a readProperties is true the file's audio properties will also be read. - * - * \note TagLib will *not* take ownership of the stream, the caller is responsible for deleting it after the File object. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Destroys this instance of the File. - */ - ~File() override; - - /*! - * Returns the Tag for this file. - * This will be an APE tag, an ID3v1 tag or a combination of the two. - */ - Strawberry_TagLib::TagLib::Tag *tag() const override; - - /*! - * 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. - */ - PropertyMap setProperties(const PropertyMap &) override; - - /*! - * Returns the MPC::AudioProperties for this file. - * If no audio properties were read then this will return a null pointer. - */ - AudioProperties *audioProperties() const override; - - /*! - * Saves the file. - * - * This returns true if the save was successful. - */ - bool save() override; - - /*! - * Returns a pointer to the ID3v1 tag of the file. - * - * If \a create is false (the default) this returns a null pointer if there is no valid APE tag. - * If \a create is true it will create an APE tag if one does not exist and returns a valid pointer. - * - * \note This may return a valid pointer regardless of whether or not the file on disk has an ID3v1 tag. - * Use hasID3v1Tag() to check if the file on disk actually has an ID3v1 tag. - * - * \note The Tag is still owned by the MPEG::File and should not be deleted by the user. - * It will be deleted when the file (object) is destroyed. - * - * \see hasID3v1Tag() - */ - ID3v1::Tag *ID3v1Tag(bool create = false); - - /*! - * Returns a pointer to the APE tag of the file. - * - * If \a create is false (the default) this may return a null pointer - * if there is no valid APE tag. - * If \a create is true it will create an APE tag if one does not exist and returns a valid pointer. - * If there already be an ID3v1 tag, the new APE tag will be placed before it. - * - * \note This may return a valid pointer regardless of whether or not the file on disk has an APE tag. - * Use hasAPETag() to check if the file on disk actually has an APE tag. - * - * \note The Tag is still owned by the MPEG::File and should not be deleted by the user. - * It will be deleted when the file (object) is destroyed. - * - * \see hasAPETag() - */ - APE::Tag *APETag(bool create = false); - - /*! - * This will remove the tags that match the OR-ed together TagTypes from the file. By default it removes all tags. - * - * \warning This will also invalidate pointers to the tags as their memory will be freed. - * - * \note In order to make the removal permanent save() still needs to be called. - */ - void strip(int tags = AllTags); - - /*! - * Returns whether or not the file on disk actually has an ID3v1 tag. - * - * \see ID3v1Tag() - */ - bool hasID3v1Tag() const; - - /*! - * Returns whether or not the file on disk actually has an APE tag. - * - * \see APETag() - */ - bool hasAPETag() const; - - /*! - * Returns whether or not the given \a stream can be opened as an MPC 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); - - class FilePrivate; - FilePrivate *d; -}; -} // namespace MPC -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpc/mpcproperties.cpp b/3rdparty/taglib/mpc/mpcproperties.cpp deleted file mode 100644 index 6ff3cff5..00000000 --- a/3rdparty/taglib/mpc/mpcproperties.cpp +++ /dev/null @@ -1,353 +0,0 @@ -/*************************************************************************** - copyright : (C) 2004 by Allan Sandfeld Jensen - email : kde@carewolf.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 -#include - -#include "tstring.h" -#include "tdebug.h" - -#include "mpcproperties.h" -#include "mpcfile.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { -const unsigned int HeaderSize = 56; -} - -class MPC::AudioProperties::AudioPropertiesPrivate { - public: - explicit AudioPropertiesPrivate() : version(0), - length(0), - bitrate(0), - sampleRate(0), - channels(0), - totalFrames(0), - sampleFrames(0), - trackGain(0), - trackPeak(0), - albumGain(0), - albumPeak(0) {} - - int version; - int length; - int bitrate; - int sampleRate; - int channels; - unsigned int totalFrames; - unsigned int sampleFrames; - unsigned int trackGain; - unsigned int trackPeak; - unsigned int albumGain; - unsigned int albumPeak; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -MPC::AudioProperties::AudioProperties(File *file, long long streamLength, ReadStyle) : Strawberry_TagLib::TagLib::AudioProperties(), d(new AudioPropertiesPrivate()) { - - ByteVector magic = file->readBlock(4); - if (magic == "MPCK") { - // Musepack version 8 - readSV8(file, streamLength); - } - else { - // Musepack version 7 or older, fixed size header - readSV7(magic + file->readBlock(HeaderSize - 4), streamLength); - } - -} - -MPC::AudioProperties::~AudioProperties() { - delete d; -} - -int MPC::AudioProperties::lengthInSeconds() const { - return d->length / 1000; -} - -int MPC::AudioProperties::lengthInMilliseconds() const { - return d->length; -} - -int MPC::AudioProperties::bitrate() const { - return d->bitrate; -} - -int MPC::AudioProperties::sampleRate() const { - return d->sampleRate; -} - -int MPC::AudioProperties::channels() const { - return d->channels; -} - -int MPC::AudioProperties::mpcVersion() const { - return d->version; -} - -unsigned int MPC::AudioProperties::totalFrames() const { - return d->totalFrames; -} - -unsigned int MPC::AudioProperties::sampleFrames() const { - return d->sampleFrames; -} - -int MPC::AudioProperties::trackGain() const { - return d->trackGain; -} - -int MPC::AudioProperties::trackPeak() const { - return d->trackPeak; -} - -int MPC::AudioProperties::albumGain() const { - return d->albumGain; -} - -int MPC::AudioProperties::albumPeak() const { - return d->albumPeak; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -namespace { -unsigned long readSize(File *file, size_t &sizeLength, bool &eof) { - - sizeLength = 0; - eof = false; - - unsigned char tmp; - unsigned long size = 0; - - do { - const ByteVector b = file->readBlock(1); - if (b.isEmpty()) { - eof = true; - break; - } - - tmp = b[0]; - size = (size << 7) | (tmp & 0x7F); - sizeLength++; - } - while ((tmp & 0x80)); - - return size; - -} - -unsigned long readSize(const ByteVector &data, size_t &pos) { - - unsigned char tmp; - unsigned long size = 0; - - do { - tmp = data[pos++]; - size = (size << 7) | (tmp & 0x7F); - } - while ((tmp & 0x80) && (pos < data.size())); - - return size; - -} - -// This array looks weird, but the same as original MusePack code found at: -// https://www.musepack.net/index.php?pg=src -const unsigned short sftable[8] = { 44100, 48000, 37800, 32000, 0, 0, 0, 0 }; -} // namespace - -void MPC::AudioProperties::readSV8(File *file, long long streamLength) { - - bool readSH = false, readRG = false; - - while (!readSH && !readRG) { - const ByteVector packetType = file->readBlock(2); - - size_t packetSizeLength; - bool eof; - const size_t packetSize = readSize(file, packetSizeLength, eof); - if (eof) { - debug("MPC::AudioProperties::readSV8() - Reached to EOF."); - break; - } - - const size_t 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."); - break; - } - - if (packetType == "SH") { - // Stream Header - // http://trac.musepack.net/wiki/SV8Specification#StreamHeaderPacket - - if (dataSize <= 5) { - debug("MPC::AudioProperties::readSV8() - \"SH\" packet is too short to parse."); - break; - } - - readSH = true; - - size_t 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."); - break; - } - - const unsigned long begSilence = readSize(data, pos); - if (pos > dataSize - 2) { - debug("MPC::AudioProperties::readSV8() - \"SH\" packet is corrupt."); - break; - } - - const unsigned short flags = data.toUInt16BE(pos); - pos += 2; - - d->sampleRate = sftable[(flags >> 13) & 0x07]; - d->channels = ((flags >> 4) & 0x0F) + 1; - - const unsigned int frameCount = d->sampleFrames - begSilence; - if (frameCount > 0 && d->sampleRate > 0) { - const double length = frameCount * 1000.0 / d->sampleRate; - d->length = static_cast(length + 0.5); - d->bitrate = static_cast(streamLength * 8.0 / length + 0.5); - } - } - else if (packetType == "RG") { - // Replay Gain - // http://trac.musepack.net/wiki/SV8Specification#ReplaygainPacket - - if (dataSize <= 9) { - debug("MPC::AudioProperties::readSV8() - \"RG\" packet is too short to parse."); - break; - } - - readRG = true; - - 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); - } - } - - else if (packetType == "SE") { - break; - } - - else { - file->seek(dataSize, File::Current); - } - } - -} - -void MPC::AudioProperties::readSV7(const ByteVector &data, long long streamLength) { - - if (data.startsWith("MP+")) { - d->version = data[3] & 15; - if (d->version < 7) - return; - - d->totalFrames = data.toUInt32LE(4); - - const unsigned int flags = data.toUInt32LE(8); - d->sampleRate = sftable[(flags >> 16) & 0x03]; - d->channels = 2; - - const unsigned int gapless = data.toUInt32LE(5); - - d->trackGain = data.toUInt16LE(14); - d->trackPeak = data.toUInt16LE(12); - d->albumGain = data.toUInt16LE(18); - d->albumPeak = data.toUInt16LE(16); - - // convert gain info - if (d->trackGain != 0) { - int tmp = static_cast((64.82 - static_cast(d->trackGain) / 100.) * 256. + .5); - if (tmp >= (1 << 16) || tmp < 0) tmp = 0; - d->trackGain = tmp; - } - - if (d->albumGain != 0) { - int tmp = static_cast((64.82 - d->albumGain / 100.) * 256. + .5); - if (tmp >= (1 << 16) || tmp < 0) tmp = 0; - d->albumGain = tmp; - } - - if (d->trackPeak != 0) - d->trackPeak = static_cast(log10(static_cast(d->trackPeak)) * 20 * 256 + .5); - - if (d->albumPeak != 0) - d->albumPeak = static_cast(log10(static_cast(d->albumPeak)) * 20 * 256 + .5); - - bool trueGapless = (gapless >> 31) & 0x0001; - if (trueGapless) { - unsigned int lastFrameSamples = (gapless >> 20) & 0x07FF; - d->sampleFrames = d->totalFrames * 1152 - lastFrameSamples; - } - else - d->sampleFrames = d->totalFrames * 1152 - 576; - } - else { - const unsigned int headerData = data.toUInt32LE(0); - - d->bitrate = (headerData >> 23) & 0x01ff; - d->version = (headerData >> 11) & 0x03ff; - d->sampleRate = 44100; - d->channels = 2; - - if (d->version >= 5) - d->totalFrames = data.toUInt32LE(4); - else - d->totalFrames = data.toUInt16LE(6); - - d->sampleFrames = d->totalFrames * 1152 - 576; - } - - if (d->sampleFrames > 0 && d->sampleRate > 0) { - const double length = d->sampleFrames * 1000.0 / d->sampleRate; - d->length = static_cast(length + 0.5); - - if (d->bitrate == 0) - d->bitrate = static_cast(streamLength * 8.0 / length + 0.5); - } - -} diff --git a/3rdparty/taglib/mpc/mpcproperties.h b/3rdparty/taglib/mpc/mpcproperties.h deleted file mode 100644 index 629c62db..00000000 --- a/3rdparty/taglib/mpc/mpcproperties.h +++ /dev/null @@ -1,132 +0,0 @@ -/*************************************************************************** - copyright : (C) 2004 by Allan Sandfeld Jensen - email : kde@carewolf.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_MPCPROPERTIES_H -#define TAGLIB_MPCPROPERTIES_H - -#include "taglib_export.h" -#include "audioproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -namespace MPC { - -class File; - -//! An implementation of audio property reading for MPC - -/*! - * This reads the data from an MPC stream found in the AudioProperties API. - */ - -class TAGLIB_EXPORT AudioProperties : public Strawberry_TagLib::TagLib::AudioProperties { - public: - /*! - * Create an instance of MPC::AudioProperties with the data read directly from a MPC::File. - */ - explicit AudioProperties(File *file, long long streamLength, ReadStyle style = Average); - - /*! - * Destroys this MPC::AudioProperties instance. - */ - ~AudioProperties() override; - - /*! - * Returns the length of the file in seconds. - * The length is rounded down to the nearest whole second. - * - * \see lengthInMilliseconds() - */ - int lengthInSeconds() const override; - - /*! - * Returns the length of the file in milliseconds. - * - * \see lengthInSeconds() - */ - int lengthInMilliseconds() const override; - - /*! - * Returns the average bit rate of the file in kb/s. - */ - int bitrate() const override; - - /*! - * Returns the sample rate in Hz. - */ - int sampleRate() const override; - - /*! - * Returns the number of audio channels. - */ - int channels() const override; - - /*! - * Returns the version of the bitstream (SV4-SV8) - */ - int mpcVersion() const; - - unsigned int totalFrames() const; - unsigned int sampleFrames() const; - - /*! - * Returns the track gain as an integer value, - * to convert to dB: trackGain in dB = 64.82 - (trackGain / 256) - */ - int trackGain() const; - - /*! - * Returns the track peak as an integer value, - * to convert to dB: trackPeak in dB = trackPeak / 256 - * to convert to floating [-1..1]: trackPeak = 10^(trackPeak / 256 / 20)/32768 - */ - int trackPeak() const; - - /*! - * Returns the album gain as an integer value, - * to convert to dB: albumGain in dB = 64.82 - (albumGain / 256) - */ - int albumGain() const; - - /*! - * Returns the album peak as an integer value, - * to convert to dB: albumPeak in dB = albumPeak / 256 - * to convert to floating [-1..1]: albumPeak = 10^(albumPeak / 256 / 20)/32768 - */ - int albumPeak() const; - - private: - void readSV7(const ByteVector &data, long long streamLength); - void readSV8(File *file, long long streamLength); - - class AudioPropertiesPrivate; - AudioPropertiesPrivate *d; -}; -} // namespace MPC -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v1/id3v1genres.cpp b/3rdparty/taglib/mpeg/id3v1/id3v1genres.cpp deleted file mode 100644 index b22e8992..00000000 --- a/3rdparty/taglib/mpeg/id3v1/id3v1genres.cpp +++ /dev/null @@ -1,268 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org -***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "id3v1genres.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { -const wchar_t *genres[] = { - L"Blues", - L"Classic Rock", - L"Country", - L"Dance", - L"Disco", - L"Funk", - L"Grunge", - L"Hip-Hop", - L"Jazz", - L"Metal", - L"New Age", - L"Oldies", - L"Other", - L"Pop", - L"R&B", - L"Rap", - L"Reggae", - L"Rock", - L"Techno", - L"Industrial", - L"Alternative", - L"Ska", - L"Death Metal", - L"Pranks", - L"Soundtrack", - L"Euro-Techno", - L"Ambient", - L"Trip-Hop", - L"Vocal", - L"Jazz+Funk", - L"Fusion", - L"Trance", - L"Classical", - L"Instrumental", - L"Acid", - L"House", - L"Game", - L"Sound Clip", - L"Gospel", - L"Noise", - L"Alternative Rock", - L"Bass", - L"Soul", - L"Punk", - L"Space", - L"Meditative", - L"Instrumental Pop", - L"Instrumental Rock", - L"Ethnic", - L"Gothic", - L"Darkwave", - L"Techno-Industrial", - L"Electronic", - L"Pop-Folk", - L"Eurodance", - L"Dream", - L"Southern Rock", - L"Comedy", - L"Cult", - L"Gangsta", - L"Top 40", - L"Christian Rap", - L"Pop/Funk", - L"Jungle", - L"Native American", - L"Cabaret", - L"New Wave", - L"Psychedelic", - L"Rave", - L"Showtunes", - L"Trailer", - L"Lo-Fi", - L"Tribal", - L"Acid Punk", - L"Acid Jazz", - L"Polka", - L"Retro", - L"Musical", - L"Rock & Roll", - L"Hard Rock", - L"Folk", - L"Folk/Rock", - L"National Folk", - L"Swing", - L"Fusion", - L"Bebob", - L"Latin", - L"Revival", - L"Celtic", - L"Bluegrass", - L"Avantgarde", - L"Gothic Rock", - L"Progressive Rock", - L"Psychedelic Rock", - L"Symphonic Rock", - L"Slow Rock", - L"Big Band", - L"Chorus", - L"Easy Listening", - L"Acoustic", - L"Humour", - L"Speech", - L"Chanson", - L"Opera", - L"Chamber Music", - L"Sonata", - L"Symphony", - L"Booty Bass", - L"Primus", - L"Porn Groove", - L"Satire", - L"Slow Jam", - L"Club", - L"Tango", - L"Samba", - L"Folklore", - L"Ballad", - L"Power Ballad", - L"Rhythmic Soul", - L"Freestyle", - L"Duet", - L"Punk Rock", - L"Drum Solo", - L"A Cappella", - L"Euro-House", - L"Dance Hall", - L"Goa", - L"Drum & Bass", - L"Club-House", - L"Hardcore", - L"Terror", - L"Indie", - L"BritPop", - L"Negerpunk", - L"Polsk Punk", - L"Beat", - L"Christian Gangsta Rap", - L"Heavy Metal", - L"Black Metal", - L"Crossover", - L"Contemporary Christian", - L"Christian Rock", - L"Merengue", - L"Salsa", - L"Thrash Metal", - L"Anime", - L"Jpop", - L"Synthpop", - L"Abstract", - L"Art Rock", - L"Baroque", - L"Bhangra", - L"Big Beat", - L"Breakbeat", - L"Chillout", - L"Downtempo", - L"Dub", - L"EBM", - L"Eclectic", - L"Electro", - L"Electroclash", - L"Emo", - L"Experimental", - L"Garage", - L"Global", - L"IDM", - L"Illbient", - L"Industro-Goth", - L"Jam Band", - L"Krautrock", - L"Leftfield", - L"Lounge", - L"Math Rock", - L"New Romantic", - L"Nu-Breakz", - L"Post-Punk", - L"Post-Rock", - L"Psytrance", - L"Shoegaze", - L"Space Rock", - L"Trop Rock", - L"World Music", - L"Neoclassical", - L"Audiobook", - L"Audio Theatre", - L"Neue Deutsche Welle", - L"Podcast", - L"Indie Rock", - L"G-Funk", - L"Dubstep", - L"Garage Rock", - L"Psybient" -}; -const int genresSize = sizeof(genres) / sizeof(genres[0]); -} // namespace - -StringList ID3v1::genreList() { - - StringList l; - for (int i = 0; i < genresSize; i++) { - l.append(genres[i]); - } - - return l; - -} - -ID3v1::GenreMap ID3v1::genreMap() { - - GenreMap m; - for (int i = 0; i < genresSize; i++) { - m.insert(genres[i], i); - } - - return m; - -} - -String ID3v1::genre(int i) { - - if (i >= 0 && i < genresSize) - return String(genres[i]); // always make a copy - else - return String(); - -} - -int ID3v1::genreIndex(const String &name) { - - for (int i = 0; i < genresSize; ++i) { - if (name == genres[i]) - return i; - } - - return 255; - -} diff --git a/3rdparty/taglib/mpeg/id3v1/id3v1genres.h b/3rdparty/taglib/mpeg/id3v1/id3v1genres.h deleted file mode 100644 index 1ae5a4bb..00000000 --- a/3rdparty/taglib/mpeg/id3v1/id3v1genres.h +++ /dev/null @@ -1,64 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_ID3V1GENRE_H -#define TAGLIB_ID3V1GENRE_H - -#include "tmap.h" -#include "tstringlist.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v1 { - -typedef Map GenreMap; - -/*! - * Returns the list of canonical ID3v1 genre names in the order that they are listed in the standard. - */ -StringList TAGLIB_EXPORT genreList(); - -/*! - * A "reverse mapping" that goes from the canonical ID3v1 genre name to the respective genre number. genreMap()["Rock"] == - */ -GenreMap TAGLIB_EXPORT genreMap(); - -/*! - * Returns the name of the genre at \a index in the ID3v1 genre list. - * If \a index is out of range -- less than zero or greater than 191 -- a null string will be returned. - */ -String TAGLIB_EXPORT genre(int index); - -/*! - * Returns the genre index for the (case sensitive) genre \a name. - * If the genre is not in the list 255 (which signifies an unknown genre in ID3v1) will be returned. - */ -int TAGLIB_EXPORT genreIndex(const String &name); -} // namespace ID3v1 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v1/id3v1tag.cpp b/3rdparty/taglib/mpeg/id3v1/id3v1tag.cpp deleted file mode 100644 index c734b6f1..00000000 --- a/3rdparty/taglib/mpeg/id3v1/id3v1tag.cpp +++ /dev/null @@ -1,247 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tdebug.h" -#include "tfile.h" -#include "tpicturemap.h" - -#include "id3v1tag.h" -#include "id3v1genres.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v1; - -namespace { -class DefaultStringHandler : public Strawberry_TagLib::TagLib::StringHandler { - public: - explicit DefaultStringHandler() : Strawberry_TagLib::TagLib::StringHandler() {} - - String parse(const ByteVector &data) const override { - return String(data, String::Latin1).stripWhiteSpace(); - } - - ByteVector render(const String &s) const override { - if (s.isLatin1()) - return s.data(String::Latin1); - else - return ByteVector(); - } -}; - -const DefaultStringHandler defaultStringHandler; -const Strawberry_TagLib::TagLib::StringHandler *stringHandler = &defaultStringHandler; -} // namespace - -class ID3v1::Tag::TagPrivate { - public: - explicit TagPrivate() : file(nullptr), - tagOffset(0), - track(0), - genre(255) {} - - File *file; - long long tagOffset; - - String title; - String artist; - String album; - String year; - String comment; - unsigned char track; - unsigned char genre; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public methods -//////////////////////////////////////////////////////////////////////////////// - -ID3v1::Tag::Tag() : d(new TagPrivate()) {} - -ID3v1::Tag::Tag(File *file, long long tagOffset) : d(new TagPrivate()) { - - d->file = file; - d->tagOffset = tagOffset; - - read(); -} - -ID3v1::Tag::~Tag() { - delete d; -} - -ByteVector ID3v1::Tag::render() const { - - ByteVector data; - - data.append(fileIdentifier()); - data.append(stringHandler->render(d->title).resize(30)); - data.append(stringHandler->render(d->artist).resize(30)); - data.append(stringHandler->render(d->album).resize(30)); - data.append(stringHandler->render(d->year).resize(4)); - data.append(stringHandler->render(d->comment).resize(28)); - data.append(char(0)); - data.append(char(d->track)); - data.append(char(d->genre)); - - return data; - -} - -ByteVector ID3v1::Tag::fileIdentifier() { - return ByteVector::fromCString("TAG"); -} - -String ID3v1::Tag::title() const { - return d->title; -} - -String ID3v1::Tag::artist() const { - return d->artist; -} - -String ID3v1::Tag::album() const { - return d->album; -} - -String ID3v1::Tag::comment() const { - return d->comment; -} - -String ID3v1::Tag::genre() const { - return ID3v1::genre(d->genre); -} - -unsigned int ID3v1::Tag::year() const { - return d->year.toInt(); -} - -unsigned int ID3v1::Tag::track() const { - return d->track; -} - -Strawberry_TagLib::TagLib::PictureMap ID3v1::Tag::pictures() const { - return PictureMap(); -} - -void ID3v1::Tag::setTitle(const String &s) { - d->title = s; -} - -void ID3v1::Tag::setArtist(const String &s) { - d->artist = s; -} - -void ID3v1::Tag::setAlbum(const String &s) { - d->album = s; -} - -void ID3v1::Tag::setComment(const String &s) { - d->comment = s; -} - -void ID3v1::Tag::setGenre(const String &s) { - d->genre = ID3v1::genreIndex(s); -} - -void ID3v1::Tag::setYear(unsigned int i) { - d->year = i > 0 ? String::number(i) : String(); -} - -void ID3v1::Tag::setTrack(unsigned int i) { - d->track = i < 256 ? i : 0; -} - -void ID3v1::Tag::setPictures(const PictureMap&) {} - -unsigned int ID3v1::Tag::genreNumber() const { - return d->genre; -} - -void ID3v1::Tag::setGenreNumber(unsigned int i) { - d->genre = i < 256 ? i : 255; -} - -void ID3v1::Tag::setStringHandler(const Strawberry_TagLib::TagLib::StringHandler *handler) { - if (handler) - stringHandler = handler; - else - stringHandler = &defaultStringHandler; -} - -//////////////////////////////////////////////////////////////////////////////// -// protected methods -//////////////////////////////////////////////////////////////////////////////// - -void ID3v1::Tag::read() { - - if (d->file && d->file->isValid()) { - d->file->seek(d->tagOffset); - // read the tag -- always 128 bytes - const ByteVector data = d->file->readBlock(128); - - // some initial sanity checking - if (data.size() == 128 && data.startsWith("TAG")) - parse(data); - else - debug("ID3v1 tag is not valid or could not be read at the specified offset."); - } - -} - -void ID3v1::Tag::parse(const ByteVector &data) { - - int offset = 3; - - d->title = stringHandler->parse(data.mid(offset, 30)); - offset += 30; - - d->artist = stringHandler->parse(data.mid(offset, 30)); - offset += 30; - - d->album = stringHandler->parse(data.mid(offset, 30)); - offset += 30; - - d->year = stringHandler->parse(data.mid(offset, 4)); - offset += 4; - - // Check for ID3v1.1 -- Note that ID3v1 *does not* support "track zero" -- this - // is not a bug in TagLib. Since a zeroed byte is what we would expect to - // indicate the end of a C-String, specifically the comment string, a value of - // zero must be assumed to be just that. - - if (data[offset + 28] == 0 && data[offset + 29] != 0) { - // ID3v1.1 detected - - d->comment = stringHandler->parse(data.mid(offset, 28)); - d->track = static_cast(data[offset + 29]); - } - else - d->comment = data.mid(offset, 30); - - offset += 30; - - d->genre = static_cast(data[offset]); - -} diff --git a/3rdparty/taglib/mpeg/id3v1/id3v1tag.h b/3rdparty/taglib/mpeg/id3v1/id3v1tag.h deleted file mode 100644 index a282caf7..00000000 --- a/3rdparty/taglib/mpeg/id3v1/id3v1tag.h +++ /dev/null @@ -1,154 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_ID3V1TAG_H -#define TAGLIB_ID3V1TAG_H - -#include "tag.h" -#include "tbytevector.h" -#include "tstringhandler.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -class File; - -//! An ID3v1 implementation - -namespace ID3v1 { - -//! The main class in the ID3v1 implementation - -/*! - * This is an implementation of the ID3v1 format. - * ID3v1 is both the simplest and most common of tag formats but is rather limited. - * Because of its pervasiveness and the way that applications have been written around the - * fields that it provides, the generic TagLib::Tag API is a mirror of what is provided by ID3v1. - * - * ID3v1 tags should generally only contain Latin1 information. - * However because many applications do not follow this rule there is now support for overriding - * the ID3v1 string handling using the ID3v1::StringHandler class. - * Please see the documentation for that class for more information. - * - * \see StringHandler - * - * \note Most fields are truncated to a maximum of 28-30 bytes. - * The truncation happens automatically when the tag is rendered. - */ - -class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag { - public: - /*! - * Create an ID3v1 tag with default values. - */ - explicit Tag(); - - /*! - * Create an ID3v1 tag and parse the data in \a file starting at \a tagOffset. - */ - explicit Tag(File *file, long long tagOffset); - - /*! - * Destroys this Tag instance. - */ - ~Tag() override; - - /*! - * Renders the in memory values to a ByteVector suitable for writing to the file. - */ - ByteVector render() const; - - /*! - * Returns the string "TAG" suitable for usage in locating the tag in a file. - */ - static ByteVector fileIdentifier(); - - // Reimplementations. - - String title() const override; - String artist() const override; - String album() const override; - String comment() const override; - String genre() const override; - unsigned int year() const override; - unsigned int track() const override; - PictureMap pictures() const override; - - void setTitle(const String &s) override; - void setArtist(const String &s) override; - void setAlbum(const String &s) override; - void setComment(const String &s) override; - void setGenre(const String &s) override; - void setYear(unsigned int i) override; - void setTrack(unsigned int i) override; - void setPictures(const PictureMap&) override; - - /*! - * Returns the genre in number. - * - * \note Normally 255 indicates that this tag contains no genre. - */ - unsigned int genreNumber() const; - - /*! - * Sets the genre in number to \a i. - * - * \note Valid value is from 0 up to 255. Normally 255 indicates that this tag contains no genre. - */ - void setGenreNumber(unsigned int i); - - /*! - * Sets the string handler that decides how the ID3v1 data will be converted to and from binary data. - * If the parameter \a handler is null, the previous handler is released and default ISO-8859-1 handler is restored. - * - * \note The caller is responsible for deleting the previous handler as needed after it is released. - * - */ - static void setStringHandler(const Strawberry_TagLib::TagLib::StringHandler *handler); - - protected: - /*! - * Reads from the file specified in the constructor. - */ - void read(); - /*! - * Pareses the body of the tag in \a data. - */ - void parse(const ByteVector &data); - - private: - Tag(const Tag&); - Tag &operator=(const Tag&); - - class TagPrivate; - TagPrivate *d; -}; - -} // namespace ID3v1 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp deleted file mode 100644 index 774625a1..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "attachedpictureframe.h" - -#include "tstringlist.h" -#include "tdebug.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -class AttachedPictureFrame::AttachedPictureFramePrivate { - public: - explicit AttachedPictureFramePrivate() : textEncoding(String::Latin1), type(AttachedPictureFrame::Other) {} - - String::Type textEncoding; - String mimeType; - AttachedPictureFrame::Type type; - String description; - ByteVector data; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -AttachedPictureFrame::AttachedPictureFrame() : Frame("APIC"), d(new AttachedPictureFramePrivate()) {} - -AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data) : Frame(data), d(new AttachedPictureFramePrivate()) { - setData(data); -} - -AttachedPictureFrame::~AttachedPictureFrame() { - delete d; -} - -String AttachedPictureFrame::toString() const { - String s = "[" + d->mimeType + "]"; - return d->description.isEmpty() ? s : d->description + " " + s; -} - -String::Type AttachedPictureFrame::textEncoding() const { - return d->textEncoding; -} - -void AttachedPictureFrame::setTextEncoding(String::Type t) { - d->textEncoding = t; -} - -String AttachedPictureFrame::mimeType() const { - return d->mimeType; -} - -void AttachedPictureFrame::setMimeType(const String &m) { - d->mimeType = m; -} - -AttachedPictureFrame::Type AttachedPictureFrame::type() const { - return d->type; -} - -void AttachedPictureFrame::setType(Type t) { - d->type = t; -} - -String AttachedPictureFrame::description() const { - return d->description; -} - -void AttachedPictureFrame::setDescription(const String &desc) { - d->description = desc; -} - -ByteVector AttachedPictureFrame::picture() const { - return d->data; -} - -void AttachedPictureFrame::setPicture(const ByteVector &p) { - d->data = p; -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void AttachedPictureFrame::parseFields(const ByteVector &data) { - - if (data.size() < 5) { - debug("A picture frame must contain at least 5 bytes."); - return; - } - - d->textEncoding = String::Type(data[0]); - - size_t pos = 1; - - d->mimeType = readStringField(data, String::Latin1, pos); - /* Now we need at least two more bytes available */ - if (pos + 1 >= data.size()) { - debug("Truncated picture frame."); - return; - } - - d->type = static_cast(data[pos++]); - d->description = readStringField(data, d->textEncoding, pos); - - d->data = data.mid(pos); - -} - -ByteVector AttachedPictureFrame::renderFields() const { - - ByteVector data; - - String::Type encoding = checkTextEncoding(d->description, d->textEncoding); - - data.append(char(encoding)); - data.append(d->mimeType.data(String::Latin1)); - data.append(textDelimiter(String::Latin1)); - data.append(char(d->type)); - data.append(d->description.data(encoding)); - data.append(textDelimiter(encoding)); - data.append(d->data); - - return data; - -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data, Header *h) : Frame(h), d(new AttachedPictureFramePrivate()) { - parseFields(fieldData(data)); -} - -//////////////////////////////////////////////////////////////////////////////// -// support for ID3v2.2 PIC frames -//////////////////////////////////////////////////////////////////////////////// - -void AttachedPictureFrameV22::parseFields(const ByteVector &data) { - - if (data.size() < 5) { - debug("A picture frame must contain at least 5 bytes."); - return; - } - - d->textEncoding = String::Type(data[0]); - - size_t pos = 1; - - String fixedString = String(data.mid(pos, 3), String::Latin1); - pos += 3; - // convert fixed string image type to mime string - if (fixedString.upper() == "JPG") { - d->mimeType = "image/jpeg"; - } - else if (fixedString.upper() == "PNG") { - d->mimeType = "image/png"; - } - else { - debug("probably unsupported image type"); - d->mimeType = "image/" + fixedString; - } - - d->type = static_cast(data[pos++]); - d->description = readStringField(data, d->textEncoding, pos); - - d->data = data.mid(pos); - -} - -AttachedPictureFrameV22::AttachedPictureFrameV22(const ByteVector &data, Header *h) { - - // set v2.2 header to make fieldData work correctly - setHeader(h, true); - - parseFields(fieldData(data)); - - // now set the v2.4 header - Frame::Header *newHeader = new Frame::Header("APIC"); - newHeader->setFrameSize(h->frameSize()); - setHeader(newHeader, true); - -} diff --git a/3rdparty/taglib/mpeg/id3v2/frames/attachedpictureframe.h b/3rdparty/taglib/mpeg/id3v2/frames/attachedpictureframe.h deleted file mode 100644 index 194c72fc..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/attachedpictureframe.h +++ /dev/null @@ -1,226 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_ATTACHEDPICTUREFRAME_H -#define TAGLIB_ATTACHEDPICTUREFRAME_H - -#include "id3v2frame.h" -#include "id3v2header.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -//! An ID3v2 attached picture frame implementation - -/*! - * This is an implementation of ID3v2 attached pictures. - * Pictures may be included in tags, one per APIC frame (but there may be multiple APIC frames in a single tag). - * These pictures are usually in either JPEG or PNG format. - */ - -class TAGLIB_EXPORT AttachedPictureFrame : public Frame { - friend class FrameFactory; - - public: - /*! - * This describes the function or content of the picture. - */ - enum Type { - //! A type not enumerated below - Other = 0x00, - //! 32x32 PNG image that should be used as the file icon - FileIcon = 0x01, - //! File icon of a different size or format - OtherFileIcon = 0x02, - //! Front cover image of the album - FrontCover = 0x03, - //! Back cover image of the album - BackCover = 0x04, - //! Inside leaflet page of the album - LeafletPage = 0x05, - //! Image from the album itself - Media = 0x06, - //! Picture of the lead artist or soloist - LeadArtist = 0x07, - //! Picture of the artist or performer - Artist = 0x08, - //! Picture of the conductor - Conductor = 0x09, - //! Picture of the band or orchestra - Band = 0x0A, - //! Picture of the composer - Composer = 0x0B, - //! Picture of the lyricist or text writer - Lyricist = 0x0C, - //! Picture of the recording location or studio - RecordingLocation = 0x0D, - //! Picture of the artists during recording - DuringRecording = 0x0E, - //! Picture of the artists during performance - DuringPerformance = 0x0F, - //! Picture from a movie or video related to the track - MovieScreenCapture = 0x10, - //! Picture of a large, coloured fish - ColouredFish = 0x11, - //! Illustration related to the track - Illustration = 0x12, - //! Logo of the band or performer - BandLogo = 0x13, - //! Logo of the publisher (record company) - PublisherLogo = 0x14 - }; - - /*! - * Constructs an empty picture frame. - * The description, content and text encoding should be set manually. - */ - explicit AttachedPictureFrame(); - - /*! - * Constructs an AttachedPicture frame based on \a data. - */ - explicit AttachedPictureFrame(const ByteVector &data); - - /*! - * Destroys the AttahcedPictureFrame instance. - */ - ~AttachedPictureFrame() override; - - /*! - * Returns a string containing the description and mime-type - */ - String toString() const override; - - /*! - * Returns the text encoding used for the description. - * - * \see setTextEncoding() - * \see description() - */ - String::Type textEncoding() const; - - /*! - * Set the text encoding used for the description. - * - * \see description() - */ - void setTextEncoding(String::Type t); - - /*! - * Returns the mime type of the image. - * This should in most cases be "image/png" or "image/jpeg". - */ - String mimeType() const; - - /*! - * Sets the mime type of the image. - * This should in most cases be "image/png" or "image/jpeg". - */ - void setMimeType(const String &m); - - /*! - * Returns the type of the image. - * - * \see Type - * \see setType() - */ - Type type() const; - - /*! - * Sets the type for the image. - * - * \see Type - * \see type() - */ - void setType(Type t); - - /*! - * Returns a text description of the image. - * - * \see setDescription() - * \see textEncoding() - * \see setTextEncoding() - */ - - String description() const; - - /*! - * Sets a textual description of the image to \a desc. - * - * \see description() - * \see textEncoding() - * \see setTextEncoding() - */ - - void setDescription(const String &desc); - - /*! - * Returns the image data as a ByteVector. - * - * \note ByteVector has a data() method that returns a const char * which should make it easy to export this data to external programs. - * - * \see setPicture() - * \see mimeType() - */ - ByteVector picture() const; - - /*! - * Sets the image data to \a p. \a p should be of the type specified in this frame's mime-type specification. - * - * \see picture() - * \see mimeType() - * \see setMimeType() - */ - void setPicture(const ByteVector &p); - - protected: - void parseFields(const ByteVector &data) override; - ByteVector renderFields() const override; - class AttachedPictureFramePrivate; - AttachedPictureFramePrivate *d; - - private: - AttachedPictureFrame(const AttachedPictureFrame&); - AttachedPictureFrame &operator=(const AttachedPictureFrame&); - explicit AttachedPictureFrame(const ByteVector &data, Header *h); -}; - -//! support for ID3v2.2 PIC frames -class TAGLIB_EXPORT AttachedPictureFrameV22 : public AttachedPictureFrame { - protected: - void parseFields(const ByteVector &data) override; - - private: - explicit AttachedPictureFrameV22(const ByteVector &data, Header *h); - friend class FrameFactory; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/frames/chapterframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/chapterframe.cpp deleted file mode 100644 index 493e04ea..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/chapterframe.cpp +++ /dev/null @@ -1,293 +0,0 @@ -/*************************************************************************** - copyright : (C) 2013 by Lukas Krejci - email : krejclu6@fel.cvut.cz - ***************************************************************************/ - -/*************************************************************************** - * 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 - -#include "tbytevectorlist.h" -#include "tpropertymap.h" -#include "tdebug.h" - -#include "chapterframe.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -class ChapterFrame::ChapterFramePrivate { - public: - explicit ChapterFramePrivate() : tagHeader(nullptr), - startTime(0), - endTime(0), - startOffset(0), - endOffset(0) { - embeddedFrameList.setAutoDelete(true); - } - - const ID3v2::Header *tagHeader; - ByteVector elementID; - unsigned int startTime; - unsigned int endTime; - unsigned int startOffset; - unsigned int endOffset; - FrameListMap embeddedFrameListMap; - FrameList embeddedFrameList; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public methods -//////////////////////////////////////////////////////////////////////////////// - -ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data) : ID3v2::Frame(data), d(new ChapterFramePrivate()) { - d->tagHeader = tagHeader; - setData(data); -} - -ChapterFrame::ChapterFrame(const ByteVector &elementID, unsigned int startTime, unsigned int endTime, unsigned int startOffset, unsigned int endOffset, const FrameList &embeddedFrames) : ID3v2::Frame("CHAP"), d(new ChapterFramePrivate()) { - - // setElementID has a workaround for a previously silly API where you had to specifically include the null byte. - - setElementID(elementID); - - d->startTime = startTime; - d->endTime = endTime; - d->startOffset = startOffset; - d->endOffset = endOffset; - - for (FrameList::ConstIterator it = embeddedFrames.begin(); - it != embeddedFrames.end(); - ++it) - addEmbeddedFrame(*it); - -} - -ChapterFrame::~ChapterFrame() { - delete d; -} - -ByteVector ChapterFrame::elementID() const { - return d->elementID; -} - -unsigned int ChapterFrame::startTime() const { - return d->startTime; -} - -unsigned int ChapterFrame::endTime() const { - return d->endTime; -} - -unsigned int ChapterFrame::startOffset() const { - return d->startOffset; -} - -unsigned int ChapterFrame::endOffset() const { - return d->endOffset; -} - -void ChapterFrame::setElementID(const ByteVector &eID) { - - d->elementID = eID; - - if (d->elementID.endsWith(char(0))) - d->elementID = d->elementID.mid(0, d->elementID.size() - 1); - -} - -void ChapterFrame::setStartTime(const unsigned int &sT) { - d->startTime = sT; -} - -void ChapterFrame::setEndTime(const unsigned int &eT) { - d->endTime = eT; -} - -void ChapterFrame::setStartOffset(const unsigned int &sO) { - d->startOffset = sO; -} - -void ChapterFrame::setEndOffset(const unsigned int &eO) { - d->endOffset = eO; -} - -const FrameListMap &ChapterFrame::embeddedFrameListMap() const { - return d->embeddedFrameListMap; -} - -const FrameList &ChapterFrame::embeddedFrameList() const { - return d->embeddedFrameList; -} - -const FrameList &ChapterFrame::embeddedFrameList(const ByteVector &frameID) const { - return d->embeddedFrameListMap[frameID]; -} - -void ChapterFrame::addEmbeddedFrame(Frame *frame) { - d->embeddedFrameList.append(frame); - d->embeddedFrameListMap[frame->frameID()].append(frame); -} - -void ChapterFrame::removeEmbeddedFrame(Frame *frame, bool del) { - - // remove the frame from the frame list - FrameList::Iterator it = d->embeddedFrameList.find(frame); - d->embeddedFrameList.erase(it); - - // ...and from the frame list map - it = d->embeddedFrameListMap[frame->frameID()].find(frame); - d->embeddedFrameListMap[frame->frameID()].erase(it); - - // ...and delete as desired - if (del) - delete frame; - -} - -void ChapterFrame::removeEmbeddedFrames(const ByteVector &id) { - - FrameList l = d->embeddedFrameListMap[id]; - for (FrameList::ConstIterator it = l.begin(); it != l.end(); ++it) - removeEmbeddedFrame(*it, true); - -} - -String ChapterFrame::toString() const { - - String s = String(d->elementID) + - ": start time: " + String::number(d->startTime) + - ", end time: " + String::number(d->endTime); - - if (d->startOffset != 0xFFFFFFFF) - s += ", start offset: " + String::number(d->startOffset); - - if (d->endOffset != 0xFFFFFFFF) - s += ", end offset: " + String::number(d->endOffset); - - if (!d->embeddedFrameList.isEmpty()) { - StringList frameIDs; - for (FrameList::ConstIterator it = d->embeddedFrameList.begin(); - it != d->embeddedFrameList.end(); - ++it) - frameIDs.append((*it)->frameID()); - s += ", sub-frames: [ " + frameIDs.toString(", ") + " ]"; - } - - return s; - -} - -PropertyMap ChapterFrame::asProperties() const { - - PropertyMap map; - - map.unsupportedData().append(frameID() + String("/") + d->elementID); - - return map; - -} - -ChapterFrame *ChapterFrame::findByElementID(const ID3v2::Tag *tag, const ByteVector &eID) { // static - - ID3v2::FrameList comments = tag->frameList("CHAP"); - - for (ID3v2::FrameList::ConstIterator it = comments.begin(); - it != comments.end(); - ++it) { - ChapterFrame *frame = dynamic_cast(*it); - if (frame && frame->elementID() == eID) - return frame; - } - - return nullptr; - -} - -void ChapterFrame::parseFields(const ByteVector &data) { - - size_t size = data.size(); - if (size < 18) { - debug("A CHAP frame must contain at least 18 bytes (1 byte element ID " - "terminated by null and 4x4 bytes for start and end time and offset)."); - return; - } - - size_t pos = 0; - size_t embPos = 0; - d->elementID = readStringField(data, String::Latin1, pos).data(String::Latin1); - d->startTime = data.toUInt32BE(pos); - pos += 4; - d->endTime = data.toUInt32BE(pos); - pos += 4; - d->startOffset = data.toUInt32BE(pos); - pos += 4; - d->endOffset = data.toUInt32BE(pos); - pos += 4; - size -= pos; - - // Embedded frames are optional - - if (size < header()->size()) - return; - - while (embPos < size - header()->size()) { - Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos), d->tagHeader); - - if (!frame) - return; - - // Checks to make sure that frame parsed correctly. - if (frame->size() <= 0) { - delete frame; - return; - } - - embPos += frame->size() + header()->size(); - addEmbeddedFrame(frame); - } - -} - -ByteVector ChapterFrame::renderFields() const { - - ByteVector data; - - data.append(d->elementID); - data.append('\0'); - data.append(ByteVector::fromUInt32BE(d->startTime)); - data.append(ByteVector::fromUInt32BE(d->endTime)); - data.append(ByteVector::fromUInt32BE(d->startOffset)); - data.append(ByteVector::fromUInt32BE(d->endOffset)); - FrameList l = d->embeddedFrameList; - for (FrameList::ConstIterator it = l.begin(); it != l.end(); ++it) - data.append((*it)->render()); - - return data; - -} - -ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h) : Frame(h), d(new ChapterFramePrivate()) { - - d->tagHeader = tagHeader; - parseFields(fieldData(data)); - -} diff --git a/3rdparty/taglib/mpeg/id3v2/frames/chapterframe.h b/3rdparty/taglib/mpeg/id3v2/frames/chapterframe.h deleted file mode 100644 index 53bdd6d1..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/chapterframe.h +++ /dev/null @@ -1,233 +0,0 @@ -/*************************************************************************** - copyright : (C) 2013 by Lukas Krejci - email : krejclu6@fel.cvut.cz - ***************************************************************************/ - -/*************************************************************************** - * 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_CHAPTERFRAME -#define TAGLIB_CHAPTERFRAME - -#include "id3v2tag.h" -#include "id3v2frame.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -/*! - * This is an implementation of ID3v2 chapter frames. - * The purpose of this frame is to describe a single chapter within an audio file. - */ - -//! An implementation of ID3v2 chapter frames - -class TAGLIB_EXPORT ChapterFrame : public ID3v2::Frame { - friend class FrameFactory; - - public: - /*! - * Creates a chapter frame based on \a data. - * \a tagHeader is required as the internal frames are parsed based on the tag version. - */ - explicit ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data); - - /*! - * Creates a chapter frame with the element ID \a elementID, - * start time \a startTime, end time \a endTime, start offset \a startOffset, - * end offset \a endOffset and optionally a list of embedded frames, - * whose ownership will then be taken over by this Frame, in \a embeededFrames; - * - * All times are in milliseconds. - */ - explicit ChapterFrame(const ByteVector &elementID, unsigned int startTime, unsigned int endTime, unsigned int startOffset, unsigned int endOffset, const FrameList &embeddedFrames = FrameList()); - - /*! - * Destroys the frame. - */ - ~ChapterFrame() override; - - /*! - * Returns the element ID of the frame. - * Element ID is a null terminated string, however it's not human-readable. - * - * \see setElementID() - */ - ByteVector elementID() const; - - /*! - * Returns time of chapter's start (in milliseconds). - * - * \see setStartTime() - */ - unsigned int startTime() const; - - /*! - * Returns time of chapter's end (in milliseconds). - * - * \see setEndTime() - */ - unsigned int endTime() const; - - /*! - * Returns zero based byte offset (count of bytes from the beginning of the audio file) of chapter's start. - * - * \note If returned value is 0xFFFFFFFF, start time should be used instead. - * \see setStartOffset() - */ - unsigned int startOffset() const; - - /*! - * Returns zero based byte offset (count of bytes from the beginning of the audio file) of chapter's end. - * - * \note If returned value is 0xFFFFFFFF, end time should be used instead. - * \see setEndOffset() - */ - unsigned int endOffset() const; - - /*! - * Sets the element ID of the frame to \a eID. If \a eID isn't null terminated, a null char is appended automatically. - * - * \see elementID() - */ - void setElementID(const ByteVector &eID); - - /*! - * Sets time of chapter's start (in milliseconds) to \a sT. - * - * \see startTime() - */ - void setStartTime(const unsigned int &sT); - - /*! - * Sets time of chapter's end (in milliseconds) to \a eT. - * - * \see endTime() - */ - void setEndTime(const unsigned int &eT); - - /*! - * Sets zero based byte offset (count of bytes from the beginning of the audio file) of chapter's start to \a sO. - * - * \see startOffset() - */ - void setStartOffset(const unsigned int &sO); - - /*! - * Sets zero based byte offset (count of bytes from the beginning of the audio file) of chapter's end to \a eO. - * - * \see endOffset() - */ - void setEndOffset(const unsigned int &eO); - - /*! - * Returns a reference to the frame list map. - * This is an FrameListMap of all of the frames embedded in the CHAP frame. - * - * This is the most convenient structure for accessing the CHAP frame's embedded frames. - * Many frame types allow multiple instances of the same rame type so this is a map of lists. - * In most cases however there will only be a single frame of a certain type. - * - * \warning You should not modify this data structure directly, instead - * use addEmbeddedFrame() and removeEmbeddedFrame(). - * - * \see embeddedFrameList() - */ - const FrameListMap &embeddedFrameListMap() const; - - /*! - * Returns a reference to the embedded frame list. - * This is an FrameList of all of the frames embedded in the CHAP frame in the order that they were parsed. - * - * This can be useful if for example you want iterate over the CHAP frame's - * embedded frames in the order that they occur in the CHAP frame. - * - * \warning You should not modify this data structure directly, - * instead use addEmbeddedFrame() and removeEmbeddedFrame(). - */ - const FrameList &embeddedFrameList() const; - - /*! - * Returns the embedded frame list for frames with the id \a frameID or an empty list if there are no embedded frames of that type. - * This is just a convenience and is equivalent to: - * - * \code - * embeddedFrameListMap()[frameID]; - * \endcode - * - * \see embeddedFrameListMap() - */ - const FrameList &embeddedFrameList(const ByteVector &frameID) const; - - /*! - * Add an embedded frame to the CHAP frame. - * At this point the CHAP frame takes ownership of the embedded frame and will handle freeing its memory. - * - * \note Using this method will invalidate any pointers on the list returned by embeddedFrameList() - */ - void addEmbeddedFrame(Frame *frame); - - /*! - * Remove an embedded frame from the CHAP frame. - * If \a del is true the frame's memory will be freed; if it is false, it must be deleted by the user. - * - * \note Using this method will invalidate any pointers on the list returned by embeddedFrameList() - */ - void removeEmbeddedFrame(Frame *frame, bool del = true); - - /*! - * Remove all embedded frames of type \a id from the CHAP frame and free their memory. - * - * \note Using this method will invalidate any pointers on the list returned by embeddedFrameList() - */ - void removeEmbeddedFrames(const ByteVector &id); - - String toString() const override; - - PropertyMap asProperties() const override; - - /*! - * CHAP frames each have a unique element ID. This searches for a CHAP frame with the element ID \a eID and returns a pointer to it. - * This can be used to link CTOC and CHAP frames together. - * - * \see elementID() - */ - static ChapterFrame *findByElementID(const Tag *tag, const ByteVector &eID); - - protected: - void parseFields(const ByteVector &data) override; - ByteVector renderFields() const override; - - private: - explicit ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h); - ChapterFrame(const ChapterFrame&); - ChapterFrame &operator=(const ChapterFrame&); - - class ChapterFramePrivate; - ChapterFramePrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/frames/commentsframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/commentsframe.cpp deleted file mode 100644 index cf47eeaa..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/commentsframe.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tbytevectorlist.h" -#include "id3v2tag.h" -#include "tdebug.h" -#include "tstringlist.h" - -#include "commentsframe.h" -#include "tpropertymap.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -class CommentsFrame::CommentsFramePrivate { - public: - explicit CommentsFramePrivate() : textEncoding(String::Latin1) {} - String::Type textEncoding; - ByteVector language; - String description; - String text; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -CommentsFrame::CommentsFrame(String::Type encoding) : Frame("COMM"), d(new CommentsFramePrivate()) { - d->textEncoding = encoding; -} - -CommentsFrame::CommentsFrame(const ByteVector &data) : Frame(data), d(new CommentsFramePrivate()) { - setData(data); -} - -CommentsFrame::~CommentsFrame() { - delete d; -} - -String CommentsFrame::toString() const { - return d->text; -} - -ByteVector CommentsFrame::language() const { - return d->language; -} - -String CommentsFrame::description() const { - return d->description; -} - -String CommentsFrame::text() const { - return d->text; -} - -void CommentsFrame::setLanguage(const ByteVector &languageEncoding) { - d->language = languageEncoding.mid(0, 3); -} - -void CommentsFrame::setDescription(const String &s) { - d->description = s; -} - -void CommentsFrame::setText(const String &s) { - d->text = s; -} - -String::Type CommentsFrame::textEncoding() const { - return d->textEncoding; -} - -void CommentsFrame::setTextEncoding(String::Type encoding) { - d->textEncoding = encoding; -} - -PropertyMap CommentsFrame::asProperties() const { - - String key = description().upper(); - PropertyMap map; - if (key.isEmpty() || key == "COMMENT") - map.insert("COMMENT", text()); - else - map.insert("COMMENT:" + key, text()); - return map; - -} - -CommentsFrame *CommentsFrame::findByDescription(const ID3v2::Tag *tag, const String &d) { // static - - ID3v2::FrameList comments = tag->frameList("COMM"); - - for (ID3v2::FrameList::ConstIterator it = comments.begin(); - it != comments.end(); - ++it) { - CommentsFrame *frame = dynamic_cast(*it); - if (frame && frame->description() == d) - return frame; - } - - return nullptr; - -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void CommentsFrame::parseFields(const ByteVector &data) { - - if (data.size() < 5) { - debug("A comment frame must contain at least 5 bytes."); - return; - } - - d->textEncoding = String::Type(data[0]); - d->language = data.mid(1, 3); - - int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2; - - ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2); - - if (l.size() == 2) { - if (d->textEncoding == String::Latin1) { - d->description = Tag::latin1StringHandler()->parse(l.front()); - d->text = Tag::latin1StringHandler()->parse(l.back()); - } - else { - d->description = String(l.front(), d->textEncoding); - d->text = String(l.back(), d->textEncoding); - } - } - -} - -ByteVector CommentsFrame::renderFields() const { - - ByteVector v; - - String::Type encoding = d->textEncoding; - - encoding = checkTextEncoding(d->description, encoding); - encoding = checkTextEncoding(d->text, encoding); - - v.append(char(encoding)); - v.append(d->language.size() == 3 ? d->language : "XXX"); - v.append(d->description.data(encoding)); - v.append(textDelimiter(encoding)); - v.append(d->text.data(encoding)); - - return v; - -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -CommentsFrame::CommentsFrame(const ByteVector &data, Header *h) : Frame(h), d(new CommentsFramePrivate()) { - parseFields(fieldData(data)); -} diff --git a/3rdparty/taglib/mpeg/id3v2/frames/commentsframe.h b/3rdparty/taglib/mpeg/id3v2/frames/commentsframe.h deleted file mode 100644 index d0c8c6f8..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/commentsframe.h +++ /dev/null @@ -1,176 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_COMMENTSFRAME_H -#define TAGLIB_COMMENTSFRAME_H - -#include "id3v2frame.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -//! An implementation of ID3v2 comments - -/*! - * This implements the ID3v2 comment format. An ID3v2 comment consists of - * a language encoding, a description and a single text field. - */ - -class TAGLIB_EXPORT CommentsFrame : public Frame { - friend class FrameFactory; - - public: - /*! - * Construct an empty comment frame that will use the text encoding \a encoding. - */ - explicit CommentsFrame(String::Type encoding = String::Latin1); - - /*! - * Construct a comment based on the data in \a data. - */ - explicit CommentsFrame(const ByteVector &data); - - /*! - * Destroys this CommentFrame instance. - */ - ~CommentsFrame() override; - - /*! - * Returns the text of this comment. - * - * \see text() - */ - String toString() const override; - - /*! - * Returns the language encoding as a 3 byte encoding as specified by - * ISO-639-2. - * - * \note Most taggers simply ignore this value. - * - * \see setLanguage() - */ - ByteVector language() const; - - /*! - * Returns the description of this comment. - * - * \note Most taggers simply ignore this value. - * - * \see setDescription() - */ - String description() const; - - /*! - * Returns the text of this comment. - * - * \see setText() - */ - String text() const; - - /*! - * Set the language using the 3 byte language code from - * ISO-639-2 to \a languageCode. - * - * \see language() - */ - void setLanguage(const ByteVector &languageEncoding); - - /*! - * Sets the description of the comment to \a s. - * - * \see description() - */ - void setDescription(const String &s); - - /*! - * Sets the text portion of the comment to \a s. - * - * \see text() - */ - void setText(const String &s) override; - - /*! - * Returns the text encoding that will be used in rendering this frame. - * This defaults to the type that was either specified in the constructor or read from the frame when parsed. - * - * \see setTextEncoding() - * \see render() - */ - String::Type textEncoding() const; - - /*! - * Sets the text encoding to be used when rendering this frame to \a encoding. - * - * \see textEncoding() - * \see render() - */ - void setTextEncoding(String::Type encoding); - - /*! - * Parses this frame as PropertyMap with a single key. - * - if description() is empty or "COMMENT", the key will be "COMMENT" - * - if description() is not a valid PropertyMap key, the frame will be - * marked unsupported by an entry "COMM/" in the unsupportedData() - * attribute of the returned map. - * - otherwise, the key will be "COMMENT:" - * - The single value will be the frame's text(). - */ - PropertyMap asProperties() const override; - - /*! - * Comments each have a unique description. - * This searches for a comment frame with the description \a d and returns a pointer to it. - * If no frame is found that matches the given description null is returned. - * - * \see description() - */ - static CommentsFrame *findByDescription(const Tag *tag, const String &d); - - protected: - // Reimplementations. - - void parseFields(const ByteVector &data) override; - ByteVector renderFields() const override; - - private: - /*! - * The constructor used by the FrameFactory. - */ - explicit CommentsFrame(const ByteVector &data, Header *h); - CommentsFrame(const CommentsFrame &); - CommentsFrame &operator=(const CommentsFrame &); - - class CommentsFramePrivate; - CommentsFramePrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp deleted file mode 100644 index 15b26cac..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/*************************************************************************** - copyright : (C) 2014 by Urs Fleisch - email : ufleisch@users.sourceforge.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "eventtimingcodesframe.h" -#include "tbytevectorlist.h" -#include "id3v2tag.h" -#include "tdebug.h" -#include "tpropertymap.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -class EventTimingCodesFrame::EventTimingCodesFramePrivate { - public: - explicit EventTimingCodesFramePrivate() : timestampFormat(EventTimingCodesFrame::AbsoluteMilliseconds) {} - EventTimingCodesFrame::TimestampFormat timestampFormat; - EventTimingCodesFrame::SynchedEventList synchedEvents; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -EventTimingCodesFrame::EventTimingCodesFrame() : Frame("ETCO"), d(new EventTimingCodesFramePrivate()) {} - -EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data) : Frame(data), d(new EventTimingCodesFramePrivate()) { - setData(data); -} - -EventTimingCodesFrame::~EventTimingCodesFrame() { - delete d; -} - -String EventTimingCodesFrame::toString() const { - return String(); -} - -EventTimingCodesFrame::TimestampFormat -EventTimingCodesFrame::timestampFormat() const { - return d->timestampFormat; -} - -EventTimingCodesFrame::SynchedEventList -EventTimingCodesFrame::synchedEvents() const { - return d->synchedEvents; -} - -void EventTimingCodesFrame::setTimestampFormat( - EventTimingCodesFrame::TimestampFormat f) { - d->timestampFormat = f; -} - -void EventTimingCodesFrame::setSynchedEvents( - const EventTimingCodesFrame::SynchedEventList &e) { - d->synchedEvents = e; -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void EventTimingCodesFrame::parseFields(const ByteVector &data) { - - const size_t end = data.size(); - if (end < 1) { - debug("An event timing codes frame must contain at least 1 byte."); - return; - } - - d->timestampFormat = TimestampFormat(data[0]); - - size_t pos = 1; - d->synchedEvents.clear(); - while (pos + 4 < end) { - EventType type = static_cast(static_cast(data[pos++])); - unsigned int time = data.toUInt32BE(pos); - pos += 4; - d->synchedEvents.append(SynchedEvent(time, type)); - } - -} - -ByteVector EventTimingCodesFrame::renderFields() const { - - ByteVector v; - - v.append(char(d->timestampFormat)); - for (SynchedEventList::ConstIterator it = d->synchedEvents.begin(); it != d->synchedEvents.end(); ++it) { - const SynchedEvent &entry = *it; - v.append(char(entry.type)); - v.append(ByteVector::fromUInt32BE(entry.time)); - } - - return v; - -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data, Header *h) : Frame(h), d(new EventTimingCodesFramePrivate()) { - parseFields(fieldData(data)); -} diff --git a/3rdparty/taglib/mpeg/id3v2/frames/eventtimingcodesframe.h b/3rdparty/taglib/mpeg/id3v2/frames/eventtimingcodesframe.h deleted file mode 100644 index 280c8d96..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/eventtimingcodesframe.h +++ /dev/null @@ -1,185 +0,0 @@ -/*************************************************************************** - copyright : (C) 2014 by Urs Fleisch - email : ufleisch@users.sourceforge.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_EVENTTIMINGCODESFRAME_H -#define TAGLIB_EVENTTIMINGCODESFRAME_H - -#include "id3v2frame.h" -#include "tlist.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -//! ID3v2 event timing codes frame -/*! - * An implementation of ID3v2 event timing codes. - */ -class TAGLIB_EXPORT EventTimingCodesFrame : public Frame { - friend class FrameFactory; - - public: - /*! - * Specifies the timestamp format used. - */ - enum TimestampFormat { - //! The timestamp is of unknown format. - Unknown = 0x00, - //! The timestamp represents the number of MPEG frames since - //! the beginning of the audio stream. - AbsoluteMpegFrames = 0x01, - //! The timestamp represents the number of milliseconds since - //! the beginning of the audio stream. - AbsoluteMilliseconds = 0x02 - }; - - /*! - * Event types defined in id3v2.4.0-frames.txt 4.5. Event timing codes. - */ - enum EventType { - Padding = 0x00, - EndOfInitialSilence = 0x01, - IntroStart = 0x02, - MainPartStart = 0x03, - OutroStart = 0x04, - OutroEnd = 0x05, - VerseStart = 0x06, - RefrainStart = 0x07, - InterludeStart = 0x08, - ThemeStart = 0x09, - VariationStart = 0x0a, - KeyChange = 0x0b, - TimeChange = 0x0c, - MomentaryUnwantedNoise = 0x0d, - SustainedNoise = 0x0e, - SustainedNoiseEnd = 0x0f, - IntroEnd = 0x10, - MainPartEnd = 0x11, - VerseEnd = 0x12, - RefrainEnd = 0x13, - ThemeEnd = 0x14, - Profanity = 0x15, - ProfanityEnd = 0x16, - NotPredefinedSynch0 = 0xe0, - NotPredefinedSynch1 = 0xe1, - NotPredefinedSynch2 = 0xe2, - NotPredefinedSynch3 = 0xe3, - NotPredefinedSynch4 = 0xe4, - NotPredefinedSynch5 = 0xe5, - NotPredefinedSynch6 = 0xe6, - NotPredefinedSynch7 = 0xe7, - NotPredefinedSynch8 = 0xe8, - NotPredefinedSynch9 = 0xe9, - NotPredefinedSynchA = 0xea, - NotPredefinedSynchB = 0xeb, - NotPredefinedSynchC = 0xec, - NotPredefinedSynchD = 0xed, - NotPredefinedSynchE = 0xee, - NotPredefinedSynchF = 0xef, - AudioEnd = 0xfd, - AudioFileEnds = 0xfe - }; - - /*! - * Single entry of time stamp and event. - */ - struct SynchedEvent { - SynchedEvent(unsigned int ms, EventType t) : time(ms), type(t) {} - unsigned int time; - EventType type; - }; - - /*! - * List of synchronized events. - */ - typedef Strawberry_TagLib::TagLib::List SynchedEventList; - - /*! - * Construct an empty event timing codes frame. - */ - explicit EventTimingCodesFrame(); - - /*! - * Construct a event timing codes frame based on the data in \a data. - */ - explicit EventTimingCodesFrame(const ByteVector &data); - - /*! - * Destroys this EventTimingCodesFrame instance. - */ - ~EventTimingCodesFrame() override; - - /*! - * Returns a null string. - */ - String toString() const override; - - /*! - * Returns the timestamp format. - */ - TimestampFormat timestampFormat() const; - - /*! - * Returns the events with the time stamps. - */ - SynchedEventList synchedEvents() const; - - /*! - * Set the timestamp format. - * - * \see timestampFormat() - */ - void setTimestampFormat(TimestampFormat f); - - /*! - * Sets the text with the time stamps. - * - * \see text() - */ - void setSynchedEvents(const SynchedEventList &e); - - protected: - // Reimplementations. - - void parseFields(const ByteVector &data) override; - ByteVector renderFields() const override; - - private: - /*! - * The constructor used by the FrameFactory. - */ - explicit EventTimingCodesFrame(const ByteVector &data, Header *h); - EventTimingCodesFrame(const EventTimingCodesFrame&); - EventTimingCodesFrame &operator=(const EventTimingCodesFrame&); - - class EventTimingCodesFramePrivate; - EventTimingCodesFramePrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp deleted file mode 100644 index 31671135..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - - copyright : (C) 2006 by Aaron VonderHaar - email : avh4@users.sourceforge.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tdebug.h" -#include "tstringlist.h" - -#include "generalencapsulatedobjectframe.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -class GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFramePrivate { - public: - explicit GeneralEncapsulatedObjectFramePrivate() : textEncoding(String::Latin1) {} - - String::Type textEncoding; - String mimeType; - String fileName; - String description; - ByteVector data; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame() : Frame("GEOB"), d(new GeneralEncapsulatedObjectFramePrivate()) {} - -GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data) : Frame(data), d(new GeneralEncapsulatedObjectFramePrivate()) { - setData(data); -} - -GeneralEncapsulatedObjectFrame::~GeneralEncapsulatedObjectFrame() { - delete d; -} - -String GeneralEncapsulatedObjectFrame::toString() const { - - String text = "[" + d->mimeType + "]"; - - if (!d->fileName.isEmpty()) - text += " " + d->fileName; - - if (!d->description.isEmpty()) - text += " \"" + d->description + "\""; - - return text; - -} - -String::Type GeneralEncapsulatedObjectFrame::textEncoding() const { - return d->textEncoding; -} - -void GeneralEncapsulatedObjectFrame::setTextEncoding(String::Type encoding) { - d->textEncoding = encoding; -} - -String GeneralEncapsulatedObjectFrame::mimeType() const { - return d->mimeType; -} - -void GeneralEncapsulatedObjectFrame::setMimeType(const String &type) { - d->mimeType = type; -} - -String GeneralEncapsulatedObjectFrame::fileName() const { - return d->fileName; -} - -void GeneralEncapsulatedObjectFrame::setFileName(const String &name) { - d->fileName = name; -} - -String GeneralEncapsulatedObjectFrame::description() const { - return d->description; -} - -void GeneralEncapsulatedObjectFrame::setDescription(const String &desc) { - d->description = desc; -} - -ByteVector GeneralEncapsulatedObjectFrame::object() const { - return d->data; -} - -void GeneralEncapsulatedObjectFrame::setObject(const ByteVector &data) { - d->data = data; -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void GeneralEncapsulatedObjectFrame::parseFields(const ByteVector &data) { - - if (data.size() < 4) { - debug("An object frame must contain at least 4 bytes."); - return; - } - - d->textEncoding = String::Type(data[0]); - - size_t pos = 1; - - d->mimeType = readStringField(data, String::Latin1, pos); - d->fileName = readStringField(data, d->textEncoding, pos); - d->description = readStringField(data, d->textEncoding, pos); - - d->data = data.mid(pos); - -} - -ByteVector GeneralEncapsulatedObjectFrame::renderFields() const { - - StringList sl; - sl.append(d->fileName); - sl.append(d->description); - - const String::Type encoding = checkTextEncoding(sl, d->textEncoding); - - ByteVector data; - - data.append(char(encoding)); - data.append(d->mimeType.data(String::Latin1)); - data.append(textDelimiter(String::Latin1)); - data.append(d->fileName.data(encoding)); - data.append(textDelimiter(encoding)); - data.append(d->description.data(encoding)); - data.append(textDelimiter(encoding)); - data.append(d->data); - - return data; - -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data, Header *h) : Frame(h), d(new GeneralEncapsulatedObjectFramePrivate()) { - parseFields(fieldData(data)); -} diff --git a/3rdparty/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.h b/3rdparty/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.h deleted file mode 100644 index 1c850d60..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.h +++ /dev/null @@ -1,179 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - - copyright : (C) 2006 by Aaron VonderHaar - email : avh4@users.sourceforge.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_GENERALENCAPSULATEDOBJECT_H -#define TAGLIB_GENERALENCAPSULATEDOBJECT_H - -#include "id3v2frame.h" -#include "id3v2header.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -//! An ID3v2 general encapsulated object frame implementation - -/*! - * This is an implementation of ID3v2 general encapsulated objects. - * Arbitrary binary data may be included in tags, stored in GEOB frames. - * There may be multiple GEOB frames in a single tag. - * Each GEOB it labelled with a content description (which may be blank), - * a required mime-type, and a file name (may be blank). - * The content description uniquely identifies the GEOB frame in the tag. - */ - -class TAGLIB_EXPORT GeneralEncapsulatedObjectFrame : public Frame { - friend class FrameFactory; - - public: - /*! - * Constructs an empty object frame. - * The description, file name and text encoding should be set manually. - */ - explicit GeneralEncapsulatedObjectFrame(); - - /*! - * Constructs a GeneralEncapsulatedObjectFrame frame based on \a data. - * - * \warning This is \em not data for the encapsulated object, for that use - * setObject(). - * This constructor is used when reading the frame from the disk. - */ - explicit GeneralEncapsulatedObjectFrame(const ByteVector &data); - - /*! - * Destroys the GeneralEncapsulatedObjectFrame instance. - */ - ~GeneralEncapsulatedObjectFrame() override; - - /*! - * Returns a string containing the description, file name and mime-type - */ - String toString() const override; - - /*! - * Returns the text encoding used for the description and file name. - * - * \see setTextEncoding() - * \see description() - * \see fileName() - */ - String::Type textEncoding() const; - - /*! - * Set the text encoding used for the description and file name. - * - * \see description() - * \see fileName() - */ - void setTextEncoding(String::Type encoding); - - /*! - * Returns the mime type of the object. - */ - String mimeType() const; - - /*! - * Sets the mime type of the object. - */ - void setMimeType(const String &type); - - /*! - * Returns the file name of the object. - * - * \see setFileName() - */ - String fileName() const; - - /*! - * Sets the file name for the object. - * - * \see fileName() - */ - void setFileName(const String &name); - - /*! - * Returns the content description of the object. - * - * \see setDescription() - * \see textEncoding() - * \see setTextEncoding() - */ - - String description() const; - - /*! - * Sets the content description of the object to \a desc. - * - * \see description() - * \see textEncoding() - * \see setTextEncoding() - */ - - void setDescription(const String &desc); - - /*! - * Returns the object data as a ByteVector. - * - * \note ByteVector has a data() method that returns a const char * which - * should make it easy to export this data to external programs. - * - * \see setObject() - * \see mimeType() - */ - ByteVector object() const; - - /*! - * Sets the object data to \a data. - * \a data should be of the type specified in this frame's mime-type specification. - * - * \see object() - * \see mimeType() - * \see setMimeType() - */ - void setObject(const ByteVector &data); - - protected: - void parseFields(const ByteVector &data) override; - ByteVector renderFields() const override; - - private: - explicit GeneralEncapsulatedObjectFrame(const ByteVector &data, Header *h); - GeneralEncapsulatedObjectFrame(const GeneralEncapsulatedObjectFrame&); - GeneralEncapsulatedObjectFrame &operator=(const GeneralEncapsulatedObjectFrame&); - - class GeneralEncapsulatedObjectFramePrivate; - GeneralEncapsulatedObjectFramePrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/frames/ownershipframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/ownershipframe.cpp deleted file mode 100644 index 68d98245..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/ownershipframe.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/*************************************************************************** - copyright : (C) 2012 by Rupert Daniel - email : rupert@cancelmonday.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 "tdebug.h" -#include "tstringlist.h" -#include "id3v2tag.h" - -#include "ownershipframe.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -class OwnershipFrame::OwnershipFramePrivate { - public: - String pricePaid; - String datePurchased; - String seller; - String::Type textEncoding; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -OwnershipFrame::OwnershipFrame(String::Type encoding) : Frame("OWNE"),d(new OwnershipFramePrivate()) { - d->textEncoding = encoding; -} - -OwnershipFrame::OwnershipFrame(const ByteVector &data) : Frame(data),d(new OwnershipFramePrivate()) { - setData(data); -} - -OwnershipFrame::~OwnershipFrame() { - delete d; -} - -String OwnershipFrame::toString() const { - return "pricePaid=" + d->pricePaid + " datePurchased=" + d->datePurchased + " seller=" + d->seller; -} - -String OwnershipFrame::pricePaid() const { - return d->pricePaid; -} - -void OwnershipFrame::setPricePaid(const String &pricePaid) { - d->pricePaid = pricePaid; -} - -String OwnershipFrame::datePurchased() const { - return d->datePurchased; -} - -void OwnershipFrame::setDatePurchased(const String &datePurchased) { - d->datePurchased = datePurchased; -} - -String OwnershipFrame::seller() const { - return d->seller; -} - -void OwnershipFrame::setSeller(const String &s) { - d->seller = s; -} - -String::Type OwnershipFrame::textEncoding() const { - return d->textEncoding; -} - -void OwnershipFrame::setTextEncoding(String::Type encoding) { - d->textEncoding = encoding; -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void OwnershipFrame::parseFields(const ByteVector &data) { - - size_t pos = 0; - - // Get the text encoding - d->textEncoding = String::Type(data[0]); - pos += 1; - - // Read the price paid this is a null terminate string - d->pricePaid = readStringField(data, String::Latin1, pos); - - // If we don't have at least 8 bytes left then don't parse the rest of the - // data - if (data.size() - pos < 8) { - return; - } - - // Read the date purchased YYYYMMDD - d->datePurchased = String(data.mid(pos, 8)); - pos += 8; - - // Read the seller - if (d->textEncoding == String::Latin1) - d->seller = Tag::latin1StringHandler()->parse(data.mid(pos)); - else - d->seller = String(data.mid(pos), d->textEncoding); - -} - -ByteVector OwnershipFrame::renderFields() const { - - StringList sl; - sl.append(d->seller); - - const String::Type encoding = checkTextEncoding(sl, d->textEncoding); - - ByteVector v; - - v.append(char(encoding)); - v.append(d->pricePaid.data(String::Latin1)); - v.append(textDelimiter(String::Latin1)); - v.append(d->datePurchased.data(String::Latin1)); - v.append(d->seller.data(encoding)); - - return v; - -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -OwnershipFrame::OwnershipFrame(const ByteVector &data, Header *h) : Frame(h), d(new OwnershipFramePrivate()) { - parseFields(fieldData(data)); -} diff --git a/3rdparty/taglib/mpeg/id3v2/frames/ownershipframe.h b/3rdparty/taglib/mpeg/id3v2/frames/ownershipframe.h deleted file mode 100644 index 46aad1ae..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/ownershipframe.h +++ /dev/null @@ -1,150 +0,0 @@ -/*************************************************************************** - copyright : (C) 2012 by Rupert Daniel - email : rupert@cancelmonday.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_OWNERSHIPFRAME_H -#define TAGLIB_OWNERSHIPFRAME_H - -#include "id3v2frame.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -//! An implementation of ID3v2 "ownership" - -/*! - * This implements the ID3v2 ownership (OWNE frame). - * It consists of a price paid, a date purchased (YYYYMMDD) and the name of the seller. - */ - -class TAGLIB_EXPORT OwnershipFrame : public Frame { - friend class FrameFactory; - - public: - /*! - * Construct an empty ownership frame. - */ - explicit OwnershipFrame(String::Type encoding = String::Latin1); - - /*! - * Construct a ownership based on the data in \a data. - */ - explicit OwnershipFrame(const ByteVector &data); - - /*! - * Destroys this OwnershipFrame instance. - */ - ~OwnershipFrame() override; - - /*! - * Returns the text of this popularimeter. - * - * \see text() - */ - String toString() const override; - - /*! - * Returns the date purchased. - * - * \see setDatePurchased() - */ - String datePurchased() const; - - /*! - * Set the date purchased. - * - * \see datePurchased() - */ - void setDatePurchased(const String &datePurchased); - - /*! - * Returns the price paid. - * - * \see setPricePaid() - */ - String pricePaid() const; - - /*! - * Set the price paid. - * - * \see pricePaid() - */ - void setPricePaid(const String &pricePaid); - - /*! - * Returns the seller. - * - * \see setSeller() - */ - String seller() const; - - /*! - * Set the seller. - * - * \see seller() - */ - void setSeller(const String &seller); - - /*! - * Returns the text encoding that will be used in rendering this frame. - * This defaults to the type that was either specified in the constructor or read from the frame when parsed. - * - * \see setTextEncoding() - * \see render() - */ - String::Type textEncoding() const; - - /*! - * Sets the text encoding to be used when rendering this frame to \a encoding. - * - * \see textEncoding() - * \see render() - */ - void setTextEncoding(String::Type encoding); - - protected: - // Reimplementations. - - void parseFields(const ByteVector &data) override; - ByteVector renderFields() const override; - - private: - /*! - * The constructor used by the FrameFactory. - */ - explicit OwnershipFrame(const ByteVector &data, Header *h); - OwnershipFrame(const OwnershipFrame&); - OwnershipFrame &operator=(const OwnershipFrame &); - - class OwnershipFramePrivate; - OwnershipFramePrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/frames/podcastframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/podcastframe.cpp deleted file mode 100644 index a2b8a141..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/podcastframe.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/*************************************************************************** - copyright : (C) 2015 by Urs Fleisch - email : ufleisch@users.sourceforge.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "podcastframe.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -class PodcastFrame::PodcastFramePrivate { - public: - ByteVector fieldData; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -PodcastFrame::PodcastFrame() : Frame("PCST"), d(new PodcastFramePrivate()) { - d->fieldData = ByteVector(4, '\0'); -} - -PodcastFrame::~PodcastFrame() { - delete d; -} - -String PodcastFrame::toString() const { - return String(); -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void PodcastFrame::parseFields(const ByteVector &data) { - d->fieldData = data; -} - -ByteVector PodcastFrame::renderFields() const { - return d->fieldData; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -PodcastFrame::PodcastFrame(const ByteVector &data, Header *h) : Frame(h), d(new PodcastFramePrivate()) { - parseFields(fieldData(data)); -} diff --git a/3rdparty/taglib/mpeg/id3v2/frames/podcastframe.h b/3rdparty/taglib/mpeg/id3v2/frames/podcastframe.h deleted file mode 100644 index cba3f7f9..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/podcastframe.h +++ /dev/null @@ -1,81 +0,0 @@ -/*************************************************************************** - copyright : (C) 2015 by Urs Fleisch - email : ufleisch@users.sourceforge.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_PODCASTFRAME_H -#define TAGLIB_PODCASTFRAME_H - -#include "id3v2frame.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -//! ID3v2 podcast frame -/*! - * An implementation of ID3v2 podcast flag, a frame with four zero bytes. - */ -class TAGLIB_EXPORT PodcastFrame : public Frame { - friend class FrameFactory; - - public: - /*! - * Construct a podcast frame. - */ - explicit PodcastFrame(); - - /*! - * Destroys this PodcastFrame instance. - */ - ~PodcastFrame() override; - - /*! - * Returns a null string. - */ - String toString() const override; - - protected: - // Reimplementations. - - void parseFields(const ByteVector &data) override; - ByteVector renderFields() const override; - - private: - /*! - * The constructor used by the FrameFactory. - */ - explicit PodcastFrame(const ByteVector &data, Header *h); - PodcastFrame(const PodcastFrame &); - PodcastFrame &operator=(const PodcastFrame &); - - class PodcastFramePrivate; - PodcastFramePrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/frames/popularimeterframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/popularimeterframe.cpp deleted file mode 100644 index fa61f8ea..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/popularimeterframe.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/*************************************************************************** - copyright : (C) 2008 by Lukas Lalinsky - email : lalinsky@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tdebug.h" - -#include "popularimeterframe.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -class PopularimeterFrame::PopularimeterFramePrivate { - public: - explicit PopularimeterFramePrivate() : rating(0), counter(0) {} - String email; - int rating; - unsigned int counter; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -PopularimeterFrame::PopularimeterFrame() : Frame("POPM"), d(new PopularimeterFramePrivate()) {} - -PopularimeterFrame::PopularimeterFrame(const ByteVector &data) : Frame(data), d(new PopularimeterFramePrivate()) { - setData(data); -} - -PopularimeterFrame::~PopularimeterFrame() { - delete d; -} - -String PopularimeterFrame::toString() const { - return d->email + " rating=" + String::number(d->rating) + " counter=" + String::number(d->counter); -} - -String PopularimeterFrame::email() const { - return d->email; -} - -void PopularimeterFrame::setEmail(const String &email) { - d->email = email; -} - -int PopularimeterFrame::rating() const { - return d->rating; -} - -void PopularimeterFrame::setRating(int rating) { - d->rating = rating; -} - -unsigned int PopularimeterFrame::counter() const { - return d->counter; -} - -void PopularimeterFrame::setCounter(unsigned int counter) { - d->counter = counter; -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void PopularimeterFrame::parseFields(const ByteVector &data) { - - size_t pos = 0; - const size_t size = data.size(); - - d->email = readStringField(data, String::Latin1, pos); - - d->rating = 0; - d->counter = 0; - if (pos < size) { - d->rating = static_cast(data[pos++]); - if (pos < size) { - d->counter = data.toUInt32BE(pos); - } - } - -} - -ByteVector PopularimeterFrame::renderFields() const { - - ByteVector data; - - data.append(d->email.data(String::Latin1)); - data.append(textDelimiter(String::Latin1)); - data.append(char(d->rating)); - data.append(ByteVector::fromUInt32BE(d->counter)); - - return data; - -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -PopularimeterFrame::PopularimeterFrame(const ByteVector &data, Header *h) : Frame(h), d(new PopularimeterFramePrivate()) { - parseFields(fieldData(data)); -} diff --git a/3rdparty/taglib/mpeg/id3v2/frames/popularimeterframe.h b/3rdparty/taglib/mpeg/id3v2/frames/popularimeterframe.h deleted file mode 100644 index 4b69c0e7..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/popularimeterframe.h +++ /dev/null @@ -1,133 +0,0 @@ -/*************************************************************************** - copyright : (C) 2008 by Lukas Lalinsky - email : lalinsky@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_POPULARIMETERFRAME_H -#define TAGLIB_POPULARIMETERFRAME_H - -#include "id3v2frame.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -//! An implementation of ID3v2 "popularimeter" - -/*! - * This implements the ID3v2 popularimeter (POPM frame). - * It consists of an email, a rating and an optional counter. - */ - -class TAGLIB_EXPORT PopularimeterFrame : public Frame { - friend class FrameFactory; - - public: - /*! - * Construct an empty popularimeter frame. - */ - explicit PopularimeterFrame(); - - /*! - * Construct a popularimeter based on the data in \a data. - */ - explicit PopularimeterFrame(const ByteVector &data); - - /*! - * Destroys this PopularimeterFrame instance. - */ - ~PopularimeterFrame() override; - - /*! - * Returns the text of this popularimeter. - * - * \see text() - */ - String toString() const override; - - /*! - * Returns the email. - * - * \see setEmail() - */ - String email() const; - - /*! - * Set the email. - * - * \see email() - */ - void setEmail(const String &email); - - /*! - * Returns the rating. - * - * \see setRating() - */ - int rating() const; - - /*! - * Set the rating. - * - * \see rating() - */ - void setRating(int rating); - - /*! - * Returns the counter. - * - * \see setCounter() - */ - unsigned int counter() const; - - /*! - * Set the counter. - * - * \see counter() - */ - void setCounter(unsigned int counter); - - protected: - // Reimplementations. - - void parseFields(const ByteVector &data) override; - ByteVector renderFields() const override; - - private: - /*! - * The constructor used by the FrameFactory. - */ - explicit PopularimeterFrame(const ByteVector &data, Header *h); - PopularimeterFrame(const PopularimeterFrame&); - PopularimeterFrame &operator=(const PopularimeterFrame&); - - class PopularimeterFramePrivate; - PopularimeterFramePrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/frames/privateframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/privateframe.cpp deleted file mode 100644 index ec8159d2..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/privateframe.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/*************************************************************************** - copyright : (C) 2008 by Serkan Kalyoncu - copyright : (C) 2008 by Scott Wheeler - email : wheeler@kde.org -***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tbytevectorlist.h" -#include "id3v2tag.h" -#include "tdebug.h" - -#include "privateframe.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - - -class PrivateFrame::PrivateFramePrivate { - public: - ByteVector data; - String owner; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -PrivateFrame::PrivateFrame() : Frame("PRIV"), d(new PrivateFramePrivate()) {} - -PrivateFrame::PrivateFrame(const ByteVector &data) : Frame(data), d(new PrivateFramePrivate()) { - setData(data); -} - -PrivateFrame::~PrivateFrame() { - delete d; -} - -String PrivateFrame::toString() const { - return d->owner; -} - -String PrivateFrame::owner() const { - return d->owner; -} - -ByteVector PrivateFrame::data() const { - return d->data; -} - -void PrivateFrame::setOwner(const String &s) { - d->owner = s; -} - -void PrivateFrame::setData(const ByteVector &data) { - d->data = data; -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void PrivateFrame::parseFields(const ByteVector &data) { - - if (data.size() < 2) { - debug("A private frame must contain at least 2 bytes."); - return; - } - - // Owner identifier is assumed to be Latin1 - - const size_t endOfOwner = data.find(textDelimiter(String::Latin1), 0, 1); - - d->owner = String(data.mid(0, endOfOwner)); - d->data = data.mid(endOfOwner + 1); - -} - -ByteVector PrivateFrame::renderFields() const { - - ByteVector v; - - v.append(d->owner.data(String::Latin1)); - v.append(textDelimiter(String::Latin1)); - v.append(d->data); - - return v; - -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -PrivateFrame::PrivateFrame(const ByteVector &data, Header *h) : Frame(h), d(new PrivateFramePrivate()) { - parseFields(fieldData(data)); -} diff --git a/3rdparty/taglib/mpeg/id3v2/frames/privateframe.h b/3rdparty/taglib/mpeg/id3v2/frames/privateframe.h deleted file mode 100644 index 5fb852d0..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/privateframe.h +++ /dev/null @@ -1,112 +0,0 @@ -/*************************************************************************** - copyright : (C) 2008 by Serkan Kalyoncu - copyright : (C) 2008 by Scott Wheeler - email : wheeler@kde.org -***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_PRIVATEFRAME_H -#define TAGLIB_PRIVATEFRAME_H - -#include "id3v2frame.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -//! An implementation of ID3v2 privateframe - -class TAGLIB_EXPORT PrivateFrame : public Frame { - friend class FrameFactory; - - public: - /*! - * Construct an empty private frame. - */ - explicit PrivateFrame(); - - /*! - * Construct a private frame based on the data in \a data. - * - * \note This is the constructor used when parsing the frame from a file. - */ - explicit PrivateFrame(const ByteVector &data); - - /*! - * Destroys this private frame instance. - */ - ~PrivateFrame() override; - - /*! - * Returns the text of this private frame, currently just the owner. - * - * \see text() - */ - String toString() const override; - - /*! - * \return The owner of the private frame. - * \note This should contain an email address or link to a website. - */ - String owner() const; - - /*! - * - */ - ByteVector data() const; - - /*! - * Sets the owner of the frame to \a s. - * \note This should contain an email address or link to a website. - */ - void setOwner(const String &s); - - /*! - * - */ - void setData(const ByteVector &data); - - protected: - // Reimplementations. - - void parseFields(const ByteVector &data) override; - ByteVector renderFields() const override; - - private: - /*! - * The constructor used by the FrameFactory. - */ - explicit PrivateFrame(const ByteVector &data, Header *h); - - PrivateFrame(const PrivateFrame&); - PrivateFrame &operator=(const PrivateFrame&); - - class PrivateFramePrivate; - PrivateFramePrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp deleted file mode 100644 index a6075e7d..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tdebug.h" -#include "tmap.h" - -#include "relativevolumeframe.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -namespace { -struct ChannelData { - explicit ChannelData() : channelType(RelativeVolumeFrame::Other), volumeAdjustment(0) {} - - RelativeVolumeFrame::ChannelType channelType; - short volumeAdjustment; - RelativeVolumeFrame::PeakVolume peakVolume; -}; -} // namespace - -class RelativeVolumeFrame::RelativeVolumeFramePrivate { - public: - String identification; - Map channels; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -RelativeVolumeFrame::RelativeVolumeFrame() : Frame("RVA2"), d(new RelativeVolumeFramePrivate()) {} - -RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data) : Frame(data), d(new RelativeVolumeFramePrivate()) { - setData(data); -} - -RelativeVolumeFrame::~RelativeVolumeFrame() { - delete d; -} - -String RelativeVolumeFrame::toString() const { - return d->identification; -} - -List RelativeVolumeFrame::channels() const { - - List l; - - Map::ConstIterator it = d->channels.begin(); - for (; it != d->channels.end(); ++it) - l.append((*it).first); - - return l; - -} - -short RelativeVolumeFrame::volumeAdjustmentIndex(ChannelType type) const { - return d->channels.contains(type) ? d->channels[type].volumeAdjustment : 0; -} - -void RelativeVolumeFrame::setVolumeAdjustmentIndex(short index, ChannelType type) { - d->channels[type].volumeAdjustment = index; -} - -float RelativeVolumeFrame::volumeAdjustment(ChannelType type) const { - return d->channels.contains(type) ? float(d->channels[type].volumeAdjustment) / float(512) : 0; -} - -void RelativeVolumeFrame::setVolumeAdjustment(float adjustment, ChannelType type) { - d->channels[type].volumeAdjustment = short(adjustment * float(512)); -} - -RelativeVolumeFrame::PeakVolume RelativeVolumeFrame::peakVolume(ChannelType type) const { - return d->channels.contains(type) ? d->channels[type].peakVolume : PeakVolume(); -} - -void RelativeVolumeFrame::setPeakVolume(const PeakVolume &peak, ChannelType type) { - d->channels[type].peakVolume = peak; -} - -String RelativeVolumeFrame::identification() const { - return d->identification; -} - -void RelativeVolumeFrame::setIdentification(const String &s) { - d->identification = s; -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void RelativeVolumeFrame::parseFields(const ByteVector &data) { - - size_t pos = 0; - d->identification = readStringField(data, String::Latin1, pos); - - // Each channel is at least 4 bytes. - - while (pos + 4 <= data.size()) { - - ChannelType type = ChannelType(data[pos]); - pos += 1; - - ChannelData &channel = d->channels[type]; - - channel.volumeAdjustment = data.toInt16BE(pos); - pos += 2; - - channel.peakVolume.bitsRepresentingPeak = data[pos]; - pos += 1; - - const int bytes = (channel.peakVolume.bitsRepresentingPeak + 7) / 8; - channel.peakVolume.peakVolume = data.mid(pos, bytes); - pos += bytes; - } - -} - -ByteVector RelativeVolumeFrame::renderFields() const { - - ByteVector data; - - data.append(d->identification.data(String::Latin1)); - data.append(textDelimiter(String::Latin1)); - - Map::ConstIterator it = d->channels.begin(); - - for (; it != d->channels.end(); ++it) { - ChannelType type = (*it).first; - const ChannelData &channel = (*it).second; - - data.append(char(type)); - data.append(ByteVector::fromUInt16BE(channel.volumeAdjustment)); - data.append(char(channel.peakVolume.bitsRepresentingPeak)); - data.append(channel.peakVolume.peakVolume); - } - - return data; - -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data, Header *h) : Frame(h), d(new RelativeVolumeFramePrivate()) { - parseFields(fieldData(data)); -} diff --git a/3rdparty/taglib/mpeg/id3v2/frames/relativevolumeframe.h b/3rdparty/taglib/mpeg/id3v2/frames/relativevolumeframe.h deleted file mode 100644 index 401f1902..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/relativevolumeframe.h +++ /dev/null @@ -1,215 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_RELATIVEVOLUMEFRAME_H -#define TAGLIB_RELATIVEVOLUMEFRAME_H - -#include "tlist.h" -#include "id3v2frame.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -//! An ID3v2 relative volume adjustment frame implementation - -/*! - * This is an implementation of ID3v2 relative volume adjustment. - * The presence of this frame makes it possible to specify an increase in volume for an audio file or specific audio tracks in that file. - * - * Multiple relative volume adjustment frames may be present in the tag each with a unique identification and describing volume adjustment for different channel types. - */ - -class TAGLIB_EXPORT RelativeVolumeFrame : public Frame { - friend class FrameFactory; - - public: - /*! - * This indicates the type of volume adjustment that should be applied. - */ - enum ChannelType { - //! A type not enumerated below - Other = 0x00, - //! The master volume for the track - MasterVolume = 0x01, - //! The front right audio channel - FrontRight = 0x02, - //! The front left audio channel - FrontLeft = 0x03, - //! The back right audio channel - BackRight = 0x04, - //! The back left audio channel - BackLeft = 0x05, - //! The front center audio channel - FrontCentre = 0x06, - //! The back center audio channel - BackCentre = 0x07, - //! The subwoofer audio channel - Subwoofer = 0x08 - }; - - //! Struct that stores the relevant values for ID3v2 peak volume - - /*! - * The peak volume is described as a series of bits that is padded to fill a block of bytes. - * These two values should always be updated in tandem. - */ - struct PeakVolume { - /*! - * Constructs an empty peak volume description. - */ - PeakVolume() : bitsRepresentingPeak(0) {} - /*! - * The number of bits (in the range of 0 to 255) used to describe the peak volume. - */ - unsigned char bitsRepresentingPeak; - /*! - * The array of bits (represented as a series of bytes) used to describe the peak volume. - */ - ByteVector peakVolume; - }; - - /*! - * Constructs a RelativeVolumeFrame. The relevant data should be set manually. - */ - explicit RelativeVolumeFrame(); - - /*! - * Constructs a RelativeVolumeFrame based on the contents of \a data. - */ - explicit RelativeVolumeFrame(const ByteVector &data); - - /*! - * Destroys the RelativeVolumeFrame instance. - */ - ~RelativeVolumeFrame() override; - - /*! - * Returns the frame's identification. - * - * \see identification() - */ - String toString() const override; - - /*! - * Returns a list of channels with information currently in the frame. - */ - List channels() const; - - /*! - * Returns the relative volume adjustment "index". - * As indicated by the ID3v2 standard this is a 16-bit signed integer that reflects the decibels of adjustment when divided by 512. - * - * This defaults to returning the value for the master volume channel if available and returns 0 if the specified channel does not exist. - * - * \see setVolumeAdjustmentIndex() - * \see volumeAjustment() - */ - short volumeAdjustmentIndex(ChannelType type = MasterVolume) const; - - /*! - * Set the volume adjustment to \a index. - * As indicated by the ID3v2 standard this is a 16-bit signed integer that reflects the decibels of adjustment when divided by 512. - * - * By default this sets the value for the master volume. - * - * \see volumeAdjustmentIndex() - * \see setVolumeAjustment() - */ - void setVolumeAdjustmentIndex(short index, ChannelType type = MasterVolume); - - /*! - * Returns the relative volume adjustment in decibels. - * - * \note Because this is actually stored internally as an "index" to this - * value the value returned by this method may not be identical to the value set using setVolumeAdjustment(). - * - * This defaults to returning the value for the master volume channel if available and returns 0 if the specified channel does not exist. - * - * \see setVolumeAdjustment() - * \see volumeAdjustmentIndex() - */ - float volumeAdjustment(ChannelType type = MasterVolume) const; - - /*! - * Set the relative volume adjustment in decibels to \a adjustment. - * - * By default this sets the value for the master volume. - * - * \note Because this is actually stored internally as an "index" to this value the value set by this method may not be identical to the one returned by volumeAdjustment(). - * - * \see setVolumeAdjustment() - * \see volumeAdjustmentIndex() - */ - void setVolumeAdjustment(float adjustment, ChannelType type = MasterVolume); - - /*! - * Returns the peak volume (represented as a length and a string of bits). - * - * This defaults to returning the value for the master volume channel if available and returns 0 if the specified channel does not exist. - * - * \see setPeakVolume() - */ - PeakVolume peakVolume(ChannelType type = MasterVolume) const; - - /*! - * Sets the peak volume to \a peak. - * - * By default this sets the value for the master volume. - * - * \see peakVolume() - */ - void setPeakVolume(const PeakVolume &peak, ChannelType type = MasterVolume); - - /*! - * Returns the identification for this frame. - */ - String identification() const; - - /*! - * Sets the identification of the frame to \a s. - * The string is used to identify the situation and/or device where this adjustment should apply. - */ - void setIdentification(const String &s); - - protected: - void parseFields(const ByteVector &data) override; - ByteVector renderFields() const override; - - private: - explicit RelativeVolumeFrame(const ByteVector &data, Header *h); - RelativeVolumeFrame(const RelativeVolumeFrame&); - RelativeVolumeFrame &operator=(const RelativeVolumeFrame&); - - class RelativeVolumeFramePrivate; - RelativeVolumeFramePrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp deleted file mode 100644 index 760461e7..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/*************************************************************************** - copyright : (C) 2014 by Urs Fleisch - email : ufleisch@users.sourceforge.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "synchronizedlyricsframe.h" -#include "tbytevectorlist.h" -#include "id3v2tag.h" -#include "tdebug.h" -#include "tpropertymap.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -class SynchronizedLyricsFrame::SynchronizedLyricsFramePrivate { - public: - explicit SynchronizedLyricsFramePrivate() : textEncoding(String::Latin1), - timestampFormat(SynchronizedLyricsFrame::AbsoluteMilliseconds), - type(SynchronizedLyricsFrame::Lyrics) {} - String::Type textEncoding; - ByteVector language; - SynchronizedLyricsFrame::TimestampFormat timestampFormat; - SynchronizedLyricsFrame::Type type; - String description; - SynchronizedLyricsFrame::SynchedTextList synchedText; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -SynchronizedLyricsFrame::SynchronizedLyricsFrame(String::Type encoding) : Frame("SYLT"), - d(new SynchronizedLyricsFramePrivate()) { - d->textEncoding = encoding; -} - -SynchronizedLyricsFrame::SynchronizedLyricsFrame(const ByteVector &data) : Frame(data), - d(new SynchronizedLyricsFramePrivate()) { - setData(data); -} - -SynchronizedLyricsFrame::~SynchronizedLyricsFrame() { - delete d; -} - -String SynchronizedLyricsFrame::toString() const { - return d->description; -} - -String::Type SynchronizedLyricsFrame::textEncoding() const { - return d->textEncoding; -} - -ByteVector SynchronizedLyricsFrame::language() const { - return d->language; -} - -SynchronizedLyricsFrame::TimestampFormat -SynchronizedLyricsFrame::timestampFormat() const { - return d->timestampFormat; -} - -SynchronizedLyricsFrame::Type SynchronizedLyricsFrame::type() const { - return d->type; -} - -String SynchronizedLyricsFrame::description() const { - return d->description; -} - -SynchronizedLyricsFrame::SynchedTextList -SynchronizedLyricsFrame::synchedText() const { - return d->synchedText; -} - -void SynchronizedLyricsFrame::setTextEncoding(String::Type encoding) { - d->textEncoding = encoding; -} - -void SynchronizedLyricsFrame::setLanguage(const ByteVector &languageEncoding) { - d->language = languageEncoding.mid(0, 3); -} - -void SynchronizedLyricsFrame::setTimestampFormat(SynchronizedLyricsFrame::TimestampFormat f) { - d->timestampFormat = f; -} - -void SynchronizedLyricsFrame::setType(SynchronizedLyricsFrame::Type t) { - d->type = t; -} - -void SynchronizedLyricsFrame::setDescription(const String &s) { - d->description = s; -} - -void SynchronizedLyricsFrame::setSynchedText( - const SynchronizedLyricsFrame::SynchedTextList &t) { - d->synchedText = t; -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void SynchronizedLyricsFrame::parseFields(const ByteVector &data) { - - const size_t end = data.size(); - if (end < 7) { - debug("A synchronized lyrics frame must contain at least 7 bytes."); - return; - } - - d->textEncoding = String::Type(data[0]); - d->language = data.mid(1, 3); - d->timestampFormat = TimestampFormat(data[4]); - d->type = Type(data[5]); - - size_t pos = 6; - - d->description = readStringField(data, d->textEncoding, pos); - if (pos == 6) - return; - - /* - * If UTF16 strings are found in SYLT frames, a BOM may only be - * present in the first string (content descriptor), and the strings of - * the synchronized text have no BOM. Here the BOM is read from - * the first string to have a specific encoding with endianness for the - * case of strings without BOM so that readStringField() will work. - */ - String::Type encWithEndianness = d->textEncoding; - if (d->textEncoding == String::UTF16) { - unsigned short bom = data.toUInt16BE(6); - if (bom == 0xfffe) { - encWithEndianness = String::UTF16LE; - } - else if (bom == 0xfeff) { - encWithEndianness = String::UTF16BE; - } - } - - d->synchedText.clear(); - while (pos < end) { - String::Type enc = d->textEncoding; - // If a UTF16 string has no BOM, use the encoding found above. - if (enc == String::UTF16 && pos + 1 < end) { - unsigned short bom = data.toUInt16BE(pos); - if (bom != 0xfffe && bom != 0xfeff) { - enc = encWithEndianness; - } - } - String text = readStringField(data, enc, pos); - if (pos + 4 > end) - return; - - unsigned int time = data.toUInt32BE(pos); - pos += 4; - - d->synchedText.append(SynchedText(time, text)); - } - -} - -ByteVector SynchronizedLyricsFrame::renderFields() const { - - ByteVector v; - - String::Type encoding = d->textEncoding; - - encoding = checkTextEncoding(d->description, encoding); - for (SynchedTextList::ConstIterator it = d->synchedText.begin(); - it != d->synchedText.end(); - ++it) { - encoding = checkTextEncoding(it->text, encoding); - } - - v.append(char(encoding)); - v.append(d->language.size() == 3 ? d->language : "XXX"); - v.append(char(d->timestampFormat)); - v.append(char(d->type)); - v.append(d->description.data(encoding)); - v.append(textDelimiter(encoding)); - for (SynchedTextList::ConstIterator it = d->synchedText.begin(); - it != d->synchedText.end(); - ++it) { - const SynchedText &entry = *it; - v.append(entry.text.data(encoding)); - v.append(textDelimiter(encoding)); - v.append(ByteVector::fromUInt32BE(entry.time)); - } - - return v; - -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -SynchronizedLyricsFrame::SynchronizedLyricsFrame(const ByteVector &data, Header *h) : Frame(h), - d(new SynchronizedLyricsFramePrivate()) { - parseFields(fieldData(data)); -} diff --git a/3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.h b/3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.h deleted file mode 100644 index 3bf6684f..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.h +++ /dev/null @@ -1,227 +0,0 @@ -/*************************************************************************** - copyright : (C) 2014 by Urs Fleisch - email : ufleisch@users.sourceforge.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_SYNCHRONIZEDLYRICSFRAME_H -#define TAGLIB_SYNCHRONIZEDLYRICSFRAME_H - -#include "id3v2frame.h" -#include "tlist.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -//! ID3v2 synchronized lyrics frame -/*! - * An implementation of ID3v2 synchronized lyrics. - */ -class TAGLIB_EXPORT SynchronizedLyricsFrame : public Frame { - friend class FrameFactory; - - public: - /*! - * Specifies the timestamp format used. - */ - enum TimestampFormat { - //! The timestamp is of unknown format. - Unknown = 0x00, - //! The timestamp represents the number of MPEG frames since - //! the beginning of the audio stream. - AbsoluteMpegFrames = 0x01, - //! The timestamp represents the number of milliseconds since - //! the beginning of the audio stream. - AbsoluteMilliseconds = 0x02 - }; - - /*! - * Specifies the type of text contained. - */ - enum Type { - //! The text is some other type of text. - Other = 0x00, - //! The text contains lyrical data. - Lyrics = 0x01, - //! The text contains a transcription. - TextTranscription = 0x02, - //! The text lists the movements in the piece. - Movement = 0x03, - //! The text describes events that occur. - Events = 0x04, - //! The text contains chord changes that occur in the music. - Chord = 0x05, - //! The text contains trivia or "pop up" information about the media. - Trivia = 0x06, - //! The text contains URLs for relevant webpages. - WebpageUrls = 0x07, - //! The text contains URLs for relevant images. - ImageUrls = 0x08 - }; - - /*! - * Single entry of time stamp and lyrics text. - */ - struct SynchedText { - SynchedText(unsigned int ms, String str) : time(ms), text(str) {} - unsigned int time; - String text; - }; - - /*! - * List of synchronized lyrics. - */ - typedef Strawberry_TagLib::TagLib::List SynchedTextList; - - /*! - * Construct an empty synchronized lyrics frame that will use the text encoding \a encoding. - */ - explicit SynchronizedLyricsFrame(String::Type encoding = String::Latin1); - - /*! - * Construct a synchronized lyrics frame based on the data in \a data. - */ - explicit SynchronizedLyricsFrame(const ByteVector &data); - - /*! - * Destroys this SynchronizedLyricsFrame instance. - */ - ~SynchronizedLyricsFrame() override; - - /*! - * Returns the description of this synchronized lyrics frame. - * - * \see description() - */ - String toString() const override; - - /*! - * Returns the text encoding that will be used in rendering this frame. - * This defaults to the type that was either specified in the constructor or read from the frame when parsed. - * - * \see setTextEncoding() - * \see render() - */ - String::Type textEncoding() const; - - /*! - * Returns the language encoding as a 3 byte encoding as specified by - * ISO-639-2. - * - * \note Most taggers simply ignore this value. - * - * \see setLanguage() - */ - ByteVector language() const; - - /*! - * Returns the timestamp format. - */ - TimestampFormat timestampFormat() const; - - /*! - * Returns the type of text contained. - */ - Type type() const; - - /*! - * Returns the description of this synchronized lyrics frame. - * - * \note Most taggers simply ignore this value. - * - * \see setDescription() - */ - String description() const; - - /*! - * Returns the text with the time stamps. - */ - SynchedTextList synchedText() const; - - /*! - * Sets the text encoding to be used when rendering this frame to \a encoding. - * - * \see textEncoding() - * \see render() - */ - void setTextEncoding(String::Type encoding); - - /*! - * Set the language using the 3 byte language code from - * ISO-639-2 to \a languageCode. - * - * \see language() - */ - void setLanguage(const ByteVector &languageEncoding); - - /*! - * Set the timestamp format. - * - * \see timestampFormat() - */ - void setTimestampFormat(TimestampFormat f); - - /*! - * Set the type of text contained. - * - * \see type() - */ - void setType(Type t); - - /*! - * Sets the description of the synchronized lyrics frame to \a s. - * - * \see description() - */ - void setDescription(const String &s); - - /*! - * Sets the text with the time stamps. - * - * \see text() - */ - void setSynchedText(const SynchedTextList &t); - - protected: - // Reimplementations. - - void parseFields(const ByteVector &data) override; - ByteVector renderFields() const override; - - private: - /*! - * The constructor used by the FrameFactory. - */ - explicit SynchronizedLyricsFrame(const ByteVector &data, Header *h); - SynchronizedLyricsFrame(const SynchronizedLyricsFrame&); - SynchronizedLyricsFrame &operator=(const SynchronizedLyricsFrame&); - - class SynchronizedLyricsFramePrivate; - SynchronizedLyricsFramePrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp deleted file mode 100644 index 221b83c7..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp +++ /dev/null @@ -1,329 +0,0 @@ -/*************************************************************************** - copyright : (C) 2013 by Lukas Krejci - email : krejclu6@fel.cvut.cz - ***************************************************************************/ - -/*************************************************************************** - * 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 "tbytevectorlist.h" -#include "tpropertymap.h" -#include "tdebug.h" - -#include "tableofcontentsframe.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -class TableOfContentsFrame::TableOfContentsFramePrivate { - public: - explicit TableOfContentsFramePrivate() : tagHeader(nullptr), isTopLevel(false), isOrdered(false) { - embeddedFrameList.setAutoDelete(true); - } - - const ID3v2::Header *tagHeader; - ByteVector elementID; - bool isTopLevel; - bool isOrdered; - ByteVectorList childElements; - FrameListMap embeddedFrameListMap; - FrameList embeddedFrameList; -}; - -namespace { - -// These functions are needed to try to aim for backward compatibility with -// an API that previously (unreasonably) required null bytes to be appended -// at the end of identifiers explicitly by the API user. - -// BIC: remove these - -ByteVector &strip(ByteVector &b) { - - if (b.endsWith('\0')) - b.resize(b.size() - 1); - return b; - -} - -ByteVectorList &strip(ByteVectorList &l) { - - for (ByteVectorList::Iterator it = l.begin(); it != l.end(); ++it) { - strip(*it); - } - return l; - -} - -} // namespace - -//////////////////////////////////////////////////////////////////////////////// -// public methods -//////////////////////////////////////////////////////////////////////////////// - -TableOfContentsFrame::TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data) : ID3v2::Frame(data), - d(new TableOfContentsFramePrivate()) { - d->tagHeader = tagHeader; - setData(data); -} - -TableOfContentsFrame::TableOfContentsFrame(const ByteVector &elementID, const ByteVectorList &children, const FrameList &embeddedFrames) : ID3v2::Frame("CTOC"), d(new TableOfContentsFramePrivate()) { - - d->elementID = elementID; - strip(d->elementID); - d->childElements = children; - - for (FrameList::ConstIterator it = embeddedFrames.begin(); it != embeddedFrames.end(); ++it) - addEmbeddedFrame(*it); - -} - -TableOfContentsFrame::~TableOfContentsFrame() { - delete d; -} - -ByteVector TableOfContentsFrame::elementID() const { - return d->elementID; -} - -bool TableOfContentsFrame::isTopLevel() const { - return d->isTopLevel; -} - -bool TableOfContentsFrame::isOrdered() const { - return d->isOrdered; -} - -unsigned int TableOfContentsFrame::entryCount() const { - return d->childElements.size(); -} - -ByteVectorList TableOfContentsFrame::childElements() const { - return d->childElements; -} - -void TableOfContentsFrame::setElementID(const ByteVector &eID) { - d->elementID = eID; - strip(d->elementID); -} - -void TableOfContentsFrame::setIsTopLevel(const bool &t) { - d->isTopLevel = t; -} - -void TableOfContentsFrame::setIsOrdered(const bool &o) { - d->isOrdered = o; -} - -void TableOfContentsFrame::setChildElements(const ByteVectorList &l) { - d->childElements = l; - strip(d->childElements); -} - -void TableOfContentsFrame::addChildElement(const ByteVector &cE) { - d->childElements.append(cE); - strip(d->childElements); -} - -void TableOfContentsFrame::removeChildElement(const ByteVector &cE) { - ByteVectorList::Iterator it = d->childElements.find(cE); - - if (it == d->childElements.end()) - it = d->childElements.find(cE + ByteVector("\0")); - - d->childElements.erase(it); -} - -const FrameListMap &TableOfContentsFrame::embeddedFrameListMap() const { - return d->embeddedFrameListMap; -} - -const FrameList &TableOfContentsFrame::embeddedFrameList() const { - return d->embeddedFrameList; -} - -const FrameList &TableOfContentsFrame::embeddedFrameList(const ByteVector &frameID) const { - return d->embeddedFrameListMap[frameID]; -} - -void TableOfContentsFrame::addEmbeddedFrame(Frame *frame) { - d->embeddedFrameList.append(frame); - d->embeddedFrameListMap[frame->frameID()].append(frame); -} - -void TableOfContentsFrame::removeEmbeddedFrame(Frame *frame, bool del) { - - // remove the frame from the frame list - FrameList::Iterator it = d->embeddedFrameList.find(frame); - d->embeddedFrameList.erase(it); - - // ...and from the frame list map - it = d->embeddedFrameListMap[frame->frameID()].find(frame); - d->embeddedFrameListMap[frame->frameID()].erase(it); - - // ...and delete as desired - if (del) - delete frame; - -} - -void TableOfContentsFrame::removeEmbeddedFrames(const ByteVector &id) { - - FrameList l = d->embeddedFrameListMap[id]; - for (FrameList::ConstIterator it = l.begin(); it != l.end(); ++it) - removeEmbeddedFrame(*it, true); - -} - -String TableOfContentsFrame::toString() const { - - String s = String(d->elementID) + - ": top level: " + (d->isTopLevel ? "true" : "false") + - ", ordered: " + (d->isOrdered ? "true" : "false"); - - if (!d->childElements.isEmpty()) { - s += ", chapters: [ " + String(d->childElements.toByteVector(", ")) + " ]"; - } - - if (!d->embeddedFrameList.isEmpty()) { - StringList frameIDs; - for (FrameList::ConstIterator it = d->embeddedFrameList.begin(); it != d->embeddedFrameList.end(); ++it) - frameIDs.append((*it)->frameID()); - s += ", sub-frames: [ " + frameIDs.toString(", ") + " ]"; - } - - return s; - -} - -PropertyMap TableOfContentsFrame::asProperties() const { - - PropertyMap map; - - map.unsupportedData().append(frameID() + String("/") + d->elementID); - - return map; - -} - -TableOfContentsFrame *TableOfContentsFrame::findByElementID(const ID3v2::Tag *tag, const ByteVector &eID) { // static - - ID3v2::FrameList tablesOfContents = tag->frameList("CTOC"); - - for (ID3v2::FrameList::ConstIterator it = tablesOfContents.begin(); - it != tablesOfContents.end(); - ++it) { - TableOfContentsFrame *frame = dynamic_cast(*it); - if (frame && frame->elementID() == eID) - return frame; - } - - return nullptr; - -} - -TableOfContentsFrame *TableOfContentsFrame::findTopLevel(const ID3v2::Tag *tag) { // static - - ID3v2::FrameList tablesOfContents = tag->frameList("CTOC"); - - for (ID3v2::FrameList::ConstIterator it = tablesOfContents.begin(); it != tablesOfContents.end(); ++it) { - TableOfContentsFrame *frame = dynamic_cast(*it); - if (frame && frame->isTopLevel() == true) - return frame; - } - - return nullptr; - -} - -void TableOfContentsFrame::parseFields(const ByteVector &data) { - - size_t size = data.size(); - if (size < 6) { - debug("A CTOC frame must contain at least 6 bytes (1 byte element ID terminated by " - "null, 1 byte flags, 1 byte entry count and 1 byte child element ID terminated " - "by null."); - return; - } - - size_t pos = 0; - size_t embPos = 0; - d->elementID = readStringField(data, String::Latin1, pos).data(String::Latin1); - d->isTopLevel = (data.at(pos) & 2) != 0; - d->isOrdered = (data.at(pos++) & 1) != 0; - unsigned int entryCount = static_cast(data.at(pos++)); - for (unsigned int i = 0; i < entryCount; i++) { - ByteVector childElementID = readStringField(data, String::Latin1, pos).data(String::Latin1); - d->childElements.append(childElementID); - } - - size -= pos; - - if (size < header()->size()) - return; - - while (embPos < size - header()->size()) { - Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos), d->tagHeader); - - if (!frame) - return; - - // Checks to make sure that frame parsed correctly. - if (frame->size() <= 0) { - delete frame; - return; - } - - embPos += frame->size() + header()->size(); - addEmbeddedFrame(frame); - } - -} - -ByteVector TableOfContentsFrame::renderFields() const { - - ByteVector data; - - data.append(d->elementID); - data.append('\0'); - char flags = 0; - if (d->isTopLevel) - flags += 2; - if (d->isOrdered) - flags += 1; - data.append(flags); - data.append(static_cast(entryCount())); - - for (ByteVectorList::ConstIterator it = d->childElements.begin() ; it != d->childElements.end() ; ++it) { - data.append(*it); - data.append('\0'); - } - FrameList l = d->embeddedFrameList; - for (FrameList::ConstIterator it = l.begin(); it != l.end(); ++it) - data.append((*it)->render()); - - return data; - -} - -TableOfContentsFrame::TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h) : Frame(h), d(new TableOfContentsFramePrivate()) { - d->tagHeader = tagHeader; - parseFields(fieldData(data)); -} diff --git a/3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.h b/3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.h deleted file mode 100644 index 67adbfd0..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.h +++ /dev/null @@ -1,242 +0,0 @@ -/*************************************************************************** - copyright : (C) 2013 by Lukas Krejci - email : krejclu6@fel.cvut.cz - ***************************************************************************/ - -/*************************************************************************** - * 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_TABLEOFCONTENTSFRAME -#define TAGLIB_TABLEOFCONTENTSFRAME - -#include "id3v2tag.h" -#include "id3v2frame.h" - -#include "tbytevectorlist.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -/*! - * This is an implementation of ID3v2 table of contents frames. - * Purpose of this frame is to allow a table of contents to be defined. - */ - -//! An implementation of ID3v2 table of contents frames - -class TAGLIB_EXPORT TableOfContentsFrame : public ID3v2::Frame { - friend class FrameFactory; - - public: - /*! - * Creates a table of contents frame based on \a data. - * \a tagHeader is required as the internal frames are parsed based on the tag version. - */ - explicit TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data); - - /*! - * Creates a table of contents frame with the element ID \a elementID, - * the child elements \a children and embedded frames, which become owned by this frame, in \a embeddedFrames. - */ - explicit TableOfContentsFrame(const ByteVector &elementID, const ByteVectorList &children = ByteVectorList(), const FrameList &embeddedFrames = FrameList()); - - /*! - * Destroys the frame. - */ - ~TableOfContentsFrame() override; - - /*! - * Returns the elementID of the frame. - * Element ID is a null terminated string, however it's not human-readable. - * - * \see setElementID() - */ - ByteVector elementID() const; - - /*! - * Returns true, if the frame is top-level (doesn't have any parent CTOC frame). - * - * \see setIsTopLevel() - */ - bool isTopLevel() const; - - /*! - * Returns true, if the child elements list entries are ordered. - * - * \see setIsOrdered() - */ - bool isOrdered() const; - - /*! - * Returns count of child elements of the frame. It always corresponds to size of child elements list. - * - * \see childElements() - */ - unsigned int entryCount() const; - - /*! - * Returns list of child elements of the frame. - * - * \see setChildElements() - */ - ByteVectorList childElements() const; - - /*! - * Sets the elementID of the frame to \a eID. If \a eID isn't null terminated, a null char is appended automatically. - * - * \see elementID() - */ - void setElementID(const ByteVector &eID); - - /*! - * Sets, if the frame is top-level (doesn't have any parent CTOC frame). - * - * \see isTopLevel() - */ - void setIsTopLevel(const bool &t); - - /*! - * Sets, if the child elements list entries are ordered. - * - * \see isOrdered() - */ - void setIsOrdered(const bool &o); - - /*! - * Sets list of child elements of the frame to \a l. - * - * \see childElements() - */ - void setChildElements(const ByteVectorList &l); - - /*! - * Adds \a cE to list of child elements of the frame. - * - * \see childElements() - */ - void addChildElement(const ByteVector &cE); - - /*! - * Removes \a cE to list of child elements of the frame. - * - * \see childElements() - */ - void removeChildElement(const ByteVector &cE); - - /*! - * Returns a reference to the frame list map. - * This is an FrameListMap of all of the frames embedded in the CTOC frame. - * - * This is the most convenient structure for accessing the CTOC frame's embedded frames. - * Many frame types allow multiple instances of the same frame type so this is a map of lists. - * In most cases however there will only be a single frame of a certain type. - * - * \warning You should not modify this data structure directly, instead use addEmbeddedFrame() and removeEmbeddedFrame(). - * - * \see embeddedFrameList() - */ - const FrameListMap &embeddedFrameListMap() const; - - /*! - * Returns a reference to the embedded frame list. - * This is an FrameList of all of the frames embedded in the CTOC frame in the order that they were parsed. - * - * This can be useful if for example you want iterate over the CTOC frame's embedded frames in the order that they occur in the CTOC frame. - * - * \warning You should not modify this data structure directly, instead use addEmbeddedFrame() and removeEmbeddedFrame(). - */ - const FrameList &embeddedFrameList() const; - - /*! - * Returns the embedded frame list for frames with the id \a frameID or an empty list if there are no embedded frames of that type. - * This is just a convenience and is equivalent to: - * - * \code - * embeddedFrameListMap()[frameID]; - * \endcode - * - * \see embeddedFrameListMap() - */ - const FrameList &embeddedFrameList(const ByteVector &frameID) const; - - /*! - * Add an embedded frame to the CTOC frame. - * At this point the CTOC frame takes ownership of the embedded frame and will handle freeing its memory. - * - * \note Using this method will invalidate any pointers on the list returned by embeddedFrameList() - */ - void addEmbeddedFrame(Frame *frame); - - /*! - * Remove an embedded frame from the CTOC frame. - * If \a del is true the frame's memory will be freed; if it is false, it must be deleted by the user. - * - * \note Using this method will invalidate any pointers on the list returned by embeddedFrameList() - */ - void removeEmbeddedFrame(Frame *frame, bool del = true); - - /*! - * Remove all embedded frames of type \a id from the CTOC frame and free their memory. - * - * \note Using this method will invalidate any pointers on the list returned by embeddedFrameList() - */ - void removeEmbeddedFrames(const ByteVector &id); - - String toString() const override; - - PropertyMap asProperties() const override; - - /*! - * CTOC frames each have a unique element ID. - * This searches for a CTOC frame with the element ID \a eID and returns a pointer to it. - * This can be used to link together parent and child CTOC frames. - * - * \see elementID() - */ - static TableOfContentsFrame *findByElementID(const Tag *tag, const ByteVector &eID); - - /*! - * CTOC frames each contain a flag that indicates, - * if CTOC frame is top-level (there isn't any frame, which contains this frame in its child elements list). - * Only a single frame within tag can be top-level. This searches for a top-level CTOC frame. - * - * \see isTopLevel() - */ - static TableOfContentsFrame *findTopLevel(const Tag *tag); - - protected: - void parseFields(const ByteVector &data) override; - ByteVector renderFields() const override; - - private: - explicit TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h); - TableOfContentsFrame(const TableOfContentsFrame&); - TableOfContentsFrame &operator=(const TableOfContentsFrame&); - - class TableOfContentsFramePrivate; - TableOfContentsFramePrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.cpp deleted file mode 100644 index 55d9e04d..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.cpp +++ /dev/null @@ -1,415 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tbytevectorlist.h" -#include "id3v2tag.h" -#include "textidentificationframe.h" -#include "tpropertymap.h" -#include "id3v1genres.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -class TextIdentificationFrame::TextIdentificationFramePrivate { - public: - explicit TextIdentificationFramePrivate() : textEncoding(String::Latin1) {} - String::Type textEncoding; - StringList fieldList; -}; - -//////////////////////////////////////////////////////////////////////////////// -// TextIdentificationFrame public members -//////////////////////////////////////////////////////////////////////////////// - -TextIdentificationFrame::TextIdentificationFrame(const ByteVector &type, String::Type encoding) : Frame(type), - d(new TextIdentificationFramePrivate()) { - d->textEncoding = encoding; -} - -TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data) : Frame(data), - d(new TextIdentificationFramePrivate()) { - setData(data); -} - -TextIdentificationFrame *TextIdentificationFrame::createTIPLFrame(const PropertyMap &properties) { // static - - TextIdentificationFrame *frame = new TextIdentificationFrame("TIPL"); - StringList l; - for (PropertyMap::ConstIterator it = properties.begin(); it != properties.end(); ++it) { - l.append(it->first); - l.append(it->second.toString(",")); // comma-separated list of names - } - frame->setText(l); - return frame; - -} - -TextIdentificationFrame *TextIdentificationFrame::createTMCLFrame(const PropertyMap &properties) { // static - - TextIdentificationFrame *frame = new TextIdentificationFrame("TMCL"); - StringList l; - for (PropertyMap::ConstIterator it = properties.begin(); it != properties.end(); ++it) { - if (!it->first.startsWith(instrumentPrefix)) // should not happen - continue; - l.append(it->first.substr(instrumentPrefix.size())); - l.append(it->second.toString(",")); - } - frame->setText(l); - return frame; - -} - -TextIdentificationFrame::~TextIdentificationFrame() { - delete d; -} - -void TextIdentificationFrame::setText(const StringList &l) { - d->fieldList = l; -} - -void TextIdentificationFrame::setText(const String &s) { - d->fieldList = s; -} - -String TextIdentificationFrame::toString() const { - return d->fieldList.toString(); -} - -StringList TextIdentificationFrame::fieldList() const { - return d->fieldList; -} - -String::Type TextIdentificationFrame::textEncoding() const { - return d->textEncoding; -} - -void TextIdentificationFrame::setTextEncoding(String::Type encoding) { - d->textEncoding = encoding; -} - -namespace { -// array of allowed TIPL prefixes and their corresponding key value -const char *involvedPeople[][2] = { - { "ARRANGER", "ARRANGER" }, - { "ENGINEER", "ENGINEER" }, - { "PRODUCER", "PRODUCER" }, - { "DJ-MIX", "DJMIXER" }, - { "MIX", "MIXER" }, -}; -const size_t involvedPeopleSize = sizeof(involvedPeople) / sizeof(involvedPeople[0]); -} // namespace - -const KeyConversionMap &TextIdentificationFrame::involvedPeopleMap() { // static - - static KeyConversionMap m; - if (m.isEmpty()) { - for (size_t i = 0; i < involvedPeopleSize; ++i) - m.insert(involvedPeople[i][1], involvedPeople[i][0]); - } - return m; - -} - -PropertyMap TextIdentificationFrame::asProperties() const { - - if (frameID() == "TIPL") - return makeTIPLProperties(); - if (frameID() == "TMCL") - return makeTMCLProperties(); - PropertyMap map; - String tagName = frameIDToKey(frameID()); - if (tagName.isEmpty()) { - map.unsupportedData().append(frameID()); - return map; - } - StringList values = fieldList(); - if (tagName == "GENRE") { - // Special case: Support ID3v1-style genre numbers. They are not officially supported in - // ID3v2, however it seems that still a lot of programs use them. - for (StringList::Iterator it = values.begin(); it != values.end(); ++it) { - bool ok = false; - int test = it->toInt(&ok); // test if the genre value is an integer - if (ok) - *it = ID3v1::genre(test); - } - } - else if (tagName == "DATE") { - for (StringList::Iterator it = values.begin(); it != values.end(); ++it) { - // ID3v2 specifies ISO8601 timestamps which contain a 'T' as separator between date and time. - // Since this is unusual in other formats, the T is removed. - const size_t tpos = it->find("T"); - if (tpos != String::npos()) - (*it)[tpos] = ' '; - } - } - PropertyMap ret; - ret.insert(tagName, values); - return ret; - -} - -//////////////////////////////////////////////////////////////////////////////// -// TextIdentificationFrame protected members -//////////////////////////////////////////////////////////////////////////////// - -void TextIdentificationFrame::parseFields(const ByteVector &data) { - - // Don't try to parse invalid frames - - if (data.size() < 2) - return; - - // read the string data type (the first byte of the field data) - - d->textEncoding = String::Type(data[0]); - - // split the byte array into chunks based on the string type (two byte delimiter - // for unicode encodings) - - int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2; - - // build a small counter to strip nulls off the end of the field - - int dataLength = data.size() - 1; - - while (dataLength > 0 && data[dataLength] == 0) - dataLength--; - - while (dataLength % byteAlign != 0) - dataLength++; - - ByteVectorList l = ByteVectorList::split(data.mid(1, dataLength), textDelimiter(d->textEncoding), byteAlign); - - d->fieldList.clear(); - - // append those split values to the list and make sure that the new string's - // type is the same specified for this frame - - for (ByteVectorList::ConstIterator it = l.begin(); it != l.end(); it++) { - if (!(*it).isEmpty()) { - if (d->textEncoding == String::Latin1) - d->fieldList.append(Tag::latin1StringHandler()->parse(*it)); - else - d->fieldList.append(String(*it, d->textEncoding)); - } - } - -} - -ByteVector TextIdentificationFrame::renderFields() const { - - String::Type encoding = checkTextEncoding(d->fieldList, d->textEncoding); - - ByteVector v; - - v.append(char(encoding)); - - for (StringList::ConstIterator it = d->fieldList.begin(); it != d->fieldList.end(); it++) { - - // Since the field list is null delimited, if this is not the first - // element in the list, append the appropriate delimiter for this - // encoding. - - if (it != d->fieldList.begin()) - v.append(textDelimiter(encoding)); - - v.append((*it).data(encoding)); - } - - return v; - -} - -//////////////////////////////////////////////////////////////////////////////// -// TextIdentificationFrame private members -//////////////////////////////////////////////////////////////////////////////// - -TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data, Header *h) : Frame(h), - d(new TextIdentificationFramePrivate()) { - parseFields(fieldData(data)); -} - -PropertyMap TextIdentificationFrame::makeTIPLProperties() const { - - PropertyMap map; - if (fieldList().size() % 2 != 0) { - // according to the ID3 spec, TIPL must contain an even number of entries - map.unsupportedData().append(frameID()); - return map; - } - StringList l = fieldList(); - for (StringList::ConstIterator it = l.begin(); it != l.end(); ++it) { - bool found = false; - for (size_t i = 0; i < involvedPeopleSize; ++i) - if (*it == involvedPeople[i][0]) { - map.insert(involvedPeople[i][1], (++it)->split(",")); - found = true; - break; - } - if (!found) { - // invalid involved role -> mark whole frame as unsupported in order to be consistent with writing - map.clear(); - map.unsupportedData().append(frameID()); - return map; - } - } - return map; - -} - -PropertyMap TextIdentificationFrame::makeTMCLProperties() const { - - PropertyMap map; - if (fieldList().size() % 2 != 0) { - // according to the ID3 spec, TMCL must contain an even number of entries - map.unsupportedData().append(frameID()); - return map; - } - StringList l = fieldList(); - for (StringList::ConstIterator it = l.begin(); it != l.end(); ++it) { - String instrument = it->upper(); - if (instrument.isEmpty()) { - // instrument is not a valid key -> frame unsupported - map.clear(); - map.unsupportedData().append(frameID()); - return map; - } - map.insert(L"PERFORMER:" + instrument, (++it)->split(",")); - } - return map; - -} - -//////////////////////////////////////////////////////////////////////////////// -// UserTextIdentificationFrame public members -//////////////////////////////////////////////////////////////////////////////// - -UserTextIdentificationFrame::UserTextIdentificationFrame(String::Type encoding) : TextIdentificationFrame("TXXX", encoding), d(nullptr) { - StringList l; - l.append(String()); - l.append(String()); - setText(l); -} - - -UserTextIdentificationFrame::UserTextIdentificationFrame(const ByteVector &data) : TextIdentificationFrame(data) { - checkFields(); -} - -UserTextIdentificationFrame::UserTextIdentificationFrame(const String &description, const StringList &values, String::Type encoding) : TextIdentificationFrame("TXXX", encoding), d(nullptr) { - setDescription(description); - setText(values); -} - -String UserTextIdentificationFrame::toString() const { - - // first entry is the description itself, drop from values list - StringList l = fieldList(); - for (StringList::Iterator it = l.begin(); it != l.end(); ++it) { - l.erase(it); - break; - } - return "[" + description() + "] " + l.toString(); - -} - -String UserTextIdentificationFrame::description() const { - return !TextIdentificationFrame::fieldList().isEmpty() ? TextIdentificationFrame::fieldList().front() : String(); -} - -StringList UserTextIdentificationFrame::fieldList() const { - // TODO: remove this function - - return TextIdentificationFrame::fieldList(); -} - -void UserTextIdentificationFrame::setText(const String &text) { - if (description().isEmpty()) - setDescription(String()); - - TextIdentificationFrame::setText(StringList(description()).append(text)); -} - -void UserTextIdentificationFrame::setText(const StringList &fields) { - if (description().isEmpty()) - setDescription(String()); - - TextIdentificationFrame::setText(StringList(description()).append(fields)); -} - -void UserTextIdentificationFrame::setDescription(const String &s) { - - StringList l = fieldList(); - - if (l.isEmpty()) - l.append(s); - else - l[0] = s; - - TextIdentificationFrame::setText(l); - -} - -PropertyMap UserTextIdentificationFrame::asProperties() const { - - PropertyMap map; - String tagName = txxxToKey(description()); - StringList v = fieldList(); - for (StringList::ConstIterator it = v.begin(); it != v.end(); ++it) - if (it != v.begin()) map.insert(tagName, *it); - return map; - -} - -UserTextIdentificationFrame *UserTextIdentificationFrame::find(ID3v2::Tag *tag, const String &description) { // static - - FrameList l = tag->frameList("TXXX"); - for (FrameList::ConstIterator it = l.begin(); it != l.end(); ++it) { - UserTextIdentificationFrame *f = dynamic_cast(*it); - if (f && f->description() == description) - return f; - } - return nullptr; - -} - -//////////////////////////////////////////////////////////////////////////////// -// UserTextIdentificationFrame private members -//////////////////////////////////////////////////////////////////////////////// - -UserTextIdentificationFrame::UserTextIdentificationFrame(const ByteVector &data, Header *h) : TextIdentificationFrame(data, h) { - checkFields(); -} - -void UserTextIdentificationFrame::checkFields() { - - const size_t fields = fieldList().size(); - - if (fields == 0) - setDescription(String()); - if (fields <= 1) - setText(String()); - -} diff --git a/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.h b/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.h deleted file mode 100644 index f563fe66..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.h +++ /dev/null @@ -1,296 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_TEXTIDENTIFICATIONFRAME_H -#define TAGLIB_TEXTIDENTIFICATIONFRAME_H - -#include "tstringlist.h" -#include "tmap.h" -#include "taglib_export.h" - -#include "id3v2frame.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -class Tag; -typedef Map KeyConversionMap; - -//! An ID3v2 text identification frame implementation - -/*! - * This is an implementation of the most common type of ID3v2 frame -- text identification frames. - * There are a number of variations on this. Those enumerated in the ID3v2.4 standard are: - * - *
    - *
  • TALB Album/Movie/Show title
  • - *
  • TBPM BPM (beats per minute)
  • - *
  • TCOM Composer
  • - *
  • TCON Content type
  • - *
  • TCOP Copyright message
  • - *
  • TDEN Encoding time
  • - *
  • TDLY Playlist delay
  • - *
  • TDOR Original release time
  • - *
  • TDRC Recording time
  • - *
  • TDRL Release time
  • - *
  • TDTG Tagging time
  • - *
  • TENC Encoded by
  • - *
  • TEXT Lyricist/Text writer
  • - *
  • TFLT File type
  • - *
  • TIPL Involved people list
  • - *
  • TIT1 Content group description
  • - *
  • TIT2 Title/songname/content description
  • - *
  • TIT3 Subtitle/Description refinement
  • - *
  • TKEY Initial key
  • - *
  • TLAN Language(s)
  • - *
  • TLEN Length
  • - *
  • TMCL Musician credits list
  • - *
  • TMED Media type
  • - *
  • TMOO Mood
  • - *
  • TOAL Original album/movie/show title
  • - *
  • TOFN Original filename
  • - *
  • TOLY Original lyricist(s)/text writer(s)
  • - *
  • TOPE Original artist(s)/performer(s)
  • - *
  • TOWN File owner/licensee
  • - *
  • TPE1 Lead performer(s)/Soloist(s)
  • - *
  • TPE2 Band/orchestra/accompaniment
  • - *
  • TPE3 Conductor/performer refinement
  • - *
  • TPE4 Interpreted, remixed, or otherwise modified by
  • - *
  • TPOS Part of a set
  • - *
  • TPRO Produced notice
  • - *
  • TPUB Publisher
  • - *
  • TRCK Track number/Position in set
  • - *
  • TRSN Internet radio station name
  • - *
  • TRSO Internet radio station owner
  • - *
  • TSOA Album sort order
  • - *
  • TSOP Performer sort order
  • - *
  • TSOT Title sort order
  • - *
  • TSRC ISRC (international standard recording code)
  • - *
  • TSSE Software/Hardware and settings used for encoding
  • - *
  • TSST Set subtitle
  • - *
- * - * The ID3v2 Frames document gives a description of each of these formats - * and the expected order of strings in each. - * ID3v2::Header::frameID() can be used to determine the frame type. - * - * \note If non-Latin1 compatible strings are used with this class, - * even if the text encoding is set to Latin1, the frame will be written using UTF8 - * (with the encoding flag appropriately set in the output). - */ - -class TAGLIB_EXPORT TextIdentificationFrame : public Frame { - friend class FrameFactory; - - public: - /*! - * Construct an empty frame of type \a type. - * Uses \a encoding as the default text encoding. - * - * \note In this case you must specify the text encoding as it resolves the ambiguity between constructors. - * - * \note Please see the note in the class description regarding Latin1. - */ - explicit TextIdentificationFrame(const ByteVector &type, String::Type encoding); - - /*! - * This is a dual purpose constructor. - * \a data can either be binary data that should be parsed or (at a minimum) the frame ID. - */ - explicit TextIdentificationFrame(const ByteVector &data); - - /*! - * This is a special factory method to create a TIPL (involved people list) frame from the given \a properties. - * Will parse key=[list of values] data into the TIPL format as specified in the ID3 standard. - */ - static TextIdentificationFrame *createTIPLFrame(const PropertyMap &properties); - - /*! - * This is a special factory method to create a TMCL (musician credits list) frame from the given \a properties. - * Will parse key=[list of values] data into the TMCL format as specified in the ID3 standard, where key should be of the form instrumentPrefix:instrument. - */ - static TextIdentificationFrame *createTMCLFrame(const PropertyMap &properties); - /*! - * Destroys this TextIdentificationFrame instance. - */ - ~TextIdentificationFrame() override; - - /*! - * Text identification frames are a list of string fields. - * - * This function will accept either a StringList or a String (using the - * StringList constructor that accepts a single String). - * - * \note This will not change the text encoding of the frame even if the strings passed in are not of the same encoding. - * Please use setEncoding(s.type()) if you wish to change the encoding of the frame. - */ - void setText(const StringList &l); - - // Reimplementations. - - void setText(const String &s) override; - String toString() const override; - - /*! - * Returns the text encoding that will be used in rendering this frame. - * This defaults to the type that was either specified in the constructor or read from the frame when parsed. - * - * \note Please see the note in the class description regarding Latin1. - * - * \see setTextEncoding() - * \see render() - */ - String::Type textEncoding() const; - - /*! - * Sets the text encoding to be used when rendering this frame to \a encoding. - * - * \note Please see the note in the class description regarding Latin1. - * - * \see textEncoding() - * \see render() - */ - void setTextEncoding(String::Type encoding); - - /*! - * Returns a list of the strings in this frame. - */ - StringList fieldList() const; - - /*! - * Returns a KeyConversionMap mapping a role as it would be used in a PropertyMap to the corresponding key used in a TIPL ID3 frame to describe that role. - */ - static const KeyConversionMap &involvedPeopleMap(); - - PropertyMap asProperties() const override; - - protected: - // Reimplementations. - - void parseFields(const ByteVector &data) override; - ByteVector renderFields() const override; - - /*! - * The constructor used by the FrameFactory. - */ - TextIdentificationFrame(const ByteVector &data, Header *h); - - private: - TextIdentificationFrame(const TextIdentificationFrame &); - TextIdentificationFrame &operator=(const TextIdentificationFrame &); - - /*! - * Parses the special structure of a TIPL frame - * Only the whitelisted roles "ARRANGER", "ENGINEER", "PRODUCER", - * "DJMIXER" (ID3: "DJ-MIX") and "MIXER" (ID3: "MIX") are allowed. - */ - PropertyMap makeTIPLProperties() const; - /*! - * Parses the special structure of a TMCL frame. - */ - PropertyMap makeTMCLProperties() const; - class TextIdentificationFramePrivate; - TextIdentificationFramePrivate *d; -}; - -/*! - * This is a specialization of text identification frames that allows for user defined entries. - * Each entry has a description in addition to the normal list of fields that a text identification frame has. - * - * This description identifies the frame and must be unique. - */ - -//! An ID3v2 custom text identification frame implementation - -class TAGLIB_EXPORT UserTextIdentificationFrame : public TextIdentificationFrame { - friend class FrameFactory; - - public: - /*! - * Constructs an empty user defined text identification frame. - * For this to be a useful frame both a description and text must be set. - */ - explicit UserTextIdentificationFrame(String::Type encoding = String::Latin1); - - /*! - * Creates a frame based on \a data. - */ - explicit UserTextIdentificationFrame(const ByteVector &data); - - /*! - * Creates a user defined text identification frame with the given \a description and \a values. - */ - UserTextIdentificationFrame(const String &description, const StringList &values, String::Type encoding = String::UTF8); - - String toString() const override; - - /*! - * Returns the description for this frame. - */ - String description() const; - - /*! - * Sets the description of the frame to \a s. \a s must be unique. - * You can check for the presence of another user defined text frame of the same type using find() and testing for null. - */ - void setDescription(const String &s); - - StringList fieldList() const; - void setText(const String &text) override; - void setText(const StringList &fields); - - /*! - * A UserTextIdentificationFrame is parsed into a PropertyMap as follows: - * - the key is the frame's description, uppercased - * - if the description contains '::', only the substring after that separator is considered as key (compatibility with exfalso) - * - if the above rules don't yield a valid key (e.g. containing non-ASCII characters), the returned map will contain an entry "TXXX/" in its unsupportedData() list. - * - The values will be copies of the fieldList(). - * - If the description() appears as value in fieldList(), it will be omitted in the value list, in order to be compatible with TagLib which copies the description() into the fieldList(). - */ - PropertyMap asProperties() const override; - - /*! - * Searches for the user defined text frame with the description \a description in \a tag. - * This returns null if no matching frames were found. - */ - static UserTextIdentificationFrame *find(Tag *tag, const String &description); - - private: - explicit UserTextIdentificationFrame(const ByteVector &data, Header *h); - explicit UserTextIdentificationFrame(const TextIdentificationFrame&); - UserTextIdentificationFrame &operator=(const UserTextIdentificationFrame&); - - void checkFields(); - - class UserTextIdentificationFramePrivate; - UserTextIdentificationFramePrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp deleted file mode 100644 index 10c42d3d..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tbytevectorlist.h" -#include "tpropertymap.h" -#include "tdebug.h" - -#include "id3v2tag.h" -#include "uniquefileidentifierframe.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -class UniqueFileIdentifierFrame::UniqueFileIdentifierFramePrivate { - public: - String owner; - ByteVector identifier; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public methods -//////////////////////////////////////////////////////////////////////////////// - -UniqueFileIdentifierFrame::UniqueFileIdentifierFrame(const ByteVector &data) : ID3v2::Frame(data), - d(new UniqueFileIdentifierFramePrivate()) { - setData(data); -} - -UniqueFileIdentifierFrame::UniqueFileIdentifierFrame(const String &owner, const ByteVector &id) : ID3v2::Frame("UFID"), - d(new UniqueFileIdentifierFramePrivate()) { - d->owner = owner; - d->identifier = id; -} - -UniqueFileIdentifierFrame::~UniqueFileIdentifierFrame() { - delete d; -} - -String UniqueFileIdentifierFrame::owner() const { - return d->owner; -} - -ByteVector UniqueFileIdentifierFrame::identifier() const { - return d->identifier; -} - -void UniqueFileIdentifierFrame::setOwner(const String &s) { - d->owner = s; -} - -void UniqueFileIdentifierFrame::setIdentifier(const ByteVector &v) { - d->identifier = v; -} - -String UniqueFileIdentifierFrame::toString() const { - return String(); -} - -PropertyMap UniqueFileIdentifierFrame::asProperties() const { - PropertyMap map; - if (d->owner == "http://musicbrainz.org") { - map.insert("MUSICBRAINZ_TRACKID", String(d->identifier)); - } - else { - map.unsupportedData().append(frameID() + String("/") + d->owner); - } - return map; -} - -UniqueFileIdentifierFrame *UniqueFileIdentifierFrame::findByOwner(const ID3v2::Tag *tag, const String &o) { // static - - ID3v2::FrameList comments = tag->frameList("UFID"); - - for (ID3v2::FrameList::ConstIterator it = comments.begin(); - it != comments.end(); - ++it) { - UniqueFileIdentifierFrame *frame = dynamic_cast(*it); - if (frame && frame->owner() == o) - return frame; - } - - return nullptr; - -} - -void UniqueFileIdentifierFrame::parseFields(const ByteVector &data) { - - if (data.size() < 1) { - debug("An UFID frame must contain at least 1 byte."); - return; - } - - size_t pos = 0; - d->owner = readStringField(data, String::Latin1, pos); - d->identifier = data.mid(pos); - -} - -ByteVector UniqueFileIdentifierFrame::renderFields() const { - - ByteVector data; - - data.append(d->owner.data(String::Latin1)); - data.append(char(0)); - data.append(d->identifier); - - return data; - -} - -UniqueFileIdentifierFrame::UniqueFileIdentifierFrame(const ByteVector &data, Header *h) : Frame(h), - d(new UniqueFileIdentifierFramePrivate()) { - parseFields(fieldData(data)); -} diff --git a/3rdparty/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h b/3rdparty/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h deleted file mode 100644 index 52c127e0..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h +++ /dev/null @@ -1,119 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_UNIQUEFILEIDENTIFIERFRAME -#define TAGLIB_UNIQUEFILEIDENTIFIERFRAME - -#include "id3v2frame.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -/*! - * This is an implementation of ID3v2 unique file identifier frames. - * This frame is used to identify the file in an arbitrary database identified by the owner field. - */ - -//! An implementation of ID3v2 unique identifier frames - -class TAGLIB_EXPORT UniqueFileIdentifierFrame : public ID3v2::Frame { - friend class FrameFactory; - - public: - /*! - * Creates a unique file identifier frame based on \a data. - */ - explicit UniqueFileIdentifierFrame(const ByteVector &data); - - /*! - * Creates a unique file identifier frame with the owner \a owner and the identification \a id. - */ - explicit UniqueFileIdentifierFrame(const String &owner, const ByteVector &id); - - /*! - * Destroys the frame. - */ - ~UniqueFileIdentifierFrame() override; - - /*! - * Returns the owner for the frame; essentially this is the key for determining which identification scheme this key belongs to. - * This will usually either be an email address or URL for the person or tool used to create the unique identifier. - * - * \see setOwner() - */ - String owner() const; - - /*! - * Returns the unique identifier. - * Though sometimes this is a text string it also may be binary data and as much should be assumed when handling - * it. - */ - ByteVector identifier() const; - - /*! - * Sets the owner of the identification scheme to \a s. - * - * \see owner() - */ - void setOwner(const String &s); - - /*! - * Sets the unique file identifier to \a v. - * - * \see identifier() - */ - void setIdentifier(const ByteVector &v); - - String toString() const override; - - PropertyMap asProperties() const override; - - /*! - * UFID frames each have a unique owner. This searches for a UFID frame with the owner \a o and returns a pointer to it. - * - * \see owner() - */ - static UniqueFileIdentifierFrame *findByOwner(const Tag *tag, const String &o); - - protected: - void parseFields(const ByteVector &data) override; - ByteVector renderFields() const override; - - private: - UniqueFileIdentifierFrame(const UniqueFileIdentifierFrame&); - UniqueFileIdentifierFrame &operator=(const UniqueFileIdentifierFrame&); - - explicit UniqueFileIdentifierFrame(const ByteVector &data, Header *h); - - class UniqueFileIdentifierFramePrivate; - UniqueFileIdentifierFramePrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/frames/unknownframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/unknownframe.cpp deleted file mode 100644 index 8faf2018..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/unknownframe.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "unknownframe.h" -#include "tpropertymap.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -class UnknownFrame::UnknownFramePrivate { - public: - ByteVector fieldData; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -UnknownFrame::UnknownFrame(const ByteVector &data) : Frame(data), - d(new UnknownFramePrivate()) { - setData(data); -} - -UnknownFrame::~UnknownFrame() { - delete d; -} - -String UnknownFrame::toString() const { - return String(); -} - -ByteVector UnknownFrame::data() const { - return d->fieldData; -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void UnknownFrame::parseFields(const ByteVector &data) { - d->fieldData = data; -} - -ByteVector UnknownFrame::renderFields() const { - return d->fieldData; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -UnknownFrame::UnknownFrame(const ByteVector &data, Header *h) : Frame(h), - d(new UnknownFramePrivate()) { - parseFields(fieldData(data)); -} - -PropertyMap UnknownFrame::asProperties() const { - - PropertyMap m; - m.unsupportedData().append("UNKNOWN/" + frameID()); - return m; - -} diff --git a/3rdparty/taglib/mpeg/id3v2/frames/unknownframe.h b/3rdparty/taglib/mpeg/id3v2/frames/unknownframe.h deleted file mode 100644 index 7f6ddfe6..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/unknownframe.h +++ /dev/null @@ -1,81 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_UNKNOWNFRAME_H -#define TAGLIB_UNKNOWNFRAME_H - -#include "id3v2frame.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -//! A frame type \e unknown to TagLib. - -/*! - * This class represents a frame type not known (or more often simply unimplemented) in TagLib. - * This is here provide a basic API for - * manipulating the binary data of unknown frames and to provide a means of rendering such \e unknown frames. - * - * Please note that a cleaner way of handling frame types that TagLib does - * not understand is to subclass ID3v2::Frame and ID3v2::FrameFactory to have - * your frame type supported through the standard ID3v2 mechanism. - */ - -class TAGLIB_EXPORT UnknownFrame : public Frame { - friend class FrameFactory; - - public: - explicit UnknownFrame(const ByteVector &data); - ~UnknownFrame() override; - - String toString() const override; - - /*! - * Returns the field data (everything but the header) for this frame. - */ - ByteVector data() const; - - PropertyMap asProperties() const override; - - protected: - void parseFields(const ByteVector &data) override; - ByteVector renderFields() const override; - - private: - explicit UnknownFrame(const ByteVector &data, Header *h); - UnknownFrame(const UnknownFrame&); - UnknownFrame &operator=(const UnknownFrame&); - - class UnknownFramePrivate; - UnknownFramePrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp deleted file mode 100644 index 84872a83..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - - copyright : (C) 2006 by Urs Fleisch - email : ufleisch@users.sourceforge.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "unsynchronizedlyricsframe.h" -#include "tbytevectorlist.h" -#include "id3v2tag.h" -#include "tdebug.h" -#include "tpropertymap.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -class UnsynchronizedLyricsFrame::UnsynchronizedLyricsFramePrivate { - public: - explicit UnsynchronizedLyricsFramePrivate() : textEncoding(String::Latin1) {} - String::Type textEncoding; - ByteVector language; - String description; - String text; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(String::Type encoding) : Frame("USLT"), - d(new UnsynchronizedLyricsFramePrivate()) { - d->textEncoding = encoding; -} - -UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(const ByteVector &data) : Frame(data), - d(new UnsynchronizedLyricsFramePrivate()) { - setData(data); -} - -UnsynchronizedLyricsFrame::~UnsynchronizedLyricsFrame() { - delete d; -} - -String UnsynchronizedLyricsFrame::toString() const { - return d->text; -} - -ByteVector UnsynchronizedLyricsFrame::language() const { - return d->language; -} - -String UnsynchronizedLyricsFrame::description() const { - return d->description; -} - -String UnsynchronizedLyricsFrame::text() const { - return d->text; -} - -void UnsynchronizedLyricsFrame::setLanguage(const ByteVector &languageEncoding) { - d->language = languageEncoding.mid(0, 3); -} - -void UnsynchronizedLyricsFrame::setDescription(const String &s) { - d->description = s; -} - -void UnsynchronizedLyricsFrame::setText(const String &s) { - d->text = s; -} - - -String::Type UnsynchronizedLyricsFrame::textEncoding() const { - return d->textEncoding; -} - -void UnsynchronizedLyricsFrame::setTextEncoding(String::Type encoding) { - d->textEncoding = encoding; -} - -PropertyMap UnsynchronizedLyricsFrame::asProperties() const { - - PropertyMap map; - String key = description().upper(); - if (key.isEmpty() || key == "LYRICS") - map.insert("LYRICS", text()); - else - map.insert("LYRICS:" + key, text()); - return map; - -} - -UnsynchronizedLyricsFrame *UnsynchronizedLyricsFrame::findByDescription(const ID3v2::Tag *tag, const String &d) { // static - - ID3v2::FrameList lyrics = tag->frameList("USLT"); - - for (ID3v2::FrameList::ConstIterator it = lyrics.begin(); it != lyrics.end(); ++it) { - UnsynchronizedLyricsFrame *frame = dynamic_cast(*it); - if (frame && frame->description() == d) - return frame; - } - return nullptr; - -} -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void UnsynchronizedLyricsFrame::parseFields(const ByteVector &data) { - - if (data.size() < 5) { - debug("An unsynchronized lyrics frame must contain at least 5 bytes."); - return; - } - - d->textEncoding = String::Type(data[0]); - d->language = data.mid(1, 3); - - int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2; - - ByteVectorList l = - ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2); - - if (l.size() == 2) { - if (d->textEncoding == String::Latin1) { - d->description = Tag::latin1StringHandler()->parse(l.front()); - d->text = Tag::latin1StringHandler()->parse(l.back()); - } - else { - d->description = String(l.front(), d->textEncoding); - d->text = String(l.back(), d->textEncoding); - } - } - -} - -ByteVector UnsynchronizedLyricsFrame::renderFields() const { - - StringList sl; - sl.append(d->description); - sl.append(d->text); - - const String::Type encoding = checkTextEncoding(sl, d->textEncoding); - - ByteVector v; - - v.append(char(encoding)); - v.append(d->language.size() == 3 ? d->language : "XXX"); - v.append(d->description.data(encoding)); - v.append(textDelimiter(encoding)); - v.append(d->text.data(encoding)); - - return v; - -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(const ByteVector &data, Header *h) : Frame(h), - d(new UnsynchronizedLyricsFramePrivate()) { - parseFields(fieldData(data)); -} diff --git a/3rdparty/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h b/3rdparty/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h deleted file mode 100644 index 1b6f396b..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h +++ /dev/null @@ -1,175 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - copyright : (C) 2006 by Urs Fleisch - email : ufleisch@users.sourceforge.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_UNSYNCHRONIZEDLYRICSFRAME_H -#define TAGLIB_UNSYNCHRONIZEDLYRICSFRAME_H - -#include "id3v2frame.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -//! ID3v2 unsynchronized lyrics frame -/*! - * An implementation of ID3v2 unsynchronized lyrics. - */ -class TAGLIB_EXPORT UnsynchronizedLyricsFrame : public Frame { - friend class FrameFactory; - - public: - /*! - * Construct an empty unsynchronized lyrics frame that will use the text encoding \a encoding. - */ - explicit UnsynchronizedLyricsFrame(String::Type encoding = String::Latin1); - - /*! - * Construct a unsynchronized lyrics frame based on the data in \a data. - */ - explicit UnsynchronizedLyricsFrame(const ByteVector &data); - - /*! - * Destroys this UnsynchronizedLyricsFrame instance. - */ - ~UnsynchronizedLyricsFrame() override; - - /*! - * Returns the text of this unsynchronized lyrics frame. - * - * \see text() - */ - String toString() const override; - - /*! - * Returns the language encoding as a 3 byte encoding as specified by - * ISO-639-2. - * - * \note Most taggers simply ignore this value. - * - * \see setLanguage() - */ - ByteVector language() const; - - /*! - * Returns the description of this unsynchronized lyrics frame. - * - * \note Most taggers simply ignore this value. - * - * \see setDescription() - */ - String description() const; - - /*! - * Returns the text of this unsynchronized lyrics frame. - * - * \see setText() - */ - String text() const; - - /*! - * Set the language using the 3 byte language code from - * ISO-639-2 to - * \a languageCode. - * - * \see language() - */ - void setLanguage(const ByteVector &languageEncoding); - - /*! - * Sets the description of the unsynchronized lyrics frame to \a s. - * - * \see description() - */ - void setDescription(const String &s); - - /*! - * Sets the text portion of the unsynchronized lyrics frame to \a s. - * - * \see text() - */ - void setText(const String &s) override; - - /*! - * Returns the text encoding that will be used in rendering this frame. - * This defaults to the type that was either specified in the constructor or read from the frame when parsed. - * - * \see setTextEncoding() - * \see render() - */ - String::Type textEncoding() const; - - /*! - * Sets the text encoding to be used when rendering this frame to - * \a encoding. - * - * \see textEncoding() - * \see render() - */ - void setTextEncoding(String::Type encoding); - - - /*! Parses this frame as PropertyMap with a single key. - * - if description() is empty or "LYRICS", the key will be "LYRICS" - * - if description() is not a valid PropertyMap key, - * the frame will be marked unsupported by an entry "USLT/" in the unsupportedData() attribute of the returned map. - * - otherwise, the key will be "LYRICS:" - * - The single value will be the frame's text(). - * Note that currently the language() field is not supported by the PropertyMap interface. - */ - PropertyMap asProperties() const override; - - /*! - * LyricsFrames each have a unique description. - * This searches for a lyrics frame with the description \a d and returns a pointer to it. - * If no frame is found that matches the given description null is returned. - * - * \see description() - */ - static UnsynchronizedLyricsFrame *findByDescription(const Tag *tag, const String &d); - - protected: - // Reimplementations. - - void parseFields(const ByteVector &data) override; - ByteVector renderFields() const override; - - private: - /*! - * The constructor used by the FrameFactory. - */ - explicit UnsynchronizedLyricsFrame(const ByteVector &data, Header *h); - UnsynchronizedLyricsFrame(const UnsynchronizedLyricsFrame&); - UnsynchronizedLyricsFrame &operator=(const UnsynchronizedLyricsFrame&); - - class UnsynchronizedLyricsFramePrivate; - UnsynchronizedLyricsFramePrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/frames/urllinkframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/urllinkframe.cpp deleted file mode 100644 index a88d522f..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/urllinkframe.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - - copyright : (C) 2006 by Urs Fleisch - email : ufleisch@users.sourceforge.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "urllinkframe.h" -#include "id3v2tag.h" -#include "tdebug.h" -#include "tstringlist.h" -#include "tpropertymap.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -class UrlLinkFrame::UrlLinkFramePrivate { - public: - String url; -}; - -class UserUrlLinkFrame::UserUrlLinkFramePrivate { - public: - explicit UserUrlLinkFramePrivate() : textEncoding(String::Latin1) {} - String::Type textEncoding; - String description; -}; - -//////////////////////////////////////////////////////////////////////////////// -// UrlLinkFrame public members -//////////////////////////////////////////////////////////////////////////////// - -UrlLinkFrame::UrlLinkFrame(const ByteVector &data) : Frame(data), - d(new UrlLinkFramePrivate()) { - setData(data); -} - -UrlLinkFrame::~UrlLinkFrame() { - delete d; -} - -void UrlLinkFrame::setUrl(const String &s) { - d->url = s; -} - -String UrlLinkFrame::url() const { - return d->url; -} - -void UrlLinkFrame::setText(const String &s) { - setUrl(s); -} - -String UrlLinkFrame::toString() const { - return url(); -} - -PropertyMap UrlLinkFrame::asProperties() const { - - String key = frameIDToKey(frameID()); - PropertyMap map; - if (key.isEmpty()) - // unknown W*** frame - this normally shouldn't happen - map.unsupportedData().append(frameID()); - else - map.insert(key, url()); - return map; - -} - -//////////////////////////////////////////////////////////////////////////////// -// UrlLinkFrame protected members -//////////////////////////////////////////////////////////////////////////////// - -void UrlLinkFrame::parseFields(const ByteVector &data) { - d->url = String(data); -} - -ByteVector UrlLinkFrame::renderFields() const { - return d->url.data(String::Latin1); -} - -UrlLinkFrame::UrlLinkFrame(const ByteVector &data, Header *h) : Frame(h), - d(new UrlLinkFramePrivate()) { - parseFields(fieldData(data)); -} - -//////////////////////////////////////////////////////////////////////////////// -// UserUrlLinkFrame public members -//////////////////////////////////////////////////////////////////////////////// - -UserUrlLinkFrame::UserUrlLinkFrame(String::Type encoding) : UrlLinkFrame("WXXX"), - d(new UserUrlLinkFramePrivate()) { - d->textEncoding = encoding; -} - -UserUrlLinkFrame::UserUrlLinkFrame(const ByteVector &data) : UrlLinkFrame(data), - d(new UserUrlLinkFramePrivate()) { - setData(data); -} - -UserUrlLinkFrame::~UserUrlLinkFrame() { - delete d; -} - -String UserUrlLinkFrame::toString() const { - return "[" + description() + "] " + url(); -} - -String::Type UserUrlLinkFrame::textEncoding() const { - return d->textEncoding; -} - -void UserUrlLinkFrame::setTextEncoding(String::Type encoding) { - d->textEncoding = encoding; -} - -String UserUrlLinkFrame::description() const { - return d->description; -} - -void UserUrlLinkFrame::setDescription(const String &s) { - d->description = s; -} - -PropertyMap UserUrlLinkFrame::asProperties() const { - - PropertyMap map; - String key = description().upper(); - if (key.isEmpty() || key == "URL") - map.insert("URL", url()); - else - map.insert("URL:" + key, url()); - return map; - -} - -UserUrlLinkFrame *UserUrlLinkFrame::find(ID3v2::Tag *tag, const String &description) { // static - - FrameList l = tag->frameList("WXXX"); - for (FrameList::ConstIterator it = l.begin(); it != l.end(); ++it) { - UserUrlLinkFrame *f = dynamic_cast(*it); - if (f && f->description() == description) - return f; - } - return nullptr; - -} - -//////////////////////////////////////////////////////////////////////////////// -// UserUrlLinkFrame protected members -//////////////////////////////////////////////////////////////////////////////// - -void UserUrlLinkFrame::parseFields(const ByteVector &data) { - - if (data.size() < 2) { - debug("A user URL link frame must contain at least 2 bytes."); - return; - } - - size_t pos = 0; - - d->textEncoding = String::Type(data[0]); - pos += 1; - - if (d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8) { - const size_t offset = data.find(textDelimiter(d->textEncoding), pos); - if (offset == ByteVector::npos() || offset < pos) - return; - - d->description = String(data.mid(pos, offset - pos), d->textEncoding); - pos = offset + 1; - } - else { - const size_t len = data.mid(pos).find(textDelimiter(d->textEncoding), 0, 2); - if (len == ByteVector::npos()) - return; - - d->description = String(data.mid(pos, len), d->textEncoding); - pos += len + 2; - } - - setUrl(String(data.mid(pos))); - -} - -ByteVector UserUrlLinkFrame::renderFields() const { - - ByteVector v; - - String::Type encoding = checkTextEncoding(d->description, d->textEncoding); - - v.append(char(encoding)); - v.append(d->description.data(encoding)); - v.append(textDelimiter(encoding)); - v.append(url().data(String::Latin1)); - - return v; - -} - -UserUrlLinkFrame::UserUrlLinkFrame(const ByteVector &data, Header *h) : UrlLinkFrame(data, h), - d(new UserUrlLinkFramePrivate()) { - parseFields(fieldData(data)); -} diff --git a/3rdparty/taglib/mpeg/id3v2/frames/urllinkframe.h b/3rdparty/taglib/mpeg/id3v2/frames/urllinkframe.h deleted file mode 100644 index 70e365ad..00000000 --- a/3rdparty/taglib/mpeg/id3v2/frames/urllinkframe.h +++ /dev/null @@ -1,187 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - - copyright : (C) 2006 by Urs Fleisch - email : ufleisch@users.sourceforge.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_URLLINKFRAME_H -#define TAGLIB_URLLINKFRAME_H - -#include "id3v2frame.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -//! ID3v2 URL frame -/*! - * An implementation of ID3v2 URL link frames. - */ -class TAGLIB_EXPORT UrlLinkFrame : public Frame { - friend class FrameFactory; - - public: - /*! - * This is a dual purpose constructor. \a data can either be binary data - * that should be parsed or (at a minimum) the frame ID. - */ - explicit UrlLinkFrame(const ByteVector &data); - - /*! - * Destroys this UrlLinkFrame instance. - */ - ~UrlLinkFrame() override; - - /*! - * Returns the URL. - */ - virtual String url() const; - - /*! - * Sets the URL to \a s. - */ - virtual void setUrl(const String &s); - - // Reimplementations. - - void setText(const String &s) override; - String toString() const override; - PropertyMap asProperties() const override; - - protected: - void parseFields(const ByteVector &data) override; - ByteVector renderFields() const override; - - /*! - * The constructor used by the FrameFactory. - */ - UrlLinkFrame(const ByteVector &data, Header *h); - - private: - UrlLinkFrame(const UrlLinkFrame &); - UrlLinkFrame &operator=(const UrlLinkFrame &); - - class UrlLinkFramePrivate; - UrlLinkFramePrivate *d; -}; - -//! ID3v2 User defined URL frame - -/*! - * This is a specialization of URL link frames that allows for user defined entries. - * Each entry has a description in addition to the normal list of fields that a URL link frame has. - * - * This description identifies the frame and must be unique. - */ -class TAGLIB_EXPORT UserUrlLinkFrame : public UrlLinkFrame { - friend class FrameFactory; - - public: - /*! - * Constructs an empty user defined URL link frame. - * For this to be a useful frame both a description and text must be set. - */ - explicit UserUrlLinkFrame(String::Type encoding = String::Latin1); - - /*! - * This is a dual purpose constructor. - * \a data can either be binary data that should be parsed or (at a minimum) the frame ID. - */ - explicit UserUrlLinkFrame(const ByteVector &data); - - /*! - * Destroys this UserUrlLinkFrame instance. - */ - ~UserUrlLinkFrame() override; - - // Reimplementations. - - String toString() const override; - - /*! - * Returns the text encoding that will be used in rendering this frame. - * This defaults to the type that was either specified in the constructor or read from the frame when parsed. - * - * \see setTextEncoding() - * \see render() - */ - String::Type textEncoding() const; - - /*! - * Sets the text encoding to be used when rendering this frame to \a encoding. - * - * \see textEncoding() - * \see render() - */ - void setTextEncoding(String::Type encoding); - - /*! - * Returns the description for this frame. - */ - String description() const; - - /*! - * Sets the description of the frame to \a s. \a s must be unique. - */ - void setDescription(const String &s); - - /*! - * Parses the UserUrlLinkFrame as PropertyMap. The description() is taken as key, - * and the URL as single value. - * - if description() is empty, the key will be "URL". - * - otherwise, if description() is not a valid key (e.g. containing non-ASCII - * characters), the returned map will contain an entry "WXXX/" - * in its unsupportedData() list. - */ - PropertyMap asProperties() const override; - - /*! - * Searches for the user defined url frame with the description \a description in \a tag. - * This returns null if no matching frames were found. - */ - static UserUrlLinkFrame *find(Tag *tag, const String &description); - - protected: - void parseFields(const ByteVector &data) override; - ByteVector renderFields() const override; - - /*! - * The constructor used by the FrameFactory. - */ - UserUrlLinkFrame(const ByteVector &data, Header *h); - - private: - UserUrlLinkFrame(const UserUrlLinkFrame&); - UserUrlLinkFrame &operator=(const UserUrlLinkFrame&); - - class UserUrlLinkFramePrivate; - UserUrlLinkFramePrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2.2.0.txt b/3rdparty/taglib/mpeg/id3v2/id3v2.2.0.txt deleted file mode 100644 index a69bddd3..00000000 --- a/3rdparty/taglib/mpeg/id3v2/id3v2.2.0.txt +++ /dev/null @@ -1,1660 +0,0 @@ - -Informal standard M. Nilsson -Document: id3v2-00.txt 26th March 1998 - - - ID3 tag version 2 - -Status of this document - - This document is an Informal standard and is released so that - implementors could have a set standard before the formal standard is - set. The formal standard will use another version number if not - identical to what is described in this document. The contents in this - document may change for clarifications but never for added or altered - functionallity. - - Distribution of this document is unlimited. - - -Abstract - - The recent gain of popularity for MPEG layer III audio files on the - internet forced a standardised way of storing information about an - audio file within itself to determinate its origin and contents. - - Today the most accepted way to do this is with the so called ID3 tag, - which is simple but very limited and in some cases very unsuitable. - The ID3 tag has very limited space in every field, very limited - numbers of fields, not expandable or upgradeable and is placed at the - end of a the file, which is unsuitable for streaming audio. This draft - is an attempt to answer these issues with a new version of the ID3 - tag. - - -1. Table of contents - - 2. Conventions in this document - 3. ID3v2 overview - 3.1. ID3v2 header - 3.2. ID3v2 frames overview - 4. Declared ID3v2 frames - 4.1. Unique file identifier - 4.2. Text information frames - 4.2.1. Text information frames - details - 4.2.2. User defined text information frame - 4.3. URL link frames - 4.3.1. URL link frames - details - 4.3.2. User defined URL link frame - 4.4. Involved people list - 4.5. Music CD Identifier - 4.6. Event timing codes - 4.7. MPEG location lookup table - 4.8. Synced tempo codes - 4.9. Unsychronised lyrics/text transcription - 4.10. Synchronised lyrics/text - 4.11. Comments - 4.12. Relative volume adjustment - 4.13. Equalisation - 4.14. Reverb - 4.15. Attached picture - 4.16. General encapsulated object - 4.17. Play counter - 4.18. Popularimeter - 4.19. Recommended buffer size - 4.20. Encrypted meta frame - 4.21. Audio encryption - 4.22. Linked information - 5. The 'unsynchronisation scheme' - 6. Copyright - 7. References - 8. Appendix - A. Appendix A - ID3-Tag Specification V1.1 - A.1. Overview - A.2. ID3v1 Implementation - A.3. Genre List - A.4. Track addition - ID3v1.1 - 9. Author's Address - - -2. Conventions in this document - - In the examples, text within "" is a text string exactly as it appears - in a file. Numbers preceded with $ are hexadecimal and numbers - preceded with % are binary. $xx is used to indicate a byte with - unknown content. %x is used to indicate a bit with unknown content. - The most significant bit (MSB) of a byte is called 'bit 7' and the - least significant bit (LSB) is called 'bit 0'. - - A tag is the whole tag described in this document. A frame is a block - of information in the tag. The tag consists of a header, frames and - optional padding. A field is a piece of information; one value, a - string etc. A numeric string is a string that consists of the - characters 0-9 only. - - -3. ID3v2 overview - - The two biggest design goals were to be able to implement ID3v2 - without disturbing old software too much and that ID3v2 should be - expandable. - - The first criterion is met by the simple fact that the MPEG [MPEG] - decoding software uses a syncsignal, embedded in the audiostream, to - 'lock on to' the audio. Since the ID3v2 tag doesn't contain a valid - syncsignal, no software will attempt to play the tag. If, for any - reason, coincidence make a syncsignal appear within the tag it will be - taken care of by the 'unsynchronisation scheme' described in section - 5. - - The second criterion has made a more noticeable impact on the design - of the ID3v2 tag. It is constructed as a container for several - information blocks, called frames, whose format need not be known to - the software that encounters them. At the start of every frame there - is an identifier that explains the frames's format and content, and a - size descriptor that allows software to skip unknown frames. - - If a total revision of the ID3v2 tag should be needed, there is a - version number and a size descriptor in the ID3v2 header. - - The ID3 tag described in this document is mainly targeted to files - encoded with MPEG-2 layer I, MPEG-2 layer II, MPEG-2 layer III and - MPEG-2.5, but may work with other types of encoded audio. - - The bitorder in ID3v2 is most significant bit first (MSB). The - byteorder in multibyte numbers is most significant byte first (e.g. - $12345678 would be encoded $12 34 56 78). - - It is permitted to include padding after all the final frame (at the - end of the ID3 tag), making the size of all the frames together - smaller than the size given in the head of the tag. A possible purpose - of this padding is to allow for adding a few additional frames or - enlarge existing frames within the tag without having to rewrite the - entire file. The value of the padding bytes must be $00. - - -3.1. ID3v2 header - - The ID3v2 tag header, which should be the first information in the - file, is 10 bytes as follows: - - ID3/file identifier "ID3" - ID3 version $02 00 - ID3 flags %xx000000 - ID3 size 4 * %0xxxxxxx - - The first three bytes of the tag are always "ID3" to indicate that - this is an ID3 tag, directly followed by the two version bytes. The - first byte of ID3 version is it's major version, while the second byte - is its revision number. All revisions are backwards compatible while - major versions are not. If software with ID3v2 and below support - should encounter version three or higher it should simply ignore the - whole tag. Version and revision will never be $FF. - - The first bit (bit 7) in the 'ID3 flags' is indicating whether or not - unsynchronisation is used (see section 5 for details); a set bit - indicates usage. - - The second bit (bit 6) is indicating whether or not compression is - used; a set bit indicates usage. Since no compression scheme has been - decided yet, the ID3 decoder (for now) should just ignore the entire - tag if the compression bit is set. - - The ID3 tag size is encoded with four bytes where the first bit (bit - 7) is set to zero in every byte, making a total of 28 bits. The zeroed - bits are ignored, so a 257 bytes long tag is represented as $00 00 02 - 01. - - The ID3 tag size is the size of the complete tag after - unsychronisation, including padding, excluding the header (total tag - size - 10). The reason to use 28 bits (representing up to 256MB) for - size description is that we don't want to run out of space here. - - A ID3v2 tag can be detected with the following pattern: - $49 44 33 yy yy xx zz zz zz zz - Where yy is less than $FF, xx is the 'flags' byte and zz is less than - $80. - - -3.2. ID3v2 frames overview - - The headers of the frames are similar in their construction. They - consist of one three character identifier (capital A-Z and 0-9) and - one three byte size field, making a total of six bytes. The header is - excluded from the size. Identifiers beginning with "X", "Y" and "Z" - are for experimental use and free for everyone to use. Have in mind - that someone else might have used the same identifier as you. All - other identifiers are either used or reserved for future use. - - The three character frame identifier is followed by a three byte size - descriptor, making a total header size of six bytes in every frame. - The size is calculated as framesize excluding frame identifier and - size descriptor (frame size - 6). - - There is no fixed order of the frames' appearance in the tag, although - it is desired that the frames are arranged in order of significance - concerning the recognition of the file. An example of such order: - UFI, MCI, TT2 ... - - A tag must contain at least one frame. A frame must be at least 1 byte - big, excluding the 6-byte header. - - If nothing else is said a string is represented as ISO-8859-1 - [ISO-8859-1] characters in the range $20 - $FF. All unicode strings - [UNICODE] use 16-bit unicode 2.0 (ISO/IEC 10646-1:1993, UCS-2). All - numeric strings are always encoded as ISO-8859-1. Terminated strings - are terminated with $00 if encoded with ISO-8859-1 and $00 00 if - encoded as unicode. If nothing else is said newline character is - forbidden. In ISO-8859-1 a new line is represented, when allowed, with - $0A only. Frames that allow different types of text encoding have a - text encoding description byte directly after the frame size. If - ISO-8859-1 is used this byte should be $00, if unicode is used it - should be $01. - - The three byte language field is used to describe the language of the - frame's content, according to ISO-639-2 [ISO-639-2]. - - All URLs [URL] may be relative, e.g. "picture.png", "../doc.txt". - - If a frame is longer than it should be, e.g. having more fields than - specified in this document, that indicates that additions to the - frame have been made in a later version of the ID3 standard. This - is reflected by the revision number in the header of the tag. - - -4. Declared ID3v2 frames - - The following frames are declared in this draft. - - 4.19 BUF Recommended buffer size - - 4.17 CNT Play counter - 4.11 COM Comments - 4.21 CRA Audio encryption - 4.20 CRM Encrypted meta frame - - 4.6 ETC Event timing codes - 4.13 EQU Equalization - - 4.16 GEO General encapsulated object - - 4.4 IPL Involved people list - - 4.22 LNK Linked information - - 4.5 MCI Music CD Identifier - 4.7 MLL MPEG location lookup table - - 4.15 PIC Attached picture - 4.18 POP Popularimeter - - 4.14 REV Reverb - 4.12 RVA Relative volume adjustment - - 4.10 SLT Synchronized lyric/text - 4.8 STC Synced tempo codes - - 4.2.1 TAL Album/Movie/Show title - 4.2.1 TBP BPM (Beats Per Minute) - 4.2.1 TCM Composer - 4.2.1 TCO Content type - 4.2.1 TCR Copyright message - 4.2.1 TDA Date - 4.2.1 TDY Playlist delay - 4.2.1 TEN Encoded by - 4.2.1 TFT File type - 4.2.1 TIM Time - 4.2.1 TKE Initial key - 4.2.1 TLA Language(s) - 4.2.1 TLE Length - 4.2.1 TMT Media type - 4.2.1 TOA Original artist(s)/performer(s) - 4.2.1 TOF Original filename - 4.2.1 TOL Original Lyricist(s)/text writer(s) - 4.2.1 TOR Original release year - 4.2.1 TOT Original album/Movie/Show title - 4.2.1 TP1 Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group - 4.2.1 TP2 Band/Orchestra/Accompaniment - 4.2.1 TP3 Conductor/Performer refinement - 4.2.1 TP4 Interpreted, remixed, or otherwise modified by - 4.2.1 TPA Part of a set - 4.2.1 TPB Publisher - 4.2.1 TRC ISRC (International Standard Recording Code) - 4.2.1 TRD Recording dates - 4.2.1 TRK Track number/Position in set - 4.2.1 TSI Size - 4.2.1 TSS Software/hardware and settings used for encoding - 4.2.1 TT1 Content group description - 4.2.1 TT2 Title/Songname/Content description - 4.2.1 TT3 Subtitle/Description refinement - 4.2.1 TXT Lyricist/text writer - 4.2.2 TXX User defined text information frame - 4.2.1 TYE Year - - 4.1 UFI Unique file identifier - 4.9 ULT Unsychronized lyric/text transcription - - 4.3.1 WAF Official audio file webpage - 4.3.1 WAR Official artist/performer webpage - 4.3.1 WAS Official audio source webpage - 4.3.1 WCM Commercial information - 4.3.1 WCP Copyright/Legal information - 4.3.1 WPB Publishers official webpage - 4.3.2 WXX User defined URL link frame - - -4.1. Unique file identifier - - This frame's purpose is to be able to identify the audio file in a - database that may contain more information relevant to the content. - Since standardisation of such a database is beyond this document, all - frames begin with a null-terminated string with a URL [URL] containing - an email address, or a link to a location where an email address can - be found, that belongs to the organisation responsible for this - specific database implementation. Questions regarding the database - should be sent to the indicated email address. The URL should not be - used for the actual database queries. If a $00 is found directly after - the 'Frame size' the whole frame should be ignored, and preferably be - removed. The 'Owner identifier' is then followed by the actual - identifier, which may be up to 64 bytes. There may be more than one - "UFI" frame in a tag, but only one with the same 'Owner identifier'. - - Unique file identifier "UFI" - Frame size $xx xx xx - Owner identifier $00 - Identifier - - -4.2. Text information frames - - The text information frames are the most important frames, containing - information like artist, album and more. There may only be one text - information frame of its kind in an tag. If the textstring is followed - by a termination ($00 (00)) all the following information should be - ignored and not be displayed. All the text information frames have the - following format: - - Text information identifier "T00" - "TZZ" , excluding "TXX", - described in 4.2.2. - Frame size $xx xx xx - Text encoding $xx - Information - - -4.2.1. Text information frames - details - - TT1 - The 'Content group description' frame is used if the sound belongs to - a larger category of sounds/music. For example, classical music is - often sorted in different musical sections (e.g. "Piano Concerto", - "Weather - Hurricane"). - - TT2 - The 'Title/Songname/Content description' frame is the actual name of - the piece (e.g. "Adagio", "Hurricane Donna"). - - TT3 - The 'Subtitle/Description refinement' frame is used for information - directly related to the contents title (e.g. "Op. 16" or "Performed - live at wembley"). - - TP1 - The 'Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group' is - used for the main artist(s). They are seperated with the "/" - character. - - TP2 - The 'Band/Orchestra/Accompaniment' frame is used for additional - information about the performers in the recording. - - TP3 - The 'Conductor' frame is used for the name of the conductor. - - TP4 - The 'Interpreted, remixed, or otherwise modified by' frame contains - more information about the people behind a remix and similar - interpretations of another existing piece. - - TCM - The 'Composer(s)' frame is intended for the name of the composer(s). - They are seperated with the "/" character. - - TXT - The 'Lyricist(s)/text writer(s)' frame is intended for the writer(s) - of the text or lyrics in the recording. They are seperated with the - "/" character. - - TLA - The 'Language(s)' frame should contain the languages of the text or - lyrics in the audio file. The language is represented with three - characters according to ISO-639-2. If more than one language is used - in the text their language codes should follow according to their - usage. - - TCO - The content type, which previously (in ID3v1.1, see appendix A) was - stored as a one byte numeric value only, is now a numeric string. You - may use one or several of the types as ID3v1.1 did or, since the - category list would be impossible to maintain with accurate and up to - date categories, define your own. - References to the ID3v1 genres can be made by, as first byte, enter - "(" followed by a number from the genres list (section A.3.) and - ended with a ")" character. This is optionally followed by a - refinement, e.g. "(21)" or "(4)Eurodisco". Several references can be - made in the same frame, e.g. "(51)(39)". If the refinement should - begin with a "(" character it should be replaced with "((", e.g. "((I - can figure out any genre)" or "(55)((I think...)". The following new - content types is defined in ID3v2 and is implemented in the same way - as the numerig content types, e.g. "(RX)". - - RX Remix - CR Cover - - TAL - The 'Album/Movie/Show title' frame is intended for the title of the - recording(/source of sound) which the audio in the file is taken from. - - TPA - The 'Part of a set' frame is a numeric string that describes which - part of a set the audio came from. This frame is used if the source - described in the "TAL" frame is divided into several mediums, e.g. a - double CD. The value may be extended with a "/" character and a - numeric string containing the total number of parts in the set. E.g. - "1/2". - - TRK - The 'Track number/Position in set' frame is a numeric string - containing the order number of the audio-file on its original - recording. This may be extended with a "/" character and a numeric - string containing the total numer of tracks/elements on the original - recording. E.g. "4/9". - - TRC - The 'ISRC' frame should contian the International Standard Recording - Code [ISRC]. - - TYE - The 'Year' frame is a numeric string with a year of the recording. - This frames is always four characters long (until the year 10000). - - TDA - The 'Date' frame is a numeric string in the DDMM format containing - the date for the recording. This field is always four characters - long. - - TIM - The 'Time' frame is a numeric string in the HHMM format containing - the time for the recording. This field is always four characters - long. - - TRD - The 'Recording dates' frame is a intended to be used as complement to - the "TYE", "TDA" and "TIM" frames. E.g. "4th-7th June, 12th June" in - combination with the "TYE" frame. - - TMT - The 'Media type' frame describes from which media the sound - originated. This may be a textstring or a reference to the predefined - media types found in the list below. References are made within "(" - and ")" and are optionally followed by a text refinement, e.g. "(MC) - with four channels". If a text refinement should begin with a "(" - character it should be replaced with "((" in the same way as in the - "TCO" frame. Predefined refinements is appended after the media type, - e.g. "(CD/S)" or "(VID/PAL/VHS)". - - DIG Other digital media - /A Analog transfer from media - - ANA Other analog media - /WAC Wax cylinder - /8CA 8-track tape cassette - - CD CD - /A Analog transfer from media - /DD DDD - /AD ADD - /AA AAD - - LD Laserdisc - /A Analog transfer from media - - TT Turntable records - /33 33.33 rpm - /45 45 rpm - /71 71.29 rpm - /76 76.59 rpm - /78 78.26 rpm - /80 80 rpm - - MD MiniDisc - /A Analog transfer from media - - DAT DAT - /A Analog transfer from media - /1 standard, 48 kHz/16 bits, linear - /2 mode 2, 32 kHz/16 bits, linear - /3 mode 3, 32 kHz/12 bits, nonlinear, low speed - /4 mode 4, 32 kHz/12 bits, 4 channels - /5 mode 5, 44.1 kHz/16 bits, linear - /6 mode 6, 44.1 kHz/16 bits, 'wide track' play - - DCC DCC - /A Analog transfer from media - - DVD DVD - /A Analog transfer from media - - TV Television - /PAL PAL - /NTSC NTSC - /SECAM SECAM - - VID Video - /PAL PAL - /NTSC NTSC - /SECAM SECAM - /VHS VHS - /SVHS S-VHS - /BETA BETAMAX - - RAD Radio - /FM FM - /AM AM - /LW LW - /MW MW - - TEL Telephone - /I ISDN - - MC MC (normal cassette) - /4 4.75 cm/s (normal speed for a two sided cassette) - /9 9.5 cm/s - /I Type I cassette (ferric/normal) - /II Type II cassette (chrome) - /III Type III cassette (ferric chrome) - /IV Type IV cassette (metal) - - REE Reel - /9 9.5 cm/s - /19 19 cm/s - /38 38 cm/s - /76 76 cm/s - /I Type I cassette (ferric/normal) - /II Type II cassette (chrome) - /III Type III cassette (ferric chrome) - /IV Type IV cassette (metal) - - TFT - The 'File type' frame indicates which type of audio this tag defines. - The following type and refinements are defined: - - MPG MPEG Audio - /1 MPEG 2 layer I - /2 MPEG 2 layer II - /3 MPEG 2 layer III - /2.5 MPEG 2.5 - /AAC Advanced audio compression - - but other types may be used, not for these types though. This is used - in a similar way to the predefined types in the "TMT" frame, but - without parenthesis. If this frame is not present audio type is - assumed to be "MPG". - - TBP - BPM is short for beats per minute, and is easily computed by - dividing the number of beats in a musical piece with its length. To - get a more accurate result, do the BPM calculation on the main-part - only. To acquire best result measure the time between each beat and - calculate individual BPM for each beat and use the median value as - result. BPM is an integer and represented as a numerical string. - - TCR - The 'Copyright message' frame, which must begin with a year and a - space character (making five characters), is intended for the - copyright holder of the original sound, not the audio file itself. The - absence of this frame means only that the copyright information is - unavailable or has been removed, and must not be interpreted to mean - that the sound is public domain. Every time this field is displayed - the field must be preceded with "Copyright " (C) " ", where (C) is one - character showing a C in a circle. - - TPB - The 'Publisher' frame simply contains the name of the label or - publisher. - - TEN - The 'Encoded by' frame contains the name of the person or - organisation that encoded the audio file. This field may contain a - copyright message, if the audio file also is copyrighted by the - encoder. - - TSS - The 'Software/hardware and settings used for encoding' frame - includes the used audio encoder and its settings when the file was - encoded. Hardware refers to hardware encoders, not the computer on - which a program was run. - - TOF - The 'Original filename' frame contains the preferred filename for the - file, since some media doesn't allow the desired length of the - filename. The filename is case sensitive and includes its suffix. - - TLE - The 'Length' frame contains the length of the audiofile in - milliseconds, represented as a numeric string. - - TSI - The 'Size' frame contains the size of the audiofile in bytes - excluding the tag, represented as a numeric string. - - TDY - The 'Playlist delay' defines the numbers of milliseconds of silence - between every song in a playlist. The player should use the "ETC" - frame, if present, to skip initial silence and silence at the end of - the audio to match the 'Playlist delay' time. The time is represented - as a numeric string. - - TKE - The 'Initial key' frame contains the musical key in which the sound - starts. It is represented as a string with a maximum length of three - characters. The ground keys are represented with "A","B","C","D","E", - "F" and "G" and halfkeys represented with "b" and "#". Minor is - represented as "m". Example "Cbm". Off key is represented with an "o" - only. - - TOT - The 'Original album/Movie/Show title' frame is intended for the title - of the original recording(/source of sound), if for example the music - in the file should be a cover of a previously released song. - - TOA - The 'Original artist(s)/performer(s)' frame is intended for the - performer(s) of the original recording, if for example the music in - the file should be a cover of a previously released song. The - performers are seperated with the "/" character. - - TOL - The 'Original Lyricist(s)/text writer(s)' frame is intended for the - text writer(s) of the original recording, if for example the music in - the file should be a cover of a previously released song. The text - writers are seperated with the "/" character. - - TOR - The 'Original release year' frame is intended for the year when the - original recording, if for example the music in the file should be a - cover of a previously released song, was released. The field is - formatted as in the "TDY" frame. - - -4.2.2. User defined text information frame - - This frame is intended for one-string text information concerning the - audiofile in a similar way to the other "T"xx frames. The frame body - consists of a description of the string, represented as a terminated - string, followed by the actual string. There may be more than one - "TXX" frame in each tag, but only one with the same description. - - User defined... "TXX" - Frame size $xx xx xx - Text encoding $xx - Description $00 (00) - Value - - -4.3. URL link frames - - With these frames dynamic data such as webpages with touring - information, price information or plain ordinary news can be added to - the tag. There may only be one URL [URL] link frame of its kind in an - tag, except when stated otherwise in the frame description. If the - textstring is followed by a termination ($00 (00)) all the following - information should be ignored and not be displayed. All URL link - frames have the following format: - - URL link frame "W00" - "WZZ" , excluding "WXX" - (described in 4.3.2.) - Frame size $xx xx xx - URL - - -4.3.1. URL link frames - details - - WAF - The 'Official audio file webpage' frame is a URL pointing at a file - specific webpage. - - WAR - The 'Official artist/performer webpage' frame is a URL pointing at - the artists official webpage. There may be more than one "WAR" frame - in a tag if the audio contains more than one performer. - - WAS - The 'Official audio source webpage' frame is a URL pointing at the - official webpage for the source of the audio file, e.g. a movie. - - WCM - The 'Commercial information' frame is a URL pointing at a webpage - with information such as where the album can be bought. There may be - more than one "WCM" frame in a tag. - - WCP - The 'Copyright/Legal information' frame is a URL pointing at a - webpage where the terms of use and ownership of the file is described. - - WPB - The 'Publishers official webpage' frame is a URL pointing at the - official wepage for the publisher. - - -4.3.2. User defined URL link frame - - This frame is intended for URL [URL] links concerning the audiofile in - a similar way to the other "W"xx frames. The frame body consists of a - description of the string, represented as a terminated string, - followed by the actual URL. The URL is always encoded with ISO-8859-1 - [ISO-8859-1]. There may be more than one "WXX" frame in each tag, but - only one with the same description. - - User defined... "WXX" - Frame size $xx xx xx - Text encoding $xx - Description $00 (00) - URL - - -4.4. Involved people list - - Since there might be a lot of people contributing to an audio file in - various ways, such as musicians and technicians, the 'Text - information frames' are often insufficient to list everyone involved - in a project. The 'Involved people list' is a frame containing the - names of those involved, and how they were involved. The body simply - contains a terminated string with the involvement directly followed by - a terminated string with the involvee followed by a new involvement - and so on. There may only be one "IPL" frame in each tag. - - Involved people list "IPL" - Frame size $xx xx xx - Text encoding $xx - People list strings - - -4.5. Music CD Identifier - - This frame is intended for music that comes from a CD, so that the CD - can be identified in databases such as the CDDB [CDDB]. The frame - consists of a binary dump of the Table Of Contents, TOC, from the CD, - which is a header of 4 bytes and then 8 bytes/track on the CD making a - maximum of 804 bytes. This frame requires a present and valid "TRK" - frame. There may only be one "MCI" frame in each tag. - - Music CD identifier "MCI" - Frame size $xx xx xx - CD TOC - - -4.6. Event timing codes - - This frame allows synchronisation with key events in a song or sound. - The head is: - - Event timing codes "ETC" - Frame size $xx xx xx - Time stamp format $xx - - Where time stamp format is: - - $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit - $02 Absolute time, 32 bit sized, using milliseconds as unit - - Abolute time means that every stamp contains the time from the - beginning of the file. - - Followed by a list of key events in the following format: - - Type of event $xx - Time stamp $xx (xx ...) - - The 'Time stamp' is set to zero if directly at the beginning of the - sound or after the previous event. All events should be sorted in - chronological order. The type of event is as follows: - - $00 padding (has no meaning) - $01 end of initial silence - $02 intro start - $03 mainpart start - $04 outro start - $05 outro end - $06 verse begins - $07 refrain begins - $08 interlude - $09 theme start - $0A variation - $0B key change - $0C time change - $0D unwanted noise (Snap, Crackle & Pop) - - $0E-$DF reserved for future use - - $E0-$EF not predefined sync 0-F - - $F0-$FC reserved for future use - - $FD audio end (start of silence) - $FE audio file ends - $FF one more byte of events follows (all the following bytes with - the value $FF have the same function) - - The 'Not predefined sync's ($E0-EF) are for user events. You might - want to synchronise your music to something, like setting of an - explosion on-stage, turning on your screensaver etc. - - There may only be one "ETC" frame in each tag. - - -4.7. MPEG location lookup table - - To increase performance and accuracy of jumps within a MPEG [MPEG] - audio file, frames with timecodes in different locations in the file - might be useful. The ID3 frame includes references that the software - can use to calculate positions in the file. After the frame header is - a descriptor of how much the 'frame counter' should increase for every - reference. If this value is two then the first reference points out - the second frame, the 2nd reference the 4th frame, the 3rd reference - the 6th frame etc. In a similar way the 'bytes between reference' and - 'milliseconds between reference' points out bytes and milliseconds - respectively. - - Each reference consists of two parts; a certain number of bits, as - defined in 'bits for bytes deviation', that describes the difference - between what is said in 'bytes between reference' and the reality and - a certain number of bits, as defined in 'bits for milliseconds - deviation', that describes the difference between what is said in - 'milliseconds between reference' and the reality. The number of bits - in every reference, i.e. 'bits for bytes deviation'+'bits for - milliseconds deviation', must be a multiple of four. There may only be - one "MLL" frame in each tag. - - Location lookup table "MLL" - ID3 frame size $xx xx xx - MPEG frames between reference $xx xx - Bytes between reference $xx xx xx - Milliseconds between reference $xx xx xx - Bits for bytes deviation $xx - Bits for milliseconds dev. $xx - - Then for every reference the following data is included; - - Deviation in bytes %xxx.... - Deviation in milliseconds %xxx.... - - -4.8. Synced tempo codes - - For a more accurate description of the tempo of a musical piece this - frame might be used. After the header follows one byte describing - which time stamp format should be used. Then follows one or more tempo - codes. Each tempo code consists of one tempo part and one time part. - The tempo is in BPM described with one or two bytes. If the first byte - has the value $FF, one more byte follows, which is added to the first - giving a range from 2 - 510 BPM, since $00 and $01 is reserved. $00 is - used to describe a beat-free time period, which is not the same as a - music-free time period. $01 is used to indicate one single beat-stroke - followed by a beat-free period. - - The tempo descriptor is followed by a time stamp. Every time the tempo - in the music changes, a tempo descriptor may indicate this for the - player. All tempo descriptors should be sorted in chronological order. - The first beat-stroke in a time-period is at the same time as the beat - description occurs. There may only be one "STC" frame in each tag. - - Synced tempo codes "STC" - Frame size $xx xx xx - Time stamp format $xx - Tempo data - - Where time stamp format is: - - $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit - $02 Absolute time, 32 bit sized, using milliseconds as unit - - Abolute time means that every stamp contains the time from the - beginning of the file. - - -4.9. Unsychronised lyrics/text transcription - - This frame contains the lyrics of the song or a text transcription of - other vocal activities. The head includes an encoding descriptor and - a content descriptor. The body consists of the actual text. The - 'Content descriptor' is a terminated string. If no descriptor is - entered, 'Content descriptor' is $00 (00) only. Newline characters - are allowed in the text. Maximum length for the descriptor is 64 - bytes. There may be more than one lyrics/text frame in each tag, but - only one with the same language and content descriptor. - - Unsynced lyrics/text "ULT" - Frame size $xx xx xx - Text encoding $xx - Language $xx xx xx - Content descriptor $00 (00) - Lyrics/text - - -4.10. Synchronised lyrics/text - - This is another way of incorporating the words, said or sung lyrics, - in the audio file as text, this time, however, in sync with the audio. - It might also be used to describing events e.g. occurring on a stage - or on the screen in sync with the audio. The header includes a content - descriptor, represented with as terminated textstring. If no - descriptor is entered, 'Content descriptor' is $00 (00) only. - - Synced lyrics/text "SLT" - Frame size $xx xx xx - Text encoding $xx - Language $xx xx xx - Time stamp format $xx - Content type $xx - Content descriptor $00 (00) - - - Encoding: $00 ISO-8859-1 [ISO-8859-1] character set is used => $00 - is sync identifier. - $01 Unicode [UNICODE] character set is used => $00 00 is - sync identifier. - - Content type: $00 is other - $01 is lyrics - $02 is text transcription - $03 is movement/part name (e.g. "Adagio") - $04 is events (e.g. "Don Quijote enters the stage") - $05 is chord (e.g. "Bb F Fsus") - - Time stamp format is: - - $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit - $02 Absolute time, 32 bit sized, using milliseconds as unit - - Abolute time means that every stamp contains the time from the - beginning of the file. - - The text that follows the frame header differs from that of the - unsynchronised lyrics/text transcription in one major way. Each - syllable (or whatever size of text is considered to be convenient by - the encoder) is a null terminated string followed by a time stamp - denoting where in the sound file it belongs. Each sync thus has the - following structure: - - Terminated text to be synced (typically a syllable) - Sync identifier (terminator to above string) $00 (00) - Time stamp $xx (xx ...) - - The 'time stamp' is set to zero or the whole sync is omitted if - located directly at the beginning of the sound. All time stamps should - be sorted in chronological order. The sync can be considered as a - validator of the subsequent string. - - Newline characters are allowed in all "SLT" frames and should be used - after every entry (name, event etc.) in a frame with the content type - $03 - $04. - - A few considerations regarding whitespace characters: Whitespace - separating words should mark the beginning of a new word, thus - occurring in front of the first syllable of a new word. This is also - valid for new line characters. A syllable followed by a comma should - not be broken apart with a sync (both the syllable and the comma - should be before the sync). - - An example: The "ULT" passage - - "Strangers in the night" $0A "Exchanging glances" - - would be "SLT" encoded as: - - "Strang" $00 xx xx "ers" $00 xx xx " in" $00 xx xx " the" $00 xx xx - " night" $00 xx xx 0A "Ex" $00 xx xx "chang" $00 xx xx "ing" $00 xx - xx "glan" $00 xx xx "ces" $00 xx xx - - There may be more than one "SLT" frame in each tag, but only one with - the same language and content descriptor. - - -4.11. Comments - - This frame replaces the old 30-character comment field in ID3v1. It - consists of a frame head followed by encoding, language and content - descriptors and is ended with the actual comment as a text string. - Newline characters are allowed in the comment text string. There may - be more than one comment frame in each tag, but only one with the same - language and content descriptor. - - Comment "COM" - Frame size $xx xx xx - Text encoding $xx - Language $xx xx xx - Short content description $00 (00) - The actual text - - -4.12. Relative volume adjustment - - This is a more subjective function than the previous ones. It allows - the user to say how much he wants to increase/decrease the volume on - each channel while the file is played. The purpose is to be able to - align all files to a reference volume, so that you don't have to - change the volume constantly. This frame may also be used to balance - adjust the audio. If the volume peak levels are known then this could - be described with the 'Peak volume right' and 'Peak volume left' - field. If Peakvolume is not known these fields could be left zeroed - or completely omitted. There may only be one "RVA" frame in each - tag. - - Relative volume adjustment "RVA" - Frame size $xx xx xx - Increment/decrement %000000xx - Bits used for volume descr. $xx - Relative volume change, right $xx xx (xx ...) - Relative volume change, left $xx xx (xx ...) - Peak volume right $xx xx (xx ...) - Peak volume left $xx xx (xx ...) - - In the increment/decrement field bit 0 is used to indicate the right - channel and bit 1 is used to indicate the left channel. 1 is - increment and 0 is decrement. - - The 'bits used for volume description' field is normally $10 (16 bits) - for MPEG 2 layer I, II and III [MPEG] and MPEG 2.5. This value may not - be $00. The volume is always represented with whole bytes, padded in - the beginning (highest bits) when 'bits used for volume description' - is not a multiple of eight. - - -4.13. Equalisation - - This is another subjective, alignment frame. It allows the user to - predefine an equalisation curve within the audio file. There may only - be one "EQU" frame in each tag. - - Equalisation "EQU" - Frame size $xx xx xx - Adjustment bits $xx - - The 'adjustment bits' field defines the number of bits used for - representation of the adjustment. This is normally $10 (16 bits) for - MPEG 2 layer I, II and III [MPEG] and MPEG 2.5. This value may not be - $00. - - This is followed by 2 bytes + ('adjustment bits' rounded up to the - nearest byte) for every equalisation band in the following format, - giving a frequency range of 0 - 32767Hz: - - Increment/decrement %x (MSB of the Frequency) - Frequency (lower 15 bits) - Adjustment $xx (xx ...) - - The increment/decrement bit is 1 for increment and 0 for decrement. - The equalisation bands should be ordered increasingly with reference - to frequency. All frequencies don't have to be declared. Adjustments - with the value $00 should be omitted. A frequency should only be - described once in the frame. - - -4.14. Reverb - - Yet another subjective one. You may here adjust echoes of different - kinds. Reverb left/right is the delay between every bounce in ms. - Reverb bounces left/right is the number of bounces that should be - made. $FF equals an infinite number of bounces. Feedback is the amount - of volume that should be returned to the next echo bounce. $00 is 0%, - $FF is 100%. If this value were $7F, there would be 50% volume - reduction on the first bounce, yet 50% on the second and so on. Left - to left means the sound from the left bounce to be played in the left - speaker, while left to right means sound from the left bounce to be - played in the right speaker. - - 'Premix left to right' is the amount of left sound to be mixed in the - right before any reverb is applied, where $00 id 0% and $FF is 100%. - 'Premix right to left' does the same thing, but right to left. Setting - both premix to $FF would result in a mono output (if the reverb is - applied symmetric). There may only be one "REV" frame in each tag. - - Reverb settings "REV" - Frame size $00 00 0C - Reverb left (ms) $xx xx - Reverb right (ms) $xx xx - Reverb bounces, left $xx - Reverb bounces, right $xx - Reverb feedback, left to left $xx - Reverb feedback, left to right $xx - Reverb feedback, right to right $xx - Reverb feedback, right to left $xx - Premix left to right $xx - Premix right to left $xx - - -4.15. Attached picture - - This frame contains a picture directly related to the audio file. - Image format is preferably "PNG" [PNG] or "JPG" [JFIF]. Description - is a short description of the picture, represented as a terminated - textstring. The description has a maximum length of 64 characters, - but may be empty. There may be several pictures attached to one file, - each in their individual "PIC" frame, but only one with the same - content descriptor. There may only be one picture with the picture - type declared as picture type $01 and $02 respectively. There is a - possibility to put only a link to the image file by using the 'image - format' "-->" and having a complete URL [URL] instead of picture data. - The use of linked files should however be used restrictively since - there is the risk of separation of files. - - Attached picture "PIC" - Frame size $xx xx xx - Text encoding $xx - Image format $xx xx xx - Picture type $xx - Description $00 (00) - Picture data - - - Picture type: $00 Other - $01 32x32 pixels 'file icon' (PNG only) - $02 Other file icon - $03 Cover (front) - $04 Cover (back) - $05 Leaflet page - $06 Media (e.g. lable side of CD) - $07 Lead artist/lead performer/soloist - $08 Artist/performer - $09 Conductor - $0A Band/Orchestra - $0B Composer - $0C Lyricist/text writer - $0D Recording Location - $0E During recording - $0F During performance - $10 Movie/video screen capture - $11 A bright coloured fish - $12 Illustration - $13 Band/artist logotype - $14 Publisher/Studio logotype - - -4.16. General encapsulated object - - In this frame any type of file can be encapsulated. After the header, - 'Frame size' and 'Encoding' follows 'MIME type' [MIME] and 'Filename' - for the encapsulated object, both represented as terminated strings - encoded with ISO 8859-1 [ISO-8859-1]. The filename is case sensitive. - Then follows a content description as terminated string, encoded as - 'Encoding'. The last thing in the frame is the actual object. The - first two strings may be omitted, leaving only their terminations. - MIME type is always an ISO-8859-1 text string. There may be more than - one "GEO" frame in each tag, but only one with the same content - descriptor. - - General encapsulated object "GEO" - Frame size $xx xx xx - Text encoding $xx - MIME type $00 - Filename $00 (00) - Content description $00 (00) - Encapsulated object - - -4.17. Play counter - - This is simply a counter of the number of times a file has been - played. The value is increased by one every time the file begins to - play. There may only be one "CNT" frame in each tag. When the counter - reaches all one's, one byte is inserted in front of the counter thus - making the counter eight bits bigger. The counter must be at least - 32-bits long to begin with. - - Play counter "CNT" - Frame size $xx xx xx - Counter $xx xx xx xx (xx ...) - - -4.18. Popularimeter - - The purpose of this frame is to specify how good an audio file is. - Many interesting applications could be found to this frame such as a - playlist that features better audiofiles more often than others or it - could be used to profile a persons taste and find other 'good' files - by comparing people's profiles. The frame is very simple. It contains - the email address to the user, one rating byte and a four byte play - counter, intended to be increased with one for every time the file is - played. The email is a terminated string. The rating is 1-255 where - 1 is worst and 255 is best. 0 is unknown. If no personal counter is - wanted it may be omitted. When the counter reaches all one's, one - byte is inserted in front of the counter thus making the counter - eight bits bigger in the same away as the play counter ("CNT"). - There may be more than one "POP" frame in each tag, but only one with - the same email address. - - Popularimeter "POP" - Frame size $xx xx xx - Email to user $00 - Rating $xx - Counter $xx xx xx xx (xx ...) - - -4.19. Recommended buffer size - - Sometimes the server from which a audio file is streamed is aware of - transmission or coding problems resulting in interruptions in the - audio stream. In these cases, the size of the buffer can be - recommended by the server using this frame. If the 'embedded info - flag' is true (1) then this indicates that an ID3 tag with the - maximum size described in 'Buffer size' may occur in the audiostream. - In such case the tag should reside between two MPEG [MPEG] frames, if - the audio is MPEG encoded. If the position of the next tag is known, - 'offset to next tag' may be used. The offset is calculated from the - end of tag in which this frame resides to the first byte of the header - in the next. This field may be omitted. Embedded tags is currently not - recommended since this could render unpredictable behaviour from - present software/hardware. The 'Buffer size' should be kept to a - minimum. There may only be one "BUF" frame in each tag. - - Recommended buffer size "BUF" - Frame size $xx xx xx - Buffer size $xx xx xx - Embedded info flag %0000000x - Offset to next tag $xx xx xx xx - - -4.20. Encrypted meta frame - - This frame contains one or more encrypted frames. This enables - protection of copyrighted information such as pictures and text, that - people might want to pay extra for. Since standardisation of such an - encryption scheme is beyond this document, all "CRM" frames begin with - a terminated string with a URL [URL] containing an email address, or a - link to a location where an email adress can be found, that belongs to - the organisation responsible for this specific encrypted meta frame. - - Questions regarding the encrypted frame should be sent to the - indicated email address. If a $00 is found directly after the 'Frame - size', the whole frame should be ignored, and preferably be removed. - The 'Owner identifier' is then followed by a short content description - and explanation as to why it's encrypted. After the - 'content/explanation' description, the actual encrypted block follows. - - When an ID3v2 decoder encounters a "CRM" frame, it should send the - datablock to the 'plugin' with the corresponding 'owner identifier' - and expect to receive either a datablock with one or several ID3v2 - frames after each other or an error. There may be more than one "CRM" - frames in a tag, but only one with the same 'owner identifier'. - - Encrypted meta frame "CRM" - Frame size $xx xx xx - Owner identifier $00 (00) - Content/explanation $00 (00) - Encrypted datablock - - -4.21. Audio encryption - - This frame indicates if the actual audio stream is encrypted, and by - whom. Since standardisation of such encrypion scheme is beyond this - document, all "CRA" frames begin with a terminated string with a - URL containing an email address, or a link to a location where an - email address can be found, that belongs to the organisation - responsible for this specific encrypted audio file. Questions - regarding the encrypted audio should be sent to the email address - specified. If a $00 is found directly after the 'Frame size' and the - audiofile indeed is encrypted, the whole file may be considered - useless. - - After the 'Owner identifier', a pointer to an unencrypted part of the - audio can be specified. The 'Preview start' and 'Preview length' is - described in frames. If no part is unencrypted, these fields should be - left zeroed. After the 'preview length' field follows optionally a - datablock required for decryption of the audio. There may be more than - one "CRA" frames in a tag, but only one with the same 'Owner - identifier'. - - Audio encryption "CRA" - Frame size $xx xx xx - Owner identifier $00 (00) - Preview start $xx xx - Preview length $xx xx - Encryption info - - -4.22. Linked information - - To keep space waste as low as possible this frame may be used to link - information from another ID3v2 tag that might reside in another audio - file or alone in a binary file. It is recommended that this method is - only used when the files are stored on a CD-ROM or other circumstances - when the risk of file seperation is low. The frame contains a frame - identifier, which is the frame that should be linked into this tag, a - URL [URL] field, where a reference to the file where the frame is - given, and additional ID data, if needed. Data should be retrieved - from the first tag found in the file to which this link points. There - may be more than one "LNK" frame in a tag, but only one with the same - contents. A linked frame is to be considered as part of the tag and - has the same restrictions as if it was a physical part of the tag - (i.e. only one "REV" frame allowed, whether it's linked or not). - - Linked information "LNK" - Frame size $xx xx xx - Frame identifier $xx xx xx - URL $00 (00) - Additional ID data - - Frames that may be linked and need no additional data are "IPL", - "MCI", "ETC", "LLT", "STC", "RVA", "EQU", "REV", "BUF", the text - information frames and the URL link frames. - - The "TXX", "PIC", "GEO", "CRM" and "CRA" frames may be linked with the - content descriptor as additional ID data. - - The "COM", "SLT" and "ULT" frames may be linked with three bytes of - language descriptor directly followed by a content descriptor as - additional ID data. - - -5. The 'unsynchronisation scheme' - - The only purpose of the 'unsychronisation scheme' is to make the ID3v2 - tag as compatible as possible with existing software. There is no use - in 'unsynchronising' tags if the file is only to be processed by new - software. Unsynchronisation may only be made with MPEG 2 layer I, II - and III and MPEG 2.5 files. - - Whenever a false synchronisation is found within the tag, one zeroed - byte is inserted after the first false synchronisation byte. The - format of a correct sync that should be altered by ID3 encoders is as - follows: - - %11111111 111xxxxx - - And should be replaced with: - - %11111111 00000000 111xxxxx - - This has the side effect that all $FF 00 combinations have to be - altered, so they won't be affected by the decoding process. Therefore - all the $FF 00 combinations have to be replaced with the $FF 00 00 - combination during the unsynchonisation. - - To indicate usage of the unsynchronisation, the first bit in 'ID3 - flags' should be set. This bit should only be set if the tag - contained a, now corrected, false synchronisation. The bit should - only be clear if the tag does not contain any false synchronisations. - - Do bear in mind, that if a compression scheme is used by the encoder, - the unsyncronisation scheme should be applied *afterwards*. When - decoding a compressed, 'unsyncronised' file, the 'unsyncronisation - scheme' should be parsed first, compression afterwards. - - -6. Copyright - - Copyright (C) Martin Nilsson 1998. All Rights Reserved. - - This document and translations of it may be copied and furnished to - others, and derivative works that comment on or otherwise explain it - or assist in its implementation may be prepared, copied, published - and distributed, in whole or in part, without restriction of any - kind, provided that a reference to this document is included on all - such copies and derivative works. However, this document itself may - not be modified in any way and reissued as the original document. - - The limited permissions granted above are perpetual and will not be - revoked. - - This document and the information contained herein is provided on an - "AS IS" basis and THE AUTHORS DISCLAIMS ALL WARRANTIES, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE - INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED - WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. - - -7. References - - [CDDB] Compact Disc Data Base - - - - [ISO-639-2] ISO/FDIS 639-2. - Codes for the representation of names of languages, Part 2: Alpha-3 - code. Technical committee / subcommittee: TC 37 / SC 2 - - [ISO-8859-1] ISO/IEC DIS 8859-1. - 8-bit single-byte coded graphic character sets, Part 1: Latin - alphabet No. 1. Technical committee / subcommittee: JTC 1 / SC 2 - - [ISRC] ISO 3901:1986 - International Standard Recording Code (ISRC). - Technical committee / subcommittee: TC 46 / SC 9 - - [JFIF] JPEG File Interchange Format, version 1.02 - - - - [MIME] Freed, N. and N. Borenstein, "Multipurpose Internet Mail - Extensions (MIME) Part One: Format of Internet Message Bodies", - RFC 2045, November 1996. - - - - [MPEG] ISO/IEC 11172-3:1993. - Coding of moving pictures and associated audio for digital storage - media at up to about 1,5 Mbit/s, Part 3: Audio. - Technical committee / subcommittee: JTC 1 / SC 29 - and - ISO/IEC 13818-3:1995 - Generic coding of moving pictures and associated audio information, - Part 3: Audio. - Technical committee / subcommittee: JTC 1 / SC 29 - and - ISO/IEC DIS 13818-3 - Generic coding of moving pictures and associated audio information, - Part 3: Audio (Revision of ISO/IEC 13818-3:1995) - - - [PNG] Portable Network Graphics, version 1.0 - - - - [UNICODE] ISO/IEC 10646-1:1993. - Universal Multiple-Octet Coded Character Set (UCS), Part 1: - Architecture and Basic Multilingual Plane. Technical committee - / subcommittee: JTC 1 / SC 2 - - - - [URL] T. Berners-Lee, L. Masinter & M. McCahill, "Uniform Resource - Locators (URL).", RFC 1738, December 1994. - - - - -8. Appendix - - -A. Appendix A - ID3-Tag Specification V1.1 - - ID3-Tag Specification V1.1 (12 dec 1997) by Michael Mutschler - , edited for space and clarity - reasons. - - -A.1. Overview - - The ID3-Tag is an information field for MPEG Layer 3 audio files. - Since a standalone MP3 doesn't provide a method of storing other - information than those directly needed for replay reasons, the - ID3-tag was invented by Eric Kemp in 1996. - - A revision from ID3v1 to ID3v1.1 was made by Michael Mutschler to - support track number information is described in A.4. - - -A.2. ID3v1 Implementation - - The Information is stored in the last 128 bytes of an MP3. The Tag - has got the following fields, and the offsets given here, are from - 0-127. - - Field Length Offsets - Tag 3 0-2 - Songname 30 3-32 - Artist 30 33-62 - Album 30 63-92 - Year 4 93-96 - Comment 30 97-126 - Genre 1 127 - - - The string-fields contain ASCII-data, coded in ISO-Latin 1 codepage. - Strings which are smaller than the field length are padded with zero- - bytes. - - Tag: The tag is valid if this field contains the string "TAG". This - has to be uppercase! - - Songname: This field contains the title of the MP3 (string as - above). - - Artist: This field contains the artist of the MP3 (string as above). - - Album: this field contains the album where the MP3 comes from - (string as above). - - Year: this field contains the year when this song has originally - been released (string as above). - - Comment: this field contains a comment for the MP3 (string as - above). Revision to this field has been made in ID3v1.1. See - A.4. - - Genre: this byte contains the offset of a genre in a predefined - list the byte is treated as an unsigned byte. The offset is - starting from 0. See A.3. - - -A.3. Genre List - - The following genres is defined in ID3v1 - - 0.Blues - 1.Classic Rock - 2.Country - 3.Dance - 4.Disco - 5.Funk - 6.Grunge - 7.Hip-Hop - 8.Jazz - 9.Metal - 10.New Age - 11.Oldies - 12.Other - 13.Pop - 14.R&B - 15.Rap - 16.Reggae - 17.Rock - 18.Techno - 19.Industrial - 20.Alternative - 21.Ska - 22.Death Metal - 23.Pranks - 24.Soundtrack - 25.Euro-Techno - 26.Ambient - 27.Trip-Hop - 28.Vocal - 29.Jazz+Funk - 30.Fusion - 31.Trance - 32.Classical - 33.Instrumental - 34.Acid - 35.House - 36.Game - 37.Sound Clip - 38.Gospel - 39.Noise - 40.AlternRock - 41.Bass - 42.Soul - 43.Punk - 44.Space - 45.Meditative - 46.Instrumental Pop - 47.Instrumental Rock - 48.Ethnic - 49.Gothic - 50.Darkwave - 51.Techno-Industrial - 52.Electronic - 53.Pop-Folk - 54.Eurodance - 55.Dream - 56.Southern Rock - 57.Comedy - 58.Cult - 59.Gangsta - 60.Top 40 - 61.Christian Rap - 62.Pop/Funk - 63.Jungle - 64.Native American - 65.Cabaret - 66.New Wave - 67.Psychadelic - 68.Rave - 69.Showtunes - 70.Trailer - 71.Lo-Fi - 72.Tribal - 73.Acid Punk - 74.Acid Jazz - 75.Polka - 76.Retro - 77.Musical - 78.Rock & Roll - 79.Hard Rock - - The following genres are Winamp extensions - - 80.Folk - 81.Folk-Rock - 82.National Folk - 83.Swing - 84.Fast Fusion - 85.Bebob - 86.Latin - 87.Revival - 88.Celtic - 89.Bluegrass - 90.Avantgarde - 91.Gothic Rock - 92.Progressive Rock - 93.Psychedelic Rock - 94.Symphonic Rock - 95.Slow Rock - 96.Big Band - 97.Chorus - 98.Easy Listening - 99.Acoustic - 100.Humour - 101.Speech - 102.Chanson - 103.Opera - 104.Chamber Music - 105.Sonata - 106.Symphony - 107.Booty Bass - 108.Primus - 109.Porn Groove - 110.Satire - 111.Slow Jam - 112.Club - 113.Tango - 114.Samba - 115.Folklore - 116.Ballad - 117.Power Ballad - 118.Rhythmic Soul - 119.Freestyle - 120.Duet - 121.Punk Rock - 122.Drum Solo - 123.A capella - 124.Euro-House - 125.Dance Hall - - -A.4. Track addition - ID3v1.1 - - In ID3v1.1, Michael Mutschler revised the specification of the - comment field in order to implement the track number. The new format - of the comment field is a 28 character string followed by a mandatory - null ($00) character and the original album tracknumber stored as an - unsigned byte-size integer. In such cases where the 29th byte is not - the null character or when the 30th is a null character, the - tracknumber is to be considered undefined. - - -9. Author's Address - - Martin Nilsson - Rydsvägen 246 C. 30 - S-584 34 Linköping - Sweden - - Email: nilsson@id3.org - - Co-authors: - - Johan Sundström Email: johan@id3.org - - diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2.3.0.txt b/3rdparty/taglib/mpeg/id3v2/id3v2.3.0.txt deleted file mode 100644 index b4ed763e..00000000 --- a/3rdparty/taglib/mpeg/id3v2/id3v2.3.0.txt +++ /dev/null @@ -1,2022 +0,0 @@ -Informal standard M. Nilsson -Document: id3v2.3.0.txt 3rd February 1999 - - - ID3 tag version 2.3.0 - -Status of this document - - This document is an informal standard and replaces the ID3v2.2.0 - standard [ID3v2]. The informal standard is released so that - implementors could have a set standard before a formal standard is - set. The formal standard will use another version or revision number - if not identical to what is described in this document. The contents - in this document may change for clarifications but never for added or - altered functionallity. - - Distribution of this document is unlimited. - - -Abstract - - This document describes the ID3v2.3.0, which is a more developed - version of the ID3v2 informal standard [ID3v2] (version 2.2.0), - evolved from the ID3 tagging system. The ID3v2 offers a flexible way - of storing information about an audio file within itself to determine - its origin and contents. The information may be technical - information, such as equalisation curves, as well as related meta - information, such as title, performer, copyright etc. - - -1. Table of contents - - 2. Conventions in this document - 3. ID3v2 overview - 3.1. ID3v2 header - 3.2. ID3v2 extended header - 3.3. ID3v2 frames overview - 3.3.1. Frame header flags - 3.3.2. Default flags - 4. Declared ID3v2 frames - 4.1. Unique file identifier - 4.2. Text information frames - 4.2.1. Text information frames - details - 4.2.2. User defined text information frame - 4.3. URL link frames - 4.3.1. URL link frames - details - 4.3.2. User defined URL link frame - 4.4. Involved people list - 4.5. Music CD Identifier - 4.6. Event timing codes - 4.7. MPEG location lookup table - 4.8. Synced tempo codes - 4.9. Unsychronised lyrics/text transcription - 4.10. Synchronised lyrics/text - 4.11. Comments - 4.12. Relative volume adjustment - 4.13. Equalisation - 4.14. Reverb - 4.15. Attached picture - 4.16. General encapsulated object - 4.17. Play counter - 4.18. Popularimeter - 4.19. Recommended buffer size - 4.20. Audio encryption - 4.21. Linked information - 4.22. Position synchronisation frame - 4.23. Terms of use - 4.24. Ownership frame - 4.25. Commercial frame - 4.26. Encryption method registration - 4.27. Group identification registration - 4.28. Private frame - 5. The 'unsynchronisation scheme' - 6. Copyright - 7. References - 8. Appendix - A. Appendix A - Genre List from ID3v1 - 9. Author's Address - - -2. Conventions in this document - - In the examples, text within "" is a text string exactly as it - appears in a file. Numbers preceded with $ are hexadecimal and - numbers preceded with % are binary. $xx is used to indicate a byte - with unknown content. %x is used to indicate a bit with unknown - content. The most significant bit (MSB) of a byte is called 'bit 7' - and the least significant bit (LSB) is called 'bit 0'. - - A tag is the whole tag described in this document. A frame is a block - of information in the tag. The tag consists of a header, frames and - optional padding. A field is a piece of information; one value, a - string etc. A numeric string is a string that consists of the - characters 0-9 only. - - -3. ID3v2 overview - - The two biggest design goals were to be able to implement ID3v2 - without disturbing old software too much and that ID3v2 should be - as flexible and expandable as possible. - - The first criterion is met by the simple fact that the MPEG [MPEG] - decoding software uses a syncsignal, embedded in the audiostream, to - 'lock on to' the audio. Since the ID3v2 tag doesn't contain a valid - syncsignal, no software will attempt to play the tag. If, for any - reason, coincidence make a syncsignal appear within the tag it will - be taken care of by the 'unsynchronisation scheme' described in - section 5. - - The second criterion has made a more noticeable impact on the design - of the ID3v2 tag. It is constructed as a container for several - information blocks, called frames, whose format need not be known to - the software that encounters them. At the start of every frame there - is an identifier that explains the frames' format and content, and a - size descriptor that allows software to skip unknown frames. - - If a total revision of the ID3v2 tag should be needed, there is a - version number and a size descriptor in the ID3v2 header. - - The ID3 tag described in this document is mainly targeted at files - encoded with MPEG-1/2 layer I, MPEG-1/2 layer II, MPEG-1/2 layer III - and MPEG-2.5, but may work with other types of encoded audio. - - The bitorder in ID3v2 is most significant bit first (MSB). The - byteorder in multibyte numbers is most significant byte first (e.g. - $12345678 would be encoded $12 34 56 78). - - It is permitted to include padding after all the final frame (at the - end of the ID3 tag), making the size of all the frames together - smaller than the size given in the head of the tag. A possible - purpose of this padding is to allow for adding a few additional - frames or enlarge existing frames within the tag without having to - rewrite the entire file. The value of the padding bytes must be $00. - - -3.1. ID3v2 header - - The ID3v2 tag header, which should be the first information in the - file, is 10 bytes as follows: - - ID3v2/file identifier "ID3" - ID3v2 version $03 00 - ID3v2 flags %abc00000 - ID3v2 size 4 * %0xxxxxxx - - The first three bytes of the tag are always "ID3" to indicate that - this is an ID3v2 tag, directly followed by the two version bytes. The - first byte of ID3v2 version is it's major version, while the second - byte is its revision number. In this case this is ID3v2.3.0. All - revisions are backwards compatible while major versions are not. If - software with ID3v2.2.0 and below support should encounter version - three or higher it should simply ignore the whole tag. Version and - revision will never be $FF. - - The version is followed by one the ID3v2 flags field, of which - currently only three flags are used. - - - a - Unsynchronisation - - Bit 7 in the 'ID3v2 flags' indicates whether or not - unsynchronisation is used (see section 5 for details); a set bit - indicates usage. - - - b - Extended header - - The second bit (bit 6) indicates whether or not the header is - followed by an extended header. The extended header is described in - section 3.2. - - - c - Experimental indicator - - The third bit (bit 5) should be used as an 'experimental - indicator'. This flag should always be set when the tag is in an - experimental stage. - - All the other flags should be cleared. If one of these undefined - flags are set that might mean that the tag is not readable for a - parser that does not know the flags function. - - The ID3v2 tag size is encoded with four bytes where the most - significant bit (bit 7) is set to zero in every byte, making a total - of 28 bits. The zeroed bits are ignored, so a 257 bytes long tag is - represented as $00 00 02 01. - - The ID3v2 tag size is the size of the complete tag after - unsychronisation, including padding, excluding the header but not - excluding the extended header (total tag size - 10). Only 28 bits - (representing up to 256MB) are used in the size description to avoid - the introducuction of 'false syncsignals'. - - An ID3v2 tag can be detected with the following pattern: - $49 44 33 yy yy xx zz zz zz zz - Where yy is less than $FF, xx is the 'flags' byte and zz is less than - $80. - - -3.2. ID3v2 extended header - - The extended header contains information that is not vital to the - correct parsing of the tag information, hence the extended header is - optional. - - Extended header size $xx xx xx xx - Extended Flags $xx xx - Size of padding $xx xx xx xx - - Where the 'Extended header size', currently 6 or 10 bytes, excludes - itself. The 'Size of padding' is simply the total tag size excluding - the frames and the headers, in other words the padding. The extended - header is considered separate from the header proper, and as such is - subject to unsynchronisation. - - The extended flags are a secondary flag set which describes further - attributes of the tag. These attributes are currently defined as - follows - - %x0000000 00000000 - - - x - CRC data present - - If this flag is set four bytes of CRC-32 data is appended to the - extended header. The CRC should be calculated before - unsynchronisation on the data between the extended header and the - padding, i.e. the frames and only the frames. - - Total frame CRC $xx xx xx xx - - -3.3. ID3v2 frame overview - - As the tag consists of a tag header and a tag body with one or more - frames, all the frames consists of a frame header followed by one or - more fields containing the actual information. The layout of the - frame header: - - Frame ID $xx xx xx xx (four characters) - Size $xx xx xx xx - Flags $xx xx - - The frame ID made out of the characters capital A-Z and 0-9. - Identifiers beginning with "X", "Y" and "Z" are for experimental use - and free for everyone to use, without the need to set the - experimental bit in the tag header. Have in mind that someone else - might have used the same identifier as you. All other identifiers are - either used or reserved for future use. - - The frame ID is followed by a size descriptor, making a total header - size of ten bytes in every frame. The size is calculated as frame - size excluding frame header (frame size - 10). - - In the frame header the size descriptor is followed by two flags - bytes. These flags are described in section 3.3.1. - - There is no fixed order of the frames' appearance in the tag, - although it is desired that the frames are arranged in order of - significance concerning the recognition of the file. An example of - such order: UFID, TIT2, MCDI, TRCK ... - - A tag must contain at least one frame. A frame must be at least 1 - byte big, excluding the header. - - If nothing else is said a string is represented as ISO-8859-1 - [ISO-8859-1] characters in the range $20 - $FF. Such strings are - represented as , or if newlines are - allowed, in the frame descriptions. All Unicode strings [UNICODE] use - 16-bit unicode 2.0 (ISO/IEC 10646-1:1993, UCS-2). Unicode strings - must begin with the Unicode BOM ($FF FE or $FE FF) to identify the - byte order. - - All numeric strings and URLs [URL] are always encoded as ISO-8859-1. - Terminated strings are terminated with $00 if encoded with ISO-8859-1 - and $00 00 if encoded as unicode. If nothing else is said newline - character is forbidden. In ISO-8859-1 a new line is represented, when - allowed, with $0A only. Frames that allow different types of text - encoding have a text encoding description byte directly after the - frame size. If ISO-8859-1 is used this byte should be $00, if Unicode - is used it should be $01. Strings dependent on encoding is - represented as , or if newlines are allowed. Any empty - Unicode strings which are NULL-terminated may have the Unicode BOM - followed by a Unicode NULL ($FF FE 00 00 or $FE FF 00 00). - - The three byte language field is used to describe the language of the - frame's content, according to ISO-639-2 [ISO-639-2]. - - All URLs [URL] may be relative, e.g. "picture.png", "../doc.txt". - - If a frame is longer than it should be, e.g. having more fields than - specified in this document, that indicates that additions to the - frame have been made in a later version of the ID3v2 standard. This - is reflected by the revision number in the header of the tag. - - -3.3.1. Frame header flags - - In the frame header the size descriptor is followed by two flags - bytes. All unused flags must be cleared. The first byte is for - 'status messages' and the second byte is for encoding purposes. If an - unknown flag is set in the first byte the frame may not be changed - without the bit cleared. If an unknown flag is set in the second byte - it is likely to not be readable. The flags field is defined as - follows. - - %abc00000 %ijk00000 - - - a - Tag alter preservation - - This flag tells the software what to do with this frame if it is - unknown and the tag is altered in any way. This applies to all - kinds of alterations, including adding more padding and reordering - the frames. - - 0 Frame should be preserved. - 1 Frame should be discarded. - - - b - File alter preservation - - This flag tells the software what to do with this frame if it is - unknown and the file, excluding the tag, is altered. This does not - apply when the audio is completely replaced with other audio data. - - 0 Frame should be preserved. - 1 Frame should be discarded. - - - c - Read only - - This flag, if set, tells the software that the contents of this - frame is intended to be read only. Changing the contents might - break something, e.g. a signature. If the contents are changed, - without knowledge in why the frame was flagged read only and - without taking the proper means to compensate, e.g. recalculating - the signature, the bit should be cleared. - - - i - Compression - - This flag indicates whether or not the frame is compressed. - - 0 Frame is not compressed. - 1 Frame is compressed using zlib [zlib] with 4 bytes for - 'decompressed size' appended to the frame header. - - - j - Encryption - - This flag indicates wether or not the frame is enrypted. If set - one byte indicating with which method it was encrypted will be - appended to the frame header. See section 4.26. for more - information about encryption method registration. - - 0 Frame is not encrypted. - 1 Frame is encrypted. - - - k - Grouping identity - - This flag indicates whether or not this frame belongs in a group - with other frames. If set a group identifier byte is added to the - frame header. Every frame with the same group identifier belongs - to the same group. - - 0 Frame does not contain group information - 1 Frame contains group information - - - Some flags indicates that the frame header is extended with - additional information. This information will be added to the frame - header in the same order as the flags indicating the additions. I.e. - the four bytes of decompressed size will preceed the encryption - method byte. These additions to the frame header, while not included - in the frame header size but are included in the 'frame size' field, - are not subject to encryption or compression. - - -3.3.2. Default flags - - The default settings for the frames described in this document can be - divided into the following classes. The flags may be set differently - if found more suitable by the software. - - 1. Discarded if tag is altered, discarded if file is altered. - - None. - - 2. Discarded if tag is altered, preserved if file is altered. - - None. - - 3. Preserved if tag is altered, discarded if file is altered. - - AENC, ETCO, EQUA, MLLT, POSS, SYLT, SYTC, RVAD, TENC, TLEN, TSIZ - - 4. Preserved if tag is altered, preserved if file is altered. - - The rest of the frames. - - -4. Declared ID3v2 frames - - The following frames are declared in this draft. - - 4.21 AENC Audio encryption - 4.15 APIC Attached picture - - 4.11 COMM Comments - 4.25 COMR Commercial frame - - 4.26 ENCR Encryption method registration - 4.13 EQUA Equalization - 4.6 ETCO Event timing codes - - 4.16 GEOB General encapsulated object - 4.27 GRID Group identification registration - - 4.4 IPLS Involved people list - - 4.21 LINK Linked information - - 4.5 MCDI Music CD identifier - 4.7 MLLT MPEG location lookup table - - 4.24 OWNE Ownership frame - - 4.28. PRIV Private frame - 4.17 PCNT Play counter - 4.18 POPM Popularimeter - 4.22 POSS Position synchronisation frame - - 4.19 RBUF Recommended buffer size - 4.12 RVAD Relative volume adjustment - 4.14 RVRB Reverb - - 4.10 SYLT Synchronized lyric/text - 4.8 SYTC Synchronized tempo codes - - 4.2.1 TALB Album/Movie/Show title - 4.2.1 TBPM BPM (beats per minute) - 4.2.1 TCOM Composer - 4.2.1 TCON Content type - 4.2.1 TCOP Copyright message - 4.2.1 TDAT Date - 4.2.1 TDLY Playlist delay - 4.2.1 TENC Encoded by - 4.2.1 TEXT Lyricist/Text writer - 4.2.1 TFLT File type - 4.2.1 TIME Time - 4.2.1 TIT1 Content group description - 4.2.1 TIT2 Title/songname/content description - 4.2.1 TIT3 Subtitle/Description refinement - 4.2.1 TKEY Initial key - 4.2.1 TLAN Language(s) - 4.2.1 TLEN Length - 4.2.1 TMED Media type - 4.2.1 TOAL Original album/movie/show title - 4.2.1 TOFN Original filename - 4.2.1 TOLY Original lyricist(s)/text writer(s) - 4.2.1 TOPE Original artist(s)/performer(s) - 4.2.1 TORY Original release year - 4.2.1 TOWN File owner/licensee - 4.2.1 TPE1 Lead performer(s)/Soloist(s) - 4.2.1 TPE2 Band/orchestra/accompaniment - 4.2.1 TPE3 Conductor/performer refinement - 4.2.1 TPE4 Interpreted, remixed, or otherwise modified by - 4.2.1 TPOS Part of a set - 4.2.1 TPUB Publisher - 4.2.1 TRCK Track number/Position in set - 4.2.1 TRDA Recording dates - 4.2.1 TRSN Internet radio station name - 4.2.1 TRSO Internet radio station owner - 4.2.1 TSIZ Size - 4.2.1 TSRC ISRC (international standard recording code) - 4.2.1 TSSE Software/Hardware and settings used for encoding - 4.2.1 TYER Year - 4.2.2 TXXX User defined text information frame - - 4.1 UFID Unique file identifier - 4.23 USER Terms of use - 4.9 USLT Unsychronized lyric/text transcription - - 4.3.1 WCOM Commercial information - 4.3.1 WCOP Copyright/Legal information - 4.3.1 WOAF Official audio file webpage - 4.3.1 WOAR Official artist/performer webpage - 4.3.1 WOAS Official audio source webpage - 4.3.1 WORS Official internet radio station homepage - 4.3.1 WPAY Payment - 4.3.1 WPUB Publishers official webpage - 4.3.2 WXXX User defined URL link frame - - -4.1. Unique file identifier - - This frame's purpose is to be able to identify the audio file in a - database that may contain more information relevant to the content. - Since standardisation of such a database is beyond this document, all - frames begin with a null-terminated string with a URL [URL] - containing an email address, or a link to a location where an email - address can be found, that belongs to the organisation responsible - for this specific database implementation. Questions regarding the - database should be sent to the indicated email address. The URL - should not be used for the actual database queries. The string - "http://www.id3.org/dummy/ufid.html" should be used for tests. - Software that isn't told otherwise may safely remove such frames. The - 'Owner identifier' must be non-empty (more than just a termination). - The 'Owner identifier' is then followed by the actual identifier, - which may be up to 64 bytes. There may be more than one "UFID" frame - in a tag, but only one with the same 'Owner identifier'. - -
- Owner identifier $00 - Identifier - - -4.2. Text information frames - - The text information frames are the most important frames, containing - information like artist, album and more. There may only be one text - information frame of its kind in an tag. If the textstring is - followed by a termination ($00 (00)) all the following information - should be ignored and not be displayed. All text frame identifiers - begin with "T". Only text frame identifiers begin with "T", with the - exception of the "TXXX" frame. All the text information frames have - the following format: - -
- Text encoding $xx - Information - - -4.2.1. Text information frames - details - - TALB - The 'Album/Movie/Show title' frame is intended for the title of the - recording(/source of sound) which the audio in the file is taken - from. - - TBPM - The 'BPM' frame contains the number of beats per minute in the - mainpart of the audio. The BPM is an integer and represented as a - numerical string. - - TCOM - The 'Composer(s)' frame is intended for the name of the composer(s). - They are seperated with the "/" character. - - TCON - The 'Content type', which previously was stored as a one byte numeric - value only, is now a numeric string. You may use one or several of - the types as ID3v1.1 did or, since the category list would be - impossible to maintain with accurate and up to date categories, - define your own. - - References to the ID3v1 genres can be made by, as first byte, enter - "(" followed by a number from the genres list (appendix A.) and - ended with a ")" character. This is optionally followed by a - refinement, e.g. "(21)" or "(4)Eurodisco". Several references can be - made in the same frame, e.g. "(51)(39)". If the refinement should - begin with a "(" character it should be replaced with "((", e.g. "((I - can figure out any genre)" or "(55)((I think...)". The following new - content types is defined in ID3v2 and is implemented in the same way - as the numerig content types, e.g. "(RX)". - - RX Remix - CR Cover - - TCOP - The 'Copyright message' frame, which must begin with a year and a - space character (making five characters), is intended for the - copyright holder of the original sound, not the audio file itself. - The absence of this frame means only that the copyright information - is unavailable or has been removed, and must not be interpreted to - mean that the sound is public domain. Every time this field is - displayed the field must be preceded with "Copyright " (C) " ", where - (C) is one character showing a C in a circle. - - TDAT - The 'Date' frame is a numeric string in the DDMM format containing - the date for the recording. This field is always four characters - long. - - TDLY - The 'Playlist delay' defines the numbers of milliseconds of silence - between every song in a playlist. The player should use the "ETC" - frame, if present, to skip initial silence and silence at the end of - the audio to match the 'Playlist delay' time. The time is represented - as a numeric string. - - TENC - The 'Encoded by' frame contains the name of the person or - organisation that encoded the audio file. This field may contain a - copyright message, if the audio file also is copyrighted by the - encoder. - - TEXT - The 'Lyricist(s)/Text writer(s)' frame is intended for the writer(s) - of the text or lyrics in the recording. They are seperated with the - "/" character. - - TFLT - The 'File type' frame indicates which type of audio this tag defines. - The following type and refinements are defined: - - MPG MPEG Audio - /1 MPEG 1/2 layer I - /2 MPEG 1/2 layer II - /3 MPEG 1/2 layer III - /2.5 MPEG 2.5 - /AAC Advanced audio compression - VQF Transform-domain Weighted Interleave Vector Quantization - PCM Pulse Code Modulated audio - - but other types may be used, not for these types though. This is used - in a similar way to the predefined types in the "TMED" frame, but - without parentheses. If this frame is not present audio type is - assumed to be "MPG". - - TIME - The 'Time' frame is a numeric string in the HHMM format containing - the time for the recording. This field is always four characters - long. - - TIT1 - The 'Content group description' frame is used if the sound belongs to - a larger category of sounds/music. For example, classical music is - often sorted in different musical sections (e.g. "Piano Concerto", - "Weather - Hurricane"). - - TIT2 - The 'Title/Songname/Content description' frame is the actual name of - the piece (e.g. "Adagio", "Hurricane Donna"). - - TIT3 - The 'Subtitle/Description refinement' frame is used for information - directly related to the contents title (e.g. "Op. 16" or "Performed - live at Wembley"). - - TKEY - The 'Initial key' frame contains the musical key in which the sound - starts. It is represented as a string with a maximum length of three - characters. The ground keys are represented with "A","B","C","D","E", - "F" and "G" and halfkeys represented with "b" and "#". Minor is - represented as "m". Example "Cbm". Off key is represented with an "o" - only. - - TLAN - The 'Language(s)' frame should contain the languages of the text or - lyrics spoken or sung in the audio. The language is represented with - three characters according to ISO-639-2. If more than one language is - used in the text their language codes should follow according to - their usage. - - TLEN - The 'Length' frame contains the length of the audiofile in - milliseconds, represented as a numeric string. - - TMED - The 'Media type' frame describes from which media the sound - originated. This may be a text string or a reference to the - predefined media types found in the list below. References are made - within "(" and ")" and are optionally followed by a text refinement, - e.g. "(MC) with four channels". If a text refinement should begin - with a "(" character it should be replaced with "((" in the same way - as in the "TCO" frame. Predefined refinements is appended after the - media type, e.g. "(CD/A)" or "(VID/PAL/VHS)". - - DIG Other digital media - /A Analog transfer from media - - ANA Other analog media - /WAC Wax cylinder - /8CA 8-track tape cassette - - CD CD - /A Analog transfer from media - /DD DDD - /AD ADD - /AA AAD - - LD Laserdisc - /A Analog transfer from media - - TT Turntable records - /33 33.33 rpm - /45 45 rpm - /71 71.29 rpm - /76 76.59 rpm - /78 78.26 rpm - /80 80 rpm - - MD MiniDisc - /A Analog transfer from media - - DAT DAT - /A Analog transfer from media - /1 standard, 48 kHz/16 bits, linear - /2 mode 2, 32 kHz/16 bits, linear - /3 mode 3, 32 kHz/12 bits, nonlinear, low speed - /4 mode 4, 32 kHz/12 bits, 4 channels - /5 mode 5, 44.1 kHz/16 bits, linear - /6 mode 6, 44.1 kHz/16 bits, 'wide track' play - - DCC DCC - /A Analog transfer from media - - DVD DVD - /A Analog transfer from media - - TV Television - /PAL PAL - /NTSC NTSC - /SECAM SECAM - - VID Video - /PAL PAL - /NTSC NTSC - /SECAM SECAM - /VHS VHS - /SVHS S-VHS - /BETA BETAMAX - - RAD Radio - /FM FM - /AM AM - /LW LW - /MW MW - - TEL Telephone - /I ISDN - - MC MC (normal cassette) - /4 4.75 cm/s (normal speed for a two sided cassette) - /9 9.5 cm/s - /I Type I cassette (ferric/normal) - /II Type II cassette (chrome) - /III Type III cassette (ferric chrome) - /IV Type IV cassette (metal) - - REE Reel - /9 9.5 cm/s - /19 19 cm/s - /38 38 cm/s - /76 76 cm/s - /I Type I cassette (ferric/normal) - /II Type II cassette (chrome) - /III Type III cassette (ferric chrome) - /IV Type IV cassette (metal) - - TOAL - The 'Original album/movie/show title' frame is intended for the title - of the original recording (or source of sound), if for example the - music in the file should be a cover of a previously released song. - - TOFN - The 'Original filename' frame contains the preferred filename for the - file, since some media doesn't allow the desired length of the - filename. The filename is case sensitive and includes its suffix. - - TOLY - The 'Original lyricist(s)/text writer(s)' frame is intended for the - text writer(s) of the original recording, if for example the music in - the file should be a cover of a previously released song. The text - writers are seperated with the "/" character. - - TOPE - The 'Original artist(s)/performer(s)' frame is intended for the - performer(s) of the original recording, if for example the music in - the file should be a cover of a previously released song. The - performers are seperated with the "/" character. - - TORY - The 'Original release year' frame is intended for the year when the - original recording, if for example the music in the file should be a - cover of a previously released song, was released. The field is - formatted as in the "TYER" frame. - - TOWN - The 'File owner/licensee' frame contains the name of the owner or - licensee of the file and it's contents. - - TPE1 - The 'Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group' is - used for the main artist(s). They are seperated with the "/" - character. - - TPE2 - The 'Band/Orchestra/Accompaniment' frame is used for additional - information about the performers in the recording. - - TPE3 - The 'Conductor' frame is used for the name of the conductor. - - TPE4 - The 'Interpreted, remixed, or otherwise modified by' frame contains - more information about the people behind a remix and similar - interpretations of another existing piece. - - TPOS - The 'Part of a set' frame is a numeric string that describes which - part of a set the audio came from. This frame is used if the source - described in the "TALB" frame is divided into several mediums, e.g. a - double CD. The value may be extended with a "/" character and a - numeric string containing the total number of parts in the set. E.g. - "1/2". - - TPUB - The 'Publisher' frame simply contains the name of the label or - publisher. - - TRCK - The 'Track number/Position in set' frame is a numeric string - containing the order number of the audio-file on its original - recording. This may be extended with a "/" character and a numeric - string containing the total numer of tracks/elements on the original - recording. E.g. "4/9". - - TRDA - The 'Recording dates' frame is a intended to be used as complement to - the "TYER", "TDAT" and "TIME" frames. E.g. "4th-7th June, 12th June" - in combination with the "TYER" frame. - - TRSN - The 'Internet radio station name' frame contains the name of the - internet radio station from which the audio is streamed. - - TRSO - The 'Internet radio station owner' frame contains the name of the - owner of the internet radio station from which the audio is - streamed. - - TSIZ - The 'Size' frame contains the size of the audiofile in bytes, - excluding the ID3v2 tag, represented as a numeric string. - - TSRC - The 'ISRC' frame should contain the International Standard Recording - Code [ISRC] (12 characters). - - TSSE - The 'Software/Hardware and settings used for encoding' frame - includes the used audio encoder and its settings when the file was - encoded. Hardware refers to hardware encoders, not the computer on - which a program was run. - - TYER - The 'Year' frame is a numeric string with a year of the recording. - This frames is always four characters long (until the year 10000). - - -4.2.2. User defined text information frame - - This frame is intended for one-string text information concerning the - audiofile in a similar way to the other "T"-frames. The frame body - consists of a description of the string, represented as a terminated - string, followed by the actual string. There may be more than one - "TXXX" frame in each tag, but only one with the same description. - -
- Text encoding $xx - Description $00 (00) - Value - - -4.3. URL link frames - - With these frames dynamic data such as webpages with touring - information, price information or plain ordinary news can be added to - the tag. There may only be one URL [URL] link frame of its kind in an - tag, except when stated otherwise in the frame description. If the - textstring is followed by a termination ($00 (00)) all the following - information should be ignored and not be displayed. All URL link - frame identifiers begins with "W". Only URL link frame identifiers - begins with "W". All URL link frames have the following format: - -
- URL - - -4.3.1. URL link frames - details - - WCOM - The 'Commercial information' frame is a URL pointing at a webpage - with information such as where the album can be bought. There may be - more than one "WCOM" frame in a tag, but not with the same content. - - WCOP - The 'Copyright/Legal information' frame is a URL pointing at a - webpage where the terms of use and ownership of the file is - described. - - WOAF - The 'Official audio file webpage' frame is a URL pointing at a file - specific webpage. - - WOAR - The 'Official artist/performer webpage' frame is a URL pointing at - the artists official webpage. There may be more than one "WOAR" frame - in a tag if the audio contains more than one performer, but not with - the same content. - - WOAS - The 'Official audio source webpage' frame is a URL pointing at the - official webpage for the source of the audio file, e.g. a movie. - - WORS - The 'Official internet radio station homepage' contains a URL - pointing at the homepage of the internet radio station. - - WPAY - The 'Payment' frame is a URL pointing at a webpage that will handle - the process of paying for this file. - - WPUB - The 'Publishers official webpage' frame is a URL pointing at the - official wepage for the publisher. - - -4.3.2. User defined URL link frame - - This frame is intended for URL [URL] links concerning the audiofile - in a similar way to the other "W"-frames. The frame body consists - of a description of the string, represented as a terminated string, - followed by the actual URL. The URL is always encoded with ISO-8859-1 - [ISO-8859-1]. There may be more than one "WXXX" frame in each tag, - but only one with the same description. - -
- Text encoding $xx - Description $00 (00) - URL - - -4.4. Involved people list - - Since there might be a lot of people contributing to an audio file in - various ways, such as musicians and technicians, the 'Text - information frames' are often insufficient to list everyone involved - in a project. The 'Involved people list' is a frame containing the - names of those involved, and how they were involved. The body simply - contains a terminated string with the involvement directly followed - by a terminated string with the involvee followed by a new - involvement and so on. There may only be one "IPLS" frame in each - tag. - -
- Text encoding $xx - People list strings - - -4.5. Music CD identifier - - This frame is intended for music that comes from a CD, so that the CD - can be identified in databases such as the CDDB [CDDB]. The frame - consists of a binary dump of the Table Of Contents, TOC, from the CD, - which is a header of 4 bytes and then 8 bytes/track on the CD plus 8 - bytes for the 'lead out' making a maximum of 804 bytes. The offset to - the beginning of every track on the CD should be described with a - four bytes absolute CD-frame address per track, and not with absolute - time. This frame requires a present and valid "TRCK" frame, even if - the CD's only got one track. There may only be one "MCDI" frame in - each tag. - -
- CD TOC - - -4.6. Event timing codes - - This frame allows synchronisation with key events in a song or sound. - The header is: - -
- Time stamp format $xx - - Where time stamp format is: - - $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit - $02 Absolute time, 32 bit sized, using milliseconds as unit - - Abolute time means that every stamp contains the time from the - beginning of the file. - - Followed by a list of key events in the following format: - - Type of event $xx - Time stamp $xx (xx ...) - - The 'Time stamp' is set to zero if directly at the beginning of the - sound or after the previous event. All events should be sorted in - chronological order. The type of event is as follows: - - $00 padding (has no meaning) - $01 end of initial silence - $02 intro start - $03 mainpart start - $04 outro start - $05 outro end - $06 verse start - $07 refrain start - $08 interlude start - $09 theme start - $0A variation start - $0B key change - $0C time change - $0D momentary unwanted noise (Snap, Crackle & Pop) - $0E sustained noise - $0F sustained noise end - $10 intro end - $11 mainpart end - $12 verse end - $13 refrain end - $14 theme end - - $15-$DF reserved for future use - - $E0-$EF not predefined sync 0-F - - $F0-$FC reserved for future use - - $FD audio end (start of silence) - $FE audio file ends - $FF one more byte of events follows (all the following bytes with - the value $FF have the same function) - - Terminating the start events such as "intro start" is not required. - The 'Not predefined sync's ($E0-EF) are for user events. You might - want to synchronise your music to something, like setting of an - explosion on-stage, turning on your screensaver etc. - - There may only be one "ETCO" frame in each tag. - - -4.7. MPEG location lookup table - - To increase performance and accuracy of jumps within a MPEG [MPEG] - audio file, frames with timecodes in different locations in the file - might be useful. The ID3v2 frame includes references that the - software can use to calculate positions in the file. After the frame - header is a descriptor of how much the 'frame counter' should - increase for every reference. If this value is two then the first - reference points out the second frame, the 2nd reference the 4th - frame, the 3rd reference the 6th frame etc. In a similar way the - 'bytes between reference' and 'milliseconds between reference' points - out bytes and milliseconds respectively. - - Each reference consists of two parts; a certain number of bits, as - defined in 'bits for bytes deviation', that describes the difference - between what is said in 'bytes between reference' and the reality and - a certain number of bits, as defined in 'bits for milliseconds - deviation', that describes the difference between what is said in - 'milliseconds between reference' and the reality. The number of bits - in every reference, i.e. 'bits for bytes deviation'+'bits for - milliseconds deviation', must be a multiple of four. There may only - be one "MLLT" frame in each tag. - -
- MPEG frames between reference $xx xx - Bytes between reference $xx xx xx - Milliseconds between reference $xx xx xx - Bits for bytes deviation $xx - Bits for milliseconds dev. $xx - - Then for every reference the following data is included; - - Deviation in bytes %xxx.... - Deviation in milliseconds %xxx.... - - -4.8. Synchronised tempo codes - - For a more accurate description of the tempo of a musical piece this - frame might be used. After the header follows one byte describing - which time stamp format should be used. Then follows one or more - tempo codes. Each tempo code consists of one tempo part and one time - part. The tempo is in BPM described with one or two bytes. If the - first byte has the value $FF, one more byte follows, which is added - to the first giving a range from 2 - 510 BPM, since $00 and $01 is - reserved. $00 is used to describe a beat-free time period, which is - not the same as a music-free time period. $01 is used to indicate one - single beat-stroke followed by a beat-free period. - - The tempo descriptor is followed by a time stamp. Every time the - tempo in the music changes, a tempo descriptor may indicate this for - the player. All tempo descriptors should be sorted in chronological - order. The first beat-stroke in a time-period is at the same time as - the beat description occurs. There may only be one "SYTC" frame in - each tag. - -
- Time stamp format $xx - Tempo data - - Where time stamp format is: - - $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit - $02 Absolute time, 32 bit sized, using milliseconds as unit - - Abolute time means that every stamp contains the time from the - beginning of the file. - - -4.9. Unsychronised lyrics/text transcription - - This frame contains the lyrics of the song or a text transcription of - other vocal activities. The head includes an encoding descriptor and - a content descriptor. The body consists of the actual text. The - 'Content descriptor' is a terminated string. If no descriptor is - entered, 'Content descriptor' is $00 (00) only. Newline characters - are allowed in the text. There may be more than one 'Unsynchronised - lyrics/text transcription' frame in each tag, but only one with the - same language and content descriptor. - -
- Text encoding $xx - Language $xx xx xx - Content descriptor $00 (00) - Lyrics/text - - -4.10. Synchronised lyrics/text - - This is another way of incorporating the words, said or sung lyrics, - in the audio file as text, this time, however, in sync with the - audio. It might also be used to describing events e.g. occurring on a - stage or on the screen in sync with the audio. The header includes a - content descriptor, represented with as terminated textstring. If no - descriptor is entered, 'Content descriptor' is $00 (00) only. - -
- Text encoding $xx - Language $xx xx xx - Time stamp format $xx - Content type $xx - Content descriptor $00 (00) - - - Encoding: $00 ISO-8859-1 [ISO-8859-1] character set is used => $00 - is sync identifier. - $01 Unicode [UNICODE] character set is used => $00 00 is - sync identifier. - - Content type: $00 is other - $01 is lyrics - $02 is text transcription - $03 is movement/part name (e.g. "Adagio") - $04 is events (e.g. "Don Quijote enters the stage") - $05 is chord (e.g. "Bb F Fsus") - $06 is trivia/'pop up' information - - Time stamp format is: - - $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit - $02 Absolute time, 32 bit sized, using milliseconds as unit - - Abolute time means that every stamp contains the time from the - beginning of the file. - - The text that follows the frame header differs from that of the - unsynchronised lyrics/text transcription in one major way. Each - syllable (or whatever size of text is considered to be convenient by - the encoder) is a null terminated string followed by a time stamp - denoting where in the sound file it belongs. Each sync thus has the - following structure: - - Terminated text to be synced (typically a syllable) - Sync identifier (terminator to above string) $00 (00) - Time stamp $xx (xx ...) - - The 'time stamp' is set to zero or the whole sync is omitted if - located directly at the beginning of the sound. All time stamps - should be sorted in chronological order. The sync can be considered - as a validator of the subsequent string. - - Newline ($0A) characters are allowed in all "SYLT" frames and should - be used after every entry (name, event etc.) in a frame with the - content type $03 - $04. - - A few considerations regarding whitespace characters: Whitespace - separating words should mark the beginning of a new word, thus - occurring in front of the first syllable of a new word. This is also - valid for new line characters. A syllable followed by a comma should - not be broken apart with a sync (both the syllable and the comma - should be before the sync). - - An example: The "USLT" passage - - "Strangers in the night" $0A "Exchanging glances" - - would be "SYLT" encoded as: - - "Strang" $00 xx xx "ers" $00 xx xx " in" $00 xx xx " the" $00 xx xx - " night" $00 xx xx 0A "Ex" $00 xx xx "chang" $00 xx xx "ing" $00 xx - xx "glan" $00 xx xx "ces" $00 xx xx - - There may be more than one "SYLT" frame in each tag, but only one - with the same language and content descriptor. - - -4.11. Comments - - This frame is indended for any kind of full text information that - does not fit in any other frame. It consists of a frame header - followed by encoding, language and content descriptors and is ended - with the actual comment as a text string. Newline characters are - allowed in the comment text string. There may be more than one - comment frame in each tag, but only one with the same language and - content descriptor. - -
- Text encoding $xx - Language $xx xx xx - Short content descrip. $00 (00) - The actual text - - -4.12. Relative volume adjustment - - This is a more subjective function than the previous ones. It allows - the user to say how much he wants to increase/decrease the volume on - each channel while the file is played. The purpose is to be able to - align all files to a reference volume, so that you don't have to - change the volume constantly. This frame may also be used to balance - adjust the audio. If the volume peak levels are known then this could - be described with the 'Peak volume right' and 'Peak volume left' - field. If Peakvolume is not known these fields could be left zeroed - or, if no other data follows, be completely omitted. There may only - be one "RVAD" frame in each tag. - -
- Increment/decrement %00xxxxxx - Bits used for volume descr. $xx - Relative volume change, right $xx xx (xx ...) - Relative volume change, left $xx xx (xx ...) - Peak volume right $xx xx (xx ...) - Peak volume left $xx xx (xx ...) - - In the increment/decrement field bit 0 is used to indicate the right - channel and bit 1 is used to indicate the left channel. 1 is - increment and 0 is decrement. - - The 'bits used for volume description' field is normally $10 (16 - bits) for MPEG 2 layer I, II and III [MPEG] and MPEG 2.5. This value - may not be $00. The volume is always represented with whole bytes, - padded in the beginning (highest bits) when 'bits used for volume - description' is not a multiple of eight. - - This datablock is then optionally followed by a volume definition for - the left and right back channels. If this information is appended to - the frame the first two channels will be treated as front channels. - In the increment/decrement field bit 2 is used to indicate the right - back channel and bit 3 for the left back channel. - - Relative volume change, right back $xx xx (xx ...) - Relative volume change, left back $xx xx (xx ...) - Peak volume right back $xx xx (xx ...) - Peak volume left back $xx xx (xx ...) - - If the center channel adjustment is present the following is appended - to the existing frame, after the left and right back channels. The - center channel is represented by bit 4 in the increase/decrease - field. - - Relative volume change, center $xx xx (xx ...) - Peak volume center $xx xx (xx ...) - - If the bass channel adjustment is present the following is appended - to the existing frame, after the center channel. The bass channel is - represented by bit 5 in the increase/decrease field. - - Relative volume change, bass $xx xx (xx ...) - Peak volume bass $xx xx (xx ...) - - -4.13. Equalisation - - This is another subjective, alignment frame. It allows the user to - predefine an equalisation curve within the audio file. There may only - be one "EQUA" frame in each tag. - -
- Adjustment bits $xx - - The 'adjustment bits' field defines the number of bits used for - representation of the adjustment. This is normally $10 (16 bits) for - MPEG 2 layer I, II and III [MPEG] and MPEG 2.5. This value may not be - $00. - - This is followed by 2 bytes + ('adjustment bits' rounded up to the - nearest byte) for every equalisation band in the following format, - giving a frequency range of 0 - 32767Hz: - - Increment/decrement %x (MSB of the Frequency) - Frequency (lower 15 bits) - Adjustment $xx (xx ...) - - The increment/decrement bit is 1 for increment and 0 for decrement. - The equalisation bands should be ordered increasingly with reference - to frequency. All frequencies don't have to be declared. The - equalisation curve in the reading software should be interpolated - between the values in this frame. Three equal adjustments for three - subsequent frequencies. A frequency should only be described once in - the frame. - - -4.14. Reverb - - Yet another subjective one. You may here adjust echoes of different - kinds. Reverb left/right is the delay between every bounce in ms. - Reverb bounces left/right is the number of bounces that should be - made. $FF equals an infinite number of bounces. Feedback is the - amount of volume that should be returned to the next echo bounce. $00 - is 0%, $FF is 100%. If this value were $7F, there would be 50% volume - reduction on the first bounce, 50% of that on the second and so on. - Left to left means the sound from the left bounce to be played in the - left speaker, while left to right means sound from the left bounce to - be played in the right speaker. - - 'Premix left to right' is the amount of left sound to be mixed in the - right before any reverb is applied, where $00 id 0% and $FF is 100%. - 'Premix right to left' does the same thing, but right to left. - Setting both premix to $FF would result in a mono output (if the - reverb is applied symmetric). There may only be one "RVRB" frame in - each tag. - -
- Reverb left (ms) $xx xx - Reverb right (ms) $xx xx - Reverb bounces, left $xx - Reverb bounces, right $xx - Reverb feedback, left to left $xx - Reverb feedback, left to right $xx - Reverb feedback, right to right $xx - Reverb feedback, right to left $xx - Premix left to right $xx - Premix right to left $xx - - -4.15. Attached picture - - This frame contains a picture directly related to the audio file. - Image format is the MIME type and subtype [MIME] for the image. In - the event that the MIME media type name is omitted, "image/" will be - implied. The "image/png" [PNG] or "image/jpeg" [JFIF] picture format - should be used when interoperability is wanted. Description is a - short description of the picture, represented as a terminated - textstring. The description has a maximum length of 64 characters, - but may be empty. There may be several pictures attached to one file, - each in their individual "APIC" frame, but only one with the same - content descriptor. There may only be one picture with the picture - type declared as picture type $01 and $02 respectively. There is the - possibility to put only a link to the image file by using the 'MIME - type' "-->" and having a complete URL [URL] instead of picture data. - The use of linked files should however be used sparingly since there - is the risk of separation of files. - -
- Text encoding $xx - MIME type $00 - Picture type $xx - Description $00 (00) - Picture data - - - Picture type: $00 Other - $01 32x32 pixels 'file icon' (PNG only) - $02 Other file icon - $03 Cover (front) - $04 Cover (back) - $05 Leaflet page - $06 Media (e.g. lable side of CD) - $07 Lead artist/lead performer/soloist - $08 Artist/performer - $09 Conductor - $0A Band/Orchestra - $0B Composer - $0C Lyricist/text writer - $0D Recording Location - $0E During recording - $0F During performance - $10 Movie/video screen capture - $11 A bright coloured fish - $12 Illustration - $13 Band/artist logotype - $14 Publisher/Studio logotype - - -4.16. General encapsulated object - - In this frame any type of file can be encapsulated. After the header, - 'Frame size' and 'Encoding' follows 'MIME type' [MIME] represented as - as a terminated string encoded with ISO 8859-1 [ISO-8859-1]. The - filename is case sensitive and is encoded as 'Encoding'. Then follows - a content description as terminated string, encoded as 'Encoding'. - The last thing in the frame is the actual object. The first two - strings may be omitted, leaving only their terminations. MIME type is - always an ISO-8859-1 text string. There may be more than one "GEOB" - frame in each tag, but only one with the same content descriptor. - -
- Text encoding $xx - MIME type $00 - Filename $00 (00) - Content description $00 (00) - Encapsulated object - - -4.17. Play counter - - This is simply a counter of the number of times a file has been - played. The value is increased by one every time the file begins to - play. There may only be one "PCNT" frame in each tag. When the - counter reaches all one's, one byte is inserted in front of the - counter thus making the counter eight bits bigger. The counter must - be at least 32-bits long to begin with. - -
- Counter $xx xx xx xx (xx ...) - - -4.18. Popularimeter - - The purpose of this frame is to specify how good an audio file is. - Many interesting applications could be found to this frame such as a - playlist that features better audiofiles more often than others or it - could be used to profile a person's taste and find other 'good' files - by comparing people's profiles. The frame is very simple. It contains - the email address to the user, one rating byte and a four byte play - counter, intended to be increased with one for every time the file is - played. The email is a terminated string. The rating is 1-255 where - 1 is worst and 255 is best. 0 is unknown. If no personal counter is - wanted it may be omitted. When the counter reaches all one's, one - byte is inserted in front of the counter thus making the counter - eight bits bigger in the same away as the play counter ("PCNT"). - There may be more than one "POPM" frame in each tag, but only one - with the same email address. - -
- Email to user $00 - Rating $xx - Counter $xx xx xx xx (xx ...) - - -4.19. Recommended buffer size - - Sometimes the server from which a audio file is streamed is aware of - transmission or coding problems resulting in interruptions in the - audio stream. In these cases, the size of the buffer can be - recommended by the server using this frame. If the 'embedded info - flag' is true (1) then this indicates that an ID3 tag with the - maximum size described in 'Buffer size' may occur in the audiostream. - In such case the tag should reside between two MPEG [MPEG] frames, if - the audio is MPEG encoded. If the position of the next tag is known, - 'offset to next tag' may be used. The offset is calculated from the - end of tag in which this frame resides to the first byte of the - header in the next. This field may be omitted. Embedded tags are - generally not recommended since this could render unpredictable - behaviour from present software/hardware. - - For applications like streaming audio it might be an idea to embed - tags into the audio stream though. If the clients connects to - individual connections like HTTP and there is a possibility to begin - every transmission with a tag, then this tag should include a - 'recommended buffer size' frame. If the client is connected to a - arbitrary point in the stream, such as radio or multicast, then the - 'recommended buffer size' frame should be included in every tag. - Every tag that is picked up after the initial/first tag is to be - considered as an update of the previous one. E.g. if there is a - "TIT2" frame in the first received tag and one in the second tag, - then the first should be 'replaced' with the second. - - The 'Buffer size' should be kept to a minimum. There may only be one - "RBUF" frame in each tag. - -
- Buffer size $xx xx xx - Embedded info flag %0000000x - Offset to next tag $xx xx xx xx - - -4.20. Audio encryption - - This frame indicates if the actual audio stream is encrypted, and by - whom. Since standardisation of such encrypion scheme is beyond this - document, all "AENC" frames begin with a terminated string with a - URL containing an email address, or a link to a location where an - email address can be found, that belongs to the organisation - responsible for this specific encrypted audio file. Questions - regarding the encrypted audio should be sent to the email address - specified. If a $00 is found directly after the 'Frame size' and the - audiofile indeed is encrypted, the whole file may be considered - useless. - - After the 'Owner identifier', a pointer to an unencrypted part of the - audio can be specified. The 'Preview start' and 'Preview length' is - described in frames. If no part is unencrypted, these fields should - be left zeroed. After the 'preview length' field follows optionally a - datablock required for decryption of the audio. There may be more - than one "AENC" frames in a tag, but only one with the same 'Owner - identifier'. - -
- Owner identifier $00 - Preview start $xx xx - Preview length $xx xx - Encryption info - - -4.21. Linked information - - To keep space waste as low as possible this frame may be used to link - information from another ID3v2 tag that might reside in another audio - file or alone in a binary file. It is recommended that this method is - only used when the files are stored on a CD-ROM or other - circumstances when the risk of file seperation is low. The frame - contains a frame identifier, which is the frame that should be linked - into this tag, a URL [URL] field, where a reference to the file where - the frame is given, and additional ID data, if needed. Data should be - retrieved from the first tag found in the file to which this link - points. There may be more than one "LINK" frame in a tag, but only - one with the same contents. A linked frame is to be considered as - part of the tag and has the same restrictions as if it was a physical - part of the tag (i.e. only one "RVRB" frame allowed, whether it's - linked or not). - -
- Frame identifier $xx xx xx - URL $00 - ID and additional data - - Frames that may be linked and need no additional data are "IPLS", - "MCID", "ETCO", "MLLT", "SYTC", "RVAD", "EQUA", "RVRB", "RBUF", the - text information frames and the URL link frames. - - The "TXXX", "APIC", "GEOB" and "AENC" frames may be linked with - the content descriptor as additional ID data. - - The "COMM", "SYLT" and "USLT" frames may be linked with three bytes - of language descriptor directly followed by a content descriptor as - additional ID data. - - -4.22. Position synchronisation frame - - This frame delivers information to the listener of how far into the - audio stream he picked up; in effect, it states the time offset of - the first frame in the stream. The frame layout is: - - - Time stamp format $xx - Position $xx (xx ...) - - Where time stamp format is: - - $01 Absolute time, 32 bit sized, using MPEG frames as unit - $02 Absolute time, 32 bit sized, using milliseconds as unit - - and position is where in the audio the listener starts to receive, - i.e. the beginning of the next frame. If this frame is used in the - beginning of a file the value is always 0. There may only be one - "POSS" frame in each tag. - - -4.23. Terms of use frame - - This frame contains a brief description of the terms of use and - ownership of the file. More detailed information concerning the legal - terms might be available through the "WCOP" frame. Newlines are - allowed in the text. There may only be one "USER" frame in a tag. - -
- Text encoding $xx - Language $xx xx xx - The actual text - - -4.24. Ownership frame - - The ownership frame might be used as a reminder of a made transaction - or, if signed, as proof. Note that the "USER" and "TOWN" frames are - good to use in conjunction with this one. The frame begins, after the - frame ID, size and encoding fields, with a 'price payed' field. The - first three characters of this field contains the currency used for - the transaction, encoded according to ISO 4217 [ISO-4217] alphabetic - currency code. Concatenated to this is the actual price payed, as a - numerical string using "." as the decimal separator. Next is an 8 - character date string (YYYYMMDD) followed by a string with the name - of the seller as the last field in the frame. There may only be one - "OWNE" frame in a tag. - -
- Text encoding $xx - Price payed $00 - Date of purch. - Seller - - -4.25. Commercial frame - - This frame enables several competing offers in the same tag by - bundling all needed information. That makes this frame rather complex - but it's an easier solution than if one tries to achieve the same - result with several frames. The frame begins, after the frame ID, - size and encoding fields, with a price string field. A price is - constructed by one three character currency code, encoded according - to ISO 4217 [ISO-4217] alphabetic currency code, followed by a - numerical value where "." is used as decimal seperator. In the price - string several prices may be concatenated, seperated by a "/" - character, but there may only be one currency of each type. - - The price string is followed by an 8 character date string in the - format YYYYMMDD, describing for how long the price is valid. After - that is a contact URL, with which the user can contact the seller, - followed by a one byte 'received as' field. It describes how the - audio is delivered when bought according to the following list: - - $00 Other - $01 Standard CD album with other songs - $02 Compressed audio on CD - $03 File over the Internet - $04 Stream over the Internet - $05 As note sheets - $06 As note sheets in a book with other sheets - $07 Music on other media - $08 Non-musical merchandise - - Next follows a terminated string with the name of the seller followed - by a terminated string with a short description of the product. The - last thing is the ability to include a company logotype. The first of - them is the 'Picture MIME type' field containing information about - which picture format is used. In the event that the MIME media type - name is omitted, "image/" will be implied. Currently only "image/png" - and "image/jpeg" are allowed. This format string is followed by the - binary picture data. This two last fields may be omitted if no - picture is to attach. - -
- Text encoding $xx - Price string $00 - Valid until - Contact URL $00 - Received as $xx - Name of seller $00 (00) - Description $00 (00) - Picture MIME type $00 - Seller logo - - -4.26. Encryption method registration - - To identify with which method a frame has been encrypted the - encryption method must be registered in the tag with this frame. The - 'Owner identifier' is a null-terminated string with a URL [URL] - containing an email address, or a link to a location where an email - address can be found, that belongs to the organisation responsible - for this specific encryption method. Questions regarding the - encryption method should be sent to the indicated email address. The - 'Method symbol' contains a value that is associated with this method - throughout the whole tag. Values below $80 are reserved. The 'Method - symbol' may optionally be followed by encryption specific data. There - may be several "ENCR" frames in a tag but only one containing the - same symbol and only one containing the same owner identifier. The - method must be used somewhere in the tag. See section 3.3.1, flag j - for more information. - -
- Owner identifier $00 - Method symbol $xx - Encryption data - - -4.27. Group identification registration - - This frame enables grouping of otherwise unrelated frames. This can - be used when some frames are to be signed. To identify which frames - belongs to a set of frames a group identifier must be registered in - the tag with this frame. The 'Owner identifier' is a null-terminated - string with a URL [URL] containing an email address, or a link to a - location where an email address can be found, that belongs to the - organisation responsible for this grouping. Questions regarding the - grouping should be sent to the indicated email address. The 'Group - symbol' contains a value that associates the frame with this group - throughout the whole tag. Values below $80 are reserved. The 'Group - symbol' may optionally be followed by some group specific data, e.g. - a digital signature. There may be several "GRID" frames in a tag but - only one containing the same symbol and only one containing the same - owner identifier. The group symbol must be used somewhere in the tag. - See section 3.3.1, flag j for more information. - -
- Owner identifier $00 - Group symbol $xx - Group dependent data - - -4.28. Private frame - - This frame is used to contain information from a software producer - that its program uses and does not fit into the other frames. The - frame consists of an 'Owner identifier' string and the binary data. - The 'Owner identifier' is a null-terminated string with a URL [URL] - containing an email address, or a link to a location where an email - address can be found, that belongs to the organisation responsible - for the frame. Questions regarding the frame should be sent to the - indicated email address. The tag may contain more than one "PRIV" - frame but only with different contents. It is recommended to keep the - number of "PRIV" frames as low as possible. - -
- Owner identifier $00 - The private data - - -5. The 'unsynchronisation scheme' - - The only purpose of the 'unsynchronisation scheme' is to make the - ID3v2 tag as compatible as possible with existing software. There is - no use in 'unsynchronising' tags if the file is only to be processed - by new software. Unsynchronisation may only be made with MPEG 2 layer - I, II and III and MPEG 2.5 files. - - Whenever a false synchronisation is found within the tag, one zeroed - byte is inserted after the first false synchronisation byte. The - format of a correct sync that should be altered by ID3 encoders is as - follows: - - %11111111 111xxxxx - - And should be replaced with: - - %11111111 00000000 111xxxxx - - This has the side effect that all $FF 00 combinations have to be - altered, so they won't be affected by the decoding process. Therefore - all the $FF 00 combinations have to be replaced with the $FF 00 00 - combination during the unsynchronisation. - - To indicate usage of the unsynchronisation, the first bit in 'ID3 - flags' should be set. This bit should only be set if the tag - contains a, now corrected, false synchronisation. The bit should - only be clear if the tag does not contain any false synchronisations. - - Do bear in mind, that if a compression scheme is used by the encoder, - the unsynchronisation scheme should be applied *afterwards*. When - decoding a compressed, 'unsynchronised' file, the 'unsynchronisation - scheme' should be parsed first, decompression afterwards. - - If the last byte in the tag is $FF, and there is a need to eliminate - false synchronisations in the tag, at least one byte of padding - should be added. - - -6. Copyright - - Copyright (C) Martin Nilsson 1998. All Rights Reserved. - - This document and translations of it may be copied and furnished to - others, and derivative works that comment on or otherwise explain it - or assist in its implementation may be prepared, copied, published - and distributed, in whole or in part, without restriction of any - kind, provided that a reference to this document is included on all - such copies and derivative works. However, this document itself may - not be modified in any way and reissued as the original document. - - The limited permissions granted above are perpetual and will not be - revoked. - - This document and the information contained herein is provided on an - "AS IS" basis and THE AUTHORS DISCLAIMS ALL WARRANTIES, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF - THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED - WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. - - -7. References - - [CDDB] Compact Disc Data Base - - http://www.cddb.com - - [ID3v2] Martin Nilsson, "ID3v2 informal standard". - - http://www.id3lib.org/id3/id3v2-00.txt - - [ISO-639-2] ISO/FDIS 639-2. - Codes for the representation of names of languages, Part 2: Alpha-3 - code. Technical committee / subcommittee: TC 37 / SC 2 - - [ISO-4217] ISO 4217:1995. - Codes for the representation of currencies and funds. - Technical committee / subcommittee: TC 68 - - [ISO-8859-1] ISO/IEC DIS 8859-1. - 8-bit single-byte coded graphic character sets, Part 1: Latin - alphabet No. 1. Technical committee / subcommittee: JTC 1 / SC 2 - - [ISRC] ISO 3901:1986 - International Standard Recording Code (ISRC). - Technical committee / subcommittee: TC 46 / SC 9 - - [JFIF] JPEG File Interchange Format, version 1.02 - - http://www.w3.org/Graphics/JPEG/jfif.txt">http://www.w3.org/Graphics/JPEG/jfif.txt - - [MIME] Freed, N. and N. Borenstein, "Multipurpose Internet Mail - Extensions (MIME) Part One: Format of Internet Message Bodies", - RFC 2045, November 1996. - - ftp://ftp.isi.edu/in-notes/rfc2045.txt">ftp://ftp.isi.edu/in-notes/rfc2045.txt - - [MPEG] ISO/IEC 11172-3:1993. - Coding of moving pictures and associated audio for digital storage - media at up to about 1,5 Mbit/s, Part 3: Audio. - Technical committee / subcommittee: JTC 1 / SC 29 - and - ISO/IEC 13818-3:1995 - Generic coding of moving pictures and associated audio information, - Part 3: Audio. - Technical committee / subcommittee: JTC 1 / SC 29 - and - ISO/IEC DIS 13818-3 - Generic coding of moving pictures and associated audio information, - Part 3: Audio (Revision of ISO/IEC 13818-3:1995) - - - [PNG] Portable Network Graphics, version 1.0 - - http://www.w3.org/TR/REC-png-multi.html - - [UNICODE] ISO/IEC 10646-1:1993. - Universal Multiple-Octet Coded Character Set (UCS), Part 1: - Architecture and Basic Multilingual Plane. - Technical committee / subcommittee: JTC 1 / SC 2 - - http://www.unicode.org/ - - [URL] T. Berners-Lee, L. Masinter & M. McCahill, "Uniform Resource - Locators (URL).", RFC 1738, December 1994. - - ftp://ftp.isi.edu/in-notes/rfc1738.txt - - [ZLIB] P. Deutsch, Aladdin Enterprises & J-L. Gailly, "ZLIB - Compressed - Data Format Specification version 3.3", RFC 1950, May 1996. - - ftp://ftp.isi.edu/in-notes/rfc1950.txt - - -8. Appendix - - -A. Appendix A - Genre List from ID3v1 - - The following genres is defined in ID3v1 - - 0.Blues - 1.Classic Rock - 2.Country - 3.Dance - 4.Disco - 5.Funk - 6.Grunge - 7.Hip-Hop - 8.Jazz - 9.Metal - 10.New Age - 11.Oldies - 12.Other - 13.Pop - 14.R&B - 15.Rap - 16.Reggae - 17.Rock - 18.Techno - 19.Industrial - 20.Alternative - 21.Ska - 22.Death Metal - 23.Pranks - 24.Soundtrack - 25.Euro-Techno - 26.Ambient - 27.Trip-Hop - 28.Vocal - 29.Jazz+Funk - 30.Fusion - 31.Trance - 32.Classical - 33.Instrumental - 34.Acid - 35.House - 36.Game - 37.Sound Clip - 38.Gospel - 39.Noise - 40.AlternRock - 41.Bass - 42.Soul - 43.Punk - 44.Space - 45.Meditative - 46.Instrumental Pop - 47.Instrumental Rock - 48.Ethnic - 49.Gothic - 50.Darkwave - 51.Techno-Industrial - 52.Electronic - 53.Pop-Folk - 54.Eurodance - 55.Dream - 56.Southern Rock - 57.Comedy - 58.Cult - 59.Gangsta - 60.Top 40 - 61.Christian Rap - 62.Pop/Funk - 63.Jungle - 64.Native American - 65.Cabaret - 66.New Wave - 67.Psychadelic - 68.Rave - 69.Showtunes - 70.Trailer - 71.Lo-Fi - 72.Tribal - 73.Acid Punk - 74.Acid Jazz - 75.Polka - 76.Retro - 77.Musical - 78.Rock & Roll - 79.Hard Rock - - The following genres are Winamp extensions - - 80.Folk - 81.Folk-Rock - 82.National Folk - 83.Swing - 84.Fast Fusion - 85.Bebob - 86.Latin - 87.Revival - 88.Celtic - 89.Bluegrass - 90.Avantgarde - 91.Gothic Rock - 92.Progressive Rock - 93.Psychedelic Rock - 94.Symphonic Rock - 95.Slow Rock - 96.Big Band - 97.Chorus - 98.Easy Listening - 99.Acoustic - 100.Humour - 101.Speech - 102.Chanson - 103.Opera - 104.Chamber Music - 105.Sonata - 106.Symphony - 107.Booty Bass - 108.Primus - 109.Porn Groove - 110.Satire - 111.Slow Jam - 112.Club - 113.Tango - 114.Samba - 115.Folklore - 116.Ballad - 117.Power Ballad - 118.Rhythmic Soul - 119.Freestyle - 120.Duet - 121.Punk Rock - 122.Drum Solo - 123.Acapella - 124.Euro-House - 125.Dance Hall - - -9. Author's Address - - Written by - - Martin Nilsson - Rydsvägen 246 C. 30 - S-584 34 Linköping - Sweden - - Email: nilsson@id3.org - - - Edited by - - Dirk Mahoney - 57 Pechey Street - Chermside Q - Australia 4032 - - Email: dirk@id3.org - - - Johan Sundström - Alsättersgatan 5 A. 34 - S-584 35 Linköping - Sweden - - Email: johan@id3.org diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2.4.0-frames.txt b/3rdparty/taglib/mpeg/id3v2/id3v2.4.0-frames.txt deleted file mode 100644 index 74a21bed..00000000 --- a/3rdparty/taglib/mpeg/id3v2/id3v2.4.0-frames.txt +++ /dev/null @@ -1,1734 +0,0 @@ -$Id$ - -Informal standard M. Nilsson -Document: id3v2.4.0-frames.txt 1st November 2000 - - - ID3 tag version 2.4.0 - Native Frames - -Status of this document - - This document is an informal standard and replaces the ID3v2.3.0 - standard [ID3v2]. A formal standard will use another revision number - even if the content is identical to document. The contents in this - document may change for clarifications but never for added or altered - functionallity. - - Distribution of this document is unlimited. - - -Abstract - - This document describes the frames natively supported by ID3v2.4.0, - which is a revised version of the ID3v2 informal standard [ID3v2.3.0] - version 2.3.0. The ID3v2 offers a flexible way of storing audio meta - information within audio file itself. The information may be - technical information, such as equalisation curves, as well as title, - performer, copyright etc. - - ID3v2.4.0 is meant to be as close as possible to ID3v2.3.0 in order - to allow for implementations to be revised as easily as possible. - - -1. Table of contents - - 2. Conventions in this document - 3. Default flags - 4. Declared ID3v2 frames - 4.1. Unique file identifier - 4.2. Text information frames - 4.2.1. Identification frames - 4.2.2. Involved persons frames - 4.2.3. Derived and subjective properties frames - 4.2.4. Rights and license frames - 4.2.5. Other text frames - 4.2.6. User defined text information frame - 4.3. URL link frames - 4.3.1. URL link frames - details - 4.3.2. User defined URL link frame - 4.4. Music CD Identifier - 4.5. Event timing codes - 4.6. MPEG location lookup table - 4.7. Synced tempo codes - 4.8. Unsynchronised lyrics/text transcription - 4.9. Synchronised lyrics/text - 4.10. Comments - 4.11. Relative volume adjustment (2) - 4.12. Equalisation (2) - 4.13. Reverb - 4.14. Attached picture - 4.15. General encapsulated object - 4.16. Play counter - 4.17. Popularimeter - 4.18. Recommended buffer size - 4.19. Audio encryption - 4.20. Linked information - 4.21. Position synchronisation frame - 4.22. Terms of use - 4.23. Ownership frame - 4.24. Commercial frame - 4.25. Encryption method registration - 4.26. Group identification registration - 4.27. Private frame - 4.28. Signature frame - 4.29. Seek frame - 4.30. Audio seek point index - 5. Copyright - 6. References - 7. Appendix - A. Appendix A - Genre List from ID3v1 - 8. Author's Address - - -2. Conventions in this document - - Text within "" is a text string exactly as it appears in a tag. - Numbers preceded with $ are hexadecimal and numbers preceded with % - are binary. $xx is used to indicate a byte with unknown content. %x - is used to indicate a bit with unknown content. The most significant - bit (MSB) of a byte is called 'bit 7' and the least significant bit - (LSB) is called 'bit 0'. - - A tag is the whole tag described the ID3v2 main structure document - [ID3v2-strct]. A frame is a block of information in the tag. The tag - consists of a header, frames and optional padding. A field is a piece - of information; one value, a string etc. A numeric string is a string - that consists of the characters "0123456789" only. - - The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", - "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this - document are to be interpreted as described in RFC 2119 [KEYWORDS]. - - -3. Default flags - - The default settings for the frames described in this document can be - divided into the following classes. The flags may be set differently - if found more suitable by the software. - - 1. Discarded if tag is altered, discarded if file is altered. - - None. - - 2. Discarded if tag is altered, preserved if file is altered. - - None. - - 3. Preserved if tag is altered, discarded if file is altered. - - ASPI, AENC, ETCO, EQU2, MLLT, POSS, SEEK, SYLT, SYTC, RVA2, TENC, - TLEN - - 4. Preserved if tag is altered, preserved if file is altered. - - The rest of the frames. - - -4. Declared ID3v2 frames - - The following frames are declared in this draft. - - 4.19 AENC Audio encryption - 4.14 APIC Attached picture - 4.30 ASPI Audio seek point index - - 4.10 COMM Comments - 4.24 COMR Commercial frame - - 4.25 ENCR Encryption method registration - 4.12 EQU2 Equalisation (2) - 4.5 ETCO Event timing codes - - 4.15 GEOB General encapsulated object - 4.26 GRID Group identification registration - - 4.20 LINK Linked information - - 4.4 MCDI Music CD identifier - 4.6 MLLT MPEG location lookup table - - 4.23 OWNE Ownership frame - - 4.27 PRIV Private frame - 4.16 PCNT Play counter - 4.17 POPM Popularimeter - 4.21 POSS Position synchronisation frame - - 4.18 RBUF Recommended buffer size - 4.11 RVA2 Relative volume adjustment (2) - 4.13 RVRB Reverb - - 4.29 SEEK Seek frame - 4.28 SIGN Signature frame - 4.9 SYLT Synchronised lyric/text - 4.7 SYTC Synchronised tempo codes - - 4.2.1 TALB Album/Movie/Show title - 4.2.3 TBPM BPM (beats per minute) - 4.2.2 TCOM Composer - 4.2.3 TCON Content type - 4.2.4 TCOP Copyright message - 4.2.5 TDEN Encoding time - 4.2.5 TDLY Playlist delay - 4.2.5 TDOR Original release time - 4.2.5 TDRC Recording time - 4.2.5 TDRL Release time - 4.2.5 TDTG Tagging time - 4.2.2 TENC Encoded by - 4.2.2 TEXT Lyricist/Text writer - 4.2.3 TFLT File type - 4.2.2 TIPL Involved people list - 4.2.1 TIT1 Content group description - 4.2.1 TIT2 Title/songname/content description - 4.2.1 TIT3 Subtitle/Description refinement - 4.2.3 TKEY Initial key - 4.2.3 TLAN Language(s) - 4.2.3 TLEN Length - 4.2.2 TMCL Musician credits list - 4.2.3 TMED Media type - 4.2.3 TMOO Mood - 4.2.1 TOAL Original album/movie/show title - 4.2.5 TOFN Original filename - 4.2.2 TOLY Original lyricist(s)/text writer(s) - 4.2.2 TOPE Original artist(s)/performer(s) - 4.2.4 TOWN File owner/licensee - 4.2.2 TPE1 Lead performer(s)/Soloist(s) - 4.2.2 TPE2 Band/orchestra/accompaniment - 4.2.2 TPE3 Conductor/performer refinement - 4.2.2 TPE4 Interpreted, remixed, or otherwise modified by - 4.2.1 TPOS Part of a set - 4.2.4 TPRO Produced notice - 4.2.4 TPUB Publisher - 4.2.1 TRCK Track number/Position in set - 4.2.4 TRSN Internet radio station name - 4.2.4 TRSO Internet radio station owner - 4.2.5 TSOA Album sort order - 4.2.5 TSOP Performer sort order - 4.2.5 TSOT Title sort order - 4.2.1 TSRC ISRC (international standard recording code) - 4.2.5 TSSE Software/Hardware and settings used for encoding - 4.2.1 TSST Set subtitle - 4.2.2 TXXX User defined text information frame - - 4.1 UFID Unique file identifier - 4.22 USER Terms of use - 4.8 USLT Unsynchronised lyric/text transcription - - 4.3.1 WCOM Commercial information - 4.3.1 WCOP Copyright/Legal information - 4.3.1 WOAF Official audio file webpage - 4.3.1 WOAR Official artist/performer webpage - 4.3.1 WOAS Official audio source webpage - 4.3.1 WORS Official Internet radio station homepage - 4.3.1 WPAY Payment - 4.3.1 WPUB Publishers official webpage - 4.3.2 WXXX User defined URL link frame - - -4.1. Unique file identifier - - This frame's purpose is to be able to identify the audio file in a - database, that may provide more information relevant to the content. - Since standardisation of such a database is beyond this document, all - UFID frames begin with an 'owner identifier' field. It is a null- - terminated string with a URL [URL] containing an email address, or a - link to a location where an email address can be found, that belongs - to the organisation responsible for this specific database - implementation. Questions regarding the database should be sent to - the indicated email address. The URL should not be used for the - actual database queries. The string - "http://www.id3.org/dummy/ufid.html" should be used for tests. The - 'Owner identifier' must be non-empty (more than just a termination). - The 'Owner identifier' is then followed by the actual identifier, - which may be up to 64 bytes. There may be more than one "UFID" frame - in a tag, but only one with the same 'Owner identifier'. - -
- Owner identifier $00 - Identifier - - -4.2. Text information frames - - The text information frames are often the most important frames, - containing information like artist, album and more. There may only be - one text information frame of its kind in an tag. All text - information frames supports multiple strings, stored as a null - separated list, where null is reperesented by the termination code - for the charater encoding. All text frame identifiers begin with "T". - Only text frame identifiers begin with "T", with the exception of the - "TXXX" frame. All the text information frames have the following - format: - -
- Text encoding $xx - Information - - -4.2.1. Identification frames - - TIT1 - The 'Content group description' frame is used if the sound belongs to - a larger category of sounds/music. For example, classical music is - often sorted in different musical sections (e.g. "Piano Concerto", - "Weather - Hurricane"). - - TIT2 - The 'Title/Songname/Content description' frame is the actual name of - the piece (e.g. "Adagio", "Hurricane Donna"). - - TIT3 - The 'Subtitle/Description refinement' frame is used for information - directly related to the contents title (e.g. "Op. 16" or "Performed - live at Wembley"). - - TALB - The 'Album/Movie/Show title' frame is intended for the title of the - recording (or source of sound) from which the audio in the file is - taken. - - TOAL - The 'Original album/movie/show title' frame is intended for the title - of the original recording (or source of sound), if for example the - music in the file should be a cover of a previously released song. - - TRCK - The 'Track number/Position in set' frame is a numeric string - containing the order number of the audio-file on its original - recording. This MAY be extended with a "/" character and a numeric - string containing the total number of tracks/elements on the original - recording. E.g. "4/9". - - TPOS - The 'Part of a set' frame is a numeric string that describes which - part of a set the audio came from. This frame is used if the source - described in the "TALB" frame is divided into several mediums, e.g. a - double CD. The value MAY be extended with a "/" character and a - numeric string containing the total number of parts in the set. E.g. - "1/2". - - TSST - The 'Set subtitle' frame is intended for the subtitle of the part of - a set this track belongs to. - - TSRC - The 'ISRC' frame should contain the International Standard Recording - Code [ISRC] (12 characters). - - -4.2.2. Involved persons frames - - TPE1 - The 'Lead artist/Lead performer/Soloist/Performing group' is - used for the main artist. - - TPE2 - The 'Band/Orchestra/Accompaniment' frame is used for additional - information about the performers in the recording. - - TPE3 - The 'Conductor' frame is used for the name of the conductor. - - TPE4 - The 'Interpreted, remixed, or otherwise modified by' frame contains - more information about the people behind a remix and similar - interpretations of another existing piece. - - TOPE - The 'Original artist/performer' frame is intended for the performer - of the original recording, if for example the music in the file - should be a cover of a previously released song. - - TEXT - The 'Lyricist/Text writer' frame is intended for the writer of the - text or lyrics in the recording. - - TOLY - The 'Original lyricist/text writer' frame is intended for the - text writer of the original recording, if for example the music in - the file should be a cover of a previously released song. - - TCOM - The 'Composer' frame is intended for the name of the composer. - - TMCL - The 'Musician credits list' is intended as a mapping between - instruments and the musician that played it. Every odd field is an - instrument and every even is an artist or a comma delimited list of - artists. - - TIPL - The 'Involved people list' is very similar to the musician credits - list, but maps between functions, like producer, and names. - - TENC - The 'Encoded by' frame contains the name of the person or - organisation that encoded the audio file. This field may contain a - copyright message, if the audio file also is copyrighted by the - encoder. - - -4.2.3. Derived and subjective properties frames - - TBPM - The 'BPM' frame contains the number of beats per minute in the - main part of the audio. The BPM is an integer and represented as a - numerical string. - - TLEN - The 'Length' frame contains the length of the audio file in - milliseconds, represented as a numeric string. - - TKEY - The 'Initial key' frame contains the musical key in which the sound - starts. It is represented as a string with a maximum length of three - characters. The ground keys are represented with "A","B","C","D","E", - "F" and "G" and halfkeys represented with "b" and "#". Minor is - represented as "m", e.g. "Dbm" $00. Off key is represented with an - "o" only. - - TLAN - The 'Language' frame should contain the languages of the text or - lyrics spoken or sung in the audio. The language is represented with - three characters according to ISO-639-2 [ISO-639-2]. If more than one - language is used in the text their language codes should follow - according to the amount of their usage, e.g. "eng" $00 "sve" $00. - - TCON - The 'Content type', which ID3v1 was stored as a one byte numeric - value only, is now a string. You may use one or several of the ID3v1 - types as numerical strings, or, since the category list would be - impossible to maintain with accurate and up to date categories, - define your own. Example: "21" $00 "Eurodisco" $00 - - You may also use any of the following keywords: - - RX Remix - CR Cover - - TFLT - The 'File type' frame indicates which type of audio this tag defines. - The following types and refinements are defined: - - MIME MIME type follows - MPG MPEG Audio - /1 MPEG 1/2 layer I - /2 MPEG 1/2 layer II - /3 MPEG 1/2 layer III - /2.5 MPEG 2.5 - /AAC Advanced audio compression - VQF Transform-domain Weighted Interleave Vector Quantisation - PCM Pulse Code Modulated audio - - but other types may be used, but not for these types though. This is - used in a similar way to the predefined types in the "TMED" frame, - but without parentheses. If this frame is not present audio type is - assumed to be "MPG". - - TMED - The 'Media type' frame describes from which media the sound - originated. This may be a text string or a reference to the - predefined media types found in the list below. Example: - "VID/PAL/VHS" $00. - - DIG Other digital media - /A Analogue transfer from media - - ANA Other analogue media - /WAC Wax cylinder - /8CA 8-track tape cassette - - CD CD - /A Analogue transfer from media - /DD DDD - /AD ADD - /AA AAD - - LD Laserdisc - - TT Turntable records - /33 33.33 rpm - /45 45 rpm - /71 71.29 rpm - /76 76.59 rpm - /78 78.26 rpm - /80 80 rpm - - MD MiniDisc - /A Analogue transfer from media - - DAT DAT - /A Analogue transfer from media - /1 standard, 48 kHz/16 bits, linear - /2 mode 2, 32 kHz/16 bits, linear - /3 mode 3, 32 kHz/12 bits, non-linear, low speed - /4 mode 4, 32 kHz/12 bits, 4 channels - /5 mode 5, 44.1 kHz/16 bits, linear - /6 mode 6, 44.1 kHz/16 bits, 'wide track' play - - DCC DCC - /A Analogue transfer from media - - DVD DVD - /A Analogue transfer from media - - TV Television - /PAL PAL - /NTSC NTSC - /SECAM SECAM - - VID Video - /PAL PAL - /NTSC NTSC - /SECAM SECAM - /VHS VHS - /SVHS S-VHS - /BETA BETAMAX - - RAD Radio - /FM FM - /AM AM - /LW LW - /MW MW - - TEL Telephone - /I ISDN - - MC MC (normal cassette) - /4 4.75 cm/s (normal speed for a two sided cassette) - /9 9.5 cm/s - /I Type I cassette (ferric/normal) - /II Type II cassette (chrome) - /III Type III cassette (ferric chrome) - /IV Type IV cassette (metal) - - REE Reel - /9 9.5 cm/s - /19 19 cm/s - /38 38 cm/s - /76 76 cm/s - /I Type I cassette (ferric/normal) - /II Type II cassette (chrome) - /III Type III cassette (ferric chrome) - /IV Type IV cassette (metal) - - TMOO - The 'Mood' frame is intended to reflect the mood of the audio with a - few keywords, e.g. "Romantic" or "Sad". - - -4.2.4. Rights and license frames - - TCOP - The 'Copyright message' frame, in which the string must begin with a - year and a space character (making five characters), is intended for - the copyright holder of the original sound, not the audio file - itself. The absence of this frame means only that the copyright - information is unavailable or has been removed, and must not be - interpreted to mean that the audio is public domain. Every time this - field is displayed the field must be preceded with "Copyright " (C) " - ", where (C) is one character showing a C in a circle. - - TPRO - The 'Produced notice' frame, in which the string must begin with a - year and a space character (making five characters), is intended for - the production copyright holder of the original sound, not the audio - file itself. The absence of this frame means only that the production - copyright information is unavailable or has been removed, and must - not be interpreted to mean that the audio is public domain. Every - time this field is displayed the field must be preceded with - "Produced " (P) " ", where (P) is one character showing a P in a - circle. - - TPUB - The 'Publisher' frame simply contains the name of the label or - publisher. - - TOWN - The 'File owner/licensee' frame contains the name of the owner or - licensee of the file and it's contents. - - TRSN - The 'Internet radio station name' frame contains the name of the - internet radio station from which the audio is streamed. - - TRSO - The 'Internet radio station owner' frame contains the name of the - owner of the internet radio station from which the audio is - streamed. - -4.2.5. Other text frames - - TOFN - The 'Original filename' frame contains the preferred filename for the - file, since some media doesn't allow the desired length of the - filename. The filename is case sensitive and includes its suffix. - - TDLY - The 'Playlist delay' defines the numbers of milliseconds of silence - that should be inserted before this audio. The value zero indicates - that this is a part of a multifile audio track that should be played - continuously. - - TDEN - The 'Encoding time' frame contains a timestamp describing when the - audio was encoded. Timestamp format is described in the ID3v2 - structure document [ID3v2-strct]. - - TDOR - The 'Original release time' frame contains a timestamp describing - when the original recording of the audio was released. Timestamp - format is described in the ID3v2 structure document [ID3v2-strct]. - - TDRC - The 'Recording time' frame contains a timestamp describing when the - audio was recorded. Timestamp format is described in the ID3v2 - structure document [ID3v2-strct]. - - TDRL - The 'Release time' frame contains a timestamp describing when the - audio was first released. Timestamp format is described in the ID3v2 - structure document [ID3v2-strct]. - - TDTG - The 'Tagging time' frame contains a timestamp describing then the - audio was tagged. Timestamp format is described in the ID3v2 - structure document [ID3v2-strct]. - - TSSE - The 'Software/Hardware and settings used for encoding' frame - includes the used audio encoder and its settings when the file was - encoded. Hardware refers to hardware encoders, not the computer on - which a program was run. - - TSOA - The 'Album sort order' frame defines a string which should be used - instead of the album name (TALB) for sorting purposes. E.g. an album - named "A Soundtrack" might preferably be sorted as "Soundtrack". - - TSOP - The 'Performer sort order' frame defines a string which should be - used instead of the performer (TPE2) for sorting purposes. - - TSOT - The 'Title sort order' frame defines a string which should be used - instead of the title (TIT2) for sorting purposes. - - -4.2.6. User defined text information frame - - This frame is intended for one-string text information concerning the - audio file in a similar way to the other "T"-frames. The frame body - consists of a description of the string, represented as a terminated - string, followed by the actual string. There may be more than one - "TXXX" frame in each tag, but only one with the same description. - -
- Text encoding $xx - Description $00 (00) - Value - - -4.3. URL link frames - - With these frames dynamic data such as webpages with touring - information, price information or plain ordinary news can be added to - the tag. There may only be one URL [URL] link frame of its kind in an - tag, except when stated otherwise in the frame description. If the - text string is followed by a string termination, all the following - information should be ignored and not be displayed. All URL link - frame identifiers begins with "W". Only URL link frame identifiers - begins with "W", except for "WXXX". All URL link frames have the - following format: - -
- URL - - -4.3.1. URL link frames - details - - WCOM - The 'Commercial information' frame is a URL pointing at a webpage - with information such as where the album can be bought. There may be - more than one "WCOM" frame in a tag, but not with the same content. - - WCOP - The 'Copyright/Legal information' frame is a URL pointing at a - webpage where the terms of use and ownership of the file is - described. - - WOAF - The 'Official audio file webpage' frame is a URL pointing at a file - specific webpage. - - WOAR - The 'Official artist/performer webpage' frame is a URL pointing at - the artists official webpage. There may be more than one "WOAR" frame - in a tag if the audio contains more than one performer, but not with - the same content. - - WOAS - The 'Official audio source webpage' frame is a URL pointing at the - official webpage for the source of the audio file, e.g. a movie. - - WORS - The 'Official Internet radio station homepage' contains a URL - pointing at the homepage of the internet radio station. - - WPAY - The 'Payment' frame is a URL pointing at a webpage that will handle - the process of paying for this file. - - WPUB - The 'Publishers official webpage' frame is a URL pointing at the - official webpage for the publisher. - - -4.3.2. User defined URL link frame - - This frame is intended for URL [URL] links concerning the audio file - in a similar way to the other "W"-frames. The frame body consists - of a description of the string, represented as a terminated string, - followed by the actual URL. The URL is always encoded with ISO-8859-1 - [ISO-8859-1]. There may be more than one "WXXX" frame in each tag, - but only one with the same description. - -
- Text encoding $xx - Description $00 (00) - URL - - -4.4. Music CD identifier - - This frame is intended for music that comes from a CD, so that the CD - can be identified in databases such as the CDDB [CDDB]. The frame - consists of a binary dump of the Table Of Contents, TOC, from the CD, - which is a header of 4 bytes and then 8 bytes/track on the CD plus 8 - bytes for the 'lead out', making a maximum of 804 bytes. The offset - to the beginning of every track on the CD should be described with a - four bytes absolute CD-frame address per track, and not with absolute - time. When this frame is used the presence of a valid "TRCK" frame is - REQUIRED, even if the CD's only got one track. It is recommended that - this frame is always added to tags originating from CDs. There may - only be one "MCDI" frame in each tag. - -
- CD TOC - - -4.5. Event timing codes - - This frame allows synchronisation with key events in the audio. The - header is: - -
- Time stamp format $xx - - Where time stamp format is: - - $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit - $02 Absolute time, 32 bit sized, using milliseconds as unit - - Absolute time means that every stamp contains the time from the - beginning of the file. - - Followed by a list of key events in the following format: - - Type of event $xx - Time stamp $xx (xx ...) - - The 'Time stamp' is set to zero if directly at the beginning of the - sound or after the previous event. All events MUST be sorted in - chronological order. The type of event is as follows: - - $00 padding (has no meaning) - $01 end of initial silence - $02 intro start - $03 main part start - $04 outro start - $05 outro end - $06 verse start - $07 refrain start - $08 interlude start - $09 theme start - $0A variation start - $0B key change - $0C time change - $0D momentary unwanted noise (Snap, Crackle & Pop) - $0E sustained noise - $0F sustained noise end - $10 intro end - $11 main part end - $12 verse end - $13 refrain end - $14 theme end - $15 profanity - $16 profanity end - - $17-$DF reserved for future use - - $E0-$EF not predefined synch 0-F - - $F0-$FC reserved for future use - - $FD audio end (start of silence) - $FE audio file ends - $FF one more byte of events follows (all the following bytes with - the value $FF have the same function) - - Terminating the start events such as "intro start" is OPTIONAL. The - 'Not predefined synch's ($E0-EF) are for user events. You might want - to synchronise your music to something, like setting off an explosion - on-stage, activating a screensaver etc. - - There may only be one "ETCO" frame in each tag. - - -4.6. MPEG location lookup table - - To increase performance and accuracy of jumps within a MPEG [MPEG] - audio file, frames with time codes in different locations in the file - might be useful. This ID3v2 frame includes references that the - software can use to calculate positions in the file. After the frame - header follows a descriptor of how much the 'frame counter' should be - increased for every reference. If this value is two then the first - reference points out the second frame, the 2nd reference the 4th - frame, the 3rd reference the 6th frame etc. In a similar way the - 'bytes between reference' and 'milliseconds between reference' points - out bytes and milliseconds respectively. - - Each reference consists of two parts; a certain number of bits, as - defined in 'bits for bytes deviation', that describes the difference - between what is said in 'bytes between reference' and the reality and - a certain number of bits, as defined in 'bits for milliseconds - deviation', that describes the difference between what is said in - 'milliseconds between reference' and the reality. The number of bits - in every reference, i.e. 'bits for bytes deviation'+'bits for - milliseconds deviation', must be a multiple of four. There may only - be one "MLLT" frame in each tag. - -
- MPEG frames between reference $xx xx - Bytes between reference $xx xx xx - Milliseconds between reference $xx xx xx - Bits for bytes deviation $xx - Bits for milliseconds dev. $xx - - Then for every reference the following data is included; - - Deviation in bytes %xxx.... - Deviation in milliseconds %xxx.... - - -4.7. Synchronised tempo codes - - For a more accurate description of the tempo of a musical piece, this - frame might be used. After the header follows one byte describing - which time stamp format should be used. Then follows one or more - tempo codes. Each tempo code consists of one tempo part and one time - part. The tempo is in BPM described with one or two bytes. If the - first byte has the value $FF, one more byte follows, which is added - to the first giving a range from 2 - 510 BPM, since $00 and $01 is - reserved. $00 is used to describe a beat-free time period, which is - not the same as a music-free time period. $01 is used to indicate one - single beat-stroke followed by a beat-free period. - - The tempo descriptor is followed by a time stamp. Every time the - tempo in the music changes, a tempo descriptor may indicate this for - the player. All tempo descriptors MUST be sorted in chronological - order. The first beat-stroke in a time-period is at the same time as - the beat description occurs. There may only be one "SYTC" frame in - each tag. - -
- Time stamp format $xx - Tempo data - - Where time stamp format is: - - $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit - $02 Absolute time, 32 bit sized, using milliseconds as unit - - Absolute time means that every stamp contains the time from the - beginning of the file. - - -4.8. Unsynchronised lyrics/text transcription - - This frame contains the lyrics of the song or a text transcription of - other vocal activities. The head includes an encoding descriptor and - a content descriptor. The body consists of the actual text. The - 'Content descriptor' is a terminated string. If no descriptor is - entered, 'Content descriptor' is $00 (00) only. Newline characters - are allowed in the text. There may be more than one 'Unsynchronised - lyrics/text transcription' frame in each tag, but only one with the - same language and content descriptor. - -
- Text encoding $xx - Language $xx xx xx - Content descriptor $00 (00) - Lyrics/text - - -4.9. Synchronised lyrics/text - - This is another way of incorporating the words, said or sung lyrics, - in the audio file as text, this time, however, in sync with the - audio. It might also be used to describing events e.g. occurring on a - stage or on the screen in sync with the audio. The header includes a - content descriptor, represented with as terminated text string. If no - descriptor is entered, 'Content descriptor' is $00 (00) only. - -
- Text encoding $xx - Language $xx xx xx - Time stamp format $xx - Content type $xx - Content descriptor $00 (00) - - Content type: $00 is other - $01 is lyrics - $02 is text transcription - $03 is movement/part name (e.g. "Adagio") - $04 is events (e.g. "Don Quijote enters the stage") - $05 is chord (e.g. "Bb F Fsus") - $06 is trivia/'pop up' information - $07 is URLs to webpages - $08 is URLs to images - - Time stamp format: - - $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit - $02 Absolute time, 32 bit sized, using milliseconds as unit - - Absolute time means that every stamp contains the time from the - beginning of the file. - - The text that follows the frame header differs from that of the - unsynchronised lyrics/text transcription in one major way. Each - syllable (or whatever size of text is considered to be convenient by - the encoder) is a null terminated string followed by a time stamp - denoting where in the sound file it belongs. Each sync thus has the - following structure: - - Terminated text to be synced (typically a syllable) - Sync identifier (terminator to above string) $00 (00) - Time stamp $xx (xx ...) - - The 'time stamp' is set to zero or the whole sync is omitted if - located directly at the beginning of the sound. All time stamps - should be sorted in chronological order. The sync can be considered - as a validator of the subsequent string. - - Newline characters are allowed in all "SYLT" frames and MUST be used - after every entry (name, event etc.) in a frame with the content type - $03 - $04. - - A few considerations regarding whitespace characters: Whitespace - separating words should mark the beginning of a new word, thus - occurring in front of the first syllable of a new word. This is also - valid for new line characters. A syllable followed by a comma should - not be broken apart with a sync (both the syllable and the comma - should be before the sync). - - An example: The "USLT" passage - - "Strangers in the night" $0A "Exchanging glances" - - would be "SYLT" encoded as: - - "Strang" $00 xx xx "ers" $00 xx xx " in" $00 xx xx " the" $00 xx xx - " night" $00 xx xx 0A "Ex" $00 xx xx "chang" $00 xx xx "ing" $00 xx - xx "glan" $00 xx xx "ces" $00 xx xx - - There may be more than one "SYLT" frame in each tag, but only one - with the same language and content descriptor. - - -4.10. Comments - - This frame is intended for any kind of full text information that - does not fit in any other frame. It consists of a frame header - followed by encoding, language and content descriptors and is ended - with the actual comment as a text string. Newline characters are - allowed in the comment text string. There may be more than one - comment frame in each tag, but only one with the same language and - content descriptor. - -
- Text encoding $xx - Language $xx xx xx - Short content descrip. $00 (00) - The actual text - - -4.11. Relative volume adjustment (2) - - This is a more subjective frame than the previous ones. It allows the - user to say how much he wants to increase/decrease the volume on each - channel when the file is played. The purpose is to be able to align - all files to a reference volume, so that you don't have to change the - volume constantly. This frame may also be used to balance adjust the - audio. The volume adjustment is encoded as a fixed point decibel - value, 16 bit signed integer representing (adjustment*512), giving - +/- 64 dB with a precision of 0.001953125 dB. E.g. +2 dB is stored as - $04 00 and -2 dB is $FC 00. There may be more than one "RVA2" frame - in each tag, but only one with the same identification string. - -
- Identification $00 - - The 'identification' string is used to identify the situation and/or - device where this adjustment should apply. The following is then - repeated for every channel - - Type of channel $xx - Volume adjustment $xx xx - Bits representing peak $xx - Peak volume $xx (xx ...) - - - Type of channel: $00 Other - $01 Master volume - $02 Front right - $03 Front left - $04 Back right - $05 Back left - $06 Front centre - $07 Back centre - $08 Subwoofer - - Bits representing peak can be any number between 0 and 255. 0 means - that there is no peak volume field. The peak volume field is always - padded to whole bytes, setting the most significant bits to zero. - - -4.12. Equalisation (2) - - This is another subjective, alignment frame. It allows the user to - predefine an equalisation curve within the audio file. There may be - more than one "EQU2" frame in each tag, but only one with the same - identification string. - -
- Interpolation method $xx - Identification $00 - - The 'interpolation method' describes which method is preferred when - an interpolation between the adjustment point that follows. The - following methods are currently defined: - - $00 Band - No interpolation is made. A jump from one adjustment level to - another occurs in the middle between two adjustment points. - $01 Linear - Interpolation between adjustment points is linear. - - The 'identification' string is used to identify the situation and/or - device where this adjustment should apply. The following is then - repeated for every adjustment point - - Frequency $xx xx - Volume adjustment $xx xx - - The frequency is stored in units of 1/2 Hz, giving it a range from 0 - to 32767 Hz. - - The volume adjustment is encoded as a fixed point decibel value, 16 - bit signed integer representing (adjustment*512), giving +/- 64 dB - with a precision of 0.001953125 dB. E.g. +2 dB is stored as $04 00 - and -2 dB is $FC 00. - - Adjustment points should be ordered by frequency and one frequency - should only be described once in the frame. - - -4.13. Reverb - - Yet another subjective frame, with which you can adjust echoes of - different kinds. Reverb left/right is the delay between every bounce - in ms. Reverb bounces left/right is the number of bounces that should - be made. $FF equals an infinite number of bounces. Feedback is the - amount of volume that should be returned to the next echo bounce. $00 - is 0%, $FF is 100%. If this value were $7F, there would be 50% volume - reduction on the first bounce, 50% of that on the second and so on. - Left to left means the sound from the left bounce to be played in the - left speaker, while left to right means sound from the left bounce to - be played in the right speaker. - - 'Premix left to right' is the amount of left sound to be mixed in the - right before any reverb is applied, where $00 id 0% and $FF is 100%. - 'Premix right to left' does the same thing, but right to left. - Setting both premix to $FF would result in a mono output (if the - reverb is applied symmetric). There may only be one "RVRB" frame in - each tag. - -
- Reverb left (ms) $xx xx - Reverb right (ms) $xx xx - Reverb bounces, left $xx - Reverb bounces, right $xx - Reverb feedback, left to left $xx - Reverb feedback, left to right $xx - Reverb feedback, right to right $xx - Reverb feedback, right to left $xx - Premix left to right $xx - Premix right to left $xx - - -4.14. Attached picture - - This frame contains a picture directly related to the audio file. - Image format is the MIME type and subtype [MIME] for the image. In - the event that the MIME media type name is omitted, "image/" will be - implied. The "image/png" [PNG] or "image/jpeg" [JFIF] picture format - should be used when interoperability is wanted. Description is a - short description of the picture, represented as a terminated - text string. There may be several pictures attached to one file, each - in their individual "APIC" frame, but only one with the same content - descriptor. There may only be one picture with the picture type - declared as picture type $01 and $02 respectively. There is the - possibility to put only a link to the image file by using the 'MIME - type' "-->" and having a complete URL [URL] instead of picture data. - The use of linked files should however be used sparingly since there - is the risk of separation of files. - -
- Text encoding $xx - MIME type $00 - Picture type $xx - Description $00 (00) - Picture data - - - Picture type: $00 Other - $01 32x32 pixels 'file icon' (PNG only) - $02 Other file icon - $03 Cover (front) - $04 Cover (back) - $05 Leaflet page - $06 Media (e.g. label side of CD) - $07 Lead artist/lead performer/soloist - $08 Artist/performer - $09 Conductor - $0A Band/Orchestra - $0B Composer - $0C Lyricist/text writer - $0D Recording Location - $0E During recording - $0F During performance - $10 Movie/video screen capture - $11 A bright coloured fish - $12 Illustration - $13 Band/artist logotype - $14 Publisher/Studio logotype - - -4.15. General encapsulated object - - In this frame any type of file can be encapsulated. After the header, - 'Frame size' and 'Encoding' follows 'MIME type' [MIME] represented as - as a terminated string encoded with ISO 8859-1 [ISO-8859-1]. The - filename is case sensitive and is encoded as 'Encoding'. Then follows - a content description as terminated string, encoded as 'Encoding'. - The last thing in the frame is the actual object. The first two - strings may be omitted, leaving only their terminations. MIME type is - always an ISO-8859-1 text string. There may be more than one "GEOB" - frame in each tag, but only one with the same content descriptor. - -
- Text encoding $xx - MIME type $00 - Filename $00 (00) - Content description $00 (00) - Encapsulated object - - -4.16. Play counter - - This is simply a counter of the number of times a file has been - played. The value is increased by one every time the file begins to - play. There may only be one "PCNT" frame in each tag. When the - counter reaches all one's, one byte is inserted in front of the - counter thus making the counter eight bits bigger. The counter must - be at least 32-bits long to begin with. - -
- Counter $xx xx xx xx (xx ...) - - -4.17. Popularimeter - - The purpose of this frame is to specify how good an audio file is. - Many interesting applications could be found to this frame such as a - playlist that features better audio files more often than others or - it could be used to profile a person's taste and find other 'good' - files by comparing people's profiles. The frame contains the email - address to the user, one rating byte and a four byte play counter, - intended to be increased with one for every time the file is played. - The email is a terminated string. The rating is 1-255 where 1 is - worst and 255 is best. 0 is unknown. If no personal counter is wanted - it may be omitted. When the counter reaches all one's, one byte is - inserted in front of the counter thus making the counter eight bits - bigger in the same away as the play counter ("PCNT"). There may be - more than one "POPM" frame in each tag, but only one with the same - email address. - -
- Email to user $00 - Rating $xx - Counter $xx xx xx xx (xx ...) - - -4.18. Recommended buffer size - - Sometimes the server from which an audio file is streamed is aware of - transmission or coding problems resulting in interruptions in the - audio stream. In these cases, the size of the buffer can be - recommended by the server using this frame. If the 'embedded info - flag' is true (1) then this indicates that an ID3 tag with the - maximum size described in 'Buffer size' may occur in the audio - stream. In such case the tag should reside between two MPEG [MPEG] - frames, if the audio is MPEG encoded. If the position of the next tag - is known, 'offset to next tag' may be used. The offset is calculated - from the end of tag in which this frame resides to the first byte of - the header in the next. This field may be omitted. Embedded tags are - generally not recommended since this could render unpredictable - behaviour from present software/hardware. - - For applications like streaming audio it might be an idea to embed - tags into the audio stream though. If the clients connects to - individual connections like HTTP and there is a possibility to begin - every transmission with a tag, then this tag should include a - 'recommended buffer size' frame. If the client is connected to a - arbitrary point in the stream, such as radio or multicast, then the - 'recommended buffer size' frame SHOULD be included in every tag. - - The 'Buffer size' should be kept to a minimum. There may only be one - "RBUF" frame in each tag. - -
- Buffer size $xx xx xx - Embedded info flag %0000000x - Offset to next tag $xx xx xx xx - - -4.19. Audio encryption - - This frame indicates if the actual audio stream is encrypted, and by - whom. Since standardisation of such encryption scheme is beyond this - document, all "AENC" frames begin with a terminated string with a - URL containing an email address, or a link to a location where an - email address can be found, that belongs to the organisation - responsible for this specific encrypted audio file. Questions - regarding the encrypted audio should be sent to the email address - specified. If a $00 is found directly after the 'Frame size' and the - audio file indeed is encrypted, the whole file may be considered - useless. - - After the 'Owner identifier', a pointer to an unencrypted part of the - audio can be specified. The 'Preview start' and 'Preview length' is - described in frames. If no part is unencrypted, these fields should - be left zeroed. After the 'preview length' field follows optionally a - data block required for decryption of the audio. There may be more - than one "AENC" frames in a tag, but only one with the same 'Owner - identifier'. - -
- Owner identifier $00 - Preview start $xx xx - Preview length $xx xx - Encryption info - - -4.20. Linked information - - To keep information duplication as low as possible this frame may be - used to link information from another ID3v2 tag that might reside in - another audio file or alone in a binary file. It is RECOMMENDED that - this method is only used when the files are stored on a CD-ROM or - other circumstances when the risk of file separation is low. The - frame contains a frame identifier, which is the frame that should be - linked into this tag, a URL [URL] field, where a reference to the - file where the frame is given, and additional ID data, if needed. - Data should be retrieved from the first tag found in the file to - which this link points. There may be more than one "LINK" frame in a - tag, but only one with the same contents. A linked frame is to be - considered as part of the tag and has the same restrictions as if it - was a physical part of the tag (i.e. only one "RVRB" frame allowed, - whether it's linked or not). - -
- Frame identifier $xx xx xx xx - URL $00 - ID and additional data - - Frames that may be linked and need no additional data are "ASPI", - "ETCO", "EQU2", "MCID", "MLLT", "OWNE", "RVA2", "RVRB", "SYTC", the - text information frames and the URL link frames. - - The "AENC", "APIC", "GEOB" and "TXXX" frames may be linked with - the content descriptor as additional ID data. - - The "USER" frame may be linked with the language field as additional - ID data. - - The "PRIV" frame may be linked with the owner identifier as - additional ID data. - - The "COMM", "SYLT" and "USLT" frames may be linked with three bytes - of language descriptor directly followed by a content descriptor as - additional ID data. - - -4.21. Position synchronisation frame - - This frame delivers information to the listener of how far into the - audio stream he picked up; in effect, it states the time offset from - the first frame in the stream. The frame layout is: - - - Time stamp format $xx - Position $xx (xx ...) - - Where time stamp format is: - - $01 Absolute time, 32 bit sized, using MPEG frames as unit - $02 Absolute time, 32 bit sized, using milliseconds as unit - - and position is where in the audio the listener starts to receive, - i.e. the beginning of the next frame. If this frame is used in the - beginning of a file the value is always 0. There may only be one - "POSS" frame in each tag. - - -4.22. Terms of use frame - - This frame contains a brief description of the terms of use and - ownership of the file. More detailed information concerning the legal - terms might be available through the "WCOP" frame. Newlines are - allowed in the text. There may be more than one 'Terms of use' frame - in a tag, but only one with the same 'Language'. - -
- Text encoding $xx - Language $xx xx xx - The actual text - - -4.23. Ownership frame - - The ownership frame might be used as a reminder of a made transaction - or, if signed, as proof. Note that the "USER" and "TOWN" frames are - good to use in conjunction with this one. The frame begins, after the - frame ID, size and encoding fields, with a 'price paid' field. The - first three characters of this field contains the currency used for - the transaction, encoded according to ISO 4217 [ISO-4217] alphabetic - currency code. Concatenated to this is the actual price paid, as a - numerical string using "." as the decimal separator. Next is an 8 - character date string (YYYYMMDD) followed by a string with the name - of the seller as the last field in the frame. There may only be one - "OWNE" frame in a tag. - -
- Text encoding $xx - Price paid $00 - Date of purch. - Seller - - -4.24. Commercial frame - - This frame enables several competing offers in the same tag by - bundling all needed information. That makes this frame rather complex - but it's an easier solution than if one tries to achieve the same - result with several frames. The frame begins, after the frame ID, - size and encoding fields, with a price string field. A price is - constructed by one three character currency code, encoded according - to ISO 4217 [ISO-4217] alphabetic currency code, followed by a - numerical value where "." is used as decimal separator. In the price - string several prices may be concatenated, separated by a "/" - character, but there may only be one currency of each type. - - The price string is followed by an 8 character date string in the - format YYYYMMDD, describing for how long the price is valid. After - that is a contact URL, with which the user can contact the seller, - followed by a one byte 'received as' field. It describes how the - audio is delivered when bought according to the following list: - - $00 Other - $01 Standard CD album with other songs - $02 Compressed audio on CD - $03 File over the Internet - $04 Stream over the Internet - $05 As note sheets - $06 As note sheets in a book with other sheets - $07 Music on other media - $08 Non-musical merchandise - - Next follows a terminated string with the name of the seller followed - by a terminated string with a short description of the product. The - last thing is the ability to include a company logotype. The first of - them is the 'Picture MIME type' field containing information about - which picture format is used. In the event that the MIME media type - name is omitted, "image/" will be implied. Currently only "image/png" - and "image/jpeg" are allowed. This format string is followed by the - binary picture data. This two last fields may be omitted if no - picture is attached. There may be more than one 'commercial frame' in - a tag, but no two may be identical. - -
- Text encoding $xx - Price string $00 - Valid until - Contact URL $00 - Received as $xx - Name of seller $00 (00) - Description $00 (00) - Picture MIME type $00 - Seller logo - - -4.25. Encryption method registration - - To identify with which method a frame has been encrypted the - encryption method must be registered in the tag with this frame. The - 'Owner identifier' is a null-terminated string with a URL [URL] - containing an email address, or a link to a location where an email - address can be found, that belongs to the organisation responsible - for this specific encryption method. Questions regarding the - encryption method should be sent to the indicated email address. The - 'Method symbol' contains a value that is associated with this method - throughout the whole tag, in the range $80-F0. All other values are - reserved. The 'Method symbol' may optionally be followed by - encryption specific data. There may be several "ENCR" frames in a tag - but only one containing the same symbol and only one containing the - same owner identifier. The method must be used somewhere in the tag. - See the description of the frame encryption flag in the ID3v2 - structure document [ID3v2-strct] for more information. - -
- Owner identifier $00 - Method symbol $xx - Encryption data - - -4.26. Group identification registration - - This frame enables grouping of otherwise unrelated frames. This can - be used when some frames are to be signed. To identify which frames - belongs to a set of frames a group identifier must be registered in - the tag with this frame. The 'Owner identifier' is a null-terminated - string with a URL [URL] containing an email address, or a link to a - location where an email address can be found, that belongs to the - organisation responsible for this grouping. Questions regarding the - grouping should be sent to the indicated email address. The 'Group - symbol' contains a value that associates the frame with this group - throughout the whole tag, in the range $80-F0. All other values are - reserved. The 'Group symbol' may optionally be followed by some group - specific data, e.g. a digital signature. There may be several "GRID" - frames in a tag but only one containing the same symbol and only one - containing the same owner identifier. The group symbol must be used - somewhere in the tag. See the description of the frame grouping flag - in the ID3v2 structure document [ID3v2-strct] for more information. - -
- Owner identifier $00 - Group symbol $xx - Group dependent data - - -4.27. Private frame - - This frame is used to contain information from a software producer - that its program uses and does not fit into the other frames. The - frame consists of an 'Owner identifier' string and the binary data. - The 'Owner identifier' is a null-terminated string with a URL [URL] - containing an email address, or a link to a location where an email - address can be found, that belongs to the organisation responsible - for the frame. Questions regarding the frame should be sent to the - indicated email address. The tag may contain more than one "PRIV" - frame but only with different contents. - -
- Owner identifier $00 - The private data - - -4.28. Signature frame - - This frame enables a group of frames, grouped with the 'Group - identification registration', to be signed. Although signatures can - reside inside the registration frame, it might be desired to store - the signature elsewhere, e.g. in watermarks. There may be more than - one 'signature frame' in a tag, but no two may be identical. - -
- Group symbol $xx - Signature - - -4.29. Seek frame - - This frame indicates where other tags in a file/stream can be found. - The 'minimum offset to next tag' is calculated from the end of this - tag to the beginning of the next. There may only be one 'seek frame' - in a tag. - -
- Minimum offset to next tag $xx xx xx xx - - -4.30. Audio seek point index - - Audio files with variable bit rates are intrinsically difficult to - deal with in the case of seeking within the file. The ASPI frame - makes seeking easier by providing a list a seek points within the - audio file. The seek points are a fractional offset within the audio - data, providing a starting point from which to find an appropriate - point to start decoding. The presence of an ASPI frame requires the - existence of a TLEN frame, indicating the duration of the file in - milliseconds. There may only be one 'audio seek point index' frame in - a tag. - -
- Indexed data start (S) $xx xx xx xx - Indexed data length (L) $xx xx xx xx - Number of index points (N) $xx xx - Bits per index point (b) $xx - - Then for every index point the following data is included; - - Fraction at index (Fi) $xx (xx) - - 'Indexed data start' is a byte offset from the beginning of the file. - 'Indexed data length' is the byte length of the audio data being - indexed. 'Number of index points' is the number of index points, as - the name implies. The recommended number is 100. 'Bits per index - point' is 8 or 16, depending on the chosen precision. 8 bits works - well for short files (less than 5 minutes of audio), while 16 bits is - advantageous for long files. 'Fraction at index' is the numerator of - the fraction representing a relative position in the data. The - denominator is 2 to the power of b. - - Here are the algorithms to be used in the calculation. The known data - must be the offset of the start of the indexed data (S), the offset - of the end of the indexed data (E), the number of index points (N), - the offset at index i (Oi). We calculate the fraction at index i - (Fi). - - Oi is the offset of the frame whose start is soonest after the point - for which the time offset is (i/N * duration). - - The frame data should be calculated as follows: - - Fi = Oi/L * 2^b (rounded down to the nearest integer) - - Offset calculation should be calculated as follows from data in the - frame: - - Oi = (Fi/2^b)*L (rounded up to the nearest integer) - - -5. Copyright - - Copyright (C) Martin Nilsson 2000. All Rights Reserved. - - This document and translations of it may be copied and furnished to - others, and derivative works that comment on or otherwise explain it - or assist in its implementation may be prepared, copied, published - and distributed, in whole or in part, without restriction of any - kind, provided that a reference to this document is included on all - such copies and derivative works. However, this document itself may - not be modified in any way and reissued as the original document. - - The limited permissions granted above are perpetual and will not be - revoked. - - This document and the information contained herein is provided on an - "AS IS" basis and THE AUTHORS DISCLAIMS ALL WARRANTIES, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF - THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED - WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. - - -6. References - - [CDDB] Compact Disc Data Base - - - - [ID3v2.3.0] Martin Nilsson, "ID3v2 informal standard". - - - - [ID3v2-strct] Martin Nilsson, - "ID3 tag version 2.4.0 - Main Structure" - - - - [ISO-639-2] ISO/FDIS 639-2. - Codes for the representation of names of languages, Part 2: Alpha-3 - code. Technical committee / subcommittee: TC 37 / SC 2 - - [ISO-4217] ISO 4217:1995. - Codes for the representation of currencies and funds. - Technical committee / subcommittee: TC 68 - - [ISO-8859-1] ISO/IEC DIS 8859-1. - 8-bit single-byte coded graphic character sets, Part 1: Latin - alphabet No. 1. Technical committee / subcommittee: JTC 1 / SC 2 - - [ISRC] ISO 3901:1986 - International Standard Recording Code (ISRC). - Technical committee / subcommittee: TC 46 / SC 9 - - [JFIF] JPEG File Interchange Format, version 1.02 - - - - [KEYWORDS] S. Bradner, 'Key words for use in RFCs to Indicate - Requirement Levels', RFC 2119, March 1997. - - - - [MIME] Freed, N. and N. Borenstein, "Multipurpose Internet Mail - Extensions (MIME) Part One: Format of Internet Message Bodies", - RFC 2045, November 1996. - - - - [MPEG] ISO/IEC 11172-3:1993. - Coding of moving pictures and associated audio for digital storage - media at up to about 1,5 Mbit/s, Part 3: Audio. - Technical committee / subcommittee: JTC 1 / SC 29 - and - ISO/IEC 13818-3:1995 - Generic coding of moving pictures and associated audio information, - Part 3: Audio. - Technical committee / subcommittee: JTC 1 / SC 29 - and - ISO/IEC DIS 13818-3 - Generic coding of moving pictures and associated audio information, - Part 3: Audio (Revision of ISO/IEC 13818-3:1995) - - - [PNG] Portable Network Graphics, version 1.0 - - - - [URL] T. Berners-Lee, L. Masinter & M. McCahill, "Uniform Resource - Locators (URL).", RFC 1738, December 1994. - - - - [ZLIB] P. Deutsch, Aladdin Enterprises & J-L. Gailly, "ZLIB - Compressed - Data Format Specification version 3.3", RFC 1950, May 1996. - - - - -7. Appendix - - -A. Appendix A - Genre List from ID3v1 - - The following genres is defined in ID3v1 - - 0.Blues - 1.Classic Rock - 2.Country - 3.Dance - 4.Disco - 5.Funk - 6.Grunge - 7.Hip-Hop - 8.Jazz - 9.Metal - 10.New Age - 11.Oldies - 12.Other - 13.Pop - 14.R&B - 15.Rap - 16.Reggae - 17.Rock - 18.Techno - 19.Industrial - 20.Alternative - 21.Ska - 22.Death Metal - 23.Pranks - 24.Soundtrack - 25.Euro-Techno - 26.Ambient - 27.Trip-Hop - 28.Vocal - 29.Jazz+Funk - 30.Fusion - 31.Trance - 32.Classical - 33.Instrumental - 34.Acid - 35.House - 36.Game - 37.Sound Clip - 38.Gospel - 39.Noise - 40.AlternRock - 41.Bass - 42.Soul - 43.Punk - 44.Space - 45.Meditative - 46.Instrumental Pop - 47.Instrumental Rock - 48.Ethnic - 49.Gothic - 50.Darkwave - 51.Techno-Industrial - 52.Electronic - 53.Pop-Folk - 54.Eurodance - 55.Dream - 56.Southern Rock - 57.Comedy - 58.Cult - 59.Gangsta - 60.Top 40 - 61.Christian Rap - 62.Pop/Funk - 63.Jungle - 64.Native American - 65.Cabaret - 66.New Wave - 67.Psychadelic - 68.Rave - 69.Showtunes - 70.Trailer - 71.Lo-Fi - 72.Tribal - 73.Acid Punk - 74.Acid Jazz - 75.Polka - 76.Retro - 77.Musical - 78.Rock & Roll - 79.Hard Rock - - -8. Author's Address - - Written by - - Martin Nilsson - Rydsvägen 246 C. 30 - SE-584 34 Linköping - Sweden - - Email: nilsson@id3.org diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2.4.0-structure.txt b/3rdparty/taglib/mpeg/id3v2/id3v2.4.0-structure.txt deleted file mode 100644 index 5fa156a0..00000000 --- a/3rdparty/taglib/mpeg/id3v2/id3v2.4.0-structure.txt +++ /dev/null @@ -1,733 +0,0 @@ - -Informal standard M. Nilsson -Document: id3v2.4.0-structure.txt 16 September 2001 - - - ID3 tag version 2.4.0 - Main Structure - -Status of this document - - This document is an informal standard and replaces the ID3v2.3.0 - standard [ID3v2]. A formal standard will use another revision number - even if the content is identical to document. The contents in this - document may change for clarifications but never for added or altered - functionallity. - - Distribution of this document is unlimited. - - -Abstract - - This document describes the main structure of ID3v2.4.0, which is a - revised version of the ID3v2 informal standard [ID3v2] version - 2.3.0. The ID3v2 offers a flexible way of storing audio meta - information within the audio file itself. The information may be - technical information, such as equalisation curves, as well as - title, performer, copyright etc. - - ID3v2.4.0 is meant to be as close as possible to ID3v2.3.0 in order - to allow for implementations to be revised as easily as possible. - - -1. Table of contents - - Status of this document - Abstract - 1. Table of contents - 2. Conventions in this document - 2. Standard overview - 3. ID3v2 overview - 3.1. ID3v2 header - 3.2. ID3v2 extended header - 3.3. Padding - 3.4. ID3v2 footer - 4. ID3v2 frames overview - 4.1. Frame header flags - 4.1.1. Frame status flags - 4.1.2. Frame format flags - 5. Tag location - 6. Unsynchronisation - 6.1. The unsynchronisation scheme - 6.2. Synchsafe integers - 7. Copyright - 8. References - 9. Author's Address - - -2. Conventions in this document - - Text within "" is a text string exactly as it appears in a tag. - Numbers preceded with $ are hexadecimal and numbers preceded with % - are binary. $xx is used to indicate a byte with unknown content. %x - is used to indicate a bit with unknown content. The most significant - bit (MSB) of a byte is called 'bit 7' and the least significant bit - (LSB) is called 'bit 0'. - - A tag is the whole tag described in this document. A frame is a block - of information in the tag. The tag consists of a header, frames and - optional padding. A field is a piece of information; one value, a - string etc. A numeric string is a string that consists of the - characters "0123456789" only. - - The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", - "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this - document are to be interpreted as described in RFC 2119 [KEYWORDS]. - - -3. ID3v2 overview - - ID3v2 is a general tagging format for audio, which makes it possible - to store meta data about the audio inside the audio file itself. The - ID3 tag described in this document is mainly targeted at files - encoded with MPEG-1/2 layer I, MPEG-1/2 layer II, MPEG-1/2 layer III - and MPEG-2.5, but may work with other types of encoded audio or as a - stand alone format for audio meta data. - - ID3v2 is designed to be as flexible and expandable as possible to - meet new meta information needs that might arise. To achieve that - ID3v2 is constructed as a container for several information blocks, - called frames, whose format need not be known to the software that - encounters them. At the start of every frame is an unique and - predefined identifier, a size descriptor that allows software to skip - unknown frames and a flags field. The flags describes encoding - details and if the frame should remain in the tag, should it be - unknown to the software, if the file is altered. - - The bitorder in ID3v2 is most significant bit first (MSB). The - byteorder in multibyte numbers is most significant byte first (e.g. - $12345678 would be encoded $12 34 56 78), also known as big endian - and network byte order. - - Overall tag structure: - - +-----------------------------+ - | Header (10 bytes) | - +-----------------------------+ - | Extended Header | - | (variable length, OPTIONAL) | - +-----------------------------+ - | Frames (variable length) | - +-----------------------------+ - | Padding | - | (variable length, OPTIONAL) | - +-----------------------------+ - | Footer (10 bytes, OPTIONAL) | - +-----------------------------+ - - In general, padding and footer are mutually exclusive. See details in - sections 3.3, 3.4 and 5. - - -3.1. ID3v2 header - - The first part of the ID3v2 tag is the 10 byte tag header, laid out - as follows: - - ID3v2/file identifier "ID3" - ID3v2 version $04 00 - ID3v2 flags %abcd0000 - ID3v2 size 4 * %0xxxxxxx - - The first three bytes of the tag are always "ID3", to indicate that - this is an ID3v2 tag, directly followed by the two version bytes. The - first byte of ID3v2 version is its major version, while the second - byte is its revision number. In this case this is ID3v2.4.0. All - revisions are backwards compatible while major versions are not. If - software with ID3v2.4.0 and below support should encounter version - five or higher it should simply ignore the whole tag. Version or - revision will never be $FF. - - The version is followed by the ID3v2 flags field, of which currently - four flags are used. - - - a - Unsynchronisation - - Bit 7 in the 'ID3v2 flags' indicates whether or not - unsynchronisation is applied on all frames (see section 6.1 for - details); a set bit indicates usage. - - - b - Extended header - - The second bit (bit 6) indicates whether or not the header is - followed by an extended header. The extended header is described in - section 3.2. A set bit indicates the presence of an extended - header. - - - c - Experimental indicator - - The third bit (bit 5) is used as an 'experimental indicator'. This - flag SHALL always be set when the tag is in an experimental stage. - - - d - Footer present - - Bit 4 indicates that a footer (section 3.4) is present at the very - end of the tag. A set bit indicates the presence of a footer. - - - All the other flags MUST be cleared. If one of these undefined flags - are set, the tag might not be readable for a parser that does not - know the flags function. - - The ID3v2 tag size is stored as a 32 bit synchsafe integer (section - 6.2), making a total of 28 effective bits (representing up to 256MB). - - The ID3v2 tag size is the sum of the byte length of the extended - header, the padding and the frames after unsynchronisation. If a - footer is present this equals to ('total size' - 20) bytes, otherwise - ('total size' - 10) bytes. - - An ID3v2 tag can be detected with the following pattern: - $49 44 33 yy yy xx zz zz zz zz - Where yy is less than $FF, xx is the 'flags' byte and zz is less than - $80. - - -3.2. Extended header - - The extended header contains information that can provide further - insight in the structure of the tag, but is not vital to the correct - parsing of the tag information; hence the extended header is - optional. - - Extended header size 4 * %0xxxxxxx - Number of flag bytes $01 - Extended Flags $xx - - Where the 'Extended header size' is the size of the whole extended - header, stored as a 32 bit synchsafe integer. An extended header can - thus never have a size of fewer than six bytes. - - The extended flags field, with its size described by 'number of flag - bytes', is defined as: - - %0bcd0000 - - Each flag that is set in the extended header has data attached, which - comes in the order in which the flags are encountered (i.e. the data - for flag 'b' comes before the data for flag 'c'). Unset flags cannot - have any attached data. All unknown flags MUST be unset and their - corresponding data removed when a tag is modified. - - Every set flag's data starts with a length byte, which contains a - value between 0 and 127 ($00 - $7f), followed by data that has the - field length indicated by the length byte. If a flag has no attached - data, the value $00 is used as length byte. - - - b - Tag is an update - - If this flag is set, the present tag is an update of a tag found - earlier in the present file or stream. If frames defined as unique - are found in the present tag, they are to override any - corresponding ones found in the earlier tag. This flag has no - corresponding data. - - Flag data length $00 - - c - CRC data present - - If this flag is set, a CRC-32 [ISO-3309] data is included in the - extended header. The CRC is calculated on all the data between the - header and footer as indicated by the header's tag length field, - minus the extended header. Note that this includes the padding (if - there is any), but excludes the footer. The CRC-32 is stored as an - 35 bit synchsafe integer, leaving the upper four bits always - zeroed. - - Flag data length $05 - Total frame CRC 5 * %0xxxxxxx - - d - Tag restrictions - - For some applications it might be desired to restrict a tag in more - ways than imposed by the ID3v2 specification. Note that the - presence of these restrictions does not affect how the tag is - decoded, merely how it was restricted before encoding. If this flag - is set the tag is restricted as follows: - - Flag data length $01 - Restrictions %ppqrrstt - - p - Tag size restrictions - - 00 No more than 128 frames and 1 MB total tag size. - 01 No more than 64 frames and 128 KB total tag size. - 10 No more than 32 frames and 40 KB total tag size. - 11 No more than 32 frames and 4 KB total tag size. - - q - Text encoding restrictions - - 0 No restrictions - 1 Strings are only encoded with ISO-8859-1 [ISO-8859-1] or - UTF-8 [UTF-8]. - - r - Text fields size restrictions - - 00 No restrictions - 01 No string is longer than 1024 characters. - 10 No string is longer than 128 characters. - 11 No string is longer than 30 characters. - - Note that nothing is said about how many bytes is used to - represent those characters, since it is encoding dependent. If a - text frame consists of more than one string, the sum of the - strungs is restricted as stated. - - s - Image encoding restrictions - - 0 No restrictions - 1 Images are encoded only with PNG [PNG] or JPEG [JFIF]. - - t - Image size restrictions - - 00 No restrictions - 01 All images are 256x256 pixels or smaller. - 10 All images are 64x64 pixels or smaller. - 11 All images are exactly 64x64 pixels, unless required - otherwise. - - -3.3. Padding - - It is OPTIONAL to include padding after the final frame (at the end - of the ID3 tag), making the size of all the frames together smaller - than the size given in the tag header. A possible purpose of this - padding is to allow for adding a few additional frames or enlarge - existing frames within the tag without having to rewrite the entire - file. The value of the padding bytes must be $00. A tag MUST NOT have - any padding between the frames or between the tag header and the - frames. Furthermore it MUST NOT have any padding when a tag footer is - added to the tag. - - -3.4. ID3v2 footer - - To speed up the process of locating an ID3v2 tag when searching from - the end of a file, a footer can be added to the tag. It is REQUIRED - to add a footer to an appended tag, i.e. a tag located after all - audio data. The footer is a copy of the header, but with a different - identifier. - - ID3v2 identifier "3DI" - ID3v2 version $04 00 - ID3v2 flags %abcd0000 - ID3v2 size 4 * %0xxxxxxx - - -4. ID3v2 frame overview - - All ID3v2 frames consists of one frame header followed by one or more - fields containing the actual information. The header is always 10 - bytes and laid out as follows: - - Frame ID $xx xx xx xx (four characters) - Size 4 * %0xxxxxxx - Flags $xx xx - - The frame ID is made out of the characters capital A-Z and 0-9. - Identifiers beginning with "X", "Y" and "Z" are for experimental - frames and free for everyone to use, without the need to set the - experimental bit in the tag header. Bear in mind that someone else - might have used the same identifier as you. All other identifiers are - either used or reserved for future use. - - The frame ID is followed by a size descriptor containing the size of - the data in the final frame, after encryption, compression and - unsynchronisation. The size is excluding the frame header ('total - frame size' - 10 bytes) and stored as a 32 bit synchsafe integer. - - In the frame header the size descriptor is followed by two flag - bytes. These flags are described in section 4.1. - - There is no fixed order of the frames' appearance in the tag, - although it is desired that the frames are arranged in order of - significance concerning the recognition of the file. An example of - such order: UFID, TIT2, MCDI, TRCK ... - - A tag MUST contain at least one frame. A frame must be at least 1 - byte big, excluding the header. - - If nothing else is said, strings, including numeric strings and URLs - [URL], are represented as ISO-8859-1 [ISO-8859-1] characters in the - range $20 - $FF. Such strings are represented in frame descriptions - as , or if newlines are allowed. If - nothing else is said newline character is forbidden. In ISO-8859-1 a - newline is represented, when allowed, with $0A only. - - Frames that allow different types of text encoding contains a text - encoding description byte. Possible encodings: - - $00 ISO-8859-1 [ISO-8859-1]. Terminated with $00. - $01 UTF-16 [UTF-16] encoded Unicode [UNICODE] with BOM. All - strings in the same frame SHALL have the same byteorder. - Terminated with $00 00. - $02 UTF-16BE [UTF-16] encoded Unicode [UNICODE] without BOM. - Terminated with $00 00. - $03 UTF-8 [UTF-8] encoded Unicode [UNICODE]. Terminated with $00. - - Strings dependent on encoding are represented in frame descriptions - as , or if newlines are allowed. Any empty strings of - type $01 which are NULL-terminated may have the Unicode BOM followed - by a Unicode NULL ($FF FE 00 00 or $FE FF 00 00). - - The timestamp fields are based on a subset of ISO 8601. When being as - precise as possible the format of a time string is - yyyy-MM-ddTHH:mm:ss (year, "-", month, "-", day, "T", hour (out of - 24), ":", minutes, ":", seconds), but the precision may be reduced by - removing as many time indicators as wanted. Hence valid timestamps - are - yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddTHH, yyyy-MM-ddTHH:mm and - yyyy-MM-ddTHH:mm:ss. All time stamps are UTC. For durations, use - the slash character as described in 8601, and for multiple non- - contiguous dates, use multiple strings, if allowed by the frame - definition. - - The three byte language field, present in several frames, is used to - describe the language of the frame's content, according to ISO-639-2 - [ISO-639-2]. The language should be represented in lower case. If the - language is not known the string "XXX" should be used. - - All URLs [URL] MAY be relative, e.g. "picture.png", "../doc.txt". - - If a frame is longer than it should be, e.g. having more fields than - specified in this document, that indicates that additions to the - frame have been made in a later version of the ID3v2 standard. This - is reflected by the revision number in the header of the tag. - - -4.1. Frame header flags - - In the frame header the size descriptor is followed by two flag - bytes. All unused flags MUST be cleared. The first byte is for - 'status messages' and the second byte is a format description. If an - unknown flag is set in the first byte the frame MUST NOT be changed - without that bit cleared. If an unknown flag is set in the second - byte the frame is likely to not be readable. Some flags in the second - byte indicates that extra information is added to the header. These - fields of extra information is ordered as the flags that indicates - them. The flags field is defined as follows (l and o left out because - ther resemblence to one and zero): - - %0abc0000 %0h00kmnp - - Some frame format flags indicate that additional information fields - are added to the frame. This information is added after the frame - header and before the frame data in the same order as the flags that - indicates them. I.e. the four bytes of decompressed size will precede - the encryption method byte. These additions affects the 'frame size' - field, but are not subject to encryption or compression. - - The default status flags setting for a frame is, unless stated - otherwise, 'preserved if tag is altered' and 'preserved if file is - altered', i.e. %00000000. - - -4.1.1. Frame status flags - - a - Tag alter preservation - - This flag tells the tag parser what to do with this frame if it is - unknown and the tag is altered in any way. This applies to all - kinds of alterations, including adding more padding and reordering - the frames. - - 0 Frame should be preserved. - 1 Frame should be discarded. - - - b - File alter preservation - - This flag tells the tag parser what to do with this frame if it is - unknown and the file, excluding the tag, is altered. This does not - apply when the audio is completely replaced with other audio data. - - 0 Frame should be preserved. - 1 Frame should be discarded. - - - c - Read only - - This flag, if set, tells the software that the contents of this - frame are intended to be read only. Changing the contents might - break something, e.g. a signature. If the contents are changed, - without knowledge of why the frame was flagged read only and - without taking the proper means to compensate, e.g. recalculating - the signature, the bit MUST be cleared. - - -4.1.2. Frame format flags - - h - Grouping identity - - This flag indicates whether or not this frame belongs in a group - with other frames. If set, a group identifier byte is added to the - frame. Every frame with the same group identifier belongs to the - same group. - - 0 Frame does not contain group information - 1 Frame contains group information - - - k - Compression - - This flag indicates whether or not the frame is compressed. - A 'Data Length Indicator' byte MUST be included in the frame. - - 0 Frame is not compressed. - 1 Frame is compressed using zlib [zlib] deflate method. - If set, this requires the 'Data Length Indicator' bit - to be set as well. - - - m - Encryption - - This flag indicates whether or not the frame is encrypted. If set, - one byte indicating with which method it was encrypted will be - added to the frame. See description of the ENCR frame for more - information about encryption method registration. Encryption - should be done after compression. Whether or not setting this flag - requires the presence of a 'Data Length Indicator' depends on the - specific algorithm used. - - 0 Frame is not encrypted. - 1 Frame is encrypted. - - n - Unsynchronisation - - This flag indicates whether or not unsynchronisation was applied - to this frame. See section 6 for details on unsynchronisation. - If this flag is set all data from the end of this header to the - end of this frame has been unsynchronised. Although desirable, the - presence of a 'Data Length Indicator' is not made mandatory by - unsynchronisation. - - 0 Frame has not been unsynchronised. - 1 Frame has been unsyrchronised. - - p - Data length indicator - - This flag indicates that a data length indicator has been added to - the frame. The data length indicator is the value one would write - as the 'Frame length' if all of the frame format flags were - zeroed, represented as a 32 bit synchsafe integer. - - 0 There is no Data Length Indicator. - 1 A data length Indicator has been added to the frame. - - -5. Tag location - - The default location of an ID3v2 tag is prepended to the audio so - that players can benefit from the information when the data is - streamed. It is however possible to append the tag, or make a - prepend/append combination. When deciding upon where an unembedded - tag should be located, the following order of preference SHOULD be - considered. - - 1. Prepend the tag. - - 2. Prepend a tag with all vital information and add a second tag at - the end of the file, before tags from other tagging systems. The - first tag is required to have a SEEK frame. - - 3. Add a tag at the end of the file, before tags from other tagging - systems. - - In case 2 and 3 the tag can simply be appended if no other known tags - are present. The suggested method to find ID3v2 tags are: - - 1. Look for a prepended tag using the pattern found in section 3.1. - - 2. If a SEEK frame was found, use its values to guide further - searching. - - 3. Look for a tag footer, scanning from the back of the file. - - For every new tag that is found, the old tag should be discarded - unless the update flag in the extended header (section 3.2) is set. - - -6. Unsynchronisation - - The only purpose of unsynchronisation is to make the ID3v2 tag as - compatible as possible with existing software and hardware. There is - no use in 'unsynchronising' tags if the file is only to be processed - only by ID3v2 aware software and hardware. Unsynchronisation is only - useful with tags in MPEG 1/2 layer I, II and III, MPEG 2.5 and AAC - files. - - -6.1. The unsynchronisation scheme - - Whenever a false synchronisation is found within the tag, one zeroed - byte is inserted after the first false synchronisation byte. The - format of synchronisations that should be altered by ID3 encoders is - as follows: - - %11111111 111xxxxx - - and should be replaced with: - - %11111111 00000000 111xxxxx - - This has the side effect that all $FF 00 combinations have to be - altered, so they will not be affected by the decoding process. - Therefore all the $FF 00 combinations have to be replaced with the - $FF 00 00 combination during the unsynchronisation. - - To indicate usage of the unsynchronisation, the unsynchronisation - flag in the frame header should be set. This bit MUST be set if the - frame was altered by the unsynchronisation and SHOULD NOT be set if - unaltered. If all frames in the tag are unsynchronised the - unsynchronisation flag in the tag header SHOULD be set. It MUST NOT - be set if the tag has a frame which is not unsynchronised. - - Assume the first byte of the audio to be $FF. The special case when - the last byte of the last frame is $FF and no padding nor footer is - used will then introduce a false synchronisation. This can be solved - by adding a footer, adding padding or unsynchronising the frame and - add $00 to the end of the frame data, thus adding more byte to the - frame size than a normal unsynchronisation would. Although not - preferred, it is allowed to apply the last method on all frames - ending with $FF. - - It is preferred that the tag is either completely unsynchronised or - not unsynchronised at all. A completely unsynchronised tag has no - false synchonisations in it, as defined above, and does not end with - $FF. A completely non-unsynchronised tag contains no unsynchronised - frames, and thus the unsynchronisation flag in the header is cleared. - - Do bear in mind, that if compression or encryption is used, the - unsynchronisation scheme MUST be applied afterwards. When decoding an - unsynchronised frame, the unsynchronisation scheme MUST be reversed - first, encryption and decompression afterwards. - - -6.2. Synchsafe integers - - In some parts of the tag it is inconvenient to use the - unsychronisation scheme because the size of unsynchronised data is - not known in advance, which is particularly problematic with size - descriptors. The solution in ID3v2 is to use synchsafe integers, in - which there can never be any false synchs. Synchsafe integers are - integers that keep its highest bit (bit 7) zeroed, making seven bits - out of eight available. Thus a 32 bit synchsafe integer can store 28 - bits of information. - - Example: - - 255 (%11111111) encoded as a 16 bit synchsafe integer is 383 - (%00000001 01111111). - - -7. Copyright - - Copyright (C) Martin Nilsson 2000. All Rights Reserved. - - This document and translations of it may be copied and furnished to - others, and derivative works that comment on or otherwise explain it - or assist in its implementation may be prepared, copied, published - and distributed, in whole or in part, without restriction of any - kind, provided that a reference to this document is included on all - such copies and derivative works. However, this document itself may - not be modified in any way and reissued as the original document. - - The limited permissions granted above are perpetual and will not be - revoked. - - This document and the information contained herein is provided on an - 'AS IS' basis and THE AUTHORS DISCLAIMS ALL WARRANTIES, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF - THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED - WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. - - -8. References - - [ID3v2] Martin Nilsson, 'ID3v2 informal standard'. - - - - [ISO-639-2] ISO/FDIS 639-2. - 'Codes for the representation of names of languages, Part 2: Alpha-3 - code.' Technical committee / subcommittee: TC 37 / SC 2 - - [ISO-3309] ISO 3309 - 'Information Processing Systems--Data Communication High-Level Data - Link Control Procedure--Frame Structure', IS 3309, October 1984, 3rd - Edition. - - [ISO-8859-1] ISO/IEC DIS 8859-1. - '8-bit single-byte coded graphic character sets, Part 1: Latin - alphabet No. 1.' Technical committee / subcommittee: JTC 1 / SC 2 - - [JFIF] 'JPEG File Interchange Format, version 1.02' - - - - [KEYWORDS] S. Bradner, 'Key words for use in RFCs to Indicate - Requirement Levels', RFC 2119, March 1997. - - - - [MPEG] ISO/IEC 11172-3:1993. - 'Coding of moving pictures and associated audio for digital storage - media at up to about 1,5 Mbit/s, Part 3: Audio.' - Technical committee / subcommittee: JTC 1 / SC 29 - and - ISO/IEC 13818-3:1995 - 'Generic coding of moving pictures and associated audio information, - Part 3: Audio.' - Technical committee / subcommittee: JTC 1 / SC 29 - and - ISO/IEC DIS 13818-3 - 'Generic coding of moving pictures and associated audio information, - Part 3: Audio (Revision of ISO/IEC 13818-3:1995)' - - [PNG] 'Portable Network Graphics, version 1.0' - - - - [UNICODE] The Unicode Consortium, - 'The Unicode Standard Version 3.0', ISBN 0-201-61633-5. - - - - [URL] T. Berners-Lee, L. Masinter & M. McCahill, 'Uniform Resource - Locators (URL)', RFC 1738, December 1994. - - - - [UTF-8] F. Yergeau, 'UTF-8, a transformation format of ISO 10646', - RFC 2279, January 1998. - - - - [UTF-16] F. Yergeau, 'UTF-16, an encoding of ISO 10646', RFC 2781, - February 2000. - - - - [ZLIB] P. Deutsch, Aladdin Enterprises & J-L. Gailly, 'ZLIB - Compressed Data Format Specification version 3.3', RFC 1950, - May 1996. - - - - -9. Author's Address - - Written by - - Martin Nilsson - Rydsvägen 246 C. 30 - SE-584 34 Linköping - Sweden - - Email: nilsson@id3.org - diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2.h b/3rdparty/taglib/mpeg/id3v2/id3v2.h deleted file mode 100644 index c528f004..00000000 --- a/3rdparty/taglib/mpeg/id3v2/id3v2.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef TAGLIB_ID3V2_H -#define TAGLIB_ID3V2_H - -namespace Strawberry_TagLib { -namespace TagLib { -//! An ID3v2 implementation - -/*! - * This is a relatively complete and flexible framework for working with ID3v2 - * tags. - * - * \see ID3v2::Tag - */ -namespace ID3v2 { -/*! - * Used to specify which version of the ID3 standard to use when saving tags. - */ -enum Version { - v3 = 3, //size; -} - -void ExtendedHeader::setData(const ByteVector &data) { - parse(data); -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void ExtendedHeader::parse(const ByteVector &data) { - d->size = SynchData::toUInt(data.mid(0, 4)); // (structure 3.2 "Extended header size") -} diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2extendedheader.h b/3rdparty/taglib/mpeg/id3v2/id3v2extendedheader.h deleted file mode 100644 index 636832b1..00000000 --- a/3rdparty/taglib/mpeg/id3v2/id3v2extendedheader.h +++ /dev/null @@ -1,92 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_ID3V2EXTENDEDHEADER_H -#define TAGLIB_ID3V2EXTENDEDHEADER_H - -#include "taglib_export.h" -#include "tbytevector.h" -#include "taglib.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -//! ID3v2 extended header implementation - -/*! - * This class implements ID3v2 extended headers. - * It attempts to follow, both semantically and programmatically, - * the structure specified in the ID3v2 standard. - * The API is based on the properties of ID3v2 extended headers specified there. - * If any of the terms used in this documentation are unclear please check the specification in the linked section. - * (Structure, 3.2) - */ - -class TAGLIB_EXPORT ExtendedHeader { - public: - /*! - * Constructs an empty ID3v2 extended header. - */ - explicit ExtendedHeader(); - - /*! - * Destroys the extended header. - */ - virtual ~ExtendedHeader(); - - /*! - * Returns the size of the extended header. This is variable for the extended header. - */ - unsigned int size() const; - - /*! - * Sets the data that will be used as the extended header. - * Since the length is not known before the extended header has been parsed, - * this should just be a pointer to the first byte of the extended header. - * It will determine the length internally and make that available through size(). - */ - void setData(const ByteVector &data); - - protected: - /*! - * Called by setData() to parse the extended header data. - * It makes this information available through the public API. - */ - void parse(const ByteVector &data); - - private: - ExtendedHeader(const ExtendedHeader&); - ExtendedHeader &operator=(const ExtendedHeader&); - - class ExtendedHeaderPrivate; - ExtendedHeaderPrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2footer.cpp b/3rdparty/taglib/mpeg/id3v2/id3v2footer.cpp deleted file mode 100644 index adc9bb44..00000000 --- a/3rdparty/taglib/mpeg/id3v2/id3v2footer.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "id3v2footer.h" -#include "id3v2header.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -class Footer::FooterPrivate {}; - -Footer::Footer() : d(nullptr) {} - -Footer::~Footer() {} - -unsigned int Footer::size() { - return 10; -} - -ByteVector Footer::render(const Header *header) const { - ByteVector headerData = header->render(); - headerData[0] = '3'; - headerData[1] = 'D'; - headerData[2] = 'I'; - return headerData; -} diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2footer.h b/3rdparty/taglib/mpeg/id3v2/id3v2footer.h deleted file mode 100644 index 2d297fa7..00000000 --- a/3rdparty/taglib/mpeg/id3v2/id3v2footer.h +++ /dev/null @@ -1,81 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_ID3V2FOOTER_H -#define TAGLIB_ID3V2FOOTER_H - -#include "taglib_export.h" -#include "tbytevector.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -class Header; - -//! ID3v2 footer implementation - -/*! - * Per the ID3v2 specification, the tag's footer is just a copy of the information in the header. - * As such there is no API for reading the data from the header, - * it can just as easily be done from the header. - * - * In fact, at this point, TagLib does not even parse the footer since it is not useful internally. - * However, if the flag to include a footer has been set in the ID3v2::Tag, TagLib will render a footer. - */ - -class TAGLIB_EXPORT Footer { - public: - /*! - * Constructs an empty ID3v2 footer. - */ - explicit Footer(); - /*! - * Destroys the footer. - */ - virtual ~Footer(); - - /*! - * Returns the size of the footer. Presently this is always 10 bytes. - */ - static unsigned int size(); - - /*! - * Renders the footer based on the data in \a header. - */ - ByteVector render(const Header *header) const; - - private: - Footer(const Footer&); - Footer &operator=(const Footer&); - - class FooterPrivate; - FooterPrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2frame.cpp b/3rdparty/taglib/mpeg/id3v2/id3v2frame.cpp deleted file mode 100644 index 643b50f0..00000000 --- a/3rdparty/taglib/mpeg/id3v2/id3v2frame.cpp +++ /dev/null @@ -1,733 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "tdebug.h" -#include "tstringlist.h" -#include "tzlib.h" - -#include "id3v2tag.h" -#include "id3v2frame.h" -#include "id3v2synchdata.h" - -#include "tpropertymap.h" -#include "frames/textidentificationframe.h" -#include "frames/urllinkframe.h" -#include "frames/unsynchronizedlyricsframe.h" -#include "frames/commentsframe.h" -#include "frames/uniquefileidentifierframe.h" -#include "frames/unknownframe.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -class Frame::FramePrivate { - public: - explicit FramePrivate() : header(nullptr) {} - - ~FramePrivate() { - delete header; - } - - Frame::Header *header; -}; - -namespace { -bool isValidFrameID(const ByteVector &frameID) { - - if (frameID.size() != 4) - return false; - - for (ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) { - if ((*it < 'A' || *it > 'Z') && (*it < '0' || *it > '9')) { - return false; - } - } - return true; - -} -} // namespace - -//////////////////////////////////////////////////////////////////////////////// -// static methods -//////////////////////////////////////////////////////////////////////////////// - -unsigned int Frame::headerSize(unsigned int version) { - return Header::size(version); -} - -ByteVector Frame::textDelimiter(String::Type t) { - if (t == String::UTF16 || t == String::UTF16BE || t == String::UTF16LE) - return ByteVector(2, '\0'); - else - return ByteVector(1, '\0'); -} - -const String Frame::instrumentPrefix("PERFORMER:"); -const String Frame::commentPrefix("COMMENT:"); -const String Frame::lyricsPrefix("LYRICS:"); -const String Frame::urlPrefix("URL:"); - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -Frame *Frame::createTextualFrame(const String &key, const StringList &values) { //static - - // check if the key is contained in the key<=>frameID mapping - ByteVector frameID = keyToFrameID(key); - if (!frameID.isEmpty()) { - // Apple proprietary WFED (Podcast URL), MVNM (Movement Name), MVIN (Movement Number), GRP1 (Grouping) are in fact text frames. - if (frameID[0] == 'T' || frameID == "WFED" || frameID == "MVNM" || frameID == "MVIN" || frameID == "GRP1") { // text frame - TextIdentificationFrame *frame = new TextIdentificationFrame(frameID, String::UTF8); - frame->setText(values); - return frame; - } - else if ((frameID[0] == 'W') && (values.size() == 1)) { // URL frame (not WXXX); support only one value - UrlLinkFrame *frame = new UrlLinkFrame(frameID); - frame->setUrl(values.front()); - return frame; - } - } - if (key == "MUSICBRAINZ_TRACKID" && values.size() == 1) { - UniqueFileIdentifierFrame *frame = new UniqueFileIdentifierFrame("http://musicbrainz.org", values.front().data(String::UTF8)); - return frame; - } - // now we check if it's one of the "special" cases: - // -LYRICS: depending on the number of values, use USLT or TXXX (with description=LYRICS) - if ((key == "LYRICS" || key.startsWith(lyricsPrefix)) && values.size() == 1) { - UnsynchronizedLyricsFrame *frame = new UnsynchronizedLyricsFrame(String::UTF8); - frame->setDescription(key == "LYRICS" ? key : key.substr(lyricsPrefix.size())); - frame->setText(values.front()); - return frame; - } - // -URL: depending on the number of values, use WXXX or TXXX (with description=URL) - if ((key == "URL" || key.startsWith(urlPrefix)) && values.size() == 1) { - UserUrlLinkFrame *frame = new UserUrlLinkFrame(String::UTF8); - frame->setDescription(key == "URL" ? key : key.substr(urlPrefix.size())); - frame->setUrl(values.front()); - return frame; - } - // -COMMENT: depending on the number of values, use COMM or TXXX (with description=COMMENT) - if ((key == "COMMENT" || key.startsWith(commentPrefix)) && values.size() == 1) { - CommentsFrame *frame = new CommentsFrame(String::UTF8); - if (key != "COMMENT") { - frame->setDescription(key.substr(commentPrefix.size())); - } - frame->setText(values.front()); - return frame; - } - // if non of the above cases apply, we use a TXXX frame with the key as description - return new UserTextIdentificationFrame(keyToTXXX(key), values, String::UTF8); - -} - -Frame::~Frame() { - delete d; -} - -ByteVector Frame::frameID() const { - - if (d->header) - return d->header->frameID(); - else - return ByteVector(); - -} - -unsigned int Frame::size() const { - - if (d->header) - return d->header->frameSize(); - else - return 0; - -} - -void Frame::setData(const ByteVector &data) { - parse(data); -} - -void Frame::setText(const String &) { -} - -ByteVector Frame::render() const { - - ByteVector fieldData = renderFields(); - d->header->setFrameSize(fieldData.size()); - ByteVector headerData = d->header->render(); - - return headerData + fieldData; - -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -Frame::Frame(const ByteVector &data) : d(new FramePrivate()) { - d->header = new Header(data); -} - -Frame::Frame(Header *h) : d(new FramePrivate()) { - d->header = h; -} - -Frame::Header *Frame::header() const { - return d->header; -} - -void Frame::setHeader(Header *h, bool deleteCurrent) { - if (deleteCurrent) - delete d->header; - - d->header = h; -} - -void Frame::parse(const ByteVector &data) { - if (d->header) - d->header->setData(data); - else - d->header = new Header(data); - - parseFields(fieldData(data)); -} - -ByteVector Frame::fieldData(const ByteVector &frameData) const { - - const size_t headerSize = Header::size(d->header->version()); - - size_t frameDataOffset = headerSize; - size_t frameDataLength = size(); - - if (d->header->compression() || d->header->dataLengthIndicator()) { - frameDataLength = SynchData::toUInt(frameData.mid(headerSize, 4)); - frameDataOffset += 4; - } - - if (zlib::isAvailable() && d->header->compression() && !d->header->encryption()) { - if (frameData.size() <= frameDataOffset) { - debug("Compressed frame doesn't have enough data to decode"); - return ByteVector(); - } - - const ByteVector outData = zlib::decompress(frameData.mid(frameDataOffset)); - if (!outData.isEmpty() && frameDataLength != outData.size()) { - debug("frameDataLength does not match the data length returned by zlib"); - } - - return outData; - } - - return frameData.mid(frameDataOffset, frameDataLength); - -} - -String Frame::readStringField(const ByteVector &data, String::Type encoding, size_t &position) { - - ByteVector delimiter = textDelimiter(encoding); - - const size_t end = data.find(delimiter, position, delimiter.size()); - - if (end == ByteVector::npos() || end < position) - return String(); - - String str; - if (encoding == String::Latin1) - str = Tag::latin1StringHandler()->parse(data.mid(position, end - position)); - else - str = String(data.mid(position, end - position), encoding); - - position = end + delimiter.size(); - - return str; - -} - -String::Type Frame::checkEncoding(const StringList &fields, String::Type encoding, unsigned int version) const { - - if ((encoding == String::UTF8 || encoding == String::UTF16BE) && version != 4) - return String::UTF16; - - if (encoding != String::Latin1) - return encoding; - - for (StringList::ConstIterator it = fields.begin(); it != fields.end(); ++it) { - if (!(*it).isLatin1()) { - if (version == 4) { - debug("Frame::checkEncoding() -- Rendering using UTF8."); - return String::UTF8; - } - else { - debug("Frame::checkEncoding() -- Rendering using UTF16."); - return String::UTF16; - } - } - } - - return String::Latin1; - -} - -String::Type Frame::checkTextEncoding(const StringList &fields, String::Type encoding) const { - return checkEncoding(fields, encoding, header()->version()); -} - -namespace { -const char *frameTranslation[][2] = { - // Text information frames - { "TALB", "ALBUM" }, - { "TBPM", "BPM" }, - { "TCOM", "COMPOSER" }, - { "TCON", "GENRE" }, - { "TCOP", "COPYRIGHT" }, - { "TDEN", "ENCODINGTIME" }, - { "TDLY", "PLAYLISTDELAY" }, - { "TDOR", "ORIGINALDATE" }, - { "TDRC", "DATE" }, - // { "TRDA", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4 - // { "TDAT", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4 - // { "TYER", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4 - // { "TIME", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4 - { "TDRL", "RELEASEDATE" }, - { "TDTG", "TAGGINGDATE" }, - { "TENC", "ENCODEDBY" }, - { "TEXT", "LYRICIST" }, - { "TFLT", "FILETYPE" }, - //{ "TIPL", "INVOLVEDPEOPLE" }, handled separately - { "TIT1", "CONTENTGROUP" }, - { "TIT2", "TITLE" }, - { "TIT3", "SUBTITLE" }, - { "TKEY", "INITIALKEY" }, - { "TLAN", "LANGUAGE" }, - { "TLEN", "LENGTH" }, - //{ "TMCL", "MUSICIANCREDITS" }, handled separately - { "TMED", "MEDIA" }, - { "TMOO", "MOOD" }, - { "TOAL", "ORIGINALALBUM" }, - { "TOFN", "ORIGINALFILENAME" }, - { "TOLY", "ORIGINALLYRICIST" }, - { "TOPE", "ORIGINALARTIST" }, - { "TOWN", "OWNER" }, - { "TPE1", "ARTIST" }, - { "TPE2", "ALBUMARTIST" }, // id3's spec says 'PERFORMER', but most programs use 'ALBUMARTIST' - { "TPE3", "CONDUCTOR" }, - { "TPE4", "REMIXER" }, // could also be ARRANGER - { "TPOS", "DISCNUMBER" }, - { "TPRO", "PRODUCEDNOTICE" }, - { "TPUB", "LABEL" }, - { "TRCK", "TRACKNUMBER" }, - { "TRSN", "RADIOSTATION" }, - { "TRSO", "RADIOSTATIONOWNER" }, - { "TSOA", "ALBUMSORT" }, - { "TSOP", "ARTISTSORT" }, - { "TSOT", "TITLESORT" }, - { "TSO2", "ALBUMARTISTSORT" }, // non-standard, used by iTunes - { "TSRC", "ISRC" }, - { "TSSE", "ENCODING" }, - // URL frames - { "WCOP", "COPYRIGHTURL" }, - { "WOAF", "FILEWEBPAGE" }, - { "WOAR", "ARTISTWEBPAGE" }, - { "WOAS", "AUDIOSOURCEWEBPAGE" }, - { "WORS", "RADIOSTATIONWEBPAGE" }, - { "WPAY", "PAYMENTWEBPAGE" }, - { "WPUB", "PUBLISHERWEBPAGE" }, - //{ "WXXX", "URL"}, handled specially - // Other frames - { "COMM", "COMMENT" }, - //{ "USLT", "LYRICS" }, handled specially - // Apple iTunes proprietary frames - { "PCST", "PODCAST" }, - { "TCAT", "PODCASTCATEGORY" }, - { "TDES", "PODCASTDESC" }, - { "TGID", "PODCASTID" }, - { "WFED", "PODCASTURL" }, - { "MVNM", "MOVEMENTNAME" }, - { "MVIN", "MOVEMENTNUMBER" }, - { "GRP1", "GROUPING" }, -}; -const size_t frameTranslationSize = sizeof(frameTranslation) / sizeof(frameTranslation[0]); - -const char *txxxFrameTranslation[][2] = { - { "MUSICBRAINZ ALBUM ID", "MUSICBRAINZ_ALBUMID" }, - { "MUSICBRAINZ ARTIST ID", "MUSICBRAINZ_ARTISTID" }, - { "MUSICBRAINZ ALBUM ARTIST ID", "MUSICBRAINZ_ALBUMARTISTID" }, - { "MUSICBRAINZ RELEASE GROUP ID", "MUSICBRAINZ_RELEASEGROUPID" }, - { "MUSICBRAINZ WORK ID", "MUSICBRAINZ_WORKID" }, - { "ACOUSTID ID", "ACOUSTID_ID" }, - { "ACOUSTID FINGERPRINT", "ACOUSTID_FINGERPRINT" }, - { "MUSICIP PUID", "MUSICIP_PUID" }, -}; -const size_t txxxFrameTranslationSize = sizeof(txxxFrameTranslation) / sizeof(txxxFrameTranslation[0]); - -// list of deprecated frames and their successors -const char *deprecatedFrames[][2] = { - { "TRDA", "TDRC" }, // 2.3 -> 2.4 (http://en.wikipedia.org/wiki/ID3) - { "TDAT", "TDRC" }, // 2.3 -> 2.4 - { "TYER", "TDRC" }, // 2.3 -> 2.4 - { "TIME", "TDRC" }, // 2.3 -> 2.4 -}; -const size_t deprecatedFramesSize = sizeof(deprecatedFrames) / sizeof(deprecatedFrames[0]); -} // namespace - -String Frame::frameIDToKey(const ByteVector &id) { - ByteVector id24 = id; - for (size_t i = 0; i < deprecatedFramesSize; ++i) { - if (id24 == deprecatedFrames[i][0]) { - id24 = deprecatedFrames[i][1]; - break; - } - } - for (size_t i = 0; i < frameTranslationSize; ++i) { - if (id24 == frameTranslation[i][0]) - return frameTranslation[i][1]; - } - return String(); -} - -ByteVector Frame::keyToFrameID(const String &s) { - const String key = s.upper(); - for (size_t i = 0; i < frameTranslationSize; ++i) { - if (key == frameTranslation[i][1]) - return frameTranslation[i][0]; - } - return ByteVector(); -} - -String Frame::txxxToKey(const String &description) { - const String d = description.upper(); - for (size_t i = 0; i < txxxFrameTranslationSize; ++i) { - if (d == txxxFrameTranslation[i][0]) - return txxxFrameTranslation[i][1]; - } - return d; -} - -String Frame::keyToTXXX(const String &s) { - const String key = s.upper(); - for (size_t i = 0; i < txxxFrameTranslationSize; ++i) { - if (key == txxxFrameTranslation[i][1]) - return txxxFrameTranslation[i][0]; - } - return s; -} - -PropertyMap Frame::asProperties() const { - - PropertyMap m; - m.unsupportedData().append(frameID()); - return m; - -} - -void Frame::splitProperties(const PropertyMap &original, PropertyMap &singleFrameProperties, PropertyMap &tiplProperties, PropertyMap &tmclProperties) { - - singleFrameProperties.clear(); - tiplProperties.clear(); - tmclProperties.clear(); - for (PropertyMap::ConstIterator it = original.begin(); it != original.end(); ++it) { - if (TextIdentificationFrame::involvedPeopleMap().contains(it->first)) - tiplProperties.insert(it->first, it->second); - else if (it->first.startsWith(TextIdentificationFrame::instrumentPrefix)) - tmclProperties.insert(it->first, it->second); - else - singleFrameProperties.insert(it->first, it->second); - } - -} - -//////////////////////////////////////////////////////////////////////////////// -// Frame::Header class -//////////////////////////////////////////////////////////////////////////////// - -class Frame::Header::HeaderPrivate { - public: - HeaderPrivate() : frameSize(0), - version(4), - tagAlterPreservation(false), - fileAlterPreservation(false), - readOnly(false), - groupingIdentity(false), - compression(false), - encryption(false), - unsynchronisation(false), - dataLengthIndicator(false) { - } - - ByteVector frameID; - unsigned int frameSize; - unsigned int version; - - // flags - - bool tagAlterPreservation; - bool fileAlterPreservation; - bool readOnly; - bool groupingIdentity; - bool compression; - bool encryption; - bool unsynchronisation; - bool dataLengthIndicator; -}; - -//////////////////////////////////////////////////////////////////////////////// -// static members (Frame::Header) -//////////////////////////////////////////////////////////////////////////////// - -unsigned int Frame::Header::size(unsigned int version) { - switch (version) { - case 0: - case 1: - case 2: - return 6; - case 3: - case 4: - default: - return 10; - } -} - -//////////////////////////////////////////////////////////////////////////////// -// public members (Frame::Header) -//////////////////////////////////////////////////////////////////////////////// - -Frame::Header::Header(const ByteVector &data, unsigned int version) : d(new HeaderPrivate()) { - setData(data, version); -} - -Frame::Header::~Header() { - delete d; -} - -void Frame::Header::setData(const ByteVector &data, bool synchSafeInts) { - setData(data, static_cast(synchSafeInts ? 4 : 3)); -} - -void Frame::Header::setData(const ByteVector &data, unsigned int version) { - - d->version = version; - - switch (version) { - case 0: - case 1: - case 2: { - // ID3v2.2 - - if (data.size() < 3) { - debug("You must at least specify a frame ID."); - return; - } - - // Set the frame ID -- the first three bytes - - d->frameID = data.mid(0, 3); - - // If the full header information was not passed in, do not continue to the - // steps to parse the frame size and flags. - - if (data.size() < 6) { - d->frameSize = 0; - return; - } - - d->frameSize = data.toUInt24BE(3); - - break; - } - case 3: { - // ID3v2.3 - - if (data.size() < 4) { - debug("You must at least specify a frame ID."); - return; - } - - // Set the frame ID -- the first four bytes - - d->frameID = data.mid(0, 4); - - // If the full header information was not passed in, do not continue to the - // steps to parse the frame size and flags. - - if (data.size() < 10) { - d->frameSize = 0; - return; - } - - // Set the size -- the frame size is the four bytes starting at byte four in - // the frame header (structure 4) - - d->frameSize = data.toUInt32BE(4); - - { // read the first byte of flags - std::bitset<8> flags(data[8]); - d->tagAlterPreservation = flags[7]; // (structure 3.3.1.a) - d->fileAlterPreservation = flags[6]; // (structure 3.3.1.b) - d->readOnly = flags[5]; // (structure 3.3.1.c) - } - - { // read the second byte of flags - std::bitset<8> flags(data[9]); - d->compression = flags[7]; // (structure 3.3.1.i) - d->encryption = flags[6]; // (structure 3.3.1.j) - d->groupingIdentity = flags[5]; // (structure 3.3.1.k) - } - break; - } - case 4: - default: { - // ID3v2.4 - - if (data.size() < 4) { - debug("You must at least specify a frame ID."); - return; - } - - // Set the frame ID -- the first four bytes - - d->frameID = data.mid(0, 4); - - // If the full header information was not passed in, do not continue to the - // steps to parse the frame size and flags. - - if (data.size() < 10) { - d->frameSize = 0; - return; - } - - // Set the size -- the frame size is the four bytes starting at byte four in - // the frame header (structure 4) - - d->frameSize = SynchData::toUInt(data.mid(4, 4)); -#ifndef NO_ITUNES_HACKS - // iTunes writes v2.4 tags with v2.3-like frame sizes - if (d->frameSize > 127) { - if (!isValidFrameID(data.mid(d->frameSize + 10, 4))) { - const unsigned int uintSize = data.toUInt32BE(4); - if (isValidFrameID(data.mid(uintSize + 10, 4))) { - d->frameSize = uintSize; - } - } - } -#endif - - { // read the first byte of flags - std::bitset<8> flags(data[8]); - d->tagAlterPreservation = flags[6]; // (structure 4.1.1.a) - d->fileAlterPreservation = flags[5]; // (structure 4.1.1.b) - d->readOnly = flags[4]; // (structure 4.1.1.c) - } - - { // read the second byte of flags - std::bitset<8> flags(data[9]); - d->groupingIdentity = flags[6]; // (structure 4.1.2.h) - d->compression = flags[3]; // (structure 4.1.2.k) - d->encryption = flags[2]; // (structure 4.1.2.m) - d->unsynchronisation = flags[1]; // (structure 4.1.2.n) - d->dataLengthIndicator = flags[0]; // (structure 4.1.2.p) - } - break; - } - } - -} - -ByteVector Frame::Header::frameID() const { - return d->frameID; -} - -void Frame::Header::setFrameID(const ByteVector &id) { - d->frameID = id.mid(0, 4); -} - -unsigned int Frame::Header::frameSize() const { - return d->frameSize; -} - -void Frame::Header::setFrameSize(unsigned int size) { - d->frameSize = size; -} - -unsigned int Frame::Header::version() const { - return d->version; -} - -void Frame::Header::setVersion(unsigned int version) { - d->version = version; -} - -bool Frame::Header::tagAlterPreservation() const { - return d->tagAlterPreservation; -} - -void Frame::Header::setTagAlterPreservation(bool preserve) { - d->tagAlterPreservation = preserve; -} - -bool Frame::Header::fileAlterPreservation() const { - return d->fileAlterPreservation; -} - -bool Frame::Header::readOnly() const { - return d->readOnly; -} - -bool Frame::Header::groupingIdentity() const { - return d->groupingIdentity; -} - -bool Frame::Header::compression() const { - return d->compression; -} - -bool Frame::Header::encryption() const { - return d->encryption; -} - -bool Frame::Header::unsycronisation() const { - return unsynchronisation(); -} - -bool Frame::Header::unsynchronisation() const { - return d->unsynchronisation; -} - -bool Frame::Header::dataLengthIndicator() const { - return d->dataLengthIndicator; -} - -ByteVector Frame::Header::render() const { - - ByteVector flags(2, char(0)); // just blank for the moment - - ByteVector v = d->frameID + - (d->version == 3 ? ByteVector::fromUInt32BE(d->frameSize) : SynchData::fromUInt(d->frameSize)) + - flags; - - return v; - -} diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2frame.h b/3rdparty/taglib/mpeg/id3v2/id3v2frame.h deleted file mode 100644 index b578cde9..00000000 --- a/3rdparty/taglib/mpeg/id3v2/id3v2frame.h +++ /dev/null @@ -1,439 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_ID3V2FRAME_H -#define TAGLIB_ID3V2FRAME_H - -#include "tstring.h" -#include "tbytevector.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -class StringList; -class PropertyMap; - -namespace ID3v2 { - -class Tag; -class FrameFactory; - -//! ID3v2 frame implementation - -/*! - * This class is the main ID3v2 frame implementation. - * In ID3v2, a tag is split between a collection of frames (which are in turn split into fields - * (Structure, 4) - * (Frames). - * This class provides an API for gathering information about and modifying ID3v2 frames. - * Funtionallity specific to a given frame type is handed in one of the many subclasses. - */ - -class TAGLIB_EXPORT Frame { - friend class Tag; - friend class FrameFactory; - - public: - /*! - * Creates a textual frame which corresponds to a single key in the PropertyMap interface. - * These are all (User)TextIdentificationFrames except TIPL and TMCL, all (User)URLLinkFrames, CommentsFrames, and UnsynchronizedLyricsFrame. - */ - static Frame *createTextualFrame(const String &key, const StringList &values); - - /*! - * Destroys this Frame instance. - */ - virtual ~Frame(); - - /*! - * Returns the Frame ID (Structure, 4) - * (Frames, 4) - */ - ByteVector frameID() const; - - /*! - * Returns the size of the frame. - */ - unsigned int size() const; - - /*! - * Returns the size of the frame header for the given ID3v2 version. - * - * \deprecated Please see the explanation above. - */ - // BIC: remove - static unsigned int headerSize(unsigned int version); - - /*! - * Sets the data that will be used as the frame. - * Since the length is not known before the frame has been parsed, - * this should just be a pointer to the first byte of the frame. - * It will determine the length internally and make that available through size(). - */ - void setData(const ByteVector &data); - - /*! - * Set the text of frame in the sanest way possible. - * This should only be reimplemented in frames where there is some logical mapping to text. - * - * \note If the frame type supports multiple text encodings, - * this will not change the text encoding of the frame; the string will be converted to that frame's encoding. - * Please use the specific APIs of the frame types to set the encoding if that is desired. - */ - virtual void setText(const String &text); - - /*! - * This returns the textual representation of the data in the frame. - * Subclasses must reimplement this method to provide a string representation of the frame's data. - */ - virtual String toString() const = 0; - - /*! - * Render the frame back to its binary format in a ByteVector. - */ - ByteVector render() const; - - /*! - * Returns the text delimiter that is used between fields for the string type \a t. - */ - static ByteVector textDelimiter(String::Type t); - - /*! - * The string with which an instrument name is prefixed to build a key in a PropertyMap; used to translate PropertyMaps to TMCL frames. - * In the current implementation, this is "PERFORMER:". - */ - static const String instrumentPrefix; - /*! - * The PropertyMap key prefix which triggers the use of a COMM frame instead of a TXXX frame for a non-standard key. - * In the current implementation, this is "COMMENT:". - */ - static const String commentPrefix; - /*! - * The PropertyMap key prefix which triggers the use of a USLT frame instead of a TXXX frame for a non-standard key. - * In the current implementation, this is "LYRICS:". - */ - static const String lyricsPrefix; - /*! - * The PropertyMap key prefix which triggers the use of a WXXX frame instead of a TXX frame for a non-standard key. - * In the current implementation, this is "URL:". - */ - static const String urlPrefix; - - protected: - class Header; - - /*! - * Constructs an ID3v2 frame using \a data to read the header information. - * All other processing of \a data should be handled in a subclass. - * - * \note This need not contain anything more than a frame ID, but \e must contain at least that. - */ - explicit Frame(const ByteVector &data); - - /*! - * This creates an Frame using the header \a h. - * - * The ownership of this header will be assigned to the frame and the header will be deleted when the frame is destroyed. - */ - explicit Frame(Header *h); - - /*! - * Returns a pointer to the frame header. - */ - Header *header() const; - - /*! - * Sets the header to \a h. If \a deleteCurrent is true, this will free the memory of the current header. - * - * The ownership of this header will be assigned to the frame and the header will be deleted when the frame is destroyed. - */ - void setHeader(Header *h, bool deleteCurrent = true); - - /*! - * Called by setData() to parse the frame data. It makes this information available through the public API. - */ - void parse(const ByteVector &data); - - /*! - * Called by parse() to parse the field data. - * It makes this information available through the public API. This must be overridden by the subclasses. - */ - virtual void parseFields(const ByteVector &data) = 0; - - /*! - * Render the field data back to a binary format in a ByteVector. This must be overridden by subclasses. - */ - virtual ByteVector renderFields() const = 0; - - /*! - * Returns a ByteVector containing the field data given the frame data. - * This correctly adjusts for the header size plus any additional frame data that's specified in the frame header flags. - */ - ByteVector fieldData(const ByteVector &frameData) const; - - /*! - * Reads a String of type \a encoding from the ByteVector \a data. - * If \a position is passed in it is used both as the starting point and is updated to return the position just after the string that has been read. - * This is useful for reading strings sequentially. - */ - String readStringField(const ByteVector &data, String::Type encoding, size_t &position); - - /*! - * Checks a the list of string values to see if they can be used with the specified encoding and returns the recommended encoding. - * This method also checks the ID3v2 version and makes sure the encoding can be used in the specified version. - */ - String::Type checkEncoding(const StringList &fields, String::Type encoding, unsigned int version) const; - - /*! - * Checks a the list of string values to see if they can be used with the specified encoding and returns the recommended encoding. - * This method also checks the ID3v2 version and makes sure the encoding can be used in the version specified by the frame's header. - */ - String::Type checkTextEncoding(const StringList &fields, String::Type encoding) const; - - - /*! - * Parses the contents of this frame as PropertyMap. - * If that fails, the returned PropertyMap will be empty, and its unsupportedData() will contain this frame's ID. - */ - virtual PropertyMap asProperties() const; - - /*! - * Returns an appropriate ID3 frame ID for the given free-form tag key. This method - * will return an empty ByteVector if no specialized translation is found. - */ - static ByteVector keyToFrameID(const String &); - - /*! - * Returns a free-form tag name for the given ID3 frame ID. - * Note that this does not work for general frame IDs such as TXXX or WXXX; in such a case an empty string is returned. - */ - static String frameIDToKey(const ByteVector &); - - /*! - * Returns an appropriate TXXX frame description for the given free-form tag key. - */ - static String keyToTXXX(const String &); - - /*! - * Returns a free-form tag name for the given ID3 frame description. - */ - static String txxxToKey(const String &); - - /*! - * This helper function splits the PropertyMap \a original into three ProperytMaps - * \a singleFrameProperties, \a tiplProperties, and \a tmclProperties, such that: - * - \a singleFrameProperties contains only of keys which can be represented with exactly one ID3 frame per key. - * In the current implementation this is everything except for the fixed "involved people" keys and keys of the - * form "TextIdentificationFrame::instrumentPrefix" + "instrument", which are mapped to a TMCL frame. - * - \a tiplProperties will consist of those keys that are present in TextIdentificationFrame::involvedPeopleMap() - * - \a tmclProperties contains the "musician credits" keys which should be mapped to a TMCL frame - */ - static void splitProperties(const PropertyMap &original, PropertyMap &singleFrameProperties, PropertyMap &tiplProperties, PropertyMap &tmclProperties); - - private: - Frame(const Frame&); - Frame &operator=(const Frame&); - - class FramePrivate; - friend class FramePrivate; - FramePrivate *d; -}; - -//! ID3v2 frame header implementation - -/*! - * The ID3v2 Frame Header (Structure, 4) - * - * Every ID3v2::Frame has an associated header that gives some general - * properties of the frame and also makes it possible to identify the frame type. - * - * As such when reading an ID3v2 tag ID3v2::FrameFactory first creates the - * frame headers and then creates the appropriate Frame subclass based on the type and attaches the header. - */ - -class TAGLIB_EXPORT Frame::Header { - public: - /*! - * Construct a Frame Header based on \a data. \a data must at least - * contain a 4 byte frame ID, and optionally can contain flag data and the frame size. - * i.e. Just the frame id -- "TALB" -- is a valid value. - * - * \a version should be the ID3v2 version of the tag. - */ - explicit Header(const ByteVector &data, unsigned int version = 4); - - /*! - * Destroys this Header instance. - */ - virtual ~Header(); - - /*! - * Sets the data for the Header. - * - * \deprecated Please use the version below that accepts an ID3v2 version - * number. - */ - void setData(const ByteVector &data, bool synchSafeInts); - - /*! - * Sets the data for the Header. - * \a version should indicate the ID3v2 version number of the tag that this frame is contained in. - */ - void setData(const ByteVector &data, unsigned int version = 4); - - /*! - * Returns the Frame ID (Structure, 4) - * (Frames, 4) - */ - ByteVector frameID() const; - - /*! - * Sets the frame's ID to \a id. Only the first four bytes of \a id will be used. - * - * \warning This method should in general be avoided. It exists simply to - * provide a mechanism for transforming frames from a deprecated frame type - * to a newer one -- i.e. TYER to TDRC from ID3v2.3 to ID3v2.4. - */ - void setFrameID(const ByteVector &id); - - /*! - * Returns the size of the frame data portion, as set when setData() was called or set explicitly via setFrameSize(). - */ - unsigned int frameSize() const; - - /*! - * Sets the size of the frame data portion. - */ - void setFrameSize(unsigned int size); - - /*! - * Returns the ID3v2 version of the header, as passed in from the construction of the header or set via setVersion(). - */ - unsigned int version() const; - - /*! - * Sets the ID3v2 version of the header, changing has impact on the correct parsing/rendering of frame data. - */ - void setVersion(unsigned int version); - - /*! - * Returns the size of the frame header in bytes for the ID3v2 version - * that's given. - */ - // BIC: remove - static unsigned int size(unsigned int version = 4); - - /*! - * Returns true if the flag for tag alter preservation is set. - * - * The semantics are a little backwards from what would seem natural - * (setting the preservation flag to throw away the frame), but this - * follows the ID3v2 standard. - * - * \see setTagAlterPreservation() - */ - bool tagAlterPreservation() const; - - /*! - * Sets the flag for preservation of this frame if the tag is set. If - * this is set to true the frame will not be written when the tag is - * saved. - * - * The semantics are a little backwards from what would seem natural - * (setting the preservation flag to throw away the frame), but this - * follows the ID3v2 standard. - * - * \see tagAlterPreservation() - */ - void setTagAlterPreservation(bool preserve); - - /*! - * Returns true if the flag for file alter preservation is set. - * - * \note This flag is currently ignored internally in TagLib. - */ - bool fileAlterPreservation() const; - - /*! - * Returns true if the frame is meant to be read only. - * - * \note This flag is currently ignored internally in TagLib. - */ - bool readOnly() const; - - /*! - * Returns true if the flag for the grouping identity is set. - * - * \note This flag is currently ignored internally in TagLib. - */ - bool groupingIdentity() const; - - /*! - * Returns true if compression is enabled for this frame. - * - * \note This flag is currently ignored internally in TagLib. - */ - bool compression() const; - - /*! - * Returns true if encryption is enabled for this frame. - * - * \note This flag is currently ignored internally in TagLib. - */ - bool encryption() const; - -#ifndef DO_NOT_DOCUMENT - bool unsycronisation() const; -#endif - - /*! - * Returns true if unsynchronisation is enabled for this frame. - */ - bool unsynchronisation() const; - - /*! - * Returns true if the flag for a data length indicator is set. - */ - bool dataLengthIndicator() const; - - /*! - * Render the Header back to binary format in a ByteVector. - */ - ByteVector render() const; - - private: - Header(const Header&); - Header &operator=(const Header&); - - class HeaderPrivate; - HeaderPrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2framefactory.cpp b/3rdparty/taglib/mpeg/id3v2/id3v2framefactory.cpp deleted file mode 100644 index b7f3411e..00000000 --- a/3rdparty/taglib/mpeg/id3v2/id3v2framefactory.cpp +++ /dev/null @@ -1,518 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tdebug.h" -#include "tzlib.h" - -#include "id3v2framefactory.h" -#include "id3v2synchdata.h" -#include "id3v1genres.h" - -#include "frames/attachedpictureframe.h" -#include "frames/commentsframe.h" -#include "frames/relativevolumeframe.h" -#include "frames/textidentificationframe.h" -#include "frames/uniquefileidentifierframe.h" -#include "frames/unknownframe.h" -#include "frames/generalencapsulatedobjectframe.h" -#include "frames/urllinkframe.h" -#include "frames/unsynchronizedlyricsframe.h" -#include "frames/popularimeterframe.h" -#include "frames/privateframe.h" -#include "frames/ownershipframe.h" -#include "frames/synchronizedlyricsframe.h" -#include "frames/eventtimingcodesframe.h" -#include "frames/chapterframe.h" -#include "frames/tableofcontentsframe.h" -#include "frames/podcastframe.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -namespace { -void updateGenre(TextIdentificationFrame *frame) { - - StringList fields = frame->fieldList(); - StringList newfields; - - for (StringList::ConstIterator it = fields.begin(); it != fields.end(); ++it) { - String s = *it; - size_t end = s.find(")"); - - if (s.startsWith("(") && end > 0) { - // "(12)Genre" - String text = s.substr(end + 1); - bool ok; - int number = s.substr(1, end - 1).toInt(&ok); - if (ok && number >= 0 && number <= 255 && !(ID3v1::genre(number) == text)) - newfields.append(s.substr(1, end - 1)); - if (!text.isEmpty()) - newfields.append(text); - } - else { - // "Genre" or "12" - newfields.append(s); - } - } - - if (newfields.isEmpty()) - fields.append(String()); - - frame->setText(newfields); - -} -} // namespace - -class FrameFactory::FrameFactoryPrivate { - public: - explicit FrameFactoryPrivate() : defaultEncoding(String::Latin1), useDefaultEncoding(false) {} - - String::Type defaultEncoding; - bool useDefaultEncoding; - - template void setTextEncoding(T *frame) { - if (useDefaultEncoding) - frame->setTextEncoding(defaultEncoding); - } -}; - -FrameFactory FrameFactory::factory; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -FrameFactory *FrameFactory::instance() { - return &factory; -} - -Frame *FrameFactory::createFrame(const ByteVector &origData, const Header *tagHeader) const { - - ByteVector data = origData; - unsigned int version = tagHeader->majorVersion(); - Frame::Header *header = new Frame::Header(data, version); - ByteVector frameID = header->frameID(); - - // A quick sanity check -- make sure that the frameID is 4 uppercase Latin1 - // characters. Also make sure that there is data in the frame. - - if (frameID.size() != (version < 3 ? 3 : 4) || - header->frameSize() <= static_cast(header->dataLengthIndicator() ? 4 : 0) || - header->frameSize() > data.size()) { - delete header; - return nullptr; - } - -#ifndef NO_ITUNES_HACKS - if (version == 3 && frameID.size() == 4 && frameID[3] == '\0') { - // iTunes v2.3 tags store v2.2 frames - convert now - frameID = frameID.mid(0, 3); - header->setFrameID(frameID); - header->setVersion(2); - updateFrame(header); - header->setVersion(3); - } -#endif - - for (ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) { - if ((*it < 'A' || *it > 'Z') && (*it < '0' || *it > '9')) { - delete header; - return nullptr; - } - } - - if (version > 3 && (tagHeader->unsynchronisation() || header->unsynchronisation())) { - // Data lengths are not part of the encoded data, but since they are synch-safe - // integers they will be never actually encoded. - ByteVector frameData = data.mid(Frame::Header::size(version), header->frameSize()); - frameData = SynchData::decode(frameData); - data = data.mid(0, Frame::Header::size(version)) + frameData; - } - - // TagLib doesn't mess with encrypted frames, so just treat them - // as unknown frames. - - if (!zlib::isAvailable() && header->compression()) { - debug("Compressed frames are currently not supported."); - return new UnknownFrame(data, header); - } - - if (header->encryption()) { - debug("Encrypted frames are currently not supported."); - return new UnknownFrame(data, header); - } - - if (!updateFrame(header)) { - header->setTagAlterPreservation(true); - return new UnknownFrame(data, header); - } - - // updateFrame() might have updated the frame ID. - - frameID = header->frameID(); - - // This is where things get necissarily nasty. Here we determine which - // Frame subclass (or if none is found simply an Frame) based - // on the frame ID. Since there are a lot of possibilities, that means - // a lot of if blocks. - - // Text Identification (frames 4.2) - - // Apple proprietary WFED (Podcast URL), MVNM (Movement Name), MVIN (Movement Number), GRP1 (Grouping) are in fact text frames. - if (frameID.startsWith("T") || frameID == "WFED" || frameID == "MVNM" || frameID == "MVIN" || frameID == "GRP1") { - - TextIdentificationFrame *f = frameID != "TXXX" ? new TextIdentificationFrame(data, header) : new UserTextIdentificationFrame(data, header); - - d->setTextEncoding(f); - - if (frameID == "TCON") - updateGenre(f); - - return f; - } - - // Comments (frames 4.10) - - if (frameID == "COMM") { - CommentsFrame *f = new CommentsFrame(data, header); - d->setTextEncoding(f); - return f; - } - - // Attached Picture (frames 4.14) - - if (frameID == "APIC") { - AttachedPictureFrame *f = new AttachedPictureFrame(data, header); - d->setTextEncoding(f); - return f; - } - - // ID3v2.2 Attached Picture - - if (frameID == "PIC") { - AttachedPictureFrame *f = new AttachedPictureFrameV22(data, header); - d->setTextEncoding(f); - return f; - } - - // Relative Volume Adjustment (frames 4.11) - - if (frameID == "RVA2") - return new RelativeVolumeFrame(data, header); - - // Unique File Identifier (frames 4.1) - - if (frameID == "UFID") - return new UniqueFileIdentifierFrame(data, header); - - // General Encapsulated Object (frames 4.15) - - if (frameID == "GEOB") { - GeneralEncapsulatedObjectFrame *f = new GeneralEncapsulatedObjectFrame(data, header); - d->setTextEncoding(f); - return f; - } - - // URL link (frames 4.3) - - if (frameID.startsWith("W")) { - if (frameID != "WXXX") { - return new UrlLinkFrame(data, header); - } - else { - UserUrlLinkFrame *f = new UserUrlLinkFrame(data, header); - d->setTextEncoding(f); - return f; - } - } - - // Unsynchronized lyric/text transcription (frames 4.8) - - if (frameID == "USLT") { - UnsynchronizedLyricsFrame *f = new UnsynchronizedLyricsFrame(data, header); - if (d->useDefaultEncoding) - f->setTextEncoding(d->defaultEncoding); - return f; - } - - // Synchronised lyrics/text (frames 4.9) - - if (frameID == "SYLT") { - SynchronizedLyricsFrame *f = new SynchronizedLyricsFrame(data, header); - if (d->useDefaultEncoding) - f->setTextEncoding(d->defaultEncoding); - return f; - } - - // Event timing codes (frames 4.5) - - if (frameID == "ETCO") - return new EventTimingCodesFrame(data, header); - - // Popularimeter (frames 4.17) - - if (frameID == "POPM") - return new PopularimeterFrame(data, header); - - // Private (frames 4.27) - - if (frameID == "PRIV") - return new PrivateFrame(data, header); - - // Ownership (frames 4.22) - - if (frameID == "OWNE") { - OwnershipFrame *f = new OwnershipFrame(data, header); - d->setTextEncoding(f); - return f; - } - - // Chapter (ID3v2 chapters 1.0) - - if (frameID == "CHAP") - return new ChapterFrame(tagHeader, data, header); - - // Table of contents (ID3v2 chapters 1.0) - - if (frameID == "CTOC") - return new TableOfContentsFrame(tagHeader, data, header); - - // Apple proprietary PCST (Podcast) - - if (frameID == "PCST") - return new PodcastFrame(data, header); - - return new UnknownFrame(data, header); - -} - -void FrameFactory::rebuildAggregateFrames(ID3v2::Tag *tag) const { - - if (tag->header()->majorVersion() < 4 && - tag->frameList("TDRC").size() == 1 && - tag->frameList("TDAT").size() == 1) { - TextIdentificationFrame *tdrc = - dynamic_cast(tag->frameList("TDRC").front()); - UnknownFrame *tdat = static_cast(tag->frameList("TDAT").front()); - - if (tdrc && - tdrc->fieldList().size() == 1 && - tdrc->fieldList().front().size() == 4 && - tdat->data().size() >= 5) { - String date(tdat->data().mid(1), String::Type(tdat->data()[0])); - if (date.length() == 4) { - tdrc->setText(tdrc->toString() + '-' + date.substr(2, 2) + '-' + date.substr(0, 2)); - if (tag->frameList("TIME").size() == 1) { - UnknownFrame *timeframe = static_cast(tag->frameList("TIME").front()); - if (timeframe->data().size() >= 5) { - String time(timeframe->data().mid(1), String::Type(timeframe->data()[0])); - if (time.length() == 4) { - tdrc->setText(tdrc->toString() + 'T' + time.substr(0, 2) + ':' + time.substr(2, 2)); - } - } - } - } - } - } - -} - -String::Type FrameFactory::defaultTextEncoding() const { - return d->defaultEncoding; -} - -void FrameFactory::setDefaultTextEncoding(String::Type encoding) { - d->useDefaultEncoding = true; - d->defaultEncoding = encoding; -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -FrameFactory::FrameFactory() : d(new FrameFactoryPrivate()) { -} - -FrameFactory::~FrameFactory() { - delete d; -} - -namespace { -// Frame conversion table ID3v2.2 -> 2.4 -const char *frameConversion2[][2] = { - { "BUF", "RBUF" }, - { "CNT", "PCNT" }, - { "COM", "COMM" }, - { "CRA", "AENC" }, - { "ETC", "ETCO" }, - { "GEO", "GEOB" }, - { "IPL", "TIPL" }, - { "MCI", "MCDI" }, - { "MLL", "MLLT" }, - { "POP", "POPM" }, - { "REV", "RVRB" }, - { "SLT", "SYLT" }, - { "STC", "SYTC" }, - { "TAL", "TALB" }, - { "TBP", "TBPM" }, - { "TCM", "TCOM" }, - { "TCO", "TCON" }, - { "TCP", "TCMP" }, - { "TCR", "TCOP" }, - { "TDY", "TDLY" }, - { "TEN", "TENC" }, - { "TFT", "TFLT" }, - { "TKE", "TKEY" }, - { "TLA", "TLAN" }, - { "TLE", "TLEN" }, - { "TMT", "TMED" }, - { "TOA", "TOAL" }, - { "TOF", "TOFN" }, - { "TOL", "TOLY" }, - { "TOR", "TDOR" }, - { "TOT", "TOAL" }, - { "TP1", "TPE1" }, - { "TP2", "TPE2" }, - { "TP3", "TPE3" }, - { "TP4", "TPE4" }, - { "TPA", "TPOS" }, - { "TPB", "TPUB" }, - { "TRC", "TSRC" }, - { "TRD", "TDRC" }, - { "TRK", "TRCK" }, - { "TS2", "TSO2" }, - { "TSA", "TSOA" }, - { "TSC", "TSOC" }, - { "TSP", "TSOP" }, - { "TSS", "TSSE" }, - { "TST", "TSOT" }, - { "TT1", "TIT1" }, - { "TT2", "TIT2" }, - { "TT3", "TIT3" }, - { "TXT", "TOLY" }, - { "TXX", "TXXX" }, - { "TYE", "TDRC" }, - { "UFI", "UFID" }, - { "ULT", "USLT" }, - { "WAF", "WOAF" }, - { "WAR", "WOAR" }, - { "WAS", "WOAS" }, - { "WCM", "WCOM" }, - { "WCP", "WCOP" }, - { "WPB", "WPUB" }, - { "WXX", "WXXX" }, - - // Apple iTunes nonstandard frames - { "PCS", "PCST" }, - { "TCT", "TCAT" }, - { "TDR", "TDRL" }, - { "TDS", "TDES" }, - { "TID", "TGID" }, - { "WFD", "WFED" }, - { "MVN", "MVNM" }, - { "MVI", "MVIN" }, - { "GP1", "GRP1" }, -}; -const size_t frameConversion2Size = sizeof(frameConversion2) / sizeof(frameConversion2[0]); - -// Frame conversion table ID3v2.3 -> 2.4 -const char *frameConversion3[][2] = { - { "TORY", "TDOR" }, - { "TYER", "TDRC" }, - { "IPLS", "TIPL" }, -}; -const size_t frameConversion3Size = sizeof(frameConversion3) / sizeof(frameConversion3[0]); -} // namespace - -bool FrameFactory::updateFrame(Frame::Header *header) const { - - const ByteVector frameID = header->frameID(); - - switch (header->version()) { - - case 2: // ID3v2.2 - { - if (frameID == "CRM" || - frameID == "EQU" || - frameID == "LNK" || - frameID == "RVA" || - frameID == "TIM" || - frameID == "TSI" || - frameID == "TDA") { - debug("ID3v2.4 no longer supports the frame type " + String(frameID) + - ". It will be discarded from the tag."); - return false; - } - - // ID3v2.2 only used 3 bytes for the frame ID, so we need to convert all of - // the frames to their 4 byte ID3v2.4 equivalent. - - for (size_t i = 0; i < frameConversion2Size; ++i) { - if (frameID == frameConversion2[i][0]) { - header->setFrameID(frameConversion2[i][1]); - break; - } - } - - break; - } - - case 3: // ID3v2.3 - { - if (frameID == "EQUA" || - frameID == "RVAD" || - frameID == "TIME" || - frameID == "TRDA" || - frameID == "TSIZ" || - frameID == "TDAT") { - debug("ID3v2.4 no longer supports the frame type " + String(frameID) + - ". It will be discarded from the tag."); - return false; - } - - for (size_t i = 0; i < frameConversion3Size; ++i) { - if (frameID == frameConversion3[i][0]) { - header->setFrameID(frameConversion3[i][1]); - break; - } - } - - break; - } - - default: - - // This should catch a typo that existed in TagLib up to and including - // version 1.1 where TRDC was used for the year rather than TDRC. - - if (frameID == "TRDC") - header->setFrameID("TDRC"); - - break; - } - - return true; - -} diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2framefactory.h b/3rdparty/taglib/mpeg/id3v2/id3v2framefactory.h deleted file mode 100644 index ba0d943e..00000000 --- a/3rdparty/taglib/mpeg/id3v2/id3v2framefactory.h +++ /dev/null @@ -1,130 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_ID3V2FRAMEFACTORY_H -#define TAGLIB_ID3V2FRAMEFACTORY_H - -#include "taglib_export.h" -#include "tbytevector.h" -#include "id3v2frame.h" -#include "id3v2header.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -class TextIdentificationFrame; - -//! A factory for creating ID3v2 frames during parsing - -/*! - * This factory abstracts away the frame creation process and instantiates - * the appropriate ID3v2::Frame subclasses based on the contents of the data. - * - * Reimplementing this factory is the key to adding support for frame types not directly supported by TagLib to your application. - * To do so you would subclass this factory reimplement createFrame(). - * Then by setting your factory to be the default factory in ID3v2::Tag constructor you can - * implement behavior that will allow for new ID3v2::Frame subclasses (also provided by you) to be used. - * - * This implements both abstract factory and singleton patterns - * of which more information is available on the web and in software design textbooks (Notably Design Patters). - * - * \note You do not need to use this factory to create new frames to add to an ID3v2::Tag. - * You can instantiate frame subclasses directly (with new) and add them to a tag using ID3v2::Tag::addFrame() - * - * \see ID3v2::Tag::addFrame() - */ - -class TAGLIB_EXPORT FrameFactory { - public: - static FrameFactory *instance(); - /*! - * Create a frame based on \a data. \a tagHeader should be a valid ID3v2::Header instance. - */ - virtual Frame *createFrame(const ByteVector &data, const Header *tagHeader) const; - - /*! - * After a tag has been read, this tries to rebuild some of them information, - * most notably the recording date, from frames that have been deprecated and can't be upgraded directly. - */ - virtual void rebuildAggregateFrames(ID3v2::Tag *tag) const; - - /*! - * Returns the default text encoding for text frames. - * If setTextEncoding() has not been explicitly called this will only be used for new text frames. - * However, if this value has been set explicitly all frames will be converted to this type - * (unless it's explicitly set differently for the individual frame) when being rendered. - * - * \see setDefaultTextEncoding() - */ - String::Type defaultTextEncoding() const; - - /*! - * Set the default text encoding for all text frames that are created to \a encoding. - * If no value is set the frames with either default to the encoding type that was parsed and new frames default to Latin1. - * - * Valid string types for ID3v2 tags are Latin1, UTF8, UTF16 and UTF16BE. - * - * \see defaultTextEncoding() - */ - void setDefaultTextEncoding(String::Type encoding); - - protected: - /*! - * Constructs a frame factory. Because this is a singleton this method is protected, but may be used for subclasses. - */ - explicit FrameFactory(); - - /*! - * Destroys the frame factory. - */ - virtual ~FrameFactory(); - - /*! - * This method checks for compliance to the current ID3v2 standard (2.4) and does nothing in the common case. - * However if a frame is found that is not compatible with the current standard, - * this method either updates the frame or indicates that it should be discarded. - * - * This method with return true (with or without changes to the frame) if this frame should be kept or false if it should be discarded. - * - * See the id3v2.4.0-changes.txt document for further information. - */ - virtual bool updateFrame(Frame::Header *header) const; - - private: - FrameFactory(const FrameFactory&); - FrameFactory &operator=(const FrameFactory&); - - static FrameFactory factory; - - class FrameFactoryPrivate; - FrameFactoryPrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2header.cpp b/3rdparty/taglib/mpeg/id3v2/id3v2header.cpp deleted file mode 100644 index 87c8dbd5..00000000 --- a/3rdparty/taglib/mpeg/id3v2/id3v2header.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include -#include - -#include "tstring.h" -#include "tdebug.h" - -#include "id3v2header.h" -#include "id3v2footer.h" -#include "id3v2synchdata.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -class Header::HeaderPrivate { - public: - explicit HeaderPrivate() : majorVersion(4), - revisionNumber(0), - unsynchronisation(false), - extendedHeader(false), - experimentalIndicator(false), - footerPresent(false), - tagSize(0) {} - - unsigned int majorVersion; - unsigned int revisionNumber; - - bool unsynchronisation; - bool extendedHeader; - bool experimentalIndicator; - bool footerPresent; - - unsigned int tagSize; -}; - -//////////////////////////////////////////////////////////////////////////////// -// static members -//////////////////////////////////////////////////////////////////////////////// - -unsigned int Header::size() { - return 10; -} - -ByteVector Header::fileIdentifier() { - return ByteVector::fromCString("ID3"); -} - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -Header::Header() : d(new HeaderPrivate()) { -} - -Header::Header(const ByteVector &data) : d(new HeaderPrivate()) { - parse(data); -} - -Header::~Header() { - delete d; -} - -unsigned int Header::majorVersion() const { - return d->majorVersion; -} - -void Header::setMajorVersion(unsigned int version) { - d->majorVersion = version; -} - -unsigned int Header::revisionNumber() const { - return d->revisionNumber; -} - -bool Header::unsynchronisation() const { - return d->unsynchronisation; -} - -bool Header::extendedHeader() const { - return d->extendedHeader; -} - -bool Header::experimentalIndicator() const { - return d->experimentalIndicator; -} - -bool Header::footerPresent() const { - return d->footerPresent; -} - -unsigned int Header::tagSize() const { - return d->tagSize; -} - -unsigned int Header::completeTagSize() const { - if (d->footerPresent) - return d->tagSize + size() + Footer::size(); - else - return d->tagSize + size(); -} - -void Header::setTagSize(unsigned int s) { - d->tagSize = s; -} - -void Header::setData(const ByteVector &data) { - parse(data); -} - -ByteVector Header::render() const { - - ByteVector v; - - // add the file identifier -- "ID3" - v.append(fileIdentifier()); - - // add the version number -- we always render a 2.4.0 tag regardless of what the tag originally was. - - v.append(char(majorVersion())); - v.append(char(0)); - - // Currently we don't actually support writing extended headers, footers or unsynchronized tags, make sure that the flags are set accordingly. - - d->extendedHeader = false; - d->footerPresent = false; - d->unsynchronisation = false; - - // render and add the flags - std::bitset<8> flags; - - flags[7] = d->unsynchronisation; - flags[6] = d->extendedHeader; - flags[5] = d->experimentalIndicator; - flags[4] = d->footerPresent; - - v.append(char(flags.to_ulong())); - - // add the size - v.append(SynchData::fromUInt(d->tagSize)); - - return v; - -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void Header::parse(const ByteVector &data) { - - if (data.size() < size()) - return; - - // do some sanity checking -- even in ID3v2.3.0 and less the tag size is a synch-safe integer, so all bytes must be less than 128. - // If this is not true then this is an invalid tag. - - // note that we're doing things a little out of order here -- the size is later in the bytestream than the version - - ByteVector sizeData = data.mid(6, 4); - - if (sizeData.size() != 4) { - d->tagSize = 0; - debug("TagLib::ID3v2::Header::parse() - The tag size as read was 0 bytes!"); - return; - } - - for (ByteVector::ConstIterator it = sizeData.begin(); it != sizeData.end(); it++) { - if (static_cast(*it) >= 128) { - d->tagSize = 0; - debug("TagLib::ID3v2::Header::parse() - One of the size bytes in the id3v2 header was greater than the allowed 128."); - return; - } - } - - // The first three bytes, data[0..2], are the File Identifier, "ID3". (structure 3.1 "file identifier") - - // Read the version number from the fourth and fifth bytes. - d->majorVersion = data[3]; // (structure 3.1 "major version") - d->revisionNumber = data[4]; // (structure 3.1 "revision number") - - // Read the flags, the first four bits of the sixth byte. - std::bitset<8> flags(data[5]); - - d->unsynchronisation = flags[7]; // (structure 3.1.a) - d->extendedHeader = flags[6]; // (structure 3.1.b) - d->experimentalIndicator = flags[5]; // (structure 3.1.c) - d->footerPresent = flags[4]; // (structure 3.1.d) - - // Get the size from the remaining four bytes (read above) - - d->tagSize = SynchData::toUInt(sizeData); // (structure 3.1 "size") - -} diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2header.h b/3rdparty/taglib/mpeg/id3v2/id3v2header.h deleted file mode 100644 index 429173df..00000000 --- a/3rdparty/taglib/mpeg/id3v2/id3v2header.h +++ /dev/null @@ -1,165 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_ID3V2HEADER_H -#define TAGLIB_ID3V2HEADER_H - -#include "tbytevector.h" -#include "taglib_export.h" -#include "id3v2.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -//! An implementation of ID3v2 headers - -/*! - * This class implements ID3v2 headers. - * It attempts to follow, both semantically and programmatically, the structure specified in the ID3v2 standard. - * The API is based on the properties of ID3v2 headers specified there. - * If any of the terms used in this documentation are unclear please check the specification in the linked section. - * (Structure, 3.1) - */ - -class TAGLIB_EXPORT Header { - public: - /*! - * Constructs an empty ID3v2 header. - */ - explicit Header(); - - /*! - * Constructs an ID3v2 header based on \a data. parse() is called immediately. - */ - explicit Header(const ByteVector &data); - - /*! - * Destroys the header. - */ - virtual ~Header(); - - /*! - * Returns the major version number. (Note: This is the 4, not the 2 in ID3v2.4.0. The 2 is implied.) - */ - unsigned int majorVersion() const; - - /*! - * Set the the major version number to \a version. (Note: This is the 4, not the 2 in ID3v2.4.0. The 2 is implied.) - * \see majorVersion() - * - * \note This is used by the internal parser; this will not change the version which is written and in general should not be called by API users. - */ - void setMajorVersion(unsigned int version); - - /*! - * Returns the revision number. (Note: This is the 0, not the 4 in ID3v2.4.0. The 2 is implied.) - */ - unsigned int revisionNumber() const; - - /*! - * Returns true if unsynchronisation has been applied to all frames. - */ - bool unsynchronisation() const; - - /*! - * Returns true if an extended header is present in the tag. - */ - bool extendedHeader() const; - - /*! - * Returns true if the experimental indicator flag is set. - */ - bool experimentalIndicator() const; - - /*! - * Returns true if a footer is present in the tag. - */ - bool footerPresent() const; - /*! - * Returns the tag size in bytes. - * This is the size of the frame content. - * The size of the \e entire tag will be this plus the header size (10 bytes) and, if present, the footer size (potentially another 10 bytes). - * - * \note This is the value as read from the header to which TagLib attempts to provide an API to; - * it was not a design decision on the part of TagLib to not include the mentioned portions of the tag in the \e size. - * - * \see completeTagSize() - */ - unsigned int tagSize() const; - - /*! - * Returns the tag size, including the header and, if present, the footer size. - * - * \see tagSize() - */ - unsigned int completeTagSize() const; - - /*! - * Set the tag size to \a s. - * \see tagSize() - */ - void setTagSize(unsigned int s); - - /*! - * Returns the size of the header. Presently this is always 10 bytes. - */ - static unsigned int size(); - - /*! - * Returns the string used to identify and ID3v2 tag inside of a file. - * Presently this is always "ID3". - */ - static ByteVector fileIdentifier(); - - /*! - * Sets the data that will be used as the header. 10 bytes, starting from the beginning of \a data are used. - */ - void setData(const ByteVector &data); - - /*! - * Renders the Header back to binary format. - */ - ByteVector render() const; - - protected: - /*! - * Called by setData() to parse the header data. It makes this information available through the public API. - */ - void parse(const ByteVector &data); - - private: - Header(const Header&); - Header &operator=(const Header&); - - class HeaderPrivate; - HeaderPrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2synchdata.cpp b/3rdparty/taglib/mpeg/id3v2/id3v2synchdata.cpp deleted file mode 100644 index f09f0cd0..00000000 --- a/3rdparty/taglib/mpeg/id3v2/id3v2synchdata.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "id3v2synchdata.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -unsigned int SynchData::toUInt(const ByteVector &data) { - - unsigned int sum = 0; - bool notSynchSafe = false; - const int last = data.size() > 4 ? 3 : data.size() - 1; - - for (int i = 0; i <= last; i++) { - if (data[i] & 0x80) { - notSynchSafe = true; - break; - } - - sum |= (data[i] & 0x7f) << ((last - i) * 7); - } - - if (notSynchSafe) { - // Invalid data; assume this was created by some buggy software that just - // put normal integers here rather than syncsafe ones, and try it that way. - if (data.size() >= 4) { - sum = data.toUInt32BE(0); - } - else { - ByteVector tmp(data); - tmp.resize(4); - sum = tmp.toUInt32BE(0); - } - } - - return sum; - -} - -ByteVector SynchData::fromUInt(unsigned int value) { - - ByteVector v(4, 0); - - for (int i = 0; i < 4; i++) - v[i] = static_cast(value >> ((3 - i) * 7) & 0x7f); - - return v; - -} - -ByteVector SynchData::decode(const ByteVector &data) { - - // We have this optimized method instead of using ByteVector::replace(), - // since it makes a great difference when decoding huge unsynchronized frames. - - ByteVector result(data.size()); - - ByteVector::ConstIterator src = data.begin(); - ByteVector::Iterator dst = result.begin(); - - while (src < data.end() - 1) { - *dst++ = *src++; - - if (*(src - 1) == '\xff' && *src == '\x00') - src++; - } - - if (src < data.end()) - *dst++ = *src++; - - result.resize(static_cast(dst - result.begin())); - - return result; - -} diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2synchdata.h b/3rdparty/taglib/mpeg/id3v2/id3v2synchdata.h deleted file mode 100644 index e7da49df..00000000 --- a/3rdparty/taglib/mpeg/id3v2/id3v2synchdata.h +++ /dev/null @@ -1,67 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_ID3V2SYNCHDATA_H -#define TAGLIB_ID3V2SYNCHDATA_H - -#include "tbytevector.h" -#include "taglib.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace ID3v2 { - -//! A few functions for ID3v2 synch safe integer conversion - -/*! - * In the ID3v2.4 standard most integer values are encoded as "synch safe" - * integers which are encoded in such a way that they will not give false MPEG syncs and confuse MPEG decoders. - * This namespace provides some methods for converting to and from these values to ByteVectors for things rendering and parsing ID3v2 data. - */ - -namespace SynchData { -/*! - * This returns the unsigned integer value of \a data where \a data is a ByteVector that contains a \e synchsafe integer (Structure, - * 6.2). - * The default \a length of 4 is used if another value is not specified. - */ -TAGLIB_EXPORT unsigned int toUInt(const ByteVector &data); - -/*! - * Returns a 4 byte (32 bit) synchsafe integer based on \a value. - */ -TAGLIB_EXPORT ByteVector fromUInt(unsigned int value); - -/*! - * Convert the data from unsynchronized data to its original format. - */ -TAGLIB_EXPORT ByteVector decode(const ByteVector &data); -} // namespace SynchData - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2tag.cpp b/3rdparty/taglib/mpeg/id3v2/id3v2tag.cpp deleted file mode 100644 index c330a539..00000000 --- a/3rdparty/taglib/mpeg/id3v2/id3v2tag.cpp +++ /dev/null @@ -1,920 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "tfile.h" -#include "tbytevector.h" -#include "tpropertymap.h" -#include "tpicturemap.h" -#include "tdebug.h" - -#include "id3v2tag.h" -#include "id3v2header.h" -#include "id3v2extendedheader.h" -#include "id3v2footer.h" -#include "id3v2synchdata.h" -#include "id3v1genres.h" - -#include "frames/attachedpictureframe.h" -#include "frames/textidentificationframe.h" -#include "frames/commentsframe.h" -#include "frames/urllinkframe.h" -#include "frames/uniquefileidentifierframe.h" -#include "frames/unsynchronizedlyricsframe.h" -#include "frames/unknownframe.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace ID3v2; - -namespace { -class DefaultStringHandler : public Strawberry_TagLib::TagLib::StringHandler { - public: - explicit DefaultStringHandler() : Strawberry_TagLib::TagLib::StringHandler() {} - - String parse(const ByteVector &data) const override { - return String(data, String::Latin1); - } - - ByteVector render(const String&) const override { - // Not implemented on purpose. This function is never used. - return ByteVector(); - } -}; - -const DefaultStringHandler defaultStringHandler; -const Strawberry_TagLib::TagLib::StringHandler *stringHandler = &defaultStringHandler; - -const long long MinPaddingSize = 1024; -const long long MaxPaddingSize = 1024 * 1024; -} // namespace - -class ID3v2::Tag::TagPrivate { - public: - explicit TagPrivate() : factory(nullptr), - file(nullptr), - tagOffset(0), - extendedHeader(nullptr), - footer(nullptr) { - frameList.setAutoDelete(true); - } - - ~TagPrivate() { - delete extendedHeader; - delete footer; - } - - const FrameFactory *factory; - - File *file; - long long tagOffset; - - Header header; - ExtendedHeader *extendedHeader; - Footer *footer; - - FrameListMap frameListMap; - FrameList frameList; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -ID3v2::Tag::Tag() : d(new TagPrivate()) { - d->factory = FrameFactory::instance(); -} - -ID3v2::Tag::Tag(File *file, long long tagOffset, const FrameFactory *factory) : d(new TagPrivate()) { - d->factory = factory; - d->file = file; - d->tagOffset = tagOffset; - - read(); -} - -ID3v2::Tag::~Tag() { - delete d; -} - -String ID3v2::Tag::title() const { - if (!d->frameListMap["TIT2"].isEmpty()) - return d->frameListMap["TIT2"].front()->toString(); - return String(); -} - -String ID3v2::Tag::artist() const { - if (!d->frameListMap["TPE1"].isEmpty()) - return d->frameListMap["TPE1"].front()->toString(); - return String(); -} - -String ID3v2::Tag::album() const { - if (!d->frameListMap["TALB"].isEmpty()) - return d->frameListMap["TALB"].front()->toString(); - return String(); -} - -String ID3v2::Tag::comment() const { - - const FrameList &comments = d->frameListMap["COMM"]; - - if (comments.isEmpty()) - return String(); - - for (FrameList::ConstIterator it = comments.begin(); it != comments.end(); ++it) { - CommentsFrame *frame = dynamic_cast(*it); - - if (frame && frame->description().isEmpty()) - return (*it)->toString(); - } - - return comments.front()->toString(); - -} - -String ID3v2::Tag::genre() const { - - // TODO: In the next major version (TagLib 2.0) a list of multiple genres - // should be separated by " / " instead of " ". For the moment to keep - // the behavior the same as released versions it is being left with " ". - - if (d->frameListMap["TCON"].isEmpty() || - !dynamic_cast(d->frameListMap["TCON"].front())) { - return String(); - } - - // ID3v2.4 lists genres as the fields in its frames field list. If the field - // is simply a number it can be assumed that it is an ID3v1 genre number. - // Here was assume that if an ID3v1 string is present that it should be - // appended to the genre string. Multiple fields will be appended as the - // string is built. - - TextIdentificationFrame *f = static_cast( - d->frameListMap["TCON"].front()); - - StringList fields = f->fieldList(); - - StringList genres; - - for (StringList::Iterator it = fields.begin(); it != fields.end(); ++it) { - - if ((*it).isEmpty()) - continue; - - bool ok; - int number = (*it).toInt(&ok); - if (ok && number >= 0 && number <= 255) { - *it = ID3v1::genre(number); - } - - if (std::find(genres.begin(), genres.end(), *it) == genres.end()) - genres.append(*it); - } - - return genres.toString(); - -} - -unsigned int ID3v2::Tag::year() const { - - if (!d->frameListMap["TDRC"].isEmpty()) - return d->frameListMap["TDRC"].front()->toString().substr(0, 4).toInt(); - return 0; - -} - -unsigned int ID3v2::Tag::track() const { - - if (!d->frameListMap["TRCK"].isEmpty()) - return d->frameListMap["TRCK"].front()->toString().toInt(); - - return 0; - -} - -Strawberry_TagLib::TagLib::PictureMap ID3v2::Tag::pictures() const { - - if (!d->frameListMap.contains("APIC")) - return PictureMap(); - - PictureMap map; - FrameList frameListMap = d->frameListMap["APIC"]; - for (FrameList::ConstIterator it = frameListMap.begin(); it != frameListMap.end(); ++it) { - const AttachedPictureFrame *frame = static_cast(*it); - Picture::Type type; - switch (frame->type()) { - case AttachedPictureFrame::FileIcon: - type = Picture::FileIcon; - break; - case AttachedPictureFrame::OtherFileIcon: - type = Picture::OtherFileIcon; - break; - case AttachedPictureFrame::FrontCover: - type = Picture::FrontCover; - break; - case AttachedPictureFrame::BackCover: - type = Picture::BackCover; - break; - case AttachedPictureFrame::LeafletPage: - type = Picture::LeafletPage; - break; - case AttachedPictureFrame::Media: - type = Picture::Media; - break; - case AttachedPictureFrame::LeadArtist: - type = Picture::LeadArtist; - break; - case AttachedPictureFrame::Artist: - type = Picture::Artist; - break; - case AttachedPictureFrame::Conductor: - type = Picture::Conductor; - break; - case AttachedPictureFrame::Band: - type = Picture::Band; - break; - case AttachedPictureFrame::Composer: - type = Picture::Composer; - break; - case AttachedPictureFrame::Lyricist: - type = Picture::Lyricist; - break; - case AttachedPictureFrame::RecordingLocation: - type = Picture::RecordingLocation; - break; - case AttachedPictureFrame::DuringRecording: - type = Picture::DuringRecording; - break; - case AttachedPictureFrame::DuringPerformance: - type = Picture::DuringPerformance; - break; - case AttachedPictureFrame::MovieScreenCapture: - type = Picture::MovieScreenCapture; - break; - case AttachedPictureFrame::ColouredFish: - type = Picture::ColouredFish; - break; - case AttachedPictureFrame::Illustration: - type = Picture::Illustration; - break; - case AttachedPictureFrame::BandLogo: - type = Picture::BandLogo; - break; - case AttachedPictureFrame::PublisherLogo: - type = Picture::PublisherLogo; - break; - default: - type = Picture::Other; - break; - } - Picture picture(frame->picture(), type, frame->mimeType(), frame->description()); - map.insert(picture); - } - return PictureMap(map); - -} - -void ID3v2::Tag::setTitle(const String &s) { - setTextFrame("TIT2", s); -} - -void ID3v2::Tag::setArtist(const String &s) { - setTextFrame("TPE1", s); -} - -void ID3v2::Tag::setAlbum(const String &s) { - setTextFrame("TALB", s); -} - -void ID3v2::Tag::setComment(const String &s) { - - if (s.isEmpty()) { - removeFrames("COMM"); - return; - } - - if (!d->frameListMap["COMM"].isEmpty()) - d->frameListMap["COMM"].front()->setText(s); - else { - CommentsFrame *f = new CommentsFrame(d->factory->defaultTextEncoding()); - addFrame(f); - f->setText(s); - } - -} - -void ID3v2::Tag::setGenre(const String &s) { - - if (s.isEmpty()) { - removeFrames("TCON"); - return; - } - - // iTunes can't handle correctly encoded ID3v2.4 numerical genres. Just use - // strings until iTunes sucks less. - -#ifdef NO_ITUNES_HACKS - - int index = ID3v1::genreIndex(s); - - if (index != 255) - setTextFrame("TCON", String::number(index)); - else - setTextFrame("TCON", s); - -#else - - setTextFrame("TCON", s); - -#endif - -} - -void ID3v2::Tag::setYear(unsigned int i) { - if (i == 0) { - removeFrames("TDRC"); - return; - } - setTextFrame("TDRC", String::number(i)); -} - -void ID3v2::Tag::setTrack(unsigned int i) { - if (i == 0) { - removeFrames("TRCK"); - return; - } - setTextFrame("TRCK", String::number(i)); -} - -void ID3v2::Tag::setPictures(const PictureMap &l) { - - removeFrames("APIC"); - - for (PictureMap::ConstIterator it = l.begin(); it != l.end(); ++it) { - - PictureList list = it->second; - FrameList framesAdded; - for (PictureList::ConstIterator it2 = list.begin(); it2 != list.end(); ++it2) { - const Picture picture = (*it2); - AttachedPictureFrame *frame = new AttachedPictureFrame(); - frame->setPicture(picture.data()); - frame->setMimeType(picture.mime()); - frame->setDescription(picture.description()); - switch (picture.type()) { - case Picture::Other: - frame->setType(AttachedPictureFrame::Other); - break; - case Picture::FileIcon: - frame->setType(AttachedPictureFrame::FileIcon); - break; - case Picture::OtherFileIcon: - frame->setType(AttachedPictureFrame::OtherFileIcon); - break; - case Picture::FrontCover: - frame->setType(AttachedPictureFrame::FrontCover); - break; - case Picture::BackCover: - frame->setType(AttachedPictureFrame::BackCover); - break; - case Picture::LeafletPage: - frame->setType(AttachedPictureFrame::LeafletPage); - break; - case Picture::Media: - frame->setType(AttachedPictureFrame::Media); - break; - case Picture::LeadArtist: - frame->setType(AttachedPictureFrame::LeadArtist); - break; - case Picture::Artist: - frame->setType(AttachedPictureFrame::Artist); - break; - case Picture::Conductor: - frame->setType(AttachedPictureFrame::Conductor); - break; - case Picture::Band: - frame->setType(AttachedPictureFrame::Band); - break; - case Picture::Composer: - frame->setType(AttachedPictureFrame::Composer); - break; - case Picture::Lyricist: - frame->setType(AttachedPictureFrame::Lyricist); - break; - case Picture::RecordingLocation: - frame->setType(AttachedPictureFrame::RecordingLocation); - break; - case Picture::DuringRecording: - frame->setType(AttachedPictureFrame::DuringRecording); - break; - case Picture::DuringPerformance: - frame->setType(AttachedPictureFrame::DuringPerformance); - break; - case Picture::MovieScreenCapture: - frame->setType(AttachedPictureFrame::MovieScreenCapture); - break; - case Picture::ColouredFish: - frame->setType(AttachedPictureFrame::ColouredFish); - break; - case Picture::Illustration: - frame->setType(AttachedPictureFrame::Illustration); - break; - case Picture::BandLogo: - frame->setType(AttachedPictureFrame::BandLogo); - break; - case Picture::PublisherLogo: - frame->setType(AttachedPictureFrame::PublisherLogo); - break; - } - framesAdded.append(frame); - } - for (FrameList::ConstIterator it2 = framesAdded.begin(); it2 != framesAdded.end(); ++it2) - addFrame(*it2); - } -} - -bool ID3v2::Tag::isEmpty() const { - return d->frameList.isEmpty(); -} - -Header *ID3v2::Tag::header() const { - return &(d->header); -} - -ExtendedHeader *ID3v2::Tag::extendedHeader() const { - return d->extendedHeader; -} - -const FrameListMap &ID3v2::Tag::frameListMap() const { - return d->frameListMap; -} - -const FrameList &ID3v2::Tag::frameList() const { - return d->frameList; -} - -const FrameList &ID3v2::Tag::frameList(const ByteVector &frameID) const { - return d->frameListMap[frameID]; -} - -void ID3v2::Tag::addFrame(Frame *frame) { - d->frameList.append(frame); - d->frameListMap[frame->frameID()].append(frame); -} - -void ID3v2::Tag::removeFrame(Frame *frame, bool del) { - - // remove the frame from the frame list - FrameList::Iterator it = d->frameList.find(frame); - d->frameList.erase(it); - - // ...and from the frame list map - it = d->frameListMap[frame->frameID()].find(frame); - d->frameListMap[frame->frameID()].erase(it); - - // ...and delete as desired - if (del) - delete frame; - -} - -void ID3v2::Tag::removeFrames(const ByteVector &id) { - FrameList l = d->frameListMap[id]; - for (FrameList::ConstIterator it = l.begin(); it != l.end(); ++it) - removeFrame(*it, true); -} - -PropertyMap ID3v2::Tag::properties() const { - PropertyMap properties; - for (FrameList::ConstIterator it = frameList().begin(); it != frameList().end(); ++it) { - PropertyMap props = (*it)->asProperties(); - properties.merge(props); - } - return properties; -} - -void ID3v2::Tag::removeUnsupportedProperties(const StringList &properties) { - - for (StringList::ConstIterator it = properties.begin(); it != properties.end(); ++it) { - if (it->startsWith("UNKNOWN/")) { - String frameID = it->substr(String("UNKNOWN/").size()); - if (frameID.size() != 4) - continue; // invalid specification - ByteVector id = frameID.data(String::Latin1); - // delete all unknown frames of given type - FrameList l = frameList(id); - for (FrameList::ConstIterator fit = l.begin(); fit != l.end(); fit++) - if (dynamic_cast(*fit) != nullptr) - removeFrame(*fit); - } - else if (it->size() == 4) { - ByteVector id = it->data(String::Latin1); - removeFrames(id); - } - else { - ByteVector id = it->substr(0, 4).data(String::Latin1); - if (it->size() <= 5) - continue; // invalid specification - String description = it->substr(5); - Frame *frame = nullptr; - if (id == "TXXX") - frame = UserTextIdentificationFrame::find(this, description); - else if (id == "WXXX") - frame = UserUrlLinkFrame::find(this, description); - else if (id == "COMM") - frame = CommentsFrame::findByDescription(this, description); - else if (id == "USLT") - frame = UnsynchronizedLyricsFrame::findByDescription(this, description); - else if (id == "UFID") - frame = UniqueFileIdentifierFrame::findByOwner(this, description); - if (frame) - removeFrame(frame); - } - } - -} - -PropertyMap ID3v2::Tag::setProperties(const PropertyMap &origProps) { - - FrameList framesToDelete; - // we split up the PropertyMap into the "normal" keys and the "complicated" ones, - // which are those according to TIPL or TMCL frames. - PropertyMap properties; - PropertyMap tiplProperties; - PropertyMap tmclProperties; - Frame::splitProperties(origProps, properties, tiplProperties, tmclProperties); - for (FrameListMap::ConstIterator it = frameListMap().begin(); it != frameListMap().end(); ++it) { - for (FrameList::ConstIterator lit = it->second.begin(); lit != it->second.end(); ++lit) { - PropertyMap frameProperties = (*lit)->asProperties(); - if (it->first == "TIPL") { - if (tiplProperties != frameProperties) - framesToDelete.append(*lit); - else - tiplProperties.erase(frameProperties); - } - else if (it->first == "TMCL") { - if (tmclProperties != frameProperties) - framesToDelete.append(*lit); - else - tmclProperties.erase(frameProperties); - } - else if (!properties.contains(frameProperties)) - framesToDelete.append(*lit); - else - properties.erase(frameProperties); - } - } - for (FrameList::ConstIterator it = framesToDelete.begin(); it != framesToDelete.end(); ++it) - removeFrame(*it); - - // now create remaining frames: - // start with the involved people list (TIPL) - if (!tiplProperties.isEmpty()) - addFrame(TextIdentificationFrame::createTIPLFrame(tiplProperties)); - // proceed with the musician credit list (TMCL) - if (!tmclProperties.isEmpty()) - addFrame(TextIdentificationFrame::createTMCLFrame(tmclProperties)); - // now create the "one key per frame" frames - for (PropertyMap::ConstIterator it = properties.begin(); it != properties.end(); ++it) - addFrame(Frame::createTextualFrame(it->first, it->second)); - return PropertyMap(); // ID3 implements the complete PropertyMap interface, so an empty map is returned - -} - -void ID3v2::Tag::downgradeFrames(FrameList *frames, FrameList *newFrames) const { -#ifdef NO_ITUNES_HACKS - const char *unsupportedFrames[] = { "ASPI", "EQU2", "RVA2", "SEEK", "SIGN", "TDRL", "TDTG", "TMOO", "TPRO", "TSOA", "TSOT", "TSST", "TSOP", nullptr }; -#else - // iTunes writes and reads TSOA, TSOT, TSOP to ID3v2.3. - const char *unsupportedFrames[] = { "ASPI", "EQU2", "RVA2", "SEEK", "SIGN", "TDRL", "TDTG", "TMOO", "TPRO", "TSST", nullptr }; -#endif - ID3v2::TextIdentificationFrame *frameTDOR = nullptr; - ID3v2::TextIdentificationFrame *frameTDRC = nullptr; - ID3v2::TextIdentificationFrame *frameTIPL = nullptr; - ID3v2::TextIdentificationFrame *frameTMCL = nullptr; - for (FrameList::ConstIterator it = d->frameList.begin(); it != d->frameList.end(); it++) { - ID3v2::Frame *frame = *it; - ByteVector frameID = frame->header()->frameID(); - for (int i = 0; unsupportedFrames[i]; i++) { - if (frameID == unsupportedFrames[i]) { - debug("A frame that is not supported in ID3v2.3 \'" + String(frameID) + "\' has been discarded"); - frame = nullptr; - break; - } - } - if (frame && frameID == "TDOR") { - frameTDOR = dynamic_cast(frame); - frame = nullptr; - } - if (frame && frameID == "TDRC") { - frameTDRC = dynamic_cast(frame); - frame = nullptr; - } - if (frame && frameID == "TIPL") { - frameTIPL = dynamic_cast(frame); - frame = nullptr; - } - if (frame && frameID == "TMCL") { - frameTMCL = dynamic_cast(frame); - frame = nullptr; - } - if (frame) { - frames->append(frame); - } - } - if (frameTDOR) { - String content = frameTDOR->toString(); - if (content.size() >= 4) { - ID3v2::TextIdentificationFrame *frameTORY = new ID3v2::TextIdentificationFrame("TORY", String::Latin1); - frameTORY->setText(content.substr(0, 4)); - frames->append(frameTORY); - newFrames->append(frameTORY); - } - } - if (frameTDRC) { - String content = frameTDRC->toString(); - if (content.size() >= 4) { - ID3v2::TextIdentificationFrame *frameTYER = new ID3v2::TextIdentificationFrame("TYER", String::Latin1); - frameTYER->setText(content.substr(0, 4)); - frames->append(frameTYER); - newFrames->append(frameTYER); - if (content.size() >= 10 && content[4] == '-' && content[7] == '-') { - ID3v2::TextIdentificationFrame *frameTDAT = new ID3v2::TextIdentificationFrame("TDAT", String::Latin1); - frameTDAT->setText(content.substr(8, 2) + content.substr(5, 2)); - frames->append(frameTDAT); - newFrames->append(frameTDAT); - if (content.size() >= 16 && content[10] == 'T' && content[13] == ':') { - ID3v2::TextIdentificationFrame *frameTIME = new ID3v2::TextIdentificationFrame("TIME", String::Latin1); - frameTIME->setText(content.substr(11, 2) + content.substr(14, 2)); - frames->append(frameTIME); - newFrames->append(frameTIME); - } - } - } - } - if (frameTIPL || frameTMCL) { - ID3v2::TextIdentificationFrame *frameIPLS = new ID3v2::TextIdentificationFrame("IPLS", String::Latin1); - StringList people; - if (frameTMCL) { - StringList v24People = frameTMCL->fieldList(); - for (unsigned int i = 0; i + 1 < v24People.size(); i += 2) { - people.append(v24People[i]); - people.append(v24People[i + 1]); - } - } - if (frameTIPL) { - StringList v24People = frameTIPL->fieldList(); - for (unsigned int i = 0; i + 1 < v24People.size(); i += 2) { - people.append(v24People[i]); - people.append(v24People[i + 1]); - } - } - frameIPLS->setText(people); - frames->append(frameIPLS); - newFrames->append(frameIPLS); - } - -} - -ByteVector ID3v2::Tag::render(Version version) const { - - // We need to render the "tag data" first so that we have to correct size to - // render in the tag's header. The "tag data" -- everything that is included - // in ID3v2::Header::tagSize() -- includes the extended header, frames and - // padding, but does not include the tag's header or footer. - - // TODO: Render the extended header. - - // Downgrade the frames that ID3v2.3 doesn't support. - - FrameList newFrames; - newFrames.setAutoDelete(true); - - FrameList frameList; - if (version == v4) { - frameList = d->frameList; - } - else { - downgradeFrames(&frameList, &newFrames); - } - - // Reserve a 10-byte blank space for an ID3v2 tag header. - - ByteVector tagData(Header::size(), '\0'); - - // Loop through the frames rendering them and adding them to the tagData. - - for (FrameList::ConstIterator it = frameList.begin(); it != frameList.end(); it++) { - (*it)->header()->setVersion(version == v3 ? 3 : 4); - if ((*it)->header()->frameID().size() != 4) { - debug("An ID3v2 frame of unsupported or unknown type \'" + String((*it)->header()->frameID()) + "\' has been discarded"); - continue; - } - if (!(*it)->header()->tagAlterPreservation()) { - const ByteVector frameData = (*it)->render(); - if (frameData.size() == Frame::headerSize((*it)->header()->version())) { - debug("An empty ID3v2 frame \'" + String((*it)->header()->frameID()) + "\' has been discarded"); - continue; - } - tagData.append(frameData); - } - } - - // Compute the amount of padding, and append that to tagData. - - long long originalSize = d->header.tagSize(); - long long paddingSize = originalSize - (tagData.size() - Header::size()); - - if (paddingSize <= 0) { - paddingSize = MinPaddingSize; - } - else { - // Padding won't increase beyond 1% of the file size or 1MB. - - long long threshold = d->file ? d->file->length() / 100 : 0; - threshold = std::max(threshold, MinPaddingSize); - threshold = std::min(threshold, MaxPaddingSize); - - if (paddingSize > threshold) - paddingSize = MinPaddingSize; - } - - tagData.resize(static_cast(tagData.size() + paddingSize), '\0'); - - // Set the version and data size. - d->header.setMajorVersion(version); - d->header.setTagSize(tagData.size() - Header::size()); - - // TODO: This should eventually include d->footer->render(). - const ByteVector headerData = d->header.render(); - std::copy(headerData.begin(), headerData.end(), tagData.begin()); - - return tagData; - -} - -Strawberry_TagLib::TagLib::StringHandler const *ID3v2::Tag::latin1StringHandler() { - return stringHandler; -} - -void ID3v2::Tag::setLatin1StringHandler(const Strawberry_TagLib::TagLib::StringHandler *handler) { - if (handler) - stringHandler = handler; - else - stringHandler = &defaultStringHandler; -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void ID3v2::Tag::read() { - - if (!d->file) - return; - - if (!d->file->isOpen()) - return; - - d->file->seek(d->tagOffset); - d->header.setData(d->file->readBlock(Header::size())); - - // If the tag size is 0, then this is an invalid tag (tags must contain at - // least one frame) - - if (d->header.tagSize() != 0) - parse(d->file->readBlock(d->header.tagSize())); - - // Look for duplicate ID3v2 tags and treat them as an extra blank of this one. - // It leads to overwriting them with zero when saving the tag. - - // This is a workaround for some faulty files that have duplicate ID3v2 tags. - // Unfortunately, TagLib itself may write such duplicate tags until v1.10. - - unsigned int extraSize = 0; - - while (true) { - - d->file->seek(d->tagOffset + d->header.completeTagSize() + extraSize); - - const ByteVector data = d->file->readBlock(Header::size()); - if (data.size() < Header::size() || !data.startsWith(Header::fileIdentifier())) - break; - - extraSize += Header(data).completeTagSize(); - } - - if (extraSize != 0) { - debug("ID3v2::Tag::read() - Duplicate ID3v2 tags found."); - d->header.setTagSize(d->header.tagSize() + extraSize); - } - -} - -void ID3v2::Tag::parse(const ByteVector &origData) { - - ByteVector data = origData; - - if (d->header.unsynchronisation() && d->header.majorVersion() <= 3) - data = SynchData::decode(data); - - size_t frameDataPosition = 0; - size_t frameDataLength = data.size(); - - // check for extended header - - if (d->header.extendedHeader()) { - if (!d->extendedHeader) - d->extendedHeader = new ExtendedHeader(); - d->extendedHeader->setData(data); - if (d->extendedHeader->size() <= data.size()) { - frameDataPosition += d->extendedHeader->size(); - frameDataLength -= d->extendedHeader->size(); - } - } - - // check for footer -- we don't actually need to parse it, as it *must* - // contain the same data as the header, but we do need to account for its - // size. - - if (d->header.footerPresent() && Footer::size() <= frameDataLength) - frameDataLength -= Footer::size(); - - // parse frames - - // Make sure that there is at least enough room in the remaining frame data for - // a frame header. - - while (frameDataPosition < frameDataLength - Frame::headerSize(d->header.majorVersion())) { - - // If the next data is position is 0, assume that we've hit the padding - // portion of the frame data. - - if (data.at(frameDataPosition) == 0) { - if (d->header.footerPresent()) { - debug("Padding *and* a footer found. This is not allowed by the spec."); - } - - break; - } - - Frame *frame = d->factory->createFrame(data.mid(frameDataPosition), - &d->header); - - if (!frame) - return; - - // Checks to make sure that frame parsed correctly. - - if (frame->size() <= 0) { - delete frame; - return; - } - - frameDataPosition += frame->size() + Frame::headerSize(d->header.majorVersion()); - addFrame(frame); - } - - d->factory->rebuildAggregateFrames(this); - -} - -void ID3v2::Tag::setTextFrame(const ByteVector &id, const String &value) { - - if (value.isEmpty()) { - removeFrames(id); - return; - } - - if (!d->frameListMap[id].isEmpty()) - d->frameListMap[id].front()->setText(value); - else { - const String::Type encoding = d->factory->defaultTextEncoding(); - TextIdentificationFrame *f = new TextIdentificationFrame(id, encoding); - addFrame(f); - f->setText(value); - } - -} diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2tag.h b/3rdparty/taglib/mpeg/id3v2/id3v2tag.h deleted file mode 100644 index ab9d8b82..00000000 --- a/3rdparty/taglib/mpeg/id3v2/id3v2tag.h +++ /dev/null @@ -1,331 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_ID3V2TAG_H -#define TAGLIB_ID3V2TAG_H - -#include "tag.h" -#include "tbytevector.h" -#include "tstring.h" -#include "tstringhandler.h" -#include "tlist.h" -#include "tmap.h" -#include "taglib_export.h" - -#include "id3v2.h" -#include "id3v2framefactory.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -class File; - -namespace ID3v2 { - -class Header; -class ExtendedHeader; -class Footer; - -typedef List FrameList; -typedef Map FrameListMap; - -//! The main class in the ID3v2 implementation - -/*! - * This is the main class in the ID3v2 implementation. - * It serves two functions. - * This first, as is obvious from the public API, is to provide a container for the other ID3v2 related classes. - * In addition, through the read() and parse() protected methods, - * it provides the most basic level of parsing. - * In these methods the ID3v2 tag is extracted from the file and split into data components. - * - * ID3v2 tags have several parts, TagLib attempts to provide an interface for them all. - * header(), footer() and extendedHeader() correspond to those data structures in - * the ID3v2 standard and the APIs for the classes that they return attempt to reflect this. - * - * Also ID3v2 tags are built up from a list of frames, which are in turn have a header and a list of fields. - * TagLib provides two ways of accessing the list of frames that are in a given ID3v2 tag. - * The first is simply via the frameList() method. This is just a list of pointers to the frames. - * The second is a map from the frame type -- i.e. "COMM" for comments -- and a list of frames of that type. - * (In some cases ID3v2 allows for multiple frames of the same type, hence this being a map to a list rather than just a map to an individual frame.) - * - * More information on the structure of frames can be found in the ID3v2::Frame class. - * - * read() and parse() pass binary data to the other ID3v2 class structures, they do not handle parsing of flags or fields, for instance. - * Those are handled by similar functions within those classes. - * - * \note All pointers to data structures within the tag will become invalid when the tag is destroyed. - * - * \warning Dealing with the nasty details of ID3v2 is not for the faint of - * heart and should not be done without much meditation on the spec. - * It's rather long, but if you're planning on messing with this class and others that deal with the details of ID3v2 - * (rather than the nice, safe, abstract TagLib::Tag and friends), it's worth your time to familiarize yourself with said spec - * (which is distributed with the TagLib sources). - * TagLib tries to do most of the work, but with a little luck, you can still convince it to generate invalid ID3v2 tags. - * The APIs for ID3v2 assume a working knowledge of ID3v2 structure. You're been warned. - */ - -class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag { - public: - /*! - * Constructs an empty ID3v2 tag. - * - * \note You must create at least one frame for this tag to be valid. - */ - explicit Tag(); - - /*! - * Constructs an ID3v2 tag read from \a file starting at \a tagOffset. - * \a factory specifies which FrameFactory will be used for the construction of new frames. - * - * \note You should be able to ignore the \a factory parameter in almost all situations. - * You would want to specify your own FrameFactory subclass in the case that you are extending TagLib - * to support additional frame types, which would be incorporated into your factory. - * - * \see FrameFactory - */ - explicit Tag(File *file, long long tagOffset, const FrameFactory *factory = FrameFactory::instance()); - - /*! - * Destroys this Tag instance. - */ - ~Tag() override; - - // Reimplementations. - - String title() const override; - String artist() const override; - String album() const override; - String comment() const override; - String genre() const override; - unsigned int year() const override; - unsigned int track() const override; - PictureMap pictures() const override; - - void setTitle(const String &s) override; - void setArtist(const String &s) override; - void setAlbum(const String &s) override; - void setComment(const String &s) override; - void setGenre(const String &s) override; - void setYear(unsigned int i) override; - void setTrack(unsigned int i) override; - void setPictures(const PictureMap &l) override; - - bool isEmpty() const override; - - /*! - * Returns a pointer to the tag's header. - */ - Header *header() const; - - /*! - * Returns a pointer to the tag's extended header or null if there is no extended header. - */ - ExtendedHeader *extendedHeader() const; - - /*! - * Returns a reference to the frame list map. This is an FrameListMap of all of the frames in the tag. - * - * This is the most convenient structure for accessing the tag's frames. - * Many frame types allow multiple instances of the same frame type so this is a map of lists. - * In most cases however there will only be a single frame of a certain type. - * - * Let's say for instance that you wanted to access the frame for total beats per minute -- the TBPM frame. - * - * \code - * TagLib::MPEG::File f("foo.mp3"); - * - * // Check to make sure that it has an ID3v2 tag - * - * if(f.ID3v2Tag()) { - * - * // Get the list of frames for a specific frame type - * - * TagLib::ID3v2::FrameList l = f.ID3v2Tag()->frameListMap()["TBPM"]; - * - * if(!l.isEmpty()) - * std::cout << l.front()->toString() << std::endl; - * } - * - * \endcode - * - * \warning You should not modify this data structure directly, instead use addFrame() and removeFrame(). - * - * \see frameList() - */ - const FrameListMap &frameListMap() const; - - /*! - * Returns a reference to the frame list. - * This is an FrameList of all of the frames in the tag in the order that they were parsed. - * - * This can be useful if for example you want iterate over the tag's frames in the order that they occur in the tag. - * - * \warning You should not modify this data structure directly, instead use addFrame() and removeFrame(). - */ - const FrameList &frameList() const; - - /*! - * Returns the frame list for frames with the id \a frameID or an empty list if there are no frames of that type. - * This is just a convenience and is equivalent to: - * - * \code - * frameListMap()[frameID]; - * \endcode - * - * \see frameListMap() - */ - const FrameList &frameList(const ByteVector &frameID) const; - - /*! - * Add a frame to the tag. At this point the tag takes ownership of the frame and will handle freeing its memory. - * - * \note Using this method will invalidate any pointers on the list returned by frameList() - */ - void addFrame(Frame *frame); - - /*! - * Remove a frame from the tag. - * If \a del is true the frame's memory will be freed; if it is false, it must be deleted by the user. - * - * \note Using this method will invalidate any pointers on the list returned by frameList() - */ - void removeFrame(Frame *frame, bool del = true); - - /*! - * Remove all frames of type \a id from the tag and free their memory. - * - * \note Using this method will invalidate any pointers on the list returned by frameList() - */ - void removeFrames(const ByteVector &id); - - /*! - * Implements the unified property interface -- export function. - * This function does some work to translate the hard-specified ID3v2 - * frame types into a free-form string-to-stringlist PropertyMap: - * - if ID3v2 frame ID is known by Frame::frameIDToKey(), the returned key is used - * - if the frame ID is "TXXX" (user text frame), the description() is used as key - * - if the frame ID is "WXXX" (user url frame), - * - if the description is empty or "URL", the key "URL" is used - * - otherwise, the key "URL:" is used; - * - if the frame ID is "COMM" (comments frame), - * - if the description is empty or "COMMENT", the key "COMMENT" is used - * - otherwise, the key "COMMENT:" is used; - * - if the frame ID is "USLT" (unsynchronized lyrics), - * - if the description is empty or "LYRICS", the key "LYRICS" is used - * - otherwise, the key "LYRICS:" is used; - * - if the frame ID is "TIPL" (involved peoples list), and if all the - * roles defined in the frame are known in TextIdentificationFrame::involvedPeopleMap(), - * then "=" will be contained in the returned object for each - * - if the frame ID is "TMCL" (musician credit list), then - * "PERFORMER:=" will be contained in the returned PropertyMap for each defined musician - * In any other case, the unsupportedData() of the returned object will contain - * the frame's ID and, in case of a frame ID which is allowed to appear more than - * once, the description, separated by a "/". - * - */ - PropertyMap properties() const override; - - /*! - * Removes unsupported frames given by \a properties. The elements of - * \a properties must be taken from properties().unsupportedData(); they - * are of one of the following forms: - * - a four-character frame ID, if the ID3 specification allows only one - * frame with that ID (thus, the frame is uniquely determined) - * - frameID + "/" + description(), when the ID is one of "TXXX", "WXXX", - * "COMM", or "USLT", - * - "UNKNOWN/" + frameID, for frames that could not be parsed by TagLib. - * In that case, *all* unknown frames with the given ID will be removed. - */ - void removeUnsupportedProperties(const StringList &properties) override; - - /*! - * Implements the unified property interface -- import function. - * See the comments in properties(). - */ - PropertyMap setProperties(const PropertyMap &) override; - - /*! - * Render the tag back to binary data, suitable to be written to disk. - * - * The \a version parameter specifies whether ID3v2.4 (default) or ID3v2.3 should be used. - */ - ByteVector render(Version version = ID3v2::v4) const; - - /*! - * Gets the current string handler that decides how the "Latin-1" data will be converted to and from binary data. - * - */ - static Strawberry_TagLib::TagLib::StringHandler const *latin1StringHandler(); - - /*! - * Sets the string handler that decides how the "Latin-1" data will be converted to and from binary data. - * If the parameter \a handler is null, the previous handler is released and default ISO-8859-1 handler is restored. - * - * \note The caller is responsible for deleting the previous handler as needed after it is released. - * - * \see Latin1StringHandler - */ - static void setLatin1StringHandler(const Strawberry_TagLib::TagLib::StringHandler *handler); - - protected: - /*! - * Reads data from the file specified in the constructor. - * It does basic parsing of the data in the largest chunks. - * It partitions the tag into the Header, the body of the tag - * (which contains the ExtendedHeader and frames) and Footer. - */ - void read(); - - /*! - * This is called by read to parse the body of the tag. It determines if an - * extended header exists and adds frames to the FrameListMap. - */ - void parse(const ByteVector &data); - - /*! - * Sets the value of the text frame with the Frame ID \a id to \a value. - * If the frame does not exist, it is created. - */ - void setTextFrame(const ByteVector &id, const String &value); - - /*! - * Dowgrade frames from ID3v2.4 (used internally and by default) to ID3v2.3 - */ - void downgradeFrames(FrameList *existingFrames, FrameList *newFrames) const; - - private: - Tag(const Tag&); - Tag &operator=(const Tag&); - - class TagPrivate; - TagPrivate *d; -}; - -} // namespace ID3v2 -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/mpegfile.cpp b/3rdparty/taglib/mpeg/mpegfile.cpp deleted file mode 100644 index 2f014e4a..00000000 --- a/3rdparty/taglib/mpeg/mpegfile.cpp +++ /dev/null @@ -1,532 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tagunion.h" -#include "tagutils.h" -#include "id3v2tag.h" -#include "id3v2header.h" -#include "id3v1tag.h" -#include "apefooter.h" -#include "apetag.h" -#include "tdebug.h" - -#include "mpegfile.h" -#include "mpegheader.h" -#include "mpegutils.h" -#include "tpropertymap.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { -enum { - ID3v2Index = 0, - APEIndex = 1, - ID3v1Index = 2 -}; -} - -class MPEG::File::FilePrivate { - public: - explicit FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) : ID3v2FrameFactory(frameFactory), - ID3v2Location(-1), - ID3v2OriginalSize(0), - APELocation(-1), - APEOriginalSize(0), - ID3v1Location(-1), - properties(nullptr) {} - - ~FilePrivate() { - delete properties; - } - - const ID3v2::FrameFactory *ID3v2FrameFactory; - - long long ID3v2Location; - long long ID3v2OriginalSize; - - long long APELocation; - long long APEOriginalSize; - - long long ID3v1Location; - - TripleTagUnion tag; - - AudioProperties *properties; -}; - -//////////////////////////////////////////////////////////////////////////////// -// static members -//////////////////////////////////////////////////////////////////////////////// - -namespace { -// Dummy file class to make a stream work with MPEG::Header. - -class AdapterFile : public Strawberry_TagLib::TagLib::File { - public: - explicit AdapterFile(IOStream *stream) : File(stream) {} - - Tag *tag() const override { return nullptr; } - AudioProperties *audioProperties() const override { return nullptr; } - bool save() override { return false; } -}; -} // namespace - -bool MPEG::File::isSupported(IOStream *stream) { - - if (!stream || !stream->isOpen()) - return false; - - // An MPEG file has MPEG frame headers. An ID3v2 tag may precede. - - // MPEG frame headers are really confusing with irrelevant binary data. - // So we check if a frame header is really valid. - - long long headerOffset; - const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true, &headerOffset); - - if (buffer.isEmpty()) - return false; - - const long long originalPosition = stream->tell(); - AdapterFile file(stream); - - for (unsigned int i = 0; i < buffer.size() - 1; ++i) { - if (isFrameSync(buffer, i)) { - const Header header(&file, headerOffset + i, true); - if (header.isValid()) { - stream->seek(originalPosition); - return true; - } - } - } - - stream->seek(originalPosition); - return false; - -} - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - - -MPEG::File::File(FileName fileName, bool readProperties, AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::File(fileName), d(new FilePrivate()) { - - if (isOpen()) - read(readProperties); - -} - -MPEG::File::File(FileName fileName, ID3v2::FrameFactory *frameFactory, bool readProperties, AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::File(fileName), d(new FilePrivate(frameFactory)) { - - if (isOpen()) - read(readProperties); - -} - -MPEG::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory, bool readProperties, AudioProperties::ReadStyle) : TagLib::File(stream), d(new FilePrivate(frameFactory)) { - if (isOpen()) - read(readProperties); -} - -MPEG::File::~File() { - delete d; -} - -Strawberry_TagLib::TagLib::Tag *MPEG::File::tag() const { - return &d->tag; -} - -PropertyMap MPEG::File::setProperties(const PropertyMap &properties) { - - // update ID3v1 tag if it exists, but ignore the return value - - if (ID3v1Tag()) - ID3v1Tag()->setProperties(properties); - - return ID3v2Tag(true)->setProperties(properties); - -} - -MPEG::AudioProperties *MPEG::File::audioProperties() const { - return d->properties; -} - -bool MPEG::File::save() { - return save(AllTags); -} - -bool MPEG::File::save(int tags, StripTags strip, ID3v2::Version version, DuplicateTags duplicate) { - - if (readOnly()) { - debug("MPEG::File::save() -- File is read only."); - return false; - } - - // Create the tags if we've been asked to. - - if (duplicate == Duplicate) { - - // Copy the values from the tag that does exist into the new tag, - // except if the existing tag is to be stripped. - - if ((tags & ID3v2) && ID3v1Tag() && !(strip == StripOthers && !(tags & ID3v1))) - Tag::duplicate(ID3v1Tag(), ID3v2Tag(true), false); - - if ((tags & ID3v1) && d->tag[ID3v2Index] && !(strip == StripOthers && !(tags & ID3v2))) - Tag::duplicate(ID3v2Tag(), ID3v1Tag(true), false); - } - - // Remove all the tags not going to be saved. - - if (strip == StripOthers) - File::strip(~tags, false); - - if (ID3v2 & tags) { - - if (ID3v2Tag() && !ID3v2Tag()->isEmpty()) { - - // ID3v2 tag is not empty. Update the old one or create a new one. - - if (d->ID3v2Location < 0) - d->ID3v2Location = 0; - - const ByteVector data = ID3v2Tag()->render(version); - insert(data, d->ID3v2Location, d->ID3v2OriginalSize); - - if (d->APELocation >= 0) - d->APELocation += (static_cast(data.size()) - d->ID3v2OriginalSize); - - if (d->ID3v1Location >= 0) - d->ID3v1Location += (static_cast(data.size()) - d->ID3v2OriginalSize); - - d->ID3v2OriginalSize = data.size(); - } - else { - - // ID3v2 tag is empty. Remove the old one. - - File::strip(ID3v2, false); - } - } - - if (ID3v1 & tags) { - - if (ID3v1Tag() && !ID3v1Tag()->isEmpty()) { - - // ID3v1 tag is not empty. Update the old one or create a new one. - - if (d->ID3v1Location >= 0) { - seek(d->ID3v1Location); - } - else { - seek(0, End); - d->ID3v1Location = tell(); - } - - writeBlock(ID3v1Tag()->render()); - } - else { - - // ID3v1 tag is empty. Remove the old one. - - File::strip(ID3v1, false); - } - } - - if (APE & tags) { - - if (APETag() && !APETag()->isEmpty()) { - - // APE tag is not empty. Update the old one or create a new one. - - if (d->APELocation < 0) { - if (d->ID3v1Location >= 0) - d->APELocation = d->ID3v1Location; - else - d->APELocation = length(); - } - - const ByteVector data = APETag()->render(); - insert(data, d->APELocation, d->APEOriginalSize); - - if (d->ID3v1Location >= 0) - d->ID3v1Location += (static_cast(data.size()) - d->APEOriginalSize); - - d->APEOriginalSize = data.size(); - } - else { - - // APE tag is empty. Remove the old one. - - File::strip(APE, false); - } - } - - return true; - -} - -ID3v2::Tag *MPEG::File::ID3v2Tag(bool create) { - return d->tag.access(ID3v2Index, create); -} - -ID3v1::Tag *MPEG::File::ID3v1Tag(bool create) { - return d->tag.access(ID3v1Index, create); -} - -APE::Tag *MPEG::File::APETag(bool create) { - return d->tag.access(APEIndex, create); -} - -bool MPEG::File::strip(int tags, bool freeMemory) { - - if (readOnly()) { - debug("MPEG::File::strip() - Cannot strip tags from a read only file."); - return false; - } - - if ((tags & ID3v2) && d->ID3v2Location >= 0) { - removeBlock(d->ID3v2Location, d->ID3v2OriginalSize); - - if (d->APELocation >= 0) - d->APELocation -= d->ID3v2OriginalSize; - - if (d->ID3v1Location >= 0) - d->ID3v1Location -= d->ID3v2OriginalSize; - - d->ID3v2Location = -1; - d->ID3v2OriginalSize = 0; - - if (freeMemory) - d->tag.set(ID3v2Index, nullptr); - } - - if ((tags & ID3v1) && d->ID3v1Location >= 0) { - truncate(d->ID3v1Location); - - d->ID3v1Location = -1; - - if (freeMemory) - d->tag.set(ID3v1Index, nullptr); - } - - if ((tags & APE) && d->APELocation >= 0) { - removeBlock(d->APELocation, d->APEOriginalSize); - - if (d->ID3v1Location >= 0) - d->ID3v1Location -= d->APEOriginalSize; - - d->APELocation = -1; - d->APEOriginalSize = 0; - - if (freeMemory) - d->tag.set(APEIndex, nullptr); - } - - return true; - -} - -long long MPEG::File::nextFrameOffset(long long position) { - - ByteVector frameSyncBytes(2, '\0'); - - while (true) { - seek(position); - const ByteVector buffer = readBlock(bufferSize()); - if (buffer.isEmpty()) - return -1; - - for (size_t i = 0; i < buffer.size(); ++i) { - frameSyncBytes[0] = frameSyncBytes[1]; - frameSyncBytes[1] = buffer[i]; - if (isFrameSync(frameSyncBytes)) { - const Header header(this, position + i - 1, true); - if (header.isValid()) - return position + i - 1; - } - } - - position += bufferSize(); - } - -} - -long long MPEG::File::previousFrameOffset(long long position) { - - ByteVector frameSyncBytes(2, '\0'); - - while (position > 0) { - const long long bufferLength = std::min(position, bufferSize()); - position -= bufferLength; - - seek(position); - const ByteVector buffer = readBlock(bufferLength); - - for (int i = buffer.size() - 1; i >= 0; --i) { - frameSyncBytes[1] = frameSyncBytes[0]; - frameSyncBytes[0] = buffer[i]; - if (isFrameSync(frameSyncBytes)) { - const Header header(this, position + i, true); - if (header.isValid()) - return position + i + header.frameLength(); - } - } - } - - return -1; - -} - -long long MPEG::File::firstFrameOffset() { - - long long position = 0; - - if (hasID3v2Tag()) - position = d->ID3v2Location + ID3v2Tag()->header()->completeTagSize(); - - return nextFrameOffset(position); - -} - -long long MPEG::File::lastFrameOffset() { - - long long position; - - if (hasAPETag()) - position = d->APELocation - 1; - else if (hasID3v1Tag()) - position = d->ID3v1Location - 1; - else - position = length(); - - return previousFrameOffset(position); - -} - -bool MPEG::File::hasID3v1Tag() const { - return (d->ID3v1Location >= 0); -} - -bool MPEG::File::hasID3v2Tag() const { - return (d->ID3v2Location >= 0); -} - -bool MPEG::File::hasAPETag() const { - return (d->APELocation >= 0); -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void MPEG::File::read(bool readProperties) { - - // Look for an ID3v2 tag - - d->ID3v2Location = findID3v2(); - - if (d->ID3v2Location >= 0) { - d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory)); - d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize(); - } - - // Look for an ID3v1 tag - - d->ID3v1Location = Utils::findID3v1(this); - - if (d->ID3v1Location >= 0) - d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); - - // Look for an APE tag - - d->APELocation = Utils::findAPE(this, d->ID3v1Location); - - if (d->APELocation >= 0) { - d->tag.set(APEIndex, new APE::Tag(this, d->APELocation)); - d->APEOriginalSize = APETag()->footer()->completeTagSize(); - d->APELocation = d->APELocation + APE::Footer::size() - d->APEOriginalSize; - } - - if (readProperties) - d->properties = new AudioProperties(this); - - // Make sure that we have our default tag types available. - - ID3v2Tag(true); - ID3v1Tag(true); - -} - -long long MPEG::File::findID3v2() { - - if (!isValid()) - return -1; - - // An ID3v2 tag or MPEG frame is most likely be at the beginning of the file. - - const ByteVector headerID = ID3v2::Header::fileIdentifier(); - - seek(0); - if (readBlock(headerID.size()) == headerID) - return 0; - - const Header firstHeader(this, 0, true); - if (firstHeader.isValid()) - return -1; - - // Look for an ID3v2 tag until reaching the first valid MPEG frame. - - ByteVector frameSyncBytes(2, '\0'); - ByteVector tagHeaderBytes(3, '\0'); - long long position = 0; - - while (true) { - seek(position); - const ByteVector buffer = readBlock(bufferSize()); - if (buffer.isEmpty()) - return -1; - - for (size_t i = 0; i < buffer.size(); ++i) { - frameSyncBytes[0] = frameSyncBytes[1]; - frameSyncBytes[1] = buffer[i]; - if (isFrameSync(frameSyncBytes)) { - const Header header(this, position + i - 1, true); - if (header.isValid()) - return -1; - } - - tagHeaderBytes[0] = tagHeaderBytes[1]; - tagHeaderBytes[1] = tagHeaderBytes[2]; - tagHeaderBytes[2] = buffer[i]; - if (tagHeaderBytes == headerID) - return position + i - 2; - } - - position += bufferSize(); - } - -} diff --git a/3rdparty/taglib/mpeg/mpegfile.h b/3rdparty/taglib/mpeg/mpegfile.h deleted file mode 100644 index 0399726e..00000000 --- a/3rdparty/taglib/mpeg/mpegfile.h +++ /dev/null @@ -1,305 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_MPEGFILE_H -#define TAGLIB_MPEGFILE_H - -#include "taglib_export.h" -#include "tfile.h" -#include "tag.h" - -#include "mpegproperties.h" - -#include "id3v2.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -namespace ID3v2 { -class Tag; -class FrameFactory; -} // namespace ID3v2 -namespace ID3v1 { -class Tag; -} -namespace APE { -class Tag; -} - -//! An implementation of TagLib::File with MPEG (MP3) specific methods - -namespace MPEG { - -//! An MPEG file class with some useful methods specific to MPEG - -/*! - * This implements the generic TagLib::File API and additionally provides - * access to properties that are distinct to MPEG files, notably access - * to the different ID3 tags. - */ - -class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File { - public: - /*! - * This set of flags is used for various operations and is suitable for being OR-ed together. - */ - enum TagTypes { - //! Empty set. Matches no tag types. - NoTags = 0x0000, - //! Matches ID3v1 tags. - ID3v1 = 0x0001, - //! Matches ID3v2 tags. - ID3v2 = 0x0002, - //! Matches APE tags. - APE = 0x0004, - //! Matches all tag types. - AllTags = 0xffff - }; - - /*! - * Constructs an MPEG file from \a file. If \a readProperties is true the - * file's audio properties will also be read. - * - * \note In the current implementation, \a propertiesStyle is ignored. - * - * \deprecated This constructor will be dropped in favor of the one below - * in a future version. - */ - explicit File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Constructs an MPEG file from \a file. - * If \a readProperties is true the file's audio properties will also be read. - * - * If this file contains and ID3v2 tag the frames will be created using - * \a frameFactory. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(FileName fileName, ID3v2::FrameFactory *frameFactory, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Constructs an MPEG file from \a stream. - * If \a readProperties is true the file's audio properties will also be read. - * - * \note TagLib will *not* take ownership of the stream, the caller is responsible for deleting it after the File object. - * - * If this file contains and ID3v2 tag the frames will be created using \a frameFactory. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(IOStream *stream, ID3v2::FrameFactory *frameFactory, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Destroys this instance of the File. - */ - ~File() override; - - /*! - * Returns a pointer to a tag that is the union of the ID3v2 and ID3v1 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 an ID3v1 tag, - * but a union of the two this pointer may not be cast to the specific tag types. - * - * \see ID3v1Tag() - * \see ID3v2Tag() - * \see APETag() - */ - Tag *tag() const override; - - /*! - * Implements the writing part of the unified tag dictionary interface. - * In order to avoid problems with deprecated tag formats, - * this method always creates an ID3v2 tag if necessary. - * If an ID3v1 tag exists, it will be updated as well, within the limitations of that format. - * The returned PropertyMap refers to the ID3v2 tag only. - */ - PropertyMap setProperties(const PropertyMap&) override; - - /*! - * Returns the MPEG::AudioProperties for this file. - * If no audio properties were read then this will return a null pointer. - */ - AudioProperties *audioProperties() const override; - - /*! - * Save the file. If at least one tag -- ID3v1 or ID3v2 -- 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 parameterized save call below. - * - * \see save(int tags) - */ - bool save() override; - - /*! - * Save the file. This will attempt to save all of the tag types that are - * specified by OR-ing together TagTypes values. - * - * \a strip can be set to strip all tags except those in \a tags. - * Those tags will not be modified in memory, and thus remain valid. - * - * \a version specifies the ID3v2 version to be used for writing tags. - * By default, the latest standard, ID3v2.4 is used. - * - * If \a duplicate is set to DuplicateTags and at least one tag -- ID3v1 - * or ID3v2 -- exists this will duplicate its content into the other tag. - */ - bool save(int tags, StripTags strip = StripOthers, ID3v2::Version version = ID3v2::v4, DuplicateTags duplicate = Duplicate); - - /*! - * Returns a pointer to the ID3v2 tag of the file. - * - * If \a create is false (the default) this may return a null pointer - * if there is no valid ID3v2 tag. - * If \a create is true it will create an ID3v2 tag if one does not exist and returns a valid pointer. - * - * \note This may return a valid pointer regardless of whether or not the file on disk has an ID3v2 tag. - * Use hasID3v2Tag() to check if the file on disk actually has an ID3v2 tag. - * - * \note The Tag is still owned by the MPEG::File and should not be deleted by the user. - * It will be deleted when the file (object) is destroyed. - * - * \see hasID3v2Tag() - */ - ID3v2::Tag *ID3v2Tag(bool create = false); - - /*! - * Returns a pointer to the ID3v1 tag of the file. - * - * If \a create is false (the default) this may return a null pointer - * if there is no valid ID3v1 tag. - * If \a create is true it will create an ID3v1 tag if one does not exist and returns a valid pointer. - * - * \note This may return a valid pointer regardless of whether or not the file on disk has an ID3v1 tag. - * Use hasID3v1Tag() to check if the file on disk actually has an ID3v1 tag. - * - * \note The Tag is still owned by the MPEG::File and should not be deleted by the user. - * It will be deleted when the file (object) is destroyed. - * - * \see hasID3v1Tag() - */ - ID3v1::Tag *ID3v1Tag(bool create = false); - - /*! - * Returns a pointer to the APE tag of the file. - * - * If \a create is false (the default) this may return a null pointer if there is no valid APE tag. - * If \a create is true it will create an APE tag if one does not exist and returns a valid pointer. - * - * \note This may return a valid pointer regardless of whether or not the file on disk has an APE tag. - * Use hasAPETag() to check if the file on disk actually has an APE tag. - * - * \note The Tag is still owned by the MPEG::File and should not be deleted by the user. - * It will be deleted when the file (object) is destroyed. - * - * \see hasAPETag() - */ - APE::Tag *APETag(bool create = false); - - /*! - * This will strip the tags that match the OR-ed together TagTypes from the file. - * By default it strips all tags. It returns true if the tags are successfully stripped. - * - * If \a freeMemory is true the ID3 and APE tags will be deleted and pointers to them will be invalidated. - * - * \note This will update the file immediately. - */ - bool strip(int tags = AllTags, bool freeMemory = true); - - /*! - * Returns the position in the file of the first MPEG frame. - */ - long long firstFrameOffset(); - - /*! - * Returns the position in the file of the next MPEG frame, using the current position as start - */ - long long nextFrameOffset(long long position); - - /*! - * Returns the position in the file of the previous MPEG frame, using the current position as start - */ - long long previousFrameOffset(long long position); - - /*! - * Returns the position in the file of the last MPEG frame. - */ - long long lastFrameOffset(); - - /*! - * Returns whether or not the file on disk actually has an ID3v1 tag. - * - * \see ID3v1Tag() - */ - bool hasID3v1Tag() const; - - /*! - * Returns whether or not the file on disk actually has an ID3v2 tag. - * - * \see ID3v2Tag() - */ - bool hasID3v2Tag() const; - - /*! - * Returns whether or not the file on disk actually has an APE tag. - * - * \see APETag() - */ - bool hasAPETag() const; - - /*! - * Returns whether or not the given \a stream can be opened as an MPEG 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); - long long findID3v2(); - - class FilePrivate; - FilePrivate *d; -}; - -} // namespace MPEG -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/mpegheader.cpp b/3rdparty/taglib/mpeg/mpegheader.cpp deleted file mode 100644 index 58c7c399..00000000 --- a/3rdparty/taglib/mpeg/mpegheader.cpp +++ /dev/null @@ -1,298 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "tbytevector.h" -#include "tstring.h" -#include "tfile.h" -#include "tdebug.h" - -#include "mpegheader.h" -#include "mpegutils.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { -struct HeaderData { - bool isValid; - MPEG::Header::Version version; - int layer; - bool protectionEnabled; - int bitrate; - int sampleRate; - bool isPadded; - MPEG::Header::ChannelMode channelMode; - bool isCopyrighted; - bool isOriginal; - int frameLength; - int samplesPerFrame; -}; -} // namespace - -class MPEG::Header::HeaderPrivate { - public: - explicit HeaderPrivate() : data(new HeaderData()) { - data->isValid = false; - data->layer = 0; - data->version = Version1; - data->protectionEnabled = false; - data->sampleRate = 0; - data->isPadded = false; - data->channelMode = Stereo; - data->isCopyrighted = false; - data->isOriginal = false; - data->frameLength = 0; - data->samplesPerFrame = 0; - } - std::shared_ptr data; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -MPEG::Header::Header(File *file, long long offset, bool checkLength) : d(new HeaderPrivate()) { - parse(file, offset, checkLength); -} - -MPEG::Header::Header(const Header &h) : d(new HeaderPrivate(*h.d)) {} - -MPEG::Header::~Header() { - delete d; -} - -bool MPEG::Header::isValid() const { - return d->data->isValid; -} - -MPEG::Header::Version MPEG::Header::version() const { - return d->data->version; -} - -int MPEG::Header::layer() const { - return d->data->layer; -} - -bool MPEG::Header::protectionEnabled() const { - return d->data->protectionEnabled; -} - -int MPEG::Header::bitrate() const { - return d->data->bitrate; -} - -int MPEG::Header::sampleRate() const { - return d->data->sampleRate; -} - -bool MPEG::Header::isPadded() const { - return d->data->isPadded; -} - -MPEG::Header::ChannelMode MPEG::Header::channelMode() const { - return d->data->channelMode; -} - -bool MPEG::Header::isCopyrighted() const { - return d->data->isCopyrighted; -} - -bool MPEG::Header::isOriginal() const { - return d->data->isOriginal; -} - -int MPEG::Header::frameLength() const { - return d->data->frameLength; -} - -int MPEG::Header::samplesPerFrame() const { - return d->data->samplesPerFrame; -} - -MPEG::Header &MPEG::Header::operator=(const Header &h) { - - *d = *h.d; - return *this; - -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void MPEG::Header::parse(File *file, long long offset, bool checkLength) { - - file->seek(offset); - const ByteVector data = file->readBlock(4); - - if (data.size() < 4) { - debug("MPEG::Header::parse() -- data is too short for an MPEG frame header."); - return; - } - - // Check for the MPEG synch bytes. - - if (!isFrameSync(data)) { - debug("MPEG::Header::parse() -- MPEG header did not match MPEG synch."); - return; - } - - // Set the MPEG version - - const int versionBits = (static_cast(data[1]) >> 3) & 0x03; - - if (versionBits == 0) - d->data->version = Version2_5; - else if (versionBits == 2) - d->data->version = Version2; - else if (versionBits == 3) - d->data->version = Version1; - else - return; - - // Set the MPEG layer - - const int layerBits = (static_cast(data[1]) >> 1) & 0x03; - - if (layerBits == 1) - d->data->layer = 3; - else if (layerBits == 2) - d->data->layer = 2; - else if (layerBits == 3) - d->data->layer = 1; - else - return; - - d->data->protectionEnabled = (static_cast(data[1] & 0x01) == 0); - - // Set the bitrate - - static const int bitrates[2][3][16] = { - { - // Version 1 - { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // layer 1 - { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, // layer 2 - { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 } // layer 3 - }, - { - // Version 2 or 2.5 - { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 }, // layer 1 - { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // layer 2 - { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 } // layer 3 - } - }; - - const int versionIndex = (d->data->version == Version1) ? 0 : 1; - const int layerIndex = (d->data->layer > 0) ? d->data->layer - 1 : 0; - - // The bitrate index is encoded as the first 4 bits of the 3rd byte, - // i.e. 1111xxxx - - const int bitrateIndex = (static_cast(data[2]) >> 4) & 0x0F; - - d->data->bitrate = bitrates[versionIndex][layerIndex][bitrateIndex]; - - if (d->data->bitrate == 0) - return; - - // Set the sample rate - - static const int sampleRates[3][4] = { - { 44100, 48000, 32000, 0 }, // Version 1 - { 22050, 24000, 16000, 0 }, // Version 2 - { 11025, 12000, 8000, 0 } // Version 2.5 - }; - - // The sample rate index is encoded as two bits in the 3nd byte, i.e. xxxx11xx - - const int samplerateIndex = (static_cast(data[2]) >> 2) & 0x03; - - d->data->sampleRate = sampleRates[d->data->version][samplerateIndex]; - - if (d->data->sampleRate == 0) { - return; - } - - // The channel mode is encoded as a 2 bit value at the end of the 3nd byte, - // i.e. xxxxxx11 - - d->data->channelMode = static_cast((static_cast(data[3]) >> 6) & 0x03); - - // TODO: Add mode extension for completeness - - d->data->isOriginal = ((static_cast(data[3]) & 0x04) != 0); - d->data->isCopyrighted = ((static_cast(data[3]) & 0x08) != 0); - d->data->isPadded = ((static_cast(data[2]) & 0x02) != 0); - - // Samples per frame - - static const int samplesPerFrame[3][2] = { - // MPEG1, 2/2.5 - { 384, 384 }, // Layer I - { 1152, 1152 }, // Layer II - { 1152, 576 } // Layer III - }; - - d->data->samplesPerFrame = samplesPerFrame[layerIndex][versionIndex]; - - // Calculate the frame length - - static const int paddingSize[3] = { 4, 1, 1 }; - - d->data->frameLength = d->data->samplesPerFrame * d->data->bitrate * 125 / d->data->sampleRate; - - if (d->data->isPadded) - d->data->frameLength += paddingSize[layerIndex]; - - if (checkLength) { - - // Check if the frame length has been calculated correctly, or the next frame - // header is right next to the end of this frame. - - // The MPEG versions, layers and sample rates of the two frames should be - // consistent. Otherwise, we assume that either or both of the frames are - // broken. - - file->seek(offset + d->data->frameLength); - const ByteVector nextData = file->readBlock(4); - - if (nextData.size() < 4) - return; - - const unsigned int HeaderMask = 0xfffe0c00; - - const unsigned int header = data.toUInt32BE(0) & HeaderMask; - const unsigned int nextHeader = nextData.toUInt32BE(0) & HeaderMask; - - if (header != nextHeader) - return; - } - - // Now that we're done parsing, set this to be a valid frame. - - d->data->isValid = true; - -} diff --git a/3rdparty/taglib/mpeg/mpegheader.h b/3rdparty/taglib/mpeg/mpegheader.h deleted file mode 100644 index 8600344d..00000000 --- a/3rdparty/taglib/mpeg/mpegheader.h +++ /dev/null @@ -1,172 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_MPEGHEADER_H -#define TAGLIB_MPEGHEADER_H - -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -class ByteVector; -class File; - -namespace MPEG { - -//! An implementation of MP3 frame headers - -/*! - * This is an implementation of MPEG Layer III headers. The API follows more - * or less the binary format of these headers. I've used - * this - * document as a reference. - */ - -class TAGLIB_EXPORT Header { - public: - /*! - * Parses an MPEG header based on \a file and \a offset. - * - * \note If \a checkLength is true, this requires the next MPEG frame to - * check if the frame length is parsed and calculated correctly. - * So it's suitable for seeking for the first valid frame. - */ - explicit Header(File *file, long long offset, bool checkLength = true); - - /*! - * Does a shallow copy of \a h. - */ - Header(const Header &h); - - /*! - * Destroys this Header instance. - */ - virtual ~Header(); - - /*! - * Returns true if the frame is at least an appropriate size and has legal values. - */ - bool isValid() const; - - /*! - * The MPEG Version. - */ - enum Version { - //! MPEG Version 1 - Version1 = 0, - //! MPEG Version 2 - Version2 = 1, - //! MPEG Version 2.5 - Version2_5 = 2 - }; - - /*! - * Returns the MPEG Version of the header. - */ - Version version() const; - - /*! - * Returns the layer version. This will be between the values 1-3. - */ - int layer() const; - - /*! - * Returns true if the MPEG protection bit is enabled. - */ - bool protectionEnabled() const; - - /*! - * Returns the bitrate encoded in the header. - */ - int bitrate() const; - - /*! - * Returns the sample rate in Hz. - */ - int sampleRate() const; - - /*! - * Returns true if the frame is padded. - */ - bool isPadded() const; - - /*! - * There are a few combinations or one or two channel audio that are - * possible: - */ - enum ChannelMode { - //! Stereo - Stereo = 0, - //! Stereo - JointStereo = 1, - //! Dual Mono - DualChannel = 2, - //! Mono - SingleChannel = 3 - }; - - /*! - * Returns the channel mode for this frame. - */ - ChannelMode channelMode() const; - - /*! - * Returns true if the copyrighted bit is set. - */ - bool isCopyrighted() const; - - /*! - * Returns true if the "original" bit is set. - */ - bool isOriginal() const; - - /*! - * Returns the frame length in bytes. - */ - int frameLength() const; - - /*! - * Returns the number of frames per sample. - */ - int samplesPerFrame() const; - - /*! - * Makes a shallow copy of the header. - */ - Header &operator=(const Header &h); - - private: - void parse(File *file, long long offset, bool checkLength); - - class HeaderPrivate; - HeaderPrivate *d; -}; - -} // namespace MPEG -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/mpegproperties.cpp b/3rdparty/taglib/mpeg/mpegproperties.cpp deleted file mode 100644 index 31c914d8..00000000 --- a/3rdparty/taglib/mpeg/mpegproperties.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "tdebug.h" -#include "tstring.h" - -#include "audioproperties.h" -#include "mpegproperties.h" -#include "mpegfile.h" -#include "xingheader.h" -#include "apetag.h" -#include "apefooter.h" - -using namespace Strawberry_TagLib::TagLib; - -class MPEG::AudioProperties::AudioPropertiesPrivate { - public: - explicit AudioPropertiesPrivate() : length(0), - bitrate(0), - sampleRate(0), - channels(0), - layer(0), - version(Header::Version1), - channelMode(Header::Stereo), - protectionEnabled(false), - isCopyrighted(false), - isOriginal(false) {} - - std::unique_ptr xingHeader; - int length; - int bitrate; - int sampleRate; - int channels; - int layer; - Header::Version version; - Header::ChannelMode channelMode; - bool protectionEnabled; - bool isCopyrighted; - bool isOriginal; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -MPEG::AudioProperties::AudioProperties(File *file, ReadStyle) : Strawberry_TagLib::TagLib::AudioProperties(), d(new AudioPropertiesPrivate()) { - read(file); -} - -MPEG::AudioProperties::~AudioProperties() { - delete d; -} - -int MPEG::AudioProperties::lengthInSeconds() const { - return d->length / 1000; -} - -int MPEG::AudioProperties::lengthInMilliseconds() const { - return d->length; -} - -int MPEG::AudioProperties::bitrate() const { - return d->bitrate; -} - -int MPEG::AudioProperties::sampleRate() const { - return d->sampleRate; -} - -int MPEG::AudioProperties::channels() const { - return d->channels; -} - -const MPEG::XingHeader *MPEG::AudioProperties::xingHeader() const { - return d->xingHeader.get(); -} - -MPEG::Header::Version MPEG::AudioProperties::version() const { - return d->version; -} - -int MPEG::AudioProperties::layer() const { - return d->layer; -} - -bool MPEG::AudioProperties::protectionEnabled() const { - return d->protectionEnabled; -} - -MPEG::Header::ChannelMode MPEG::AudioProperties::channelMode() const { - return d->channelMode; -} - -bool MPEG::AudioProperties::isCopyrighted() const { - return d->isCopyrighted; -} - -bool MPEG::AudioProperties::isOriginal() const { - return d->isOriginal; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void MPEG::AudioProperties::read(File *file) { - - // Only the first valid frame is required if we have a VBR header. - - const long long firstFrameOffset = file->firstFrameOffset(); - if (firstFrameOffset < 0) { - debug("MPEG::AudioProperties::read() -- Could not find an MPEG frame in the stream."); - return; - } - - const Header firstHeader(file, firstFrameOffset, false); - - // Check for a VBR header that will help us in gathering information about a - // VBR stream. - - file->seek(firstFrameOffset); - d->xingHeader.reset(new XingHeader(file->readBlock(firstHeader.frameLength()))); - if (!d->xingHeader->isValid()) - d->xingHeader.reset(); - - if (d->xingHeader && firstHeader.samplesPerFrame() > 0 && firstHeader.sampleRate() > 0) { - - // Read the length and the bitrate from the VBR header. - - const double timePerFrame = firstHeader.samplesPerFrame() * 1000.0 / firstHeader.sampleRate(); - const double length = timePerFrame * d->xingHeader->totalFrames(); - - d->length = static_cast(length + 0.5); - d->bitrate = static_cast(d->xingHeader->totalSize() * 8.0 / length + 0.5); - } - else if (firstHeader.bitrate() > 0) { - - // Since there was no valid VBR header found, we hope that we're in a constant - // bitrate file. - - // TODO: Make this more robust with audio property detection for VBR without a - // Xing header. - - d->bitrate = firstHeader.bitrate(); - - // Look for the last MPEG audio frame to calculate the stream length. - - const long long lastFrameOffset = file->lastFrameOffset(); - if (lastFrameOffset < 0) { - debug("MPEG::AudioProperties::read() -- Could not find an MPEG frame in the stream."); - return; - } - - const Header lastHeader(file, lastFrameOffset, false); - const long long streamLength = lastFrameOffset - firstFrameOffset + lastHeader.frameLength(); - if (streamLength > 0) - d->length = static_cast(streamLength * 8.0 / d->bitrate + 0.5); - } - - d->sampleRate = firstHeader.sampleRate(); - d->channels = firstHeader.channelMode() == Header::SingleChannel ? 1 : 2; - d->version = firstHeader.version(); - d->layer = firstHeader.layer(); - d->protectionEnabled = firstHeader.protectionEnabled(); - d->channelMode = firstHeader.channelMode(); - d->isCopyrighted = firstHeader.isCopyrighted(); - d->isOriginal = firstHeader.isOriginal(); - -} diff --git a/3rdparty/taglib/mpeg/mpegproperties.h b/3rdparty/taglib/mpeg/mpegproperties.h deleted file mode 100644 index 7bb2e0bd..00000000 --- a/3rdparty/taglib/mpeg/mpegproperties.h +++ /dev/null @@ -1,134 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_MPEGPROPERTIES_H -#define TAGLIB_MPEGPROPERTIES_H - -#include "taglib_export.h" -#include "audioproperties.h" - -#include "mpegheader.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace MPEG { - -class File; -class XingHeader; - -//! An implementation of audio property reading for MP3 - -/*! - * This reads the data from an MPEG Layer III stream found in the AudioProperties API. - */ - -class TAGLIB_EXPORT AudioProperties : public Strawberry_TagLib::TagLib::AudioProperties { - public: - /*! - * Create an instance of MPEG::AudioProperties with the data read from the MPEG::File \a file. - */ - explicit AudioProperties(File *file, ReadStyle style = Average); - - /*! - * Destroys this MPEG AudioProperties instance. - */ - ~AudioProperties() override; - - /*! - * Returns the length of the file in seconds. The length is rounded down to the nearest whole second. - * - * \see lengthInMilliseconds() - */ - int lengthInSeconds() const override; - - /*! - * Returns the length of the file in milliseconds. - * - * \see lengthInSeconds() - */ - int lengthInMilliseconds() const override; - - /*! - * Returns the average bit rate of the file in kb/s. - */ - int bitrate() const override; - - /*! - * Returns the sample rate in Hz. - */ - int sampleRate() const override; - - /*! - * Returns the number of audio channels. - */ - int channels() const override; - - /*! - * Returns a pointer to the Xing/VBRI header if one exists or null if no Xing/VBRI header was found. - */ - const XingHeader *xingHeader() const; - - /*! - * Returns the MPEG Version of the file. - */ - Header::Version version() const; - - /*! - * Returns the layer version. This will be between the values 1-3. - */ - int layer() const; - - /*! - * Returns true if the MPEG protection bit is enabled. - */ - bool protectionEnabled() const; - - /*! - * Returns the channel mode for this frame. - */ - Header::ChannelMode channelMode() const; - - /*! - * Returns true if the copyrighted bit is set. - */ - bool isCopyrighted() const; - - /*! - * Returns true if the "original" bit is set. - */ - bool isOriginal() const; - - private: - void read(File *file); - - class AudioPropertiesPrivate; - AudioPropertiesPrivate *d; -}; - -} // namespace MPEG -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/mpeg/mpegutils.h b/3rdparty/taglib/mpeg/mpegutils.h deleted file mode 100644 index 3fc37ce2..00000000 --- a/3rdparty/taglib/mpeg/mpegutils.h +++ /dev/null @@ -1,61 +0,0 @@ -/*************************************************************************** - copyright : (C) 2015 by Tsuda Kageyu - email : tsuda.kageyu@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_MPEGUTILS_H -#define TAGLIB_MPEGUTILS_H - -// THIS FILE IS NOT A PART OF THE TAGLIB API - -#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header - -namespace Strawberry_TagLib { -namespace TagLib { -namespace MPEG { -namespace { - -/*! - * MPEG frames can be recognized by the bit pattern 11111111 111, so the - * first byte is easy to check for, however checking to see if the second byte - * starts with \e 111 is a bit more tricky, hence these functions. - * - * \note This does not check the length of the vector, since this is an - * internal utility function. - */ -inline bool isFrameSync(const ByteVector &bytes, unsigned int offset = 0) { - // 0xFF in the second byte is possible in theory, but it's very unlikely. - - const unsigned char b1 = bytes[offset + 0]; - const unsigned char b2 = bytes[offset + 1]; - return (b1 == 0xFF && b2 != 0xFF && (b2 & 0xE0) == 0xE0); -} - -} // namespace -} // namespace MPEG -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif - -#endif diff --git a/3rdparty/taglib/mpeg/xingheader.cpp b/3rdparty/taglib/mpeg/xingheader.cpp deleted file mode 100644 index c555a670..00000000 --- a/3rdparty/taglib/mpeg/xingheader.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/*************************************************************************** - copyright : (C) 2003 by Ismael Orenstein - email : orenstein@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tbytevector.h" -#include "tstring.h" -#include "tdebug.h" - -#include "xingheader.h" -#include "mpegfile.h" - -using namespace Strawberry_TagLib::TagLib; - -class MPEG::XingHeader::XingHeaderPrivate { - public: - explicit XingHeaderPrivate() : frames(0), size(0), type(MPEG::XingHeader::Invalid) {} - - unsigned int frames; - unsigned int size; - - MPEG::XingHeader::HeaderType type; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -MPEG::XingHeader::XingHeader(const ByteVector &data) : d(new XingHeaderPrivate()) { - parse(data); -} - -MPEG::XingHeader::~XingHeader() { - delete d; -} - -bool MPEG::XingHeader::isValid() const { - return (d->type != Invalid && d->frames > 0 && d->size > 0); -} - -unsigned int MPEG::XingHeader::totalFrames() const { - return d->frames; -} - -unsigned int MPEG::XingHeader::totalSize() const { - return d->size; -} - -MPEG::XingHeader::HeaderType MPEG::XingHeader::type() const { - return d->type; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void MPEG::XingHeader::parse(const ByteVector &data) { - - // Look for a Xing header. - - size_t offset = data.find("Xing"); - if (offset == ByteVector::npos()) - offset = data.find("Info"); - - if (offset != ByteVector::npos()) { - - // Xing header found. - - if (data.size() < static_cast(offset + 16)) { - debug("MPEG::XingHeader::parse() -- Xing header found but too short."); - return; - } - - if ((data[offset + 7] & 0x03) != 0x03) { - debug("MPEG::XingHeader::parse() -- Xing header doesn't contain the required information."); - return; - } - - d->frames = data.toUInt32BE(offset + 8); - d->size = data.toUInt32BE(offset + 12); - d->type = Xing; - } - else { - - // Xing header not found. Then look for a VBRI header. - - offset = data.find("VBRI"); - - if (offset != ByteVector::npos()) { - - // VBRI header found. - - if (data.size() < static_cast(offset + 32)) { - debug("MPEG::XingHeader::parse() -- VBRI header found but too short."); - return; - } - - d->frames = data.toUInt32BE(offset + 14); - d->size = data.toUInt32BE(offset + 10); - d->type = VBRI; - } - } - -} diff --git a/3rdparty/taglib/mpeg/xingheader.h b/3rdparty/taglib/mpeg/xingheader.h deleted file mode 100644 index e17d6bd5..00000000 --- a/3rdparty/taglib/mpeg/xingheader.h +++ /dev/null @@ -1,118 +0,0 @@ -/*************************************************************************** - copyright : (C) 2003 by Ismael Orenstein - email : orenstein@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_XINGHEADER_H -#define TAGLIB_XINGHEADER_H - -#include "mpegheader.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -class ByteVector; - -namespace MPEG { - -class File; - -//! An implementation of the Xing/VBRI headers - -/*! - * This is a minimalistic implementation of the Xing/VBRI VBR headers. - * Xing/VBRI headers are often added to VBR (variable bit rate) MP3 streams - * to make it easy to compute the length and quality of a VBR stream. - * Our implementation is only concerned with the total size of the stream - * (so that we can calculate the total playing time and the average bitrate). - * It uses - * this text and the XMMS sources as references. - */ - -class TAGLIB_EXPORT XingHeader { - public: - /*! - * The type of the VBR header. - */ - enum HeaderType { - /*! - * Invalid header or no VBR header found. - */ - Invalid = 0, - - /*! - * Xing header. - */ - Xing = 1, - - /*! - * VBRI header. - */ - VBRI = 2, - }; - - /*! - * Parses an Xing/VBRI header based on \a data which contains the entire first MPEG frame. - */ - explicit XingHeader(const ByteVector &data); - - /*! - * Destroy this XingHeader instance. - */ - virtual ~XingHeader(); - - /*! - * Returns true if the data was parsed properly and if there is a valid Xing/VBRI header present. - */ - bool isValid() const; - - /*! - * Returns the total number of frames. - */ - unsigned int totalFrames() const; - - /*! - * Returns the total size of stream in bytes. - */ - unsigned int totalSize() const; - - /*! - * Returns the type of the VBR header. - */ - HeaderType type() const; - - private: - XingHeader(const XingHeader&); - XingHeader &operator=(const XingHeader&); - - void parse(const ByteVector &data); - - class XingHeaderPrivate; - XingHeaderPrivate *d; -}; -} // namespace MPEG -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/ogg/flac/oggflacfile.cpp b/3rdparty/taglib/ogg/flac/oggflacfile.cpp deleted file mode 100644 index 8477525d..00000000 --- a/3rdparty/taglib/ogg/flac/oggflacfile.cpp +++ /dev/null @@ -1,299 +0,0 @@ -/*************************************************************************** - copyright : (C) 2004-2005 by Allan Sandfeld Jensen - email : kde@carewolf.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 "tstring.h" -#include "tdebug.h" -#include "tpropertymap.h" -#include "tagutils.h" - -#include "xiphcomment.h" -#include "oggflacfile.h" - -using namespace Strawberry_TagLib::TagLib; -using Strawberry_TagLib::TagLib::FLAC::AudioProperties; - -class Ogg::FLAC::File::FilePrivate { - public: - explicit FilePrivate() : comment(nullptr), - properties(nullptr), - streamStart(0), - streamLength(0), - scanned(false), - hasXiphComment(false), - commentPacket(0) {} - - ~FilePrivate() { - delete comment; - delete properties; - } - - Ogg::XiphComment *comment; - - AudioProperties *properties; - ByteVector streamInfoData; - ByteVector xiphCommentData; - long long streamStart; - long long streamLength; - bool scanned; - - bool hasXiphComment; - int commentPacket; -}; - -//////////////////////////////////////////////////////////////////////////////// -// static members -//////////////////////////////////////////////////////////////////////////////// - -bool Ogg::FLAC::File::isSupported(IOStream *stream) { - // An Ogg FLAC file has IDs "OggS" and "fLaC" somewhere. - - const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false); - return (buffer.find("OggS") != ByteVector::npos() && buffer.find("fLaC") != ByteVector::npos()); -} - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -Ogg::FLAC::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle propertiesStyle) : Ogg::File(file), d(new FilePrivate()) { - - if (isOpen()) - read(readProperties, propertiesStyle); - -} - -Ogg::FLAC::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle propertiesStyle) : Ogg::File(stream), d(new FilePrivate()) { - - if (isOpen()) - read(readProperties, propertiesStyle); - -} - -Ogg::FLAC::File::~File() { - delete d; -} - -Ogg::XiphComment *Ogg::FLAC::File::tag() const { - return d->comment; -} - -FLAC::AudioProperties *Ogg::FLAC::File::audioProperties() const { - return d->properties; -} - - -bool Ogg::FLAC::File::save() { - - d->xiphCommentData = d->comment->render(false); - - // Create FLAC metadata-block: - - // Put the size in the first 32 bit (I assume no more than 24 bit are used) - - ByteVector v = ByteVector::fromUInt32BE(d->xiphCommentData.size()); - - // Set the type of the metadata-block to be a Xiph / Vorbis comment - - v[0] = 4; - - // Append the comment-data after the 32 bit header - - v.append(d->xiphCommentData); - - // Save the packet at the old spot - // FIXME: Use padding if size is increasing - - setPacket(d->commentPacket, v); - - return Ogg::File::save(); - -} - -bool Ogg::FLAC::File::hasXiphComment() const { - return d->hasXiphComment; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void Ogg::FLAC::File::read(bool readProperties, AudioProperties::ReadStyle propertiesStyle) { - - // Sanity: Check if we really have an Ogg/FLAC file - - /* - ByteVector oggHeader = packet(0); - - if (oggHeader.mid(28,4) != "fLaC") { - debug("Ogg::FLAC::File::read() -- Not an Ogg/FLAC file"); - setValid(false); - return; - }*/ - - // Look for FLAC metadata, including vorbis comments - - scan(); - - if (!d->scanned) { - setValid(false); - return; - } - - - if (d->hasXiphComment) - d->comment = new Ogg::XiphComment(xiphCommentData()); - else - d->comment = new Ogg::XiphComment(); - - - if (readProperties) - d->properties = new AudioProperties(streamInfoData(), streamLength(), propertiesStyle); - -} - -ByteVector Ogg::FLAC::File::streamInfoData() { - scan(); - return d->streamInfoData; -} - -ByteVector Ogg::FLAC::File::xiphCommentData() { - scan(); - return d->xiphCommentData; -} - -long long Ogg::FLAC::File::streamLength() { - scan(); - return d->streamLength; -} - -void Ogg::FLAC::File::scan() { - - // Scan the metadata pages - - if (d->scanned) - return; - - if (!isValid()) - return; - - int ipacket = 0; - long long overhead = 0; - - ByteVector metadataHeader = packet(ipacket); - if (metadataHeader.isEmpty()) - return; - - if (!metadataHeader.startsWith("fLaC")) { - // FLAC 1.1.2+ - // See https://xiph.org/flac/ogg_mapping.html for the header specification. - if (metadataHeader.size() < 13) - return; - - if (metadataHeader[0] != 0x7f) - return; - - if (metadataHeader.mid(1, 4) != "FLAC") - return; - - if (metadataHeader[5] != 1 && metadataHeader[6] != 0) - return; // not version 1.0 - - if (metadataHeader.mid(9, 4) != "fLaC") - return; - - metadataHeader = metadataHeader.mid(13); - } - else { - // FLAC 1.1.0 & 1.1.1 - metadataHeader = packet(++ipacket); - } - - ByteVector header = metadataHeader.mid(0, 4); - if (header.size() != 4) { - debug("Ogg::FLAC::File::scan() -- Invalid Ogg/FLAC metadata header"); - return; - } - - // Header format (from spec): - // <1> Last-metadata-block flag - // <7> BLOCK_TYPE - // 0 : STREAMINFO - // 1 : PADDING - // .. - // 4 : VORBIS_COMMENT - // .. - // <24> Length of metadata to follow - - char blockType = header[0] & 0x7f; - bool lastBlock = (header[0] & 0x80) != 0; - unsigned int length = header.toUInt24BE(1); - overhead += length; - - // Sanity: First block should be the stream_info metadata - - if (blockType != 0) { - debug("Ogg::FLAC::File::scan() -- Invalid Ogg/FLAC stream"); - return; - } - - d->streamInfoData = metadataHeader.mid(4, length); - - // Search through the remaining metadata - - while (!lastBlock) { - metadataHeader = packet(++ipacket); - header = metadataHeader.mid(0, 4); - if (header.size() != 4) { - debug("Ogg::FLAC::File::scan() -- Invalid Ogg/FLAC metadata header"); - return; - } - - blockType = header[0] & 0x7f; - lastBlock = (header[0] & 0x80) != 0; - length = header.toUInt24BE(1); - overhead += length; - - if (blockType == 1) { - // debug("Ogg::FLAC::File::scan() -- Padding found"); - } - else if (blockType == 4) { - // debug("Ogg::FLAC::File::scan() -- Vorbis-comments found"); - d->xiphCommentData = metadataHeader.mid(4, length); - d->hasXiphComment = true; - d->commentPacket = ipacket; - } - else if (blockType > 5) { - debug("Ogg::FLAC::File::scan() -- Unknown metadata block"); - } - } - - // End of metadata, now comes the datastream - d->streamStart = overhead; - d->streamLength = File::length() - d->streamStart; - - d->scanned = true; - -} diff --git a/3rdparty/taglib/ogg/flac/oggflacfile.h b/3rdparty/taglib/ogg/flac/oggflacfile.h deleted file mode 100644 index aa4348f6..00000000 --- a/3rdparty/taglib/ogg/flac/oggflacfile.h +++ /dev/null @@ -1,151 +0,0 @@ -/*************************************************************************** - copyright : (C) 2004 by Allan Sandfeld Jensen - email : kde@carewolf.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_OGGFLACFILE_H -#define TAGLIB_OGGFLACFILE_H - -#include "taglib_export.h" -#include "oggfile.h" -#include "xiphcomment.h" - -#include "flacproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -class Tag; - -namespace Ogg { - -//! An implementation of Ogg FLAC metadata - -/*! - * This is implementation of FLAC metadata for Ogg FLAC files. For "pure" - * FLAC files look under the FLAC hierarchy. - * - * Unlike "pure" FLAC-files, Ogg FLAC only supports Xiph-comments, - * while the audio-properties are the same. - */ -namespace FLAC { - -using Strawberry_TagLib::TagLib::FLAC::AudioProperties; - -//! An implementation of TagLib::File with Ogg/FLAC specific methods - -/*! - * This implements and provides an interface for Ogg/FLAC 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 Ogg FLAC files. - */ - -class TAGLIB_EXPORT File : public Ogg::File { - public: - /*! - * Constructs an Ogg/FLAC file from \a file. - * If \a readProperties is true the file's audio properties will also be read. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Constructs an Ogg/FLAC file from \a stream. - * If \a readProperties is true the file's audio properties will also be read. - * - * \note TagLib will *not* take ownership of the stream, the caller is responsible for deleting it after the File object. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Destroys this instance of the File. - */ - ~File() override; - - /*! - * Returns the Tag for this file. This will always be a XiphComment. - * - * \note This always returns a valid pointer regardless of whether or not the file on disk has a XiphComment. - * Use hasXiphComment() to check if the file on disk actually has a XiphComment. - * - * \note The Tag is still owned by the FLAC::File and should not be deleted by the user. - * It will be deleted when the file (object) is destroyed. - * - * \see hasXiphComment() - */ - XiphComment *tag() const override; - - /*! - * Returns the FLAC::AudioProperties for this file. - * If no audio properties were read then this will return a null pointer. - */ - AudioProperties *audioProperties() const override; - - /*! - * Save the file. This will primarily save and update the XiphComment. - * Returns true if the save is successful. - */ - bool save() override; - - /*! - * Returns the length of the audio-stream, used by FLAC::AudioProperties for calculating the bitrate. - */ - long long streamLength(); - - /*! - * Returns whether or not the file on disk actually has a XiphComment. - * - * \see tag() - */ - bool hasXiphComment() const; - - /*! - * Check if the given \a stream can be opened as an Ogg FLAC 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); - void scan(); - ByteVector streamInfoData(); - ByteVector xiphCommentData(); - - class FilePrivate; - FilePrivate *d; -}; - -} // namespace FLAC -} // namespace Ogg -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/ogg/oggfile.cpp b/3rdparty/taglib/ogg/oggfile.cpp deleted file mode 100644 index 51db601f..00000000 --- a/3rdparty/taglib/ogg/oggfile.cpp +++ /dev/null @@ -1,304 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tbytevectorlist.h" -#include "tmap.h" -#include "tstring.h" -#include "tdebug.h" - -#include "oggfile.h" -#include "oggpage.h" -#include "oggpageheader.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { -// Returns the first packet index of the right next page to the given one. -unsigned int nextPacketIndex(const Ogg::Page *page) { - - if (page->header()->lastPacketCompleted()) - return page->firstPacketIndex() + page->packetCount(); - else - return page->firstPacketIndex() + page->packetCount() - 1; - -} -} // namespace - -class Ogg::File::FilePrivate { - public: - explicit FilePrivate() : streamSerialNumber(0), firstPageHeader(nullptr), lastPageHeader(nullptr) { - pages.setAutoDelete(true); - } - - ~FilePrivate() { - delete firstPageHeader; - delete lastPageHeader; - } - - unsigned int streamSerialNumber; - List pages; - PageHeader *firstPageHeader; - PageHeader *lastPageHeader; - Map dirtyPackets; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -Ogg::File::~File() { - delete d; -} - -ByteVector Ogg::File::packet(unsigned int i) { - - // Check to see if we're called setPacket() for this packet since the last save: - - if (d->dirtyPackets.contains(i)) - return d->dirtyPackets[i]; - - // If we haven't indexed the page where the packet we're interested in starts, - // begin reading pages until we have. - - if (!readPages(i)) { - debug("Ogg::File::packet() -- Could not find the requested packet."); - return ByteVector(); - } - - // Look for the first page in which the requested packet starts. - - List::ConstIterator it = d->pages.begin(); - while ((*it)->containsPacket(i) == Page::DoesNotContainPacket) - ++it; - - // If the packet is completely contained in the first page that it's in. - - // If the packet is *not* completely contained in the first page that it's a - // part of then that packet trails off the end of the page. Continue appending - // the pages' packet data until we hit a page that either does not end with the - // packet that we're fetching or where the last packet is complete. - - ByteVector packet = (*it)->packets()[i - (*it)->firstPacketIndex()]; - - while (nextPacketIndex(*it) <= i) { - ++it; - packet.append((*it)->packets().front()); - } - - return packet; - -} - -void Ogg::File::setPacket(unsigned int i, const ByteVector &p) { - - if (!readPages(i)) { - debug("Ogg::File::setPacket() -- Could not set the requested packet."); - return; - } - - d->dirtyPackets[i] = p; - -} - -const Ogg::PageHeader *Ogg::File::firstPageHeader() { - - if (!d->firstPageHeader) { - const long long firstPageHeaderOffset = find("OggS"); - if (firstPageHeaderOffset < 0) - return nullptr; - - d->firstPageHeader = new PageHeader(this, firstPageHeaderOffset); - } - - return d->firstPageHeader->isValid() ? d->firstPageHeader : nullptr; - -} - -const Ogg::PageHeader *Ogg::File::lastPageHeader() { - - if (!d->lastPageHeader) { - const long long lastPageHeaderOffset = rfind("OggS"); - if (lastPageHeaderOffset < 0) - return nullptr; - - d->lastPageHeader = new PageHeader(this, lastPageHeaderOffset); - } - - return d->lastPageHeader->isValid() ? d->lastPageHeader : nullptr; - -} - -bool Ogg::File::save() { - - if (readOnly()) { - debug("Ogg::File::save() - Cannot save to a read only file."); - return false; - } - - Map::ConstIterator it; - for (it = d->dirtyPackets.begin(); it != d->dirtyPackets.end(); ++it) - writePacket(it->first, it->second); - - d->dirtyPackets.clear(); - - return true; - -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -Ogg::File::File(FileName file) : Strawberry_TagLib::TagLib::File(file), d(new FilePrivate()) {} - -Ogg::File::File(IOStream *stream) : Strawberry_TagLib::TagLib::File(stream), d(new FilePrivate()) {} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -bool Ogg::File::readPages(unsigned int i) { - - while (true) { - unsigned int packetIndex; - long long offset; - - if (d->pages.isEmpty()) { - packetIndex = 0; - offset = find("OggS"); - if (offset < 0) - return false; - } - else { - const Page *page = d->pages.back(); - packetIndex = nextPacketIndex(page); - offset = page->fileOffset() + page->size(); - } - - // Enough pages have been fetched. - - if (packetIndex > i) - return true; - - // Read the next page and add it to the page list. - - Page *nextPage = new Page(this, offset); - if (!nextPage->header()->isValid()) { - delete nextPage; - return false; - } - - nextPage->setFirstPacketIndex(packetIndex); - d->pages.append(nextPage); - - if (nextPage->header()->lastPageOfStream()) - return false; - } - -} - -void Ogg::File::writePacket(unsigned int i, const ByteVector &packet) { - - if (!readPages(i)) { - debug("Ogg::File::writePacket() -- Could not find the requested packet."); - return; - } - - // Look for the pages where the requested packet should belong to. - - List::ConstIterator it = d->pages.begin(); - while ((*it)->containsPacket(i) == Page::DoesNotContainPacket) - ++it; - - const Page *firstPage = *it; - - while (nextPacketIndex(*it) <= i) - ++it; - - const Page *lastPage = *it; - - // Replace the requested packet and create new pages to replace the located pages. - - ByteVectorList packets = firstPage->packets(); - packets[i - firstPage->firstPacketIndex()] = packet; - - if (firstPage != lastPage && lastPage->packetCount() > 1) { - ByteVectorList lastPagePackets = lastPage->packets(); - lastPagePackets.erase(lastPagePackets.begin()); - packets.append(lastPagePackets); - } - - // TODO: This pagination method isn't accurate for what's being done here. - // This should account for real possibilities like non-aligned packets and such. - - List pages = Page::paginate(packets, - Page::SinglePagePerGroup, - firstPage->header()->streamSerialNumber(), - firstPage->pageSequenceNumber(), - firstPage->header()->firstPacketContinued(), - lastPage->header()->lastPacketCompleted()); - pages.setAutoDelete(true); - - // Write the pages. - - ByteVector data; - for (it = pages.begin(); it != pages.end(); ++it) - data.append((*it)->render()); - - const long long originalOffset = firstPage->fileOffset(); - const long long originalLength = lastPage->fileOffset() + lastPage->size() - originalOffset; - - insert(data, originalOffset, originalLength); - - // Renumber the following pages if the pages have been split or merged. - - const int numberOfNewPages = pages.back()->pageSequenceNumber() - lastPage->pageSequenceNumber(); - - if (numberOfNewPages != 0) { - long long pageOffset = originalOffset + data.size(); - - while (true) { - Page page(this, pageOffset); - if (!page.header()->isValid()) - break; - - page.setPageSequenceNumber(page.pageSequenceNumber() + numberOfNewPages); - const ByteVector data2 = page.render(); - - seek(pageOffset + 18); - writeBlock(data2.mid(18, 8)); - - if (page.header()->lastPageOfStream()) - break; - - pageOffset += page.size(); - } - } - - // Discard all the pages to keep them up-to-date by fetching them again. - - d->pages.clear(); - -} diff --git a/3rdparty/taglib/ogg/oggfile.h b/3rdparty/taglib/ogg/oggfile.h deleted file mode 100644 index 9b54db24..00000000 --- a/3rdparty/taglib/ogg/oggfile.h +++ /dev/null @@ -1,118 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "taglib_export.h" -#include "tfile.h" -#include "tbytevectorlist.h" - -#ifndef TAGLIB_OGGFILE_H -#define TAGLIB_OGGFILE_H - -namespace Strawberry_TagLib { -namespace TagLib { - -//! A namespace for the classes used by Ogg-based metadata files - -namespace Ogg { - -class PageHeader; - -//! An implementation of Strawberry_TagLib::TagLib::File with some helpers for Ogg based formats - -/*! - * This is an implementation of Ogg file page and packet rendering and is of use to Ogg based formats. - * While the API is small this handles the non-trivial details of breaking up an Ogg stream into packets and makes - * these available (via subclassing) to the codec meta data implementations. - */ - -class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File { - public: - ~File() override; - - /*! - * Returns the packet contents for the i-th packet (starting from zero) in the Ogg bitstream. - * - * \warning This requires reading at least the packet header for every page up to the requested page. - */ - ByteVector packet(unsigned int i); - - /*! - * Sets the packet with index \a i to the value \a p. - */ - void setPacket(unsigned int i, const ByteVector &p); - - /*! - * Returns a pointer to the PageHeader for the first page in the stream or null if the page could not be found. - */ - const PageHeader *firstPageHeader(); - - /*! - * Returns a pointer to the PageHeader for the last page in the stream or null if the page could not be found. - */ - const PageHeader *lastPageHeader(); - - bool save() override; - - protected: - /*! - * Constructs an Ogg file from \a file. - * - * \note This constructor is protected since Ogg::File shouldn't be instantiated directly but rather should be used through the codec specific subclasses. - */ - explicit File(FileName file); - - /*! - * Constructs an Ogg file from \a stream. - * - * \note This constructor is protected since Ogg::File shouldn't be instantiated directly but rather should be used through the codec specific subclasses. - * - * \note TagLib will *not* take ownership of the stream, the caller is responsible for deleting it after the File object. - */ - explicit File(IOStream *stream); - - private: - File(const File &); - File &operator=(const File &); - - /*! - * Reads the pages from the beginning of the file until enough to compose - * the requested packet. - */ - bool readPages(unsigned int i); - - /*! - * Writes the requested packet to the file. - */ - void writePacket(unsigned int i, const ByteVector &packet); - - class FilePrivate; - FilePrivate *d; -}; - -} // namespace Ogg -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/ogg/oggpage.cpp b/3rdparty/taglib/ogg/oggpage.cpp deleted file mode 100644 index 85b33a91..00000000 --- a/3rdparty/taglib/ogg/oggpage.cpp +++ /dev/null @@ -1,332 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "tstring.h" -#include "tdebug.h" - -#include "oggpage.h" -#include "oggpageheader.h" -#include "oggfile.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { -ByteVector checksum(const ByteVector &v) { - const unsigned int crcTable[256] = { - 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, - 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, - 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, - 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, - 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, - 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, - 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, - 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, - 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, - 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, - 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, - 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, - 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, - 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, - 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, - 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, - 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, - 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, - 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, - 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, - 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, - 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, - 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, - 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, - 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, - 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, - 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, - 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, - 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, - 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, - 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, - 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, - 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, - 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, - 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, - 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, - 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, - 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, - 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, - 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, - 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, - 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, - 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 - }; - - unsigned int sum = 0; - for (ByteVector::ConstIterator it = v.begin(); it != v.end(); ++it) - sum = (sum << 8) ^ crcTable[((sum >> 24) & 0xff) ^ static_cast(*it)]; - - return ByteVector::fromUInt32LE(sum); -} -} // namespace - -class Ogg::Page::PagePrivate { - public: - explicit PagePrivate(File *f = nullptr, long long pageOffset = -1) : file(f), fileOffset(pageOffset), header(f, pageOffset), firstPacketIndex(-1) {} - - File *file; - long long fileOffset; - PageHeader header; - int firstPacketIndex; - ByteVectorList packets; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -Ogg::Page::Page(Ogg::File *file, long long pageOffset) : d(new PagePrivate(file, pageOffset)) {} - -Ogg::Page::~Page() { - delete d; -} - -long long Ogg::Page::fileOffset() const { - return d->fileOffset; -} - -const Ogg::PageHeader *Ogg::Page::header() const { - return &d->header; -} - -int Ogg::Page::pageSequenceNumber() const { - return d->header.pageSequenceNumber(); -} - -void Ogg::Page::setPageSequenceNumber(int sequenceNumber) { - d->header.setPageSequenceNumber(sequenceNumber); -} - -int Ogg::Page::firstPacketIndex() const { - return d->firstPacketIndex; -} - -void Ogg::Page::setFirstPacketIndex(int index) { - d->firstPacketIndex = index; -} - -Ogg::Page::ContainsPacketFlags Ogg::Page::containsPacket(int index) const { - - const int lastPacketIndex = d->firstPacketIndex + packetCount() - 1; - if (index < d->firstPacketIndex || index > lastPacketIndex) - return DoesNotContainPacket; - - ContainsPacketFlags flags = DoesNotContainPacket; - - if (index == d->firstPacketIndex) - flags = ContainsPacketFlags(flags | BeginsWithPacket); - - if (index == lastPacketIndex) - flags = ContainsPacketFlags(flags | EndsWithPacket); - - // If there's only one page and it's complete: - - if (packetCount() == 1 && - !d->header.firstPacketContinued() && - d->header.lastPacketCompleted()) { - flags = ContainsPacketFlags(flags | CompletePacket); - } - - // Or if there is more than one page and the page is - // (a) the first page and it's complete or - // (b) the last page and it's complete or - // (c) a page in the middle. - else if (packetCount() > 1 && - ((flags & BeginsWithPacket && !d->header.firstPacketContinued()) || - (flags & EndsWithPacket && d->header.lastPacketCompleted()) || - (!(flags & BeginsWithPacket) && !(flags & EndsWithPacket)))) { - flags = ContainsPacketFlags(flags | CompletePacket); - } - - return flags; - -} - -unsigned int Ogg::Page::packetCount() const { - return d->header.packetSizes().size(); -} - -ByteVectorList Ogg::Page::packets() const { - - if (!d->packets.isEmpty()) - return d->packets; - - ByteVectorList l; - - if (d->file && d->header.isValid()) { - - d->file->seek(d->fileOffset + d->header.size()); - - List packetSizes = d->header.packetSizes(); - - List::ConstIterator it = packetSizes.begin(); - for (; it != packetSizes.end(); ++it) - l.append(d->file->readBlock(*it)); - } - else - debug("Ogg::Page::packets() -- attempting to read packets from an invalid page."); - - return l; - -} - -int Ogg::Page::size() const { - return d->header.size() + d->header.dataSize(); -} - -ByteVector Ogg::Page::render() const { - - ByteVector data; - - data.append(d->header.render()); - - if (d->packets.isEmpty()) { - if (d->file) { - d->file->seek(d->fileOffset + d->header.size()); - data.append(d->file->readBlock(d->header.dataSize())); - } - else - debug("Ogg::Page::render() -- this page is empty!"); - } - else { - ByteVectorList::ConstIterator it = d->packets.begin(); - for (; it != d->packets.end(); ++it) - data.append(*it); - } - - // Compute and set the checksum for the Ogg page. The checksum is taken over - // the entire page with the 4 bytes reserved for the checksum zeroed and then - // inserted in bytes 22-25 of the page header. - - const ByteVector sum = checksum(data); - std::copy(sum.begin(), sum.end(), data.begin() + 22); - - return data; - -} - -List Ogg::Page::paginate(const ByteVectorList &packets, PaginationStrategy strategy, unsigned int streamSerialNumber, int firstPage, bool firstPacketContinued, bool lastPacketCompleted, bool containsLastPacket) { - - // SplitSize must be a multiple of 255 in order to get the lacing values right - // create pages of about 8KB each - - static const unsigned int SplitSize = 32 * 255; - - // Force repagination if the segment table will exceed the size limit. - - if (strategy != Repaginate) { - - size_t tableSize = 0; - for (ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it) - tableSize += it->size() / 255 + 1; - - if (tableSize > 255) - strategy = Repaginate; - } - - List l; - - // Handle creation of multiple pages with appropriate pagination. - - if (strategy == Repaginate) { - - int pageIndex = firstPage; - - for (ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it) { - - const bool lastPacketInList = (it == --packets.end()); - - // mark very first packet? - - bool continued = (firstPacketContinued && it == packets.begin()); - unsigned int pos = 0; - - while (pos < it->size()) { - - const bool lastSplit = (pos + SplitSize >= it->size()); - - ByteVectorList packetList; - packetList.append(it->mid(pos, SplitSize)); - - l.append(new Page(packetList, - streamSerialNumber, - pageIndex, - continued, - lastSplit && (lastPacketInList ? lastPacketCompleted : true), - lastSplit && (containsLastPacket && lastPacketInList))); - pageIndex++; - continued = true; - - pos += SplitSize; - } - } - } - else { - l.append(new Page(packets, - streamSerialNumber, - firstPage, - firstPacketContinued, - lastPacketCompleted, - containsLastPacket)); - } - - return l; - -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -Ogg::Page::Page(const ByteVectorList &packets, unsigned int streamSerialNumber, int pageNumber, bool firstPacketContinued, bool lastPacketCompleted, bool containsLastPacket) : d(new PagePrivate()) { - - d->header.setFirstPageOfStream(pageNumber == 0 && !firstPacketContinued); - d->header.setLastPageOfStream(containsLastPacket); - d->header.setFirstPacketContinued(firstPacketContinued); - d->header.setLastPacketCompleted(lastPacketCompleted); - d->header.setStreamSerialNumber(streamSerialNumber); - d->header.setPageSequenceNumber(pageNumber); - - // Build a page from the list of packets. - - ByteVector data; - List packetSizes; - - for (ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it) { - packetSizes.append((*it).size()); - data.append(*it); - } - d->packets = packets; - d->header.setPacketSizes(packetSizes); - -} diff --git a/3rdparty/taglib/ogg/oggpage.h b/3rdparty/taglib/ogg/oggpage.h deleted file mode 100644 index f9c18a5e..00000000 --- a/3rdparty/taglib/ogg/oggpage.h +++ /dev/null @@ -1,207 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_OGGPAGE_H -#define TAGLIB_OGGPAGE_H - -#include "taglib_export.h" -#include "tbytevectorlist.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -namespace Ogg { - -class File; -class PageHeader; - -//! An implementation of Ogg pages - -/*! - * This is an implementation of the pages that make up an Ogg stream. - * This handles parsing pages and breaking them down into packets and handles - * the details of packets spanning multiple pages and pages that contain multiple packets. - * - * In most Xiph.org formats the comments are found in the first few packets, - * this however is a reasonably complete implementation of Ogg pages that - * could potentially be useful for non-meta data purposes. - */ - -class TAGLIB_EXPORT Page { - public: - /*! - * Read an Ogg page from the \a file at the position \a pageOffset. - */ - explicit Page(File *file, long long pageOffset); - - virtual ~Page(); - - /*! - * Returns the page's position within the file (in bytes). - */ - long long fileOffset() const; - - /*! - * Returns a pointer to the header for this page. This pointer will become invalid when the page is deleted. - */ - const PageHeader *header() const; - - /*! - * Returns the index of the page within the Ogg stream. This helps make it possible to determine if pages have been lost. - * - * \see setPageSequenceNumber() - */ - int pageSequenceNumber() const; - - /*! - * Sets the page's position in the stream to \a sequenceNumber. - * - * \see pageSequenceNumber() - */ - void setPageSequenceNumber(int sequenceNumber); - - /*! - * Returns the index of the first packet wholly or partially contained in this page. - * - * \see setFirstPacketIndex() - */ - int firstPacketIndex() const; - - /*! - * Sets the index of the first packet in the page. - * - * \see firstPacketIndex() - */ - void setFirstPacketIndex(int index); - - /*! - * When checking to see if a page contains a given packet this set of flags represents the possible values for that packets status in the page. - * - * \see containsPacket() - */ - enum ContainsPacketFlags { - //! No part of the packet is contained in the page - DoesNotContainPacket = 0x0000, - //! The packet is wholly contained in the page - CompletePacket = 0x0001, - //! The page starts with the given packet - BeginsWithPacket = 0x0002, - //! The page ends with the given packet - EndsWithPacket = 0x0004 - }; - - /*! - * Checks to see if the specified \a packet is contained in the current page. - * - * \see ContainsPacketFlags - */ - ContainsPacketFlags containsPacket(int index) const; - - /*! - * Returns the number of packets (whole or partial) in this page. - */ - unsigned int packetCount() const; - - /*! - * Returns a list of the packets in this page. - * - * \note Either or both the first and last packets may be only partial. - * \see PageHeader::firstPacketContinued() - */ - ByteVectorList packets() const; - - /*! - * Returns the size of the page in bytes. - */ - int size() const; - - ByteVector render() const; - - /*! - * Defines a strategy for pagination, or grouping pages into Ogg packets, for use with pagination methods. - * - * \note Yes, I'm aware that this is not a canonical "Strategy Pattern", the term was simply convenient. - */ - enum PaginationStrategy { - /*! - * Attempt to put the specified set of packets into a single Ogg packet. - * If the sum of the packet data is greater than will fit into a single - * Ogg page -- 65280 bytes -- this will fall back to repagination using - * the recommended page sizes. - */ - SinglePagePerGroup, - /*! - * Split the packet or group of packets into pages that conform to the sizes recommended in the Ogg standard. - */ - Repaginate - }; - - /*! - * Pack \a packets into Ogg pages using the \a strategy for pagination. - * The page number indicator inside of the rendered packets will start with \a firstPage and be incremented for each page rendered. - * \a containsLastPacket should be set to true if \a packets contains the - * last page in the stream and will set the appropriate flag in the last rendered Ogg page's header. - * \a streamSerialNumber should be set to the serial number for this stream. - * - * \note The "absolute granule position" is currently always zeroed using this method as this suffices for the comment headers. - * - * \warning The pages returned by this method must be deleted by the user. - * You can use List::setAutoDelete(true) to set these pages to be automatically deleted when this list passes out of scope. - * - * \see PaginationStrategy - * \see List::setAutoDelete() - */ - static List paginate(const ByteVectorList &packets, - PaginationStrategy strategy, - unsigned int streamSerialNumber, - int firstPage, - bool firstPacketContinued = false, - bool lastPacketCompleted = true, - bool containsLastPacket = false); - - protected: - /*! - * Creates an Ogg packet based on the data in \a packets. The page number for each page will be set to \a pageNumber. - */ - Page(const ByteVectorList &packets, - unsigned int streamSerialNumber, - int pageNumber, - bool firstPacketContinued = false, - bool lastPacketCompleted = true, - bool containsLastPacket = false); - - private: - Page(const Page &); - Page &operator=(const Page &); - - class PagePrivate; - PagePrivate *d; -}; - -} // namespace Ogg -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/ogg/oggpageheader.cpp b/3rdparty/taglib/ogg/oggpageheader.cpp deleted file mode 100644 index 68d791b6..00000000 --- a/3rdparty/taglib/ogg/oggpageheader.cpp +++ /dev/null @@ -1,293 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "tstring.h" -#include "tdebug.h" -#include "taglib.h" - -#include "oggpageheader.h" -#include "oggfile.h" - -using namespace Strawberry_TagLib::TagLib; - -class Ogg::PageHeader::PageHeaderPrivate { - public: - explicit PageHeaderPrivate() : isValid(false), - firstPacketContinued(false), - lastPacketCompleted(false), - firstPageOfStream(false), - lastPageOfStream(false), - absoluteGranularPosition(0), - streamSerialNumber(0), - pageSequenceNumber(-1), - size(0), - dataSize(0) {} - - bool isValid; - List packetSizes; - bool firstPacketContinued; - bool lastPacketCompleted; - bool firstPageOfStream; - bool lastPageOfStream; - long long absoluteGranularPosition; - unsigned int streamSerialNumber; - int pageSequenceNumber; - int size; - int dataSize; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -Ogg::PageHeader::PageHeader(Ogg::File *file, long long pageOffset) : d(new PageHeaderPrivate()) { - - if (file && pageOffset >= 0) - read(file, pageOffset); - -} - -Ogg::PageHeader::~PageHeader() { - delete d; -} - -bool Ogg::PageHeader::isValid() const { - return d->isValid; -} - -List Ogg::PageHeader::packetSizes() const { - return d->packetSizes; -} - -void Ogg::PageHeader::setPacketSizes(const List &sizes) { - d->packetSizes = sizes; -} - -bool Ogg::PageHeader::firstPacketContinued() const { - return d->firstPacketContinued; -} - -void Ogg::PageHeader::setFirstPacketContinued(bool continued) { - d->firstPacketContinued = continued; -} - -bool Ogg::PageHeader::lastPacketCompleted() const { - return d->lastPacketCompleted; -} - -void Ogg::PageHeader::setLastPacketCompleted(bool completed) { - d->lastPacketCompleted = completed; -} - -bool Ogg::PageHeader::firstPageOfStream() const { - return d->firstPageOfStream; -} - -void Ogg::PageHeader::setFirstPageOfStream(bool first) { - d->firstPageOfStream = first; -} - -bool Ogg::PageHeader::lastPageOfStream() const { - return d->lastPageOfStream; -} - -void Ogg::PageHeader::setLastPageOfStream(bool last) { - d->lastPageOfStream = last; -} - -long long Ogg::PageHeader::absoluteGranularPosition() const { - return d->absoluteGranularPosition; -} - -void Ogg::PageHeader::setAbsoluteGranularPosition(long long agp) { - d->absoluteGranularPosition = agp; -} - -int Ogg::PageHeader::pageSequenceNumber() const { - return d->pageSequenceNumber; -} - -void Ogg::PageHeader::setPageSequenceNumber(int sequenceNumber) { - d->pageSequenceNumber = sequenceNumber; -} - -unsigned int Ogg::PageHeader::streamSerialNumber() const { - return d->streamSerialNumber; -} - -void Ogg::PageHeader::setStreamSerialNumber(unsigned int n) { - d->streamSerialNumber = n; -} - -int Ogg::PageHeader::size() const { - return d->size; -} - -int Ogg::PageHeader::dataSize() const { - return d->dataSize; -} - -ByteVector Ogg::PageHeader::render() const { - - ByteVector data; - - // capture pattern - - data.append("OggS"); - - // stream structure version - - data.append(char(0)); - - // header type flag - - std::bitset<8> flags; - flags[0] = d->firstPacketContinued; - flags[1] = d->pageSequenceNumber == 0; - flags[2] = d->lastPageOfStream; - - data.append(char(flags.to_ulong())); - - // absolute granular position - - data.append(ByteVector::fromUInt64LE(d->absoluteGranularPosition)); - - // stream serial number - - data.append(ByteVector::fromUInt32LE(d->streamSerialNumber)); - - // page sequence number - - data.append(ByteVector::fromUInt32LE(d->pageSequenceNumber)); - - // checksum -- this is left empty and should be filled in by the Ogg::Page - // class - - data.append(ByteVector(4, 0)); - - // page segment count and page segment table - - ByteVector pageSegments = lacingValues(); - - data.append(static_cast(pageSegments.size())); - data.append(pageSegments); - - return data; - -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void Ogg::PageHeader::read(Ogg::File *file, long long pageOffset) { - - file->seek(pageOffset); - - // An Ogg page header is at least 27 bytes, so we'll go ahead and read that - // much and then get the rest when we're ready for it. - - const ByteVector data = file->readBlock(27); - - // Sanity check -- make sure that we were in fact able to read as much data as - // we asked for and that the page begins with "OggS". - - if (data.size() != 27 || !data.startsWith("OggS")) { - debug("Ogg::PageHeader::read() -- error reading page header"); - return; - } - - const std::bitset<8> flags(data[5]); - - d->firstPacketContinued = flags.test(0); - d->firstPageOfStream = flags.test(1); - d->lastPageOfStream = flags.test(2); - - d->absoluteGranularPosition = data.toInt64LE(6); - d->streamSerialNumber = data.toUInt32LE(14); - d->pageSequenceNumber = data.toUInt32LE(18); - - // Byte number 27 is the number of page segments, which is the only variable - // length portion of the page header. After reading the number of page - // segments we'll then read in the corresponding data for this count. - - int pageSegmentCount = static_cast(data[26]); - - const ByteVector pageSegments = file->readBlock(pageSegmentCount); - - // Another sanity check. - - if (pageSegmentCount < 1 || int(pageSegments.size()) != pageSegmentCount) - return; - - // The base size of an Ogg page 27 bytes plus the number of lacing values. - - d->size = 27 + pageSegmentCount; - - int packetSize = 0; - - for (int i = 0; i < pageSegmentCount; i++) { - d->dataSize += static_cast(pageSegments[i]); - packetSize += static_cast(pageSegments[i]); - - if (static_cast(pageSegments[i]) < 255) { - d->packetSizes.append(packetSize); - packetSize = 0; - } - } - - if (packetSize > 0) { - d->packetSizes.append(packetSize); - d->lastPacketCompleted = false; - } - else - d->lastPacketCompleted = true; - - d->isValid = true; - -} - -ByteVector Ogg::PageHeader::lacingValues() const { - - ByteVector data; - - for (List::ConstIterator it = d->packetSizes.begin(); it != d->packetSizes.end(); ++it) { - - // The size of a packet in an Ogg page is indicated by a series of "lacing - // values" where the sum of the values is the packet size in bytes. Each of - // these values is a byte. A value of less than 255 (0xff) indicates the end - // of the packet. - - data.resize(data.size() + (*it / 255), '\xff'); - - if (it != --d->packetSizes.end() || d->lastPacketCompleted) - data.append(static_cast(*it % 255)); - } - - return data; - -} diff --git a/3rdparty/taglib/ogg/oggpageheader.h b/3rdparty/taglib/ogg/oggpageheader.h deleted file mode 100644 index dde50b25..00000000 --- a/3rdparty/taglib/ogg/oggpageheader.h +++ /dev/null @@ -1,217 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_OGGPAGEHEADER_H -#define TAGLIB_OGGPAGEHEADER_H - -#include "tlist.h" -#include "tbytevector.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace Ogg { - -class File; - -//! An implementation of the page headers associated with each Ogg::Page - -/*! - * This class implements Ogg page headers which contain the information about Ogg pages needed to break them into packets which can be passed on to the codecs. - */ - -class TAGLIB_EXPORT PageHeader { - public: - /*! - * Reads a PageHeader from \a file starting at \a pageOffset. - * The defaults create a page with no (and as such, invalid) data that must be set later. - */ - explicit PageHeader(File *file = nullptr, long long pageOffset = -1); - - /*! - * Deletes this instance of the PageHeader. - */ - virtual ~PageHeader(); - - /*! - * Returns true if the header parsed properly and is valid. - */ - bool isValid() const; - - /*! - * Ogg pages contain a list of packets (which are used by the contained codecs). - * The sizes of these pages is encoded in the page header. This returns a list of the packet sizes in bytes. - * - * \see setPacketSizes() - */ - List packetSizes() const; - - /*! - * Sets the sizes of the packets in this page to \a sizes. Internally this updates the lacing values in the header. - * - * \see packetSizes() - */ - void setPacketSizes(const List &sizes); - - /*! - * Some packets can be continued across multiple pages. - * If the first packet in the current page is a continuation this will return true. - * If this is page starts with a new packet this will return false. - * - * \see lastPacketCompleted() - * \see setFirstPacketContinued() - */ - bool firstPacketContinued() const; - - /*! - * Sets the internal flag indicating if the first packet in this page is continued to \a continued. - * - * \see firstPacketContinued() - */ - void setFirstPacketContinued(bool continued); - - /*! - * Returns true if the last packet of this page is completely contained in this page. - * - * \see firstPacketContinued() - * \see setLastPacketCompleted() - */ - bool lastPacketCompleted() const; - - /*! - * Sets the internal flag indicating if the last packet in this page is complete to \a completed. - * - * \see lastPacketCompleted() - */ - void setLastPacketCompleted(bool completed); - - /*! - * This returns true if this is the first page of the Ogg (logical) stream. - * - * \see setFirstPageOfStream() - */ - bool firstPageOfStream() const; - - /*! - * Marks this page as the first page of the Ogg stream. - * - * \see firstPageOfStream() - */ - void setFirstPageOfStream(bool first); - - /*! - * This returns true if this is the last page of the Ogg (logical) stream. - * - * \see setLastPageOfStream() - */ - bool lastPageOfStream() const; - - /*! - * Marks this page as the last page of the Ogg stream. - * - * \see lastPageOfStream() - */ - void setLastPageOfStream(bool last); - - /*! - * A special value of containing the position of the packet to be interpreted by the codec. - * In the case of Vorbis this contains the PCM value and is used to calculate the length of the stream. - * - * \see setAbsoluteGranularPosition() - */ - long long absoluteGranularPosition() const; - - /*! - * A special value of containing the position of the packet to be interpreted by the codec. - * It is only supported here so that it may be copied from one page to another. - * - * \see absoluteGranularPosition() - */ - void setAbsoluteGranularPosition(long long agp); - - /*! - * Every Ogg logical stream is given a random serial number which is common to every page in that logical stream. - * This returns the serial number of the stream associated with this packet. - * - * \see setStreamSerialNumber() - */ - unsigned int streamSerialNumber() const; - - /*! - * Every Ogg logical stream is given a random serial number which is common to every page in that logical stream. - * This sets this pages serial number. - * This method should be used when adding new pages to a logical stream. - * - * \see streamSerialNumber() - */ - void setStreamSerialNumber(unsigned int n); - - /*! - * Returns the index of the page within the Ogg stream. This helps make it possible to determine if pages have been lost. - * - * \see setPageSequenceNumber() - */ - int pageSequenceNumber() const; - - /*! - * Sets the page's position in the stream to \a sequenceNumber. - * - * \see pageSequenceNumber() - */ - void setPageSequenceNumber(int sequenceNumber); - - /*! - * Returns the complete header size. - */ - int size() const; - - /*! - * Returns the size of the data portion of the page -- i.e. the size of the page less the header size. - */ - int dataSize() const; - - /*! - * Render the page header to binary data. - * - * \note The checksum -- bytes 22 - 25 -- will be left empty and must be filled in when rendering the entire page. - */ - ByteVector render() const; - - private: - PageHeader(const PageHeader&); - PageHeader &operator=(const PageHeader&); - - void read(Ogg::File *file, long long pageOffset); - ByteVector lacingValues() const; - - class PageHeaderPrivate; - PageHeaderPrivate *d; -}; - -} // namespace Ogg -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/ogg/opus/opusfile.cpp b/3rdparty/taglib/ogg/opus/opusfile.cpp deleted file mode 100644 index fc8825d3..00000000 --- a/3rdparty/taglib/ogg/opus/opusfile.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/*************************************************************************** - copyright : (C) 2012 by Lukáš Lalinský - email : lalinsky@gmail.com - - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - (original Vorbis implementation) - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tstring.h" -#include "tdebug.h" -#include "tpropertymap.h" -#include "tagutils.h" - -#include "opusfile.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace Strawberry_TagLib::TagLib::Ogg; - -class Opus::File::FilePrivate { - public: - explicit FilePrivate() : comment(nullptr), properties(nullptr) {} - - ~FilePrivate() { - delete comment; - delete properties; - } - - Ogg::XiphComment *comment; - AudioProperties *properties; -}; - -//////////////////////////////////////////////////////////////////////////////// -// static members -//////////////////////////////////////////////////////////////////////////////// - -bool Ogg::Opus::File::isSupported(IOStream *stream) { - - // An Opus file has IDs "OggS" and "OpusHead" somewhere. - - const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false); - return (buffer.find("OggS") != ByteVector::npos() && buffer.find("OpusHead") != ByteVector::npos()); - -} - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -Opus::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) : Ogg::File(file), d(new FilePrivate()) { - - if (isOpen()) - read(readProperties); - -} - -Opus::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle) : Ogg::File(stream), d(new FilePrivate()) { - - if (isOpen()) - read(readProperties); - -} - -Opus::File::~File() { - delete d; -} - -Ogg::XiphComment *Opus::File::tag() const { - return d->comment; -} - -Opus::AudioProperties *Opus::File::audioProperties() const { - return d->properties; -} - -bool Opus::File::save() { - - if (!d->comment) - d->comment = new Ogg::XiphComment(); - - setPacket(1, ByteVector("OpusTags", 8) + d->comment->render(false)); - - return Ogg::File::save(); - -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void Opus::File::read(bool readProperties) { - - ByteVector opusHeaderData = packet(0); - - if (!opusHeaderData.startsWith("OpusHead")) { - setValid(false); - debug("Opus::File::read() -- invalid Opus identification header"); - return; - } - - ByteVector commentHeaderData = packet(1); - - if (!commentHeaderData.startsWith("OpusTags")) { - setValid(false); - debug("Opus::File::read() -- invalid Opus tags header"); - return; - } - - d->comment = new Ogg::XiphComment(commentHeaderData.mid(8)); - - if (readProperties) - d->properties = new AudioProperties(this); - -} diff --git a/3rdparty/taglib/ogg/opus/opusfile.h b/3rdparty/taglib/ogg/opus/opusfile.h deleted file mode 100644 index ea42c066..00000000 --- a/3rdparty/taglib/ogg/opus/opusfile.h +++ /dev/null @@ -1,122 +0,0 @@ -/*************************************************************************** - copyright : (C) 2012 by Lukáš Lalinský - email : lalinsky@gmail.com - - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - (original Vorbis implementation) -***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_OPUSFILE_H -#define TAGLIB_OPUSFILE_H - -#include "oggfile.h" -#include "xiphcomment.h" - -#include "opusproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace Ogg { - -//! A namespace containing classes for Opus metadata - -namespace Opus { - -//! An implementation of Ogg::File with Opus specific methods - -/*! - * This is the central class in the Ogg Opus metadata processing collection of classes. - * It's built upon Ogg::File which handles processing of the Ogg logical bitstream and - * breaking it down into pages which are handled by the codec implementations, - * in this case Opus specifically. - * - */ - -class TAGLIB_EXPORT File : public Ogg::File { - public: - /*! - * Constructs an Opus file from \a file. - * If \a readProperties is true the file's audio properties will also be read. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Constructs an Opus file from \a stream. - * If \a readProperties is true the file's audio properties will also be read. - * - * \note TagLib will *not* take ownership of the stream, the caller is responsible for deleting it after the File object. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Destroys this instance of the File. - */ - ~File() override; - - /*! - * Returns the XiphComment for this file. - * XiphComment implements the tag interface, so this serves as the reimplementation of TagLib::File::tag(). - */ - Ogg::XiphComment *tag() const override; - - /*! - * Returns the Opus::AudioProperties for this file. - * If no audio properties were read then this will return a null pointer. - */ - AudioProperties *audioProperties() const override; - - /*! - * Save the file. - * - * This returns true if the save was successful. - */ - bool save() override; - - /*! - * Returns whether or not the given \a stream can be opened as an Opus 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); - - class FilePrivate; - FilePrivate *d; -}; - -} // namespace Opus -} // namespace Ogg -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/ogg/opus/opusproperties.cpp b/3rdparty/taglib/ogg/opus/opusproperties.cpp deleted file mode 100644 index 1436f0b4..00000000 --- a/3rdparty/taglib/ogg/opus/opusproperties.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/*************************************************************************** - copyright : (C) 2012 by Lukáš Lalinský - email : lalinsky@gmail.com - - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - (original Vorbis implementation) - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tstring.h" -#include "tdebug.h" - -#include "oggpageheader.h" - -#include "opusproperties.h" -#include "opusfile.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace Strawberry_TagLib::TagLib::Ogg; - -class Opus::AudioProperties::AudioPropertiesPrivate { - public: - explicit AudioPropertiesPrivate() : length(0), - bitrate(0), - inputSampleRate(0), - channels(0), - opusVersion(0) {} - - int length; - int bitrate; - int inputSampleRate; - int channels; - int opusVersion; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -Opus::AudioProperties::AudioProperties(File *file, ReadStyle) : Strawberry_TagLib::TagLib::AudioProperties(), d(new AudioPropertiesPrivate()) { - read(file); -} - -Opus::AudioProperties::~AudioProperties() { - delete d; -} - -int Opus::AudioProperties::lengthInSeconds() const { - return d->length / 1000; -} - -int Opus::AudioProperties::lengthInMilliseconds() const { - return d->length; -} - -int Opus::AudioProperties::bitrate() const { - return d->bitrate; -} - -int Opus::AudioProperties::sampleRate() const { - - // Opus can decode any stream at a sample rate of 8, 12, 16, 24, or 48 kHz, - // so there is no single sample rate. Let's assume it's the highest - // possible. - return 48000; - -} - -int Opus::AudioProperties::channels() const { - return d->channels; -} - -int Opus::AudioProperties::inputSampleRate() const { - return d->inputSampleRate; -} - -int Opus::AudioProperties::opusVersion() const { - return d->opusVersion; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void Opus::AudioProperties::read(File *file) { - - // Get the identification header from the Ogg implementation. - - // http://tools.ietf.org/html/draft-terriberry-oggopus-01#section-5.1 - - const ByteVector data = file->packet(0); - - // *Magic Signature* - size_t pos = 8; - - // *Version* (8 bits, unsigned) - d->opusVersion = static_cast(data.at(pos)); - pos += 1; - - // *Output Channel Count* 'C' (8 bits, unsigned) - d->channels = static_cast(data.at(pos)); - pos += 1; - - // *Pre-skip* (16 bits, unsigned, little endian) - const unsigned short preSkip = data.toUInt16LE(pos); - pos += 2; - - // *Input Sample Rate* (32 bits, unsigned, little endian) - d->inputSampleRate = data.toUInt32LE(pos); - pos += 4; - - // *Output Gain* (16 bits, signed, little endian) - pos += 2; - - // *Channel Mapping Family* (8 bits, unsigned) - pos += 1; - - const Ogg::PageHeader *first = file->firstPageHeader(); - const Ogg::PageHeader *last = file->lastPageHeader(); - - if (first && last) { - const long long start = first->absoluteGranularPosition(); - const long long end = last->absoluteGranularPosition(); - - if (start >= 0 && end >= 0) { - const long long frameCount = (end - start - preSkip); - - if (frameCount > 0) { - const double length = frameCount * 1000.0 / 48000.0; - //long fileLengthWithoutOverhead = file->length(); - // Ignore the two mandatory header packets, see "3. Packet Organization" - // in https://tools.ietf.org/html/rfc7845.html - //for (unsigned int i = 0; i < 2; ++i) { - //fileLengthWithoutOverhead -= file->packet(i).size(); - //} - d->length = static_cast(length + 0.5); - //d->bitrate = static_cast(fileLengthWithoutOverhead * 8.0 / length + 0.5); - d->bitrate = static_cast(file->length() * 8.0 / length + 0.5); - } - } - else { - debug("Opus::AudioProperties::read() -- The PCM values for the start or " - "end of this file was incorrect."); - } - } - else - debug("Opus::AudioProperties::read() -- Could not find valid first and last Ogg pages."); - -} diff --git a/3rdparty/taglib/ogg/opus/opusproperties.h b/3rdparty/taglib/ogg/opus/opusproperties.h deleted file mode 100644 index 9e73c899..00000000 --- a/3rdparty/taglib/ogg/opus/opusproperties.h +++ /dev/null @@ -1,117 +0,0 @@ -/*************************************************************************** - copyright : (C) 2012 by Lukáš Lalinský - email : lalinsky@gmail.com - - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - (original Vorbis implementation) -***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_OPUSPROPERTIES_H -#define TAGLIB_OPUSPROPERTIES_H - -#include "audioproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -namespace Ogg { - -namespace Opus { - -class File; - -//! An implementation of audio property reading for Ogg Opus - -/*! - * This reads the data from an Ogg Opus stream found in the AudioProperties API. - */ - -class TAGLIB_EXPORT AudioProperties : public Strawberry_TagLib::TagLib::AudioProperties { - public: - /*! - * Create an instance of Opus::AudioProperties with the data read from the - * Opus::File \a file. - */ - explicit AudioProperties(File *file, ReadStyle style = Average); - - /*! - * Destroys this Opus::AudioProperties instance. - */ - ~AudioProperties() override; - - /*! - * Returns the length of the file in seconds. The length is rounded down to the nearest whole second. - * - * \see lengthInMilliseconds() - */ - int lengthInSeconds() const override; - - /*! - * Returns the length of the file in milliseconds. - * - * \see lengthInSeconds() - */ - int lengthInMilliseconds() const override; - - /*! - * Returns the average bit rate of the file in kb/s. - */ - int bitrate() const override; - - /*! - * Returns the sample rate in Hz. - * - * \note Always returns 48000, because Opus can decode any stream at a sample rate of 8, 12, 16, 24, or 48 kHz, - */ - int sampleRate() const override; - - /*! - * Returns the number of audio channels. - */ - int channels() const override; - - /*! - * The Opus codec supports decoding at multiple sample rates, there is no single sample rate of the encoded stream. - * This returns the sample rate of the original audio stream. - */ - int inputSampleRate() const; - - /*! - * Returns the Opus version, in the range 0...255. - */ - int opusVersion() const; - - private: - void read(File *file); - - class AudioPropertiesPrivate; - AudioPropertiesPrivate *d; -}; - -} // namespace Opus -} // namespace Ogg -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/ogg/speex/speexfile.cpp b/3rdparty/taglib/ogg/speex/speexfile.cpp deleted file mode 100644 index c630667a..00000000 --- a/3rdparty/taglib/ogg/speex/speexfile.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/*************************************************************************** - copyright : (C) 2006 by Lukáš Lalinský - email : lalinsky@gmail.com - - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - (original Vorbis implementation) - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tstring.h" -#include "tdebug.h" -#include "tpropertymap.h" -#include "tagutils.h" - -#include "speexfile.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace Strawberry_TagLib::TagLib::Ogg; - -class Speex::File::FilePrivate { - public: - explicit FilePrivate() : comment(nullptr), properties(nullptr) {} - - ~FilePrivate() { - delete comment; - delete properties; - } - - Ogg::XiphComment *comment; - AudioProperties *properties; -}; - -//////////////////////////////////////////////////////////////////////////////// -// static members -//////////////////////////////////////////////////////////////////////////////// - -bool Ogg::Speex::File::isSupported(IOStream *stream) { - - // A Speex file has IDs "OggS" and "Speex " somewhere. - - const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false); - return (buffer.find("OggS") != ByteVector::npos() && buffer.find("Speex ") != ByteVector::npos()); - -} - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -Speex::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) : Ogg::File(file), d(new FilePrivate()) { - if (isOpen()) - read(readProperties); -} - -Speex::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle) : Ogg::File(stream), d(new FilePrivate()) { - if (isOpen()) - read(readProperties); -} - -Speex::File::~File() { - delete d; -} - -Ogg::XiphComment *Speex::File::tag() const { - return d->comment; -} - -Speex::AudioProperties *Speex::File::audioProperties() const { - return d->properties; -} - -bool Speex::File::save() { - - if (!d->comment) - d->comment = new Ogg::XiphComment(); - - setPacket(1, d->comment->render()); - - return Ogg::File::save(); - -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void Speex::File::read(bool readProperties) { - - ByteVector speexHeaderData = packet(0); - - if (!speexHeaderData.startsWith("Speex ")) { - debug("Speex::File::read() -- invalid Speex identification header"); - setValid(false); - return; - } - - ByteVector commentHeaderData = packet(1); - - d->comment = new Ogg::XiphComment(commentHeaderData); - - if (readProperties) - d->properties = new AudioProperties(this); - -} diff --git a/3rdparty/taglib/ogg/speex/speexfile.h b/3rdparty/taglib/ogg/speex/speexfile.h deleted file mode 100644 index 5e4993dc..00000000 --- a/3rdparty/taglib/ogg/speex/speexfile.h +++ /dev/null @@ -1,122 +0,0 @@ -/*************************************************************************** - copyright : (C) 2006 by Lukáš Lalinský - email : lalinsky@gmail.com - - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - (original Vorbis implementation) -***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_SPEEXFILE_H -#define TAGLIB_SPEEXFILE_H - -#include "oggfile.h" -#include "xiphcomment.h" - -#include "speexproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -namespace Ogg { - -//! A namespace containing classes for Speex metadata - -namespace Speex { - -//! An implementation of Ogg::File with Speex specific methods - -/*! - * This is the central class in the Ogg Speex metadata processing collection of classes. - * It's built upon Ogg::File which handles processing of the Ogg logical bitstream - * and breaking it down into pages which are handled by the codec implementations, - * in this case Speex specifically. - * - */ - -class TAGLIB_EXPORT File : public Ogg::File { - public: - /*! - * Constructs a Speex file from \a file. - * If \a readProperties is true the file's audio properties will also be read. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Constructs a Speex file from \a stream. - * If \a readProperties is true the file's audio properties will also be read. - * - * \note TagLib will *not* take ownership of the stream, the caller is responsible for deleting it after the File object. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Destroys this instance of the File. - */ - ~File() override; - - /*! - * Returns the XiphComment for this file. XiphComment implements the tag interface, so this serves as the reimplementation of TagLib::File::tag(). - */ - Ogg::XiphComment *tag() const override; - - /*! - * Returns the Speex::AudioProperties for this file. - * If no audio properties were read then this will return a null pointer. - */ - AudioProperties *audioProperties() const override; - - /*! - * Save the file. - * - * This returns true if the save was successful. - */ - bool save() override; - - /*! - * Returns whether or not the given \a stream can be opened as a Speex 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); - - class FilePrivate; - FilePrivate *d; -}; - -} // namespace Speex -} // namespace Ogg -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/ogg/speex/speexproperties.cpp b/3rdparty/taglib/ogg/speex/speexproperties.cpp deleted file mode 100644 index 6e3219fe..00000000 --- a/3rdparty/taglib/ogg/speex/speexproperties.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/*************************************************************************** - copyright : (C) 2006 by Lukáš Lalinský - email : lalinsky@gmail.com - - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - (original Vorbis implementation) - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tstring.h" -#include "tdebug.h" - -#include "oggpageheader.h" - -#include "speexproperties.h" -#include "speexfile.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace Strawberry_TagLib::TagLib::Ogg; - -class Speex::AudioProperties::AudioPropertiesPrivate { - public: - explicit AudioPropertiesPrivate() : length(0), - bitrate(0), - bitrateNominal(0), - sampleRate(0), - channels(0), - speexVersion(0), - vbr(false), - mode(0) {} - - int length; - int bitrate; - int bitrateNominal; - int sampleRate; - int channels; - int speexVersion; - bool vbr; - int mode; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -Speex::AudioProperties::AudioProperties(File *file, ReadStyle) : Strawberry_TagLib::TagLib::AudioProperties(), d(new AudioPropertiesPrivate()) { - read(file); -} - -Speex::AudioProperties::~AudioProperties() { - delete d; -} - -int Speex::AudioProperties::lengthInSeconds() const { - return d->length / 1000; -} - -int Speex::AudioProperties::lengthInMilliseconds() const { - return d->length; -} - -int Speex::AudioProperties::bitrate() const { - return d->bitrate; -} - -int Speex::AudioProperties::bitrateNominal() const { - return d->bitrateNominal; -} - -int Speex::AudioProperties::sampleRate() const { - return d->sampleRate; -} - -int Speex::AudioProperties::channels() const { - return d->channels; -} - -int Speex::AudioProperties::speexVersion() const { - return d->speexVersion; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void Speex::AudioProperties::read(File *file) { - - // Get the identification header from the Ogg implementation. - - const ByteVector data = file->packet(0); - if (data.size() < 64) { - debug("Speex::AudioProperties::read() -- data is too short."); - return; - } - - size_t pos = 28; - - // speex_version_id; /**< Version for Speex (for checking compatibility) */ - d->speexVersion = data.toUInt32LE(pos); - pos += 4; - - // header_size; /**< Total size of the header ( sizeof(SpeexHeader) ) */ - pos += 4; - - // rate; /**< Sampling rate used */ - d->sampleRate = data.toUInt32LE(pos); - pos += 4; - - // mode; /**< Mode used (0 for narrowband, 1 for wideband) */ - d->mode = data.toUInt32LE(pos); - pos += 4; - - // mode_bitstream_version; /**< Version ID of the bit-stream */ - pos += 4; - - // nb_channels; /**< Number of channels encoded */ - d->channels = data.toUInt32LE(pos); - pos += 4; - - // bitrate; /**< Bit-rate used */ - d->bitrateNominal = data.toUInt32LE(pos); - pos += 4; - - // frame_size; /**< Size of frames */ - // unsigned int frameSize = data.mid(pos, 4).toUInt(false); - pos += 4; - - // vbr; /**< 1 for a VBR encoding, 0 otherwise */ - d->vbr = data.toUInt32LE(pos) == 1; - pos += 4; - - // frames_per_packet; /**< Number of frames stored per Ogg packet */ - // unsigned int framesPerPacket = data.mid(pos, 4).toUInt(false); - - const Ogg::PageHeader *first = file->firstPageHeader(); - const Ogg::PageHeader *last = file->lastPageHeader(); - - if (first && last) { - const long long start = first->absoluteGranularPosition(); - const long long end = last->absoluteGranularPosition(); - - if (start >= 0 && end >= 0 && d->sampleRate > 0) { - const long long frameCount = end - start; - - if (frameCount > 0) { - const double length = frameCount * 1000.0 / d->sampleRate; - long fileLengthWithoutOverhead = file->length(); - // Ignore the two header packets, see "Ogg file format" in - // https://www.speex.org/docs/manual/speex-manual/node8.html - for (unsigned int i = 0; i < 2; ++i) { - fileLengthWithoutOverhead -= file->packet(i).size(); - } - d->length = static_cast(length + 0.5); - d->bitrate = static_cast(fileLengthWithoutOverhead * 8.0 / length + 0.5); - } - } - else { - debug("Speex::AudioProperties::read() -- Either the PCM values for the start or " - "end of this file was incorrect or the sample rate is zero."); - } - } - else - debug("Speex::AudioProperties::read() -- Could not find valid first and last Ogg pages."); - - // Alternative to the actual average bitrate. - - if (d->bitrate == 0 && d->bitrateNominal > 0) - d->bitrate = static_cast(d->bitrateNominal / 1000.0 + 0.5); - -} diff --git a/3rdparty/taglib/ogg/speex/speexproperties.h b/3rdparty/taglib/ogg/speex/speexproperties.h deleted file mode 100644 index b9adf93d..00000000 --- a/3rdparty/taglib/ogg/speex/speexproperties.h +++ /dev/null @@ -1,108 +0,0 @@ -/*************************************************************************** - copyright : (C) 2006 by Lukáš Lalinský - email : lalinsky@gmail.com - - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - (original Vorbis implementation) -***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_SPEEXPROPERTIES_H -#define TAGLIB_SPEEXPROPERTIES_H - -#include "audioproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace Ogg { -namespace Speex { - -class File; - -//! An implementation of audio property reading for Ogg Speex -//! This reads the data from an Ogg Speex stream found in the AudioProperties API. - -class TAGLIB_EXPORT AudioProperties : public Strawberry_TagLib::TagLib::AudioProperties { - public: - /*! - * Create an instance of Speex::AudioProperties with the data read from the Speex::File \a file. - */ - explicit AudioProperties(File *file, ReadStyle style = Average); - - /*! - * Destroys this Speex::AudioProperties instance. - */ - ~AudioProperties() override; - - /*! - * Returns the length of the file in seconds. The length is rounded down to the nearest whole second. - * - * \see lengthInMilliseconds() - */ - int lengthInSeconds() const override; - - /*! - * Returns the length of the file in milliseconds. - * - * \see lengthInSeconds() - */ - int lengthInMilliseconds() const override; - - /*! - * Returns the average bit rate of the file in kb/s. - */ - int bitrate() const override; - - /*! - * Returns the nominal bit rate as read from the Speex header in kb/s. - */ - int bitrateNominal() const; - - /*! - * Returns the sample rate in Hz. - */ - int sampleRate() const override; - - /*! - * Returns the number of audio channels. - */ - int channels() const override; - - /*! - * Returns the Speex version, currently "0" (as specified by the spec). - */ - int speexVersion() const; - - private: - void read(File *file); - - class AudioPropertiesPrivate; - AudioPropertiesPrivate *d; -}; - -} // namespace Speex -} // namespace Ogg -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/ogg/vorbis/vorbisfile.cpp b/3rdparty/taglib/ogg/vorbis/vorbisfile.cpp deleted file mode 100644 index c8ff800f..00000000 --- a/3rdparty/taglib/ogg/vorbis/vorbisfile.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "tstring.h" -#include "tdebug.h" -#include "tpropertymap.h" -#include "tagutils.h" - -#include "vorbisfile.h" - -using namespace Strawberry_TagLib::TagLib; - -class Ogg::Vorbis::File::FilePrivate { - public: - explicit FilePrivate() : comment(nullptr), properties(nullptr) {} - - ~FilePrivate() { - delete comment; - delete properties; - } - - Ogg::XiphComment *comment; - AudioProperties *properties; -}; - -namespace Strawberry_TagLib { -namespace TagLib { -/*! - * Vorbis headers can be found with one type ID byte and the string "vorbis" in - * an Ogg stream. 0x03 indicates the comment header. - */ -static const char vorbisCommentHeaderID[] = { 0x03, 'v', 'o', 'r', 'b', 'i', 's', 0 }; -} // namespace TagLib -} // namespace Strawberry_TagLib - -//////////////////////////////////////////////////////////////////////////////// -// static members -//////////////////////////////////////////////////////////////////////////////// - -bool Ogg::Vorbis::File::isSupported(IOStream *stream) { - // An Ogg Vorbis file has IDs "OggS" and "\x01vorbis" somewhere. - - const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false); - return (buffer.find("OggS") != ByteVector::npos() && buffer.find("\x01vorbis") != ByteVector::npos()); - -} - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -Ogg::Vorbis::File::File(FileName file, bool readProperties, Strawberry_TagLib::TagLib::AudioProperties::ReadStyle) : Ogg::File(file), - d(new FilePrivate()) { - if (isOpen()) - read(readProperties); -} - -Ogg::Vorbis::File::File(IOStream *stream, bool readProperties, Strawberry_TagLib::TagLib::AudioProperties::ReadStyle) : Ogg::File(stream), - d(new FilePrivate()) { - if (isOpen()) - read(readProperties); -} - -Ogg::Vorbis::File::~File() { - delete d; -} - -Ogg::XiphComment *Ogg::Vorbis::File::tag() const { - return d->comment; -} - -Ogg::Vorbis::AudioProperties *Ogg::Vorbis::File::audioProperties() const { - return d->properties; -} - -bool Ogg::Vorbis::File::save() { - - ByteVector v(vorbisCommentHeaderID); - - if (!d->comment) - d->comment = new Ogg::XiphComment(); - v.append(d->comment->render()); - - setPacket(1, v); - - return Ogg::File::save(); - -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void Ogg::Vorbis::File::read(bool readProperties) { - - ByteVector commentHeaderData = packet(1); - - if (commentHeaderData.mid(0, 7) != vorbisCommentHeaderID) { - debug("Ogg::Vorbis::File::read() - Could not find the Vorbis comment header."); - setValid(false); - return; - } - - d->comment = new Ogg::XiphComment(commentHeaderData.mid(7)); - - if (readProperties) - d->properties = new AudioProperties(this); - -} diff --git a/3rdparty/taglib/ogg/vorbis/vorbisfile.h b/3rdparty/taglib/ogg/vorbis/vorbisfile.h deleted file mode 100644 index 76d24d20..00000000 --- a/3rdparty/taglib/ogg/vorbis/vorbisfile.h +++ /dev/null @@ -1,115 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_VORBISFILE_H -#define TAGLIB_VORBISFILE_H - -#include "taglib_export.h" -#include "oggfile.h" -#include "xiphcomment.h" - -#include "vorbisproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace Ogg { - -//! A namespace containing classes for Vorbis metadata - -namespace Vorbis { - -//! An implementation of Ogg::File with Vorbis specific methods - -/*! - * This is the central class in the Ogg Vorbis metadata processing collection of classes. - * It's built upon Ogg::File which handles processing of the Ogg logical bitstream and breaking - * it down into pages which are handled by the codec implementations, in this case Vorbis specifically. - */ - -class TAGLIB_EXPORT File : public Ogg::File { - public: - /*! - * Constructs a Vorbis file from \a file. If \a readProperties is true the file's audio properties will also be read. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(FileName file, bool readProperties = true, Strawberry_TagLib::TagLib::AudioProperties::ReadStyle propertiesStyle = Strawberry_TagLib::TagLib::AudioProperties::Average); - - /*! - * Constructs a Vorbis file from \a stream. - * If \a readProperties is true the file's audio properties will also be read. - * - * \note TagLib will *not* take ownership of the stream, the caller is responsible for deleting it after the File object. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(IOStream *stream, bool readProperties = true, Strawberry_TagLib::TagLib::AudioProperties::ReadStyle propertiesStyle = Strawberry_TagLib::TagLib::AudioProperties::Average); - - /*! - * Destroys this instance of the File. - */ - ~File() override; - - /*! - * Returns the XiphComment for this file. - * XiphComment implements the tag interface, so this serves as the reimplementation of TagLib::File::tag(). - */ - Ogg::XiphComment *tag() const override; - - /*! - * Returns the Vorbis::AudioProperties for this file. If no audio properties were read then this will return a null pointer. - */ - AudioProperties *audioProperties() const override; - - /*! - * Save the file. - * - * This returns true if the save was successful. - */ - bool save() override; - - /*! - * Check if the given \a stream can be opened as an Ogg Vorbis 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); - - class FilePrivate; - FilePrivate *d; -}; - -} // namespace Vorbis -} // namespace Ogg -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/ogg/vorbis/vorbisproperties.cpp b/3rdparty/taglib/ogg/vorbis/vorbisproperties.cpp deleted file mode 100644 index f96c2d7b..00000000 --- a/3rdparty/taglib/ogg/vorbis/vorbisproperties.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tstring.h" -#include "tdebug.h" - -#include "oggpageheader.h" - -#include "vorbisproperties.h" -#include "vorbisfile.h" - -using namespace Strawberry_TagLib::TagLib; - -class Ogg::Vorbis::AudioProperties::AudioPropertiesPrivate { - public: - explicit AudioPropertiesPrivate() : length(0), - bitrate(0), - sampleRate(0), - channels(0), - vorbisVersion(0), - bitrateMaximum(0), - bitrateNominal(0), - bitrateMinimum(0) {} - - int length; - int bitrate; - int sampleRate; - int channels; - int vorbisVersion; - int bitrateMaximum; - int bitrateNominal; - int bitrateMinimum; -}; - -namespace Strawberry_TagLib { -namespace TagLib { -/*! - * Vorbis headers can be found with one type ID byte and the string "vorbis" in - * an Ogg stream. 0x01 indicates the setup header. - */ -const char vorbisSetupHeaderID[] = { 0x01, 'v', 'o', 'r', 'b', 'i', 's', 0 }; -} // namespace TagLib -} // namespace Strawberry_TagLib - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -Ogg::Vorbis::AudioProperties::AudioProperties(File *file, ReadStyle) : Strawberry_TagLib::TagLib::AudioProperties(), d(new AudioPropertiesPrivate()) { - read(file); -} - -Ogg::Vorbis::AudioProperties::~AudioProperties() { - delete d; -} - -int Ogg::Vorbis::AudioProperties::lengthInSeconds() const { - return d->length / 1000; -} - -int Ogg::Vorbis::AudioProperties::lengthInMilliseconds() const { - return d->length; -} - -int Ogg::Vorbis::AudioProperties::bitrate() const { - return d->bitrate; -} - -int Ogg::Vorbis::AudioProperties::sampleRate() const { - return d->sampleRate; -} - -int Ogg::Vorbis::AudioProperties::channels() const { - return d->channels; -} - -int Ogg::Vorbis::AudioProperties::vorbisVersion() const { - return d->vorbisVersion; -} - -int Ogg::Vorbis::AudioProperties::bitrateMaximum() const { - return d->bitrateMaximum; -} - -int Ogg::Vorbis::AudioProperties::bitrateNominal() const { - return d->bitrateNominal; -} - -int Ogg::Vorbis::AudioProperties::bitrateMinimum() const { - return d->bitrateMinimum; -} - -String Ogg::Vorbis::AudioProperties::toString() const { - - StringList desc; - desc.append("Ogg Vorbis audio (version " + String::number(vorbisVersion()) + ")"); - desc.append(String::number(lengthInSeconds()) + " seconds"); - desc.append(String::number(bitrate()) + " kbps"); - return desc.toString(", "); - -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void Ogg::Vorbis::AudioProperties::read(File *file) { - - // Get the identification header from the Ogg implementation. - - const ByteVector data = file->packet(0); - if (data.size() < 28) { - debug("Ogg::Vorbis::AudioProperties::read() -- data is too short."); - return; - } - - size_t pos = 0; - - if (data.mid(pos, 7) != vorbisSetupHeaderID) { - debug("Ogg::Vorbis::AudioProperties::read() -- invalid Vorbis identification header"); - return; - } - - pos += 7; - - d->vorbisVersion = data.toUInt32LE(pos); - pos += 4; - - d->channels = static_cast(data[pos]); - pos += 1; - - d->sampleRate = data.toUInt32LE(pos); - pos += 4; - - d->bitrateMaximum = data.toUInt32LE(pos); - pos += 4; - - d->bitrateNominal = data.toUInt32LE(pos); - pos += 4; - - d->bitrateMinimum = data.toUInt32LE(pos); - pos += 4; - - // Find the length of the file. See http://wiki.xiph.org/VorbisStreamLength/ - // for my notes on the topic. - - const Ogg::PageHeader *first = file->firstPageHeader(); - const Ogg::PageHeader *last = file->lastPageHeader(); - - if (first && last) { - const long long start = first->absoluteGranularPosition(); - const long long end = last->absoluteGranularPosition(); - - if (start >= 0 && end >= 0 && d->sampleRate > 0) { - const long long frameCount = end - start; - - if (frameCount > 0) { - const double length = frameCount * 1000.0 / d->sampleRate; - //long fileLengthWithoutOverhead = file->length(); - // Ignore the three initial header packets, see "1.3.1. Decode Setup" in - // https://xiph.org/vorbis/doc/Vorbis_I_spec.html - //for (unsigned int i = 0; i < 3; ++i) { - //fileLengthWithoutOverhead -= file->packet(i).size(); - //} - d->length = static_cast(length + 0.5); - //d->bitrate = static_cast(fileLengthWithoutOverhead * 8.0 / length + 0.5); - d->bitrate = static_cast(file->length() * 8.0 / length + 0.5); - } - } - else { - debug("Ogg::Vorbis::AudioProperties::read() -- Either the PCM values for the start or " - "end of this file was incorrect or the sample rate is zero."); - } - } - else - debug("Ogg::Vorbis::AudioProperties::read() -- Could not find valid first and last Ogg pages."); - - // Alternative to the actual average bitrate. - - if (d->bitrate == 0 && d->bitrateNominal > 0) - d->bitrate = static_cast(d->bitrateNominal / 1000.0 + 0.5); - -} diff --git a/3rdparty/taglib/ogg/vorbis/vorbisproperties.h b/3rdparty/taglib/ogg/vorbis/vorbisproperties.h deleted file mode 100644 index 20539b2d..00000000 --- a/3rdparty/taglib/ogg/vorbis/vorbisproperties.h +++ /dev/null @@ -1,121 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_VORBISPROPERTIES_H -#define TAGLIB_VORBISPROPERTIES_H - -#include "taglib_export.h" -#include "audioproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace Ogg { - -namespace Vorbis { - -class File; - -//! An implementation of audio property reading for Ogg Vorbis - -/*! - * This reads the data from an Ogg Vorbis stream found in the AudioProperties API. - */ - -class TAGLIB_EXPORT AudioProperties : public Strawberry_TagLib::TagLib::AudioProperties { - public: - /*! - * Create an instance of Vorbis::AudioProperties with the data read from the Vorbis::File \a file. - */ - explicit AudioProperties(File *file, ReadStyle style = Average); - - /*! - * Destroys this VorbisProperties instance. - */ - ~AudioProperties() override; - - /*! - * Returns the length of the file in seconds. The length is rounded down to the nearest whole second. - * - * \see lengthInMilliseconds() - */ - int lengthInSeconds() const override; - - /*! - * Returns the length of the file in milliseconds. - * - * \see lengthInSeconds() - */ - int lengthInMilliseconds() const override; - - /*! - * Returns the average bit rate of the file in kb/s. - */ - int bitrate() const override; - - /*! - * Returns the sample rate in Hz. - */ - int sampleRate() const override; - - /*! - * Returns the number of audio channels. - */ - int channels() const override; - - String toString() const override; - - /*! - * Returns the Vorbis version, currently "0" (as specified by the spec). - */ - int vorbisVersion() const; - - /*! - * Returns the maximum bitrate as read from the Vorbis identification header. - */ - int bitrateMaximum() const; - - /*! - * Returns the nominal bitrate as read from the Vorbis identification header. - */ - int bitrateNominal() const; - - /*! - * Returns the minimum bitrate as read from the Vorbis identification header. - */ - int bitrateMinimum() const; - - private: - void read(File *file); - - class AudioPropertiesPrivate; - AudioPropertiesPrivate *d; -}; - -} // namespace Vorbis -} // namespace Ogg -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/ogg/xiphcomment.cpp b/3rdparty/taglib/ogg/xiphcomment.cpp deleted file mode 100644 index babbe685..00000000 --- a/3rdparty/taglib/ogg/xiphcomment.cpp +++ /dev/null @@ -1,515 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tbytevector.h" -#include "tdebug.h" - -#include "flacpicture.h" -#include "xiphcomment.h" -#include "tpicturemap.h" -#include "tpropertymap.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { -typedef Ogg::FieldListMap::Iterator FieldIterator; -typedef Ogg::FieldListMap::ConstIterator FieldConstIterator; - -typedef List XiphPictureList; -typedef XiphPictureList::Iterator PictureIterator; -typedef XiphPictureList::Iterator PictureConstIterator; -} // namespace - -class Ogg::XiphComment::XiphCommentPrivate { - public: - explicit XiphCommentPrivate() { - pictureList.setAutoDelete(true); - } - - FieldListMap fieldListMap; - String vendorID; - String commentField; - XiphPictureList pictureList; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -Ogg::XiphComment::XiphComment() : d(new XiphCommentPrivate()) {} - -Ogg::XiphComment::XiphComment(const ByteVector &data) : d(new XiphCommentPrivate()) { - parse(data); -} - -Ogg::XiphComment::~XiphComment() { - delete d; -} - -String Ogg::XiphComment::title() const { - - if (d->fieldListMap["TITLE"].isEmpty()) - return String(); - return d->fieldListMap["TITLE"].toString(); - -} - -String Ogg::XiphComment::artist() const { - - if (d->fieldListMap["ARTIST"].isEmpty()) - return String(); - return d->fieldListMap["ARTIST"].toString(); - -} - -String Ogg::XiphComment::album() const { - - if (d->fieldListMap["ALBUM"].isEmpty()) - return String(); - return d->fieldListMap["ALBUM"].toString(); - -} - -String Ogg::XiphComment::comment() const { - - if (!d->fieldListMap["DESCRIPTION"].isEmpty()) { - d->commentField = "DESCRIPTION"; - return d->fieldListMap["DESCRIPTION"].toString(); - } - - if (!d->fieldListMap["COMMENT"].isEmpty()) { - d->commentField = "COMMENT"; - return d->fieldListMap["COMMENT"].toString(); - } - - return String(); - -} - -String Ogg::XiphComment::genre() const { - - if (d->fieldListMap["GENRE"].isEmpty()) - return String(); - return d->fieldListMap["GENRE"].toString(); - -} - -unsigned int Ogg::XiphComment::year() const { - - if (!d->fieldListMap["DATE"].isEmpty()) - return d->fieldListMap["DATE"].front().toInt(); - if (!d->fieldListMap["YEAR"].isEmpty()) - return d->fieldListMap["YEAR"].front().toInt(); - return 0; - -} - -unsigned int Ogg::XiphComment::track() const { - - if (!d->fieldListMap["TRACKNUMBER"].isEmpty()) - return d->fieldListMap["TRACKNUMBER"].front().toInt(); - if (!d->fieldListMap["TRACKNUM"].isEmpty()) - return d->fieldListMap["TRACKNUM"].front().toInt(); - - return 0; - -} - -Strawberry_TagLib::TagLib::PictureMap Ogg::XiphComment::pictures() const { - return PictureMap(); -} - -void Ogg::XiphComment::setTitle(const String &s) { - addField("TITLE", s); -} - -void Ogg::XiphComment::setArtist(const String &s) { - addField("ARTIST", s); -} - -void Ogg::XiphComment::setAlbum(const String &s) { - addField("ALBUM", s); -} - -void Ogg::XiphComment::setComment(const String &s) { - - if (d->commentField.isEmpty()) { - if (!d->fieldListMap["DESCRIPTION"].isEmpty()) - d->commentField = "DESCRIPTION"; - else - d->commentField = "COMMENT"; - } - - addField(d->commentField, s); - -} - -void Ogg::XiphComment::setGenre(const String &s) { - addField("GENRE", s); -} - -void Ogg::XiphComment::setYear(unsigned int i) { - - removeFields("YEAR"); - if (i == 0) - removeFields("DATE"); - else - addField("DATE", String::number(i)); - -} - -void Ogg::XiphComment::setTrack(unsigned int i) { - - removeFields("TRACKNUM"); - if (i == 0) - removeFields("TRACKNUMBER"); - else - addField("TRACKNUMBER", String::number(i)); - -} - -void Ogg::XiphComment::setPictures(const PictureMap&) {} - -bool Ogg::XiphComment::isEmpty() const { - - for (FieldConstIterator it = d->fieldListMap.begin(); it != d->fieldListMap.end(); ++it) { - if (!(*it).second.isEmpty()) - return false; - } - - return true; - -} - -unsigned int Ogg::XiphComment::fieldCount() const { - - size_t count = 0; - - for (FieldConstIterator it = d->fieldListMap.begin(); it != d->fieldListMap.end(); ++it) - count += (*it).second.size(); - - count += d->pictureList.size(); - - return count; - -} - -const Ogg::FieldListMap &Ogg::XiphComment::fieldListMap() const { - return d->fieldListMap; -} - -PropertyMap Ogg::XiphComment::properties() const { - return d->fieldListMap; -} - -PropertyMap Ogg::XiphComment::setProperties(const PropertyMap &properties) { - - // check which keys are to be deleted - StringList toRemove; - for (FieldConstIterator it = d->fieldListMap.begin(); it != d->fieldListMap.end(); ++it) - if (!properties.contains(it->first)) - toRemove.append(it->first); - - for (StringList::ConstIterator it = toRemove.begin(); it != toRemove.end(); ++it) - removeFields(*it); - - // now go through keys in \a properties and check that the values match those in the xiph comment - PropertyMap invalid; - PropertyMap::ConstIterator it = properties.begin(); - for (; it != properties.end(); ++it) { - if (!checkKey(it->first)) - invalid.insert(it->first, it->second); - else if (!d->fieldListMap.contains(it->first) || !(it->second == d->fieldListMap[it->first])) { - const StringList &sl = it->second; - if (sl.isEmpty()) - // zero size string list -> remove the tag with all values - removeFields(it->first); - else { - // replace all strings in the list for the tag - StringList::ConstIterator valueIterator = sl.begin(); - addField(it->first, *valueIterator, true); - ++valueIterator; - for (; valueIterator != sl.end(); ++valueIterator) - addField(it->first, *valueIterator, false); - } - } - } - return invalid; - -} - -bool Ogg::XiphComment::checkKey(const String &key) { - - if (key.size() < 1) - return false; - - // A key may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded. - - for (String::ConstIterator it = key.begin(); it != key.end(); it++) { - if (*it < 0x20 || *it > 0x7D || *it == 0x3D) - return false; - } - - return true; - -} - -String Ogg::XiphComment::vendorID() const { - return d->vendorID; -} - -void Ogg::XiphComment::addField(const String &key, const String &value, bool replace) { - - if (!checkKey(key)) { - debug("Ogg::XiphComment::addField() - Invalid key. Field not added."); - return; - } - - const String upperKey = key.upper(); - - if (replace) - removeFields(upperKey); - - if (!key.isEmpty() && !value.isEmpty()) - d->fieldListMap[upperKey].append(value); - -} - -void Ogg::XiphComment::removeFields(const String &key) { - d->fieldListMap.erase(key.upper()); -} - -void Ogg::XiphComment::removeFields(const String &key, const String &value) { - - StringList &fields = d->fieldListMap[key.upper()]; - for (StringList::Iterator it = fields.begin(); it != fields.end();) { - if (*it == value) - it = fields.erase(it); - else - ++it; - } - -} - -void Ogg::XiphComment::removeAllFields() { - d->fieldListMap.clear(); -} - -bool Ogg::XiphComment::contains(const String &key) const { - return !d->fieldListMap[key.upper()].isEmpty(); -} - -void Ogg::XiphComment::removePicture(FLAC::Picture *picture, bool del) { - - PictureIterator it = d->pictureList.find(picture); - if (it != d->pictureList.end()) - d->pictureList.erase(it); - - if (del) - delete picture; - -} - -void Ogg::XiphComment::removeAllPictures() { - d->pictureList.clear(); -} - -void Ogg::XiphComment::addPicture(FLAC::Picture *picture) { - d->pictureList.append(picture); -} - -List Ogg::XiphComment::pictureList() { - return d->pictureList; -} - -ByteVector Ogg::XiphComment::render(bool addFramingBit) const { - - ByteVector data; - - // Add the vendor ID length and the vendor ID. It's important to use the - // length of the data(String::UTF8) rather than the length of the the string - // since this is UTF8 text and there may be more characters in the data than - // in the UTF16 string. - - ByteVector vendorData = d->vendorID.data(String::UTF8); - - data.append(ByteVector::fromUInt32LE(vendorData.size())); - data.append(vendorData); - - // Add the number of fields. - - data.append(ByteVector::fromUInt32LE(fieldCount())); - - // Iterate over the the field lists. Our iterator returns a - // std::pair where the first String is the field name and - // the StringList is the values associated with that field. - - for (FieldListMap::ConstIterator it = d->fieldListMap.begin(); it != d->fieldListMap.end(); ++it) { - - // And now iterate over the values of the current list. - - String fieldName = (*it).first; - StringList values = (*it).second; - - StringList::ConstIterator valuesIt = values.begin(); - for (; valuesIt != values.end(); ++valuesIt) { - ByteVector fieldData = fieldName.data(String::UTF8); - fieldData.append('='); - fieldData.append((*valuesIt).data(String::UTF8)); - - data.append(ByteVector::fromUInt32LE(fieldData.size())); - data.append(fieldData); - } - } - - for (PictureConstIterator it = d->pictureList.begin(); it != d->pictureList.end(); ++it) { - ByteVector picture = (*it)->render().toBase64(); - data.append(ByteVector::fromUInt32LE(picture.size() + 23)); - data.append("METADATA_BLOCK_PICTURE="); - data.append(picture); - } - - // Append the "framing bit". - - if (addFramingBit) - data.append(char(1)); - - return data; - -} - -String Ogg::XiphComment::toString() const { - - StringList desc; - for (FieldListMap::ConstIterator i = d->fieldListMap.begin(); i != d->fieldListMap.end(); i++) { - for (StringList::ConstIterator j = i->second.begin(); j != i->second.end(); j++) { - desc.append(i->first + "=" + *j); - } - } - return desc.toString("\n"); - -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void Ogg::XiphComment::parse(const ByteVector &data) { - - // The first thing in the comment data is the vendor ID length, followed by a - // UTF8 string with the vendor ID. - - size_t pos = 0; - - const unsigned int vendorLength = data.toUInt32LE(0); - pos += 4; - - d->vendorID = String(data.mid(pos, vendorLength), String::UTF8); - pos += vendorLength; - - // Next the number of fields in the comment vector. - - const unsigned int commentFields = data.toUInt32LE(pos); - pos += 4; - - if (commentFields > (data.size() - 8) / 4) { - return; - } - - for (unsigned int i = 0; i < commentFields; i++) { - - // Each comment field is in the format "KEY=value" in a UTF8 string and has - // 4 bytes before the text starts that gives the length. - - const unsigned int commentLength = data.toUInt32LE(pos); - pos += 4; - - const ByteVector entry = data.mid(pos, commentLength); - pos += commentLength; - - // Don't go past data end - - if (pos > data.size()) - break; - - // Check for field separator - - const size_t sep = entry.find('='); - if (sep == 0 || sep == ByteVector::npos()) { - debug("Ogg::XiphComment::parse() - Discarding a field. Separator not found."); - continue; - } - - // Parse the key - - const String key = String(entry.mid(0, sep), String::UTF8).upper(); - if (!checkKey(key)) { - debug("Ogg::XiphComment::parse() - Discarding a field. Invalid key."); - continue; - } - - if (key == "METADATA_BLOCK_PICTURE" || key == "COVERART") { - - // Handle Pictures separately - - const ByteVector picturedata = ByteVector::fromBase64(entry.mid(sep + 1)); - if (picturedata.isEmpty()) { - debug("Ogg::XiphComment::parse() - Discarding a field. Invalid base64 data"); - continue; - } - - if (key[0] == L'M') { - - // Decode FLAC Picture - - FLAC::Picture *picture = new FLAC::Picture(); - if (picture->parse(picturedata)) { - d->pictureList.append(picture); - } - else { - delete picture; - debug("Ogg::XiphComment::parse() - Failed to decode FLAC Picture block"); - } - } - else { - - // Assume it's some type of image file - - FLAC::Picture *picture = new FLAC::Picture(); - picture->setData(picturedata); - picture->setMimeType("image/"); - picture->setType(FLAC::Picture::Other); - d->pictureList.append(picture); - } - } - else { - - // Parse the text - - addField(key, String(entry.mid(sep + 1), String::UTF8), false); - } - } - -} diff --git a/3rdparty/taglib/ogg/xiphcomment.h b/3rdparty/taglib/ogg/xiphcomment.h deleted file mode 100644 index 3041aedf..00000000 --- a/3rdparty/taglib/ogg/xiphcomment.h +++ /dev/null @@ -1,253 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_VORBISCOMMENT_H -#define TAGLIB_VORBISCOMMENT_H - -#include "tag.h" -#include "tlist.h" -#include "tmap.h" -#include "tstring.h" -#include "tstringlist.h" -#include "tbytevector.h" -#include "flacpicture.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace Ogg { - -/*! - * A mapping between a list of field names, or keys, and a list of values associated with that field. - * - * \see XiphComment::fieldListMap() - */ -typedef Map FieldListMap; - -//! Ogg Vorbis comment implementation - -/*! - * This class is an implementation of the Ogg Vorbis comment specification, - * to be found in section 5 of the Ogg Vorbis specification. - * Because this format is also used in other (currently unsupported) Xiph.org formats, - * it has been made part of a generic implementation rather than being limited to strictly Vorbis. - * - * Vorbis comments are a simple vector of keys and values, called fields. - * Multiple values for a given key are supported. - * - * \see fieldListMap() - */ - -class TAGLIB_EXPORT XiphComment : public Strawberry_TagLib::TagLib::Tag { - public: - /*! - * Constructs an empty Vorbis comment. - */ - explicit XiphComment(); - - /*! - * Constructs a Vorbis comment from \a data. - */ - explicit XiphComment(const ByteVector &data); - - /*! - * Destroys this instance of the XiphComment. - */ - ~XiphComment() override; - - String title() const override; - String artist() const override; - String album() const override; - String comment() const override; - String genre() const override; - unsigned int year() const override; - unsigned int track() const override; - PictureMap pictures() const override; - - void setTitle(const String &s) override; - void setArtist(const String &s) override; - void setAlbum(const String &s) override; - void setComment(const String &s) override; - void setGenre(const String &s) override; - void setYear(unsigned int i) override; - void setTrack(unsigned int i) override; - void setPictures(const PictureMap&) override; - - bool isEmpty() const override; - String toString() const override; - - /*! - * Returns the number of fields present in the comment. - */ - unsigned int fieldCount() const; - - /*! - * Returns a reference to the map of field lists. - * Because Xiph comments support multiple fields with the same key, a pure Map would not work. - * As such this is a Map of string lists, keyed on the comment field name. - * - * The standard set of Xiph/Vorbis fields (which may or may not be contained in any specific comment) is: - * - *
    - *
  • TITLE
  • - *
  • VERSION
  • - *
  • ALBUM
  • - *
  • ARTIST
  • - *
  • PERFORMER
  • - *
  • COPYRIGHT
  • - *
  • ORGANIZATION
  • - *
  • DESCRIPTION
  • - *
  • GENRE
  • - *
  • DATE
  • - *
  • LOCATION
  • - *
  • CONTACT
  • - *
  • ISRC
  • - *
- * - * For a more detailed description of these fields, please see the Ogg - * Vorbis specification, section 5.2.2.1. - * - * \note The Ogg Vorbis comment specification does allow these key values to be either upper or lower case. - * However, it is conventional for them to be upper case. - * As such, TagLib, when parsing a Xiph/Vorbis comment, converts all fields to uppercase. - * When you are using this data structure, you will need to specify the field name in upper case. - * - * \warning You should not modify this data structure directly, instead use addField() and removeField(). - */ - const FieldListMap &fieldListMap() const; - - /*! - * Implements the unified property interface -- export function. - * The result is a one-to-one match of the Xiph comment, - * since it is completely compatible with the property interface - * (in fact, a Xiph comment is nothing more than a map from tag names to list of values, as is the dict interface). - */ - PropertyMap properties() const override; - - /*! - * Implements the unified property interface -- import function. - * The tags from the given map will be stored one-to-one in the file, - * except for invalid keys (less than one character, non-ASCII, - * or containing '=' or '~') in which case the according values will be contained in the returned PropertyMap. - */ - PropertyMap setProperties(const PropertyMap&) override; - - /*! - * Check if the given String is a valid Xiph comment key. - */ - static bool checkKey(const String &); - - /*! - * Returns the vendor ID of the Ogg Vorbis encoder. - * libvorbis 1.0 as the most common case always returns "Xiph.Org libVorbis I 20020717". - */ - String vendorID() const; - - /*! - * Add the field specified by \a key with the data \a value. - * If \a replace is true, then all of the other fields with the same key will be removed first. - * - * If the field value is empty, the field will be removed. - */ - void addField(const String &key, const String &value, bool replace = true); - - /*! - * Remove all the fields specified by \a key. - * - * \see removeAllFields() - */ - void removeFields(const String &key); - - /*! - * Remove all the fields specified by \a key with the data \a value. - * - * \see removeAllFields() - */ - void removeFields(const String &key, const String &value); - - /*! - * Remove all the fields in the comment. - * - * \see removeFields() - */ - void removeAllFields(); - - /*! - * Returns true if the field is contained within the comment. - * - * \note This is safer than checking for membership in the FieldListMap. - */ - bool contains(const String &key) const; - - /*! - * Renders the comment to a ByteVector suitable for inserting into a file. - * - * If \a addFramingBit is true the standard Vorbis comment framing bit will - * be appended. However some formats (notably FLAC) do not work with this - * in place. - */ - ByteVector render(bool addFramingBit = true) const; - - - /*! - * Returns a list of pictures attached to the xiph comment. - */ - List pictureList(); - - /*! - * Removes an 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(FLAC::Picture *picture, bool del = true); - - /*! - * Remove all pictures. - */ - void removeAllPictures(); - - /*! - * Add a new picture to the comment block. The comment block takes ownership of the picture and will handle freeing its memory. - * - * \note The file will be saved only after calling save(). - */ - void addPicture(FLAC::Picture *picture); - - protected: - /*! - * Reads the tag from the file specified in the constructor and fills the FieldListMap. - */ - void parse(const ByteVector &data); - - private: - XiphComment(const XiphComment&); - XiphComment &operator=(const XiphComment&); - - class XiphCommentPrivate; - XiphCommentPrivate *d; -}; -} // namespace Ogg -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/riff/aiff/aifffile.cpp b/3rdparty/taglib/riff/aiff/aifffile.cpp deleted file mode 100644 index 8b495f61..00000000 --- a/3rdparty/taglib/riff/aiff/aifffile.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/*************************************************************************** - copyright : (C) 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "tbytevector.h" -#include "tdebug.h" -#include "id3v2tag.h" -#include "tstringlist.h" -#include "tpropertymap.h" -#include "tagutils.h" - -#include "aifffile.h" - -using namespace Strawberry_TagLib::TagLib; - -class RIFF::AIFF::File::FilePrivate { - public: - FilePrivate() : hasID3v2(false) {} - - std::unique_ptr properties; - std::unique_ptr tag; - - bool hasID3v2; -}; - -//////////////////////////////////////////////////////////////////////////////// -// static members -//////////////////////////////////////////////////////////////////////////////// - -bool RIFF::AIFF::File::isSupported(IOStream *stream) { - - // An AIFF file has to start with "FORM????AIFF" or "FORM????AIFC". - - const ByteVector id = Utils::readHeader(stream, 12, false); - return (id.startsWith("FORM") && (id.containsAt("AIFF", 8) || id.containsAt("AIFC", 8))); - -} - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -RIFF::AIFF::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) : RIFF::File(file, BigEndian), d(new FilePrivate()) { - - if (isOpen()) - read(readProperties); -} - -RIFF::AIFF::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle) : RIFF::File(stream, BigEndian), d(new FilePrivate()) { - - if (isOpen()) - read(readProperties); -} - -RIFF::AIFF::File::~File() { - delete d; -} - -ID3v2::Tag *RIFF::AIFF::File::tag() const { - return d->tag.get(); -} - -RIFF::AIFF::AudioProperties *RIFF::AIFF::File::audioProperties() const { - return d->properties.get(); -} - -bool RIFF::AIFF::File::save() { - return save(ID3v2::v4); -} - -bool RIFF::AIFF::File::save(ID3v2::Version version) { - - if (readOnly()) { - debug("RIFF::AIFF::File::save() -- File is read only."); - return false; - } - - if (!isValid()) { - debug("RIFF::AIFF::File::save() -- Trying to save invalid file."); - return false; - } - - if (d->hasID3v2) { - removeChunk("ID3 "); - removeChunk("id3 "); - d->hasID3v2 = false; - } - - if (tag() && !tag()->isEmpty()) { - setChunkData("ID3 ", d->tag->render(version)); - d->hasID3v2 = true; - } - - return true; - -} - -bool RIFF::AIFF::File::hasID3v2Tag() const { - return d->hasID3v2; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void RIFF::AIFF::File::read(bool readProperties) { - - for (unsigned int i = 0; i < chunkCount(); ++i) { - const ByteVector name = chunkName(i); - if (name == "ID3 " || name == "id3 ") { - if (!d->tag) { - d->tag.reset(new ID3v2::Tag(this, chunkOffset(i))); - d->hasID3v2 = true; - } - else { - debug("RIFF::AIFF::File::read() - Duplicate ID3v2 tag found."); - } - } - } - - if (!d->tag) - d->tag.reset(new ID3v2::Tag()); - - if (readProperties) - d->properties.reset(new AudioProperties(this, AudioProperties::Average)); - -} diff --git a/3rdparty/taglib/riff/aiff/aifffile.h b/3rdparty/taglib/riff/aiff/aifffile.h deleted file mode 100644 index c527b37a..00000000 --- a/3rdparty/taglib/riff/aiff/aifffile.h +++ /dev/null @@ -1,141 +0,0 @@ -/*************************************************************************** - copyright : (C) 2008 by Scott Wheeler - email : wheeler@kde.org -***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_AIFFFILE_H -#define TAGLIB_AIFFFILE_H - -#include "rifffile.h" -#include "id3v2tag.h" -#include "aiffproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace RIFF { - -//! An implementation of AIFF metadata - -/*! - * This is implementation of AIFF metadata. - * - * This supports an ID3v2 tag as well as reading stream from the ID3 RIFF - * chunk as well as properties from the file. - */ - -namespace AIFF { - -//! An implementation of TagLib::File with AIFF specific methods - -/*! - * This implements and provides an interface for AIFF 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 AIFF files. - */ - -class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::RIFF::File { - public: - /*! - * Constructs an AIFF file from \a file. - * If \a readProperties is true the file's audio properties will also be read. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Constructs an AIFF file from \a stream. - * If \a readProperties is true the file's audio properties will also be read. - * - * \note TagLib will *not* take ownership of the stream, the caller is - * responsible for deleting it after the File object. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Destroys this instance of the File. - */ - ~File() override; - - /*! - * Returns the Tag for this file. - * - * \note This always returns a valid pointer regardless of whether or not the file on disk has an ID3v2 tag. - * Use hasID3v2Tag() to check if the file on disk actually has an ID3v2 tag. - * - * \see hasID3v2Tag() - */ - ID3v2::Tag *tag() const override; - - /*! - * Returns the AIFF::AudioProperties for this file. - * If no audio properties were read then this will return a null pointer. - */ - AudioProperties *audioProperties() const override; - - /*! - * Saves the file. - */ - bool save() override; - - /*! - * Save using a specific ID3v2 version (e.g. v3) - */ - bool save(ID3v2::Version version); - - /*! - * Returns whether or not the file on disk actually has an ID3v2 tag. - * - * \see ID3v2Tag() - */ - bool hasID3v2Tag() const; - - /*! - * Check if the given \a stream can be opened as an AIFF 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); - - friend class AudioProperties; - - class FilePrivate; - FilePrivate *d; -}; - -} // namespace AIFF -} // namespace RIFF -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/riff/aiff/aiffproperties.cpp b/3rdparty/taglib/riff/aiff/aiffproperties.cpp deleted file mode 100644 index 40dbafc4..00000000 --- a/3rdparty/taglib/riff/aiff/aiffproperties.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/*************************************************************************** - copyright : (C) 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tstring.h" -#include "tdebug.h" -#include "aifffile.h" -#include "aiffproperties.h" - -using namespace Strawberry_TagLib::TagLib; - -class RIFF::AIFF::AudioProperties::AudioPropertiesPrivate { - public: - AudioPropertiesPrivate() : length(0), - bitrate(0), - sampleRate(0), - channels(0), - bitsPerSample(0), - sampleFrames(0) {} - - int length; - int bitrate; - int sampleRate; - int channels; - int bitsPerSample; - - ByteVector compressionType; - String compressionName; - - unsigned int sampleFrames; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -RIFF::AIFF::AudioProperties::AudioProperties(File *file, ReadStyle) : Strawberry_TagLib::TagLib::AudioProperties(), d(new AudioPropertiesPrivate()) { - read(file); -} - -RIFF::AIFF::AudioProperties::~AudioProperties() { - delete d; -} - -int RIFF::AIFF::AudioProperties::lengthInSeconds() const { - return d->length / 1000; -} - -int RIFF::AIFF::AudioProperties::lengthInMilliseconds() const { - return d->length; -} - -int RIFF::AIFF::AudioProperties::bitrate() const { - return d->bitrate; -} - -int RIFF::AIFF::AudioProperties::sampleRate() const { - return d->sampleRate; -} - -int RIFF::AIFF::AudioProperties::channels() const { - return d->channels; -} - -int RIFF::AIFF::AudioProperties::bitsPerSample() const { - return d->bitsPerSample; -} - -unsigned int RIFF::AIFF::AudioProperties::sampleFrames() const { - return d->sampleFrames; -} - -bool RIFF::AIFF::AudioProperties::isAiffC() const { - return (!d->compressionType.isEmpty()); -} - -ByteVector RIFF::AIFF::AudioProperties::compressionType() const { - return d->compressionType; -} - -String RIFF::AIFF::AudioProperties::compressionName() const { - return d->compressionName; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void RIFF::AIFF::AudioProperties::read(File *file) { - - ByteVector data; - unsigned int streamLength = 0; - for (unsigned int i = 0; i < file->chunkCount(); i++) { - const ByteVector name = file->chunkName(i); - if (name == "COMM") { - if (data.isEmpty()) - data = file->chunkData(i); - else - debug("RIFF::AIFF::AudioProperties::read() - Duplicate 'COMM' chunk found."); - } - else if (name == "SSND") { - if (streamLength == 0) - streamLength = file->chunkDataSize(i) + file->chunkPadding(i); - else - debug("RIFF::AIFF::AudioProperties::read() - Duplicate 'SSND' chunk found."); - } - } - - if (data.size() < 18) { - debug("RIFF::AIFF::AudioProperties::read() - 'COMM' chunk not found or too short."); - return; - } - - if (streamLength == 0) { - debug("RIFF::AIFF::AudioProperties::read() - 'SSND' chunk not found."); - return; - } - - d->channels = data.toUInt16BE(0); - d->sampleFrames = data.toUInt32BE(2); - d->bitsPerSample = data.toUInt16BE(6); - - const long double sampleRate = data.toFloat80BE(8); - if (sampleRate >= 1.0) - d->sampleRate = static_cast(sampleRate + 0.5); - - if (d->sampleFrames > 0 && d->sampleRate > 0) { - const double length = d->sampleFrames * 1000.0 / sampleRate; - d->length = static_cast(length + 0.5); - d->bitrate = static_cast(streamLength * 8.0 / length + 0.5); - } - - if (data.size() >= 23) { - d->compressionType = data.mid(18, 4); - d->compressionName = String(data.mid(23, static_cast(data[22])), String::Latin1); - } - -} diff --git a/3rdparty/taglib/riff/aiff/aiffproperties.h b/3rdparty/taglib/riff/aiff/aiffproperties.h deleted file mode 100644 index f53a9996..00000000 --- a/3rdparty/taglib/riff/aiff/aiffproperties.h +++ /dev/null @@ -1,130 +0,0 @@ -/*************************************************************************** - copyright : (C) 2008 by Scott Wheeler - email : wheeler@kde.org -***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_AIFFPROPERTIES_H -#define TAGLIB_AIFFPROPERTIES_H - -#include "audioproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace RIFF { -namespace AIFF { - -class File; - -//! An implementation of audio property reading for AIFF - -/*! - * This reads the data from an AIFF stream found in the AudioProperties API. - */ - -class TAGLIB_EXPORT AudioProperties : public Strawberry_TagLib::TagLib::AudioProperties { - public: - - /*! - * Create an instance of AIFF::AudioProperties with the data read from the AIFF::File \a file. - */ - explicit AudioProperties(File *file, ReadStyle style); - - /*! - * Destroys this AIFF::AudioProperties instance. - */ - ~AudioProperties() override; - - /*! - * Returns the length of the file in seconds. The length is rounded down to the nearest whole second. - * - * \see lengthInMilliseconds() - */ - int lengthInSeconds() const override; - - /*! - * Returns the length of the file in milliseconds. - * - * \see lengthInSeconds() - */ - int lengthInMilliseconds() const override; - - /*! - * Returns the average bit rate of the file in kb/s. - */ - int bitrate() const override; - - /*! - * Returns the sample rate in Hz. - */ - int sampleRate() const override; - - /*! - * Returns the number of audio channels. - */ - int channels() const override; - - /*! - * Returns the number of bits per audio sample. - */ - int bitsPerSample() const; - - /*! - * Returns the number of sample frames - */ - unsigned int sampleFrames() const; - - /*! - * Returns true if the file is in AIFF-C format, false if AIFF format. - */ - bool isAiffC() const; - - /*! - * Returns the compression type of the AIFF-C file. For example, "NONE" for not compressed, "ACE2" for ACE 2-to-1. - * - * If the file is in AIFF format, always returns an empty vector. - * - * \see isAiffC() - */ - ByteVector compressionType() const; - - /*! - * Returns the concrete compression name of the AIFF-C file. - * - * If the file is in AIFF format, always returns an empty string. - * - * \see isAiffC() - */ - String compressionName() const; - - private: - void read(File *file); - - class AudioPropertiesPrivate; - AudioPropertiesPrivate *d; -}; -} // namespace AIFF -} // namespace RIFF -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/riff/rifffile.cpp b/3rdparty/taglib/riff/rifffile.cpp deleted file mode 100644 index 2e99e931..00000000 --- a/3rdparty/taglib/riff/rifffile.cpp +++ /dev/null @@ -1,382 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include -#include - -#include "tbytevector.h" -#include "tdebug.h" -#include "tstring.h" - -#include "rifffile.h" -#include "riffutils.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { -struct Chunk { - ByteVector name; - long long offset; - unsigned int size; - unsigned int padding; -}; - -unsigned int toUInt32(const ByteVector &v, size_t offset, ByteOrder endian) { - if (endian == BigEndian) - return v.toUInt32BE(offset); - else - return v.toUInt32LE(offset); -} - -ByteVector fromUInt32(size_t value, ByteOrder endian) { - if (endian == BigEndian) - return ByteVector::fromUInt32BE(value); - else - return ByteVector::fromUInt32LE(value); -} -} // namespace - -class RIFF::File::FilePrivate { - public: - explicit FilePrivate(ByteOrder _endianness) : endianness(_endianness), size(0), sizeOffset(0) {} - - const ByteOrder endianness; - - unsigned int size; - long long sizeOffset; - - std::vector chunks; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -RIFF::File::~File() { - delete d; -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -RIFF::File::File(FileName file, ByteOrder endianness) : Strawberry_TagLib::TagLib::File(file), d(new FilePrivate(endianness)) { - if (isOpen()) - read(); -} - -RIFF::File::File(IOStream *stream, ByteOrder endianness) : Strawberry_TagLib::TagLib::File(stream), d(new FilePrivate(endianness)) { - if (isOpen()) - read(); -} - -unsigned int RIFF::File::riffSize() const { - return d->size; -} - -size_t RIFF::File::chunkCount() const { - return d->chunks.size(); -} - -unsigned int RIFF::File::chunkDataSize(unsigned int i) const { - - if (i >= d->chunks.size()) { - debug("RIFF::File::chunkDataSize() - Index out of range. Returning 0."); - return 0; - } - - return d->chunks[i].size; - -} - -long long RIFF::File::chunkOffset(unsigned int i) const { - - if (i >= d->chunks.size()) { - debug("RIFF::File::chunkOffset() - Index out of range. Returning 0."); - return 0; - } - - return d->chunks[i].offset; - -} - -unsigned int RIFF::File::chunkPadding(unsigned int i) const { - - if (i >= d->chunks.size()) { - debug("RIFF::File::chunkPadding() - Index out of range. Returning 0."); - return 0; - } - - return d->chunks[i].padding; - -} - -ByteVector RIFF::File::chunkName(unsigned int i) const { - - if (i >= d->chunks.size()) { - debug("RIFF::File::chunkName() - Index out of range. Returning an empty vector."); - return ByteVector(); - } - - return d->chunks[i].name; - -} - -ByteVector RIFF::File::chunkData(unsigned int i) { - - if (i >= d->chunks.size()) { - debug("RIFF::File::chunkData() - Index out of range. Returning an empty vector."); - return ByteVector(); - } - - seek(d->chunks[i].offset); - return readBlock(d->chunks[i].size); - -} - -void RIFF::File::setChunkData(unsigned int i, const ByteVector &data) { - - if (i >= d->chunks.size()) { - debug("RIFF::File::setChunkData() - Index out of range."); - return; - } - - // Now update the specific chunk - - std::vector::iterator it = d->chunks.begin(); - std::advance(it, i); - - const long long originalSize = static_cast(it->size) + it->padding; - - writeChunk(it->name, data, it->offset - 8, it->size + it->padding + 8); - - it->size = data.size(); - it->padding = data.size() % 2; - - const long long diff = static_cast(it->size) + it->padding - originalSize; - - // Now update the internal offsets - - for (++it; it != d->chunks.end(); ++it) - it->offset += diff; - - // Update the global size. - - updateGlobalSize(); - -} - -void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data) { - setChunkData(name, data, false); -} - -void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data, bool alwaysCreate) { - - if (d->chunks.empty()) { - debug("RIFF::File::setChunkData - No valid chunks found."); - return; - } - - if (alwaysCreate && name != "LIST") { - debug("RIFF::File::setChunkData - alwaysCreate should be used for only \"LIST\" chunks."); - return; - } - - if (!alwaysCreate) { - for (unsigned int i = 0; i < d->chunks.size(); i++) { - if (d->chunks[i].name == name) { - setChunkData(i, data); - return; - } - } - } - - // Couldn't find an existing chunk, so let's create a new one. - - // Adjust the padding of the last chunk to place the new chunk at even position. - - Chunk &last = d->chunks.back(); - - long long offset = last.offset + last.size + last.padding; - if (offset & 1) { - if (last.padding == 1) { - last.padding = 0; // This should not happen unless the file is corrupted. - offset--; - removeBlock(offset, 1); - } - else { - insert(ByteVector("\0", 1), offset, 0); - last.padding = 1; - offset++; - } - } - - // Now add the chunk to the file. - - writeChunk(name, data, offset, 0); - - // And update our internal structure - - Chunk chunk; - chunk.name = name; - chunk.size = data.size(); - chunk.offset = offset + 8; - chunk.padding = data.size() % 2; - - d->chunks.push_back(chunk); - - // Update the global size. - - updateGlobalSize(); - -} - -void RIFF::File::removeChunk(unsigned int i) { - - if (i >= d->chunks.size()) { - debug("RIFF::File::removeChunk() - Index out of range."); - return; - } - - std::vector::iterator it = d->chunks.begin(); - std::advance(it, i); - - const size_t removeSize = it->size + it->padding + 8; - removeBlock(it->offset - 8, removeSize); - it = d->chunks.erase(it); - - for (; it != d->chunks.end(); ++it) - it->offset -= removeSize; - - // Update the global size. - - updateGlobalSize(); - -} - -void RIFF::File::removeChunk(const ByteVector &name) { - - for (int i = static_cast(d->chunks.size()) - 1; i >= 0; --i) { - if (d->chunks[i].name == name) - removeChunk(i); - } - -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void RIFF::File::read() { - - long long offset = tell(); - - offset += 4; - d->sizeOffset = offset; - - seek(offset); - d->size = toUInt32(readBlock(4), 0, d->endianness); - - offset += 8; - - // + 8: chunk header at least, fix for additional junk bytes - while (offset + 8 <= length()) { - - seek(offset); - const ByteVector chunkName = readBlock(4); - const unsigned int chunkSize = toUInt32(readBlock(4), 0, d->endianness); - - if (!isValidChunkName(chunkName)) { - debug("RIFF::File::read() -- Chunk '" + chunkName + "' has invalid ID"); - setValid(false); - break; - } - - if (offset + 8 + chunkSize > length()) { - debug("RIFF::File::read() -- Chunk '" + chunkName + "' has invalid size (larger than the file size)"); - setValid(false); - break; - } - - Chunk chunk; - chunk.name = chunkName; - chunk.size = chunkSize; - chunk.offset = offset + 8; - chunk.padding = 0; - - offset = chunk.offset + chunk.size; - - // Check padding - - if (offset & 1) { - seek(offset); - const ByteVector iByte = readBlock(1); - if (iByte.size() == 1) { - bool skipPadding = iByte[0] == '\0'; - if (!skipPadding) { - // Padding byte is not zero, check if it is good to ignore it - const ByteVector fourCcAfterPadding = readBlock(4); - if (isValidChunkName(fourCcAfterPadding)) { - // Use the padding, it is followed by a valid chunk name. - skipPadding = true; - } - } - if (skipPadding) { - chunk.padding = 1; - offset++; - } - } - } - - d->chunks.push_back(chunk); - } - -} - -void RIFF::File::writeChunk(const ByteVector &name, const ByteVector &data, long long offset, size_t replace) { - - ByteVector combined; - - combined.append(name); - combined.append(fromUInt32(data.size(), d->endianness)); - combined.append(data); - - if (data.size() & 1) - combined.resize(combined.size() + 1, '\0'); - - insert(combined, offset, replace); - -} - -void RIFF::File::updateGlobalSize() { - - const Chunk first = d->chunks.front(); - const Chunk last = d->chunks.back(); - d->size = last.offset + last.size + last.padding - first.offset + 12; - - const ByteVector data = fromUInt32(d->size, d->endianness); - insert(data, d->sizeOffset, 4); - -} diff --git a/3rdparty/taglib/riff/rifffile.h b/3rdparty/taglib/riff/rifffile.h deleted file mode 100644 index ae7e05df..00000000 --- a/3rdparty/taglib/riff/rifffile.h +++ /dev/null @@ -1,157 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_RIFFFILE_H -#define TAGLIB_RIFFFILE_H - -#include "taglib_export.h" -#include "tfile.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -//! An implementation of TagLib::File with RIFF specific methods - -namespace RIFF { - -//! An RIFF file class with some useful methods specific to RIFF - -/*! - * This implements the generic TagLib::File API and additionally provides - * access to properties that are distinct to RIFF files, notably access - * to the different ID3 tags. - */ - -class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File { - public: - /*! - * Destroys this instance of the File. - */ - ~File() override; - - protected: - explicit File(FileName file, ByteOrder endianness); - explicit File(IOStream *stream, ByteOrder endianness); - - /*! - * \return The size of the main RIFF chunk. - */ - unsigned int riffSize() const; - - /*! - * \return The number of chunks in the file. - */ - size_t chunkCount() const; - - /*! - * \return The offset within the file for the selected chunk number. - */ - long long chunkOffset(unsigned int i) const; - - /*! - * \return The size of the chunk data. - */ - unsigned int chunkDataSize(unsigned int i) const; - - /*! - * \return The size of the padding after the chunk (can be either 0 or 1). - */ - unsigned int chunkPadding(unsigned int i) const; - - /*! - * \return The name of the specified chunk, for instance, "COMM" or "ID3 " - */ - ByteVector chunkName(unsigned int i) const; - - /*! - * Reads the chunk data from the file and returns it. - * - * \note This \e will move the read pointer for the file. - */ - ByteVector chunkData(unsigned int i); - - /*! - * Sets the data for the specified chunk to \a data. - * - * \warning This will update the file immediately. - */ - void setChunkData(unsigned int i, const ByteVector &data); - - /*! - * Sets the data for the chunk \a name to \a data. - * If a chunk with the given name already exists it will be overwritten, otherwise it will be created after the existing chunks. - * - * \warning This will update the file immediately. - */ - void setChunkData(const ByteVector &name, const ByteVector &data); - - /*! - * Sets the data for the chunk \a name to \a data. - * If a chunk with the given name already exists it will be overwritten, otherwise it will be created after the existing chunks. - * - * \note If \a alwaysCreate is true, a new chunk is created regardless of - * whether or not the chunk \a name exists. It should only be used for - * "LIST" chunks. - * - * \warning This will update the file immediately. - */ - void setChunkData(const ByteVector &name, const ByteVector &data, bool alwaysCreate); - - /*! - * Removes the specified chunk. - * - * \warning This will update the file immediately. - */ - void removeChunk(unsigned int i); - - /*! - * Removes the chunk \a name. - * - * \warning This will update the file immediately. - * \warning This removes all the chunks with the given name. - */ - void removeChunk(const ByteVector &name); - - private: - File(const File&); - File &operator=(const File&); - - void read(); - void writeChunk(const ByteVector &name, const ByteVector &data, long long offset, size_t replace = 0); - - /*! - * Update the global RIFF size based on the current internal structure. - */ - void updateGlobalSize(); - - class FilePrivate; - FilePrivate *d; -}; - -} // namespace RIFF -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/riff/riffutils.h b/3rdparty/taglib/riff/riffutils.h deleted file mode 100644 index 710d35e5..00000000 --- a/3rdparty/taglib/riff/riffutils.h +++ /dev/null @@ -1,60 +0,0 @@ -/*************************************************************************** - copyright : (C) 2015 by Tsuda Kageyu - email : tsuda.kageyu@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_RIFFUTILS_H -#define TAGLIB_RIFFUTILS_H - -// THIS FILE IS NOT A PART OF THE TAGLIB API - -#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header - -namespace Strawberry_TagLib { -namespace TagLib { -namespace RIFF { -namespace { - -inline bool isValidChunkName(const ByteVector &name) { - - if (name.size() != 4) - return false; - - for (ByteVector::ConstIterator it = name.begin(); it != name.end(); ++it) { - const int c = static_cast(*it); - if (c < 32 || 127 < c) - return false; - } - - return true; - -} - -} // namespace -} // namespace RIFF -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif - -#endif diff --git a/3rdparty/taglib/riff/wav/infotag.cpp b/3rdparty/taglib/riff/wav/infotag.cpp deleted file mode 100644 index 01a16ecd..00000000 --- a/3rdparty/taglib/riff/wav/infotag.cpp +++ /dev/null @@ -1,234 +0,0 @@ -/*************************************************************************** - copyright : (C) 2012 by Tsuda Kageyu - email : tsuda.kageyu@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tdebug.h" -#include "tfile.h" -#include "tpicturemap.h" - -#include "rifffile.h" -#include "infotag.h" -#include "riffutils.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace RIFF::Info; - -namespace { -class DefaultStringHandler : public Strawberry_TagLib::TagLib::StringHandler { - public: - explicit DefaultStringHandler() : Strawberry_TagLib::TagLib::StringHandler() {} - - String parse(const ByteVector &data) const override { - return String(data, String::UTF8); - } - - ByteVector render(const String &s) const override { - return s.data(String::UTF8); - } -}; - -const DefaultStringHandler defaultStringHandler; -const Strawberry_TagLib::TagLib::StringHandler *stringHandler = &defaultStringHandler; -} // namespace - -class RIFF::Info::Tag::TagPrivate { - public: - FieldMap fieldMap; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -RIFF::Info::Tag::Tag(const ByteVector &data) : d(new TagPrivate()) { - parse(data); -} - -RIFF::Info::Tag::Tag() : d(new TagPrivate()) {} - -RIFF::Info::Tag::~Tag() { - delete d; -} - -String RIFF::Info::Tag::title() const { - return fieldText("INAM"); -} - -String RIFF::Info::Tag::artist() const { - return fieldText("IART"); -} - -String RIFF::Info::Tag::album() const { - return fieldText("IPRD"); -} - -String RIFF::Info::Tag::comment() const { - return fieldText("ICMT"); -} - -String RIFF::Info::Tag::genre() const { - return fieldText("IGNR"); -} - -unsigned int RIFF::Info::Tag::year() const { - return fieldText("ICRD").substr(0, 4).toInt(); -} - -unsigned int RIFF::Info::Tag::track() const { - return fieldText("IPRT").toInt(); -} - -Strawberry_TagLib::TagLib::PictureMap RIFF::Info::Tag::pictures() const { - return PictureMap(); -} - -void RIFF::Info::Tag::setTitle(const String &s) { - setFieldText("INAM", s); -} - -void RIFF::Info::Tag::setArtist(const String &s) { - setFieldText("IART", s); -} - -void RIFF::Info::Tag::setAlbum(const String &s) { - setFieldText("IPRD", s); -} - -void RIFF::Info::Tag::setComment(const String &s) { - setFieldText("ICMT", s); -} - -void RIFF::Info::Tag::setGenre(const String &s) { - setFieldText("IGNR", s); -} - -void RIFF::Info::Tag::setYear(unsigned int i) { - if (i != 0) - setFieldText("ICRD", String::number(i)); - else - d->fieldMap.erase("ICRD"); -} - -void RIFF::Info::Tag::setTrack(unsigned int i) { - if (i != 0) - setFieldText("IPRT", String::number(i)); - else - d->fieldMap.erase("IPRT"); -} - -void RIFF::Info::Tag::setPictures(const PictureMap&) {} - -bool RIFF::Info::Tag::isEmpty() const { - return d->fieldMap.isEmpty(); -} - -FieldMap RIFF::Info::Tag::fieldMap() const { - return d->fieldMap; -} - -String RIFF::Info::Tag::fieldText(const ByteVector &id) const { - - if (d->fieldMap.contains(id)) - return String(d->fieldMap[id]); - else - return String(); - -} - -void RIFF::Info::Tag::setFieldText(const ByteVector &id, const String &s) { - - // id must be four-byte long pure ascii string. - if (!isValidChunkName(id)) - return; - - if (!s.isEmpty()) - d->fieldMap[id] = s; - else - removeField(id); - -} - -void RIFF::Info::Tag::removeField(const ByteVector &id) { - - if (d->fieldMap.contains(id)) - d->fieldMap.erase(id); - -} - -ByteVector RIFF::Info::Tag::render() const { - - ByteVector data("INFO"); - - for (FieldMap::ConstIterator it = d->fieldMap.begin(); it != d->fieldMap.end(); ++it) { - ByteVector text = stringHandler->render(it->second); - if (text.isEmpty()) - continue; - - data.append(it->first); - data.append(ByteVector::fromUInt32LE(text.size() + 1)); - data.append(text); - - do { - data.append('\0'); - } while (data.size() & 1); - } - - if (data.size() == 4) - return ByteVector(); - else - return data; - -} - -void RIFF::Info::Tag::setStringHandler(const Strawberry_TagLib::TagLib::StringHandler *handler) { - - if (handler) - stringHandler = handler; - else - stringHandler = &defaultStringHandler; - -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void RIFF::Info::Tag::parse(const ByteVector &data) { - - size_t p = 4; - while (p < data.size()) { - const unsigned int size = data.toUInt32LE(p + 4); - if (size > data.size() - p - 8) - break; - - const ByteVector id = data.mid(p, 4); - if (isValidChunkName(id)) { - const String text = stringHandler->parse(data.mid(p + 8, size)); - d->fieldMap[id] = text; - } - - p += ((size + 1) & ~1) + 8; - } - -} diff --git a/3rdparty/taglib/riff/wav/infotag.h b/3rdparty/taglib/riff/wav/infotag.h deleted file mode 100644 index 61dc0f8a..00000000 --- a/3rdparty/taglib/riff/wav/infotag.h +++ /dev/null @@ -1,162 +0,0 @@ -/*************************************************************************** - copyright : (C) 2012 by Tsuda Kageyu - email : tsuda.kageyu@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_INFOTAG_H -#define TAGLIB_INFOTAG_H - -#include "tag.h" -#include "tmap.h" -#include "tstring.h" -#include "tstringlist.h" -#include "tstringhandler.h" -#include "tbytevector.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -class File; - -//! A RIFF INFO tag implementation. -namespace RIFF { -namespace Info { - -typedef Map FieldMap; - -//! The main class in the RIFF INFO tag implementation - -/*! - * This is the main class in the INFO tag implementation. - * RIFF INFO tag is a metadata format found in WAV audio and AVI video files. - * Though it is a part of Microsoft/IBM's RIFF specification, - * the author could not find the official documents about it. - * So, this implementation is referring to unofficial documents online and some applications' - * behaviors especially Windows Explorer. - */ -class TAGLIB_EXPORT Tag : public Strawberry_TagLib::TagLib::Tag { - public: - /*! - * Constructs an empty INFO tag. - */ - explicit Tag(); - - /*! - * Constructs an INFO tag read from \a data which is contents of "LIST" chunk. - */ - explicit Tag(const ByteVector &data); - - ~Tag() override; - - // Reimplementations - - String title() const override; - String artist() const override; - String album() const override; - String comment() const override; - String genre() const override; - unsigned int year() const override; - unsigned int track() const override; - PictureMap pictures() const override; - - void setTitle(const String &s) override; - void setArtist(const String &s) override; - void setAlbum(const String &s) override; - void setComment(const String &s) override; - void setGenre(const String &s) override; - void setYear(unsigned int i) override; - void setTrack(unsigned int i) override; - void setPictures(const PictureMap&) override; - - bool isEmpty() const override; - - /*! - * Returns a copy of the internal fields of the tag. - * The returned map directly reflects the contents of the "INFO" chunk. - * - * \note Modifying this map does not affect the tag's internal data. - * Use setFieldText() and removeField() instead. - * - * \see setFieldText() - * \see removeField() - */ - FieldMap fieldMap() const; - - /* - * Gets the value of the field with the ID \a id. - */ - String fieldText(const ByteVector &id) const; - - /* - * Sets the value of the field with the ID \a id to \a s. - * If the field does not exist, it is created. - * If \s is empty, the field is removed. - * - * \note fieldId must be four-byte long pure ASCII string. - * This function performs nothing if fieldId is invalid. - */ - void setFieldText(const ByteVector &id, const String &s); - - /* - * Removes the field with the ID \a id. - */ - void removeField(const ByteVector &id); - - /*! - * Render the tag back to binary data, suitable to be written to disk. - * - * \note Returns empty ByteVector is the tag contains no fields. - */ - ByteVector render() const; - - /*! - * Sets the string handler that decides how the text data will be - * converted to and from binary data. - * If the parameter \a handler is null, the previous handler is released and default UTF-8 handler is restored. - * - * \note The caller is responsible for deleting the previous handler as needed after it is released. - * - */ - static void setStringHandler(const Strawberry_TagLib::TagLib::StringHandler *handler); - - protected: - /*! - * Pareses the body of the tag in \a data. - */ - void parse(const ByteVector &data); - - private: - Tag(const Tag&); - Tag &operator=(const Tag&); - - class TagPrivate; - TagPrivate *d; -}; - -} // namespace Info -} // namespace RIFF -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/riff/wav/wavfile.cpp b/3rdparty/taglib/riff/wav/wavfile.cpp deleted file mode 100644 index fabde048..00000000 --- a/3rdparty/taglib/riff/wav/wavfile.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/*************************************************************************** - copyright : (C) 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "tbytevector.h" -#include "tdebug.h" -#include "tstringlist.h" -#include "tpropertymap.h" -#include "tagutils.h" - -#include "wavfile.h" -#include "id3v2tag.h" -#include "infotag.h" -#include "tagunion.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { -enum { - ID3v2Index = 0, - InfoIndex = 1 -}; -} - -class RIFF::WAV::File::FilePrivate { - public: - explicit FilePrivate() : hasID3v2(false), hasInfo(false) {} - - std::unique_ptr properties; - DoubleTagUnion tag; - - bool hasID3v2; - bool hasInfo; -}; - -//////////////////////////////////////////////////////////////////////////////// -// static members -//////////////////////////////////////////////////////////////////////////////// - -bool RIFF::WAV::File::isSupported(IOStream *stream) { - - // A WAV file has to start with "RIFF????WAVE". - - const ByteVector id = Utils::readHeader(stream, 12, false); - return (id.startsWith("RIFF") && id.containsAt("WAVE", 8)); - -} - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -RIFF::WAV::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) : RIFF::File(file, LittleEndian), d(new FilePrivate()) { - - if (isOpen()) - read(readProperties); - -} - -RIFF::WAV::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle) : RIFF::File(stream, LittleEndian), d(new FilePrivate()) { - - if (isOpen()) - read(readProperties); - -} - -RIFF::WAV::File::~File() { - delete d; -} - -Strawberry_TagLib::TagLib::Tag *RIFF::WAV::File::tag() const { - return &d->tag; -} - -ID3v2::Tag *RIFF::WAV::File::ID3v2Tag() const { - return d->tag.access(ID3v2Index, false); -} - -RIFF::Info::Tag *RIFF::WAV::File::InfoTag() const { - return d->tag.access(InfoIndex, false); -} - -void RIFF::WAV::File::strip(TagTypes tags) { - - removeTagChunks(tags); - - if (tags & ID3v2) - d->tag.set(ID3v2Index, new ID3v2::Tag()); - - if (tags & Info) - d->tag.set(InfoIndex, new RIFF::Info::Tag()); - -} - -PropertyMap RIFF::WAV::File::setProperties(const PropertyMap &properties) { - - InfoTag()->setProperties(properties); - return ID3v2Tag()->setProperties(properties); - -} - -RIFF::WAV::AudioProperties *RIFF::WAV::File::audioProperties() const { - return d->properties.get(); -} - -bool RIFF::WAV::File::save() { - return RIFF::WAV::File::save(AllTags); -} - -bool RIFF::WAV::File::save(TagTypes tags, StripTags strip, ID3v2::Version version) { - - if (readOnly()) { - debug("RIFF::WAV::File::save() -- File is read only."); - return false; - } - - if (!isValid()) { - debug("RIFF::WAV::File::save() -- Trying to save invalid file."); - return false; - } - - if (strip == StripOthers) - File::strip(static_cast(AllTags & ~tags)); - - if (tags & ID3v2) { - removeTagChunks(ID3v2); - - if (ID3v2Tag() && !ID3v2Tag()->isEmpty()) { - setChunkData("ID3 ", ID3v2Tag()->render(version)); - d->hasID3v2 = true; - } - } - - if (tags & Info) { - removeTagChunks(Info); - - if (InfoTag() && !InfoTag()->isEmpty()) { - setChunkData("LIST", InfoTag()->render(), true); - d->hasInfo = true; - } - } - - return true; - -} - -bool RIFF::WAV::File::hasID3v2Tag() const { - return d->hasID3v2; -} - -bool RIFF::WAV::File::hasInfoTag() const { - return d->hasInfo; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void RIFF::WAV::File::read(bool readProperties) { - - for (unsigned int i = 0; i < chunkCount(); ++i) { - const ByteVector name = chunkName(i); - if (name == "ID3 " || name == "id3 ") { - if (!d->tag[ID3v2Index]) { - d->tag.set(ID3v2Index, new ID3v2::Tag(this, chunkOffset(i))); - d->hasID3v2 = true; - } - else { - debug("RIFF::WAV::File::read() - Duplicate ID3v2 tag found."); - } - } - else if (name == "LIST") { - const ByteVector data = chunkData(i); - if (data.startsWith("INFO")) { - if (!d->tag[InfoIndex]) { - d->tag.set(InfoIndex, new RIFF::Info::Tag(data)); - d->hasInfo = true; - } - else { - debug("RIFF::WAV::File::read() - Duplicate INFO tag found."); - } - } - } - } - - if (!d->tag[ID3v2Index]) - d->tag.set(ID3v2Index, new ID3v2::Tag()); - - if (!d->tag[InfoIndex]) - d->tag.set(InfoIndex, new RIFF::Info::Tag()); - - if (readProperties) - d->properties.reset(new AudioProperties(this, AudioProperties::Average)); -} - -void RIFF::WAV::File::removeTagChunks(TagTypes tags) { - - if ((tags & ID3v2) && d->hasID3v2) { - removeChunk("ID3 "); - removeChunk("id3 "); - - d->hasID3v2 = false; - } - - if ((tags & Info) && d->hasInfo) { - for (int i = static_cast(chunkCount()) - 1; i >= 0; --i) { - if (chunkName(i) == "LIST" && chunkData(i).startsWith("INFO")) - removeChunk(i); - } - - d->hasInfo = false; - } - -} diff --git a/3rdparty/taglib/riff/wav/wavfile.h b/3rdparty/taglib/riff/wav/wavfile.h deleted file mode 100644 index d28482e1..00000000 --- a/3rdparty/taglib/riff/wav/wavfile.h +++ /dev/null @@ -1,193 +0,0 @@ -/*************************************************************************** - copyright : (C) 2008 by Scott Wheeler - email : wheeler@kde.org -***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_WAVFILE_H -#define TAGLIB_WAVFILE_H - -#include "rifffile.h" -#include "id3v2tag.h" -#include "infotag.h" -#include "wavproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -namespace RIFF { - -//! An implementation of WAV metadata - -/*! - * This is implementation of WAV metadata. - * - * This supports an ID3v2 tag as well as reading stream from the ID3 RIFF - * chunk as well as properties from the file. - */ - -namespace WAV { - -//! An implementation of TagLib::File with WAV specific methods - -/*! - * This implements and provides an interface for WAV 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 WAV files. - */ - -class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::RIFF::File { - public: - enum TagTypes { - //! Empty set. Matches no tag types. - NoTags = 0x0000, - //! Matches ID3v2 tags. - ID3v2 = 0x0001, - //! Matches INFO tags. - Info = 0x0002, - //! Matches all tag types. - AllTags = 0xffff - }; - - /*! - * Constructs a WAV file from \a file. - * If \a readProperties is true the file's audio properties will also be read. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Constructs a WAV file from \a stream. - * If \a readProperties is true the file's audio properties will also be read. - * - * \note TagLib will *not* take ownership of the stream, the caller is responsible for deleting it after the File object. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Destroys this instance of the File. - */ - ~File() override; - - /*! - * Returns the ID3v2 Tag for this file. - * - * \note This method does not return all the tags for this file for backward compatibility. Will be fixed in TagLib 2.0. - */ - Strawberry_TagLib::TagLib::Tag *tag() const override; - - /*! - * Returns the ID3v2 Tag for this file. - * - * \note This always returns a valid pointer regardless of whether or not the file on disk has an ID3v2 tag. - * Use hasID3v2Tag() to check if the file on disk actually has an ID3v2 tag. - * - * \see hasID3v2Tag() - */ - ID3v2::Tag *ID3v2Tag() const; - - /*! - * Returns the RIFF INFO Tag for this file. - * - * \note This always returns a valid pointer regardless of whether or not the file on disk has a RIFF INFO tag. - * Use hasInfoTag() to check if the file on disk actually has a RIFF INFO tag. - * - * \see hasInfoTag() - */ - Info::Tag *InfoTag() const; - - /*! - * This will strip the tags that match the OR-ed together TagTypes from the file. - * By default it strips all tags. It returns true if the tags are successfully stripped. - * - * \note This will update the file immediately. - */ - void strip(TagTypes tags = AllTags); - - /*! - * Implements the unified property interface -- import function. - * This method forwards to ID3v2::Tag::setProperties(). - */ - PropertyMap setProperties(const PropertyMap &) override; - - /*! - * Returns the WAV::AudioProperties for this file. If no audio properties - * were read then this will return a null pointer. - */ - AudioProperties *audioProperties() const override; - - /*! - * Saves the file. - */ - bool save() override; - - /*! - * Save the file. - * If \a strip is specified, it is possible to choose if tags not specified in \a tags should be stripped from the file or retained. - * With \a version, it is possible to specify whether ID3v2.4 or ID3v2.3 should be used. - */ - bool save(TagTypes tags, StripTags strip = StripOthers, ID3v2::Version version = ID3v2::v4); - - /*! - * Returns whether or not the file on disk actually has an ID3v2 tag. - * - * \see ID3v2Tag() - */ - bool hasID3v2Tag() const; - - /*! - * Returns whether or not the file on disk actually has a RIFF INFO tag. - * - * \see InfoTag() - */ - bool hasInfoTag() const; - - /*! - * Returns whether or not the given \a stream can be opened as a WAV 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); - void removeTagChunks(TagTypes tags); - - friend class AudioProperties; - - class FilePrivate; - FilePrivate *d; -}; - -} // namespace WAV -} // namespace RIFF -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/riff/wav/wavproperties.cpp b/3rdparty/taglib/riff/wav/wavproperties.cpp deleted file mode 100644 index f2a15e9e..00000000 --- a/3rdparty/taglib/riff/wav/wavproperties.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/*************************************************************************** - copyright : (C) 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tdebug.h" -#include "wavfile.h" -#include "wavproperties.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { -// Quoted from RFC 2361. -enum WaveFormat { - FORMAT_UNKNOWN = 0x0000, - FORMAT_PCM = 0x0001 -}; -} // namespace - -class RIFF::WAV::AudioProperties::AudioPropertiesPrivate { - public: - explicit AudioPropertiesPrivate() : format(0), - length(0), - bitrate(0), - sampleRate(0), - channels(0), - bitsPerSample(0), - sampleFrames(0) {} - - int format; - int length; - int bitrate; - int sampleRate; - int channels; - int bitsPerSample; - unsigned int sampleFrames; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -Strawberry_TagLib::TagLib::RIFF::WAV::AudioProperties::AudioProperties(File *file, ReadStyle) : Strawberry_TagLib::TagLib::AudioProperties(), d(new AudioPropertiesPrivate()) { - read(file); -} - -RIFF::WAV::AudioProperties::~AudioProperties() { - delete d; -} - -int RIFF::WAV::AudioProperties::lengthInSeconds() const { - return d->length / 1000; -} - -int RIFF::WAV::AudioProperties::lengthInMilliseconds() const { - return d->length; -} - -int RIFF::WAV::AudioProperties::bitrate() const { - return d->bitrate; -} - -int RIFF::WAV::AudioProperties::sampleRate() const { - return d->sampleRate; -} - -int RIFF::WAV::AudioProperties::channels() const { - return d->channels; -} - -int RIFF::WAV::AudioProperties::bitsPerSample() const { - return d->bitsPerSample; -} - -unsigned int RIFF::WAV::AudioProperties::sampleFrames() const { - return d->sampleFrames; -} - -int RIFF::WAV::AudioProperties::format() const { - return d->format; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void RIFF::WAV::AudioProperties::read(File *file) { - - ByteVector data; - unsigned int streamLength = 0; - unsigned int totalSamples = 0; - - for (unsigned int i = 0; i < file->chunkCount(); ++i) { - const ByteVector name = file->chunkName(i); - if (name == "fmt ") { - if (data.isEmpty()) - data = file->chunkData(i); - else - debug("RIFF::WAV::AudioProperties::read() - Duplicate 'fmt ' chunk found."); - } - else if (name == "data") { - if (streamLength == 0) - streamLength = file->chunkDataSize(i) + file->chunkPadding(i); - else - debug("RIFF::WAV::AudioProperties::read() - Duplicate 'data' chunk found."); - } - else if (name == "fact") { - if (totalSamples == 0) - totalSamples = file->chunkData(i).toUInt32LE(0); - else - debug("RIFF::WAV::AudioProperties::read() - Duplicate 'fact' chunk found."); - } - } - - if (data.size() < 16) { - debug("RIFF::WAV::AudioProperties::read() - 'fmt ' chunk not found or too short."); - return; - } - - if (streamLength == 0) { - debug("RIFF::WAV::AudioProperties::read() - 'data' chunk not found."); - return; - } - - d->format = data.toUInt16LE(0); - if (d->format != FORMAT_PCM && totalSamples == 0) { - debug("RIFF::WAV::AudioProperties::read() - Non-PCM format, but 'fact' chunk not found."); - return; - } - - d->channels = data.toUInt16LE(2); - d->sampleRate = data.toUInt32LE(4); - d->bitsPerSample = data.toUInt16LE(14); - - if (d->format != FORMAT_PCM) - d->sampleFrames = totalSamples; - else if (d->channels > 0 && d->bitsPerSample > 0) - d->sampleFrames = streamLength / (d->channels * ((d->bitsPerSample + 7) / 8)); - - if (d->sampleFrames > 0 && d->sampleRate > 0) { - const double length = d->sampleFrames * 1000.0 / d->sampleRate; - d->length = static_cast(length + 0.5); - d->bitrate = static_cast(streamLength * 8.0 / length + 0.5); - } - else { - const unsigned int byteRate = data.toUInt32LE(8); - if (byteRate > 0) { - d->length = static_cast(streamLength * 1000.0 / byteRate + 0.5); - d->bitrate = static_cast(byteRate * 8.0 / 1000.0 + 0.5); - } - } - -} diff --git a/3rdparty/taglib/riff/wav/wavproperties.h b/3rdparty/taglib/riff/wav/wavproperties.h deleted file mode 100644 index e734fe10..00000000 --- a/3rdparty/taglib/riff/wav/wavproperties.h +++ /dev/null @@ -1,121 +0,0 @@ -/*************************************************************************** - copyright : (C) 2008 by Scott Wheeler - email : wheeler@kde.org -***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_WAVPROPERTIES_H -#define TAGLIB_WAVPROPERTIES_H - -#include "taglib.h" -#include "audioproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -class ByteVector; - -namespace RIFF { -namespace WAV { - -class File; - -//! An implementation of audio property reading for WAV - -/*! - * This reads the data from an WAV stream found in the AudioProperties - * API. - */ - -class TAGLIB_EXPORT AudioProperties : public Strawberry_TagLib::TagLib::AudioProperties { - public: - /*! - * Create an instance of WAV::AudioProperties with the data read from the WAV::File \a file. - */ - explicit AudioProperties(File *file, ReadStyle style = Average); - - /*! - * Destroys this WAV::AudioProperties instance. - */ - ~AudioProperties() override; - - /*! - * Returns the length of the file in seconds. - * The length is rounded down to the nearest whole second. - * - * \see lengthInMilliseconds() - */ - int lengthInSeconds() const override; - - /*! - * Returns the length of the file in milliseconds. - * - * \see lengthInSeconds() - */ - int lengthInMilliseconds() const override; - - /*! - * Returns the average bit rate of the file in kb/s. - */ - int bitrate() const override; - - /*! - * Returns the sample rate in Hz. - */ - int sampleRate() const override; - - /*! - * Returns the number of audio channels. - */ - int channels() const override; - - /*! - * Returns the number of bits per audio sample. - */ - int bitsPerSample() const; - - /*! - * Returns the number of sample frames. - */ - unsigned int sampleFrames() const; - - /*! - * Returns the format ID of the file. - * 0 for unknown, 1 for PCM, 2 for ADPCM, 3 for 32/64-bit IEEE754, and so forth. - * - * \note For further information, refer to the WAVE Form Registration Numbers in RFC 2361. - */ - int format() const; - - private: - void read(File *file); - - class AudioPropertiesPrivate; - AudioPropertiesPrivate *d; -}; - -} // namespace WAV -} // namespace RIFF -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/s3m/s3mfile.cpp b/3rdparty/taglib/s3m/s3mfile.cpp deleted file mode 100644 index bbe09eee..00000000 --- a/3rdparty/taglib/s3m/s3mfile.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - - -#include "s3mfile.h" -#include "tstringlist.h" -#include "tdebug.h" -#include "modfileprivate.h" -#include "tpropertymap.h" - -#include - -using namespace Strawberry_TagLib::TagLib; -using namespace S3M; - -class S3M::File::FilePrivate { - public: - explicit FilePrivate(AudioProperties::ReadStyle propertiesStyle) : properties(propertiesStyle) {} - - Mod::Tag tag; - S3M::AudioProperties properties; -}; - -S3M::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(file), d(new FilePrivate(propertiesStyle)) { - - if (isOpen()) - read(readProperties); - -} - -S3M::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(stream), d(new FilePrivate(propertiesStyle)) { - - if (isOpen()) - read(readProperties); - -} - -S3M::File::~File() { - delete d; -} - -Mod::Tag *S3M::File::tag() const { - return &d->tag; -} - -S3M::AudioProperties *S3M::File::audioProperties() const { - return &d->properties; -} - -bool S3M::File::save() { - - if (readOnly()) { - debug("S3M::File::save() - Cannot save to a read only file."); - return false; - } - // note: if title starts with "Extended Module: " - // the file would look like an .xm file - seek(0); - writeString(d->tag.title(), 27); - // string terminating NUL is not optional: - writeByte(0); - - seek(32); - - unsigned short length = 0; - unsigned short sampleCount = 0; - - if (!readU16L(length) || !readU16L(sampleCount)) - return false; - - seek(28, Current); - - int channels = 0; - for (int i = 0; i < 32; ++i) { - unsigned char setting = 0; - if (!readByte(setting)) - return false; - // or if(setting >= 128)? - // or channels = i + 1;? - // need a better spec! - if (setting != 0xff) ++channels; - } - - seek(channels, Current); - - StringList lines = d->tag.comment().split("\n"); - // write comment as sample names: - for (unsigned short i = 0; i < sampleCount; ++i) { - seek(96L + length + (static_cast(i) << 1)); - - unsigned short instrumentOffset = 0; - if (!readU16L(instrumentOffset)) - return false; - seek((static_cast(instrumentOffset) << 4) + 48); - - if (i < lines.size()) - writeString(lines[i], 27); - else - writeString(String(), 27); - // string terminating NUL is not optional: - writeByte(0); - } - return true; - -} - -void S3M::File::read(bool) { - - if (!isOpen()) - return; - - READ_STRING(d->tag.setTitle, 28); - READ_BYTE_AS(mark); - READ_BYTE_AS(type); - - READ_ASSERT(mark == 0x1A && type == 0x10); - - seek(32); - - READ_U16L_AS(length); - READ_U16L_AS(sampleCount); - - d->properties.setSampleCount(sampleCount); - - READ_U16L(d->properties.setPatternCount); - READ_U16L(d->properties.setFlags); - READ_U16L(d->properties.setTrackerVersion); - READ_U16L(d->properties.setFileFormatVersion); - - READ_ASSERT(readBlock(4) == "SCRM"); - - READ_BYTE(d->properties.setGlobalVolume); - READ_BYTE(d->properties.setBpmSpeed); - READ_BYTE(d->properties.setTempo); - - READ_BYTE_AS(masterVolume); - d->properties.setMasterVolume(masterVolume & 0x7f); - d->properties.setStereo((masterVolume & 0x80) != 0); - - // I've seen players who call the next two bytes - // "ultra click" and "use panning values" (if == 0xFC). - // I don't see them in any spec, though. - // Hm, but there is "UltraClick-removal" and some other - // variables in ScreamTracker IIIs GUI. - - seek(12, Current); - - int channels = 0; - for (int i = 0; i < 32; ++i) { - READ_BYTE_AS(setting); - // or if(setting >= 128)? - // or channels = i + 1;? - // need a better spec! - if (setting != 0xff) ++channels; - } - d->properties.setChannels(channels); - - seek(96); - unsigned short realLength = 0; - for (unsigned short i = 0; i < length; ++i) { - READ_BYTE_AS(order); - if (order == 255) break; - if (order != 254) ++realLength; - } - d->properties.setLengthInPatterns(realLength); - - seek(channels, Current); - - // Note: The S3M spec mentions samples and instruments, but in - // the header there are only pointers to instruments. - // However, there I never found instruments (SCRI) but - // instead samples (SCRS). - StringList comment; - for (unsigned short i = 0; i < sampleCount; ++i) { - seek(96L + length + (static_cast(i) << 1)); - - READ_U16L_AS(sampleHeaderOffset); - seek(static_cast(sampleHeaderOffset) << 4); - - READ_BYTE_AS(sampleType); - READ_STRING_AS(dosFileName, 13); - READ_U16L_AS(sampleDataOffset); - READ_U32L_AS(sampleLength); - READ_U32L_AS(repeatStart); - READ_U32L_AS(repeatStop); - READ_BYTE_AS(sampleVolume); - - seek(1, Current); - - READ_BYTE_AS(packing); - READ_BYTE_AS(sampleFlags); - READ_U32L_AS(baseFrequency); - - seek(12, Current); - - READ_STRING_AS(sampleName, 28); - // The next 4 bytes should be "SCRS", but I've found - // files that are otherwise ok with 4 nils instead. - // READ_ASSERT(readBlock(4) == "SCRS"); - - comment.append(sampleName); - } - - d->tag.setComment(comment.toString("\n")); - d->tag.setTrackerName("ScreamTracker III"); - -} diff --git a/3rdparty/taglib/s3m/s3mfile.h b/3rdparty/taglib/s3m/s3mfile.h deleted file mode 100644 index 898d3532..00000000 --- a/3rdparty/taglib/s3m/s3mfile.h +++ /dev/null @@ -1,95 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_S3MFILE_H -#define TAGLIB_S3MFILE_H - -#include "tfile.h" -#include "audioproperties.h" -#include "taglib_export.h" -#include "modfilebase.h" -#include "modtag.h" -#include "s3mproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace S3M { - -class TAGLIB_EXPORT File : public Mod::FileBase { - public: - /*! - * Constructs a ScreamTracker III from \a file. - * - * \note In the current implementation, both \a readProperties and - * \a propertiesStyle are ignored. The audio properties are always read. - */ - explicit File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Constructs a ScreamTracker III file from \a stream. - * - * \note In the current implementation, both \a readProperties and \a propertiesStyle are ignored. - * The audio properties are always read. - * - * \note TagLib will *not* take ownership of the stream, the caller is responsible for deleting it after the File object. - */ - explicit File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Destroys this instance of the File. - */ - ~File() override; - - Mod::Tag *tag() const override; - - /*! - * Returns the S3M::AudioProperties for this file. If no audio properties - * were read then this will return a null pointer. - */ - S3M::AudioProperties *audioProperties() const override; - - /*! - * Save the file. - * This is the same as calling save(AllTags); - * - * \note Saving ScreamTracker III tags is not supported. - */ - bool save() override; - - private: - File(const File &); - File &operator=(const File &); - - void read(bool readProperties); - - class FilePrivate; - FilePrivate *d; -}; - -} // namespace S3M -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/s3m/s3mproperties.cpp b/3rdparty/taglib/s3m/s3mproperties.cpp deleted file mode 100644 index 6bbfee83..00000000 --- a/3rdparty/taglib/s3m/s3mproperties.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - - -#include "s3mproperties.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace S3M; - -class S3M::AudioProperties::AudioPropertiesPrivate { - public: - explicit AudioPropertiesPrivate() : lengthInPatterns(0), - channels(0), - stereo(false), - sampleCount(0), - patternCount(0), - flags(0), - trackerVersion(0), - fileFormatVersion(0), - globalVolume(0), - masterVolume(0), - tempo(0), - bpmSpeed(0) { - } - - unsigned short lengthInPatterns; - int channels; - bool stereo; - unsigned short sampleCount; - unsigned short patternCount; - unsigned short flags; - unsigned short trackerVersion; - unsigned short fileFormatVersion; - unsigned char globalVolume; - unsigned char masterVolume; - unsigned char tempo; - unsigned char bpmSpeed; -}; - -S3M::AudioProperties::AudioProperties(AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::AudioProperties(), d(new AudioPropertiesPrivate()) {} - -S3M::AudioProperties::~AudioProperties() { - delete d; -} - -int S3M::AudioProperties::lengthInSeconds() const { - return 0; -} - -int S3M::AudioProperties::lengthInMilliseconds() const { - return 0; -} - -int S3M::AudioProperties::bitrate() const { - return 0; -} - -int S3M::AudioProperties::sampleRate() const { - return 0; -} - -int S3M::AudioProperties::channels() const { - return d->channels; -} - -unsigned short S3M::AudioProperties::lengthInPatterns() const { - return d->lengthInPatterns; -} - -bool S3M::AudioProperties::stereo() const { - return d->stereo; -} - -unsigned short S3M::AudioProperties::sampleCount() const { - return d->sampleCount; -} - -unsigned short S3M::AudioProperties::patternCount() const { - return d->patternCount; -} - -unsigned short S3M::AudioProperties::flags() const { - return d->flags; -} - -unsigned short S3M::AudioProperties::trackerVersion() const { - return d->trackerVersion; -} - -unsigned short S3M::AudioProperties::fileFormatVersion() const { - return d->fileFormatVersion; -} - -unsigned char S3M::AudioProperties::globalVolume() const { - return d->globalVolume; -} - -unsigned char S3M::AudioProperties::masterVolume() const { - return d->masterVolume; -} - -unsigned char S3M::AudioProperties::tempo() const { - return d->tempo; -} - -unsigned char S3M::AudioProperties::bpmSpeed() const { - return d->bpmSpeed; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void S3M::AudioProperties::setLengthInPatterns(unsigned short lengthInPatterns) { - d->lengthInPatterns = lengthInPatterns; -} - -void S3M::AudioProperties::setChannels(int channels) { - d->channels = channels; -} - -void S3M::AudioProperties::setStereo(bool stereo) { - d->stereo = stereo; -} - -void S3M::AudioProperties::setSampleCount(unsigned short sampleCount) { - d->sampleCount = sampleCount; -} - -void S3M::AudioProperties::setPatternCount(unsigned short patternCount) { - d->patternCount = patternCount; -} - -void S3M::AudioProperties::setFlags(unsigned short flags) { - d->flags = flags; -} - -void S3M::AudioProperties::setTrackerVersion(unsigned short trackerVersion) { - d->trackerVersion = trackerVersion; -} - -void S3M::AudioProperties::setFileFormatVersion(unsigned short fileFormatVersion) { - d->fileFormatVersion = fileFormatVersion; -} - -void S3M::AudioProperties::setGlobalVolume(unsigned char globalVolume) { - d->globalVolume = globalVolume; -} - -void S3M::AudioProperties::setMasterVolume(unsigned char masterVolume) { - d->masterVolume = masterVolume; -} - -void S3M::AudioProperties::setTempo(unsigned char tempo) { - d->tempo = tempo; -} - -void S3M::AudioProperties::setBpmSpeed(unsigned char bpmSpeed) { - d->bpmSpeed = bpmSpeed; -} diff --git a/3rdparty/taglib/s3m/s3mproperties.h b/3rdparty/taglib/s3m/s3mproperties.h deleted file mode 100644 index fc0ecd5d..00000000 --- a/3rdparty/taglib/s3m/s3mproperties.h +++ /dev/null @@ -1,96 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_S3MPROPERTIES_H -#define TAGLIB_S3MPROPERTIES_H - -#include "taglib.h" -#include "audioproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace S3M { - -class TAGLIB_EXPORT AudioProperties : public Strawberry_TagLib::TagLib::AudioProperties { - friend class File; - - public: - /*! Flag bits. */ - enum { - ST2Vibrato = 1, - ST2Tempo = 2, - AmigaSlides = 4, - Vol0MixOptimizations = 8, - AmigaLimits = 16, - EnableFilter = 32, - CustomData = 128 - }; - - explicit AudioProperties(AudioProperties::ReadStyle); - ~AudioProperties() override; - - int lengthInSeconds() const override; - int lengthInMilliseconds() const override; - int bitrate() const override; - int sampleRate() const override; - int channels() const override; - - unsigned short lengthInPatterns() const; - bool stereo() const; - unsigned short sampleCount() const; - unsigned short patternCount() const; - unsigned short flags() const; - unsigned short trackerVersion() const; - unsigned short fileFormatVersion() const; - unsigned char globalVolume() const; - unsigned char masterVolume() const; - unsigned char tempo() const; - unsigned char bpmSpeed() const; - - private: - void setChannels(int channels); - - void setLengthInPatterns(unsigned short lengthInPatterns); - void setStereo(bool stereo); - void setSampleCount(unsigned short sampleCount); - void setPatternCount(unsigned short patternCount); - void setFlags(unsigned short flags); - void setTrackerVersion(unsigned short trackerVersion); - void setFileFormatVersion(unsigned short fileFormatVersion); - void setGlobalVolume(unsigned char globalVolume); - void setMasterVolume(unsigned char masterVolume); - void setTempo(unsigned char tempo); - void setBpmSpeed(unsigned char bpmSpeed); - - private: - class AudioPropertiesPrivate; - AudioPropertiesPrivate *d; -}; - -} // namespace S3M -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/tag.cpp b/3rdparty/taglib/tag.cpp deleted file mode 100644 index 70f92425..00000000 --- a/3rdparty/taglib/tag.cpp +++ /dev/null @@ -1,195 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tag.h" -#include "tstringlist.h" -#include "tpicturemap.h" -#include "tpropertymap.h" - -using namespace Strawberry_TagLib::TagLib; - -class Tag::TagPrivate {}; - -Tag::Tag() : d(nullptr) {} - -Tag::~Tag() {} - -bool Tag::isEmpty() const { - return (title().isEmpty() && - artist().isEmpty() && - album().isEmpty() && - comment().isEmpty() && - genre().isEmpty() && - year() == 0 && - track() == 0 && - pictures().isEmpty()); -} - -PropertyMap Tag::properties() const { - - PropertyMap map; - if (!(title().isEmpty())) - map["TITLE"].append(title()); - if (!(artist().isEmpty())) - map["ARTIST"].append(artist()); - if (!(album().isEmpty())) - map["ALBUM"].append(album()); - if (!(comment().isEmpty())) - map["COMMENT"].append(comment()); - if (!(genre().isEmpty())) - map["GENRE"].append(genre()); - if (!(year() == 0)) - map["DATE"].append(String::number(year())); - if (!(track() == 0)) - map["TRACKNUMBER"].append(String::number(track())); - return map; - -} - -void Tag::removeUnsupportedProperties(const StringList &) {} - -PropertyMap Tag::setProperties(const PropertyMap &origProps) { - - PropertyMap properties(origProps); - properties.removeEmpty(); - StringList oneValueSet; - // can this be simplified by using some preprocessor defines / function pointers? - if (properties.contains("TITLE")) { - setTitle(properties["TITLE"].front()); - oneValueSet.append("TITLE"); - } - else - setTitle(String()); - - if (properties.contains("ARTIST")) { - setArtist(properties["ARTIST"].front()); - oneValueSet.append("ARTIST"); - } - else - setArtist(String()); - - if (properties.contains("ALBUM")) { - setAlbum(properties["ALBUM"].front()); - oneValueSet.append("ALBUM"); - } - else - setAlbum(String()); - - if (properties.contains("COMMENT")) { - setComment(properties["COMMENT"].front()); - oneValueSet.append("COMMENT"); - } - else - setComment(String()); - - if (properties.contains("GENRE")) { - setGenre(properties["GENRE"].front()); - oneValueSet.append("GENRE"); - } - else - setGenre(String()); - - if (properties.contains("DATE")) { - bool ok; - int date = properties["DATE"].front().toInt(&ok); - if (ok) { - setYear(date); - oneValueSet.append("DATE"); - } - else - setYear(0); - } - else - setYear(0); - - if (properties.contains("TRACKNUMBER")) { - bool ok; - int track = properties["TRACKNUMBER"].front().toInt(&ok); - if (ok) { - setTrack(track); - oneValueSet.append("TRACKNUMBER"); - } - else - setTrack(0); - } - else - setTrack(0); - - // for each tag that has been set above, remove the first entry in the corresponding - // value list. The others will be returned as unsupported by this format. - for (StringList::ConstIterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) { - if (properties[*it].size() == 1) - properties.erase(*it); - else - properties[*it].erase(properties[*it].begin()); - } - return properties; - -} - -void Tag::duplicate(const Tag *source, Tag *target, bool overwrite) { // static - - if (overwrite) { - target->setTitle(source->title()); - target->setArtist(source->artist()); - target->setAlbum(source->album()); - target->setComment(source->comment()); - target->setGenre(source->genre()); - target->setYear(source->year()); - target->setTrack(source->track()); - target->setPictures(source->pictures()); - } - else { - if (target->title().isEmpty()) - target->setTitle(source->title()); - if (target->artist().isEmpty()) - target->setArtist(source->artist()); - if (target->album().isEmpty()) - target->setAlbum(source->album()); - if (target->comment().isEmpty()) - target->setComment(source->comment()); - if (target->genre().isEmpty()) - target->setGenre(source->genre()); - if (target->year() <= 0) - target->setYear(source->year()); - if (target->track() <= 0) - target->setTrack(source->track()); - if (target->pictures().isEmpty()) - target->setPictures(source->pictures()); - } - -} - -String Tag::toString() const { - StringList desc; - desc.append("title=" + title()); - desc.append("artist=" + artist()); - desc.append("album=" + album()); - desc.append("comment=" + comment()); - desc.append("genre=" + genre()); - desc.append("year=" + String::number(year())); - desc.append("track=" + String::number(track())); - return desc.toString("\n"); -} diff --git a/3rdparty/taglib/tag.h b/3rdparty/taglib/tag.h deleted file mode 100644 index e0a1b523..00000000 --- a/3rdparty/taglib/tag.h +++ /dev/null @@ -1,196 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_TAG_H -#define TAGLIB_TAG_H - -#include "taglib_export.h" -#include "tstring.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -//! A simple, generic interface to common audio meta data fields - -/*! - * This is an attempt to abstract away the difference in the meta data formats of various audio codecs and tagging schemes. - * As such it is generally a subset of what is available in the specific formats but should be suitable for most applications. - * This is meant to compliment the generic APIs found in TagLib::AudioProperties, TagLib::File and TagLib::FileRef. - */ - -class PropertyMap; -class PictureMap; - -class TAGLIB_EXPORT Tag { - public: - /*! - * Destroys this Tag instance. - */ - virtual ~Tag(); - - /*! - * Exports the tags of the file as dictionary mapping (human readable) tag names (Strings) to StringLists of tag values. - * The default implementation in this class considers only the usual built-in tags (artist, album, ...) and only one value per key. - */ - virtual PropertyMap properties() const; - - /*! - * Removes unsupported properties, or a subset of them, from the tag. - * The parameter \a properties must contain only entries from properties().unsupportedData(). - */ - virtual void removeUnsupportedProperties(const StringList &properties); - - /*! - * Sets the tags of this File to those specified in \a properties. - * This default implementation sets only the tags for which setter methods exist in this class - * (artist, album, ...), and only one value per key; the rest will be contained in the returned PropertyMap. - */ - virtual PropertyMap setProperties(const PropertyMap &origProps); - - /*! - * Returns the track name; if no track name is present in the tag String::null will be returned. - */ - virtual String title() const = 0; - - /*! - * Returns the artist name; if no artist name is present in the tag String::null will be returned. - */ - virtual String artist() const = 0; - - /*! - * Returns the album name; if no album name is present in the tag String::null will be returned. - */ - virtual String album() const = 0; - - /*! - * Returns the track comment; if no comment is present in the tag String::null will be returned. - */ - virtual String comment() const = 0; - - /*! - * Returns the genre name; if no genre is present in the tag String::null will be returned. - */ - virtual String genre() const = 0; - - /*! - * Returns the year; if there is no year set, this will return 0. - */ - virtual unsigned int year() const = 0; - - /*! - * Returns the track number; if there is no track number set, this will - * return 0. - */ - virtual unsigned int track() const = 0; - - /*! - * Returns a list of pictures available; if there is no picture, the list - * will be empty - */ - virtual PictureMap pictures() const = 0; - - /*! - * Sets the title to \a s. If \a s is String::null then this value will be cleared. - */ - virtual void setTitle(const String &s) = 0; - - /*! - * Sets the artist to \a s. If \a s is String::null then this value will be cleared. - */ - virtual void setArtist(const String &s) = 0; - - /*! - * Sets the album to \a s. If \a s is String::null then this value will be cleared. - */ - virtual void setAlbum(const String &s) = 0; - - /*! - * Sets the comment to \a s. If \a s is String::null then this value will be cleared. - */ - virtual void setComment(const String &s) = 0; - - /*! - * Sets the genre to \a s. If \a s is String::null then this value will be cleared. - * For tag formats that use a fixed set of genres, the appropriate value will be selected based on a string comparison. - * A list of available genres for those formats should be available in that type's implementation. - */ - virtual void setGenre(const String &s) = 0; - - /*! - * Sets the year to \a i. If \a s is 0 then this value will be cleared. - */ - virtual void setYear(unsigned int i) = 0; - - /*! - * Sets the track to \a i. If \a s is 0 then this value will be cleared. - */ - virtual void setTrack(unsigned int i) = 0; - - /*! - * Sets the list of pictures - */ - virtual void setPictures(const PictureMap &l) = 0; - - /*! - * Returns true if the tag does not contain any data. - * This should be reimplemented in subclasses that provide more than the basic tagging abilities in this class. - */ - virtual bool isEmpty() const; - - /*! - * Copies the generic data from one tag to another. - * - * \note This will no affect any of the lower level details of the tag. - * For instance if any of the tag type specific data (maybe a URL for a band) is set, - * this will not modify or copy that. - * This just copies using the API in this class. - * - * If \a overwrite is true then the values will be unconditionally copied. - * If false only empty values will be overwritten. - */ - static void duplicate(const Tag *source, Tag *target, bool overwrite = true); - - /*! - * Returns description of the tag. - */ - virtual String toString() const; - - protected: - /*! - * Construct a Tag. This is protected since tags should only be instantiated through subclasses. - */ - explicit Tag(); - - private: - Tag(const Tag&); - Tag &operator=(const Tag&); - - class TagPrivate; - TagPrivate *d; -}; -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/taglib-config.h.cmake b/3rdparty/taglib/taglib-config.h.cmake deleted file mode 100644 index c1a31094..00000000 --- a/3rdparty/taglib/taglib-config.h.cmake +++ /dev/null @@ -1,35 +0,0 @@ -/* config.h. Generated by cmake from config.h.cmake */ - -#ifndef TAGLIB_CONFIG_H -#define TAGLIB_CONFIG_H - -/* Defined if your compiler supports some byte swap functions */ -#cmakedefine HAVE_GCC_BYTESWAP 1 -#cmakedefine HAVE_GLIBC_BYTESWAP 1 -#cmakedefine HAVE_MSC_BYTESWAP 1 -#cmakedefine HAVE_MAC_BYTESWAP 1 -#cmakedefine HAVE_OPENBSD_BYTESWAP 1 - -/* Defined if your compiler supports some atomic operations */ -#cmakedefine HAVE_STD_ATOMIC 1 -#cmakedefine HAVE_GCC_ATOMIC 1 -#cmakedefine HAVE_MAC_ATOMIC 1 -#cmakedefine HAVE_WIN_ATOMIC 1 -#cmakedefine HAVE_IA64_ATOMIC 1 - -/* Defined if your compiler supports some safer version of vsprintf */ -#cmakedefine HAVE_VSNPRINTF 1 -#cmakedefine HAVE_VSPRINTF_S 1 - -/* Defined if your compiler supports ISO _strdup */ -#cmakedefine HAVE_ISO_STRDUP 1 - -/* Defined if zlib is installed */ -#cmakedefine HAVE_ZLIB 1 - -/* Indicates whether debug messages are shown even in release mode */ -#cmakedefine TRACE_IN_RELEASE 1 - -#cmakedefine TESTS_DIR "@TESTS_DIR@" - -#endif diff --git a/3rdparty/taglib/taglib_export.h b/3rdparty/taglib/taglib_export.h deleted file mode 100644 index 90e20f0e..00000000 --- a/3rdparty/taglib/taglib_export.h +++ /dev/null @@ -1,43 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_EXPORT_H -#define TAGLIB_EXPORT_H - -#if defined(TAGLIB_STATIC) -# define TAGLIB_EXPORT -#elif (defined(_WIN32) || defined(_WIN64)) -# ifdef MAKE_TAGLIB_LIB -# define TAGLIB_EXPORT __declspec(dllexport) -# else -# define TAGLIB_EXPORT __declspec(dllimport) -# endif -#elif defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 1) -# define TAGLIB_EXPORT __attribute__((visibility("default"))) -#else -# define TAGLIB_EXPORT -#endif - -#endif diff --git a/3rdparty/taglib/tagunion.cpp b/3rdparty/taglib/tagunion.cpp deleted file mode 100644 index 5b4fa140..00000000 --- a/3rdparty/taglib/tagunion.cpp +++ /dev/null @@ -1,251 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "tagunion.h" -#include "tstringlist.h" -#include "tpropertymap.h" -#include "tpicturemap.h" - -#define stringUnion(method) \ - for (size_t j = 0; j < COUNT; ++j) { \ - String val = d->tags[j] ? d->tags[j]->method() : String(); \ - if (!val.isEmpty()) \ - return val; \ - } \ - return String(); - -#define numberUnion(method) \ - for (size_t j = 0; j < COUNT; ++j) { \ - unsigned int val = d->tags[j] ? d->tags[j]->method() : 0; \ - if (val > 0) \ - return val; \ - } \ - return 0; - -#define pictureMapUnion(method) \ - for (size_t j = 0; j < COUNT; ++j) { \ - PictureMap val = d->tags[j] ? d->tags[j]->method() : PictureMap(); \ - if (!val.isEmpty()) \ - return val; \ - } \ - return PictureMap(); - -#define setUnion(method, value) \ - for (size_t j = 0; j < COUNT; ++j) { \ - if (d->tags[j]) \ - d->tags[j]->set##method(value); \ - } - -namespace Strawberry_TagLib { -namespace TagLib { -template -class TagUnion::TagUnionPrivate { - public: - std::unique_ptr tags[COUNT]; -}; - -template -TagUnion::TagUnion() : d(new TagUnionPrivate()) {} - -template -TagUnion::~TagUnion() { - delete d; -} - -template -Tag *TagUnion::operator[](size_t index) const { - return tag(index); -} - -template -Tag *TagUnion::tag(size_t index) const { - return d->tags[index].get(); -} - -template -void TagUnion::set(size_t index, Tag *tag) { - d->tags[index].reset(tag); -} - -template -PropertyMap TagUnion::properties() const { - - for (size_t i = 0; i < COUNT; ++i) { - if (d->tags[i] && !d->tags[i]->isEmpty()) - return d->tags[i]->properties(); - } - - return PropertyMap(); - -} - -template -void TagUnion::removeUnsupportedProperties(const StringList &unsupported) { - - for (size_t i = 0; i < COUNT; ++i) { - if (d->tags[i]) - d->tags[i]->removeUnsupportedProperties(unsupported); - } - -} - -template -PropertyMap TagUnion::setProperties(const PropertyMap &properties) { - - // Record unassigned properties for each tag in the union - std::vector returnCandidates; - for (size_t i = 0; i < COUNT; ++i) { - if (d->tags[i]) - returnCandidates.insert(returnCandidates.end(), d->tags[i]->setProperties(properties)); - } - - if (!returnCandidates.empty()) { - // Only one tag present, return its unassigned properties - if (returnCandidates.size() == 1) { - return returnCandidates.front(); - } - - // Multiple tags in union: - // if a property has been assigned in any member tag - // remove it from ignored properties to return - PropertyMap propertiesCopy(properties); - for (std::vector::iterator i = returnCandidates.begin(); i != returnCandidates.end(); i++) { - for (PropertyMap::Iterator j = propertiesCopy.begin(); j != propertiesCopy.end();) { - if (!i->contains(j->first)) { - j = propertiesCopy.erase(j->first).begin(); - } - else { - j++; - } - } - } - } - - // No assignments made by union member tags. - // Return input (this should not happen) - return properties; -} - -template -String TagUnion::title() const { - stringUnion(title); -} - -template -String TagUnion::artist() const { - stringUnion(artist); -} - -template -String TagUnion::album() const { - stringUnion(album); -} - -template -String TagUnion::comment() const { - stringUnion(comment); -} - -template -String TagUnion::genre() const { - stringUnion(genre); -} - -template -unsigned int TagUnion::year() const { - numberUnion(year); -} - -template -unsigned int TagUnion::track() const { - numberUnion(track); -} - -template -void TagUnion::setTitle(const String &s) { - setUnion(Title, s); -} - -template -void TagUnion::setArtist(const String &s) { - setUnion(Artist, s); -} - -template -Strawberry_TagLib::TagLib::PictureMap TagUnion::pictures() const { - pictureMapUnion(pictures); -} - -template -void TagUnion::setAlbum(const String &s) { - setUnion(Album, s); -} - -template -void TagUnion::setComment(const String &s) { - setUnion(Comment, s); -} - -template -void TagUnion::setGenre(const String &s) { - setUnion(Genre, s); -} - -template -void TagUnion::setYear(unsigned int i) { - setUnion(Year, i); -} - -template -void TagUnion::setTrack(unsigned int i) { - setUnion(Track, i); -} - -template -void TagUnion::setPictures(const PictureMap &l) { - setUnion(Pictures, l); -} - -template -bool TagUnion::isEmpty() const { - - for (size_t i = 0; i < COUNT; ++i) { - if (d->tags[i] && !d->tags[i]->isEmpty()) - return false; - } - - return true; - -} - -// All the versions of TagUnion should be explicitly instantiated here. - -template class TagUnion<2>; -template class TagUnion<3>; - -} // namespace TagLib -} // namepsace Strawberry_TagLib diff --git a/3rdparty/taglib/tagunion.h b/3rdparty/taglib/tagunion.h deleted file mode 100644 index 9a44f462..00000000 --- a/3rdparty/taglib/tagunion.h +++ /dev/null @@ -1,109 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_TAGUNION_H -#define TAGLIB_TAGUNION_H - -// This file is not a part of TagLib public interface. This is not installed. - -#include "tag.h" - -#ifndef DO_NOT_DOCUMENT - -namespace Strawberry_TagLib { -namespace TagLib { - -/*! - * \internal - */ - -template -class TagUnion : public Tag { - public: - enum AccessType { - Read, - Write - }; - - /*! - * Creates a TagLib::Tag that is the union of \a count tags. - */ - TagUnion(); - - ~TagUnion() override; - - Tag *operator[](size_t index) const; - Tag *tag(size_t index) const; - - void set(size_t index, Tag *tag); - - PropertyMap properties() const override; - void removeUnsupportedProperties(const StringList &unsupported) override; - PropertyMap setProperties(const PropertyMap &properties) override; - - String title() const override; - String artist() const override; - String album() const override; - String comment() const override; - String genre() const override; - unsigned int year() const override; - unsigned int track() const override; - PictureMap pictures() const override; - - void setTitle(const String &s) override; - void setArtist(const String &s) override; - void setAlbum(const String &s) override; - void setComment(const String &s) override; - void setGenre(const String &s) override; - void setYear(unsigned int i) override; - void setTrack(unsigned int i) override; - void setPictures(const PictureMap &l) override; - - bool isEmpty() const override; - - template T *access(size_t index, bool create) { - - if (!create || tag(index)) - return static_cast(tag(index)); - - set(index, new T); - return static_cast(tag(index)); - - } - - private: - class TagUnionPrivate; - TagUnionPrivate *d; -}; - -// If you add a new typedef here, add a corresponding explicit instantiation at the end of tagunion.cpp as well. - -typedef TagUnion<2> DoubleTagUnion; -typedef TagUnion<3> TripleTagUnion; -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif -#endif diff --git a/3rdparty/taglib/tagutils.cpp b/3rdparty/taglib/tagutils.cpp deleted file mode 100644 index 220ac3af..00000000 --- a/3rdparty/taglib/tagutils.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/*************************************************************************** - copyright : (C) 2015 by Tsuda Kageyu - email : tsuda.kageyu@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "id3v1tag.h" -#include "id3v2header.h" -#include "apetag.h" - -#include "tagutils.h" - -using namespace Strawberry_TagLib::TagLib; - -long long Utils::findID3v1(File *file) { - - if (!file->isValid()) - return -1; - - file->seek(-128, File::End); - const long long p = file->tell(); - - if (file->readBlock(3) == ID3v1::Tag::fileIdentifier()) - return p; - - return -1; - -} - -long long Utils::findID3v2(File *file) { - - if (!file->isValid()) - return -1; - - file->seek(0); - - if (file->readBlock(3) == ID3v2::Header::fileIdentifier()) - return 0; - - return -1; - -} - -long long Utils::findAPE(File *file, long long id3v1Location) { - - if (!file->isValid()) - return -1; - - if (id3v1Location >= 0) - file->seek(id3v1Location - 32, File::Beginning); - else - file->seek(-32, File::End); - - const long long p = file->tell(); - - if (file->readBlock(8) == APE::Tag::fileIdentifier()) - return p; - - return -1; - -} - -ByteVector Strawberry_TagLib::TagLib::Utils::readHeader(IOStream *stream, size_t length, bool skipID3v2, long long *headerOffset) { - - if (!stream || !stream->isOpen()) - return ByteVector(); - - const long long originalPosition = stream->tell(); - long long bufferOffset = 0; - - if (skipID3v2) { - stream->seek(0); - const ByteVector data = stream->readBlock(ID3v2::Header::size()); - if (data.startsWith(ID3v2::Header::fileIdentifier())) - bufferOffset = ID3v2::Header(data).completeTagSize(); - } - - stream->seek(bufferOffset); - const ByteVector header = stream->readBlock(length); - stream->seek(originalPosition); - - if (headerOffset) - *headerOffset = bufferOffset; - - return header; - -} diff --git a/3rdparty/taglib/tagutils.h b/3rdparty/taglib/tagutils.h deleted file mode 100644 index 5d00cc3b..00000000 --- a/3rdparty/taglib/tagutils.h +++ /dev/null @@ -1,54 +0,0 @@ -/*************************************************************************** - copyright : (C) 2015 by Tsuda Kageyu - email : tsuda.kageyu@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_TAGUTILS_H -#define TAGLIB_TAGUTILS_H - -// THIS FILE IS NOT A PART OF THE TAGLIB API - -#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header - -#include "tbytevector.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -class File; -class IOStream; - -namespace Utils { - -long long findID3v1(File *file); -long long findID3v2(File *file); -long long findAPE(File *file, long long id3v1Location); - -ByteVector readHeader(IOStream *stream, size_t length, bool skipID3v2, long long *headerOffset = nullptr); -} // namespace Utils -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif - -#endif diff --git a/3rdparty/taglib/toolkit/taglib.cpp b/3rdparty/taglib/toolkit/taglib.cpp deleted file mode 100644 index 22ea58f3..00000000 --- a/3rdparty/taglib/toolkit/taglib.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/*************************************************************************** - copyright : (C) 2016 by Michael Helmling - email : helmling@mathematik.uni-kl.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 "taglib.h" -#include "tstring.h" - -using namespace Strawberry_TagLib::TagLib; - -String Strawberry_TagLib::TagLib::Version::string() { - return String::number(TAGLIB_MAJOR_VERSION) + "." + String::number(TAGLIB_MINOR_VERSION) + "." + String::number(TAGLIB_PATCH_VERSION); -} - -unsigned int Strawberry_TagLib::TagLib::Version::combined() { - return (TAGLIB_MAJOR_VERSION << 16) | (TAGLIB_MINOR_VERSION << 8) | (TAGLIB_PATCH_VERSION << 4); -} - -unsigned int(Strawberry_TagLib::TagLib::Version::major)() { - return TAGLIB_MAJOR_VERSION; -} - -unsigned int(Strawberry_TagLib::TagLib::Version::minor)() { - return TAGLIB_MINOR_VERSION; -} - -unsigned int Strawberry_TagLib::TagLib::Version::patch() { - return TAGLIB_PATCH_VERSION; -} diff --git a/3rdparty/taglib/toolkit/taglib.h b/3rdparty/taglib/toolkit/taglib.h deleted file mode 100644 index 1cf6071f..00000000 --- a/3rdparty/taglib/toolkit/taglib.h +++ /dev/null @@ -1,182 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_H -#define TAGLIB_H - -#define TAGLIB_MAJOR_VERSION 1 -#define TAGLIB_MINOR_VERSION 11 -#define TAGLIB_PATCH_VERSION 1 - -#if (defined(_MSC_VER) && _MSC_VER >= 1600) -# define TAGLIB_CONSTRUCT_BITSET(x) static_cast(x) -#else -# define TAGLIB_CONSTRUCT_BITSET(x) static_cast(x) -#endif - -//! A namespace for all TagLib related classes and functions - -/*! - * This namespace contains everything in TagLib. For projects working with - * TagLib extensively it may be convenient to add a - * \code - * using namespace TagLib; - * \endcode - */ - -namespace Strawberry_TagLib { -namespace TagLib { - -enum ByteOrder { - LittleEndian, - BigEndian -}; - -class String; - -namespace Version { -/*! - * Returns the version as a string in the form - * (Major Version).(Minor Version).(Patch Version), e.g. "4.2.0". - */ -String string(); - -/*! - * Returns the version as an unsigned integer in the form - * (Major Version << 16) | (Minor Version << 8) | (Patch Version), e.g. 0x040200 - * Use this for simple and consistent version comparison, e.g. - * if (TagLib::GetVersion() <= ((1 << 16) | (11 << 8))) return false; - */ -unsigned int combined(); - -/*! - * Returns the major version, e.g. 4 - */ -unsigned int(major)(); - -/*! - * Returns the minor version, e.g. 2 - */ -unsigned int(minor)(); - -/*! - * Returns the patch version, e.g. 0 - */ -unsigned int patch(); -} // namespace Version -} // namespace TagLib -} // namespace Strawberry_TagLib - -/*! - * \mainpage TagLib - * - * \section intro Introduction - * - * TagLib is a library for reading and editing audio meta data, commonly know as \e tags. - * - * Features: - * - A clean, high level, C++ API to handling audio meta data. - * - Format specific APIs for advanced API users. - * - ID3v1, ID3v2, APE, FLAC, Xiph, iTunes-style MP4 and WMA tag formats. - * - MP3, MPC, FLAC, MP4, ASF, AIFF, WAV, TrueAudio, WavPack, Ogg FLAC, Ogg Vorbis, Speex and Opus file formats. - * - Basic audio file properties such as length, sample rate, etc. - * - Long term binary and source compatibility. - * - Extensible design, notably the ability to add other formats or extend current formats as a library user. - * - Full support for unicode and internationalized tags. - * - Dual MPL and - * LGPL licenses. - * - No external toolkit dependencies. - * - * \section why Why TagLib? - * - * TagLib originally was written to provide an updated and improved ID3v2 implementation in C++ for use - * in a variety of Open Source projects. Since development began in 2002 and the 1.0 release in 2004 - * it has expanded to cover a wide variety of tag and file formats and is used in a wide variety of - * Open Source and proprietary applications. It now supports a variety of UNIXes, including Apple's OS - * X, as well as Microsoft Windows. - * - * \section commercial Usage in Commercial Applications - * - * TagLib's licenses \e do allow usage within propriety (\e closed) applications, however TagLib is \e not - * public domain. Please note the requirements of the LGPL or MPL, and adhere to at least one of them. - * In simple terms, you must at a minimum note your usage of TagLib, note the licensing terms of TagLib and - * if you make changes to TagLib publish them. Please review the licenses above before using TagLib in your - * software. Note that you may choose either the MPL or the LGPL, you do not have to fulfill the - * requirements of both. - * - * \section installing Installing TagLib - * - * Please see the TagLib website for the latest - * downloads. - * - * TagLib can be built using the CMake build system. TagLib installs a taglib-config and pkg-config file to - * make it easier to integrate into various build systems. Note that TagLib's include install directory \e must - * be included in the header include path. Simply adding will \e not work. - * - * \section start Getting Started - * - * TagLib provides both simple, abstract APIs which make it possible to ignore the differences between tagging - * formats and format specific APIs which allow programmers to work with the features of specific tagging - * schemes. There is a similar abstraction mechanism for AudioProperties. - * - * The best place to start is with the Class Hierarchy linked at the top of the page. The File and - * AudioProperties classes and their subclasses are the core of TagLib. The FileRef class is also a convenient - * way for using a value-based handle. - * - * \note When working with FileRef please consider that it has only the most basic (extension-based) file - * type resolution. Please see its documentation on how to plug in more advanced file type resolution. (Such - * resolution may be part of later TagLib releases by default.) - * - * Here's a very simple example with TagLib: - * - * \code - * - * Strawberry_TagLib::TagLib::FileRef f("Latex Solar Beef.mp3"); - * Strawberry_TagLib::TagLib::String artist = f.tag()->artist(); // artist == "Frank Zappa" - * - * f.tag()->setAlbum("Fillmore East"); - * f.save(); - * - * Strawberry_TagLib::TagLib::FileRef g("Free City Rhymes.ogg"); - * Strawberry_TagLib::TagLib::String album = g.tag()->album(); // album == "NYC Ghosts & Flowers" - * - * g.tag()->setTrack(1); - * g.save(); - * - * \endcode - * - * More examples can be found in the \e examples directory of the source distribution. - * - * \section Contact - * - * Questions about TagLib should be directed to the TagLib mailing list, not directly to the author. - * - * - TagLib Homepage - * - TagLib Mailing List (taglib-devel@kde.org) - * - * \author TagLib authors. - */ - -#endif diff --git a/3rdparty/taglib/toolkit/tbytevector.cpp b/3rdparty/taglib/toolkit/tbytevector.cpp deleted file mode 100644 index e9868f76..00000000 --- a/3rdparty/taglib/toolkit/tbytevector.cpp +++ /dev/null @@ -1,898 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include - -#include "tstring.h" -#include "tdebug.h" -#include "tutils.h" - -#include "tbytevector.h" - -// This is a bit ugly to keep writing over and over again. - -// A rather obscure feature of the C++ spec that I hadn't thought of that makes working with C libs much more efficient. -// There's more here: -// http://www.informit.com/isapi/product_id~{9C84DAB4-FE6E-49C5-BB0A-FB50331233EA}/content/index.asp - -namespace Strawberry_TagLib { -namespace TagLib { - -template -size_t findChar(const TIterator dataBegin, const TIterator dataEnd, char c, size_t offset, size_t byteAlign) { - - const size_t dataSize = dataEnd - dataBegin; - if (offset + 1 > dataSize) - return ByteVector::npos(); - - // n % 0 is invalid - - if (byteAlign == 0) - return ByteVector::npos(); - - for (TIterator it = dataBegin + offset; it < dataEnd; it += byteAlign) { - if (*it == c) - return (it - dataBegin); - } - - return ByteVector::npos(); - -} - -template -size_t findVector(const TIterator dataBegin, const TIterator dataEnd, const TIterator patternBegin, const TIterator patternEnd, size_t offset, size_t byteAlign) { - - const size_t dataSize = dataEnd - dataBegin; - const size_t patternSize = patternEnd - patternBegin; - if (patternSize == 0 || offset + patternSize > dataSize) - return ByteVector::npos(); - - // Special case that pattern contains just single char. - - if (patternSize == 1) - return findChar(dataBegin, dataEnd, *patternBegin, offset, byteAlign); - - // n % 0 is invalid - - if (byteAlign == 0) - return ByteVector::npos(); - - // We don't use sophisticated algorithms like Knuth-Morris-Pratt here. - - // In the current implementation of TagLib, data and patterns are too small - // for such algorithms to work effectively. - - for (TIterator it = dataBegin + offset; it < dataEnd - patternSize + 1; it += byteAlign) { - - TIterator itData = it; - TIterator itPattern = patternBegin; - - while (*itData == *itPattern) { - ++itData; - ++itPattern; - - if (itPattern == patternEnd) - return (it - dataBegin); - } - } - - return ByteVector::npos(); - -} - -template -inline T toNumber(const ByteVector &v, size_t offset) { - if (LENGTH == sizeof(T) && offset + LENGTH <= v.size()) { - // Uses memcpy instead of reinterpret_cast to avoid an alignment exception. - T tmp; - ::memcpy(&tmp, v.data() + offset, sizeof(T)); - - if (ENDIAN != Utils::systemByteOrder()) - return Utils::byteSwap(tmp); - else - return tmp; - } - else if (offset < v.size()) { - const size_t length = std::min(LENGTH, v.size() - offset); - T sum = 0; - for (size_t i = 0; i < length; ++i) { - const size_t shift = (ENDIAN == BigEndian ? length - 1 - i : i) * 8; - sum |= static_cast(static_cast(v[offset + i])) << shift; - } - - return sum; - } - else { - debug("toNumber() - offset is out of range. Returning 0."); - return 0; - } -} - -template -inline ByteVector fromNumber(T value) { - - if (ENDIAN != Utils::systemByteOrder()) - value = Utils::byteSwap(value); - - return ByteVector(reinterpret_cast(&value), sizeof(T)); - -} - -template -TFloat toFloat(const ByteVector &v, size_t offset) { - - if (offset > v.size() - sizeof(TInt)) { - debug("toFloat() - offset is out of range. Returning 0."); - return 0.0; - } - - union { - TInt i; - TFloat f; - } tmp; - ::memcpy(&tmp, v.data() + offset, sizeof(TInt)); - - if (ENDIAN != Utils::systemByteOrder()) - tmp.i = Utils::byteSwap(tmp.i); - - return tmp.f; - -} - -template -ByteVector fromFloat(TFloat value) { - - union { - TInt i; - TFloat f; - } tmp; - tmp.f = value; - - if (ENDIAN != Utils::systemByteOrder()) - tmp.i = Utils::byteSwap(tmp.i); - - return ByteVector(reinterpret_cast(&tmp), sizeof(TInt)); - -} - -template -long double toFloat80(const ByteVector &v, size_t offset) { - - using std::swap; - - if (offset > v.size() - 10) { - debug("toFloat80() - offset is out of range. Returning 0."); - return 0.0; - } - - unsigned char bytes[10]; - ::memcpy(bytes, v.data() + offset, 10); - - if (ENDIAN == LittleEndian) { - swap(bytes[0], bytes[9]); - swap(bytes[1], bytes[8]); - swap(bytes[2], bytes[7]); - swap(bytes[3], bytes[6]); - swap(bytes[4], bytes[5]); - } - - // 1-bit sign - const bool negative = ((bytes[0] & 0x80) != 0); - - // 15-bit exponent - const int exponent = ((bytes[0] & 0x7F) << 8) | bytes[1]; - - // 64-bit fraction. Leading 1 is explicit. - const unsigned long long fraction = (static_cast(bytes[2]) << 56) | (static_cast(bytes[3]) << 48) | (static_cast(bytes[4]) << 40) | (static_cast(bytes[5]) << 32) | (static_cast(bytes[6]) << 24) | (static_cast(bytes[7]) << 16) | (static_cast(bytes[8]) << 8) | (static_cast(bytes[9])); - - long double val; - if (exponent == 0 && fraction == 0) - val = 0; - else { - if (exponent == 0x7FFF) { - debug("toFloat80() - can't handle the infinity or NaN. Returning 0."); - return 0.0; - } - else - val = ::ldexp(static_cast(fraction), exponent - 16383 - 63); - } - - if (negative) - return -val; - else - return val; - -} - -class ByteVector::ByteVectorPrivate { - public: - ByteVectorPrivate() : data(new std::vector()), - offset(0), - length(0) {} - - ByteVectorPrivate(ByteVectorPrivate *d, size_t o, size_t l) : data(d->data), - offset(d->offset + o), - length(l) {} - - ByteVectorPrivate(size_t l, char c) : data(new std::vector(l, c)), - offset(0), - length(l) {} - - ByteVectorPrivate(const char *s, size_t l) : data(new std::vector(s, s + l)), - offset(0), - length(l) {} - - std::shared_ptr> data; - size_t offset; - size_t length; -}; - -//////////////////////////////////////////////////////////////////////////////// -// static members -//////////////////////////////////////////////////////////////////////////////// - -size_t ByteVector::npos() { - return static_cast(-1); -} - -ByteVector ByteVector::fromCString(const char *s, size_t length) { - if (length == npos()) - return ByteVector(s); - else - return ByteVector(s, length); -} - -ByteVector ByteVector::fromUInt16LE(size_t value) { - return fromNumber(static_cast(value)); -} - -ByteVector ByteVector::fromUInt16BE(size_t value) { - return fromNumber(static_cast(value)); -} - -ByteVector ByteVector::fromUInt32LE(size_t value) { - return fromNumber(static_cast(value)); -} - -ByteVector ByteVector::fromUInt32BE(size_t value) { - return fromNumber(static_cast(value)); -} - -ByteVector ByteVector::fromUInt64LE(unsigned long long value) { - return fromNumber(value); -} - -ByteVector ByteVector::fromUInt64BE(unsigned long long value) { - return fromNumber(value); -} - -ByteVector ByteVector::fromFloat32LE(float value) { - return fromFloat(value); -} - -ByteVector ByteVector::fromFloat32BE(float value) { - return fromFloat(value); -} - -ByteVector ByteVector::fromFloat64LE(double value) { - return fromFloat(value); -} - -ByteVector ByteVector::fromFloat64BE(double value) { - return fromFloat(value); -} - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -ByteVector::ByteVector() : d(new ByteVectorPrivate()) {} - -ByteVector::ByteVector(size_t size, char value) : d(new ByteVectorPrivate(size, value)) {} - -ByteVector::ByteVector(const ByteVector &v) : d(new ByteVectorPrivate(*v.d)) {} - -ByteVector::ByteVector(const ByteVector &v, size_t offset, size_t length) : d(new ByteVectorPrivate(v.d, offset, length)) {} - -ByteVector::ByteVector(char c) : d(new ByteVectorPrivate(1, c)) {} - -ByteVector::ByteVector(const char *data, size_t length) : d(new ByteVectorPrivate(data, length)) {} - -ByteVector::ByteVector(const char *data) : d(new ByteVectorPrivate(data, ::strlen(data))) {} - -ByteVector::~ByteVector() { - delete d; -} - -ByteVector &ByteVector::setData(const char *data, size_t length) { - ByteVector(data, length).swap(*this); - return *this; -} - -ByteVector &ByteVector::setData(const char *data) { - ByteVector(data).swap(*this); - return *this; -} - -char *ByteVector::data() { - detach(); - return (size() > 0) ? (&(*d->data)[d->offset]) : nullptr; -} - -const char *ByteVector::data() const { - return (size() > 0) ? (&(*d->data)[d->offset]) : nullptr; -} - -ByteVector ByteVector::mid(size_t index, size_t length) const { - - index = std::min(index, size()); - length = std::min(length, size() - index); - - return ByteVector(*this, index, length); - -} - -char ByteVector::at(size_t index) const { - return (index < size()) ? (*d->data)[d->offset + index] : 0; -} - -size_t ByteVector::find(const ByteVector &pattern, size_t offset, size_t byteAlign) const { - return findVector( - begin(), end(), pattern.begin(), pattern.end(), offset, byteAlign); - -} - -size_t ByteVector::find(char c, size_t offset, size_t byteAlign) const { - return findChar(begin(), end(), c, offset, byteAlign); -} - -size_t ByteVector::rfind(const ByteVector &pattern, size_t offset, size_t byteAlign) const { - if (offset > 0) { - offset = size() - offset - pattern.size(); - if (offset >= size()) - offset = 0; - } - - const size_t pos = findVector( - rbegin(), rend(), pattern.rbegin(), pattern.rend(), offset, byteAlign); - - if (pos == npos()) - return npos(); - else - return size() - pos - pattern.size(); -} - -bool ByteVector::containsAt(const ByteVector &pattern, size_t offset, size_t patternOffset, size_t patternLength) const { - - if (pattern.size() < patternLength) - patternLength = pattern.size(); - - // do some sanity checking -- all of these things are needed for the search to be valid - const size_t compareLength = patternLength - patternOffset; - if (offset + compareLength > size() || patternOffset >= pattern.size() || patternLength == 0) - return false; - - return (::memcmp(data() + offset, pattern.data() + patternOffset, compareLength) == 0); - -} - -bool ByteVector::startsWith(const ByteVector &pattern) const { - return containsAt(pattern, 0); -} - -bool ByteVector::endsWith(const ByteVector &pattern) const { - return containsAt(pattern, size() - pattern.size()); -} - -ByteVector &ByteVector::replace(char oldByte, char newByte) { - - detach(); - - for (ByteVector::Iterator it = begin(); it != end(); ++it) { - if (*it == oldByte) - *it = newByte; - } - - return *this; - -} - -ByteVector &ByteVector::replace(const ByteVector &pattern, const ByteVector &with) { - - if (pattern.size() == 1 && with.size() == 1) - return replace(pattern[0], with[0]); - - // Check if there is at least one occurrence of the pattern. - - size_t offset = find(pattern, 0); - if (offset == ByteVector::npos()) - return *this; - - if (pattern.size() == with.size()) { - - // We think this case might be common enough to optimize it. - - detach(); - do { - ::memcpy(data() + offset, with.data(), with.size()); - offset = find(pattern, offset + pattern.size()); - } while (offset != ByteVector::npos()); - } - else { - - // Loop once to calculate the result size. - - size_t dstSize = size(); - do { - dstSize += with.size() - pattern.size(); - offset = find(pattern, offset + pattern.size()); - } while (offset != ByteVector::npos()); - - // Loop again to copy modified data to the new vector. - - ByteVector dst(dstSize); - size_t dstOffset = 0; - - offset = 0; - while (true) { - const size_t next = find(pattern, offset); - if (next == ByteVector::npos()) { - ::memcpy(dst.data() + dstOffset, data() + offset, size() - offset); - break; - } - - ::memcpy(dst.data() + dstOffset, data() + offset, next - offset); - dstOffset += next - offset; - - ::memcpy(dst.data() + dstOffset, with.data(), with.size()); - dstOffset += with.size(); - - offset = next + pattern.size(); - } - - swap(dst); - } - - return *this; - -} - -size_t ByteVector::endsWithPartialMatch(const ByteVector &pattern) const { - - if (pattern.size() > size()) - return npos(); - - const size_t startIndex = size() - pattern.size(); - - // try to match the last n-1 bytes from the vector (where n is the pattern - // size) -- continue trying to match n-2, n-3...1 bytes - - for (size_t i = 1; i < pattern.size(); i++) { - if (containsAt(pattern, startIndex + i, 0, pattern.size() - i)) - return startIndex + i; - } - - return npos(); -} - -ByteVector &ByteVector::append(const ByteVector &v) { - - if (v.isEmpty()) - return *this; - - detach(); - - const size_t originalSize = size(); - const size_t appendSize = v.size(); - - resize(originalSize + appendSize); - ::memcpy(data() + originalSize, v.data(), appendSize); - - return *this; - -} - -ByteVector &ByteVector::append(char c) { - resize(size() + 1, c); - return *this; -} - -ByteVector &ByteVector::clear() { - ByteVector().swap(*this); - return *this; -} - -size_t ByteVector::size() const { - return d->length; -} - -ByteVector &ByteVector::resize(size_t size, char padding) { - - if (size != d->length) { - detach(); - - // Remove the excessive length of the internal buffer first to pad correctly. - // This doesn't reallocate the buffer, since std::vector::resize() doesn't - // reallocate the buffer when shrinking. - - d->data->resize(d->offset + d->length); - d->data->resize(d->offset + size, padding); - - d->length = size; - } - - return *this; - -} - -ByteVector::Iterator ByteVector::begin() { - detach(); - return d->data->begin() + d->offset; -} - -ByteVector::ConstIterator ByteVector::begin() const { - return d->data->begin() + d->offset; -} - -ByteVector::Iterator ByteVector::end() { - detach(); - return d->data->begin() + d->offset + d->length; -} - -ByteVector::ConstIterator ByteVector::end() const { - return d->data->begin() + d->offset + d->length; -} - -ByteVector::ReverseIterator ByteVector::rbegin() { - detach(); - return d->data->rbegin() + (d->data->size() - (d->offset + d->length)); -} - -ByteVector::ConstReverseIterator ByteVector::rbegin() const { - - // Workaround for the Solaris Studio 12.4 compiler. - // We need a const reference to the data vector so we can ensure the const version of rbegin() is called. - const std::vector &v = *d->data; - return v.rbegin() + (v.size() - (d->offset + d->length)); - -} - -ByteVector::ReverseIterator ByteVector::rend() { - detach(); - return d->data->rbegin() + (d->data->size() - d->offset); -} - -ByteVector::ConstReverseIterator ByteVector::rend() const { - - // Workaround for the Solaris Studio 12.4 compiler. - // We need a const reference to the data vector so we can ensure the const version of rbegin() is called. - const std::vector &v = *d->data; - return v.rbegin() + (v.size() - d->offset); - -} - -bool ByteVector::isEmpty() const { - return (d->length == 0); -} - -short ByteVector::toInt16LE(size_t offset) const { - return static_cast(toNumber(*this, offset)); -} - -short ByteVector::toInt16BE(size_t offset) const { - return static_cast(toNumber(*this, offset)); -} - -unsigned short ByteVector::toUInt16LE(size_t offset) const { - return toNumber(*this, offset); -} - -unsigned short ByteVector::toUInt16BE(size_t offset) const { - return toNumber(*this, offset); -} - -unsigned int ByteVector::toUInt24LE(size_t offset) const { - return toNumber(*this, offset); -} - -unsigned int ByteVector::toUInt24BE(size_t offset) const { - return toNumber(*this, offset); -} - -unsigned int ByteVector::toUInt32LE(size_t offset) const { - return toNumber(*this, offset); -} - -unsigned int ByteVector::toUInt32BE(size_t offset) const { - return toNumber(*this, offset); -} - -long long ByteVector::toInt64LE(size_t offset) const { - return static_cast(toNumber(*this, offset)); -} - -long long ByteVector::toInt64BE(size_t offset) const { - return static_cast(toNumber(*this, offset)); -} - -float ByteVector::toFloat32LE(size_t offset) const { - return toFloat(*this, offset); -} - -float ByteVector::toFloat32BE(size_t offset) const { - return toFloat(*this, offset); -} - -double ByteVector::toFloat64LE(size_t offset) const { - return toFloat(*this, offset); -} - -double ByteVector::toFloat64BE(size_t offset) const { - return toFloat(*this, offset); -} - -long double ByteVector::toFloat80LE(size_t offset) const { - return toFloat80(*this, offset); -} - -long double ByteVector::toFloat80BE(size_t offset) const { - return toFloat80(*this, offset); -} - -const char &ByteVector::operator[](size_t index) const { - return (*d->data)[d->offset + index]; -} - -char &ByteVector::operator[](size_t index) { - detach(); - return (*d->data)[d->offset + index]; -} - -bool ByteVector::operator==(const ByteVector &v) const { - if (size() != v.size()) - return false; - - return (::memcmp(data(), v.data(), size()) == 0); -} - -bool ByteVector::operator!=(const ByteVector &v) const { - return !(*this == v); -} - -bool ByteVector::operator==(const char *s) const { - if (size() != ::strlen(s)) - return false; - - return (::memcmp(data(), s, size()) == 0); -} - -bool ByteVector::operator!=(const char *s) const { - return !(*this == s); -} - -bool ByteVector::operator<(const ByteVector &v) const { - const int result = ::memcmp(data(), v.data(), std::min(size(), v.size())); - if (result != 0) - return result < 0; - else - return size() < v.size(); -} - -bool ByteVector::operator>(const ByteVector &v) const { - return (v < *this); -} - -ByteVector ByteVector::operator+(const ByteVector &v) const { - ByteVector sum(*this); - sum.append(v); - return sum; -} - -ByteVector &ByteVector::operator=(const ByteVector &v) { - ByteVector(v).swap(*this); - return *this; -} - -ByteVector &ByteVector::operator=(char c) { - ByteVector(c).swap(*this); - return *this; -} - -ByteVector &ByteVector::operator=(const char *data) { - ByteVector(data).swap(*this); - return *this; -} - -void ByteVector::swap(ByteVector &v) { - using std::swap; - - swap(d, v.d); -} - -ByteVector ByteVector::toHex() const { - static const char hexTable[17] = "0123456789abcdef"; - - ByteVector encoded(size() * 2); - char *p = encoded.data(); - - for (unsigned int i = 0; i < size(); i++) { - unsigned char c = data()[i]; - *p++ = hexTable[(c >> 4) & 0x0F]; - *p++ = hexTable[(c)&0x0F]; - } - - return encoded; -} - -ByteVector ByteVector::fromBase64(const ByteVector &input) { - - static const unsigned char base64[256] = { - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x3e, 0x80, 0x80, 0x80, 0x3f, - 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 - }; - - size_t len = input.size(); - - ByteVector output(len); - - const unsigned char *src = reinterpret_cast(input.data()); - unsigned char *dst = reinterpret_cast(output.data()); - - while (4 <= len) { - - // Check invalid character - if (base64[src[0]] == 0x80) - break; - - // Check invalid character - if (base64[src[1]] == 0x80) - break; - - // Decode first byte - *dst++ = ((base64[src[0]] << 2) & 0xfc) | ((base64[src[1]] >> 4) & 0x03); - - if (src[2] != '=') { - - // Check invalid character - if (base64[src[2]] == 0x80) - break; - - // Decode second byte - *dst++ = ((base64[src[1]] & 0x0f) << 4) | ((base64[src[2]] >> 2) & 0x0f); - - if (src[3] != '=') { - - // Check invalid character - if (base64[src[3]] == 0x80) - break; - - // Decode third byte - *dst++ = ((base64[src[2]] & 0x03) << 6) | (base64[src[3]] & 0x3f); - } - else { - // assume end of data - len -= 4; - break; - } - } - else { - // assume end of data - len -= 4; - break; - } - src += 4; - len -= 4; - } - - // Only return output if we processed all bytes - if (len == 0) { - output.resize(dst - reinterpret_cast(output.data())); - return output; - } - return ByteVector(); -} - -ByteVector ByteVector::toBase64() const { - - static const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - if (!isEmpty()) { - size_t len = size(); - ByteVector output(4 * ((len - 1) / 3 + 1)); // note roundup - - const char *src = data(); - char *dst = output.data(); - while (3 <= len) { - *dst++ = alphabet[(src[0] >> 2) & 0x3f]; - *dst++ = alphabet[((src[0] & 0x03) << 4) | ((src[1] >> 4) & 0x0f)]; - *dst++ = alphabet[((src[1] & 0x0f) << 2) | ((src[2] >> 6) & 0x03)]; - *dst++ = alphabet[src[2] & 0x3f]; - src += 3; - len -= 3; - } - if (len) { - *dst++ = alphabet[(src[0] >> 2) & 0x3f]; - if (len > 1) { - *dst++ = alphabet[((src[0] & 0x03) << 4) | ((src[1] >> 4) & 0x0f)]; - *dst++ = alphabet[((src[1] & 0x0f) << 2)]; - } - else { - *dst++ = alphabet[(src[0] & 0x03) << 4]; - *dst++ = '='; - } - *dst++ = '='; - } - return output; - } - return ByteVector(); - -} - - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void ByteVector::detach() { - if (!d->data.unique()) { - if (!isEmpty()) - ByteVector(&d->data->front() + d->offset, d->length).swap(*this); - else - ByteVector().swap(*this); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// related functions -//////////////////////////////////////////////////////////////////////////////// - -std::ostream &operator<<(std::ostream &s, const ByteVector &v) { - for (size_t i = 0; i < v.size(); i++) - s << v[i]; - return s; -} - -} // namespace TagLib -} // namespace Strawberry_TagLib - diff --git a/3rdparty/taglib/toolkit/tbytevector.h b/3rdparty/taglib/toolkit/tbytevector.h deleted file mode 100644 index f2bde5f6..00000000 --- a/3rdparty/taglib/toolkit/tbytevector.h +++ /dev/null @@ -1,558 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_BYTEVECTOR_H -#define TAGLIB_BYTEVECTOR_H - -#include "taglib.h" -#include "taglib_export.h" - -#include -#include - -namespace Strawberry_TagLib { -namespace TagLib { - -//! A byte vector - -/*! - * This class provides a byte vector with some methods that are useful for tagging purposes. - * Many of the search functions are tailored to what is useful for finding tag related patterns in a data array. - */ - -class TAGLIB_EXPORT ByteVector { - public: -#ifndef DO_NOT_DOCUMENT - typedef std::vector::iterator Iterator; - typedef std::vector::const_iterator ConstIterator; - typedef std::vector::reverse_iterator ReverseIterator; - typedef std::vector::const_reverse_iterator ConstReverseIterator; -#endif - - /*! - * Constructs an empty byte vector. - */ - explicit ByteVector(); - - /*! - * Construct a vector of size \a size with all values set to \a value by default. - */ - explicit ByteVector(size_t size, char value = 0); - - /*! - * Constructs a byte vector that is a copy of \a v. - */ - ByteVector(const ByteVector &v); - - /*! - * Constructs a byte vector that is a copy of \a v. - */ - explicit ByteVector(const ByteVector &v, size_t offset, size_t length); - - /*! - * Constructs a byte vector that contains \a c. - */ - ByteVector(char c); - - /*! - * Constructs a byte vector that copies \a data for up to \a length bytes. - */ - explicit ByteVector(const char *data, size_t length); - - /*! - * Constructs a byte vector that copies \a data up to the first null byte. - * This is particularly useful for constructing byte arrays from string constants. - * - * \warning The behavior is undefined if \a data is not null terminated. - */ - ByteVector(const char *data); - - /*! - * Destroys this ByteVector instance. - */ - virtual ~ByteVector(); - - /*! - * Sets the data for the byte array using the first \a length bytes of \a data - */ - ByteVector &setData(const char *data, size_t length); - - /*! - * Sets the data for the byte array copies \a data up to the first null byte. - * The behavior is undefined if \a data is not null terminated. - */ - ByteVector &setData(const char *data); - - /*! - * Returns a pointer to the internal data structure. - * - * \warning Care should be taken when modifying this data structure as it is easy to corrupt the ByteVector when doing so. - * Specifically, while the data may be changed, its length may not be. - */ - char *data(); - - /*! - * Returns a pointer to the internal data structure which may not be modified. - */ - const char *data() const; - - /*! - * Returns a byte vector made up of the bytes starting at \a index and for \a length bytes. - * If \a length is not specified it will return the bytes from \a index to the end of the vector. - */ - ByteVector mid(size_t index, size_t length = npos()) const; - - /*! - * This essentially performs the same as operator[](), but instead of causing - * a runtime error if the index is out of bounds, it will return a null byte. - */ - char at(size_t index) const; - - /*! - * Searches the ByteVector for \a pattern starting at \a offset and returns - * the offset. Returns -1 if the pattern was not found. - * If \a byteAlign is specified the pattern will only be matched if it starts on a byte divisible - * by \a byteAlign (starting from \a offset). - */ - size_t find(const ByteVector &pattern, size_t offset = 0, size_t byteAlign = 1) const; - - /*! - * Searches the char for \a c starting at \a offset and returns the offset. - * Returns \a -1 if the pattern was not found. - * If \a byteAlign is specified the pattern will only be matched if it starts on a byte divisible - * by \a byteAlign (starting from \a offset). - */ - size_t find(char c, size_t offset = 0, size_t byteAlign = 1) const; - - /*! - * Searches the ByteVector for \a pattern starting from either the end of the vector or \a offset and returns the offset. - * Returns -1 if the pattern was not found. - * If \a byteAlign is specified the pattern will only be matched if it starts on a byte divisible by \a byteAlign (starting from \a offset). - */ - size_t rfind(const ByteVector &pattern, size_t offset = 0, size_t byteAlign = 1) const; - - /*! - * Checks to see if the vector contains the \a pattern starting at position \a offset. - * Optionally, if you only want to search for part of the pattern you can specify an offset within the pattern to start from. - * Also, you can specify to only check for the first \a patternLength bytes of \a pattern with the \a patternLength argument. - */ - bool containsAt(const ByteVector &pattern, size_t offset, size_t patternOffset = 0, size_t patternLength = npos()) const; - - /*! - * Returns true if the vector starts with \a pattern. - */ - bool startsWith(const ByteVector &pattern) const; - - /*! - * Returns true if the vector ends with \a pattern. - */ - bool endsWith(const ByteVector &pattern) const; - - /*! - * Replaces \a oldByte with \a newByte and returns a reference to the ByteVector after the operation. This \e does modify the vector. - */ - ByteVector &replace(char oldByte, char newByte); - - /*! - * Replaces \a pattern with \a with and returns a reference to the ByteVector after the operation. This \e does modify the vector. - */ - ByteVector &replace(const ByteVector &pattern, const ByteVector &with); - - /*! - * Checks for a partial match of \a pattern at the end of the vector. - * It returns the offset of the partial match within the vector, or -1 if the pattern is not found. - * This method is particularly useful when searching for patterns that start in one vector and end in another. - * When combined with startsWith() it can be used to find a pattern that overlaps two buffers. - * - * \note This will not match the complete pattern at the end of the string; use endsWith() for that. - */ - size_t endsWithPartialMatch(const ByteVector &pattern) const; - - /*! - * Appends \a v to the end of the ByteVector. - */ - ByteVector &append(const ByteVector &v); - - /*! - * Appends \a c to the end of the ByteVector. - */ - ByteVector &append(char c); - - /*! - * Clears the data. - */ - ByteVector &clear(); - - /*! - * Returns the size of the array. - */ - size_t size() const; - - /*! - * Resize the vector to \a size. - * If the vector is currently less than \a size, pad the remaining spaces with \a padding. - * Returns a reference to the resized vector. - */ - ByteVector &resize(size_t size, char padding = 0); - - /*! - * Returns an Iterator that points to the front of the vector. - */ - Iterator begin(); - - /*! - * Returns a ConstIterator that points to the front of the vector. - */ - ConstIterator begin() const; - - /*! - * Returns an Iterator that points to the back of the vector. - */ - Iterator end(); - - /*! - * Returns a ConstIterator that points to the back of the vector. - */ - ConstIterator end() const; - - /*! - * Returns a ReverseIterator that points to the front of the vector. - */ - ReverseIterator rbegin(); - - /*! - * Returns a ConstReverseIterator that points to the front of the vector. - */ - ConstReverseIterator rbegin() const; - - /*! - * Returns a ReverseIterator that points to the back of the vector. - */ - ReverseIterator rend(); - - /*! - * Returns a ConstReverseIterator that points to the back of the vector. - */ - ConstReverseIterator rend() const; - - /*! - * Returns true if the ByteVector is empty. - * - * \see size() - */ - bool isEmpty() const; - - /*! - * Converts the 2 bytes at \a offset of the vector to a short as a signed 16-bit little-endian integer. - * - * \see fromUInt16LE() - */ - short toInt16LE(size_t offset) const; - - /*! - * Converts the 2 bytes at \a offset of the vector to a short as a signed 16-bit big-endian integer. - * - * \see fromUInt16BE() - */ - short toInt16BE(size_t offset) const; - - /*! - * Converts the 2 bytes at \a offset of the vector to a unsigned short as an unsigned 16-bit little-endian integer. - * - * \see fromUInt16LE() - */ - unsigned short toUInt16LE(size_t offset) const; - - /*! - * Converts the 2 bytes at \a offset of the vector to a unsigned short as an unsigned 16-bit big-endian integer. - * - * \see fromUInt16BE() - */ - unsigned short toUInt16BE(size_t offset) const; - - /*! - * Converts the 3 bytes at \a offset of the vector to a unsigned int as an unsigned 24-bit little-endian integer. - */ - unsigned int toUInt24LE(size_t offset) const; - - /*! - * Converts the 3 bytes at \a offset of the vector to a unsigned int as an unsigned 24-bit big-endian integer. - */ - unsigned int toUInt24BE(size_t offset) const; - - /*! - * Converts the 4 bytes at \a offset of the vector to a unsigned int as an unsigned 32-bit little-endian integer. - * - * \see fromUInt32LE() - */ - unsigned int toUInt32LE(size_t offset) const; - - /*! - * Converts the 4 bytes at \a offset of the vector to a unsigned int as an unsigned 32-bit big-endian integer. - * - * \see fromUInt32BE() - */ - unsigned int toUInt32BE(size_t offset) const; - - /*! - * Converts the 8 bytes at \a offset of the vector to a long long as a signed 64-bit little-endian integer. - * - * \see fromUInt64LE() - */ - long long toInt64LE(size_t offset) const; - - /*! - * Converts the 8 bytes at \a offset of the vector to a long long as a signed 64-bit big-endian integer. - * - * \see fromUInt64BE() - */ - long long toInt64BE(size_t offset) const; - - /* - * Converts the 4 bytes at \a offset of the vector to a float as an IEEE754 32-bit little-endian floating point number. - */ - float toFloat32LE(size_t offset) const; - - /* - * Converts the 4 bytes at \a offset of the vector to a float as an IEEE754 32-bit big-endian floating point number. - */ - float toFloat32BE(size_t offset) const; - - /* - * Converts the 8 bytes at \a offset of the vector to a double as an IEEE754 64-bit little-endian floating point number. - */ - double toFloat64LE(size_t offset) const; - - /* - * Converts the 8 bytes at \a offset of the vector to a double as an IEEE754 64-bit big-endian floating point number. - */ - double toFloat64BE(size_t offset) const; - - /* - * Converts the 10 bytes at \a offset of the vector to a long double as an IEEE754 80-bit little-endian floating point number. - * - * \note This may compromise the precision depends on the size of long double. - */ - long double toFloat80LE(size_t offset) const; - - /* - * Converts the 10 bytes at \a offset of the vector to a long double as an IEEE754 80-bit big-endian floating point number. - * - * \note This may compromise the precision depends on the size of long double. - */ - long double toFloat80BE(size_t offset) const; - - /*! - * Creates a 2 byte ByteVector based on \a value as an unsigned 16-bit little-endian integer. - * - * \note If \a value is larger than 16-bit, the lowest 16 bits are used. - * \see toUInt16LE() - */ - static ByteVector fromUInt16LE(size_t value); - - /*! - * Creates a 2 byte ByteVector based on \a value as an unsigned 16-bit big-endian integer. - * - * \note If \a value is larger than 16-bit, the lowest 16 bits are used. - * \see toUInt16BE() - */ - static ByteVector fromUInt16BE(size_t value); - - /*! - * Creates a 4 byte ByteVector based on \a value as an unsigned 32-bit little-endian integer. - * - * \note If \a value is larger than 32-bit, the lowest 32 bits are used. - * \see toUInt32LE() - */ - static ByteVector fromUInt32LE(size_t value); - - /*! - * Creates a 4 byte ByteVector based on \a value as an unsigned 32-bit big-endian integer. - * - * \note If \a value is larger than 32-bit, the lowest 32 bits are used. - * \see toUInt32BE() - */ - static ByteVector fromUInt32BE(size_t value); - - /*! - * Creates a 8 byte ByteVector based on \a value as an unsigned 64-bit little-endian integer. - * - * \see toUInt64LE() - */ - static ByteVector fromUInt64LE(unsigned long long value); - - /*! - * Creates a 8 byte ByteVector based on \a value as an unsigned 64-bit big-endian integer. - * - * \see toUInt64BE() - */ - static ByteVector fromUInt64BE(unsigned long long value); - - /*! - * Creates a 4 byte ByteVector based on \a value as an IEEE754 32-bit little-endian floating point number. - * - * \see fromFloat32BE() - */ - static ByteVector fromFloat32LE(float value); - - /*! - * Creates a 4 byte ByteVector based on \a value as an IEEE754 32-bit big-endian floating point number. - * - * \see fromFloat32LE() - */ - static ByteVector fromFloat32BE(float value); - - /*! - * Creates a 8 byte ByteVector based on \a value as an IEEE754 64-bit little-endian floating point number. - * - * \see fromFloat64BE() - */ - static ByteVector fromFloat64LE(double value); - - /*! - * Creates a 8 byte ByteVector based on \a value as an IEEE754 64-bit big-endian floating point number. - * - * \see fromFloat64LE() - */ - static ByteVector fromFloat64BE(double value); - - /*! - * Returns a ByteVector based on the CString \a s. - */ - static ByteVector fromCString(const char *s, size_t length = npos()); - - /*! - * Returns a const reference to the byte at \a index. - */ - const char &operator[](size_t index) const; - - /*! - * Returns a reference to the byte at \a index. - */ - char &operator[](size_t index); - - /*! - * Returns true if this ByteVector and \a v are equal. - */ - bool operator==(const ByteVector &v) const; - - /*! - * Returns true if this ByteVector and \a v are not equal. - */ - bool operator!=(const ByteVector &v) const; - - /*! - * Returns true if this ByteVector and the null terminated C string \a s contain the same data. - */ - bool operator==(const char *s) const; - - /*! - * Returns true if this ByteVector and the null terminated C string \a s do not contain the same data. - */ - bool operator!=(const char *s) const; - - /*! - * Returns true if this ByteVector is less than \a v. - * The value of the vectors is determined by evaluating the character from left to right, - * and in the event one vector is a superset of the other, the size is used. - */ - bool operator<(const ByteVector &v) const; - - /*! - * Returns true if this ByteVector is greater than \a v. - */ - bool operator>(const ByteVector &v) const; - - /*! - * Returns a vector that is \a v appended to this vector. - */ - ByteVector operator+(const ByteVector &v) const; - - /*! - * Copies ByteVector \a v. - */ - ByteVector &operator=(const ByteVector &v); - - /*! - * Copies a byte \a c. - */ - ByteVector &operator=(char c); - - /*! - * Copies \a data up to the first null byte. - * - * \warning The behavior is undefined if \a data is not null terminated. - */ - ByteVector &operator=(const char *data); - - /*! - * Exchanges the content of the ByteVector by the content of \a v. - */ - void swap(ByteVector &v); - - /*! - * Returns a special value used for \a length or \a patternLength parameters - * of ByteVector's member functions, means "until the end of the data". - * As a return value, it is usually used to indicate no matches. - */ - static size_t npos(); - - /*! - * Returns a hex-encoded copy of the byte vector. - */ - ByteVector toHex() const; - - /*! - * Returns a base64 encoded copy of the byte vector - */ - ByteVector toBase64() const; - - /*! - * Decodes the base64 encoded byte vector. - */ - static ByteVector fromBase64(const ByteVector &); - - protected: - /* - * If this ByteVector is being shared via implicit sharing, do a deep copy of the data and separate from the shared members. - * This should be called by all non-const subclass members. - */ - void detach(); - - private: - class ByteVectorPrivate; - ByteVectorPrivate *d; -}; - -/*! - * \relates Strawberry_TagLib::TagLib::ByteVector - * Streams the ByteVector \a v to the output stream \a s. - */ -TAGLIB_EXPORT std::ostream &operator<<(std::ostream &s, const Strawberry_TagLib::TagLib::ByteVector &v); - -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/toolkit/tbytevectorlist.cpp b/3rdparty/taglib/toolkit/tbytevectorlist.cpp deleted file mode 100644 index 5e68605b..00000000 --- a/3rdparty/taglib/toolkit/tbytevectorlist.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tbytevectorlist.h" - -using namespace Strawberry_TagLib::TagLib; - -//////////////////////////////////////////////////////////////////////////////// -// static members -//////////////////////////////////////////////////////////////////////////////// - -ByteVectorList ByteVectorList::split(const ByteVector &v, const ByteVector &pattern, size_t byteAlign, size_t max) { - - ByteVectorList l; - - size_t previousOffset = 0; - for (size_t offset = v.find(pattern, 0, byteAlign); offset != ByteVector::npos() && (max == 0 || max > l.size() + 1); offset = v.find(pattern, offset + pattern.size(), byteAlign)) { - if (offset - previousOffset >= 1) - l.append(v.mid(previousOffset, offset - previousOffset)); - else - l.append(ByteVector()); - - previousOffset = offset + pattern.size(); - } - - if (previousOffset < v.size()) - l.append(v.mid(previousOffset, v.size() - previousOffset)); - - return l; - -} - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -ByteVectorList::ByteVectorList() {} - -ByteVectorList::ByteVectorList(const ByteVectorList &l) : List(l) {} - -ByteVectorList &ByteVectorList::operator=(const ByteVectorList &l) { - List::operator=(l); - return *this; -} - -ByteVector ByteVectorList::toByteVector(const ByteVector &separator) const { - - ByteVector v; - - ConstIterator it = begin(); - - while (it != end()) { - v.append(*it); - it++; - if (it != end()) v.append(separator); - } - - return v; - -} diff --git a/3rdparty/taglib/toolkit/tbytevectorlist.h b/3rdparty/taglib/toolkit/tbytevectorlist.h deleted file mode 100644 index b8cd8867..00000000 --- a/3rdparty/taglib/toolkit/tbytevectorlist.h +++ /dev/null @@ -1,80 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_BYTEVECTORLIST_H -#define TAGLIB_BYTEVECTORLIST_H - -#include "taglib_export.h" -#include "tbytevector.h" -#include "tlist.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -//! A list of ByteVectors - -/*! - * A List specialization with some handy features useful for ByteVectors. - */ - -class TAGLIB_EXPORT ByteVectorList : public List { - public: - /*! - * Construct an empty ByteVectorList. - */ - explicit ByteVectorList(); - - /*! - * Make a shallow, implicitly shared, copy of \a l. - * Because this is implicitly shared, this method is lightweight and suitable for pass-by-value usage. - */ - ByteVectorList(const ByteVectorList &l); - - /*! - * Make a shallow, implicitly shared, copy of \a l. - * Because this is implicitly shared, this method is lightweight and suitable for pass-by-value usage. - */ - ByteVectorList &operator=(const ByteVectorList &l); - - /*! - * Convert the ByteVectorList to a ByteVector separated by \a separator. By default a space is used. - */ - ByteVector toByteVector(const ByteVector &separator = " ") const; - - /*! - * Splits the ByteVector \a v into several strings at \a pattern. - * This will not include the pattern in the returned ByteVectors. - * \a max is the maximum number of entries that will be separated. - * If \a max for instance is 2 then a maximum of 1 match will be found and the vector will be split on that match. - */ - - static ByteVectorList split(const ByteVector &v, const ByteVector &pattern, size_t byteAlign = 1, size_t max = 0); - -}; - -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/toolkit/tbytevectorstream.cpp b/3rdparty/taglib/toolkit/tbytevectorstream.cpp deleted file mode 100644 index e700b145..00000000 --- a/3rdparty/taglib/toolkit/tbytevectorstream.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Lukas Lalinsky - email : lalinsky@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include -#include -#include -#include - -#include "tbytevectorstream.h" -#include "tstring.h" -#include "tdebug.h" - -using namespace Strawberry_TagLib::TagLib; - -class ByteVectorStream::ByteVectorStreamPrivate { - public: - explicit ByteVectorStreamPrivate(const ByteVector &data); - - ByteVector data; - long long position; -}; - -ByteVectorStream::ByteVectorStreamPrivate::ByteVectorStreamPrivate(const ByteVector &_data) : data(_data), position(0) {} - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -ByteVectorStream::ByteVectorStream(const ByteVector &data) : d(new ByteVectorStreamPrivate(data)) {} - -ByteVectorStream::~ByteVectorStream() { - delete d; -} - -FileName ByteVectorStream::name() const { - return FileName(""); // XXX do we need a name? -} - -ByteVector ByteVectorStream::readBlock(size_t length) { - - if (length == 0) - return ByteVector(); - - ByteVector v = d->data.mid(d->position, length); - d->position += v.size(); - return v; - -} - -void ByteVectorStream::writeBlock(const ByteVector &data) { - - const size_t size = data.size(); - if (static_cast(d->position + size) > length()) { - truncate(d->position + size); - } - ::memcpy(d->data.data() + d->position, data.data(), size); - d->position += size; - -} - -void ByteVectorStream::insert(const ByteVector &data, long long start, size_t replace) { - - if (data.size() < replace) { - removeBlock(start + data.size(), replace - data.size()); - } - else if (data.size() > replace) { - const size_t sizeDiff = data.size() - replace; - truncate(length() + sizeDiff); - const size_t readPosition = static_cast(start + replace); - const size_t writePosition = static_cast(start + data.size()); - ::memmove(d->data.data() + writePosition, d->data.data() + readPosition, static_cast(length() - sizeDiff - readPosition)); - } - seek(start); - writeBlock(data); - -} - -void ByteVectorStream::removeBlock(long long start, size_t length) { - - const long long readPosition = start + length; - long long writePosition = start; - if (readPosition < ByteVectorStream::length()) { - size_t bytesToMove = static_cast(ByteVectorStream::length() - readPosition); - ::memmove(d->data.data() + static_cast(writePosition), d->data.data() + static_cast(readPosition), bytesToMove); - writePosition += bytesToMove; - } - d->position = writePosition; - truncate(writePosition); - -} - -bool ByteVectorStream::readOnly() const { - return false; -} - -bool ByteVectorStream::isOpen() const { - return true; -} - -void ByteVectorStream::seek(long long offset, Position p) { - - switch (p) { - case Beginning: - d->position = offset; - break; - case Current: - d->position += offset; - break; - case End: - d->position = length() + offset; // offset is expected to be negative - break; - } - -} - -void ByteVectorStream::clear() {} - -long long ByteVectorStream::tell() const { - return d->position; -} - -long long ByteVectorStream::length() { - return d->data.size(); -} - -void ByteVectorStream::truncate(long long length) { - d->data.resize(length); -} - -ByteVector *ByteVectorStream::data() { - return &d->data; -} diff --git a/3rdparty/taglib/toolkit/tbytevectorstream.h b/3rdparty/taglib/toolkit/tbytevectorstream.h deleted file mode 100644 index d0b24e0d..00000000 --- a/3rdparty/taglib/toolkit/tbytevectorstream.h +++ /dev/null @@ -1,137 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Lukas Lalinsky - email : lalinsky@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_BYTEVECTORSTREAM_H -#define TAGLIB_BYTEVECTORSTREAM_H - -#include "taglib_export.h" -#include "taglib.h" -#include "tbytevector.h" -#include "tiostream.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -class String; -class Tag; -class AudioProperties; - -//! In-memory Stream class using ByteVector for its storage. - -class TAGLIB_EXPORT ByteVectorStream : public IOStream { - public: - /*! - * Construct a File object and opens the \a file. \a file should be a be a C-string in the local file system encoding. - */ - explicit ByteVectorStream(const ByteVector &data); - - /*! - * Destroys this ByteVectorStream instance. - */ - ~ByteVectorStream() override; - - /*! - * Returns the file name in the local file system encoding. - */ - FileName name() const override; - - /*! - * Reads a block of size \a length at the current get pointer. - */ - ByteVector readBlock(size_t length) override; - - /*! - * Attempts to write the block \a data at the current get pointer. - * If the file is currently only opened read only -- i.e. readOnly() returns true -- this attempts to reopen the file in read/write mode. - * - * \note This should be used instead of using the streaming output operator for a ByteVector. - * And even this function is significantly slower than doing output with a char[]. - */ - void writeBlock(const ByteVector &data) override; - - /*! - * Insert \a data at position \a start in the file overwriting \a replace bytes of the original content. - * - * \note This method is slow since it requires rewriting all of the file after the insertion point. - */ - void insert(const ByteVector &data, long long start = 0, size_t replace = 0) override; - - /*! - * Removes a block of the file starting a \a start and continuing for \a length bytes. - * - * \note This method is slow since it involves rewriting all of the file after the removed portion. - */ - void removeBlock(long long start = 0, size_t length = 0) override; - - /*! - * Returns true if the file is read only (or if the file can not be opened). - */ - bool readOnly() const override; - - /*! - * Since the file can currently only be opened as an argument to the constructor (sort-of by design), this returns if that open succeeded. - */ - bool isOpen() const override; - - /*! - * Move the I/O pointer to \a offset in the file from position \a p. - * This defaults to seeking from the beginning of the file. - * - * \see Position - */ - void seek(long long offset, Position p = Beginning) override; - - /*! - * Reset the end-of-file and error flags on the file. - */ - void clear() override; - - /*! - * Returns the current offset within the file. - */ - long long tell() const override; - - /*! - * Returns the length of the file. - */ - long long length() override; - - /*! - * Truncates the file to a \a length. - */ - void truncate(long long length) override; - - ByteVector *data(); - - protected: - private: - class ByteVectorStreamPrivate; - ByteVectorStreamPrivate *d; -}; - -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/toolkit/tdebug.cpp b/3rdparty/taglib/toolkit/tdebug.cpp deleted file mode 100644 index 5764be4c..00000000 --- a/3rdparty/taglib/toolkit/tdebug.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "taglib-config.h" - -#if !defined(NDEBUG) || defined(TRACE_IN_RELEASE) - -# include "tdebug.h" -# include "tstring.h" -# include "tdebuglistener.h" -# include "tutils.h" - -# include -# include -# include - -namespace Strawberry_TagLib { -namespace TagLib { -// The instance is defined in tdebuglistener.cpp. -extern DebugListener *debugListener; - -void debug(const String &s) { - debugListener->printMessage("TagLib: " + s + "\n"); -} - -void debugData(const ByteVector &v) { - - for (unsigned int i = 0; i < v.size(); ++i) { - const std::string bits = std::bitset<8>(v[i]).to_string(); - const String msg = Utils::formatString( - "*** [%u] - char '%c' - int %d, 0x%02x, 0b%s\n", - i, v[i], v[i], v[i], bits.c_str()); - - debugListener->printMessage(msg); - } - -} -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/toolkit/tdebug.h b/3rdparty/taglib/toolkit/tdebug.h deleted file mode 100644 index a7963390..00000000 --- a/3rdparty/taglib/toolkit/tdebug.h +++ /dev/null @@ -1,72 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_DEBUG_H -#define TAGLIB_DEBUG_H - -namespace Strawberry_TagLib { -namespace TagLib { - -class String; -class ByteVector; - -#ifndef DO_NOT_DOCUMENT -# if !defined(NDEBUG) || defined(TRACE_IN_RELEASE) - -/*! - * A simple function that outputs the debug messages to the listener. - * The default listener redirects the messages to \a stderr when NDEBUG is - * not defined. - * - * \warning Do not use this outside of TagLib, it could lead to undefined - * symbols in your build if TagLib is built with NDEBUG defined and your - * application is not. - * - * \internal - */ -void debug(const String &s); - -/*! - * For debugging binary data. - * - * \warning Do not use this outside of TagLib, it could lead to undefined - * symbols in your build if TagLib is built with NDEBUG defined and your - * application is not. - * - * \internal - */ -void debugData(const ByteVector &v); - -# else - -# define debug(x) ((void)0) -# define debugData(x) ((void)0) - -# endif -} -} - -#endif -#endif diff --git a/3rdparty/taglib/toolkit/tdebuglistener.cpp b/3rdparty/taglib/toolkit/tdebuglistener.cpp deleted file mode 100644 index 398aa447..00000000 --- a/3rdparty/taglib/toolkit/tdebuglistener.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/*************************************************************************** - copyright : (C) 2013 by Tsuda Kageyu - email : tsuda.kageyu@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tdebuglistener.h" - -#include -#include - -#ifdef _WIN32 -# include -#endif - -using namespace Strawberry_TagLib::TagLib; - -namespace { -class DefaultListener : public DebugListener { - public: - void printMessage(const String &msg) override { -#ifdef _WIN32 - - const std::wstring wstr = msg.toWString(); - const int len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL); - if (len != 0) { - std::vector buf(len); - WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, &buf[0], len, NULL, NULL); - - std::cerr << std::string(&buf[0]); - } - -#else - - std::cerr << msg; - -#endif - } -}; - -DefaultListener defaultListener; -} // namespace - -namespace Strawberry_TagLib { -namespace TagLib { -DebugListener *debugListener = &defaultListener; - -DebugListener::DebugListener() {} - -DebugListener::~DebugListener() {} - -void setDebugListener(DebugListener *listener) { - - if (listener) - debugListener = listener; - else - debugListener = &defaultListener; - -} -} // namespace TagLib -} // namespace Strawberry_TagLib diff --git a/3rdparty/taglib/toolkit/tdebuglistener.h b/3rdparty/taglib/toolkit/tdebuglistener.h deleted file mode 100644 index 5c6418e8..00000000 --- a/3rdparty/taglib/toolkit/tdebuglistener.h +++ /dev/null @@ -1,72 +0,0 @@ -/*************************************************************************** - copyright : (C) 2013 by Tsuda Kageyu - email : tsuda.kageyu@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_DEBUGLISTENER_H -#define TAGLIB_DEBUGLISTENER_H - -#include "taglib_export.h" -#include "tstring.h" - -namespace Strawberry_TagLib { -namespace TagLib { -//! An abstraction for the listener to the debug messages. - -/*! - * This class enables you to handle the debug messages in your preferred - * way by subclassing this class, reimplementing printMessage() and setting - * your reimplementation as the default with setDebugListener(). - * - * \see setDebugListener() - */ -class TAGLIB_EXPORT DebugListener { - public: - explicit DebugListener(); - virtual ~DebugListener(); - - /*! - * When overridden in a derived class, redirects \a msg to your preferred - * channel such as stderr, Windows debugger or so forth. - */ - virtual void printMessage(const String &msg) = 0; - - private: - // Noncopyable - DebugListener(const DebugListener&); - DebugListener &operator=(const DebugListener&); -}; - -/*! - * Sets the listener that decides how the debug messages are redirected. - * If the parameter \a listener is null, the previous listener is released and default stderr listener is restored. - * - * \note The caller is responsible for deleting the previous listener as needed after it is released. - * - * \see DebugListener - */ -TAGLIB_EXPORT void setDebugListener(DebugListener *listener); -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/toolkit/tfile.cpp b/3rdparty/taglib/toolkit/tfile.cpp deleted file mode 100644 index 0bad7330..00000000 --- a/3rdparty/taglib/toolkit/tfile.cpp +++ /dev/null @@ -1,311 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tfile.h" -#include "tfilestream.h" -#include "tstring.h" -#include "tdebug.h" -#include "tpropertymap.h" -#include "audioproperties.h" - -using namespace Strawberry_TagLib::TagLib; - -class File::FilePrivate { - public: - explicit FilePrivate(IOStream *_stream, bool _owner) : stream(_stream), streamOwner(_owner), valid(true) {} - - ~FilePrivate() { - if (streamOwner) - delete stream; - } - - IOStream *stream; - bool streamOwner; - bool valid; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -File::~File() { - delete d; -} - -FileName File::name() const { - return d->stream->name(); -} - -PropertyMap File::properties() const { - return tag()->properties(); -} - -void File::removeUnsupportedProperties(const StringList &properties) { - tag()->removeUnsupportedProperties(properties); - -} - -PropertyMap File::setProperties(const PropertyMap &properties) { - return tag()->setProperties(properties); -} - -ByteVector File::readBlock(size_t length) { - return d->stream->readBlock(length); -} - -void File::writeBlock(const ByteVector &data) { - d->stream->writeBlock(data); -} - -long long File::find(const ByteVector &pattern, long long fromOffset, const ByteVector &before) { - - if (!d->stream || pattern.size() > bufferSize()) - return -1; - - // The position in the file that the current buffer starts at. - - long long bufferOffset = fromOffset; - - // These variables are used to keep track of a partial match that happens at - // the end of a buffer. - - size_t previousPartialMatch = ByteVector::npos(); - size_t beforePreviousPartialMatch = ByteVector::npos(); - - // Save the location of the current read pointer. We will restore the - // position using seek() before all returns. - - long long originalPosition = tell(); - - // Start the search at the offset. - - seek(fromOffset); - - // This loop is the crux of the find method. There are three cases that we - // want to account for: - // - // (1) The previously searched buffer contained a partial match of the search - // pattern and we want to see if the next one starts with the remainder of - // that pattern. - // - // (2) The search pattern is wholly contained within the current buffer. - // - // (3) The current buffer ends with a partial match of the pattern. We will - // note this for use in the next iteration, where we will check for the rest - // of the pattern. - // - // All three of these are done in two steps. First we check for the pattern - // and do things appropriately if a match (or partial match) is found. We - // then check for "before". The order is important because it gives priority - // to "real" matches. - - for (ByteVector buffer = readBlock(bufferSize()); buffer.size() > 0; buffer = readBlock(bufferSize())) { - - // (1) previous partial match - - if (previousPartialMatch != ByteVector::npos() && bufferSize() > previousPartialMatch) { - const size_t patternOffset = (bufferSize() - previousPartialMatch); - if (buffer.containsAt(pattern, 0, patternOffset)) { - seek(originalPosition); - return bufferOffset - bufferSize() + previousPartialMatch; - } - } - - if (!before.isEmpty() && beforePreviousPartialMatch != ByteVector::npos() && bufferSize() > beforePreviousPartialMatch) { - const size_t beforeOffset = (bufferSize() - beforePreviousPartialMatch); - if (buffer.containsAt(before, 0, beforeOffset)) { - seek(originalPosition); - return -1; - } - } - - // (2) pattern contained in current buffer - - size_t location = buffer.find(pattern); - if (location != ByteVector::npos()) { - seek(originalPosition); - return bufferOffset + location; - } - - if (!before.isEmpty() && buffer.find(before) != ByteVector::npos()) { - seek(originalPosition); - return -1; - } - - // (3) partial match - - previousPartialMatch = buffer.endsWithPartialMatch(pattern); - - if (!before.isEmpty()) - beforePreviousPartialMatch = buffer.endsWithPartialMatch(before); - - bufferOffset += bufferSize(); - } - - // Since we hit the end of the file, reset the status before continuing. - - clear(); - - seek(originalPosition); - - return -1; - -} - - -long long File::rfind(const ByteVector &pattern, long long fromOffset, const ByteVector &before) { - - if (!d->stream || pattern.size() > bufferSize()) - return -1; - - // Save the location of the current read pointer. We will restore the - // position using seek() before all returns. - - long long originalPosition = tell(); - - // Start the search at the offset. - - if (fromOffset == 0) - fromOffset = length(); - - long long bufferLength = bufferSize(); - long long bufferOffset = fromOffset + pattern.size(); - - // See the notes in find() for an explanation of this algorithm. - - while (true) { - - if (bufferOffset > bufferLength) { - bufferOffset -= bufferLength; - } - else { - bufferLength = bufferOffset; - bufferOffset = 0; - } - seek(bufferOffset); - - const ByteVector buffer = readBlock(static_cast(bufferLength)); - if (buffer.isEmpty()) - break; - - // TODO: (1) previous partial match - - // (2) pattern contained in current buffer - - const size_t location = buffer.rfind(pattern); - if (location != ByteVector::npos()) { - seek(originalPosition); - return bufferOffset + location; - } - - if (!before.isEmpty() && buffer.find(before) != ByteVector::npos()) { - seek(originalPosition); - return -1; - } - - // TODO: (3) partial match - } - - // Since we hit the end of the file, reset the status before continuing. - - clear(); - - seek(originalPosition); - - return -1; - -} - -void File::insert(const ByteVector &data, long long start, size_t replace) { - d->stream->insert(data, start, replace); -} - -void File::removeBlock(long long start, size_t length) { - d->stream->removeBlock(start, length); -} - -bool File::readOnly() const { - return d->stream->readOnly(); -} - -bool File::isOpen() const { - return d->stream->isOpen(); -} - -bool File::isValid() const { - return isOpen() && d->valid; -} - -void File::seek(long long offset, Position p) { - d->stream->seek(offset, IOStream::Position(p)); -} - -void File::truncate(long long length) { - d->stream->truncate(length); -} - -void File::clear() { - d->stream->clear(); -} - -long long File::tell() const { - return d->stream->tell(); -} - -long long File::length() { - return d->stream->length(); -} - -String File::toString() const { - - StringList desc; - AudioProperties *properties = audioProperties(); - if (properties) { - desc.append(properties->toString()); - } - Tag *t = tag(); - if (t) { - desc.append(t->toString()); - } - return desc.toString("\n"); - -} - - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -File::File(const FileName &fileName) : d(new FilePrivate(new FileStream(fileName), true)) {} - -File::File(IOStream *stream) : d(new FilePrivate(stream, false)) {} - -size_t File::bufferSize() { - return FileStream::bufferSize(); -} - -void File::setValid(bool valid) { - d->valid = valid; -} diff --git a/3rdparty/taglib/toolkit/tfile.h b/3rdparty/taglib/toolkit/tfile.h deleted file mode 100644 index 4743ba43..00000000 --- a/3rdparty/taglib/toolkit/tfile.h +++ /dev/null @@ -1,293 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_FILE_H -#define TAGLIB_FILE_H - -#include "taglib_export.h" -#include "taglib.h" -#include "tag.h" -#include "tbytevector.h" -#include "tiostream.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -class String; -class Tag; -class AudioProperties; -class PropertyMap; - -//! A file class with some useful methods for tag manipulation - -/*! - * This class is a basic file class with some methods that are particularly - * useful for tag editors. It has methods to take advantage of - * ByteVector and a binary search method for finding patterns in a file. - */ - -class TAGLIB_EXPORT File { - public: - /*! - * Position in the file used for seeking. - */ - enum Position { - //! Seek from the beginning of the file. - Beginning, - //! Seek from the current position in the file. - Current, - //! Seek from the end of the file. - End - }; - - /*! - * Specify which tags to strip either explicitly, or on save. - */ - enum StripTags { - StripNone, // -#else -# include -# include -#endif - -using namespace Strawberry_TagLib::TagLib; - -namespace { -#ifdef _WIN32 - -// Uses Win32 native API instead of POSIX API to reduce the resource consumption. - -typedef FileName FileNameHandle; -typedef HANDLE FileHandle; - -const FileHandle InvalidFileHandle = INVALID_HANDLE_VALUE; - -FileHandle openFile(const FileName &path, bool readOnly) { - - const DWORD access = readOnly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE); - -# if defined(PLATFORM_WINRT) - return CreateFile2(path.wstr(), access, FILE_SHARE_READ, OPEN_EXISTING, NULL); -# else - return CreateFileW(path.wstr(), access, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); -# endif -} - -FileHandle openFile(const int, bool) { - return InvalidFileHandle; -} - -void closeFile(FileHandle file) { - CloseHandle(file); -} - -size_t readFile(FileHandle file, ByteVector &buffer) { - - DWORD length; - if (ReadFile(file, buffer.data(), static_cast(buffer.size()), &length, NULL)) - return static_cast(length); - else - return 0; - -} - -size_t writeFile(FileHandle file, const ByteVector &buffer) { - - DWORD length; - if (WriteFile(file, buffer.data(), static_cast(buffer.size()), &length, NULL)) - return static_cast(length); - else - return 0; - -} - -#else // _WIN32 - -struct FileNameHandle : public std::string { - explicit FileNameHandle(FileName name) : std::string(name) {} - operator FileName() const { return c_str(); } -}; - -typedef FILE *FileHandle; - -const FileHandle InvalidFileHandle = nullptr; - -FileHandle openFile(const FileName &path, bool readOnly) { - return fopen(path, readOnly ? "rb" : "rb+"); -} - -FileHandle openFile(const int fileDescriptor, bool readOnly) { - return fdopen(fileDescriptor, readOnly ? "rb" : "rb+"); -} - -void closeFile(FileHandle file) { - fclose(file); -} - -size_t readFile(FileHandle file, ByteVector &buffer) { - return fread(buffer.data(), sizeof(char), buffer.size(), file); -} - -size_t writeFile(FileHandle file, const ByteVector &buffer) { - return fwrite(buffer.data(), sizeof(char), buffer.size(), file); -} - -#endif // _WIN32 -} // namespace - -class FileStream::FileStreamPrivate { - public: - explicit FileStreamPrivate(const FileName &fileName) - : file(InvalidFileHandle), name(fileName), readOnly(true) { - } - - FileHandle file; - FileNameHandle name; - bool readOnly; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -FileStream::FileStream(FileName fileName, bool openReadOnly) - : d(new FileStreamPrivate(fileName)) { - - // First try with read / write mode, if that fails, fall back to read only. - - if (!openReadOnly) - d->file = openFile(fileName, false); - - if (d->file != InvalidFileHandle) - d->readOnly = false; - else - d->file = openFile(fileName, true); - - if (d->file == InvalidFileHandle) -#ifdef _WIN32 - debug("Could not open file " + String(fileName.wstr())); -#else - debug("Could not open file " + String(static_cast(d->name))); -#endif - -} - -FileStream::FileStream(int fileDescriptor, bool openReadOnly) : d(new FileStreamPrivate("")) { - - // First try with read / write mode, if that fails, fall back to read only. - - if (!openReadOnly) - d->file = openFile(fileDescriptor, false); - - if (d->file != InvalidFileHandle) - d->readOnly = false; - else - d->file = openFile(fileDescriptor, true); - - if (d->file == InvalidFileHandle) - debug("Could not open file using file descriptor"); - -} - -FileStream::~FileStream() { - - if (isOpen()) - closeFile(d->file); - - delete d; - -} - -FileName FileStream::name() const { - return d->name; -} - -ByteVector FileStream::readBlock(size_t length) { - - if (!isOpen()) { - debug("FileStream::readBlock() -- invalid file."); - return ByteVector(); - } - - if (length == 0) - return ByteVector(); - - const long long streamLength = FileStream::length(); - if (length > bufferSize() && static_cast(length) > streamLength) - length = streamLength; - - ByteVector buffer(length); - - const size_t count = readFile(d->file, buffer); - buffer.resize(count); - - return buffer; - -} - -void FileStream::writeBlock(const ByteVector &data) { - - if (!isOpen()) { - debug("FileStream::writeBlock() -- invalid file."); - return; - } - - if (readOnly()) { - debug("FileStream::writeBlock() -- read only file."); - return; - } - - writeFile(d->file, data); - -} - -void FileStream::insert(const ByteVector &data, long long start, size_t replace) { - - if (!isOpen()) { - debug("FileStream::insert() -- invalid file."); - return; - } - - if (readOnly()) { - debug("FileStream::insert() -- read only file."); - return; - } - - if (data.size() == replace) { - seek(start); - writeBlock(data); - return; - } - else if (data.size() < replace) { - seek(start); - writeBlock(data); - removeBlock(start + data.size(), replace - data.size()); - return; - } - - // Woohoo! Faster (about 20%) than id3lib at last. I had to get hardcore - // and avoid TagLib's high level API for rendering just copying parts of - // the file that don't contain tag data. - // - // Now I'll explain the steps in this ugliness: - - // First, make sure that we're working with a buffer that is longer than - // the *difference* in the tag sizes. We want to avoid overwriting parts - // that aren't yet in memory, so this is necessary. - - size_t bufferLength = bufferSize(); - - while (data.size() - replace > bufferLength) - bufferLength += bufferSize(); - - // Set where to start the reading and writing. - - long long readPosition = start + replace; - long long writePosition = start; - - ByteVector buffer = data; - ByteVector aboutToOverwrite(bufferLength); - - while (true) { - // Seek to the current read position and read the data that we're about - // to overwrite. Appropriately increment the readPosition. - - seek(readPosition); - const unsigned int bytesRead = static_cast(readFile(d->file, aboutToOverwrite)); - aboutToOverwrite.resize(bytesRead); - readPosition += bufferLength; - - // Check to see if we just read the last block. We need to call clear() - // if we did so that the last write succeeds. - - if (bytesRead < bufferLength) - clear(); - - // Seek to the write position and write our buffer. Increment the - // writePosition. - - seek(writePosition); - writeBlock(buffer); - - // We hit the end of the file. - - if (bytesRead == 0) - break; - - writePosition += buffer.size(); - - // Make the current buffer the data that we read in the beginning. - - buffer = aboutToOverwrite; - } - -} - -void FileStream::removeBlock(long long start, size_t length) { - - if (!isOpen()) { - debug("FileStream::removeBlock() -- invalid file."); - return; - } - - size_t bufferLength = bufferSize(); - - long long readPosition = start + length; - long long writePosition = start; - - ByteVector buffer(bufferLength, 0); - - for (unsigned int bytesRead = -1; bytesRead != 0;) { - seek(readPosition); - bytesRead = static_cast(readFile(d->file, buffer)); - readPosition += bytesRead; - - // Check to see if we just read the last block. We need to call clear() - // if we did so that the last write succeeds. - - if (bytesRead < buffer.size()) { - clear(); - buffer.resize(bytesRead); - } - - seek(writePosition); - writeFile(d->file, buffer); - - writePosition += bytesRead; - } - - truncate(writePosition); - -} - -bool FileStream::readOnly() const { - return d->readOnly; -} - -bool FileStream::isOpen() const { - return (d->file != InvalidFileHandle); -} - -void FileStream::seek(long long offset, Position p) { - - if (!isOpen()) { - debug("FileStream::seek() -- invalid file."); - return; - } - -#ifdef _WIN32 - - if (p != Beginning && p != Current && p != End) { - debug("FileStream::seek() -- Invalid Position value."); - return; - } - - LARGE_INTEGER liOffset; - liOffset.QuadPart = offset; - - if (!SetFilePointerEx(d->file, liOffset, NULL, static_cast(p))) { - debug("FileStream::seek() -- Failed to set the file pointer."); - } - -#else - - int whence; - switch (p) { - case Beginning: - whence = SEEK_SET; - break; - case Current: - whence = SEEK_CUR; - break; - case End: - whence = SEEK_END; - break; - default: - debug("FileStream::seek() -- Invalid Position value."); - return; - } - - fseeko(d->file, offset, whence); - -#endif - -} - -void FileStream::clear() { -#ifdef _WIN32 - - // NOP - -#else - - clearerr(d->file); - -#endif -} - -long long FileStream::tell() const { -#ifdef _WIN32 - - const LARGE_INTEGER zero = {}; - LARGE_INTEGER position; - - if (SetFilePointerEx(d->file, zero, &position, FILE_CURRENT)) { - return position.QuadPart; - } - else { - debug("FileStream::tell() -- Failed to get the file pointer."); - return 0; - } - -#else - - return ftello(d->file); - -#endif -} - -long long FileStream::length() { - - if (!isOpen()) { - debug("FileStream::length() -- invalid file."); - return 0; - } - -#ifdef _WIN32 - - LARGE_INTEGER fileSize; - - if (GetFileSizeEx(d->file, &fileSize)) { - return fileSize.QuadPart; - } - else { - debug("FileStream::length() -- Failed to get the file size."); - return 0; - } - -#else - - const long long curpos = tell(); - - seek(0, End); - const long long endpos = tell(); - - seek(curpos, Beginning); - - return endpos; - -#endif -} - -size_t FileStream::bufferSize() { - return 1024; -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void FileStream::truncate(long long length) { -#ifdef _WIN32 - - const long long currentPos = tell(); - - seek(length); - - if (!SetEndOfFile(d->file)) { - debug("FileStream::truncate() -- Failed to truncate the file."); - } - - seek(currentPos); - -#else - - fflush(d->file); - const int error = ftruncate(fileno(d->file), length); - if (error != 0) - debug("FileStream::truncate() -- Coundn't truncate the file."); - -#endif -} - diff --git a/3rdparty/taglib/toolkit/tfilestream.h b/3rdparty/taglib/toolkit/tfilestream.h deleted file mode 100644 index b08731b9..00000000 --- a/3rdparty/taglib/toolkit/tfilestream.h +++ /dev/null @@ -1,151 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_FILESTREAM_H -#define TAGLIB_FILESTREAM_H - -#include "taglib_export.h" -#include "taglib.h" -#include "tbytevector.h" -#include "tiostream.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -class String; -class Tag; -class AudioProperties; - -//! A file class with some useful methods for tag manipulation - -/*! - * This class is a basic file class with some methods that are particularly useful for tag editors. - * It has methods to take advantage of ByteVector and a binary search method for finding patterns in a file. - */ - -class TAGLIB_EXPORT FileStream : public IOStream { - public: - /*! - * Construct a File object and opens the \a file. - * \a file should be a be a C-string in the local file system encoding. - */ - explicit FileStream(FileName file, bool openReadOnly = false); - - /*! - * Construct a File object and opens the \a file using file descriptor. - */ - explicit FileStream(int fileDescriptor, bool openReadOnly = false); - - /*! - * Destroys this FileStream instance. - */ - ~FileStream() override; - - /*! - * Returns the file name in the local file system encoding. - */ - FileName name() const override; - - /*! - * Reads a block of size \a length at the current get pointer. - */ - ByteVector readBlock(size_t length) override; - - /*! - * Attempts to write the block \a data at the current get pointer. - * If the file is currently only opened read only -- i.e. readOnly() returns true -- this attempts to reopen the file in read/write mode. - * - * \note This should be used instead of using the streaming output operator for a ByteVector. - * And even this function is significantly slower than doing output with a char[]. - */ - void writeBlock(const ByteVector &data) override; - - /*! - * Insert \a data at position \a start in the file overwriting \a replace bytes of the original content. - * - * \note This method is slow since it requires rewriting all of the file after the insertion point. - */ - void insert(const ByteVector &data, long long start = 0, size_t replace = 0) override; - - /*! - * Removes a block of the file starting a \a start and continuing for \a length bytes. - * - * \note This method is slow since it involves rewriting all of the file - * after the removed portion. - */ - void removeBlock(long long start = 0, size_t length = 0) override; - - /*! - * Returns true if the file is read only (or if the file can not be opened). - */ - bool readOnly() const override; - - /*! - * Since the file can currently only be opened as an argument to the constructor (sort-of by design), this returns if that open succeeded. - */ - bool isOpen() const override; - - /*! - * Move the I/O pointer to \a offset in the file from position \a p. - * This defaults to seeking from the beginning of the file. - * - * \see Position - */ - void seek(long long offset, Position p = Beginning) override; - - /*! - * Reset the end-of-file and error flags on the file. - */ - void clear() override; - - /*! - * Returns the current offset within the file. - */ - long long tell() const override; - - /*! - * Returns the length of the file. - */ - long long length() override; - - /*! - * Truncates the file to a \a length. - */ - void truncate(long long length) override; - - /*! - * Returns the buffer size that is used for internal buffering. - */ - static size_t bufferSize(); - - private: - class FileStreamPrivate; - FileStreamPrivate *d; -}; - -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/toolkit/tiostream.cpp b/3rdparty/taglib/toolkit/tiostream.cpp deleted file mode 100644 index 745005e5..00000000 --- a/3rdparty/taglib/toolkit/tiostream.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Lukas Lalinsky - email : lalinsky@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifdef _WIN32 -# include -# include -# include "tstring.h" -#endif - -#include "tiostream.h" - -using namespace Strawberry_TagLib::TagLib; - -#ifdef _WIN32 - -namespace { -std::wstring ansiToUnicode(const char *str) { - - const int len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); - if (len == 0) - return std::wstring(); - - std::wstring wstr(len - 1, L'\0'); - MultiByteToWideChar(CP_ACP, 0, str, -1, &wstr[0], len); - - return wstr; - -} -} // namespace - -class FileName::FileNamePrivate { - public: - FileNamePrivate() : data(new std::wstring()) {} - - FileNamePrivate(const wchar_t *name) : data(new std::wstring(name)) {} - - FileNamePrivate(const char *name) : data(new std::wstring(ansiToUnicode(name))) {} - - std::shared_ptr data; -}; - -FileName::FileName(const wchar_t *name) : d(new FileNamePrivate(name)) {} - -FileName::FileName(const char *name) : d(new FileNamePrivate(name)) {} - -FileName::FileName(const FileName &name) : d(new FileNamePrivate()) { - *d = *name.d; -} - -FileName::~FileName() { - delete d; -} - -FileName &FileName::operator=(const FileName &name) { - *d = *name.d; - return *this; -} - -const wchar_t *FileName::wstr() const { - return d->data->c_str(); -} - -#endif // _WIN32 - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -IOStream::IOStream() {} - -IOStream::~IOStream() {} - -void IOStream::clear() {} diff --git a/3rdparty/taglib/toolkit/tiostream.h b/3rdparty/taglib/toolkit/tiostream.h deleted file mode 100644 index 987c2b68..00000000 --- a/3rdparty/taglib/toolkit/tiostream.h +++ /dev/null @@ -1,163 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Lukas Lalinsky - email : lalinsky@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_IOSTREAM_H -#define TAGLIB_IOSTREAM_H - -#include "taglib_export.h" -#include "taglib.h" -#include "tbytevector.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -#ifdef _WIN32 - -class String; - -class TAGLIB_EXPORT FileName { - public: - FileName(const wchar_t *name); - FileName(const char *name); - FileName(const FileName &name); - - ~FileName(); - - FileName &operator=(const FileName &name); - - const wchar_t *wstr() const; - - private: - class FileNamePrivate; - FileNamePrivate *d; -}; -#else -typedef const char *FileName; -#endif - -//! An abstract class that provides operations on a sequence of bytes - -class TAGLIB_EXPORT IOStream { - public: - /*! - * Position in the file used for seeking. - */ - enum Position { - //! Seek from the beginning of the file. - Beginning, - //! Seek from the current position in the file. - Current, - //! Seek from the end of the file. - End - }; - - explicit IOStream(); - - /*! - * Destroys this IOStream instance. - */ - virtual ~IOStream(); - - /*! - * Returns the stream name in the local file system encoding. - */ - virtual FileName name() const = 0; - - /*! - * Reads a block of size \a length at the current get pointer. - */ - virtual ByteVector readBlock(size_t length) = 0; - - /*! - * Attempts to write the block \a data at the current get pointer. - * If the file is currently only opened read only -- i.e. readOnly() returns true -- this attempts to reopen the file in read/write mode. - * - * \note This should be used instead of using the streaming output operator for a ByteVector. - * And even this function is significantly slower than doing output with a char[]. - */ - virtual void writeBlock(const ByteVector &data) = 0; - - /*! - * Insert \a data at position \a start in the file overwriting \a replace bytes of the original content. - * - * \note This method is slow since it requires rewriting all of the file after the insertion point. - */ - virtual void insert(const ByteVector &data, - long long start = 0, size_t replace = 0) = 0; - - /*! - * Removes a block of the file starting a \a start and continuing for \a length bytes. - * - * \note This method is slow since it involves rewriting all of the file after the removed portion. - */ - virtual void removeBlock(long long start = 0, size_t length = 0) = 0; - - /*! - * Returns true if the file is read only (or if the file can not be opened). - */ - virtual bool readOnly() const = 0; - - /*! - * Since the file can currently only be opened as an argument to the constructor (sort-of by design), this returns if that open succeeded. - */ - virtual bool isOpen() const = 0; - - /*! - * Move the I/O pointer to \a offset in the stream from position \a p. - * This defaults to seeking from the beginning of the stream. - * - * \see Position - */ - virtual void seek(long long offset, Position p = Beginning) = 0; - - /*! - * Reset the end-of-stream and error flags on the stream. - */ - virtual void clear(); - - /*! - * Returns the current offset within the stream. - */ - virtual long long tell() const = 0; - - /*! - * Returns the length of the stream. - */ - virtual long long length() = 0; - - /*! - * Truncates the stream to a \a length. - */ - virtual void truncate(long long length) = 0; - - private: - IOStream(const IOStream&); - IOStream &operator=(const IOStream&); -}; - -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/toolkit/tlist.h b/3rdparty/taglib/toolkit/tlist.h deleted file mode 100644 index 0585b386..00000000 --- a/3rdparty/taglib/toolkit/tlist.h +++ /dev/null @@ -1,261 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_LIST_H -#define TAGLIB_LIST_H - -#include "taglib.h" - -#include -#include - -namespace Strawberry_TagLib { -namespace TagLib { - -//! A generic, implicitly shared list. - -/*! - * This is basic generic list that's somewhere between a std::list and a QValueList. - * This class is implicitly shared. For example: - * - * \code - * - * Strawberry_TagLib::TagLib::List l = someOtherIntList; - * - * \endcode - * - * The above example is very cheap. - * This also makes lists suitable for the return types of functions. - * The above example will just copy a pointer rather than copying the data in the list. - * When your \e shared list's data changes, only \e then will the data be copied. - */ - -template class List { - public: -#ifndef DO_NOT_DOCUMENT - typedef typename std::list::iterator Iterator; - typedef typename std::list::const_iterator ConstIterator; -#endif - - /*! - * Constructs an empty list. - */ - explicit List(); - - /*! - * Make a shallow, implicitly shared, copy of \a l. - * Because this is implicitly shared, this method is lightweight and suitable for pass-by-value usage. - */ - List(const List &l); - - /*! - * Destroys this List instance. If auto deletion is enabled and this list contains a pointer type all of the members are also deleted. - */ - virtual ~List(); - - /*! - * Returns an STL style iterator to the beginning of the list. - * See std::list::const_iterator for the semantics. - */ - Iterator begin(); - - /*! - * Returns an STL style constant iterator to the beginning of the list. - * See std::list::iterator for the semantics. - */ - ConstIterator begin() const; - - /*! - * Returns an STL style iterator to the end of the list. - * See std::list::iterator for the semantics. - */ - Iterator end(); - - /*! - * Returns an STL style constant iterator to the end of the list. - * See std::list::const_iterator for the semantics. - */ - ConstIterator end() const; - - /*! - * Inserts a copy of \a value before \a it. - */ - Iterator insert(Iterator it, const T &value); - - /*! - * Inserts the \a value into the list. This assumes that the list is currently sorted. - * If \a unique is true then the value will not be inserted if it is already in the list. - */ - List &sortedInsert(const T &value, bool unique = false); - - /*! - * Appends \a item to the end of the list and returns a reference to the list. - */ - List &append(const T &item); - - /*! - * Appends all of the values in \a l to the end of the list and returns a reference to the list. - */ - List &append(const List &l); - - /*! - * Prepends \a item to the beginning list and returns a reference to the list. - */ - List &prepend(const T &item); - - /*! - * Prepends all of the items in \a l to the beginning list and returns a reference to the list. - */ - List &prepend(const List &l); - - /*! - * Clears the list. If auto deletion is enabled and this list contains a pointer type the members are also deleted. - * - * \see setAutoDelete() - */ - List &clear(); - - /*! - * Returns the number of elements in the list. - * - * \see isEmpty() - */ - size_t size() const; - - /*! - * Returns whether or not the list is empty. - * - * \see size() - */ - bool isEmpty() const; - - /*! - * Find the first occurrence of \a value. - */ - template - Iterator find(const U &value); - - /*! - * Find the first occurrence of \a value. - */ - template - ConstIterator find(const U &value) const; - - /*! - * Returns true if the list contains \a value. - */ - template - bool contains(const U &value) const; - - /*! - * Erase the item at \a it from the list. - */ - Iterator erase(Iterator it); - - /*! - * Returns a reference to the first item in the list. - */ - const T &front() const; - - /*! - * Returns a reference to the first item in the list. - */ - T &front(); - - /*! - * Returns a reference to the last item in the list. - */ - const T &back() const; - - /*! - * Returns a reference to the last item in the list. - */ - T &back(); - - /*! - * Auto delete the members of the list when the last reference to the list passes out of scope. - * This will have no effect on lists which do not contain a pointer type. - * - * \note This relies on partial template instantiation -- most modern C++ compilers should now support this. - */ - void setAutoDelete(bool autoDelete); - - /*! - * Returns a reference to item \a i in the list. - * - * \warning This method is slow. Use iterators to loop through the list. - */ - T &operator[](size_t i); - - /*! - * Returns a const reference to item \a i in the list. - * - * \warning This method is slow. Use iterators to loop through the list. - */ - const T &operator[](size_t i) const; - - /*! - * Make a shallow, implicitly shared, copy of \a l. - * Because this is implicitly shared, this method is lightweight and suitable for pass-by-value usage. - */ - List &operator=(const List &l); - - /*! - * Exchanges the content of this list by the content of \a l. - */ - void swap(List &l); - - /*! - * Compares this list with \a l and returns true if all of the elements are the same. - */ - bool operator==(const List &l) const; - - /*! - * Compares this list with \a l and returns true if the lists differ. - */ - bool operator!=(const List &l) const; - - protected: - /* - * If this List is being shared via implicit sharing, do a deep copy of the data and separate from the shared members. - * This should be called by all non-const subclass members. - */ - void detach(); - - private: -#ifndef DO_NOT_DOCUMENT - template class ListPrivate; - ListPrivate *d; -#endif -}; - -} // namespace TagLib -} // namespace Strawberry_TagLib - -// Since GCC doesn't support the "export" keyword, we have to include the implementation. - -#include "tlist.tcc" - -#endif diff --git a/3rdparty/taglib/toolkit/tlist.tcc b/3rdparty/taglib/toolkit/tlist.tcc deleted file mode 100644 index 16b9cd6f..00000000 --- a/3rdparty/taglib/toolkit/tlist.tcc +++ /dev/null @@ -1,324 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include -#include "trefcounter.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -// The functionality of List::setAutoDelete() is implemented here partial template specialization. -// This is implemented in such a way that calling setAutoDelete() on non-pointer types will simply have no effect. - -// A generic implementation - -template -template class List::ListPrivate : public RefCounter -{ -public: - explicit ListPrivate() {} - explicit ListPrivate(const std::list &l) : list(l) {} - - void clear() { - std::list().swap(list); - } - - void setAutoDelete(bool) {} - - std::list list; -}; - -// A partial specialization for all pointer types that implements the setAutoDelete() functionality. - -template -template class List::ListPrivate : public RefCounter -{ -public: - explicit ListPrivate() : autoDelete(false) {} - - explicit ListPrivate(const std::list &l) : list(l), autoDelete(false) {} - - ~ListPrivate() override { - deletePointers(); - } - - void clear() { - deletePointers(); - std::list().swap(list); - } - - void setAutoDelete(bool del) { - autoDelete = del; - } - - std::list list; - -private: - void deletePointers() { - if(autoDelete) { - typename std::list::const_iterator it = list.begin(); - for(; it != list.end(); ++it) - delete *it; - } - } - - bool autoDelete; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -template -List::List() : d(new ListPrivate()){} - -template -List::List(const List &l) : d(l.d) { - d->ref(); -} - -template -List::~List() { - if(d->deref()) - delete d; -} - -template -typename List::Iterator List::begin() { - detach(); - return d->list.begin(); -} - -template -typename List::ConstIterator List::begin() const { - return d->list.begin(); -} - -template -typename List::Iterator List::end() { - detach(); - return d->list.end(); -} - -template -typename List::ConstIterator List::end() const { - return d->list.end(); -} - -template -typename List::Iterator List::insert(Iterator it, const T &item) { - - detach(); - return d->list.insert(it, item); - -} - -template -List &List::sortedInsert(const T &value, bool unique) { - - detach(); - Iterator it = begin(); - while (it != end() && *it < value) - ++it; - if (unique && it != end() && *it == value) - return *this; - insert(it, value); - return *this; - -} - -template -List &List::append(const T &item) { - - detach(); - d->list.push_back(item); - return *this; - -} - -template -List &List::append(const List &l) { - - detach(); - d->list.insert(d->list.end(), l.begin(), l.end()); - return *this; - -} - -template -List &List::prepend(const T &item) { - - detach(); - d->list.push_front(item); - return *this; - -} - -template -List &List::prepend(const List &l) { - - detach(); - d->list.insert(d->list.begin(), l.begin(), l.end()); - return *this; - -} - -template -List &List::clear() { - - detach(); - d->clear(); - return *this; - -} - -template -size_t List::size() const { - return d->list.size(); -} - -template -bool List::isEmpty() const { - return d->list.empty(); -} - -template -template -typename List::Iterator List::find(const U &value) { - detach(); - return std::find(d->list.begin(), d->list.end(), value); -} - -template -template -typename List::ConstIterator List::find(const U &value) const { - return std::find(d->list.begin(), d->list.end(), value); -} - -template -template -bool List::contains(const U &value) const { - return std::find(d->list.begin(), d->list.end(), value) != d->list.end(); -} - -template -typename List::Iterator List::erase(Iterator it) { - return d->list.erase(it); -} - -template -const T &List::front() const { - return d->list.front(); -} - -template -T &List::front() { - detach(); - return d->list.front(); -} - -template -const T &List::back() const { - return d->list.back(); -} - -template -void List::setAutoDelete(bool autoDelete) { - d->setAutoDelete(autoDelete); -} - -template -T &List::back() { - - detach(); - return d->list.back(); - -} - -template -T &List::operator[](size_t i) { - - Iterator it = d->list.begin(); - std::advance(it, i); - - return *it; - -} - -template -const T &List::operator[](size_t i) const { - - ConstIterator it = d->list.begin(); - std::advance(it, i); - - return *it; - -} - -template -List &List::operator=(const List &l) { - - List(l).swap(*this); - return *this; - -} - -template -void List::swap(List &l) { - - using std::swap; - swap(d, l.d); - -} - -template -bool List::operator==(const List &l) const { - return d->list == l.d->list; -} - -template -bool List::operator!=(const List &l) const { - return d->list != l.d->list; -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -template -void List::detach() { - - if (!d->unique()) { - d->deref(); - d = new ListPrivate(d->list); - } - -} -} -} // namespace Strawberry_TagLib::TagLib diff --git a/3rdparty/taglib/toolkit/tmap.h b/3rdparty/taglib/toolkit/tmap.h deleted file mode 100644 index 7e7c4991..00000000 --- a/3rdparty/taglib/toolkit/tmap.h +++ /dev/null @@ -1,202 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_MAP_H -#define TAGLIB_MAP_H - -#include -#include - -#include "taglib.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -//! A generic, implicitly shared map. - -/*! - * This implements a standard map container that associates a key with a value and has fast key-based lookups. - * This map is also implicitly shared making it suitable for pass-by-value usage. - */ - -template class Map { - private: -#ifndef DO_NOT_DOCUMENT -# ifdef WANT_CLASS_INSTANTIATION_OF_MAP - // Some STL implementations get snippy over the use of the class keyword to distinguish different templates; Sun Studio - // in particular finds multiple specializations in certain rare cases and complains about that. - // GCC doesn't seem to mind, and uses the typedefs further below without the class keyword. - // Not all the specializations of Map can use the class keyword (when T is not actually a class type), - // so don't apply this generally. - typedef std::map MapType; -# else - typedef std::map MapType; -# endif -#endif - - public: -#ifndef DO_NOT_DOCUMENT - typedef typename MapType::iterator Iterator; - typedef typename MapType::const_iterator ConstIterator; -#endif - - /*! - * Constructs an empty Map. - */ - explicit Map(); - - /*! - * Make a shallow, implicitly shared, copy of \a m. - * Because this is implicitly shared, this method is lightweight and suitable for pass-by-value usage. - */ - Map(const Map &m); - - /*! - * Destroys this instance of the Map. - */ - virtual ~Map(); - - /*! - * Returns an STL style iterator to the beginning of the map. - * See std::map::iterator for the semantics. - */ - Iterator begin(); - - /*! - * Returns an STL style iterator to the beginning of the map. - * See std::map::const_iterator for the semantics. - */ - ConstIterator begin() const; - - /*! - * Returns an STL style iterator to the end of the map. - * See std::map::iterator for the semantics. - */ - Iterator end(); - - /*! - * Returns an STL style iterator to the end of the map. - * See std::map::const_iterator for the semantics. - */ - ConstIterator end() const; - - /*! - * Inserts \a value under \a key in the map. - * If a value for \a key already exists it will be overwritten. - */ - Map &insert(const Key &key, const T &value); - - /*! - * Removes all of the elements from elements from the map. - * This however will not delete pointers if the mapped type is a pointer type. - */ - Map &clear(); - - /*! - * The number of elements in the map. - * - * \see isEmpty() - */ - size_t size() const; - - /*! - * Returns true if the map is empty. - * - * \see size() - */ - bool isEmpty() const; - - /*! - * Find the first occurrence of \a key. - */ - Iterator find(const Key &key); - - /*! - * Find the first occurrence of \a key. - */ - ConstIterator find(const Key &key) const; - - /*! - * Returns true if the map contains an instance of \a key. - */ - bool contains(const Key &key) const; - - /*! - * Erase the item at \a it from the list. - */ - Map &erase(Iterator it); - - /*! - * Erase the item with \a key from the list. - */ - Map &erase(const Key &key); - - /*! - * Returns a reference to the value associated with \a key. - * - * \note This has undefined behavior if the key is not present in the map. - */ - const T &operator[](const Key &key) const; - - /*! - * Returns a reference to the value associated with \a key. - * - * \note This has undefined behavior if the key is not present in the map. - */ - T &operator[](const Key &key); - - /*! - * Make a shallow, implicitly shared, copy of \a m. - * Because this is implicitly shared, this method is lightweight and suitable for pass-by-value usage. - */ - Map &operator=(const Map &m); - - /*! - * Exchanges the content of this map by the content of \a m. - */ - void swap(Map &m); - - protected: - /* - * If this List is being shared via implicit sharing, do a deep copy of the data and separate from the shared members. - * This should be called by all non-const subclass members. - */ - void detach(); - - private: -#ifndef DO_NOT_DOCUMENT - template class MapPrivate; - MapPrivate *d; -#endif -}; - -} // namespace TagLib -} // namespace Strawberry_TagLib - -// Since GCC doesn't support the "export" keyword, we have to include the implementation. - -#include "tmap.tcc" - -#endif diff --git a/3rdparty/taglib/toolkit/tmap.tcc b/3rdparty/taglib/toolkit/tmap.tcc deleted file mode 100644 index d2a16ebf..00000000 --- a/3rdparty/taglib/toolkit/tmap.tcc +++ /dev/null @@ -1,171 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "trefcounter.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -template -template -class Map::MapPrivate : public RefCounter { - public: - explicit MapPrivate() : RefCounter() {} - explicit MapPrivate(const MapType &m) : RefCounter(), map(m) {} - - MapType map; -}; - -template -Map::Map() : d(new MapPrivate()){} - -template -Map::Map(const Map &m) : d(m.d) { - d->ref(); -} - -template -Map::~Map() { - if(d->deref()) - delete(d); -} - -template -typename Map::Iterator Map::begin() { - detach(); - return d->map.begin(); -} - -template -typename Map::ConstIterator Map::begin() const { - return d->map.begin(); -} - -template -typename Map::Iterator Map::end() { - detach(); - return d->map.end(); -} - -template -typename Map::ConstIterator Map::end() const { - return d->map.end(); -} - -template -Map &Map::insert(const Key &key, const T &value) { - detach(); - d->map[key] = value; - return *this; -} - -template -Map &Map::clear() { - detach(); - d->map.clear(); - return *this; -} - -template -bool Map::isEmpty() const { - return d->map.empty(); -} - -template -typename Map::Iterator Map::find(const Key &key) { - detach(); - return d->map.find(key); -} - -template -typename Map::ConstIterator Map::find(const Key &key) const { - return d->map.find(key); -} - -template -bool Map::contains(const Key &key) const { - return d->map.find(key) != d->map.end(); -} - -template -Map &Map::erase(Iterator it) { - detach(); - d->map.erase(it); - return *this; -} - -template -Map &Map::erase(const Key &key) { - detach(); - d->map.erase(key); - return *this; -} - -template -size_t Map::size() const { - return d->map.size(); -} - -template -const T &Map::operator[](const Key &key) const { - return d->map[key]; -} - -template -T &Map::operator[](const Key &key) { - detach(); - return d->map[key]; -} - -template -Map &Map::operator=(const Map &m) { - Map(m).swap(*this); - return *this; -} - -template -void Map::swap(Map &m) { - using std::swap; - - swap(d, m.d); -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -template -void Map::detach() { - if (!d->unique()) { - d->deref(); - d = new MapPrivate(d->map); - } -} -} -} // namespace Strawberry_TagLib::TagLib diff --git a/3rdparty/taglib/toolkit/tpicture.cpp b/3rdparty/taglib/toolkit/tpicture.cpp deleted file mode 100644 index 56ddf03f..00000000 --- a/3rdparty/taglib/toolkit/tpicture.cpp +++ /dev/null @@ -1,179 +0,0 @@ -/*************************************************************************** - copyright : (C) 2015 by Maxime Leblanc - email : lblnc.maxime@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include -#include - -#include "tpicture.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { -struct PictureData { - explicit PictureData() : type(Picture::Other) {} - - ByteVector data; - Picture::Type type; - String mime; - String description; -}; -} // namespace - -class Picture::PicturePrivate { - public: - explicit PicturePrivate() : data(new PictureData()) {} - - std::shared_ptr data; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -Picture::Picture() : d(new PicturePrivate()) {} - -Picture::Picture(const ByteVector &data, Type type, const String &mime, const String &description) : d(new PicturePrivate()) { - - d->data->data = data; - d->data->type = type; - d->data->mime = mime; - d->data->description = description; - -} - -Picture::Picture(const Picture &p) : d(new PicturePrivate(*p.d)) {} - -Picture::~Picture() { - delete d; -} - -String Picture::description() const { - return d->data->description; -} - -ByteVector Picture::data() const { - return d->data->data; -} - -String Picture::mime() const { - return d->data->mime; -} - -Picture::Type Picture::type() const { - return d->data->type; -} - -void Picture::swap(Picture &other) { - using std::swap; - - swap(d, other.d); -} - -/* =========== OPERATORS =========== */ - -Picture &Picture::operator=(const Picture &p) { - Picture(p).swap(*this); - return *this; -} - -std::ostream &operator<<(std::ostream &s, const Picture &p) { - - String type; - switch (p.type()) { - case Picture::FileIcon: - type = "FileIcon"; - break; - case Picture::OtherFileIcon: - type = "OtherFileIcon"; - break; - case Picture::FrontCover: - type = "FrontCover"; - break; - case Picture::BackCover: - type = "BackCover"; - break; - case Picture::LeafletPage: - type = "LeafletPage"; - break; - case Picture::Media: - type = "Media"; - break; - case Picture::LeadArtist: - type = "LeadArtist"; - break; - case Picture::Artist: - type = "Artist"; - break; - case Picture::Conductor: - type = "Conductor"; - break; - case Picture::Band: - type = "Band"; - break; - case Picture::Composer: - type = "Composer"; - break; - case Picture::Lyricist: - type = "Lyricist"; - break; - case Picture::RecordingLocation: - type = "RecordingLocation"; - break; - case Picture::DuringRecording: - type = "DuringRecording"; - break; - case Picture::DuringPerformance: - type = "DuringPerformance"; - break; - case Picture::MovieScreenCapture: - type = "MovieScreenCapture"; - break; - case Picture::ColouredFish: - type = "ColouredFish"; - break; - case Picture::Illustration: - type = "Illustration"; - break; - case Picture::BandLogo: - type = "BandLogo"; - break; - case Picture::PublisherLogo: - type = "PublisherLogo"; - break; - default: - type = "Other"; - break; - } - - ByteVector displayableData = p.data().mid(0, 20).toHex(); - s << "\nPicture:\n" - << "\ttype: " << type.to8Bit() << std::endl - << "\tdesc: " << p.description().to8Bit() << std::endl - << "\tmime: " << p.mime().to8Bit() << std::endl - << "\tdata: " << std::hex << displayableData << "..." << std::endl; - - return s; - -} diff --git a/3rdparty/taglib/toolkit/tpicture.h b/3rdparty/taglib/toolkit/tpicture.h deleted file mode 100644 index 9220c1cf..00000000 --- a/3rdparty/taglib/toolkit/tpicture.h +++ /dev/null @@ -1,157 +0,0 @@ -/*************************************************************************** - copyright : (C) 2015 by Maxime Leblanc - email : lblnc.maxime@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_PICTURE_H -#define TAGLIB_PICTURE_H - -#include "taglib_export.h" -#include "tbytevector.h" -#include "tmap.h" -#include "tstring.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -class TAGLIB_EXPORT Picture { - - class PicturePrivate; - - public: - /*! - * The Type enum is based on types in id3v2 tags - */ - enum Type { - //! A type not enumerated below - Other = 0x00, - //! 32x32 PNG image that should be used as the file icon - FileIcon = 0x01, - //! File icon of a different size or format - OtherFileIcon = 0x02, - //! Front cover image of the album - FrontCover = 0x03, - //! Back cover image of the album - BackCover = 0x04, - //! Inside leaflet page of the album - LeafletPage = 0x05, - //! Image from the album itself - Media = 0x06, - //! Picture of the lead artist or soloist - LeadArtist = 0x07, - //! Picture of the artist or performer - Artist = 0x08, - //! Picture of the conductor - Conductor = 0x09, - //! Picture of the band or orchestra - Band = 0x0A, - //! Picture of the composer - Composer = 0x0B, - //! Picture of the lyricist or text writer - Lyricist = 0x0C, - //! Picture of the recording location or studio - RecordingLocation = 0x0D, - //! Picture of the artists during recording - DuringRecording = 0x0E, - //! Picture of the artists during performance - DuringPerformance = 0x0F, - //! Picture from a movie or video related to the track - MovieScreenCapture = 0x10, - //! Picture of a large, coloured fish - ColouredFish = 0x11, - //! Illustration related to the track - Illustration = 0x12, - //! Logo of the band or performer - BandLogo = 0x13, - //! Logo of the publisher (record company) - PublisherLogo = 0x14 - }; - - /*! - * Constructs an empty Picture. - */ - explicit Picture(); - - /*! - * Constructs a Picture object base on an other Picture - */ - Picture(const Picture &p); - - /*! - * Constructs a Picture object based on the \a ByteVector given. - * - * \note type, mime and description are optional - */ - explicit Picture(const ByteVector &data, Type type = Other, const String &mime = "image/", const String &description = String()); - - /*! - * Destroys this Picture instance. - */ - virtual ~Picture(); - - /*! - * Returns the mime of the picture - */ - String mime() const; - - /*! - * Returns the description of the picture - */ - String description() const; - - /*! - * Returns the type of the picture - */ - Type type() const; - - /*! - * Returns data of the picture - */ - ByteVector data() const; - - /*! - * Performs a shallow, implicitly shared, copy of \a p, overwriting the - * Picture's current data. - */ - Picture &operator=(const Picture &p); - - /*! - * Exchanges the content of the Picture by the content of \a p. - */ - void swap(Picture &p); - - private: - PicturePrivate *d; -}; - -} // namespace TagLib -} // namespace Strawberry_TagLib - -/*! - * \relates TagLib::Picture - * - * Send the picture to an output stream. - */ -TAGLIB_EXPORT std::ostream &operator<<(std::ostream &s, const Strawberry_TagLib::TagLib::Picture &picture); - -#endif // TAGLIB_PICTUREMAP_H diff --git a/3rdparty/taglib/toolkit/tpicturemap.cpp b/3rdparty/taglib/toolkit/tpicturemap.cpp deleted file mode 100644 index db308384..00000000 --- a/3rdparty/taglib/toolkit/tpicturemap.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/*************************************************************************** - copyright : (C) 2015 by Maxime Leblanc - email : lblnc.maxime@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tpicturemap.h" - -using namespace Strawberry_TagLib::TagLib; - -PictureMap::PictureMap() : Map() {} - -PictureMap::PictureMap(const PictureList &l) : Map() { - insert(l); -} - -PictureMap::PictureMap(const Picture &p) : Map() { - insert(p); -} - -void PictureMap::insert(const Picture &p) { - - PictureList list; - if (contains(p.type())) { - list = Map::find(p.type())->second; - list.append(p); - Map::insert(p.type(), list); - } - else { - list.append(p); - Map::insert(p.type(), list); - } - -} - -void PictureMap::insert(const PictureList &l) { - - for (PictureList::ConstIterator it = l.begin(); it != l.end(); ++it) { - Picture picture = (*it); - insert(picture); - } - -} - -PictureMap::~PictureMap() {} - -std::ostream &operator<<(std::ostream &s, const PictureMap &map) { - - for (PictureMap::ConstIterator it = map.begin(); it != map.end(); ++it) { - PictureList list = it->second; - for (PictureList::ConstIterator it2 = list.begin(); it2 != list.end(); ++it2) - s << *it2; - } - return s; - -} diff --git a/3rdparty/taglib/toolkit/tpicturemap.h b/3rdparty/taglib/toolkit/tpicturemap.h deleted file mode 100644 index 18cdaec9..00000000 --- a/3rdparty/taglib/toolkit/tpicturemap.h +++ /dev/null @@ -1,89 +0,0 @@ -/*************************************************************************** - copyright : (C) 2015 by Maxime Leblanc - email : lblnc.maxime@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_PICTUREMAP_H -#define TAGLIB_PICTUREMAP_H - -#include "tlist.h" -#include "tmap.h" -#include "taglib_export.h" -#include "tpicture.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -//! A list of pictures -typedef List PictureList; - -/// TODO: review this interface before the release of TagLib v2.x in light of -/// https://github.com/taglib/taglib/issues/734#issuecomment-214001325 - -/*! - * This is a spcialization of the List class with some members. - */ -class TAGLIB_EXPORT PictureMap : public Map { - public: - /*! - * Constructs an empty PictureList. - */ - explicit PictureMap(); - - /*! - * Constructs a PictureMap with \a Picture. - */ - explicit PictureMap(const Picture &p); - - /*! - * Constructs a PictureMap with \a PictureList as a member. - */ - explicit PictureMap(const PictureList &l); - - /*! - * Destroys this PictureList instance. - */ - ~PictureMap() override; - - /*! - * Inserts a PictureList into the picture map - */ - void insert(const PictureList &l); - - /*! - * Inserts a Picture into the picture map - */ - void insert(const Picture &p); -}; - -} // namespace TagLib -} // namespace Strawberry_TagLib - -/*! - * \relates TagLib::PictureMap - * - * Send the PictureMap to on output stream - */ -TAGLIB_EXPORT std::ostream &operator<<(std::ostream &s, const Strawberry_TagLib::TagLib::PictureMap &map); - -#endif // TAGLIB_PICTUREMAP_H diff --git a/3rdparty/taglib/toolkit/tpropertymap.cpp b/3rdparty/taglib/toolkit/tpropertymap.cpp deleted file mode 100644 index 426b5a28..00000000 --- a/3rdparty/taglib/toolkit/tpropertymap.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/*************************************************************************** - copyright : (C) 2012 by Michael Helmling - email : helmling@mathematik.uni-kl.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 "tpropertymap.h" -using namespace Strawberry_TagLib::TagLib; - - -PropertyMap::PropertyMap() {} - -PropertyMap::PropertyMap(const SimplePropertyMap &m) { - - for (SimplePropertyMap::ConstIterator it = m.begin(); it != m.end(); ++it) { - String key = it->first.upper(); - if (!key.isEmpty()) - insert(it->first, it->second); - else - unsupported.append(it->first); - } -} - -PropertyMap::~PropertyMap() {} - -bool PropertyMap::insert(const String &key, const StringList &values) { - - String realKey = key.upper(); - Iterator result = SimplePropertyMap::find(realKey); - if (result == end()) - SimplePropertyMap::insert(realKey, values); - else - SimplePropertyMap::operator[](realKey).append(values); - return true; - -} - -bool PropertyMap::replace(const String &key, const StringList &values) { - - String realKey = key.upper(); - SimplePropertyMap::erase(realKey); - SimplePropertyMap::insert(realKey, values); - return true; - -} - -PropertyMap::Iterator PropertyMap::find(const String &key) { - return SimplePropertyMap::find(key.upper()); -} - -PropertyMap::ConstIterator PropertyMap::find(const String &key) const { - return SimplePropertyMap::find(key.upper()); -} - -bool PropertyMap::contains(const String &key) const { - return SimplePropertyMap::contains(key.upper()); -} - -bool PropertyMap::contains(const PropertyMap &other) const { - - for (ConstIterator it = other.begin(); it != other.end(); ++it) { - if (!SimplePropertyMap::contains(it->first)) - return false; - if ((*this)[it->first] != it->second) - return false; - } - - return true; - -} - -PropertyMap &PropertyMap::erase(const String &key) { - SimplePropertyMap::erase(key.upper()); - return *this; -} - -PropertyMap &PropertyMap::erase(const PropertyMap &other) { - for (ConstIterator it = other.begin(); it != other.end(); ++it) - erase(it->first); - return *this; -} - -PropertyMap &PropertyMap::merge(const PropertyMap &other) { - - for (PropertyMap::ConstIterator it = other.begin(); it != other.end(); ++it) - insert(it->first, it->second); - unsupported.append(other.unsupported); - return *this; - -} - -const StringList &PropertyMap::operator[](const String &key) const { - return SimplePropertyMap::operator[](key.upper()); -} - -StringList &PropertyMap::operator[](const String &key) { - return SimplePropertyMap::operator[](key.upper()); -} - -bool PropertyMap::operator==(const PropertyMap &other) const { - - for (ConstIterator it = other.begin(); it != other.end(); ++it) { - ConstIterator thisFind = find(it->first); - if (thisFind == end() || (thisFind->second != it->second)) - return false; - } - for (ConstIterator it = begin(); it != end(); ++it) { - ConstIterator otherFind = other.find(it->first); - if (otherFind == other.end() || (otherFind->second != it->second)) - return false; - } - return unsupported == other.unsupported; - -} - -bool PropertyMap::operator!=(const PropertyMap &other) const { - return !(*this == other); -} - -String PropertyMap::toString() const { - - String ret; - - for (ConstIterator it = begin(); it != end(); ++it) - ret += it->first + "=" + it->second.toString(", ") + "\n"; - if (!unsupported.isEmpty()) - ret += "Unsupported Data: " + unsupported.toString(", ") + "\n"; - return ret; - -} - -void PropertyMap::removeEmpty() { - - PropertyMap m; - for (ConstIterator it = begin(); it != end(); ++it) { - if (!it->second.isEmpty()) - m.insert(it->first, it->second); - } - *this = m; - -} - -StringList &PropertyMap::unsupportedData() { - return unsupported; -} - -const StringList &PropertyMap::unsupportedData() const { - return unsupported; -} diff --git a/3rdparty/taglib/toolkit/tpropertymap.h b/3rdparty/taglib/toolkit/tpropertymap.h deleted file mode 100644 index 9cd984b3..00000000 --- a/3rdparty/taglib/toolkit/tpropertymap.h +++ /dev/null @@ -1,222 +0,0 @@ -/*************************************************************************** - copyright : (C) 2012 by Michael Helmling - email : helmling@mathematik.uni-kl.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_PROPERTYMAP_H_ -#define TAGLIB_PROPERTYMAP_H_ - -#include "tmap.h" -#include "tstringlist.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -typedef Map SimplePropertyMap; - -//! A map for format-independent tag representations. - -/*! - * This map implements a generic representation of textual audio metadata ("tags") realized as pairs of a case-insensitive key and a nonempty list of corresponding values, - * each value being an arbitrary unicode String. - * - * Note that most metadata formats pose additional conditions on the tag keys. - * The most popular ones (Vorbis, APE, ID3v2) should support all ASCII only words of length between 2 and 16. - * - * This class can contain any tags, but here is a list of "well-known" tags that you might want to use: - * - * Basic tags: - * - * - TITLE - * - ALBUM - * - ARTIST - * - ALBUMARTIST - * - SUBTITLE - * - TRACKNUMBER - * - DISCNUMBER - * - DATE - * - ORIGINALDATE - * - GENRE - * - COMMENT - * - * Sort names: - * - * - TITLESORT - * - ALBUMSORT - * - ARTISTSORT - * - ALBUMARTISTSORT - * - * Credits: - * - * - COMPOSER - * - LYRICIST - * - CONDUCTOR - * - REMIXER - * - PERFORMER: - * - * Other tags: - * - * - ISRC - * - ASIN - * - BPM - * - COPYRIGHT - * - ENCODEDBY - * - MOOD - * - COMMENT - * - MEDIA - * - LABEL - * - CATALOGNUMBER - * - BARCODE - * - * MusicBrainz identifiers: - * - * - MUSICBRAINZ_TRACKID - * - MUSICBRAINZ_ALBUMID - * - MUSICBRAINZ_RELEASEGROUPID - * - MUSICBRAINZ_WORKID - * - MUSICBRAINZ_ARTISTID - * - MUSICBRAINZ_ALBUMARTISTID - * - ACOUSTID_ID - * - ACOUSTID_FINGERPRINT - * - MUSICIP_PUID - * - */ - -class TAGLIB_EXPORT PropertyMap : public SimplePropertyMap { - public: - typedef SimplePropertyMap::Iterator Iterator; - typedef SimplePropertyMap::ConstIterator ConstIterator; - - explicit PropertyMap(); - - /*! - * Creates a PropertyMap initialized from a SimplePropertyMap. - * Copies all entries from \a m that have valid keys. - * Invalid keys will be appended to the unsupportedData() list. - */ - PropertyMap(const SimplePropertyMap &m); - - ~PropertyMap() override; - - /*! - * Inserts \a values under \a key in the map. - * If \a key already exists, then \a values will be appended to the existing StringList. - * The returned value indicates success, i.e. whether \a key is a valid key. - */ - bool insert(const String &key, const StringList &values); - - /*! - * Replaces any existing values for \a key with the given \a values, and simply insert them if \a key did not exist before. - * The returned value indicates success, i.e. whether \a key is a valid key. - */ - bool replace(const String &key, const StringList &values); - - /*! - * Find the first occurrence of \a key. - */ - Iterator find(const String &key); - - /*! - * Find the first occurrence of \a key. - */ - ConstIterator find(const String &key) const; - - /*! - * Returns true if the map contains values for \a key. - */ - bool contains(const String &key) const; - - /*! - * Returns true if this map contains all keys of \a other and the values coincide for that keys. - * Does not take the unsupportedData list into account. - */ - bool contains(const PropertyMap &other) const; - - /*! - * Erase the \a key and its values from the map. - */ - PropertyMap &erase(const String &key); - - /*! - * Erases from this map all keys that appear in \a other. - */ - PropertyMap &erase(const PropertyMap &other); - - /*! - * Merge the contents of \a other into this PropertyMap. - * If a key is contained in both maps, the values of the second are appended to that of the first. - * The unsupportedData() lists are concatenated as well. - */ - PropertyMap &merge(const PropertyMap &other); - - /*! - * Returns a reference to the value associated with \a key. - * - * \note: If \a key is not contained in the map, an empty StringList is returned without error. - */ - const StringList &operator[](const String &key) const; - - /*! - * Returns a reference to the value associated with \a key. - * - * \note: If \a key is not contained in the map, an empty StringList is returned. - * You can also directly add entries by using this function as an lvalue. - */ - StringList &operator[](const String &key); - - /*! - * Returns true if and only if \other has the same contents as this map. - */ - bool operator==(const PropertyMap &other) const; - - /*! - * Returns false if and only \other has the same contents as this map. - */ - bool operator!=(const PropertyMap &other) const; - - /*! - * If a PropertyMap is read from a File object using File::properties(), - * the StringList returned from this function will represent metadata - * that could not be parsed into the PropertyMap representation. - * This could be e.g. binary data, unknown ID3 frames, etc. - * You can remove items from the returned list, which tells TagLib to remove - * those unsupported elements if you call File::setProperties() with the - * same PropertyMap as argument. - */ - StringList &unsupportedData(); - const StringList &unsupportedData() const; - - /*! - * Removes all entries which have an empty value list. - */ - void removeEmpty(); - - String toString() const; - - private: - StringList unsupported; -}; - -} // namespace TagLib -} // namespace Strawberry_TagLib -#endif /* TAGLIB_PROPERTYMAP_H_ */ diff --git a/3rdparty/taglib/toolkit/trefcounter.cpp b/3rdparty/taglib/toolkit/trefcounter.cpp deleted file mode 100644 index 13e7d4b0..00000000 --- a/3rdparty/taglib/toolkit/trefcounter.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/*************************************************************************** - copyright : (C) 2013 by Tsuda Kageyu - email : tsuda.kageyu@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "taglib-config.h" - -#include "trefcounter.h" - -#if defined(HAVE_STD_ATOMIC) -# include -# define ATOMIC_INT std::atomic_int -# define ATOMIC_INC(x) (++(x)) -# define ATOMIC_DEC(x) (--(x)) -#elif defined(HAVE_GCC_ATOMIC) -# define ATOMIC_INT int -# define ATOMIC_INC(x) ::__sync_add_and_fetch(&x, 1) -# define ATOMIC_DEC(x) ::__sync_sub_and_fetch(&x, 1) -#elif defined(HAVE_WIN_ATOMIC) -# if !defined(NOMINMAX) -# define NOMINMAX -# endif -# include -# define ATOMIC_INT volatile LONG -# define ATOMIC_INC(x) ::InterlockedIncrement(&x) -# define ATOMIC_DEC(x) ::InterlockedDecrement(&x) -#elif defined(HAVE_MAC_ATOMIC) -# include -# define ATOMIC_INT int32_t -# define ATOMIC_INC(x) ::OSAtomicIncrement32Barrier(&x) -# define ATOMIC_DEC(x) ::OSAtomicDecrement32Barrier(&x) -#elif defined(HAVE_IA64_ATOMIC) -# include -# define ATOMIC_INT int -# define ATOMIC_INC(x) ::__sync_add_and_fetch(&x, 1) -# define ATOMIC_DEC(x) ::__sync_sub_and_fetch(&x, 1) -#else -# define ATOMIC_INT int -# define ATOMIC_INC(x) (++(x)) -# define ATOMIC_DEC(x) (--(x)) -#endif - -namespace Strawberry_TagLib { -namespace TagLib { - -class RefCounter::RefCounterPrivate { - public: - RefCounterPrivate() : refCount(1) {} - - ATOMIC_INT refCount; -}; - -RefCounter::RefCounter() : d(new RefCounterPrivate()) {} - -RefCounter::~RefCounter() { - delete d; -} - -void RefCounter::ref() { - ATOMIC_INC(d->refCount); -} - -bool RefCounter::deref() { - return (ATOMIC_DEC(d->refCount) == 0); -} - -int RefCounter::count() const { - return static_cast(d->refCount); -} - -bool RefCounter::unique() const { - return (d->refCount == 1); -} -} // namespace TagLib -} // namespace Strawberry_TagLib diff --git a/3rdparty/taglib/toolkit/trefcounter.h b/3rdparty/taglib/toolkit/trefcounter.h deleted file mode 100644 index 26b7c980..00000000 --- a/3rdparty/taglib/toolkit/trefcounter.h +++ /dev/null @@ -1,64 +0,0 @@ -/*************************************************************************** - copyright : (C) 2013 by Tsuda Kageyu - email : tsuda.kageyu@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_REFCOUNTER_H -#define TAGLIB_REFCOUNTER_H - -#include "taglib_export.h" -#include "taglib.h" - -#ifndef DO_NOT_DOCUMENT // Tell Doxygen to skip this class. -/*! - * \internal - * This is just used as a base class for shared classes in TagLib. - * - * \warning This is not part of the TagLib public API! - */ -namespace Strawberry_TagLib { -namespace TagLib { - -class TAGLIB_EXPORT RefCounter { - public: - explicit RefCounter(); - virtual ~RefCounter(); - - void ref(); - bool deref(); - int count() const; - bool unique() const; - - private: - RefCounter(const RefCounter&); - RefCounter &operator=(const RefCounter&); - - class RefCounterPrivate; - RefCounterPrivate *d; -}; - -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif // DO_NOT_DOCUMENT -#endif diff --git a/3rdparty/taglib/toolkit/tstring.cpp b/3rdparty/taglib/toolkit/tstring.cpp deleted file mode 100644 index 2606225c..00000000 --- a/3rdparty/taglib/toolkit/tstring.cpp +++ /dev/null @@ -1,693 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include -#include -#include - -#include - -#include "tdebug.h" -#include "tstringlist.h" -#include "tutils.h" - -#include "tstring.h" - -namespace { -using namespace Strawberry_TagLib::TagLib; - -// Returns the native format of std::wstring. -String::Type wcharByteOrder() { - - if (Utils::systemByteOrder() == LittleEndian) - return String::UTF16LE; - else - return String::UTF16BE; - -} - -// Converts a Latin-1 string into UTF-16(without BOM/CPU byte order) -// and copies it to the internal buffer. -void copyFromLatin1(std::wstring &data, const char *s, size_t length) { - - data.resize(length); - - for (size_t i = 0; i < length; ++i) - data[i] = static_cast(s[i]); - -} - -// Converts a UTF-8 string into UTF-16(without BOM/CPU byte order) -// and copies it to the internal buffer. -void copyFromUTF8(std::wstring &data, const char *s, size_t length) { - - data.resize(length); - - try { - const std::wstring::iterator dstEnd = utf8::utf8to16(s, s + length, data.begin()); - data.resize(dstEnd - data.begin()); - } - catch (const utf8::exception &e) { - const String message(e.what()); - debug("String::copyFromUTF8() - UTF8-CPP error: " + message); - data.clear(); - } - -} - -// Helper functions to read a UTF-16 character from an array. -template -unsigned short nextUTF16(const T **p); - -template<> -unsigned short nextUTF16(const wchar_t **p) { - return static_cast(*(*p)++); -} - -template<> -unsigned short nextUTF16(const char **p) { - - union { - unsigned short w; - char c[2]; - } u; - u.c[0] = *(*p)++; - u.c[1] = *(*p)++; - return u.w; - -} - -// Converts a UTF-16 (with BOM), UTF-16LE or UTF16-BE string into -// UTF-16(without BOM/CPU byte order) and copies it to the internal buffer. -template -void copyFromUTF16(std::wstring &data, const T *s, size_t length, String::Type t) { - - bool swap; - if (t == String::UTF16) { - if (length < 1) { - debug("String::copyFromUTF16() - Invalid UTF16 string. Too short to have a BOM."); - return; - } - - const unsigned short bom = nextUTF16(&s); - if (bom == 0xfeff) - swap = false; // Same as CPU endian. No need to swap bytes. - else if (bom == 0xfffe) - swap = true; // Not same as CPU endian. Need to swap bytes. - else { - debug("String::copyFromUTF16() - Invalid UTF16 string. BOM is broken."); - return; - } - - length--; - } - else { - swap = (t != wcharByteOrder()); - } - - data.resize(length); - for (size_t i = 0; i < length; ++i) { - const unsigned short c = nextUTF16(&s); - if (swap) - data[i] = Utils::byteSwap(c); - else - data[i] = c; - } - -} - -} // namespace - -namespace Strawberry_TagLib { -namespace TagLib { - -class String::StringPrivate { - public: - StringPrivate() : data(new std::wstring()) {} - - /*! - * Stores string in UTF-16. The byte order depends on the CPU endian. - */ - std::shared_ptr data; - - /*! - * This is only used to hold the the most recent value of toCString(). - */ - std::shared_ptr cstring; -}; - -//////////////////////////////////////////////////////////////////////////////// -// static members -//////////////////////////////////////////////////////////////////////////////// - -size_t String::npos() { - return std::wstring::npos; -} - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -String::String() : d(new StringPrivate()) { -} - -String::String(const String &s) : d(new StringPrivate(*s.d)) {} - -String::String(const std::string &s, Type t) : d(new StringPrivate()) { - - if (t == Latin1) - copyFromLatin1(*d->data, s.c_str(), s.length()); - else if (t == String::UTF8) - copyFromUTF8(*d->data, s.c_str(), s.length()); - else { - debug("String::String() -- std::string should not contain UTF16."); - - } -} - -String::String(const std::wstring &s, Type t) : d(new StringPrivate()) { - - if (t == UTF16Native) - t = wcharByteOrder(); - - if (t == UTF16 || t == UTF16BE || t == UTF16LE) - copyFromUTF16(*d->data, s.c_str(), s.length(), t); - else { - debug("String::String() -- std::wstring should not contain Latin1 or UTF-8."); - } - -} - -String::String(const wchar_t *s, Type t) : d(new StringPrivate()) { - - if (t == UTF16Native) - t = wcharByteOrder(); - - if (t == UTF16 || t == UTF16BE || t == UTF16LE) - copyFromUTF16(*d->data, s, ::wcslen(s), t); - else { - debug("String::String() -- const wchar_t * should not contain Latin1 or UTF-8."); - } - -} - -String::String(const char *s, Type t) : d(new StringPrivate()) { - - if (t == Latin1) - copyFromLatin1(*d->data, s, ::strlen(s)); - else if (t == String::UTF8) - copyFromUTF8(*d->data, s, ::strlen(s)); - else { - debug("String::String() -- const char * should not contain UTF16."); - } - -} - -String::String(wchar_t c, Type t) : d(new StringPrivate()) { - - if (t == UTF16Native) - t = wcharByteOrder(); - - if (t == UTF16 || t == UTF16BE || t == UTF16LE) - copyFromUTF16(*d->data, &c, 1, t); - else { - debug("String::String() -- wchar_t should not contain Latin1 or UTF-8."); - } - -} - -String::String(char c, Type t) : d(new StringPrivate()) { - - if (t == Latin1) - copyFromLatin1(*d->data, &c, 1); - else if (t == String::UTF8) - copyFromUTF8(*d->data, &c, 1); - else { - debug("String::String() -- char should not contain UTF16."); - } - -} - -String::String(const ByteVector &v, Type t) : d(new StringPrivate()) { - - if (v.isEmpty()) - return; - - if (t == UTF16Native) - t = wcharByteOrder(); - - if (t == Latin1) - copyFromLatin1(*d->data, v.data(), v.size()); - else if (t == UTF8) - copyFromUTF8(*d->data, v.data(), v.size()); - else - copyFromUTF16(*d->data, v.data(), v.size() / 2, t); - - // If we hit a null in the ByteVector, shrink the string again. - d->data->resize(::wcslen(d->data->c_str())); - -} - -//////////////////////////////////////////////////////////////////////////////// - -String::~String() { - delete d; -} - -std::string String::to8Bit(bool unicode) const { - - const ByteVector v = data(unicode ? UTF8 : Latin1); - return std::string(v.data(), v.size()); - -} - -const std::wstring &String::toWString() const { - return *d->data; -} - -const char *String::toCString(bool unicode) const { - d->cstring.reset(new std::string(to8Bit(unicode))); - return d->cstring->c_str(); -} - -const wchar_t *String::toCWString() const { - return d->data->c_str(); -} - -String::Iterator String::begin() { - detach(); - return d->data->begin(); -} - -String::ConstIterator String::begin() const { - return d->data->begin(); -} - -String::Iterator String::end() { - detach(); - return d->data->end(); -} - -String::ConstIterator String::end() const { - return d->data->end(); -} - -size_t String::find(const String &s, size_t offset) const { - return d->data->find(*s.d->data, offset); -} - -size_t String::rfind(const String &s, size_t offset) const { - return d->data->rfind(*s.d->data, offset); -} - -StringList String::split(const String &separator) const { - - StringList list; - for (size_t index = 0;;) { - const size_t sep = find(separator, index); - if (sep == npos()) { - list.append(substr(index, size() - index)); - break; - } - else { - list.append(substr(index, sep - index)); - index = sep + separator.size(); - } - } - return list; - -} - -bool String::startsWith(const String &s) const { - if (s.length() > length()) - return false; - - return substr(0, s.length()) == s; -} - -String String::substr(size_t position, size_t length) const { - - if (position == 0 && length >= size()) - return *this; - else - return String(d->data->substr(position, length)); -} - -String &String::append(const String &s) { - - detach(); - *d->data += *s.d->data; - return *this; - -} - -String &String::clear() { - - *this = String(); - return *this; - -} - -String String::upper() const { - - String s; - s.d->data->reserve(size()); - - for (ConstIterator it = begin(); it != end(); ++it) { - if (*it >= 'a' && *it <= 'z') - s.d->data->push_back(*it + 'A' - 'a'); - else - s.d->data->push_back(*it); - } - - return s; - -} - -size_t String::size() const { - return d->data->size(); -} - -size_t String::length() const { - return size(); -} - -bool String::isEmpty() const { - return d->data->empty(); -} - -ByteVector String::data(Type t) const { - - switch (t) { - case Latin1: { - ByteVector v(size(), 0); - char *p = v.data(); - - for (ConstIterator it = begin(); it != end(); ++it) - *p++ = static_cast(*it); - - return v; - } - case UTF8: { - ByteVector v(size() * 4, 0); - - try { - const ByteVector::Iterator dstEnd = utf8::utf16to8(begin(), end(), v.begin()); - v.resize(static_cast(dstEnd - v.begin())); - } catch (const utf8::exception &e) { - const String message(e.what()); - debug("String::data() - UTF8-CPP error: " + message); - v.clear(); - } - - return v; - } - case UTF16: { - ByteVector v(2 + size() * 2, 0); - char *p = v.data(); - - // We use little-endian encoding here and need a BOM. - - *p++ = '\xff'; - *p++ = '\xfe'; - - for (ConstIterator it = begin(); it != end(); ++it) { - *p++ = static_cast(*it & 0xff); - *p++ = static_cast(*it >> 8); - } - - return v; - } - case UTF16BE: { - ByteVector v(size() * 2, 0); - char *p = v.data(); - - for (ConstIterator it = begin(); it != end(); ++it) { - *p++ = static_cast(*it >> 8); - *p++ = static_cast(*it & 0xff); - } - - return v; - } - case UTF16LE: { - ByteVector v(size() * 2, 0); - char *p = v.data(); - - for (ConstIterator it = begin(); it != end(); ++it) { - *p++ = static_cast(*it & 0xff); - *p++ = static_cast(*it >> 8); - } - - return v; - } - default: { - debug("String::data() - Invalid Type value."); - return ByteVector(); - } - } - -} - -int String::toInt(bool *ok) const { - - const wchar_t *begin = d->data->c_str(); - wchar_t *end; - errno = 0; - const long value = ::wcstol(begin, &end, 10); - - // Has wcstol() consumed the entire string and not overflowed? - if (ok) { - *ok = (errno == 0 && end > begin && *end == L'\0'); - *ok = (*ok && value > INT_MIN && value < INT_MAX); - } - - return static_cast(value); - -} - -String String::stripWhiteSpace() const { - - static const wchar_t *WhiteSpaceChars = L"\t\n\f\r "; - - const size_t pos1 = d->data->find_first_not_of(WhiteSpaceChars); - if (pos1 == std::wstring::npos) - return String(); - - const size_t pos2 = d->data->find_last_not_of(WhiteSpaceChars); - return substr(pos1, pos2 - pos1 + 1); - -} - -bool String::isLatin1() const { - - for (ConstIterator it = begin(); it != end(); ++it) { - if (*it >= 256) - return false; - } - return true; - -} - -bool String::isAscii() const { - - for (ConstIterator it = begin(); it != end(); ++it) { - if (*it >= 128) - return false; - } - return true; - -} - -String String::number(int n) { // static - return Utils::formatString("%d", n); -} - -wchar_t &String::operator[](size_t i) { - detach(); - return (*d->data)[i]; -} - -const wchar_t &String::operator[](size_t i) const { - return (*d->data)[i]; -} - -bool String::operator==(const String &s) const { - return (d->data == s.d->data || *d->data == *s.d->data); -} - -bool String::operator!=(const String &s) const { - return !(*this == s); -} - -bool String::operator==(const char *s) const { - - const wchar_t *p = toCWString(); - - while (*p != L'\0' || *s != '\0') { - if (*p++ != static_cast(*s++)) - return false; - } - return true; - -} - -bool String::operator!=(const char *s) const { - return !(*this == s); -} - -bool String::operator==(const wchar_t *s) const { - return (*d->data == s); -} - -bool String::operator!=(const wchar_t *s) const { - return !(*this == s); -} - -String &String::operator+=(const String &s) { - detach(); - - *d->data += *s.d->data; - return *this; -} - -String &String::operator+=(const wchar_t *s) { - detach(); - - *d->data += s; - return *this; -} - -String &String::operator+=(const char *s) { - detach(); - - for (int i = 0; s[i] != 0; i++) - *d->data += static_cast(s[i]); - - return *this; -} - -String &String::operator+=(wchar_t c) { - detach(); - - *d->data += c; - return *this; -} - -String &String::operator+=(char c) { - detach(); - - *d->data += static_cast(c); - return *this; -} - -String &String::operator=(const String &s) { - String(s).swap(*this); - return *this; -} - -String &String::operator=(const std::string &s) { - String(s).swap(*this); - return *this; -} - -String &String::operator=(const std::wstring &s) { - String(s).swap(*this); - return *this; -} - -String &String::operator=(const wchar_t *s) { - String(s).swap(*this); - return *this; -} - -String &String::operator=(char c) { - String(c).swap(*this); - return *this; -} - -String &String::operator=(wchar_t c) { - String(c).swap(*this); - return *this; -} - -String &String::operator=(const char *s) { - String(s).swap(*this); - return *this; -} - -String &String::operator=(const ByteVector &v) { - String(v).swap(*this); - return *this; -} - -void String::swap(String &s) { - using std::swap; - - swap(d, s.d); -} - -bool String::operator<(const String &s) const { - return (*d->data < *s.d->data); -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void String::detach() { - if (!d->data.unique()) - String(d->data->c_str()).swap(*this); -} - -//////////////////////////////////////////////////////////////////////////////// -// related non-member functions -//////////////////////////////////////////////////////////////////////////////// - -const String operator+(const String &s1, const String &s2) { - Strawberry_TagLib::TagLib::String s(s1); - s.append(s2); - return s; -} - -const String operator+(const char *s1, const String &s2) { - Strawberry_TagLib::TagLib::String s(s1); - s.append(s2); - return s; -} - -const String operator+(const String &s1, const char *s2) { - Strawberry_TagLib::TagLib::String s(s1); - s.append(s2); - return s; -} - -std::ostream &operator<<(std::ostream &s, const Strawberry_TagLib::TagLib::String &str) { - s << str.to8Bit(); - return s; -} - -} // namespace TagLib -} // namespace Strawberry_TagLib - diff --git a/3rdparty/taglib/toolkit/tstring.h b/3rdparty/taglib/toolkit/tstring.h deleted file mode 100644 index b0941f57..00000000 --- a/3rdparty/taglib/toolkit/tstring.h +++ /dev/null @@ -1,521 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_STRING_H -#define TAGLIB_STRING_H - -#include "taglib_export.h" -#include "taglib.h" -#include "tbytevector.h" - -#include -#include - -/*! - * \relates TagLib::String - * - * Converts a QString to a TagLib::String without a requirement to link to Qt. - * - * \note consider conversion via usual char-by-char for loop to avoid UTF16->UTF8->UTF16 - * conversion happening in the background - */ - -#if defined(QT_VERSION) && (QT_VERSION >= 0x040000) -# define QStringToTString(s) Strawberry_TagLib::TagLib::String((s).toUtf8().data(), Strawberry_TagLib::TagLib::String::UTF8) -#else -# define QStringToTString(s) Strawberry_TagLib::TagLib::String((s).utf8().data(), Strawberry_TagLib::TagLib::String::UTF8) -#endif - -/*! - * \relates TagLib::String - * - * Converts a TagLib::String to a QString without a requirement to link to Qt. - * - * \note consider conversion via usual char-by-char for loop to avoid UTF16->UTF8->UTF16 - * conversion happening in the background - * - */ - -#define TStringToQString(s) QString::fromUtf8((s).toCString(true)) - -namespace Strawberry_TagLib { -namespace TagLib { - -class StringList; - -//! A \e wide string class suitable for unicode. - -/*! - * This is an implicitly shared \e wide string. - * For storage it uses std::wstring, but as this is an implementation detail this of course could change. - * Strings are stored internally as UTF-16(without BOM/ CPU byte order) - * - * The use of implicit sharing means that copying a string is cheap, the only \e cost comes into play when the copy is modified. - * Prior to that the string just has a pointer to the data of the \e parent String. - * This also makes this class suitable as a function return type. - * - * In addition to adding implicit sharing, this class keeps track of four - * possible encodings, which are the four supported by the ID3v2 standard. - */ - -class TAGLIB_EXPORT String { - public: -#ifndef DO_NOT_DOCUMENT - typedef std::wstring::iterator Iterator; - typedef std::wstring::const_iterator ConstIterator; -#endif - - /** - * The four types of string encodings supported by the ID3v2 specification. - * ID3v1 is assumed to be Latin1 and Ogg Vorbis comments use UTF8. - */ - enum Type { - /*! - * IS08859-1, or Latin1 encoding. 8 bit characters. - */ - Latin1 = 0, - /*! - * UTF16 with a byte order mark. 16 bit characters. - */ - UTF16 = 1, - /*! - * UTF16 big endian. 16 bit characters. - */ - UTF16BE = 2, - /*! - * UTF8 encoding. Characters are usually 8 bits but can be up to 32. - */ - UTF8 = 3, - /*! - * UTF16 little endian. 16 bit characters. - */ - UTF16LE = 4, - /*! - * UTF16 in the native byte order of the system. 16 bit characters. - */ - UTF16Native = 5 - }; - - /*! - * Constructs an empty String. - */ - explicit String(); - - /*! - * Make a shallow, implicitly shared, copy of \a s. - * Because this is implicitly shared, this method is lightweight and suitable for pass-by-value usage. - */ - String(const String &s); - - /*! - * Makes a deep copy of the data in \a s. - * - * \note This should only be used with the 8-bit codecs Latin1 and UTF8, when used with other codecs it will simply print a warning and exit. - */ - String(const std::string &s, Type t = Latin1); - - /*! - * Makes a deep copy of the data in \a s. - * - * \note This should only be used with the 16-bit codecs UTF16, UTF16BE or UTF16LE, - * when used with other codecs it will simply print a warning and exit. - * UTF16BE or UTF16LE is automatically chosen as default according to the CPU byte order - */ - String(const std::wstring &s, Type t = UTF16Native); - - /*! - * Makes a deep copy of the data in \a s. - * - * \note This should only be used with the 16-bit codecs UTF16, UTF16BE or UTF16LE, - * when used with other codecs it will simply print a warning and exit. - * UTF16BE or UTF16LE is automatically chosen as default according to the CPU byte order - */ - String(const wchar_t *s, Type t = UTF16Native); - - /*! - * Makes a deep copy of the data in \a c. - * - * \note This should only be used with the 8-bit codecs Latin1 and UTF8, when used with other codecs it will simply print a warning and exit. - */ - String(char c, Type t = Latin1); - - /*! - * Makes a deep copy of the data in \a c. - * - * \note This should only be used with the 16-bit codecs UTF16, UTF16BE or UTF16LE, - * when used with other codecs it will simply print a warning and exit. - * UTF16BE or UTF16LE is automatically chosen as default according to the CPU byte order - */ - String(wchar_t c, Type t = UTF16Native); - - /*! - * Makes a deep copy of the data in \a s. - * - * \note This should only be used with the 8-bit codecs Latin1 and UTF8, when used with other codecs it will simply print a warning and exit. - */ - String(const char *s, Type t = Latin1); - - /*! - * Makes a deep copy of the data in \a v. - */ - String(const ByteVector &v, Type t = Latin1); - - /*! - * Destroys this String instance. - */ - virtual ~String(); - - /*! - * Returns a deep copy of this String as an std::string. - * The returned string is encoded in UTF8 if \a unicode is true, otherwise Latin1. - * - * \see toCString() - */ - std::string to8Bit(bool unicode = false) const; - - /*! - * Returns a deep copy of this String as a wstring. - * The returned string is encoded in UTF-16 (without BOM/CPU byte order), not UTF-32 even if wchar_t is 32-bit wide. - * - * \see toCWString() - */ - const std::wstring &toWString() const; - - /*! - * Creates and returns a standard C-style (null-terminated) version of this String. - * The returned string is encoded in UTF8 if \a unicode is true, otherwise Latin1. - * - * The returned string is still owned by this String and should not be deleted by the user. - * - * The returned pointer remains valid until this String instance is destroyed or toCString() is called again. - * - * \warning This however has the side effect that the returned string will remain - * in memory in addition to other memory that is consumed by this String instance. - * So, this method should not be used on large strings or where memory is critical. Consider using to8Bit() instead to avoid it. - * - * \see to8Bit() - */ - const char *toCString(bool unicode = false) const; - - /*! - * Returns a standard C-style (null-terminated) wide character version of this String. - * The returned string is encoded in UTF-16 (without BOM/CPU byte order), not UTF-32 even if wchar_t is 32-bit wide. - * - * The returned string is still owned by this String and should not be deleted by the user. - * - * The returned pointer remains valid until this String instance is destroyed or any other method of this String is called. - * - * \note This returns a pointer to the String's internal data without any conversions. - * - * \see toWString() - */ - const wchar_t *toCWString() const; - - /*! - * Returns an iterator pointing to the beginning of the string. - */ - Iterator begin(); - - /*! - * Returns a const iterator pointing to the beginning of the string. - */ - ConstIterator begin() const; - - /*! - * Returns an iterator pointing to the end of the string (the position after the last character). - */ - Iterator end(); - - /*! - * Returns a const iterator pointing to the end of the string (the position after the last character). - */ - ConstIterator end() const; - - /*! - * Finds the first occurrence of pattern \a s in this string starting from \a offset. - * If the pattern is not found, \a npos is returned. - */ - size_t find(const String &s, size_t offset = 0) const; - - /*! - * Finds the last occurrence of pattern \a s in this string, searched backwards, - * either from the end of the string or starting from \a offset. - * If the pattern is not found, \a npos is returned. - */ - size_t rfind(const String &s, size_t offset = npos()) const; - - /*! - * Splits the string on each occurrence of \a separator. - */ - StringList split(const String &separator = " ") const; - - /*! - * Returns true if the strings starts with the substring \a s. - */ - bool startsWith(const String &s) const; - - /*! - * Extract a substring from this string starting at \a position and continuing for \a n characters. - */ - String substr(size_t position, size_t n = npos()) const; - - /*! - * Append \a s to the current string and return a reference to the current string. - */ - String &append(const String &s); - - /*! - * Clears the string. - */ - String &clear(); - - /*! - * Returns an upper case version of the string. - * - * \warning This only works for the characters in US-ASCII, i.e. A-Z. - */ - String upper() const; - - /*! - * Returns the size of the string. - */ - size_t size() const; - - /*! - * Returns the length of the string. Equivalent to size(). - */ - size_t length() const; - - /*! - * Returns true if the string is empty. - */ - bool isEmpty() const; - - /*! - * Returns a ByteVector containing the string's data. - * If \a t is Latin1 or UTF8, this will return a vector of 8 bit characters, otherwise it will use 16 bit characters. - * - * \note If \a t is UTF16, the returned data is encoded in little-endian format and has a BOM. - * - * \note The returned data is not null terminated. - */ - ByteVector data(Type t) const; - - /*! - * Convert the string to an integer. - * - * If the conversion was successful, it sets the value of \a *ok to true and returns the integer. - * Otherwise it sets \a *ok to false and the result is undefined. - */ - int toInt(bool *ok = nullptr) const; - - /*! - * Returns a string with the leading and trailing whitespace stripped. - */ - String stripWhiteSpace() const; - - /*! - * Returns true if the file only uses characters required by Latin1. - */ - bool isLatin1() const; - - /*! - * Returns true if the file only uses characters required by (7-bit) ASCII. - */ - bool isAscii() const; - - /*! - * Converts the base-10 integer \a n to a string. - */ - static String number(int n); - - /*! - * Returns a reference to the character at position \a i. - */ - wchar_t &operator[](size_t i); - - /*! - * Returns a const reference to the character at position \a i. - */ - const wchar_t &operator[](size_t i) const; - - /*! - * Compares each character of the String with each character of \a s and returns true if the strings match. - */ - bool operator==(const String &s) const; - - /*! - * Compares each character of the String with each character of \a s and returns false if the strings match. - */ - bool operator!=(const String &s) const; - - /*! - * Compares each character of the String with each character of \a s and returns true if the strings match. - */ - bool operator==(const char *s) const; - - /*! - * Compares each character of the String with each character of \a s and returns false if the strings match. - */ - bool operator!=(const char *s) const; - - /*! - * Compares each character of the String with each character of \a s and returns true if the strings match. - */ - bool operator==(const wchar_t *s) const; - - /*! - * Compares each character of the String with each character of \a s and returns false if the strings match. - */ - bool operator!=(const wchar_t *s) const; - - /*! - * Appends \a s to the end of the String. - */ - String &operator+=(const String &s); - - /*! - * Appends \a s to the end of the String. - */ - String &operator+=(const wchar_t *s); - - /*! - * Appends \a s to the end of the String. - */ - String &operator+=(const char *s); - - /*! - * Appends \a s to the end of the String. - */ - String &operator+=(wchar_t c); - - /*! - * Appends \a c to the end of the String. - */ - String &operator+=(char c); - - /*! - * Performs a shallow, implicitly shared, copy of \a s, overwriting the String's current data. - */ - String &operator=(const String &s); - - /*! - * Performs a deep copy of the data in \a s. - */ - String &operator=(const std::string &s); - - /*! - * Performs a deep copy of the data in \a s. - */ - String &operator=(const std::wstring &s); - - /*! - * Performs a deep copy of the data in \a s. - */ - String &operator=(const wchar_t *s); - - /*! - * Performs a deep copy of the data in \a s. - */ - String &operator=(char c); - - /*! - * Performs a deep copy of the data in \a s. - */ - String &operator=(wchar_t c); - - /*! - * Performs a deep copy of the data in \a s. - */ - String &operator=(const char *s); - - /*! - * Performs a deep copy of the data in \a v. - */ - String &operator=(const ByteVector &v); - - /*! - * Exchanges the content of the String by the content of \a s. - */ - void swap(String &s); - - /*! - * To be able to use this class in a Map, this operator needed to be implemented. - * Returns true if \a s is less than this string in a byte-wise comparison. - */ - bool operator<(const String &s) const; - - /*! - * Returns a special value used for \a length parameter in String's member - * functions, means "until the end of the string". - * As a return value, it is usually used to indicate no matches. - */ - static size_t npos(); - - protected: - /*! - * If this String is being shared via implicit sharing, do a deep copy of the data and separate from the shared members. - * This should be called by all non-const subclass members. - */ - void detach(); - - private: - class StringPrivate; - StringPrivate *d; -}; - -/*! - * \relates TagLib::String - * - * Concatenates \a s1 and \a s2 and returns the result as a string. - */ -TAGLIB_EXPORT const Strawberry_TagLib::TagLib::String operator+(const Strawberry_TagLib::TagLib::String &s1, const Strawberry_TagLib::TagLib::String &s2); - -/*! - * \relates TagLib::String - * - * Concatenates \a s1 and \a s2 and returns the result as a string. - */ -TAGLIB_EXPORT const Strawberry_TagLib::TagLib::String operator+(const char *s1, const Strawberry_TagLib::TagLib::String &s2); - -/*! - * \relates TagLib::String - * - * Concatenates \a s1 and \a s2 and returns the result as a string. - */ -TAGLIB_EXPORT const Strawberry_TagLib::TagLib::String operator+(const Strawberry_TagLib::TagLib::String &s1, const char *s2); - - -/*! - * \relates TagLib::String - * - * Send the string to an output stream. - */ -TAGLIB_EXPORT std::ostream &operator<<(std::ostream &s, const Strawberry_TagLib::TagLib::String &str); - -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/toolkit/tstringhandler.cpp b/3rdparty/taglib/toolkit/tstringhandler.cpp deleted file mode 100644 index 66a35412..00000000 --- a/3rdparty/taglib/toolkit/tstringhandler.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/*************************************************************************** - copyright : (C) 2012 by Tsuda Kageyu - email : tsuda.kageyu@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tstringhandler.h" - -Strawberry_TagLib::TagLib::StringHandler::StringHandler() {} - -Strawberry_TagLib::TagLib::StringHandler::~StringHandler() {} diff --git a/3rdparty/taglib/toolkit/tstringhandler.h b/3rdparty/taglib/toolkit/tstringhandler.h deleted file mode 100644 index a557c574..00000000 --- a/3rdparty/taglib/toolkit/tstringhandler.h +++ /dev/null @@ -1,72 +0,0 @@ -/*************************************************************************** - copyright : (C) 2012 by Tsuda Kageyu - email : tsuda.kageyu@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_STRINGHANDLER_H -#define TAGLIB_STRINGHANDLER_H - -#include "tstring.h" -#include "tbytevector.h" -#include "taglib_export.h" - -namespace Strawberry_TagLib { -namespace TagLib { -//! A abstraction for the string to data encoding. - -/*! - * ID3v1, ID3v2 and RIFF Info tag sometimes store strings in local encodings - * encodings instead of ISO-8859-1 (Latin1), such as Windows-1252 for western - * languages, Shift_JIS for Japanese and so on. However, TagLib only supports - * genuine ISO-8859-1 by default. - * - * Here is an option to read and write tags in your preferrd encoding - * by subclassing this class, reimplementing parse() and render() and setting - * your reimplementation as the default with ID3v1::Tag::setStringHandler(), - * ID3v2::Tag::setStringHandler() or Info::Tag::setStringHandler(). - * - * \see ID3v1::Tag::setStringHandler() - * \see ID3v2::Tag::setStringHandler() - * \see Info::Tag::setStringHandler() - */ - -class TAGLIB_EXPORT StringHandler { - public: - explicit StringHandler(); - virtual ~StringHandler(); - - /*! - * Decode a string from \a data. - */ - virtual String parse(const ByteVector &data) const = 0; - - /*! - * Encode a ByteVector with the data from \a s. - */ - virtual ByteVector render(const String &s) const = 0; -}; - -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/toolkit/tstringlist.cpp b/3rdparty/taglib/toolkit/tstringlist.cpp deleted file mode 100644 index abe1f002..00000000 --- a/3rdparty/taglib/toolkit/tstringlist.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tstringlist.h" - -using namespace Strawberry_TagLib::TagLib; - -//////////////////////////////////////////////////////////////////////////////// -// static members -//////////////////////////////////////////////////////////////////////////////// - -StringList StringList::split(const String &s, const String &pattern) { - - StringList l; - - size_t previousOffset = 0; - for (size_t offset = s.find(pattern); offset != String::npos(); offset = s.find(pattern, offset + 1)) { - l.append(s.substr(previousOffset, offset - previousOffset)); - previousOffset = offset + 1; - } - - l.append(s.substr(previousOffset, s.size() - previousOffset)); - - return l; - -} - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -StringList::StringList() {} - -StringList::StringList(const StringList &l) : List(l) {} - -StringList::StringList(const String &s) { - append(s); -} - -StringList::StringList(const ByteVectorList &bl, String::Type t) { - - ByteVectorList::ConstIterator i = bl.begin(); - for (; i != bl.end(); i++) { - append(String(*i, t)); - } - -} - -String StringList::toString(const String &separator) const { - - String s; - - ConstIterator it = begin(); - ConstIterator itEnd = end(); - - while (it != itEnd) { - s += *it; - it++; - if (it != itEnd) - s += separator; - } - - return s; - -} - -StringList &StringList::append(const String &s) { - - List::append(s); - return *this; - -} - -StringList &StringList::append(const StringList &l) { - - List::append(l); - return *this; - -} - -StringList &StringList::operator=(const StringList &l) { - List::operator=(l); - return *this; -} - -//////////////////////////////////////////////////////////////////////////////// -// related functions -//////////////////////////////////////////////////////////////////////////////// - -std::ostream &Strawberry_TagLib::TagLib::operator<<(std::ostream &s, const StringList &l) { - s << l.toString(); - return s; -} diff --git a/3rdparty/taglib/toolkit/tstringlist.h b/3rdparty/taglib/toolkit/tstringlist.h deleted file mode 100644 index ea74e60c..00000000 --- a/3rdparty/taglib/toolkit/tstringlist.h +++ /dev/null @@ -1,108 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002 - 2008 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_STRINGLIST_H -#define TAGLIB_STRINGLIST_H - -#include "tstring.h" -#include "tlist.h" -#include "tbytevectorlist.h" -#include "taglib_export.h" - -#include - -namespace Strawberry_TagLib { -namespace TagLib { - -//! A list of strings - -/*! - * This is a specialization of the List class with some members convention for string operations. - */ - -class TAGLIB_EXPORT StringList : public List { - public: - /*! - * Constructs an empty StringList. - */ - explicit StringList(); - - /*! - * Make a shallow, implicitly shared, copy of \a l. - * Because this is implicitly shared, this method is lightweight and suitable for pass-by-value usage. - */ - StringList(const StringList &l); - - /*! - * Constructs a StringList with \a s as a member. - */ - StringList(const String &s); - - /*! - * Makes a deep copy of the data in \a vl. - * - * \note This should only be used with the 8-bit codecs Latin1 and UTF8, - * when used with other codecs it will simply print a warning and exit. - */ - StringList(const ByteVectorList &bl, String::Type t = String::Latin1); - - /*! - * Concatenate the list of strings into one string separated by \a separator. - */ - String toString(const String &separator = " ") const; - - /*! - * Appends \a s to the end of the list and returns a reference to the list. - */ - StringList &append(const String &s); - - /*! - * Appends all of the values in \a l to the end of the list and returns a reference to the list. - */ - StringList &append(const StringList &l); - - /*! - * Make a shallow, implicitly shared, copy of \a l. - * Because this is implicitly shared, this method is lightweight and suitable for pass-by-value usage. - */ - StringList &operator=(const StringList &l); - - /*! - * Splits the String \a s into several strings at \a pattern. - * This will not include the pattern in the returned strings. - */ - static StringList split(const String &s, const String &pattern); -}; - -/*! - * \related Strawberry_TagLib::TagLib::StringList - * Send the StringList to an output stream. - */ -TAGLIB_EXPORT std::ostream &operator<<(std::ostream &s, const Strawberry_TagLib::TagLib::StringList &l); - -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/toolkit/tutils.h b/3rdparty/taglib/toolkit/tutils.h deleted file mode 100644 index b34a7240..00000000 --- a/3rdparty/taglib/toolkit/tutils.h +++ /dev/null @@ -1,218 +0,0 @@ -/*************************************************************************** - copyright : (C) 2013 by Tsuda Kageyu - email : tsuda.kageyu@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_TUTILS_H -#define TAGLIB_TUTILS_H - -// THIS FILE IS NOT A PART OF THE TAGLIB API - -#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header - -#include "taglib-config.h" - -#if defined(HAVE_MSC_BYTESWAP) -# include -#elif defined(HAVE_GLIBC_BYTESWAP) -# include -#elif defined(HAVE_MAC_BYTESWAP) -# include -#elif defined(HAVE_OPENBSD_BYTESWAP) -# include -#endif - -#include "tstring.h" -#include -#include -#include - -namespace Strawberry_TagLib { -namespace TagLib { -namespace Utils { -namespace { - -/*! - * Reverses the order of bytes in an 16-bit integer. - */ -inline unsigned short byteSwap(unsigned short x) { -# if defined(HAVE_GCC_BYTESWAP) - - return __builtin_bswap16(x); - -# elif defined(HAVE_MSC_BYTESWAP) - - return _byteswap_ushort(x); - -# elif defined(HAVE_GLIBC_BYTESWAP) - - return __bswap_16(x); - -# elif defined(HAVE_MAC_BYTESWAP) - - return OSSwapInt16(x); - -# elif defined(HAVE_OPENBSD_BYTESWAP) - - return swap16(x); - -# else - - return ((x >> 8) & 0xff) | ((x & 0xff) << 8); - -# endif -} - -/*! - * Reverses the order of bytes in an 32-bit integer. - */ -inline unsigned int byteSwap(unsigned int x) { -# if defined(HAVE_GCC_BYTESWAP) - - return __builtin_bswap32(x); - -# elif defined(HAVE_MSC_BYTESWAP) - - return _byteswap_ulong(x); - -# elif defined(HAVE_GLIBC_BYTESWAP) - - return __bswap_32(x); - -# elif defined(HAVE_MAC_BYTESWAP) - - return OSSwapInt32(x); - -# elif defined(HAVE_OPENBSD_BYTESWAP) - - return swap32(x); - -# else - - return ((x & 0xff000000u) >> 24) | ((x & 0x00ff0000u) >> 8) | ((x & 0x0000ff00u) << 8) | ((x & 0x000000ffu) << 24); - -# endif -} - -/*! - * Reverses the order of bytes in an 64-bit integer. - */ -inline unsigned long long byteSwap(unsigned long long x) { -# if defined(HAVE_GCC_BYTESWAP) - - return __builtin_bswap64(x); - -# elif defined(HAVE_MSC_BYTESWAP) - - return _byteswap_uint64(x); - -# elif defined(HAVE_GLIBC_BYTESWAP) - - return __bswap_64(x); - -# elif defined(HAVE_MAC_BYTESWAP) - - return OSSwapInt64(x); - -# elif defined(HAVE_OPENBSD_BYTESWAP) - - return swap64(x); - -# else - - return ((x & 0xff00000000000000ull) >> 56) | ((x & 0x00ff000000000000ull) >> 40) | ((x & 0x0000ff0000000000ull) >> 24) | ((x & 0x000000ff00000000ull) >> 8) | ((x & 0x00000000ff000000ull) << 8) | ((x & 0x0000000000ff0000ull) << 24) | ((x & 0x000000000000ff00ull) << 40) | ((x & 0x00000000000000ffull) << 56); - -# endif -} - -/*! - * Returns a formatted string just like standard sprintf(), but makes use of safer functions such as snprintf() if available. - */ -inline String formatString(const char *format, ...) { - // Sufficient buffer size for the current internal uses. - // Consider changing this value when you use this function. - - static const size_t BufferSize = 128; - - va_list args; - va_start(args, format); - - char buf[BufferSize]; - int length; - - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wformat-nonliteral" - -# if defined(HAVE_VSNPRINTF) - - length = vsnprintf(buf, BufferSize, format, args); - -# elif defined(HAVE_VSPRINTF_S) - - length = vsprintf_s(buf, format, args); - -# else - - // The last resort. May cause a buffer overflow. - - length = vsprintf(buf, format, args); - if (length >= BufferSize) { - debug("Utils::formatString() - Buffer overflow! Returning an empty string."); - length = -1; - } - -# endif - - #pragma GCC diagnostic pop - - va_end(args); - - if (length > 0) - return String(buf); - else - return String(); -} - -/*! - * Returns the byte order of the system. - */ -inline ByteOrder systemByteOrder() { - union { - int i; - char c; - } u; - - u.i = 1; - if (u.c == 1) - return LittleEndian; - else - return BigEndian; -} -} // namespace -} // namespace Utils -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif - -#endif diff --git a/3rdparty/taglib/toolkit/tzlib.cpp b/3rdparty/taglib/toolkit/tzlib.cpp deleted file mode 100644 index 055a24f7..00000000 --- a/3rdparty/taglib/toolkit/tzlib.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/*************************************************************************** - copyright : (C) 2016 by Tsuda Kageyu - email : tsuda.kageyu@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "taglib-config.h" - -#ifdef HAVE_ZLIB -# include -# include "tstring.h" -# include "tdebug.h" -#endif - -#include "tzlib.h" - -using namespace Strawberry_TagLib::TagLib; - -bool zlib::isAvailable() { -#ifdef HAVE_ZLIB - - return true; - -#else - - return false; - -#endif -} - -ByteVector zlib::decompress(const ByteVector &data) { -#ifdef HAVE_ZLIB - - z_stream stream = {}; - - if (inflateInit(&stream) != Z_OK) { - debug("zlib::decompress() - Failed to initizlize zlib."); - return ByteVector(); - } - - ByteVector inData = data; - - stream.avail_in = static_cast(inData.size()); - stream.next_in = reinterpret_cast(inData.data()); - - const unsigned int chunkSize = 1024; - - ByteVector outData; - - do { - const size_t offset = outData.size(); - outData.resize(outData.size() + chunkSize); - - stream.avail_out = static_cast(chunkSize); - stream.next_out = reinterpret_cast(outData.data() + offset); - - const int result = inflate(&stream, Z_NO_FLUSH); - - if (result == Z_STREAM_ERROR || - result == Z_NEED_DICT || - result == Z_DATA_ERROR || - result == Z_MEM_ERROR) { - if (result != Z_STREAM_ERROR) - inflateEnd(&stream); - - debug("zlib::decompress() - Error reading compressed stream."); - return ByteVector(); - } - - outData.resize(outData.size() - stream.avail_out); - } while (stream.avail_out == 0); - - inflateEnd(&stream); - - return outData; - -#else - - return ByteVector(); - -#endif -} diff --git a/3rdparty/taglib/toolkit/tzlib.h b/3rdparty/taglib/toolkit/tzlib.h deleted file mode 100644 index 892eb36b..00000000 --- a/3rdparty/taglib/toolkit/tzlib.h +++ /dev/null @@ -1,56 +0,0 @@ -/*************************************************************************** - copyright : (C) 2016 by Tsuda Kageyu - email : tsuda.kageyu@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_TZLIB_H -#define TAGLIB_TZLIB_H - -#include "tbytevector.h" - -// THIS FILE IS NOT A PART OF THE TAGLIB API - -#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header - -namespace Strawberry_TagLib { -namespace TagLib { - -namespace zlib { - -/*! - * Returns whether or not zlib is installed and ready to use. - */ -bool isAvailable(); - -/*! - * Decompress \a data by zlib. - */ -ByteVector decompress(const ByteVector &data); - -} // namespace zlib -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif - -#endif diff --git a/3rdparty/taglib/trueaudio/trueaudiofile.cpp b/3rdparty/taglib/trueaudio/trueaudiofile.cpp deleted file mode 100644 index 3b8db24d..00000000 --- a/3rdparty/taglib/trueaudio/trueaudiofile.cpp +++ /dev/null @@ -1,287 +0,0 @@ -/*************************************************************************** - copyright : (C) 2006 by Lukáš Lalinský - email : lalinsky@gmail.com - - copyright : (C) 2004 by Allan Sandfeld Jensen - email : kde@carewolf.org - (original MPC implementation) - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tbytevector.h" -#include "tstring.h" -#include "tdebug.h" -#include "tagunion.h" -#include "tstringlist.h" -#include "tpropertymap.h" -#include "tagutils.h" - -#include "trueaudiofile.h" -#include "id3v1tag.h" -#include "id3v2tag.h" -#include "id3v2header.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { -enum { - TrueAudioID3v2Index = 0, - TrueAudioID3v1Index = 1 -}; -const unsigned int HeaderSize = 18; -} // namespace - -class TrueAudio::File::FilePrivate { - public: - explicit FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) : ID3v2FrameFactory(frameFactory), - ID3v2Location(-1), - ID3v2OriginalSize(0), - ID3v1Location(-1), - properties(nullptr) {} - - ~FilePrivate() { - delete properties; - } - - const ID3v2::FrameFactory *ID3v2FrameFactory; - long long ID3v2Location; - long long ID3v2OriginalSize; - - long long ID3v1Location; - - DoubleTagUnion tag; - - AudioProperties *properties; -}; - -//////////////////////////////////////////////////////////////////////////////// -// static members -//////////////////////////////////////////////////////////////////////////////// - -bool TrueAudio::File::isSupported(IOStream *stream) { - - // A TrueAudio file has to start with "TTA". An ID3v2 tag may precede. - - const ByteVector id = Utils::readHeader(stream, 3, true); - return (id == "TTA"); - -} - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -TrueAudio::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::File(file), d(new FilePrivate()) { - - if (isOpen()) - read(readProperties); - -} - -TrueAudio::File::File(FileName file, ID3v2::FrameFactory *frameFactory, bool readProperties, AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::File(file), d(new FilePrivate(frameFactory)) { - - if (isOpen()) - read(readProperties); - -} - -TrueAudio::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::File(stream), d(new FilePrivate()) { - - if (isOpen()) - read(readProperties); - -} - -TrueAudio::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory, bool readProperties, AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::File(stream), d(new FilePrivate(frameFactory)) { - - if (isOpen()) - read(readProperties); - -} - -TrueAudio::File::~File() { - delete d; -} - -Strawberry_TagLib::TagLib::Tag *TrueAudio::File::tag() const { - return &d->tag; -} - -PropertyMap TrueAudio::File::setProperties(const PropertyMap &properties) { - - if (ID3v1Tag()) - ID3v1Tag()->setProperties(properties); - - return ID3v2Tag(true)->setProperties(properties); - -} - -TrueAudio::AudioProperties *TrueAudio::File::audioProperties() const { - return d->properties; -} - -bool TrueAudio::File::save() { - - if (readOnly()) { - debug("TrueAudio::File::save() -- File is read only."); - return false; - } - - // Update ID3v2 tag - - if (ID3v2Tag() && !ID3v2Tag()->isEmpty()) { - - // ID3v2 tag is not empty. Update the old one or create a new one. - - if (d->ID3v2Location < 0) - d->ID3v2Location = 0; - - const ByteVector data = ID3v2Tag()->render(); - insert(data, d->ID3v2Location, d->ID3v2OriginalSize); - - if (d->ID3v1Location >= 0) - d->ID3v1Location += (static_cast(data.size()) - d->ID3v2OriginalSize); - - d->ID3v2OriginalSize = data.size(); - } - else { - - // ID3v2 tag is empty. Remove the old one. - - if (d->ID3v2Location >= 0) { - removeBlock(d->ID3v2Location, d->ID3v2OriginalSize); - - if (d->ID3v1Location >= 0) - d->ID3v1Location -= d->ID3v2OriginalSize; - - d->ID3v2Location = -1; - d->ID3v2OriginalSize = 0; - } - } - - // Update ID3v1 tag - - if (ID3v1Tag() && !ID3v1Tag()->isEmpty()) { - - // ID3v1 tag is not empty. Update the old one or create a new one. - - if (d->ID3v1Location >= 0) { - seek(d->ID3v1Location); - } - else { - seek(0, End); - d->ID3v1Location = tell(); - } - - writeBlock(ID3v1Tag()->render()); - } - else { - - // ID3v1 tag is empty. Remove the old one. - - if (d->ID3v1Location >= 0) { - truncate(d->ID3v1Location); - d->ID3v1Location = -1; - } - } - - return true; - -} - -ID3v1::Tag *TrueAudio::File::ID3v1Tag(bool create) { - return d->tag.access(TrueAudioID3v1Index, create); -} - -ID3v2::Tag *TrueAudio::File::ID3v2Tag(bool create) { - return d->tag.access(TrueAudioID3v2Index, create); -} - -void TrueAudio::File::strip(int tags) { - - if (tags & ID3v1) - d->tag.set(TrueAudioID3v1Index, nullptr); - - if (tags & ID3v2) - d->tag.set(TrueAudioID3v2Index, nullptr); - - if (!ID3v1Tag()) - ID3v2Tag(true); - -} - -bool TrueAudio::File::hasID3v1Tag() const { - return (d->ID3v1Location >= 0); -} - -bool TrueAudio::File::hasID3v2Tag() const { - return (d->ID3v2Location >= 0); -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void TrueAudio::File::read(bool readProperties) { - - // Look for an ID3v2 tag - - d->ID3v2Location = Utils::findID3v2(this); - - if (d->ID3v2Location >= 0) { - d->tag.set(TrueAudioID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory)); - d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize(); - } - - // Look for an ID3v1 tag - - d->ID3v1Location = Utils::findID3v1(this); - - if (d->ID3v1Location >= 0) - d->tag.set(TrueAudioID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); - - if (d->ID3v1Location < 0) - ID3v2Tag(true); - - // Look for TrueAudio metadata - - if (readProperties) { - - long long streamLength; - - if (d->ID3v1Location >= 0) - streamLength = d->ID3v1Location; - else - streamLength = length(); - - if (d->ID3v2Location >= 0) { - seek(d->ID3v2Location + d->ID3v2OriginalSize); - streamLength -= (d->ID3v2Location + d->ID3v2OriginalSize); - } - else { - seek(0); - } - - d->properties = new AudioProperties(readBlock(HeaderSize), streamLength); - } - -} diff --git a/3rdparty/taglib/trueaudio/trueaudiofile.h b/3rdparty/taglib/trueaudio/trueaudiofile.h deleted file mode 100644 index 1239a836..00000000 --- a/3rdparty/taglib/trueaudio/trueaudiofile.h +++ /dev/null @@ -1,226 +0,0 @@ -/*************************************************************************** - copyright : (C) 2006 by Lukáš Lalinský - email : lalinsky@gmail.com - - copyright : (C) 2004 by Allan Sandfeld Jensen - email : kde@carewolf.org - (original MPC implementation) - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_TRUEAUDIOFILE_H -#define TAGLIB_TRUEAUDIOFILE_H - -#include "tfile.h" -#include "trueaudioproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -class Tag; - -namespace ID3v2 { -class Tag; -class FrameFactory; -} // namespace ID3v2 -namespace ID3v1 { -class Tag; -} - -//! An implementation of TrueAudio metadata - -/*! - * This is implementation of TrueAudio metadata. - * - * This supports ID3v1 and ID3v2 tags as well as reading stream properties from the file. - */ - -namespace TrueAudio { - -//! An implementation of TagLib::File with TrueAudio specific methods - -/*! - * This implements and provides an interface for TrueAudio 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 TrueAudio files. - */ - -class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File { - public: - /*! - * This set of flags is used for various operations and is suitable for being OR-ed together. - */ - enum TagTypes { - //! Empty set. Matches no tag types. - NoTags = 0x0000, - //! Matches ID3v1 tags. - ID3v1 = 0x0001, - //! Matches ID3v2 tags. - ID3v2 = 0x0002, - //! Matches all tag types. - AllTags = 0xffff - }; - - /*! - * Constructs a TrueAudio 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. - */ - explicit File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Constructs a TrueAudio file from \a file. - * If \a readProperties is true the file's audio properties will also be read. - * - * If this file contains and ID3v2 tag the frames will be created using \a frameFactory. - * - * \note In the current implementation, \a propertiesStyle is ignored. - */ - explicit File(FileName file, ID3v2::FrameFactory *frameFactory, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Constructs a TrueAudio 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. - */ - explicit File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Constructs a TrueAudio 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. - */ - explicit File(IOStream *stream, ID3v2::FrameFactory *frameFactory, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Destroys this instance of the File. - */ - ~File() override; - - /*! - * Returns the Tag for this file. - */ - Strawberry_TagLib::TagLib::Tag *tag() const override; - - /*! - * Implements the unified property interface -- import function. - * Creates in ID3v2 tag if necessary. If an ID3v1 tag exists, it will be updated as well, within the limitations of ID3v1. - */ - PropertyMap setProperties(const PropertyMap &) override; - - /*! - * Returns the TrueAudio::AudioProperties for this file. - * If no audio properties were read then this will return a null pointer. - */ - AudioProperties *audioProperties() const override; - - /*! - * Saves the file. - */ - bool save() override; - - /*! - * Returns a pointer to the ID3v1 tag of the file. - * - * If \a create is false (the default) this may return a null pointer if there is no valid ID3v1 tag. - * If \a create is true it will create an ID3v1 tag if one does not exist and returns a valid pointer. - * - * \note This may return a valid pointer regardless of whether or not the file on disk has an ID3v1 tag. - * Use hasID3v1Tag() to check if the file on disk actually has an ID3v1 tag. - * - * \note The Tag is still owned by the MPEG::File and should not be deleted by the user. - * It will be deleted when the file (object) is destroyed. - * - * \see hasID3v1Tag() - */ - ID3v1::Tag *ID3v1Tag(bool create = false); - - /*! - * Returns a pointer to the ID3v2 tag of the file. - * - * If \a create is false (the default) this may return a null pointer if there is no valid ID3v2 tag. - * If \a create is true it will create an ID3v2 tag if one does not exist and returns a valid pointer. - * - * \note This may return a valid pointer regardless of whether or not the file on disk has an ID3v2 tag. - * Use hasID3v2Tag() to check if the file on disk actually has an ID3v2 tag. - * - * \note The Tag is still owned by the MPEG::File and should not be deleted by the user. - * It will be deleted when the file (object) is destroyed. - * - * \see hasID3v2Tag() - */ - ID3v2::Tag *ID3v2Tag(bool create = false); - - /*! - * This will remove the tags that match the OR-ed together TagTypes from the file. - * By default it removes all tags. - * - * \note This will also invalidate pointers to the tags as their memory will be freed. - * \note In order to make the removal permanent save() still needs to be called - */ - void strip(int tags = AllTags); - - /*! - * Returns whether or not the file on disk actually has an ID3v1 tag. - * - * \see ID3v1Tag() - */ - bool hasID3v1Tag() const; - - /*! - * Returns whether or not the file on disk actually has an ID3v2 tag. - * - * \see ID3v2Tag() - */ - bool hasID3v2Tag() const; - - /*! - * Returns whether or not the given \a stream can be opened as a TrueAudio 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); - - class FilePrivate; - FilePrivate *d; -}; - -} // namespace TrueAudio -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/trueaudio/trueaudioproperties.cpp b/3rdparty/taglib/trueaudio/trueaudioproperties.cpp deleted file mode 100644 index 1bf082a4..00000000 --- a/3rdparty/taglib/trueaudio/trueaudioproperties.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/*************************************************************************** - copyright : (C) 2006 by Lukáš Lalinský - email : lalinsky@gmail.com - - copyright : (C) 2004 by Allan Sandfeld Jensen - email : kde@carewolf.org - (original MPC implementation) - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tstring.h" -#include "tdebug.h" - -#include "trueaudioproperties.h" -#include "trueaudiofile.h" - -using namespace Strawberry_TagLib::TagLib; - -class TrueAudio::AudioProperties::AudioPropertiesPrivate { - public: - explicit AudioPropertiesPrivate() : version(0), - length(0), - bitrate(0), - sampleRate(0), - channels(0), - bitsPerSample(0), - sampleFrames(0) {} - - int version; - int length; - int bitrate; - int sampleRate; - int channels; - int bitsPerSample; - unsigned int sampleFrames; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -TrueAudio::AudioProperties::AudioProperties(const ByteVector &data, long long streamLength, ReadStyle) : Strawberry_TagLib::TagLib::AudioProperties(), d(new AudioPropertiesPrivate()) { - read(data, streamLength); -} - -TrueAudio::AudioProperties::~AudioProperties() { - delete d; -} - -int TrueAudio::AudioProperties::lengthInSeconds() const { - return d->length / 1000; -} - -int TrueAudio::AudioProperties::lengthInMilliseconds() const { - return d->length; -} - -int TrueAudio::AudioProperties::bitrate() const { - return d->bitrate; -} - -int TrueAudio::AudioProperties::sampleRate() const { - return d->sampleRate; -} - -int TrueAudio::AudioProperties::bitsPerSample() const { - return d->bitsPerSample; -} - -int TrueAudio::AudioProperties::channels() const { - return d->channels; -} - -unsigned int TrueAudio::AudioProperties::sampleFrames() const { - return d->sampleFrames; -} - -int TrueAudio::AudioProperties::ttaVersion() const { - return d->version; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void TrueAudio::AudioProperties::read(const ByteVector &data, long long streamLength) { - - if (data.size() < 4) { - debug("TrueAudio::AudioProperties::read() -- data is too short."); - return; - } - - if (!data.startsWith("TTA")) { - debug("TrueAudio::AudioProperties::read() -- invalid header signature."); - return; - } - - size_t pos = 3; - - d->version = data[pos] - '0'; - pos += 1; - - // According to http://en.true-audio.com/TTA_Lossless_Audio_Codec_-_Format_Description - // TTA2 headers are in development, and have a different format - if (1 == d->version) { - if (data.size() < 18) { - debug("TrueAudio::AudioProperties::read() -- data is too short."); - return; - } - - // Skip the audio format - pos += 2; - - d->channels = data.toUInt16LE(pos); - pos += 2; - - d->bitsPerSample = data.toUInt16LE(pos); - pos += 2; - - d->sampleRate = data.toUInt32LE(pos); - pos += 4; - - d->sampleFrames = data.toUInt32LE(pos); - - if (d->sampleFrames > 0 && d->sampleRate > 0) { - const double length = d->sampleFrames * 1000.0 / d->sampleRate; - d->length = static_cast(length + 0.5); - d->bitrate = static_cast(streamLength * 8.0 / length + 0.5); - } - } - -} diff --git a/3rdparty/taglib/trueaudio/trueaudioproperties.h b/3rdparty/taglib/trueaudio/trueaudioproperties.h deleted file mode 100644 index 97e57315..00000000 --- a/3rdparty/taglib/trueaudio/trueaudioproperties.h +++ /dev/null @@ -1,114 +0,0 @@ -/*************************************************************************** - copyright : (C) 2006 by Lukáš Lalinský - email : lalinsky@gmail.com - - copyright : (C) 2004 by Allan Sandfeld Jensen - email : kde@carewolf.org - (original MPC implementation) - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_TRUEAUDIOPROPERTIES_H -#define TAGLIB_TRUEAUDIOPROPERTIES_H - -#include "audioproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace TrueAudio { - -class File; - -//! An implementation of audio property reading for TrueAudio - -/*! - * This reads the data from an TrueAudio stream found in the AudioProperties API. - */ - -class TAGLIB_EXPORT AudioProperties : public Strawberry_TagLib::TagLib::AudioProperties { - public: - /*! - * Create an instance of TrueAudio::AudioProperties with the data read from the ByteVector \a data. - */ - explicit AudioProperties(const ByteVector &data, long long streamLength, ReadStyle style = Average); - - /*! - * Destroys this TrueAudio::AudioProperties instance. - */ - ~AudioProperties() override; - - /*! - * Returns the length of the file in seconds. The length is rounded down to the nearest whole second. - * - * \see lengthInMilliseconds() - */ - int lengthInSeconds() const override; - - /*! - * Returns the length of the file in milliseconds. - * - * \see lengthInSeconds() - */ - int lengthInMilliseconds() const override; - - /*! - * Returns the average bit rate of the file in kb/s. - */ - int bitrate() const override; - - /*! - * Returns the sample rate in Hz. - */ - int sampleRate() const override; - - /*! - * Returns the number of audio channels. - */ - int channels() const override; - - /*! - * Returns the number of bits per audio sample. - */ - int bitsPerSample() const; - - /*! - * Returns the total number of sample frames - */ - unsigned int sampleFrames() const; - - /*! - * Returns the major version number. - */ - int ttaVersion() const; - - private: - void read(const ByteVector &data, long long streamLength); - - class AudioPropertiesPrivate; - AudioPropertiesPrivate *d; -}; - -} // namespace TrueAudio -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/wavpack/wavpackfile.cpp b/3rdparty/taglib/wavpack/wavpackfile.cpp deleted file mode 100644 index 819078c5..00000000 --- a/3rdparty/taglib/wavpack/wavpackfile.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/*************************************************************************** - copyright : (C) 2006 by Lukáš Lalinský - email : lalinsky@gmail.com - - copyright : (C) 2004 by Allan Sandfeld Jensen - email : kde@carewolf.org - (original MPC implementation) - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tbytevector.h" -#include "tstring.h" -#include "tdebug.h" -#include "tagunion.h" -#include "tpropertymap.h" -#include "tagutils.h" - -#include "wavpackfile.h" -#include "id3v1tag.h" -#include "id3v2header.h" -#include "apetag.h" -#include "apefooter.h" - -using namespace Strawberry_TagLib::TagLib; - -namespace { -enum { - WavAPEIndex, - WavID3v1Index -}; -} - -class WavPack::File::FilePrivate { - public: - explicit FilePrivate() : APELocation(-1), - APESize(0), - ID3v1Location(-1), - properties(nullptr) {} - - ~FilePrivate() { - delete properties; - } - - long long APELocation; - long long APESize; - - long long ID3v1Location; - - DoubleTagUnion tag; - - AudioProperties *properties; -}; - -//////////////////////////////////////////////////////////////////////////////// -// static members -//////////////////////////////////////////////////////////////////////////////// - -bool WavPack::File::isSupported(IOStream *stream) { - - // A WavPack file has to start with "wvpk". - - const ByteVector id = Utils::readHeader(stream, 4, false); - return (id == "wvpk"); - -} - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -WavPack::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::File(file), d(new FilePrivate()) { - - if (isOpen()) - read(readProperties); - -} - -WavPack::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::File(stream), d(new FilePrivate()) { - - if (isOpen()) - read(readProperties); - -} - -WavPack::File::~File() { - delete d; -} - -Strawberry_TagLib::TagLib::Tag *WavPack::File::tag() const { - return &d->tag; -} - -PropertyMap WavPack::File::setProperties(const PropertyMap &properties) { - - if (ID3v1Tag()) - ID3v1Tag()->setProperties(properties); - - return APETag(true)->setProperties(properties); - -} - -WavPack::AudioProperties *WavPack::File::audioProperties() const { - return d->properties; -} - -bool WavPack::File::save() { - - if (readOnly()) { - debug("WavPack::File::save() -- File is read only."); - return false; - } - - // Update ID3v1 tag - - if (ID3v1Tag() && !ID3v1Tag()->isEmpty()) { - - // ID3v1 tag is not empty. Update the old one or create a new one. - - if (d->ID3v1Location >= 0) { - seek(d->ID3v1Location); - } - else { - seek(0, End); - d->ID3v1Location = tell(); - } - - writeBlock(ID3v1Tag()->render()); - } - else { - - // ID3v1 tag is empty. Remove the old one. - - if (d->ID3v1Location >= 0) { - truncate(d->ID3v1Location); - d->ID3v1Location = -1; - } - } - - // Update APE tag - - if (APETag() && !APETag()->isEmpty()) { - - // APE tag is not empty. Update the old one or create a new one. - - if (d->APELocation < 0) { - if (d->ID3v1Location >= 0) - d->APELocation = d->ID3v1Location; - else - d->APELocation = length(); - } - - const ByteVector data = APETag()->render(); - insert(data, d->APELocation, d->APESize); - - if (d->ID3v1Location >= 0) - d->ID3v1Location += (static_cast(data.size()) - d->APESize); - - d->APESize = data.size(); - } - else { - - // APE tag is empty. Remove the old one. - - if (d->APELocation >= 0) { - removeBlock(d->APELocation, d->APESize); - - if (d->ID3v1Location >= 0) - d->ID3v1Location -= d->APESize; - - d->APELocation = -1; - d->APESize = 0; - } - } - - return true; - -} - -ID3v1::Tag *WavPack::File::ID3v1Tag(bool create) { - return d->tag.access(WavID3v1Index, create); -} - -APE::Tag *WavPack::File::APETag(bool create) { - return d->tag.access(WavAPEIndex, create); -} - -void WavPack::File::strip(int tags) { - - if (tags & ID3v1) - d->tag.set(WavID3v1Index, nullptr); - - if (tags & APE) - d->tag.set(WavAPEIndex, nullptr); - - if (!ID3v1Tag()) - APETag(true); - -} - -bool WavPack::File::hasID3v1Tag() const { - return (d->ID3v1Location >= 0); -} - -bool WavPack::File::hasAPETag() const { - return (d->APELocation >= 0); -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void WavPack::File::read(bool readProperties) { - - // Look for an ID3v1 tag - - d->ID3v1Location = Utils::findID3v1(this); - - if (d->ID3v1Location >= 0) - d->tag.set(WavID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); - - // Look for an APE tag - - d->APELocation = Utils::findAPE(this, d->ID3v1Location); - - if (d->APELocation >= 0) { - d->tag.set(WavAPEIndex, new APE::Tag(this, d->APELocation)); - d->APESize = APETag()->footer()->completeTagSize(); - d->APELocation = d->APELocation + APE::Footer::size() - d->APESize; - } - - if (d->ID3v1Location >= 0) - APETag(true); - - // Look for WavPack audio properties - - if (readProperties) { - - long long streamLength; - - if (d->APELocation >= 0) - streamLength = d->APELocation; - else if (d->ID3v1Location >= 0) - streamLength = d->ID3v1Location; - else - streamLength = length(); - - d->properties = new AudioProperties(this, streamLength); - } - -} diff --git a/3rdparty/taglib/wavpack/wavpackfile.h b/3rdparty/taglib/wavpack/wavpackfile.h deleted file mode 100644 index 7cb5efaa..00000000 --- a/3rdparty/taglib/wavpack/wavpackfile.h +++ /dev/null @@ -1,212 +0,0 @@ -/*************************************************************************** - copyright : (C) 2006 by Lukáš Lalinský - email : lalinsky@gmail.com - - copyright : (C) 2004 by Allan Sandfeld Jensen - email : kde@carewolf.org - (original MPC implementation) - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_WVFILE_H -#define TAGLIB_WVFILE_H - -#include "tfile.h" -#include "taglib_export.h" -#include "wavpackproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { - -class Tag; - -namespace ID3v1 { -class Tag; -} -namespace APE { -class Tag; -} - -//! An implementation of WavPack metadata - -/*! - * This is implementation of WavPack metadata. - * - * This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream - * properties from the file. - * - */ - -namespace WavPack { - -//! An implementation of Strawberry_TagLib::TagLib::File with WavPack specific methods - -/*! - * This implements and provides an interface for WavPack files to the - * Strawberry_TagLib::TagLib::Tag and Strawberry_TagLib::TagLib::AudioProperties interfaces by way of implementing - * the abstract Strawberry_TagLib::TagLib::File API as well as providing some additional - * information specific to WavPack files. - * - */ - -class TAGLIB_EXPORT File : public Strawberry_TagLib::TagLib::File { - public: - /*! - * This set of flags is used for various operations and is suitable for being OR-ed together. - */ - enum TagTypes { - //! Empty set. Matches no tag types. - NoTags = 0x0000, - //! Matches ID3v1 tags. - ID3v1 = 0x0001, - //! Matches APE tags. - APE = 0x0002, - //! Matches all tag types. - AllTags = 0xffff - }; - - /*! - * Constructs a WavPack file from \a file. - * If \a readProperties is true the file's audio properties will also be read using \a propertiesStyle. - * If false, \a propertiesStyle is ignored - */ - explicit File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Constructs an WavPack file from \a file. - * If \a readProperties is true the file's audio properties will also be read using \a propertiesStyle. - * If false, \a propertiesStyle is ignored. - * - * \note TagLib will *not* take ownership of the stream, the caller is - * responsible for deleting it after the File object. - */ - explicit File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Destroys this instance of the File. - */ - ~File() override; - - /*! - * Returns the Tag for this file. - * This will be an APE tag, an ID3v1 tag or a combination of the two. - */ - Strawberry_TagLib::TagLib::Tag *tag() const override; - - /*! - * Implements the unified property interface -- import function. - * Creates an APE tag if it does not exists and calls setProperties() on that. - * Any existing ID3v1 tag will be updated as well. - */ - PropertyMap setProperties(const PropertyMap&) override; - - /*! - * Returns the MPC::AudioProperties for this file. - * If no audio properties were read then this will return a null pointer. - */ - AudioProperties *audioProperties() const override; - - /*! - * Saves the file. - * - * This returns true if the save was successful. - */ - bool save() override; - - /*! - * Returns a pointer to the ID3v1 tag of the file. - * - * If \a create is false (the default) this may return a null pointer if there is no valid ID3v1 tag. - * If \a create is true it will create an ID3v1 tag if one does not exist and returns a valid pointer. - * - * \note This may return a valid pointer regardless of whether or not the file on disk has an ID3v1 tag. - * Use hasID3v1Tag() to check if the file on disk actually has an ID3v1 tag. - * - * \note The Tag is still owned by the MPEG::File and should not be deleted by the user. - * It will be deleted when the file (object) is destroyed. - * - * \see hasID3v1Tag() - */ - ID3v1::Tag *ID3v1Tag(bool create = false); - - /*! - * Returns a pointer to the APE tag of the file. - * - * If \a create is false (the default) this may return a null pointer if there is no valid APE tag. - * If \a create is true it will create an APE tag if one does not exist and returns a valid pointer. - * - * \note This may return a valid pointer regardless of whether or not the file on disk has an APE tag. - * Use hasAPETag() to check if the file on disk actually has an APE tag. - * - * \note The Tag is still owned by the MPEG::File and should not be deleted by the user. - * It will be deleted when the file (object) is destroyed. - * - * \see hasAPETag() - */ - APE::Tag *APETag(bool create = false); - - /*! - * This will remove the tags that match the OR-ed together TagTypes from the file. - * By default it removes all tags. - * - * \note This will also invalidate pointers to the tags as their memory will be freed. - * \note In order to make the removal permanent save() still needs to be called - */ - void strip(int tags = AllTags); - - /*! - * Returns whether or not the file on disk actually has an ID3v1 tag. - * - * \see ID3v1Tag() - */ - bool hasID3v1Tag() const; - - /*! - * Returns whether or not the file on disk actually has an APE tag. - * - * \see APETag() - */ - bool hasAPETag() const; - - /*! - * Check if the given \a stream can be opened as a WavPack 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); - - class FilePrivate; - FilePrivate *d; -}; - -} // namespace WavPack -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/wavpack/wavpackproperties.cpp b/3rdparty/taglib/wavpack/wavpackproperties.cpp deleted file mode 100644 index c7f8634b..00000000 --- a/3rdparty/taglib/wavpack/wavpackproperties.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/*************************************************************************** - copyright : (C) 2006 by Lukáš Lalinský - email : lalinsky@gmail.com - - copyright : (C) 2004 by Allan Sandfeld Jensen - email : kde@carewolf.org - (original MPC implementation) - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tstring.h" -#include "tdebug.h" - -#include "wavpackproperties.h" -#include "wavpackfile.h" - -// Implementation of this class is based on the information at: -// http://www.wavpack.com/file_format.txt - -using namespace Strawberry_TagLib::TagLib; - -class WavPack::AudioProperties::AudioPropertiesPrivate { - public: - explicit AudioPropertiesPrivate() : length(0), - bitrate(0), - sampleRate(0), - channels(0), - version(0), - bitsPerSample(0), - lossless(false), - sampleFrames(0) {} - - int length; - int bitrate; - int sampleRate; - int channels; - int version; - int bitsPerSample; - bool lossless; - unsigned int sampleFrames; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -WavPack::AudioProperties::AudioProperties(File *file, long long streamLength, ReadStyle) : Strawberry_TagLib::TagLib::AudioProperties(), d(new AudioPropertiesPrivate()) { - read(file, streamLength); -} - -WavPack::AudioProperties::~AudioProperties() { - delete d; -} - -int WavPack::AudioProperties::lengthInSeconds() const { - return d->length / 1000; -} - -int WavPack::AudioProperties::lengthInMilliseconds() const { - return d->length; -} - -int WavPack::AudioProperties::bitrate() const { - return d->bitrate; -} - -int WavPack::AudioProperties::sampleRate() const { - return d->sampleRate; -} - -int WavPack::AudioProperties::channels() const { - return d->channels; -} - -int WavPack::AudioProperties::version() const { - return d->version; -} - -int WavPack::AudioProperties::bitsPerSample() const { - return d->bitsPerSample; -} - -bool WavPack::AudioProperties::isLossless() const { - return d->lossless; -} - -unsigned int WavPack::AudioProperties::sampleFrames() const { - return d->sampleFrames; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -namespace { -const unsigned int sample_rates[] = { - 6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000, - 32000, 44100, 48000, 64000, 88200, 96000, 192000, 0 -}; -} - -#define BYTES_STORED 3 -#define MONO_FLAG 4 -#define LOSSLESS_FLAG 8 - -#define SHIFT_LSB 13 -#define SHIFT_MASK (0x1fL << SHIFT_LSB) - -#define SRATE_LSB 23 -#define SRATE_MASK (0xfL << SRATE_LSB) - -#define MIN_STREAM_VERS 0x402 -#define MAX_STREAM_VERS 0x410 - -#define FINAL_BLOCK 0x1000 - -void WavPack::AudioProperties::read(File *file, long long streamLength) { - - long offset = 0; - - while (true) { - file->seek(offset); - const ByteVector data = file->readBlock(32); - - if (data.size() < 32) { - debug("WavPack::AudioProperties::read() -- data is too short."); - break; - } - - if (!data.startsWith("wvpk")) { - debug("WavPack::AudioProperties::read() -- Block header not found."); - break; - } - - const unsigned int flags = data.toUInt32LE(24); - - if (offset == 0) { - d->version = data.toUInt16LE(8); - if (d->version < MIN_STREAM_VERS || d->version > MAX_STREAM_VERS) - break; - - d->bitsPerSample = ((flags & BYTES_STORED) + 1) * 8 - ((flags & SHIFT_MASK) >> SHIFT_LSB); - d->sampleRate = sample_rates[(flags & SRATE_MASK) >> SRATE_LSB]; - d->lossless = !(flags & LOSSLESS_FLAG); - d->sampleFrames = data.toUInt32LE(12); - } - - d->channels += (flags & MONO_FLAG) ? 1 : 2; - - if (flags & FINAL_BLOCK) - break; - - const unsigned int blockSize = data.toUInt32LE(4); - offset += blockSize + 8; - } - - if (d->sampleFrames == ~0u) - d->sampleFrames = seekFinalIndex(file, streamLength); - - if (d->sampleFrames > 0 && d->sampleRate > 0) { - const double length = d->sampleFrames * 1000.0 / d->sampleRate; - d->length = static_cast(length + 0.5); - d->bitrate = static_cast(streamLength * 8.0 / length + 0.5); - } - -} - -unsigned int WavPack::AudioProperties::seekFinalIndex(File *file, long long streamLength) { - - const long long offset = file->rfind("wvpk", streamLength); - if (offset == -1) - return 0; - - file->seek(offset); - const ByteVector data = file->readBlock(32); - if (data.size() < 32) - return 0; - - const int version = data.toUInt16LE(8); - if (version < MIN_STREAM_VERS || version > MAX_STREAM_VERS) - return 0; - - const unsigned int flags = data.toUInt32LE(24); - if (!(flags & FINAL_BLOCK)) - return 0; - - const unsigned int blockIndex = data.toUInt32LE(16); - const unsigned int blockSamples = data.toUInt32LE(20); - - return blockIndex + blockSamples; - -} diff --git a/3rdparty/taglib/wavpack/wavpackproperties.h b/3rdparty/taglib/wavpack/wavpackproperties.h deleted file mode 100644 index e9fe8710..00000000 --- a/3rdparty/taglib/wavpack/wavpackproperties.h +++ /dev/null @@ -1,126 +0,0 @@ -/*************************************************************************** - copyright : (C) 2006 by Lukáš Lalinský - email : lalinsky@gmail.com - - copyright : (C) 2004 by Allan Sandfeld Jensen - email : kde@carewolf.org - (original MPC implementation) - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_WVPROPERTIES_H -#define TAGLIB_WVPROPERTIES_H - -#include "taglib_export.h" -#include "audioproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace WavPack { - -class File; - -static const unsigned int HeaderSize = 32; - -//! An implementation of audio property reading for WavPack - -/*! - * This reads the data from an WavPack stream found in the AudioProperties - * API. - */ - -class TAGLIB_EXPORT AudioProperties : public Strawberry_TagLib::TagLib::AudioProperties { - public: - - /*! - * Create an instance of WavPack::Properties. - */ - explicit AudioProperties(File *file, long long streamLength, ReadStyle style = Average); - - /*! - * Destroys this WavPack::AudioProperties instance. - */ - ~AudioProperties() override; - - /*! - * Returns the length of the file in seconds. - * The length is rounded down to the nearest whole second. - * - * \see lengthInMilliseconds() - */ - int lengthInSeconds() const override; - - /*! - * Returns the length of the file in milliseconds. - * - * \see lengthInSeconds() - */ - int lengthInMilliseconds() const override; - - /*! - * Returns the average bit rate of the file in kb/s. - */ - int bitrate() const override; - - /*! - * Returns the sample rate in Hz. 0 means unknown or custom. - */ - int sampleRate() const override; - - /*! - * Returns the number of audio channels. - */ - int channels() const override; - - /*! - * Returns the number of bits per audio sample. - */ - int bitsPerSample() const; - - /*! - * Returns whether or not the file is lossless encoded. - */ - bool isLossless() const; - - /*! - * Returns the total number of audio samples in file. - */ - unsigned int sampleFrames() const; - - /*! - * Returns WavPack version. - */ - int version() const; - - private: - void read(File *file, long long streamLength); - unsigned int seekFinalIndex(File *file, long long streamLength); - - class AudioPropertiesPrivate; - AudioPropertiesPrivate *d; -}; - -} // namespace WavPack -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/xm/xmfile.cpp b/3rdparty/taglib/xm/xmfile.cpp deleted file mode 100644 index 5d2990eb..00000000 --- a/3rdparty/taglib/xm/xmfile.cpp +++ /dev/null @@ -1,597 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include "tstringlist.h" -#include "tdebug.h" -#include "xmfile.h" -#include "modfileprivate.h" -#include "tpropertymap.h" - -#include -#include - -using namespace Strawberry_TagLib::TagLib; -using namespace XM; - -/*! - * The Reader classes are helpers to make handling of the stripped XM - * format more easy. In the stripped XM format certain header sizes might - * be smaller than one would expect. The fields that are not included - * are then just some predefined valued (e.g. 0). - * - * Using these classes this code: - * - * if(headerSize >= 4) { - * if(!readU16L(value1)) ERROR(); - * if(headerSize >= 8) { - * if(!readU16L(value2)) ERROR(); - * if(headerSize >= 12) { - * if(!readString(value3, 22)) ERROR(); - * ... - * } - * } - * } - * - * Becomes: - * - * StructReader header; - * header.u16L(value1).u16L(value2).string(value3, 22). ...; - * if(header.read(*this, headerSize) < std::min(header.size(), headerSize)) - * ERROR(); - * - * Maybe if this is useful to other formats these classes can be moved to - * their own public files. - */ - -namespace { -class Reader { - public: - virtual ~Reader() { - } - - /*! - * Reads associated values from \a file, but never reads more then \a limit bytes. - */ - virtual unsigned int read(Strawberry_TagLib::TagLib::File &file, unsigned int limit) = 0; - - /*! - * Returns the number of bytes this reader would like to read. - */ - virtual unsigned int size() const = 0; -}; - -class SkipReader : public Reader { - public: - explicit SkipReader(unsigned int size) : m_size(size) {} - - unsigned int read(Strawberry_TagLib::TagLib::File &file, unsigned int limit) override { - unsigned int count = std::min(m_size, limit); - file.seek(count, Strawberry_TagLib::TagLib::File::Current); - return count; - } - - unsigned int size() const override { - return m_size; - } - - private: - unsigned int m_size; -}; - -template -class ValueReader : public Reader { - public: - explicit ValueReader(T &_value) : value(_value) {} - - protected: - T &value; -}; - -class StringReader : public ValueReader { - public: - StringReader(String &string, unsigned int size) : ValueReader(string), m_size(size) {} - - unsigned int read(Strawberry_TagLib::TagLib::File &file, unsigned int limit) override { - - ByteVector data = file.readBlock(std::min(m_size, limit)); - size_t count = data.size(); - size_t index = data.find('\0'); - if (index != ByteVector::npos()) { - data.resize(index); - } - data.replace('\xff', ' '); - value = data; - return count; - - } - - unsigned int size() const override { - return m_size; - } - - private: - unsigned int m_size; -}; - -class ByteReader : public ValueReader { - public: - explicit ByteReader(unsigned char _byte) : ValueReader(_byte) {} - - unsigned int read(Strawberry_TagLib::TagLib::File &file, unsigned int limit) override { - ByteVector data = file.readBlock(std::min(1U, limit)); - if (data.size() > 0) { - value = data[0]; - } - return data.size(); - } - - unsigned int size() const override { - return 1; - } -}; - -template -class NumberReader : public ValueReader { - public: - NumberReader(T &_value, bool _bigEndian) : ValueReader(_value), bigEndian(_bigEndian) { - } - - protected: - bool bigEndian; -}; - -class U16Reader : public NumberReader { - public: - U16Reader(unsigned short &_value, bool _bigEndian) - : NumberReader(_value, _bigEndian) {} - - unsigned int read(Strawberry_TagLib::TagLib::File &file, unsigned int limit) override { - ByteVector data = file.readBlock(std::min(2U, limit)); - - if (bigEndian) - value = data.toUInt16BE(0); - else - value = data.toUInt16LE(0); - - return static_cast(data.size()); - } - - unsigned int size() const override { - return 2; - } -}; - -class U32Reader : public NumberReader { - public: - explicit U32Reader(unsigned int _value, bool _bigEndian = true) : NumberReader(_value, _bigEndian) { - } - - unsigned int read(Strawberry_TagLib::TagLib::File &file, unsigned int limit) override { - ByteVector data = file.readBlock(std::min(4U, limit)); - - if (bigEndian) - value = data.toUInt32BE(0); - else - value = data.toUInt32LE(0); - - return static_cast(data.size()); - } - - unsigned int size() const override { - return 4; - } -}; - -class StructReader : public Reader { - public: - StructReader() { - m_readers.setAutoDelete(true); - } - - /*! - * Add a nested reader. This reader takes ownership. - */ - StructReader &reader(Reader *reader) { - m_readers.append(reader); - return *this; - } - - /*! - * Don't read anything but skip \a size bytes. - */ - StructReader &skip(unsigned int size) { - m_readers.append(new SkipReader(size)); - return *this; - } - - /*! - * Read a string of \a size characters (bytes) into \a string. - */ - StructReader &string(String &string, unsigned int size) { - m_readers.append(new StringReader(string, size)); - return *this; - } - - /*! - * Read a byte into \a byte. - */ - StructReader &byte(unsigned char &byte) { - m_readers.append(new ByteReader(byte)); - return *this; - } - - /*! - * Read a unsigned 16 Bit integer into \a number. The byte order - * is controlled by \a bigEndian. - */ - StructReader &u16(unsigned short &number, bool bigEndian) { - m_readers.append(new U16Reader(number, bigEndian)); - return *this; - } - - /*! - * Read a unsigned 16 Bit little endian integer into \a number. - */ - StructReader &u16L(unsigned short &number) { - return u16(number, false); - } - - /*! - * Read a unsigned 16 Bit big endian integer into \a number. - */ - StructReader &u16B(unsigned short &number) { - return u16(number, true); - } - - /*! - * Read a unsigned 32 Bit integer into \a number. The byte order - * is controlled by \a bigEndian. - */ - StructReader &u32(unsigned int number, bool bigEndian) { - m_readers.append(new U32Reader(number, bigEndian)); - return *this; - } - - /*! - * Read a unsigned 32 Bit little endian integer into \a number. - */ - StructReader &u32L(unsigned int number) { - return u32(number, false); - } - - /*! - * Read a unsigned 32 Bit big endian integer into \a number. - */ - StructReader &u32B(unsigned int number) { - return u32(number, true); - } - - unsigned int size() const override { - unsigned int size = 0; - for (List::ConstIterator i = m_readers.begin(); - i != m_readers.end(); - ++i) { - size += (*i)->size(); - } - return size; - } - - unsigned int read(Strawberry_TagLib::TagLib::File &file, unsigned int limit) override { - unsigned int sumcount = 0; - for (List::ConstIterator i = m_readers.begin(); - limit > 0 && i != m_readers.end(); - ++i) { - unsigned int count = (*i)->read(file, limit); - limit -= count; - sumcount += count; - } - return sumcount; - } - - private: - List m_readers; -}; -} // namespace - -class XM::File::FilePrivate { - public: - explicit FilePrivate(AudioProperties::ReadStyle propertiesStyle) : properties(propertiesStyle) {} - - Mod::Tag tag; - XM::AudioProperties properties; -}; - -XM::File::File(FileName file, bool readProperties, - AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(file), - d(new FilePrivate(propertiesStyle)) { - if (isOpen()) - read(readProperties); -} - -XM::File::File(IOStream *stream, bool readProperties, - AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(stream), - d(new FilePrivate(propertiesStyle)) { - if (isOpen()) - read(readProperties); -} - -XM::File::~File() { - delete d; -} - -Mod::Tag *XM::File::tag() const { - return &d->tag; -} - -XM::AudioProperties *XM::File::audioProperties() const { - return &d->properties; -} - -bool XM::File::save() { - - if (readOnly()) { - debug("XM::File::save() - Cannot save to a read only file."); - return false; - } - - seek(17); - writeString(d->tag.title(), 20); - - seek(38); - writeString(d->tag.trackerName(), 20); - - seek(60); - unsigned int headerSize = 0; - if (!readU32L(headerSize)) - return false; - - seek(70); - unsigned short patternCount = 0; - unsigned short instrumentCount = 0; - if (!readU16L(patternCount) || !readU16L(instrumentCount)) - return false; - - long pos = 60 + headerSize; // should be long long in taglib2. - - // need to read patterns again in order to seek to the instruments: - for (unsigned short i = 0; i < patternCount; ++i) { - seek(pos); - unsigned int patternHeaderLength = 0; - if (!readU32L(patternHeaderLength) || patternHeaderLength < 4) - return false; - - seek(pos + 7); - unsigned short dataSize = 0; - if (!readU16L(dataSize)) - return false; - - pos += patternHeaderLength + dataSize; - } - - const StringList lines = d->tag.comment().split("\n"); - unsigned int sampleNameIndex = instrumentCount; - for (unsigned short i = 0; i < instrumentCount; ++i) { - seek(pos); - unsigned int instrumentHeaderSize = 0; - if (!readU32L(instrumentHeaderSize) || instrumentHeaderSize < 4) - return false; - - seek(pos + 4); - const unsigned int len = std::min(22U, instrumentHeaderSize - 4U); - if (i >= lines.size()) - writeString(String(), len); - else - writeString(lines[i], len); - - unsigned short sampleCount = 0; - if (instrumentHeaderSize >= 29U) { - seek(pos + 27); - if (!readU16L(sampleCount)) - return false; - } - - unsigned int sampleHeaderSize = 0; - if (sampleCount > 0) { - seek(pos + 29); - if (instrumentHeaderSize < 33U || !readU32L(sampleHeaderSize)) - return false; - } - - pos += instrumentHeaderSize; - - for (unsigned short j = 0; j < sampleCount; ++j) { - if (sampleHeaderSize > 4U) { - seek(pos); - unsigned int sampleLength = 0; - if (!readU32L(sampleLength)) - return false; - - if (sampleHeaderSize > 18U) { - seek(pos + 18); - const unsigned int len2 = std::min(sampleHeaderSize - 18U, 22U); - if (sampleNameIndex >= lines.size()) - writeString(String(), len2); - else - writeString(lines[sampleNameIndex++], len2); - } - } - pos += sampleHeaderSize; - } - } - - return true; - -} - -void XM::File::read(bool) { - - if (!isOpen()) - return; - - seek(0); - ByteVector magic = readBlock(17); - // it's all 0x00 for stripped XM files: - READ_ASSERT(magic == "Extended Module: " || magic == ByteVector(17, 0)); - - READ_STRING(d->tag.setTitle, 20); - READ_BYTE_AS(escape); - // in stripped XM files this is 0x00: - READ_ASSERT(escape == 0x1A || escape == 0x00); - - READ_STRING(d->tag.setTrackerName, 20); - READ_U16L(d->properties.setVersion); - - READ_U32L_AS(headerSize); - READ_ASSERT(headerSize >= 4); - - unsigned short length = 0; - unsigned short restartPosition = 0; - unsigned short channels = 0; - unsigned short patternCount = 0; - unsigned short instrumentCount = 0; - unsigned short flags = 0; - unsigned short tempo = 0; - unsigned short bpmSpeed = 0; - - StructReader header; - header.u16L(length) - .u16L(restartPosition) - .u16L(channels) - .u16L(patternCount) - .u16L(instrumentCount) - .u16L(flags) - .u16L(tempo) - .u16L(bpmSpeed); - - unsigned int count = header.read(*this, headerSize - 4U); - unsigned int size = std::min(headerSize - 4U, header.size()); - - READ_ASSERT(count == size); - - d->properties.setLengthInPatterns(length); - d->properties.setRestartPosition(restartPosition); - d->properties.setChannels(channels); - d->properties.setPatternCount(patternCount); - d->properties.setInstrumentCount(instrumentCount); - d->properties.setFlags(flags); - d->properties.setTempo(tempo); - d->properties.setBpmSpeed(bpmSpeed); - - seek(60 + headerSize); - - // read patterns: - for (unsigned short i = 0; i < patternCount; ++i) { - READ_U32L_AS(patternHeaderLength); - READ_ASSERT(patternHeaderLength >= 4); - - unsigned char packingType = 0; - unsigned short rowCount = 0; - unsigned short dataSize = 0; - StructReader pattern; - pattern.byte(packingType).u16L(rowCount).u16L(dataSize); - - unsigned int count2 = pattern.read(*this, patternHeaderLength - 4U); - READ_ASSERT(count2 == std::min(patternHeaderLength - 4U, pattern.size())); - - seek(patternHeaderLength - (4 + count2) + dataSize, Current); - } - - StringList intrumentNames; - StringList sampleNames; - unsigned int sumSampleCount = 0; - - // read instruments: - for (unsigned short i = 0; i < instrumentCount; ++i) { - READ_U32L_AS(instrumentHeaderSize); - READ_ASSERT(instrumentHeaderSize >= 4); - - String instrumentName; - unsigned char instrumentType = 0; - unsigned short sampleCount = 0; - - StructReader instrument; - instrument.string(instrumentName, 22).byte(instrumentType).u16L(sampleCount); - - // 4 for instrumentHeaderSize - unsigned int count2 = 4 + instrument.read(*this, instrumentHeaderSize - 4U); - READ_ASSERT(count2 == std::min(instrumentHeaderSize, instrument.size() + 4)); - - long offset = 0; - if (sampleCount > 0) { - unsigned int sampleHeaderSize = 0; - sumSampleCount += sampleCount; - // wouldn't know which header size to assume otherwise: - READ_ASSERT(instrumentHeaderSize >= count2 + 4 && readU32L(sampleHeaderSize)); - // skip unhandled header proportion: - seek(instrumentHeaderSize - count2 - 4, Current); - - for (unsigned short j = 0; j < sampleCount; ++j) { - unsigned int sampleLength = 0; - unsigned int loopStart = 0; - unsigned int loopLength = 0; - unsigned char volume = 0; - unsigned char finetune = 0; - unsigned char sampleType = 0; - unsigned char panning = 0; - unsigned char noteNumber = 0; - unsigned char compression = 0; - String sampleName; - StructReader sample; - sample.u32L(sampleLength) - .u32L(loopStart) - .u32L(loopLength) - .byte(volume) - .byte(finetune) - .byte(sampleType) - .byte(panning) - .byte(noteNumber) - .byte(compression) - .string(sampleName, 22); - - unsigned int count3 = sample.read(*this, sampleHeaderSize); - READ_ASSERT(count3 == std::min(sampleHeaderSize, sample.size())); - // skip unhandled header proportion: - seek(sampleHeaderSize - count3, Current); - - offset += sampleLength; - sampleNames.append(sampleName); - } - } - else { - offset = instrumentHeaderSize - count2; - } - intrumentNames.append(instrumentName); - seek(offset, Current); - } - - d->properties.setSampleCount(sumSampleCount); - String comment(intrumentNames.toString("\n")); - if (!sampleNames.isEmpty()) { - comment += "\n"; - comment += sampleNames.toString("\n"); - } - d->tag.setComment(comment); - -} diff --git a/3rdparty/taglib/xm/xmfile.h b/3rdparty/taglib/xm/xmfile.h deleted file mode 100644 index c559edb7..00000000 --- a/3rdparty/taglib/xm/xmfile.h +++ /dev/null @@ -1,95 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_XMFILE_H -#define TAGLIB_XMFILE_H - -#include "tfile.h" -#include "audioproperties.h" -#include "taglib_export.h" -#include "modfilebase.h" -#include "modtag.h" -#include "xmproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace XM { - -class TAGLIB_EXPORT File : public Mod::FileBase { - public: - /*! - * Constructs an Extended Module file from \a file. - * - * \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 an Extended Module file from \a stream. - * - * \note In the current implementation, both \a readProperties and \a propertiesStyle are ignored. - * The audio properties are always read. - * - * \note TagLib will *not* take ownership of the stream, the caller is responsible for deleting it after the File object. - */ - explicit File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); - - /*! - * Destroys this instance of the File. - */ - ~File() override; - - Mod::Tag *tag() const override; - - /*! - * Returns the XM::AudioProperties for this file. - * If no audio properties were read then this will return a null pointer. - */ - XM::AudioProperties *audioProperties() const override; - - /*! - * Save the file. - * This is the same as calling save(AllTags); - * - * \note Saving Extended Module tags is not supported. - */ - bool save() override; - - private: - File(const File&); - File &operator=(const File&); - - void read(bool readProperties); - - class FilePrivate; - FilePrivate *d; -}; - -} // namespace XM -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/taglib/xm/xmproperties.cpp b/3rdparty/taglib/xm/xmproperties.cpp deleted file mode 100644 index a364e529..00000000 --- a/3rdparty/taglib/xm/xmproperties.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - - -#include "xmproperties.h" - -using namespace Strawberry_TagLib::TagLib; -using namespace XM; - -class XM::AudioProperties::AudioPropertiesPrivate { - public: - explicit AudioPropertiesPrivate() : lengthInPatterns(0), - channels(0), - version(0), - restartPosition(0), - patternCount(0), - instrumentCount(0), - sampleCount(0), - flags(0), - tempo(0), - bpmSpeed(0) { - } - - unsigned short lengthInPatterns; - int channels; - unsigned short version; - unsigned short restartPosition; - unsigned short patternCount; - unsigned short instrumentCount; - unsigned int sampleCount; - unsigned short flags; - unsigned short tempo; - unsigned short bpmSpeed; -}; - -XM::AudioProperties::AudioProperties(AudioProperties::ReadStyle) : Strawberry_TagLib::TagLib::AudioProperties(), d(new AudioPropertiesPrivate()) {} - -XM::AudioProperties::~AudioProperties() { - delete d; -} - -int XM::AudioProperties::lengthInSeconds() const { - return 0; -} - -int XM::AudioProperties::lengthInMilliseconds() const { - return 0; -} - -int XM::AudioProperties::bitrate() const { - return 0; -} - -int XM::AudioProperties::sampleRate() const { - return 0; -} - -int XM::AudioProperties::channels() const { - return d->channels; -} - -unsigned short XM::AudioProperties::lengthInPatterns() const { - return d->lengthInPatterns; -} - -unsigned short XM::AudioProperties::version() const { - return d->version; -} - -unsigned short XM::AudioProperties::restartPosition() const { - return d->restartPosition; -} - -unsigned short XM::AudioProperties::patternCount() const { - return d->patternCount; -} - -unsigned short XM::AudioProperties::instrumentCount() const { - return d->instrumentCount; -} - -unsigned int XM::AudioProperties::sampleCount() const { - return d->sampleCount; -} - -unsigned short XM::AudioProperties::flags() const { - return d->flags; -} - -unsigned short XM::AudioProperties::tempo() const { - return d->tempo; -} - -unsigned short XM::AudioProperties::bpmSpeed() const { - return d->bpmSpeed; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void XM::AudioProperties::setLengthInPatterns(unsigned short lengthInPatterns) { - d->lengthInPatterns = lengthInPatterns; -} - -void XM::AudioProperties::setChannels(int channels) { - d->channels = channels; -} - -void XM::AudioProperties::setVersion(unsigned short version) { - d->version = version; -} - -void XM::AudioProperties::setRestartPosition(unsigned short restartPosition) { - d->restartPosition = restartPosition; -} - -void XM::AudioProperties::setPatternCount(unsigned short patternCount) { - d->patternCount = patternCount; -} - -void XM::AudioProperties::setInstrumentCount(unsigned short instrumentCount) { - d->instrumentCount = instrumentCount; -} - -void XM::AudioProperties::setSampleCount(unsigned int sampleCount) { - d->sampleCount = sampleCount; -} - -void XM::AudioProperties::setFlags(unsigned short flags) { - d->flags = flags; -} - -void XM::AudioProperties::setTempo(unsigned short tempo) { - d->tempo = tempo; -} - -void XM::AudioProperties::setBpmSpeed(unsigned short bpmSpeed) { - d->bpmSpeed = bpmSpeed; -} diff --git a/3rdparty/taglib/xm/xmproperties.h b/3rdparty/taglib/xm/xmproperties.h deleted file mode 100644 index d4f33dd3..00000000 --- a/3rdparty/taglib/xm/xmproperties.h +++ /dev/null @@ -1,89 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#ifndef TAGLIB_XMPROPERTIES_H -#define TAGLIB_XMPROPERTIES_H - -#include "taglib.h" -#include "tstring.h" -#include "audioproperties.h" - -namespace Strawberry_TagLib { -namespace TagLib { -namespace XM { - -class File; - -class TAGLIB_EXPORT AudioProperties : public Strawberry_TagLib::TagLib::AudioProperties { - - public: - /*! Flag bits. */ - enum { - LinearFreqTable = 1 // otherwise its the amiga freq. table - }; - - explicit AudioProperties(AudioProperties::ReadStyle); - ~AudioProperties() override; - - int lengthInSeconds() const override; - int lengthInMilliseconds() const override; - int bitrate() const override; - int sampleRate() const override; - int channels() const override; - - unsigned short lengthInPatterns() const; - unsigned short version() const; - unsigned short restartPosition() const; - unsigned short patternCount() const; - unsigned short instrumentCount() const; - unsigned int sampleCount() const; - unsigned short flags() const; - unsigned short tempo() const; - unsigned short bpmSpeed() const; - - private: - void setChannels(int channels); - - void setLengthInPatterns(unsigned short lengthInPatterns); - void setVersion(unsigned short version); - void setRestartPosition(unsigned short restartPosition); - void setPatternCount(unsigned short patternCount); - void setInstrumentCount(unsigned short instrumentCount); - void setSampleCount(unsigned int sampleCount); - void setFlags(unsigned short flags); - void setTempo(unsigned short tempo); - void setBpmSpeed(unsigned short bpmSpeed); - - private: - friend class File; - class AudioPropertiesPrivate; - AudioPropertiesPrivate *d; -}; - -} // namespace XM -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif diff --git a/3rdparty/utf8-cpp/CMakeLists.txt b/3rdparty/utf8-cpp/CMakeLists.txt deleted file mode 100644 index 03be826d..00000000 --- a/3rdparty/utf8-cpp/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -cmake_minimum_required(VERSION 3.0) diff --git a/3rdparty/utf8-cpp/checked.h b/3rdparty/utf8-cpp/checked.h deleted file mode 100644 index e3d04ddc..00000000 --- a/3rdparty/utf8-cpp/checked.h +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright 2006-2016 Nemanja Trifunovic - -/* -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -*/ - - -#ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 -#define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 - -#include "core.h" -#include - -namespace utf8 -{ - // Base for the exceptions that may be thrown from the library - class exception : public ::std::exception { - }; - - // Exceptions that may be thrown from the library functions. - class invalid_code_point : public exception { - uint32_t cp; - public: - explicit invalid_code_point(uint32_t codepoint) : cp(codepoint) {} - const char* what() const throw() override { return "Invalid code point"; } - uint32_t code_point() const {return cp;} - }; - - class invalid_utf8 : public exception { - uint8_t u8; - public: - explicit invalid_utf8 (uint8_t u) : u8(u) {} - const char* what() const throw() override { return "Invalid UTF-8"; } - uint8_t utf8_octet() const {return u8;} - }; - - class invalid_utf16 : public exception { - uint16_t u16; - public: - explicit invalid_utf16 (uint16_t u) : u16(u) {} - const char* what() const throw() override { return "Invalid UTF-16"; } - uint16_t utf16_word() const {return u16;} - }; - - class not_enough_room : public exception { - public: - const char* what() const throw() override { return "Not enough space"; } - }; - - /// The library API - functions intended to be called by the users - - template - octet_iterator append(uint32_t cp, octet_iterator result) - { - if (!utf8::internal::is_code_point_valid(cp)) - throw invalid_code_point(cp); - - if (cp < 0x80) // one octet - *(result++) = static_cast(cp); - else if (cp < 0x800) { // two octets - *(result++) = static_cast((cp >> 6) | 0xc0); - *(result++) = static_cast((cp & 0x3f) | 0x80); - } - else if (cp < 0x10000) { // three octets - *(result++) = static_cast((cp >> 12) | 0xe0); - *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); - *(result++) = static_cast((cp & 0x3f) | 0x80); - } - else { // four octets - *(result++) = static_cast((cp >> 18) | 0xf0); - *(result++) = static_cast(((cp >> 12) & 0x3f) | 0x80); - *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); - *(result++) = static_cast((cp & 0x3f) | 0x80); - } - return result; - } - - template - output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement) - { - while (start != end) { - octet_iterator sequence_start = start; - internal::utf_error err_code = utf8::internal::validate_next(start, end); - switch (err_code) { - case internal::UTF8_OK : - for (octet_iterator it = sequence_start; it != start; ++it) - *out++ = *it; - break; - case internal::NOT_ENOUGH_ROOM: - throw not_enough_room(); - case internal::INVALID_LEAD: - out = utf8::append (replacement, out); - ++start; - break; - case internal::INCOMPLETE_SEQUENCE: - case internal::OVERLONG_SEQUENCE: - case internal::INVALID_CODE_POINT: - out = utf8::append (replacement, out); - ++start; - // just one replacement mark for the sequence - while (start != end && utf8::internal::is_trail(*start)) - ++start; - break; - } - } - return out; - } - - template - inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out) - { - static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd); - return utf8::replace_invalid(start, end, out, replacement_marker); - } - - template - uint32_t next(octet_iterator& it, octet_iterator end) - { - uint32_t cp = 0; - internal::utf_error err_code = utf8::internal::validate_next(it, end, cp); - switch (err_code) { - case internal::UTF8_OK : - break; - case internal::NOT_ENOUGH_ROOM : - throw not_enough_room(); - case internal::INVALID_LEAD : - case internal::INCOMPLETE_SEQUENCE : - case internal::OVERLONG_SEQUENCE : - throw invalid_utf8(*it); - case internal::INVALID_CODE_POINT : - throw invalid_code_point(cp); - } - return cp; - } - - template - uint32_t peek_next(octet_iterator it, octet_iterator end) - { - return utf8::next(it, end); - } - - template - uint32_t prior(octet_iterator& it, octet_iterator start) - { - // can't do much if it == start - if (it == start) - throw not_enough_room(); - - octet_iterator end = it; - // Go back until we hit either a lead octet or start - while (utf8::internal::is_trail(*(--it))) - if (it == start) - throw invalid_utf8(*it); // error - no lead byte in the sequence - return utf8::peek_next(it, end); - } - - /// Deprecated in versions that include "prior" - template - uint32_t previous(octet_iterator& it, octet_iterator pass_start) - { - octet_iterator end = it; - while (utf8::internal::is_trail(*(--it))) - if (it == pass_start) - throw invalid_utf8(*it); // error - no lead byte in the sequence - octet_iterator temp = it; - return utf8::next(temp, end); - } - - template - void advance (octet_iterator& it, distance_type n, octet_iterator end) - { - for (distance_type i = 0; i < n; ++i) - utf8::next(it, end); - } - - template - typename std::iterator_traits::difference_type - distance (octet_iterator first, octet_iterator last) - { - typename std::iterator_traits::difference_type dist; - for (dist = 0; first < last; ++dist) - utf8::next(first, last); - return dist; - } - - template - octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) - { - while (start != end) { - uint32_t cp = utf8::internal::mask16(*start++); - // Take care of surrogate pairs first - if (utf8::internal::is_lead_surrogate(cp)) { - if (start != end) { - uint32_t trail_surrogate = utf8::internal::mask16(*start++); - if (utf8::internal::is_trail_surrogate(trail_surrogate)) - cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; - else - throw invalid_utf16(static_cast(trail_surrogate)); - } - else - throw invalid_utf16(static_cast(cp)); - - } - // Lone trail surrogate - else if (utf8::internal::is_trail_surrogate(cp)) - throw invalid_utf16(static_cast(cp)); - - result = utf8::append(cp, result); - } - return result; - } - - template - u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) - { - while (start < end) { - uint32_t cp = utf8::next(start, end); - if (cp > 0xffff) { //make a surrogate pair - *result++ = static_cast((cp >> 10) + internal::LEAD_OFFSET); - *result++ = static_cast((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); - } - else - *result++ = static_cast(cp); - } - return result; - } - - template - octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) - { - while (start != end) - result = utf8::append(*(start++), result); - - return result; - } - - template - u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) - { - while (start < end) - (*result++) = utf8::next(start, end); - - return result; - } - - // The iterator class - template - class iterator : public std::iterator { - octet_iterator it; - octet_iterator range_start; - octet_iterator range_end; - public: - iterator () {} - explicit iterator (const octet_iterator& octet_it, - const octet_iterator& rangestart, - const octet_iterator& rangeend) : - it(octet_it), range_start(rangestart), range_end(rangeend) - { - if (it < range_start || it > range_end) - throw std::out_of_range("Invalid utf-8 iterator position"); - } - // the default "big three" are OK - octet_iterator base () const { return it; } - uint32_t operator * () const - { - octet_iterator temp = it; - return utf8::next(temp, range_end); - } - bool operator == (const iterator& rhs) const - { - if (range_start != rhs.range_start || range_end != rhs.range_end) - throw std::logic_error("Comparing utf-8 iterators defined with different ranges"); - return (it == rhs.it); - } - bool operator != (const iterator& rhs) const - { - return !(operator == (rhs)); - } - iterator& operator ++ () - { - utf8::next(it, range_end); - return *this; - } - iterator operator ++ (int) - { - iterator temp = *this; - utf8::next(it, range_end); - return temp; - } - iterator& operator -- () - { - utf8::prior(it, range_start); - return *this; - } - iterator operator -- (int) - { - iterator temp = *this; - utf8::prior(it, range_start); - return temp; - } - }; // class iterator - -} // namespace utf8 - -#endif //header guard - - diff --git a/3rdparty/utf8-cpp/core.h b/3rdparty/utf8-cpp/core.h deleted file mode 100644 index ae0f367d..00000000 --- a/3rdparty/utf8-cpp/core.h +++ /dev/null @@ -1,332 +0,0 @@ -// Copyright 2006 Nemanja Trifunovic - -/* -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -*/ - - -#ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 -#define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 - -#include - -namespace utf8 -{ - // The typedefs for 8-bit, 16-bit and 32-bit unsigned integers - // You may need to change them to match your system. - // These typedefs have the same names as ones from cstdint, or boost/cstdint - typedef unsigned char uint8_t; - typedef unsigned short uint16_t; - typedef unsigned int uint32_t; - -// Helper code - not intended to be directly called by the library users. May be changed at any time -namespace internal -{ - // Unicode constants - // Leading (high) surrogates: 0xd800 - 0xdbff - // Trailing (low) surrogates: 0xdc00 - 0xdfff - const uint16_t LEAD_SURROGATE_MIN = 0xd800u; - const uint16_t LEAD_SURROGATE_MAX = 0xdbffu; - const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u; - const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu; - const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10); - const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN; - - // Maximum valid value for a Unicode code point - const uint32_t CODE_POINT_MAX = 0x0010ffffu; - - template - inline uint8_t mask8(octet_type oc) - { - return static_cast(0xff & oc); - } - template - inline uint16_t mask16(u16_type oc) - { - return static_cast(0xffff & oc); - } - template - inline bool is_trail(octet_type oc) - { - return ((utf8::internal::mask8(oc) >> 6) == 0x2); - } - - template - inline bool is_lead_surrogate(u16 cp) - { - return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX); - } - - template - inline bool is_trail_surrogate(u16 cp) - { - return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); - } - - template - inline bool is_surrogate(u16 cp) - { - return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); - } - - template - inline bool is_code_point_valid(u32 cp) - { - return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp)); - } - - template - inline typename std::iterator_traits::difference_type - sequence_length(octet_iterator lead_it) - { - uint8_t lead = utf8::internal::mask8(*lead_it); - if (lead < 0x80) - return 1; - else if ((lead >> 5) == 0x6) - return 2; - else if ((lead >> 4) == 0xe) - return 3; - else if ((lead >> 3) == 0x1e) - return 4; - else - return 0; - } - - template - inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length) - { - if (cp < 0x80) { - if (length != 1) - return true; - } - else if (cp < 0x800) { - if (length != 2) - return true; - } - else if (cp < 0x10000) { - if (length != 3) - return true; - } - - return false; - } - - enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT}; - - /// Helper for get_sequence_x - template - utf_error increase_safely(octet_iterator& it, octet_iterator end) - { - if (++it == end) - return NOT_ENOUGH_ROOM; - - if (!utf8::internal::is_trail(*it)) - return INCOMPLETE_SEQUENCE; - - return UTF8_OK; - } - - #define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;} - - /// get_sequence_x functions decode utf-8 sequences of the length x - template - utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point) - { - if (it == end) - return NOT_ENOUGH_ROOM; - - code_point = utf8::internal::mask8(*it); - - return UTF8_OK; - } - - template - utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point) - { - if (it == end) - return NOT_ENOUGH_ROOM; - - code_point = utf8::internal::mask8(*it); - - UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) - - code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f); - - return UTF8_OK; - } - - template - utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point) - { - if (it == end) - return NOT_ENOUGH_ROOM; - - code_point = utf8::internal::mask8(*it); - - UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) - - code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); - - UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) - - code_point += (*it) & 0x3f; - - return UTF8_OK; - } - - template - utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point) - { - if (it == end) - return NOT_ENOUGH_ROOM; - - code_point = utf8::internal::mask8(*it); - - UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) - - code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); - - UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) - - code_point += (utf8::internal::mask8(*it) << 6) & 0xfff; - - UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) - - code_point += (*it) & 0x3f; - - return UTF8_OK; - } - - #undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR - - template - utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point) - { - if (it == end) - return NOT_ENOUGH_ROOM; - - // Save the original value of it so we can go back in case of failure - // Of course, it does not make much sense with i.e. stream iterators - octet_iterator original_it = it; - - uint32_t cp = 0; - // Determine the sequence length based on the lead octet - typedef typename std::iterator_traits::difference_type octet_difference_type; - const octet_difference_type length = utf8::internal::sequence_length(it); - - // Get trail octets and calculate the code point - utf_error err = UTF8_OK; - switch (length) { - case 0: - return INVALID_LEAD; - case 1: - err = utf8::internal::get_sequence_1(it, end, cp); - break; - case 2: - err = utf8::internal::get_sequence_2(it, end, cp); - break; - case 3: - err = utf8::internal::get_sequence_3(it, end, cp); - break; - case 4: - err = utf8::internal::get_sequence_4(it, end, cp); - break; - } - - if (err == UTF8_OK) { - // Decoding succeeded. Now, security checks... - if (utf8::internal::is_code_point_valid(cp)) { - if (!utf8::internal::is_overlong_sequence(cp, length)){ - // Passed! Return here. - code_point = cp; - ++it; - return UTF8_OK; - } - else - err = OVERLONG_SEQUENCE; - } - else - err = INVALID_CODE_POINT; - } - - // Failure branch - restore the original value of the iterator - it = original_it; - return err; - } - - template - inline utf_error validate_next(octet_iterator& it, octet_iterator end) { - uint32_t ignored; - return utf8::internal::validate_next(it, end, ignored); - } - -} // namespace internal - - /// The library API - functions intended to be called by the users - - // Byte order mark - const uint8_t bom[] = {0xef, 0xbb, 0xbf}; - - template - octet_iterator find_invalid(octet_iterator start, octet_iterator end) - { - octet_iterator result = start; - while (result != end) { - utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end); - if (err_code != internal::UTF8_OK) - return result; - } - return result; - } - - template - inline bool is_valid(octet_iterator start, octet_iterator end) - { - return (utf8::find_invalid(start, end) == end); - } - - template - inline bool starts_with_bom (octet_iterator it, octet_iterator end) - { - return ( - ((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) && - ((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) && - ((it != end) && (utf8::internal::mask8(*it)) == bom[2]) - ); - } - - //Deprecated in release 2.3 - template - inline bool is_bom (octet_iterator it) - { - return ( - (utf8::internal::mask8(*it++)) == bom[0] && - (utf8::internal::mask8(*it++)) == bom[1] && - (utf8::internal::mask8(*it)) == bom[2] - ); - } -} // namespace utf8 - -#endif // header guard - - diff --git a/CMakeLists.txt b/CMakeLists.txt index 52240cf5..479f4272 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -234,28 +234,15 @@ else() endif() # TAGLIB -option(USE_SYSTEM_TAGLIB "Use system taglib" OFF) -if(USE_SYSTEM_TAGLIB) - pkg_check_modules(TAGLIB REQUIRED taglib>=1.11.1) - message(WARNING "Using system taglib library.") - find_path(HAVE_TAGLIB_DSFFILE_H taglib/dsffile.h) - find_path(HAVE_TAGLIB_DSDIFFFILE_H taglib/dsdifffile.h) - if(HAVE_TAGLIB_DSFFILE_H) - set(HAVE_TAGLIB_DSFFILE ON) - endif(HAVE_TAGLIB_DSFFILE_H) - if(HAVE_TAGLIB_DSDIFFFILE_H) - set(HAVE_TAGLIB_DSDIFFFILE ON) - endif(HAVE_TAGLIB_DSDIFFFILE_H) -else(USE_SYSTEM_TAGLIB) - set(TAGLIB_INCLUDE_DIRS "${CMAKE_BINARY_DIR}/3rdparty/taglib/headers/taglib/;${CMAKE_BINARY_DIR}/3rdparty/taglib/headers/") - set(TAGLIB_LIBRARY_DIRS "") - set(TAGLIB_LIBRARIES tag) - add_subdirectory(3rdparty/utf8-cpp) - add_subdirectory(3rdparty/taglib) +pkg_check_modules(TAGLIB REQUIRED taglib>=1.11.1) +find_path(HAVE_TAGLIB_DSFFILE_H taglib/dsffile.h) +find_path(HAVE_TAGLIB_DSDIFFFILE_H taglib/dsdifffile.h) +if(HAVE_TAGLIB_DSFFILE_H) set(HAVE_TAGLIB_DSFFILE ON) +endif(HAVE_TAGLIB_DSFFILE_H) +if(HAVE_TAGLIB_DSDIFFFILE_H) set(HAVE_TAGLIB_DSDIFFFILE ON) - add_definitions(-DTAGLIB_STATIC) -endif(USE_SYSTEM_TAGLIB) +endif(HAVE_TAGLIB_DSDIFFFILE_H) # SingleApplication add_subdirectory(3rdparty/singleapplication) @@ -440,14 +427,10 @@ if(HAVE_MOODBAR) endif() option(BUILD_TESTS "Build the test suite" OFF) -option(BUILD_TAGLIB_TESTS "Build the test suite" OFF) if(BUILD_TESTS) add_subdirectory(tests) endif(BUILD_TESTS) -if(NOT USE_SYSTEM_TAGLIB AND BUILD_TAGLIB_TESTS) - add_subdirectory(tests/taglib) -endif(NOT USE_SYSTEM_TAGLIB AND BUILD_TAGLIB_TESTS) # Uninstall support configure_file( @@ -470,6 +453,6 @@ if(NOT SQLITE3_FTS5 AND NOT CMAKE_CROSSCOMPILING) message(WARNING "sqlite3 must be enabled with FTS5. See: https://www.sqlite.org/fts5.html") endif() -if(USE_SYSTEM_TAGLIB AND NOT TAGLIB_VERSION VERSION_GREATER 1.11.2) - message(WARNING "Using system taglib library. There is a critical bug in the current latest version of TagLib (1.11.1) that can corrupt Ogg files, make sure your systems version has been patched, see: https://github.com/taglib/taglib/issues/864, TagLib upstream is currently not maintained. Do not set USE_SYSTEM_TAGLIB unless you are prepared keep the TagLib in your system up to date with critical fixes.") +if(NOT TAGLIB_VERSION VERSION_GREATER_EQUAL 1.12) + message(WARNING "There is a critical bug in TagLib (1.11.1) that can result in corrupt Ogg files, see: https://github.com/taglib/taglib/issues/864, please consider updating TagLib to the newest version.") endif() diff --git a/README.md b/README.md index 2778fb43..0dd5a890 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ To build Strawberry from source you need the following installed on your system * [PulseAudio (linux optional)](https://www.freedesktop.org/wiki/Software/PulseAudio/?) * [GStreamer](https://gstreamer.freedesktop.org/) or [VLC](https://www.videolan.org) * [GnuTLS](https://www.gnutls.org/) +* [TagLib](https://www.taglib.org/) Optional dependencies: diff --git a/debian/copyright b/debian/copyright index 23037e02..380a609e 100644 --- a/debian/copyright +++ b/debian/copyright @@ -370,182 +370,6 @@ Files: 3rdparty/singleapplication/* Copyright: 2015-2018, Itay Grudev License: MIT -Files: 3rdparty/taglib/* -Copyright: 2002-2008, Scott Wheeler -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/ape/* -Copyright: 2003-2005, Allan Sandfeld Jensen -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/ape/apefile.cpp - 3rdparty/taglib/ape/apefile.h -Copyright: 2010, Alex Novichkov - 2006, Lukáš Lalinský - 2004, Allan Sandfeld Jensen -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/ape/apefooter.cpp -Copyright: 2004, Allan Sandfeld Jensen - 2002-2008, Scott Wheeler (id3v2header.cpp) -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/ape/apeproperties.cpp - 3rdparty/taglib/ape/apeproperties.h -Copyright: 2010, Alex Novichkov - 2006, Lukáš Lalinský -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/asf/* -Copyright: 2005-2007, 2009-2011, Lukáš Lalinský -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/asf/asfpicture.cpp - 3rdparty/taglib/asf/asfpicture.h -Copyright: 2010, Anton Sergunov -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/asf/asfutils.h -Copyright: 2012, 2013, 2015, 2016, Tsuda Kageyu -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/dsdiff/* -Copyright: 2016, Damien Plisson, Audirvana -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/dsf/* -Copyright: 2013-2018, Stephen F. Booth -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/fileref.cpp -Copyright: 2010, Alex Novichkov - 2002-2008, Scott Wheeler -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/flac/* - 3rdparty/taglib/mp4/* -Copyright: 2005-2007, 2009-2011, Lukáš Lalinský -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/flac/flacfile.cpp - 3rdparty/taglib/flac/flacfile.h - 3rdparty/taglib/flac/flacproperties.cpp - 3rdparty/taglib/flac/flacproperties.h -Copyright: 2003-2005, Allan Sandfeld Jensen -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/it/* - 3rdparty/taglib/mod/* -Copyright: 2011, Mathias Panzenböck -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/mpc/* -Copyright: 2003-2005, Allan Sandfeld Jensen -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/mpeg/id3v2/frames/chapterframe.cpp - 3rdparty/taglib/mpeg/id3v2/frames/chapterframe.h - 3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp - 3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.h -Copyright: 2013, Lukas Krejci -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp - 3rdparty/taglib/mpeg/id3v2/frames/eventtimingcodesframe.h - 3rdparty/taglib/mpeg/id3v2/frames/podcastframe.cpp - 3rdparty/taglib/mpeg/id3v2/frames/podcastframe.h - 3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp - 3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.h -Copyright: 2014, 2015, Urs Fleisch -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp - 3rdparty/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.h -Copyright: 2006, Aaron VonderHaar - 2002-2008, Scott Wheeler -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/mpeg/id3v2/frames/ownershipframe.cpp - 3rdparty/taglib/mpeg/id3v2/frames/ownershipframe.h -Copyright: 2012, Rupert Daniel -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/mpeg/id3v2/frames/popularimeterframe.cpp - 3rdparty/taglib/mpeg/id3v2/frames/popularimeterframe.h -Copyright: 2008, 2011, Lukas Lalinsky -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/mpeg/id3v2/frames/privateframe.cpp - 3rdparty/taglib/mpeg/id3v2/frames/privateframe.h -Copyright: 2008, Serkan Kalyoncu - 2008, Scott Wheeler -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp - 3rdparty/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h - 3rdparty/taglib/mpeg/id3v2/frames/urllinkframe.cpp - 3rdparty/taglib/mpeg/id3v2/frames/urllinkframe.h -Copyright: 2006, Urs Fleisch - 2002-2008, Scott Wheeler -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/mpeg/xingheader.cpp - 3rdparty/taglib/mpeg/xingheader.h -Copyright: 2003, Ismael Orenstein -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/ogg/flac/* -Copyright: 2003-2005, Allan Sandfeld Jensen -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/ogg/opus/* - 3rdparty/taglib/ogg/speex/* -Copyright: 2006, 2012, Lukáš Lalinský - 2002-2008, Scott Wheeler -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/riff/riffutils.h - 3rdparty/taglib/riff/wav/infotag.cpp - 3rdparty/taglib/riff/wav/infotag.h - 3rdparty/taglib/tagutils.cpp - 3rdparty/taglib/tagutils.h - 3rdparty/taglib/toolkit/tdebuglistener.cpp - 3rdparty/taglib/toolkit/tdebuglistener.h - 3rdparty/taglib/toolkit/trefcounter.cpp - 3rdparty/taglib/toolkit/trefcounter.h - 3rdparty/taglib/toolkit/tutils.h - 3rdparty/taglib/toolkit/tzlib.cpp - 3rdparty/taglib/toolkit/tzlib.h - 3rdparty/taglib/mpeg/mpegutils.h -Copyright: 2012, 2013, 2015, 2016, Tsuda Kageyu -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/s3m/* - 3rdparty/taglib/xm/* -Copyright: 2011, Mathias Panzenböck -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/toolkit/tbytevectorstream.cpp - 3rdparty/taglib/toolkit/tbytevectorstream.h - 3rdparty/taglib/toolkit/tiostream.cpp - 3rdparty/taglib/toolkit/tiostream.h -Copyright: 2008, 2011, Lukas Lalinsky -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/toolkit/tpropertymap.cpp - 3rdparty/taglib/toolkit/tpropertymap.h -Copyright: 2012, Michael Helmling -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/taglib/trueaudio/* - 3rdparty/taglib/wavpack/* -Copyright: 2006, Lukáš Lalinský - 2004, Allan Sandfeld Jensen -License: LGPL-2.1 and/or MPL-1.1 - -Files: 3rdparty/utf8-cpp/* -Copyright: 2006-2016, Nemanja Trifunovic -License: BSL License: LGPL-2.1 This library is free software; you can redistribute it and/or diff --git a/ext/libstrawberry-tagreader/tagreader.h b/ext/libstrawberry-tagreader/tagreader.h index b69c495a..4a78a9d1 100644 --- a/ext/libstrawberry-tagreader/tagreader.h +++ b/ext/libstrawberry-tagreader/tagreader.h @@ -35,10 +35,6 @@ #include "tagreadermessages.pb.h" -#ifndef USE_SYSTEM_TAGLIB -using namespace Strawberry_TagLib; -#endif - class FileRefFactory; /** diff --git a/src/config.h.in b/src/config.h.in index d0d6fece..4f9f3802 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -53,7 +53,6 @@ #cmakedefine HAVE_KEYSYMDEF_H #cmakedefine HAVE_XF86KEYSYM_H -#cmakedefine USE_SYSTEM_TAGLIB #cmakedefine HAVE_TAGLIB_DSFFILE #cmakedefine HAVE_TAGLIB_DSDIFFFILE diff --git a/src/core/song.cpp b/src/core/song.cpp index 2eb36ea7..37d5b3ca 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -67,10 +67,6 @@ #include "collection/sqlrow.h" #include "tagreadermessages.pb.h" -#ifndef USE_SYSTEM_TAGLIB -using namespace Strawberry_TagLib; -#endif - const QStringList Song::kColumns = QStringList() << "title" << "album" << "artist" diff --git a/tests/taglib/CMakeLists.txt b/tests/taglib/CMakeLists.txt deleted file mode 100644 index 68b602b4..00000000 --- a/tests/taglib/CMakeLists.txt +++ /dev/null @@ -1,91 +0,0 @@ -enable_testing() -find_package(CppUnit REQUIRED) - -SET(TAGLIB_TESTS_SOURCES - main.cpp - test_list.cpp - test_map.cpp - test_mpeg.cpp - test_synchdata.cpp - test_trueaudio.cpp - test_bytevector.cpp - test_bytevectorlist.cpp - test_bytevectorstream.cpp - test_string.cpp - test_propertymap.cpp - test_file.cpp - test_fileref.cpp - test_id3v1.cpp - test_id3v2.cpp - test_xiphcomment.cpp - test_aiff.cpp - test_riff.cpp - test_ogg.cpp - test_oggflac.cpp - test_flac.cpp - test_flacpicture.cpp - test_flacunknownmetadatablock.cpp - test_ape.cpp - test_apetag.cpp - test_wav.cpp - test_info.cpp - test_wavpack.cpp - test_mp4.cpp - test_mp4item.cpp - test_mp4coverart.cpp - test_asf.cpp - test_mod.cpp - test_s3m.cpp - test_it.cpp - test_xm.cpp - test_mpc.cpp - test_opus.cpp - test_speex.cpp - test_dsf.cpp - test_dsdiff.cpp -) - -link_directories(${TAGLIB_LIBRARY_DIRS}) - -add_executable(taglib_tests ${TAGLIB_TESTS_SOURCES}) - -target_include_directories(taglib_tests PRIVATE - ${CPPUNIT_INCLUDE_DIR} - ${TAGLIB_INCLUDE_DIRS} - ${CMAKE_CURRENT_BINARY_DIR}/../../3rdparty/taglib - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/toolkit - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/ape - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/asf - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/mpeg/id3v1 - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/mpeg/id3v2 - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/mpeg/id3v2/frames - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/mpeg - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/mpc - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/mp4 - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/riff - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/riff/aiff - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/riff/wav - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/trueaudio - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/ogg - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/ogg/vorbis - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/ogg/flac - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/ogg/speex - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/ogg/opus - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/flac - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/wavpack - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/mod - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/s3m - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/it - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/xm - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/dsf - ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/taglib/dsdiff -) - -target_link_libraries(taglib_tests PRIVATE - ${CPPUNIT_LIBRARIES} - ${TAGLIB_LIBRARIES} -) - -add_test(taglib_tests taglib_tests) -add_custom_target(run_taglib_tests COMMAND ${CMAKE_CTEST_COMMAND} -V DEPENDS taglib_tests) diff --git a/tests/taglib/data/005411.id3 b/tests/taglib/data/005411.id3 deleted file mode 100644 index ab2e0997a0bcbf27aa169a09fd3f730d8319de9d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38402 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 AdjJ3c diff --git a/tests/taglib/data/64bit.mp4 b/tests/taglib/data/64bit.mp4 deleted file mode 100644 index 0bd7f9f33dfa28317996eab962cce5cb2d2afc0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 85 zcmZQzU|`J6&o5(u0AC=*SejCj2<6*?__?WYzAjW;F*B#Q1jv?5F38LQ(t;_8U^Sv3 Gc}4)(BMUG9 diff --git a/tests/taglib/data/alaw.aifc b/tests/taglib/data/alaw.aifc deleted file mode 100644 index 33b4ea2a5734c10af92aa556de6c50aa102efac6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1890 zcmX|B2~d+~8vb*iBm@uwf(e)eaX^Vl{)7;~EUlJSP=Q*Zvn~oOpa_aq?d(j=eIJC| zaD+e(?1G$0AV6@{+3u+9+6ubuw2Kr$%28cfS9i3#`_plKXTF*5d*0`J-s_uh-@)8$ z0KgQAv-V}~JMwNW0077?$>F`32eTm@NCaR2fIb2Q?a zKcBvT7oW|hi<2skU(6QgegF~P2aub$004iYw2Xd^URhmz1_0o{BBfO&ZxO^>PR$z* z@-62Ll9iK}4`E><0Jsdhyy=CMH5b`&Sjp))nJ`pl;_ z6=mh;>Fksgb|U@%{v2`kyO1AuvAVkCL}hjHDIhN|2dW8u3_^dk**7*b^Kd3_U)FCI zlq!laDQPJ+ISExx0J&9OfuK9vuW`Fj?E_q7&M|5v|8N zJnHJ}bE=3Lse+U=He0}ArH6X_F1vetK;xV8PpAO_9m*w{NCx+S(ZuB%tOC#+?mkw^qLB{nfJR)qOMEjMZg$Gflf^f^6$hJ&1B zPDvZ_1hJB`bRC?0RoxA8l4!k_y;&JwV{y7vvcaO*gMe&I{5)xS=Oq1R_ zG-~hfx!ULUm4PWjA(IuCm_$z{{NnSwT%BG0*ZMmr8o=1Z-El0YkjWHsqD<~hy4^Y9 zH8n?Z81%R}db}`=Po&fhxcYl~dR>#ohTKqWI(>J-t~e&|c)iLnGUe{NcCE{)seu}l zA&iIautaE!XDBf4bh|n`9m7|m*eOf~BR)Q!kxqK$3AkM@$9Q03K+}K%1@W;gW{N18 zhrDhaa@srXj=+FUeFMeKV8qg6nIb+=S+ALNLSuD}jVKFHygeyQ7Cn}cMmpK98xHs# z(4fY23rN050M*A}q;i0l+DWIw;SWqr`XoOi`58i{P>`OH1_l*dy`yfs9SS~Vc!37f z8LU_qL%_iWHyQ@V98glDLmtIl8W|J`p!l$m7(9qbCL)kw&pM=1wW8J5-rjDRKO3VI&>_K`WYjw7k65Xj93h*4p(O7fUan zF9}8yNko8JIlZvUGHHS2tvZEbm779NQvbCdUQ_;AY2<_@LG zsNeLM>o3#LP`BA>86ptH|QPF7>sgN`^HH$ zmb;1bNSLYxg~mHD;Psegs+QG*F+?y;#Np#$FL7(Qqu6izmaV z71vkmuc)*>?}%2?v3NWt3JH+0SR6DyYRQ^art^7ydYPj3<@u@*43q&I-Ld-M6cr%hxjX_1i2jX5W!7p*(Nfo;JzG+I{15Ma_0Ly}|GD$s%vaZ+zxes~7y0{h z4v60oAIv(ixA4&1%^Tm`fAHmZU*Ebpee3?=V;_HfwBT4l!S8ZEdU)^Nmk(!VXa5J= CPx9XY diff --git a/tests/taglib/data/alaw.wav b/tests/taglib/data/alaw.wav deleted file mode 100644 index cf548effcb450bf6a22521c2e22645597bec2d21..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56858 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 g_Q`M@ERUWh5hHq(~w=J0he= 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 TQ{Lgz37b4<6&cL`>)-zY=z)&) diff --git a/tests/taglib/data/blank_video.m4v b/tests/taglib/data/blank_video.m4v deleted file mode 100644 index 4bb15ded33f50b99b8887adbc62b2236fa18b860..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15018 zcmeI2?`s@I7{{O8q^4rAXSvjfQ8y5%7O%{OgcPZ8sX<$)=o_VqN-nqCyQ8<+b9e7F zvFg02(3iebTZMoH75odtA1C-iMEXJm!BsZzMwD5afM#-$r_uIOd|Gv6FBM2Sk~U#4lkJ+2 zYdkHPmZ_nzCHAqUEVG^)Q^V6DtWX;qhf*mv7?Ogp}t0HM63AD34Ee*fXQ z@5lm`mfH!H!mobY^-@jo^Oz2f)z{pPM`f%({Px-@6y?6JD$RC#)=l!J-O!^l6exdB zFYGvpwmy#H)eh=UqBv4lDY3(Q^?sv@nnJctWw0d<`xr_(C=^O0=FIoh5HAH(u+}H6 zqk{*Bzrp?{#=qL~n?KJ2?7awMpUER9zmOJ;w=Nx#Sh&0zhse_YF{x&zgz;1qE6!x1 zMjrr}_zB}Oc3)lik)84$Mq2}83N{{OqiatwrSBf;b69QzbsDfv)#hDjxS0oztr3hm zf&JTk6=3!R_T__||5|z?sd3;bIGHkm2B=1vvE|dXY#!JPdEeA+e2#PDQyBT?iL7r% zL2oTv&G9IB+Cv|Ao@{qK=h-lDdkxQZR@a@Uf}q*)9(5Kf)oNu7to}c{O8Zn< zl~!MOSmZ2u$2*8;Qnty-_M<&@vhqlOnR7Bc-@ispY1Y11-0<-aj1N=1a;A4Ntr=Q(`K4WJ12iSn^PCkF>#53{+%~v6!%Q8H)i6 zQz=3LA!BxyA{QZFXDkLRZ!#4F71?JjCM+RiF<^<9ih&C6<%&>n%`K9t?l;!=Rr$H%bJ!e zT1qW9wH#=NprVnYfs_ zS$J4^+4$J`|6>4720jL11_=gPBu%)O4Gp+tK;jNSyjlTBAc2sK%wh!~O3YKp%quP_ zD$PyJD^bWx%uQ89QiYxA02D?y&o@7%G$&OdEfYlK7v(0F6l2$nMHHKPiAkl!spw84 o(nzf46)WVH=9FaSWagzRB5YV!#en6q%O&5V?)%?IDR#Jf_6-$P0O7rLKjODm-Uu07V zcm`V!&sdj&QEr5;*ctrbL=>d>0FQq!HwF=(qXg6O# z!noGnvIK)N?hy32Rk(+8bBJUxE?Tt?9`%zMjcohDR2xHL-uT*L3~4b=-u)Pq;N-6% oNkStK1VVC%C_$7UN=O7m38Dm1LQg>KAa>}*ApZ$shZD8K2XpUN-T(jq diff --git a/tests/taglib/data/click.mpc b/tests/taglib/data/click.mpc deleted file mode 100644 index a41f14e9ea2175684ff2ab74d3682ddbd0785765..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1588 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 diff --git a/tests/taglib/data/covr-junk.m4a b/tests/taglib/data/covr-junk.m4a deleted file mode 100644 index ac80cb29d2585e9d728a86b5027973690f51d174..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5108 zcmeHK-)qxw5Wgf{e{Jnj?c7gBwr+EX>$Gmjbg*@+oe|w+6ciC=)}*ekX-1O`6_MhT zBErBui7@d&7>dvOAXFF#J}Wo};-i0nuM+Rl)U8DXVW1EBw8`D~^8Mc3_ru*cEdaD+ zj2lbp(ohT#B(u7v0@S@$Wy$#B0?1|OGm?}Rjz(ic02A;~aK&J%3&d5n6(U7PaY%3~ z!RZ303;u^L00>4@Oh~~@2qLmBtE0;&^7V@oR5oF<*bA* z1XZrtFT6+qd%I-2?^=*^g-R%0Th3Q>TXOHzY(9enURTSG><2w^+1?=+6naje6dRTc zIiaj`_jO}Fhx%3B&@Ix-cDR6VR7#Gpvn>{QGGKayV~SoTk3;gh(cnTE9&Z9@!yw-P z>gVgw!tUeUC>Y{f&<(@l8+975<02Fkf-T{-hH2a+gN-LB(HZ!h%i-W3)j&ViWEx`= zsAj1h4%?E7x5L)4w$mEw1hNY_^$h?g;_xGl)77<)eyrNZ9w7K4%d#P%Y*!VM7OHw5 z&x?Yzv_$P%0lSvX=?0FT4j@o=sETAA5NS<~(qtu?O)cjYYPxVD|C#_*%5;j@!t9Aser%Dk7__XotQ;Q6tiBs|M>8T(mo(2 z;;HM8)3?^&mk^tDD*=Rj%*E$-w=KrSQ`3o;6IbqFm_gfp09S(f$@~iZnWUHmhG9U) z518992|l;S>v8+MUa#Nps}D9e1_Oa$N4P1}+}#<8bae_s@1fy=-h;7zL5PkG#*UAS zj*j*WOeEtclEWuQN5~1|_xpnl!S=?+_K`lJZ{)9GmLTNC{jwa>4Qz zz6%~ShxuA7&}7jreQ|_4eI$ yky3O+h!>2kRcc)uhDNNn?w*p2$0zN7TJB4kf}&tz4jcv?1{?+)1{?w^X zB5sypBpv{lRIYpTW<;JiaUwFWpML(|Pq*#eAD=vX`t;wQf12y>#n|M3R9iwNNTi^I+4wl^KlfSvIznPcO>+Lf37x_J}r}*Fw zOcz<@&|kdh*VPY=Ni@A?`@fCVZF%Qvd@uqI#}a>|2;y83(adFSxCjsX6Bqz7AJ@Fz z*}>A8u1d^ua~}JA@)zu|HT-P3yQxSAt5#W|XM$#;?zQ9f5L^#~?+_Aib4Au}8xwr8 z(p{XOTlw3cX6E^cuZ3<<-i>#%2>WtzcLbHxx_L_8YF1(~TQkBaalwL`7+%Jk_{SIQ zmHOgoqJc#^@cro4Lgjm_{pPSiC9b zgBYLM%C+8GM^$5>DyBtN>(Eh3eT8MRG6=w-H&&dEtFA_C><7Ql)8da_C%4T{#)z%z zhgJI?54WlHpHuVLia~9_9y%OPT922C2TzU5-SAa>p|xde71*h(n`eIKC0At2`dEF* z49dmCT@_oFDgw(=9sPz$c}9ec8u9l)?=16S%H3U*`kgM?dB}RJ4p`Uc_8l^2os|xPq*2D^ zR9SeypSsJV(u-r(fb5awlbN|&b%bZL?W$mAu4*zGca!Tx$oU11nyX^1YNC8v6;1 zk&AEGG!ML&akl4tCbO*S8mhvx6nhPdz=x*~wL~u%mGe8S$*IwtR%M=-#nQhx9G$(8 z_cLS5)6Bo9eRe8%ItfViJqS>cs-74skC|si$seo~(x4?a)xs&_GOP5;?N_vPsa;d? zRaMrCmB*k!V|g|miKy%ht;Kpqt;iucEU#sC_Y8xkh>TCIRlH74@LXQvX)2hN+&Mjk zFDpG%<{G)EM!$NRo++lm*4Zqc*cDnc_}R0F{fhOJT{%}1qXuwI)~xA?p!g%;%)sYf zSFOcS=~Vl~HHwq5S|>hyZ<|cZ4*RSpD5_L@{w7bcI92MXY2{S4`VKrbj&1FI?ad6x zk|>Q-(WIJA1JN*OiMw$PG!TqXXB z#p)U5iIZr3XIBB_Yk2f()u6gC5|GHQ3d>!iLZ_aZ==JG4nO7+Ol!LOY+>9cKZkSqE zagdMqDtT1{mSXl)hC=NTMA7j#);;p}HC`rScV_=jW3{P;g$N5yy6NQcVMlo*U1d~Z zR#_h$?Bi&)YM?d9U1Hb~nfuX}yL{;=GnnNUtJ5Ae=g-zh&l|nSH&tsLlV9FsF5`m{ z>YtdzQNLM740~5@TbReMk&Wg?1Nq(a$Ns_p{MKHhPJL5qG`I^|73ceqD)7Cm=XNQIY8&Gqh$cziYxYHV&yH3 zlU!%HYEwo*dAIAKiFJ+xJrVlWvCa@!%eXkF!h@y|O+A&Wh^a>pc~5;k{ypz_A|TdQ zPG%LNVrZ1%xrf#05%MbMTIqchQ$3~`R!23`jPE(XQ!#zY^5|#QaYtP55)&0RXhqdg zZLmzWbw%YF%u!ugy)vQ=c0`Nn%%&=X@v6j%udp=}?z8OtUZG{IRnYz)?eaGjYEHJR zkDp)R>e)x{{d3z6ALKVZZ`&^4zj}7{I2W(wd-dYw*Izxldh*G?df>^|Uwr?~qpSaX z{D`UfKKb_Li|<}?cmKI(;GTiMDFZLQ{HreV_>W!W@t?W~UFZID&%iwce^Um22R5}3 AyZ`_I diff --git a/tests/taglib/data/duplicate_id3v2.mp3 b/tests/taglib/data/duplicate_id3v2.mp3 deleted file mode 100644 index 34f4f158ce454d71677f450d1db8c40abbb85d30..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10138 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 diff --git a/tests/taglib/data/empty.aiff b/tests/taglib/data/empty.aiff deleted file mode 100644 index 849b762da5a8d2b1a366ea8f9d3daa87be7a194c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5936 zcmZvgVQwTv423(HNysh|Li~U$a10O-i66^KWUs@GI3K&&Bs;v%pWW3FjFg$~>ay$k z`Po&S`}cpo+qNHm{Q39$`(NI_%k}o%cC~F^|FwPl(>H(rn)`en9{$MH_mAGy_x0sz zyScn<*V}fvT)yP`^Y+!{al5VYr|q1vZ98T3_VOH$*UY_K+QU_zo-%&QFHdgD@;TnF za>wXZ=GI^QGY3oV^T~Ja>pSx@`n27}{wBYtdWsM3z;u&U&i#iM{krI_O z-E&1cShdOuJrgt&b*~+-=is^xzH>-?$rV|9X-x3RO0RK(Zsl)(nwh7AuZ3<WvJdIXi!x_L_8YF1(~TQkBaalwL`7+%Jk_{SIQmHOgoqJ+?7(IA7am# zGe2!_E+3<6_1kJ~JlH&y`jd~cSotZ&Gf#~=&J|j?2hsTmF|v}L`_)L%&l%w zF+R5fTW6)YsURwMDoLeU&BO{u6*_7&IuWXy&V0=y#$&Zrjw{P!0h3seAFj;9q3Ysi z#oYR+EMZ|bE905fRZ=WW-j_$cPlifo<%bzWSk+pqapfK8d0s1U(;ro0_Qic<9qlZJ zu`h2GcR6SlSH#vGn5_%f^cueXtlr?f;OcsqS{$xHj8ARlTJNo+sxeR%(;};N=qRPW z!ZKMI1mMscD^AB%SEDudgHQCd_@md!ZS#{cVypUL)xO8WOKSaNY93oLs14XdhvP}> z@lx^Nsd2d*KE@YXTeen#ow~Yt=67ClMYgPu)tAhmTuj_mv1O?uuq@TlZ$X~h+v8FA0_OjEip56-DN zuD+z!=Co73%bPpwbFbHHR;GgJu%7rB0p;#JYlWM9FD=8#WA9F=3`=Hk-gvQ`K8MDqx2SL&(V{=p%9`L8`@~HIUm^C1KWce^NcdL%@ zY_?q$%*<6yM&oYsln6P$z)^EmtW`~vPphJ;eCKm|py)s~Cp;sT!@*glA7Q*6AlY!iay zPHdXtmMw=DSTP!p)%LVMK$`>)AUR+4Ytl^@x-ps zn!(SWMeJ9sM|S00O^h1AHCeN!CxYUSfHMQ1dtJ2_N2OEk6W1tC#%i7T@V#v^Ej#S9 zo}j2w?fFigVsTXJsA=U?wfYV`HI8lVeeKN*$dV|HR$F#6xfrEeHxuWqZ~Rmq=Mj}U zc&0M-i^bl1JGVT^UvX&lR+Tbpq-~)k^SDa<6^qp~$`dEi`p&Kb$k*`b)v7^tVI&}t zT@{wQMukp2HPP$ScQUU~{3i!xS-BZS5Zy4fuHqmc?^W`u1}w$wsSJhMBZ#8oZ>)Rd z?Q6VD#O}=gpT=ra3kwkzoOIL4@VOCim9PHz0wQ8U>$X#OC5t;kZmb-lE zC^MMlA6Ca6HRsRPN6#C*$TwAM9g|<)WbWdF5$d0q#8JOlM+|#cZd;hguaS-BMg#fX z^T+&Owp8hjgvtyGJ z;)OYww1SCDotP2)$T>jguA^lD-ij;sWMbtljgwqwxoT5JLHWAtp^0^l13eM?*0IhI zSj)ILr^17#5KTRms)(sa&v{RM6aSufJP{CUD<`uGQ86^i@Z7^{^ay#CbFK6~im4vc z46CCWX~y>);Hj8CvOM~kb=(oxyTn9=4O&rkR2wW)ZCz1$26I$bRDxAv?6 diff --git a/tests/taglib/data/empty.dsf b/tests/taglib/data/empty.dsf deleted file mode 100644 index 373a73b46c1d74cbcc8370f1920a9cbe31fd74e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92 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 diff --git a/tests/taglib/data/empty.spx b/tests/taglib/data/empty.spx deleted file mode 100644 index 70572b4587f26b1dfa1b93035b65a90f47ffe511..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24301 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 diff --git a/tests/taglib/data/empty.tta b/tests/taglib/data/empty.tta deleted file mode 100644 index 9cc00ba8156b8ef1b16401b0efe65b6d64a1b1de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79538 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 z 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 n!DH@pWX@G)8Y5+O z5={kYj%19D-VBn3C8R(_OFES@nXOe(Ymp`m*d+x^KaYo$u+)l1#2m`JM@W2A&#MYH%_dO_8CRaLW3PPQz|JXsP9_0a6;cJHKT zvmN+#wY#UAqMD9(nO0U4KIoeIfNdRWKUzlIvCpn{GyN4Y(Gok>OEc@3ebek@98+B$ zwguZ>?c(U^v$P@QGZ z@vD~J*U~@Q!`_kM*y-UP+iAfujZ?O}w=z@~95ZXAg?sUj7o4M>-80(nFE|DtA2OP7 zxM!KEN4tiRif61|N@NC0FH)+>YR6lL>|t#8hb2bj)zqbrE#=x%*50aGTPgbcYB@Hq zrjoI_-JOGSGRDr8FVk~09hl?zSh@M+y{FMYW9eWbS@FO4hwAg9?57oyo|u!TniHS- zn+JEu(!<8AQp{J2xLow(>m-KH`~PioWZnN{vtE<@kqK=4{FwYj5uxjK2`x=y=W(3K zM+=@`k4?-_MHJ7G2!>O|V2C=0Q5UgD4ULRO9u&iQAK}SGrXwhED_9HWrwb?%B(GDR z{N|ePExh0x4b@S!faFkbNVjkMxZkzp@KJ5M2WgDf?)ZY zFCw9Y6b+J8-AhtZ(dVb~5_%pFQBbEVNcs$JQTr z+KY*n=`h<)PaK+wzBRD4j-FT^La2z{qd~T#pPC(0xSRMN3(L-F+h`?P1K8@Y@rSS3 zyPuxvL*rAiJJ6lQ)#2-Qw&R$c?CL^iUtG>?cha3itMj_O*y`(jaaHWZp^876%L!+& z67LK|aofrEU!RymvwxIvUiOTkz6jB3EIOHf`_*^0HZU`N_O7_l%0htX;(pIeYw>}# zXJkZSVbE*areT|hnyK4HpxZN9K~U4XHsdVaGmlS}w03Iv^~ooiy>)1BnTmEzV>Dyy zSfO|6T{aN6w-lqwGMZg9ma2!}9;9OSl;Ibu$qHSvqU(Xc9J9{NHIhp68A;z(uuF5C zqEMxSXVmzM;`oOZV*SaKOjc)0kxEo159&Bak)M(HSAM@t;l%pv72+$2^7|XLV)Wq^ z5|3aQ&TaazF>K5q3gIM~mx{={1V+KW^5yd$Ec7rR#5X(`Y(YQlLQg6D=S^Q28zFGS zU-RWr5_ajD6!b=NVFdFK(Hqj=jpd{-!*5}=`?WQ1E-azmT#yJ$KgaO!!*G4gCxwyR z?A78%V@AnwuuB7lK}Vv;fieIl6Iw1oPnaV9i%slctMk@lDv_ z8$l}gUl%>`yLS!U)gt1>tFG--Xk#2Ag_Zo}H5kIIKBI!+4~ zYq45PwD`;aHEtX)sq~|`b@j(BZLh}*hX%xm7*A-bW{QTLv9#=wrfGtpb=$qRW{A$U zg;ql7@2w>Iy^OZs7KbMwB*s}UBP`q<7-C=RXSHsoWBe(5W(uuWy52f#YppZ8-%D%{ z(?i4gYwy$?8vRUHTOMY+=JHuKzTNt3JN{88y*x-eFU^6`QMU*BYNi$6?uwnh9{+dy zREKA?5HP1%Cp!d<`7zyk*%MotuFw@hhjeGfv9*>M-xgcq;J1tWnz(B8PqYEMEe(*f`eor$?f@(64cI}(H z2E@uIm*d(=`B-U}t>4peMq{3)Y$awcRSuPuwWF}sv!yD-9PaE?t9&_7I{r2qpab+( zn&gyy8b4^d$xkC47vkop`6p~ST0A%hY^ z1|zH!yeRwxFk*cqa$NDL3v8kL?}-dkrFEV z{QeM08v%RQ<7P zJbt7m;!dh%TDs=kblPcA?C%+>&@$C{M|%ld(6PnNistBvmM+{qGk@FH2j;!*D>L2K zoh;nm?wb3CV|35jna;q3q)FRPv|hHmnv>`_hI3{+EBjh&nC_=RLi&rXp}GIEomuXH z(rDe4uDBY9A`l(@Z2WE~(;Bw_w9;R3Ob91^pbxY-6ji&g45M!h>_p-tp`-S*qGQGf z`k8HO4s%x!I*E+X)kQJkXht@XaN5Uh>z<)zRAG2CBP1Y&6fHwLKI!!oOA9Ork9X~p zn}&H)++)SdA44XprQ$y*22aNiSz0(;;x&d=uU>w7{H$8FcKE9CwNm8*^V&|Ok*owt z0op2F4wUFvtTg^;N0DM6{uEuh@S#dl#hC{E+FRHB+iQkj~; zKbw{75*(ZlBRE!~BtJf0sX@XKB?@HD@6S=NgQUD9)zMNo7m3`e2MHqY#m32^Hy3#u zi{w2xRSUw-%=wz12OkuFm!BqTpbxiD$bXPfF^mwNvCVKjh~$vmbPgTCrTYj`e+L_V zP`i(gVizAEx!?#<{5g^L)&CCL@F4}SCscfAj9Nn;ppbF-d=ffZ*tmhze1-7;x&f&b z6{dn4Yakpq>cQ(*pgOEJEun>J;vz9Be~E+&VTj!PIgAWq4?S3}h|Uo8&9z8$IuwDh z#t1SJEl$guJ`#r^=E;X-J{rl9BQ!XUrAp!nn&q7;o{^Zp6i-;~C=i6)7a z+?;~^l_~$rw|AQ9>OosfmvVDaSr;+mAId(Qg~(E4IVleUxx?)%ng!bm+T@?LWkwmnT_$AJjy1 zvV)%3SsduY%+Oo}I7xIDJHsqQfYx6bXigfmVVDKQcx6HWrT6=0Kim0huluTZX7u&W zu-(_Y>1E+==hW=K0;qt%$#xUY!d=H$h3u{Eq#e^q0Fs6U648 z`xFq0_!lh!lwzLtOvwH8biZebf@8q{tRWiC2_x*ARz`n$t=k68{6V=U?C@-*l1y<) zz5yJ;rjq4kCCOJyWjK5q6{e9amy_it$Cs-Wj%~nKv1$@7K_O&#APHCHB%g#5Tr*aM z#xNT;Uo?CI#O^`9|qYk1I^I%tMvX$A;TOC|#~J zxn#KkwKbqQC|oY{P=6!FHf88f3TiA@Sk@hp9BaV%%jF7uTW-qG8rre-g(g!m7y>ltb_hlQ9e#6!yA445ZjUN47!waoz$`RmXaR~t zjSa30GY(HcLR5v?_zH{%N&rPvWN00x1Zcvo(47Hj(d{9hY*flJSLI<*pfMh5kmU-) zD_jMJ%qLkW;jRHJh&#qAEE^nSGV~Cp5FUpcRp9KsHOB}gxEg$~1nb5Nk{uEPJ1{~IYyNnYLMT>ghsl@@zD$6JhQZXGHbEt;vPpi=DL6==8GC$(2FdwuWri%%+8fg_fylst^YSZnsUQV*~TI zdd%>qs%l!V&BhH4ra=rWsE=u9%j#`GuG>q&l)=#@a#~EI>3^P7W4p==MX{A8XDGaz z3UJ5KNmWs3mZj!=O&HAq#YNd&E+&-AXUexiq#*=7eXNs#%1!9Ua8)HiT!>#90j$>k#DjW$ANTnCE z$2(ie6mu0M>HDeOMojyJd0$|TQnbNcR;_YOZS%Yk5DsCnh27rL-U++MsNxIuo~Sc} zRK_sv)Bx}Iwh93X&J`x&qWAURv? z#9JM$8^7B>YY*N8PP)6&0zb^`4BCH!Byju89;CrWgJ|mxvS+%m?EvsEEGJryDXu2= zH76r%14;Fb({^^amr1OOLMxqcMCha_?wfi-Y(b#MyKHOG5X8Ry-PNuQiK}I4hSk2@ zwR_j%njq>|9Uj0Y=D@kp`(%$1cVmw=R<(Jfe2)>o{{&`Jqc!$WIaX2u6%x5>?66Ux z11itP=AZ8HZVne?<{*3f_D-Neb4umFKe^78o6n|V)rk))jR`JAmYTCk<<`WcOg3hL zUZn`o1aV6sG!`1um@8A(P61tsKj=drQ#SQ7=%2}${a=iUDu z#vYOoFT{qo6fV>eOhSR_gCQx1fhkqo{2Z8FzJbmZ!hjd3-%EJNe7K1I*`=8W;BXPX zymu5U22ngnplE3HEsR1D96|=cy#;Ua{xsqB64!~~#SrpVsBqt#L!_`bgcN;+`{!~< zKAgXCG2+Qj=Y4O7JXrC*bOY)Q)zIshRR25(L?s2I9?~1ip%0|#+=f>g8H-5M1X1@! zbG6O?B2j3F$ldamf)RqGJYzJ9ReU%hho6MyATHPbT%_`Oa{OKSRul*DBbCi4h0!&f zrk>Pi3f3Gq^&LG3B(G^1Qz(+t!tM)rj74+lBQxYF@cU>f|M-|p3hOrvTkZ^Mzahy zlR1jT)r@BKdOfg_K}ZZ8{v8?c^r6O zXIA!u?(k%BXo(#$epgrq)Nq{MX!VY|~eMF1Oso_Mzd`sAGTPVF;WY^A}NPDAZF zgr&XMT^txMZLx0(pX$)2=xA}LEyP<|y3@{R;2i_wbWGD2riQ(CFKb#^5exuBRWqis zdt}1MtGjA8un<>u(2In@3Wrv8*Gg&jP4i^yn!pHqiUHu?Ys9Qn;1PdSwci6`+`{AZ zVflcgFYoSD6^^+L7Ct?1&(b9tsD`^RKQ|xaoWUnF;+yw#)c9@kHt>z& z$LILxb+S~AR>)H2J(v5F<8yOWd=ezw|343&Md!*i<3cCoX-uqqIA!fDLCzacces+a z#emVts14?ju@p}I?(3w*8^7P>4Nhd@mj!|T(M=#AS~UB8;htgJpdK0P{e=OO5Heyy z(?t#3R=}84e8JHzV^Qe$_RO=?LPv))Sl1W2nR_4;->qbvtkyAK9_f9EmEFN_2lk-1 zxc`TRWq6IEC+-dm;QzFvblX2DcXy6sce(I5r1#_8IXW-(7G=x0TWAmq(EkZ(!S%oWg*34$m{{`{SqXDrn8QIh1raeuMq^~1Z0 z<_+}S5LNe4i2Tl1=-U_wLIhrG_~32JL)gAKFERZ4m`13 zu;@jxi;;~m67rBWv>q(}u5gZ=E=b|G@d%0o|BvF((a{nF?k1F)$rYz3B9M=TKt|ww z(S{WDVi*|;MJSTWJ)Drpe1s&yOqMsLhh*g6>M$7xbt<}3m;6uuVFLTg|AcDRr%JQU zC{`*{<8##+`O10|^3=&vlLXSI0F>UNA+>rk^)yP;r6vb}3W!sNmn_H6mFgC}WSJDo zfkP!p3!o>o`HV4@!yQ)pVXJB|*4$G@yY$aS4A{ecx1t{^g7*E%P1Uq}yr!nDBm0C| z5E24dV~MyC|G@RdApH_Fr~1KmRuuNzpd}|dBv%H!gv>6qjQC$Pr=4)%eMfqb9j<_h zyl)J(;mVmg?44S}p17QVeLu)L#(sKr@lzlVt)s3Ezy|1^LR7(9lVPUyDmzH;Yj+b6 z2c1F25jvn9r?vpO*S^>qq<_>~!`I4oA70-K%SmLmq&4^u4#M53?*r&TIMg|75XX}9ljaE~sGMh}60cQD> zOQ$J7OPB8(9H3tr?f@yfxb4DWH7U!0q5y($vl@dNu@ss` z08d@m1st!Gnt-k@+`8-v>=^@qeuLlOFq8p*Sr^yYD$p>r04Q8>+XF`c%mLQ`;G58% z%cgH&)O8TtH=Riydicg3NJ1B(1oYUYcNh}jG1TVLvWw46xJm-b0=&ZaQ1=_$cIg)^ z2pEpb_uw%6)BqF!`UOYhryj!zTm?sjhaS4s$uK`KM={qhC_@RDJ(z-@91DhLQ_y`F zAP<1gvd{yjDmO~5CE_|HP=7^s-2$NJDhK`p;6029Jd{SM3{S&&p|A{HhWdcs!2bO91?pSTA5Zo9L)-I)aHLE_1O zL%c}AqemeMLPh)|?4RJxLt%YyKiqAsB2-XeUNU)a-g`5@dB1&|F92X&wHs`-t}s9$ zErQm%BYi=0fO1v%{s0YE3^pzDd^}s)$MFH+)m1Q_w4ns_%;m@g-DC51o_U;Q02o^; zyie#rWvT7y%nibjpWg(1Kq!|@hi=@G!p<-Vmj>Px~V9m|m`j$IP+AfRiM z$QLBBDFfr1ft|ul@Z=f0YI}A*g1%3}_0HN3=gqOk|2nUC`n|P{Q)8B5O$FcQ6nW*B*QSMf=N6#OLaN^0Wk&6_N z0#ZNi`B0c8zAYvR(9KB@)ou`&2~|a$Ke6cR}l}Q*8&K(hJnlX=r^fHFXbi-lDe7 zKxH?m$2Xx3x2em;(8jxzeim}MsY7mPlb3pW7;;xq=_zP)1$Al!^6aLreTTetl(P>i T_fuYbCLMKbvFzDNznJ+2futKO diff --git a/tests/taglib/data/empty_vorbis.oga b/tests/taglib/data/empty_vorbis.oga deleted file mode 100644 index aa533104d6fde177772775137cb7660a3be11418..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4328 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 diff --git a/tests/taglib/data/excessive_alloc.aif b/tests/taglib/data/excessive_alloc.aif deleted file mode 100644 index 9cb3a6e10defc7bff06bfa670aae859f687b0193..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2170 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Ҵ8( zn}|J)l0lWtvqN%Nl7u>vBr9(NcTb4iEJ#6{exOCa^^3v?T?V^{nu4q5$2NxF3r&u* zrDRVMPPoZ5O9Zz(vyBIM<}k+f^)8sg31jEaZfNOoq#}q3EcSif(I|;%OUyp5xwa;l zqMq!9?xB7Lf2-ac5@t6~QT5I-;0|dm!y$gW&q` z<7ahPq&BbpWWDRxU=b|v%mhsLl}%P=-ZE3(D)eBqNWSW_uHtQ37cdIflr zggON^VxL6-B=aP%jUAepp}EcK_nY0s^8wlE;-lOHZ+;X91tTp&>(or-)rxivY~y+B z1M|K2A?*wK1YTkHRDO6#%-W;|1Yk45EP+JzRL$E8X>@!5Bbr44=qej2CDCEXp-sP`movVh8@ zBCl$gMn1u%=;?Ik!XQgd6V;bigYVDgA6YjwK!1xwUSFep;5;T~FQC-iDK4Lj=$;Pj z86>}l2iHPY!fDV|y%Un@o}|tTu?8l9cWZFw2OMNsqp*K3W(;#Qyng)t?DKNyJ>{}} z{1G&9tqR-+Z@aO?&{nyqm3G9uleHdzcGx;atm*7=H-eF2Gz70CC^?679(LUJ*LU@itjQ#JbCqObr}NdbP{T8Z4U zEcS$>vE?QsSqXu5Ehd?1r;IvpSSDd2-69)e�>pUG+-CyH%x(PN!u9KA5nzmBeVI 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 Z3_louwluOe$j@hI_-7#bBkRHP{{U-F$SnW> diff --git a/tests/taglib/data/garbage.mp3 b/tests/taglib/data/garbage.mp3 deleted file mode 100644 index 730b74e7c2babe736acc8b5922e0a0fbc1f6f33f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8190 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 YdjCZ_?Wr7v#yEfT4BEkpaZzjfUmgnumH+?% diff --git a/tests/taglib/data/gnre.m4a b/tests/taglib/data/gnre.m4a deleted file mode 100644 index f925ea9eb5d1cfc12a1effb51e3208737a97aeb9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5026 zcmeHL%Wl(95Iu1mS_G(8d9|erCuP9`Arblk4Np}<;-N}(frOmcX|39E@V%)73zR*Z zO0b7*C4PVfQg;0U7HnAa0qnyWKS&y(Zb9OXV$IC)&CK{{=H@y8mb&^?KklzLK@lCQ zBnE1qW8c%?Z6FHUUC-;dH*Yl8f!lcJ+D$yDWAngT#@f&rh6K|IW(v#{{0mdSJ$zm^ zZs6Q@oFx17qj-PUm(6|j^WL|kg}Dfo7YC?aK936IGm&Es;TeBpvhq)sMQ|xhd?h!X z;cz&IN8|CrcnsKDr`{gn=Xx)zrSiL$GKn4B^K9%=6;YU082M zMfm!zAEjj~l?-~NUFhmj9QL|&92Mt*iW^t`9AEX*K<oXF&{EI0zNJds<0%E_yeyaoZA`ohLVV1-G30B71YBG#gFimD=fyGrYf zZ`$I2;FqW*g*D>!o@KlippcK$42IL@G93J+9=OJy>@c^8^sJ1-^+HSYt`;_XyOYos z5!~g{FF#MMWft z2StQ};z5Lpx1xB~gAk!0cvi3$#G`+JS6OE^8=C|Xgn}OSm&eTf_S>2HW@o+x06KH( zwH0Y)Gztikc}120T3$;6uYR!t6!Hr>p3ibeBhgWSNqE3nqc9zU^g7!G;j*ciMKC+T zoC0$S{)Z_5@Ps8wpuE1 zRo!w=Q5OoRUr|&=FM5@S3;2sl$q{z9#e$CrOpkC(RI0_ZVDb8pu%Zl1v;%ZwkZ%C3 z3oXd7`+PPO4Aq-cR8_Ax>SS-=O(-Y?TfxsmOyfEc2A-lsXW%qHW)1#P4-8>XW-&H_ zdX~oFupy~=gNBa1ot0515YOS#HvpD2njgu|HugUH(R&|ffZzFT3N@`yB z@oX4@OM;Tf>xWEMmLe2TjN~(`MUe(KP~;^MB@i<+J9iG%rFxg#5d(X!;HDmBXYn^jrSGA7JXF!{7&ZL z^E*5GQ%j$kNxq!Cd<(;j81CJ8CupCvui!hAN~eHf7!dFU+73*C(`I+rZBB>7;c_`! zJsn<;+wBPk+Wj4Uz2R`Em*WNwjSUYRj1F;JWPBugJQk0~`-dk}i4&=@lkpfiVO%bk zr_Iyj_4dRDxxv_9L#u$_f%|1GOdqg*#^PtREojG7Dfc?qkq-EpS)j$Vtp-?awAkOo v;;IqswGRAV!<+=rJThPcm;wNbB1{obmO{J| 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 diff --git a/tests/taglib/data/infloop.wv b/tests/taglib/data/infloop.wv deleted file mode 100644 index d8c720cfe06ede7b8275d051730591dc34bf3f26..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2462 zcmXRfE68TZU|?WnVPN_!atrxq1w=I1FyJBB&C=t6>q2Qz3O{`Yh-RzUT?WZ3`zK>tI`M)g0?=@|YuO3f?6 z?f;_GR7bG?Ne7VPHB{?iOmn7`1=ncAOHX(ah;a{ diff --git a/tests/taglib/data/invalid-frames1.mp3 b/tests/taglib/data/invalid-frames1.mp3 deleted file mode 100644 index c076712c029bb734f1627817660a4b2a4fdf8925..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8164 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 KB$cp(mq{A(q@Lmc diff --git a/tests/taglib/data/invalid-frames2.mp3 b/tests/taglib/data/invalid-frames2.mp3 deleted file mode 100644 index 01976fc54f8da0d764c156a20156998f0b829991..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7898 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 diff --git a/tests/taglib/data/invalid-frames3.mp3 b/tests/taglib/data/invalid-frames3.mp3 deleted file mode 100644 index 6bbd2d39720f4894cbea4358e5b85d6507ce0a0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8192 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 diff --git a/tests/taglib/data/lame_vbr.mp3 b/tests/taglib/data/lame_vbr.mp3 deleted file mode 100644 index 643056ef837c7f35db265db857814aa2c5ecda8b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4096 zcmeH|`7_&FAIHDeM#R3<+6J*jM2ISVP}CO0)@T#^5>zaY2CcOcjkVPhsh!7C`%^jzl)8krs&r8;X}Qcizdg@?aJ^^F%xBKLXU_ZmI&m_VPPFt13mk6*CY;RtAIBT+i213`&q|23cdf97z1pI`umG&u00bq)+i zIwEjH;E2Exfg=L{2m<@;tik^&iVq44JlGThIAqYk&mRbYllvGLA}AszDJ^#bs-&W> zp@~B48T`x0Dl?kCDz-ukDtDL`@!BjI8VpU6l;M-8W@nd>kl&_RRQ1> z6vk;{3UKrv=7>6*cle9{qqNU18w9v|0ggjLP#y}*1OT{`z=e|*YA<0xz-90kf>E61 zv1RS2M2;4&Nu(YGG;$IuOyv|d>5sdz9lIgTi!qTh=I)^FIxNLm+p-20fAs@qqt}f4 zfQj7`Q)AaR*Y#E7Pa3DLu)DO~LI;=W7#XObb-wKDvS%uq0k3;(wGx9$&ZNzY`(U_v zM0va18>*94s?Fk(Qaylt7MhuL#T{KdF4GdiBEy+@Ww-w9IQNl!p@D=NxF#3|P168yg5IP^WsVpkn_|0d?H zTh4_4sconE&h;BEMG1);*2`&GN*dX#i%(2{+<3NPSC7PfF&~>&vAcGfX`27*hqG&?Y2Gj#$~C6C6}M10x_$)uG{%vcNE!>B2*_8Q$*Zr zzTm%h`}2GG7SHCn?~00>yffJu%GRPm#cY!X(H819|Fls1kYyHDnxs;H!_sFdzS7q;_gX3-pcN$_HN@q%L z#^#){D2W@hIPCKY0F^};)U^0fV{ffSZbPCUhf3$lK5X{CwSp%RRBzVXwNbuFh9e?F zYi1RH7x&tD&2w+Z$?|EAEEG8e$+HEO%LF5`heXZk&h}ix4*EhxP<0ZIJtLsjEIJ&s z5V#xCC$o(OL0xfJ`o0s>Ff$F?OAM)vJ9O8NVZp;iZ&Sq6B|2vH?1*#Mo)X*$JUqw7gp=~GXv_RXCDLAo`= zyHu)ZI8gzbvx1}!)$T#cdK$m9Y%$*}IlWI{BHxQOp2B;-{ngCKIc_+X%%}bV|5B^f zql!7A*qFsTF#juweB3vE$RUn)cgh?1*PjqgGun=>(wgL?p!(knO*`Df9&u}@+*w47 zMNHt;*q+k5xm>o!=6v&rs?oxn?)m+>_|3!&jbmZowf3WWRC@@m%rRp2=!`i9exCI- zDT%9s(CyTA(O`Z^XC*+bgTa!6C&!EC7%v^(!dC#yvOw3g^is)?fP>UoaILQHVXtig zu8|Nj(|lGAp??`>Q`TMdideiP-$S=^E3umR?nraL;se4i6_Va0BurTqhH}HIdMW&q zV%blUH9YNlQqeNLbT^`hXFaKR0WCa%ER7q`SFOB`<&*D3Vy-K{I1RTW%LqZKR3(r+ zgCorK@r-Mc)pWwu8Q=U5e==owg)Bdc&LP{I*POxxQNP6DzvLRlQ&KAgaZ3f+EE7?| zO9F2A@(Le=>qC86W#yb^mdS7VWDomnQ~2>l+NmuAR4jkM!7K3w+$q5s?t2O_Y?Bve_&clAfSrL03>;tnxaN1m@@;UZhv@5~EW>mK-~7Dn z)JoT41;^5q%zS6Pw1QNiSZ+a4YH@L9ex751Ylx$}Ys3Y11_mXdLtcFO|NlP&g98Ht HkOndU{4PaD diff --git a/tests/taglib/data/lossless.wma b/tests/taglib/data/lossless.wma deleted file mode 100644 index e29befcc837ba603af32e9cb54ff523092138d37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99013 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

QZ{TtP5pu~X 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 diff --git a/tests/taglib/data/lowercase-fields.ogg b/tests/taglib/data/lowercase-fields.ogg deleted file mode 100644 index 0ddd49357a906c70d6f3e5f222f0fa5404e4e2cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4477 zcmeHLeNatHl+?=l0qu3&|>lZIIRhca`BP{0# z{ezHojK%z(U=`3a9~62fJ@Qrimjm3>|NMSg5gyXC76eMnjddFenrej&=Bi>wzfdF0 zkjXM-8)fov5Qu}PvZ>i@Y2Iv5>kUE`WzgsiwZPqDYObuQY_5Evs^O*T`WG6_)y*wU zrp+oSB;BYQ8>_bGzF4ML?5)l#ev#&E*`C=@(?*8)MKs4qH+giUrW;i?-DvdZ4 z)|Zx0br_gxHa9nzLiPV0XbQJJCWSR zZ5L#0E1wWsdPKfH(ovqef2zlb=n1c=T6uP-iX*a%H(lf!TAxvO3td%2!vRO19JIJUw-$u`7XTF zC1&cvt3m0+C{r_->G=p17R9Ld@>VeOY7pY|WjXyNPJf+q(b3JZA2+eu^IEOp-6`;JTa2-11!VJb;{0I?}KdRYhwqZh817+ z&i9qZdbJ~pwY~XcZLYVSA1M}lEtBULdebL?8t5&J@q*l9@0q^R=n$1M(l+Il(7e5q zH+J@N$AMZu+}7iLZ49W0VRN^4>j+T!AGJ|$Z>4z_S{2jWJbOdX8&7u!fB%u^?Wwas z6^!5bvKMx8-7s-%n?s_&L$QAxI5^5*nXozK0ABR;s7 z(x!v}4?{|hsDnr4-*2iMq67S<3&gv9?OMsu;6PW#g!KK3Otb0sk@sVti%CC&ZFy$+ z45ih{ z>?wNIG@DtWf40I{yl1;-PxYyXUi9xW*+myK37!gKv3nymd$tWJC}~i_paxXmt*?ItN?3mmIFTw>*PG z-e)cZZl64Q>xb$}=78f6P4K)XJdeWF6c7x$))*RH!N=*ix=A`QBM3)zN;B%yE>r5udgBij-K<;}55PvD9v14s^l541-l$HzYG(I) zebq74ZU0EVq;~AX3+WyFv7aZs;x58|J%m>~RTtBIQ^fk=IRn|j^SxOuNuXjWa9#(( zysYbu0_CU_DuWC{J@Qpk*{hGqS1+tLp@sdW9 zks}^8=EvbtF7~J4`JM=!E$~M<%Vko{_L*h6hgje+pqdprkp>2)Tn9g*td)h?geo8rVZv)Khavv;xWkool8Y#1}yZT=5l5`%Mwy#{R>=!)~W=%!t6hFYzUy!XA`LLvM+|DD{i~TcHQ0Sv5BEfFZ zO2miV)NaCvQN)ltnv{qSQV3n$9}Z7PVcj_`60Sy8SuXw2em>d9tDaP(a4Bc{V(;tP z^aV(RG(;kN%@=)cCgq{DG*3bJnY_nMX(3a3UQ3AWld2TIdj|5Oc!UfL+K10Au-B;= zxG5Hj>8_^X2BbVVP0V7(^ffZ+N2cpd~d($EJbR}vsE&>4*tqf!RTnqX^w$uRwiaPZsB85lJhh@)|Myy;SmT zJ&=-{Ffv+XcT*gAD;_!iaVQ^-eAmbvolwmwYoP(kdXecqY$$s4VfR0x(jYlwGPFmU z2=otQ7P|MdtH(B-)=Zb|1dp<;aNJMv{d`LdN%##M@IbSntaF5Pq%Y#W&&nFkS|(Mg zqJwi)(nFS5k|53&R~Hd>iqfVXFX`0wA9hjN;majm=|jG`vd-c0sxA%j!DnTi>1)hp0*60`767+ug4lG8TAb40%(~ zvW8A^8eDt0K}U~O3X=4iNxE#j!8v3*vypttek_?KEwuX=upkhBOvFG8!( z9a!KF#N|$3tgNYPXl!nM*=l!m^+14xl89c0o2yuvlOwnA6O*14tbOWfp(uGB4q-g> sCxFn3*z8b7MMrQpo4Mt)PyH$s&*@t}P3`*azVd$7kG-Ji*dKTK7v71uJOBUy diff --git a/tests/taglib/data/mac-390-hdr.ape b/tests/taglib/data/mac-390-hdr.ape deleted file mode 100644 index c703e2e2fd595469973c4162cfbebcc0722ab829..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 128 zcmeZubXKt8U%)TMz{KFPhJitc0Rp&yR9*@L!%cMt1`s>Q)6MOUpcX^8W0-4NZi#{b zP>d0(M_?lp0}F!yLrP*v;trsi8lap{9TP*cE-ORIPIiXhwpPx# diff --git a/tests/taglib/data/mac-396.ape b/tests/taglib/data/mac-396.ape deleted file mode 100644 index fa7ae4149460cc0a94174a1e3ce72fa4f1106138..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 104 zcmeZubXKU~zrZfWz{KFPhJitc0Rk9-)PX7{FbBjA@^o`kdCkcX?il8pmRq7A02E_{ h>Jiw;#K6KJz>tzylE?s569JSH7VO>34%f}V003n#561ui diff --git a/tests/taglib/data/mac-399-id3v2.ape b/tests/taglib/data/mac-399-id3v2.ape deleted file mode 100644 index 2ea97fc45334ff952b0ccf0dacf6d5d11516dc28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 89155 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 BFTDT& diff --git a/tests/taglib/data/mpeg2.mp3 b/tests/taglib/data/mpeg2.mp3 deleted file mode 100644 index 13e8d53df6f32dc924cf5b153eb62b92fc13e65b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 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 AumAu6 diff --git a/tests/taglib/data/multiple-vc.flac b/tests/taglib/data/multiple-vc.flac deleted file mode 100644 index 93d9a8a14a2eb60b75cbb1be5c7975a095407b18..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4754 zcmeI$%PWLY9LDi8jLQr&USo`5#&u@nGP#vYk!EC~rrc(MTpAkESO~c-l#s&SWrvl8 zVkZfOg(N#EJ3D28gq86=9=861bL#Z=>381Eclqo01!{#5IVNFJKSm)G{ki8^L`vVbfg3&;Y01;U^0RuQ6mLz<{0X*9Qu7ITpq}xq zrKk%XoVi`d>YzG%pj0Pyl?SD{sMkHnmPZ9IAbSlpI}fE-P?i@cqnIiTL78RLK?any dOnrHw>=kOT6v|nkns&mUQ&xxeWQ}gC`vy%NpK<^I diff --git a/tests/taglib/data/no-extension b/tests/taglib/data/no-extension deleted file mode 100644 index 65f57c2ee985713476ac0b6e3483e6fe472e2176..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 256 LcmZQz7})>-0RR92 diff --git a/tests/taglib/data/no-tags.3g2 b/tests/taglib/data/no-tags.3g2 deleted file mode 100644 index d31a6ce96d7cb701216e29bf17e15903e3fca5c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68335 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%0DOmT!Moqh7w1Y^2~HTN>C$n{#(z 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 AGXMYp diff --git a/tests/taglib/data/no-tags.flac b/tests/taglib/data/no-tags.flac deleted file mode 100644 index 41714416760792085717cb85fa1542e5a7e28981..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4692 zcmeI$&nv@m9LMpGZGLQwubCN}`LSJ$g_Iwqw8?TLq{0=Y9F~=<_~3kMFQA*eisnHwZ)aDHcM}-FQ+$wQ{w8 z>i!PD#`dnCm6iit_fk|SP1zMWvVbfg3&;YpfGi*j$O5u}EFcTW0F)6(kxN;Ls0Gl6>va#5o$UC<*!noNvL3$YV3lvBh-Zt z(v4H`N2oAB84jVM0qW}o()UxrZK&8y`SnnVmwH%+6bm);29;W=)GlNwr|#5H8O|%& znOKC3`P6v_^tUKn9>}ESpYaK*$ev^{YsZk;PK`}Ml@2QDgsNQB#|dO{QlVSO+CxQS vP<1C|dWUSSRP!8E(@ve)pxRC9#|zbMQL}AOeViIR%A_M^yXJC-_V@h)*-@2S diff --git a/tests/taglib/data/no-tags.m4a b/tests/taglib/data/no-tags.m4a deleted file mode 100644 index ba4e92baef225c0204347186c4aaf42e0259112f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2898 zcmeHJJ#W)s5Psq~5L6^AKGYUEIFx|_Arblmn$m(QRYIyNu^?4WUWZ!sTL-^yD#1`? z?^3DV=~{_Dz<|icFJORynIF(SJZHO2S|L^#{3zDlJ+FOtKHZ%IV5V!|^>lBc2|{FK z5)IToX%*O?E)YlUZV+_*Cy$#8z!DC9w}}@GtR6VCm>-l%S%k6^$|)$P;D49`?(n)S zZZ7b;PLf^PvEJQMvfPKKE8mBwYhO^=JV0&w7Alb67#9rEm+Zyyoqy6Ck~2}Fj2wD{ z!Qd)hjz%{|Bfzyb4Yo2Z2=i_=>MFCy-T1~#M#Q3-j zXKz@zZ)~YJJqu-${?3_R$m&-e?R05)M(2)-g9j?-=TsWXxj<69-cMsc*Q@)+wqxpV zj5S%&a~`kwWtHRvSBN3LlhFi8S*+}8t;WDa7;;OxK>_e4gOINJY-LH7+WAcE93B}kkY(!h0fmY zB-91M`@HotP+g_@P3+Fa-lw0e_c;R@>V0JcYHzz~7*5VS_e2`>dNQWxSc_uArHRIg nO(f_ncc?r}r@-@8>sj&l7xO2r^$l|OCU4jXjrCipjg0*b4AhjE diff --git a/tests/taglib/data/no_length.wv b/tests/taglib/data/no_length.wv deleted file mode 100644 index c06d1071d5ac76d9be9d6eff977a5f8cd4c82863..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 532 zcmXRfE6C1ZU|?WpVPNYZf+_+IT^wo!(7vHOB4iv zDi|4<7+lscFbHg9VqjqqU`R$tHroccd6NuFXB>(^Kh$sizX9DDd z>~pAN0^5abA2t(k=>@rfT)k;bjAZNWh#+6@s&(Y+J;TDpu*(J#H}CIsO5qHv`jVgM K;oRm(yj}nfOJeE( diff --git a/tests/taglib/data/noise.aif b/tests/taglib/data/noise.aif deleted file mode 100644 index 310b995e3c64878ca14d096b8e219f59389e6ef9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4400 zcmZ?s5AtPT5L9>cbaQj|_XV;UgculsWGaJ%1K%1KAPWfGe0+i!82ArCc%$TK2#kin zXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQQzsf#6_2V4t4R9Og=v!*?MBu_cG%{JsSW!C zT}EbahC3BZKKwj0_&NXcGl(r^iH`c+{NdX@hCeqieQ&j4Ui^?7xLvG;;9x)r9 ztO_m#Z}I$guEn~%jO}di4~t7zaP9iQuHpT=j{nc^rECfh*#h{5PBQ$;KFoGim$AH^ zdD1j?&Zu8*TYf%S`b+Z?vr!b6l{aIl&5s+G{)O0l)|30SLhk33Ex&f@GBz${`}l!z z*N1P`6%3O6e^sV&>OSP+l@pvX?Z46`4woolcklnjx~%CBzp!O~dzSet@-T~~Cj$T^ C(vHsn diff --git a/tests/taglib/data/noise_odd.aif b/tests/taglib/data/noise_odd.aif deleted file mode 100644 index bccfd7283c4be54cb38d869161148a620cc0647b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4399 zcmZ?s5AtPT5L9>cbaQj|_XV;UgculsWGaJ%1K%1KAPWfGe0+i!82ArCc%$TK2#kin zXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQQzsf#6_2V4t4R9Og=v!*?MBu_cG%{JsSW!C zT}EbahC3BZKKwj0_&NXcGl(r^iH`c+{NdX@hCeqieQ&j4Ui^?7xLvG;;9x)r9 ztO_m#Z}I$guEn~%jO}di4~t7zaP9iQuHpT=j{nc^rECfh*#h{5PBQ$;KFoGim$AH^ zdD1j?&Zu8*TYf%S`b+Z?vr!b6l{aIl&5s+G{)O0l)|30SLhk33Ex&f@GBz${`}l!z z*N1P`6%3O6e^sV&>OSP+l@pvX?Z46`4woolcklnjx~%CBzp!O~dzSet@-T~~Cjgc7 Bj?Vx9 diff --git a/tests/taglib/data/pcm_with_fact_chunk.wav b/tests/taglib/data/pcm_with_fact_chunk.wav deleted file mode 100644 index a6dc1d6c58bdbd08730cc16c71a940d581910765..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14756 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 N691C_K>+}&{{TD^OUM8K diff --git a/tests/taglib/data/segfault.aif b/tests/taglib/data/segfault.aif deleted file mode 100644 index 5dce192b05e0219b947cefde7573b7df1f3393b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31 icmZ?s5AtP9KB4F6>E`C_@9WC|0>Qz4E-;${T;af=&?wZf>p|rsRR^Hyd>{Y-^*$$~ diff --git a/tests/taglib/data/segfault.wav b/tests/taglib/data/segfault.wav deleted file mode 100644 index 0385e99be958fdf68181244ea8cb2ecd74eb1725..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30 kcmWIYbaR_v$-ofq80MOmTcW_kz`*eTe@bFWB9OrV0D{&CB>(^b diff --git a/tests/taglib/data/segfault2.mpc b/tests/taglib/data/segfault2.mpc deleted file mode 100644 index fcfa982f..00000000 --- a/tests/taglib/data/segfault2.mpc +++ /dev/null @@ -1 +0,0 @@ -MPCKSH \ No newline at end of file diff --git a/tests/taglib/data/silence-1.wma b/tests/taglib/data/silence-1.wma deleted file mode 100644 index e06f9176648836ab73928f0e15af92abd5c82f8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35416 zcmeI*U2Icj7zgnG>1H=(g&i@v5Dd+t1e`W(kcGvMY<7&vhmhfvIB*UrvIw%Q8zV>} zOfZXlh+a4_MsZHvT2QiJNYDfmBXL`#V~Pm|k$@p;Oi*LyoEZJT=bQ#M>y;Pl#s75Y z^z=O+@7v$$#gnJcS!Ye}Y2LFYVZU<*F|0bdZqsBqQx=z&Z}yn2bKH%{fzNw1A%ZBH zyms^S=GpkJ`P~V7&v*DZ5L^~n>un1Ub^q}0lDikQ9y~m=aY5ec+A-5@Qe5{(|*Zq z_v0Y9bXSD8oWFc!Vb9Ex`_7fE*b#Df=2`twJjEA*2%g4WXy&^CR_^>|`u2H0&3~hI zVq{uoRqvGK-qw%(!=Jui(^9$p$=I&)kwtx*lUw*~A>gP05AZAx<9<|mTP+wlKj(6K zZA3ku;By!o*ykE98?hdBTsN%`abAxGtYeGO1s0)*%cd{=dEfR;nAdsi)p-9avF{ru zu~Cx*Bp?9^NI(J-kbndvAOQ(TKmz|up#SYlH#MFp3O~2MyS~a_^XT&bI9v9})f~mO z>v85POwPjMBh-=1AOOL0u`{=9d2Pu@Gj>#ev+qJj{=d#!+BqFtq$sdxm7QGsR5BP?WwA;Qc-l-UH6M=;Y=6k--aoUbltqnO!;Vs4!>>gWe~bOfuN z*&KVbcZ@mOJ%c$56CiJs>2kf^xImV32q4%6tplnRL#daU^*mFI{ zxSAtv;fRBL7pCue<~V{VW7fs2Z{T_f`!3`$w{hK!JILhBDrQ5XxRdL1FxP3R#AfHU z$vhc@h;wWa^EVAVW7O#(?B!p4sIy+mI(zi!H94V1Z?vVa*AFkQN!VR~VQcM*@`9zG ze{%fAx`e&6huN?r7lPhybOo|GTlj3~*UUQ0hpiv-y-wqm&&g#r3~j(99J+&h#B<^~ z7I3n8_?g`mr|jLWc)Y#0qbGSP^Jy^ItKu=4weuU@zQ$(Bl36j!YUL1CI{4KV2f!)Y ze8fz8Yyyz=?C-Y+^Ya6jfBW<0y|(Rd8M*gJTd*}d$ip;AKmrnwfCMBU0SQQ8JO$iH zwwh5gBU$IWUMG@0&F@F^fXv)7{*CAO)VBmAAOQ(TKmrnwfCN$txRGp(*;q!hPHxtT zWDA%po}oxKwV7HaAOQ(TKmrnwfCSP(z>Q@8W;T|QtaHbW6UlC4X~A4YvgvSzGzJMs zKmrnwfCMBUfz$$Cbpc~oMzZFXYqw7JE7r7?zPNi(|b=cH0fW z+GHd<`Ut&}fCMBU0SQPz0uqowItX~x1uTnYB+F&8x?m!(Ns(+iTp^7?0uqpb1SB8< z2}mHdfLC3>vRFp4Tqdgv_+v?JieyupsZ|0JkbndvAOQ(TARPp}>H_{$0wY=H2NT@? zU0?(I6v?K;719_aAOQ(TKmrnwfCN$tc+~|gi)AFs+q{z11vdg+$w*dre5JNis{|w< P0SQPz0uqqGcnbUjXqG|4 diff --git a/tests/taglib/data/silence-44-s.flac b/tests/taglib/data/silence-44-s.flac deleted file mode 100644 index 24e15deb8c85ef82baf22a231901d646397dd8c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50904 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 diff --git a/tests/taglib/data/sinewave.flac b/tests/taglib/data/sinewave.flac deleted file mode 100644 index 25d31b2d72ebd2d4e23c0923e06cefdd452678e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64567 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 diff --git a/tests/taglib/data/sv5_header.mpc b/tests/taglib/data/sv5_header.mpc deleted file mode 100644 index 6d17e65f0f865303397cdb08632cca5a2aa0365a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 128 zcmV-`0Du3%FCYN&0{{RVcBcU78pg2y2S5XsQc5XpYAK~=`TMqG7PBld%R&|wvn-27 z)(~SG+b-oV^~l!Gc|2h@R~)T+MB#OHHdkBzU)gt`zIpg}+1_WT^O~=66~40@oh*DH|+sQguls;y*_RL diff --git a/tests/taglib/data/sv8_header.mpc b/tests/taglib/data/sv8_header.mpc deleted file mode 100644 index 3405545a29460883251a5ccd6135ae440bca5815..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 zcmeYbaP|)N;Crv7*~rn-9LK;a8RX8x=vo_f28=z~0~zHQg8eyM7=XYrfJKH(ED#*R L#wc_k*p(Ro^T!wZ diff --git a/tests/taglib/data/tagged.tta b/tests/taglib/data/tagged.tta deleted file mode 100644 index 1677a7edfbca692ad1e659f1d477be40d4138de7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 81819 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 Wc?A899X%42n)rVm{C~Xn=l=qGix&X^ diff --git a/tests/taglib/data/tagged.wv b/tests/taglib/data/tagged.wv deleted file mode 100644 index 333f8687177ea8d6b9075ef35e80c54869bc0aa0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76627 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 diff --git a/tests/taglib/data/test.it b/tests/taglib/data/test.it deleted file mode 100644 index 379444b91869139a9ca1b73d5b2b9bed98370e9f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 644 zcmebD4e%{VEiO?g&d*C%$V<#kWk3Qf0!%<2D+42gIFkqyCj%Qpg8^G@15|}y3da)# z3V{QK;Q#+GK-ai1GB8X5;wM0C0&+4CFf&}Yi&S_j#sH*WDj9Qv==Zx`3otNv`UV6; z)IdoG28WQ0%wh!~N>nIL%q_@C1qX#5iYhQ?wE{w{1!@yE^E~qua`RJ4fd-{zf{6U0 z+{BV%gkBhnSo4rv1$1OlszPE?X>n=_%osRLta*vW3b~~@C7C&yd8rD?`MJ5Nc_qaZ O_*Vh$0{jeY?gId>FJKJ- diff --git a/tests/taglib/data/test.mod b/tests/taglib/data/test.mod deleted file mode 100644 index 136b6119187bf574bcfdca2c4a4e9155880d553a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3132 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 diff --git a/tests/taglib/data/test.xm b/tests/taglib/data/test.xm deleted file mode 100644 index b09d91324bb3e6edcdae4f659965e1a87b5e1954..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5471 zcmeH}Jx{|h5QZ-i;$vq*Lb?$N!4F`{(t)89`~lNA$f`do+mT>E^q<2yPHTn$T@XX= z68kLqJowT3+FrawX_ z+)vv~@$sJXPkjS)Ou&OM8!dmY`%0`-C#=Ft6iENbBR0uVLT6gen%LI)sshQ!z#7@z5**R}S47n&x-ds<8<4Q`? tkyd^iJV|I60z=3SVI{Bu>R3PCGAqxqih=mL!j3WFYArv80Bz;hX^nu;?u-Lgc&~L`F70AoBDB8}Az)*{~&%v4ao~(s+=?gFGJ8;z2zg zG~z)K51R2Hv|n0ZJLroCef6L(AN2Ku76fewS`oA()s9p5$h0HVj!Zi;?Z~tv(~e9#GVRE;BiD{xJ96#FwIkP#Tsv~@$h9Naj$Aux+ELSv zns(H*qoy4-?Wk!-O*?AZQPYmPcGR__t{rvlsB1@EJL=j|*N(b&)U~6b9S!YhXh%ak z8rsp&j)rzLw40DLOTlWD72%{j;3}rwWFyWP3>rEM^ihR z+R@aGrgk*7BP3c95^V{I)`UcRLZU?>(Wa1SRYFQ@3F6vR@=ttK+#^ItMC60znn!z|+ zRHVewkx5$_hl`SwI9f947~^nJlM+WyCNEjKf7$N*rC8(#<$rl%>Sc zmMKGw!$n<69DSMcIn$X-tuI*5M7r^zcIOUfeZk@fW+&rt))%alVE$qp&iaC7Rpi|D zjKf)9um*!U$T*z!1q(JVT<0?mXMMry4(0{M;jAzCRRHE^#^J0l_`M-Ab%1d=>kBS5 z!5m;ZbD8x87p5Z9<}eOteZgfcm~D*1SzmC`3+6cEaMl-GBI8%-D#qcgFSwuvvzKu= z>kBTwMJ|}mIGptb7wcd~7>BdI;L;w&H923BbXD6!&zUjy%d?T zhH*IS3pS%*_A?G=eZkgMUBs%{ZL(1)FG*%T_WDXMMqz z8_XWY;jAy%po`3$#yFhy1>1Qr!%SzcZhgV#AIuTP;jAwh6^P8jo1IlSob?5R1u(l9 zhqJz53?VYR6^FCFU}ynuka0Nc3q~AZ4lxdAeZfFPhAm)z zV;s)FK!T*LZ;K@OM?7>BdIVC*9@r-yMk>kEcNV4h_h&iaCp5}5B8hqJz5 z03~u|KjU!L7mTaG>|-3x`hww>$W=2KhqJz56b5E1<8am&49>tDV;s) zuCTsfCkGz%U_NIY&iaC3qR2e_F)w#G>kCGY zV0JPNXMMq-63kzW!&zT2))cvVJ>ziJ7Ysqc9Aq5M`htkG!nV74(1XMMrY8O(9U z;jAwhQHxx+ig7sW3kKR?_A(9^eOan=H;lhUuAk1F#!W5H>!L8QB;EqUv>en3|9vQVMlk{f(8;nOrampmkSKkx`*CNrx7mWE?V7IMkZ@E?OtDXq`t^I27p6 zqEW^pD;&Zd7TlHSWjwONA>5DwwVmPf~UD;&bDDo|fD4yl9Sl6|d7@Y0ncOILbig+r}b@X|dX z_jqK5L#=6WGEF3z=8+W+wdTReFvww#tZ=9`5l)VPJmQfR4z*^&{AxnskrfWLrov$t z$Xy;;;ZSQX%rh+&kF0R0H5pC^K@NH(eHnj@e*)v5!2k3UIQ8eggXTwHz3IWtc=E#5 z9r>+dK3_#1sU^e?~tmaMN?Y?<(Bd{6mkZ+HK4kx{|>_~X!1cgKGi z|NAWc1X{*T9F+;X@dNaT{%ad<>qZ diff --git a/tests/taglib/data/unsupported-extension.xx b/tests/taglib/data/unsupported-extension.xx deleted file mode 100644 index 65f57c2ee985713476ac0b6e3483e6fe472e2176..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 256 LcmZQz7})>-0RR92 diff --git a/tests/taglib/data/unsynch.id3 b/tests/taglib/data/unsynch.id3 deleted file mode 100644 index cfe6ee1a6f13da6901627a4543f7849dc2d99ed9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 320 zcmeZtF=l3HU|?W02=NRtVqjn}1+xGBXJGJUsAN!JNMc9?;#43ji=mXEn4tv7N(PD* z0mX`e>@gBe(t!T;YAco_IU9AokT z$}`9?cnSb1W(H0j5I2rd^#G9ij|`svU-M@HkGx&2`|172;{QKAZewCMWnwU>eR}Y_ dH>%vO**}Anyl%v>9odbd=d!cx(>YD)697C+bpQYW diff --git a/tests/taglib/data/w000.mp3 b/tests/taglib/data/w000.mp3 deleted file mode 100644 index f9c2261764526ff40aba618a1e9b9933acada5fd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 512 zcmdUqQA@)x6ore5`rwm4K<|s750-TE!M*9)BBSeCW@Pvz=}fz@G#PD5?Z5XFd=USE z2R@Q>a&K<3h^EILbb1qJX^PDTNK<)vV{1#9(F5I6-}nAu3@p6AwKcLxwXRV`=ae;M zOL9BaQmM2gtmDzvp%+~{RVXJveLl~}0Vg1sJn}GG{kYrd9gkNb+$H!mI5SISBwz3e z@(Yj{+bdJiyX|-yM@Y^9V-IXL<9QswT!UBHRLate&~^}pN}sr>8$pSbMi`#I%~5m? z*0v~=X*Y~9xGT@o-Cnf578>b(c#>bDK4>}q_ygkWFhSiB2>A1IyyefUko+%w1F}q5 ADgXcg diff --git a/tests/taglib/data/xing.mp3 b/tests/taglib/data/xing.mp3 deleted file mode 100644 index 0c880151b6b60b5f5ac4823a064320710b687449..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8208 zcmYkAcRbbq8}~owpcKbBNQA5;du5M0W_EUDBqKX}mK>Q`M@ERU<&cb!ks^ug?1(Q? zq~tVEQSSHWeEjbF{v$o~N7v(dU9ao)y566?9h@_SB?m#O5FUaU2nPw&d`zf!G}P#1 z2S|#*gJ(>J&h|ew;;s^W|Lc0R|Ke`$F3vw@L-RcD2z}_i9sRiJGU=tMFKmYOA)J|Y zYp2(aCU$4lUBr1MM6wS`jo<)5bOcZcf=~%GltQWy4*~qT zV)JdrM$Y?=ggwx9r8|Ugn?MFWDls5&;c4(h}0>iIe-QgR3IMkk5I|XAJr7xFza>m zAC}QcTuIjc`tMgV&J3nL`_|#R79`cJdF_c<$t0(X>E$N*z@#hGugs?${|M=s3)dHy z1}Bnc*|(Z^U?2`6*%PG>PY4UKNm2u#r$`}Hq;m(2v$(1<&7B$ciY}fXUkt~jncZp1 zKIc84nx-wRdQ>P{+BLheUcX`>CxyD9f6W>r;>7JPU*b5y<@@&mE{x^n)r}W05D$^; z3>pXOU>p#jFErHfge0NM;3tIrV9{;gz$@3@ARWFXRLOFDH$vw~Beq!SyB(udi~Nj{ z-?~MQ7df|uksXT~YIE}NjA;;ZyEg6OnUX&kXR^ zI9=dW{T4i`l`z?(UA?qg;#i;u14&@X;mCvz7t&Ic(1A2g5t)!}nHIySynF0d>a`wW zR$Q^q6$Po{yeSP6-O%FP`W3cXO{v*;qVXo8DwiZ4c+=Gdjf!?!3NdU===H|UWnEy>1xj43_K#>No(&IneTe zk)ZNvXyFOT5-5`6!sJdjj=ro|I-T#3rX*v#*4Nv>SL7I-Z%sdTpS8J)@22E2F=vUO zjnOhrZ`Ytz21eG+n4!$O$JGVjvfcg`IC3?!X%Pm}!otf+D1;z-Slj>qvVsXE>zos= z-Ka25-CQdfc)sSCA|D!BTGr>klj6-8Tgj@C{njwSaQh!~R{09a8z(%_8i-k|R;%L^ zR3Ds0na7ohpH;|e)q;WaiDU;bAv^%;K!BF14p0FgCAe`3I^H+>NsYh4l%nC4av7AH|ZI0N1P9 zw{PAn9>_a8-de)!xNf=}Yx~U7v+>{otWUb|s0D+d<`)85{TqPD;@Iv-{hpVHyt)i4DP0}+AeBk{UfY0dB)lxCtOczYpU#nM4s{} zhD6)a(0IsBU^=KG(KkutVeem!uhlGd74!)YGGQP`inb}HfM>(OrVzN_ImG3ZG;K*V zv(u_p?AEs$!p#CM6i*BpB}IfLRCC&Qh1l_GAKI`ONYl2Ss@|F5*IpTk3XFF-%weJ| zRjR$sD`sJA?eo|G2682m1HpvUa1g@+8_&M^P;47%+|$7?g&w_fmSdU1tk-4>ZYxaL z&+HHf1xtTq_ zkT;PWj4Uf=e}`faQ;>#TE*Xj+sOdX8dijmNm{ojVorr?WP*%?$bE!HjzYjgl=_Ubr ztx1fZ;&RHmUfh`@>ATd6MCM|KnC#xnu9+AenwwmF3)A+kd zF=Cm6w8_6t(l={VVY)xo{2?oT`nx(jq3c+n?HBNb=y?#A8%HJtFDot~ZQ8wIEU2)_ zIi**&@xYT)(rjaHm%qJeOjzS?NqsKe(!$UCQbM4RCi1oKF_v~2x`T%^9|q4|oO7C9 z8B!cLP*&^6G{^{68C=&+G3OoL{MN@gOyWNo zFwBzT{oN?9HM=gep|v|?;>XW$56qZD6rI5<{)W*%zZ}f`oE%}G+em;H!lnQw1k9%v z*&$V63SdzElG?<3XNz8kcE|m(r@DMWL{?qgDNiS?R?*O^?gh;yyo`eC)s}=L-X6W6Aaa$N@~qVPD&b6ciC4-)ep1@((1h z5SdwO_mTNmy{bKjZTu|sq=dPu{DUuLJu0)EvP_Hh$kbw;5nth3pRTR_k`~vRDw2>G zPvgW`shbadRNW4rxyr61l8$twS6t!x^d#6Gl)ONN2U z5L4&?30{b}PazU`%1Gxh&$8->TJ`9}rseRsa+4s--4dL6tKS91R4Rt@;KTNhrt7lG zBigaZ#beU=fR)5x-h_mqFMH-APt z-h0e*liuWoq3+H{K|^jlK4GYlp*Hk>KtG*KOU{EJPc~IeT#xZ+|Ba8bMW=6Sk8)pd zi27iDFw};m0t2;z#vyg+3COOlB6Xw)z|8llFI^G}S@M4Jn}^%IYMo(KbV5XrVk zf@64)2{j{^f-)k77DM%X-&|qir?0Cf6S3|_P4a()qsE=>HLq74y8N$feY8MO`-U5* zP2|6wa1-ttoyyJV=!2cdUa|~U>$q=MhQ;FI_+X$OA~^s6IRGVq9fECp8IiU;?4}8j zR`^cvUZZe<*|i$`UK_`-=l4e~Fm;KZbnF_Za!1$2Y%EK4EKSazwNSrhVsfEq;Fcks zp!^lJN4}2T^`pyDA({C+*6A?NAd&38Zwe67xdz0xpy?WUWT z+?kJZFwi)b>x>I}zWL(6dLaL|-w;b+GnWv<9ifGD zq_zi#Xv4l69X^77`bKZrD?BY0hao)Fh-<8566%gm?`t_NTqs{dVpNPTJ{}qa1I=Q| z{{I6)wEYmdv0%x8Wp%5nZV{}ocy+fSPt?B=Go#giY;AaC#9Ti6QsiuHgn>}gL*888 zdfmil%gPU!KJM+2zvy;d3CWvK_3gA@H^bEYcj*rdv9Y^g7N1B|5B+Cw_s<6eV;+f=Qes?=vqThK{JrsJ)^V*dT zv+99Pe+UD8!jg-?d=?-yGsFf_J3;D#2+%nQLbS&bv^HLJ@bJYf25W(@ z0_v>(mZ>GW75yJ@>o<$I_FR8Y&HUh~KKt9QAtM4C{Et`@^5dCmZ?*4*-H7wO#mT zbb!u6Ja}9}PF=i|yL4{iygLI!Q`l`P2{vpE<0r*Nzh8DoDnC78;HpP6bpR!R#8#m}qZ9D7XDJhc0wd>!N7oq?wCVaIXj55+bbXajn zrQ@RyFME2i^MUDz#`d-92^w=-7Y1fA^#&N|BawUy0CloKj1E8vd=Q!%>?)8PuyQ=8 z%jl=Z!n-v|Xr=i@*h$_JWX0Yo8;VF@4}08o;IO5P$`r%7z>W7yr^A>GubnHMYMb5O zcs{b>DWX(Xc3|Br%9V5Yq@lpRTVGrM<;#xy6&l>97B8JlY3J zhdKzII5e0LM1Mj-4Lrf&&cUCTP|1*})U`q%B{q=>_upPF;?`IDFwg$}Q!g)DVi6j$ zbZ+@oVJU4Y{yOY^f3y9!4Nj|(ZL!_|0zP-M4*#3PxsJT)dv4M+4+DK7k_+HEz!V%P z?=H1zu&V_4#Y)-L1<&=T3-A1QwM~uwG$;p01|9gMlj^dnu%z)oOiuV`RE_SFdtM2l zIw~UhMdWrbzwHcz+Uu+Jnbf|D+3fyp`jgXa%|$Q}u+st*9(uusWEc$sc2%8axtUV#n&qGQ zyspET+e$LR9Heo^VGe<)rH4ds{Qu(N4lsoxHZ+hNENwIoa&2n>An?05nrQdexc~dk zy}_Av9{2v;Mhk{6L+Pg40H)lx<|UFp{OFkD*UB*NlKGM%0wkwEXN~p-nHXp5tp+!f z0lqs0#3MYEUB!}9Kpj}xU{^sMb$TR*Ks-Po9`;^qyKJ%)&9oJ83_Rc7varBL@m!BN zXv4KB%6>VI)bXOr)Z2rb&+Ba`=D>@8Mbb1$D)Kr%i)^HuLfEBWCaQeEjs}6RJ|G^5 zjrS0|_lX~%k zUnLCtyVxf;xZ`dvvWKM?-207-U^=KQSg*c(G(a53B4C2@qEl@1kloJ3`jy?4(6>UH9^Q#^u?%Bdn#=j$TRB*`y2uq-Bb6TM zZo1CjBw4RnD<>^(3S&wl-Zic5dF|I%|itPL2%>1#2UAb_PA`GYmXDillcC?qmN@l zz7bO?*mw7monTn0B7KHqzhFOG#^ZF%@*8GDuN zPiVi;{!<@q4s4qML3+r9CIu4BCmTWpqEn8#yJ}@-Ve^hkd;QGcU~aZsLhs;oJiEJB zsDfG;g1S$R-;yE7<{oF69)B8oAf|$}_LezUwB)lj&A$1>Bd!fz4-_|LLnNn70Ydd8 z3U>9~uk5^Q>>ZU|ao2(&ja|FC9RD>#I5?{)iu^<`x@CAzBd)F}n#d}@^*6*zxgpdc z_>{QSHeZwr$u*fvAzo^VG9eyf1cLKI4=gS@ps|;FQY-UJA^IS*;fWxvI|pKOo?y$*9iGY+l;SL#_7K2m82BGSya193O7f z$PjESw8;okmv8aZaGbZ(@({Yn5q8+URZW1ZNR?T5Av?ckPbAWvt9EZY;QVdMRS!-q zSlb=&CuhnB&Pq1oFzP--&PjgxOeuH zhqWR5+>Z`aU$pu+^@Z+oGP>tLuYFPac|FSt_q9)kos?YgQ(ipay^=>Yf*=5jx$Wvbn0O1lAHUp|ZFNxVtDdhbN>kas)Kk|8k zfp^KzvJma8RCAMz7i8yFH!Tw)I^aIbi*_srP|qoprfOWia#EnkcFYhnnpl+LN zq>zD3hzZXILEzfvM_f(?27;h3rqnj)g{>#BBBVug#YB}x?wA26_=6C0uKVw#3m#J< zSHFayzQA*nLj8r(@uY6F{vnsf)CgR@1vx=9z_JN4|hXMgn!oK9R;1er> zcFq6b!kP14dv-HmS8lXzmEOV#bAzKK+)C}{RHJyIUX)ff1IxENnGX!qJ;oEgMGel- z73`gBxc0m#{I=ot#OVpfYI({IAvYr)JoO6^JM{%ppmv}&1o?nVM)CQ)ugSR;(wi%{ zF%VF)G&K{>x$`YzHsKI9Prv)h#|pti$Bvun2FyME_Shk~dd$mOt!VX@Kw?pqv7NlbNPU*q=}WV%s=iKA;ZT7?=WRoE|VA zaLU;RinVRc?Obd8&*!RI_uXGM7qO%-;UTqmM`zaU3U`Feei-nLN}?|*g{7P|oqSd5 z+wOBR5_RX`|Ds1vax5OJ5(Ze)~$k$#xdc=6HP*x6V|{sqrSs+oGFMV^>PfCCZr&WlqV3bvb^YPA4qk?Q-Bcn z0skjv|MYBbnYdBy58%eoKEz<0o3tO z>yoGtzjSd_a>2?v+x7uTkBjS-Y-0DQu#@MZ6u+UYT2J}Y5jBPuyLJCDuV?@LueGOw z+hi1#PCj&)hN-F9s`60wvHcx_zioGb1n;0sNbMK$GTy(oD>~f$E69gWTd_dZ;7jm3nF`{#pit>UjzsXfItB> z#T4+g1_X*J9Hs02h>oD?d(ZMV{5H12k@)Ctn*al+#=2N6$-gZR7q~X`S&$s?%bt0*>{0Sm;dz8 zv6m#4IdR42RLaFn!a(2&4icQOh(j~uS;19Lv2ApW1EdWXN`{Ioct41%X4fTqknGm5 z*Xe$wh1^tlXY<5GsLjhhwZQ9^?ki2-Jd5;b3;#D`B_bAYs(&P(j_XK#ChH>PFW=HH wr}E<+1qcgvl`t<8 diff --git a/tests/taglib/data/zero-length-mdat.m4a b/tests/taglib/data/zero-length-mdat.m4a deleted file mode 100644 index 578d2ef7a8b000addc1e6d5ad9a6b12211f840b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4517 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 HgBSiA+4#i{ diff --git a/tests/taglib/data/zero-size-chunk.wav b/tests/taglib/data/zero-size-chunk.wav deleted file mode 100644 index 8517e797dc803bfbcea502588f70d68e643a6aad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1024 zcmWIYbaUfiU|`?nJDKmRm;b`&U7tQZ`g~5$<2&pPb_*fwCSg*qTp<+0wfjw^Ene=O zbbW_kR(7wRl!kpn`@Bvl)#?|aQIAa#!q?~X_H_8=$O5u}EFcTW0vVbfg z3&;Ypz&|YzQ+EWZr3MaD N$*9$#yV$1vJ-_48A29#` diff --git a/tests/taglib/data/zerodiv.ape b/tests/taglib/data/zerodiv.ape deleted file mode 100644 index 683bc2ddb5ae4f932fd7554664421bd0134f871e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 946 zcmV;j15Nx*K|>&x00960001BW0001#000220002kRAT@D000000001iN0A!y@L(fn z1FU6N?rMC{2LJ#7AOQfNG5`Q206+i`00IC+tN;KE0RR9H4gvsT-~<51$p-+VsR{s- zij4rzNe}?G9}@um{uKbw(-;7Lp%VZJXC46PH6Z}&6eIwKtR?`4NGSlc5i9^d=q>>8 zg)snlwKD*2CN==G`Zxfn+B*O$yFLK?Zb1MVG(-T(7DoUL^GN_yy-Wy#iBABVR#E^k zO;rHm?NQMOH;-ZFC?I0000100IC+tN;KI zu|fa@01yCVVRT`D%Ax?3!cHIFm<<4a!=@`~=9M8_a>)?-3o?rrEy&ZL%zk)>+(y7{ zti6@zFo%OMvExuM@b9ZWskn=Nx)d zp{lxNl;FOWNjsXPDz006ie=`z|Sc`@D|u6b|?eVW<@?#of*UN^Y1 zOL~}h7^3Oos~_tf*;Xi9HdD$1_aPTqNN9T?_8E|DYC5#x>-?NN5Ai5voy)Q3NK4cG zJuC2S8B)}7DLfa8z^x4ieZdyl&G)oQCu}+rb?G;-8Vma+oiAWXea%wR_ z$qYG4i(SKF?(75aFPu{Z3be-lU%pm{{PTCgQ6?MbnuMe`Cm{S`1}090IRnYaX#b4J zgEP$xl{{Z*&K!#sAt@QUHWtKSbN?o=80P1Dx3}|w776XkW{5LLU{{;6uu0kbouJ5| zm;VK#|1S|zq>i2O-okJhkE^gBL^7Dc?{k7xXp+w3tutQlp`kcIK?MK?4tzlMFtAho zx5^vD}ZcdL`aRAUqP~eRFlu_v`AWZb?g{O&9mmZd=QMX+YL*6(|ofqh$x$UIpa@4}XS)kS0|OX1<|LKoGB_oAYnU*r0Omshf*Zff{mv8pNPtA(!>Pz6wf z0!T@+LUF5wf&$P~h4RdjjQ>DT4AJ|VAp})#WNKm&gS~-)fn$Jch@-n}`~`LfhLsE) z3~X^FD3+ip)Jy}q&kSN6Lx=zalI;x9V8`SZpgQCK|B}RXJkI!o -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int main(int argc, char *argv[]) { - std::string testPath = (argc > 1) ? std::string(argv[1]) : ""; - - // Create the event manager and test controller - CppUnit::TestResult controller; - - // Add a listener that collects test result - CppUnit::TestResultCollector result; - controller.addListener(&result); - - // Add a listener that print dots as test run. - CppUnit::BriefTestProgressListener progress; - controller.addListener(&progress); - - // Add the top suite to the test runner - CppUnit::TestRunner runner; - runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest()); - - try { - std::cout << "Running " << testPath; - runner.run(controller, testPath); - - std::cerr << std::endl; - - // Print test in a compiler compatible format. - CppUnit::CompilerOutputter outputter(&result, std::cerr); - outputter.write(); - -#if defined(_MSC_VER) && _MSC_VER > 1500 - char *xml = NULL; - ::_dupenv_s(&xml, NULL, "CPPUNIT_XML"); -#else - char *xml = ::getenv("CPPUNIT_XML"); -#endif - if (xml && !::strcmp(xml, "1")) { - std::ofstream xmlfileout("cpptestresults.xml"); - CppUnit::XmlOutputter xmlout(&result, xmlfileout); - xmlout.write(); - } -#if defined(_MSC_VER) && _MSC_VER > 1500 - ::free(xml); -#endif - } catch (std::invalid_argument &e) { - std::cerr << std::endl - << "ERROR: " << e.what() - << std::endl; - return 0; - } - - return result.wasSuccessful() ? 0 : 1; -} diff --git a/tests/taglib/test_aiff.cpp b/tests/taglib/test_aiff.cpp deleted file mode 100644 index d80dff25..00000000 --- a/tests/taglib/test_aiff.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/*************************************************************************** - copyright : (C) 2009 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "tag.h" -#include "tbytevectorlist.h" -#include "aifffile.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestAIFF : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestAIFF); - CPPUNIT_TEST(testAiffProperties); - CPPUNIT_TEST(testAiffCProperties); - CPPUNIT_TEST(testSaveID3v2); - CPPUNIT_TEST(testDuplicateID3v2); - CPPUNIT_TEST(testFuzzedFile1); - CPPUNIT_TEST(testFuzzedFile2); - CPPUNIT_TEST_SUITE_END(); - - public: - void testAiffProperties() { - RIFF::AIFF::File f(TEST_FILE_PATH_C("empty.aiff")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(67, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(706, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(2941U, f.audioProperties()->sampleFrames()); - CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isAiffC()); - } - - void testAiffCProperties() { - RIFF::AIFF::File f(TEST_FILE_PATH_C("alaw.aifc")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(37, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(355, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(1622U, f.audioProperties()->sampleFrames()); - CPPUNIT_ASSERT_EQUAL(true, f.audioProperties()->isAiffC()); - CPPUNIT_ASSERT_EQUAL(ByteVector("ALAW"), f.audioProperties()->compressionType()); - CPPUNIT_ASSERT_EQUAL(String("SGI CCITT G.711 A-law"), f.audioProperties()->compressionName()); - } - - void testSaveID3v2() { - ScopedFileCopy copy("empty", ".aiff"); - string newname = copy.fileName(); - - { - RIFF::AIFF::File f(newname.c_str()); - CPPUNIT_ASSERT(!f.hasID3v2Tag()); - - f.tag()->setTitle(L"TitleXXX"); - f.save(); - CPPUNIT_ASSERT(f.hasID3v2Tag()); - } - { - RIFF::AIFF::File f(newname.c_str()); - CPPUNIT_ASSERT(f.hasID3v2Tag()); - CPPUNIT_ASSERT_EQUAL(String(L"TitleXXX"), f.tag()->title()); - - f.tag()->setTitle(""); - f.save(); - CPPUNIT_ASSERT(!f.hasID3v2Tag()); - } - { - RIFF::AIFF::File f(newname.c_str()); - CPPUNIT_ASSERT(!f.hasID3v2Tag()); - } - } - - void testDuplicateID3v2() { - ScopedFileCopy copy("duplicate_id3v2", ".aiff"); - - // duplicate_id3v2.aiff has duplicate ID3v2 tag chunks. - // title() returns "Title2" if can't skip the second tag. - - RIFF::AIFF::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(f.hasID3v2Tag()); - CPPUNIT_ASSERT_EQUAL(String("Title1"), f.tag()->title()); - - f.save(); - CPPUNIT_ASSERT_EQUAL(7030LL, f.length()); - CPPUNIT_ASSERT_EQUAL(-1LL, f.find("Title2")); - } - - void testFuzzedFile1() { - RIFF::AIFF::File f(TEST_FILE_PATH_C("segfault.aif")); - CPPUNIT_ASSERT(!f.isValid()); - } - - void testFuzzedFile2() { - RIFF::AIFF::File f(TEST_FILE_PATH_C("excessive_alloc.aif")); - CPPUNIT_ASSERT(!f.isValid()); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestAIFF); diff --git a/tests/taglib/test_ape.cpp b/tests/taglib/test_ape.cpp deleted file mode 100644 index fdac5714..00000000 --- a/tests/taglib/test_ape.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/*************************************************************************** - copyright : (C) 2010 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "apetag.h" -#include "id3v1tag.h" -#include "tstringlist.h" -#include "tbytevectorlist.h" -#include "tpropertymap.h" -#include "apefile.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestAPE : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestAPE); - CPPUNIT_TEST(testProperties399); - CPPUNIT_TEST(testProperties399Tagged); - CPPUNIT_TEST(testProperties399Id3v2); - CPPUNIT_TEST(testProperties396); - CPPUNIT_TEST(testProperties390); - CPPUNIT_TEST(testFuzzedFile1); - CPPUNIT_TEST(testFuzzedFile2); - CPPUNIT_TEST(testStripAndProperties); - CPPUNIT_TEST(testRepeatedSave); - CPPUNIT_TEST_SUITE_END(); - - public: - void testProperties399() { - APE::File f(TEST_FILE_PATH_C("mac-399.ape")); - CPPUNIT_ASSERT(f.audioProperties());; - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(3550, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(192, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(156556U, f.audioProperties()->sampleFrames()); - CPPUNIT_ASSERT_EQUAL(3990, f.audioProperties()->version()); - } - - void testProperties399Tagged() { - APE::File f(TEST_FILE_PATH_C("mac-399-tagged.ape")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(3550, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(192, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(156556U, f.audioProperties()->sampleFrames()); - CPPUNIT_ASSERT_EQUAL(3990, f.audioProperties()->version()); - } - - void testProperties399Id3v2() { - APE::File f(TEST_FILE_PATH_C("mac-399-id3v2.ape")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(3550, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(192, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(156556U, f.audioProperties()->sampleFrames()); - CPPUNIT_ASSERT_EQUAL(3990, f.audioProperties()->version()); - } - - void testProperties396() { - APE::File f(TEST_FILE_PATH_C("mac-396.ape")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(3685, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(162496U, f.audioProperties()->sampleFrames()); - CPPUNIT_ASSERT_EQUAL(3960, f.audioProperties()->version()); - } - - void testProperties390() { - APE::File f(TEST_FILE_PATH_C("mac-390-hdr.ape")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(15, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(15630, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(689262U, f.audioProperties()->sampleFrames()); - CPPUNIT_ASSERT_EQUAL(3900, f.audioProperties()->version()); - } - - void testFuzzedFile1() { - APE::File f(TEST_FILE_PATH_C("longloop.ape")); - CPPUNIT_ASSERT(f.isValid()); - } - - void testFuzzedFile2() { - APE::File f(TEST_FILE_PATH_C("zerodiv.ape")); - CPPUNIT_ASSERT(f.isValid()); - } - - void testStripAndProperties() { - ScopedFileCopy copy("mac-399", ".ape"); - - { - APE::File f(copy.fileName().c_str()); - f.APETag(true)->setTitle("APE"); - f.ID3v1Tag(true)->setTitle("ID3v1"); - f.save(); - } - { - APE::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT_EQUAL(String("APE"), f.properties()["TITLE"].front()); - f.strip(APE::File::APE); - CPPUNIT_ASSERT_EQUAL(String("ID3v1"), f.properties()["TITLE"].front()); - f.strip(APE::File::ID3v1); - CPPUNIT_ASSERT(f.properties().isEmpty()); - } - } - - void testRepeatedSave() { - ScopedFileCopy copy("mac-399", ".ape"); - - { - APE::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(!f.hasAPETag()); - CPPUNIT_ASSERT(!f.hasID3v1Tag()); - - f.APETag(true)->setTitle("01234 56789 ABCDE FGHIJ"); - f.save(); - - f.APETag()->setTitle("0"); - f.save(); - - f.ID3v1Tag(true)->setTitle("01234 56789 ABCDE FGHIJ"); - f.APETag()->setTitle("01234 56789 ABCDE FGHIJ 01234 56789 ABCDE FGHIJ 01234 56789"); - f.save(); - } - { - APE::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(f.hasAPETag()); - CPPUNIT_ASSERT(f.hasID3v1Tag()); - } - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestAPE); diff --git a/tests/taglib/test_apetag.cpp b/tests/taglib/test_apetag.cpp deleted file mode 100644 index 4253eab5..00000000 --- a/tests/taglib/test_apetag.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/*************************************************************************** - copyright : (C) 2010 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "tag.h" -#include "tstringlist.h" -#include "tbytevectorlist.h" -#include "tpropertymap.h" -#include "apetag.h" -#include "tdebug.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestAPETag : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestAPETag); - CPPUNIT_TEST(testIsEmpty); - CPPUNIT_TEST(testIsEmpty2); - CPPUNIT_TEST(testPropertyInterface1); - CPPUNIT_TEST(testPropertyInterface2); - CPPUNIT_TEST(testInvalidKeys); - CPPUNIT_TEST(testTextBinary); - CPPUNIT_TEST_SUITE_END(); - - public: - void testIsEmpty() { - APE::Tag tag; - CPPUNIT_ASSERT(tag.isEmpty()); - tag.addValue("COMPOSER", "Mike Oldfield"); - CPPUNIT_ASSERT(!tag.isEmpty()); - } - - void testIsEmpty2() { - APE::Tag tag; - CPPUNIT_ASSERT(tag.isEmpty()); - tag.setArtist("Mike Oldfield"); - CPPUNIT_ASSERT(!tag.isEmpty()); - } - - void testPropertyInterface1() { - APE::Tag tag; - PropertyMap dict = tag.properties(); - CPPUNIT_ASSERT(dict.isEmpty()); - dict["ARTIST"] = String("artist 1"); - dict["ARTIST"].append("artist 2"); - dict["TRACKNUMBER"].append("17"); - tag.setProperties(dict); - CPPUNIT_ASSERT_EQUAL(String("17"), tag.itemListMap()["TRACK"].values()[0]); - CPPUNIT_ASSERT_EQUAL((size_t)2, tag.itemListMap()["ARTIST"].values().size()); - CPPUNIT_ASSERT_EQUAL(String("artist 1 artist 2"), tag.artist()); - CPPUNIT_ASSERT_EQUAL(17u, tag.track()); - } - - void testPropertyInterface2() { - APE::Tag tag; - APE::Item item1 = APE::Item("TRACK", "17"); - tag.setItem("TRACK", item1); - - APE::Item item2 = APE::Item(); - item2.setType(APE::Item::Binary); - tag.setItem("TESTBINARY", item2); - - PropertyMap properties = tag.properties(); - CPPUNIT_ASSERT_EQUAL((size_t)1u, properties.unsupportedData().size()); - CPPUNIT_ASSERT(properties.contains("TRACKNUMBER")); - CPPUNIT_ASSERT(!properties.contains("TRACK")); - CPPUNIT_ASSERT(tag.itemListMap().contains("TESTBINARY")); - - tag.removeUnsupportedProperties(properties.unsupportedData()); - CPPUNIT_ASSERT(!tag.itemListMap().contains("TESTBINARY")); - - APE::Item item3 = APE::Item("TRACKNUMBER", "29"); - tag.setItem("TRACKNUMBER", item3); - properties = tag.properties(); - CPPUNIT_ASSERT_EQUAL((size_t)2, properties["TRACKNUMBER"].size()); - CPPUNIT_ASSERT_EQUAL(String("17"), properties["TRACKNUMBER"][0]); - CPPUNIT_ASSERT_EQUAL(String("29"), properties["TRACKNUMBER"][1]); - } - - void testInvalidKeys() { - PropertyMap properties; - properties["A"] = String("invalid key: one character"); - properties["MP+"] = String("invalid key: forbidden string"); - properties[L"\x1234\x3456"] = String("invalid key: Unicode"); - properties["A B~C"] = String("valid key: space and tilde"); - properties["ARTIST"] = String("valid key: normal one"); - - APE::Tag tag; - PropertyMap unsuccessful = tag.setProperties(properties); - CPPUNIT_ASSERT_EQUAL((size_t)3, unsuccessful.size()); - CPPUNIT_ASSERT(unsuccessful.contains("A")); - CPPUNIT_ASSERT(unsuccessful.contains("MP+")); - CPPUNIT_ASSERT(unsuccessful.contains(L"\x1234\x3456")); - - CPPUNIT_ASSERT_EQUAL((size_t)2, tag.itemListMap().size()); - tag.addValue("VALID KEY", "Test Value 1"); - tag.addValue("INVALID KEY \x7f", "Test Value 2"); - tag.addValue(L"INVALID KEY \x1234\x3456", "Test Value 3"); - CPPUNIT_ASSERT_EQUAL((size_t)3, tag.itemListMap().size()); - } - - void testTextBinary() { - APE::Item item = APE::Item("DUMMY", "Test Text"); - CPPUNIT_ASSERT_EQUAL(String("Test Text"), item.toString()); - CPPUNIT_ASSERT_EQUAL(ByteVector(), item.binaryData()); - - ByteVector data("Test Data"); - item.setBinaryData(data); - CPPUNIT_ASSERT(item.values().isEmpty()); - CPPUNIT_ASSERT_EQUAL(String(), item.toString()); - CPPUNIT_ASSERT_EQUAL(data, item.binaryData()); - - item.setValue("Test Text 2"); - CPPUNIT_ASSERT_EQUAL(String("Test Text 2"), item.toString()); - CPPUNIT_ASSERT_EQUAL(ByteVector(), item.binaryData()); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestAPETag); diff --git a/tests/taglib/test_asf.cpp b/tests/taglib/test_asf.cpp deleted file mode 100644 index d897045f..00000000 --- a/tests/taglib/test_asf.cpp +++ /dev/null @@ -1,306 +0,0 @@ -/*************************************************************************** - copyright : (C) 2008 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "tag.h" -#include "tstring.h" -#include "tstringlist.h" -#include "tbytevectorlist.h" -#include "tpropertymap.h" -#include "asffile.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestASF : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestASF); - CPPUNIT_TEST(testAudioProperties); - CPPUNIT_TEST(testLosslessProperties); - CPPUNIT_TEST(testRead); - CPPUNIT_TEST(testSaveMultipleValues); - CPPUNIT_TEST(testSaveStream); - CPPUNIT_TEST(testSaveLanguage); - CPPUNIT_TEST(testDWordTrackNumber); -// CPPUNIT_TEST(testSaveLargeValue); - CPPUNIT_TEST(testSavePicture); - CPPUNIT_TEST(testSaveMultiplePictures); - CPPUNIT_TEST(testProperties); - CPPUNIT_TEST(testRepeatedSave); - CPPUNIT_TEST_SUITE_END(); - - public: - void testAudioProperties() { - ASF::File f(TEST_FILE_PATH_C("silence-1.wma")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(3712, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(64, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(48000, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(ASF::AudioProperties::WMA2, f.audioProperties()->codec()); - CPPUNIT_ASSERT_EQUAL(String("Windows Media Audio 9.1"), f.audioProperties()->codecName()); - CPPUNIT_ASSERT_EQUAL(String("64 kbps, 48 kHz, stereo 2-pass CBR"), f.audioProperties()->codecDescription()); - CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted()); - } - - void testLosslessProperties() { - ASF::File f(TEST_FILE_PATH_C("lossless.wma")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(3549, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(1152, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(ASF::AudioProperties::WMA9Lossless, f.audioProperties()->codec()); - CPPUNIT_ASSERT_EQUAL(String("Windows Media Audio 9.2 Lossless"), f.audioProperties()->codecName()); - CPPUNIT_ASSERT_EQUAL(String("VBR Quality 100, 44 kHz, 2 channel 16 bit 1-pass VBR"), f.audioProperties()->codecDescription()); - CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted()); - } - - void testRead() { - ASF::File f(TEST_FILE_PATH_C("silence-1.wma")); - CPPUNIT_ASSERT_EQUAL(String("test"), f.tag()->title()); - } - - void testSaveMultipleValues() { - ScopedFileCopy copy("silence-1", ".wma"); - string newname = copy.fileName(); - - { - ASF::File f(newname.c_str()); - ASF::AttributeList values; - values.append("Foo"); - values.append("Bar"); - f.tag()->setAttribute("WM/AlbumTitle", values); - f.save(); - } - { - ASF::File f(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(2, (int)f.tag()->attributeListMap()["WM/AlbumTitle"].size()); - } - } - - void testDWordTrackNumber() { - ScopedFileCopy copy("silence-1", ".wma"); - string newname = copy.fileName(); - - { - ASF::File f(newname.c_str()); - CPPUNIT_ASSERT(!f.tag()->contains("WM/TrackNumber")); - f.tag()->setAttribute("WM/TrackNumber", static_cast(123)); - f.save(); - } - { - ASF::File f(newname.c_str()); - CPPUNIT_ASSERT(f.tag()->contains("WM/TrackNumber")); - CPPUNIT_ASSERT_EQUAL(ASF::Attribute::DWordType, f.tag()->attribute("WM/TrackNumber").front().type()); - CPPUNIT_ASSERT_EQUAL(static_cast(123), f.tag()->track()); - f.tag()->setTrack(234); - f.save(); - } - { - ASF::File f(newname.c_str()); - CPPUNIT_ASSERT(f.tag()->contains("WM/TrackNumber")); - CPPUNIT_ASSERT_EQUAL(ASF::Attribute::UnicodeType, f.tag()->attribute("WM/TrackNumber").front().type()); - CPPUNIT_ASSERT_EQUAL((unsigned int)234, f.tag()->track()); - } - } - - void testSaveStream() { - ScopedFileCopy copy("silence-1", ".wma"); - string newname = copy.fileName(); - - { - ASF::File f(newname.c_str()); - ASF::Attribute attr("Foo"); - attr.setStream(43); - f.tag()->setAttribute("WM/AlbumTitle", attr); - f.save(); - } - - { - ASF::File f(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(43, f.tag()->attribute("WM/AlbumTitle").front().stream()); - } - } - - void testSaveLanguage() { - ScopedFileCopy copy("silence-1", ".wma"); - string newname = copy.fileName(); - - { - ASF::File f(newname.c_str()); - ASF::Attribute attr("Foo"); - attr.setStream(32); - attr.setLanguage(56); - f.tag()->setAttribute("WM/AlbumTitle", attr); - f.save(); - } - { - ASF::File f(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(32, f.tag()->attribute("WM/AlbumTitle").front().stream()); - CPPUNIT_ASSERT_EQUAL(56, f.tag()->attribute("WM/AlbumTitle").front().language()); - } - } - - void testSaveLargeValue() { - ScopedFileCopy copy("silence-1", ".wma"); - string newname = copy.fileName(); - - { - ASF::File f(newname.c_str()); - ASF::Attribute attr(ByteVector(70000, 'x')); - f.tag()->setAttribute("WM/Blob", attr); - f.save(); - } - { - ASF::File f(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(ByteVector(70000, 'x'), f.tag()->attribute("WM/Blob").front().toByteVector()); - } - } - - void testSavePicture() { - ScopedFileCopy copy("silence-1", ".wma"); - string newname = copy.fileName(); - - { - ASF::File f(newname.c_str()); - ASF::Picture picture; - picture.setMimeType("image/jpeg"); - picture.setType(ASF::Picture::FrontCover); - picture.setDescription("description"); - picture.setPicture("data"); - f.tag()->setAttribute("WM/Picture", picture); - f.save(); - } - { - ASF::File f(newname.c_str()); - ASF::AttributeList values2 = f.tag()->attribute("WM/Picture"); - CPPUNIT_ASSERT_EQUAL((size_t)1, values2.size()); - ASF::Attribute attr2 = values2.front(); - ASF::Picture picture2 = attr2.toPicture(); - CPPUNIT_ASSERT(picture2.isValid()); - CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), picture2.mimeType()); - CPPUNIT_ASSERT_EQUAL(ASF::Picture::FrontCover, picture2.type()); - CPPUNIT_ASSERT_EQUAL(String("description"), picture2.description()); - CPPUNIT_ASSERT_EQUAL(ByteVector("data"), picture2.picture()); - } - } - - void testSaveMultiplePictures() { - ScopedFileCopy copy("silence-1", ".wma"); - string newname = copy.fileName(); - - { - ASF::File f(newname.c_str()); - ASF::AttributeList values; - ASF::Picture picture; - picture.setMimeType("image/jpeg"); - picture.setType(ASF::Picture::FrontCover); - picture.setDescription("description"); - picture.setPicture("data"); - values.append(ASF::Attribute(picture)); - ASF::Picture picture2; - picture2.setMimeType("image/png"); - picture2.setType(ASF::Picture::BackCover); - picture2.setDescription("back cover"); - picture2.setPicture("PNG data"); - values.append(ASF::Attribute(picture2)); - f.tag()->setAttribute("WM/Picture", values); - f.save(); - } - { - ASF::File f(newname.c_str()); - ASF::AttributeList values2 = f.tag()->attribute("WM/Picture"); - CPPUNIT_ASSERT_EQUAL((size_t)2, values2.size()); - ASF::Picture picture3 = values2[1].toPicture(); - CPPUNIT_ASSERT(picture3.isValid()); - CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), picture3.mimeType()); - CPPUNIT_ASSERT_EQUAL(ASF::Picture::FrontCover, picture3.type()); - CPPUNIT_ASSERT_EQUAL(String("description"), picture3.description()); - CPPUNIT_ASSERT_EQUAL(ByteVector("data"), picture3.picture()); - ASF::Picture picture4 = values2[0].toPicture(); - CPPUNIT_ASSERT(picture4.isValid()); - CPPUNIT_ASSERT_EQUAL(String("image/png"), picture4.mimeType()); - CPPUNIT_ASSERT_EQUAL(ASF::Picture::BackCover, picture4.type()); - CPPUNIT_ASSERT_EQUAL(String("back cover"), picture4.description()); - CPPUNIT_ASSERT_EQUAL(ByteVector("PNG data"), picture4.picture()); - } - } - - void testProperties() { - ASF::File f(TEST_FILE_PATH_C("silence-1.wma")); - - PropertyMap tags = f.properties(); - - tags["TRACKNUMBER"] = StringList("2"); - tags["DISCNUMBER"] = StringList("3"); - tags["BPM"] = StringList("123"); - tags["ARTIST"] = StringList("Foo Bar"); - f.setProperties(tags); - - tags = f.properties(); - - CPPUNIT_ASSERT_EQUAL(String("Foo Bar"), f.tag()->artist()); - CPPUNIT_ASSERT_EQUAL(StringList("Foo Bar"), tags["ARTIST"]); - - CPPUNIT_ASSERT(f.tag()->contains("WM/BeatsPerMinute")); - CPPUNIT_ASSERT_EQUAL((size_t)1, f.tag()->attributeListMap()["WM/BeatsPerMinute"].size()); - CPPUNIT_ASSERT_EQUAL(String("123"), f.tag()->attribute("WM/BeatsPerMinute").front().toString()); - CPPUNIT_ASSERT_EQUAL(StringList("123"), tags["BPM"]); - - CPPUNIT_ASSERT(f.tag()->contains("WM/TrackNumber")); - CPPUNIT_ASSERT_EQUAL((size_t)1, f.tag()->attributeListMap()["WM/TrackNumber"].size()); - CPPUNIT_ASSERT_EQUAL(String("2"), f.tag()->attribute("WM/TrackNumber").front().toString()); - CPPUNIT_ASSERT_EQUAL(StringList("2"), tags["TRACKNUMBER"]); - - CPPUNIT_ASSERT(f.tag()->contains("WM/PartOfSet")); - CPPUNIT_ASSERT_EQUAL((size_t)1, f.tag()->attributeListMap()["WM/PartOfSet"].size()); - CPPUNIT_ASSERT_EQUAL(String("3"), f.tag()->attribute("WM/PartOfSet").front().toString()); - CPPUNIT_ASSERT_EQUAL(StringList("3"), tags["DISCNUMBER"]); - } - - void testRepeatedSave() { - ScopedFileCopy copy("silence-1", ".wma"); - - { - ASF::File f(copy.fileName().c_str()); - f.tag()->setTitle(longText(128 * 1024)); - f.save(); - CPPUNIT_ASSERT_EQUAL(297578LL, f.length()); - f.tag()->setTitle(longText(16 * 1024)); - f.save(); - CPPUNIT_ASSERT_EQUAL(68202LL, f.length()); - } - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestASF); diff --git a/tests/taglib/test_bytevector.cpp b/tests/taglib/test_bytevector.cpp deleted file mode 100644 index 5ea208fb..00000000 --- a/tests/taglib/test_bytevector.cpp +++ /dev/null @@ -1,579 +0,0 @@ -/*************************************************************************** - copyright : (C) 2007 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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/ * - ***************************************************************************/ - -#define _USE_MATH_DEFINES -#include -#include - -#include "tbytevector.h" -#include "tbytevectorlist.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestByteVector : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestByteVector); - CPPUNIT_TEST(testByteVector); - CPPUNIT_TEST(testFind1); - CPPUNIT_TEST(testFind2); - CPPUNIT_TEST(testFind3); - CPPUNIT_TEST(testRfind1); - CPPUNIT_TEST(testRfind2); - CPPUNIT_TEST(testRfind3); - CPPUNIT_TEST(testToHex); - CPPUNIT_TEST(testIntegerConversion); - CPPUNIT_TEST(testFloatingPointConversion); - CPPUNIT_TEST(testReplace); - CPPUNIT_TEST(testReplaceAndDetach); - CPPUNIT_TEST(testIterator); - CPPUNIT_TEST(testResize); - CPPUNIT_TEST(testAppend1); - CPPUNIT_TEST(testAppend2); - CPPUNIT_TEST(testBase64); - CPPUNIT_TEST_SUITE_END(); - - public: - void testByteVector() { - ByteVector s1("foo"); - CPPUNIT_ASSERT(ByteVectorList::split(s1, " ").size() == 1); - - ByteVector s2("f"); - CPPUNIT_ASSERT(ByteVectorList::split(s2, " ").size() == 1); - - CPPUNIT_ASSERT(ByteVector().isEmpty()); - CPPUNIT_ASSERT_EQUAL((size_t)0, ByteVector().size()); - CPPUNIT_ASSERT(ByteVector("asdf").clear().isEmpty()); - CPPUNIT_ASSERT_EQUAL((size_t)0, ByteVector("asdf").clear().size()); - CPPUNIT_ASSERT_EQUAL(ByteVector(), ByteVector("asdf").clear()); - - ByteVector i("blah blah"); - ByteVector j("blah"); - CPPUNIT_ASSERT(i.containsAt(j, 5, 0)); - CPPUNIT_ASSERT(i.containsAt(j, 6, 1)); - CPPUNIT_ASSERT(i.containsAt(j, 6, 1, 3)); - - i.clear(); - CPPUNIT_ASSERT(i.isEmpty()); - } - - void testFind1() { - CPPUNIT_ASSERT_EQUAL((size_t)4, ByteVector("....SggO.").find("SggO")); - CPPUNIT_ASSERT_EQUAL((size_t)4, ByteVector("....SggO.").find("SggO", 0)); - CPPUNIT_ASSERT_EQUAL((size_t)4, ByteVector("....SggO.").find("SggO", 1)); - CPPUNIT_ASSERT_EQUAL((size_t)4, ByteVector("....SggO.").find("SggO", 2)); - CPPUNIT_ASSERT_EQUAL((size_t)4, ByteVector("....SggO.").find("SggO", 3)); - CPPUNIT_ASSERT_EQUAL((size_t)4, ByteVector("....SggO.").find("SggO", 4)); - CPPUNIT_ASSERT_EQUAL(ByteVector::npos(), ByteVector("....SggO.").find("SggO", 5)); - CPPUNIT_ASSERT_EQUAL(ByteVector::npos(), ByteVector("....SggO.").find("SggO", 6)); - CPPUNIT_ASSERT_EQUAL(ByteVector::npos(), ByteVector("....SggO.").find("SggO", 7)); - CPPUNIT_ASSERT_EQUAL(ByteVector::npos(), ByteVector("....SggO.").find("SggO", 8)); - - // Intentional out-of-bounds access. - ByteVector v("0123456789x"); - v.resize(10); - v.data()[10] = 'x'; - CPPUNIT_ASSERT_EQUAL(ByteVector::npos(), v.find("789x", 7)); - } - - void testFind2() { - CPPUNIT_ASSERT_EQUAL((size_t)0, ByteVector("\x01", 1).find("\x01")); - CPPUNIT_ASSERT_EQUAL((size_t)0, ByteVector("\x01\x02", 2).find("\x01\x02")); - CPPUNIT_ASSERT_EQUAL(ByteVector::npos(), ByteVector("\x01", 1).find("\x02")); - CPPUNIT_ASSERT_EQUAL(ByteVector::npos(), ByteVector("\x01\x02", 2).find("\x01\x03")); - } - - void testFind3() { - CPPUNIT_ASSERT_EQUAL((size_t)4, ByteVector("....SggO.").find('S')); - CPPUNIT_ASSERT_EQUAL((size_t)4, ByteVector("....SggO.").find('S', 0)); - CPPUNIT_ASSERT_EQUAL((size_t)4, ByteVector("....SggO.").find('S', 1)); - CPPUNIT_ASSERT_EQUAL((size_t)4, ByteVector("....SggO.").find('S', 2)); - CPPUNIT_ASSERT_EQUAL((size_t)4, ByteVector("....SggO.").find('S', 3)); - CPPUNIT_ASSERT_EQUAL((size_t)4, ByteVector("....SggO.").find('S', 4)); - CPPUNIT_ASSERT_EQUAL(ByteVector::npos(), ByteVector("....SggO.").find('S', 5)); - CPPUNIT_ASSERT_EQUAL(ByteVector::npos(), ByteVector("....SggO.").find('S', 6)); - CPPUNIT_ASSERT_EQUAL(ByteVector::npos(), ByteVector("....SggO.").find('S', 7)); - CPPUNIT_ASSERT_EQUAL(ByteVector::npos(), ByteVector("....SggO.").find('S', 8)); - } - - void testRfind1() { - CPPUNIT_ASSERT_EQUAL((size_t)1, ByteVector(".OggS....").rfind("OggS", 0)); - CPPUNIT_ASSERT_EQUAL((size_t)1, ByteVector(".OggS....").rfind("OggS", 1)); - CPPUNIT_ASSERT_EQUAL((size_t)1, ByteVector(".OggS....").rfind("OggS", 2)); - CPPUNIT_ASSERT_EQUAL((size_t)1, ByteVector(".OggS....").rfind("OggS", 3)); - CPPUNIT_ASSERT_EQUAL((size_t)1, ByteVector(".OggS....").rfind("OggS", 4)); - CPPUNIT_ASSERT_EQUAL((size_t)1, ByteVector(".OggS....").rfind("OggS", 5)); - CPPUNIT_ASSERT_EQUAL((size_t)1, ByteVector(".OggS....").rfind("OggS", 6)); - CPPUNIT_ASSERT_EQUAL((size_t)1, ByteVector(".OggS....").rfind("OggS", 7)); - CPPUNIT_ASSERT_EQUAL((size_t)1, ByteVector(".OggS....").rfind("OggS", 8)); - CPPUNIT_ASSERT_EQUAL((size_t)1, ByteVector(".OggS....").rfind("OggS")); - } - - void testRfind2() { - ByteVector r0("**************"); - ByteVector r1("OggS**********"); - ByteVector r2("**********OggS"); - ByteVector r3("OggS******OggS"); - ByteVector r4("OggS*OggS*OggS"); - - CPPUNIT_ASSERT_EQUAL(ByteVector::npos(), r0.find("OggS")); - CPPUNIT_ASSERT_EQUAL(ByteVector::npos(), r0.rfind("OggS")); - CPPUNIT_ASSERT_EQUAL((size_t)0, r1.find("OggS")); - CPPUNIT_ASSERT_EQUAL((size_t)0, r1.rfind("OggS")); - CPPUNIT_ASSERT_EQUAL((size_t)10, r2.find("OggS")); - CPPUNIT_ASSERT_EQUAL((size_t)10, r2.rfind("OggS")); - CPPUNIT_ASSERT_EQUAL((size_t)0, r3.find("OggS")); - CPPUNIT_ASSERT_EQUAL((size_t)10, r3.rfind("OggS")); - CPPUNIT_ASSERT_EQUAL((size_t)10, r4.rfind("OggS")); - CPPUNIT_ASSERT_EQUAL((size_t)10, r4.rfind("OggS", 0)); - CPPUNIT_ASSERT_EQUAL((size_t)5, r4.rfind("OggS", 7)); - CPPUNIT_ASSERT_EQUAL((size_t)10, r4.rfind("OggS", 12)); - } - - void testRfind3() { - CPPUNIT_ASSERT_EQUAL((size_t)1, ByteVector(".OggS....").rfind('O', 0)); - CPPUNIT_ASSERT_EQUAL((size_t)1, ByteVector(".OggS....").rfind('O', 1)); - CPPUNIT_ASSERT_EQUAL((size_t)1, ByteVector(".OggS....").rfind('O', 2)); - CPPUNIT_ASSERT_EQUAL((size_t)1, ByteVector(".OggS....").rfind('O', 3)); - CPPUNIT_ASSERT_EQUAL((size_t)1, ByteVector(".OggS....").rfind('O', 4)); - CPPUNIT_ASSERT_EQUAL((size_t)1, ByteVector(".OggS....").rfind('O', 5)); - CPPUNIT_ASSERT_EQUAL((size_t)1, ByteVector(".OggS....").rfind('O', 6)); - CPPUNIT_ASSERT_EQUAL((size_t)1, ByteVector(".OggS....").rfind('O', 7)); - CPPUNIT_ASSERT_EQUAL((size_t)1, ByteVector(".OggS....").rfind('O', 8)); - CPPUNIT_ASSERT_EQUAL((size_t)1, ByteVector(".OggS....").rfind('O')); - } - - void testToHex() { - ByteVector v("\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87\x78\x69\x5a\x4b\x3c\x2d\x1e\x0f", 16); - - CPPUNIT_ASSERT_EQUAL(ByteVector("f0e1d2c3b4a5968778695a4b3c2d1e0f"), v.toHex()); - } - - void testIntegerConversion() { - const ByteVector data("\x00\xff\x01\xff\x00\xff\x01\xff\x00\xff\x01\xff\x00\xff", 14); - - CPPUNIT_ASSERT_EQUAL((short)0x00ff, data.toInt16BE(0)); - CPPUNIT_ASSERT_EQUAL((short)0xff00, data.toInt16LE(0)); - CPPUNIT_ASSERT_EQUAL((short)0xff01, data.toInt16BE(5)); - CPPUNIT_ASSERT_EQUAL((short)0x01ff, data.toInt16LE(5)); - CPPUNIT_ASSERT_EQUAL((short)0xff, data.toInt16BE(13)); - CPPUNIT_ASSERT_EQUAL((short)0xff, data.toInt16LE(13)); - - CPPUNIT_ASSERT_EQUAL((unsigned short)0x00ff, data.toUInt16BE(0)); - CPPUNIT_ASSERT_EQUAL((unsigned short)0xff00, data.toUInt16LE(0)); - CPPUNIT_ASSERT_EQUAL((unsigned short)0xff01, data.toUInt16BE(5)); - CPPUNIT_ASSERT_EQUAL((unsigned short)0x01ff, data.toUInt16LE(5)); - CPPUNIT_ASSERT_EQUAL((unsigned short)0xff, data.toUInt16BE(13)); - CPPUNIT_ASSERT_EQUAL((unsigned short)0xff, data.toUInt16LE(13)); - - CPPUNIT_ASSERT_EQUAL(0x00ff01ffU, data.toUInt32BE(0)); - CPPUNIT_ASSERT_EQUAL(0xff01ff00U, data.toUInt32LE(0)); - CPPUNIT_ASSERT_EQUAL(0xff01ff00U, data.toUInt32BE(5)); - CPPUNIT_ASSERT_EQUAL(0x00ff01ffU, data.toUInt32LE(5)); - CPPUNIT_ASSERT_EQUAL(0x00ffU, data.toUInt32BE(12)); - CPPUNIT_ASSERT_EQUAL(0xff00U, data.toUInt32LE(12)); - - CPPUNIT_ASSERT_EQUAL(0x00ff01U, data.toUInt24BE(0)); - CPPUNIT_ASSERT_EQUAL(0x01ff00U, data.toUInt24LE(0)); - CPPUNIT_ASSERT_EQUAL(0xff01ffU, data.toUInt24BE(5)); - CPPUNIT_ASSERT_EQUAL(0xff01ffU, data.toUInt24LE(5)); - CPPUNIT_ASSERT_EQUAL(0x00ffU, data.toUInt24BE(12)); - CPPUNIT_ASSERT_EQUAL(0xff00U, data.toUInt24LE(12)); - - CPPUNIT_ASSERT_EQUAL((long long)0x00ff01ff00ff01ffULL, data.toInt64BE(0)); - CPPUNIT_ASSERT_EQUAL((long long)0xff01ff00ff01ff00ULL, data.toInt64LE(0)); - CPPUNIT_ASSERT_EQUAL((long long)0xff01ff00ff01ff00ULL, data.toInt64BE(5)); - CPPUNIT_ASSERT_EQUAL((long long)0x00ff01ff00ff01ffULL, data.toInt64LE(5)); - CPPUNIT_ASSERT_EQUAL((long long)0x00ffU, data.toInt64BE(12)); - CPPUNIT_ASSERT_EQUAL((long long)0xff00U, data.toInt64LE(12)); - } - - void testFloatingPointConversion() { - const double Tolerance = 1.0e-7; - - const ByteVector pi32le("\xdb\x0f\x49\x40", 4); - CPPUNIT_ASSERT(std::abs(pi32le.toFloat32LE(0) - M_PI) < Tolerance); - CPPUNIT_ASSERT_EQUAL(pi32le, ByteVector::fromFloat32LE(pi32le.toFloat32LE(0))); - - const ByteVector pi32be("\x40\x49\x0f\xdb", 4); - CPPUNIT_ASSERT(std::abs(pi32be.toFloat32BE(0) - M_PI) < Tolerance); - CPPUNIT_ASSERT_EQUAL(pi32be, ByteVector::fromFloat32BE(pi32be.toFloat32BE(0))); - - const ByteVector pi64le("\x18\x2d\x44\x54\xfb\x21\x09\x40", 8); - CPPUNIT_ASSERT(std::abs(pi64le.toFloat64LE(0) - M_PI) < Tolerance); - CPPUNIT_ASSERT_EQUAL(pi64le, ByteVector::fromFloat64LE(pi64le.toFloat64LE(0))); - - const ByteVector pi64be("\x40\x09\x21\xfb\x54\x44\x2d\x18", 8); - CPPUNIT_ASSERT(std::abs(pi64be.toFloat64BE(0) - M_PI) < Tolerance); - CPPUNIT_ASSERT_EQUAL(pi64be, ByteVector::fromFloat64BE(pi64be.toFloat64BE(0))); - - const ByteVector pi80le("\x00\xc0\x68\x21\xa2\xda\x0f\xc9\x00\x40", 10); - CPPUNIT_ASSERT(std::abs(pi80le.toFloat80LE(0) - M_PI) < Tolerance); - - const ByteVector pi80be("\x40\x00\xc9\x0f\xda\xa2\x21\x68\xc0\x00", 10); - CPPUNIT_ASSERT(std::abs(pi80be.toFloat80BE(0) - M_PI) < Tolerance); - } - - void testReplace() { - { - ByteVector a("abcdabf"); - a.replace(ByteVector(""), ByteVector("")); - CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), a); - } - { - ByteVector a("abcdabf"); - a.replace(ByteVector("foobartoolong"), ByteVector("")); - CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), a); - } - { - ByteVector a("abcdabf"); - a.replace(ByteVector("xx"), ByteVector("yy")); - CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), a); - } - { - ByteVector a("abcdabf"); - a.replace(ByteVector("a"), ByteVector("x")); - CPPUNIT_ASSERT_EQUAL(ByteVector("xbcdxbf"), a); - a.replace(ByteVector("x"), ByteVector("a")); - CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), a); - } - { - ByteVector a("abcdabf"); - a.replace('a', 'x'); - CPPUNIT_ASSERT_EQUAL(ByteVector("xbcdxbf"), a); - a.replace('x', 'a'); - CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), a); - } - { - ByteVector a("abcdabf"); - a.replace(ByteVector("ab"), ByteVector("xy")); - CPPUNIT_ASSERT_EQUAL(ByteVector("xycdxyf"), a); - a.replace(ByteVector("xy"), ByteVector("ab")); - CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), a); - } - { - ByteVector a("abcdabf"); - a.replace(ByteVector("a"), ByteVector("")); - CPPUNIT_ASSERT_EQUAL(ByteVector("bcdbf"), a); - a.replace(ByteVector(""), ByteVector("a")); - CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), a); - } - { - ByteVector a("abcdabf"); - a.replace(ByteVector("b"), ByteVector("")); - CPPUNIT_ASSERT_EQUAL(ByteVector("acdaf"), a); - a.replace(ByteVector(""), ByteVector("b")); - CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), a); - } - { - ByteVector a("abcdabc"); - a.replace(ByteVector("c"), ByteVector("")); - CPPUNIT_ASSERT_EQUAL(ByteVector("abdab"), a); - a.replace(ByteVector(""), ByteVector("c")); - CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabc"), a); - } - { - ByteVector a("abcdaba"); - a.replace(ByteVector("a"), ByteVector("")); - CPPUNIT_ASSERT_EQUAL(ByteVector("bcdb"), a); - a.replace(ByteVector(""), ByteVector("a")); - CPPUNIT_ASSERT_EQUAL(ByteVector("abcdaba"), a); - } - } - - void testReplaceAndDetach() { - { - ByteVector a("abcdabf"); - ByteVector b = a; - a.replace(ByteVector("a"), ByteVector("x")); - CPPUNIT_ASSERT_EQUAL(ByteVector("xbcdxbf"), a); - CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), b); - } - { - ByteVector a("abcdabf"); - ByteVector b = a; - a.replace('a', 'x'); - CPPUNIT_ASSERT_EQUAL(ByteVector("xbcdxbf"), a); - CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), b); - } - { - ByteVector a("abcdabf"); - ByteVector b = a; - a.replace(ByteVector("ab"), ByteVector("xy")); - CPPUNIT_ASSERT_EQUAL(ByteVector("xycdxyf"), a); - CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), b); - } - { - ByteVector a("abcdabf"); - ByteVector b = a; - a.replace(ByteVector("a"), ByteVector("")); - CPPUNIT_ASSERT_EQUAL(ByteVector("bcdbf"), a); - CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), b); - } - { - ByteVector a("abdab"); - ByteVector b = a; - a.replace(ByteVector(""), ByteVector("c")); - CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabc"), a); - CPPUNIT_ASSERT_EQUAL(ByteVector("abdab"), b); - } - } - - void testIterator() { - ByteVector v1("taglib"); - ByteVector v2 = v1; - - ByteVector::Iterator it1 = v1.begin(); - ByteVector::Iterator it2 = v2.begin(); - - CPPUNIT_ASSERT_EQUAL('t', *it1); - CPPUNIT_ASSERT_EQUAL('t', *it2); - - std::advance(it1, 4); - std::advance(it2, 4); - *it2 = 'I'; - CPPUNIT_ASSERT_EQUAL('i', *it1); - CPPUNIT_ASSERT_EQUAL('I', *it2); - CPPUNIT_ASSERT_EQUAL(ByteVector("taglib"), v1); - CPPUNIT_ASSERT_EQUAL(ByteVector("taglIb"), v2); - - ByteVector::ReverseIterator it3 = v1.rbegin(); - ByteVector::ReverseIterator it4 = v2.rbegin(); - - CPPUNIT_ASSERT_EQUAL('b', *it3); - CPPUNIT_ASSERT_EQUAL('b', *it4); - - std::advance(it3, 4); - std::advance(it4, 4); - *it4 = 'A'; - CPPUNIT_ASSERT_EQUAL('a', *it3); - CPPUNIT_ASSERT_EQUAL('A', *it4); - CPPUNIT_ASSERT_EQUAL(ByteVector("taglib"), v1); - CPPUNIT_ASSERT_EQUAL(ByteVector("tAglIb"), v2); - - ByteVector v3; - v3 = ByteVector("0123456789").mid(3, 4); - - it1 = v3.begin(); - it2 = v3.end() - 1; - CPPUNIT_ASSERT_EQUAL('3', *it1); - CPPUNIT_ASSERT_EQUAL('6', *it2); - - it3 = v3.rbegin(); - it4 = v3.rend() - 1; - CPPUNIT_ASSERT_EQUAL('6', *it3); - CPPUNIT_ASSERT_EQUAL('3', *it4); - } - - void testResize() { - ByteVector a = ByteVector("0123456789"); - ByteVector b = a.mid(3, 4); - b.resize(6, 'A'); - CPPUNIT_ASSERT_EQUAL((size_t)6, b.size()); - CPPUNIT_ASSERT_EQUAL('6', b[3]); - CPPUNIT_ASSERT_EQUAL('A', b[4]); - CPPUNIT_ASSERT_EQUAL('A', b[5]); - b.resize(10, 'B'); - CPPUNIT_ASSERT_EQUAL((size_t)10, b.size()); - CPPUNIT_ASSERT_EQUAL('6', b[3]); - CPPUNIT_ASSERT_EQUAL('B', b[6]); - CPPUNIT_ASSERT_EQUAL('B', b[9]); - b.resize(3, 'C'); - CPPUNIT_ASSERT_EQUAL((size_t)3, b.size()); - CPPUNIT_ASSERT_EQUAL(ByteVector::npos(), b.find('C')); - b.resize(3); - CPPUNIT_ASSERT_EQUAL((size_t)3, b.size()); - - // Check if a and b were properly detached. - - CPPUNIT_ASSERT_EQUAL((size_t)10, a.size()); - CPPUNIT_ASSERT_EQUAL('3', a[3]); - CPPUNIT_ASSERT_EQUAL('5', a[5]); - - // Special case that refCount == 1 and d->offset != 0. - - ByteVector c = ByteVector("0123456789").mid(3, 4); - c.resize(6, 'A'); - CPPUNIT_ASSERT_EQUAL(size_t(6), c.size()); - CPPUNIT_ASSERT_EQUAL('6', c[3]); - CPPUNIT_ASSERT_EQUAL('A', c[4]); - CPPUNIT_ASSERT_EQUAL('A', c[5]); - c.resize(10, 'B'); - CPPUNIT_ASSERT_EQUAL((size_t)10, c.size()); - CPPUNIT_ASSERT_EQUAL('6', c[3]); - CPPUNIT_ASSERT_EQUAL('B', c[6]); - CPPUNIT_ASSERT_EQUAL('B', c[9]); - c.resize(3, 'C'); - CPPUNIT_ASSERT_EQUAL((size_t)3, c.size()); - CPPUNIT_ASSERT_EQUAL(ByteVector::npos(), c.find('C')); - } - - void testAppend1() { - ByteVector v1("foo"); - v1.append("bar"); - CPPUNIT_ASSERT_EQUAL(ByteVector("foobar"), v1); - - ByteVector v2("foo"); - v2.append("b"); - CPPUNIT_ASSERT_EQUAL(ByteVector("foob"), v2); - - ByteVector v3; - v3.append("b"); - CPPUNIT_ASSERT_EQUAL(ByteVector("b"), v3); - - ByteVector v4("foo"); - v4.append(v1); - CPPUNIT_ASSERT_EQUAL(ByteVector("foofoobar"), v4); - - ByteVector v5("foo"); - v5.append('b'); - CPPUNIT_ASSERT_EQUAL(ByteVector("foob"), v5); - - ByteVector v6; - v6.append('b'); - CPPUNIT_ASSERT_EQUAL(ByteVector("b"), v6); - - ByteVector v7("taglib"); - ByteVector v8 = v7; - - v7.append("ABC"); - CPPUNIT_ASSERT_EQUAL(ByteVector("taglibABC"), v7); - v7.append('1'); - v7.append('2'); - v7.append('3'); - CPPUNIT_ASSERT_EQUAL(ByteVector("taglibABC123"), v7); - CPPUNIT_ASSERT_EQUAL(ByteVector("taglib"), v8); - } - - void testAppend2() { - ByteVector a("1234"); - a.append(a); - CPPUNIT_ASSERT_EQUAL(ByteVector("12341234"), a); - } - - void testBase64() { - ByteVector sempty; - ByteVector t0("a"); // test 1 byte - ByteVector t1("any carnal pleasure."); - ByteVector t2("any carnal pleasure"); - ByteVector t3("any carnal pleasur"); - ByteVector s0("a"); // test 1 byte - ByteVector s1("any carnal pleasure."); - ByteVector s2("any carnal pleasure"); - ByteVector s3("any carnal pleasur"); - ByteVector eempty; - ByteVector e0("YQ=="); - ByteVector e1("YW55IGNhcm5hbCBwbGVhc3VyZS4="); - ByteVector e2("YW55IGNhcm5hbCBwbGVhc3VyZQ=="); - ByteVector e3("YW55IGNhcm5hbCBwbGVhc3Vy"); - - // Encode - CPPUNIT_ASSERT_EQUAL(eempty, sempty.toBase64()); - CPPUNIT_ASSERT_EQUAL(e0, s0.toBase64()); - CPPUNIT_ASSERT_EQUAL(e1, s1.toBase64()); - CPPUNIT_ASSERT_EQUAL(e2, s2.toBase64()); - CPPUNIT_ASSERT_EQUAL(e3, s3.toBase64()); - - // Decode - CPPUNIT_ASSERT_EQUAL(sempty, ByteVector::fromBase64(eempty)); - CPPUNIT_ASSERT_EQUAL(s0, ByteVector::fromBase64(e0)); - CPPUNIT_ASSERT_EQUAL(s1, ByteVector::fromBase64(e1)); - CPPUNIT_ASSERT_EQUAL(s2, ByteVector::fromBase64(e2)); - CPPUNIT_ASSERT_EQUAL(s3, ByteVector::fromBase64(e3)); - - CPPUNIT_ASSERT_EQUAL(t0, ByteVector::fromBase64(s0.toBase64())); - CPPUNIT_ASSERT_EQUAL(t1, ByteVector::fromBase64(s1.toBase64())); - CPPUNIT_ASSERT_EQUAL(t2, ByteVector::fromBase64(s2.toBase64())); - CPPUNIT_ASSERT_EQUAL(t3, ByteVector::fromBase64(s3.toBase64())); - - ByteVector all((size_t)256, '\0'); - - // in order - { - for (int i = 0; i < 256; i++) { - all[i] = (unsigned char)i; - } - ByteVector b64 = all.toBase64(); - ByteVector original = ByteVector::fromBase64(b64); - CPPUNIT_ASSERT_EQUAL(all, original); - } - - // reverse - { - for (int i = 0; i < 256; i++) { - all[i] = (unsigned char)255 - i; - } - ByteVector b64 = all.toBase64(); - ByteVector original = ByteVector::fromBase64(b64); - CPPUNIT_ASSERT_EQUAL(all, original); - } - - // all zeroes - { - for (int i = 0; i < 256; i++) { - all[i] = 0; - } - ByteVector b64 = all.toBase64(); - ByteVector original = ByteVector::fromBase64(b64); - CPPUNIT_ASSERT_EQUAL(all, original); - } - - // all ones - { - for (int i = 0; i < 256; i++) { - all[i] = (unsigned char)0xff; - } - ByteVector b64 = all.toBase64(); - ByteVector original = ByteVector::fromBase64(b64); - CPPUNIT_ASSERT_EQUAL(all, original); - } - - // Missing end bytes - { - // No missing bytes - ByteVector m0("YW55IGNhcm5hbCBwbGVhc3VyZQ=="); - CPPUNIT_ASSERT_EQUAL(s2, ByteVector::fromBase64(m0)); - - // 1 missing byte - ByteVector m1("YW55IGNhcm5hbCBwbGVhc3VyZQ="); - CPPUNIT_ASSERT_EQUAL(sempty, ByteVector::fromBase64(m1)); - - // 2 missing bytes - ByteVector m2("YW55IGNhcm5hbCBwbGVhc3VyZQ"); - CPPUNIT_ASSERT_EQUAL(sempty, ByteVector::fromBase64(m2)); - - // 3 missing bytes - ByteVector m3("YW55IGNhcm5hbCBwbGVhc3VyZ"); - CPPUNIT_ASSERT_EQUAL(sempty, ByteVector::fromBase64(m3)); - } - - // Grok invalid characters - { - ByteVector invalid("abd\x00\x01\x02\x03\x04"); - CPPUNIT_ASSERT_EQUAL(sempty, ByteVector::fromBase64(invalid)); - } - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestByteVector); diff --git a/tests/taglib/test_bytevectorlist.cpp b/tests/taglib/test_bytevectorlist.cpp deleted file mode 100644 index 75da4a6f..00000000 --- a/tests/taglib/test_bytevectorlist.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/*************************************************************************** - copyright : (C) 2009 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 "tbytevectorlist.h" -#include - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestByteVectorList : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestByteVectorList); - CPPUNIT_TEST(testSplitSingleChar); - CPPUNIT_TEST(testSplitSingleChar_2); - CPPUNIT_TEST_SUITE_END(); - - public: - void testSplitSingleChar() { - ByteVector v("a b"); - - ByteVectorList l = ByteVectorList::split(v, " "); - CPPUNIT_ASSERT_EQUAL((size_t)2, l.size()); - CPPUNIT_ASSERT_EQUAL(ByteVector("a"), l[0]); - CPPUNIT_ASSERT_EQUAL(ByteVector("b"), l[1]); - } - - void testSplitSingleChar_2() { - ByteVector v("a"); - - ByteVectorList l = ByteVectorList::split(v, " "); - CPPUNIT_ASSERT_EQUAL((size_t)1, l.size()); - CPPUNIT_ASSERT_EQUAL(ByteVector("a"), l[0]); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestByteVectorList); diff --git a/tests/taglib/test_bytevectorstream.cpp b/tests/taglib/test_bytevectorstream.cpp deleted file mode 100644 index 21b111fc..00000000 --- a/tests/taglib/test_bytevectorstream.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 "tbytevectorstream.h" -#include - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestByteVectorStream : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestByteVectorStream); - CPPUNIT_TEST(testInitialData); - CPPUNIT_TEST(testWriteBlock); - CPPUNIT_TEST(testWriteBlockResize); - CPPUNIT_TEST(testReadBlock); - CPPUNIT_TEST(testRemoveBlock); - CPPUNIT_TEST(testInsert); - CPPUNIT_TEST(testSeekEnd); - CPPUNIT_TEST_SUITE_END(); - - public: - void testInitialData() { - ByteVector v("abcd"); - ByteVectorStream stream(v); - - CPPUNIT_ASSERT_EQUAL(ByteVector("abcd"), *stream.data()); - } - - void testWriteBlock() { - ByteVector v("abcd"); - ByteVectorStream stream(v); - - stream.seek(1); - stream.writeBlock(ByteVector("xx")); - CPPUNIT_ASSERT_EQUAL(ByteVector("axxd"), *stream.data()); - } - - void testWriteBlockResize() { - ByteVector v("abcd"); - ByteVectorStream stream(v); - - stream.seek(3); - stream.writeBlock(ByteVector("xx")); - CPPUNIT_ASSERT_EQUAL(ByteVector("abcxx"), *stream.data()); - stream.seek(5); - stream.writeBlock(ByteVector("yy")); - CPPUNIT_ASSERT_EQUAL(ByteVector("abcxxyy"), *stream.data()); - } - - void testReadBlock() { - ByteVector v("abcd"); - ByteVectorStream stream(v); - - CPPUNIT_ASSERT_EQUAL(ByteVector("a"), stream.readBlock(1)); - CPPUNIT_ASSERT_EQUAL(ByteVector("bc"), stream.readBlock(2)); - CPPUNIT_ASSERT_EQUAL(ByteVector("d"), stream.readBlock(3)); - CPPUNIT_ASSERT_EQUAL(ByteVector(""), stream.readBlock(3)); - } - - void testRemoveBlock() { - ByteVector v("abcd"); - ByteVectorStream stream(v); - - stream.removeBlock(1, 1); - CPPUNIT_ASSERT_EQUAL(ByteVector("acd"), *stream.data()); - stream.removeBlock(0, 2); - CPPUNIT_ASSERT_EQUAL(ByteVector("d"), *stream.data()); - stream.removeBlock(0, 2); - CPPUNIT_ASSERT_EQUAL(ByteVector(""), *stream.data()); - } - - void testInsert() { - ByteVector v("abcd"); - ByteVectorStream stream(v); - - stream.insert(ByteVector("xx"), 1, 1); - CPPUNIT_ASSERT_EQUAL(ByteVector("axxcd"), *stream.data()); - stream.insert(ByteVector("yy"), 0, 2); - CPPUNIT_ASSERT_EQUAL(ByteVector("yyxcd"), *stream.data()); - stream.insert(ByteVector("foa"), 3, 2); - CPPUNIT_ASSERT_EQUAL(ByteVector("yyxfoa"), *stream.data()); - stream.insert(ByteVector("123"), 3, 0); - CPPUNIT_ASSERT_EQUAL(ByteVector("yyx123foa"), *stream.data()); - } - - void testSeekEnd() { - ByteVector v("abcdefghijklmnopqrstuvwxyz"); - ByteVectorStream stream(v); - CPPUNIT_ASSERT_EQUAL(26LL, stream.length()); - - stream.seek(-4, IOStream::End); - CPPUNIT_ASSERT_EQUAL(ByteVector("w"), stream.readBlock(1)); - - stream.seek(-25, IOStream::End); - CPPUNIT_ASSERT_EQUAL(ByteVector("b"), stream.readBlock(1)); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestByteVectorStream); diff --git a/tests/taglib/test_dsdiff.cpp b/tests/taglib/test_dsdiff.cpp deleted file mode 100644 index 86a43911..00000000 --- a/tests/taglib/test_dsdiff.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include -#include - -#include "tag.h" -#include "tbytevectorlist.h" -#include "dsdifffile.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestDSDIFF : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestDSDIFF); - CPPUNIT_TEST(testProperties); - CPPUNIT_TEST(testTags); - CPPUNIT_TEST(testSaveID3v2); - CPPUNIT_TEST(testRepeatedSave); - CPPUNIT_TEST_SUITE_END(); - - public: - void testProperties() { - DSDIFF::File f(TEST_FILE_PATH_C("empty10ms.dff")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(10, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(5644, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(2822400, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL((long long)28224, f.audioProperties()->sampleCount()); - } - - void testTags() { - ScopedFileCopy copy("empty10ms", ".dff"); - string newname = copy.fileName(); - - DSDIFF::File *f = new DSDIFF::File(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(String(""), f->tag()->artist()); - f->tag()->setArtist("The Artist"); - f->save(); - delete f; - - f = new DSDIFF::File(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(String("The Artist"), f->tag()->artist()); - delete f; - } - - void testSaveID3v2() { - ScopedFileCopy copy("empty10ms", ".dff"); - string newname = copy.fileName(); - - { - DSDIFF::File f(newname.c_str()); - CPPUNIT_ASSERT(!f.hasID3v2Tag()); - - f.tag()->setTitle(L"TitleXXX"); - f.save(); - CPPUNIT_ASSERT(f.hasID3v2Tag()); - } - { - DSDIFF::File f(newname.c_str()); - CPPUNIT_ASSERT(f.hasID3v2Tag()); - CPPUNIT_ASSERT_EQUAL(String(L"TitleXXX"), f.tag()->title()); - - f.tag()->setTitle(""); - f.save(); - CPPUNIT_ASSERT(!f.hasID3v2Tag()); - } - { - DSDIFF::File f(newname.c_str()); - CPPUNIT_ASSERT(!f.hasID3v2Tag()); - f.tag()->setTitle(L"TitleXXX"); - f.save(); - CPPUNIT_ASSERT(f.hasID3v2Tag()); - } - { - DSDIFF::File f(newname.c_str()); - CPPUNIT_ASSERT(f.hasID3v2Tag()); - CPPUNIT_ASSERT_EQUAL(String(L"TitleXXX"), f.tag()->title()); - } - } - - void testRepeatedSave() { - ScopedFileCopy copy("empty10ms", ".dff"); - string newname = copy.fileName(); - - { - DSDIFF::File f(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(String(""), f.tag()->title()); - f.tag()->setTitle("NEW TITLE"); - f.save(); - CPPUNIT_ASSERT_EQUAL(String("NEW TITLE"), f.tag()->title()); - f.tag()->setTitle("NEW TITLE 2"); - f.save(); - CPPUNIT_ASSERT_EQUAL(String("NEW TITLE 2"), f.tag()->title()); - CPPUNIT_ASSERT_EQUAL(8252LL, f.length()); - f.save(); - CPPUNIT_ASSERT_EQUAL(8252LL, f.length()); - } - { - DSDIFF::File f(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(String("NEW TITLE 2"), f.tag()->title()); - } - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestDSDIFF); diff --git a/tests/taglib/test_dsf.cpp b/tests/taglib/test_dsf.cpp deleted file mode 100644 index 7e73f7a5..00000000 --- a/tests/taglib/test_dsf.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include -#include -#include - -#include "tag.h" -#include "tbytevectorlist.h" -#include "dsffile.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestDSF : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestDSF); - CPPUNIT_TEST(testBasic1); - CPPUNIT_TEST(testBasic2); - CPPUNIT_TEST(testTags); - CPPUNIT_TEST_SUITE_END(); - - public: - void testBasic1() { - DSF::File f(TEST_FILE_PATH_C("empty.dsf")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(2822, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(2822400, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->formatVersion()); - CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->formatID()); - CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channelType()); - CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL((long long)0, f.audioProperties()->sampleCount()); - } - - void testBasic2() { - DSF::File f(TEST_FILE_PATH_C("empty10ms.dsf")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(10, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(5645, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(2822400, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->formatVersion()); - CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->formatID()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channelType()); - CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL((long long)28224, f.audioProperties()->sampleCount()); - CPPUNIT_ASSERT_EQUAL(4096, f.audioProperties()->blockSizePerChannel()); - } - - void testTags() { - ScopedFileCopy copy("empty10ms", ".dsf"); - string newname = copy.fileName(); - - DSF::File *f = new DSF::File(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(String(""), f->tag()->artist()); - f->tag()->setArtist("The Artist"); - f->save(); - delete f; - - f = new DSF::File(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(String("The Artist"), f->tag()->artist()); - delete f; - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestDSF); diff --git a/tests/taglib/test_file.cpp b/tests/taglib/test_file.cpp deleted file mode 100644 index 6a370e87..00000000 --- a/tests/taglib/test_file.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/*************************************************************************** - copyright : (C) 2015 by Tsuda Kageyu - email : tsuda.kageyu@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "tfile.h" -#include "utils.h" - -using namespace Strawberry_TagLib::TagLib; - -// File subclass that gives tests access to filesystem operations -class PlainFile : public File { - public: - explicit PlainFile(FileName name) : File(name) {} - Tag *tag() const override { return nullptr; } - AudioProperties *audioProperties() const override { return nullptr; } - bool save() override { return false; } - void truncate(long length) { File::truncate(length); } -}; - -class TestFile : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestFile); - CPPUNIT_TEST(testFindInSmallFile); - CPPUNIT_TEST(testRFindInSmallFile); - CPPUNIT_TEST(testSeek); - CPPUNIT_TEST(testTruncate); - CPPUNIT_TEST_SUITE_END(); - - public: - void testFindInSmallFile() { - ScopedFileCopy copy("empty", ".ogg"); - std::string name = copy.fileName(); - { - PlainFile file(name.c_str()); - file.seek(0); - file.writeBlock(ByteVector("0123456239", 10)); - file.truncate(10); - } - { - PlainFile file(name.c_str()); - CPPUNIT_ASSERT_EQUAL(10LL, file.length()); - - CPPUNIT_ASSERT_EQUAL(2LL, file.find(ByteVector("23", 2))); - CPPUNIT_ASSERT_EQUAL(2LL, file.find(ByteVector("23", 2), 2)); - CPPUNIT_ASSERT_EQUAL(7LL, file.find(ByteVector("23", 2), 3)); - - file.seek(0); - const ByteVector v = file.readBlock(static_cast(file.length())); - CPPUNIT_ASSERT_EQUAL((size_t)10, v.size()); - - CPPUNIT_ASSERT_EQUAL((long long)v.find("23"), file.find("23")); - CPPUNIT_ASSERT_EQUAL((long long)v.find("23", 2), file.find("23", 2)); - CPPUNIT_ASSERT_EQUAL((long long)v.find("23", 3), file.find("23", 3)); - } - } - - void testRFindInSmallFile() { - ScopedFileCopy copy("empty", ".ogg"); - std::string name = copy.fileName(); - { - PlainFile file(name.c_str()); - file.seek(0); - file.writeBlock(ByteVector("0123456239", 10)); - file.truncate(10); - } - { - PlainFile file(name.c_str()); - CPPUNIT_ASSERT_EQUAL(10LL, file.length()); - - CPPUNIT_ASSERT_EQUAL(7LL, file.rfind(ByteVector("23", 2))); - CPPUNIT_ASSERT_EQUAL(7LL, file.rfind(ByteVector("23", 2), 7)); - CPPUNIT_ASSERT_EQUAL(2LL, file.rfind(ByteVector("23", 2), 6)); - - file.seek(0); - const ByteVector v = file.readBlock(static_cast(file.length())); - CPPUNIT_ASSERT_EQUAL((size_t)10, v.size()); - - CPPUNIT_ASSERT_EQUAL((long long)v.rfind("23"), file.rfind("23")); - CPPUNIT_ASSERT_EQUAL((long long)v.rfind("23", 7), file.rfind("23", 7)); - CPPUNIT_ASSERT_EQUAL((long long)v.rfind("23", 6), file.rfind("23", 6)); - } - } - - void testSeek() { - ScopedFileCopy copy("empty", ".ogg"); - std::string name = copy.fileName(); - - PlainFile f(name.c_str()); - CPPUNIT_ASSERT_EQUAL(0LL, f.tell()); - CPPUNIT_ASSERT_EQUAL(4328LL, f.length()); - - f.seek(100, File::Beginning); - CPPUNIT_ASSERT_EQUAL(100LL, f.tell()); - f.seek(100, File::Current); - CPPUNIT_ASSERT_EQUAL(200LL, f.tell()); - f.seek(-300, File::Current); - CPPUNIT_ASSERT_EQUAL(200LL, f.tell()); - - f.seek(-100, File::End); - CPPUNIT_ASSERT_EQUAL(4228LL, f.tell()); - f.seek(-100, File::Current); - CPPUNIT_ASSERT_EQUAL(4128LL, f.tell()); - f.seek(300, File::Current); - CPPUNIT_ASSERT_EQUAL(4428LL, f.tell()); - } - - void testTruncate() { - ScopedFileCopy copy("empty", ".ogg"); - std::string name = copy.fileName(); - - { - PlainFile f(name.c_str()); - CPPUNIT_ASSERT_EQUAL(4328LL, f.length()); - - f.truncate(2000); - CPPUNIT_ASSERT_EQUAL(2000LL, f.length()); - } - { - PlainFile f(name.c_str()); - CPPUNIT_ASSERT_EQUAL(2000LL, f.length()); - } - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestFile); diff --git a/tests/taglib/test_fileref.cpp b/tests/taglib/test_fileref.cpp deleted file mode 100644 index f224686e..00000000 --- a/tests/taglib/test_fileref.cpp +++ /dev/null @@ -1,305 +0,0 @@ -/*************************************************************************** - copyright : (C) 2007 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "tag.h" -#include "fileref.h" -#include "oggflacfile.h" -#include "vorbisfile.h" -#include "mpegfile.h" -#include "mpcfile.h" -#include "asffile.h" -#include "speexfile.h" -#include "flacfile.h" -#include "trueaudiofile.h" -#include "mp4file.h" -#include "wavfile.h" -#include "apefile.h" -#include "aifffile.h" -#include "dsffile.h" -#include "dsdifffile.h" -#include "tfilestream.h" -#include "tbytevectorstream.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -namespace { -class DummyResolver : public FileRef::FileTypeResolver { - public: - File *createFile(FileName fileName, bool, AudioProperties::ReadStyle) const override { - return new Ogg::Vorbis::File(fileName); - } -}; -} // namespace - -class TestFileRef : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestFileRef); - CPPUNIT_TEST(testASF); - CPPUNIT_TEST(testMusepack); - CPPUNIT_TEST(testVorbis); - CPPUNIT_TEST(testSpeex); - CPPUNIT_TEST(testFLAC); - CPPUNIT_TEST(testMP3); - CPPUNIT_TEST(testOGA_FLAC); - CPPUNIT_TEST(testOGA_Vorbis); - CPPUNIT_TEST(testMP4_1); - CPPUNIT_TEST(testMP4_2); - CPPUNIT_TEST(testMP4_3); - CPPUNIT_TEST(testMP4_4); - CPPUNIT_TEST(testTrueAudio); - CPPUNIT_TEST(testAPE); - CPPUNIT_TEST(testWav); - CPPUNIT_TEST(testAIFF_1); - CPPUNIT_TEST(testAIFF_2); - CPPUNIT_TEST(testDSF); - CPPUNIT_TEST(testDSDIFF); - CPPUNIT_TEST(testUnsupported); - CPPUNIT_TEST(testFileResolver); - CPPUNIT_TEST_SUITE_END(); - - public: - template - void fileRefSave(const string &filename, const string &ext) { - ScopedFileCopy copy(filename, ext); - string newname = copy.fileName(); - - { - FileRef f(newname.c_str()); - CPPUNIT_ASSERT(dynamic_cast(f.file())); - CPPUNIT_ASSERT(!f.isNull()); - f.tag()->setArtist("test artist"); - f.tag()->setTitle("test title"); - f.tag()->setGenre("Test!"); - f.tag()->setAlbum("albummmm"); - f.tag()->setTrack(5); - f.tag()->setYear(2020); - f.save(); - } - { - FileRef f(newname.c_str()); - CPPUNIT_ASSERT(!f.isNull()); - CPPUNIT_ASSERT_EQUAL(f.tag()->artist(), String("test artist")); - CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("test title")); - CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("Test!")); - CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("albummmm")); - CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)5); - CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2020); - f.tag()->setArtist("ttest artist"); - f.tag()->setTitle("ytest title"); - f.tag()->setGenre("uTest!"); - f.tag()->setAlbum("ialbummmm"); - f.tag()->setTrack(7); - f.tag()->setYear(2080); - f.save(); - } - { - FileRef f(newname.c_str()); - CPPUNIT_ASSERT(!f.isNull()); - CPPUNIT_ASSERT_EQUAL(f.tag()->artist(), String("ttest artist")); - CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("ytest title")); - CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("uTest!")); - CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("ialbummmm")); - CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)7); - CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2080); - } - - { - FileStream fs(newname.c_str()); - FileRef f(&fs); - CPPUNIT_ASSERT(dynamic_cast(f.file())); - CPPUNIT_ASSERT(!f.isNull()); - CPPUNIT_ASSERT_EQUAL(f.tag()->artist(), String("ttest artist")); - CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("ytest title")); - CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("uTest!")); - CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("ialbummmm")); - CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)7); - CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2080); - f.tag()->setArtist("test artist"); - f.tag()->setTitle("test title"); - f.tag()->setGenre("Test!"); - f.tag()->setAlbum("albummmm"); - f.tag()->setTrack(5); - f.tag()->setYear(2020); - f.save(); - } - - ByteVector fileContent; - { - FileStream fs(newname.c_str()); - FileRef f(&fs); - CPPUNIT_ASSERT(dynamic_cast(f.file())); - CPPUNIT_ASSERT(!f.isNull()); - CPPUNIT_ASSERT_EQUAL(f.tag()->artist(), String("test artist")); - CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("test title")); - CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("Test!")); - CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("albummmm")); - CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)5); - CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2020); - - fs.seek(0); - fileContent = fs.readBlock(fs.length()); - } - - { - ByteVectorStream bs(fileContent); - FileRef f(&bs); - CPPUNIT_ASSERT(dynamic_cast(f.file())); - CPPUNIT_ASSERT(!f.isNull()); - CPPUNIT_ASSERT_EQUAL(f.tag()->artist(), String("test artist")); - CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("test title")); - CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("Test!")); - CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("albummmm")); - CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)5); - CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2020); - f.tag()->setArtist("ttest artist"); - f.tag()->setTitle("ytest title"); - f.tag()->setGenre("uTest!"); - f.tag()->setAlbum("ialbummmm"); - f.tag()->setTrack(7); - f.tag()->setYear(2080); - f.save(); - - fileContent = *bs.data(); - } - { - ByteVectorStream bs(fileContent); - FileRef f(&bs); - CPPUNIT_ASSERT(dynamic_cast(f.file())); - CPPUNIT_ASSERT(!f.isNull()); - CPPUNIT_ASSERT_EQUAL(f.tag()->artist(), String("ttest artist")); - CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("ytest title")); - CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("uTest!")); - CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("ialbummmm")); - CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)7); - CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2080); - } - } - - void testMusepack() { - fileRefSave("click", ".mpc"); - } - - void testASF() { - fileRefSave("silence-1", ".wma"); - } - - void testVorbis() { - fileRefSave("empty", ".ogg"); - } - - void testSpeex() { - fileRefSave("empty", ".spx"); - } - - void testFLAC() { - fileRefSave("no-tags", ".flac"); - } - - void testMP3() { - fileRefSave("xing", ".mp3"); - } - - void testTrueAudio() { - fileRefSave("empty", ".tta"); - } - - void testMP4_1() { - fileRefSave("has-tags", ".m4a"); - } - - void testMP4_2() { - fileRefSave("no-tags", ".m4a"); - } - - void testMP4_3() { - fileRefSave("no-tags", ".3g2"); - } - - void testMP4_4() { - fileRefSave("blank_video", ".m4v"); - } - - void testWav() { - fileRefSave("empty", ".wav"); - } - - void testOGA_FLAC() { - fileRefSave("empty_flac", ".oga"); - } - - void testOGA_Vorbis() { - fileRefSave("empty_vorbis", ".oga"); - } - - void testAPE() { - fileRefSave("mac-399", ".ape"); - } - - void testAIFF_1() { - fileRefSave("empty", ".aiff"); - } - - void testAIFF_2() { - fileRefSave("alaw", ".aifc"); - } - - void testDSF() { - fileRefSave("empty10ms", ".dsf"); - } - - void testDSDIFF() { - fileRefSave("empty10ms", ".dff"); - } - - void testUnsupported() { - FileRef f1(TEST_FILE_PATH_C("no-extension")); - CPPUNIT_ASSERT(f1.isNull()); - - FileRef f2(TEST_FILE_PATH_C("unsupported-extension.xx")); - CPPUNIT_ASSERT(f2.isNull()); - } - - void testFileResolver() { - { - FileRef f(TEST_FILE_PATH_C("xing.mp3")); - CPPUNIT_ASSERT(dynamic_cast(f.file()) != nullptr); - } - - DummyResolver resolver; - FileRef::addFileTypeResolver(&resolver); - - { - FileRef f(TEST_FILE_PATH_C("xing.mp3")); - CPPUNIT_ASSERT(dynamic_cast(f.file()) != nullptr); - } - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestFileRef); diff --git a/tests/taglib/test_flac.cpp b/tests/taglib/test_flac.cpp deleted file mode 100644 index 0e10eb09..00000000 --- a/tests/taglib/test_flac.cpp +++ /dev/null @@ -1,514 +0,0 @@ -/*************************************************************************** - copyright : (C) 2009 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "tag.h" -#include "tstringlist.h" -#include "tbytevectorlist.h" -#include "tpropertymap.h" -#include "flacfile.h" -#include "xiphcomment.h" -#include "id3v1tag.h" -#include "id3v2tag.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestFLAC : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestFLAC); - CPPUNIT_TEST(testSignature); - CPPUNIT_TEST(testMultipleCommentBlocks); - CPPUNIT_TEST(testReadPicture); - CPPUNIT_TEST(testAddPicture); - CPPUNIT_TEST(testReplacePicture); - CPPUNIT_TEST(testRemoveAllPictures); - CPPUNIT_TEST(testRepeatedSave1); - CPPUNIT_TEST(testRepeatedSave2); - CPPUNIT_TEST(testRepeatedSave3); - CPPUNIT_TEST(testSaveMultipleValues); - CPPUNIT_TEST(testDict); - CPPUNIT_TEST(testInvalid); - CPPUNIT_TEST(testAudioProperties); - CPPUNIT_TEST(testZeroSizedPadding1); - CPPUNIT_TEST(testZeroSizedPadding2); - CPPUNIT_TEST(testShrinkPadding); - CPPUNIT_TEST(testSaveID3v1); - CPPUNIT_TEST(testUpdateID3v2); - CPPUNIT_TEST(testEmptyID3v2); - CPPUNIT_TEST(testStripTags); - CPPUNIT_TEST(testRemoveXiphField); - CPPUNIT_TEST(testEmptySeekTable); - CPPUNIT_TEST_SUITE_END(); - - public: - void testSignature() { - FLAC::File f(TEST_FILE_PATH_C("no-tags.flac")); - CPPUNIT_ASSERT_EQUAL(ByteVector("a1b141f766e9849ac3db1030a20a3c77"), f.audioProperties()->signature().toHex()); - } - - void testMultipleCommentBlocks() { - ScopedFileCopy copy("multiple-vc", ".flac"); - string newname = copy.fileName(); - - { - FLAC::File f(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(String("Artist 1"), f.tag()->artist()); - f.tag()->setArtist("The Artist"); - f.save(); - } - { - FLAC::File f(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(String("The Artist"), f.tag()->artist()); - CPPUNIT_ASSERT_EQUAL(69LL, f.find("Artist")); - CPPUNIT_ASSERT_EQUAL(-1LL, f.find("Artist", 70)); - } - } - - void testReadPicture() { - ScopedFileCopy copy("silence-44-s", ".flac"); - string newname = copy.fileName(); - - FLAC::File f(newname.c_str()); - List lst = f.pictureList(); - CPPUNIT_ASSERT_EQUAL((size_t)1, lst.size()); - - FLAC::Picture *pic = lst.front(); - CPPUNIT_ASSERT_EQUAL(FLAC::Picture::FrontCover, pic->type()); - CPPUNIT_ASSERT_EQUAL(1, pic->width()); - CPPUNIT_ASSERT_EQUAL(1, pic->height()); - CPPUNIT_ASSERT_EQUAL(24, pic->colorDepth()); - CPPUNIT_ASSERT_EQUAL(0, pic->numColors()); - CPPUNIT_ASSERT_EQUAL(String("image/png"), pic->mimeType()); - CPPUNIT_ASSERT_EQUAL(String("A pixel."), pic->description()); - CPPUNIT_ASSERT_EQUAL((size_t)150, pic->data().size()); - } - - void testAddPicture() { - ScopedFileCopy copy("silence-44-s", ".flac"); - string newname = copy.fileName(); - - { - FLAC::File f(newname.c_str()); - List lst = f.pictureList(); - CPPUNIT_ASSERT_EQUAL((size_t)1, lst.size()); - - FLAC::Picture *newpic = new FLAC::Picture(); - newpic->setType(FLAC::Picture::BackCover); - newpic->setWidth(5); - newpic->setHeight(6); - newpic->setColorDepth(16); - newpic->setNumColors(7); - newpic->setMimeType("image/jpeg"); - newpic->setDescription("new image"); - newpic->setData("JPEG data"); - f.addPicture(newpic); - f.save(); - } - { - FLAC::File f(newname.c_str()); - List lst = f.pictureList(); - CPPUNIT_ASSERT_EQUAL((size_t)2, lst.size()); - - FLAC::Picture *pic = lst[0]; - CPPUNIT_ASSERT_EQUAL(FLAC::Picture::FrontCover, pic->type()); - CPPUNIT_ASSERT_EQUAL(1, pic->width()); - CPPUNIT_ASSERT_EQUAL(1, pic->height()); - CPPUNIT_ASSERT_EQUAL(24, pic->colorDepth()); - CPPUNIT_ASSERT_EQUAL(0, pic->numColors()); - CPPUNIT_ASSERT_EQUAL(String("image/png"), pic->mimeType()); - CPPUNIT_ASSERT_EQUAL(String("A pixel."), pic->description()); - CPPUNIT_ASSERT_EQUAL((size_t)150, pic->data().size()); - - pic = lst[1]; - CPPUNIT_ASSERT_EQUAL(FLAC::Picture::BackCover, pic->type()); - CPPUNIT_ASSERT_EQUAL(5, pic->width()); - CPPUNIT_ASSERT_EQUAL(6, pic->height()); - CPPUNIT_ASSERT_EQUAL(16, pic->colorDepth()); - CPPUNIT_ASSERT_EQUAL(7, pic->numColors()); - CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), pic->mimeType()); - CPPUNIT_ASSERT_EQUAL(String("new image"), pic->description()); - CPPUNIT_ASSERT_EQUAL(ByteVector("JPEG data"), pic->data()); - } - } - - void testReplacePicture() { - ScopedFileCopy copy("silence-44-s", ".flac"); - string newname = copy.fileName(); - - { - FLAC::File f(newname.c_str()); - List lst = f.pictureList(); - CPPUNIT_ASSERT_EQUAL((size_t)1, lst.size()); - - FLAC::Picture *newpic = new FLAC::Picture(); - newpic->setType(FLAC::Picture::BackCover); - newpic->setWidth(5); - newpic->setHeight(6); - newpic->setColorDepth(16); - newpic->setNumColors(7); - newpic->setMimeType("image/jpeg"); - newpic->setDescription("new image"); - newpic->setData("JPEG data"); - f.removePictures(); - f.addPicture(newpic); - f.save(); - } - { - FLAC::File f(newname.c_str()); - List lst = f.pictureList(); - CPPUNIT_ASSERT_EQUAL((size_t)1, lst.size()); - - FLAC::Picture *pic = lst[0]; - CPPUNIT_ASSERT_EQUAL(FLAC::Picture::BackCover, pic->type()); - CPPUNIT_ASSERT_EQUAL(5, pic->width()); - CPPUNIT_ASSERT_EQUAL(6, pic->height()); - CPPUNIT_ASSERT_EQUAL(16, pic->colorDepth()); - CPPUNIT_ASSERT_EQUAL(7, pic->numColors()); - CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), pic->mimeType()); - CPPUNIT_ASSERT_EQUAL(String("new image"), pic->description()); - CPPUNIT_ASSERT_EQUAL(ByteVector("JPEG data"), pic->data()); - } - } - - void testRemoveAllPictures() { - ScopedFileCopy copy("silence-44-s", ".flac"); - string newname = copy.fileName(); - - { - FLAC::File f(newname.c_str()); - List lst = f.pictureList(); - CPPUNIT_ASSERT_EQUAL((size_t)1, lst.size()); - - f.removePictures(); - f.save(); - } - { - FLAC::File f(newname.c_str()); - List lst = f.pictureList(); - CPPUNIT_ASSERT_EQUAL((size_t)0, lst.size()); - } - } - - void testRepeatedSave1() { - ScopedFileCopy copy("silence-44-s", ".flac"); - string newname = copy.fileName(); - - { - FLAC::File f(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(String("Silence"), f.tag()->title()); - f.tag()->setTitle("NEW TITLE"); - f.save(); - CPPUNIT_ASSERT_EQUAL(String("NEW TITLE"), f.tag()->title()); - f.tag()->setTitle("NEW TITLE 2"); - f.save(); - CPPUNIT_ASSERT_EQUAL(String("NEW TITLE 2"), f.tag()->title()); - } - { - FLAC::File f(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(String("NEW TITLE 2"), f.tag()->title()); - } - } - - void testRepeatedSave2() { - ScopedFileCopy copy("no-tags", ".flac"); - - FLAC::File f(copy.fileName().c_str()); - f.ID3v2Tag(true)->setTitle("0123456789"); - f.save(); - CPPUNIT_ASSERT_EQUAL(5735LL, f.length()); - f.save(); - CPPUNIT_ASSERT_EQUAL(5735LL, f.length()); - CPPUNIT_ASSERT(f.find("fLaC") >= 0); - } - - void testRepeatedSave3() { - ScopedFileCopy copy("no-tags", ".flac"); - - FLAC::File f(copy.fileName().c_str()); - f.xiphComment()->setTitle(longText(8 * 1024)); - f.save(); - CPPUNIT_ASSERT_EQUAL(12862LL, f.length()); - f.save(); - CPPUNIT_ASSERT_EQUAL(12862LL, f.length()); - } - - void testSaveMultipleValues() { - ScopedFileCopy copy("silence-44-s", ".flac"); - string newname = copy.fileName(); - - { - FLAC::File f(newname.c_str()); - f.xiphComment(true)->addField("ARTIST", "artist 1", true); - f.xiphComment(true)->addField("ARTIST", "artist 2", false); - f.save(); - } - { - FLAC::File f(newname.c_str()); - Ogg::FieldListMap m = f.xiphComment()->fieldListMap(); - CPPUNIT_ASSERT_EQUAL((size_t)2, m["ARTIST"].size()); - CPPUNIT_ASSERT_EQUAL(String("artist 1"), m["ARTIST"][0]); - CPPUNIT_ASSERT_EQUAL(String("artist 2"), m["ARTIST"][1]); - } - } - - void testDict() { - // test unicode & multiple values with dict interface - ScopedFileCopy copy("silence-44-s", ".flac"); - string newname = copy.fileName(); - - { - FLAC::File f(newname.c_str()); - PropertyMap dict; - dict["ARTIST"].append("artøst 1"); - dict["ARTIST"].append("artöst 2"); - f.setProperties(dict); - f.save(); - } - { - FLAC::File f(newname.c_str()); - PropertyMap dict = f.properties(); - CPPUNIT_ASSERT_EQUAL((size_t)2, dict["ARTIST"].size()); - CPPUNIT_ASSERT_EQUAL(String("artøst 1"), dict["ARTIST"][0]); - CPPUNIT_ASSERT_EQUAL(String("artöst 2"), dict["ARTIST"][1]); - } - } - - void testInvalid() { - ScopedFileCopy copy("silence-44-s", ".flac"); - PropertyMap map; - map[L"H\x00c4\x00d6"] = String("bla"); - FLAC::File f(copy.fileName().c_str()); - PropertyMap invalid = f.setProperties(map); - CPPUNIT_ASSERT_EQUAL((size_t)1, invalid.size()); - CPPUNIT_ASSERT_EQUAL((size_t)0, f.properties().size()); - } - - void testAudioProperties() { - FLAC::File f(TEST_FILE_PATH_C("sinewave.flac")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(3550, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(145, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(156556ULL, f.audioProperties()->sampleFrames()); - CPPUNIT_ASSERT_EQUAL( - ByteVector("\xcf\xe3\xd9\xda\xba\xde\xab\x2c\xbf\x2c\xa2\x35\x27\x4b\x7f\x76"), - f.audioProperties()->signature()); - } - - void testZeroSizedPadding1() { - ScopedFileCopy copy("zero-sized-padding", ".flac"); - - FLAC::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(f.isValid()); - } - - void testZeroSizedPadding2() { - ScopedFileCopy copy("silence-44-s", ".flac"); - - { - FLAC::File f(copy.fileName().c_str()); - f.xiphComment()->setTitle("ABC"); - f.save(); - } - { - FLAC::File f(copy.fileName().c_str()); - f.xiphComment()->setTitle(std::string(3067, 'X').c_str()); - f.save(); - } - { - FLAC::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(f.isValid()); - } - } - - void testShrinkPadding() { - ScopedFileCopy copy("no-tags", ".flac"); - - { - FLAC::File f(copy.fileName().c_str()); - f.xiphComment()->setTitle(longText(128 * 1024)); - f.save(); - CPPUNIT_ASSERT(f.length() > 128 * 1024); - } - { - FLAC::File f(copy.fileName().c_str()); - f.xiphComment()->setTitle("0123456789"); - f.save(); - CPPUNIT_ASSERT(f.length() < 8 * 1024); - } - } - - void testSaveID3v1() { - ScopedFileCopy copy("no-tags", ".flac"); - - ByteVector audioStream; - { - FLAC::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(!f.hasID3v1Tag()); - CPPUNIT_ASSERT_EQUAL(4692LL, f.length()); - - f.seek(0x0100); - audioStream = f.readBlock(4436); - - f.ID3v1Tag(true)->setTitle("01234 56789 ABCDE FGHIJ"); - f.save(); - CPPUNIT_ASSERT(f.hasID3v1Tag()); - CPPUNIT_ASSERT_EQUAL(4820LL, f.length()); - - f.seek(0x0100); - CPPUNIT_ASSERT_EQUAL(audioStream, f.readBlock(4436)); - } - } - - void testUpdateID3v2() { - ScopedFileCopy copy("no-tags", ".flac"); - - { - FLAC::File f(copy.fileName().c_str()); - f.ID3v2Tag(true)->setTitle("0123456789"); - f.save(); - } - { - FLAC::File f(copy.fileName().c_str()); - f.ID3v2Tag()->setTitle("ABCDEFGHIJ"); - f.save(); - } - { - FLAC::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT_EQUAL(String("ABCDEFGHIJ"), f.ID3v2Tag()->title()); - } - } - - void testEmptyID3v2() { - ScopedFileCopy copy("no-tags", ".flac"); - - { - FLAC::File f(copy.fileName().c_str()); - f.ID3v2Tag(true); - f.save(); - } - { - FLAC::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(!f.hasID3v2Tag()); - } - } - - void testStripTags() { - ScopedFileCopy copy("silence-44-s", ".flac"); - - { - FLAC::File f(copy.fileName().c_str()); - f.xiphComment(true)->setTitle("XiphComment Title"); - f.ID3v1Tag(true)->setTitle("ID3v1 Title"); - f.ID3v2Tag(true)->setTitle("ID3v2 Title"); - f.save(); - } - { - FLAC::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(f.hasXiphComment()); - CPPUNIT_ASSERT(f.hasID3v1Tag()); - CPPUNIT_ASSERT(f.hasID3v2Tag()); - CPPUNIT_ASSERT_EQUAL(String("XiphComment Title"), f.xiphComment()->title()); - CPPUNIT_ASSERT_EQUAL(String("ID3v1 Title"), f.ID3v1Tag()->title()); - CPPUNIT_ASSERT_EQUAL(String("ID3v2 Title"), f.ID3v2Tag()->title()); - f.strip(FLAC::File::ID3v2); - f.save(); - } - { - FLAC::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(f.hasXiphComment()); - CPPUNIT_ASSERT(f.hasID3v1Tag()); - CPPUNIT_ASSERT(!f.hasID3v2Tag()); - CPPUNIT_ASSERT_EQUAL(String("XiphComment Title"), f.xiphComment()->title()); - CPPUNIT_ASSERT_EQUAL(String("ID3v1 Title"), f.ID3v1Tag()->title()); - f.strip(FLAC::File::ID3v1); - f.save(); - } - { - FLAC::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(f.hasXiphComment()); - CPPUNIT_ASSERT(!f.hasID3v1Tag()); - CPPUNIT_ASSERT(!f.hasID3v2Tag()); - CPPUNIT_ASSERT_EQUAL(String("XiphComment Title"), f.xiphComment()->title()); - f.strip(FLAC::File::XiphComment); - f.save(); - } - { - FLAC::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(f.hasXiphComment()); - CPPUNIT_ASSERT(!f.hasID3v1Tag()); - CPPUNIT_ASSERT(!f.hasID3v2Tag()); - CPPUNIT_ASSERT(f.xiphComment()->isEmpty()); - CPPUNIT_ASSERT_EQUAL(String("reference libFLAC 1.1.0 20030126"), f.xiphComment()->vendorID()); - } - } - - void testRemoveXiphField() { - ScopedFileCopy copy("silence-44-s", ".flac"); - - { - FLAC::File f(copy.fileName().c_str()); - f.xiphComment(true)->setTitle("XiphComment Title"); - f.ID3v2Tag(true)->setTitle("ID3v2 Title"); - f.save(); - } - { - FLAC::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT_EQUAL(String("XiphComment Title"), f.xiphComment()->title()); - f.xiphComment()->removeFields("TITLE"); - f.save(); - } - { - FLAC::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT_EQUAL(String(), f.xiphComment()->title()); - } - } - - void testEmptySeekTable() { - ScopedFileCopy copy("empty-seektable", ".flac"); - { - FLAC::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(f.isValid()); - f.xiphComment(true)->setTitle("XiphComment Title"); - f.save(); - } - { - FLAC::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(f.isValid()); - f.seek(42); - const ByteVector data = f.readBlock(4); - CPPUNIT_ASSERT_EQUAL(ByteVector("\x03\x00\x00\x00", 4), data); - } - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestFLAC); diff --git a/tests/taglib/test_flacpicture.cpp b/tests/taglib/test_flacpicture.cpp deleted file mode 100644 index 4efbb101..00000000 --- a/tests/taglib/test_flacpicture.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/*************************************************************************** - copyright : (C) 2010 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "tag.h" -#include "tstringlist.h" -#include "tbytevectorlist.h" -#include "flacfile.h" -#include "flacmetadatablock.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestFLACPicture : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestFLACPicture); - CPPUNIT_TEST(testParse); - CPPUNIT_TEST(testPassThrough); - CPPUNIT_TEST_SUITE_END(); - - public: - void testParse() { - const unsigned char data[] = { 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x09, 0x69, 0x6D, 0x61, 0x67, 0x65, 0x2F, 0x70, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x08, 0x41, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6C, 0x2E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53, 0xDE, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0B, 0x13, 0x00, 0x00, 0x0B, 0x13, 0x01, 0x00, 0x9A, 0x9C, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4D, 0x45, 0x07, 0xD6, 0x0B, 0x1C, 0x0A, 0x36, 0x06, 0x08, 0x44, 0x3D, 0x32, 0x00, 0x00, 0x00, 0x1D, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6F, 0x6D, 0x6D, 0x65, 0x6E, 0x74, 0x00, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x54, 0x68, 0x65, 0x20, 0x47, 0x49, 0x4D, 0x50, 0xEF, 0x64, 0x25, 0x6E, 0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, 0x08, 0xD7, 0x63, 0xF8, 0xFF, 0xFF, 0x3F, 0x00, 0x05, 0xFE, 0x02, 0xFE, 0xDC, 0xCC, 0x59, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 }; - const char *pdata = reinterpret_cast(data); - - FLAC::Picture pic(ByteVector(pdata, 199)); - - CPPUNIT_ASSERT_EQUAL(3, int(pic.type())); - CPPUNIT_ASSERT_EQUAL(1, pic.width()); - CPPUNIT_ASSERT_EQUAL(1, pic.height()); - CPPUNIT_ASSERT_EQUAL(24, pic.colorDepth()); - CPPUNIT_ASSERT_EQUAL(0, pic.numColors()); - CPPUNIT_ASSERT_EQUAL(String("image/png"), pic.mimeType()); - CPPUNIT_ASSERT_EQUAL(String("A pixel."), pic.description()); - CPPUNIT_ASSERT_EQUAL((size_t)150, pic.data().size()); - } - - void testPassThrough() { - const unsigned char data[] = { 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x09, 0x69, 0x6D, 0x61, 0x67, 0x65, 0x2F, 0x70, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x08, 0x41, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6C, 0x2E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53, 0xDE, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0B, 0x13, 0x00, 0x00, 0x0B, 0x13, 0x01, 0x00, 0x9A, 0x9C, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4D, 0x45, 0x07, 0xD6, 0x0B, 0x1C, 0x0A, 0x36, 0x06, 0x08, 0x44, 0x3D, 0x32, 0x00, 0x00, 0x00, 0x1D, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6F, 0x6D, 0x6D, 0x65, 0x6E, 0x74, 0x00, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x54, 0x68, 0x65, 0x20, 0x47, 0x49, 0x4D, 0x50, 0xEF, 0x64, 0x25, 0x6E, 0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, 0x08, 0xD7, 0x63, 0xF8, 0xFF, 0xFF, 0x3F, 0x00, 0x05, 0xFE, 0x02, 0xFE, 0xDC, 0xCC, 0x59, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 }; - const char *pdata = reinterpret_cast(data); - - FLAC::Picture pic(ByteVector(pdata, 199)); - CPPUNIT_ASSERT_EQUAL(ByteVector(pdata, 199), pic.render()); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestFLACPicture); diff --git a/tests/taglib/test_flacunknownmetadatablock.cpp b/tests/taglib/test_flacunknownmetadatablock.cpp deleted file mode 100644 index 85e146e8..00000000 --- a/tests/taglib/test_flacunknownmetadatablock.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/*************************************************************************** - copyright : (C) 2012 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "tag.h" -#include "tstringlist.h" -#include "tbytevectorlist.h" -#include "flacunknownmetadatablock.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestFLACUnknownMetadataBlock : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestFLACUnknownMetadataBlock); - CPPUNIT_TEST(testAccessors); - CPPUNIT_TEST_SUITE_END(); - - public: - void testAccessors() { - ByteVector data("abc\x01", 4); - FLAC::UnknownMetadataBlock block(42, data); - CPPUNIT_ASSERT_EQUAL(42, block.code()); - CPPUNIT_ASSERT_EQUAL(data, block.data()); - CPPUNIT_ASSERT_EQUAL(data, block.render()); - ByteVector data2("xxx", 3); - block.setCode(13); - block.setData(data2); - CPPUNIT_ASSERT_EQUAL(13, block.code()); - CPPUNIT_ASSERT_EQUAL(data2, block.data()); - CPPUNIT_ASSERT_EQUAL(data2, block.render()); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestFLACUnknownMetadataBlock); diff --git a/tests/taglib/test_id3v1.cpp b/tests/taglib/test_id3v1.cpp deleted file mode 100644 index b90c67fd..00000000 --- a/tests/taglib/test_id3v1.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/*************************************************************************** - copyright : (C) 2007 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "tstring.h" -#include "mpegfile.h" -#include "id3v1tag.h" -#include "id3v1genres.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestID3v1 : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestID3v1); - CPPUNIT_TEST(testStripWhiteSpace); - CPPUNIT_TEST(testGenres); - CPPUNIT_TEST_SUITE_END(); - - public: - void testStripWhiteSpace() { - ScopedFileCopy copy("xing", ".mp3"); - string newname = copy.fileName(); - - { - MPEG::File f(newname.c_str()); - f.ID3v1Tag(true)->setArtist("Artist "); - f.save(); - } - - { - MPEG::File f(newname.c_str()); - CPPUNIT_ASSERT(f.ID3v1Tag(false)); - CPPUNIT_ASSERT_EQUAL(String("Artist"), f.ID3v1Tag(false)->artist()); - } - } - - void testGenres() { - CPPUNIT_ASSERT_EQUAL(String("Darkwave"), ID3v1::genre(50)); - CPPUNIT_ASSERT_EQUAL(100, ID3v1::genreIndex("Humour")); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestID3v1); diff --git a/tests/taglib/test_id3v2.cpp b/tests/taglib/test_id3v2.cpp deleted file mode 100644 index dd683673..00000000 --- a/tests/taglib/test_id3v2.cpp +++ /dev/null @@ -1,1285 +0,0 @@ -/*************************************************************************** - copyright : (C) 2007 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "id3v2tag.h" -#include "mpegfile.h" -#include "id3v2frame.h" -#include "uniquefileidentifierframe.h" -#include "textidentificationframe.h" -#include "attachedpictureframe.h" -#include "unsynchronizedlyricsframe.h" -#include "synchronizedlyricsframe.h" -#include "eventtimingcodesframe.h" -#include "generalencapsulatedobjectframe.h" -#include "relativevolumeframe.h" -#include "popularimeterframe.h" -#include "urllinkframe.h" -#include "ownershipframe.h" -#include "unknownframe.h" -#include "chapterframe.h" -#include "tableofcontentsframe.h" -#include "tdebug.h" -#include "tpropertymap.h" -#include "tzlib.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class PublicFrame : public ID3v2::Frame { - public: - PublicFrame() : ID3v2::Frame(ByteVector("XXXX\0\0\0\0\0\0", 10)) {} - String readStringField(const ByteVector &data, String::Type encoding) { - size_t position = 0; - return ID3v2::Frame::readStringField(data, encoding, position); - } - String toString() const override { return String(); } - void parseFields(const ByteVector &) override {} - ByteVector renderFields() const override { return ByteVector(); } -}; - -class TestID3v2 : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestID3v2); - CPPUNIT_TEST(testUnsynchDecode); - CPPUNIT_TEST(testDowngradeUTF8ForID3v23_1); - CPPUNIT_TEST(testDowngradeUTF8ForID3v23_2); - CPPUNIT_TEST(testUTF16BEDelimiter); - CPPUNIT_TEST(testUTF16Delimiter); - CPPUNIT_TEST(testReadStringField); - CPPUNIT_TEST(testParseAPIC); - CPPUNIT_TEST(testParseAPIC_UTF16_BOM); - CPPUNIT_TEST(testParseAPICv22); - CPPUNIT_TEST(testDontRender22); - CPPUNIT_TEST(testParseGEOB); - CPPUNIT_TEST(testPOPMtoString); - CPPUNIT_TEST(testParsePOPM); - CPPUNIT_TEST(testParsePOPMWithoutCounter); - CPPUNIT_TEST(testRenderPOPM); - CPPUNIT_TEST(testPOPMFromFile); - CPPUNIT_TEST(testParseRelativeVolumeFrame); - CPPUNIT_TEST(testParseUniqueFileIdentifierFrame); - CPPUNIT_TEST(testParseEmptyUniqueFileIdentifierFrame); - CPPUNIT_TEST(testBrokenFrame1); - CPPUNIT_TEST(testItunes24FrameSize); - CPPUNIT_TEST(testParseUrlLinkFrame); - CPPUNIT_TEST(testRenderUrlLinkFrame); - CPPUNIT_TEST(testParseUserUrlLinkFrame); - CPPUNIT_TEST(testRenderUserUrlLinkFrame); - CPPUNIT_TEST(testParseOwnershipFrame); - CPPUNIT_TEST(testRenderOwnershipFrame); - CPPUNIT_TEST(testParseSynchronizedLyricsFrame); - CPPUNIT_TEST(testParseSynchronizedLyricsFrameWithEmptyDescritpion); - CPPUNIT_TEST(testRenderSynchronizedLyricsFrame); - CPPUNIT_TEST(testParseEventTimingCodesFrame); - CPPUNIT_TEST(testRenderEventTimingCodesFrame); - CPPUNIT_TEST(testSaveUTF16Comment); - CPPUNIT_TEST(testUpdateGenre23_1); - CPPUNIT_TEST(testUpdateGenre23_2); - CPPUNIT_TEST(testUpdateGenre24); - CPPUNIT_TEST(testUpdateDate22); - CPPUNIT_TEST(testDowngradeTo23); - // CPPUNIT_TEST(testUpdateFullDate22); TODO TYE+TDA should be upgraded to TDRC together - CPPUNIT_TEST(testCompressedFrameWithBrokenLength); - CPPUNIT_TEST(testW000); - CPPUNIT_TEST(testPropertyInterface); - CPPUNIT_TEST(testPropertyInterface2); - CPPUNIT_TEST(testPropertiesMovement); - CPPUNIT_TEST(testPropertyGrouping); - CPPUNIT_TEST(testDeleteFrame); - CPPUNIT_TEST(testSaveAndStripID3v1ShouldNotAddFrameFromID3v1ToId3v2); - CPPUNIT_TEST(testParseChapterFrame); - CPPUNIT_TEST(testRenderChapterFrame); - CPPUNIT_TEST(testParseTableOfContentsFrame); - CPPUNIT_TEST(testRenderTableOfContentsFrame); - CPPUNIT_TEST(testShrinkPadding); - CPPUNIT_TEST(testEmptyFrame); - CPPUNIT_TEST(testDuplicateTags); - CPPUNIT_TEST(testParseTOCFrameWithManyChildren); - CPPUNIT_TEST_SUITE_END(); - - public: - void testUnsynchDecode() { - MPEG::File f(TEST_FILE_PATH_C("unsynch.id3"), false); - CPPUNIT_ASSERT(f.tag()); - CPPUNIT_ASSERT_EQUAL(String("My babe just cares for me"), f.tag()->title()); - } - - void testDowngradeUTF8ForID3v23_1() { - ScopedFileCopy copy("xing", ".mp3"); - string newname = copy.fileName(); - - ID3v2::TextIdentificationFrame *f = new ID3v2::TextIdentificationFrame(ByteVector("TPE1"), String::UTF8); - StringList sl; - sl.append("Foo"); - f->setText(sl); - - MPEG::File file(newname.c_str()); - file.ID3v2Tag(true)->addFrame(f); - file.save(MPEG::File::ID3v2, File::StripOthers, ID3v2::v3); - CPPUNIT_ASSERT_EQUAL(true, file.hasID3v2Tag()); - - ByteVector data = f->render(); - CPPUNIT_ASSERT_EQUAL((size_t)(4 + 4 + 2 + 1 + 6 + 2), data.size()); - - ID3v2::TextIdentificationFrame f2(data); - CPPUNIT_ASSERT_EQUAL(sl, f2.fieldList()); - CPPUNIT_ASSERT_EQUAL(String::UTF16, f2.textEncoding()); - } - - void testDowngradeUTF8ForID3v23_2() { - ScopedFileCopy copy("xing", ".mp3"); - - ID3v2::UnsynchronizedLyricsFrame *f = new ID3v2::UnsynchronizedLyricsFrame(String::UTF8); - f->setText("Foo"); - - MPEG::File file(copy.fileName().c_str()); - file.ID3v2Tag(true)->addFrame(f); - file.save(MPEG::File::ID3v2, File::StripOthers, ID3v2::v3); - CPPUNIT_ASSERT(file.hasID3v2Tag()); - - ByteVector data = f->render(); - CPPUNIT_ASSERT_EQUAL(static_cast(4 + 4 + 2 + 1 + 3 + 2 + 2 + 6 + 2), data.size()); - - ID3v2::UnsynchronizedLyricsFrame f2(data); - CPPUNIT_ASSERT_EQUAL(String("Foo"), f2.text()); - CPPUNIT_ASSERT_EQUAL(String::UTF16, f2.textEncoding()); - } - - void testUTF16BEDelimiter() { - ID3v2::TextIdentificationFrame f(ByteVector("TPE1"), String::UTF16BE); - StringList sl; - sl.append("Foo"); - sl.append("Bar"); - f.setText(sl); - CPPUNIT_ASSERT_EQUAL(static_cast(4 + 4 + 2 + 1 + 6 + 2 + 6), f.render().size()); - } - - void testUTF16Delimiter() { - ID3v2::TextIdentificationFrame f(ByteVector("TPE1"), String::UTF16); - StringList sl; - sl.append("Foo"); - sl.append("Bar"); - f.setText(sl); - CPPUNIT_ASSERT_EQUAL(static_cast(4 + 4 + 2 + 1 + 8 + 2 + 8), f.render().size()); - } - - void testBrokenFrame1() { - MPEG::File f(TEST_FILE_PATH_C("broken-tenc.id3"), false); - CPPUNIT_ASSERT(f.tag()); - CPPUNIT_ASSERT(!f.ID3v2Tag()->frameListMap().contains("TENC")); - } - - void testReadStringField() { - PublicFrame f; - ByteVector data("abc\0", 4); - String str = f.readStringField(data, String::Latin1); - CPPUNIT_ASSERT_EQUAL(String("abc"), str); - } - - // http://bugs.kde.org/show_bug.cgi?id=151078 - void testParseAPIC() { - std::cout << __PRETTY_FUNCTION__ << "\n"; - ID3v2::AttachedPictureFrame f(ByteVector("APIC" - "\x00\x00\x00\x07" - "\x00\x00" - "\x00" - "m\x00" - "\x01" - "d\x00" - "\x00", - 17)); - std::cout << __PRETTY_FUNCTION__ << "\n"; - CPPUNIT_ASSERT_EQUAL(String("m"), f.mimeType()); - CPPUNIT_ASSERT_EQUAL(ID3v2::AttachedPictureFrame::FileIcon, f.type()); - CPPUNIT_ASSERT_EQUAL(String("d"), f.description()); - } - - void testParseAPIC_UTF16_BOM() { - ID3v2::AttachedPictureFrame f(ByteVector( - "\x41\x50\x49\x43\x00\x02\x0c\x59\x00\x00\x01\x69\x6d\x61\x67\x65" - "\x2f\x6a\x70\x65\x67\x00\x00\xfe\xff\x00\x63\x00\x6f\x00\x76\x00" - "\x65\x00\x72\x00\x2e\x00\x6a\x00\x70\x00\x67\x00\x00\xff\xd8\xff", - 16 * 3)); - CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), f.mimeType()); - CPPUNIT_ASSERT_EQUAL(ID3v2::AttachedPictureFrame::Other, f.type()); - CPPUNIT_ASSERT_EQUAL(String("cover.jpg"), f.description()); - CPPUNIT_ASSERT_EQUAL(ByteVector("\xff\xd8\xff", 3), f.picture()); - } - - void testParseAPICv22() { - ID3v2::FrameFactory *factory = ID3v2::FrameFactory::instance(); - ByteVector data = ByteVector("PIC" - "\x00\x00\x08" - "\x00" - "JPG" - "\x01" - "d\x00" - "\x00", - 14); - ID3v2::Header header; - header.setMajorVersion(2); - ID3v2::AttachedPictureFrame *frame = dynamic_cast(factory->createFrame(data, &header)); - - CPPUNIT_ASSERT(frame); - CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), frame->mimeType()); - CPPUNIT_ASSERT_EQUAL(ID3v2::AttachedPictureFrame::FileIcon, frame->type()); - CPPUNIT_ASSERT_EQUAL(String("d"), frame->description()); - - delete frame; - } - - void testDontRender22() { - ID3v2::FrameFactory *factory = ID3v2::FrameFactory::instance(); - ByteVector data = ByteVector("FOO" - "\x00\x00\x08" - "\x00" - "JPG" - "\x01" - "d\x00" - "\x00", - 14); - ID3v2::Header header; - header.setMajorVersion(2); - ID3v2::UnknownFrame *frame = - dynamic_cast(factory->createFrame(data, &header)); - - CPPUNIT_ASSERT(frame); - - ID3v2::Tag tag; - tag.addFrame(frame); - CPPUNIT_ASSERT_EQUAL((size_t)1034, tag.render().size()); - } - - // http://bugs.kde.org/show_bug.cgi?id=151078 - void testParseGEOB() { - ID3v2::GeneralEncapsulatedObjectFrame f(ByteVector("GEOB" - "\x00\x00\x00\x08" - "\x00\x00" - "\x00" - "m\x00" - "f\x00" - "d\x00" - "\x00", - 18)); - CPPUNIT_ASSERT_EQUAL(String("m"), f.mimeType()); - CPPUNIT_ASSERT_EQUAL(String("f"), f.fileName()); - CPPUNIT_ASSERT_EQUAL(String("d"), f.description()); - } - - void testParsePOPM() { - ID3v2::PopularimeterFrame f(ByteVector("POPM" - "\x00\x00\x00\x17" - "\x00\x00" - "email@example.com\x00" - "\x02" - "\x00\x00\x00\x03", - 33)); - CPPUNIT_ASSERT_EQUAL(String("email@example.com"), f.email()); - CPPUNIT_ASSERT_EQUAL(2, f.rating()); - CPPUNIT_ASSERT_EQUAL((unsigned int)3, f.counter()); - } - - void testParsePOPMWithoutCounter() { - ID3v2::PopularimeterFrame f(ByteVector("POPM" - "\x00\x00\x00\x13" - "\x00\x00" - "email@example.com\x00" - "\x02", - 29)); - CPPUNIT_ASSERT_EQUAL(String("email@example.com"), f.email()); - CPPUNIT_ASSERT_EQUAL(2, f.rating()); - CPPUNIT_ASSERT_EQUAL((unsigned int)0, f.counter()); - } - - void testRenderPOPM() { - ID3v2::PopularimeterFrame f; - f.setEmail("email@example.com"); - f.setRating(2); - f.setCounter(3); - CPPUNIT_ASSERT_EQUAL( - ByteVector("POPM" - "\x00\x00\x00\x17" - "\x00\x00" - "email@example.com\x00" - "\x02" - "\x00\x00\x00\x03", - 33), - f.render()); - } - - void testPOPMtoString() { - ID3v2::PopularimeterFrame f; - f.setEmail("email@example.com"); - f.setRating(2); - f.setCounter(3); - CPPUNIT_ASSERT_EQUAL( - String("email@example.com rating=2 counter=3"), f.toString()); - } - - void testPOPMFromFile() { - ScopedFileCopy copy("xing", ".mp3"); - string newname = copy.fileName(); - - ID3v2::PopularimeterFrame *f = new ID3v2::PopularimeterFrame(); - f->setEmail("email@example.com"); - f->setRating(200); - f->setCounter(3); - - { - MPEG::File foo(newname.c_str()); - foo.ID3v2Tag()->addFrame(f); - foo.save(); - } - { - MPEG::File bar(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(String("email@example.com"), dynamic_cast(bar.ID3v2Tag()->frameList("POPM").front())->email()); - CPPUNIT_ASSERT_EQUAL(200, dynamic_cast(bar.ID3v2Tag()->frameList("POPM").front())->rating()); - } - } - - // http://bugs.kde.org/show_bug.cgi?id=150481 - void testParseRelativeVolumeFrame() { - ID3v2::RelativeVolumeFrame f( - ByteVector("RVA2" // Frame ID - "\x00\x00\x00\x0B" // Frame size - "\x00\x00" // Frame flags - "ident\x00" // Identification - "\x02" // Type of channel - "\x00\x0F" // Volume adjustment - "\x08" // Bits representing peak - "\x45", - 21)); // Peak volume - CPPUNIT_ASSERT_EQUAL(String("ident"), f.identification()); - CPPUNIT_ASSERT_EQUAL(15.0f / 512.0f, - f.volumeAdjustment(ID3v2::RelativeVolumeFrame::FrontRight)); - CPPUNIT_ASSERT_EQUAL((unsigned char)8, - f.peakVolume(ID3v2::RelativeVolumeFrame::FrontRight).bitsRepresentingPeak); - CPPUNIT_ASSERT_EQUAL(ByteVector("\x45"), - f.peakVolume(ID3v2::RelativeVolumeFrame::FrontRight).peakVolume); - } - - void testParseUniqueFileIdentifierFrame() { - ID3v2::UniqueFileIdentifierFrame f( - ByteVector("UFID" // Frame ID - "\x00\x00\x00\x09" // Frame size - "\x00\x00" // Frame flags - "owner\x00" // Owner identifier - "\x00\x01\x02", - 19)); // Identifier - CPPUNIT_ASSERT_EQUAL(String("owner"), - f.owner()); - CPPUNIT_ASSERT_EQUAL(ByteVector("\x00\x01\x02", 3), - f.identifier()); - } - - void testParseEmptyUniqueFileIdentifierFrame() { - ID3v2::UniqueFileIdentifierFrame f( - ByteVector("UFID" // Frame ID - "\x00\x00\x00\x01" // Frame size - "\x00\x00" // Frame flags - "\x00" // Owner identifier - "", - 11)); // Identifier - CPPUNIT_ASSERT_EQUAL(String(), - f.owner()); - CPPUNIT_ASSERT_EQUAL(ByteVector(), - f.identifier()); - } - - void testParseUrlLinkFrame() { - ID3v2::UrlLinkFrame f( - ByteVector("WOAF" // Frame ID - "\x00\x00\x00\x12" // Frame size - "\x00\x00" // Frame flags - "http://example.com", - 28)); // URL - CPPUNIT_ASSERT_EQUAL(String("http://example.com"), f.url()); - } - - void testRenderUrlLinkFrame() { - ID3v2::UrlLinkFrame f("WOAF"); - f.setUrl("http://example.com"); - CPPUNIT_ASSERT_EQUAL( - ByteVector("WOAF" // Frame ID - "\x00\x00\x00\x12" // Frame size - "\x00\x00" // Frame flags - "http://example.com", - 28), // URL - f.render()); - } - - void testParseUserUrlLinkFrame() { - ID3v2::UserUrlLinkFrame f( - ByteVector("WXXX" // Frame ID - "\x00\x00\x00\x17" // Frame size - "\x00\x00" // Frame flags - "\x00" // Text encoding - "foo\x00" // Description - "http://example.com", - 33)); // URL - CPPUNIT_ASSERT_EQUAL(String("foo"), f.description()); - CPPUNIT_ASSERT_EQUAL(String("http://example.com"), f.url()); - } - - void testRenderUserUrlLinkFrame() { - ID3v2::UserUrlLinkFrame f; - f.setDescription("foo"); - f.setUrl("http://example.com"); - CPPUNIT_ASSERT_EQUAL( - ByteVector("WXXX" // Frame ID - "\x00\x00\x00\x17" // Frame size - "\x00\x00" // Frame flags - "\x00" // Text encoding - "foo\x00" // Description - "http://example.com", - 33), // URL - f.render()); - } - - void testParseOwnershipFrame() { - ID3v2::OwnershipFrame f( - ByteVector("OWNE" // Frame ID - "\x00\x00\x00\x19" // Frame size - "\x00\x00" // Frame flags - "\x00" // Text encoding - "GBP1.99\x00" // Price paid - "20120905" // Date of purchase - "Beatport", - 35)); // Seller - CPPUNIT_ASSERT_EQUAL(String("GBP1.99"), f.pricePaid()); - CPPUNIT_ASSERT_EQUAL(String("20120905"), f.datePurchased()); - CPPUNIT_ASSERT_EQUAL(String("Beatport"), f.seller()); - } - - void testRenderOwnershipFrame() { - ID3v2::OwnershipFrame f; - f.setPricePaid("GBP1.99"); - f.setDatePurchased("20120905"); - f.setSeller("Beatport"); - CPPUNIT_ASSERT_EQUAL( - ByteVector("OWNE" // Frame ID - "\x00\x00\x00\x19" // Frame size - "\x00\x00" // Frame flags - "\x00" // Text encoding - "GBP1.99\x00" // Price paid - "20120905" // Date of purchase - "Beatport", - 35), // URL - f.render()); - } - - void testParseSynchronizedLyricsFrame() { - ID3v2::SynchronizedLyricsFrame f( - ByteVector("SYLT" // Frame ID - "\x00\x00\x00\x21" // Frame size - "\x00\x00" // Frame flags - "\x00" // Text encoding - "eng" // Language - "\x02" // Time stamp format - "\x01" // Content type - "foo\x00" // Content descriptor - "Example\x00" // 1st text - "\x00\x00\x04\xd2" // 1st time stamp - "Lyrics\x00" // 2nd text - "\x00\x00\x11\xd7", - 43)); // 2nd time stamp - CPPUNIT_ASSERT_EQUAL(String::Latin1, f.textEncoding()); - CPPUNIT_ASSERT_EQUAL(ByteVector("eng", 3), f.language()); - CPPUNIT_ASSERT_EQUAL(ID3v2::SynchronizedLyricsFrame::AbsoluteMilliseconds, - f.timestampFormat()); - CPPUNIT_ASSERT_EQUAL(ID3v2::SynchronizedLyricsFrame::Lyrics, f.type()); - CPPUNIT_ASSERT_EQUAL(String("foo"), f.description()); - ID3v2::SynchronizedLyricsFrame::SynchedTextList stl = f.synchedText(); - CPPUNIT_ASSERT_EQUAL(static_cast(2), stl.size()); - CPPUNIT_ASSERT_EQUAL(String("Example"), stl[0].text); - CPPUNIT_ASSERT_EQUAL((unsigned int)1234, stl[0].time); - CPPUNIT_ASSERT_EQUAL(String("Lyrics"), stl[1].text); - CPPUNIT_ASSERT_EQUAL((unsigned int)4567, stl[1].time); - } - - void testParseSynchronizedLyricsFrameWithEmptyDescritpion() { - ID3v2::SynchronizedLyricsFrame f( - ByteVector("SYLT" // Frame ID - "\x00\x00\x00\x21" // Frame size - "\x00\x00" // Frame flags - "\x00" // Text encoding - "eng" // Language - "\x02" // Time stamp format - "\x01" // Content type - "\x00" // Content descriptor - "Example\x00" // 1st text - "\x00\x00\x04\xd2" // 1st time stamp - "Lyrics\x00" // 2nd text - "\x00\x00\x11\xd7", - 40)); // 2nd time stamp - CPPUNIT_ASSERT_EQUAL(String::Latin1, f.textEncoding()); - CPPUNIT_ASSERT_EQUAL(ByteVector("eng", 3), f.language()); - CPPUNIT_ASSERT_EQUAL(ID3v2::SynchronizedLyricsFrame::AbsoluteMilliseconds, - f.timestampFormat()); - CPPUNIT_ASSERT_EQUAL(ID3v2::SynchronizedLyricsFrame::Lyrics, f.type()); - CPPUNIT_ASSERT(f.description().isEmpty()); - ID3v2::SynchronizedLyricsFrame::SynchedTextList stl = f.synchedText(); - CPPUNIT_ASSERT_EQUAL(static_cast(2), stl.size()); - CPPUNIT_ASSERT_EQUAL(String("Example"), stl[0].text); - CPPUNIT_ASSERT_EQUAL((unsigned int)1234, stl[0].time); - CPPUNIT_ASSERT_EQUAL(String("Lyrics"), stl[1].text); - CPPUNIT_ASSERT_EQUAL((unsigned int)4567, stl[1].time); - } - - void testRenderSynchronizedLyricsFrame() { - ID3v2::SynchronizedLyricsFrame f; - f.setTextEncoding(String::Latin1); - f.setLanguage(ByteVector("eng", 3)); - f.setTimestampFormat(ID3v2::SynchronizedLyricsFrame::AbsoluteMilliseconds); - f.setType(ID3v2::SynchronizedLyricsFrame::Lyrics); - f.setDescription("foo"); - ID3v2::SynchronizedLyricsFrame::SynchedTextList stl; - stl.append(ID3v2::SynchronizedLyricsFrame::SynchedText(1234, "Example")); - stl.append(ID3v2::SynchronizedLyricsFrame::SynchedText(4567, "Lyrics")); - f.setSynchedText(stl); - CPPUNIT_ASSERT_EQUAL( - ByteVector("SYLT" // Frame ID - "\x00\x00\x00\x21" // Frame size - "\x00\x00" // Frame flags - "\x00" // Text encoding - "eng" // Language - "\x02" // Time stamp format - "\x01" // Content type - "foo\x00" // Content descriptor - "Example\x00" // 1st text - "\x00\x00\x04\xd2" // 1st time stamp - "Lyrics\x00" // 2nd text - "\x00\x00\x11\xd7", - 43), // 2nd time stamp - f.render()); - } - - void testParseEventTimingCodesFrame() { - ID3v2::EventTimingCodesFrame f( - ByteVector("ETCO" // Frame ID - "\x00\x00\x00\x0b" // Frame size - "\x00\x00" // Frame flags - "\x02" // Time stamp format - "\x02" // 1st event - "\x00\x00\xf3\x5c" // 1st time stamp - "\xfe" // 2nd event - "\x00\x36\xee\x80", - 21)); // 2nd time stamp - CPPUNIT_ASSERT_EQUAL(ID3v2::EventTimingCodesFrame::AbsoluteMilliseconds, - f.timestampFormat()); - ID3v2::EventTimingCodesFrame::SynchedEventList sel = f.synchedEvents(); - CPPUNIT_ASSERT_EQUAL(size_t(2), sel.size()); - CPPUNIT_ASSERT_EQUAL(ID3v2::EventTimingCodesFrame::IntroStart, sel[0].type); - CPPUNIT_ASSERT_EQUAL((unsigned int)62300, sel[0].time); - CPPUNIT_ASSERT_EQUAL(ID3v2::EventTimingCodesFrame::AudioFileEnds, sel[1].type); - CPPUNIT_ASSERT_EQUAL((unsigned int)3600000, sel[1].time); - } - - void testRenderEventTimingCodesFrame() { - ID3v2::EventTimingCodesFrame f; - f.setTimestampFormat(ID3v2::EventTimingCodesFrame::AbsoluteMilliseconds); - ID3v2::EventTimingCodesFrame::SynchedEventList sel; - sel.append(ID3v2::EventTimingCodesFrame::SynchedEvent(62300, ID3v2::EventTimingCodesFrame::IntroStart)); - sel.append(ID3v2::EventTimingCodesFrame::SynchedEvent(3600000, ID3v2::EventTimingCodesFrame::AudioFileEnds)); - f.setSynchedEvents(sel); - CPPUNIT_ASSERT_EQUAL( - ByteVector("ETCO" // Frame ID - "\x00\x00\x00\x0b" // Frame size - "\x00\x00" // Frame flags - "\x02" // Time stamp format - "\x02" // 1st event - "\x00\x00\xf3\x5c" // 1st time stamp - "\xfe" // 2nd event - "\x00\x36\xee\x80", - 21), // 2nd time stamp - f.render()); - } - - void testItunes24FrameSize() { - MPEG::File f(TEST_FILE_PATH_C("005411.id3"), false); - CPPUNIT_ASSERT(f.tag()); - CPPUNIT_ASSERT(f.ID3v2Tag()->frameListMap().contains("TIT2")); - CPPUNIT_ASSERT_EQUAL(String("Sunshine Superman"), f.ID3v2Tag()->frameListMap()["TIT2"].front()->toString()); - } - - void testSaveUTF16Comment() { - String::Type defaultEncoding = ID3v2::FrameFactory::instance()->defaultTextEncoding(); - ScopedFileCopy copy("xing", ".mp3"); - string newname = copy.fileName(); - ID3v2::FrameFactory::instance()->setDefaultTextEncoding(String::UTF16); - { - MPEG::File foo(newname.c_str()); - foo.strip(); - foo.tag()->setComment("Test comment!"); - foo.save(); - } - { - MPEG::File bar(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(String("Test comment!"), bar.tag()->comment()); - ID3v2::FrameFactory::instance()->setDefaultTextEncoding(defaultEncoding); - } - } - - void testUpdateGenre23_1() { - // "Refinement" is the same as the ID3v1 genre - duplicate - ID3v2::FrameFactory *factory = ID3v2::FrameFactory::instance(); - ByteVector data = ByteVector("TCON" // Frame ID - "\x00\x00\x00\x10" // Frame size - "\x00\x00" // Frame flags - "\x00" // Encoding - "(22)Death Metal", - 26); // Text - ID3v2::Header header; - header.setMajorVersion(3); - ID3v2::TextIdentificationFrame *frame = - dynamic_cast(factory->createFrame(data, &header)); - CPPUNIT_ASSERT_EQUAL((size_t)1, frame->fieldList().size()); - CPPUNIT_ASSERT_EQUAL(String("Death Metal"), frame->fieldList()[0]); - - ID3v2::Tag tag; - tag.addFrame(frame); - CPPUNIT_ASSERT_EQUAL(String("Death Metal"), tag.genre()); - } - - void testUpdateGenre23_2() { - // "Refinement" is different from the ID3v1 genre - ID3v2::FrameFactory *factory = ID3v2::FrameFactory::instance(); - ByteVector data = ByteVector("TCON" // Frame ID - "\x00\x00\x00\x13" // Frame size - "\x00\x00" // Frame flags - "\x00" // Encoding - "(4)Eurodisco", - 23); // Text - ID3v2::Header header; - header.setMajorVersion(3); - ID3v2::TextIdentificationFrame *frame = - dynamic_cast(factory->createFrame(data, &header)); - CPPUNIT_ASSERT_EQUAL((size_t)2, frame->fieldList().size()); - CPPUNIT_ASSERT_EQUAL(String("4"), frame->fieldList()[0]); - CPPUNIT_ASSERT_EQUAL(String("Eurodisco"), frame->fieldList()[1]); - - ID3v2::Tag tag; - tag.addFrame(frame); - CPPUNIT_ASSERT_EQUAL(String("Disco Eurodisco"), tag.genre()); - } - - void testUpdateGenre24() { - ID3v2::FrameFactory *factory = ID3v2::FrameFactory::instance(); - ByteVector data = ByteVector("TCON" // Frame ID - "\x00\x00\x00\x0D" // Frame size - "\x00\x00" // Frame flags - "\0" // Encoding - "14\0Eurodisco", - 23); // Text - ID3v2::Header header; - ID3v2::TextIdentificationFrame *frame = - dynamic_cast(factory->createFrame(data, &header)); - CPPUNIT_ASSERT_EQUAL((size_t)2, frame->fieldList().size()); - CPPUNIT_ASSERT_EQUAL(String("14"), frame->fieldList()[0]); - CPPUNIT_ASSERT_EQUAL(String("Eurodisco"), frame->fieldList()[1]); - - ID3v2::Tag tag; - tag.addFrame(frame); - CPPUNIT_ASSERT_EQUAL(String("R&B Eurodisco"), tag.genre()); - } - - void testUpdateDate22() { - MPEG::File f(TEST_FILE_PATH_C("id3v22-tda.mp3"), false); - CPPUNIT_ASSERT(f.tag()); - CPPUNIT_ASSERT_EQUAL((unsigned int)2010, f.tag()->year()); - } - - void testUpdateFullDate22() { - MPEG::File f(TEST_FILE_PATH_C("id3v22-tda.mp3"), false); - CPPUNIT_ASSERT(f.tag()); - CPPUNIT_ASSERT_EQUAL(String("2010-04-03"), f.ID3v2Tag()->frameListMap()["TDRC"].front()->toString()); - } - - void testDowngradeTo23() { - ScopedFileCopy copy("xing", ".mp3"); - string newname = copy.fileName(); - - ID3v2::TextIdentificationFrame *tf; - { - MPEG::File foo(newname.c_str()); - tf = new ID3v2::TextIdentificationFrame("TDOR", String::Latin1); - tf->setText("2011-03-16"); - foo.ID3v2Tag()->addFrame(tf); - tf = new ID3v2::TextIdentificationFrame("TDRC", String::Latin1); - tf->setText("2012-04-17T12:01"); - foo.ID3v2Tag()->addFrame(tf); - tf = new ID3v2::TextIdentificationFrame("TMCL", String::Latin1); - tf->setText(StringList().append("Guitar").append("Artist 1").append("Drums").append("Artist 2")); - foo.ID3v2Tag()->addFrame(tf); - tf = new ID3v2::TextIdentificationFrame("TIPL", String::Latin1); - tf->setText(StringList().append("Producer").append("Artist 3").append("Mastering").append("Artist 4")); - foo.ID3v2Tag()->addFrame(tf); - foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TDRL", String::Latin1)); - foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TDTG", String::Latin1)); - foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TMOO", String::Latin1)); - foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TPRO", String::Latin1)); - foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TSOA", String::Latin1)); - foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TSOT", String::Latin1)); - foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TSST", String::Latin1)); - foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TSOP", String::Latin1)); - foo.save(MPEG::File::AllTags, File::StripOthers, ID3v2::v3); - } - { - MPEG::File bar(newname.c_str()); - tf = dynamic_cast(bar.ID3v2Tag()->frameList("TDOR").front()); - CPPUNIT_ASSERT(tf); - CPPUNIT_ASSERT_EQUAL((size_t)1, tf->fieldList().size()); - CPPUNIT_ASSERT_EQUAL(String("2011"), tf->fieldList().front()); - tf = dynamic_cast(bar.ID3v2Tag()->frameList("TDRC").front()); - CPPUNIT_ASSERT(tf); - CPPUNIT_ASSERT_EQUAL((size_t)1, tf->fieldList().size()); - CPPUNIT_ASSERT_EQUAL(String("2012-04-17T12:01"), tf->fieldList().front()); - tf = dynamic_cast(bar.ID3v2Tag()->frameList("TIPL").front()); - CPPUNIT_ASSERT(tf); - CPPUNIT_ASSERT_EQUAL((size_t)8, tf->fieldList().size()); - CPPUNIT_ASSERT_EQUAL(String("Guitar"), tf->fieldList()[0]); - CPPUNIT_ASSERT_EQUAL(String("Artist 1"), tf->fieldList()[1]); - CPPUNIT_ASSERT_EQUAL(String("Drums"), tf->fieldList()[2]); - CPPUNIT_ASSERT_EQUAL(String("Artist 2"), tf->fieldList()[3]); - CPPUNIT_ASSERT_EQUAL(String("Producer"), tf->fieldList()[4]); - CPPUNIT_ASSERT_EQUAL(String("Artist 3"), tf->fieldList()[5]); - CPPUNIT_ASSERT_EQUAL(String("Mastering"), tf->fieldList()[6]); - CPPUNIT_ASSERT_EQUAL(String("Artist 4"), tf->fieldList()[7]); - CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TDRL")); - CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TDTG")); - CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TMOO")); - CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TPRO")); -#ifdef NO_ITUNES_HACKS - CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TSOA")); - CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TSOT")); - CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TSOP")); -#endif - CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TSST")); - } - } - - void testCompressedFrameWithBrokenLength() { - MPEG::File f(TEST_FILE_PATH_C("compressed_id3_frame.mp3"), false); - CPPUNIT_ASSERT(f.ID3v2Tag()->frameListMap().contains("APIC")); - - if (zlib::isAvailable()) { - ID3v2::AttachedPictureFrame *frame = dynamic_cast(f.ID3v2Tag()->frameListMap()["APIC"].front()); - CPPUNIT_ASSERT(frame); - CPPUNIT_ASSERT_EQUAL(String("image/bmp"), frame->mimeType()); - CPPUNIT_ASSERT_EQUAL(ID3v2::AttachedPictureFrame::Other, frame->type()); - CPPUNIT_ASSERT_EQUAL(String(""), frame->description()); - CPPUNIT_ASSERT_EQUAL((size_t)86414, frame->picture().size()); - } - else { - // Skip the test if ZLIB is not installed. - // The message "Compressed frames are currently not supported." will be displayed. - - ID3v2::UnknownFrame *frame = dynamic_cast(f.ID3v2Tag()->frameListMap()["APIC"].front()); - CPPUNIT_ASSERT(frame); - } - } - - void testW000() { - MPEG::File f(TEST_FILE_PATH_C("w000.mp3"), false); - CPPUNIT_ASSERT(f.ID3v2Tag()->frameListMap().contains("W000")); - ID3v2::UrlLinkFrame *frame = - dynamic_cast(f.ID3v2Tag()->frameListMap()["W000"].front()); - CPPUNIT_ASSERT(frame); - CPPUNIT_ASSERT_EQUAL(String("lukas.lalinsky@example.com____"), frame->url()); - } - - void testPropertyInterface() { - ScopedFileCopy copy("rare_frames", ".mp3"); - string newname = copy.fileName(); - MPEG::File f(newname.c_str()); - PropertyMap dict = f.ID3v2Tag(false)->properties(); - CPPUNIT_ASSERT_EQUAL((size_t)6, dict.size()); - - CPPUNIT_ASSERT(dict.contains("USERTEXTDESCRIPTION1")); - CPPUNIT_ASSERT(dict.contains("QuodLibet::USERTEXTDESCRIPTION2")); - CPPUNIT_ASSERT_EQUAL((size_t)2, dict["USERTEXTDESCRIPTION1"].size()); - CPPUNIT_ASSERT_EQUAL((size_t)2, dict["QuodLibet::USERTEXTDESCRIPTION2"].size()); - CPPUNIT_ASSERT_EQUAL(String("userTextData1"), dict["USERTEXTDESCRIPTION1"][0]); - CPPUNIT_ASSERT_EQUAL(String("userTextData2"), dict["USERTEXTDESCRIPTION1"][1]); - CPPUNIT_ASSERT_EQUAL(String("userTextData1"), dict["QuodLibet::USERTEXTDESCRIPTION2"][0]); - CPPUNIT_ASSERT_EQUAL(String("userTextData2"), dict["QuodLibet::USERTEXTDESCRIPTION2"][1]); - - CPPUNIT_ASSERT_EQUAL(String("Pop"), dict["GENRE"].front()); - - CPPUNIT_ASSERT_EQUAL(String("http://a.user.url"), dict["URL:USERURL"].front()); - - CPPUNIT_ASSERT_EQUAL(String("http://a.user.url/with/empty/description"), dict["URL"].front()); - CPPUNIT_ASSERT_EQUAL(String("A COMMENT"), dict["COMMENT"].front()); - - CPPUNIT_ASSERT_EQUAL((size_t)1u, dict.unsupportedData().size()); - CPPUNIT_ASSERT_EQUAL(String("UFID/supermihi@web.de"), dict.unsupportedData().front()); - } - - void testPropertyInterface2() { - ID3v2::Tag tag; - ID3v2::UnsynchronizedLyricsFrame *frame1 = new ID3v2::UnsynchronizedLyricsFrame(); - frame1->setDescription("test"); - frame1->setText("la-la-la test"); - tag.addFrame(frame1); - - ID3v2::UnsynchronizedLyricsFrame *frame2 = new ID3v2::UnsynchronizedLyricsFrame(); - frame2->setDescription(""); - frame2->setText("la-la-la nodescription"); - tag.addFrame(frame2); - - ID3v2::AttachedPictureFrame *frame3 = new ID3v2::AttachedPictureFrame(); - frame3->setDescription("test picture"); - tag.addFrame(frame3); - - ID3v2::TextIdentificationFrame *frame4 = new ID3v2::TextIdentificationFrame("TIPL"); - frame4->setText("single value is invalid for TIPL"); - tag.addFrame(frame4); - - ID3v2::TextIdentificationFrame *frame5 = new ID3v2::TextIdentificationFrame("TMCL"); - StringList tmclData; - tmclData.append("VIOLIN"); - tmclData.append("a violinist"); - tmclData.append("PIANO"); - tmclData.append("a pianist"); - frame5->setText(tmclData); - tag.addFrame(frame5); - - ID3v2::UniqueFileIdentifierFrame *frame6 = new ID3v2::UniqueFileIdentifierFrame("http://musicbrainz.org", "152454b9-19ba-49f3-9fc9-8fc26545cf41"); - tag.addFrame(frame6); - - ID3v2::UniqueFileIdentifierFrame *frame7 = new ID3v2::UniqueFileIdentifierFrame("http://example.com", "123"); - tag.addFrame(frame7); - - ID3v2::UserTextIdentificationFrame *frame8 = new ID3v2::UserTextIdentificationFrame(); - frame8->setDescription("MusicBrainz Album Id"); - frame8->setText("95c454a5-d7e0-4d8f-9900-db04aca98ab3"); - tag.addFrame(frame8); - - PropertyMap properties = tag.properties(); - - CPPUNIT_ASSERT_EQUAL((size_t)3u, properties.unsupportedData().size()); - CPPUNIT_ASSERT(properties.unsupportedData().contains("TIPL")); - CPPUNIT_ASSERT(properties.unsupportedData().contains("APIC")); - CPPUNIT_ASSERT(properties.unsupportedData().contains("UFID/http://example.com")); - - CPPUNIT_ASSERT(properties.contains("PERFORMER:VIOLIN")); - CPPUNIT_ASSERT(properties.contains("PERFORMER:PIANO")); - CPPUNIT_ASSERT_EQUAL(String("a violinist"), properties["PERFORMER:VIOLIN"].front()); - CPPUNIT_ASSERT_EQUAL(String("a pianist"), properties["PERFORMER:PIANO"].front()); - - CPPUNIT_ASSERT(properties.contains("LYRICS")); - CPPUNIT_ASSERT(properties.contains("LYRICS:TEST")); - - CPPUNIT_ASSERT(properties.contains("MUSICBRAINZ_TRACKID")); - CPPUNIT_ASSERT_EQUAL(String("152454b9-19ba-49f3-9fc9-8fc26545cf41"), properties["MUSICBRAINZ_TRACKID"].front()); - - CPPUNIT_ASSERT(properties.contains("MUSICBRAINZ_ALBUMID")); - CPPUNIT_ASSERT_EQUAL(String("95c454a5-d7e0-4d8f-9900-db04aca98ab3"), properties["MUSICBRAINZ_ALBUMID"].front()); - - tag.removeUnsupportedProperties(properties.unsupportedData()); - CPPUNIT_ASSERT(tag.frameList("APIC").isEmpty()); - CPPUNIT_ASSERT(tag.frameList("TIPL").isEmpty()); - CPPUNIT_ASSERT_EQUAL((ID3v2::UniqueFileIdentifierFrame *)nullptr, ID3v2::UniqueFileIdentifierFrame::findByOwner(&tag, "http://example.com")); - CPPUNIT_ASSERT_EQUAL(frame6, ID3v2::UniqueFileIdentifierFrame::findByOwner(&tag, "http://musicbrainz.org")); - } - - void testPropertiesMovement() { - ID3v2::Tag tag; - ID3v2::TextIdentificationFrame *frameMvnm = new ID3v2::TextIdentificationFrame("MVNM"); - frameMvnm->setText("Movement Name"); - tag.addFrame(frameMvnm); - - ID3v2::TextIdentificationFrame *frameMvin = new ID3v2::TextIdentificationFrame("MVIN"); - frameMvin->setText("2/3"); - tag.addFrame(frameMvin); - - PropertyMap properties = tag.properties(); - CPPUNIT_ASSERT(properties.contains("MOVEMENTNAME")); - CPPUNIT_ASSERT(properties.contains("MOVEMENTNUMBER")); - CPPUNIT_ASSERT_EQUAL(String("Movement Name"), properties["MOVEMENTNAME"].front()); - CPPUNIT_ASSERT_EQUAL(String("2/3"), properties["MOVEMENTNUMBER"].front()); - - ByteVector frameDataMvnm("MVNM" - "\x00\x00\x00\x0e" - "\x00\x00" - "\x00" - "Movement Name", - 24); - CPPUNIT_ASSERT_EQUAL(frameDataMvnm, frameMvnm->render()); - ByteVector frameDataMvin("MVIN" - "\x00\x00\x00\x04" - "\x00\x00" - "\x00" - "2/3", - 14); - CPPUNIT_ASSERT_EQUAL(frameDataMvin, frameMvin->render()); - - ID3v2::Header header; - ID3v2::FrameFactory *factory = ID3v2::FrameFactory::instance(); - ID3v2::TextIdentificationFrame *parsedFrameMvnm = - dynamic_cast(factory->createFrame(frameDataMvnm, &header)); - ID3v2::TextIdentificationFrame *parsedFrameMvin = - dynamic_cast(factory->createFrame(frameDataMvin, &header)); - CPPUNIT_ASSERT(parsedFrameMvnm); - CPPUNIT_ASSERT(parsedFrameMvin); - CPPUNIT_ASSERT_EQUAL(String("Movement Name"), parsedFrameMvnm->toString()); - CPPUNIT_ASSERT_EQUAL(String("2/3"), parsedFrameMvin->toString()); - - tag.addFrame(parsedFrameMvnm); - tag.addFrame(parsedFrameMvin); - } - - void testPropertyGrouping() - { - ID3v2::Tag tag; - ID3v2::TextIdentificationFrame *frameGrp1 = new ID3v2::TextIdentificationFrame("GRP1"); - frameGrp1->setText("Grouping"); - tag.addFrame(frameGrp1); - - PropertyMap properties = tag.properties(); - CPPUNIT_ASSERT(properties.contains("GROUPING")); - CPPUNIT_ASSERT_EQUAL(String("Grouping"), properties["GROUPING"].front()); - - ByteVector frameDataGrp1("GRP1" - "\x00\x00\x00\x09" - "\x00\x00" - "\x00" - "Grouping", 19); - CPPUNIT_ASSERT_EQUAL(frameDataGrp1, frameGrp1->render()); - - ID3v2::FrameFactory *factory = ID3v2::FrameFactory::instance(); - ID3v2::Header header; - ID3v2::TextIdentificationFrame *parsedFrameGrp1 = - dynamic_cast( - factory->createFrame(frameDataGrp1, &header)); - CPPUNIT_ASSERT(parsedFrameGrp1); - CPPUNIT_ASSERT_EQUAL(String("Grouping"), parsedFrameGrp1->toString()); - - tag.addFrame(parsedFrameGrp1); - } - - void testDeleteFrame() { - ScopedFileCopy copy("rare_frames", ".mp3"); - string newname = copy.fileName(); - - { - MPEG::File f(newname.c_str()); - ID3v2::Tag *t = f.ID3v2Tag(); - ID3v2::Frame *frame = t->frameList("TCON")[0]; - CPPUNIT_ASSERT_EQUAL(static_cast(1), t->frameList("TCON").size()); - t->removeFrame(frame, true); - f.save(MPEG::File::ID3v2); - } - { - MPEG::File f2(newname.c_str()); - ID3v2::Tag *t = f2.ID3v2Tag(); - CPPUNIT_ASSERT(t->frameList("TCON").isEmpty()); - } - } - - void testSaveAndStripID3v1ShouldNotAddFrameFromID3v1ToId3v2() { - ScopedFileCopy copy("xing", ".mp3"); - string newname = copy.fileName(); - - { - MPEG::File foo(newname.c_str()); - foo.tag()->setArtist("Artist"); - foo.save(MPEG::File::ID3v1 | MPEG::File::ID3v2); - } - - { - MPEG::File bar(newname.c_str()); - bar.ID3v2Tag()->removeFrames("TPE1"); - // Should strip ID3v1 here and not add old values to ID3v2 again - bar.save(MPEG::File::ID3v2, File::StripOthers); - } - - MPEG::File f(newname.c_str()); - CPPUNIT_ASSERT(!f.ID3v2Tag()->frameListMap().contains("TPE1")); - } - - void testParseChapterFrame() { - ID3v2::Header header; - - ByteVector chapterData = - ByteVector("CHAP" // Frame ID - "\x00\x00\x00\x20" // Frame size - "\x00\x00" // Frame flags - "\x43\x00" // Element ID ("C") - "\x00\x00\x00\x03" // Start time - "\x00\x00\x00\x05" // End time - "\x00\x00\x00\x02" // Start offset - "\x00\x00\x00\x03", - 28); // End offset - ByteVector embeddedFrameData = - ByteVector("TIT2" // Embedded frame ID - "\x00\x00\x00\x04" // Embedded frame size - "\x00\x00" // Embedded frame flags - "\x00" // TIT2 frame text encoding - "CH1", - 14); // Chapter title - - ID3v2::ChapterFrame f1(&header, chapterData); - - CPPUNIT_ASSERT_EQUAL(ByteVector("C"), f1.elementID()); - CPPUNIT_ASSERT((unsigned int)0x03 == f1.startTime()); - CPPUNIT_ASSERT((unsigned int)0x05 == f1.endTime()); - CPPUNIT_ASSERT((unsigned int)0x02 == f1.startOffset()); - CPPUNIT_ASSERT((unsigned int)0x03 == f1.endOffset()); - CPPUNIT_ASSERT((unsigned int)0x00 == f1.embeddedFrameList().size()); - - ID3v2::ChapterFrame f2(&header, chapterData + embeddedFrameData); - - CPPUNIT_ASSERT_EQUAL(ByteVector("C"), f2.elementID()); - CPPUNIT_ASSERT((unsigned int)0x03 == f2.startTime()); - CPPUNIT_ASSERT((unsigned int)0x05 == f2.endTime()); - CPPUNIT_ASSERT((unsigned int)0x02 == f2.startOffset()); - CPPUNIT_ASSERT((unsigned int)0x03 == f2.endOffset()); - CPPUNIT_ASSERT((unsigned int)0x01 == f2.embeddedFrameList().size()); - CPPUNIT_ASSERT(f2.embeddedFrameList("TIT2").size() == 1); - CPPUNIT_ASSERT(f2.embeddedFrameList("TIT2")[0]->toString() == "CH1"); - } - - void testRenderChapterFrame() { - ID3v2::Header header; - ID3v2::ChapterFrame f1(&header, "CHAP"); - f1.setElementID(ByteVector("\x43\x00", 2)); - f1.setStartTime(3); - f1.setEndTime(5); - f1.setStartOffset(2); - f1.setEndOffset(3); - ID3v2::TextIdentificationFrame *eF = new ID3v2::TextIdentificationFrame("TIT2"); - eF->setText("CH1"); - f1.addEmbeddedFrame(eF); - - ByteVector expected = - ByteVector("CHAP" // Frame ID - "\x00\x00\x00\x20" // Frame size - "\x00\x00" // Frame flags - "\x43\x00" // Element ID - "\x00\x00\x00\x03" // Start time - "\x00\x00\x00\x05" // End time - "\x00\x00\x00\x02" // Start offset - "\x00\x00\x00\x03" // End offset - "TIT2" // Embedded frame ID - "\x00\x00\x00\x04" // Embedded frame size - "\x00\x00" // Embedded frame flags - "\x00" // TIT2 frame text encoding - "CH1", - 42); // Chapter title - - CPPUNIT_ASSERT_EQUAL(expected, f1.render()); - - f1.setElementID("C"); - - CPPUNIT_ASSERT_EQUAL(expected, f1.render()); - - ID3v2::FrameList frames; - eF = new ID3v2::TextIdentificationFrame("TIT2"); - eF->setText("CH1"); - frames.append(eF); - - ID3v2::ChapterFrame f2(ByteVector("\x43\x00", 2), 3, 5, 2, 3, frames); - CPPUNIT_ASSERT_EQUAL(expected, f2.render()); - - frames.clear(); - eF = new ID3v2::TextIdentificationFrame("TIT2"); - eF->setText("CH1"); - frames.append(eF); - - ID3v2::ChapterFrame f3(ByteVector("C\x00", 2), 3, 5, 2, 3, frames); - CPPUNIT_ASSERT_EQUAL(expected, f3.render()); - - frames.clear(); - eF = new ID3v2::TextIdentificationFrame("TIT2"); - eF->setText("CH1"); - frames.append(eF); - - ID3v2::ChapterFrame f4("C", 3, 5, 2, 3, frames); - CPPUNIT_ASSERT_EQUAL(expected, f4.render()); - - CPPUNIT_ASSERT(!f4.toString().isEmpty()); - - ID3v2::ChapterFrame f5("C", 3, 5, 2, 3); - eF = new ID3v2::TextIdentificationFrame("TIT2"); - eF->setText("CH1"); - f5.addEmbeddedFrame(eF); - CPPUNIT_ASSERT_EQUAL(expected, f5.render()); - } - - void testParseTableOfContentsFrame() { - ID3v2::Header header; - ID3v2::TableOfContentsFrame f( - &header, - ByteVector("CTOC" // Frame ID - "\x00\x00\x00\x16" // Frame size - "\x00\x00" // Frame flags - "\x54\x00" // Element ID ("T") - "\x01" // CTOC flags - "\x02" // Entry count - "\x43\x00" // First entry ("C") - "\x44\x00" // Second entry ("D") - "TIT2" // Embedded frame ID - "\x00\x00\x00\x04" // Embedded frame size - "\x00\x00" // Embedded frame flags - "\x00" // TIT2 frame text encoding - "TC1", - 32)); // Table of contents title - CPPUNIT_ASSERT_EQUAL(ByteVector("T"), f.elementID()); - CPPUNIT_ASSERT(!f.isTopLevel()); - CPPUNIT_ASSERT(f.isOrdered()); - CPPUNIT_ASSERT((unsigned int)0x02 == f.entryCount()); - CPPUNIT_ASSERT_EQUAL(ByteVector("C"), f.childElements()[0]); - CPPUNIT_ASSERT_EQUAL(ByteVector("D"), f.childElements()[1]); - CPPUNIT_ASSERT((unsigned int)0x01 == f.embeddedFrameList().size()); - CPPUNIT_ASSERT(f.embeddedFrameList("TIT2").size() == 1); - CPPUNIT_ASSERT(f.embeddedFrameList("TIT2")[0]->toString() == "TC1"); - } - - void testRenderTableOfContentsFrame() { - ID3v2::Header header; - ID3v2::TableOfContentsFrame f(&header, "CTOC"); - f.setElementID(ByteVector("\x54\x00", 2)); - f.setIsTopLevel(false); - f.setIsOrdered(true); - f.addChildElement(ByteVector("\x43\x00", 2)); - f.addChildElement(ByteVector("\x44\x00", 2)); - ID3v2::TextIdentificationFrame *eF = new ID3v2::TextIdentificationFrame("TIT2"); - eF->setText("TC1"); - f.addEmbeddedFrame(eF); - CPPUNIT_ASSERT_EQUAL( - ByteVector("CTOC" // Frame ID - "\x00\x00\x00\x16" // Frame size - "\x00\x00" // Frame flags - "\x54\x00" // Element ID - "\x01" // CTOC flags - "\x02" // Entry count - "\x43\x00" // First entry - "\x44\x00" // Second entry - "TIT2" // Embedded frame ID - "\x00\x00\x00\x04" // Embedded frame size - "\x00\x00" // Embedded frame flags - "\x00" // TIT2 frame text encoding - "TC1", - 32), // Table of contents title - f.render()); - } - - void testShrinkPadding() { - ScopedFileCopy copy("xing", ".mp3"); - string newname = copy.fileName(); - - { - MPEG::File f(newname.c_str()); - f.ID3v2Tag()->setTitle(longText(64 * 1024)); - f.save(MPEG::File::ID3v2, File::StripOthers); - } - { - MPEG::File f(newname.c_str()); - CPPUNIT_ASSERT(f.hasID3v2Tag()); - CPPUNIT_ASSERT_EQUAL(74789LL, f.length()); - f.ID3v2Tag()->setTitle("ABCDEFGHIJ"); - f.save(MPEG::File::ID3v2, File::StripOthers); - } - { - MPEG::File f(newname.c_str()); - CPPUNIT_ASSERT(f.hasID3v2Tag()); - CPPUNIT_ASSERT_EQUAL(9263LL, f.length()); - } - } - - void testEmptyFrame() { - ScopedFileCopy copy("xing", ".mp3"); - string newname = copy.fileName(); - - { - MPEG::File f(newname.c_str()); - ID3v2::Tag *tag = f.ID3v2Tag(true); - - ID3v2::UrlLinkFrame *frame1 = new ID3v2::UrlLinkFrame( - ByteVector("WOAF\x00\x00\x00\x01\x00\x00\x00", 11)); - tag->addFrame(frame1); - - ID3v2::TextIdentificationFrame *frame2 = new ID3v2::TextIdentificationFrame("TIT2"); - frame2->setText("Title"); - tag->addFrame(frame2); - - f.save(); - } - - { - MPEG::File f(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(true, f.hasID3v2Tag()); - - ID3v2::Tag *tag = f.ID3v2Tag(); - CPPUNIT_ASSERT_EQUAL(String("Title"), tag->title()); - CPPUNIT_ASSERT_EQUAL(true, tag->frameListMap()["WOAF"].isEmpty()); - } - } - - void testDuplicateTags() { - ScopedFileCopy copy("duplicate_id3v2", ".mp3"); - - ByteVector audioStream; - { - MPEG::File f(copy.fileName().c_str()); - f.seek(f.ID3v2Tag()->header()->completeTagSize()); - audioStream = f.readBlock(2089); - - // duplicate_id3v2.mp3 has duplicate ID3v2 tags. - // Sample rate will be 32000 if we can't skip the second tag. - - CPPUNIT_ASSERT(f.hasID3v2Tag()); - CPPUNIT_ASSERT_EQUAL((unsigned int)8049, f.ID3v2Tag()->header()->completeTagSize()); - - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - - f.ID3v2Tag()->setArtist("Artist A"); - f.save(MPEG::File::ID3v2, File::StripOthers); - } - { - MPEG::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(f.hasID3v2Tag()); - CPPUNIT_ASSERT_EQUAL(3594LL, f.length()); - CPPUNIT_ASSERT_EQUAL((unsigned int)1505, f.ID3v2Tag()->header()->completeTagSize()); - CPPUNIT_ASSERT_EQUAL(String("Artist A"), f.ID3v2Tag()->artist()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - - f.seek(f.ID3v2Tag()->header()->completeTagSize()); - CPPUNIT_ASSERT_EQUAL(f.readBlock(2089), audioStream); - } - } - - void testParseTOCFrameWithManyChildren() { - MPEG::File f(TEST_FILE_PATH_C("toc_many_children.mp3")); - CPPUNIT_ASSERT(f.isValid()); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestID3v2); diff --git a/tests/taglib/test_info.cpp b/tests/taglib/test_info.cpp deleted file mode 100644 index 46404803..00000000 --- a/tests/taglib/test_info.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/*************************************************************************** - copyright : (C) 2012 by Tsuda Kageyu - email : tsuda.kageyu@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include -#include -#include - -#include "infotag.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestInfoTag : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestInfoTag); - CPPUNIT_TEST(testTitle); - CPPUNIT_TEST(testNumericFields); - CPPUNIT_TEST_SUITE_END(); - - public: - void testTitle() { - RIFF::Info::Tag tag; - - CPPUNIT_ASSERT_EQUAL(String(""), tag.title()); - tag.setTitle("Test title 1"); - tag.setFieldText("TEST", "Dummy Text"); - - CPPUNIT_ASSERT_EQUAL(String("Test title 1"), tag.title()); - - RIFF::Info::FieldMap map = tag.fieldMap(); - CPPUNIT_ASSERT_EQUAL(String("Test title 1"), map["INAM"]); - CPPUNIT_ASSERT_EQUAL(String("Dummy Text"), map["TEST"]); - } - - void testNumericFields() { - RIFF::Info::Tag tag; - - CPPUNIT_ASSERT_EQUAL((unsigned int)0, tag.track()); - tag.setTrack(1234); - CPPUNIT_ASSERT_EQUAL((unsigned int)1234, tag.track()); - CPPUNIT_ASSERT_EQUAL(String("1234"), tag.fieldText("IPRT")); - - CPPUNIT_ASSERT_EQUAL((unsigned int)0, tag.year()); - tag.setYear(1234); - CPPUNIT_ASSERT_EQUAL((unsigned int)1234, tag.year()); - CPPUNIT_ASSERT_EQUAL(String("1234"), tag.fieldText("ICRD")); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestInfoTag); diff --git a/tests/taglib/test_it.cpp b/tests/taglib/test_it.cpp deleted file mode 100644 index 0268cd93..00000000 --- a/tests/taglib/test_it.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "itfile.h" -#include "tstringlist.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -static const String titleBefore("test song name"); -static const String titleAfter("changed title"); - -static const String commentBefore( - "This is a sample name.\n" - "In module file formats\n" - "sample names are abused\n" - "as multiline comments.\n" - " "); - -static const String newComment( - "This is a sample name!\n" - "In module file formats\n" - "sample names are abused\n" - "as multiline comments.\n" - "-----------------------------------\n" - "The previous line is truncated but starting with this line\n" - "the comment is not limeted in the line length but to 8000\n" - "additional characters (bytes).\n" - "\n" - "This is because it is saved in the 'message' proportion of\n" - "IT files."); - -static const String commentAfter( - "This is a sample name!\n" - "In module file formats\n" - "sample names are abused\n" - "as multiline comments.\n" - "-------------------------\n" - "The previous line is truncated but starting with this line\n" - "the comment is not limeted in the line length but to 8000\n" - "additional characters (bytes).\n" - "\n" - "This is because it is saved in the 'message' proportion of\n" - "IT files."); - -class TestIT : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestIT); - CPPUNIT_TEST(testReadTags); - CPPUNIT_TEST(testWriteTags); - CPPUNIT_TEST_SUITE_END(); - - public: - void testReadTags() { - testRead(TEST_FILE_PATH_C("test.it"), titleBefore, commentBefore); - } - - void testWriteTags() { - ScopedFileCopy copy("test", ".it"); - { - IT::File file(copy.fileName().c_str()); - CPPUNIT_ASSERT(file.tag() != nullptr); - file.tag()->setTitle(titleAfter); - file.tag()->setComment(newComment); - file.tag()->setTrackerName("won't be saved"); - CPPUNIT_ASSERT(file.save()); - } - testRead(copy.fileName().c_str(), titleAfter, commentAfter); - } - - private: - void testRead(FileName fileName, const String &title, const String &comment) { - IT::File file(fileName); - - CPPUNIT_ASSERT(file.isValid()); - - IT::AudioProperties *p = file.audioProperties(); - Mod::Tag *t = file.tag(); - - CPPUNIT_ASSERT(nullptr != p); - CPPUNIT_ASSERT(nullptr != t); - - CPPUNIT_ASSERT_EQUAL(0, p->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(0, p->bitrate()); - CPPUNIT_ASSERT_EQUAL(0, p->sampleRate()); - CPPUNIT_ASSERT_EQUAL(64, p->channels()); - CPPUNIT_ASSERT_EQUAL((unsigned short)0, p->lengthInPatterns()); - CPPUNIT_ASSERT_EQUAL(true, p->stereo()); - CPPUNIT_ASSERT_EQUAL((unsigned short)0, p->instrumentCount()); - CPPUNIT_ASSERT_EQUAL((unsigned short)5, p->sampleCount()); - CPPUNIT_ASSERT_EQUAL((unsigned short)1, p->patternCount()); - CPPUNIT_ASSERT_EQUAL((unsigned short)535, p->version()); - CPPUNIT_ASSERT_EQUAL((unsigned short)532, p->compatibleVersion()); - CPPUNIT_ASSERT_EQUAL((unsigned short)9, p->flags()); - CPPUNIT_ASSERT_EQUAL((unsigned char)128, p->globalVolume()); - CPPUNIT_ASSERT_EQUAL((unsigned char)48, p->mixVolume()); - CPPUNIT_ASSERT_EQUAL((unsigned char)125, p->tempo()); - CPPUNIT_ASSERT_EQUAL((unsigned char)6, p->bpmSpeed()); - CPPUNIT_ASSERT_EQUAL((unsigned char)128, p->panningSeparation()); - CPPUNIT_ASSERT_EQUAL((unsigned char)0, p->pitchWheelDepth()); - CPPUNIT_ASSERT_EQUAL(title, t->title()); - CPPUNIT_ASSERT_EQUAL(String(), t->artist()); - CPPUNIT_ASSERT_EQUAL(String(), t->album()); - CPPUNIT_ASSERT_EQUAL(comment, t->comment()); - CPPUNIT_ASSERT_EQUAL(String(), t->genre()); - CPPUNIT_ASSERT_EQUAL(0U, t->year()); - CPPUNIT_ASSERT_EQUAL(0U, t->track()); - CPPUNIT_ASSERT_EQUAL(String("Impulse Tracker"), t->trackerName()); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestIT); diff --git a/tests/taglib/test_list.cpp b/tests/taglib/test_list.cpp deleted file mode 100644 index bb7ad20d..00000000 --- a/tests/taglib/test_list.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/*************************************************************************** - copyright : (C) 2007 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 - -#include "tlist.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestList : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestList); - CPPUNIT_TEST(testAppend); - CPPUNIT_TEST(testDetach); - CPPUNIT_TEST_SUITE_END(); - - public: - void testAppend() { - List l1; - List l2; - List l3; - l1.append(2); - l2.append(3); - l2.append(4); - l1.append(l2); - l1.prepend(1); - l3.append(1); - l3.append(2); - l3.append(3); - l3.append(4); - CPPUNIT_ASSERT_EQUAL((size_t)4, l1.size()); - CPPUNIT_ASSERT(l1 == l3); - } - - void testDetach() { - List l1; - l1.append(1); - l1.append(2); - l1.append(3); - l1.append(4); - - List l2 = l1; - List::Iterator it = l2.find(3); - *it = 33; - CPPUNIT_ASSERT_EQUAL(3, l1[2]); - CPPUNIT_ASSERT_EQUAL(33, l2[2]); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestList); diff --git a/tests/taglib/test_map.cpp b/tests/taglib/test_map.cpp deleted file mode 100644 index 5a58be7c..00000000 --- a/tests/taglib/test_map.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/*************************************************************************** - copyright : (C) 2007 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 - -#include "tstring.h" -#include "tmap.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestMap : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestMap); - CPPUNIT_TEST(testInsert); - CPPUNIT_TEST(testDetach); - CPPUNIT_TEST_SUITE_END(); - - public: - void testInsert() { - Map m1; - m1.insert("foo", 3); - m1.insert("bar", 5); - CPPUNIT_ASSERT_EQUAL((size_t)2, m1.size()); - CPPUNIT_ASSERT_EQUAL(3, m1["foo"]); - CPPUNIT_ASSERT_EQUAL(5, m1["bar"]); - m1.insert("foo", 7); - CPPUNIT_ASSERT_EQUAL((size_t)2, m1.size()); - CPPUNIT_ASSERT_EQUAL(7, m1["foo"]); - CPPUNIT_ASSERT_EQUAL(5, m1["bar"]); - } - - void testDetach() { - Map m1; - m1.insert("alice", 5); - m1.insert("bob", 9); - m1.insert("carol", 11); - - Map m2 = m1; - Map::Iterator it = m2.find("bob"); - (*it).second = 99; - CPPUNIT_ASSERT_EQUAL(9, m1["bob"]); - CPPUNIT_ASSERT_EQUAL(99, m2["bob"]); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestMap); diff --git a/tests/taglib/test_mod.cpp b/tests/taglib/test_mod.cpp deleted file mode 100644 index 22bf0859..00000000 --- a/tests/taglib/test_mod.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "modfile.h" -#include "tpropertymap.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -static const String titleBefore("title of song"); -static const String titleAfter("changed title"); - -static const String commentBefore( - "Instrument names\n" - "are abused as\n" - "comments in\n" - "module file formats.\n" - "-+-+-+-+-+-+-+-+-+-+-+\n" - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); - -static const String newComment( - "This line will be truncated because it is too long for a mod instrument name.\n" - "This line is ok."); - -static const String commentAfter( - "This line will be trun\n" - "This line is ok.\n" - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); - -class TestMod : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestMod); - CPPUNIT_TEST(testReadTags); - CPPUNIT_TEST(testWriteTags); - CPPUNIT_TEST(testPropertyInterface); - CPPUNIT_TEST_SUITE_END(); - - public: - void testReadTags() { - testRead(TEST_FILE_PATH_C("test.mod"), titleBefore, commentBefore); - } - - void testWriteTags() { - ScopedFileCopy copy("test", ".mod"); - { - Mod::File file(copy.fileName().c_str()); - CPPUNIT_ASSERT(file.tag() != nullptr); - file.tag()->setTitle(titleAfter); - file.tag()->setComment(newComment); - CPPUNIT_ASSERT(file.save()); - } - testRead(copy.fileName().c_str(), titleAfter, commentAfter); - CPPUNIT_ASSERT(fileEqual( - copy.fileName(), - TEST_FILE_PATH_C("changed.mod"))); - } - - void testPropertyInterface() { - Mod::Tag t; - PropertyMap properties; - properties["BLA"] = String("bla"); - properties["ARTIST"] = String("artist1"); - properties["ARTIST"].append("artist2"); - properties["TITLE"] = String("title"); - - PropertyMap unsupported = t.setProperties(properties); - CPPUNIT_ASSERT(unsupported.contains("BLA")); - CPPUNIT_ASSERT(unsupported.contains("ARTIST")); - CPPUNIT_ASSERT_EQUAL(properties["ARTIST"], unsupported["ARTIST"]); - CPPUNIT_ASSERT(!unsupported.contains("TITLE")); - } - - private: - void testRead(FileName fileName, const String &title, const String &comment) { - Mod::File file(fileName); - - CPPUNIT_ASSERT(file.isValid()); - - Mod::AudioProperties *p = file.audioProperties(); - Mod::Tag *t = file.tag(); - - CPPUNIT_ASSERT(nullptr != p); - CPPUNIT_ASSERT(nullptr != t); - - CPPUNIT_ASSERT_EQUAL(0, p->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(0, p->bitrate()); - CPPUNIT_ASSERT_EQUAL(0, p->sampleRate()); - CPPUNIT_ASSERT_EQUAL(8, p->channels()); - CPPUNIT_ASSERT_EQUAL(31U, p->instrumentCount()); - CPPUNIT_ASSERT_EQUAL((unsigned char)1, p->lengthInPatterns()); - CPPUNIT_ASSERT_EQUAL(title, t->title()); - CPPUNIT_ASSERT_EQUAL(String(), t->artist()); - CPPUNIT_ASSERT_EQUAL(String(), t->album()); - CPPUNIT_ASSERT_EQUAL(comment, t->comment()); - CPPUNIT_ASSERT_EQUAL(String(), t->genre()); - CPPUNIT_ASSERT_EQUAL(0U, t->year()); - CPPUNIT_ASSERT_EQUAL(0U, t->track()); - CPPUNIT_ASSERT_EQUAL(String("StarTrekker"), t->trackerName()); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestMod); diff --git a/tests/taglib/test_mp4.cpp b/tests/taglib/test_mp4.cpp deleted file mode 100644 index ac4bb1b3..00000000 --- a/tests/taglib/test_mp4.cpp +++ /dev/null @@ -1,439 +0,0 @@ -/*************************************************************************** - copyright : (C) 2008 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "tag.h" -#include "mp4tag.h" -#include "tbytevectorlist.h" -#include "tpropertymap.h" -#include "mp4atom.h" -#include "mp4file.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestMP4 : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestMP4); - CPPUNIT_TEST(testPropertiesAAC); - CPPUNIT_TEST(testPropertiesALAC); - CPPUNIT_TEST(testPropertiesM4V); - CPPUNIT_TEST(testFreeForm); - CPPUNIT_TEST(testCheckValid); - CPPUNIT_TEST(testHasTag); - CPPUNIT_TEST(testIsEmpty); - CPPUNIT_TEST(testUpdateStco); - CPPUNIT_TEST(testSaveExisingWhenIlstIsLast); - CPPUNIT_TEST(test64BitAtom); - CPPUNIT_TEST(testGnre); - CPPUNIT_TEST(testCovrRead); - CPPUNIT_TEST(testCovrWrite); - CPPUNIT_TEST(testCovrRead2); - CPPUNIT_TEST(testProperties); - CPPUNIT_TEST(testPropertiesMovement); - CPPUNIT_TEST(testFuzzedFile); - CPPUNIT_TEST(testRepeatedSave); - CPPUNIT_TEST(testWithZeroLengthAtom); - CPPUNIT_TEST_SUITE_END(); - - public: - void testPropertiesAAC() { - MP4::File f(TEST_FILE_PATH_C("has-tags.m4a")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(3708, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted()); - CPPUNIT_ASSERT_EQUAL(MP4::AudioProperties::AAC, f.audioProperties()->codec()); - } - - void testPropertiesALAC() { - MP4::File f(TEST_FILE_PATH_C("empty_alac.m4a")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(3705, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted()); - CPPUNIT_ASSERT_EQUAL(MP4::AudioProperties::ALAC, f.audioProperties()->codec()); - } - - void testPropertiesM4V() { - MP4::File f(TEST_FILE_PATH_C("blank_video.m4v")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(975, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(96, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted()); - CPPUNIT_ASSERT_EQUAL(MP4::AudioProperties::AAC, f.audioProperties()->codec()); - } - - void testCheckValid() { - MP4::File f(TEST_FILE_PATH_C("empty.aiff")); - CPPUNIT_ASSERT(!f.isValid()); - } - - void testHasTag() { - { - MP4::File f(TEST_FILE_PATH_C("has-tags.m4a")); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT(f.hasMP4Tag()); - } - - ScopedFileCopy copy("no-tags", ".m4a"); - - { - MP4::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT(!f.hasMP4Tag()); - f.tag()->setTitle("TITLE"); - f.save(); - } - { - MP4::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT(f.hasMP4Tag()); - } - } - - void testIsEmpty() { - MP4::Tag t1; - CPPUNIT_ASSERT(t1.isEmpty()); - t1.setArtist("Foo"); - CPPUNIT_ASSERT(!t1.isEmpty()); - - MP4::Tag t2; - t2.setItem("foo", "bar"); - CPPUNIT_ASSERT(!t2.isEmpty()); - } - - void testUpdateStco() { - ScopedFileCopy copy("no-tags", ".3g2"); - string filename = copy.fileName(); - - ByteVectorList data1; - - { - MP4::File f(filename.c_str()); - f.tag()->setArtist(ByteVector(3000, 'x')); - - MP4::Atoms a(&f); - MP4::Atom *stco = a.find("moov")->findall("stco", true)[0]; - f.seek(stco->offset + 12); - ByteVector data = f.readBlock(static_cast(stco->length - 12)); - unsigned int count = data.mid(0, 4).toUInt32BE(0); - int pos = 4; - while (count--) { - unsigned int offset = data.mid(pos, 4).toUInt32BE(0); - f.seek(offset); - data1.append(f.readBlock(20)); - pos += 4; - } - - f.save(); - } - - { - MP4::File f(filename.c_str()); - - MP4::Atoms a(&f); - MP4::Atom *stco = a.find("moov")->findall("stco", true)[0]; - f.seek(stco->offset + 12); - ByteVector data = f.readBlock(static_cast(stco->length - 12)); - unsigned int count = data.mid(0, 4).toUInt32BE(0); - int pos = 4, i = 0; - while (count--) { - unsigned int offset = data.mid(pos, 4).toUInt32BE(0); - f.seek(offset); - CPPUNIT_ASSERT_EQUAL(data1[i], f.readBlock(20)); - pos += 4; - i++; - } - } - } - - void testFreeForm() { - ScopedFileCopy copy("has-tags", ".m4a"); - string filename = copy.fileName(); - - { - MP4::File f(filename.c_str()); - CPPUNIT_ASSERT(f.tag()->contains("----:com.apple.iTunes:iTunNORM")); - f.tag()->setItem("----:org.kde.TagLib:Foo", StringList("Bar")); - f.save(); - } - { - MP4::File f(filename.c_str()); - CPPUNIT_ASSERT(f.tag()->contains("----:org.kde.TagLib:Foo")); - CPPUNIT_ASSERT_EQUAL(String("Bar"), - f.tag()->item("----:org.kde.TagLib:Foo").toStringList().front()); - f.save(); - } - } - - void testSaveExisingWhenIlstIsLast() { - ScopedFileCopy copy("ilst-is-last", ".m4a"); - string filename = copy.fileName(); - - { - MP4::File f(filename.c_str()); - CPPUNIT_ASSERT_EQUAL(String("82,164"), - f.tag()->item("----:com.apple.iTunes:replaygain_track_minmax").toStringList().front()); - CPPUNIT_ASSERT_EQUAL(String("Pearl Jam"), f.tag()->artist()); - f.tag()->setComment("foo"); - f.save(); - } - { - MP4::File f(filename.c_str()); - CPPUNIT_ASSERT_EQUAL(String("82,164"), - f.tag()->item("----:com.apple.iTunes:replaygain_track_minmax").toStringList().front()); - CPPUNIT_ASSERT_EQUAL(String("Pearl Jam"), f.tag()->artist()); - CPPUNIT_ASSERT_EQUAL(String("foo"), f.tag()->comment()); - } - } - - void test64BitAtom() { - ScopedFileCopy copy("64bit", ".mp4"); - string filename = copy.fileName(); - - { - MP4::File f(filename.c_str()); - CPPUNIT_ASSERT_EQUAL(true, f.tag()->itemMap()["cpil"].toBool()); - - MP4::Atoms atoms(&f); - MP4::Atom *moov = atoms.atoms[0]; - CPPUNIT_ASSERT_EQUAL(77LL, moov->length); - - f.tag()->setItem("pgap", true); - f.save(); - } - { - MP4::File f(filename.c_str()); - CPPUNIT_ASSERT_EQUAL(true, f.tag()->item("cpil").toBool()); - CPPUNIT_ASSERT_EQUAL(true, f.tag()->item("pgap").toBool()); - - MP4::Atoms atoms(&f); - MP4::Atom *moov = atoms.atoms[0]; - // original size + 'pgap' size + padding - CPPUNIT_ASSERT_EQUAL(77 + 25 + 974LL, moov->length); - } - } - - void testGnre() { - MP4::File f(TEST_FILE_PATH_C("gnre.m4a")); - CPPUNIT_ASSERT_EQUAL(Strawberry_TagLib::TagLib::String("Ska"), f.tag()->genre()); - } - - void testCovrRead() { - MP4::File f(TEST_FILE_PATH_C("has-tags.m4a")); - CPPUNIT_ASSERT(f.tag()->contains("covr")); - MP4::CoverArtList l = f.tag()->item("covr").toCoverArtList(); - CPPUNIT_ASSERT_EQUAL((size_t)2, l.size()); - CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format()); - CPPUNIT_ASSERT_EQUAL((size_t)79, l[0].data().size()); - CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::JPEG, l[1].format()); - CPPUNIT_ASSERT_EQUAL((size_t)287, l[1].data().size()); - } - - void testCovrWrite() { - ScopedFileCopy copy("has-tags", ".m4a"); - string filename = copy.fileName(); - - { - MP4::File f(filename.c_str()); - CPPUNIT_ASSERT(f.tag()->contains("covr")); - MP4::CoverArtList l = f.tag()->item("covr").toCoverArtList(); - l.append(MP4::CoverArt(MP4::CoverArt::PNG, "foo")); - f.tag()->setItem("covr", l); - f.save(); - } - { - MP4::File f(filename.c_str()); - CPPUNIT_ASSERT(f.tag()->contains("covr")); - MP4::CoverArtList l = f.tag()->item("covr").toCoverArtList(); - CPPUNIT_ASSERT_EQUAL((size_t)3, l.size()); - CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format()); - CPPUNIT_ASSERT_EQUAL((size_t)79, l[0].data().size()); - CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::JPEG, l[1].format()); - CPPUNIT_ASSERT_EQUAL((size_t)287, l[1].data().size()); - CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[2].format()); - CPPUNIT_ASSERT_EQUAL((size_t)3, l[2].data().size()); - } - } - - void testCovrRead2() { - MP4::File f(TEST_FILE_PATH_C("covr-junk.m4a")); - CPPUNIT_ASSERT(f.tag()->contains("covr")); - MP4::CoverArtList l = f.tag()->item("covr").toCoverArtList(); - CPPUNIT_ASSERT_EQUAL((size_t)2, l.size()); - CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format()); - CPPUNIT_ASSERT_EQUAL((size_t)79, l[0].data().size()); - CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::JPEG, l[1].format()); - CPPUNIT_ASSERT_EQUAL((size_t)287, l[1].data().size()); - } - - void testProperties() { - MP4::File f(TEST_FILE_PATH_C("has-tags.m4a")); - - PropertyMap tags = f.properties(); - - CPPUNIT_ASSERT_EQUAL(StringList("Test Artist"), tags["ARTIST"]); - - tags["TRACKNUMBER"] = StringList("2/4"); - tags["DISCNUMBER"] = StringList("3/5"); - tags["BPM"] = StringList("123"); - tags["ARTIST"] = StringList("Foo Bar"); - tags["COMPILATION"] = StringList("1"); - f.setProperties(tags); - - tags = f.properties(); - - CPPUNIT_ASSERT(f.tag()->contains("trkn")); - CPPUNIT_ASSERT_EQUAL(2, f.tag()->item("trkn").toIntPair().first); - CPPUNIT_ASSERT_EQUAL(4, f.tag()->item("trkn").toIntPair().second); - CPPUNIT_ASSERT_EQUAL(StringList("2/4"), tags["TRACKNUMBER"]); - - CPPUNIT_ASSERT(f.tag()->contains("disk")); - CPPUNIT_ASSERT_EQUAL(3, f.tag()->item("disk").toIntPair().first); - CPPUNIT_ASSERT_EQUAL(5, f.tag()->item("disk").toIntPair().second); - CPPUNIT_ASSERT_EQUAL(StringList("3/5"), tags["DISCNUMBER"]); - - CPPUNIT_ASSERT(f.tag()->contains("tmpo")); - CPPUNIT_ASSERT_EQUAL(123, f.tag()->item("tmpo").toInt()); - CPPUNIT_ASSERT_EQUAL(StringList("123"), tags["BPM"]); - - CPPUNIT_ASSERT(f.tag()->contains("\251ART")); - CPPUNIT_ASSERT_EQUAL(StringList("Foo Bar"), f.tag()->item("\251ART").toStringList()); - CPPUNIT_ASSERT_EQUAL(StringList("Foo Bar"), tags["ARTIST"]); - - CPPUNIT_ASSERT(f.tag()->contains("cpil")); - CPPUNIT_ASSERT_EQUAL(true, f.tag()->item("cpil").toBool()); - CPPUNIT_ASSERT_EQUAL(StringList("1"), tags["COMPILATION"]); - - tags["COMPILATION"] = StringList("0"); - f.setProperties(tags); - - tags = f.properties(); - - CPPUNIT_ASSERT(f.tag()->contains("cpil")); - CPPUNIT_ASSERT_EQUAL(false, f.tag()->item("cpil").toBool()); - CPPUNIT_ASSERT_EQUAL(StringList("0"), tags["COMPILATION"]); - - // Empty properties do not result in access violations - // when converting integers - tags["TRACKNUMBER"] = StringList(); - tags["DISCNUMBER"] = StringList(); - tags["BPM"] = StringList(); - tags["COMPILATION"] = StringList(); - f.setProperties(tags); - } - - void testPropertiesMovement() { - MP4::File f(TEST_FILE_PATH_C("has-tags.m4a")); - - PropertyMap tags = f.properties(); - - tags["WORK"] = StringList("Foo"); - tags["MOVEMENTNAME"] = StringList("Bar"); - tags["MOVEMENTNUMBER"] = StringList("2"); - tags["MOVEMENTCOUNT"] = StringList("3"); - tags["SHOWWORKMOVEMENT"] = StringList("1"); - f.setProperties(tags); - - tags = f.properties(); - - CPPUNIT_ASSERT(f.tag()->contains("\251wrk")); - CPPUNIT_ASSERT_EQUAL(StringList("Foo"), f.tag()->item("\251wrk").toStringList()); - CPPUNIT_ASSERT_EQUAL(StringList("Foo"), tags["WORK"]); - - CPPUNIT_ASSERT(f.tag()->contains("\251mvn")); - CPPUNIT_ASSERT_EQUAL(StringList("Bar"), f.tag()->item("\251mvn").toStringList()); - CPPUNIT_ASSERT_EQUAL(StringList("Bar"), tags["MOVEMENTNAME"]); - - CPPUNIT_ASSERT(f.tag()->contains("\251mvi")); - CPPUNIT_ASSERT_EQUAL(2, f.tag()->item("\251mvi").toInt()); - CPPUNIT_ASSERT_EQUAL(StringList("2"), tags["MOVEMENTNUMBER"]); - - CPPUNIT_ASSERT(f.tag()->contains("\251mvc")); - CPPUNIT_ASSERT_EQUAL(3, f.tag()->item("\251mvc").toInt()); - CPPUNIT_ASSERT_EQUAL(StringList("3"), tags["MOVEMENTCOUNT"]); - - CPPUNIT_ASSERT(f.tag()->contains("shwm")); - CPPUNIT_ASSERT_EQUAL(true, f.tag()->item("shwm").toBool()); - CPPUNIT_ASSERT_EQUAL(StringList("1"), tags["SHOWWORKMOVEMENT"]); - - tags["SHOWWORKMOVEMENT"] = StringList("0"); - f.setProperties(tags); - - tags = f.properties(); - - CPPUNIT_ASSERT(f.tag()->contains("shwm")); - CPPUNIT_ASSERT_EQUAL(false, f.tag()->item("shwm").toBool()); - CPPUNIT_ASSERT_EQUAL(StringList("0"), tags["SHOWWORKMOVEMENT"]); - - tags["WORK"] = StringList(); - tags["MOVEMENTNAME"] = StringList(); - tags["MOVEMENTNUMBER"] = StringList(); - tags["MOVEMENTCOUNT"] = StringList(); - tags["SHOWWORKMOVEMENT"] = StringList(); - f.setProperties(tags); - } - - void testFuzzedFile() { - MP4::File f(TEST_FILE_PATH_C("infloop.m4a")); - CPPUNIT_ASSERT(f.isValid()); - } - - void testRepeatedSave() { - ScopedFileCopy copy("no-tags", ".m4a"); - - MP4::File f(copy.fileName().c_str()); - f.tag()->setTitle("0123456789"); - f.save(); - f.save(); - CPPUNIT_ASSERT_EQUAL(2862LL, f.find("0123456789")); - CPPUNIT_ASSERT_EQUAL(-1LL, f.find("0123456789", 2863)); - } - - void testWithZeroLengthAtom() { - MP4::File f(TEST_FILE_PATH_C("zero-length-mdat.m4a")); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT_EQUAL(1115, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(22050, f.audioProperties()->sampleRate()); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestMP4); diff --git a/tests/taglib/test_mp4coverart.cpp b/tests/taglib/test_mp4coverart.cpp deleted file mode 100644 index a9d2f3b0..00000000 --- a/tests/taglib/test_mp4coverart.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/*************************************************************************** - copyright : (C) 2009 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "tag.h" -#include "mp4coverart.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestMP4CoverArt : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestMP4CoverArt); - CPPUNIT_TEST(testSimple); - CPPUNIT_TEST(testList); - CPPUNIT_TEST_SUITE_END(); - - public: - void testSimple() { - MP4::CoverArt c(MP4::CoverArt::PNG, "foo"); - CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, c.format()); - CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), c.data()); - - MP4::CoverArt c2(c); - CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, c2.format()); - CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), c2.data()); - - MP4::CoverArt c3 = c; - CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, c3.format()); - CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), c3.data()); - } - - void testList() { - MP4::CoverArtList l; - l.append(MP4::CoverArt(MP4::CoverArt::PNG, "foo")); - l.append(MP4::CoverArt(MP4::CoverArt::JPEG, "bar")); - - CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format()); - CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), l[0].data()); - CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::JPEG, l[1].format()); - CPPUNIT_ASSERT_EQUAL(ByteVector("bar"), l[1].data()); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestMP4CoverArt); diff --git a/tests/taglib/test_mp4item.cpp b/tests/taglib/test_mp4item.cpp deleted file mode 100644 index 5569ee21..00000000 --- a/tests/taglib/test_mp4item.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/*************************************************************************** - copyright : (C) 2009 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "tag.h" -#include "mp4coverart.h" -#include "mp4item.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestMP4Item : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestMP4Item); - CPPUNIT_TEST(testCoverArtList); - CPPUNIT_TEST_SUITE_END(); - - public: - void testCoverArtList() { - MP4::CoverArtList l; - l.append(MP4::CoverArt(MP4::CoverArt::PNG, "foo")); - l.append(MP4::CoverArt(MP4::CoverArt::JPEG, "bar")); - - MP4::Item i(l); - MP4::CoverArtList l2 = i.toCoverArtList(); - - CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format()); - CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), l[0].data()); - CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::JPEG, l[1].format()); - CPPUNIT_ASSERT_EQUAL(ByteVector("bar"), l[1].data()); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestMP4Item); diff --git a/tests/taglib/test_mpc.cpp b/tests/taglib/test_mpc.cpp deleted file mode 100644 index 9bcb7397..00000000 --- a/tests/taglib/test_mpc.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/*************************************************************************** - copyright : (C) 2012 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "apetag.h" -#include "id3v1tag.h" -#include "tstringlist.h" -#include "tbytevectorlist.h" -#include "tpropertymap.h" -#include "mpcfile.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestMPC : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestMPC); - CPPUNIT_TEST(testPropertiesSV8); - CPPUNIT_TEST(testPropertiesSV7); - CPPUNIT_TEST(testPropertiesSV5); - CPPUNIT_TEST(testPropertiesSV4); - CPPUNIT_TEST(testFuzzedFile1); - CPPUNIT_TEST(testFuzzedFile2); - CPPUNIT_TEST(testFuzzedFile3); - CPPUNIT_TEST(testFuzzedFile4); - CPPUNIT_TEST(testStripAndProperties); - CPPUNIT_TEST(testRepeatedSave); - CPPUNIT_TEST_SUITE_END(); - - public: - void testPropertiesSV8() { - MPC::File f(TEST_FILE_PATH_C("sv8_header.mpc")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(8, f.audioProperties()->mpcVersion()); - CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(1497, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(66014U, f.audioProperties()->sampleFrames()); - } - - void testPropertiesSV7() { - MPC::File f(TEST_FILE_PATH_C("click.mpc")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(7, f.audioProperties()->mpcVersion()); - CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(40, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(318, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(1760U, f.audioProperties()->sampleFrames()); - CPPUNIT_ASSERT_EQUAL(14221, f.audioProperties()->trackGain()); - CPPUNIT_ASSERT_EQUAL(19848, f.audioProperties()->trackPeak()); - CPPUNIT_ASSERT_EQUAL(14221, f.audioProperties()->albumGain()); - CPPUNIT_ASSERT_EQUAL(19848, f.audioProperties()->albumPeak()); - } - - void testPropertiesSV5() { - MPC::File f(TEST_FILE_PATH_C("sv5_header.mpc")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(5, f.audioProperties()->mpcVersion()); - CPPUNIT_ASSERT_EQUAL(26, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(26371, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(1162944U, f.audioProperties()->sampleFrames()); - } - - void testPropertiesSV4() { - MPC::File f(TEST_FILE_PATH_C("sv4_header.mpc")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(4, f.audioProperties()->mpcVersion()); - CPPUNIT_ASSERT_EQUAL(26, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(26371, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(1162944U, f.audioProperties()->sampleFrames()); - } - - void testFuzzedFile1() { - MPC::File f(TEST_FILE_PATH_C("zerodiv.mpc")); - CPPUNIT_ASSERT(f.isValid()); - } - - void testFuzzedFile2() { - MPC::File f(TEST_FILE_PATH_C("infloop.mpc")); - CPPUNIT_ASSERT(f.isValid()); - } - - void testFuzzedFile3() { - MPC::File f(TEST_FILE_PATH_C("segfault.mpc")); - CPPUNIT_ASSERT(f.isValid()); - } - - void testFuzzedFile4() { - MPC::File f(TEST_FILE_PATH_C("segfault2.mpc")); - CPPUNIT_ASSERT(f.isValid()); - } - - void testStripAndProperties() { - ScopedFileCopy copy("click", ".mpc"); - - { - MPC::File f(copy.fileName().c_str()); - f.APETag(true)->setTitle("APE"); - f.ID3v1Tag(true)->setTitle("ID3v1"); - f.save(); - } - { - MPC::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT_EQUAL(String("APE"), f.properties()["TITLE"].front()); - f.strip(MPC::File::APE); - CPPUNIT_ASSERT_EQUAL(String("ID3v1"), f.properties()["TITLE"].front()); - f.strip(MPC::File::ID3v1); - CPPUNIT_ASSERT(f.properties().isEmpty()); - } - } - - void testRepeatedSave() { - ScopedFileCopy copy("click", ".mpc"); - - { - MPC::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(!f.hasAPETag()); - CPPUNIT_ASSERT(!f.hasID3v1Tag()); - - f.APETag(true)->setTitle("01234 56789 ABCDE FGHIJ"); - f.save(); - - f.APETag()->setTitle("0"); - f.save(); - - f.ID3v1Tag(true)->setTitle("01234 56789 ABCDE FGHIJ"); - f.APETag()->setTitle("01234 56789 ABCDE FGHIJ 01234 56789 ABCDE FGHIJ 01234 56789"); - f.save(); - } - { - MPC::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(f.hasAPETag()); - CPPUNIT_ASSERT(f.hasID3v1Tag()); - } - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestMPC); diff --git a/tests/taglib/test_mpeg.cpp b/tests/taglib/test_mpeg.cpp deleted file mode 100644 index 86c8b183..00000000 --- a/tests/taglib/test_mpeg.cpp +++ /dev/null @@ -1,392 +0,0 @@ -/*************************************************************************** - copyright : (C) 2007 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "tstring.h" -#include "tpropertymap.h" -#include "tfile.h" -#include "mpegfile.h" -#include "id3v2tag.h" -#include "id3v1tag.h" -#include "apetag.h" -#include "mpegproperties.h" -#include "xingheader.h" -#include "mpegheader.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestMPEG : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestMPEG); - CPPUNIT_TEST(testAudioPropertiesXingHeaderCBR); - CPPUNIT_TEST(testAudioPropertiesXingHeaderVBR); - CPPUNIT_TEST(testAudioPropertiesVBRIHeader); - CPPUNIT_TEST(testAudioPropertiesNoVBRHeaders); - CPPUNIT_TEST(testSkipInvalidFrames1); - CPPUNIT_TEST(testSkipInvalidFrames2); - CPPUNIT_TEST(testSkipInvalidFrames3); - CPPUNIT_TEST(testVersion2DurationWithXingHeader); - CPPUNIT_TEST(testSaveID3v24); - CPPUNIT_TEST(testSaveID3v23); - CPPUNIT_TEST(testDuplicateID3v2); - CPPUNIT_TEST(testFuzzedFile); - CPPUNIT_TEST(testFrameOffset); - CPPUNIT_TEST(testStripAndProperties); - CPPUNIT_TEST(testRepeatedSave1); - CPPUNIT_TEST(testRepeatedSave2); - CPPUNIT_TEST(testRepeatedSave3); - CPPUNIT_TEST(testEmptyID3v2); - CPPUNIT_TEST(testEmptyID3v1); - CPPUNIT_TEST(testEmptyAPE); - CPPUNIT_TEST(testIgnoreGarbage); - CPPUNIT_TEST_SUITE_END(); - - public: - void testAudioPropertiesXingHeaderCBR() { - MPEG::File f(TEST_FILE_PATH_C("lame_cbr.mp3")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(1887, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(1887164, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(64, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(MPEG::XingHeader::Xing, f.audioProperties()->xingHeader()->type()); - } - - void testAudioPropertiesXingHeaderVBR() { - MPEG::File f(TEST_FILE_PATH_C("lame_vbr.mp3")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(1887, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(1887164, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(70, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(MPEG::XingHeader::Xing, f.audioProperties()->xingHeader()->type()); - } - - void testAudioPropertiesVBRIHeader() { - MPEG::File f(TEST_FILE_PATH_C("rare_frames.mp3")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(222, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(222198, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(233, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(MPEG::XingHeader::VBRI, f.audioProperties()->xingHeader()->type()); - } - - void testAudioPropertiesNoVBRHeaders() { - MPEG::File f(TEST_FILE_PATH_C("bladeenc.mp3")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(3553, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(64, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT(!f.audioProperties()->xingHeader()); - - const long long last = f.lastFrameOffset(); - const MPEG::Header lastHeader(&f, last, false); - - CPPUNIT_ASSERT_EQUAL(28213LL, last); - CPPUNIT_ASSERT_EQUAL(209, lastHeader.frameLength()); - } - - void testSkipInvalidFrames1() { - MPEG::File f(TEST_FILE_PATH_C("invalid-frames1.mp3")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(392, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(160, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT(!f.audioProperties()->xingHeader()); - } - - void testSkipInvalidFrames2() { - MPEG::File f(TEST_FILE_PATH_C("invalid-frames2.mp3")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(314, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(192, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT(!f.audioProperties()->xingHeader()); - } - - void testSkipInvalidFrames3() { - MPEG::File f(TEST_FILE_PATH_C("invalid-frames3.mp3")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(183, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(320, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT(!f.audioProperties()->xingHeader()); - } - - void testVersion2DurationWithXingHeader() { - MPEG::File f(TEST_FILE_PATH_C("mpeg2.mp3")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(5387, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(5387285, f.audioProperties()->lengthInMilliseconds()); - } - - void testSaveID3v24() { - ScopedFileCopy copy("xing", ".mp3"); - string newname = copy.fileName(); - - String xxx = ByteVector(254, 'X'); - { - MPEG::File f(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(false, f.hasID3v2Tag()); - - f.tag()->setTitle(xxx); - f.tag()->setArtist("Artist A"); - f.save(MPEG::File::AllTags, File::StripOthers, ID3v2::v4); - CPPUNIT_ASSERT_EQUAL(true, f.hasID3v2Tag()); - } - { - MPEG::File f2(newname.c_str()); - CPPUNIT_ASSERT_EQUAL((unsigned int)4, f2.ID3v2Tag()->header()->majorVersion()); - CPPUNIT_ASSERT_EQUAL(String("Artist A"), f2.tag()->artist()); - CPPUNIT_ASSERT_EQUAL(xxx, f2.tag()->title()); - } - } - - void testSaveID3v23() { - ScopedFileCopy copy("xing", ".mp3"); - string newname = copy.fileName(); - - String xxx = ByteVector(254, 'X'); - { - MPEG::File f(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(false, f.hasID3v2Tag()); - - f.tag()->setTitle(xxx); - f.tag()->setArtist("Artist A"); - f.save(MPEG::File::AllTags, File::StripOthers, ID3v2::v3); - CPPUNIT_ASSERT_EQUAL(true, f.hasID3v2Tag()); - } - { - MPEG::File f2(newname.c_str()); - CPPUNIT_ASSERT_EQUAL((unsigned int)3, f2.ID3v2Tag()->header()->majorVersion()); - CPPUNIT_ASSERT_EQUAL(String("Artist A"), f2.tag()->artist()); - CPPUNIT_ASSERT_EQUAL(xxx, f2.tag()->title()); - } - } - - void testDuplicateID3v2() { - MPEG::File f(TEST_FILE_PATH_C("duplicate_id3v2.mp3")); - - // duplicate_id3v2.mp3 has duplicate ID3v2 tags. - // Sample rate will be 32000 if can't skip the second tag. - - CPPUNIT_ASSERT(f.hasID3v2Tag()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - } - - void testFuzzedFile() { - MPEG::File f(TEST_FILE_PATH_C("excessive_alloc.mp3")); - CPPUNIT_ASSERT(f.isValid()); - } - - void testFrameOffset() { - { - MPEG::File f(TEST_FILE_PATH_C("ape.mp3")); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT_EQUAL(0x0000LL, f.firstFrameOffset()); - CPPUNIT_ASSERT_EQUAL(0x1FD6LL, f.lastFrameOffset()); - } - { - MPEG::File f(TEST_FILE_PATH_C("ape-id3v1.mp3")); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT_EQUAL(0x0000LL, f.firstFrameOffset()); - CPPUNIT_ASSERT_EQUAL(0x1FD6LL, f.lastFrameOffset()); - } - { - MPEG::File f(TEST_FILE_PATH_C("ape-id3v2.mp3")); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT_EQUAL(0x041ALL, f.firstFrameOffset()); - CPPUNIT_ASSERT_EQUAL(0x23F0LL, f.lastFrameOffset()); - } - } - - void testStripAndProperties() { - ScopedFileCopy copy("xing", ".mp3"); - - { - MPEG::File f(copy.fileName().c_str()); - f.ID3v2Tag(true)->setTitle("ID3v2"); - f.APETag(true)->setTitle("APE"); - f.ID3v1Tag(true)->setTitle("ID3v1"); - f.save(); - } - { - MPEG::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT_EQUAL(String("ID3v2"), f.properties()["TITLE"].front()); - f.strip(MPEG::File::ID3v2); - CPPUNIT_ASSERT_EQUAL(String("APE"), f.properties()["TITLE"].front()); - f.strip(MPEG::File::APE); - CPPUNIT_ASSERT_EQUAL(String("ID3v1"), f.properties()["TITLE"].front()); - f.strip(MPEG::File::ID3v1); - CPPUNIT_ASSERT(f.properties().isEmpty()); - } - } - - void testRepeatedSave1() { - ScopedFileCopy copy("xing", ".mp3"); - - { - MPEG::File f(copy.fileName().c_str()); - f.ID3v2Tag(true)->setTitle(std::string(4096, 'X').c_str()); - f.save(); - } - { - MPEG::File f(copy.fileName().c_str()); - f.ID3v2Tag(true)->setTitle(""); - f.save(); - f.ID3v2Tag(true)->setTitle(std::string(4096, 'X').c_str()); - f.save(); - CPPUNIT_ASSERT_EQUAL(5141LL, f.firstFrameOffset()); - } - } - - void testRepeatedSave2() { - ScopedFileCopy copy("xing", ".mp3"); - - MPEG::File f(copy.fileName().c_str()); - f.ID3v2Tag(true)->setTitle("0123456789"); - f.save(); - f.save(); - CPPUNIT_ASSERT_EQUAL(-1LL, f.find("ID3", 3)); - } - - void testRepeatedSave3() { - ScopedFileCopy copy("xing", ".mp3"); - - { - MPEG::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(!f.hasAPETag()); - CPPUNIT_ASSERT(!f.hasID3v1Tag()); - - f.APETag(true)->setTitle("01234 56789 ABCDE FGHIJ"); - f.save(); - f.APETag()->setTitle("0"); - f.save(); - f.ID3v1Tag(true)->setTitle("01234 56789 ABCDE FGHIJ"); - f.APETag()->setTitle("01234 56789 ABCDE FGHIJ 01234 56789 ABCDE FGHIJ 01234 56789"); - f.save(); - } - { - MPEG::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(f.hasAPETag()); - CPPUNIT_ASSERT(f.hasID3v1Tag()); - } - } - - void testEmptyID3v2() { - ScopedFileCopy copy("xing", ".mp3"); - - { - MPEG::File f(copy.fileName().c_str()); - f.ID3v2Tag(true)->setTitle("0123456789"); - f.save(MPEG::File::ID3v2); - } - { - MPEG::File f(copy.fileName().c_str()); - f.ID3v2Tag(true)->setTitle(""); - f.save(MPEG::File::ID3v2, File::StripNone); - } - { - MPEG::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(!f.hasID3v2Tag()); - } - } - - void testEmptyID3v1() { - ScopedFileCopy copy("xing", ".mp3"); - - { - MPEG::File f(copy.fileName().c_str()); - f.ID3v1Tag(true)->setTitle("0123456789"); - f.save(MPEG::File::ID3v1); - } - { - MPEG::File f(copy.fileName().c_str()); - f.ID3v1Tag(true)->setTitle(""); - f.save(MPEG::File::ID3v1, File::StripNone); - } - { - MPEG::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(!f.hasID3v1Tag()); - } - } - - void testEmptyAPE() { - ScopedFileCopy copy("xing", ".mp3"); - - { - MPEG::File f(copy.fileName().c_str()); - f.APETag(true)->setTitle("0123456789"); - f.save(MPEG::File::APE); - } - { - MPEG::File f(copy.fileName().c_str()); - f.APETag(true)->setTitle(""); - f.save(MPEG::File::APE, File::StripNone); - } - { - MPEG::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(!f.hasAPETag()); - } - } - - void testIgnoreGarbage() { - const ScopedFileCopy copy("garbage", ".mp3"); - { - MPEG::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT(f.hasID3v2Tag()); - CPPUNIT_ASSERT_EQUAL(2255LL, f.firstFrameOffset()); - CPPUNIT_ASSERT_EQUAL(6015LL, f.lastFrameOffset()); - CPPUNIT_ASSERT_EQUAL(String("Title A"), f.ID3v2Tag()->title()); - f.ID3v2Tag()->setTitle("Title B"); - f.save(); - } - { - MPEG::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT(f.hasID3v2Tag()); - CPPUNIT_ASSERT_EQUAL(String("Title B"), f.ID3v2Tag()->title()); - } - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestMPEG); diff --git a/tests/taglib/test_ogg.cpp b/tests/taglib/test_ogg.cpp deleted file mode 100644 index c827f719..00000000 --- a/tests/taglib/test_ogg.cpp +++ /dev/null @@ -1,218 +0,0 @@ -/*************************************************************************** - copyright : (C) 2009 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "tag.h" -#include "tstringlist.h" -#include "tbytevectorlist.h" -#include "tpropertymap.h" -#include "oggfile.h" -#include "vorbisfile.h" -#include "oggpageheader.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestOGG : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestOGG); - CPPUNIT_TEST(testSimple); - CPPUNIT_TEST(testSplitPackets1); - CPPUNIT_TEST(testSplitPackets2); - CPPUNIT_TEST(testDictInterface1); - CPPUNIT_TEST(testDictInterface2); - CPPUNIT_TEST(testAudioProperties); - CPPUNIT_TEST(testPageChecksum); - CPPUNIT_TEST_SUITE_END(); - - public: - void testSimple() { - ScopedFileCopy copy("empty", ".ogg"); - string newname = copy.fileName(); - - { - Ogg::Vorbis::File f(newname.c_str()); - f.tag()->setArtist("The Artist"); - f.save(); - } - { - Ogg::Vorbis::File f(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(String("The Artist"), f.tag()->artist()); - } - } - - void testSplitPackets1() { - ScopedFileCopy copy("empty", ".ogg"); - string newname = copy.fileName(); - - const String text = longText(128 * 1024, true); - - { - Ogg::Vorbis::File f(newname.c_str()); - f.tag()->setTitle(text); - f.save(); - } - { - Ogg::Vorbis::File f(newname.c_str()); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT_EQUAL(136383LL, f.length()); - CPPUNIT_ASSERT_EQUAL(19, f.lastPageHeader()->pageSequenceNumber()); - CPPUNIT_ASSERT_EQUAL((size_t)30, f.packet(0).size()); - CPPUNIT_ASSERT_EQUAL((size_t)131127, f.packet(1).size()); - CPPUNIT_ASSERT_EQUAL((size_t)3832, f.packet(2).size()); - CPPUNIT_ASSERT_EQUAL(text, f.tag()->title()); - - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3685, f.audioProperties()->lengthInMilliseconds()); - - f.tag()->setTitle("ABCDE"); - f.save(); - } - { - Ogg::Vorbis::File f(newname.c_str()); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT_EQUAL(4370LL, f.length()); - CPPUNIT_ASSERT_EQUAL(3, f.lastPageHeader()->pageSequenceNumber()); - CPPUNIT_ASSERT_EQUAL((size_t)30, f.packet(0).size()); - CPPUNIT_ASSERT_EQUAL((size_t)60, f.packet(1).size()); - CPPUNIT_ASSERT_EQUAL((size_t)3832, f.packet(2).size()); - CPPUNIT_ASSERT_EQUAL(String("ABCDE"), f.tag()->title()); - - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3685, f.audioProperties()->lengthInMilliseconds()); - } - } - - void testSplitPackets2() { - ScopedFileCopy copy("empty", ".ogg"); - string newname = copy.fileName(); - - const String text = longText(60890, true); - - { - Ogg::Vorbis::File f(newname.c_str()); - f.tag()->setTitle(text); - f.save(); - } - { - Ogg::Vorbis::File f(newname.c_str()); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT_EQUAL(text, f.tag()->title()); - - f.tag()->setTitle("ABCDE"); - f.save(); - } - { - Ogg::Vorbis::File f(newname.c_str()); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT_EQUAL(String("ABCDE"), f.tag()->title()); - } - } - - void testDictInterface1() { - ScopedFileCopy copy("empty", ".ogg"); - string newname = copy.fileName(); - - Ogg::Vorbis::File f(newname.c_str()); - - CPPUNIT_ASSERT_EQUAL((size_t)0, f.tag()->properties().size()); - - PropertyMap newTags; - StringList values("value 1"); - values.append("value 2"); - newTags["ARTIST"] = values; - f.tag()->setProperties(newTags); - - PropertyMap map = f.tag()->properties(); - CPPUNIT_ASSERT_EQUAL((size_t)1, map.size()); - CPPUNIT_ASSERT_EQUAL((size_t)2, map["ARTIST"].size()); - CPPUNIT_ASSERT_EQUAL(String("value 1"), map["ARTIST"][0]); - } - - void testDictInterface2() { - ScopedFileCopy copy("test", ".ogg"); - string newname = copy.fileName(); - - Ogg::Vorbis::File f(newname.c_str()); - PropertyMap tags = f.tag()->properties(); - - CPPUNIT_ASSERT_EQUAL((size_t)2, tags["UNUSUALTAG"].size()); - CPPUNIT_ASSERT_EQUAL(String("usual value"), tags["UNUSUALTAG"][0]); - CPPUNIT_ASSERT_EQUAL(String("another value"), tags["UNUSUALTAG"][1]); - CPPUNIT_ASSERT_EQUAL( - String("\xC3\xB6\xC3\xA4\xC3\xBC\x6F\xCE\xA3\xC3\xB8", String::UTF8), - tags["UNICODETAG"][0]); - - tags["UNICODETAG"][0] = String( - "\xCE\xBD\xCE\xB5\xCF\x89\x20\xCE\xBD\xCE\xB1\xCE\xBB\xCF\x85\xCE\xB5", - String::UTF8); - tags.erase("UNUSUALTAG"); - f.tag()->setProperties(tags); - CPPUNIT_ASSERT_EQUAL( - String("\xCE\xBD\xCE\xB5\xCF\x89\x20\xCE\xBD\xCE\xB1\xCE\xBB\xCF\x85\xCE\xB5", String::UTF8), - f.tag()->properties()["UNICODETAG"][0]); - CPPUNIT_ASSERT_EQUAL(false, f.tag()->properties().contains("UNUSUALTAG")); - } - - void testAudioProperties() { - Ogg::Vorbis::File f(TEST_FILE_PATH_C("empty.ogg")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(3685, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(9, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->vorbisVersion()); - CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->bitrateMaximum()); - CPPUNIT_ASSERT_EQUAL(112000, f.audioProperties()->bitrateNominal()); - CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->bitrateMinimum()); - } - - void testPageChecksum() { - ScopedFileCopy copy("empty", ".ogg"); - - { - Ogg::Vorbis::File f(copy.fileName().c_str()); - f.tag()->setArtist("The Artist"); - f.save(); - - f.seek(0x50); - CPPUNIT_ASSERT_EQUAL((unsigned int)0x3d3bd92d, f.readBlock(4).toUInt32BE(0)); - } - { - Ogg::Vorbis::File f(copy.fileName().c_str()); - f.tag()->setArtist("The Artist 2"); - f.save(); - - f.seek(0x50); - CPPUNIT_ASSERT_EQUAL((unsigned int)0xd985291c, f.readBlock(4).toUInt32BE(0)); - } - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestOGG); diff --git a/tests/taglib/test_oggflac.cpp b/tests/taglib/test_oggflac.cpp deleted file mode 100644 index 4c2e8855..00000000 --- a/tests/taglib/test_oggflac.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/*************************************************************************** - copyright : (C) 2009 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "tag.h" -#include "tstringlist.h" -#include "tbytevectorlist.h" -#include "oggfile.h" -#include "oggflacfile.h" -#include "oggpageheader.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestOggFLAC : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestOggFLAC); - CPPUNIT_TEST(testFramingBit); - CPPUNIT_TEST(testFuzzedFile); - CPPUNIT_TEST(testSplitPackets); - CPPUNIT_TEST_SUITE_END(); - - public: - void testFramingBit() { - ScopedFileCopy copy("empty_flac", ".oga"); - string newname = copy.fileName(); - - { - Ogg::FLAC::File f(newname.c_str()); - f.tag()->setArtist("The Artist"); - f.save(); - } - { - Ogg::FLAC::File f(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(String("The Artist"), f.tag()->artist()); - - f.seek(0, File::End); - CPPUNIT_ASSERT_EQUAL(9134LL, f.tell()); - } - } - - void testFuzzedFile() { - Ogg::FLAC::File f(TEST_FILE_PATH_C("segfault.oga")); - CPPUNIT_ASSERT(!f.isValid()); - } - - void testSplitPackets() { - ScopedFileCopy copy("empty_flac", ".oga"); - string newname = copy.fileName(); - - const String text = longText(128 * 1024, true); - - { - Ogg::FLAC::File f(newname.c_str()); - f.tag()->setTitle(text); - f.save(); - } - { - Ogg::FLAC::File f(newname.c_str()); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT_EQUAL(141141LL, f.length()); - CPPUNIT_ASSERT_EQUAL(21, f.lastPageHeader()->pageSequenceNumber()); - CPPUNIT_ASSERT_EQUAL((size_t)51, f.packet(0).size()); - CPPUNIT_ASSERT_EQUAL((size_t)131126, f.packet(1).size()); - CPPUNIT_ASSERT_EQUAL((size_t)22, f.packet(2).size()); - CPPUNIT_ASSERT_EQUAL((size_t)8196, f.packet(3).size()); - CPPUNIT_ASSERT_EQUAL(text, f.tag()->title()); - - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3705, f.audioProperties()->lengthInMilliseconds()); - - f.tag()->setTitle("ABCDE"); - f.save(); - } - { - Ogg::FLAC::File f(newname.c_str()); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT_EQUAL(9128LL, f.length()); - CPPUNIT_ASSERT_EQUAL(5, f.lastPageHeader()->pageSequenceNumber()); - CPPUNIT_ASSERT_EQUAL((size_t)51, f.packet(0).size()); - CPPUNIT_ASSERT_EQUAL((size_t)59, f.packet(1).size()); - CPPUNIT_ASSERT_EQUAL((size_t)22, f.packet(2).size()); - CPPUNIT_ASSERT_EQUAL((size_t)8196, f.packet(3).size()); - CPPUNIT_ASSERT_EQUAL(String("ABCDE"), f.tag()->title()); - - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3705, f.audioProperties()->lengthInMilliseconds()); - } - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestOggFLAC); diff --git a/tests/taglib/test_opus.cpp b/tests/taglib/test_opus.cpp deleted file mode 100644 index 7b4350a3..00000000 --- a/tests/taglib/test_opus.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/*************************************************************************** - copyright : (C) 2012 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "tag.h" -#include "tbytevectorlist.h" -#include "opusfile.h" -#include "oggpageheader.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestOpus : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestOpus); - CPPUNIT_TEST(testAudioProperties); - CPPUNIT_TEST(testReadComments); - CPPUNIT_TEST(testWriteComments); - CPPUNIT_TEST(testSplitPackets); - CPPUNIT_TEST_SUITE_END(); - - public: - void testAudioProperties() { - Ogg::Opus::File f(TEST_FILE_PATH_C("correctness_gain_silent_output.opus")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(7, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(7737, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(37, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(48000, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(48000, f.audioProperties()->inputSampleRate()); - CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->opusVersion()); - } - - void testReadComments() { - Ogg::Opus::File f(TEST_FILE_PATH_C("correctness_gain_silent_output.opus")); - CPPUNIT_ASSERT_EQUAL(StringList("Xiph.Org Opus testvectormaker"), f.tag()->fieldListMap()["ENCODER"]); - CPPUNIT_ASSERT(f.tag()->fieldListMap().contains("TESTDESCRIPTION")); - CPPUNIT_ASSERT(!f.tag()->fieldListMap().contains("ARTIST")); - CPPUNIT_ASSERT_EQUAL(String("libopus 0.9.11-66-g64c2dd7"), f.tag()->vendorID()); - } - - void testWriteComments() { - ScopedFileCopy copy("correctness_gain_silent_output", ".opus"); - string filename = copy.fileName(); - - { - Ogg::Opus::File f(filename.c_str()); - f.tag()->setArtist("Your Tester"); - f.save(); - } - { - Ogg::Opus::File f(filename.c_str()); - CPPUNIT_ASSERT_EQUAL(StringList("Xiph.Org Opus testvectormaker"), f.tag()->fieldListMap()["ENCODER"]); - CPPUNIT_ASSERT(f.tag()->fieldListMap().contains("TESTDESCRIPTION")); - CPPUNIT_ASSERT_EQUAL(StringList("Your Tester"), f.tag()->fieldListMap()["ARTIST"]); - CPPUNIT_ASSERT_EQUAL(String("libopus 0.9.11-66-g64c2dd7"), f.tag()->vendorID()); - } - } - - void testSplitPackets() { - ScopedFileCopy copy("correctness_gain_silent_output", ".opus"); - string newname = copy.fileName(); - - const String text = longText(128 * 1024, true); - - { - Ogg::Opus::File f(newname.c_str()); - f.tag()->setTitle(text); - f.save(); - } - { - Ogg::Opus::File f(newname.c_str()); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT_EQUAL(167534LL, f.length()); - CPPUNIT_ASSERT_EQUAL(27, f.lastPageHeader()->pageSequenceNumber()); - CPPUNIT_ASSERT_EQUAL((size_t)19, f.packet(0).size()); - CPPUNIT_ASSERT_EQUAL((size_t)131380, f.packet(1).size()); - CPPUNIT_ASSERT_EQUAL((size_t)5, f.packet(2).size()); - CPPUNIT_ASSERT_EQUAL((size_t)5, f.packet(3).size()); - CPPUNIT_ASSERT_EQUAL(text, f.tag()->title()); - - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(7737, f.audioProperties()->lengthInMilliseconds()); - - f.tag()->setTitle("ABCDE"); - f.save(); - } - { - Ogg::Opus::File f(newname.c_str()); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT_EQUAL(35521LL, f.length()); - CPPUNIT_ASSERT_EQUAL(11, f.lastPageHeader()->pageSequenceNumber()); - CPPUNIT_ASSERT_EQUAL((size_t)19, f.packet(0).size()); - CPPUNIT_ASSERT_EQUAL((size_t)313, f.packet(1).size()); - CPPUNIT_ASSERT_EQUAL((size_t)5, f.packet(2).size()); - CPPUNIT_ASSERT_EQUAL((size_t)5, f.packet(3).size()); - CPPUNIT_ASSERT_EQUAL(String("ABCDE"), f.tag()->title()); - - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(7737, f.audioProperties()->lengthInMilliseconds()); - } - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestOpus); diff --git a/tests/taglib/test_propertymap.cpp b/tests/taglib/test_propertymap.cpp deleted file mode 100644 index e48c4abf..00000000 --- a/tests/taglib/test_propertymap.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/*************************************************************************** - copyright : (C) 2012 by Michael Helmling - email : helmling@mathematik.uni-kl.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 - -#include "tpropertymap.h" -#include "tag.h" -#include "id3v1tag.h" -#include "utils.h" - -using namespace Strawberry_TagLib::TagLib; - -class TestPropertyMap : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestPropertyMap); - CPPUNIT_TEST(testInvalidKeys); - CPPUNIT_TEST(testGetSet); - CPPUNIT_TEST_SUITE_END(); - - public: - void testInvalidKeys() { - PropertyMap map1; - CPPUNIT_ASSERT(map1.isEmpty()); - map1[L"\x00c4\x00d6\x00dc"].append("test"); - CPPUNIT_ASSERT_EQUAL(map1.size(), (size_t)1); - - PropertyMap map2; - map2[L"\x00c4\x00d6\x00dc"].append("test"); - CPPUNIT_ASSERT(map1 == map2); - CPPUNIT_ASSERT(map1.contains(map2)); - - map2["ARTIST"] = String("Test Artist"); - CPPUNIT_ASSERT(map1 != map2); - CPPUNIT_ASSERT(map2.contains(map1)); - - map2["\xc4\xd6\xdc"].append("test 2"); - map2[L"\x00c4\x00d6\x00dc"].append("test 2"); - CPPUNIT_ASSERT(!map2.contains(map1)); - } - - void testGetSet() { - ID3v1::Tag tag; - - tag.setTitle("Test Title"); - tag.setArtist("Test Artist"); - tag.setAlbum("Test Album"); - tag.setYear(2015); - tag.setTrack(10); - - { - PropertyMap prop = tag.properties(); - CPPUNIT_ASSERT_EQUAL(String("Test Title"), prop["TITLE"].front()); - CPPUNIT_ASSERT_EQUAL(String("Test Artist"), prop["ARTIST"].front()); - CPPUNIT_ASSERT_EQUAL(String("Test Album"), prop["ALBUM"].front()); - CPPUNIT_ASSERT_EQUAL(String("2015"), prop["DATE"].front()); - CPPUNIT_ASSERT_EQUAL(String("10"), prop["TRACKNUMBER"].front()); - - prop["TITLE"].front() = "Test Title 2"; - prop["ARTIST"].front() = "Test Artist 2"; - prop["TRACKNUMBER"].front() = "5"; - - tag.setProperties(prop); - } - - CPPUNIT_ASSERT_EQUAL(String("Test Title 2"), tag.title()); - CPPUNIT_ASSERT_EQUAL(String("Test Artist 2"), tag.artist()); - CPPUNIT_ASSERT_EQUAL(5U, tag.track()); - - tag.setProperties(PropertyMap()); - - CPPUNIT_ASSERT_EQUAL(String(""), tag.title()); - CPPUNIT_ASSERT_EQUAL(String(""), tag.artist()); - CPPUNIT_ASSERT_EQUAL(0U, tag.track()); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestPropertyMap); diff --git a/tests/taglib/test_riff.cpp b/tests/taglib/test_riff.cpp deleted file mode 100644 index e4fdf1f0..00000000 --- a/tests/taglib/test_riff.cpp +++ /dev/null @@ -1,288 +0,0 @@ -/*************************************************************************** - copyright : (C) 2009 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "tag.h" -#include "tbytevectorlist.h" -#include "rifffile.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class PublicRIFF : public RIFF::File { - public: - explicit PublicRIFF(FileName file) : RIFF::File(file, BigEndian) {}; - unsigned int riffSize() { return RIFF::File::riffSize(); }; - size_t chunkCount() { return RIFF::File::chunkCount(); }; - long long chunkOffset(unsigned int i) { return RIFF::File::chunkOffset(i); }; - unsigned int chunkPadding(unsigned int i) { return RIFF::File::chunkPadding(i); }; - unsigned int chunkDataSize(unsigned int i) { return RIFF::File::chunkDataSize(i); }; - ByteVector chunkName(unsigned int i) { return RIFF::File::chunkName(i); }; - ByteVector chunkData(unsigned int i) { return RIFF::File::chunkData(i); }; - void setChunkData(unsigned int i, const ByteVector &data) { - RIFF::File::setChunkData(i, data); - } - void setChunkData(const ByteVector &name, const ByteVector &data) { - RIFF::File::setChunkData(name, data); - }; - Strawberry_TagLib::TagLib::Tag *tag() const override { return nullptr; } - Strawberry_TagLib::TagLib::AudioProperties *audioProperties() const override { return nullptr; } - bool save() override { return false; } - void removeChunk(unsigned int i) { RIFF::File::removeChunk(i); } - void removeChunk(const ByteVector &name) { RIFF::File::removeChunk(name); } -}; - -class TestRIFF : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestRIFF); - CPPUNIT_TEST(testPadding); - CPPUNIT_TEST(testLastChunkAtEvenPosition); - CPPUNIT_TEST(testLastChunkAtEvenPosition2); - CPPUNIT_TEST(testLastChunkAtEvenPosition3); - CPPUNIT_TEST(testChunkOffset); - CPPUNIT_TEST_SUITE_END(); - - public: - void testPadding() { - ScopedFileCopy copy("empty", ".aiff"); - string filename = copy.fileName(); - - { - PublicRIFF f(filename.c_str()); - CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(2)); - CPPUNIT_ASSERT_EQUAL((long long)(0x1728 + 8), f.chunkOffset(2)); - - f.setChunkData("TEST", "foo"); - } - { - PublicRIFF f(filename.c_str()); - CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(2)); - CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), f.chunkData(2)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(3), f.chunkDataSize(2)); - CPPUNIT_ASSERT_EQUAL((long long)(0x1728 + 8), f.chunkOffset(2)); - - f.setChunkData("SSND", "abcd"); - - CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(1)); - CPPUNIT_ASSERT_EQUAL(ByteVector("abcd"), f.chunkData(1)); - - f.seek(f.chunkOffset(1)); - CPPUNIT_ASSERT_EQUAL(ByteVector("abcd"), f.readBlock(4)); - - CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(2)); - CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), f.chunkData(2)); - - f.seek(f.chunkOffset(2)); - CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), f.readBlock(3)); - } - { - PublicRIFF f(filename.c_str()); - CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(1)); - CPPUNIT_ASSERT_EQUAL(ByteVector("abcd"), f.chunkData(1)); - - CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(2)); - CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), f.chunkData(2)); - } - } - - void testLastChunkAtEvenPosition() { - ScopedFileCopy copy("noise", ".aif"); - string filename = copy.fileName(); - - { - PublicRIFF f(filename.c_str()); - CPPUNIT_ASSERT_EQUAL((long long)(0xff0 + 8), f.chunkOffset(2)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(311), f.chunkDataSize(2)); - CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(2)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(1), f.chunkPadding(2)); - CPPUNIT_ASSERT_EQUAL((long long)(4400), f.length()); - CPPUNIT_ASSERT_EQUAL((unsigned int)(4399 - 8), f.riffSize()); - f.setChunkData("TEST", "abcd"); - CPPUNIT_ASSERT_EQUAL((long long)(4088), f.chunkOffset(2)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(311), f.chunkDataSize(2)); - CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(2)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(1), f.chunkPadding(2)); - CPPUNIT_ASSERT_EQUAL((long long)(4408), f.chunkOffset(3)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(4), f.chunkDataSize(3)); - CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(3)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(0), f.chunkPadding(3)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(4412 - 8), f.riffSize()); - } - { - PublicRIFF f(filename.c_str()); - CPPUNIT_ASSERT_EQUAL((long long)(4088), f.chunkOffset(2)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(311), f.chunkDataSize(2)); - CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(2)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(1), f.chunkPadding(2)); - CPPUNIT_ASSERT_EQUAL((long long)(4408), f.chunkOffset(3)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(4), f.chunkDataSize(3)); - CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(3)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(0), f.chunkPadding(3)); - CPPUNIT_ASSERT_EQUAL((long long)(4412), f.length()); - } - } - - void testLastChunkAtEvenPosition2() { - ScopedFileCopy copy("noise_odd", ".aif"); - string filename = copy.fileName(); - - { - PublicRIFF f(filename.c_str()); - CPPUNIT_ASSERT_EQUAL((long long)(0xff0 + 8), f.chunkOffset(2)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(311), f.chunkDataSize(2)); - CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(2)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(0), f.chunkPadding(2)); - CPPUNIT_ASSERT_EQUAL((long long)(4399), f.length()); - CPPUNIT_ASSERT_EQUAL((unsigned int)(4399 - 8), f.riffSize()); - f.setChunkData("TEST", "abcd"); - CPPUNIT_ASSERT_EQUAL((long long)(4088), f.chunkOffset(2)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(311), f.chunkDataSize(2)); - CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(2)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(1), f.chunkPadding(2)); - CPPUNIT_ASSERT_EQUAL((long long)(4408), f.chunkOffset(3)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(4), f.chunkDataSize(3)); - CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(3)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(0), f.chunkPadding(3)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(4412 - 8), f.riffSize()); - } - { - PublicRIFF f(filename.c_str()); - CPPUNIT_ASSERT_EQUAL((long long)(4088), f.chunkOffset(2)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(311), f.chunkDataSize(2)); - CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(2)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(1), f.chunkPadding(2)); - CPPUNIT_ASSERT_EQUAL((long long)(4408), f.chunkOffset(3)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(4), f.chunkDataSize(3)); - CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(3)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(0), f.chunkPadding(3)); - CPPUNIT_ASSERT_EQUAL((long long)(4412), f.length()); - } - } - - void testLastChunkAtEvenPosition3() { - ScopedFileCopy copy("noise_odd", ".aif"); - string filename = copy.fileName(); - - { - PublicRIFF f(filename.c_str()); - CPPUNIT_ASSERT_EQUAL((long long)(0xff0 + 8), f.chunkOffset(2)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(311), f.chunkDataSize(2)); - CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(2)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(0), f.chunkPadding(2)); - CPPUNIT_ASSERT_EQUAL((long long)(4399), f.length()); - CPPUNIT_ASSERT_EQUAL((unsigned int)(4399 - 8), f.riffSize()); - f.setChunkData("TEST", "abc"); - CPPUNIT_ASSERT_EQUAL((long long)(4088), f.chunkOffset(2)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(311), f.chunkDataSize(2)); - CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(2)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(1), f.chunkPadding(2)); - CPPUNIT_ASSERT_EQUAL((long long)(4408), f.chunkOffset(3)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(3), f.chunkDataSize(3)); - CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(3)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(1), f.chunkPadding(3)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(4412 - 8), f.riffSize()); - } - { - PublicRIFF f(filename.c_str()); - CPPUNIT_ASSERT_EQUAL((long long)(4088), f.chunkOffset(2)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(311), f.chunkDataSize(2)); - CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(2)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(1), f.chunkPadding(2)); - CPPUNIT_ASSERT_EQUAL((long long)(4408), f.chunkOffset(3)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(3), f.chunkDataSize(3)); - CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(3)); - CPPUNIT_ASSERT_EQUAL((unsigned int)(1), f.chunkPadding(3)); - CPPUNIT_ASSERT_EQUAL((long long)(4412), f.length()); - } - } - - void testChunkOffset() { - ScopedFileCopy copy("empty", ".aiff"); - string filename = copy.fileName(); - - PublicRIFF f(filename.c_str()); - - CPPUNIT_ASSERT_EQUAL(5928U, f.riffSize()); - CPPUNIT_ASSERT_EQUAL(5936LL, f.length()); - CPPUNIT_ASSERT_EQUAL(ByteVector("COMM"), f.chunkName(0)); - CPPUNIT_ASSERT_EQUAL((long long)(0x000C + 8), f.chunkOffset(0)); - CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(1)); - CPPUNIT_ASSERT_EQUAL((long long)(0x0026 + 8), f.chunkOffset(1)); - CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(2)); - CPPUNIT_ASSERT_EQUAL((long long)(0x1728 + 8), f.chunkOffset(2)); - - const ByteVector data(0x400, ' '); - f.setChunkData("SSND", data); - CPPUNIT_ASSERT_EQUAL(1070U, f.riffSize()); - CPPUNIT_ASSERT_EQUAL(1078LL, f.length()); - CPPUNIT_ASSERT_EQUAL((long long)(0x000C + 8), f.chunkOffset(0)); - CPPUNIT_ASSERT_EQUAL((long long)(0x0026 + 8), f.chunkOffset(1)); - CPPUNIT_ASSERT_EQUAL((long long)(0x042E + 8), f.chunkOffset(2)); - - f.seek(f.chunkOffset(0) - 8); - CPPUNIT_ASSERT_EQUAL(ByteVector("COMM"), f.readBlock(4)); - f.seek(f.chunkOffset(1) - 8); - CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.readBlock(4)); - f.seek(f.chunkOffset(2) - 8); - CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.readBlock(4)); - - f.setChunkData(0, data); - CPPUNIT_ASSERT_EQUAL(2076U, f.riffSize()); - CPPUNIT_ASSERT_EQUAL(2084LL, f.length()); - CPPUNIT_ASSERT_EQUAL((long long)(0x000C + 8), f.chunkOffset(0)); - CPPUNIT_ASSERT_EQUAL((long long)(0x0414 + 8), f.chunkOffset(1)); - CPPUNIT_ASSERT_EQUAL((long long)(0x081C + 8), f.chunkOffset(2)); - - f.seek(f.chunkOffset(0) - 8); - CPPUNIT_ASSERT_EQUAL(ByteVector("COMM"), f.readBlock(4)); - f.seek(f.chunkOffset(1) - 8); - CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.readBlock(4)); - f.seek(f.chunkOffset(2) - 8); - CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.readBlock(4)); - - f.removeChunk("SSND"); - CPPUNIT_ASSERT_EQUAL(1044U, f.riffSize()); - CPPUNIT_ASSERT_EQUAL(1052LL, f.length()); - CPPUNIT_ASSERT_EQUAL((long long)(0x000C + 8), f.chunkOffset(0)); - CPPUNIT_ASSERT_EQUAL((long long)(0x0414 + 8), f.chunkOffset(1)); - - f.seek(f.chunkOffset(0) - 8); - CPPUNIT_ASSERT_EQUAL(ByteVector("COMM"), f.readBlock(4)); - f.seek(f.chunkOffset(1) - 8); - CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.readBlock(4)); - - f.removeChunk(0); - CPPUNIT_ASSERT_EQUAL(12U, f.riffSize()); - CPPUNIT_ASSERT_EQUAL((long long)(0x000C + 8), f.chunkOffset(0)); - - f.seek(f.chunkOffset(0) - 8); - CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.readBlock(4)); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestRIFF); diff --git a/tests/taglib/test_s3m.cpp b/tests/taglib/test_s3m.cpp deleted file mode 100644 index 8fa5ba2f..00000000 --- a/tests/taglib/test_s3m.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "s3mfile.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -static const String titleBefore("test song name"); -static const String titleAfter("changed title"); - -static const String commentBefore( - "This is an instrument name.\n" - "Module file formats\n" - "abuse instrument names\n" - "as multiline comments.\n" - " "); - -static const String newComment( - "This is an instrument name!\n" - "Module file formats\n" - "abuse instrument names\n" - "as multiline comments.\n" - "-----------------------------------\n" - "This line will be dropped and the previous is truncated."); - -static const String commentAfter( - "This is an instrument name!\n" - "Module file formats\n" - "abuse instrument names\n" - "as multiline comments.\n" - "---------------------------"); - -class TestS3M : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestS3M); - CPPUNIT_TEST(testReadTags); - CPPUNIT_TEST(testWriteTags); - CPPUNIT_TEST_SUITE_END(); - - public: - void testReadTags() { - testRead(TEST_FILE_PATH_C("test.s3m"), titleBefore, commentBefore); - } - - void testWriteTags() { - ScopedFileCopy copy("test", ".s3m"); - { - S3M::File file(copy.fileName().c_str()); - CPPUNIT_ASSERT(file.tag() != nullptr); - file.tag()->setTitle(titleAfter); - file.tag()->setComment(newComment); - file.tag()->setTrackerName("won't be saved"); - CPPUNIT_ASSERT(file.save()); - } - testRead(copy.fileName().c_str(), titleAfter, commentAfter); - CPPUNIT_ASSERT(fileEqual( - copy.fileName(), - TEST_FILE_PATH_C("changed.s3m"))); - } - - private: - void testRead(FileName fileName, const String &title, const String &comment) { - S3M::File file(fileName); - - CPPUNIT_ASSERT(file.isValid()); - - S3M::AudioProperties *p = file.audioProperties(); - Mod::Tag *t = file.tag(); - - CPPUNIT_ASSERT(nullptr != p); - CPPUNIT_ASSERT(nullptr != t); - - CPPUNIT_ASSERT_EQUAL(0, p->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(0, p->bitrate()); - CPPUNIT_ASSERT_EQUAL(0, p->sampleRate()); - CPPUNIT_ASSERT_EQUAL(16, p->channels()); - CPPUNIT_ASSERT_EQUAL((unsigned short)0, p->lengthInPatterns()); - CPPUNIT_ASSERT_EQUAL(false, p->stereo()); - CPPUNIT_ASSERT_EQUAL((unsigned short)5, p->sampleCount()); - CPPUNIT_ASSERT_EQUAL((unsigned short)1, p->patternCount()); - CPPUNIT_ASSERT_EQUAL((unsigned short)0, p->flags()); - CPPUNIT_ASSERT_EQUAL((unsigned short)4896, p->trackerVersion()); - CPPUNIT_ASSERT_EQUAL((unsigned short)2, p->fileFormatVersion()); - CPPUNIT_ASSERT_EQUAL((unsigned char)64, p->globalVolume()); - CPPUNIT_ASSERT_EQUAL((unsigned char)48, p->masterVolume()); - CPPUNIT_ASSERT_EQUAL((unsigned char)125, p->tempo()); - CPPUNIT_ASSERT_EQUAL((unsigned char)6, p->bpmSpeed()); - CPPUNIT_ASSERT_EQUAL(title, t->title()); - CPPUNIT_ASSERT_EQUAL(String(), t->artist()); - CPPUNIT_ASSERT_EQUAL(String(), t->album()); - CPPUNIT_ASSERT_EQUAL(comment, t->comment()); - CPPUNIT_ASSERT_EQUAL(String(), t->genre()); - CPPUNIT_ASSERT_EQUAL(0U, t->year()); - CPPUNIT_ASSERT_EQUAL(0U, t->track()); - CPPUNIT_ASSERT_EQUAL(String("ScreamTracker III"), t->trackerName()); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestS3M); diff --git a/tests/taglib/test_speex.cpp b/tests/taglib/test_speex.cpp deleted file mode 100644 index 8b1fc1cb..00000000 --- a/tests/taglib/test_speex.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/*************************************************************************** - copyright : (C) 2015 by Tsuda Kageyu - email : tsuda.kageyu@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "speexfile.h" -#include "oggpageheader.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestSpeex : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestSpeex); - CPPUNIT_TEST(testAudioProperties); - CPPUNIT_TEST(testSplitPackets); - CPPUNIT_TEST_SUITE_END(); - - public: - void testAudioProperties() { - Ogg::Speex::File f(TEST_FILE_PATH_C("empty.spx")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(3685, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(53, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(-1, f.audioProperties()->bitrateNominal()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - } - - void testSplitPackets() { - ScopedFileCopy copy("empty", ".spx"); - string newname = copy.fileName(); - - const String text = longText(128 * 1024, true); - - { - Ogg::Speex::File f(newname.c_str()); - f.tag()->setTitle(text); - f.save(); - } - { - Ogg::Speex::File f(newname.c_str()); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT_EQUAL(156330LL, f.length()); - CPPUNIT_ASSERT_EQUAL(23, f.lastPageHeader()->pageSequenceNumber()); - CPPUNIT_ASSERT_EQUAL((size_t)80, f.packet(0).size()); - CPPUNIT_ASSERT_EQUAL((size_t)131116, f.packet(1).size()); - CPPUNIT_ASSERT_EQUAL((size_t)93, f.packet(2).size()); - CPPUNIT_ASSERT_EQUAL((size_t)93, f.packet(3).size()); - CPPUNIT_ASSERT_EQUAL(text, f.tag()->title()); - - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3685, f.audioProperties()->lengthInMilliseconds()); - - f.tag()->setTitle("ABCDE"); - f.save(); - } - { - Ogg::Speex::File f(newname.c_str()); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT_EQUAL(24317LL, f.length()); - CPPUNIT_ASSERT_EQUAL(7, f.lastPageHeader()->pageSequenceNumber()); - CPPUNIT_ASSERT_EQUAL((size_t)80, f.packet(0).size()); - CPPUNIT_ASSERT_EQUAL((size_t)49, f.packet(1).size()); - CPPUNIT_ASSERT_EQUAL((size_t)93, f.packet(2).size()); - CPPUNIT_ASSERT_EQUAL((size_t)93, f.packet(3).size()); - CPPUNIT_ASSERT_EQUAL(String("ABCDE"), f.tag()->title()); - - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3685, f.audioProperties()->lengthInMilliseconds()); - } - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestSpeex); diff --git a/tests/taglib/test_string.cpp b/tests/taglib/test_string.cpp deleted file mode 100644 index 97ac8f8d..00000000 --- a/tests/taglib/test_string.cpp +++ /dev/null @@ -1,362 +0,0 @@ -/*************************************************************************** - copyright : (C) 2007 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include - -#include "tstring.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestString : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestString); - CPPUNIT_TEST(testString); - CPPUNIT_TEST(testRfind); - CPPUNIT_TEST(testUTF16Encode); - CPPUNIT_TEST(testUTF16Decode); - CPPUNIT_TEST(testUTF16DecodeInvalidBOM); - CPPUNIT_TEST(testUTF16DecodeEmptyWithBOM); - CPPUNIT_TEST(testSurrogatePair); - CPPUNIT_TEST(testAppendCharDetach); - CPPUNIT_TEST(testAppendStringDetach); - CPPUNIT_TEST(testToInt); - CPPUNIT_TEST(testFromInt); - CPPUNIT_TEST(testSubstr); - CPPUNIT_TEST(testNewline); - CPPUNIT_TEST(testUpper); - CPPUNIT_TEST(testEncodeNonLatin1); - CPPUNIT_TEST(testEncodeEmpty); - CPPUNIT_TEST(testEncodeNonBMP); - CPPUNIT_TEST(testIterator); - CPPUNIT_TEST(testInvalidUTF8); - CPPUNIT_TEST_SUITE_END(); - - public: - void testString() { - // Needs to know the system byte order for some Unicode tests. - bool littleEndian; - { - union { - int i; - char c; - } u; - - u.i = 1; - littleEndian = (u.c == 1) ? true : false; - } - - String s = "taglib string"; - ByteVector v = "taglib string"; - CPPUNIT_ASSERT(v == s.data(String::Latin1)); - - char str[] = "taglib string"; - CPPUNIT_ASSERT(strcmp(s.toCString(), str) == 0); - - s.clear(); - CPPUNIT_ASSERT(s.isEmpty()); - - String unicode("José Carlos", String::UTF8); - CPPUNIT_ASSERT(strcmp(unicode.toCString(), "Jos\xe9 Carlos") == 0); - - String latin = "Jos\xe9 Carlos"; - CPPUNIT_ASSERT(strcmp(latin.toCString(true), "José Carlos") == 0); - - String c; - c = "1"; - CPPUNIT_ASSERT(c == L"1"); - - c = L'\u4E00'; - CPPUNIT_ASSERT(c == L"\u4E00"); - - String unicode2(unicode.to8Bit(true), String::UTF8); - CPPUNIT_ASSERT(unicode == unicode2); - - String unicode3(L"\u65E5\u672C\u8A9E"); - CPPUNIT_ASSERT(*(unicode3.toCWString() + 1) == L'\u672C'); - - String unicode4(L"\u65e5\u672c\u8a9e"); - CPPUNIT_ASSERT(unicode4[1] == L'\u672c'); - - String unicode5(L"\u65e5\u672c\u8a9e", String::UTF16BE); - CPPUNIT_ASSERT(unicode5[1] == (littleEndian ? L'\u2c67' : L'\u672c')); - - String unicode6(L"\u65e5\u672c\u8a9e", String::UTF16LE); - CPPUNIT_ASSERT(unicode6[1] == (littleEndian ? L'\u672c' : L'\u2c67')); - - CPPUNIT_ASSERT(String(" foo ").stripWhiteSpace() == String("foo")); - CPPUNIT_ASSERT(String("foo ").stripWhiteSpace() == String("foo")); - CPPUNIT_ASSERT(String(" foo").stripWhiteSpace() == String("foo")); - CPPUNIT_ASSERT(String("foo").stripWhiteSpace() == String("foo")); - CPPUNIT_ASSERT(String("f o o").stripWhiteSpace() == String("f o o")); - CPPUNIT_ASSERT(String(" f o o ").stripWhiteSpace() == String("f o o")); - - CPPUNIT_ASSERT(memcmp(String("foo").data(String::Latin1).data(), "foo", 3) == 0); - CPPUNIT_ASSERT(memcmp(String("f").data(String::Latin1).data(), "f", 1) == 0); - } - - void testUTF16Encode() { - String a("foo"); - ByteVector b("\0f\0o\0o", 6); - ByteVector c("f\0o\0o\0", 6); - ByteVector d("\377\376f\0o\0o\0", 8); - CPPUNIT_ASSERT(a.data(String::UTF16BE) != a.data(String::UTF16LE)); - CPPUNIT_ASSERT(b == a.data(String::UTF16BE)); - CPPUNIT_ASSERT(c == a.data(String::UTF16LE)); - CPPUNIT_ASSERT_EQUAL(d, a.data(String::UTF16)); - } - - void testUTF16Decode() { - String a("foo"); - ByteVector b("\0f\0o\0o", 6); - ByteVector c("f\0o\0o\0", 6); - ByteVector d("\377\376f\0o\0o\0", 8); - CPPUNIT_ASSERT_EQUAL(a, String(b, String::UTF16BE)); - CPPUNIT_ASSERT_EQUAL(a, String(c, String::UTF16LE)); - CPPUNIT_ASSERT_EQUAL(a, String(d, String::UTF16)); - } - - // this test is expected to print "TagLib: String::copyFromUTF16() - - // Invalid UTF16 string." on the console 3 times - void testUTF16DecodeInvalidBOM() { - ByteVector b(" ", 1); - ByteVector c(" ", 2); - ByteVector d(" \0f\0o\0o", 8); - CPPUNIT_ASSERT_EQUAL(String(), String(b, String::UTF16)); - CPPUNIT_ASSERT_EQUAL(String(), String(c, String::UTF16)); - CPPUNIT_ASSERT_EQUAL(String(), String(d, String::UTF16)); - } - - void testUTF16DecodeEmptyWithBOM() { - ByteVector a("\377\376", 2); - ByteVector b("\376\377", 2); - CPPUNIT_ASSERT_EQUAL(String(), String(a, String::UTF16)); - CPPUNIT_ASSERT_EQUAL(String(), String(b, String::UTF16)); - } - - void testSurrogatePair() { - // Make sure that a surrogate pair is converted into single UTF-8 char - // and vice versa. - - const ByteVector v1("\xff\xfe\x42\xd8\xb7\xdf\xce\x91\x4b\x5c"); - const ByteVector v2("\xf0\xa0\xae\xb7\xe9\x87\x8e\xe5\xb1\x8b"); - - const String s1(v1, String::UTF16); - CPPUNIT_ASSERT_EQUAL(s1.data(String::UTF8), v2); - - const String s2(v2, String::UTF8); - CPPUNIT_ASSERT_EQUAL(s2.data(String::UTF16), v1); - - const ByteVector v3("\xfe\xff\xd8\x01\x30\x42"); - CPPUNIT_ASSERT(String(v3, String::UTF16).data(String::UTF8).isEmpty()); - - const ByteVector v4("\xfe\xff\x30\x42\xdc\x01"); - CPPUNIT_ASSERT(String(v4, String::UTF16).data(String::UTF8).isEmpty()); - - const ByteVector v5("\xfe\xff\xdc\x01\xd8\x01"); - CPPUNIT_ASSERT(String(v5, String::UTF16).data(String::UTF8).isEmpty()); - } - - void testAppendStringDetach() { - String a("abc"); - String b = a; - String c = a; - - b += "def"; - c += L"def"; - - CPPUNIT_ASSERT_EQUAL(String("abc"), a); - CPPUNIT_ASSERT_EQUAL(String("abcdef"), b); - CPPUNIT_ASSERT_EQUAL(String("abcdef"), c); - } - - void testAppendCharDetach() { - String a("abc"); - String b = a; - String c = a; - - b += 'd'; - c += L'd'; - - CPPUNIT_ASSERT_EQUAL(String("abc"), a); - CPPUNIT_ASSERT_EQUAL(String("abcd"), b); - CPPUNIT_ASSERT_EQUAL(String("abcd"), c); - } - - void testRfind() { - CPPUNIT_ASSERT_EQUAL(String::npos(), String("foo.bar").rfind(".", 0)); - CPPUNIT_ASSERT_EQUAL(String::npos(), String("foo.bar").rfind(".", 1)); - CPPUNIT_ASSERT_EQUAL(String::npos(), String("foo.bar").rfind(".", 2)); - CPPUNIT_ASSERT_EQUAL((size_t)3, String("foo.bar").rfind(".", 3)); - CPPUNIT_ASSERT_EQUAL((size_t)3, String("foo.bar").rfind(".", 4)); - CPPUNIT_ASSERT_EQUAL((size_t)3, String("foo.bar").rfind(".", 5)); - CPPUNIT_ASSERT_EQUAL((size_t)3, String("foo.bar").rfind(".", 6)); - CPPUNIT_ASSERT_EQUAL((size_t)3, String("foo.bar").rfind(".", 7)); - CPPUNIT_ASSERT_EQUAL((size_t)3, String("foo.bar").rfind(".")); - } - - void testToInt() { - bool ok; - CPPUNIT_ASSERT_EQUAL(String("123").toInt(&ok), 123); - CPPUNIT_ASSERT_EQUAL(ok, true); - - CPPUNIT_ASSERT_EQUAL(String("-123").toInt(&ok), -123); - CPPUNIT_ASSERT_EQUAL(ok, true); - - CPPUNIT_ASSERT_EQUAL(String("123aa").toInt(&ok), 123); - CPPUNIT_ASSERT_EQUAL(ok, false); - - CPPUNIT_ASSERT_EQUAL(String("-123aa").toInt(&ok), -123); - CPPUNIT_ASSERT_EQUAL(ok, false); - - CPPUNIT_ASSERT_EQUAL(String("abc").toInt(&ok), 0); - CPPUNIT_ASSERT_EQUAL(ok, false); - - CPPUNIT_ASSERT_EQUAL(String("1x").toInt(&ok), 1); - CPPUNIT_ASSERT_EQUAL(ok, false); - - CPPUNIT_ASSERT_EQUAL(String("").toInt(&ok), 0); - CPPUNIT_ASSERT_EQUAL(ok, false); - - CPPUNIT_ASSERT_EQUAL(String("-").toInt(&ok), 0); - CPPUNIT_ASSERT_EQUAL(ok, false); - - String("9999999999").toInt(&ok); - CPPUNIT_ASSERT_EQUAL(ok, false); - - CPPUNIT_ASSERT_EQUAL(String("0000").toInt(), 0); - CPPUNIT_ASSERT_EQUAL(String("0001").toInt(), 1); - - String("2147483648").toInt(&ok); - CPPUNIT_ASSERT_EQUAL(ok, false); - - String("-2147483649").toInt(&ok); - CPPUNIT_ASSERT_EQUAL(ok, false); - } - - void testFromInt() { - CPPUNIT_ASSERT_EQUAL(String::number(0), String("0")); - CPPUNIT_ASSERT_EQUAL(String::number(12345678), String("12345678")); - CPPUNIT_ASSERT_EQUAL(String::number(-12345678), String("-12345678")); - } - - void testSubstr() { - CPPUNIT_ASSERT_EQUAL(String("01"), String("0123456").substr(0, 2)); - CPPUNIT_ASSERT_EQUAL(String("12"), String("0123456").substr(1, 2)); - CPPUNIT_ASSERT_EQUAL(String("123456"), String("0123456").substr(1, 200)); - CPPUNIT_ASSERT_EQUAL(String("0123456"), String("0123456").substr(0, 7)); - CPPUNIT_ASSERT_EQUAL(String("0123456"), String("0123456").substr(0, 200)); - } - - void testNewline() { - ByteVector cr("abc\x0dxyz", 7); - ByteVector lf("abc\x0axyz", 7); - ByteVector crlf("abc\x0d\x0axyz", 8); - - CPPUNIT_ASSERT_EQUAL((size_t)7, String(cr).size()); - CPPUNIT_ASSERT_EQUAL((size_t)7, String(lf).size()); - CPPUNIT_ASSERT_EQUAL((size_t)8, String(crlf).size()); - - CPPUNIT_ASSERT_EQUAL(L'\x0d', String(cr)[3]); - CPPUNIT_ASSERT_EQUAL(L'\x0a', String(lf)[3]); - CPPUNIT_ASSERT_EQUAL(L'\x0d', String(crlf)[3]); - CPPUNIT_ASSERT_EQUAL(L'\x0a', String(crlf)[4]); - } - - void testUpper() { - String s1 = "tagLIB 012 strING"; - String s2 = s1.upper(); - CPPUNIT_ASSERT_EQUAL(String("tagLIB 012 strING"), s1); - CPPUNIT_ASSERT_EQUAL(String("TAGLIB 012 STRING"), s2); - } - - void testEncodeNonLatin1() { - const String jpn(L"\u65E5\u672C\u8A9E"); - CPPUNIT_ASSERT_EQUAL(ByteVector("\xE5\x2C\x9E"), jpn.data(String::Latin1)); - CPPUNIT_ASSERT_EQUAL(ByteVector("\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E"), jpn.data(String::UTF8)); - CPPUNIT_ASSERT_EQUAL(ByteVector("\xFF\xFE\xE5\x65\x2C\x67\x9E\x8A"), jpn.data(String::UTF16)); - CPPUNIT_ASSERT_EQUAL(ByteVector("\xE5\x65\x2C\x67\x9E\x8A"), jpn.data(String::UTF16LE)); - CPPUNIT_ASSERT_EQUAL(ByteVector("\x65\xE5\x67\x2C\x8A\x9E"), jpn.data(String::UTF16BE)); - CPPUNIT_ASSERT_EQUAL(std::string("\xE5\x2C\x9E"), jpn.to8Bit(false)); - CPPUNIT_ASSERT_EQUAL(std::string("\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E"), jpn.to8Bit(true)); - } - - void testEncodeEmpty() { - const String empty; - CPPUNIT_ASSERT(empty.data(String::Latin1).isEmpty()); - CPPUNIT_ASSERT(empty.data(String::UTF8).isEmpty()); - CPPUNIT_ASSERT_EQUAL(ByteVector("\xFF\xFE"), empty.data(String::UTF16)); - CPPUNIT_ASSERT(empty.data(String::UTF16LE).isEmpty()); - CPPUNIT_ASSERT(empty.data(String::UTF16BE).isEmpty()); - CPPUNIT_ASSERT(empty.to8Bit(false).empty()); - CPPUNIT_ASSERT(empty.to8Bit(true).empty()); - } - - void testEncodeNonBMP() { - const ByteVector a("\xFF\xFE\x3C\xD8\x50\xDD\x40\xD8\xF5\xDC\x3C\xD8\x00\xDE", 14); - const ByteVector b("\xF0\x9F\x85\x90\xF0\xA0\x83\xB5\xF0\x9F\x88\x80"); - CPPUNIT_ASSERT_EQUAL(b, String(a, String::UTF16).data(String::UTF8)); - } - - void testIterator() { - String s1 = "taglib string"; - String s2 = s1; - - String::Iterator it1 = s1.begin(); - String::Iterator it2 = s2.begin(); - - CPPUNIT_ASSERT_EQUAL(L't', *it1); - CPPUNIT_ASSERT_EQUAL(L't', *it2); - - std::advance(it1, 4); - std::advance(it2, 4); - *it2 = L'I'; - CPPUNIT_ASSERT_EQUAL(L'i', *it1); - CPPUNIT_ASSERT_EQUAL(L'I', *it2); - } - - void testInvalidUTF8() { - CPPUNIT_ASSERT_EQUAL(String("/"), String(ByteVector("\x2F"), String::UTF8)); - CPPUNIT_ASSERT(String(ByteVector("\xC0\xAF"), String::UTF8).isEmpty()); - CPPUNIT_ASSERT(String(ByteVector("\xE0\x80\xAF"), String::UTF8).isEmpty()); - CPPUNIT_ASSERT(String(ByteVector("\xF0\x80\x80\xAF"), String::UTF8).isEmpty()); - - CPPUNIT_ASSERT(String(ByteVector("\xF8\x80\x80\x80\x80"), String::UTF8).isEmpty()); - CPPUNIT_ASSERT(String(ByteVector("\xFC\x80\x80\x80\x80\x80"), String::UTF8).isEmpty()); - - CPPUNIT_ASSERT(String(ByteVector("\xC2"), String::UTF8).isEmpty()); - CPPUNIT_ASSERT(String(ByteVector("\xE0\x80"), String::UTF8).isEmpty()); - CPPUNIT_ASSERT(String(ByteVector("\xF0\x80\x80"), String::UTF8).isEmpty()); - CPPUNIT_ASSERT(String(ByteVector("\xF8\x80\x80\x80"), String::UTF8).isEmpty()); - CPPUNIT_ASSERT(String(ByteVector("\xFC\x80\x80\x80\x80"), String::UTF8).isEmpty()); - - CPPUNIT_ASSERT(String('\x80', String::UTF8).isEmpty()); - - CPPUNIT_ASSERT(String(ByteVector("\xED\xA0\x80\xED\xB0\x80"), String::UTF8).isEmpty()); - CPPUNIT_ASSERT(String(ByteVector("\xED\xB0\x80\xED\xA0\x80"), String::UTF8).isEmpty()); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestString); diff --git a/tests/taglib/test_synchdata.cpp b/tests/taglib/test_synchdata.cpp deleted file mode 100644 index e4bea0da..00000000 --- a/tests/taglib/test_synchdata.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/*************************************************************************** - copyright : (C) 2007 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 - -#include "id3v2synchdata.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestID3v2SynchData : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestID3v2SynchData); - CPPUNIT_TEST(test1); - CPPUNIT_TEST(test2); - CPPUNIT_TEST(test3); - CPPUNIT_TEST(testToUIntBroken); - CPPUNIT_TEST(testToUIntBrokenAndTooLarge); - CPPUNIT_TEST(testDecode1); - CPPUNIT_TEST(testDecode2); - CPPUNIT_TEST(testDecode3); - CPPUNIT_TEST(testDecode4); - CPPUNIT_TEST_SUITE_END(); - - public: - void test1() { - char data[] = { 0, 0, 0, 127 }; - ByteVector v(data, 4); - - CPPUNIT_ASSERT_EQUAL(ID3v2::SynchData::toUInt(v), (unsigned int)127); - CPPUNIT_ASSERT_EQUAL(ID3v2::SynchData::fromUInt(127), v); - } - - void test2() { - char data[] = { 0, 0, 1, 0 }; - ByteVector v(data, 4); - - CPPUNIT_ASSERT_EQUAL(ID3v2::SynchData::toUInt(v), (unsigned int)128); - CPPUNIT_ASSERT_EQUAL(ID3v2::SynchData::fromUInt(128), v); - } - - void test3() { - char data[] = { 0, 0, 1, 1 }; - ByteVector v(data, 4); - - CPPUNIT_ASSERT_EQUAL(ID3v2::SynchData::toUInt(v), (unsigned int)129); - CPPUNIT_ASSERT_EQUAL(ID3v2::SynchData::fromUInt(129), v); - } - - void testToUIntBroken() { - char data[] = { 0, 0, 0, (char)-1 }; - char data2[] = { 0, 0, (char)-1, (char)-1 }; - - CPPUNIT_ASSERT_EQUAL((unsigned int)255, ID3v2::SynchData::toUInt(ByteVector(data, 4))); - CPPUNIT_ASSERT_EQUAL((unsigned int)65535, ID3v2::SynchData::toUInt(ByteVector(data2, 4))); - } - - void testToUIntBrokenAndTooLarge() { - char data[] = { 0, 0, 0, (char)-1, 0 }; - ByteVector v(data, 5); - - CPPUNIT_ASSERT_EQUAL((unsigned int)255, ID3v2::SynchData::toUInt(v)); - } - - void testDecode1() { - ByteVector a("\xff\x00\x00", 3); - a = ID3v2::SynchData::decode(a); - CPPUNIT_ASSERT_EQUAL((size_t)2, a.size()); - CPPUNIT_ASSERT_EQUAL(ByteVector("\xff\x00", 2), a); - } - - void testDecode2() { - ByteVector a("\xff\x44", 2); - a = ID3v2::SynchData::decode(a); - CPPUNIT_ASSERT_EQUAL((size_t)2, a.size()); - CPPUNIT_ASSERT_EQUAL(ByteVector("\xff\x44", 2), a); - } - - void testDecode3() { - ByteVector a("\xff\xff\x00", 3); - a = ID3v2::SynchData::decode(a); - CPPUNIT_ASSERT_EQUAL((size_t)2, a.size()); - CPPUNIT_ASSERT_EQUAL(ByteVector("\xff\xff", 2), a); - } - - void testDecode4() { - ByteVector a("\xff\xff\xff", 3); - a = ID3v2::SynchData::decode(a); - CPPUNIT_ASSERT_EQUAL((size_t)3, a.size()); - CPPUNIT_ASSERT_EQUAL(ByteVector("\xff\xff\xff", 3), a); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestID3v2SynchData); diff --git a/tests/taglib/test_trueaudio.cpp b/tests/taglib/test_trueaudio.cpp deleted file mode 100644 index bfb01c70..00000000 --- a/tests/taglib/test_trueaudio.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/*************************************************************************** - copyright : (C) 2007 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "id3v1tag.h" -#include "id3v2tag.h" -#include "tpropertymap.h" -#include "trueaudiofile.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestTrueAudio : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestTrueAudio); - CPPUNIT_TEST(testReadPropertiesWithoutID3v2); - CPPUNIT_TEST(testReadPropertiesWithTags); - CPPUNIT_TEST(testStripAndProperties); - CPPUNIT_TEST(testRepeatedSave); - CPPUNIT_TEST_SUITE_END(); - - public: - void testReadPropertiesWithoutID3v2() { - TrueAudio::File f(TEST_FILE_PATH_C("empty.tta")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(3685, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(173, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(162496U, f.audioProperties()->sampleFrames()); - CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->ttaVersion()); - } - - void testReadPropertiesWithTags() { - TrueAudio::File f(TEST_FILE_PATH_C("tagged.tta")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(3685, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(173, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(162496U, f.audioProperties()->sampleFrames()); - CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->ttaVersion()); - } - - void testStripAndProperties() { - ScopedFileCopy copy("empty", ".tta"); - - { - TrueAudio::File f(copy.fileName().c_str()); - f.ID3v2Tag(true)->setTitle("ID3v2"); - f.ID3v1Tag(true)->setTitle("ID3v1"); - f.save(); - } - { - TrueAudio::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT_EQUAL(String("ID3v2"), f.properties()["TITLE"].front()); - f.strip(TrueAudio::File::ID3v2); - CPPUNIT_ASSERT_EQUAL(String("ID3v1"), f.properties()["TITLE"].front()); - f.strip(TrueAudio::File::ID3v1); - CPPUNIT_ASSERT(f.properties().isEmpty()); - } - } - - void testRepeatedSave() { - ScopedFileCopy copy("empty", ".tta"); - - { - TrueAudio::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(!f.hasID3v2Tag()); - CPPUNIT_ASSERT(!f.hasID3v1Tag()); - - f.ID3v2Tag(true)->setTitle("01234 56789 ABCDE FGHIJ"); - f.save(); - - f.ID3v2Tag()->setTitle("0"); - f.save(); - - f.ID3v1Tag(true)->setTitle("01234 56789 ABCDE FGHIJ"); - f.ID3v2Tag()->setTitle("01234 56789 ABCDE FGHIJ 01234 56789 ABCDE FGHIJ 01234 56789"); - f.save(); - } - { - TrueAudio::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(f.hasID3v2Tag()); - CPPUNIT_ASSERT(f.hasID3v1Tag()); - } - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestTrueAudio); diff --git a/tests/taglib/test_wav.cpp b/tests/taglib/test_wav.cpp deleted file mode 100644 index 8e232a25..00000000 --- a/tests/taglib/test_wav.cpp +++ /dev/null @@ -1,295 +0,0 @@ -/*************************************************************************** - copyright : (C) 2010 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "id3v2tag.h" -#include "infotag.h" -#include "tbytevectorlist.h" -#include "tpropertymap.h" -#include "wavfile.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestWAV : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestWAV); - CPPUNIT_TEST(testPCMProperties); - CPPUNIT_TEST(testALAWProperties); - CPPUNIT_TEST(testFloatProperties); - CPPUNIT_TEST(testZeroSizeDataChunk); - CPPUNIT_TEST(testID3v2Tag); - CPPUNIT_TEST(testSaveID3v23); - CPPUNIT_TEST(testInfoTag); - CPPUNIT_TEST(testStripTags); - CPPUNIT_TEST(testDuplicateTags); - CPPUNIT_TEST(testFuzzedFile1); - CPPUNIT_TEST(testFuzzedFile2); - CPPUNIT_TEST(testStripAndProperties); - CPPUNIT_TEST(testPCMWithFactChunk); - CPPUNIT_TEST_SUITE_END(); - - public: - void testPCMProperties() { - RIFF::WAV::File f(TEST_FILE_PATH_C("empty.wav")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(3675, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(32, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(1000, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(3675U, f.audioProperties()->sampleFrames()); - CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->format()); - } - - void testALAWProperties() { - RIFF::WAV::File f(TEST_FILE_PATH_C("alaw.wav")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(3550, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(128, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(8000, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(8, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(28400U, f.audioProperties()->sampleFrames()); - CPPUNIT_ASSERT_EQUAL(6, f.audioProperties()->format()); - } - - void testFloatProperties() { - RIFF::WAV::File f(TEST_FILE_PATH_C("float64.wav")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(97, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(5645, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(64, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(4281U, f.audioProperties()->sampleFrames()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->format()); - } - - void testZeroSizeDataChunk() { - RIFF::WAV::File f(TEST_FILE_PATH_C("zero-size-chunk.wav")); - CPPUNIT_ASSERT(!f.isValid()); - } - - void testID3v2Tag() { - ScopedFileCopy copy("empty", ".wav"); - string filename = copy.fileName(); - - { - RIFF::WAV::File f(filename.c_str()); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT(!f.hasID3v2Tag()); - - f.ID3v2Tag()->setTitle(L"Title"); - f.ID3v2Tag()->setArtist(L"Artist"); - f.save(); - CPPUNIT_ASSERT(f.hasID3v2Tag()); - } - { - RIFF::WAV::File f(filename.c_str()); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT(f.hasID3v2Tag()); - CPPUNIT_ASSERT_EQUAL(String(L"Title"), f.ID3v2Tag()->title()); - CPPUNIT_ASSERT_EQUAL(String(L"Artist"), f.ID3v2Tag()->artist()); - - f.ID3v2Tag()->setTitle(L""); - f.ID3v2Tag()->setArtist(L""); - f.save(); - CPPUNIT_ASSERT(!f.hasID3v2Tag()); - } - { - RIFF::WAV::File f(filename.c_str()); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT(!f.hasID3v2Tag()); - CPPUNIT_ASSERT_EQUAL(String(L""), f.ID3v2Tag()->title()); - CPPUNIT_ASSERT_EQUAL(String(L""), f.ID3v2Tag()->artist()); - } - } - - void testSaveID3v23() { - ScopedFileCopy copy("empty", ".wav"); - string newname = copy.fileName(); - - String xxx = ByteVector(254, 'X'); - { - RIFF::WAV::File f(newname.c_str()); - CPPUNIT_ASSERT_EQUAL(false, f.hasID3v2Tag()); - - f.tag()->setTitle(xxx); - f.tag()->setArtist("Artist A"); - f.save(RIFF::WAV::File::AllTags, File::StripOthers, ID3v2::v3); - CPPUNIT_ASSERT_EQUAL(true, f.hasID3v2Tag()); - } - { - RIFF::WAV::File f2(newname.c_str()); - CPPUNIT_ASSERT_EQUAL((unsigned int)3, f2.ID3v2Tag()->header()->majorVersion()); - CPPUNIT_ASSERT_EQUAL(String("Artist A"), f2.tag()->artist()); - CPPUNIT_ASSERT_EQUAL(xxx, f2.tag()->title()); - } - } - - void testInfoTag() { - ScopedFileCopy copy("empty", ".wav"); - string filename = copy.fileName(); - - { - RIFF::WAV::File f(filename.c_str()); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT(!f.hasInfoTag()); - - f.InfoTag()->setTitle(L"Title"); - f.InfoTag()->setArtist(L"Artist"); - f.save(); - CPPUNIT_ASSERT(f.hasInfoTag()); - } - { - RIFF::WAV::File f(filename.c_str()); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT(f.hasInfoTag()); - CPPUNIT_ASSERT_EQUAL(String(L"Title"), f.InfoTag()->title()); - CPPUNIT_ASSERT_EQUAL(String(L"Artist"), f.InfoTag()->artist()); - - f.InfoTag()->setTitle(L""); - f.InfoTag()->setArtist(L""); - f.save(); - CPPUNIT_ASSERT(!f.hasInfoTag()); - } - - { - RIFF::WAV::File f(filename.c_str()); - CPPUNIT_ASSERT(f.isValid()); - CPPUNIT_ASSERT(!f.hasInfoTag()); - CPPUNIT_ASSERT_EQUAL(String(L""), f.InfoTag()->title()); - CPPUNIT_ASSERT_EQUAL(String(L""), f.InfoTag()->artist()); - } - } - - void testStripTags() { - ScopedFileCopy copy("empty", ".wav"); - string filename = copy.fileName(); - - { - RIFF::WAV::File f(filename.c_str()); - f.ID3v2Tag()->setTitle("test title"); - f.InfoTag()->setTitle("test title"); - f.save(); - } - { - RIFF::WAV::File f(filename.c_str()); - CPPUNIT_ASSERT(f.hasID3v2Tag()); - CPPUNIT_ASSERT(f.hasInfoTag()); - f.save(RIFF::WAV::File::ID3v2, File::StripOthers); - } - { - RIFF::WAV::File f(filename.c_str()); - CPPUNIT_ASSERT(f.hasID3v2Tag()); - CPPUNIT_ASSERT(!f.hasInfoTag()); - f.ID3v2Tag()->setTitle("test title"); - f.InfoTag()->setTitle("test title"); - f.save(); - } - { - RIFF::WAV::File f(filename.c_str()); - CPPUNIT_ASSERT(f.hasID3v2Tag()); - CPPUNIT_ASSERT(f.hasInfoTag()); - f.save(RIFF::WAV::File::Info, File::StripOthers); - } - { - RIFF::WAV::File f(filename.c_str()); - CPPUNIT_ASSERT(!f.hasID3v2Tag()); - CPPUNIT_ASSERT(f.hasInfoTag()); - } - } - - void testDuplicateTags() { - ScopedFileCopy copy("duplicate_tags", ".wav"); - - RIFF::WAV::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT_EQUAL(17052LL, f.length()); - - // duplicate_tags.wav has duplicate ID3v2/INFO tags. - // title() returns "Title2" if can't skip the second tag. - - CPPUNIT_ASSERT(f.hasID3v2Tag()); - CPPUNIT_ASSERT_EQUAL(String("Title1"), f.ID3v2Tag()->title()); - - CPPUNIT_ASSERT(f.hasInfoTag()); - CPPUNIT_ASSERT_EQUAL(String("Title1"), f.InfoTag()->title()); - - f.save(); - CPPUNIT_ASSERT_EQUAL(15898LL, f.length()); - CPPUNIT_ASSERT_EQUAL(-1LL, f.find("Title2")); - } - - void testFuzzedFile1() { - RIFF::WAV::File f1(TEST_FILE_PATH_C("infloop.wav")); - CPPUNIT_ASSERT(!f1.isValid()); - } - - void testFuzzedFile2() { - RIFF::WAV::File f2(TEST_FILE_PATH_C("segfault.wav")); - CPPUNIT_ASSERT(f2.isValid()); - } - - void testStripAndProperties() { - ScopedFileCopy copy("empty", ".wav"); - - { - RIFF::WAV::File f(copy.fileName().c_str()); - f.ID3v2Tag()->setTitle("ID3v2"); - f.InfoTag()->setTitle("INFO"); - f.save(); - } - { - RIFF::WAV::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT_EQUAL(String("ID3v2"), f.properties()["TITLE"].front()); - f.strip(RIFF::WAV::File::ID3v2); - CPPUNIT_ASSERT_EQUAL(String("INFO"), f.properties()["TITLE"].front()); - f.strip(RIFF::WAV::File::Info); - CPPUNIT_ASSERT(f.properties().isEmpty()); - } - } - - void testPCMWithFactChunk() { - RIFF::WAV::File f(TEST_FILE_PATH_C("pcm_with_fact_chunk.wav")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(3675, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(32, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(1000, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(3675U, f.audioProperties()->sampleFrames()); - CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->format()); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestWAV); diff --git a/tests/taglib/test_wavpack.cpp b/tests/taglib/test_wavpack.cpp deleted file mode 100644 index cbe7c49b..00000000 --- a/tests/taglib/test_wavpack.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/*************************************************************************** - copyright : (C) 2010 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 -#include -#include - -#include "apetag.h" -#include "id3v1tag.h" -#include "tbytevectorlist.h" -#include "tpropertymap.h" -#include "wavpackfile.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestWavPack : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestWavPack); - CPPUNIT_TEST(testNoLengthProperties); - CPPUNIT_TEST(testMultiChannelProperties); - CPPUNIT_TEST(testTaggedProperties); - CPPUNIT_TEST(testFuzzedFile); - CPPUNIT_TEST(testStripAndProperties); - CPPUNIT_TEST(testRepeatedSave); - CPPUNIT_TEST_SUITE_END(); - - public: - void testNoLengthProperties() { - WavPack::File f(TEST_FILE_PATH_C("no_length.wv")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(3705, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(true, f.audioProperties()->isLossless()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(163392U, f.audioProperties()->sampleFrames()); - CPPUNIT_ASSERT_EQUAL(1031, f.audioProperties()->version()); - } - - void testMultiChannelProperties() { - WavPack::File f(TEST_FILE_PATH_C("four_channels.wv")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(3833, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(112, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(4, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isLossless()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(169031U, f.audioProperties()->sampleFrames()); - CPPUNIT_ASSERT_EQUAL(1031, f.audioProperties()->version()); - } - - void testTaggedProperties() { - WavPack::File f(TEST_FILE_PATH_C("tagged.wv")); - CPPUNIT_ASSERT(f.audioProperties()); - CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(3550, f.audioProperties()->lengthInMilliseconds()); - CPPUNIT_ASSERT_EQUAL(172, f.audioProperties()->bitrate()); - CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); - CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isLossless()); - CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(156556U, f.audioProperties()->sampleFrames()); - CPPUNIT_ASSERT_EQUAL(1031, f.audioProperties()->version()); - } - - void testFuzzedFile() { - WavPack::File f(TEST_FILE_PATH_C("infloop.wv")); - CPPUNIT_ASSERT(f.isValid()); - } - - void testStripAndProperties() { - ScopedFileCopy copy("click", ".wv"); - - { - WavPack::File f(copy.fileName().c_str()); - f.APETag(true)->setTitle("APE"); - f.ID3v1Tag(true)->setTitle("ID3v1"); - f.save(); - } - { - WavPack::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT_EQUAL(String("APE"), f.properties()["TITLE"].front()); - f.strip(WavPack::File::APE); - CPPUNIT_ASSERT_EQUAL(String("ID3v1"), f.properties()["TITLE"].front()); - f.strip(WavPack::File::ID3v1); - CPPUNIT_ASSERT(f.properties().isEmpty()); - } - } - - void testRepeatedSave() { - ScopedFileCopy copy("click", ".wv"); - - { - WavPack::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(!f.hasAPETag()); - CPPUNIT_ASSERT(!f.hasID3v1Tag()); - - f.APETag(true)->setTitle("01234 56789 ABCDE FGHIJ"); - f.save(); - - f.APETag()->setTitle("0"); - f.save(); - - f.ID3v1Tag(true)->setTitle("01234 56789 ABCDE FGHIJ"); - f.APETag()->setTitle("01234 56789 ABCDE FGHIJ 01234 56789 ABCDE FGHIJ 01234 56789"); - f.save(); - } - { - WavPack::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(f.hasAPETag()); - CPPUNIT_ASSERT(f.hasID3v1Tag()); - } - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestWavPack); diff --git a/tests/taglib/test_xiphcomment.cpp b/tests/taglib/test_xiphcomment.cpp deleted file mode 100644 index 921478c2..00000000 --- a/tests/taglib/test_xiphcomment.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/*************************************************************************** - copyright : (C) 2009 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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 - -#include -#include -#include "xiphcomment.h" -#include "vorbisfile.h" -#include "tpropertymap.h" -#include "tdebug.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -class TestXiphComment : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestXiphComment); - CPPUNIT_TEST(testYear); - CPPUNIT_TEST(testSetYear); - CPPUNIT_TEST(testTrack); - CPPUNIT_TEST(testSetTrack); - CPPUNIT_TEST(testInvalidKeys1); - CPPUNIT_TEST(testInvalidKeys2); - CPPUNIT_TEST(testClearComment); - CPPUNIT_TEST(testRemoveFields); - CPPUNIT_TEST(testPicture); - CPPUNIT_TEST(testLowercaseFields); - CPPUNIT_TEST_SUITE_END(); - - public: - void testYear() { - Ogg::XiphComment cmt; - CPPUNIT_ASSERT_EQUAL((unsigned int)0, cmt.year()); - cmt.addField("YEAR", "2009"); - CPPUNIT_ASSERT_EQUAL((unsigned int)2009, cmt.year()); - cmt.addField("DATE", "2008"); - CPPUNIT_ASSERT_EQUAL((unsigned int)2008, cmt.year()); - } - - void testSetYear() { - Ogg::XiphComment cmt; - cmt.addField("YEAR", "2009"); - cmt.addField("DATE", "2008"); - cmt.setYear(1995); - CPPUNIT_ASSERT(cmt.fieldListMap()["YEAR"].isEmpty()); - CPPUNIT_ASSERT_EQUAL(String("1995"), cmt.fieldListMap()["DATE"].front()); - } - - void testTrack() { - Ogg::XiphComment cmt; - CPPUNIT_ASSERT_EQUAL((unsigned int)0, cmt.track()); - cmt.addField("TRACKNUM", "7"); - CPPUNIT_ASSERT_EQUAL((unsigned int)7, cmt.track()); - cmt.addField("TRACKNUMBER", "8"); - CPPUNIT_ASSERT_EQUAL((unsigned int)8, cmt.track()); - } - - void testSetTrack() { - Ogg::XiphComment cmt; - cmt.addField("TRACKNUM", "7"); - cmt.addField("TRACKNUMBER", "8"); - cmt.setTrack(3); - CPPUNIT_ASSERT(cmt.fieldListMap()["TRACKNUM"].isEmpty()); - CPPUNIT_ASSERT_EQUAL(String("3"), cmt.fieldListMap()["TRACKNUMBER"].front()); - } - - void testInvalidKeys1() { - PropertyMap map; - map[""] = String("invalid key: empty string"); - map["A=B"] = String("invalid key: contains '='"); - map["A~B"] = String("invalid key: contains '~'"); - map["A\x7F" - "B"] = String("invalid key: contains '\x7F'"); - map[L"A\x3456" - "B"] = String("invalid key: Unicode"); - - Ogg::XiphComment cmt; - PropertyMap unsuccessful = cmt.setProperties(map); - CPPUNIT_ASSERT_EQUAL((size_t)5, unsuccessful.size()); - CPPUNIT_ASSERT(cmt.properties().isEmpty()); - } - - void testInvalidKeys2() { - Ogg::XiphComment cmt; - cmt.addField("", "invalid key: empty string"); - cmt.addField("A=B", "invalid key: contains '='"); - cmt.addField("A~B", "invalid key: contains '~'"); - cmt.addField("A\x7F" - "B", - "invalid key: contains '\x7F'"); - cmt.addField(L"A\x3456" - "B", - "invalid key: Unicode"); - CPPUNIT_ASSERT_EQUAL(0U, cmt.fieldCount()); - } - - void testClearComment() { - ScopedFileCopy copy("empty", ".ogg"); - - { - Ogg::Vorbis::File f(copy.fileName().c_str()); - f.tag()->addField("COMMENT", "Comment1"); - f.save(); - } - { - Ogg::Vorbis::File f(copy.fileName().c_str()); - f.tag()->setComment(""); - CPPUNIT_ASSERT_EQUAL(String(""), f.tag()->comment()); - } - } - - void testRemoveFields() { - Ogg::Vorbis::File f(TEST_FILE_PATH_C("empty.ogg")); - f.tag()->addField("title", "Title1"); - f.tag()->addField("Title", "Title1", false); - f.tag()->addField("titlE", "Title2", false); - f.tag()->addField("TITLE", "Title3", false); - f.tag()->addField("artist", "Artist1"); - f.tag()->addField("ARTIST", "Artist2", false); - CPPUNIT_ASSERT_EQUAL(String("Title1 Title1 Title2 Title3"), f.tag()->title()); - CPPUNIT_ASSERT_EQUAL(String("Artist1 Artist2"), f.tag()->artist()); - - f.tag()->removeFields("title", "Title1"); - CPPUNIT_ASSERT_EQUAL(String("Title2 Title3"), f.tag()->title()); - CPPUNIT_ASSERT_EQUAL(String("Artist1 Artist2"), f.tag()->artist()); - - f.tag()->removeFields("Artist"); - CPPUNIT_ASSERT_EQUAL(String("Title2 Title3"), f.tag()->title()); - CPPUNIT_ASSERT(f.tag()->artist().isEmpty()); - - f.tag()->removeAllFields(); - CPPUNIT_ASSERT(f.tag()->title().isEmpty()); - CPPUNIT_ASSERT(f.tag()->artist().isEmpty()); - CPPUNIT_ASSERT_EQUAL(String("Xiph.Org libVorbis I 20050304"), f.tag()->vendorID()); - } - - void testPicture() { - ScopedFileCopy copy("empty", ".ogg"); - string newname = copy.fileName(); - - { - Ogg::Vorbis::File f(newname.c_str()); - FLAC::Picture *newpic = new FLAC::Picture(); - newpic->setType(FLAC::Picture::BackCover); - newpic->setWidth(5); - newpic->setHeight(6); - newpic->setColorDepth(16); - newpic->setNumColors(7); - newpic->setMimeType("image/jpeg"); - newpic->setDescription("new image"); - newpic->setData("JPEG data"); - f.tag()->addPicture(newpic); - f.save(); - } - { - Ogg::Vorbis::File f(newname.c_str()); - List lst = f.tag()->pictureList(); - CPPUNIT_ASSERT_EQUAL((size_t)1, lst.size()); - CPPUNIT_ASSERT_EQUAL((int)5, lst[0]->width()); - CPPUNIT_ASSERT_EQUAL((int)6, lst[0]->height()); - CPPUNIT_ASSERT_EQUAL((int)16, lst[0]->colorDepth()); - CPPUNIT_ASSERT_EQUAL((int)7, lst[0]->numColors()); - CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), lst[0]->mimeType()); - CPPUNIT_ASSERT_EQUAL(String("new image"), lst[0]->description()); - CPPUNIT_ASSERT_EQUAL(ByteVector("JPEG data"), lst[0]->data()); - } - } - - void testLowercaseFields() { - const ScopedFileCopy copy("lowercase-fields", ".ogg"); - { - Ogg::Vorbis::File f(copy.fileName().c_str()); - List lst = f.tag()->pictureList(); - CPPUNIT_ASSERT_EQUAL(String("TEST TITLE"), f.tag()->title()); - CPPUNIT_ASSERT_EQUAL(String("TEST ARTIST"), f.tag()->artist()); - CPPUNIT_ASSERT_EQUAL((size_t)1, lst.size()); - f.save(); - } - { - Ogg::Vorbis::File f(copy.fileName().c_str()); - CPPUNIT_ASSERT(f.find("METADATA_BLOCK_PICTURE") > 0); - } - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestXiphComment); diff --git a/tests/taglib/test_xm.cpp b/tests/taglib/test_xm.cpp deleted file mode 100644 index 657d7e32..00000000 --- a/tests/taglib/test_xm.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/*************************************************************************** - copyright : (C) 2011 by Mathias Panzenböck - email : grosser.meister.morti@gmx.net - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * - * 02110-1301 USA * - * * - * Alternatively, this file is available under the Mozilla Public * - * License Version 1.1. You may obtain a copy of the License at * - * http://www.mozilla.org/MPL/ * - ***************************************************************************/ - -#include - -#include "xmfile.h" -#include "utils.h" - -using namespace std; -using namespace Strawberry_TagLib::TagLib; - -static const String titleBefore("title of song"); -static const String titleAfter("changed title"); - -static const String trackerNameBefore("MilkyTracker "); -static const String trackerNameAfter("TagLib"); - -static const String commentBefore( - "Instrument names\n" - "are abused as\n" - "comments in\n" - "module file formats.\n" - "-+-+-+-+-+-+-+-+-+-+-+\n" - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" - "\n\n\n" - "Sample\n" - "names\n" - "are sometimes\n" - "also abused as\n" - "comments."); - -static const String newCommentShort( - "Instrument names\n" - "are abused as\n" - "comments in\n" - "module file formats.\n" - "======================\n" - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" - "\n\n\n" - "Sample names\n" - "are sometimes\n" - "also abused as\n" - "comments."); - -static const String newCommentLong( - "Instrument names\n" - "are abused as\n" - "comments in\n" - "module file formats.\n" - "======================\n" - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" - "\n\n\n" - "Sample names\n" - "are sometimes\n" - "also abused as\n" - "comments.\n" - "\n\n\n\n\n\n\n" - "TEST"); - -static const String commentAfter( - "Instrument names\n" - "are abused as\n" - "comments in\n" - "module file formats.\n" - "======================\n" - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" - "\n\n\n" - "Sample names\n" - "are sometimes\n" - "also abused as\n" - "comments.\n"); - -class TestXM : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TestXM); - CPPUNIT_TEST(testReadTags); - CPPUNIT_TEST(testReadStrippedTags); - CPPUNIT_TEST(testWriteTagsShort); - CPPUNIT_TEST(testWriteTagsLong); - CPPUNIT_TEST_SUITE_END(); - - public: - void testReadTags() { - testRead(TEST_FILE_PATH_C("test.xm"), titleBefore, - commentBefore, trackerNameBefore); - } - - void testReadStrippedTags() { - XM::File file(TEST_FILE_PATH_C("stripped.xm")); - CPPUNIT_ASSERT(file.isValid()); - - XM::AudioProperties *p = file.audioProperties(); - Mod::Tag *t = file.tag(); - - CPPUNIT_ASSERT(nullptr != p); - CPPUNIT_ASSERT(nullptr != t); - - CPPUNIT_ASSERT_EQUAL(0, p->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(0, p->bitrate()); - CPPUNIT_ASSERT_EQUAL(0, p->sampleRate()); - CPPUNIT_ASSERT_EQUAL(8, p->channels()); - CPPUNIT_ASSERT_EQUAL((unsigned short)1, p->lengthInPatterns()); - CPPUNIT_ASSERT_EQUAL((unsigned short)0, p->version()); - CPPUNIT_ASSERT_EQUAL((unsigned short)0, p->restartPosition()); - CPPUNIT_ASSERT_EQUAL((unsigned short)1, p->patternCount()); - CPPUNIT_ASSERT_EQUAL((unsigned short)0, p->instrumentCount()); - CPPUNIT_ASSERT_EQUAL((unsigned short)1, p->flags()); - CPPUNIT_ASSERT_EQUAL((unsigned short)6, p->tempo()); - CPPUNIT_ASSERT_EQUAL((unsigned short)125, p->bpmSpeed()); - CPPUNIT_ASSERT_EQUAL(titleBefore, t->title()); - CPPUNIT_ASSERT_EQUAL(String(), t->artist()); - CPPUNIT_ASSERT_EQUAL(String(), t->album()); - CPPUNIT_ASSERT_EQUAL(String(), t->comment()); - CPPUNIT_ASSERT_EQUAL(String(), t->genre()); - CPPUNIT_ASSERT_EQUAL(0U, t->year()); - CPPUNIT_ASSERT_EQUAL(0U, t->track()); - CPPUNIT_ASSERT_EQUAL(String(), t->trackerName()); - } - - void testWriteTagsShort() { - testWriteTags(newCommentShort); - } - - void testWriteTagsLong() { - testWriteTags(newCommentLong); - } - - private: - void testRead(FileName fileName, const String &title, - const String &comment, const String &trackerName) { - XM::File file(fileName); - - CPPUNIT_ASSERT(file.isValid()); - - XM::AudioProperties *p = file.audioProperties(); - Mod::Tag *t = file.tag(); - - CPPUNIT_ASSERT(nullptr != p); - CPPUNIT_ASSERT(nullptr != t); - - CPPUNIT_ASSERT_EQUAL(0, p->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(0, p->bitrate()); - CPPUNIT_ASSERT_EQUAL(0, p->sampleRate()); - CPPUNIT_ASSERT_EQUAL(8, p->channels()); - CPPUNIT_ASSERT_EQUAL((unsigned short)1, p->lengthInPatterns()); - CPPUNIT_ASSERT_EQUAL((unsigned short)260, p->version()); - CPPUNIT_ASSERT_EQUAL((unsigned short)0, p->restartPosition()); - CPPUNIT_ASSERT_EQUAL((unsigned short)1, p->patternCount()); - CPPUNIT_ASSERT_EQUAL((unsigned short)128, p->instrumentCount()); - CPPUNIT_ASSERT_EQUAL((unsigned short)1, p->flags()); - CPPUNIT_ASSERT_EQUAL((unsigned short)6, p->tempo()); - CPPUNIT_ASSERT_EQUAL((unsigned short)125, p->bpmSpeed()); - CPPUNIT_ASSERT_EQUAL(title, t->title()); - CPPUNIT_ASSERT_EQUAL(String(), t->artist()); - CPPUNIT_ASSERT_EQUAL(String(), t->album()); - CPPUNIT_ASSERT_EQUAL(comment, t->comment()); - CPPUNIT_ASSERT_EQUAL(String(), t->genre()); - CPPUNIT_ASSERT_EQUAL(0U, t->year()); - CPPUNIT_ASSERT_EQUAL(0U, t->track()); - CPPUNIT_ASSERT_EQUAL(trackerName, t->trackerName()); - } - - void testWriteTags(const String &comment) { - ScopedFileCopy copy("test", ".xm"); - { - XM::File file(copy.fileName().c_str()); - CPPUNIT_ASSERT(file.tag() != nullptr); - file.tag()->setTitle(titleAfter); - file.tag()->setComment(comment); - file.tag()->setTrackerName(trackerNameAfter); - CPPUNIT_ASSERT(file.save()); - } - testRead(copy.fileName().c_str(), titleAfter, - commentAfter, trackerNameAfter); - CPPUNIT_ASSERT(fileEqual( - copy.fileName(), - TEST_FILE_PATH_C("changed.xm"))); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(TestXM); diff --git a/tests/taglib/utils.h b/tests/taglib/utils.h deleted file mode 100644 index abdca6cc..00000000 --- a/tests/taglib/utils.h +++ /dev/null @@ -1,140 +0,0 @@ -/*************************************************************************** - copyright : (C) 2007 by Lukas Lalinsky - email : lukas@oxygene.sk - ***************************************************************************/ - -/*************************************************************************** - * 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/ * - ***************************************************************************/ - -#pragma once - -#include "taglib-config.h" - -#ifdef _WIN32 -# include -#else -# include -# include -# include -# include -#endif -#include -#include -#include -#include - -using namespace std; - -inline string testFilePath(const string &filename) { - return string(TESTS_DIR "data/") + filename; -} - -#define TEST_FILE_PATH_C(f) testFilePath(f).c_str() - -inline string copyFile(const string &filename, const string &ext) { - char testFileName[1024]; - -#ifdef _WIN32 - char tempDir[MAX_PATH + 1]; - GetTempPathA(sizeof(tempDir), tempDir); - wsprintfA(testFileName, "%s\\taglib-test%s", tempDir, ext.c_str()); -#else - snprintf(testFileName, sizeof(testFileName), "/%s/taglib-test%s", P_tmpdir, ext.c_str()); -#endif - - string sourceFileName = testFilePath(filename) + ext; - ifstream source(sourceFileName.c_str(), std::ios::binary); - ofstream destination(testFileName, std::ios::binary); - destination << source.rdbuf(); - return string(testFileName); -} - -inline void deleteFile(const string &filename) { - remove(filename.c_str()); -} - -inline bool fileEqual(const string &filename1, const string &filename2) { - char buf1[BUFSIZ]; - char buf2[BUFSIZ]; - - ifstream stream1(filename1.c_str(), ios_base::in | ios_base::binary); - ifstream stream2(filename2.c_str(), ios_base::in | ios_base::binary); - - if (!stream1 && !stream2) return true; - if (!stream1 || !stream2) return false; - - for (;;) { - stream1.read(buf1, BUFSIZ); - stream2.read(buf2, BUFSIZ); - - streamsize n1 = stream1.gcount(); - streamsize n2 = stream2.gcount(); - - if (n1 != n2) return false; - - if (n1 == 0) break; - - if (memcmp(buf1, buf2, static_cast(n1)) != 0) return false; - } - - return stream1.good() == stream2.good(); -} - -#ifdef TAGLIB_STRING_H - -namespace Strawberry_TagLib { -namespace TagLib { - -inline String longText(size_t length, bool random = false) { - const wchar_t chars[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"; - - std::wstring text(length, L'X'); - - if (random) { - for (size_t i = 0; i < length; ++i) - text[i] = chars[rand() % 53]; - } - - return String(text); -} -} // namespace TagLib -} // namespace Strawberry_TagLib - -#endif - -class ScopedFileCopy { - public: - ScopedFileCopy(const string &filename, const string &ext, bool deleteFile = true) : m_deleteFile(deleteFile), - m_filename(copyFile(filename, ext)) { - } - - ~ScopedFileCopy() { - if (m_deleteFile) - deleteFile(m_filename); - } - - string fileName() const { - return m_filename; - } - - private: - const bool m_deleteFile; - const string m_filename; -};