MP3-Tag-1.13/0000700000000000000000000000000011417072072007423 5ustar MP3-Tag-1.13/cddb.tm0000700000000000000000000000227410056037424010671 0ustar # xmcd # # Track frame offsets: # # 182 # 20835 # 31897 # 42677 # 73390 # 82750 # 105560 # 119460 # 122405 # 231667 # 235075 # # Disc length: 1945 seconds # # Revision: 0 # Submitted via: Not submitted yet DISCID=c401590b DGENRE= DTITLE=Crumb Piece / Ancient Voices TTITLE0=Ancient Voices of Children - Ia. El Nino Busca Su Voz TTITLE1=Ancient Voices of Children - Ib. Dances Of The Ancient Earth TTITLE2=Ancient Voices of Children - II. Me He Perdido Muchas Veces Por El Mar TTITLE3=Ancient Voices of Children - III. Dance Of The Sacred Life Cycle + IVa TTITLE4=Ancient Voices of Children - IVb. Ghost Dance TTITLE5=Ancient Voices of Children - V. Se Ha Iienado De Luces Mi Corazon De S TTITLE6=Makrokosmos III - I. Nocturnal Sounds (The Awakening) TTITLE7=Piece of Crumb / Makrokosmos III - II. Wanderer-Fantasy TTITLE8=Makrokosmos III - III. The Advent TTITLE9=Makrokosmos III - IV. Myth TTITLE10=Makrokosmos III - V. Music Of The Starry Night DYEAR= EXTD= ID3Y: 1234; Fake entry. ID3G: 28 EXTT0=comment1 EXTT1=comment2 EXTT2=comment3 EXTT3=comment4 EXTT4=comment5 EXTT5=comment6 EXTT6=comment7 EXTT7=comment8; Recorded on 2001-10-23--30,2002-02-28 EXTT8=comment9 EXTT9=comment10 EXTT10=comment11 PLAYORDER= MP3-Tag-1.13/cddb.tmp0000700000000000000000000000217710011766654011060 0ustar # xmcd # # Track frame offsets: # # 182 # 20835 # 31897 # 42677 # 73390 # 82750 # 105560 # 119460 # 122405 # 231667 # 235075 # # Disc length: 1945 seconds # # Revision: 0 # Submitted via: Not submitted yet DISCID=c401590b DGENRE=A special genre DTITLE=Crumb Piece / Ancient Voices TTITLE0=Ancient Voices of Children - Ia. El Nino Busca Su Voz TTITLE1=Ancient Voices of Children - Ib. Dances Of The Ancient Earth TTITLE2=Ancient Voices of Children - II. Me He Perdido Muchas Veces Por El Mar TTITLE3=Ancient Voices of Children - III. Dance Of The Sacred Life Cycle + IVa TTITLE4=Ancient Voices of Children - IVb. Ghost Dance TTITLE5=Ancient Voices of Children - V. Se Ha Iienado De Luces Mi Corazon De S TTITLE6=Makrokosmos III - I. Nocturnal Sounds (The Awakening) TTITLE7=Makrokosmos III - II. Wanderer-Fantasy TTITLE8=Makrokosmos III - III. The Advent TTITLE9=Makrokosmos III - IV. Myth TTITLE10=Makrokosmos III - V. Music Of The Starry Night DYEAR=1234 EXTD=Fake entry EXTT0=comment1 EXTT1=comment2 EXTT2=comment3 EXTT3=comment4 EXTT4=comment5 EXTT5=comment6 EXTT6=comment7 EXTT7=comment8 EXTT8=comment9 EXTT9=comment10 EXTT10=comment11 PLAYORDER= MP3-Tag-1.13/Changes0000700000000000000000000014430511416757566010751 0ustar Release Name: 1.13 ==================== Apply patches from Ian: Makefile.PL,data_pod.PL: Fix path of ID3v2-Data.pod. extract-y.pl: Fix #! path to perl. data_pod.PL, Cue.pm, File.pm, LastResort.pm, ID3v1.pm, ID3v2.pm, Tag.pm: Fix pod ID3v2.pm: fix frame numbering documentation (http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=3D523804) Many: preceeded/explicitely striking again ;-) ID3v2: add section BUGS with explanation of how we treat v2.4 frames. extract MP3 start bytes even if ID3v2 tag is present. recognize MP3 MIME type by start bytes Tag.pm: document mime_Pretype(), mime_type(). New optional arguments to mime_Pretype(), mime_type(). ID3v2 genre tag with value "Blues" or "(0)" could not be set (thanks to Eric Benson for analysis). ID3v2_Data.pod: rename s/-/_/ - cygwin buggy, fails manifying - this was BS, the problem is elsewhere mp3info2: state another time that COMM(eng)[lyrics] is a non-standard place for lyrics... Release Name: 1.12 ==================== Music_Normalize_Fields: new short names in RUS table. Eliminate one record which caused infinite recursion (vas i iva). ID3v2: replace order of encode() and func_back() when writing frames (e.g., would break unicode genre with space in it). "very low-level" docs had get_frame() mixed up with get_frames() (thanks to Brendan Byrd) new_with_parent() inserts header into parent on failure. optional argument to _Data_to_MIME to return false on unknown. Tag.pm: better docs for interpolate(). misprints in example of update_tags(). When truncating inside interpolation, and the value is a number round it, not truncate. Move old meaning of %{aR} to %{a3} (truncation to 3 places). %{aR} is not rounded now; one may truncate as in %.5{aR} to restrict width. New method mime_Pretype(); new interpolation escape %{mP}. Optional argument to _Data_to_MIME() to return false on unknown. For (image) mime types we can handle ourselves, do it. default return for mime_type() is MP3 (in absense of support modules). Misprint in _Data_to_MIME() made it useless. New method aspect_ratio_inverted(). New escape %{aI} for aspect_ratio_inverted(). Allow for rounding of duration before formatting T[=>m,?H:,{mL}]. File.pm: fix mismatched C<> in POD. mp3info2: -F disk_n=DISKNUMBER supported, new configuration variable frames_write_creates_dirs (default 0) to create intermediate directories baz/boz on -F "foo > baz/boz/bar". Use %{mP} instead of `Audio' in the -x output. Make -x=output of MP3-related data conditional on its presence (still wrong since MP3::Info can return such data for non-MP3 files too XXX) typeset_audio_dir: new option -r Make haveLyrics/haveAPIC symbols take less space Macroize pre/postSectionSkip, make it and \columnsep font-size dependent Slightly more docs in the generated code on grid/non-grid typesetting Slightly more docs Release Name: 1.11 ==================== ImageExifTool.pm: new handler (a very minimal implementation). Tag.pm: support for ImageExifTool.pm (problems: deleting frames is not noticed by ExifTool, so the field can be "revived" by update_tags(), so we disable id3 in ExifTool [XXX: must be made conditional on the list of handlers]; genre-->None massaged to '') 'NumColors' => 'Use BitDepth'??? rename aspect() to aspect_ratio() (undocumented) new function aspect_ratio3() rounds to 3 decimals in the fractional part map %{aR} to aspect_ratio3() [instead of aspect()] new method bit_depth() (for images; exact semantic [8 or 24?!] not clear) (undocumented) new interpolation %{bD} for bit_depth new method _duration() (for very few formats only???) (undocumented) caching MP3::Info data avoid extraneous calls Make duration-fetch methods try _duration() too. mpeg_layer_roman(), frames_padded_YN(), is_copyrighted_YN() now may return '' if MP3::Info fails. Allow for the rest of 1-char escapes NvLrqQSmsCpouMH to be conditioned and %=X-ed. //o-ptimise RExes. ID3v2.pm: could modify config('default_language'). mp3info2: make MP3-specific fields optional emit %wx%h for images and video (and append bit_depth if present) Release Name: 1.10 ==================== Bump the version number to 1.10 to sync with mp3info2. Tag.pm: allow %=n to match against 1 if %n is 01. new methods extension_is(), can_write(), can_write_or_die(), die_cant_write(), writable_by_extension(). new methods (require Image::Size) width(), height(), img_type(), mime_type(), aspect(). (undocumented) new interpolation escapes %w %h %{iT} %{mT} %{aR} for these methods. new configuration variables: `is_writable' defaults to writable_by_extension (older versions behaved as if it is 1); `writable_extensions' to mp3 mp2 id3 tag ogg mpg mpeg mp4 aiff flac ape ram mpc update_tags() checks for can_write(). cddb.tmp was omitted from distribution (==> test failure). ID3v2.pm: the code to treat compressed fields was completely busted. (Still not tested a lot...) Now always write UTF-16 as `UTF-16LE with BOM' to cowtow to M$'s bugs (must disable: triggers unsync...; set $ENV{MP3TAG_USE_UTF_16LE} to TRUE to enable). Music_Fields.pm: could give `uninitialized' warning in presence of one-word person names in the tables. ImageSize.pm: new handler. mp3info2: -d was busted by the repeated-option logic. allow a repeated -u to force update. typeset_audio_dir: if one of the arguments was 0, -e could fail. Decrease default min-gap before [year] from 0.75em to 0.65em (looks better). Release Name: 1.00 ==================== Bump all the version numbers to 1.00 (except Normalize::Text::Music_Fields). Move code to a ./lib subdirectory. (Allows auto-install of transliterate_cp1251.) Music_Normalize_Fields.pm: renamed to Normalize::Text::Music_Fields.pm Now check environment variable MUSIC_FIELDS_PATH and/or ~/.music_fields/*. name_for_field_normalization method is used to choose "composer name". CDDB_File.pm: minor changes to the docs. Cue.pm: new sub-module. Tag.pm: allow access to Cue, and customization of the corresponding encoding. Allow conditional interpolation on user variables (check emptiness) %{U1:\n}. New configuration variable and method `name_for_field_normalization'. typeset_audio_dir: Major simplification of user interface, better docs. Can generate short+long-type listings itself (two-pass so far...). More consistent ornaments in the generated files. Auto-generate the file *_titles.tex (used as the title of the listing). Move common code to file *_common.tex. New options -N, -P, -F. Document option -L. Directory list defaults to ".". Indicator of presence of APIC frames. Lyrics symbols explanation emitted correctly. Allow duplication of comment-like info and a part of the main record title (emphasizes this part of the title). Document known possible problems during typesetting. mp3info2: Since Normalize::Text::Music_Fields is always loadable now, one uses a different trigger sequence to load this module (environment variable and a directory ~/.music_fields are checked). With -e N, N in 1..3, do not translate the non-option ARGV (could conflict with interation with filesystems). New bit 0x4 of -e N used to influence translation of non-option ARGV. When @ARGV is re-encoded: translate back to 8-bit whatever is possible. ID3v2.pm: when ordering frames in generated tags, sort APIC frames at the end (since the simplest reason for unsync-related bugs in other programs is misparse of APIC frames, and it is the *consequent* frames which are busted, no non-APIC frames would be affected. (This is applicable in the case when other frames contain Latin-1 info, so the chance of unsync-sequences there is low...) Release Name: 0.9714 ==================== Tag.pm: misprint in POD. Add new variable MP3TAG_SKIP_WEAKEN for debugging (does not help with 5.005). Make documentation of encoding settings more clear. New configuration variable `extra_config_keys'. `n' and `I' in interpolation flags were broken. interpolate_with_flags(): take into account that ->track may be not a number, as "1/17". ID3v2.pm: do not prepend "(C) " to empty TCOP fields. mp3info2: -F "WHOLE > file" would not write if the given tags are not present. would skip -F "WHOLE ?< file" if corresponding tags were present (as opposed to fields). Long description for TDRC was missing. Long description was wrongly handled in __frames_as_printable(). New configuration variable `empty-F-deletes'. The default now is to remove the frame if value -F is empty. Take into account `decode_encoding_files' when processing -F "FRAME < file"; transliterate_win1251.pm: was not updated during move to a subdirectory. typeset_audio_dir: better docs. Typeset track number without second part, and allowing customization too. CDDB_File.pm: take into account that ->track may be not a number, as "1/17". Release Name: 0.9713 ==================== mp3info2: -e was broken by support for duplicated-options. Better structuring of documentation. Release Name: 0.9712 ==================== Tag.pm: _massage_genres(): 'num' is required if number is wished; new semantic with 'prefer_num'. Make methods *_set() return TRUE. In dump of frames, put explanation of frames purposes. Improve the docs. ID3v2.pm: add `func_back' to frame's field descriptor. add 're2b' to frame's field descriptor. When adding TCOM frame, massage it back to the v2.3 conventions; now one can use the same values for setting frame as returned when reading frame, even in presence of multiple genres. Likewise for TCOP frames (via `re2b'). Repeated APIC frames were not expanded in get_frame_descriptors(). Would not support "fake" MP3 objects. Allow setting of "filtered for human" fields (except TMED) via add_frame(), using both raw and human-readable forms of the corresponding argument. When splitting an argument for add_frame, split from the end for POPM - they have numbers at end, so it is not ambiguous. AENC and COMR frames had invalid format (trailing 0 omitted from URL). Dump of frames: put explanation of frames purposes, align stuff vertically. Optional string fields were treated with warnings... Improve the docs. ID3v1.pm: reading v1 genre immediately after setting might return numeric value. Test suite for changes (except encoding stuff and as_bin()). Makefile.PL was clean()ing a shipped file. Release Name: 0.9711 ==================== ID3v1.pm: last winamp genre was not supported; logic of checking fitting to ID3v1 was not accepting "from"ified tags; add more WinAmp genres (from table in MP3::Info). Avoid an elusive Perl (parser?) bug with can't use string ("") as an ARRAY ref while "strict refs" in use at MP3/Tag/ID3v1.pm line 220. Use the leading part of track numbers of the form NUMBER/WHATEVER. Tag.pm: logic of massaging genre was not accepting "from"ified tags. id3v23_unsync id3v23_unsync_size_w - better docs. Minor improvements to docs. New methods: "simplified" title_set(), artist_set(), album_set(), year_set(), comment_set(), track_set(), genre_set(); _Data_to_MIME(), copy_id3v2_frames(), track1(), track2(), track0(), disk1(), disk2(), disk_alphanum(). Prefer frame_select_by_descr_simpler() to frame_select_by_descr_simple() in select_id3v2_frame_by_descr(); so we can: remove special-casing of _Data in _interpolate(), and the first ("supported") frame of the given type can be interpolated via, e.g., "COMM", "APIC". remove setting $MP3::Info::try_harder: may loop infinitely... New interpolation: %{ID3v2-size}, %{ID3v2-pad}, %{ID3v2-stripped}, %{ID3v2-modified} %{out_frames/QQPRE//QQPOST}. Allow condition in conditional interpolation to be a 2-char (n1 etc) escape. Allow select_id3v2_frames() etc to write frames which require multiple arguments to add_frame(), either by adding extra arguments, or by joining arguments together with ";". [Will catch less errors, but is more flexible.] _massage_genres(): extra arg must now be 'num' if number is wished; new semantic with 'prefer_num'. ID3v2.pm: misprint of getconfig1, and in printout of supported versions. Add some E to POD; better doc of APIC in frame_select(). _Data_to_MIME() documented. Use "Unknown... Error?" as string representation of unknown pictype in APIC. Do extra close/open before writing ID3v2, so that the size of ID3v1 tag is taken into account when calculating the "best" padding. New methods: frame_select_by_descr_simpler(), add_raw_frame() [undocumented], copy_frames(), add_frame_split(). Supports optional fields in frames for read/write (missing fields not set by get_frame() etc, and should be omitted in call to add_frame). allow multiple values in "write" flavor of frame_select() (as in add_frame()); moreover, since it calls add_frame_select(), allow joining fields into one with separator=";". [Will catch less errors, but is more flexible.] For APIC/COMR frames, allow the numeric "type" argument to add_frame() to be specified both as a byte, and as a stringified number (no conflict possible). audio_rename: move dependent module transliterate_win1251.pm to Encode:: namespace. Change the default rename recipe to use disk number, and pad track number to width of the maximal track number (if known). mp3info2: With -x, print "ID3v1: present" if applicable, and ID3v2 size(s). Documentation was using I(fimb) where I{fimbB} was needed. Move id3v2_frames_autofill() before -p and -x. Do not autofill unless -u is given, or other modifications are done to ID3v2. Uses foo_set() to process -a/-t/-l/-y/-c/-n/-g options, so no "sane value" check is done any more... Also disallows "asyncroneous on-demand order of setting" of these fields (but it was hardly documentable anyway)... Document order of processing the options. New semantics "FRAME_spec < FILE", "FRAME_spec ?< FILE", "FRAME_spec > FILE" for -F option (including full-tag operations). Documentation on the usage strategy: Escalation of complexity. Options -d/-F/-C/-P may be repeated (old nonsense of gluing together via triplication is still supported, but deprecated). -x may be repeated (dumps frames). File.pm: better wording in an error message. Release Name: 0.9710 ==================== Some minimal changes to make test suite pass "better" on 5.005 (Music_Translate_Fields does not work; %F is not guarantied to get full name; some proxying problems remains [leaks ==> files remain open]; t/update_tags.t works well under debugger, not otherwise...). Tag.pm ID3v2.pm ParseData.pm mp3info2: support Language with parentheses, as in "Cover (back)" (when used as "Picture type"). Document id3v2_frame_descriptors(). New interpolation token: %{frames}, %{frames/SEPARATOR}. Allow %{ID3v2: ... } and ID3v1 as conditionals. Parsing of %{composer} etc inside false conditionals was wrong. ID3v1.pm: is not confused by multipart genres (such as described by id3v2). ID3v2.pm: extra $raw-ness for get_frame() (return array [=ordered hash], or array of values, or same with ignoring `decode_encoding_v2'); get_frame() handles MCDI as a "simple frame"; document get_frame_descr() and get_frame_descriptors(); new argument $raw_ok for as_bin() to return original tag if unmodified; new method fix_frames_encoding() to make encoding standard-conformant; fix_frames_encoding() autocalled (controled by `decode_encoding_v2', `id3v2_fix_encoding_on_write' and `id3v2_fix_encoding_on_edit'); `decode_encoding_v2' may be disabled per tag via frame TXXX[trusted_encoding0_v2] (in turn, this feature may be disabled by `ignore_trusted_encoding0_v2'); frame TXXX[trusted_encoding0_v2] may be auto-added by fix_frames_encoding() (controlled by `id3v2_set_trusted_encoding0'); special-field-readers now check their calling API (to allow future extensions); reading TCON normalizes genres, skips duplicates, and handles 0-separated genres (as mandated by v2.4) [thanks to neil verplank for the prototype implementation]; reading/writing of v2.4 tags controlled by `prohibit_v24'/`write_v24'; misprint in "Media (e.g. label side of CD)" of APIC corrected (backward compatibility value preserved); APIC frames handled by frame_select() (and derivatives); uses "Picture Type" instead of "Language", and auto-calculates "MIME type" for TIFF/JPEG/GIF/PNG; likewise for get_frame_descr(). Tag.pm: simplified method get_config1() for accessing one-element configuration arrays; conversion milleseconds ==> seconds was wrong (!!!) when NVs are 80-bit; new configuration variables `id3v2_recalculate', `ignore_trusted_encoding0_v2', `id3v2_set_trusted_encoding0', `id3v2_fix_encoding_on_write', `id3v2_fix_encoding_on_edit', `prohibit_v24', `write_v24' with sane defaults. instead of MCDI frame, autofills TXXX[MCDI-fulltoc] (since MCDI is supposed to be the "short TOC", and I do not know how to reduce one to another, and know only how to easily read the "full TOC"; please advice if you know). [For best results, it is better to rename MCDI frame if it was autofilled with v0.9709: mp3info2 -u@P "mi/@{MCDI}/@{TXXX[MCDI-fulltoc]}///m//@{MCDI}" f.mp3 ] Due to these changes, the mess of fixing broken encodings is more or less resolved now (before, it was easy to read tags with broken encoding, but not edit it). Likewise, reading genre is much easier now (but setting multipart genre may still be some challenge). [If there are some problems with reading v2.4 tags, I would like to hear about them; likewise, I beg for code donation for better writing of v2.4 tags.] mp3info2: new option -N to switch off "smart-alec interface"; -F allows human-oriented identifiers: composer,text_by,orchestra,conductor will only call autoinfo() if needed (useful for copying ID3 frames, so that they are not modified before copying); better logic in choosing when to emit "Performer:" line. beef up docs. typeset_audio_dir: could try to print total time even without -T. audio_rename: pacify some warnings; better docs. New scripts in examples: fulltoc_2fake_cddb.pl dir_mp3_2fake_cddb.pl inf_2fake_cddb cddb2cddb Music_Translate_Fields.pm: renamed to Music_Normalize_Fields.pm; a tiny shim module with old name added; subroutines renamed from *translate* to *normalize*; shim subroutines with old names added; minimal docs added; new subroutine read_composer_file() [cut out from load_composer()]; new subroutine normalize_opus(); better normalization of opus when followed by variants of `No.4'; new subroutine _normalize_piece(): as normalize_piece(), but allows better control over (no) database lookup; better recognition of a key ("Piece; b#"); support for mail-header-format databases; support for shorted opus-number ids; support for creation of mail-header-format databases; minimal testing suite (builtin); other minor improvements. Composer files: converted to mail-header format; new files for Brahms, Dvorak, Schnittke, Shostakovich added. Moved to subdirectory `Music_Normalize_Fields'; should be relative to Music_Normalize_Fields.pm Release Name: 0.9709 ==================== extract-y.pl was reversing the "informativeness" test, thus one needed to redo Music_Translate_Fields-L_van_Beethoven.comp; add missing sonata names to the latter. Improve docs for ENVIRONMENT. New customization variable local_cfg_file, new method parse_cfg() (reads ~/.mp3tagprc). [not tested] New interpolation directives iC and iI to access cddb_id, cdindex_id. New methods cddb_id, cdindex_id, id3v2_frames_autofill. New configuration variable id3v2_frames_autofill. Support for filling MCDI frame. mp3info2: id3v2_frames_autofill() is called unless -d option is given. (Most probably the result is not propagated back to file unless -u option was given.) CDDB_File.pm: new method (and parse directive) cddb_id. Inf.pm: new methods (and parse directives) cddb_id, cdindex_id (cddb_id has the leading '0x' stripped). ID3v2.pm: new methods cddb_id, cdindex_id (read the corresponding TXXX frames). Splitting UCS-2 fields by short-\0 was wrong. Workaround for ITunes bug was not complete: only size calculation was conditional on a configuration variable, but not the actual unsyncronization... typeset_audio_dir-try.pl - new variant (untested). typeset_audio_dir was ignoring -n option. Extend matching of track number when parsing to 999 (not more to avoid confusion with years); such tracks are, apparently, written mod 256 into ID3v1 tag. Release Name: 0.9708 ======================= Allow character devices for files (so /dev/null now "works" mp3info2 -D -a beethoven -t "piano in F#" /dev/null will print the info deduced from per-composer databases). total_secs_trunc() was not documented. New methods total_hours(), leftover_secs_float(), leftover_secs_trunc(), leftover_mins(). Interpolated as %H, %{SML}, %{SL}, %{mL}. Interpolate leftover_msec() as %{ML}. New method format_time(). New interpolated %{T[format1,format2...]}. New class method new_fake(). 'update_length' config variable was not writable. New method id3v2_frame_descriptors(). XXXX Undocumented... ID3v1: support new option encoded_v1_fits (default NOT as it was). ID3v2: add_frame() was not documented to support encoding. New configuration variables default_language, default_descr_c. New functions get_frame_desc(), get_frame_descriptors(). Music_Translate_Fields: normalizes title from unambiguous short form (order significant, so "piano sonata" and "sonata for piano" still not synonimous). Allow #dup_opus_rex, #no_opus_no, #prev_short in composer table. New function normalize_file_lines(). Table of names for Beethoven expanded to include all the Op/WoO works; dates are extracted from 3 sources. Table for Gershwin. New file examples/Music_Translate_Fields-normalize.pl. mp3info2: processing of the first part of -E was completely wrong; only one option could have been \\-interpolated. New syntax -E +something to add to the defaults. New option -x (modelled after mp3info, plus frame descriptors). If file name is empty, will work with a fake tag object. Print (guessed) composer and orchestra fields too. audio_rename: increase the length of -s to 110 (len controlled by environment variable). Add .tag .mp3 to -e. Add documentation. New option -r. Now is installed. typeset_audio_dir: now explains the meaning of (L) and (S). Release Name: 0.9707 ======================= %M was not recognized. parsing for "%n" allows track numbers of the form 123/789 too; such track numbers are considered as "not fit for ID3v1". Document that set_id3v2_frame(), select_id3v2_frame() remove the old value(s). New method interpolate_with_flags(). Make ...::Parse use this method. New methods composer(), performer(), delete_tag(), _interpolate() in MP3::Tag. Allow customization via local configuration modules MP3::Tag::User MP3::Tag::Site MP3::Tag::Vendor. New interpolated escape %{I(FLAGS)VALUE}. New configuration variables id3v23_unsync, composer, performer, translate_composer, translate_performer. New test t/interpolate.t. Incompatible change: kill backslashitis in %-escapes: frame_select_by_descr() etc: no backslashitis whatsoever. interpolate(): no need to backwack all \{}[], only the innermost delimiter char, and only those backslashes which preseed the innermost delimiter. Likewise for parse(), _parse_rex_microinterpolate(). Need to change '%{y||<%\{COMM(rus,EN,#1,)[foo]\}>}' to '%{y||<%{COMM(rus,EN,#1,)[foo]}>}' '%%02t_Title: `%012.12t\'; %{TLAN} %{TLAN01: have %\{TLAN01\}} %{!TLAN02:, do not have TLAN02}' to '%%02t_Title: `%012.12t\'; %{TLAN} %{TLAN01: have %{TLAN01}} %{!TLAN02:, do not have TLAN02}' '%{!COMM(rus,EN,#1)[foo]:<%\{COMM(rus,EN,#1,)[foo]\}>}' to '%{!COMM(rus,EN,#1)[foo]:<%{COMM(rus,EN,#1,)[foo]}>}' '%{COMM(rus,EN,#1)[foo]||<%\{COMM(rus,EN,#1,)[foo]\}>}' to '%{COMM(rus,EN,#1)[foo]||<%{COMM(rus,EN,#1,)[foo]}>}' '%{COMM(rus,EN,#1,)[foo]||<%\{COMM(rus,EN,#1,)[foo]\}>}' to '%{COMM(rus,EN,#1,)[foo]||<%{COMM(rus,EN,#1,)[foo]}>}' '%{COMM01||<%\{COMM(rus,EN,#1,)[foo]\}>}' to '%{COMM01||<%{COMM(rus,EN,#1,)[foo]}>}' '%{TLEN||<%\{COMM(rus,EN,#1,)[foo]\}>}' to '%{TLEN||<%{COMM(rus,EN,#1,)[foo]}>}' $mp3->select_id3v2_frame_by_descr('TXXX[with[\]]', 'this is my TXXX[with[]]') to $mp3->select_id3v2_frame_by_descr('TXXX[with[]]', 'this is my TXXX[with[]]') In mp3info2: print $mp3->interpolate(exists $opt{p} ? $opt{p} : <interpolate(exists $opt{p} ? $opt{p} : < $_)} system qw(mp3info2 -a Beethoven -l), qq(piano sonata No. $_), $_}" examples/empty.mp3 got a tag accidentally. New file examples/empty_10sec.mp3 perl -we "print qq(\0) x (8000*2*10)" | lame -b 8 -r -s 8 -m m - empty_10sec.mp3 (For experiments with players which only show ID3 when playing.) If INSTALLSCRIPT is present in Makefile.PL options, LIB will not disable installation of scripts. Release Name: 0.9706 ======================= The weaken()ing code was failing since weaken() does not return TRUE. (Tested for no obvious memory leaks.) Allow for backwacked \[ \] \\ in descriptor of, e.g., COMM[descriptor]. New configuration options: encode_encoding_v1 decode_encoding_v1 decode_encoding_v2 decode_encoding_filename decode_encoding_inf decode_encoding_cddb_file decode_encoding_files encode_encoding_files (en/decode_encoding_files for ParseData; applicable to non-binary i/o). Initialized from corresponding environment variables Document environment variables. translate_title_collection etc configuration values were not settable (neither were they documented...). New methods of ID3v2: frame_select_by_descr(), frame_have_by_descr(), frame_list_by_descr(), frame_select_by_descr_simple(). New methods have_id3v2_frame_by_descr(), select_id3v2_frame_by_descr(), frame_translate(), frames_translate(), shorten_person() in MP3::Tag. interpolate() significantly simplified; new interpolation %{shP{NAME}}. New type of interpolation: frame ids connected by &. Slightly more readable documentation of interpolate() and parse_rex(). New configuration settings: person_frames, and translate_person (supported only by select_id3v2_frame_by_descr()). Music_Translate_Fields.pm: much better detection of non-canonical names (e.g., year removal), support for person names shortening. New method check_persons() for checking consistency of translation/shortening. Piecewise translation for multiple authors in a field (separated by ', ', '; ', or ' - '). Will translate title_collection and other CDDB fields. Music_Translate_Fields-rus.lst: better list of persons for bard repertoire and audiobooks; allow "fixing" wrong years too; handle "translated" and many more separators of multi-names; cursory support for cases (genitive); support for disabling translation (#keep). audio_rename.pl: transliterate cyrillic with unicode too. New options -d, -e, -F for the script mp3info2; brush up the docs. typeset_audio_dir: new option -c; new generated file *_backcover.tex; slightly better conversion of ASCII to TeX; use 'textcomp' (another incompatible LaTeX change...); new macros for skips about second-level headings; encapsulate \addOnRight; ornaments in *_text.tex and *_cdbooklet.tex; commented out support for title; begin merging together the code to generate different documents; consistent columnseprule; better positioning of hrule's; template to merge several lists into one document; document latex, dvips etc. audio_rename.pl: support unicode cyrillic. Release Name: 0.9705 ======================= New environment variables MP3TAG_DECODE_V1_DEFAULT, MP3TAG_DECODE_DEFAULT, MP3TAG_ENCODE_V1_DEFAULT, MP3TAG_ENCODE_DEFAULT (0 or encoding name). XXXX Not documented yet; does not cover all aspects (filenames, external files, Inf, CDDB files). v2 ENCODE (to illegal encodings) is not implemented yet. Default for MP3TAG_DECODE_UTF8 is 1 (used for encoding too). Encoding argument for v2->add_frame() is (finally!) implemented. Text field of TXXX, USER, USLT, OWNE were wrongly marked as not encoded. Setting lang=XXX via frame_select() or id3v2_select_frame() would result in lang=xxx. frame_select() returns the count of found frames if it is used for removal of frames. New parameter value 'hash' for get_frame(). New method frames_list() (variant of frame_select()). Sanity check for tag size parameter in ID3v2. Sanity check for wide characters in the prepared raw tag. New method ->size for ::File. More examples for update_tags(). Music_Translate_Fields: only the parsing logic remains; the data is moved into separate files (encoding supported) Music_Translate_Fields*.lst (should live alongside the .pm file). typeset_audio_dir: new option -e; unify the charset logic; add utf8. Add example/empty.mp3 (some MP3 applications do not accept "really empty" MP3s; this one is "almost empty"). Release Name: 0.9704 ======================= Document why using "encoding" and "TPE1" mindlessly is a bad idea. Cleaning up read support of v2.4. Better error message for version mismatch. Do not warn "not supported" any more if the footer is found (how could it be "supported" anyway, except for skipping, which is already done, [was wrong]?). (untested) Extended header parsed for v2.4 too. (untested) Footer was treated wrong... Apparently frame flags were extracted very wrong as well... (untested) Unknown frame flags are mapped to 'unknown_'. Rename 'padding_size' field to 'buggy_padding_size' to avoid confusion with ID3v2 padding (it is the padding after the tag [unrelated to the standard], not the padding inside tag). fits_tag(): return FALSE if data contains chars above 255. update_tags(): better docs, new argument $force2. mp3info2: in the standard report, "artist" was offset by one space. New option -2. README.shrink updated correspondingly. typeset_audio_dir: more comments on duplex output. eat_wav_mp3_header: new options -D for dry run, -M for finding .mp3 files. Release Name: 0.9703 ======================= typeset_audio_dir: would not work without -B switch Add generation of cdbooklet with -B. mp3info2: when reporting contents, print "Composer" and "Text" lines too. Without -I and -p, writes "Performer" line if TXXX{TPE1} is present. New option -r. method interpolate() could return '' when ID3v2 tag was missing even if the tag would affect only a part of the output. There was no way to inform the parser that it is OK if the parse data is coming from file, and the file does not exist. Add 'F' flag... Release Name: 0.9702 ======================= examples/type_mp3_dir.pl: new name typeset_audio_dir.pl; Fully document; Change the semantic of numbers in .top_heading files by 1; New option -n; Generate 1st level header for all "toplevel" dirs; TeXify ", ` and '; Add \hrule's for default toplevel headings. -@ was not working; Correct rounding of total_secs_int(). Truncated version in total_secs_int_trunc(). Rename total_millisecs_int() to total_millisecs_int_fetch(), likewise for total_secs_fetch(). New method total_millisecs_int() (with caching). Cache duration in ms. New escape: %M (total millisecs, integer). New confiration variable: update_length. Will update TLEN field if needed (governed by `update_length'). Alternation inside escapes implemented. New file README.shrink with recommendations on how to fix results of the bug with extra unmarked 0-padding. Remove trailing .pl from names of typeset_audio_dir, mp3info2. Install typeset_audio_dir, mp3info2 unless one of options -n or LIB= is given to Makefile.PL. Release Name: 0.9701 ======================= -P option of mp3info2 would ignore trailing empty parse patterns (not very restrictive, since to succeed, pattern should have a set-field, or match-exactly field; workaround: append @={U0}, and do not set U0). When updating ID3v2 tag without growing the file, the tag length would not include padding; thus the following update would not use the padding ==> tag size leak. New configuration options: id3v2_minpadding, id3v2_sizemult, id3v2_shrink, id3v2_mergepadding (the first two have new defaults: 128 and 512; was 2K and 4K). Close file for write of ID3v2 tag as early as possible. Release Name: 0.97 ======================= Better docs for add_frame(), set_id3v2_frame(), for configuration parameter id3v2_frame_empty_ok, parse_minmatch. frame_select() now returns undef if no frame is found; likewise for select_id3v2_frame(); better behaviour with very low number of arguments. When interpolating %{COMM[text]}, use lang='', not undef (otherwise answer is returned as hash). Interpolation would interpret second % in '%%02n' as fill character. Flag 'b' of ParseData would disable whitespace stripping from the data to match, but not from matches. ->get_config() would not work as class method. Ask MP3::Info to 'try_harder'; needed with very long ID3v2 headers... Decoding Unicode was not flexible enough: now configured by two environment variables: MP3TAG_DECODE_UNICODE (default 1) to enable decoding; the target of decoding is determined by MP3TAG_DECODE_UTF8: if 0 (default), decoded values are byte-encoded UTF-8 (every Perl character contains a byte of UTF-8 encoded string); otherwise it is a native Perl Unicode string. Language=XXX is the *official* designation of unknown language. Make dependency for ID3v2-Data.pod on Tag/ID3v2.pm. Warn in mp3info2 if -P is used without ParseData in autoinfo. Document example of attaching lyrics to audio file. CDDB_File could generate warnings for files without DGENRE field. type_mp3_dir: new macro \hourmark, exchange ' and '' for minutes/sec. Release Name: 0.96 ======================= A bug fix by Ben Bennett for 2.4 support. Having a genre of the form '(2345)Some new type' would emit a warning. UTF encoded fields are decoded by default. Set MP3TAG_ENCODING=0 in environment to disable. Add ID3v2-Data.pod to MANIFEST to avoid circular dependency (XXXX bug: it is generated using the installed version of ID3v2.pm, not the current version). Release Name: 0.95 ======================= After removing several of repeated frames, get_frame(s) could get confused. So could the code using get_frame(s). Now better documented, and fixed. New method ->is_modified() of ID3v2. New method ->as_bin() of ID3v1 and ID3v2. New methods ->is_id3v2_modified(), ->get_id3v2_frames(), ->select_id3v2_frame(), ->have_id3v2_frame(), ->get_id3v2_frame_ids() of MP3::Tag. Some `next's needed to be replaced by `return' in mp3info2 and audio_rename. mp3info2 would not decide that an edit is not needed if an ID3v2 frame was modified directly. Avoid re-parsing by ParseData more aggressively (10x speedup in one complicated real-life example). set_id3v2_frame() and update_tags() were assuming that get_tags() was already called. File->write() could lead to disaster with -l. !!!! New options 'b', 'B', 'o', 'O', 'D' for ParseData. With 'n', 'l' ParseData does not ignore empty trailing lines any more. New %-interpolations: %{ID3v1} for the tag as a whole, same for ID3v2. Be more strict during interpolation: do not recognize WORD1 as a valid short name of ID3v2 frame. Two new example scripts: mp3_total_time.pl, eat_wav_mp3_header.pl. Mention example scripts in documentation of MP3::Tag. Release Name: 0.94 ======================= The example of the "long" form of ID3v2-comment setting API has arguments swapped. New methods ->frame_select(), ->frame_have() of ID3v2. New methods ->pure_filetags() and ->update_tags() of MP3::Tag. New config variables id3v2_missing_fatal (it was as if it was defaulting to TRUE; now defaults to FALSE), parse_minmatch. New test files in t/: v2_comments.t update_tags.t parser.t New escape %{COMM(rus,eng,xxx,#4)[short_description]} (chooses a comment with "short_description" descriptor in the specified list of languages; #4 means 4th comment field, or COMM03; empty language means any language.) Works as a condition for conditional interpolation too, and as a target of parsing and ParseData. Similarly for other frames which have 'Language' and/or 'Description' fields. Some POD formatting fixed. Setting "extra" ID3v2 frames was not immediately visible (only after write and re-read from the file). COMM's short-description was not marked as 'encoded' (due to a misprint). UNCOMPATIBLE CHANGEs: COMM's frame short-description now sits on the key "Description", not "short". [If you want backward compatibility, check both keys.] get_frame() considers frames the only content of which is a URL as a "simple frame"; likewise for frame_select(), but it also considers frames with _Data only as "simple frames". Release Name: 0.93 ======================= More robust processing of "Recorded..." in CDDB_File. Better error message when parse expression has some `=' omitted. When parsing with nothing to set, would emit "Unsupported reg..." (regmatch would return 1 elt if no capturing parens present). When rewriting id3v2 tag, protect against $\ being set. Move test.pl to t/mp3tags.t, add t/set_v2.t Allow arbitrary escapes of the form %{ABCD} or %{ABCD03} in the method parse() etc; here ABCD is a 4-letter word. New config parameter id3v2_frame_empty_ok. It is possible to set individual id3v2 frames via ParseData; use %{TIT1} or some such. Setting to an empty string deletes the frame if config parameter id3v2_frame_empty_ok is true. Uses TCOM or TPE3 to find "artist" ($mp3->artist) too. New option -R (recurse into directories) for example scripts. New example script type_mp3_dir to produce a TeX file with contents of directories. (Put files `.content_comment' into a directory to include comments specific for the directory layout.) Connect parts of comment by \n if \n is already present there. `Recorded: 1980,1985,1992' was not recognized. Allow date only at start/end of the comment. Release Name: 0.92 ======================= Include a fix by Marco Moreno of Cwd::abspath() not working on files (on some architectures). Release Name: 0.91 ======================= CDDB_File was removing whitespace on the boundary of continuation fields. get_user() calls ParseData->parse() to populate the userdata if $ENV{MP3TAG_TEST_WEAKEN}, will not have a workaround against broken weakrefs (currently tests fail!) New %-escapes v L r q Q S m s C p o u (require MP3::Info module). New methods mpeg_version() mpeg_layer() mpeg_layer_roman() is_stereo() is_vbr() bitrate_kbps() frequency_Hz() frequency_kHz() size_bytes() total_secs() total_secs_int() total_mins() leftover_secs() leftover_msec() time_mm_ss() is_copyrighted() is_copyrighted_YN() frames_padded() frames_padded_YN() channel_mode_int() frames() frame_len() vbr_scale() (require MP3::Info module). The year() method of ID3v2 takes/returns timestamps in human-readable form; parse() recognizes the same formats for %y if option year_is_timestamp is TRUE (default). New option 'intact' to get_frame() of ID3v2. Support YEAR, ID3Y, ID3G in EXTD, EXTT* fields of CDDB_File. Support also an alternative syntax "Recorded"/"Recorded on"/"Recorded in"/ with the format of the date recognized by ID3v2::year(), or just a date field without a prefix. The declarations of the former form are stripped from the returned comment. New fields artist_collection, title_track, comment_collection, comment_track in CDDB_File and corresponding accessor methods in MP3::Tag, and corresponding escapes aC, tT, cC, cT for interpolate(). New configuration variables 'year_is_timestamp', 'comment_remove_date', 'translate_*' (for different values of *). New handler module LastResort; currently it uses artist_collection() as comment if comment is not otherwise defined. New example scripts audio_rename and mod/Music_Translate_Fields.pm. The example script mp3info2 will use Music_Translate_Fields.pm (if found) to postprocess the resulting tag fields via translate_*() methods. Do not load Compress::Zlib unless when processing compressed data. Release Name: 0.9 ======================= Revision history for Perl modules MP3::Tag, MP3::Tag::ID3v1, MP3::Tag::ID3v2, MP3::Tag::File, and for Perl program tk-tag Changes: * Bugfix for ID3v2.pm: - Added support for reading ID3v2.2 and ID3v2.4 tags (alpha stage!!) and converting them to ID3v2.3 - Bugfix for unsynchronization (thanks to Ian Beckwith) - Bugfix for number-handling in frames - Bugfix for frame compression - Set unsynchronization flag only, if unsynchronization was done and changed anything * Bugfix for ID3v1.pm: - genres didn't return the id if the genre name was passed as parameter, but returned the same name * Makefile.PL: - overriding the manifypods() function in MakeMaker to add ID3v2-Data.pod to the MAN3PODS hash (thanks to Dagfinn Ilmari Mannsaker) * Added copyright text, using Perl Artistic License Thanks to Ilya Zakharevich for the following changes: * Three new modules MP3::Tag::Inf, MP3::Tag::ParseData, and MP3::Tag::CDDB_File added * Tag.pm: - autoinfo() method returns the info for all ID3v1 tags; - autoinfo() method may return the info which tag is obtained from which source; - Methods title(), author() etc (one per each ID3v1 tag) exist and have uniform interface through all the subpackages; - method song() is renamed to title(); method read_filename() is renamed to parse_filename(); backward compatible name still preserved; - config() subroutine can now change all the arbitrary decisions; - new methods interpolate() for tag interpolation into strings and parse(), parse_rex() for an inverse operation; - new subpackage `__hasparent' to treat cyclic object dependencies; - per-object configuration; - new configuration options extension, parse_data, parse_split, parse_filename_ignore_case, parse_filename_merge_dots, parse_join; - a way to store and query user-supplied data inside the object (may be used as scratch space when parsing); - new methods get_config(), get_user(), set_user(), parse_prepare(), parse_rex_prepare(), parse_rex_match(), filename(), abs_filename(), filename_nodir(), filename_noextension(), filename_nodir_noextension(), abs_filename_noextension(), dirname(), filename_extension(), filename_extension_nodot(), dir_component(). * ID3v1.pm: - new ID3v1 method fits_tags() to check whether the info can be placed into an ID3v1 tag - If no track given use automatically ID3v1.0 tag with longer comment - open() file if needed; do not close() close during new(). * ID3v2.pm: - allow multiple frames to be returned by get_frame() in ID3v2; - new method get_frames() with better order of returned values (comparing to get_frame); - get_frame_ids() in ID3v2 improved to take new argument 'truename' to simplify looping over repeated frames; - simpler treatement of sync; fix uncorrect calculation of padding length; - overwrite the tail of the previously present tag with 0s; - a couple of new v2.4 field names added. * File.pm: - more robust parsing of filenames; year can be read from filename if appended (in parentheses) to author or title; filename can be F<.wav> as well. - support file names starting with track numbers as in 03_This_is_the_title.mp3 - support track_title.ext and title_track.ext formats with one-word title too. - new method filename(); - use the 'extension' configuration variable to strip extension. * misprints in the docs corrected and some general harmonization of function and field names in the different modules * new example script mp3info2 (which provides most of functionality of the "standard" mp3info utility, and much more). Release Name: 0.40 ================== Changes: * Updated documentation for MP3::Tag, MP3::Tag::ID3v1, MP3::Tag::ID3v2 and MP3::Tag::ID3v2-Data * Renamed some functions. The old names will still work, but try to use the new names. The following names were changed: - MP3::Tag::getTags() to MP3::Tag::get_tags() - MP3::Tag::newTag() to MP3::Tag::new_tag() - MP3::Tag::ID3v1::removeTag() to MP3::Tag::ID3v1::remove_tag() - MP3::Tag::ID3v1::writeTag() to MP3::Tag::ID3v1::write_tag() - MP3::Tag::ID2v2::getFrameIDs() to MP3::Tag::ID3v2::get_frame_ids() - MP3::Tag::ID2v2::getFrame() to MP3::Tag::ID3v2::get_frame() * Bugfix for ID3v2.pm: - getFrame() returned "undef" as a string instead of simple undef - artist() produced an error when TPE1 is missing in TAG * Bugfix for Tag.pm: - DESTROY() checked only if $mp3->obj{filename} exists and not if it is defined before trying to close it * Bugfix for ID3v1.pm: - genres() expected an id3v1-object as first parameter and a genre only as second parameter. Now the object can be omitted as in a call like MP3::Tag::ID3v1->genres($genre) used by Tag.pm and ID3v2.pm * bugfix for File.pm: - Filenames may contain surrounding whitespaces now Release Name: 0.30 ================== Changes: * Tag.pm - autoinfo() function added. This returns artist/songtitle/track/album. It tries to find this information in an ID3v1 or ID3v2 tag or tries to extract it from the filename. The order in which this happens can be configured with the new config() function. * ID3v2.pm - four new functions: artist(), song(), track() and album(). These are included for compability with the ID3v1 and filename module. * Tag.pm / Tag::File.pm - All file access routines are collected in an extra modul now. This prevents circular references between Tag.pm and the ID3v..pm modules. These circular references prevented Perl from calling the destructor for mp3-objects. * tk-tag - Loading/Saving of binary data in frame-fields is supported now - tk-tag uses now the new what_data functionally of ID3v2 to offer BrowseEntrys for TCON and TFLT and also for parts of APIC and COMR - Set Filename uses now the actual contents of the Tags not the old saved one - Set Filename from ID3v2-Tag works now at least with Artist (%a), Album(%l) and Song (%s) * ID3v2::what_data returns now also information about possible restricted input for some frame fields (APIC; TCON; COMR; TFLT are supported yet). Release Name: 0.25 ================== Changes: * Bug-fix for MP3::Tag If you created several mp3-objects for different files, the filehandles for each file were not used correctly. Thanks to hakimian for reporting this bug. * Bug-fix for ID3v2::remove_tag() It was tried twice to rename one temp-file after removing the tag. Thanks to Brent Sarten for reporting this. * Bug-fix for ID3v2::add_tag() When adding a second (or third, ...) frame of a kind, a wrong header could be written for this frame * Bug-fix for tk-tag When opening a new file, the contents of the ID3v2 tag of the last file would be copied to the ID3v2 tag of the new file for all frames which existed in the last file, but not in the new one. Release Name: 0.21 ================== Changes: * tk-tag has lots of new functions (see tk-tag README) * getFrameIDs returns now always a hash ref of all existing frames, not only when called the first time * Some minor problems in documentation are corrected. * ID3v2::add_frame returns the name of the newly created frame, which can differ from the short name when already such a frame exists. Release Name: 0.20 (beta) ======================== Changes: --due to problems when run at windows: * Added a second seek to ID3v1::write_tag, as windows writes at a wrong position otherwise * Setting Filehandle to binmode after opening a mp3 file * ID3v2: write_tag creates a temp file (if neccessary) now in the same directory where the original mp3 files is located and not in /tmp * Added tk-tag.pl, a graphical interface for MP3::Tag. tk-tag.pl is a alpha version * Added a new manpage MP3::Tag::ID3v2-Data which contains information about the ID3v2 frames and the data returned by MP3::Tag::Id3v2::getFrame() * Frames RVRB ("Reverb"), COMR ("Commercial frame"), AENC ("Audio encryption"), GRID ("Group identification registration"), RBUF ("Recommended buffer size") and SYTC ("Synchronized lyric/text") are now supported * Added some test to test.pl for creating new tags * ID3v2::getFrameIDs returns now a hash reference, which contains the found frames. The keys are the 4 byte codes of the frames, which are needed for getFrame . The according values are the english (long) names of the frames. * ID3v2::write_tag - Updating tagsize after writing tag * ID3v1::all() returns in array context all fields, otherwise only the song * MP3::ID3v1::write_tag didn't returned an error if a file couldn't be opened for writing. Now it does. * Renamed MP3::TAG to MP3::Tag following a suggestion of ANDK from CPAN * Makefile.PL : Added that Compress::Zlib and File::Basename is needed for installation of MP3::Tag Release Name: 0.1 (beta) ======================== Changes: * Added documentation to the modules * Writing/removing of ID3v2.3 tags is supported now * Adding, changing, removing frames of ID3v2.3 is supported * Changed directory structure * Added file for proper install of modules Release Name: 0.2-alpha ======================= Changes: * ID3v2.3 compressed frames are supported now * changed directory structure, support librarys for MP3::Tag are now in a subdirectory * tagged.pl calls xview to show pictures, which were found in ID3v2 tags (sorry, not configurable at the moment, but easy to change in tagged.pl) Release Name: 0.1-alpha ======================= This is the first alpha version. It contains perl modules to read ID3v1/ID3v2 tags, but they are still lacking a lot of features. * Reading / Writing ID3v1 works * Reading of most frames of ID3v2.3 works Included is a demo program tagged.pl, and a program to change ID3v1 tags and to set automatically the filename of a mp3 file: tagit.pl See README.txt for details. More documentation is still lacking. Sorry. MP3::Tag can be found at http://sourceforge.net/projects/taggedMP3-Tag-1.13/data_pod.PL0000700000000000000000000001116611304131040011425 0ustar #!/usr/bin/perl -w ## data_pod.PL creates the documentation File MP3::Tag::ID3v2_Data use MP3::Tag; use MP3::Tag::ID3v2; $filename=shift || "./lib/MP3/Tag/ID3v2_Data.pod"; open(POD, ">$filename"); $std = select(POD); @frames = keys %{MP3::Tag::ID3v2::supported_frames()}; print <<"INTRO"; =head1 NAME MP3::Tag::ID3v2_Data - get_frame() data format and supported frames =head1 SYNOPSIS \$mp3 = MP3::Tag->new(\$filename); \$mp3->get_tags(); \$id3v2 = \$mp3->{ID3v2} if exists \$mp3->{id3v2}; (\$info, \$long) = \$id3v2->get_frame(\$id); # or (\$info, \$long) = \$id3v2->get_frame(\$id, 'raw'); =head1 DESCRIPTION This document describes how to use the results of the get_frame function of MP3::Tag::ID3v2, thus the data format of frames retrieved with MP3::Tag::ID3v2::get_frame(). It contains also a list of all supported ID3v2-Frames. =head2 get_frame() (\$info, \$long) = \$id3v2->get_frame(\$id); # or (\$info, \$long) = \$id3v2->get_frame(\$id, 'raw'); \$id has to be a name of a frame like "APIC". For more variants of calling see L. The names of all frames found in a tag can be retrieved with the L function. =head2 Using the returned data In the ID3v2.3 specifications $#frames frames are defined, which can contain very different information. That means that get_frame returns the information of different frames also in different ways. =over 4 =item Simple Frames A lot of the tags contain only a text string and encoding information. If you call (\$info, \$long) = \$id3v2->get_frame(\$id) for such a frame, \$info will contain the text string and \$long will contain the english name of the frame. Example: get_frame("TIT2"); # returns ("Birdhouse In Your Soul", "Title/songname/content description") =item Complex Frames For more complex frames the returned \$info is a reference to a hash, where each entry of the hash decribes a part of the information found in the frame. The key of a hash entry contains the name of this part, the according value contains the information itself. Example: get_frame("APIC"); # returns ( { "Description" => "Flood", "MIME Type" => "/image/jpeg", "Picture Type" => "Cover (front)", "_Data" => "..data of jpeg picture (binary).." }, "Attached Picture"); =item Other Frames Some frames are not supported at the moment, ie the data found in the frame is not returned in a descriptive way. But you can read the data of this frames (and also of all other frames too) in raw mode. Then the complete data field of the frame is returned, without any modifications. This means that the returned data will be almost binary data. Example: get_frame("TIT2", 'raw'); # returns ("\\x00Birdhouse In Your Soul", "Title/songname/content description") =back The frames which (in addition to C/C) contain only C and C fields are in some intermediate position between "simple" and "complex" frames. They can be handled very similarly to "simple" frames by using "long names", such as C or C, and the corresponding "quick" API such as frame_select(). INTRO @frames = keys %MP3::Tag::ID3v2::long_names; @other = (); @text = (); @complex = (); foreach (@frames) { $data = MP3::Tag::ID3v2::what_data("", $_); if (ref $data) { if ($#$data == 0 and ($$data[0] =~ /^(Text|URL)$/ or $_ eq 'MCDI')) { push @text, $_; } else { push @complex, $_; } } else { push @other, $_; } } print "\n\n=head2 List of Simple Frames\n\nFollowing Frames are supported and return a single string (text). In the List you can find the frame IDs and the long names of the frames as returned by \$id3v2->get_frame():\n\n=over 4\n\n"; foreach (sort @text) { $long = $MP3::Tag::ID3v2::long_names{$_}; print "\n=item $_ : $long\n"; } print "\n=back\n\n"; print "\n\n=head2 List of Complex Frames\n\n"; print "Following frames are supported and return a reference to a hash. The list shows which keys can be found in the returned hash:\n"; print "\n=over 4\n\n"; foreach (sort @complex) { $long = $MP3::Tag::ID3v2::long_names{$_}; print "\n=item $_ : $long\n\n"; $data = MP3::Tag::ID3v2::what_data("", $_); print " Keys: ", join(", ",@$data), "\n"; } print "\n=back\n\n"; print "\n\n=head2 List of Other Frames\n\n"; print "Following frames are only supported in raw mode:\n"; print "\n=over 4\n\n"; foreach (sort @other) { $long = $MP3::Tag::ID3v2::long_names{$_}; print "\n=item $_ : $long\n"; } print "\n=back\n\n"; print <, L END select($std); close POD; MP3-Tag-1.13/examples/0000700000000000000000000000000011417072072011241 5ustar MP3-Tag-1.13/examples/audio_rename0000700000000000000000000003513011100044676013617 0ustar #!/usr/bin/perl -w eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}' if 0; # not running under some shell $VERSION = '1.03'; use MP3::Tag 0.9711; use Getopt::Std 'getopts'; use File::Spec; use File::Path; use strict; $Getopt::Std::STANDARD_HELP_VERSION = 1; my %opt = (E => '', p => '%{mA}%{n0}_%t', e => '.inf|.tag|.mp3', r => qr(\.mp3$)i); # WinCyrillic (win1251), short (CDFS), Keep non-filename chars, Dry run, Glob, # path via pattern, |-separated list of associated extensions # (PEC@R as in mp3info2) getopts('csKDGp:e:P:E:C:@Rx', \%opt); # Interprete Escape sequences: my %r = ( 'n' => "\n", 't' => "\t", '\\' => "\\" ); for my $e (split //, $opt{E}) { $opt{$e} =~ s/\\([nt\\])/$r{$1}/g if defined $opt{$e}; } if ($opt{'@'}) { for my $k (keys %opt) { $opt{$k} =~ s/\@/%/g; } } # Configure stuff... if (defined $opt{C}) { my ($c) = ($opt{C} =~ /^(\W)/); $c = quotemeta $c if defined $c; $c = '(?!)' unless defined $c; # Never match my @opts = split /$c/, $opt{C}; shift @opts if @opts > 1; for $c (@opts) { $c =~ s/^(\w+)=/$1,/; MP3::Tag->config(split /,/, $c); } } my @parse_data; if (defined $opt{P}) { my ($c) = ($opt{P} =~ /^\w*(\W)/s); $c = quotemeta $c if defined $c; $c = '(?!)' unless defined $c; # Never match @parse_data = map [split /$c/], split /$c$c$c/, $opt{P}; for $c (@parse_data) { die "Two few parts in parse directive `@$c'.\n" if @$c < 3; } } sub convert_to_filename ($) { my $outfile = shift; $outfile =~ tr( ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\x80-\x9F) ( !cLXY|S"Ca<__R~o+23'mP.,1o>...?AAAAAAACEEEEIIIIDNOOOOOx0UUUUYpbaaaaaaaceeeeiiiidnooooo:ouuuuyPy_); $outfile =~ s/\s+/ /g; $outfile =~ s/\s*-\s*/-/g; #$outfile =~ s/([?.:!,;\/õ"\\\' ])/$filename_subs{$1}/g; $outfile =~ s/[?|.:!,;\/õ"\\\' <>|]/_/g; #$outfile =~ s/_+/_/g; $outfile; } my $translator; sub setup_translator () { return if $translator; require FindBin; push @INC, $FindBin::Bin if $FindBin::Bin; # Pacify "used only once..." require Encode::transliterate_win1251; $translator = Encode::transliterate_win1251::make_translator( (Encode::transliterate_win1251::prepare_translation( Encode::transliterate_win1251::cyr_table(), Encode::transliterate_win1251::lat_table()))[0] ); } sub cyr_unicode_to_volapuk ($) { my $in = shift; $in =~ s/([^\x00-\xFF]+)/ require Encode; { setup_translator unless $translator; local $_ = Encode::encode('cp1251', $1); $translator->(); $_; } /eg; $in; } sub win1251_to_volapuk ($) { my $in = shift; return cyr_unicode_to_volapuk $in if $in =~ /[^\x00-\xFF]/; setup_translator unless $translator; local $_ = $in; # Detect broken stuff where cyrillic aR is written as latin p my $c = (tr/a-zA-Z//); my $c1 = (tr/p//); $translator->(); my $c2 = (tr/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ//); # Assume p=cyr(r) if there is a lot of cyrillic stuff # and either p is the only Latin, or p is always surrounded by cyrillic stuff # at least on one side: /((?<=[^\s -~])|(?=.[^\s -~]))p/: funny stuff on one side tr/p/r/ if $c1 and $c2 > 2*$c1 and ($c == $c1 or (not /((?<=[^\s -~])|(?=.[^\s -~]))p/ and not $in =~ /(?!((?<=[^\s -~])|(?=.[^\s -~])))p/)); $_ } my $EXT = $opt{x} ? '' : '%E'; warn "Target spec: $opt{p}$EXT\n"; my @comp = split m|/|, $opt{p}; my @ext = split m/\|/, $opt{e}; my ($extl_add, $e) = 0; for $e (@ext) { $extl_add = length $e if $extl_add < length $e; } my @f = @ARGV; if ($opt{G}) { require File::Glob; # "usual" glob() fails on spaces... @f = map File::Glob::bsd_glob($_), @f; } sub mk_path_filename ($) { # Assume '/' for dirnames (my $d = shift) =~ s,(.*)/.*,$1,s; # print "mkpath `$d'\n"; mkpath $d; } sub process_file ($) { my $f = shift; print "File: $f\n"; my $mp3=MP3::Tag->new($f); if ($mp3) { $mp3->config('parse_data', @parse_data) if @parse_data; my ($ext, $base); if ($opt{x}) { $ext = ''; $base = $mp3->interpolate("%A%E"); } else { $ext = $mp3->filename_extension(); $base = $mp3->interpolate("%A"); } my $extl = length $ext; for $e (@ext) { # XXX aux files may result in different name? $extl = length $e if $extl < length $e and -f "$base$e"; } my $i = -1; my ($name, $dirname, $lastcomp); while (++$i < @comp) { my $comp = $comp[$i]; my $ocomp = $comp; $comp = $mp3->interpolate($comp); warn("Component `$ocomp' interpolates to empty, skipping the file\n"), return unless defined $comp and length $comp; unless ($ocomp =~ /%[fFDABN]/) { # Already valid $comp = win1251_to_volapuk($comp) if $opt{c}; $comp = convert_to_filename $comp unless $opt{K}; } my $last = $i == $#comp; # mkisofs -J --joliet-long (2.01a32) is 110 my $maxlen = $ENV{AUDIO_MAX_FILENAME_LEN} || 110; $comp = substr $comp, 0, $maxlen - ($last ? $extl : 0) if $opt{s}; if ($last) { my $post1 = ''; my $post2 = ''; while (1) { my $n = ((defined $name) ? File::Spec->catfile($name, "$comp$post1$post2$ext") : "$comp$post1$post2$ext"); last unless -e $n; if ($post1) { $post2++; } else { $post1 = '-'; $post2 = 'a'; } $comp = substr $comp, 0, $maxlen - length "$post1$post2$ext" if $opt{s}; } } $dirname = $name; if (not defined $name) { $name = $comp; } elsif ($last) { $name = File::Spec->catfile($name, $comp); $lastcomp = $comp; } else { $name = File::Spec->catdir($name, $comp); } } print("... No change\n"), return if $f eq "$name$ext"; print " ==> `$name$ext'\n"; return if $opt{D}; mkpath $dirname if defined $dirname and not -d $dirname; mk_path_filename("$name$ext") if $lastcomp and $lastcomp =~ m,[\\/],; undef $mp3; # Close the file rename $f, "$name$ext" or die "rename: $!"; for $e (@ext) { next unless -f "$base$e"; rename "$base$e", "$name$e" or die "rename $base$e => $name$e: $!"; } } else { print "Not found...\n"; } } if ($opt{R}) { require File::Find; File::Find::find({wanted => sub {return unless -f and /$opt{r}/io; process_file $_}, no_chdir => 1}, @f); } else { my $f; for $f (@f) { process_file $f; } } =head1 NAME audio_rename - rename an audio file via information got via L. =head1 SYNOPSIS audio_rename -csR -@p "@a/@l/@02n_@t" . renames all the audio files in this directory and its subdirectories into a 3-level directory structure given by F, with the basename of F being the 2-digit track number separated from the title by underscore; it also transliterates cyrillic, and shortens long names. (Due to use of C<-@> and double quotes, this command line should work both with UNIXish and DOSish shells; the other examples can be massaged likewise.) (Replacing C<@02n> by C<@{mA}@{n0}> (as in the default value of C<-p>) may provide more intelligent semantic. See the description of C<-p>. audio_rename -KD *.wav Reports how it would rename the F<*.wav> files in this directory according to the default B<-p> rule, but without protectiing "funny" characters. Will not do actual renaming. audio_rename -sc *.mp3 Rename the F<*.mp3> files in this directory according to the default B<-p> rule, translating cyrillic characters into Latin "equivalents", shortening the names of long components, and protecting "funny" characters. audio_rename -p '%a/%{d0}/%B' -G '*/*.mp3' Assuming one-level subdirectory structure F, finds files with extension F<.mp3>, and "sorts" them into a two-level subdirectory structure; toplevel directory is based on the "artist" field, the remaing level is preserved. audio_rename -p '%a/%{d0}/%B' -R . Likewise, but does not suppose any particular depth of the current directory structure; only the filename and the most internal directory name are preserved. audio_rename -p '%a/%N' -R . Likewise, but all directory names (inside the current directory) are preserved. =head1 DESCRIPTION The script takes a list of files (or, with B<-R> option, directories) and renames the given files (or audio files in the directories) according to the rules specified through the command line options. File extensions are preserved (by default). Some "companion" files (i.e., files with the same basename, and with an extension from a certain list) may be renamed together with audio files. A lot of care is taken to make the resulting file names as portable as possible: e.g., "funny" characters in file names are dumbed down (unless requested otherwise), long filename components may be shortened to certain limits. A care is taken so that renaming will not overwrite existing files; however, on OSes which allow rename() to overwrite files, race conditions can ruin the best intentions. E.g., do not run several "overlapping" rename procedures simultaneously! =head1 Recognized options General use options: =over =item B<-p> C Target file name/basename pattern; is subject to interpolation via C method L|MP3::Tag/interpolate>. Default is C<%{mA}%{n0}_%t>; in simplest cases this uses 2-digit track number separated from the title by underscore. See L for more details. Here is the explanation of the default value: due to semantic of escapes C<%{mA}> and C<%{n0}>, if C frame (disk number) is present, it is encoded as a letter, and put before the track number. If the track number has a form C (meaning track N1 of N2), then N1 is used, and padded by 0s to the width of N2. If C is not present, padding to width=2 is used. For example, if C is 3/12, and track is C<14/173>, then what is prepended to the title is C; if there is no C frame, and track is C<4/8>, C<4_> is prepended without any leading 0. (If you want to modify the semantic of C<%{n0}>, note that it is equivalent to C<%{n2:%{n0}}%{!n2:%02n}>. So while C<%02{n0}> will ALWAYS 0-pad to at least width=2, the pattern C<%{n2:%{n0}}%{!n2:%03n}> will 0-pad to width=3 in the case N2 is absent. =item B<-e> C<.ext1|.ext2|...> C<|>-separated list of associated extensions; when renaming F to F, the similar rename will be done to files with the same basename, and extensions F<.ext1>, F<.ext2>, etc. Defaults to C<.inf|.tag|.id3>. =item B<-x> If not present, the pattern of B<-p> is the basename; the extension of the initial file is appended (as interpolated by C<%E>). If present, the pattern of B<-p> is the complete file name. Behaviour with non-empty list of associated extensions is not defined. =back The following options have the same meaning as for script C =over =item B<-D> "Dry run": do not rename, just report the calculated renames. =item B<-G> Arguments are glob patterns; expand them. =item B<-R> Arguments are directory names, recurse inside using option B<-E> for choosing audio files via their extension. =item B<-r> Regular expression to use when looking for audio files per option B<-R>. Defaults to C<(?i:\.mp3$)>: will find files ending in F<.mp3> (ignoring the case). Note that this expression is put into a case-ignoring regular expression, so if you want it to be case-sensitive, protect it as in C<(?-i:REGEXPR)>. =item B<-E> C Controls expansion of escape characters. It should contain the letters of the command-line options where C<\\, \n, \t> are interpolated. Default is none. =item B<-@> Replace C<@> by C<%> in option values. (May be useful since B<-p> and B<-P> may have a lot of embedded characters C<%>, which may be hard to deal with on some shells, e.g., DOSISH shells. DOSish shells recognize double quotes, so if one wants shell-transparent examples of command lines, use -@ and double quotes.) =item B<-P> C Patterns to parse before application of the rule B<-p>. See L for details. =item B<-C> C Configuration options for L. See L for details. =back File name portability options: =over =item B<-s> Make the components of file names short enough to fit on a CD file system. Currently this means the restriction to 110 chars (as with C, at least of version 2.01a32). The limit may be modified per C environment variable. Note that "components" are parts separated by a literal character C in the given pattern (not slashes coming from interpolated strings). =item B<-c> Latinize file names (for portability) assuming they are in WinCyrillic encoding. Needs F (in F directory of the distribution; put it in the subdirectory F of the script directory). =item B<-K> Do not convert "exotic" characters to underscores (those characters which have a low portability score, so the files will have problem being moved between systems). =back Note that this utility performes very similarly to L utility when the latter one is used with B<-p> option; only instead of printing the result of interpolation of B<-p>, it uses the result as the target file name for renaming (after some "sanitizing" of the result). (However, the defaults for C<-E> options differ!) Please take into account that the option B<-P> is provided for completeness only. If one needs really complicated parsing rules to deduce the resulting file name, it is much safer to use L utility to set the wanted file name into some ID3v2 frame (such as C), and then, after checking for errors, use this result similarly to audio_rename -p "%{TXXX[wanted-target-name]]}" -R . After rename, one can delete this frame from the resulting files. If you want to be absolutely error-prone, preserve the initial file name inside the files by doing something similar to mp3info2 -@F "TXXX[orig-fname]=@A" -R . before the rename. If worst comes to worst (but no race conditions happend, so files are not overwritten), one should be able to restore the status quo by running audio_rename -@p "@A" files_or_directories_list (giving B<-R> option if needed). =head1 POSSIBLE PROBLEMS With B<-R> option, there might be situations when the scan of subdirectories first finds a source file in some directory, renames it, then continues the scan of other subdirectories, and will find the target file, so will try to rename it again. In practice, I do not recall ever encountering this situation; if the target file name depends only on the contents of the file, and not its name, then the second rename will be tautological, so not visible. =head1 AUTHOR Ilya Zakharevich . =head1 SEE ALSO MP3::Tag, MP3::Tag::ParseData, mp3info2 =cut MP3-Tag-1.13/examples/cddb2cddb.pl0000700000000000000000000000223410741357042013377 0ustar #!/usr/bin/perl -w use strict; my %opt; use Getopt::Std 'getopts'; getopts('r:', \%opt); # n-th record to chose #use lib 'J:\test-programs-other\.cpan\tagged-CVS\CDDB-1.11'; $_ = <>; /^\s*#\s*xmcd\s*$/ or die "Unexpected format: `$_'"; $_ = <>; $_ = <> while /^\s*#\s*$/; /^\s*#\s*Track frame offsets:\s*$/ or die "Unexpected format: `$_'"; $_ = <>; $_ = <> while /^\s*#\s*$/; my @offsets; (push @offsets, $1), $_ = <> while defined and /^\s*#\s*(\d+)\s*$/; $_ = <> while /^\s*#\s*$/; /^\s*#\s*Disc length: (\d+)(\s.*)?$/ or die "Unexpected format: `$_'"; my $len = $1; $_ = <>; $_ = <> while /^\s*#\s*(|(Revision|Submitted via|Processed by|Normalized):\s.*)$/; /^\s*DISCID\s*=\s*([\da-f]+)\s*$/i or die "Unexpected format: `$_'"; my $id = $1; use CDDB; my $d = new CDDB or die; warn "submitting $id, len=$len, offsets @offsets\n"; my @disks = $d->get_discs($id, \@offsets, $len) or die "No disks found!\n"; for my $disk (@disks) { warn "@$disk\n"; } my $rec = $opt{r} || 1; if (@disks == 1 or defined $opt{r}) { my ($disc_genre, $disc_id, $disc_title) = @{$disks[$rec - 1]}; my $info = $d->get_disc_details($disc_genre, $disc_id); print $info->{xmcd_record}; } MP3-Tag-1.13/examples/dir_mp3_2fake_cddb.pl0000700000000000000000000000244210741357022015164 0ustar #!/usr/bin/perl -w # Read .inf files from current directory, produce a leader of a cddb file # on STDOUT (processable with `fdquery --i file' from Net::FreeDB2) use strict; use MP3::Info; my @sect = 150; my @l; my $round_offset = 0; # Rounding down gives better match to initial size... for my $file (<*.mp3>) { my $mp3 = MP3::Info::get_mp3info($file); my $t = $mp3->{SECS}; $t = int($round_offset + $t*75) or warn; # 75 sectors per sec... push @l, $t; push @sect, $sect[-1] + $t; } print < 0) { $ret += ($n % 10); $n /= 10; } return $ret; } sub compute_discid_my { my @sect = @_; my @secs = map int($_/75), @sect; # cdda2wav rounds down my $n = 0; $n += cddb_sum($_) for @secs[0 .. $#secs - 1]; # Skip leadout my $t = $secs[-1] - $secs[0]; return sprintf '%08x', (($n % 0xFF) << 24) | ($t << 8) | (@secs - 1); } MP3-Tag-1.13/examples/eat_wav_mp3_header.pl0000700000000000000000000001111710340566166015325 0ustar #!/usr/bin/perl -w use strict; use Getopt::Std 'getopts'; # The "size*" fields may be followed by a byte to get to an even alignment; # it is not included into size! (Not applicable to these formats) my $wav_header = < $prefix, buf => $in} unless $read == $header_size; if ($opt{I} and $in =~ /^ID3(..)(.)([\x00-\x7f]{4})/s) { my $f = 0 + ord $2; # Make into integer my $s = 0; for my $c (split //, $3) { $s <<= 7; $s |= ord $c; } $s += (($f & 0x10) ? 20 : 10); $read = sysread $fh, $in, $s, $header_size or die "can't read the header"; return {buf => $in} unless $read == $s; $prefix = substr $in, 0, $s; $in = substr $in, $s; } my %vals; @vals{@wav_fields} = unpack $wav_header, $in or return {buf => $in}; return {prefix => $prefix, buf => $in} unless $vals{header} eq 'RIFF'; if ($vals{size1} == 0x20) { # Format above expects 0x1e... my $in2; $read = sysread $fh, $in2, 2 or die "can't read rest of the header"; $in .= $in2; return {prefix => $prefix, buf => $in} unless $read == 2; my %vals1; @vals1{@wav_fields} = unpack $wav_header, substr $in, 2 or return {buf => $in}; @vals{'type2', 'sizedata'} = @vals1{'type2', 'sizedata'}; } if ($vals{type2} eq 'fact') { my $h2_size = length pack "V a4 V", (0) x 20; # No Optional part my $in2; $read = sysread $fh, $in2, $h2_size or die "can't read rest of the header"; $in .= $in2; return {prefix => $prefix, buf => $in} unless $read == $h2_size; @vals{qw[type12 size12]} = @vals{qw[type2 sizedata]}; @vals{qw[Unknown5 type2 sizedata]} = unpack "V a4 V", $in2; } die <{type2}; return; } (my $o = $f) =~ s/\.wav$/.mp3/i or die "`$f' is not with extension .wav"; unless (defined $rc->{type2}) { close IN or die; die "File `$f': no valid RIFF header" unless $opt{F}; rename $f, $o or die "rename `$f' => `$o': $!"; return; } open OUT, ">$o" or die; binmode OUT; syswrite OUT, $rc->{prefix} if length $rc->{prefix}; my ($in, $c); while ($c = sysread IN, $in, 1<<20) { syswrite OUT, $in or die; } close IN or die "close for read: $!"; close OUT or die "close for write: $!"; unlink $f or die "unlink: $!" if $opt{d}; } if ($opt{R}) { require File::Find; File::Find::find({wanted => sub {return unless -f and ($opt{M} ? /\.mp3$/i : /\.wav$/i); process_file $_}, no_chdir => 1}, @ARGV); } else { for my $f (@ARGV) { process_file $f; } } MP3-Tag-1.13/examples/empty.mp30000700000000000000000000000055010425007122013013 0ustar ÿãÄH€LAME3.93UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄ;HÀUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÿãÄvHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÿãıHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÿãÄìHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUMP3-Tag-1.13/examples/empty_10sec.mp30000700000000000000000000002376010425010300014006 0ustar ÿãÄH€LAME3.93UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄ;HÀUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄvHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãıHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄìHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.93UUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÿãÄÿHUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUMP3-Tag-1.13/examples/extract-y.pl0000700000000000000000000001704111304126510013514 0ustar #!/usr/bin/perl -w use strict; my $opus = qr/Op|WoO/i; my $rx_date = qr/[^()]*\b\d{4}\b[^()]*/; my $rx_op = qr/\b(?:$opus)\b\.?\s*\d+[a-d]?(?:-\d[.\d]*)?(?:(?:[.,:;]\s*)No\.?\s*\d[.\d]*)?/i; my $rx_isodate = qr/\b\d{4}(?:-\d{1,2}\b){0,2}/; my $rx_isorange = qr,$rx_isodate(?:(?:[-/]|--)$rx_isodate)?,; sub short_opus ($) { my $op = shift; my ($opt, $opn, $n) = ($op =~ /\b((?:$opus)\b\.?)\s*(\d+[a-d]?(?:-\d[.\d]*)?)(?:(?:[.,:;]\s*)No\.?\s*(\d[.\d]*))?/io); $opt =~ s/^Op\b\.?\s*/Op. /i; # add dot if needed $opt =~ s/(?<=\S)$/ /; # add space if needed my $short = $op = "$opt$opn"; if (defined $n and length $n) { $op .= "-$n"; } ($short, $op, $n) } sub process_opus_year ($$$$) { my ($op, $y, $seen, $no) = @_; my ($short, $long, $n) = short_opus $op; push @{$$no{$short}}, $n if defined $n and length $n; push @{$$seen{$long}}, $y; } sub extract_years_all ($) { # Format as in: # Piano Trio No. 1 in E flat major; Op. 1, No. 1 (1795) my $fh = shift; my (%seen, %no); while (<$fh>) { # Some Op numbers are repeated (Beeth Op. 72) warn($_), next unless /[.,;:]\s*($rx_op)(?:\s*\(($rx_date)\))?\s*$/o; my ($op, $y) = ($1, $2); next unless $y; process_opus_year $op, $y, \%seen, \%no; } (\%seen, \%no) } sub extract_years_all_slashes ($$) { # Format as in # ### CHAMBER MUSIC // # Op. 1 // 1792--1794 // Pf. trios: Op.1, Nos. 1-3, in Eb, G major, and C minor # open my $codm, '<', 'Beethoven.codm' or die; my ($no_name, $fh) = (shift, shift); my (%seen, %no); #my $unknown = 0; while (<$fh>) { next if /###/; #s,^(?= // ), "?" . ++$unknown ,e; chomp; my ($op, $d, $n) = split m( // ), $_, 3; $n or $no_name or die "unexpected: <$_>"; process_opus_year $op, $d, \%seen, \%no; } # close $codm or die; (\%seen, \%no) } sub find_no ($) { my $seen = shift; my %no; for my $op (keys %$seen) { push @{$no{$1}}, $2 if $op =~ /^(.*)-(\d+[.\d]*)$/s; } \%no; } sub remove_deducible ($$) { # Will act only if one date is present my ($seen, $no) = (shift, shift); for my $op (keys %$no) { next unless exists $$seen{$op}; my $y = $$seen{$op}; next unless @$y == 1; $y = $y->[0]; my @dups; for my $n (@{$$no{$op}}) { my $y1 = $$seen{"$op-$n"}; next unless @$y1 == 1; $y1 = $y1->[0]; push @dups, $n if $y eq $y1; } delete $$seen{"$op-$_"} for @dups; } } sub norm_nums {(my $s = shift) =~ s/(\d+)/sprintf '%05d', $1/e; $s } sub by_with_nums { norm_nums($a) cmp norm_nums($b)} sub sort_with_nums {sort by_with_nums @_} sub extract_years ($$) { my ($how, $fh) = (shift, shift); my ($seen, $no); if ($how eq 'wiki') { ($seen, $no) = extract_years_all \*ARGV; } elsif ($how eq 'merge') { ($seen, $no) = extract_years_all_slashes 'no_name', \*ARGV; } else { ($seen, $no) = extract_years_all_slashes 0, \*ARGV; } remove_deducible $seen, $no; $seen } sub years_of_range ($) { my $r = shift; if ($r =~ /^$rx_isodate$/) { $r =~ /(\d{4})/ or die "<$r>"; return $1; } my @y = ($r =~ /(\d{4})/g); @y == 2 or die "<$r>"; $y[0] .. $y[1] } sub cmp_sets ($$) { # undef on unclear; returns -1 if $a <<< $b my ($a, $b) = (shift, shift); my (%a, %b, %only_a, %only_b); @a{@$a} = (1) x @$a; @b{@$b} = (1) x @$b; %only_a = %a, %only_b = %b; delete $only_a{$_} for keys %b; delete $only_b{$_} for keys %a; return undef if %only_a and %only_b; return 0 unless %only_a or %only_b; return (%only_a ? 1 : -1); } sub cmp_dates ($$) { # cmp dates' "informativeness"; undef on unclear my ($a, $b) = (shift, shift); # Should be strings; returns -1 if $a <<< $b unless (defined $a) { return undef unless defined $b; return -1; } return 1 unless defined $b; # Both defined now return 0 if $a eq $b; my ($a_words, $b_words) = (0,0); $a_words = 1 if $a =~ /[^-\d\s]/; # Check non-ranges $b_words = 1 if $b =~ /[^-\d\s]/; return undef if $a_words and $b_words; # Now at most one of them has non-ranges my $diff_w = $a_words <=> $b_words; my @aranges = ($a =~ /$rx_isorange/g); my @branges = ($b =~ /$rx_isorange/g); my @ayears = map years_of_range($_), @aranges; my @byears = map years_of_range($_), @branges; my $diff_r = cmp_sets \@ayears, \@byears; return undef unless defined $diff_r; return undef if $diff_r and $diff_w and $diff_r ne $diff_w; # Now the differences are in the same direction, if any my $diff_rw = $diff_w; @ayears = ($a =~ /$rx_isodate/g); @byears = ($b =~ /$rx_isodate/g); my $diff_y = cmp_sets \@ayears, \@byears; return undef unless defined $diff_y; return undef if $diff_rw and $diff_y and $diff_rw ne $diff_y; # Now all the differences are in the same direction, if any $diff_rw or $diff_y; } sub fix_year ($$) { # Format as in: # Piano Trio No. 1 in E flat major; Op. 1, No. 1 (1795) my ($fh, $ys) = (shift, shift); my (%seen, %no); while (<$fh>) { # Some Op numbers are repeated (Beeth Op. 72) warn($_), next unless /[.,;:]\s*($rx_op)(?:\s*\(($rx_date)\))?\s*$/o; my ($op, $y) = ($1, $2); my ($short, $long, undef) = short_opus $op; my $yy = $ys->{$long} || $ys->{$short}; if ($yy) { s/([.,;:]\s*($rx_op))(?:\s*\(($rx_date)\))?\s*$/$1 ($yy)\n/o or warn "<$_>"; } print } } die "usage:\n\t$0: (wiki|codm|merge ids|fix datefile) file(s)\n" unless @ARGV and $ARGV[0] =~ /^(wiki|codm|merge|fix)$/i; my $how = shift; my $seen; if ($how eq 'fix') { my $fn = shift; open my $f, '<', $fn or die "error opening $fn for read"; my %ys; while (<$f>) { next if /^##/; my @fields = split m( // ), $_, 4; @fields == 4 or warn "<$_>"; $ys{$fields[0]} = $fields[2]; } close $f or die "error closing $fn for read"; fix_year \*ARGV, \%ys; exit 0; } elsif ($how eq 'merge') { my (@seen, %seen_a, %seen); my @ids = split m(/), shift; my @f = @ARGV; my $c = 0; for my $f (@f) { @ARGV = $f; my $sub_seen = extract_years $how, \*ARGV; @{$sub_seen->{$_}} == 1 or die "$_: <@{$sub_seen->{$_}}>" for keys %$sub_seen; $sub_seen->{$_} = $sub_seen->{$_}[0] for keys %$sub_seen; push @seen, $sub_seen; $seen_a{$_}[$c] = $sub_seen->{$_} for keys %$sub_seen; # Transposed $#{$seen_a{$_}} = $#f for keys %$sub_seen; # put enough undef's $c++; } $seen{$_} = [join ' // ', map $_ || '', @{$seen_a{$_}}] for keys %seen_a; # Assumes values of $seen are arrays of date strings remove_deducible \%seen, find_no \%seen; delete $seen_a{$_} for grep !exists $seen{$_}, keys %seen_a; for my $op (keys %seen_a) { my $ys = $seen_a{$op}; my @best; for my $ff (0..$#$ys) { next unless defined $ys->[$ff]; my $bad; for my $fff (0..$#$ys) { next if $fff == $ff or not defined $ys->[$fff]; my $res = cmp_dates $ys->[$ff], $ys->[$fff]; $bad++, last if not defined $res or $res < 0; $bad++, last if not $res and $fff < $ff; # Reject a later one of the same } push @best, $ff unless $bad; } warn "panic: $op: <@$ys>" if @best > 1; my ($id, $y) = ''; unless (@best) { push @best, 0; $best[0]++ until defined $ys->[$best[0]]; $id = 'guess-'; } $y = $ys->[$best[0]]; $id .= $ids[$best[0]]; $seen->{$op} = [join ' // ', $id, $y, map $_ || '', @$ys]; #push @$ys, $seen->{$op} = [(@best ? $ys->[$best[0]] : '')]; } my $ids = join ' // ', @ids; print <) { $work++ if /^\s*Works having assigned Opus/; next unless $work; s/\.?\s*$//; if (s/^\s*\*\s*(\w+\s.*?):\s*//) { $op = $1; $op =~ s/\bOpus\b/Op./; $no = 0; $op_publ_year{$op} = ( s/\s*\(([-\d]+)\)\s*$// ? $1 : '' ); my $opy = $op_publ_year{$op}; $opy = " ($opy)" if length $opy; $opus{$op} = "$_; $op$opy\n"; } elsif (s/^\s*\+\s*//) { $no++; $opus{$op} =~ s/^#*\s*/### /; my $opy = $op_publ_year{$op}; $opy = " ($opy)" if length $opy; push @{$opnums{$op}}, "$_; $op, No. $no$opy\n"; } } close $f or die "error closing lynx pipe: $!"; # Get years my $oxford_url = shift || 'http://www.classicalarchives.com/bios/codm/beethoven.html'; $f = get_url_txt $oxford_url; $work = 0; my %bywork; while (<$f>) { $work = $1, $bywork{$work} ||= '' if s/^\s*(OPERA|SYMPHONIES|CONCERTOS|ORCHESTRAL|PIANO SONATAS|OTHER PIANO WORKS|CHAMBER MUSIC|CHORAL|SOLO VOICE)\s*(\([^()]+\)\s*)?:\s*//i; next unless $work; last if /\[Home\]/i; $bywork{$work} .= $_; } 1 while <$f>; close $f or die "error closing lynx pipe: $!"; my $months_short = q(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec); my $months_long = q(January|February|March|April|May|June|July|August|September|October|November|December); my $months_s_rex = qr($months_short)i; my $months_l_rex = qr($months_long)i; my $months_days_rex = qr(\b(?:\d{1,2}\s+)?(?:$months_s_rex\b\.?|$months_l_rex\b))i; my $years_rx1 = qr/(?:$months_days_rex\s+)?\d\d\d\d(?:-\d{1,4})?/; my $years_rx2 = qr/$years_rx1(?:(?:,|\sand)\s$years_rx1)*/; # Abbrev: 2consonants, or ob. my $year_spec_rx = qr/pubd?\.|rev\.|comp\.|arr\.\s(?:by|for|of)(?:\s\w+|\sob\.|\s[b-df-hj-np-tv-z]{1,2}\.)*/; my $years_rx = qr/(?:$year_spec_rx\s)?$years_rx2(?:,\s(?:$year_spec_rx\s)?$years_rx2)*/; my $years_fp_rx = qr/$years_rx(?:(?:[,;]|\sand)\s(?:$years_rx|f\.\s*(?:pub\.\s*)?p\.\s[^;]*(?=;|$)))*/; my $o = 1; my %iso_month; $iso_month{lc $_} = $o++ for split /\|/, $months_short; sub date_to_ISO { # "Our version" of ISO; use -- instead of / my $d = shift; # Suppose it is matched by $years_rx1 $d =~ /(?:(?:(\d+)\s+)?($months_short)\w*\.?\s+)?(\d{4})(?:-(\d{1,4}))?/i or die "Unrecognized format of date: `$d'"; my $y = $3; $y .= sprintf '-%02d', $iso_month{lc $2} if $2; $y .= sprintf '-%02d', $1 if defined $1; $y .= ('--' . substr($3, 0, 4 - length $4) . $4) if $4; $y } open ERR, "> $comp.err" or die "open $comp.err for write: $!"; my %per_opnum; for my $w (keys %bywork) { my $txt = $bywork{$w}; $txt =~ s/\.?\s*$//; $txt =~ s/\s+/ /g; my $dot = 1; # 1st try: break into sentences ending in date (see Beethoven's Symphonies) my @parts = split /(?:(?<=\b\d\d\d\d)|(?<=\b\d\d\d\d-\d)|(?<=\b\d\d\d\d-\d\d)|(?<=\b\d\d\d\d-\d\d\d)|(?<=\b\d\d\d\d-\d\d\d\d))\.\s+/, $txt; my $match = qr/^(\?|c\.\s*)?$years_fp_rx$/; unless (@parts > 1) { # 2nd try: as above, but allow parens before dot; then break via semicolons # preceeded by date (see Beethoven's non Symphonies) my @p = split /(?<=(?:(?<=\b\d\d\d\d)|(?<=\b\d\d\d\d-\d)|(?<=\b\d\d\d\d-\d\d)|(?<=\b\d\d\d\d-\d\d\d)|(?<=\b\d\d\d\d-\d\d\d\d))\))\.\s+/, $txt; @parts = (); push @parts, split /(?<=[)\d]);\s+/ for @p; $dot = 0; $match = qr/^(\?|c\.\s*)?$years_rx$/; } my($pref, $npref) = ''; # Look for subheader # Is beneficial only as in "Op.47, in A major (Kreutzer)", except: # Pf. trios: Variations on `Ich bin der Schneider Kakadu', Op.121a (Kakadu) # Overtures: Die Weihe des Hauses (Consecration of the House), Op.124 # Vc. sonatas: Op.102, Nos. 1-2, in C major and D major for my $p (@parts) { # Is beneficial only as in "Op.47, in A major (Kreutzer)", except: # Pf. trios: Variations on `Ich bin der Schneider Kakadu', Op.121a (Kakadu) # Overtures: Die Weihe des Hauses (Consecration of the House), Op.124 # Vc. sonatas: Op.102, Nos. 1-2, in C major and D major # String trios (notes):, Vc. Sonatas:, Miscelaneous, str. qts: if ($p =~ /^((?:\w+|\w\w\w?\.)(?:\s\w+|\sob\.|\s[b-df-hj-np-tv-z]{1,3}\.)*(?:\s\([^()]+\))?):\s+/i) { $npref = $1; $pref = ''; } else { # $p =~ s/^/$pref/ if $pref; } my $y; $p =~ s/,*\s*(?=$years_fp_rx\s*$)/ (/ and $p .= ')' if $dot; my $txt = $p; $txt =~ s/\s\(([^()]+)\)\s*$// and $y = $1; # Explanation: == can't find year; ## Duplicate Op; plain: !unique Op+year print(ERR "==### $w // $pref\n$p\n"), next unless $y and $y =~ /\b\d\d\d\d\b/; my @opn = ($txt =~ /\b(?:Op\.\s*|(?=WoO\b))((?:WoO\.?\s*)?\d+[a-d]?)(?:[,\s]|$)/); print(ERR "###$w // $pref\n$p\n"), next unless $y =~ /$match/ and @opn == 1 and $txt !~ /\b\d\d\d\d\b/; if ($per_opnum{$opn[0]}) { print(ERR "#####$per_opnum{$opn[0]}[3] // $per_opnum{$opn[0]}[4]\n$per_opnum{$opn[0]}[2]\n") if @{$per_opnum{$opn[0]}}; $per_opnum{$opn[0]} = []; print(ERR "#####$w // $pref\n$p\n"), next } $y =~ s/($years_rx1)/date_to_ISO $1/ge; $per_opnum{$opn[0]} = [$y, $txt, $p, $w, $pref]; ($pref, $npref) = ($npref, '') if $npref; #print "@opn // $y // $txt\n"; } } close ERR or die "close $comp.err for write: $!"; sub alignnums ($) { my $s = shift; $s =~ s/(\d+)/ sprintf '%029d', $1/ge; $s } open COMP, "> $comp.wiki" or die "open $comp.wiki for write: $!"; for (sort {alignnums($a) cmp alignnums $b} keys %opus) { print COMP $opus{$_}; print COMP for @{$opnums{$_}}; } close COMP or die "close $comp.wiki for write: $!"; open COMP, "> $comp.codm" or die "open $comp.codm for write: $!"; for (sort {alignnums($a) cmp alignnums $b} keys %per_opnum) { next unless @{$per_opnum{$_}}; print COMP < $comp.diffs" or die "open $comp.diffs for write: $!"; for (sort {alignnums($a) cmp alignnums $b} keys %per_opnum) { (my $op = $_) =~ s/^(\d+)/Op. $1/; next unless @{$per_opnum{$_}} and (defined $op_publ_year{$op} and $per_opnum{$_}[0] ne $op_publ_year{$op} and -1 == index $per_opnum{$_}[0], $op_publ_year{$op}); print DIFF "##$opus{$op}"; print DIFF "##### $_" for @{$opnums{$op}}; print DIFF <) { $work++ if /^\s*Works having assigned Opus/; next unless $work; s/\.?\s*$//; if (s/^\s*\*\s*(\w+\s.*?):\s*//) { $op = $1; $op =~ s/\bOpus\b/Op./; $no = 0; $op_publ_year{$op} = ( s/\s*(\([-\d]+\))\s*$// ? " $1" : '' ); $opus{$op} = "$_; $op$op_publ_year{$op}\n"; } elsif (s/^\s*\+\s*//) { $no++; $opus{$op} =~ s/^#*\s*/### /; push @{$opnums{$op}}, "$_; $op, No. $no$op_publ_year{$op}\n"; } } close $f or die "error closing lynx pipe: $!"; sub alignnums ($) { my $s = shift; $s =~ s/(\d+)/ sprintf '%029d', $1/ge; $s } for (sort {alignnums($a) cmp alignnums $b} keys %opus) { print $opus{$_}; print for @{$opnums{$_}}; } MP3-Tag-1.13/examples/fulltoc_2fake_cddb.pl0000700000000000000000000000331710741357150015303 0ustar #!/usr/bin/perl -w # Read a file (from STDIN) created via (e.g.) # readcd2 -fulltoc dev=0,1,0 -f=audio_cd # , produce a leader of a cddb file on STDOUT # (processable with `fdquery --i file' from Net::FreeDB2, or cddb2cddb) use strict; use MP3::Tag; my $round_offset = 0; # Rounding down gives better match to initial size... binmode STDIN; my $in = do { local $/; }; my $s = 2 + unpack 'n', $in; my $s1 = length $in; die "TOC size mismatch: header=$s, actual=$s1" unless $s == $s1; my %chunks = unpack 'x4 (x3 C x4 a3)*', $in; sub msf2_sector { my ($m,$s,$f) = unpack 'CCC', shift; $f + 75*($s + 60*$m) # Apparently, already shifted by 150 } my @tracks = sort {$a <=> $b} grep $_ <= 99, keys %chunks; die "Gaps in tracks (@tracks)" unless @tracks == $tracks[-1] and $tracks[0] == 1; my @sect = map msf2_sector($_), @chunks{@tracks, 0xa2}; # 0xa2 is leadout #print "$_\n" for @sect; #exit; my @l = map $sect[$_] - $sect[$_ - 1], 1..$#sect; print < 0) { $ret += ($n % 10); $n /= 10; } return $ret; } sub compute_discid_my { my @sect = @_; my @secs = map int($_/75), @sect; # cdda2wav rounds down my $n = 0; $n += cddb_sum($_) for @secs[0 .. $#secs - 1]; # Skip leadout my $t = $secs[-1] - $secs[0]; return sprintf '%08x', (($n % 0xFF) << 24) | ($t << 8) | (@secs - 1); } MP3-Tag-1.13/examples/inf_2fake_cddb.pl0000700000000000000000000000371610741357012014407 0ustar #!/usr/bin/perl -w # Read .inf files from current directory, produce a leader of a cddb file # on STDOUT (processable with `fdquery --i file' from Net::FreeDB2) use strict; my @inf; for my $file (<*.inf>) { local *F; open F, $file or die; my @lines = ; close F or die; chomp @lines; my %line; for my $line (@lines) { next if $line =~ /^\s*#/; die unless $line =~ /^\s*(\S+)\s*=\s*(.*?)\s*$/; $line{lc $1} = $2; } die unless exists $line{tracknumber}; $inf[$line{tracknumber}] = \%line; } for my $n (1..$#inf) { die "Missing track number $n" unless defined $inf[$n]; } print < 0) { $ret += ($n % 10); $n /= 10; } return $ret; } sub compute_discid { my @frames = @_; my $tracks = $#frames + 1; my $n = 0; my @start_secs; my $i; for ($i = 0; $i < $tracks; $i++) { $start_secs[$i] = int ($frames[$i] / 75); } for ($i = 0; $i < $tracks-1; $i++) { $n = $n + cddb_sum ($start_secs[$i]); } my $t = $start_secs[$tracks-1] - $start_secs[0]; my $id = ((($n % 0xFF) << 24) | ($t << 8) | $tracks-1); return sprintf ("%08x", $id); } MP3-Tag-1.13/examples/mod/0000700000000000000000000000000011417072072012020 5ustar MP3-Tag-1.13/examples/mod/manage_M_N_F.pm0000700000000000000000000000042111171175300014577 0ustar use strict; use Normalize::Text::Music_Fields; use MP3::Tag; for (qw(read_composer_file prepare_tag_object_comp normalize_file_lines emit_as_mail_header merge_info check_persons test_normalize_piece)) { no strict; *$_ = \&{"Normalize::Text::Music_Fields::$_"}; } 1; MP3-Tag-1.13/examples/mod/Music_Normalize_Fields.pm0000700000000000000000000000044211171250216016742 0ustar package Music_Normalize_Fields; require Normalize::Text::Normalize_Fields; use strict; for my $n (keys %Normalize::Text::Normalize_Fields::) { my $glob = $Normalize::Text::Normalize_Fields::{$n}; next unless defined *$glob{CODE}; *$n = \&{"Normalize::Text::Normalize_Fields::$n"}; } MP3-Tag-1.13/examples/mod/Music_Translate_Fields.pm0000700000000000000000000000100410735251516016743 0ustar package Music_Translate_Fields; require Music_Normalize_Fields; for my $elt ( qw( title track artist album comment year genre title_track artist_collection person ) ) { *{"normalize_$elt"} = \&{"Music_Normalize_Fields::normalize_$elt"} if defined &{"Music_Normalize_Fields::normalize_$elt"}; *{"translate_$elt"} = \&{"normalize_$elt"} if defined &{"normalize_$elt"}; } for my $elt ( qw( short_person ) ) { *{"$elt"} = \&{"Music_Normalize_Fields::$elt"} if defined &{"Music_Normalize_Fields::$elt"}; } 1; MP3-Tag-1.13/examples/mp3info.pl0000700000000000000000000000103310003562674013153 0ustar #!/usr/bin/perl -w use MP3::Tag; # define how autoinfo tries to get information # default: # MP3::Tag->config("autoinfo","ID3v2","ID3v1","filename"); # don't use ID3v2: # MP3::Tag->config("autoinfo","ID3v1","filename"); while () { chomp; if (my $mp3=MP3::Tag->new($_)) { print "$_ (Tags: ", join(", ",$mp3->get_tags),")\n"; @info=$mp3->autoinfo; print "* Song: $info[0]\n"; print "* Track: $info[1]\n"; print "* Artist: $info[2]\n"; print "* Album: $info[3]\n"; print "* Comment: $info[4]\n"; } } MP3-Tag-1.13/examples/mp3info20000700000000000000000000012315511345013144012626 0ustar #!/usr/bin/perl -w use FindBin; use lib "$FindBin::Bin"; use MP3::Tag 1.12; # Need conditional %L; %{mP} use Getopt::Std 'getopts'; use Config; use File::Path; $VERSION = '1.12'; use strict; $Getopt::Std::STANDARD_HELP_VERSION = 1; my %opt; sub MULTIV::TIEHASH {bless \my $a, 'MULTIV'} sub MULTIV::STORE {shift; my $k = shift; $opt{$k} ||= []; push @{$opt{$k}}, shift} my %opt_d = (r => '(?i:\.mp3$)', E => 'p/i:Fp'); my @oARGV = @ARGV; my $opts = 'c:a:t:l:n:g:y:uDp:C:P:E:G@Rr:I2e:d:F:xN'; my %o; tie %o, 'MULTIV'; exec 'perldoc', '-F', $0 unless @ARGV; sub massage_o { getopts($opts, \%o); for my $o (keys %opt) { if (-1 == index $opts, "$o:") { $opt{$o} = @{$opt{$o}}; # Number of occurences } elsif ($o =~ /[PFCd]/) { # Keep as is } else { die "Multiple option `-$o' not supported" if @{$opt{$o}} > 1; $opt{$o} = $opt{$o}[0]; } } %opt = (%opt_d, %opt); } massage_o(); sub my_decode($$) { # If file names are utf-ized, glob fails??? # De-utf-ize if possible... join '', map chr ord, split //, &Encode::decode; } sub my_decode_deep($$); sub my_decode_deep($$) { my($e,$t) = (shift, shift); if (ref $t eq 'ARRAY') { return [map my_decode_deep($e, $_), @$t]; } elsif (ref $t) { die "panic: reference of type `$t' unexpected" } # De-utf-ize if possible... join '', map chr ord, split //, Encode::decode($e, $t); } # if ($opt{e} and exists $opt{p} ? 0 == length $opt{p} : 1) { if ($opt{e}) { my $skip; if ($opt{e} =~ /^[1-7]$/) { require Encode; my $locale = $ENV{LC_CTYPE} || $ENV{LC_ALL} || $ENV{LANG}; if ($^O eq 'os2' and not eval {Encode::resolve_alias($locale)} ) { require OS2::Process; $locale = 'cp' . OS2::Process::out_codepage(); } $skip = !($opt{e} & 1); # Reinterpret @ARGV @ARGV = map my_decode($locale, $_), @ARGV if $opt{e} & 4; # Reinterpret opts @opt{keys %opt} = map my_decode_deep($locale, $_), values %opt if $opt{e} & 2; $opt{e} = $locale; } elsif ($opt{e} eq 'binary') { binmode STDOUT; $skip = 1; } binmode STDOUT, ":encoding($opt{e})" unless $skip; } my $e_opt = MP3::Tag->get_config('extra_config_keys'); MP3::Tag->config('extra_config_keys', @$e_opt, qw(empty-F-deletes frames_write_creates_dirs)); MP3::Tag->config('empty-F-deletes', 1) unless defined MP3::Tag->get_config1('empty-F-deletes'); # keys of %opt to the MP3::Tag keywords: my %trans = ( 't' => 'title', 'a' => 'artist', 'l' => 'album', 'y' => 'year', 'g' => 'genre', 'c' => 'comment', 'n' => 'track' ); # Interprete Escape sequences: my %r = ( 'n' => "\n", 't' => "\t", '\\' => "\\" ); my ($e_backsl, $e_interp); if ($opt{E} =~ s/^\+//) { ($e_backsl, $e_interp) = ((split m(/i:), $opt{E}, 2), ''); $e_backsl .= 'p' unless $e_backsl =~ /p/; $e_interp =~ s/[Fp]//g; $e_interp .= 'Fp'; } else { ($e_backsl, $e_interp) = ((split m(/i:), $opt{E}, 2), ''); } for my $e (split //, $e_backsl) { $opt{$e} =~ s/\\([nt\\])/$r{$1}/g if defined $opt{$e}; } $e_interp = {map +($_, 1), split //, $e_interp}; if ($opt{'@'}) { for my $k (keys %opt) { if (ref $opt{$k}) { s/\@/%/g for @{ $opt{$k} }; } else { $opt{$k} =~ s/\@/%/g; } } } my %F_human = qw( composer TCOM text_by TEXT orchestra TPE2 conductor TPE3 disk_n TPOS); # Only most useful, and not -l etc... my $FNAME = qr/(?: # 1: Whole specifier \w{4} # 2: Frame name (?: \d\d # 3: Frame number | (?: \( [^()]* (?:\([^()]+\)[^()]*)* \) )? # 4: Language part (?: \[ (?: \\. | [^]\\] )* \] )? # 5: Description part )? ) /x; my $FNAME_human = join '|', keys %F_human; my @set_f; my %textish = map +($_, 1), qw( _encoding Text Language Description URL ); for my $F (@{ $opt{F} }) { my ($lead, @s) = ($F =~ /^(\W)/); if (defined $lead) { @s = split /\Q$lead$lead$lead/, substr $F, 1; } else { @s = $F; } for my $s (@s) { $s =~ /^($FNAME|$FNAME_human|(?:TAGS|ID3v[12])(?=\s+[\?<>]))(?:=|\s+(\??<|>)\s+)(.*)/so or die "unrecognized part of -F option: `$s'"; my $FF = $F_human{$1} || $1; push @set_f, [$FF, $3, ($2 || '')]; } } my (@del, @del_tag); for my $o (@{ $opt{d} }) { my @D; push @D, $1 while $o =~ s/^ ( $FNAME | ID3v[12] ) (,|$) //xo; die "Unrecognized part of -d option: `$o'" if length $o; push @del_tag, grep /^ID3v[12]$/, @D; push @del, grep !/^ID3v[12]$/, @D; } # Configure stuff... MP3::Tag->config(autoinfo => qw(ParseData ID3v2 ID3v1)) if $opt{N}; # Naive algo for my $C (@{ $opt{C} || [] }) { my ($c) = ($C =~ /^(\W)/); $c = quotemeta $c if defined $c; $c = '(?!)' unless defined $c; # Never match my @opts = split /$c/, $C; shift @opts if @opts > 1; for $c (@opts) { $c =~ s/^(\w+)=/$1,/; MP3::Tag->config(split /,/, $c); } } unless ($opt{N}) {{ my $cfg = $ENV{MP3TAG_NORMALIZE_FIELDS}; last if defined $cfg and not $cfg; last unless defined $cfg or $ENV{HOME} and -d "$ENV{HOME}/.music_fields"; no strict 'refs'; eval 'require Normalize::Text::Music_Fields'; for my $elt ( qw( title track artist album comment year genre title_track artist_collection person ) ) { MP3::Tag->config("translate_$elt", \&{"Normalize::Text::Music_Fields::normalize_$elt"}) if defined &{"Normalize::Text::Music_Fields::normalize_$elt"}; } MP3::Tag->config("short_person", \&Normalize::Text::Music_Fields::short_person) if defined &Normalize::Text::Music_Fields::short_person; $cfg = '' if not defined $cfg or $cfg =~ /^[01]$/; my @d = split /$Config{path_sep}/, $cfg; Normalize::Text::Music_Fields::set_path(@d) if @d and defined &Normalize::Text::Music_Fields::set_path; }} my @parse_data; die 'Option -P requires ParseData in autoinfo' if $opt{P} and not grep $_ eq 'ParseData', @{ MP3::Tag->get_config('autoinfo') }; for my $o (@{ $opt{P} }) { my ($c) = ($o =~ /^\w*(\W)/s); $c = quotemeta $c if defined $c; $c = '(?!)' unless defined $c; # Never match push @parse_data, map [split /$c/, $_, -1], split /$c$c$c/, $o; } for my $c (@parse_data) { die "Two few parts in parse directive `@$c'.\n" if @$c < 3; } # E.g., to make Inf overwrite existing title, do # mp3info2.pl -C title,Inf,ID3v2,ID3v1,filename -u *.mp3 sub new_tag_object ($) { my $fname = shift; return MP3::Tag->new($fname) unless $fname eq ''; MP3::Tag->new_fake('settable'); } sub process_file ($) { my $f = shift; my $mp3 = new_tag_object($f); # BUGXX Can't merge into if(): extra refcount if ($mp3) { print $mp3->interpolate(<delete_tag($tag); } $mp3 = new_tag_object($f) if @del_tag; #$mp3->get_tags; # XXXX won't copy ID3v1/2 tags otherwise... my $need_data = ($opt{u} or not $opt{D} or $opt{2} or @set_f or @del or 1); for my $k (keys %trans) { # if not -D, id3v2 may be modified $need_data = 1 if exists $opt{$k}; } # If $need_data FALSE, $modify will be FALSE my $data; # XXXX May be needed by interpolate()??? $data = $mp3->autoinfo('from') if $need_data; my $modify = $opt{2}; my (@args, @set_v); for my $k (keys %trans) { if (exists $opt{$k}) { my $i = ($e_interp->{$k} ? 'i' : ''); push @set_v, [$trans{$k}, $opt{$k}, $e_interp->{$k}]; #push @args, ["mz$i", $opt{$k}, "%$k"]; if (exists $data->{$trans{$k}}) { # If the autocalculated value differs, or comes from non-ID3-tag # write to a tag if ( $data->{$trans{$k}}->[0] ne $opt{$k} or $data->{$trans{$k}}->[1] !~ /^id3/i ) { warn "Need to change $trans{$k}\n"; $data->{$trans{$k}} = [$opt{$k}, 'cmd']; $modify = 1; } } else { warn "Need to add $trans{$k}\n" unless $f eq ''; $data->{$trans{$k}} = [$opt{$k}, 'cmd']; $modify = 1; } } } if ($opt{u} and not $modify) { # Update for my $k (keys %$data) { next if $k eq 'song'; # Alias for title (otherwise double warn) next if $data->{$k}->[1] =~ /^(ID3|cmd)/; next unless defined $data->{$k}->[0]; next unless length $data->{$k}->[0]; $modify = 1; warn "Need to propagate $k from $data->{$k}->[1]\n"; } } my $odata = $data; # Now, when we know what should be updated, retry with arguments if (@args or @set_v or @parse_data or @set_f) { $mp3 = new_tag_object($f); $mp3->config('parse_data', @parse_data, @args); for my $set (@set_v) { my $v = $set->[1]; $v = $mp3->interpolate($v) if $set->[2]; my $meth = $set->[0] . '_set'; $mp3->$meth($v); } for my $set (@set_f) { my($have, $b, $whole, $e); if ($set->[2] =~ /^(?:\?<|(>))$/) { my $write = $1; $have = ($set->[0] =~ /^(TAGS|ID3v[12])$/ or $mp3->have_id3v2_frame_by_descr($set->[0])); next if $write and not $have; } my $v = $set->[1]; $v = $mp3->interpolate($v) if $e_interp->{F}; next if $set->[2] eq '?<' and not -e $v; unless ($whole = ($set->[0] =~ /^(TAGS|ID3v[12])$/)) { my($FF) = MP3::Tag::ID3v2->what_data(substr $set->[0], 0, 4); $b = grep !$textish{$_}, @$FF; } if ($set->[2] eq '>') { # we know frame exists my $o; if ($whole) { $mp3->get_tags; if ($set->[0] eq 'TAGS') { next unless exists $mp3->{ID3v2} or exists $mp3->{ID3v1}; $o = $mp3->interpolate("%{ID3v2}%{ID3v1}"); } else { next unless exists $mp3->{$set->[0]}; $o = $mp3->interpolate("%{$set->[0]}"); } } else { $o = $mp3->select_id3v2_frame_by_descr($set->[0]); } next unless defined $o; # Should not happen??? die "An attempt to extract `non-simple' frame `$set->[0]' to a file" if ref $o; unless (open FF, "> $v") { my $rc; if (MP3::Tag->get_config1('frames_write_creates_dirs')) { my ($dir) = ($v =~ m,^(.*)[\\/],s); if (defined $dir and not -d $dir) { mkpath $dir; # would die on error $rc = open FF, "> $v" } } die "Can't open `$v' for write: $!" unless $rc; } binmode FF if $b; syswrite FF, $o, length $o or die "syswrite to `$v': $!" if length $o; close FF or die "Can't close `$v' for read: $!"; next; } if ($set->[2]) { # < or ?< my $cond = ($set->[2] eq '?<'); next if $cond and not -e $v; if ($whole) { my $from = MP3::Tag->new($v) or die "Can't create tags for `$v'"; $from->get_tags; if ($set->[0] =~ /^(TAGS|(ID3v1)?)$/) { # Process "simple" fields my $from1 = ($2 ? $from->{ID3v1} : $from); # $2: ID3v1 for my $field (values %trans) { # Use "named method" for access my $v = ($from1 and $from1->$field()); next unless defined $v and length $v; my $check_v = (not $cond or $mp3->$field); next unless defined $check_v and length $check_v; my $ff = $field .= '_set'; $mp3->$ff($v); $modify++; } } $modify += $from->copy_id3v2_frames($mp3, ($cond ? '' : 'delete'), 'flags') if $set->[0] =~ /^(TAGS|ID3v2)$/; next; } open FF, "< $v" or die "Can't open `$v' for read: $!"; if ($b) { binmode FF } elsif ($e = $mp3->get_config1('decode_encoding_files')) { eval "binmode FF, ':encoding($e)'"; # old binmode won't compile... warn $@ if $@ and $] >= 5.008; } undef $/; $v = ; close FF or die "Can't close `$v' for read: $!"; $v =~ s/^\s+//, $v =~ s/^\s+// unless $b; } undef $v if not length $v and $mp3->get_config1('empty-F-deletes'); $mp3->select_id3v2_frame_by_descr($set->[0], $v); $modify++; } $mp3->get_tags; $data = $mp3->autoinfo('from') if $need_data; } for my $del (@del) { # delete my $c = $mp3->select_id3v2_frame_by_descr($del, undef); warn "No frames found for $del.\n" unless $c; $modify++ if $c; } # Recheck whether we need to update if (not $modify and $opt{u} and @parse_data) { for my $k (keys %$data) { $modify = 1, last if defined $data->{$k} and (not defined $odata->{$k} or $data->{$k} ne $odata->{$k}); } } $mp3->id3v2_frames_autofill() unless @{$opt{d}} or $opt{N} or $f eq '' or not ($modify or $opt{u} or $mp3->is_id3v2_modified); $opt{u} and warn "No update needed\n" unless $modify or $mp3->is_id3v2_modified; my ($com,$lyr,$p) = map $mp3->interpolate("%{$_}"), qw(TCOM TEXT TPE1); my ($perf,$_p) = 'Artist: %a'; # Fallback; otherwise print "Performer:" unless (exists $opt{p} or $opt{I}) { if (defined $p and not $mp3->{ID3v1} # No forward propagation problems and length($_p = $mp3->interpolate('%{TPE1}'))) { $perf = "Performer: $_p"; } elsif (length($_p = $mp3->interpolate('%{TXXX[TPE1]}'))) { $perf = "Performer: $_p"; } elsif ($p and defined $com and defined $lyr and $p ne $com and $p ne $lyr) { # So we know it is different $perf = "Performer: $p"; } } print $mp3->interpolate(exists $opt{p} ? $opt{p} : <interpolate(< 1) { my $binary = $opt{x} > 2 ? '_' : ''; print $mp3->interpolate("%{ID3v2:%{${binary}out_frames[<>]}\n}"); } return unless ($modify or $opt{u} and ($opt{u}>1 or $mp3->is_id3v2_modified)) and not $opt{D}; # Dry run $mp3->frames_translate if $opt{2}; $mp3->update_tags($data, $opt{2}); } else { print "Not found...\n"; } } my @f = @ARGV; if ($opt{G}) { require File::Glob; # "usual" glob() fails on spaces... @f = map File::Glob::bsd_glob($_), @f; } if ($opt{R}) { require File::Find; File::Find::find({wanted => sub {return unless -f and /$opt{r}/o; process_file $_}, no_chdir => 1}, @f); } else { my $f; for $f (@f) { process_file $f; } } =head1 NAME mp3info2 - get/set MP3 tags; uses L to get default values. =head1 SYNOPSIS # Print the information in tags and autodeduced info mp3info2 *.mp3 # In addition, set the year field to 1981 mp3info2 -y 1981 *.mp3 # Same without printout of info, recursively in the current directory mp3info2 -R -p "" -y 1981 . # Do not deduce any field, print (normalized) info from the tags only mp3info2 -C autoinfo=ID3v2,ID3v1 *.mp3 # As above, but without normalization/autofill, the raw information in tags mp3info2 -N *.mp3 # As above, but only with ID2v1 tag read mp3info2 -NC autoinfo=ID3v1 *.mp3 # Get artist from CDDB_File, autodeduce other info, write it to tags mp3info2 -C artist=CDDB_File -u *.mp3 # For title, prefer information from .inf file; autodeduce rest, update mp3info2 -C title=Inf,ID3v2,ID3v1,filename -u *.mp3 # Same, and get the artist from CDDB file mp3info2 -C title=Inf,ID3v2,ID3v1,filename -C artist=CDDB_File -u *.mp3 # Write a script for conversion of .wav to .mp3, autodeducing tags mp3info2 -p "lame -h --vbr-new --tt '%t' --tn %n --ta '%a' --tc '%c' --tl '%l' --ty '%y' '%f'\n" *.wav >xxx.sh =head1 DESCRIPTION The program prints a message summarizing tag info (obtained via L module) for specified files. It may also update the information in ID3 tags. This happens in three different cases. =over =item * If the information supplied in command-line options C differs from the content of the corresponding ID3 tags (or there is no corresponding ID3 tags). =item * If options C<-d> or C<-F> were given. =item * if C obtains the info from other means than MP3 tags, and C<-u> forces the update of the ID3 tags. =back (All these ways are disabled by C<-D> option.) ID3v2 tag is written if needed, or if C<-2> option is given. (Automatic fill-in of deduceable fields (via the method id3v2_frames_autofill()) is performed unless C<-d> or C<-N> options are given.) The option C<-u> writes (Cpdates) the fetched information to the MP3 ID3 tags. This option is assumed if there are command-line options which explicitly set tag elements (C<-a>, C<-t> etc., and C<-F>, C<-d>). (Effects of this option may be overridden by giving C<-D> option.) If C<-2> option is also given, forces write of ID3v2 tag even if the info fits the ID3v1 tag (in addition, this option enables auto-update of "personal name" fields, and corresponding titles according to values of C, C etc. configuration settings; see L<"Normalization of fields">). This option is ignored if no change to tags is detected; however, one can force an update by repeating this option (useful if you expect the change the "format" of the tag, as opposed to its "content"). The option C<-p> prints a message using the next argument as format (by default C<\\>, C<\t>, C<\n> are replaced by backslash, tab and newline; governed by the value of C<-E> option); see L for details of the format of sprintf()-like escapes. If no option C<-p> is given, message in default format will be emitted. The value of option C<-e> is the encoding used for the output; if the value is a number, system-specific encoding is guessed (and used for the output if bit 0x1 is set); if bit 0x2 is set, then, command line options are assumed to be in the guessed encoding; if bit 0x4 is set, then, command line arguments are assumed to be in the guessed encoding. Use the value C to do binary output. With option C<-D> (dry run) no update is performed, no matter what the other options are. With this option, no parsing of tags is performed unless needed. Use options t a l y g c n to overwrite the information (title artist album year genre comment track-number) obtained via C heuristics (C<-u> switch is implied if any one of these arguments differs from what would be found otherwise; use C<-D> switch to disable auto-update). By default, the values of these options are not C<%>-interpolated; this may be changed by C<-E> option. The option C<-d> should contain the comma-separated list of ID3v2 frames to delete. A frame specification is the same as what might be given to C<"%{...}"> frame interpolation command, e.g., C, C, C; the difference with modify-access is that B (and not the B of) matching frames are deleted. (Option -d may be repeated.) For example, C<-d APIC> would remove all picture frames. In addition, if the list contains C or C, whole tags will be deleted. Likewise, the option C<-F> allows setting of arbitrary C frames: if one needs to set one frame, use the directive C: -F TIT2=The_new_Title Again, on modify, B matching frames are deleted first, so be carefull with -F COMM=MyComment Option C<-F> may be repeated to set more than one frame. If configuration variable C is TRUE (default), empty arguments will delete the frame. One can replace C by C FILE>; in this case the value to set is read from the file named F; if the frame is text-only (meaning: at most C<[encoded]Text URL Language Description> fields are present), the file is read in text mode (and with starting/trailing whitespace stripped), otherwise it is read in binary mode. (Whitespace is required about the C> signs.) If C> is replaced by C>, the value is set only if frame is not yet present, and if the file exists; if replaced by C>, the value (if present) is written to F (creation of intermediate directories is controlled by configuration option C, the default is FALSE). Additionally, C may be one of C or C or C; in this case, whole tags are written or read. For example, for C FILE>, C info is calculated from F<FILE>, which may be raw tags, as produced with C<E<gt>>, or a valid MP3 file; if L<Image::ExifTool|Image::ExifTool> is present, the data may be read from arbitrary multimedia file. (Likewise, for C<ID3v1 E<lt> FILE>, the same info is extracted from C<ID3v1> tag only.) After this, in case of C<ID3v2> or C<TAGS>, C<ID3v2> frames are copied from the C<ID3v2> tag one-by-one. (With suitable modifications for C<?E<lt>>.) By default, the "VALUE" for C<-F> is C<%>-interpolated; this can be changed by option C<-E>. For user convenience, human-friendlier forms C<composer, text_by, orchestra, conductor, disk_n> can be used instead of C<TCOM, TEXT, TPE2, TPE3, TPOS>. The option C<-P RECIPE> is a very powerful generalization of what can be done by options C<-F>, C<-d>, and C<-t -a -l -y -g -c -n>. It may be repeated; the values should contain the parse recipes. They become the configuration item C<parse_data> of C<MP3::Tag>; eventually this information is processed by L<MP3::Tag::ParseData|MP3::Tag::ParseData> module (if the latter is present in the chain of heuristics; see option C<-C>). The C<RECIPE> is split into C<$flags, $string, @patterns> on its first non-alphanumeric character; the first of @patterns which matches $string is going to be executed (for side effects). (See examples: L<EXAMPLES: parse rules>.) If option C<-G> is specified, the file names on the command line are considered as glob patterns. This may be useful if the maximal command-line length is too low. With the option C<-R> arguments can be directories, which are searched recursively for audio (default F<*.mp3>) files to process; use option C<-r> to reset the regular expression to look for (the default is C<(?i:\.mp3$)>). The option C<-E> controls expansion of escape characters. It should contain the letters of the command-line options where C<\\, \n, \t> are interpolated; one can append the letters of C<t a l y g c n F> options requiring C<%>-interpolation after the separator C</i:> (for C<-F>, only the values are interpolated). The default value is C<p/i:Fp>: only C<-p> is C<\>-interpolated, and only C<-F> and C<-p> are subject to C<%>-interpolation. If all one wants is to I<add> to the defaults, preceed the value of C<-E> (containing added options) by C<"+">. (Some parts of the value of option C<-P> are interpolated, but this should be governed by flags, not C<-E>; do I<NOT> put C<P> into the C<%>-interpolated part of C<-E>.) If the option C<-@> is given, all characters C<@> in the options are replaced by C<%>. This may be convenient if the shell treats C<%> specially (e.g., DOSISH shells). If option C<-I> is given, no guessworking for I<artist> field is performed on typeout. The option C<-C CONFIG_OPT=VALUE1,VALUE2...> sets C<MP3::Tag> configuration data the same way as C<MP3::Tag->config()> would do (recall that the value is an array; separate elements by commas if more than one). The option may be repeated to set more than one value. Note that since C<ParseData> is used to process C<-P> parse recipes, it should be better be kept in the C<autoinfo> configuration (and related fields C<author> etc) in presence of C<-P>. If the option C<-x> is given, the technical information about the audio file is printed (MP3 level, duration, number of frames, padding, copyright, and the list of ID3v2 frame names in format suitable to C<%{...}> escapes). If C<-x> is repeated, content of frames is also printed out (may output non-printable chars, if it is repeated more than twice). If option C<-N> is given, all the "smarts" are disabled - no normalization of fields happens, and (by default) no attempt to deduce the values of fields from non-ID3 information is done. This option is (currently) equivalent to having C<-C autoinfo=ParseData,ID3v2,ID3v1> as the first directive, to having no F<Normalize::Text::Music_Fields.pm> present on @INC path, and not calling autofill() method. =head1 Normalization of fields (The loading of normalization module and all subsequent operations may be disabled by the option C<-N>, or by setting the environment variable C<MP3TAG_NORMALIZE_FIELDS> to be FALSE. If not prohibited, the module is attempted to be loaded if directory F<~/.music_fields> is present, or C<MP3TAG_NORMALIZE_FIELDS> is set and TRUE.) If loading of the module C<Normalize::Text::Music_Fields> is successful, the following is applicable: If the value of C<MP3TAG_NORMALIZE_FIELDS> is defined and not 1, this value is broken into directories as a PATH, and load path of C<Normalize::Text::Music_Fields> is set to be this list of directories. Then L<MP3::Tag> is instructed (via corresponding configuration settings) to use C<normalize_artist> (etc.) methods defined by this module. These methods may normalize certain tag data. The current version defines methods for "normalization" of personal names, and titles (based on the composer). This normalization is driven through user-editable configuration tables. In addition to automatical normalization of MP3 tag data, one can use "fake MP3 files" to manually access some features of this module. For this, use an empty file name, and C<-D> option. E.g, mp3info2 -D -a beethoven -p "%a\n" "" mp3info2 -D -a beethoven -p "%{shP[%a]}\n" "" mp3info2 -D -a beethoven -t "sonata #28" -p "%t\n" "" mp3info2 -D -a beethoven -t "allegretto, Bes" -@p "@t\n" "" mp3info2 -D -a beethoven -t "op93" -@p "@t\n" "" will print the normalized person-name for C<beethoven>, the corresponding normalized short person-name, and the normalized title for C<sonata #28> of composer C<beethoven>. E.g., with the shipped normalization tables, it will print Ludwig van Beethoven (1770-1827) L. van Beethoven Piano Sonata No. 28 in A major; Op. 101 (1816) Allegretto for Piano Trio in B flat major; WoO 39 (1812) Symphony No. 8 in F major; Op. 93 (comp. 1812, f.p. Vienna, 1814-02-27, cond. Beethoven; pubd. 1816) =head1 The order of operation Currently, the operations are done in the following order =over 2 =item Deletion of ID3v1 or ID3v2 as a whole via C<-d> option; =item Recipies of C<-P> option are set up (to be triggered by interpolation); =item The setting done via C<-a/-t/-l/-y/-g/-c/-n> options; =item The settings done via C<-F> option; =item Deletion of individual frames via C<-d> option; =item autofill of ID3v2 (id) frames; =item Emit info based on C<-p> and C<-x> options; =item Trigger recipies of C<-P> (if not triggered by interpolation); =item Update tags if needed. =back =head1 Usage strategy: escalation of complexity The purpose of this script is to to make handling of ID3 tags as simple I<as possible>. On one end of the scale, one can perform arbitrarily complex manipulations with tags using L<C<MP3::Tag>|MP3::Tag> Perl module. On the other end, it is much more convenient to handle simplest manipulations with tags using this script's options C<-t -a -l -y -g -c -n> and C<-p -F -d>. For slightly more complicated tasks, one may need to use the more elaborate method of I<parse rules>, provided to this script by the option C<-P>; the rules depend heavily on I<interpolation>, see L<MP3::Tag/interpolate>, L<MP3::Tag/interpolate_with_flags>. To simplify upgrade from "simplest manipulations" to "more elaborate ones", here we provide "parse rule" I<synonyms> to the simplest options. So if you start with C<-t -a -l -y -g -c -n> and C<-p -F -d> options which "almost work" for you, you have a good chance to be able to fully achieve your aim by modifying the synonyms described below. (Below we assume that C<-E> option is set to its default value, so C<-F -p> are C<%>-interpolated, other options are not. Note also that if your TTY's encoding is recognized by Perl, it is highly recommended to set C<-e 3> option; on DOSISH shells, better use C<-@>, and replace C<%>'s by C<@>'s below.) =over 14 =item C<-t VALUE> -P "mz/VALUE/%t" =item C<-a -l -y -g -c -n> Likewise. =item C<-F> "TIT2=VALUE" -P "mzi/VALUE/%{TIT2}" =item C<-F> "APIC[myDescr] < FILE" -F "APIC[myDescr]=%{I(fimbB)FILE}" or -P "mzi/%{I(fimbB)FILE}/%{APIC[myDescr]}" (remove C<bB> for text-only frames). =item C<-F> "APIC[myDescr] > FILE" -P "bOi,%{APIC[myDescr]},FILE" (remove C<b> for text-only frames); or use C<-e binary -p "%{APIC[myDescr]}"> with redirection, see L<"EXAMPLES: parse rules">. =item C<-d> TIT2 -P "m//%{TIT2}" =item C<-F> "TIT2 ?< FILE" Very tricky. This won't set distinguish empty file and non-existing one: -P "mzi/%{TIT2:1}0%{I(fFim)FILE}/10/10%{TIT2}/0%{U1}" (add C<bB> to C<fFim> for non-text-only frames); the last part may be omitted if one omits the flag C<m> - it is present to catch misprints only. =back For details on "parse rules", see L<EXAMPLES: parse rules> and L<MP3::Tag::ParseData/DESCRIPTION>. =head1 EXAMPLES: parse rules Only the C<-P> option is complicated enough to deserve comments... For full details on I<parse rules>, see L<MP3::Tag::ParseData/DESCRIPTION>; for full details on interpolation, see L<MP3::Tag/interpolate>, L<MP3::Tag/interpolate_with_flags>. For a (silly) example, one can replace C<-a Homer -t Iliad> by -P mz=Homer=%a -P mz=Iliad=%t A less silly example is forcing a particular way of parsing a file name via -P "im=%{d0}/%f=%a/%n %t.%e" It is broken into flags string pattern1 "im" "%{d0}/%f" "%a/%n %t.%e" The flag letters stand for I<interpolate>, I<must_match>. This interpolates the string C<"%{d0}/%f"> and parses the result (which is the file name with one level of the directory part preserved) using the given pattern; thus the directory name becomes the artist, the leading numeric part - the track number, and the rest of the file name (without extension) - the title. Note that since multiple patterns are allowed, one can similarly allow for multiple formats of the names, e.g. -P "im=%{d0}/%f=%a/%n %t.%e=%a/%t (%y).%e" allows for the file basename to be also of the form "TITLE (YEAR)". An alternative way to obtain the same results is -P "im=%{d0}=%a" -P "im=%f=%n %t.%e=%t (%y).%e" which corresponds to two recipies: flags string pattern1 pattern2 "im" "%{d0}" "%a" "im" "%f" "%n %t.%e" "%t (%y).%e" Of course, one could use "im" "%B" "%n %t" "%t (%y)" as a replacement for the second one. Note that it may be more readable to set I<artist> to C<%{d0}> by an explicit asignment, with arguments similar to -E "p/i:Fpa" -a "%{d0}" (this value of C<-E> requests C<%>-interpolation of the option C<-a> in addition to the default C<\>-interpolation of C<-p>, and C<%>-interpolation of C<-F> and C<-p>; one can shortcut it with C<-E +/i:a>). To give more examples, -P "if=%D/.comment=%c" will read comment from the file F<.comment> in the directory of the audio file; -P "ifn=%D/.comment=%c" has similar effect if the file F<.comment> has one-line comments, one per track (this assumes the the track number can be found by other means). Suppose that a file F<Parts> in a directory of MP3 files has the following format: it has a preamble, then has a short paragraph of information per audio file, preceded by the track number and dot: ... 12. Rezitativ. (Pizarro, Rocco) 13. Duett: jetzt, Alter, jetzt hat es Eile, (Pizarro, Rocco) ... The following command puts this info into the title of the ID3 tag (provided the audio file names are informative enough so that MP3::Tag can deduce the track number): mp3info2 -u -C parse_split='\n(?=\d+\.)' -P 'fl;Parts;%=n. %t' If this paragraph of information has the form C<TITLE (COMMENT)> with the C<COMMENT> part being optional, then use mp3info2 -u -C parse_split='\n(?=\d+\.)' -P 'fl;Parts;%=n. %t (%c);%=n. %t' If you want to remove a dot or a comma got into the end of the title, use mp3info2 -u -C parse_split='\n(?=\d+\.)' \ -P 'fl;Parts;%=n. %t (%c);%=n. %t' -P 'iR;%t;%t[.,]$' The second pattern of this invocation is converted to ['iR', '%t' => '%t[.,]$'] which essentially applies the substitution C<s/(.*)[.,]$/$1/s> to the title. Now suppose that in addition to F<Parts>, we have a text file F<Comment> with additional info; we want to put this info into the comment field I<after> what is extracted from C<TITLE (COMMENT)>; separate these two parts of the comment by an empty line: mp3info2 -E C -C 'parse_split=\n(?=\d+\.)' -C 'parse_join=\n\n' \ -P 'f;Comment;%c' -P 'fl;Parts;%=n. %t' \ -P 'i;%t///%c;%t (%c)///%c' -P 'iR;%t;%t[.,]$' This assumes that the title and the comment do not contain C<'///'> as a substring. Explanation: the first pattern of C<-P>, ['f', 'Comment' => '%c'], reads comment from the file C<Comment> into the comment field; the second, ['fl', 'Parts' => '%=n. %t'], reads a chunk of C<Parts> into the title field. The third one ['i', '%t///%c' => '%t (%c)///%c'] rearranges the title and comment I<provided> the title is of the form C<TITLE (COMMENT)>. (The configuration option C<parse_join> takes care of separating two chunks of comment corresponding to two occurences of C<%c> on the right hand side.) Finally, the fourth pattern is the same as in the preceding example; it removes spurious punctuation at the end of the title. More examples: removing string "with violin" from the start of the comment field (removing comment altogether if nothing remains): mp3info2 -u -P 'iz;%c;with violin%c' *.mp3 setting the artist field without letting auto-update feature deduce other fields from other sources; mp3info2 -C autoinfo=ParseData -a "A. U. Thor" *.mp3 setting a comment field unless it it already present: mp3info2 -u -P 'i;%c///with piano;///%c' *.mp3 The last example shows how to actually write "programs" in the language of the C<-P> option: the example gives a conditional assignment. With user variables (as in C<%{U8}>) for temporaries, and a possibility to use regular expressions, one could provide arbitrary programmatic logic. Of course, at some level of complexity one should better switch to direct interfacing with C<MP3::Tag> Perl module (use the code of this Perl script as an example!). Here is a typical task setting "advanced" id3v2 frames: composer (C<TCOM>), orchestra (C<TPE2>), conductor (C<TPE3>). We assume a directory tree which contains MP3 files tagged with the following conventions: C<artist> is actually a composer; C<comment> is of one of two forms: Performers; Orchestra; Conductor Orchestra; Conductor To set the specific MP3 frames via C<-P> rules, use mp3info2 -@P "mi/@a/@{TCOM}" \ -P "mi/@c/@{U1}; @{TPE2}; @{TPE3}/@{TPE2}; @{TPE3}" -R . With C<-F> options, this can be simplified as mp3info2 -@F "TCOM=@a" -P "mi/@c/@{U1}; @{TPE2}; @{TPE3}/@{TPE2}; @{TPE3}" -R . or mp3info2 -@F "composer=@a" -P "mi/@c/@{U1}; @{TPE2}; @{TPE3}/@{TPE2}; @{TPE3}" -R . To copy ID3 tags of MP3 files in the current directory to files in directory F</tmp/mp3> with the extension F<.tag> (and print "progress report"), use mp3info2 -p "@N@E\n" -@P "bODi,@{ID3v2}@{ID3v1},/tmp/mp3/@N.tag" -DNR . Since we did not use C<z> flag, MP3 files without tags are skipped. Now suppose that there are two parallel file hierarchies of audio files, and of lyrics: audio files are in F<audio/dir_name/audio_name.mp3> with corresponding lyrics file in F<text/dir_name/audio_name.mp3>. To attach lyrics to MP3 files (in C<COMM> frame with description C<lyrics> in language C<eng> - I<this is a non-standard location, see below!>), call mp3info2 -@P "fim;../text/@{d0}/@B.txt;@{COMM(eng)[lyrics]}" -Ru . inside the directory F<audio>. (Change C<fim> to C<Ffim> to ignore the audio files for which the corresponding text file does not exist.) (Of course, to follow the specifications, one should have used the field C<"%{USLT(eng)[]}"> instead of C<"%{COMM(eng)[lyrics]}">; see below for variations). Finish by a very simple example: all what the pattern -P 'i;%t;%t' does is removal of trailing and leading blanks from the title (which is deduced by other means). =head1 More examples With C<-F> option, one could set the C<USLT> frame as mp3info2 -@F "USLT(eng)[] < ../text/@{d0}/@B.txt" -Ru . Print out such a frame (in any language) with mp3info2 -@p "@{USLT[]}\n" file.mp3 Similarly, to print out the APIC frame with empty description, use mp3info2 -e binary -@p "@{APIC[]}" file.mp3 > output_picture_file or (with description "cover") mp3info2 -@P "bOi,@{APIC[cover]},output_picture_file.jpg" audio_07.mp3 To set such a frame from file F<xxx.gif> (with the default C<Picture Type>, C<"Cover (front)">, and empty description), do one of mp3info2 -F "APIC < xxx.gif" file.mp3 mp3info2 -@F "APIC[]=@{I(fimbB)xxx.gif}" file.mp3 The difference of C<APIC> and C<APIC[]> is that the first removes all C<APIC> frames first, and the second removes only all C<APIC> frames with empty description - but arbitrary image type. So it may be more suitable to use the full specification, as in C<APIC(Cover (front))[]>. To remove C<APIC> frames with empty descriptions, arbitrary C<Picture Type>s (and C<MIME type>s which may be correctly calculated by F<mp3info2>, e.g., C<TIFF/JPEG/GIF/PNG>), use mp3info2 -d "APIC[]" file.mp3 (note that this wouldn't free disk space, unless "shrink" is forced by configuration variables). To do the same with the "Conductor" picture type only, do mp3info2 -d "APIC(Conductor)[]" file.mp3 To scan through subdirectories, and add file F<cover.jpg> from the directory of the file as a "default" C<APIC> frame, but only if there is no C<APIC> frame, and a file exists, do mp3info2 -@F "APIC ?< @D/cover.jpg" -R . This deletes empty frames for date, C<TCOP, TENC, WXXX[], COMM(eng)[]>, and removes the leading 0 from track number from MP3 file in current directory: mp3info2 -@ -E +/i:y -F "TCOP=@{TCOP}" -F "TENC=@{TENC}" -F "WXXX[]=@{WXXX[]}" -F "COMM(eng)[]=@{COMM(eng)[]}" -y "@y" -P "mi/@n/0@n/@n" *.mp3 =head1 Examples on dealing with broken encodings One of principal weaknesses of ID3 specification was that it required that data is provided in C<latin-1> encoding. Since most languages in the world are not expressible in C<latin-1>, this lead to (majority?) of ID3 tags being not standard-conforming. Newer versions of the specs fixed this shortcoming, but the damage was already done. Fortunately, this script can use abilities of L<C<MP3::Tag>|MP3::Tag/ENVIRONMENT> to convert from non-conforming content to a conforming one. The following example converts ID3v2 tags which were written in (non-standard-conforming) encoding C<cp1251> to be in standard-conforming encoding. For the purpose of this example, assume that ID3v1 tags are in the same encoding (and that one wants to leave them in the encoding C<cp1251>); the files to process are found in the current directory and (recursively) in its subdirectories (C<set> syntax for DOSISH shells): set MP3TAG_DECODE_V1_DEFAULT=cp1251 set MP3TAG_DECODE_V2_DEFAULT=cp1251 mp3info2 -C id3v2_fix_encoding_on_write=1 -u2R . For more information, see L<MP3::Tag/ENVIRONMENT>, L<MP3::Tag/config>, and L<MP3::Tag/CUSTOMIZATION>. =head1 INCOMPATIBILITIES with F<mp3info> This tool is loosely modeled on the program F<mp3info>; it is "mostly" backward compatible (especially when in "naive" mode via C<-N>), and allows a very significant superset of functionality. Known backward incompatibilities are: -G -h -r -d -x Missing functionality: -f -F -i Incompatible C<%>-I<escapes>: %e %E - absolutely different semantic %v - has no trailing 0s %q - has fractional part %r - is a number, not a word "Variable" for VBR %u - is one less (in presence of descriptor frame only?) Missing C<%>-I<escapes>: %b %G Backslash escapes: only C<\\>, C<\n>, C<\t> supported. C<-x> prints data in a different format, not all fields are present, and ID3v2 tag names are output. =head1 ENVIRONMENT With C<-e> 1, 2 or 3, this script may consult environment variables C<LC_CTYPE, LC_ALL, LANG> to deduce the current encoding. No other environment variables are directly read by this script. Note however, that L<MP3::Tag> module has a rich set of defaults for encoding settings settable by environment variables; see L<MP3::Tag/"ENVIRONMENT">. So these variables affect (indirectly) how this script works. =head1 OBSOLETE INTERFACE If you do not understand what it is about, it is safe to ignore this announcement: The old, pre-version=C<1.05> way (by triplication of a separator, without repetition of options) to provide multiple commands to C<-F> and <-P> options is still supported, but is strongly discouraged. (It does not conflict with the current interface.) =head1 AUTHOR Ilya Zakharevich <cpan@ilyaz.org>. =head1 Utilities to create CDDB file Good CD reapers (e.g., F<cdda2wav> with option C<cddb=0>) create a CDDB file with fetched information - as far as an Internet connection is present. However, if not available, other options exist. The scripts (supplied with the distribution in F<./examples>) can create a "stub" CDDB file basing on: =over 23 =item F<fulltoc2fake_cddb.pl> a dump of a full TOC of a CD; create one, e.g., by readcd -fulltoc dev=0,1,0 -f=audiocd =item F<inf2fake_cddb.pl> directory of F<*.inf> files (e.g., created by F<cdda2wav> without Internet connection); =item F<dir_mp3_2fake_cddb.pl> a directory of MP3 files ripped from a CD (via some guesswork). =back Passing this stub to the script F<cddb2cddb.pl>, it can be transformed to a "filled" CDDB file via a connection to some online database. Use C<-r> option if multiple records in the database match the CD signature. fulltoc2fake_cddb audiocd.toc | cddb2cddb > audio.cddb inf_2fake_cddb | cddb2cddb > audio.cddb dir_mp3_2fake_cddb | cddb2cddb -r3 > audio.cddb # 3rd record When such a CDDB file is present, it will be used by L<MP3::Tag> module to deduce the information about an audio file. This information is (by default, transparently) used by this script. =head1 SEE ALSO MP3::Tag, MP3::Tag::ParseData, audio_rename, typeset_audio_dir =cut �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/examples/mp3_total_time.pl�������������������������������������������������������������0000700�0000000�0000000�00000001231�10427536460�014524� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use File::Find 'find'; use MP3::Info; $MP3::Info::try_harder = 1; my (@f,$r); $r = shift if @ARGV and lc $ARGV[0] eq '-r'; die "Usage: $0 [-r] FILENAMES" unless @ARGV; if ($r) { find sub { -f and /\.mp3/i and push @f, $File::Find::name }, @ARGV } else { @f = @ARGV; } die "No files found" unless @f; my $t = 0; for my $f (@f) { my $info = get_mp3info($f); warn("No mp3info for `$f': $@\n"), next unless defined $info; $t += $info->{SECS} } #my @l = `mp3info -p "%S\n" @f`; #$t += $_ for @l; my $h = int($t/3600); my $m = int(($t - 3600 * $h)/60); my $s = $t - 3600 * $h - 60 * $m; printf "%.1f = %d:%02d:%02.1f\n", $t, $h, $m, $s; �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/examples/Music_Normalize_Fields-normalize.pl�������������������������������������������0000700�0000000�0000000�00000000343�10440516050�020157� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use strict; use Music_Translate_Fields; use MP3::Tag; my $tag = MP3::Tag->new(q(/dev/null)) or die; $tag->config(parse_data => [qw(mi %a), shift]); $tag->Music_Translate_Fields::normalize_file_lines(shift); ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/examples/README.txt��������������������������������������������������������������������0000700�0000000�0000000�00000006543�07544163054�012761� 0����������������������������������������������������������������������������������������������������ustar �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� Short documentation of tagged.pl, tagit.pl and extractID3v2.pl ===================================================================== You will find three perl programs as examples in this release. You can use them after installing the MP3::Tag module. Look at the README.txt in the main directory to see, how to do this. Then simply run tagged.pl or tagit.pl (see description below), giving filename(s) on the command line. mp3info.pl expects the filename(s) on standard input <STDIN>. To run the examples you need Perl installed, at least 5.x I think. I wrote this on Redhat/Debian Linux with Perl 5.6.0, but I think, it should run with other versions too. I didn't test it with windows. If you try to run it with windows, please send me a short message, either if you had success or not. Thomas <thg@users.sourceforge.net> http://tagged.sourceforge.net tagged.pl ######### tagged.pl demonstrates at the moment the function of MP3::Tag. It reads the tags of files, which are given on the command line, and prints them to the console. Later tagged.pl should be a program to change tags interactivly, to check them for consistency (is there different information in ID3v1 and ID3v2 tag and/or the filename?), and so on... mp3info.pl ########## mp3info demonstrates how easy it can be to extract some main fields from a mp3-file with the new autoinfo() function. It expects its input (filenames) on STDIN, so call it like ls *.mp3 | /path/to/mp3info.pl tagit.pl ######## tagit.pl is another demo program. It runs at the console and can change ID3v1/ID3v1.1 tags. Therefor a lot of command line switches exist. It can also set the filename of a mp3 file, according to the information found in a ID3v1 tag. For this a format string says how the filename has to be formed. With this it is for example possible to set a filename to 'artist - song.mp3' (format string: '%a - %s.mp3'), or even to put the files in directorys like 'artist/album/track. song.mp3' (format string: './%a/%l/%t. %s.mp3') And if you want that the track number always consists of two digits, do a %2:0t instead of the %t. Directorys, which do not exist yet, can be created on the fly. try tagit.pl --help to get a list of all command line options. Format string: %a - replaced with artist %s - replaced with song %l - replaced with album %t - replaced with track %y - replaced with year %g - replaced with genre %c - replaced with comment options for %x: (where x is one of a,s,l,t,y,g,c) [only valid with --setfilename] %nx => use only first n characters eg. artist="artist name" %5a = "artis" %n:cx => use at least n characters, if %x is shorter, fill it with character c eg. track=3 %2:0t = '03' , artist="abc" %5:_a = "__abc" %n!:cx => same as %n:cx, but if %x is longer than n, cut it at n eg. artist="abc" %5:_a = "__abc" artist="abcdefg" %5:_a = "abcde" extractID3v2.pl ############### This is a small utility to extract ID3v2 headers from a file. The extracted header will be written to STDOUT. It's only purpose if for testing/debugging. If you encounter a problem with a ID3v2 tag, which creates an erroeneus output, you can extract the header and sent it to me, so that I can check it (NEVER send a complete mp3 file!). Because this is an alpha release, you have to realise, that some frame/tags are not supported yet. �������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/examples/src/��������������������������������������������������������������������������0000700�0000000�0000000�00000000000�11417072072�012030� 5����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/examples/src/Beethoven.all_y�����������������������������������������������������������0000700�0000000�0000000�00000077477�10440420436�015016� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������### format: Opus number // source // guessed value // codm // beethovenlv // wiki ? // codm // pubd. 1841 / 1790 // pubd. 1841 / 1790 // // Op. 1 // guess-codm // 1792--1794 // 1792--1794 // // 1795 Op. 1-1 // beethovenlv // 1792--1793 // // 1792--1793 // Op. 1-2 // beethovenlv // 1792--1794 // // 1792--1794 // Op. 1-3 // beethovenlv // 1792--1794 // // 1792--1794 // Op. 2 // guess-codm // 1794--1795 // 1794--1795 // // 1796 Op. 2-1 // beethovenlv // 1793--1795 // // 1793--1795 // Op. 2-2 // beethovenlv // 1794--1795 // // 1794--1795 // Op. 2-3 // beethovenlv // 1794--1795 // // 1794--1795 // Op. 3 // codm // pre-1794 // pre-1794 // 1794 // 1794 Op. 4 // wiki // 1795 // 1795-1796 // 1795 // 1795 Op. 5 // wiki // 1796 // 1796 // 1796 // 1796 Op. 6 // wiki // 1797 // 1797 // 1796--1797 // 1797 Op. 7 // guess-codm // 1796 // 1796 // 1796--1797 // 1797 Op. 8 // wiki // 1797 // 1796--1797 // 1796--1797 // 1797 Op. 9 // wiki // 1798 // 1797--1798 // 1797--1798 // 1798 Op. 10 // wiki // 1798 // 1798 // // 1798 Op. 10-1 // beethovenlv // 1795--1797 // // 1795--1797 // Op. 10-2 // beethovenlv // 1796--1797 // // 1796--1797 // Op. 10-3 // beethovenlv // 1797--1798 // // 1797--1798 // Op. 11 // guess-codm // 1797 // 1797 // 1797--1798 // 1798 Op. 12 // wiki // 1798 // 1797--1798 // 1797--1798 // 1798 Op. 13 // guess-codm // 1799 // 1799 // 1797--1798 // 1799 Op. 14 // wiki // 1799 // 1799 // // 1799 Op. 14-1 // beethovenlv // 1798--1799 // // 1798--1799 // Op. 14-2 // beethovenlv // 1799 // // 1799 // Op. 15 // guess-codm // comp. 1795--1798, f.p. (presumed) Vienna, 1800-4-2, soloist Beethoven, cond. Wranitzky; pubd. 1801-3 // comp. 1795--1798, f.p. (presumed) Vienna, 1800-4-2, soloist Beethoven, cond. Wranitzky; pubd. 1801-3 // 1795--1800 // Op. 16 // guess-codm // 1796, pubd. 1801 // 1796, pubd. 1801 // 1796--1797 // 1796 Op. 17 // wiki // 1800 // 1800 // 1800 // 1800 Op. 18 // wiki // 1800 // 1798--1800 // 1798--1800 // 1800 Op. 19 // guess-codm // comp. 1794--1795, f.p. Vienna, 1795-03-29, soloist Beethoven; pubd. 1801-12 // comp. 1794--1795, f.p. Vienna, 1795-03-29, soloist Beethoven; pubd. 1801-12 // 1788--1801 // 1795 Op. 20 // wiki // 1799 // 1799--1800 // 1799 // 1799 Op. 21 // guess-codm // comp. 1799--1800, f.p. Vienna, 1800-04-02, cond. P. Wranitzky; pubd. 1801 // comp. 1799--1800, f.p. Vienna, 1800-04-02, cond. P. Wranitzky; pubd. 1801 // 1799--1800 // 1800 Op. 22 // wiki // 1800 // 1800 // 1800 // 1800 Op. 23 // guess-codm // 1800 // 1800 // 1800--1801 // 1801 Op. 24 // wiki // 1801 // 1800--1801 // 1800--1801 // 1801 Op. 25 // wiki // 1801 // 1801 // 1801 // 1801 Op. 26 // wiki // 1801 // 1800--1801 // 1800--1801 // 1801 Op. 27 // wiki // 1801 // 1800--1801 // // 1801 Op. 27-1 // beethovenlv // 1801 // // 1801 // Op. 27-2 // beethovenlv // 1800--1801 // // 1800--1801 // Op. 28 // wiki // 1801 // 1801 // 1801 // 1801 Op. 29 // wiki // 1801 // 1800--1801 // 1801 // 1801 Op. 30 // guess-codm // 1801--1802 // 1801--1802 // 1801--1802 // 1803 Op. 31 // wiki // 1802 // 1801--1802 // 1802 // 1802 Op. 32 // wiki // 1805 // 1805 // 1804--1805 // 1805 Op. 33 // wiki // 1802 // 1782--1802 // 1801--1802 // 1802 Op. 34 // wiki // 1802 // 1802 // 1802 // 1802 Op. 35 // wiki // 1802 // 1802 // 1802 // 1802 Op. 36 // guess-codm // comp. 1801--1802, f.p. Vienna, 1803-04-05, cond. Beethoven; pubd. 1804 // comp. 1801--1802, f.p. Vienna, 1803-04-05, cond. Beethoven; pubd. 1804 // 1801--1802 // 1803 Op. 37 // guess-codm // comp. 1800--1801, f.p. Vienna, 1803-04-05, soloist Beethoven; pubd. 1804 // comp. 1800--1801, f.p. Vienna, 1803-04-05, soloist Beethoven; pubd. 1804 // 1800--1803 // 1803 Op. 38 // guess-codm // 1820--1823 // 1820--1823 // 1802--1803 // 1803 Op. 39 // wiki // 1789 // // 1789 // 1789 Op. 40 // wiki // 1802 // // 1800--1802 // 1802 Op. 41 // wiki // 1803 // // 1803 // 1803 Op. 42 // wiki // 1803 // // 1803--1804 // 1803 Op. 43 // wiki // 1801 // 1800--1801 // 1800--1801 // 1801 Op. 44 // guess-codm // 1802--1803 // 1802--1803 // 1792 // 1792 Op. 45 // wiki // 1803 // // 1803 // 1803 Op. 46 // wiki // 1795 // 1795 // 1794--1795 // 1795 Op. 47 // wiki // 1802 // 1802--1803 // 1802--1803 // 1802 Op. 48 // wiki // 1802 // // 1801--1802 // 1802 Op. 49 // guess-codm // 1802 // 1802 // // 1792 Op. 49-1 // beethovenlv // 1797 // // 1797 // Op. 49-2 // beethovenlv // 1795--1796 // // 1795--1796 // Op. 50 // wiki // 1798 // // 1798 // 1798 Op. 51 // wiki // 1797 // // // 1797 Op. 51-1 // beethovenlv // 1796--1797 // // 1796--1797 // Op. 51-2 // beethovenlv // 1798 // // 1798 // Op. 52 // guess-beethovenlv // 1785--1793 // // 1785--1793 // 1805 Op. 53 // guess-codm // 1804 // 1804 // 1803--1804 // 1803 Op. 54 // wiki // 1804 // 1804 // 1804 // 1804 Op. 55 // guess-codm // comp. 1803--1804, f.pub.p. Vienna, 1805-04-07; pubd. 1806 // comp. 1803--1804, f.pub.p. Vienna, 1805-04-07; pubd. 1806 // 1803 // 1805 Op. 56 // guess-codm // comp. 1804, f.p. 1808; pubd. 1807 // comp. 1804, f.p. 1808; pubd. 1807 // 1804--1805 // 1805 Op. 57 // wiki // 1805 // 1804--1805 // 1804--1805 // 1805 Op. 58 // guess-codm // comp. 1805--1806, f.p. Vienna, 1808-12-22, soloist Beethoven; pubd. 1808 // comp. 1805--1806, f.p. Vienna, 1808-12-22, soloist Beethoven; pubd. 1808 // 1804--1807 // 1807 Op. 59 // codm // comp. 1806 // comp. 1806 // 1806 // 1806 Op. 60 // guess-codm // comp. 1806, f.pub.p. Vienna, 1807-11-15, cond. Clement; pubd. 1808 // comp. 1806, f.pub.p. Vienna, 1807-11-15, cond. Clement; pubd. 1808 // 1806 // 1807 Op. 61 // guess-codm // (arr. for pf. by Beethoven 1807, pubd. 1808) / comp. 1806, f.p. Vienna, 1806-12-23, soloist Franz Clement; publ. 1809 // (arr. for pf. by Beethoven 1807, pubd. 1808) / comp. 1806, f.p. Vienna, 1806-12-23, soloist Franz Clement; publ. 1809 // 1806,1807 // 1808 Op. 62 // wiki // 1807 // 1807 // 1807 // 1807 Op. 63 // wiki // 1806 // // // 1806 Op. 64 // wiki // 1807 // // // 1807 Op. 65 // codm // comp. 1796 // comp. 1796 // 1796 // 1796 Op. 66 // wiki // 1796 // 1796 // 1796 // 1796 Op. 67 // guess-codm // comp. 1804--1808, f.p. Vienna, 1808-12-22, cond. Beethoven; pubd. 1809 // comp. 1804--1808, f.p. Vienna, 1808-12-22, cond. Beethoven; pubd. 1809 // 1803--1808 // 1808 Op. 68 // guess-codm // comp. 1807--1808, f.p. Vienna, 1808-12-22, cond. Beethoven; pubd. 1809 // comp. 1807--1808, f.p. Vienna, 1808-12-22, cond. Beethoven; pubd. 1809 // 1807--1808 // 1808 Op. 69 // wiki // 1808 // 1807--1808 // 1807--1808 // 1808 Op. 70 // wiki // 1808 // 1808 // 1808 // 1808 Op. 71 // wiki // 1796 // // 1792--1796 // 1796 Op. 72 // guess-codm // 1805, rev. 1806 and 1814 / 1814 // 1805, rev. 1806 and 1814 / 1814 // 1804--1805,1805--1806,1814 // Op. 72-1 // beethovenlv // 1805--1806 // // 1805--1806 // Op. 72-2 // beethovenlv // 1814 // // 1814 // Op. 72a // wiki // 1805 // // // 1805 Op. 72b // wiki // 1806 // // // 1806 Op. 73 // guess-codm // comp. 1809, f.p. Leipzig, 1810-12, soloist F. Schneider, f. Vienna p. 1812-02-12, soloist Czerny; pubd. 1811 // comp. 1809, f.p. Leipzig, 1810-12, soloist F. Schneider, f. Vienna p. 1812-02-12, soloist Czerny; pubd. 1811 // 1808--1809 // 1809 Op. 74 // wiki // 1809 // 1809 // 1809 // 1809 Op. 75 // wiki // 1809 // // 1809 // 1809 Op. 76 // guess-codm // 1810 // 1810 // 1809 // 1809 Op. 77 // guess-codm // 1810 // 1810 // 1809 // 1809 Op. 78 // wiki // 1809 // 1809 // 1809 // 1809 Op. 79 // wiki // 1809 // 1809 // 1809 // 1809 Op. 80 // wiki // 1808 // 1808 // 1808--1809 // 1808 Op. 81-1 // beethovenlv // 1809--1810 // // 1809--1810 // Op. 81-2 // beethovenlv // 1795 // // 1795 // Op. 81a // wiki // 1809 // 1809--1810 // // 1809 Op. 81b // codm // ?1795 // ?1795 // // 1795 Op. 82 // wiki // 1809 // // 1809--1810 // 1809 Op. 83 // wiki // 1810 // // 1810 // 1810 Op. 84 // wiki // 1810 // 1809--1810 // 1809--1810 // 1810 Op. 85 // wiki // 1803 // 1803 // 1803--1804 // 1803 Op. 86 // wiki // 1807 // 1807 // 1807 // 1807 Op. 87 // wiki // 1795 // // 1794--1795 // 1795 Op. 88 // wiki // 1803 // // 1803 // 1803 Op. 89 // wiki // 1814 // // 1814 // 1814 Op. 90 // wiki // 1814 // 1814 // 1814 // 1814 Op. 91 // guess-codm // comp. 1813, f.p. Vienna, 1813-12-08, cond. Beethoven; pubd. 1816 // comp. 1813, f.p. Vienna, 1813-12-08, cond. Beethoven; pubd. 1816 // 1813 // 1813 Op. 92 // guess-codm // comp. 1811--1812, f.p. Vienna, 1813-12-08, cond. Beethoven; pubd. 1816 // comp. 1811--1812, f.p. Vienna, 1813-12-08, cond. Beethoven; pubd. 1816 // 1811--1812 // 1813 Op. 93 // guess-codm // comp. 1812, f.p. Vienna, 1814-02-27, cond. Beethoven; pubd. 1816 // comp. 1812, f.p. Vienna, 1814-02-27, cond. Beethoven; pubd. 1816 // 1812 // 1814 Op. 94 // guess-beethovenlv // 1813--1815 // // 1813--1815 // 1814 Op. 95 // wiki // 1810 // 1810 // 1810--1811 // 1810 Op. 96 // guess-codm // 1812, rev. 1815 // 1812, rev. 1815 // 1812 // 1812 Op. 97 // guess-codm // 1810--1811 // 1810--1811 // 1810--1815 // 1811 Op. 98 // wiki // 1816 // 1816 // 1816 // 1816 Op. 99 // wiki // 1816 // // 1816 // 1816 Op. 100 // guess-beethovenlv // 1815 // // 1815 // 1814 Op. 101 // wiki // 1816 // 1816 // 1816 // 1816 Op. 102 // wiki // 1815 // 1815 // 1815 // 1815 Op. 103 // wiki // 1792 // // 1792--1793 // 1792 Op. 104 // guess-codm // arr. by Beethoven in 1817 of his pf. trio, Op.1, No.3 (1792-4) // arr. by Beethoven in 1817 of his pf. trio, Op.1, No.3 (1792-4) // 1817 // Op. 105 // wiki // 1819 // // // 1819 Op. 105-1 // beethovenlv // 1818 // // 1818 // Op. 105-2 // beethovenlv // 1818 // // 1818 // Op. 105-3 // beethovenlv // 1819 // // 1819 // Op. 105-4 // beethovenlv // 1818 // // 1818 // Op. 105-5 // beethovenlv // 1818 // // 1818 // Op. 105-6 // beethovenlv // 1818 // // 1818 // Op. 106 // wiki // 1818 // 1817--1818 // 1816--1818 // 1818 Op. 107 // wiki // 1820 // // // 1820 Op. 107-1 // beethovenlv // 1818 // // 1818 // Op. 107-10 // beethovenlv // 1818 // // 1818 // Op. 107-2 // beethovenlv // 1818 // // 1818 // Op. 107-3 // beethovenlv // 1819 // // 1819 // Op. 107-4 // beethovenlv // 1818 // // 1818 // Op. 107-5 // beethovenlv // 1818 // // 1818 // Op. 107-6 // beethovenlv // 1819 // // 1819 // Op. 107-7 // beethovenlv // 1819 // // 1819 // Op. 107-8 // beethovenlv // 1818 // // 1818 // Op. 107-9 // beethovenlv // 1818 // // 1818 // Op. 108 // guess-codm // 1815-1816 // 1815-1816 // // 1818 Op. 108-1 // beethovenlv // 1817 // // 1817 // Op. 108-10 // beethovenlv // 1815 // // 1815 // Op. 108-11 // beethovenlv // 1815 // // 1815 // Op. 108-12 // beethovenlv // 1816 // // 1816 // Op. 108-13 // beethovenlv // 1817 // // 1817 // Op. 108-14 // beethovenlv // 1816 // // 1816 // Op. 108-15 // beethovenlv // 1816 // // 1816 // Op. 108-16 // beethovenlv // 1816 // // 1816 // Op. 108-17 // beethovenlv // 1817 // // 1817 // Op. 108-18 // beethovenlv // 1818 // // 1818 // Op. 108-19 // beethovenlv // 1815 // // 1815 // Op. 108-2 // beethovenlv // 1818 // // 1818 // Op. 108-20 // beethovenlv // 1813 // // 1813 // Op. 108-21 // beethovenlv // 1817 // // 1817 // Op. 108-22 // beethovenlv // 1817 // // 1817 // Op. 108-23 // beethovenlv // 1818 // // 1818 // Op. 108-24 // beethovenlv // 1815 // // 1815 // Op. 108-25 // beethovenlv // 1817 // // 1817 // Op. 108-3 // beethovenlv // 1817 // // 1817 // Op. 108-4 // beethovenlv // 1817 // // 1817 // Op. 108-5 // beethovenlv // 1815 // // 1815 // Op. 108-6 // beethovenlv // 1815 // // 1815 // Op. 108-7 // beethovenlv // 1815 // // 1815 // Op. 108-8 // beethovenlv // 1816 // // 1816 // Op. 108-9 // beethovenlv // 1817 // // 1817 // Op. 109 // guess-codm // 1820 // 1820 // 1820 // 1822 Op. 110 // guess-codm // 1821 // 1821 // 1821--1822 // 1822 Op. 111 // wiki // 1822 // 1821--1822 // 1821--1822 // 1822 Op. 112 // wiki // 1815 // 1814--1815 // 1814--1815 // 1815 Op. 113 // wiki // 1811 // 1811 // 1811 // 1811 Op. 114 // beethovenlv // 1822 // // 1822 // Op. 115 // wiki // 1815 // 1814--1815 // 1814--1815 // 1815 Op. 116 // wiki // 1802 // // 1802--1814 // 1802 Op. 117 // wiki // 1811 // 1811 // 1811 // 1811 Op. 118 // wiki // 1814 // // 1814 // 1814 Op. 119 // guess-codm // 1821 // 1821 // // 1822 Op. 119-1 // beethovenlv // 1820--1822 // // 1820--1822 // Op. 119-10 // beethovenlv // 1820--1821 // // 1820--1821 // Op. 119-11 // beethovenlv // 1820--1821 // // 1820--1821 // Op. 119-2 // beethovenlv // 1820--1822 // // 1820--1822 // Op. 119-3 // beethovenlv // 1820--1822 // // 1820--1822 // Op. 119-4 // beethovenlv // 1820--1822 // // 1820--1822 // Op. 119-5 // beethovenlv // 1820--1822 // // 1820--1822 // Op. 119-6 // beethovenlv // 1820--1822 // // 1820--1822 // Op. 119-7 // beethovenlv // 1820--1821 // // 1820--1821 // Op. 119-8 // beethovenlv // 1820--1821 // // 1820--1821 // Op. 119-9 // beethovenlv // 1820--1821 // // 1820--1821 // Op. 120 // wiki // 1823 // 1819--1823 // 1819--1823 // 1823 Op. 121 // wiki // 1803 // // // 1803 Op. 121-1 // beethovenlv // 1803--1816 // // 1803--1816 // Op. 121-2 // beethovenlv // 1822--1824 // // 1822--1824 // Op. 121a // codm // c.1798 // c.1798 // // Op. 121b // wiki // 1822 // // // 1822 Op. 122 // wiki // 1824 // // 1822--1824 // 1824 Op. 123 // guess-codm // 1819--1822 // 1819--1822 // 1819--1823 // 1822 Op. 124 // wiki // 1822 // 1822 // 1822 // 1822 Op. 125 // guess-codm // comp. 1817--1823, f.p. Vienna, 1824-05-07, cond. Beethoven; pubd. 1826 // comp. 1817--1823, f.p. Vienna, 1824-05-07, cond. Beethoven; pubd. 1826 // 1817--1824 // 1824 Op. 126 // wiki // 1824 // 1823--1824 // 1824 // 1824 Op. 127 // wiki // 1825 // 1822--1825 // 1822--1825 // 1825 Op. 128 // wiki // 1822 // // 1798--1822 // 1822 Op. 129 // guess-codm // 1825--1826 // 1825--1826 // 1795 // 1795 Op. 130 // wiki // 1825 // 1825-1826 // 1825--1826 // 1825 Op. 131 // wiki // 1826 // 1825--1826 // 1825--1826 // 1826 Op. 132 // wiki // 1825 // 1825 // 1823--1825 // 1825 Op. 133 // guess-codm // 1825 // 1825 // 1825--1826 // 1826 Op. 134 // wiki // 1826 // 1826 // 1826 // 1826 Op. 135 // wiki // 1826 // 1826 // 1826 // 1826 Op. 136 // wiki // 1814 // 1814 // 1814 // 1814 Op. 137 // wiki // 1817 // // 1817 // 1817 Op. 138 // guess-codm // 1805 / 1806 // 1805 / 1806 // 1807 // 1807 WoO 1 // beethovenlv // 1790--1791 // // 1790--1791 // WoO 2 // beethovenlv // 1813 // // 1813 // WoO 3 // beethovenlv // 1822 // // 1822 // WoO 4 // beethovenlv // 1784 // // 1784 // WoO 5 // beethovenlv // 1790--1792 // // 1790--1792 // WoO 6 // beethovenlv // 1793 // // 1793 // WoO 7 // beethovenlv // 1795 // // 1795 // WoO 8 // beethovenlv // 1795 // // 1795 // WoO 9 // beethovenlv // 1795 // // 1795 // WoO 10 // beethovenlv // 1795 // // 1795 // WoO 11 // beethovenlv // 1799 // // 1799 // WoO 12 // beethovenlv // 1799 // // 1799 // WoO 13 // beethovenlv // 1792--1797 // // 1792--1797 // WoO 14-1 // beethovenlv // 1791--1802 // // 1791--1802 // WoO 14-10 // beethovenlv // 1801--1802 // // 1801--1802 // WoO 14-11 // beethovenlv // 1800--1802 // // 1800--1802 // WoO 14-12 // beethovenlv // 1791--1802 // // 1791--1802 // WoO 14-2 // beethovenlv // 1801--1802 // // 1801--1802 // WoO 14-3 // beethovenlv // 1795--1802 // // 1795--1802 // WoO 14-4 // beethovenlv // 1795--1802 // // 1795--1802 // WoO 14-5 // beethovenlv // 1791--1802 // // 1791--1802 // WoO 14-6 // beethovenlv // 1795--1802 // // 1795--1802 // WoO 14-7 // beethovenlv // 1800--1802 // // 1800--1802 // WoO 14-8 // beethovenlv // 1791--1802 // // 1791--1802 // WoO 14-9 // beethovenlv // 1801--1802 // // 1801--1802 // WoO 15 // beethovenlv // 1802 // // 1802 // WoO 17 // beethovenlv // 1819 // // 1819 // WoO 18 // beethovenlv // 1809--1810 // // 1809--1810 // WoO 19 // beethovenlv // 1810 // // 1810 // WoO 20 // beethovenlv // 1809--1822 // // 1809--1822 // WoO 21 // beethovenlv // 1810 // // 1810 // WoO 22 // beethovenlv // 1809--1810 // // 1809--1810 // WoO 23 // beethovenlv // 1810 // // 1810 // WoO 24 // beethovenlv // 1816 // // 1816 // WoO 25 // beethovenlv // 1792--1793 // // 1792--1793 // WoO 26 // beethovenlv // 1792 // // 1792 // WoO 28 // beethovenlv // 1795 // // 1795 // WoO 29 // beethovenlv // 1797--1798 // // 1797--1798 // WoO 30 // beethovenlv // 1812 // // 1812 // WoO 31 // beethovenlv // 1783 // // 1783 // WoO 32 // beethovenlv // 1796--1797 // // 1796--1797 // WoO 33-1 // beethovenlv // 1799 // // 1799 // WoO 33-2 // beethovenlv // 1799--1800 // // 1799--1800 // WoO 33-3 // beethovenlv // 1799 // // 1799 // WoO 33-4 // beethovenlv // 1794 // // 1794 // WoO 33-5 // beethovenlv // 1794 // // 1794 // WoO 34 // beethovenlv // 1822 // // 1822 // WoO 35 // beethovenlv // 1825 // // 1825 // WoO 36 // beethovenlv // 1785 // // 1785 // WoO 37 // beethovenlv // 1786 // // 1786 // WoO 38 // beethovenlv // 1785--1791 // // 1785--1791 // WoO 39 // beethovenlv // 1812 // // 1812 // WoO 40 // beethovenlv // 1792--1793 // // 1792--1793 // WoO 41 // beethovenlv // 1793--1794 // // 1793--1794 // WoO 42 // beethovenlv // 1796 // // 1796 // WoO 43 // beethovenlv // 1796 // // 1796 // WoO 44 // beethovenlv // 1796 // // 1796 // WoO 45 // beethovenlv // 1796 // 1796 // 1796 // WoO 46 // beethovenlv // 1801 // 1801 // 1801 // WoO 47 // beethovenlv // 1783 // // 1783 // WoO 48 // beethovenlv // 1783 // // 1783 // WoO 49 // beethovenlv // 1783 // // 1783 // WoO 50 // beethovenlv // 1790--1792 // // 1790--1792 // WoO 51 // beethovenlv // 1791--1798 // // 1791--1798 // WoO 52 // beethovenlv // 1795--1822 // // 1795--1822 // WoO 53 // beethovenlv // 1796--1797 // // 1796--1797 // WoO 54 // beethovenlv // 1802 // // 1802 // WoO 55 // beethovenlv // 1803 // // 1803 // WoO 56 // beethovenlv // 1803--1822 // // 1803--1822 // WoO 57 // guess-beethovenlv // 1803--1804 // // 1803--1804 // 1805 WoO 58 // beethovenlv // 1809 // // 1809 // WoO 59 // wiki // 1808 // // 1808--1810 // 1808 WoO 60 // beethovenlv // 1818 // // 1818 // WoO 61 // beethovenlv // 1821 // // 1821 // WoO 61-1 // beethovenlv // 1825 // // 1825 // WoO 62 // beethovenlv // 1826 // // 1826 // WoO 63 // beethovenlv // 1782 // // 1782 // WoO 64 // beethovenlv // 1790--1792 // // 1790--1792 // WoO 65 // beethovenlv // 1790--1791 // // 1790--1791 // WoO 66 // beethovenlv // 1792 // // 1792 // WoO 67 // beethovenlv // 1792 // // 1792 // WoO 68 // beethovenlv // 1795 // // 1795 // WoO 69 // beethovenlv // 1795 // // 1795 // WoO 70 // beethovenlv // 1795 // // 1795 // WoO 71 // beethovenlv // 1796--1797 // // 1796--1797 // WoO 72 // beethovenlv // 1795--1798 // // 1795--1798 // WoO 73 // beethovenlv // 1799 // // 1799 // WoO 74 // beethovenlv // 1799--1803 // // 1799--1803 // WoO 75 // beethovenlv // 1792--1799 // // 1792--1799 // WoO 76 // beethovenlv // 1799 // // 1799 // WoO 77 // beethovenlv // 1800 // // 1800 // WoO 78 // beethovenlv // 1802--1803 // // 1802--1803 // WoO 79 // beethovenlv // 1803 // // 1803 // WoO 80 // beethovenlv // 1806 // 1806-1807 // 1806 // WoO 81 // beethovenlv // 1793--1822 // // 1793--1822 // WoO 82 // beethovenlv // 1803 // // 1803 // WoO 83 // beethovenlv // 1806 // // 1806 // WoO 84 // beethovenlv // 1824 // // 1824 // WoO 85 // beethovenlv // 1825 // // 1825 // WoO 86 // beethovenlv // 1825 // // 1825 // WoO 87 // beethovenlv // 1790 // // 1790 // WoO 88 // beethovenlv // 1790 // // 1790 // WoO 89 // beethovenlv // 1790--1792 // // 1790--1792 // WoO 90 // beethovenlv // 1790--1792 // // 1790--1792 // WoO 91 // beethovenlv // 1795 // // 1795 // WoO 92 // beethovenlv // 1791--1792 // // 1791--1792 // WoO 92-1 // beethovenlv // 1802 // // 1802 // WoO 93 // beethovenlv // 1802 // // 1802 // WoO 94 // beethovenlv // 1814 // // 1814 // WoO 95 // beethovenlv // 1814 // // 1814 // WoO 96 // beethovenlv // 1815 // // 1815 // WoO 97 // beethovenlv // 1815 // // 1815 // WoO 98 // beethovenlv // 1822 // // 1822 // WoO 99-1 // beethovenlv // 1801--1803 // // 1801--1803 // WoO 99-10.1 // beethovenlv // 1801--1803 // // 1801--1803 // WoO 99-10.2 // beethovenlv // 1801--1803 // // 1801--1803 // WoO 99-10.3 // beethovenlv // 1801--1803 // // 1801--1803 // WoO 99-11 // beethovenlv // 1801--1803 // // 1801--1803 // WoO 99-12 // beethovenlv // 1801--1802 // // 1801--1802 // WoO 99-2 // beethovenlv // 1801--1803 // // 1801--1803 // WoO 99-3.1 // beethovenlv // 1801--1803 // // 1801--1803 // WoO 99-3.2 // beethovenlv // 1801--1803 // // 1801--1803 // WoO 99-3.3 // beethovenlv // 1801--1803 // // 1801--1803 // WoO 99-4.1 // beethovenlv // 1801--1803 // // 1801--1803 // WoO 99-4.2 // beethovenlv // 1801--1803 // // 1801--1803 // WoO 99-5.1 // beethovenlv // 1801--1803 // // 1801--1803 // WoO 99-5.2 // beethovenlv // 1801--1803 // // 1801--1803 // WoO 99-6 // beethovenlv // 1801--1803 // // 1801--1803 // WoO 99-7.1 // beethovenlv // 1801--1803 // // 1801--1803 // WoO 99-7.2 // beethovenlv // 1801--1803 // // 1801--1803 // WoO 99-8 // beethovenlv // 1794--1795 // // 1794--1795 // WoO 99-9 // beethovenlv // 1801--1803 // // 1801--1803 // WoO 100 // beethovenlv // 1801 // // 1801 // WoO 101 // beethovenlv // 1802 // // 1802 // WoO 102 // beethovenlv // 1814 // // 1814 // WoO 103 // beethovenlv // 1814 // // 1814 // WoO 104 // beethovenlv // 1817 // // 1817 // WoO 105 // beethovenlv // 1819 // // 1819 // WoO 106 // beethovenlv // 1823 // // 1823 // WoO 107 // beethovenlv // 1782 // // 1782 // WoO 108 // beethovenlv // 1783 // // 1783 // WoO 109 // beethovenlv // 1791--1792 // // 1791--1792 // WoO 110 // beethovenlv // 1790 // // 1790 // WoO 111 // beethovenlv // 1791--1792 // // 1791--1792 // WoO 112 // beethovenlv // 1792 // // 1792 // WoO 113 // beethovenlv // 1790 // // 1790 // WoO 114 // beethovenlv // 1793 // // 1793 // WoO 115 // beethovenlv // 1792 // // 1792 // WoO 116 // beethovenlv // 1793 // // 1793 // WoO 117 // beethovenlv // 1792--1794 // // 1792--1794 // WoO 118 // beethovenlv // 1794--1795 // // 1794--1795 // WoO 119 // beethovenlv // 1794--1795 // // 1794--1795 // WoO 120 // beethovenlv // 1800--1802 // // 1800--1802 // WoO 121 // beethovenlv // 1796 // // 1796 // WoO 122 // beethovenlv // 1797 // // 1797 // WoO 123 // beethovenlv // 1795 // // 1795 // WoO 124 // beethovenlv // 1795 // // 1795 // WoO 125 // beethovenlv // 1798--1799 // // 1798--1799 // WoO 126 // beethovenlv // 1794--1795 // // 1794--1795 // WoO 127 // beethovenlv // 1798--1799 // // 1798--1799 // WoO 128 // beethovenlv // 1798--1799 // // 1798--1799 // WoO 129 // beethovenlv // 1803 // // 1803 // WoO 130 // beethovenlv // 1804--1820 // // 1804--1820 // WoO 131 // beethovenlv // 1794--1796 // // 1794--1796 // WoO 132 // beethovenlv // 1806 // // 1806 // WoO 133 // beethovenlv // 1806--1807 // // 1806--1807 // WoO 134 // beethovenlv // 1807--1808 // // 1807--1808 // WoO 135 // beethovenlv // 1814--1815 // // 1814--1815 // WoO 136 // beethovenlv // 1808 // // 1808 // WoO 137 // beethovenlv // 1809 // // 1809 // WoO 138 // beethovenlv // 1809 // // 1809 // WoO 139 // beethovenlv // 1809 // // 1809 // WoO 140-1 // beethovenlv // 1811 // // 1811 // WoO 140-2 // beethovenlv // 1814 // // 1814 // WoO 141 // beethovenlv // 1813 // // 1813 // WoO 142 // beethovenlv // 1813 // // 1813 // WoO 143 // beethovenlv // 1814 // // 1814 // WoO 144 // beethovenlv // 1814 // // 1814 // WoO 145 // beethovenlv // 1815 // // 1815 // WoO 146 // beethovenlv // 1816 // // 1816 // WoO 147 // beethovenlv // 1816 // // 1816 // WoO 148 // beethovenlv // 1817 // // 1817 // WoO 149 // beethovenlv // 1817 // // 1817 // WoO 150 // beethovenlv // 1820 // // 1820 // WoO 151 // beethovenlv // 1823 // // 1823 // WoO 152-1 // beethovenlv // 1810 // // 1810 // WoO 152-10 // beethovenlv // 1812 // // 1812 // WoO 152-11 // beethovenlv // 1812 // // 1812 // WoO 152-12 // beethovenlv // 1810 // // 1810 // WoO 152-13 // beethovenlv // 1812 // // 1812 // WoO 152-14 // beethovenlv // 1810 // // 1810 // WoO 152-15 // beethovenlv // 1810 // // 1810 // WoO 152-16 // beethovenlv // 1810 // // 1810 // WoO 152-17 // beethovenlv // 1810 // // 1810 // WoO 152-18 // beethovenlv // 1810 // // 1810 // WoO 152-19 // beethovenlv // 1812 // // 1812 // WoO 152-2 // beethovenlv // 1810 // // 1810 // WoO 152-20 // beethovenlv // 1810 // // 1810 // WoO 152-21 // beethovenlv // 1812 // // 1812 // WoO 152-22 // beethovenlv // 1812 // // 1812 // WoO 152-23 // beethovenlv // 1810 // // 1810 // WoO 152-24 // beethovenlv // 1812 // // 1812 // WoO 152-25 // beethovenlv // 1812 // // 1812 // WoO 152-3 // beethovenlv // 1810 // // 1810 // WoO 152-4 // beethovenlv // 1810 // // 1810 // WoO 152-5 // beethovenlv // 1810 // // 1810 // WoO 152-6 // beethovenlv // 1810 // // 1810 // WoO 152-7 // beethovenlv // 1810 // // 1810 // WoO 152-8 // beethovenlv // 1810 // // 1810 // WoO 152-9 // beethovenlv // 1810 // // 1810 // WoO 153-1 // beethovenlv // 1810 // // 1810 // WoO 153-10 // beethovenlv // 1810 // // 1810 // WoO 153-11 // beethovenlv // 1813 // // 1813 // WoO 153-12 // beethovenlv // 1813 // // 1813 // WoO 153-13 // beethovenlv // 1815 // // 1815 // WoO 153-14 // beethovenlv // 1810 // // 1810 // WoO 153-15 // beethovenlv // 1813 // // 1813 // WoO 153-16 // beethovenlv // 1813 // // 1813 // WoO 153-17 // beethovenlv // 1813 // // 1813 // WoO 153-18 // beethovenlv // 1813 // // 1813 // WoO 153-19 // beethovenlv // 1813 // // 1813 // WoO 153-2 // beethovenlv // 1810 // // 1810 // WoO 153-20 // beethovenlv // 1813 // // 1813 // WoO 153-3 // beethovenlv // 1810 // // 1810 // WoO 153-4 // beethovenlv // 1810 // // 1810 // WoO 153-5 // beethovenlv // 1813 // // 1813 // WoO 153-6 // beethovenlv // 1815 // // 1815 // WoO 153-7 // beethovenlv // 1813 // // 1813 // WoO 153-8 // beethovenlv // 1812--1813 // // 1812--1813 // WoO 153-9 // beethovenlv // 1813 // // 1813 // WoO 154 // beethovenlv // 1813 // // 1813 // WoO 155-1 // beethovenlv // 1810 // // 1810 // WoO 155-10 // beethovenlv // 1810 // // 1810 // WoO 155-11 // beethovenlv // 1810 // // 1810 // WoO 155-12 // beethovenlv // 1810 // // 1810 // WoO 155-13 // beethovenlv // 1810 // // 1810 // WoO 155-14 // beethovenlv // 1810 // // 1810 // WoO 155-15 // beethovenlv // 1813 // // 1813 // WoO 155-16 // beethovenlv // 1810 // // 1810 // WoO 155-17 // beethovenlv // 1810 // // 1810 // WoO 155-18 // beethovenlv // 1810 // // 1810 // WoO 155-19 // beethovenlv // 1810 // // 1810 // WoO 155-2 // beethovenlv // 1810 // // 1810 // WoO 155-20 // beethovenlv // 1813 // // 1813 // WoO 155-21 // beethovenlv // 1810 // // 1810 // WoO 155-22 // beethovenlv // 1810 // // 1810 // WoO 155-23 // beethovenlv // 1810 // // 1810 // WoO 155-24 // beethovenlv // 1810 // // 1810 // WoO 155-25 // beethovenlv // 1815 // // 1815 // WoO 155-26 // beethovenlv // 1810 // // 1810 // WoO 155-3 // beethovenlv // 1810 // // 1810 // WoO 155-4 // beethovenlv // 1810 // // 1810 // WoO 155-5 // beethovenlv // 1810 // // 1810 // WoO 155-6 // beethovenlv // 1810 // // 1810 // WoO 155-7 // beethovenlv // 1810 // // 1810 // WoO 155-8 // beethovenlv // 1810 // // 1810 // WoO 155-9 // beethovenlv // 1810 // // 1810 // WoO 156-1 // beethovenlv // 1819 // // 1819 // WoO 156-10 // beethovenlv // 1819 // // 1819 // WoO 156-11 // beethovenlv // 1818 // // 1818 // WoO 156-12 // beethovenlv // 1818 // // 1818 // WoO 156-2 // beethovenlv // 1818 // // 1818 // WoO 156-3 // beethovenlv // 1819 // // 1819 // WoO 156-4 // beethovenlv // 1818 // // 1818 // WoO 156-5 // beethovenlv // 1817 // // 1817 // WoO 156-6 // beethovenlv // 1815 // // 1815 // WoO 156-7 // beethovenlv // 1818 // // 1818 // WoO 156-8 // beethovenlv // 1818 // // 1818 // WoO 156-9 // beethovenlv // 1818 // // 1818 // WoO 157-1 // beethovenlv // 1817 // // 1817 // WoO 157-10 // beethovenlv // 1817 // // 1817 // WoO 157-11 // beethovenlv // 1815 // // 1815 // WoO 157-12 // beethovenlv // 1816 // // 1816 // WoO 157-2 // beethovenlv // 1815 // // 1815 // WoO 157-3 // beethovenlv // 1819 // // 1819 // WoO 157-4 // beethovenlv // 1817 // // 1817 // WoO 157-5 // beethovenlv // 1819 // // 1819 // WoO 157-6 // beethovenlv // 1815 // // 1815 // WoO 157-7 // beethovenlv // 1815 // // 1815 // WoO 157-8 // beethovenlv // 1815 // // 1815 // WoO 157-9 // beethovenlv // 1820 // // 1820 // WoO 158-1 // beethovenlv // 1813,1817 // // 1813,1817 // WoO 158-10 // beethovenlv // 1816 // // 1816 // WoO 158-11 // beethovenlv // 1816 // // 1816 // WoO 158-12 // beethovenlv // 1816 // // 1816 // WoO 158-13 // beethovenlv // 1816 // // 1816 // WoO 158-14 // beethovenlv // 1816 // // 1816 // WoO 158-15 // beethovenlv // 1816 // // 1816 // WoO 158-16 // beethovenlv // 1816 // // 1816 // WoO 158-17 // beethovenlv // 1817 // // 1817 // WoO 158-18 // beethovenlv // 1816 // // 1816 // WoO 158-19 // beethovenlv // 1816 // // 1816 // WoO 158-2 // beethovenlv // 1813,1816,1817 // // 1813,1816,1817 // WoO 158-20 // beethovenlv // 1816 // // 1816 // WoO 158-21 // beethovenlv // 1816 // // 1816 // WoO 158-22 // beethovenlv // 1817 // // 1817 // WoO 158-23 // beethovenlv // 1816 // // 1816 // WoO 158-3 // beethovenlv // 1816,1817,1820 // // 1816,1817,1820 // WoO 158-4 // beethovenlv // 1816,1817,1820 // // 1816,1817,1820 // WoO 158-5 // beethovenlv // 1815,1816,1818 // // 1815,1816,1818 // WoO 158-6 // beethovenlv // 1810,1815,1816 // // 1810,1815,1816 // WoO 158-7 // beethovenlv // 1810,1817 // // 1810,1817 // WoO 158-8 // beethovenlv // 1817 // // 1817 // WoO 158-9 // beethovenlv // 1816 // // 1816 // WoO 159 // beethovenlv // 1795 // // 1795 // WoO 160 // beethovenlv // 1795 // // 1795 // WoO 161 // beethovenlv // 1811 // // 1811 // WoO 162 // beethovenlv // 1812 // // 1812 // WoO 163 // beethovenlv // 1813 // // 1813 // WoO 164 // beethovenlv // 1814 // // 1814 // WoO 165 // beethovenlv // 1815 // // 1815 // WoO 166 // beethovenlv // 1815 // // 1815 // WoO 167 // beethovenlv // 1815 // // 1815 // WoO 168 // beethovenlv // 1816 // // 1816 // WoO 169 // beethovenlv // 1816 // // 1816 // WoO 170 // beethovenlv // 1816 // // 1816 // WoO 171 // beethovenlv // 1817 // // 1817 // WoO 172 // beethovenlv // 1818 // // 1818 // WoO 173 // beethovenlv // 1819 // // 1819 // WoO 174 // beethovenlv // 1819 // // 1819 // WoO 175 // beethovenlv // 1820 // // 1820 // WoO 176 // beethovenlv // 1819 // // 1819 // WoO 177 // beethovenlv // 1820 // // 1820 // WoO 178 // beethovenlv // 1820 // // 1820 // WoO 179 // beethovenlv // 1819 // // 1819 // WoO 180 // beethovenlv // 1820 // // 1820 // WoO 181 // beethovenlv // 1820 // // 1820 // WoO 182 // beethovenlv // 1821 // // 1821 // WoO 183 // beethovenlv // 1823 // // 1823 // WoO 184 // beethovenlv // 1823 // // 1823 // WoO 185 // beethovenlv // 1823 // // 1823 // WoO 186 // beethovenlv // 1824 // // 1824 // WoO 187 // beethovenlv // 1824 // // 1824 // WoO 188 // beethovenlv // 1825 // // 1825 // WoO 189 // beethovenlv // 1825 // // 1825 // WoO 190 // beethovenlv // 1825 // // 1825 // WoO 191 // beethovenlv // 1825 // // 1825 // WoO 192 // beethovenlv // 1825 // // 1825 // WoO 193 // beethovenlv // 1825 // // 1825 // WoO 194 // beethovenlv // 1825 // // 1825 // WoO 195 // beethovenlv // 1825 // // 1825 // WoO 196 // beethovenlv // 1826 // // 1826 // WoO 197 // beethovenlv // 1826 // // 1826 // WoO 198 // beethovenlv // 1826 // // 1826 // WoO 199 // beethovenlv // 1814 // // 1814 // WoO 200 // beethovenlv // 1818 // // 1818 // WoO 201 // beethovenlv // 1818 // // 1818 // WoO 202 // beethovenlv // 1823 // // 1823 // WoO 203 // beethovenlv // 1825 // // 1825 // WoO 204 // beethovenlv // 1825 // // 1825 // WoO 205-1 // beethovenlv // 1798 // // 1798 // WoO 205-10 // beethovenlv // 1826 // // 1826 // WoO 205-2 // beethovenlv // 1814 // // 1814 // WoO 205-3 // beethovenlv // 1817 // // 1817 // WoO 205-4 // beethovenlv // 1817 // // 1817 // WoO 205-5 // beethovenlv // 1819 // // 1819 // WoO 205-6 // beethovenlv // 1822 // // 1822 // WoO 205-7 // beethovenlv // 1824 // // 1824 // WoO 205-8 // beethovenlv // 1825 // // 1825 // WoO 205-9 // beethovenlv // 1826 // // 1826 // �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/examples/src/HOWTO���������������������������������������������������������������������0000700�0000000�0000000�00000006655�10440426362�012672� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������J:\test-programs\perl\beethoven_process.pl 0 Opus SubOp Dates m:/html/beethoven/Oeuvres/ListOpus.html >Beethoven.lv-Op J:\test-programs\perl\beethoven_process.pl 0 WoO SubWoO "Composition Dates" m:/html/beethoven/Oeuvres/ListWoO.html >Beethoven.lv-WoO perl "-F/\x20\/\/\x20/" -wnale "$l{$F[0]}{$F[1] or q()} = $F[2] if $F[2]; END {my $pref = q(Op. ); for $op (sort keys %l) {my @v = values %{$l{$op}}; my $v = $v[0]; my $same = 1; $_ eq $v or $same = 0 for @v; if ($same) {print qq($pref$op // $v)} else {for my $n (sort keys %{$l{$op}}) {print qq($pref$op-$n // $l{$op}{$n});}}}}" Beethoven.lv-Op >Beethoven.lv perl "-F/\x20\/\/\x20/" -wnale "$l{$F[0]}{$F[1] or q()} = $F[2] if $F[2]; END {my $pref = q(WoO ); for $op (sort keys %l) {my @v = values %{$l{$op}}; my $v = $v[0]; my $same = 1; $_ eq $v or $same = 0 for @v; if ($same) {print qq($pref$op // $v)} else {for my $n (sort keys %{$l{$op}}) {print qq($pref$op-$n // $l{$op}{$n});}}}}" Beethoven.lv-WoO >>Beethoven.lv perl "-F/\x20\/\/\x20/" -wnale "push @{$l{$F[0]}{$F[1] or q()}}, $F[2] if $F[2] and (not $l{$F[0]}{$F[1] or q()} or $l{$F[0]}{$F[1] or q()}[0] ne $F[2]); END {my $pref = q(Op. ); for $op (sort keys %l) {my @v = values %{$l{$op}}; my $v = qq(@{$v[0]}); my $same = 1; qq(@$_) eq $v or $same = 0 for @v; if ($same) {print qq($pref$op // $v)} else {for my $n (sort keys %{$l{$op}}) {print qq($pref$op-$n // @{$l{$op}{$n}});}}}}" Beethoven.lv-Op >Beethoven.lv perl "-F/\x20\/\/\x20/" -wnale "push @{$l{$F[0]}{$F[1] or q()}}, $F[2] if $F[2] and (not $l{$F[0]}{$F[1] or q()} or $l{$F[0]}{$F[1] or q()}[0] ne $F[2]); END {my $pref = q(WoO ); for $op (sort keys %l) {my @v = values %{$l{$op}}; my $v = qq(@{$v[0]}); my $same = 1; qq(@$_) eq $v or $same = 0 for @v; if ($same) {print qq($pref$op // $v)} else {for my $n (sort keys %{$l{$op}}) {print qq($pref$op-$n // @{$l{$op}{$n}});}}}}" Beethoven.lv-WoO >>Beethoven.lv perl "-F/\x20\/\/\x20/" -wnale "($y = $F[1]) =~ s/\s*--?\s*/--/g; $y = join q(,), sort split /\s+/, $y; print qq($F[0] // $y)" Beethoven.lv >Beethoven.lv-y compare-lv.pl "!=" >o-neq compare-lv.pl ">" >o-more compare-lv.pl "<" >o-less ## Find duplicates (same for Op): perl "-F/\x20\/\/\x20/" -wnale "$F[1] ||= ''; print qq($F[0]-$F[1])" Beethoven.lv-WoO | sort | uniq -d OP: 113- Overture + Incidental Music 117- Overture + Incidental Music 130- Alternate Finale 16- Different arrangements (same datas) 61- Different arrangements (different datas) 72- Different overtures (different datas); overtures have same Opus, may have No 84- Overture + Incidental Music WoO 105- Different versions of a song (different tonalities too) 116- 2 versions 15-1 Dance + Piano version 15-2 15-3 15-4 15-5 15-6 158-1 158a 158b (different dates) 158-2 158-3 158-4 158-5 158-6 158-7 74- Song + Piano Variations extract-y.pl wiki Beethoven.wiki >Beethoven.wiki_y extract-y.pl codm Beethoven.codm Beethoven.codm-from-err >"Beethoven.codm+codm-from-err_y" extract-y.pl merge codm/beethovenlv/wiki Beethoven.codm_y Beethoven.lv-y Beethoven.wiki_y >Beethoven.all_y J:\test-programs\perl\beethoven_process.pl 0 WoO SubWoO "Title" m:/html/beethoven/Oeuvres/ListWoO.html >Beethoven.lv-WoO-tit0 perl "-F/\x20\/\/\x20/" -wnale "$F[0] .= qq(, No. $F[1]) if $F[1]; $F[2] =~ s/:\s+\"/ \"/; print qq($F[2]; WoO $F[0])" Beethoven.lv-WoO-tit0 >Beethoven.lv-WoO-tit extract-y.pl fix Beethoven.all_y Beethoven.lv-WoO-tit >Beethoven.lv-WoO-tit_with_y extract-y.pl fix Beethoven.all_y Beethoven.wiki >Beethoven.wiki_with_y�����������������������������������������������������������������������������������MP3-Tag-1.13/examples/tagged.pl���������������������������������������������������������������������0000700�0000000�0000000�00000004720�10263071102�013026� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use strict; use MP3::Tag; my ($mp3, $count, $v1, $v2)=(undef,0,0,0); die "usage: tagged.pl filename(s)" if $#ARGV == -1; my $t = time; for my $filename (@ARGV) { next until -f $filename; print " -- $filename:\n"; $mp3 = MP3::Tag->new($filename); $mp3->get_tags; $count++; if (exists $mp3->{ID3v1}) { $v1++; print " ** found ID3v1 - TAG\n"; print " Song: " .$mp3->{ID3v1}->song . "\n"; print " Artist: " .$mp3->{ID3v1}->artist . "\n"; print " Album: " .$mp3->{ID3v1}->album . "\n"; print "Comment: " .$mp3->{ID3v1}->comment . "\n"; print " Year: " .$mp3->{ID3v1}->year . "\n"; print " Genre: " .$mp3->{ID3v1}->genre . "\n"; print " Track: " .$mp3->{ID3v1}->track . "\n"; if (0==1) { # write a test tag $mp3->new_tag("ID3v1") unless exists $mp3->{ID3v1}; $mp3->{ID3v1}->comment("This is only a Test Tag"); $mp3->{ID3v1}->song("testing"); $mp3->{ID3v1}->genre("Blues"); $mp3->{ID3v1}->artist("Artest"); $mp3->{ID3v1}->album("Test it"); $mp3->{ID3v1}->year("1965"); $mp3->{ID3v1}->track("5"); # or at once # $mp3->{ID3v1}->all("song title","artist","album","1900","comment",10,"Ska"); $mp3->{ID3v1}->write_tag; } } if (exists $mp3->{ID3v2}) { $v2++; print " ** found ID3v2 - TAG; size = $mp3->{ID3v2}->{tagsize} (+10);\n"; my $frames = $mp3->{ID3v2}->get_frame_ids(); foreach my $frame (keys %$frames) { my ($info, $name) = $mp3->{ID3v2}->get_frame($frame); next unless defined $info; if (ref $info) { print "$frame $name:\n"; while(my ($key,$val)=each %$info) { if (0==1 && $frame eq "APIC" && $key eq "_Data") { # view pics open (FH, ">/tmp/temp.$v2"); print FH $val; close FH; system("xview /tmp/temp.$v2 &"); #choose this to another program if you want } $val= length($val) ." Bytes" if $key =~ /^_/; # _... means binary data $val =~ s/\0/\\0/g; # Multiple strings in a frame print " * $key => '$val'\n" unless $key eq "tagname"; } } else { $info =~ s/\0/', '/g; # Multiple strings in a frame print "$frame $name: '$info'\n"; } } if (0==1) { # add a id3v2 comment $mp3->new_tag("ID3v2") unless exists $mp3->{ID3v2}; $mp3->{ID3v2}->add_frame("COMM","ENG","Test","This is an example, how to add an ID3v2 frame"); $mp3->{ID3v2}->write_tag; } } } warn "$count Files | $v1 ID3v1 Tags | $v2 ID3v2 Tags | ". (time-$t) . "s \n"; ������������������������������������������������MP3-Tag-1.13/examples/tagit.pl����������������������������������������������������������������������0000700�0000000�0000000�00000025241�10003306424�012704� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use File::Copy; use MP3::Tag; # some settings for getting command-line options use Getopt::Long; Getopt::Long::Configure(qw/no_ignore_case_always ignore_case bundling/); my %options = ( '--song=s' => "Name of the song", '--album=s' => "Album", '--artist=s' => "Artist", '--comment=s' => "Comment", '--track=i' => "Track", '--genre=s' => "Genre", '--year=i' => "Year", '--removetag' => "Removes an existing tag", '-q' => "Be quiet", '-v' => "Be verbose", '-f' => "Force", '--show' => "Show the existing tag", '--showgenres'=> "Show the existing genres (!!not yet!!)", '--setfilename' => "Set filename from tag (according to format string)", '--getfilename' => "Set tag according to filename (and format string)", '--format=s' => "Set the format string for get/setfilenam (default: %a - %s.mp3)", '--nospaces' => "Replace Spaces through _ in filenames", '--test' => "Do NOT change the files. Only print which changes would be made", '--skipwithoutv1' => "Don't do anything if no ID3v1 tag exists", '--skipwithv1' => "Don't use --getfilename option if ID3v1 tags already exists", ); # get the command line options my %opt; getoptions(\%opt, %options); if (exists $opt{showgenres}) { my $genres = MP3::Tag::genres(); print join (", ", @$genres) ."\n"; } unless ($#ARGV >=0) { print "error: Filename(s) missing\n" unless exists $opt{showgenres}; exit 0 if exists $opt{showgenres}; exit 1; } # is there only one or more files to work with? $opt{single}=1 if $#ARGV==0; # prepare v and q flag (v has higher priority) delete $opt{q} if (exists $opt{v} && exists $opt{q}); # prepare for setfilename / getfilename (not allowed together) if (exists $opt{setfilename} && exists $opt{getfilename}) { print "error: Cannot use --setfilename and --getfilename together\n"; delete $opt{setfilename}; delete $opt{getfilename}; } $opt{format} = "%a - %s.mp3" unless exists $opt{format}; my ($stencil, $details); ($stencil, $details) = formatstr_setfilename($opt{format}) if exists $opt{setfilename}; ($stencil, $details) = formatstr_getfilename($opt{format}) if exists $opt{getfilename}; # loop for each file chomp(@ARGV = <STDIN>) unless @ARGV; for my $filename (@ARGV) { # get the tags $mp3 = MP3::Tag->new($filename); unless (defined $mp3) { print "Skipping $filename ...\n"; next; } $mp3->get_tags; unless (exists $mp3->{ID3v1}) { print "No ID3v1-Tag found\n" if exists $opt{show} || exists $opt{v}; next if exists $opt{skipwithoutv1}; $mp3->new_tag("ID3v1"); } else { next if exists $opt{skipwithv1}; } # deletet tag if wanted (option: --removetag) $mp3->{ID3v1}->remove_tag if $opt{removetag}; # set tag if this is wanted # option: --song, --artist, --album, --comment, --year, --genre, --track $mp3->{ID3v1}->song($opt{song}) if exists $opt{song}; $mp3->{ID3v1}->artist($opt{artist}) if exists $opt{artist}; $mp3->{ID3v1}->album($opt{album}) if exists $opt{album}; $mp3->{ID3v1}->comment($opt{comment}) if exists $opt{comment}; $mp3->{ID3v1}->year($opt{year}) if exists $opt{year}; $mp3->{ID3v1}->genre($opt{genre}) if exists $opt{genre}; $mp3->{ID3v1}->track($opt{track}) if exists $opt{track}; if (exists $opt{song} || exists $opt{artist} || exists $opt{album} || exists $opt{comment} || exists $opt{year} || exists $opt{genre} || exists $opt{track}) { if (exists $opt{test}) { # do nothing, but show new tag $opt{show} = 1; } else { if ($mp3->{ID3v1}->write_tag()) { print "Tag written\n" unless exists $opt{q}; } else { print "Couldn't write tag\n" unless exists $opt{q}; } } } # show tag (option --show) if ($opt{show}) { print "\nID3v1-Tag: $filename\n" unless exists $opt{single}; print "New tag would be:\n" if exists $opt{test}; print " Song: " .$mp3->{ID3v1}->song . "\n"; print " Artist: " .$mp3->{ID3v1}->artist . "\n"; print " Album: " .$mp3->{ID3v1}->album . "\n"; print "Comment: " .$mp3->{ID3v1}->comment . "\n"; print " Year: " .$mp3->{ID3v1}->year . "\n"; print " Genre: " .$mp3->{ID3v1}->genre . "\n"; print " Track: " .$mp3->{ID3v1}->track . "\n"; } # set filename from tag (option: --setfilename) # with --format the format of the new filename can be set (see formatstr below) if (exists $opt{setfilename}) { unless (exists $opt{test}) { # check if there really exists a tag $mp3->get_tags; unless (exists $mp3->{ID3v1}) { print "No ID3v1 Tag exists. Can't change $filename\n"; exit -1; } } my $new = $stencil; my $i=0; foreach (@$details) { my $txt = $mp3->{ID3v1}->{$_->{tag}}; $txt =~ s/ *$//; $txt = substr $txt, 0, $_->{length} if exists $_->{length} && ((! exists $_->{fill}) || exists $_->{precise} ); $txt = $_->{fill} x ($_->{length}-length($txt)) . $txt if exists $_->{fill}; $new =~ s/%$i/$txt/; $i++; } $new =~ s/ /_/g if exists $opt{nospaces}; print "Trying to rename $filename to $new\n" if exists $opt{v} && !exists $opt{test}; print "$filename => $new\n" if exists $opt{test}; if ( !exists $opt{test} && $new && checkpath($new)) { $mp3->close; move($filename, $new); } else { print "Cannot set filename from tag: $new is invalid\n" unless exists $opt{q} || exists $opt{test} ; } } # set tag from filename (option: --getfilename) # with --format the format of the filename can be set if (exists $opt{getfilename}) { my @matches; if (@matches = ($filename =~ $stencil)) { while(($key,$val)=each %$details) { $mp3->{ID3v1}->$val($matches[$key]); } if (exists $opt{test} or exists $opt{v} or exists $opt{show}) { print "\nID3v1-Tag: $filename\n" unless exists $opt{single}; print "After scanning filename, new tag would be:\n"; print " Song: " .$mp3->{ID3v1}->song . "\n"; print " Artist: " .$mp3->{ID3v1}->artist . "\n"; print " Album: " .$mp3->{ID3v1}->album . "\n"; print "Comment: " .$mp3->{ID3v1}->comment . "\n"; print " Year: " .$mp3->{ID3v1}->year . "\n"; print " Genre: " .$mp3->{ID3v1}->genre . "\n"; print " Track: " .$mp3->{ID3v1}->track . "\n"; } unless (exists $opt{test}) { if ($mp3->{ID3v1}->write_tag()) { print "Tag written\n" unless exists $opt{q}; } else { print "Couldn't write tag\n" unless exists $opt{q}; } } } else { print"Couldn't analyze '$filename' with /$stencil/\n" unless exists $opt{q}; } } } ######################################## SUBS # check if the path to this file exists # if not, ask if missing dirs should be created # return true/false if the path is ok sub checkpath { my $file = shift; my $tree=""; my $treeok=1; while ($file =~ /([^\/]*)\//g && $treeok) { unless ($1 eq "" || -d $tree.$1) { print "$tree$1/ doesn't exists!\n" if exists $opt{v}; $treeok=0; last unless confirm("Should I create the directory $tree$1/"); if (mkdir $tree.$1, 0755) { $treeok=1; } else { print "Cannot create directory $tree$1/\n"; } } $tree .=$1."/"; } return $treeok; } # prints a question and waits for a [y]es or [n]o # returns true (1) for [y] or false (o) for [n] # with option -f (force) returns always true (1) sub confirm { return 1 if $opt{f}; my $question = shift; print $question ." [y/n] ?"; my $key = <STDIN>; chomp $key; return ($key =~ /^[YyJj]/) ? 1 : 0; } ####################################### # converts formatstr into the internal dataformat # # the formatstr may include any text, valid for a filename # also it contains symbols,which must start with a % and end with one of [salgyt] # %s song, %a - artist %l - album, %y -year, %g - genre, %t -track # # each symbol can contain additional information: # a length l, directly after the % # a fill char, given as :x or !:x where x is the fillchar, and :x or !:x follows after the length # # * if there is only a length l given, the string will be max. l chars long, # that means cut off after l chars if it is longer # * if there is a length and a :x, then a string which is shorter then l chars, # will be filled from left with the char x to meet the length l # a longer sting will not be affected # * if there is a length and a !:x, then a string shorter than l, will also filled # from left with x, but a longer string than l, will be cut off at l # # eg track=3 artist=Abba song=Waterloo # '%2:0t.$a - $s.mp3' will be translated to '03.Abba - Waterloo.mp3' # '%t.$6!:_a - $6!:_s.mp3' will be translated to '3.__Abba - Waterl.mp3' # # intern format contains $stencil and @details # the stencil contains text and %i makros. i is an integer, counting up from 0 # $details[i] contains {tag} which should be used to replace %i in the stencil # {length}, {fill} and {precise} give some additional information for replacing sub formatstr_setfilename { my $format = shift; my %tags = (s=>"song", a=>"artist", l=>"album", y=>"year", g=>"genre", t=>"track", c=>"comment"); my @fmt; while ($format =~ /%([0-9]*)(?:(!)?:(.))?([salygct])/g) { my $t; $t->{length}=$1 if defined $1 && $1 ne ""; $t->{precise}=1 if defined $2 && $2 ne ""; $t->{fill}=$3 if defined $3 && $3 ne ""; $t->{tag} = $tags{$4} if defined $4 && $4 ne ""; push @fmt, $t if defined $4 && $4 ne ""; } my $i=0; $format =~ s/%([0-9]*)(?:(!)?:(.))?([salygt])/"%".$i++/eg; return ($format, \@fmt); } sub formatstr_getfilename { my $format = shift; my $pos=0; my %tags = (s=>"song", a=>"artist", l=>"album", y=>"year", g=>"genre", t=>"track", c=>"comment"); my %info; while ($format =~ /%([salgcyt])/g) { $info{$pos++}=$tags{$1}; } $format =~ s/([\[\]*.?()])/\\$1/g; $format =~ s/%[yt]/(\\d+)/g; $format =~ s/%[salgc]/(.+?)/g; return (qr!$format!, \%info); } ######################################## sub getoptions { my ($optref, %options) = @_; unless ( GetOptions ($optref, keys %options) ) { # found unknown option # show usage of options print "\nUSAGE: $0 " . join(" ", sort keys %options) ." file(s)\n\n"; my ($eq, $co, $neg) = (0,0,0); foreach (sort keys %options) { printf "%13s : %s\n", $_, $options{$_}; $eq = 1 if /=/; $co = 1 if /:/; $neg = 1 if /!/; } print "\n" if $eq || $co || $neg; print " --switch! - switch may be negated as --noswitch\n" if $neg; print " --switch=x - switch must be followed by a value x\n" if $eq; print " --switch:x - switch can be followed by a value x\n" if $co; print " / f - value must be a float\n". " x = s - value must be a string\n". " \\ i - value must be an integer\n" if $eq || $co; # and exit because of unknown options exit(0); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/examples/typeset_audio_dir�������������������������������������������������������������0000700�0000000�0000000�00000142415�11316544506�014716� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w $VERSION = '1.10'; use strict; use MP3::Tag '0.9711'; # %{mA} use File::Find; use Getopt::Std 'getopts'; use Cwd; $Getopt::Std::STANDARD_HELP_VERSION = 1; my %opt = my %ini_opt = (2 => '%a', a => 2, t => 1, F => 'T2A'); my @INI_ARGV = @ARGV; # Level=2 header; Level=1 header (default - dir), # use duration, year, whole dates, replace @ by %, basename of output files, # ignore 'author' on level > this in directory tree # use 'album' for titles with depth above this..., encodings, no comment,lyrics getopts(my $opts = '2:1:TyYn@B:a:t:e:cLN:P:F:r:', \%opt); my(%plan, $both); sub set_plan ($) { my $how = shift; if ($how eq 'long') { %opt = (%opt, 1 => '', 2 => '%l', t => 1e100, a => 1e100); } if ($how eq 'short') { %opt = (%opt, 1 => '', 2 => '', t => -1e100, a => -1e100, c => 1); } } my $ini_c = $opt{c}; if ($opt{P}) { my @plan = split m/,/, $opt{P}; # Currenlty only one item in the plan @plan{@plan} = (1) x @plan; $both = 1 if $plan{short} and $plan{long}; die "You can't have both short and long without -B" if $both and not $opt{B}; %opt = %ini_opt; set_plan 'long' if $plan{long}; set_plan 'short' if $plan{short}; @ARGV = @INI_ARGV; # Redo getopts with different defaults getopts($opts, \%opt); } if ($opt{'@'}) { $opt{$_} =~ s/\@/\%/g for keys %opt; } my %enc; if ($opt{e}) { # Comma-separated, each 'encoding' or '[d][h][o]:encoding' for my $e (split /,/, $opt{e}) { # o=output, d=dirname, h=.hintfiles $enc{o} = $enc{d} = $enc{h} = $e, next unless $e =~ /:/; my($what, $enc) = split /:/, $e, 2; for my $w (split //, $what) { $enc{$w} = $enc; } } } $opt{n} = $ENV{TYPESET_AUDIO_TRACK} || 1 if $opt{n}; my $glob_pat = qr/\.mp3$/i; $glob_pat = qr/$opt{r}/ if defined $opt{r}; # booklet using precooked (broken) style cd-cover # Since it is very feature-poor, we did not try to debug a more decorated # appearance... my $out_cdcover_std = \*STDOUT; my $out_cdcover_our; # jewel case booklet using our `style' my $out_normal_text; # booklet for print on "usual size" paper my $out_envelop_backcover; # Back insert into a jewel case my $out_titles; # List of top Headings my $out_common; # Common definitions my $out_list = \*STDOUT; if (defined $opt{B}) { $opt{B} =~ s,\\,/,g; open LIST, "> $opt{B}_list.tex" or die "open `$opt{B}_list.tex' for write: $!"; select LIST; $out_list = \*LIST; if (-e "$opt{B}_titles.tex" and not -w "$opt{B}_titles.tex") { warn "Will not overwrite Read-Only file `$opt{B}_titles.tex'.\n"; } else { open TITLES, "> $opt{B}_titles.tex" or die "open `$opt{B}_titles.tex' for write: $!"; $out_titles = \*TITLES; } if (-e "$opt{B}_common.tex" and not -w "$opt{B}_common.tex") { warn "Will not overwrite Read-Only file `$opt{B}_common.tex'.\n"; } else { open COMMON, "> $opt{B}_common.tex" or die "open `$opt{B}_common.tex' for write: $!"; $out_common = \*COMMON; } if (-e "$opt{B}_cdcover.tex") { warn "Will not overwrite existing file `$opt{B}_cdcover.tex'.\n"; undef $out_cdcover_std; } else { open CDCOV, "> $opt{B}_cdcover.tex" or die "open `$opt{B}_cdcover.tex' for write: $!"; $out_cdcover_std = \*CDCOV; } if (-e "$opt{B}_backcover.tex") { warn "Will not overwrite existing file `$opt{B}_backcover.tex'.\n"; } else { open BACKCOV, "> $opt{B}_backcover.tex" or die "open `$opt{B}_backcover.tex' for write: $!"; $out_envelop_backcover = \*BACKCOV; } if (-e "$opt{B}_text.tex") { warn "Will not overwrite existing file `$opt{B}_text.tex'.\n"; } else { open TXT, "> $opt{B}_text.tex" or die "open `$opt{B}_text.tex' for write: $!"; $out_normal_text = \*TXT; } if (-e "$opt{B}_cdbooklet.tex") { warn "Will not overwrite existing file `$opt{B}_cdbooklet.tex'.\n"; } else { open _12CM, "> $opt{B}_cdbooklet.tex" or die "open `$opt{B}_cdbooklet.tex' for write: $!"; $out_cdcover_our = \*_12CM; } } if ($enc{o}) { if (defined $opt{B}) { eval { binmode $out_list, ":encoding($enc{o})"; binmode $out_titles, ":encoding($enc{o})"} or warn $@ if defined $out_titles; } else { eval { binmode $out_cdcover_std, ":encoding($enc{o})"} or warn $@; } } sub to_TeX ($) { # Assume high-bit characters are letters (my $in = shift) =~ s/([&_\$#%~])/\\$1/g; $in =~ s/(\b|(?<=\.\.\.)|(?<=[\x80-\xFF]))`\B|`$/'/g; # Quote: word-like` $in =~ s/\B'(\b|(?=[\x80-\xFF]))|^'/`/g; # Quote: `word-like $in =~ s/(\b|(?<=\.\.\.)|(?<=[\x80-\xFF]))"\B|"$/''/g; # Quote: word-like" $in =~ s/\B"(\b|(?=[\x80-\xFF]))|^"/``/g; # Quote: "word-like $in =~ s/\.{3}/\\dots{}/g; # \dots $in =~ s/\s+-\s+/---/g; # em-dash with possible line breaks $in =~ s/(?<=\b[[:upper:]]\.)\s+(?=[[:upper:]])/~/g; $in =~ s/\bDvorák\b/Dvo\\v rák/g; $in; } my $out_name = $opt{N} ? to_TeX($opt{N}).'%' : 'COLLECTION% Replace what is before "%" by the name of the collection'; print $out_titles "\\makePreTitle{%\n$out_name\n}%\n" if $out_titles; # suppose that the directory structure is author/TIT1/TIT2.mp3 or author/TIT2.mp3 # and TIT1 eq "%l" my $author = ''; my $TIT1 = ''; my $had_subdir; my $print_dir; # $|=1; # For debugging sub align_numbers ($) { (my $in = shift) =~ s/([\s_]*)(\d+)/ sprintf ' %09d%s', $2, $1 /eg; $in; } my(@comments, @performers, @level); # Called with short names; $File::Find::dir is set sub preprocess_and_sort_with_aligned_numbers { push @level, $level[-1]+1; my $comment; my $performer; for my $f (@_) { if ($f eq '.content_comment' or $f eq '.top_heading') { my $ff = "$File::Find::dir/$f"; next unless -f $ff; # ignore non-files local $/; local *F; open F, "< $ff" or die "open `$ff' failed: $!"; my $c = <F>; if ($enc{h}) { eval { require Encode; $c = Encode::decode($enc{h}, $c); 1; } or warn $@; } $c =~ s/^\s+//; $c =~ s/\s+$//; if ($f eq '.top_heading') { my $lev = 0; if (not length $c) { $lev = -1; } elsif ($c =~ /^-?\d+$/) { $lev = $c - 1; $c = ''; } $level[-1] = $lev; } ($f eq '.top_heading' ? $performer : $comment) = $c if length $c; } } if (not defined $performer and $level[-1] == 0 and $File::Find::dir =~ m,.*/(.+), ) { $performer = $1; if ($enc{d}) { eval { require Encode; $performer = Encode::decode($enc{h}, $performer); } or warn $@; } $performer =~ s/_/ /g; } push @comments, (defined $comment ? $comment: $comments[-1]); push @performers, (defined $performer ? $performer: $performers[-1]); sort {align_numbers($a) cmp align_numbers($b) or $a cmp $b} @_; } sub unwind_dir { $print_dir = 1 if $level[-1] == 0; # Need to print again pop @comments; pop @performers; pop @level; } sub to_duration ($) { my $s = shift; my $h = int($s/3600); $s -= $h*3600; my $m = int($s / 60); $s -= $m*60; return sprintf "%d\\hourmark{}%02d'%02d''", $h, $m, $s if $h; return sprintf "%d'%02d''", $m, $s; } sub cmp_u ($$) { my ($a, $b) = (shift, shift); (defined $a) ? (not defined $b or $a cmp $b) : defined $b; } my $total_sec = 0; # Compare with postponed data, either emit, or postpone my $previous; my $previousTop; my $hadDir; sub print_this_mp3 ($) { my $new = shift; # Print only if toplevel or one level deep, or if directory1 changed... # return if defined $new->{dir1} # and not cmp_u $new->{dir1}, $previous->{dir1}; my $changed; for my $p (qw(author title comment)) { # No year! if (cmp_u $new->{$p}, $previous->{$p}) { # Need to print... $changed++; last } } $previous->{len} += $new->{len} if not $changed and $new->{len}; return unless $changed; # Once per toplevel directory if (defined $print_dir and cmp_u $new->{top}, $previousTop) { $previousTop = $new->{print_top} = $new->{top}; undef $print_dir; } # Author may be ignored in deep directories if (cmp_u $new->{author_dir}, $previous->{author_dir} and cmp_u $new->{author}, $previous->{author}) { $new->{print_author} = $new->{author}; } my $this = $previous; #my $this = $new; $previous = $new; return unless %$this; # Once per directory given as argument to the script, or marked `toplevel' if (defined $this->{print_top}) { my $pr = to_TeX $this->{print_top}; print $out_titles "\\sepTitle\n" if $hadDir and $out_titles; print $out_titles "$pr%\n" if $out_titles; print "\n\\preSection $pr\\postSection\n"; $hadDir++; } if ($this->{print_author}) { print "\n\\preSubSection "; print to_TeX $this->{author}; print "\\postSubSection\n"; } my $comment = ''; $comment = "\\precomment " . to_TeX($this->{comment}) . "\\postcomment " if defined $this->{comment}; my $year = (defined $this->{year} ? $this->{year} : ''); $year .= '\hasSyncLyrics' if $this->{syncLyr}; $year .= '\hasUnsyncLyrics' if $this->{unsyncLyr}; $year .= '\hasAPIC' if $this->{APIC}; if ($opt{T}) { $total_sec += $this->{len}; # on 2nd pass too, but the result is ignored my $dur = to_duration $this->{len}; $dur .= '\postduration ' if length $year; $year = "$dur$year" } $year = "\\preyear $year\\postyear" if length $year; print "\\pretitle "; if ($this->{track}) { # Do not typeset 0 or empty print "\\pretrack "; print to_TeX $this->{track}; print "\\posttrack "; } print to_TeX $this->{title}; print "$comment$year\\posttitle\n"; } # Callback for find(): sub print_mp3 { return unless -f $_ and /$glob_pat/; #print STDERR "... $_\n"; my $tag = MP3::Tag->new($_); my @parts = split m<[/\\]>, $File::Find::dir; shift @parts if @parts and $parts[0] eq '.'; my $this; $this->{top} = $opt{1} ? $tag->interpolate($opt{1}) : $performers[-1]; $this->{author} = $tag->interpolate($opt{2}); # default '%a' $this->{title} = $tag->interpolate(@parts <= $opt{t} ? '%t' : '%l'); $this->{track} = $tag->interpolate($opt{n} eq 1 ? '%{mA}%{n1}' : $opt{n}) if $opt{n} and @parts <= $opt{t}; $this->{len} = $tag->interpolate('%S') if $opt{T}; my $l_part = $opt{a}; $l_part = $#parts if $l_part >= $#parts; $this->{author_dir} = join '/', @parts[0..$l_part]; $this->{dir1} = $parts[1]; # Not used anymore... my $c = !$opt{c} && $tag->select_id3v2_frame_by_descr('TXXX[add-to:file-by-person,l,t,n]'); if (defined $c) { $c =~ s/^\(\s*([^()]*?)\s*\)$/$1/; # Remove $c from title if duplicated there; # effectively, $c is italizied inside title $this->{title} =~ s/\s*\(\s*\Q$c\E\s*\)$// or $this->{title} =~ s/\s*(\b|(?!\w))\Q$c\E$//; } $this->{comment} = (defined $c and length $c) ? "($c)" : $comments[-1]; if ($opt{y}) { my $year = $tag->year; $year =~ s/(\d)-(?=\d{4})/$1--/g; # Contract long dates (with both ',' and '-') if ($year and $year =~ /,/ and $year =~ /-/ and not $opt{Y}) { $year =~ s/-?(-\d\d?\b)+//g; # Remove month etc 1 while $year =~ s/\b(\d{4})(?:,|--)\1/$1/g; # Remove "the same" year (my $y = $year) =~ s/--/,/g; # Remove intermediate dates if more than 3 years remain $year =~ s/(,|--).*(,|--)/--/ if ($y =~ tr/,//) > 2; } $this->{year} = $year if length $year; } if ($opt{L}) { $this->{syncLyr} = $tag->have_id3v2_frame('SYLT'); $this->{unsyncLyr} = $tag->have_id3v2_frame('USLT'); $this->{APIC} = $tag->have_id3v2_frame('APIC'); } print_this_mp3($this); return; } ############################################# Now prepare LaTeX preambles: my $oenc = $enc{o} || 'utf8'; my $common_enc = <<EOP . <<'EOPQ2'; # EOP interpolates! % T2A+textcomp allow common Cyrillic AND Latin; since not always available: % consider T1 (best for purely Latin docs); OT1 is not as good. \\usepackage[$opt{F}]{fontenc} \\usepackage[$oenc]{inputenc} EOP %\usepackage[utf8]{inputenc} %\usepackage[latin1]{inputenc} % cp866 for DosCyrillic, cp1251 for WinCyrillic %\usepackage[cp866]{inputenc} %\usepackage[cp1251]{inputenc} %\usepackage[russian]{babel} % Load language file for non-English hyphenation \usepackage{textcomp} % More Unicode symbols \textFOO, and accented Latin EOPQ2 sub output_setup ($) { my($how, $out) = (shift); $out = <<'EOP'; % For \normalsize=10pt documentclasses: \small = 9pt, \footnotesize = 8pt, % \scriptsize = 7pt, \tinyish = 6pt, \tiny = 5pt EOP for my $type (qw(Flap Title Section SubSection Record)) { next unless $how->{"${type}Font"}; # Flap and Title not in all styles $out .= <<EOP if $how->{"${type}NameFont"}; \\def\\${type}NameFont{$how->{"${type}NameFont"}} EOP $out .= <<EOP; \\def\\${type}Font{$how->{"${type}Font"}} \\def\\${type}Squeeze{\\squeezeContunuationLines} EOP } $out .= <<EOP; \\def\\COLUMNS{$how->{columns}} %%% This is very squeezed; increase by 0.8ex to loosen: \\def\\preSectionSKIP{0.3ex plus 0.6ex minus 0.3ex} \\def\\postSectionSKIP{0.05ex plus 0.6ex minus 0.15ex} \\def\\preSubSectionSKIP{0pt} \\def\\postSubSectionSKIP{0pt} EOP } my $common = <<'EOP'; \def\squeezeContunuationLines{% typesets continuation lines very squeezed % ARGS = BETWEEN LINES / +ITS INC / BASELINE-SKIP-in-height-of(0) / PARSKIP \contunuationLineSkip{-0.2ex}{plus 0.24ex}{1.3}{-0.08ex plus 0.44ex}} %\pretolerance=-1% Always hyphenation: always \def\tinyish{\fontsize{6}{7}\selectfont} % Between \scriptsize and \tiny... %%%% This is actually not needed: %\newlength\Multicolsep % Insert manually %% Changes with font size (this is very squeezed; increase by 0.8ex to loose) %\def\topMulticolsep{\setlength{\Multicolsep}{0.3ex plus 0.6ex minus 0.3ex}% % \addvspace\Multicolsep} %\def\botMulticolsep{\setlength{\Multicolsep}{0.05ex plus 0.6ex minus 0.15ex}% % \addvspace\Multicolsep} %\def\hourmark#1{$\mathsurround0pt{}^{\scriptscriptstyle\circ}$} \def\hourmark#1{\kern-.05em\textdegree\kern-.05em\relax} \def\preSubSection{\pagebreak[1]\addvspace{\preSubSectionSKIP}\bgroup\centering \SubSectionFont \SubSectionSqueeze } \def\postSubSection{\par\egroup\addvspace{\postSubSectionSKIP}} \def\pretitle{\bgroup} \def\posttitle{\par\egroup} \def\precomment{ \bgroup\it} \def\postcomment{\egroup} \def\pretrack#1\posttrack{#1.\hbox{~}} % Not expandable %\def\preyear{ \hfil\hbox{}\hskip0pt\hbox{}\nobreak\hskip0pt plus 1fill\nobreak[} %%\def\preyear{\unskip\nobreak\hfil\penalty50\hskip0.75em\hbox{}\nobreak\hfill[} %\def\postyear{]} \def\postduration{, } % Sigh... We want year to be right-aligned, moved to the next row if it % does not fit into the last row, want it to be not hyphenated if it fits % into the line, and want it to not change the typesetting of the rest of % the text (as far as it is possible). % We need at least two \hfil's since if line break happens, we need to push % the previous line left, and the year right. Breaks happen only on the % left end of leftmost kern/glue or on penalties; so we need \nobreak only % at the left ends. \null is needed to create a break place between two % pieces of glue. % If break happens, \hskip disappears, and we get two \hfil's; % If it does not happen, we get 0.65em plus 1fil plus 1fill. % \penalty 80 helps squeezing the line a little bit if year fits into % the last line, but only tightly (or if an extra hyphenation is required?). % Finally, an extra line break can make the ending-hyphenation of a paragraph % to become non-ending; if \finalhyphendemerits is non-0, this makes % the break performed even if year fits (in the presense of ending-hyphenation % in the main text). This \finalhyphendemerits requires groups in \pretitle % \postttile... \def\addOnRight#1{% \unskip\nobreak\hfil\penalty 80\hskip0.65em\null\nobreak\hfill \sbox 0{#1}\ifdim \wd 0 > \linewidth #1\else \box 0\fi \finalhyphendemerits=0\relax } % \linewidth differs from \hsize by \left-\rightmargin's. \def\preyear#1\postyear{\addOnRight{[#1]}} % Two different variants for squeezing; 2nd interacts better with \parskip. % (\offinterlineskip messes multicol???) % Make this smaller for smaller skip between continuation lines of a record \def\squeezedHeight{0.83} % First variant: use denser grid only \def\squeezeHeight{\baselineskip\squeezedHeight\baselineskip\relax} % This would switch off the grid typesetting (=\baselineskip) for most lines % (separate lines mostly w.r.t. INTERline spacing, not BASEline spacing) % ARGS = BETWEEN LINES / +ITS INCR / BASELINE-SKIP-in-ht(0) / PARSKIP \def\contunuationLineSkip#1#2#3#4{\setbox 0\hbox{0}% % #3=1.3 (rest as below): no effect on (most) lines with descenders/raisers \baselineskip=\ht0\relax \baselineskip=#3\baselineskip \lineskiplimit=#1\relax \def\myA{#2}\def\myB{}\ifx \myA \myB % If #2 empty \lineskip=#1\relax \else \lineskip=#1 #2\relax \def\myB plus ##1\relax{\def\myA{##1}}\myB#2\relax \advance\lineskiplimit\myA \fi % Make this more negative for smaller skip between records % (but better be larger than \lineskip) \parskip=#4\relax % After a paragraph (= one record) } % Make this more negative for smaller skip between records % (but larger than in squeezeContunuationLines) % [Now this is set inside \contunuationLineSkip] % \parskip=-0.2pt plus 1.1pt\relax % After a paragraph (= one record) \def\topRULES{\vskip 1.2pt\hrule height1.7pt\vskip 1.2pt% \hrule height0.8pt\relax\vskip 2.4pt\relax} \def\bottomRULES{\vskip 3.2pt\hrule height0.8pt% \vskip 1.2pt\hrule height1.7pt\relax} \def\myLB{\discretionary{}{}{}} % \linebreak[1] is mandatory in {center}??? \def\sepTitle{\myLB/\myLB} \def\makeNPreTitle#1{{\TitleNameFont #1 }} % Good for top of document \let\makePreTitle\makeNPreTitle \def\makePostTitle{} \def\makeFlapPreTitle#1{{\FlapNameFont #1 }} % Good for backcover flaps... \def\makeFlapPostTitle{\addOnRight{\bf\tiny \today}} EOP if (1 or $opt{L}) { # Allows inclusion of -L lists in non-L docs $common .= <<'EOP'; \def\negTHINspace{\kern-.12em} % \negTHINspace is -.1666666em \def\hasUnsyncLyrics{\global\let\ifhaveLyrics\iftrue \negTHINspace\textcircled{\textsc{l}}\negTHINspace} \def\hasSyncLyrics{\global\let\ifhaveLyrics\iftrue \negTHINspace\textcircled{\textsc{s}}\negTHINspace} \def\hasAPIC{\global\let\ifhaveAPIC\iftrue \negTHINspace\textcircled{\textsc{i}}\negTHINspace} \newif\ifhaveLyrics \haveLyricsfalse \newif\ifhaveAPIC \haveAPICfalse \def\reportLyricsSyntaxEtc{\ifhaveLyrics{\tiny\hasUnsyncLyrics/\hasSyncLyrics\quad---\quad has (un)syncronized lyrics\ifhaveAPIC ;\qquad \else \par \fi}\fi \ifhaveAPIC{\tiny\hasAPIC\quad---\quad has embedded image(s)\par}\fi } EOP } else { $common .= <<'EOP'; \def\reportLyricsSyntaxEtc{} EOP } my $cdcover_on = <<'EOP'; \begin{bookletsheets} %\begin{bookletsheetsTwo} %\begin{singlesheet}{Title}{Slip text} %\begin{multicols}{4} EOP my $base = defined $opt{B}? $opt{B} : ''; # Avoid warning my $backcover_on = <<'EOP' . <<EOPQ . <<'EOP2'; %\begin{bookletsheets} %\begin{bookletsheetsTwo} %\begin{singlesheet}{Title}{Slip text} %%% Replace by backsheet* to get other direction of spines %\begin{backsheet}{% Set up title (possibly multiline) \begin{backcover-ml}{\FlapFont \FlapSqueeze \let\makePreTitle\makeFlapPreTitle EOP \\input{${base}_titles}% EOPQ \makeFlapPostTitle} \let\makePreTitle\makeNPreTitle % Reset back EOP2 my $global_multicol_on = <<'EOP'; \begin{multicols}{\COLUMNS} EOP my $title_rules_on = <<EOP; \\TitleFont \\TitleSqueeze %%% Uncomment this for fine-tune of the 1st page top separator % \\hrule height0pt\\vskip -1.2mm\\vskip 0pt\\hrule height0pt \\begin{center} \\unskip \\input{${base}_titles}% \\makePostTitle \\end{center}\\unskip % How else to remove the gap??? %\\normalbaselines % Somehow needed for multicol??? \\topRULES EOP my $recordSetup = <<'EOP'; \RecordFont \RecordSqueeze % "Inverse-indent" of continuation lines \leftskip=1.3em \parindent=-\leftskip\relax EOP my $class_cdcover_std = <<'EOP'; %\documentclass[12pt]{article} \documentclass{cd-cover} %\documentclass{cd-cover2} %\usepackage{multicol} EOP my $cdcover_set = <<'EOP'; # XXXX \small for backcover? \begin{document} \topskip 0.3pt\relax \def\preSection{\pagebreak[2]\bgroup\centering\SectionFont \SectionSqueeze \addvspace{\preSectionSKIP}% } \def\postSection{\par\egroup \addvspace{\postSectionSKIP}} \CDbookletMargin=1.5mm \CDbookletTopMargin=1.5mm \CDsingleMargin=1.5mm \CDsingleTopMargin=1.5mm \CDbackMargin=1.5mm \CDbackTopMargin=1.5mm EOP my $class_backcover = <<'EOP'; %\documentclass[12pt]{article} \documentclass{cd-cover} \newenvironment{backcover-ml}[1]{% `backsheet' puts multiline spines % in a wrong place.... \backsheet{{% Set up title (possibly multiline) \setlength\unitlength{1mm}% \begin{picture}(0,0) %% There MUST be an easier way to put mid-of-left-edge of a block at pos \put(0.5,2.5){\makebox(0,0)[bl]{% \raisebox{-0.5\height}[0pt][0pt]{% \begin{minipage}[b]{11.7cm}% %%% This will create really tight multi-line: \lineskiplimit=10cm\lineskip=-0.5pt minus 1pt\relax #1% \end{minipage}% }% }} \end{picture} }}% }{\endbacksheet} \usepackage{multicol} EOP my $set_columns = <<'EOP'; % Tested to work reasonably well with fonts between 5pt and 10pt \RecordFont \columnsep 1.8ex \advance \columnsep -1.4pt % ex of RecordFont \columnseprule.4pt %\multicolsep 3pt plus 4pt minus 3pt % Need to put adjacent horizontal lines, so this is better: \multicolsep 0pt EOP my $duplex_instr_and_class = <<EOP . <<'QEOP'; %% Postprocess this file with something like % dvips -t landscape -f < This_File.dvi | psbook | pstops "2:0(0,6cm)+1(0,-6cm)" > Output.ps %% For more details, consult % perldoc -F $0 % perldoc typeset_audio_dir EOP %% E.g., With some versions (of graphics.cfg?) one needs to invert the offsets: % dvips -t landscape -f < This_File.dvi | psbook | pstops "2:0(0,-6cm)+1(0,6cm)" > Output.ps %% and/or shift all the page to compensate printer's problems: % dvips -t landscape -f < This_File.dvi | psbook | pstops "2:0(0,-5.86cm)+1(0,6.14cm)" > Output.ps %% To duplex with binding along the long size of paper, modify as in % dvips -t landscape -f < This_File.dvi | psbook | pstops "2:0(0,6cm)+1(0,-6cm)" | pstops "2:0,1U(1w,1h)" > Output-even_flipped.ps % ps2pdf -dAutoRotatePages=/None Output-even_flipped \documentclass{article} % Use 2mm margin inside 12cm x 12cm page; page numbers fit only accidentally... % add a4paper or letterpaper if needed (e.g., on broken LaTeX installations) \usepackage[centering,landscape,width=11.6truecm,height=11.6truecm,nohead,nofoot]{geometry} \usepackage{multicol} QEOP $common .= <<'EOP' if defined $opt{B}; % Page style adding a frame about the text area (used only for *_cdbooklet.tex) \makeatletter \def\ps@framed{% Prepend frame to current ornaments \def\ps@framed@head{{% Localize, calculate and draw \setlength\unitlength{1sp}% So that \number works OK \linethickness{0.2pt}% % Text may actually go below \textheight - it gives the baseline... %\advance\textheight\maxdepth % It is localized anyway... % Use this as a temporary register for frame offset from text-box \maxdepth=2truemm\relax % Increase box size by 4mm \advance\headsep -\maxdepth \advance\textwidth \maxdepth \advance\textwidth \maxdepth \advance\textheight \maxdepth \advance\textheight \maxdepth \begin{picture}(0,0)% % This is put at bottom of heading, so \headheight is above us \put(-\number\maxdepth,-\number\headsep){% \begin{picture}(0,0)% \put(0,-\number\textheight){% \framebox(\number\textwidth,\number\textheight){}}% \end{picture}}% \end{picture}% }}% \let\ps@framed@oddhead\@oddhead % current ornaments \let\ps@framed@evenhead\@evenhead % current ornaments \def\@oddhead{\ps@framed@head\ps@framed@oddhead}% % prepend \def\@evenhead{\ps@framed@head\ps@framed@evenhead}} % prepend \makeatother EOP my $set_headers = <<'EOP'; \begin{document} \topskip 0.3pt\relax % Calculations done by multicol to insert vspace are too complicated to grasp. % But they are effectively disabled by \nointerlineskip \def\SETSEC#1{\bgroup\centering\SectionFont\SectionSqueeze #1\vphantom{q}\parskip0pt\relax\par\egroup % add descender \addvspace{\postSectionSKIP}\hrule \vbox to 0pt{}\relax\nointerlineskip} \def\preSECii{\end{multicols}% % XXXX Why 0.5ex is needed??? \nointerlineskip\vbox to 0.5ex{}\relax\hrule\addvspace{\preSectionSKIP}} \def\preSECi{\let\preSEC\preSECii} \let\preSEC\preSECi % Do nothing on the first invocation \def\preSection#1\postSection{\preSEC \normalbaselines % Somehow needed for multicol (with offinterlineskip)??? \begin{multicols}{\COLUMNS}[\SETSEC{#1}]\relax \RecordSqueeze % Needed? Probably to undo \normalbaselines... } EOP my $normal_text_class = <<'EOP'; \documentclass[12pt]{article} \usepackage[margin=1cm,nohead,nofoot]{geometry} % may need a4paper/letterpaper \usepackage{multicol} EOP ############################################ Output preambles for my $o (grep defined $_->[0], [$out_envelop_backcover, "$class_backcover$set_columns$common_enc$cdcover_set" . "$backcover_on", {qw(FlapNameFont \bfseries\scriptsize FlapFont \tiny TitleNameFont \bfseries\footnotesize TitleFont \tinyish SectionFont \bfseries\footnotesize SubSectionFont \bfseries\scriptsize RecordFont \mdseries\tinyish columns 3 global_multicol 1 rules 1)}], [$out_cdcover_std, "$class_cdcover_std$common_enc$cdcover_set$cdcover_on", {qw(TitleNameFont \bfseries\tiny TitleFont \tiny SectionFont \bfseries\small SubSectionFont \bfseries\small RecordFont \mdseries\scriptsize columns 2)}], [$out_normal_text, "$normal_text_class$set_columns$common_enc$set_headers", {qw(TitleNameFont \bfseries\normalsize TitleFont \small SectionFont \bfseries\normalsize SubSectionFont \bfseries\small RecordFont \mdseries\footnotesize columns 2 rules 1)}], [$out_cdcover_our, "$duplex_instr_and_class$set_columns$common_enc" . "$set_headers", {qw(TitleNameFont \bfseries\normalsize TitleFont \footnotesize SectionFont \bfseries\normalsize SubSectionFont \bfseries\small RecordFont \mdseries\scriptsize columns 2 rules 1 frame 1)}]) { my($out,$txt) = ($o->[0], $o->[1]); print $out output_setup($o->[2]); if (defined $opt{B}) { print $out <<"EOQ" . <<'EOP'; # \include has extra \clearpage \\input{$opt{B}_common}% EOQ %\def\squeezeContunuationLines{% typeset continuation lines almost max-squeezed % % ARGS = BETWEEN LINES / +ITS INC / BASELINE-SKIP-in-height-of(0) / PARSKIP % % PARSKIP is the skip between records, the rest before continuation line % \contunuationLineSkip{-0.2ex}{plus 0.24ex}{1.3}{-0.08ex plus 0.44ex}} EOP } else { print $out $common; } print $out $txt if defined $txt; print $out $title_rules_on if $o->[2]{rules}; print $out $global_multicol_on if $o->[2]{global_multicol}; print $out <<'EOP' if $o->[2]{frame}; \pagestyle{framed} EOP print $out $recordSetup; print $out <<"EOP" if defined $opt{B}; # \include has extra \clearpage \\input{$opt{B}_list}% EOP } print $out_common $common if defined $opt{B}; my $long_list = $both ? "$opt{B}_list_long" : 'another_list'; my $optional_cont = $both ? <<'EOB' : <<'EOF'; \iftrue % ================ Replace by \iffalse to not embed a longer list... EOB \iffalse % ================ Replace by \iftrue to embed a longer list... EOF $optional_cont .= <<'EOQ' . <<EOI . <<'EOP'; \pagebreak[4] % Mandatory page break \let\preSEC\preSECi % Reset multicolumn logic %%% You can change here the configuration of fonts, squeezes and columns; %%% Just copy the needed lines from top of file; or uncomment and edit these: % \def\SectionFont{\bfseries\normalsize} % \def\SubSectionFont{\bfseries\scriptsize} % \def\RecordFont{\tinyish} % \def\COLUMNS{3} % Choose suitable number of columns %%% This is very squeezed; increase by 0.8ex to loosen: % \def\preSectionSKIP{0.3ex plus 0.6ex minus 0.3ex} % \def\postSectionSKIP{0.05ex plus 0.6ex minus 0.15ex} \RecordFont \RecordSqueeze % enable now %\def\squeezeContunuationLines{% typesets continuation lines almost max-squeezed % % ARGS = BETWEEN LINES / +ITS INC / BASELINE-SKIP-in-height-of(0) / PARSKIP % % PARSKIP is the skip between records, the rest before continuation line % \contunuationLineSkip{-0.2ex}{plus 0.24ex}{1.3}{-0.08ex plus 0.44ex}} { \def\preSubSectionSKIP{1.2pt} % Pre- and post- level-2 heading \def\postSubSectionSKIP{0.6pt} \topRULES EOQ \\input{$long_list}% EOI \end{multicols} \bottomRULES EOP $optional_cont .= <<'EOP' if $opt{T}; \begin{center} \unskip \addvspace{3.6pt}% {\bf \small \totalDuration\today} \end{center} EOP $optional_cont .= <<'EOP'; \vfill\vfil \reportLyricsSyntaxEtc } \fi \end{document} EOP print $out_cdcover_std <<'EOP' if $opt{B} and defined $out_cdcover_std; %\end{multicols} \reportLyricsSyntaxEtc \end{bookletsheets} %\end{bookletsheetsTwo} %\end{singlesheet} \end{document} EOP print $out_envelop_backcover <<'EOP' if defined $out_envelop_backcover; \end{multicols} \bottomRULES EOP print $out_envelop_backcover <<'EOP' if $opt{T} and defined $out_envelop_backcover; %\addvspace{-1.5ex} \begin{center}\unskip\addvspace{3.6pt}% {\bf \small \totalDuration\today} \end{center} EOP print $out_envelop_backcover <<'EOP' if defined $out_envelop_backcover; \vfill\vfil \reportLyricsSyntaxEtc %\vfill\vfil \end{backcover-ml} %\end{backsheet} %\end{bookletsheets} %\end{bookletsheetsTwo} %\end{singlesheet} \end{document} EOP for my $out (grep defined, $out_normal_text, $out_cdcover_our) { print $out <<'EOP'; \end{multicols} \bottomRULES EOP print $out <<'EOP' if $opt{T}; %\addvspace{-1.5ex} \begin{center}\unskip\addvspace{3.6pt}% {\bf \small \totalDuration\today} \end{center} EOP print $out <<'EOP', $optional_cont; \vfill\vfil \reportLyricsSyntaxEtc EOP } my $d = Cwd::cwd; sub process_files () { for (@ARGV) { $had_subdir = 0; warn("Not a directory: `$_'"), next unless -d; chdir $_ or die "Can't chdir `$_'"; (my $name = $_) =~ s,.*[/\\](?!$),,; $name =~ s/_/ /g; $print_dir = $name; @level = (0); @performers = ($print_dir); @comments = (undef); # print <<EOP; #\\preSection $name\\postSection #EOP $author = ''; $TIT1 = ''; undef $previous; File::Find::find { wanted => \&print_mp3, no_chdir => 1, postprocess => \&unwind_dir, preprocess => \&preprocess_and_sort_with_aligned_numbers }, '.'; print_this_mp3({}); # Flush the postponed data chdir $d or die; } } @ARGV = '.' unless @ARGV; process_files; if ($opt{T}) { my $tot = to_duration $total_sec; print "\\gdef\\totalDuration{Total time: $tot. }%\n"; } print $out_cdcover_std <<'EOP' if defined $out_cdcover_std; %\end{multicols} \reportLyricsSyntaxEtc \end{bookletsheets} %\end{bookletsheetsTwo} %\end{singlesheet} \end{document} EOP if ($opt{B}) { # Otherwise cdcover is STDOUT... (close $_ or warn "Error closing wrapper for write: $!"), undef $_ for grep defined, $out_envelop_backcover, $out_common, $out_titles, $out_cdcover_std, $out_normal_text, $out_cdcover_our, $out_list; } if ($opt{B} and $both) { # 2nd pass set_plan 'long'; delete $opt{c} unless defined $ini_c; open LIST, "> $opt{B}_list_long.tex" or die "open `$opt{B}_list_long.tex' for write: $!"; select LIST; $out_list = \*LIST; if ($enc{o}) { eval { binmode $out_list, ":encoding($enc{o})"} or warn $@; } undef $previousTop; process_files; } =head1 NAME typeset_audio_dir - produce B<TeX> listing of directories with audio files. =head1 SYNOPSIS # E.g.: current directory contains 1 subdirectory-per-performer. # Inside each directory the structure is # Composer/single*.mp3 (fine-grain output: <title> field) # and # Composer/MultiPart/part*.mp3 (fine-grain output: <album> field) # Emit year and duration info; use "Quartets" as basename typeset_audio_dir -y -T -B Quartets * # Likewise, but this directory structure is w.r.t. current directory; # Do not emit year and duration, output to STDOUT typeset_audio_dir . typeset_audio_dir # Use artist as toplevel heading, album as the 2nd level; use track numbers; # name is based on title for any depth in directory hierarchy; # likewise for generation of 2nd level heading. Mark audios with lyrics typeset_audio_dir -ynTL -P long -B All # Likewise, but the name is based on the album; ignore comments typeset_audio_dir -yTn -P short -B All_short # Likewise, but produce both long and short listings. The short one serves # as a table-of-contents for the long one typeset_audio_dir -ynTL -P short,long -B All =head1 DESCRIPTION Scans directory (or directories) given on the command line, using L<MP3::Tag|MP3::Tag> to obtain information about audio files (to process non-MP3 files, extra modules may be needed, see L<MP3::Tag>, and B<-r FILENAME_FILTER> option must be given). Produces (one or more, depending on B<-B> option) B<TeX> files with commands to typeset human-readable listings. Non-directories on the command line are ignored. (May also be used to process non-audio files, if L<MP3::Tag|MP3::Tag> may extract the title/etc info from them.) With B<-B>, the file F<*_list.tex> contains all the data about audio files (when B<-P> with both C<short,long> is given, another similar file F<*_list_long.tex> is also written); the file F<*_titles.tex> contains a 0th approximation to the possible "title" of the collection (one based on B<-N> option and a short summary of toplevel directories). The file F<*_common.tex> contains macros common for the following files. The remaining files define different environments to typeset the listing (including two TeX files with "content" as needed): a "normal" listing (for A4/Letter, F<*_text.tex>), two flavors of a "compressed" listing (for jewel case insert, F<*_cdbooklet.tex> and F<*_cdcover.tex>), and a back insert for the jewel case (F<*_backcover.tex>). The intent is to support many different layouts of directories with audio files with as little tinkering with command-line options as possible; thus C<type_audio_dir> tries to do as much as possible by guestimates. Similtaneously, one should be able to tune the script to handle the layout they have. The script emits headers for several levels of "grouping". The "toplevel" group header is emited once for every "toplevel" directory (with audio files), further headers are emited based on changes in descriptors of the audio files during scan. =head1 OPTIONS =over =item B<-B> gives basename of the output file. Without this option the script will output to STDOUT. With this option, script separates the layout from content, and produces 6 B<TeX> files: basename_text.tex basename_cdcover.tex basename_cdbooklet.tex basename_backcover.tex basename_list.tex basename_titles.tex basename_common.tex The last file contains the common macros needed for typesetting. The previous two files contain the information about audio files encountered. The others files contain frameworks to typeset this information. The first four files are supposed to be human-editable; they will not be overwritten by a following rerun with the same basename given to the script. By editing these files, one can choose between several encodings, languages, multicolumn output, font size, interline spacing, margins, page size etc. The C<*_titles.tex> file is of mixed nature: it reflects the content of audio files, I<and> is supposed to be human-editable. It will be overwritten unless it is Read-Only; so if you hand-edit it, make it Read-Only. Similar overwrite logic is applied to C<*_common.tex> file too. =item B<-P> C<plan> a shortcut to setting hairy options; currently, two values of C<plan> are supported: short => -1 "" -2 "" -t -1e100 -a -1e100 -c long => -1 "" -2 "@l" -t 1e100 -a 1e100 for generation of short/long listings. In the short listing, records correspond to the album names. In the long listing, records correspond to individual files, and album names serve as second-level headings. =item B<-y> Emit year (or date) information if present. Very long date descriptors (e.g., when multiple ranges of dates are present) are compressed as much as possible. =item B<-Y> Emit the whole date information if present. =item B<-T> Emit duration information. =item B<-n> Enable emit track number. Environment variable TYPESET_AUDIO_TRACK may contain the format to interpolate for typesetting (defaults to C<%{mA}%{n1}>). For example, set TYPESET_AUDIO_TRACK to C<%{n1}> to use "pure" track number instead of combination of media/disk number and track number. =item B<-1> Toplevel header format; is interpolate()d by L<MP3::Tag> based on the content of the first audio file encountered during scan of this toplevel directory. The empty value is the default; in this case the header is based on the name of the directory (with some normalization: underscore is converted to space). =item B<-2> Second-level heading format; is interpolate()d by L<MP3::Tag>. Calculated based on the content of each audio file. The heading is emited when the interpolated value changes (subject to option L<B<-a>>). Empty string disables generation. =item B<-a> Ignore changes to the second-level heading for directories deeper than this inside top-level directory. Defaults to 2. For example, in Performer/Composer/Collection/part1.mp3 Performer/Composer/Collection/part2.mp3 Performer/Composer/single1.mp3 Performer/Composer/single2.mp3 if the toplevel directory is F<Performer>, then changes of the second-level header in F<single*.mp3> would create a new second-level heading. However, similar changes in F<part*.mp3> will not create a new heading. B<NOTE:> maybe this default of 2 is not very intuitive. It is recommended to explicitly set this option to the value you feel appropriate (C<1e100> would play role of infinity - so any change will generate a new second-level heading). =item B<-t> The title-cutoff depth (w.r.t. toplevel directory). Defaults to 2. In audio files deeper than this the album C<%l> is used as the name; otherwise the title C<%t> of the audio file is used. Set to C<-1e100> to always use C<%l>, and to C<1e100> to always use C<%a>. =item B<-@> Replace all C<@> by C<%> in options. Very useful with DOSISH shells to include C<%>-escapes necessary for L<MP3::Tag>'s interpolate(). =item B<-e ENCODINGS> Sets encodings for output files, directory names (when uses to generate headings), and hint files. B<ENCODINGS> is a comma-separated list of directives; each directive is either an encoding name (to use for all targets), or C<TARGET_LETTERS:encoding>. Target letters are C<o>, C<d>, and C<h> for output, names of directories, and files F<.top_heading> correspondingly. Use 0 instead of an encoding to do byte-oriented read/write. =item B<-c> What to use as "comment" for a record (a part which is typeset differently). If not given, the ID3v2 frame C<TXXX[add-to:file-by-person,l,t,n]> is used. If the content of this field is contained at end of the title, nothing is added, just this part is typeset differently. =item B<-L> Mark files with embedded (un)syncronized lyrics and pictures. Put the explanation of used symbols at the end of the listing. =item B<-N COLLECTION_NAME> (defaults to "COLLECTION") the name of the collection to insert into the file F<*_title.tex>. The interaction with encoding may be less than intuitive; you may want to check/edit this file for corrections. =item B<-F FONT_ENCODING_SYMBOL> (defaults to C<T2A>): the name of C<LaTeX> font encoding. If your installation is broken and C<T2A> is not available, you may try C<T1> or C<OT1>. See L<"PROBLEMS when TYPESETTING">. =item B<-r FILENAME_FILTER> sets the regular expression for filenames to look for (the default is C<(?i:\.mp3$)>. =back =head1 Info read from file system The following files are used to give hints to F<typeset_audio_dir>: =over =item F<.content_comment> Content of this file is used as a comment field in the output for all files in this directory. =item F<.top_heading> If empty, indicates that when the depth of files modifies the output, it is calculated w.r.t. the subdirectories of the directory of this file (ouph!). If contains a number, it is added to this depth. B<Example>: suppose your section heading is based on directory names. Suppose the directory tree to process contains a directory F<Mixed/2009>. If you want names of subdirectories of this directory to become section headings, make file F<Mixed/2009/.top_heading> which contains C<0>. If the same holds for other subdirectories of F<Mixed>, instead of creation of such file in all year-subdirectories, one can make file F<Mixed/.top_heading> which contains C<-1>. Otherwise the content of this file is used as a toplevel heading for this directory. =back =head1 TYPESETTING Running this script will only generate necessary TeX files, but will not typeset them (they will look much better if you first edit the files to suit your needs). Recall how to typeset TeX documents (here we assume PDF target): latex document.tex && dvips document.dvi && ps2pdf document (a lot of temporary files are going to be generated too; you can break this into multiple commands on C<&&>). Some of the files (e.g., F<..._cdcover.tex>) fit better with landscape orientation; one needs latex document.tex && dvips -t landscape document.dvi && ps2pdf document With F<..._cdbooklet.tex>, for best result, one better should rearrange pages for booklet 2up 2-pages-per-side printing: latex document.tex && dvips -t landscape -f < document.dvi | psbook | pstops "2:0(0,-6cm)+1(0,6cm)" > document.ps && ps2pdf -dAutoRotatePages=/None document (all on one line, or give 3 separate commands, breaking on C<&&>; more details on running dvips is put in the beginning of the TeX file). If you can easily print a F<.ps> file, you can omit the last step. (The option C<-dAutoRotatePages=/None> interferes with viewing; one may omit it I<unless> one does "extra flipping of even pages", as below.) Note that this assumes that when you send files to printer you request duplexing with "binding on the short side of paper". If you printer can survive manual duplexing, do as usual: print first the even pages in opposite order, reload paper, then print odd pages (you need to understand in which orientation you must put paper back when reloading; there are 4 variants, and only one is correct ;-). For "real" duplex printers, see below. =head1 PROBLEMS when TYPESETTING =over 4 =item incomplete installations ! Font T2A/cmr/m/n/10.95=larm1095 at 10.95pt not loadable: Metric (TFM) file not found. For best multilanguage coverage I could find, by default the generated LaTeX files use C<T2A>-encoded-fonts with extra Latin characters provided by C<textcomp>. Apparently, some C<TeX> installations omit C<T2A> encoding tables. You may want to change C<T2A> to, e.g., C<T1> by using option C<-F T1>. =item In a booklet, page 1 is at end, the rest is a mess The C<landscape> option of C<geometry> package should rotate the page 90 degrees. Depending on the way it is configured, the direction of rotation varies. If F<.pdf> file obtained with C<-dAutoRotatePages=/None> option has top of page on the left, you may need to invert the direction of shifting: instead of C<2:0(0,-6cm)+1(0,6cm)> one should use C<2:0(0,6cm)+1(0,-6cm)>. =item Duplexing with "bind on the long side of paper" By default, most duplex printers are configured to "bind on the long side of paper"; so to avoid manual setup of binding options, you may want to flip even pages in the generated file. To do this, add an extra F<ps2ps> step at the end of pipeline, e.g.: ... psbook | pstops "2:0(0,-6cm)+1(0,6cm)" | pstops "2:0,1U(1w,1h)" > document.ps =item A4-sized paper vs. Letter-sized paper Some TeX/PS installations do not have correctly set-up site configuration files, so do not know what is the usual paper size on your printer. Fortunately, all steps of the typesetting pipeline allow a manual reconfiguration. Unfortunately, command options for the required reconfigurations are subtly different for different steps. For example, if your TeX/PS-utils think that your paper size is C<letter>, while what you actually print to is C<a4>, you need to do the following (depending on which configuration files are broken, you might be able to omit some modifications): =over 4 =item 1. Add C<a4paper> to the C<\usepackage[...,...]{geometry}> options (the comma-separated list in brackets) in TeX files which use C<geometry>. =item 2. Add C<-t a4> as a C<dvips> options. =item 3. Add C<-pa4> as a C<pstops> option. (If it breaks rotation, omit it, sigh!) =item 4. Add C<-sPAPERSIZE=a4> as a C<ps2pdf> option. =back Example commandline working with some of complications dvips -t landscape -f < All_cdbooklet-a4.dvi | psbook | pstops -pa4 "2:0(0,-6cm)+1(0,6cm)" | pstops -pa4 "2:0,1U(1w,1h)" > Output-even_flipped-a4.ps && ps2pdf -sPAPERSIZE=a4 -dAutoRotatePages=/None Output-even_flipped-a4 Likewise, quite often one needs to add C<-pletter> to C<ps2ps> commandlines for correct printing to letter-size paper. You can check the resulting PDF file in a viewer: the status line should show the correct paper size (e.g., 8.5in x 11in is "Letter"), even pages should be flipped (for binding "on the long side"), and the wireframes on different pages should be positioned exactly at same positions (for visual verification, choose "fit-to-page" scaling, and quickly switch pages back-and-forth by keyboard or by "Next page" button). =item Warnings from dvips Note also that if your C<TeX/dvips> installation is I<completely correct>, you can remove C<-t landscape> from your C<dvips> command line; not removing it would produce a warning C<both both landscape and papersize specified: ignoring landscape>. =item Systematic duplexing offset Some printers can't reliably match positions on the front and back side when printing; there is little one can do with it. However, if your printer adds some I<consistent> misplacement of front and back sides, one can put workarounds for it. For example, when "binding on the short side", the common error is that (in landscape orientation) backside is offset horizontally w.r.t. frontside. For example, if offset is 3.4mm to the left, one can shift the image on the page by half of this, 0.17cm to the left: replace C<"2:0(0,-6cm)+1(0,6cm)"> by "2:0(0,-6.17cm)+1(0,5.83cm)". With "binding on the long side", the typical error is vertical offset. To work around, one needs to shift vertically (again, by half the amount) I<after> flipping even pages. To shift 0.17cm up, add an extra step C<pstops "(0.17cm,0)"> to the pipeline after the C<"2:0,1U(1w,1h)"> step (untested). =back =head1 HINTS The default font sizes and density of type is chosen to optimize printing of a DL-DVD collection of short high quality audio (of song-like duration: about 100 subheadings, and 2000 audio files). You may improve the visual quality if you tune the typesetting to your particular needs. The most commonly changed settings are on top of the generated files. These are fonts and degrees of vertical squeeze of paragraphs for the principal title, titles of sections (1st level) and subsections (2nd level), and of actual records emited for each audio file, as well as the number of columns. Slightly further in the file are settings for gaps to left around section headings, and for fine-tuning of squeezing. Do not forget that if you can't describe a complicated layout by command-line options, you still have a possibility to run this script many times (once per directory with "handable layout", using B<-B> and other options suitable for this subdirectory). Then you can use B<LaTeX> C<\input> directives to include the generated F<basename_list.tex> files into the toplevel C<LaTeX> file. You can also redefine C<\preSection * \postSection> to do nothing, and put the necessary code to generate the headers into the top-level file. Modify the formatting macros to suit your needs. (Of more tricky stuff, mention C<\squeezeContunuationLines> and C<\parskip>, which regulate the density of lines - without changing the line font; note that setting C<\parskip> is a part of the action of C<\squeezeContunuationLines>. C<\columnsep> regulates the horizontal separation of columns. One can also fine-tune the vertical position of the start of the first page; for backcover, also tune up C<\CDbackMargin> and C<\CDbackTopMargin>. The definition(s) of C<\squeezeContunuationLines> are commented out (by C<%>) in non-F<*_common.tex> files; you may uncomment it, and tune it up separately for each TeX file.) One can combine two (or more) lists (e.g., one with the short style, and one with the long style) into one output file; the generated files F<..._cdbooklet.tex> and F<..._text.tex> already have a necessary template (disabled) at the end. (Moreover, with B<-P> C<short,long>, this is done automatically. For example, with two lists created in L<"SYNOPSIS">, F<All_list.tex>, and F<All_short_list.tex>, find C<\iffalse> near the end of F<All_short_cdbooklet.tex> and change it to C<\iftrue>; then change the name in the directive \input{another_list} to F<All_list> This will make the "short" cdbooklet become a kind of "table of contents" for the combined "short+long" cdbooklet. (Of course, one can change the values of macros C<\SectionFont> etc, C<\COLUMNS>, type of squeeze to suit your needs - the point is that they should not be necessarily the same for the second list.) =head1 WORKFLOW The module is quite flexible; here is one of the possible workflows (suitable if all you need is B<-P> <short> and B<-P> <long>: Put all the "toplevel" directories as subdirectories of the current directory (well, this is not really necessary!), and put the heading to use for each directory into a file F<.top_heading>. You may need to specify the encoding used in this file into the options (do similar to C<-e h:cp1252>). =cut ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/�����������������������������������������������������������������������������������0000700�0000000�0000000�00000000000�11417072072�010171� 5����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/Encode/����������������������������������������������������������������������������0000700�0000000�0000000�00000000000�11417072072�011366� 5����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/Encode/transliterate_win1251.pm����������������������������������������������������0000700�0000000�0000000�00000003317�11171241736�016004� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w $VERSION = '1.00'; use strict; package Encode::transliterate_win1251; my $debug; # Assume that FROM are 1-char, and have no REx charclass special characters my $uc = "ß ÂÅÐÒÛÓÈÎÏØ Ù ÀÑÄÔÃÕÉÊËÇÜ ÖÆ ÁÍÌÝÞ × ¨Ú"; my $ul = "YAVERTYUIOPSHSCHASDFGHJKLZ''CZHBNMEYUCHE'"; # titlecase and random alternative translations my $tc = "ß Ø Ù Æ Þ Þ Þ þ × × × ÷ ß ß ÿ ß ß ÿ Ù Ù ù ¨ ¨ ¸ "; my $tl = "YaShSchZhYuIUIuiuChTchTCHtchIAIaiaJAJajaTCHTchtchJOJojo"; # Assume that 1-char parts of TO have no REx charclass special characters my $lc = "ÿ âåðòûóèîïø ù àñäôãõéêëçüöæ áíìýþ ÷ ¸ú¹"; my $ll = "yavertyuiopshschasdfghjklz'czhbnmeyuche'N"; sub prepare_translation { my ($from, $to) = @_; die "Mismatch of length:\nfrom: '$from'\nto: '$to'\n" unless length($from) == length $to; my @from = ($from =~ /(\S\s*)/g); my (%hash_from, %hash_to); for my $chunk (@from) { my $chunk_to = substr($to, 0, length $chunk); substr($to, 0, length $chunk) = ""; $chunk =~ s/\s+$//; $hash_from{$chunk} = $chunk_to; # Prefer earlier definition for reverse translation $hash_to{$chunk_to} = $chunk unless exists $hash_to{$chunk_to}; } (\%hash_from, \%hash_to) } sub make_translator { my ($hash) = @_; die unless keys %$hash; my @keys2 = grep length > 1, keys %$hash; my $keys1 = join '', grep length == 1, keys %$hash; my $rex = ''; $rex .= (join('|', sort {length $b <=> length $a} @keys2) . '|') if @keys2; $rex .= "[\Q$keys1\E]" if length $keys1; warn "rex = '$rex'\n" if $debug; eval "sub {s/($rex)/\$hash->{\$1}/g}" or die; } sub cyr_table {"$uc$lc$tc"} sub lat_table {"$ul$ll$tl"} #my $to = make_translator( (prepare_translation("$uc$lc$tc", "$ul$ll$tl"))[0] ); 1; �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/MP3/�������������������������������������������������������������������������������0000700�0000000�0000000�00000000000�11417072072�010570� 5����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/MP3/Tag/���������������������������������������������������������������������������0000700�0000000�0000000�00000000000�11417072072�011303� 5����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/MP3/Tag/CDDB_File.pm���������������������������������������������������������������0000700�0000000�0000000�00000021433�11171241456�013303� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package MP3::Tag::CDDB_File; use strict; use File::Basename; use File::Spec; use vars qw /$VERSION @ISA/; $VERSION="1.00"; @ISA = 'MP3::Tag::__hasparent'; =pod =head1 NAME MP3::Tag::CDDB_File - Module for parsing CDDB files. =head1 SYNOPSIS my $db = MP3::Tag::CDDB_File->new($filename, $track); # Name of audio file my $db = MP3::Tag::CDDB_File->new_from($record, $track); # Contents of CDDB ($title, $artist, $album, $year, $comment, $track) = $db->parse(); see L<MP3::Tag> =head1 DESCRIPTION MP3::Tag::CDDB_File is designed to be called from the MP3::Tag module. It parses the content of CDDB file. The file is found in the same directory as audio file; the list of possible file names is taken from the field C<cddb_files> if set by MP3::Tag config() method. =over 4 =cut # Constructor sub new_from { my ($class, $data, $track) = @_; bless {data => [split /\n/, $data], track => $track}, $class; } sub new_setdir { my $class = shift; my $filename = shift; $filename = $filename->filename if ref $filename; $filename = dirname($filename); return bless {dir => $filename}, $class; # bless to enable get_config() } sub new_fromdir { my $class = shift; my $h = shift; my $dir = $h->{dir}; my ($found, $e); my $l = $h->get_config('cddb_files'); for my $file (@$l) { my $f = File::Spec->catdir($dir, $file); $found = $f, last if -r $f; } return unless $found; local *F; open F, "< $found" or die "Can't open `$found': $!"; if ($e = $h->get_config('decode_encoding_cddb_file') and $e->[0]) { eval "binmode F, ':encoding($e->[0])'"; # old binmode won't compile... } my @data = <F>; close F or die "Error closing `$found': $!"; bless {filename => $found, data => \@data, track => shift, parent => $h->{parent}}, $class; } sub new { my $class = shift; my $h = $class->new_setdir(@_); $class->new_fromdir($h); } sub new_with_parent { my ($class, $filename, $parent) = @_; my $h = $class->new_setdir($filename); $h->{parent} = $parent; $class->new_fromdir($h); } # Destructor sub DESTROY {} =item parse() ($title, $artist, $album, $year, $comment, $track) = $db->parse($what); parse_filename() extracts information about artist, title, track number, album and year from the CDDB record. $what is optional; it maybe title, track, artist, album, year, genre or comment. If $what is defined parse() will return only this element. Additionally, $what can take values C<artist_collection> (returns the value of artist in the disk-info field DTITLE, but only if author is specified in the track-info field TTITLE), C<title_track> (returns the title specifically from track-info field - the C<track> may fall back to the info from disk-info field), C<comment_collection> (processed EXTD comment), C<comment_track> (processed EXTT comment). The returned year and genre is taken from DYEAR, DGENRE, EXTT, EXTD fields; recognized prefixes in the two last fields are YEAR, ID3Y, ID3G. The declarations of this form are stripped from the returned comment. An alternative syntax "Recorded"/"Recorded on"/"Recorded in"/ is also supported; the format of the date recognized by ID3v2::year(), or just a date field without a prefix. =cut sub return_parsed { my ($self,$what) = @_; if (defined $what) { return $self->{parsed}{a_in_title} if $what =~/^artist_collection/i; return $self->{parsed}{t_in_track} if $what =~/^title_track/i; return $self->{parsed}{extt} if $what =~/^comment_track/i; return $self->{parsed}{extd} if $what =~/^comment_collection/i; return $self->{parsed}{DISCID} if $what =~/^cddb_id/i; return $self->{parsed}{album} if $what =~/^al/i; return $self->{parsed}{artist} if $what =~/^a/i; return $self->{parsed}{track} if $what =~/^tr/i; return $self->{parsed}{year} if $what =~/^y/i; return $self->{parsed}{comment}if $what =~/^c/i; return $self->{parsed}{genre} if $what =~/^g/i; return $self->{parsed}{title}; } return $self->{parsed} unless wantarray; return map $self->{parsed}{$_} , qw(title artist album year comment track); } my %r = ( 'n' => "\n", 't' => "\t", '\\' => "\\" ); sub parse_lines { my ($self) = @_; return if $self->{fields}; for my $l (@{$self->{data}}) { next unless $l =~ /^\s*(\w+)\s*=(\s*(.*))/; my $app = $2; $self->{fields}{$1} = "", $app = $3 unless exists $self->{fields}{$1}; $self->{fields}{$1} .= $app; $self->{last} = $1 if $1 =~ /\d+$/; } s/\\([nt\\])/$r{$1}/g for values %{$self->{fields}}; } sub parse { my ($self,$what) = @_; return $self->return_parsed($what) if exists $self->{parsed}; $self->parse_lines; my %parsed; my ($t1, $c1, $t2, $c2) = map $self->{fields}{$_}, qw(DTITLE EXTD); my $track = $self->track; if ($track) { my $t = $track - 1; ($t2, $c2) = map $self->{fields}{$_}, "TTITLE$t", "EXTT$t"; } my ($a, $t, $aa, $tt, $a_in_title, $t_in_track); ($a, $t) = split /\s+\/\s+/, $t1, 2 if defined $t1; ($a, $t) = ($t, $a) unless defined $t; ($aa, $tt) = split /\s+\/\s+/, $t2, 2 if defined $t2; ($aa, $tt) = ($tt, $aa) unless defined $tt; undef $a if defined $a and $a =~ /^\s*(<<\s*)?(Various Artists|compilation disc)\s*(>>\s*)?$/i; undef $aa if defined $aa and $aa =~ /^\s*(<<\s*)?(Various Artists|compilation disc)\s*(>>\s*)?$/i; $a_in_title = $a if defined $a and length $a and defined $aa and length $aa; $aa = $a unless defined $aa and length $aa; $t_in_track = $tt; $tt = $t unless defined $tt and length $tt; my ($y, $cat) = ($self->{fields}{DYEAR}, $self->{fields}{DGENRE}); for my $f ($c2, $c1) { if (defined $f and length $f) { # Process old style declarations while ($f =~ s/^\s*((YEAR|ID3Y)|ID3G)\b:?\s*(\d+)\b\s*(([;.,]|\s-\s)\s*)?//i || $f =~ s/(?:\s*(?:[;.,]|\s-\s))?\s*\b((YEAR|ID3Y)|ID3G)\b:?\s*(\d+)\s*([;.,]\s*)?$//i) { $y = $3 if $2 and not $y; $cat = $3 if not $2 and not $cat; } if ($f =~ s{ ((^|[;,.]|\s+-\s) # 1,2 \s* (Recorded (\s+[io]n)? \s* (:\s*)? )? # 3, 4, 5 (\d{4}([-,][-\d\/,]+)?) # 6, 7 \b \s* (?: [.;] \s* )? ((?:[;.,]|\s-\s|$)\s*)) # 8 } { ((($self->{parent}->get_config('comment_remove_date'))->[0] and not ($2 and $8)) ? '' : $1) . ($2 && $8 ? $8 : '') }xeim and not ($2 and $8)) { # Overwrite the disk year for longer forms $y = $6 if $3 or $7 or not $y or $c2 and $f eq $c2; } $f =~ s/^\s+//; $f =~ s/\s+$//; undef $f unless length $f; } } my ($cc1, $cc2) = ($c1, $c2); if (defined $c2 and length $c2) { # Merge unless one is truncation of another if ( defined $c1 and length $c1 and $c1 ne substr $c2, 0, length $c1 and $c1 ne substr $c2, -length $c1 ) { $c2 =~ s/\s*[.,:;]$//; my $sep = (("$c1$c2" =~ /\n/) ? "\n" : '; '); $c1 = "$c2$sep$c1"; } else { $c1 = $c2; } } if (defined $cat and $cat =~ /^\d+$/) { require MP3::Tag::ID3v1; $cat = $MP3::Tag::ID3v1::winamp_genres[$cat] if $cat < scalar @MP3::Tag::ID3v1::winamp_genres; } @parsed{ qw( title artist album year comment track genre a_in_title t_in_track extt extd) } = ($tt, $aa, $t, $y, $c1, $track, $cat, $a_in_title, $t_in_track, $cc2, $cc1); $parsed{DISCID} = $self->{fields}{DISCID}; $self->{parsed} = \%parsed; $self->return_parsed($what); } =pod =item title() $title = $db->title(); Returns the title, obtained from the C<'Tracktitle'> entry of the file. =cut *song = \&title; sub title { return shift->parse("title"); } =pod =item artist() $artist = $db->artist(); Returns the artist name, obtained from the C<'Performer'> or C<'Albumperformer'> entries (the first which is present) of the file. =cut sub artist { return shift->parse("artist"); } =pod =item track() $track = $db->track(); Returns the track number, stored during object creation, or queried from the parent. =cut sub track { my $self = shift; return $self->{track} if defined $self->{track}; return if $self->{recursive} or not $self->parent_ok; local $self->{recursive} = 1; return $self->{parent}->track1; } =item year() $year = $db->year(); Returns the year, obtained from the C<'Year'> entry of the file. (Often not present.) =cut sub year { return shift->parse("year"); } =pod =item album() $album = $db->album(); Returns the album name, obtained from the C<'Albumtitle'> entry of the file. =cut sub album { return shift->parse("album"); } =item comment() $comment = $db->comment(); Returns the C<'Trackcomment'> entry of the file. (Often not present.) =cut sub comment { return shift->parse("comment"); } =item genre() $genre = $db->genre($filename); =cut sub genre { return shift->parse("genre"); } for my $elt ( qw( cddb_id ) ) { no strict 'refs'; *$elt = sub (;$) { return shift->parse($elt); } } 1; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/MP3/Tag/Cue.pm���������������������������������������������������������������������0000700�0000000�0000000�00000016257�11304126510�012363� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package MP3::Tag::Cue; use strict; use File::Basename; #use File::Spec; use vars qw /$VERSION @ISA/; $VERSION="1.00"; @ISA = 'MP3::Tag::__hasparent'; =pod =head1 NAME MP3::Tag::Cue - Module for parsing F<.cue> files. =head1 SYNOPSIS my $db = MP3::Tag::Cue->new($filename, $track); # Name of audio file my $db = MP3::Tag::Cue->new_from($record, $track); # Contents of .cue file ($title, $artist, $album, $year, $comment, $track) = $db->parse(); see L<MP3::Tag> =head1 DESCRIPTION MP3::Tag::Cue is designed to be called from the MP3::Tag module. It parses the content of a F<.cue> file. The F<.cue> file is looked for in the same directory as audio file; one of the following conditions must be satisfied: =over 4 =item * The "audio" file is specified is actually a F<.cue> file; =item * There is exactly one F<.cue> file in the directory of audio file; =item * There is exactly one F<.cue> file in the directory of audio file with basename which is a beginning of the name of audio file. =item * There is exactly one F<.cue> file in the directory of audio file with basename which matches (case-insensitive) a beginning of the name of audio file. =back If no F<.cue> file is found in the directory of audio file, the same process is repeated once one directory uplevel, with the name of the file's directory used instead of the file name. E.g., with the files like this Foo/bar.cue Foo/bar/04.wav audio file F<Foo/bar/04.wav> will be associated with F<Foo/bar.cue>. =cut # Constructor sub new_from { my ($class, $data, $track) = @_; bless {data => [split /\n/, $data], track => $track}, $class; } sub matches($$$) { my ($f1, $f, $case) = (shift, shift, shift); substr($f1, -4, 4) = ''; return $f1 eq substr $f, 0, length $f1 if $case; return lc $f1 eq lc substr $f, 0, length $f1; } sub find_cue ($$) { my ($f, $d, %seen) = (shift, shift); require File::Glob; # "usual" glob() fails on spaces... my @cue = (File::Glob::bsd_glob("$d/*.cue"), File::Glob::bsd_glob('$d/*.CUE')); @seen{@cue} = (1) x @cue; # remove duplicates: @cue = keys %seen; my $c = @cue; @cue = grep matches($_, $f, 0), @cue if @cue > 1; @cue = grep matches($_, $f, 1), @cue if @cue > 1; ($c, @cue) } sub new_with_parent { my ($class, $f, $p, $e, %seen, @cue) = (shift, shift, shift); $f = $f->filename if ref $f; $f = MP3::Tag->rel2abs($f); if ($f =~ /\.cue$/i and -f $f) { @cue = $f; } else { my $d = dirname($f); (my $c, @cue) = find_cue($f, $d); unless ($c) { my $d1 = dirname($d); (my $c, @cue) = find_cue($d, $d1); } } return unless @cue == 1; local *F; open F, "< $cue[0]" or die "Can't open `$cue[0]': $!"; if ($e = ($p or 'MP3::Tag')->get_config1('decode_encoding_cue_file')) { eval "binmode F, ':encoding($e->[0])'"; # old binmode won't compile... } my @data = <F>; close F or die "Error closing `$cue[0]': $!"; bless {filename => $cue[0], data => \@data, track => shift, parent => $p}, $class; } sub new { my ($class, $f) = (shift, shift); $class->new_with_parent($f, undef, @_); } # Destructor sub DESTROY {} =over 4 =item parse() ($title, $artist, $album, $year, $comment, $track) = $db->parse($what); parse_filename() extracts information about artist, title, track number, album and year from the F<.cue> file. $what is optional; it maybe title, track, artist, album, year, genre or comment. If $what is defined parse() will return only this element. Additionally, $what can take values C<artist_collection> (returns the value of artist in the whole-disk-info field C<PERFORMER>, C<songwriter>. =cut sub return_parsed { my ($self,$what) = @_; if (defined $what) { return $self->{parsed}{collection_performer} if $what =~/^artist_collection/i; return $self->{parsed}{album} if $what =~/^al/i; return $self->{parsed}{performer} if $what =~/^a/i; return $self->{parsed}{songwriter} if $what =~/^songwriter/i; return $self->{parsed}{track} if $what =~/^tr/i; return $self->{parsed}{date} if $what =~/^y/i; return $self->{parsed}{comment}if $what =~/^c/i; return $self->{parsed}{genre} if $what =~/^g/i; return $self->{parsed}{title}; } return $self->{parsed} unless wantarray; return map $self->{parsed}{$_} , qw(title artist album year comment track); } my %r = ( 'n' => "\n", 't' => "\t", '\\' => "\\" ); sub parse_lines { my ($self) = @_; # return if $self->{fields}; my $track_seen = ''; my $track = $self->track; $track = -1e100 unless $track or length $track; for my $l (@{$self->{data}}) { # http://digitalx.org/cuesheetsyntax.php # http://wiki.hydrogenaudio.org/index.php?title=Cuesheet # What about http://cue2toc.sourceforge.net/ ? Can it deal with .toc of cdrecord? # http://www.willwap.co.uk/Programs/vbrfix.php - may inspect gap info??? next unless $l =~ /^\s*(REM\s+)? (GENRE|DATE|DISCID|COMMENT|PERFORMER|TITLE |ISRC|POSTGAP|PREGAP|SONGWRITER |FILE|INDEX|TRACK|CATALOG|CDTEXTFILE|FLAGS)\s+(.*)/x; my $field = lc $2; my $val = $3; $val =~ s/^\"(.*)\"/$1/; # Ignore trailing fields after TRACK, FILE $track_seen = $1 if $field eq 'track' and $val =~ /^0?(\d+)/; next if length $track_seen and $track_seen != $track; $self->{fields}{$field} = $val; # unless exists $self->{fields}{$field}; next if length $track_seen; $self->{fields}{album} = $val if $field eq 'title'; $self->{fields}{collection_performer} = $val if $field eq 'performer'; } } sub parse { my ($self,$what) = @_; return $self->return_parsed($what) if exists $self->{parsed}; $self->parse_lines; $self->{parsed} = { %{$self->{fields}} }; # Make a copy $self->return_parsed($what); } =pod =item title() $title = $db->title(); Returns the title, obtained from the C<'Tracktitle'> entry of the file. =cut # *song = \&title; sub title { return shift->parse("title"); } =pod =item artist() $artist = $db->artist(); Returns the artist name, obtained from the C<'Performer'> or C<'Albumperformer'> entries (the first which is present) of the file. =cut sub artist { return shift->parse("artist"); } =pod =item track() $track = $db->track(); Returns the track number, stored during object creation, or queried from the parent. =cut sub track { my $self = shift; return $self->{track} if defined $self->{track}; return if $self->{recursive} or not $self->parent_ok; local $self->{recursive} = 1; return $self->{parent}->track1; } =item year() $year = $db->year(); Returns the year, obtained from the C<'Year'> entry of the file. (Often not present.) =cut sub year { return shift->parse("year"); } =pod =item album() $album = $db->album(); Returns the album name, obtained from the C<'Albumtitle'> entry of the file. =cut sub album { return shift->parse("album"); } =item comment() $comment = $db->comment(); Returns the C<'REM COMMENT'> entry of the file. (Often not present.) =cut sub comment { return shift->parse("comment"); } =item genre() $genre = $db->genre($filename); =cut sub genre { return shift->parse("genre"); } for my $elt ( qw( artist_collection songwriter ) ) { no strict 'refs'; *$elt = sub (;$) { return shift->parse($elt); } } 1; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/MP3/Tag/File.pm��������������������������������������������������������������������0000700�0000000�0000000�00000025077�11304126510�012526� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package MP3::Tag::File; use strict; use Fcntl; use File::Basename; use vars qw /$VERSION @ISA/; $VERSION="1.00"; @ISA = 'MP3::Tag::__hasparent'; =pod =head1 NAME MP3::Tag::File - Module for reading / writing files =head1 SYNOPSIS my $mp3 = MP3::Tag->new($filename); ($title, $artist, $no, $album, $year) = $mp3->parse_filename(); see L<MP3::Tag> =head1 DESCRIPTION MP3::Tag::File is designed to be called from the MP3::Tag module. It offers possibilities to read/write data from files via read(), write(), truncate(), seek(), tell(), open(), close(); one can find the filename via the filename() method. =cut # Constructor sub new_with_parent { my ($class, $filename, $parent) = @_; return undef unless -f $filename or -c $filename; return bless {filename => $filename, parent => $parent}, $class; } *new = \&new_with_parent; # Obsolete handler # Destructor sub DESTROY { my $self=shift; if (exists $self->{FH} and defined $self->{FH}) { $self->close; } } # File subs sub filename { shift->{filename} } sub open { my $self=shift; my $mode= shift; if (defined $mode and $mode =~ /w/i) { $mode=O_RDWR; # read/write mode } else { $mode=O_RDONLY; # read only mode } unless (exists $self->{FH}) { local *FH; if (sysopen (FH, $self->filename, $mode)) { $self->{FH} = *FH; binmode $self->{FH}; } else { warn "Open `" . $self->filename() . "' failed: $!\n"; } } return exists $self->{FH}; } sub close { my $self=shift; if (exists $self->{FH}) { close $self->{FH}; delete $self->{FH}; } } sub write { my ($self, $data) = @_; if (exists $self->{FH}) { local $\ = ''; print {$self->{FH}} $data; } } sub truncate { my ($self, $length) = @_; if ($length<0) { my @stat = stat $self->{FH}; $length = $stat[7] + $length; } if (exists $self->{FH}) { truncate $self->{FH}, $length; } } sub size { my ($self) = @_; return -s $self->{FH} if exists $self->{FH}; return -s ($self->filename); } sub seek { my ($self, $pos, $whence)=@_; $self->open unless exists $self->{FH}; seek $self->{FH}, $pos, $whence; } sub tell { my ($self, $pos, $whence)=@_; return undef unless exists $self->{FH}; return tell $self->{FH}; } sub read { my ($self, $buf_, $length) = @_; $self->open unless exists $self->{FH}; return read $self->{FH}, $$buf_, $length; } sub is_open { return exists shift->{FH}; } # keep the old name *isOpen = \&is_open; # read and decode the header of the mp3 part of the file # the raw content of the header fields is stored, the values # are not interpreted in any way (e.g. layer==3 means 'Layer I' # as specified in the mp3 format) sub get_mp3_frame_header { my ($self, $start) = @_; $start = 0 unless $start; if (exists $self->{mp3header}) { return $self->{mp3header}; } $self->seek($start, 0); my ($data, $bits)=""; while (1) { my $nextdata; $self->read(\$nextdata, 512); return unless $nextdata; # no header found $data .= $nextdata; if ($data =~ /(\xFF[\xE0-\xFF]..)/) { $bits = unpack("B32", $1); last; } $data = substr $data, -3 } my @fields; for (qw/11 2 2 1 4 2 1 1 1 2 2 1 1 2/) { push @fields, oct "0b" . substr $bits, 0, $_; $bits = substr $bits, $_ if length $bits > $_; } $self->{mp3header}={}; for (qw/sync version layer proctection bitrate_id sampling_rate_id padding private channel_mode mode_ext copyright original emphasis/) { $self->{mp3header}->{$_}=shift @fields; } return $self->{mp3header} } # use filename to determine information about song/artist/album =pod =over 4 =item parse_filename() ($title, $artist, $no, $album, $year) = $mp3->parse_filename($what, $filename); parse_filename() tries to extract information about artist, title, track number, album and year from the filename. (For backward compatibility it may be also called by deprecated name read_filename().) This is likely to fail for a lot of filenames, especially the album will be often wrongly guessed, as the name of the parent directory is taken as album name. $what and $filename are optional. $what maybe title, track, artist, album or year. If $what is defined parse_filename() will return only this element. If $filename is defined this filename will be used and not the real filename which was set by L<MP3::Tag> with C<MP3::Tag-E<gt>new($filename)>. Otherwise the actual filename is used (subject to configuration variable C<decode_encoding_filename>). Following formats will be hopefully recognized: - album name/artist name - song name.mp3 - album_name/artist_name-song_name.mp3 - album.name/artist.name_song.name.mp3 - album name/(artist name) song name.mp3 - album name/01. artist name - song name.mp3 - album name/artist name - 01 - song.name.mp3 If artist or title end in C<(NUMBER)> with 4-digit NUMBER, it is considered the year. =cut *read_filename = \&parse_filename; sub return_parsed { my ($self,$what) = @_; if (defined $what) { return $self->{parsed}{album} if $what =~/^al/i; return $self->{parsed}{artist} if $what =~/^a/i; return $self->{parsed}{no} if $what =~/^tr/i; return $self->{parsed}{year} if $what =~/^y/i; return $self->{parsed}{title}; } return $self->{parsed} unless wantarray; return map $self->{parsed}{$_} , qw(title artist no album year); } sub parse_filename { my ($self,$what,$filename) = @_; unless (defined $filename) { $filename = $self->filename; my $e; if ($e = $self->get_config('decode_encoding_filename') and $e->[0]) { require Encode; $filename = Encode::decode($e->[0], $filename); } } my $pathandfile = $filename; $self->return_parsed($what) if exists $self->{parsed_filename} and $self->{parsed_filename} eq $filename; # prepare pathandfile for easier use my $ext_rex = $self->get_config('extension')->[0]; $pathandfile =~ s/$ext_rex//; # remove extension $pathandfile =~ s/ +/ /g; # replace several spaces by one space # Keep two last components of the file name my ($file, $path) = fileparse($pathandfile, ""); ($path) = fileparse($path, ""); my $orig_file = $file; # check which chars are used for seperating words # assumption: spaces between words unless ($file =~/ /) { # no spaces used, find word seperator my $Ndot = $file =~ tr/././; my $Nunderscore = $file =~ tr/_/_/; my $Ndash = $file =~ tr/-/-/; if (($Ndot>$Nunderscore) && ($Ndot>1)) { $file =~ s/\./ /g; } elsif ($Nunderscore > 1) { $file =~ s/_/ /g; } elsif ($Ndash>2) { $file =~ s/-/ /g; } } # check wich chars are used for seperating parts # assumption: " - " is used my $partsep = " - "; unless ($file =~ / - /) { if ($file =~ /-/) { $partsep = "-"; } elsif ($file =~ /^\(.*\)/) { # replace brackets by - $file =~ s/^\((.*?)\)/$1 - /; $file =~ s/ +/ /; $partsep = " - "; } elsif ($file =~ /_/) { $partsep = "_"; } else { $partsep = "DoesNotExist"; } } # get parts of name my ($title, $artist, $no, $album, $year)=("","","","",""); # try to find a track-number in front of filename if ($file =~ /^ *(\d+)[\W_]/) { $no=$1; # store number $file =~ s/^ *\d+//; # and delete it $file =~ s/^$partsep// || $file =~ s/^.//; $file =~ s/^ +//; } $file =~ s/_+/ /g unless $partsep =~ /_/; #remove underscore unless they are needed for part seperation my @parts = split /$partsep/, $file; if (@parts == 1) { $title=$parts[0]; $no = $file if $title and $title =~ /^\d{1,2}$/; } elsif (@parts == 2) { if ($parts[0] =~ /^\d{1,2}$/) { $no = $parts[0]; $title = $file; } elsif ($parts[1] =~ /^\d{1,2}$/) { $no = $parts[1]; $title = $file; } else { $artist=$parts[0]; $title=$parts[1]; } } elsif (@parts > 2) { my $temp = ""; $artist = shift @parts; foreach (@parts) { if (/^ *(\d+)\.? *$/) { $artist.= $partsep . $temp if $temp; $temp=""; $no=$1; } else { $temp .= $partsep if $temp; $temp .= $_; } } $title=$temp; } $title =~ s/ +$//; $artist =~ s/ +$//; $no =~ s/ +$//; # Special-case names like audio12 etc created by some software # (cdda2wav, gramofile, etc) $no = $+ if not $no and $title =~ /^(\d+)?(?:audio|track|processed)\s*(\d+)?$/i and $+; $no =~ s/^0+//; if ($path) { unless ($artist) { $artist = $path; } else { $album = $path; } } # Keep the year in the title/artist (XXXX Should we?) $year = $1 if $title =~ /\((\d{4})\)/ or $artist =~ /\((\d{4})\)/; $self->{parsed_filename} = $filename; $self->{parsed} = { artist=>$artist, song=>$title, no=>$no, album=>$album, title=>$title, year => $year}; $self->return_parsed($what); } =pod =item title() $title = $mp3->title($filename); Returns the title, guessed from the filename. See also parse_filename(). (For backward compatibility, can be called by deprecated name song().) $filename is optional and will be used instead of the real filename if defined. =cut *song = \&title; sub title { my $self = shift; return $self->parse_filename("title", @_); } =pod =item artist() $artist = $mp3->artist($filename); Returns the artist name, guessed from the filename. See also parse_filename() $filename is optional and will be used instead of the real filename if defined. =cut sub artist { my $self = shift; return $self->parse_filename("artist", @_); } =pod =item track() $track = $mp3->track($filename); Returns the track number, guessed from the filename. See also parse_filename() $filename is optional and will be used instead of the real filename if defined. =cut sub track { my $self = shift; return $self->parse_filename("track", @_); } =item year() $year = $mp3->year($filename); Returns the year, guessed from the filename. See also parse_filename() $filename is optional and will be used instead of the real filename if defined. =cut sub year { my $self = shift; my $y = $self->parse_filename("year", @_); return $y if length $y; return; } =pod =item album() $album = $mp3->album($filename); Returns the album name, guessed from the filename. See also parse_filename() The album name is guessed from the parent directory, so it is very likely to fail. $filename is optional and will be used instead of the real filename if defined. =cut sub album { my $self = shift; return $self->parse_filename("album", @_); } =item comment() $comment = $mp3->comment($filename); # Always undef =cut sub comment {} =item genre() $genre = $mp3->genre($filename); # Always undef =cut sub genre {} 1; �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/MP3/Tag/ID3v1.pm�������������������������������������������������������������������0000700�0000000�0000000�00000034506�11304126510�012472� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package MP3::Tag::ID3v1; # Copyright (c) 2000-2004 Thomas Geffert. All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the Artistic License, distributed # with Perl. use strict; use vars qw /@mp3_genres @winamp_genres $AUTOLOAD %ok_length $VERSION @ISA/; $VERSION="1.00"; @ISA = 'MP3::Tag::__hasparent'; # allowed fields in ID3v1.1 and max length of this fields (except for track and genre which are coded later) %ok_length = (title => 30, artist => 30, album => 30, comment => 28, track => 3, genre => 3000, year=>4, genreID=>1); =pod =head1 NAME MP3::Tag::ID3v1 - Module for reading / writing ID3v1 tags of MP3 audio files =head1 SYNOPSIS MP3::Tag::ID3v1 is designed to be called from the MP3::Tag module. use MP3::Tag; $mp3 = MP3::Tag->new($filename); # read an existing tag $mp3->get_tags(); $id3v1 = $mp3->{ID3v1} if exists $mp3->{ID3v1}; # or create a new tag $id3v1 = $mp3->new_tag("ID3v1"); See L<MP3::Tag|according documentation> for information on the above used functions. * Reading the tag print " Title: " .$id3v1->title . "\n"; print " Artist: " .$id3v1->artist . "\n"; print " Album: " .$id3v1->album . "\n"; print "Comment: " .$id3v1->comment . "\n"; print " Year: " .$id3v1->year . "\n"; print " Genre: " .$id3v1->genre . "\n"; print " Track: " .$id3v1->track . "\n"; # or at once @tagdata = $mp3->all(); foreach $tag (@tagdata) { print $tag; } * Changing / Writing the tag $id3v1->comment("This is only a Test Tag"); $id3v1->title("testing"); $id3v1->artist("Artest"); $id3v1->album("Test it"); $id3v1->year("1965"); $id3v1->track("5"); $id3v1->genre("Blues"); # or at once $id3v1->all("song title","artist","album","1900","comment",10,"Ska"); $id3v1->write_tag(); * Removing the tag from the file $id3v1->remove_tag(); =head1 AUTHOR Thomas Geffert, thg@users.sourceforge.net =head1 DESCRIPTION =pod =over =item title(), artist(), album(), year(), comment(), track(), genre() $artist = $id3v1->artist; $artist = $id3v1->artist($artist); $album = $id3v1->album; $album = $id3v1->album($album); $year = $id3v1->year; $year = $id3v1->year($year); $comment = $id3v1->comment; $comment = $id3v1->comment($comment); $track = $id3v1->track; $track = $id3v1->track($track); $genre = $id3v1->genre; $genre = $id3v1->genre($genre); Use these functions to retrieve the date of these fields, or to set the data. $genre can be a string with the name of the genre, or a number describing the genre. =cut sub AUTOLOAD { my $self = shift; my $attr = $AUTOLOAD; # is it an allowed field $attr =~ s/.*:://; return unless $attr =~ /[^A-Z]/; $attr = 'title' if $attr eq 'song'; warn "invalid field: ->$attr()" unless $ok_length{$attr}; if (@_) { my $new = shift; $new =~ s/ *$//; if ($attr eq "genre") { if ($new =~ /^\d+$/) { $self->{genreID} = $new; } else { $self->{genreID} = genre2id($new); } $new = id2genre($self->{genreID}) if defined $self->{genreID} and $self->{genreID} < @winamp_genres; } $new = substr $new, 0, $ok_length{$attr}; $self->{$attr}=$new; $self->{changed} = 1; } $self->{$attr} =~ s/ +$//; return $self->{$attr}; } =pod =item all() @tagdata = $id3v1->all; @tagdata = $id3v1->all($title, $artist, $album, $year, $comment, $track, $genre); Returns all information of the tag in a list. You can use this sub also to set the data of the complete tag. The order of the data is always title, artist, album, year, comment, track, and genre. genre has to be a string with the name of the genre, or a number identifying the genre. =cut sub all { my $self=shift; if ($#_ == 6) { my $new; for (qw/title artist album year comment track genre/) { $new = shift; $new =~ s/ +$//; $new = substr $new, 0, $ok_length{$_}; $self->{$_}=$new; } if ($self->{genre} =~ /^\d+$/) { $self->{genreID} = $self->{genre}; } else { $self->{genreID} = genre2id($self->{genre}); } $self->{genre} = id2genre($self->{genreID}) if defined $self->{genreID} and $self->{genreID} < @winamp_genres; $self->{changed} = 1; } for (qw/title artist album year comment track genre/) { $self->{$_} =~ s/ +$//; } if (wantarray) { return ($self->{title},$self->{artist},$self->{album}, $self->{year},$self->{comment}, $self->{track}, $self->{genre}); } return $self->{title}; } =pod =item fits_tag() warn "data truncated" unless $id3v1->fits_tag($hash); Check whether the info in ID3v1 tag fits into the format of the file. =cut sub fits_tag { my ($self, $hash) = (shift, shift); my $elt; if (defined (my $track = $hash->{track})) { $track = $track->[0] if ref $track; return unless $track =~ /^\d{0,3}$/ and ($track eq '' or $track < 256); } my $s = ''; for $elt (qw(title artist album comment year)) { next unless defined (my $data = $hash->{$elt}); $data = $data->[0] if ref $data; return if $data =~ /[^\x00-\xFF]/; $s .= $data; next if $ok_length{$elt} >= length $data; next if $elt eq 'comment' and not $hash->{track} and length $data <= 30; return; } if (defined (my $genre = $hash->{genre})) { $genre = $genre->[0] if ref $genre; my @g = MP3::Tag::Implemenation::_massage_genres($genre); return if @g > 1; my $id = MP3::Tag::Implemenation::_massage_genres($genre, 'num'); return if not defined $id or $id eq '' or $id == 255; } if ($s =~ /[^\x00-\x7E]/) { my $w = ($self->get_config('encode_encoding_v1') || [0])->[0]; my $r = ($self->get_config('decode_encoding_v1') || [0])->[0]; $_ = (lc or 'iso-8859-1') for $r, $w; # Safe: per-standard and read+write is idempotent: return 1 if $r eq $w and $w eq 'iso-8859-1'; return !(($self->get_config('encoded_v1_fits')||[0])->[0]) if $w eq 'iso-8859-1'; # read+write not idempotent return if $w ne $r and not (($self->get_config('encoded_v1_fits')||[0])->[0]); } return 1; } =item as_bin() $str = $id3v1->as_bin(); Returns the ID3v1 tag as a string. =item write_tag() $id3v1->write_tag(); [old name: writeTag() . The old name is still available, but you should use the new name] Writes the ID3v1 tag to the file. =cut sub as_bin { my $self = shift; my($t) = ( $self->{track} =~ m[^(\d+)(?:/|$)], 0 ); my (%f, $f, $e); for $f (qw(title artist album comment) ) { $f{$f} = $self->{$f}; } if ($e = $self->get_config('encode_encoding_v1') and $e->[0]) { my $field; require Encode; for $field (qw(title artist album comment)) { $f{$field} = Encode::encode($e->[0], $f{$field}); } } $f{comment} = pack "a28 x C", $f{comment}, $t if $t; $self->{genreID}=255 unless $self->{genreID} =~ /^\d+$/; return pack("a3a30a30a30a4a30C","TAG",$f{title}, $f{artist}, $f{album}, $self->{year}, $f{comment}, $self->{genreID}); } sub write_tag { my $self = shift; return undef unless exists $self->{title} && exists $self->{changed}; my $data = $self->as_bin(); my $mp3obj = $self->{mp3}; my $mp3tag; $mp3obj->close; if ($mp3obj->open("write")) { $mp3obj->seek(-128,2); $mp3obj->read(\$mp3tag, 3); if ($mp3tag eq "TAG") { $mp3obj->seek(-125,2); # neccessary for windows $mp3obj->write(substr $data, 3); } else { $mp3obj->seek(0,2); $mp3obj->write($data); } } else { warn "Couldn't open file `" . $mp3obj->filename() . "' to write tag"; return 0; } return 1; } *writeTag = \&write_tag; =pod =item remove_tag() $id3v1->remove_tag(); Removes the ID3v1 tag from the file. Returns negative on failure, FALSE if no tag was found. (Caveat: only I<one tag> is removed; some - broken - files may have many chain-loaded one after another; you may need to call remove_tag() in a loop to handle such beasts.) [old name: removeTag() . The old name is still available, but you should use the new name] =cut sub remove_tag { my $self = shift; my $mp3obj = $self->{mp3}; my $mp3tag; $mp3obj->seek(-128,2); $mp3obj->read(\$mp3tag, 3); if ($mp3tag eq "TAG") { $mp3obj->close; if ($mp3obj->open("write")) { $mp3obj->truncate(-128); $self->all("","","","","",0,255); $mp3obj->close; $self->{changed} = 1; return 1; } return -1; } return 0; } *removeTag = \&remove_tag; =pod =item genres() @allgenres = $id3v1->genres; $genreName = $id3v1->genres($genreID); $genreID = $id3v1->genres($genreName); Returns a list of all genres, or the according name or id to a given id or name. =cut sub genres { # return an array with all genres, of if a parameter is given, the according genre my ($self, $genre) = @_; if ( (defined $self) and (not defined $genre) and ($self !~ /MP3::Tag/)) { ## genres may be called directly via MP3::Tag::ID3v1::genres() ## and $self is then not used for an id3v1 object $genre = $self; } return \@winamp_genres unless defined $genre; if ($genre =~ /^\d+$/) { return $winamp_genres[$genre] if $genre<scalar @winamp_genres; return undef; } my ($id, $found)=0; foreach (@winamp_genres) { if (uc $_ eq uc $genre) { $found = 1; last; } $id++; } $id=255 unless $found; return $id; } =item new() $id3v1 = MP3::Tag::ID3v1->new($mp3fileobj[, $create]); Generally called from MP3::Tag, because a $mp3fileobj is needed. If $create is true, a new tag is created. Otherwise undef is returned, if now ID3v1 tag is found in the $mp3obj. Please use $mp3 = MP3::Tag->new($filename); $id3v1 = $mp3->new_tag("ID3v1"); # Empty new tag or $mp3 = MP3::Tag->new($filename); $mp3->get_tags(); $id3v1 = $mp3->{ID3v1}; # Existing tag (if present) instead of using this function directly =back =cut # create a ID3v1 object sub new { my ($class, $fileobj, $create) = @_; my $self={mp3=>$fileobj}; my $buffer; if ($create) { $self->{new} = 1; } else { $fileobj->open or return unless $fileobj->is_open; $fileobj->seek(-128,2); $fileobj->read(\$buffer, 128); return undef unless substr ($buffer,0,3) eq "TAG"; } bless $self, $class; $self->read_tag($buffer); # $buffer unused if ->{new} return $self; } sub new_with_parent { my ($class, $filename, $parent) = @_; return unless my $new = $class->new($filename, undef); $new->{parent} = $parent; $new; } ################# ## ## internal subs # actually read the tag data sub read_tag { my ($self, $buffer) = @_; my ($id3v1, $e); if ($self->{new}) { ($self->{title}, $self->{artist}, $self->{album}, $self->{year}, $self->{comment}, $self->{track}, $self->{genre}, $self->{genreID}) = ("","","","","",'',"",255); $self->{changed} = 1; } else { (undef, $self->{title}, $self->{artist}, $self->{album}, $self->{year}, $self->{comment}, $id3v1, $self->{track}, $self->{genreID}) = unpack (($] < 5.6 ? "a3 A30 A30 A30 A4 A28 C C C" # Trailing spaces stripped too : "a3 Z30 Z30 Z30 Z4 Z28 C C C"), $buffer); if ($id3v1!=0) { # ID3v1 tag found: track is not valid, comment two chars longer $self->{comment} .= chr($id3v1); $self->{comment} .= chr($self->{track}) if $self->{track} and $self->{track}!=32; $self->{track} = ''; }; $self->{track} = '' unless $self->{track}; $self->{genre} = id2genre($self->{genreID}); if ($e = $self->get_config('decode_encoding_v1') and $e->[0]) { my $field; require Encode; for $field (qw(title artist album comment)) { $self->{$field} = Encode::decode($e->[0], $self->{$field}); } } } } # convert small integer id to genre name sub id2genre { my $id=shift; return "" unless defined $id and $id < @winamp_genres; return $winamp_genres[$id]; } # convert genre name to small integer id sub genre2id { my $genre = MP3::Tag::Implemenation::_massage_genres(shift, 'num'); return $genre if defined $genre; return 255; } # nothing to do for destroy sub DESTROY { } 1; ######## define all the genres BEGIN { @mp3_genres = ( 'Blues', 'Classic Rock', 'Country', 'Dance', 'Disco', 'Funk', 'Grunge', 'Hip-Hop', 'Jazz', 'Metal', 'New Age', 'Oldies', 'Other', 'Pop', 'R&B', 'Rap', 'Reggae', 'Rock', 'Techno', 'Industrial', 'Alternative', 'Ska', 'Death Metal', 'Pranks', 'Soundtrack', 'Euro-Techno', 'Ambient', 'Trip-Hop', 'Vocal', 'Jazz+Funk', 'Fusion', 'Trance', 'Classical', 'Instrumental', 'Acid', 'House', 'Game', 'Sound Clip', 'Gospel', 'Noise', 'AlternRock', 'Bass', 'Soul', 'Punk', 'Space', 'Meditative', 'Instrumental Pop', 'Instrumental Rock', 'Ethnic', 'Gothic', 'Darkwave', 'Techno-Industrial', 'Electronic', 'Pop-Folk', 'Eurodance', 'Dream', 'Southern Rock', 'Comedy', 'Cult', 'Gangsta', 'Top 40', 'Christian Rap', 'Pop/Funk', 'Jungle', 'Native American', 'Cabaret', 'New Wave', 'Psychadelic', 'Rave', 'Showtunes', 'Trailer', 'Lo-Fi', 'Tribal', 'Acid Punk', 'Acid Jazz', 'Polka', 'Retro', 'Musical', 'Rock & Roll', 'Hard Rock', ); @winamp_genres = ( @mp3_genres, 'Folk', 'Folk-Rock', 'National Folk', 'Swing', 'Fast Fusion', 'Bebob', 'Latin', 'Revival', 'Celtic', 'Bluegrass', 'Avantgarde', 'Gothic Rock', 'Progressive Rock', 'Psychedelic Rock', 'Symphonic Rock', 'Slow Rock', 'Big Band', 'Chorus', 'Easy Listening', 'Acoustic', 'Humour', 'Speech', 'Chanson', 'Opera', 'Chamber Music', 'Sonata', 'Symphony', 'Booty Bass', 'Primus', 'Porn Groove', 'Satire', 'Slow Jam', 'Club', 'Tango', 'Samba', 'Folklore', 'Ballad', 'Power Ballad', 'Rhythmic Soul', 'Freestyle', 'Duet', 'Punk Rock', 'Drum Solo', 'Acapella', 'Euro-House', 'Dance Hall', # More from MP3::Info 'Goa', 'Drum & Bass', 'Club-House', 'Hardcore', 'Terror', 'Indie', 'BritPop', 'Negerpunk', 'Polsk Punk', 'Beat', 'Christian Gangsta Rap', 'Heavy Metal', 'Black Metal', 'Crossover', 'Contemporary Christian Music', 'Christian Rock', 'Merengue', 'Salsa', 'Thrash Metal', 'Anime', 'JPop', 'SynthPop', # 149 ); } =pod =head1 SEE ALSO L<MP3::Tag>, L<MP3::Tag::ID3v2> ID3v1 standard - http://www.id3.org =head1 COPYRIGHT Copyright (c) 2000-2004 Thomas Geffert. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the Artistic License, distributed with Perl. =cut ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/MP3/Tag/ID3v2.pm�������������������������������������������������������������������0000700�0000000�0000000�00000305040�11317677210�012501� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package MP3::Tag::ID3v2; # Copyright (c) 2000-2004 Thomas Geffert. All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the Artistic License, distributed # with Perl. use strict; use File::Basename; # use Compress::Zlib; use vars qw /%format %long_names %res_inp @supported_majors %v2names_to_v3 $VERSION @ISA %field_map %field_map_back %is_small_int %back_splt %embedded_Descr /; $VERSION = "1.12"; @ISA = 'MP3::Tag::__hasparent'; my $trustencoding = $ENV{MP3TAG_DECODE_UNICODE}; $trustencoding = 1 unless defined $trustencoding; my $decode_utf8 = $ENV{MP3TAG_DECODE_UTF8}; $decode_utf8 = 1 unless defined $decode_utf8; my $encode_utf8 = $decode_utf8; =pod =head1 NAME MP3::Tag::ID3v2 - Read / Write ID3v2.x.y tags from mp3 audio files =head1 SYNOPSIS MP3::Tag::ID3v2 supports * Reading of ID3v2.2.0 and ID3v2.3.0 tags (some ID3v2.4.0 frames too) * Writing of ID3v2.3.0 tags MP3::Tag::ID3v2 is designed to be called from the MP3::Tag module. If you want to make calls from user code, please consider using highest-level wrapper code in MP3::Tag, such as update_tags() and select_id3v2_frame_by_descr(). Low-level creation code: use MP3::Tag; $mp3 = MP3::Tag->new($filename); # read an existing tag $mp3->get_tags(); $id3v2 = $mp3->{ID3v2} if exists $mp3->{ID3v2}; # or create a new tag $id3v2 = $mp3->new_tag("ID3v2"); See L<MP3::Tag|according documentation> for information on the above used functions. * Reading a tag, very low-level: $frameIDs_hash = $id3v2->get_frame_ids('truename'); foreach my $frame (keys %$frameIDs_hash) { my ($name, @info) = $id3v2->get_frames($frame); for my $info (@info) { if (ref $info) { print "$name ($frame):\n"; while(my ($key,$val)=each %$info) { print " * $key => $val\n"; } } else { print "$name: $info\n"; } } } * Adding / Changing / Removing a frame in memory (higher-level) $t = $id3v2->frame_select("TIT2", undef, undef); # Very flexible $c = $id3v2->frame_select_by_descr("COMM(fre,fra,eng,#0)[]"); $t = $id3v2->frame_select_by_descr("TIT2"); $id3v2->frame_select_by_descr("TIT2", "MyT"); # Set/Change $id3v2->frame_select_by_descr("RBUF", $n1, $n2, $n3); # Set/Change $id3v2->frame_select_by_descr("RBUF", "$n1;$n2;$n3"); # Set/Change $id3v2->frame_select_by_descr("TIT2", undef); # Remove * Adding / Changing / Removing a frame in memory (low-level) $id3v2->add_frame("TIT2", "Title of the audio"); $id3v2->change_frame("TALB","Greatest Album"); $id3v2->remove_frame("TLAN"); * Output the modified-in-memory version of the tag: $id3v2->write_tag(); * Removing the whole tag from the file $id3v2->remove_tag(); * Get information about supported frames %tags = $id3v2->supported_frames(); while (($fname, $longname) = each %tags) { print "$fname $longname: ", join(", ", @{$id3v2->what_data($fname)}), "\n"; } =head1 AUTHOR Thomas Geffert, thg@users.sourceforge.net Ilya Zakharevich, ilyaz@cpan.org =head1 DESCRIPTION =over 4 =item get_frame_ids() $frameIDs = $tag->get_frame_ids; $frameIDs = $tag->get_frame_ids('truename'); [old name: getFrameIDs() . The old name is still available, but you should use the new name] get_frame_ids loops through all frames, which exist in the tag. It returns a hash reference with a list of all available Frame IDs. The keys of the returned hash are 4-character-codes (short names), the internal names of the frames, the according value is the english (long) name of the frame. You can use this list to iterate over all frames to get their data, or to check if a specific frame is included in the tag. If there are multiple occurences of a frame in one tag, the first frame is returned with its normal short name, following frames of this type get a '01', '02', '03', ... appended to this name. These names can then used with C<get_frame> to get the information of these frames. These fake frames are not returned if C<'truename'> argument is set; one can still use C<get_frames()> to extract the info for all of the frames with the given short name. =cut ###### structure of a tag frame # # major=> Identifies format of frame, normally set to major version of the whole # tag, but many ID3v2.2 frames are converted automatically to ID3v2.3 frames # flags=> Frame flags, depend on major version # data => Data of frame, including gid # gid => group id, if any (created by get_frame()) # sub un_syncsafe_4bytes ($) { my ($rawsize,$size) = (shift, 0); foreach my $b (unpack("C4", $rawsize)) { $size = ($size << 7) + $b; } return $size; } sub get_frame_ids { my $self = shift; # Tag my $basic = shift; # frame headers format for the different majors my $headersize = (0,0,6,10,10)[$self->{major}]; my $headerformat=("","","a3a3","a4Nn","a4a4n")[$self->{major}]; if (exists $self->{frameIDs}) { return unless defined wantarray; my %return; foreach (keys %{$self->{frames}}) { next if $basic and length > 4; # ignore frames with 01 etc. at end $return{$_}=$long_names{substr($_,0,4)}; } return \%return; } my $pos = $self->{frame_start}; # if ($self->{flags}->{extheader}) { # warn "get_frame_ids: possible wrong IDs because of unsupported extended header\n"; # } my $buf; while ($pos + $headersize < $self->{data_size}) { $buf = substr ($self->{tag_data}, $pos, $headersize); my ($ID, $size, $flags) = unpack($headerformat, $buf); # tag size is handled differently for all majors if ($self->{major} == 2) { # flags don't exist in id3v2.2 $flags=0; my $rawsize=$size; $size=0; foreach (unpack("C3", $rawsize)) { $size = ($size << 8) + $_; } } elsif ($self->{major} == 4) { $size = un_syncsafe_4bytes $size; } elsif ($self->{major}==3 and $size>255) { # Size>255 means at least 2 bytes are used for size. # Some programs use (incorectly) for the frame size # the format of the tag size (snchsafe). Trying do detect that here if ($pos + $headersize + $size > $self->{data_size} || !exists $long_names{substr ($self->{tag_data}, $pos+$size,4)}) { # wrong size or last frame my $fsize = un_syncsafe_4bytes substr $buf, 4, 4; if ($pos + 20 + $fsize < $self->{data_size} && exists $long_names{substr ($self->{tag_data}, $pos+10+$fsize,4)}) { warn "Probably wrong size format found in frame $ID. Trying to correct it\n"; #probably false size format detected, using corrected size $size = $fsize; } } } if ($ID !~ "\000\000\000") { my $major = $self->{major}; if ($major == 2) { # most frame IDs can be converted directly to id3v2.3 IDs if (exists $v2names_to_v3{$ID}) { # frame is direct convertable to major 3 $ID = $v2names_to_v3{$ID}; $major=3; } } if (exists $self->{frames}->{$ID}) { ++$self->{extra_frames}->{$ID}; $ID .= '01'; while (exists $self->{frames}->{$ID}) { $ID++; } } $self->{frames}->{$ID} = {flags=>$self->check_flags($flags), major=>$major, data=>substr($self->{tag_data}, $pos+$headersize, $size)}; $pos += $size+$headersize; } else { # Padding reached, cut tag data here last; } } $self->{endpos} = $pos; # Since tag_data is de-synced, this doesn't count the forced final "\0" $self->{padding} = length($self->{tag_data}) - $pos; # tag is seperated into frames, tagdata not more needed $self->{tag_data}=""; $self->{frameIDs} =1; my %return; foreach (keys %{$self->{frames}}) { next if $basic and length > 4; # ignore frames with 01 etc. at end $return{$_}=$long_names{substr($_,0,4)}; } return \%return; } *getFrameIDs = \&get_frame_ids; =pod =item get_frame() ($info, $name, @rest) = $tag->get_frame($ID); ($info, $name, @rest) = $tag->get_frame($ID, 'raw'); [old name: getFrame() . The old name is still available, but you should use the new name] get_frame gets the contents of a specific frame, which must be specified by the 4-character-ID (aka short name). You can use C<get_frame_ids> to get the IDs of the tag, or use IDs which you hope to find in the tag. If the ID is not found, C<get_frame> returns empty list, so $info and $name become undefined. Otherwise it extracts the contents of the frame. Frames in ID3v2 tags can be very small, or complex and huge. That is the reason, that C<get_frame> returns the frame data in two ways, depending on the tag. If it is a simple tag, with only one piece of data, these data is returned directly as ($info, $name), where $info is the text string, and $name is the long (english) name of the frame. If the frame consist of different pieces of data, $info is a hash reference, $name is again the long name of the frame. The hash, to which $info points, contains key/value pairs, where the key is always the name of the data, and the value is the data itself. If the name starts with a underscore (as eg '_code'), the data is probably binary data and not printable. If the name starts without an underscore, it should be a text string and printable. If the second parameter is given as C<'raw'>, the whole frame data is returned, but not the frame header. If the second parameter is C<'intact'>, no mangling of embedded C<"\0"> and trailing spaces is performed. If the second parameter is C<'hash'>, then, additionally, the result is always in the hash format; likewise, if it is C<'array'>, the result is an array reference (with C<key =E<gt> value> pairs same as with C<'hash'>, but ordered as in the frame). If it is C<'array_nokey'>, only the "value" parts are returned (in particular, the result is suitable to give to add_frame(), change_frame()); in addition, if it is C<'array_nodecode'>, then keys are not returned, and the setting of C<decode_encoding_v2> is ignored. (The "return array" flavors don't massage the fields for better consumption by humans, so the fields should be in format suitable for frame_add().) If the data was stored compressed, it is uncompressed before it is returned (even in raw mode). Then $info contains a string with all data (which might be binary), and $name the long frame name. See also L<MP3::Tag::ID3v2_Data> for a list of all supported frames, and some other explanations of the returned data structure. If more than one frame with name $ID is present, @rest contains $info fields for all consequent frames with the same name. Note that after removal of frames there may be holes in the list of frame names (as in C<FRAM FRAM01 FRAM02>) in the case when multiple frames of the given type were present; the removed frames are returned as C<undef>. ! Encrypted frames are not supported yet ! ! Some frames are not supported yet, but the most common ones are supported ! =cut sub get_frame { my ($self, $fname, $raw) = @_; $self->get_frame_ids() unless exists $self->{frameIDs}; my ($e, @extra) = 0; # More frames follow? $e = $self->{extra_frames}->{$fname} || 0 if wantarray and $self->{extra_frames} and length $fname == 4; @extra = map scalar $self->get_frame((sprintf "%s%02d", $fname, $_), $raw), 1..$e; $e = grep defined, @extra; my $frame = $self->{frames}->{$fname}; return unless defined $frame or $e; $fname = substr ($fname, 0, 4); return (undef, $long_names{$fname}, @extra) unless defined $frame; my $start_offset=0; if ($frame->{flags}->{encryption}) { warn "Frame $fname: encryption not supported yet\n" ; return; } my $result = $frame->{data}; # 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. if ($frame->{flags}->{groupid}) { $frame->{gid} = substring $result, 0, 1; $result = substring $result, 1; } if ($frame->{flags}->{compression}) { my $usize=unpack("N", $result); require Compress::Zlib; $result = Compress::Zlib::uncompress(substr ($result, 4)); warn "$fname: Wrong size of uncompressed data\n" if $usize=!length($result); } if (($raw ||= 0) eq 'raw') { return ($result, $long_names{$fname}, @extra) if wantarray; return $result; } my $format = get_format($fname); if (defined $format) { my($as_arr, $nodecode); $as_arr = 2 if $raw eq 'array'; $as_arr = 1 if $raw eq 'array_nokey' or $raw eq 'array_nodecode'; $nodecode = 1 if $raw eq 'array_nodecode'; $format = [map +{%$_}, @$format], $format->[-1]{data} = 1 if $raw eq 'intact' or $raw eq 'hash' or $as_arr; $result = extract_data($self, $result, $format, $nodecode, $as_arr); unless ($as_arr or $raw eq 'hash') { my $k = scalar keys %$result; $k-- if exists $result->{encoding}; if ($k == 1) { if (exists $result->{Text}) { $result = $result->{Text}; } elsif (exists $result->{URL}) { $result = $result->{URL}; } elsif ($fname =~ /^MCDI/) { # Per ID3v2_Data.pod $result = $result->{_Data}; } # In fact, no other known frame has one element } } } if (wantarray) { return ($result, $long_names{$fname}, @extra); } else { return $result; } } *getFrame= \&get_frame; =item get_frame_descr() $long_name = $self->get_frame_descr($fname); returns a "long name" for the frame (such as C<COMM(eng)[lyricist birthdate]>), appropriate for interpolation, or for frame_select_by_descr(). =item get_frame_descriptors() @long_names = $self->get_frame_descriptors(); return "long names" for the frames in the tag (see C<get_frame_descr>). =cut sub get_frame_descr { my ($self, $fname)=@_; (undef, my $frame) = $self->get_frames($fname); # Ignore the rest return unless defined $frame; return $fname unless ref $frame; my $k = scalar keys %$frame; if ($k == 5 and substr($fname, 0, 4) eq 'APIC') { return $fname unless $frame->{'MIME type'} eq $self->_Data_to_MIME($frame->{_Data}); delete $frame->{'MIME type'}; $k--; $frame->{Language} = delete $frame->{'Picture Type'}; } return $fname unless $k <= 4; # encoding, Language, Description + 1 $k-- if exists $frame->{encoding}; return $fname unless $k <= 3; my $l = delete $frame->{Language}; $k-- if defined $l; return $fname unless $k <= 2; my $d = delete $frame->{Description}; $k-- if defined $d; return $fname unless $k <= 1; $fname =~ s/^(\w{4})\d{2}/$1/; $l = "($l)" if defined $l; $d = "[$d]" if defined $d; $l ||= ''; $d ||= ''; return "$fname$l$d"; } sub get_frame_descriptors { my $self = shift; my $h = $self->get_frame_ids(); map $self->get_frame_descr($_), sort keys %$h; } # I'm not yet ready to freeze these APIs sub __frame_as_printable { my ($self,$descr,$pre,$post,$fsep,$pre_mult,$val_sep, $bin) = (shift, shift, shift, shift, shift, shift, shift, shift); my $val = $self->frame_select_by_descr($descr); # Simple binary frames: my $l = length $val; return '__binary_DATA__ [len='.length($val).']' if not $bin and not ref $val and $descr =~ /^(MCDI|APIC)/; return "$pre$val$post" unless ref $val; my $format = get_format(substr $descr, 0, 4); my %optnl = map(($_->{name},$_->{optional}), @$format); my @keys = map $_->{name}, @$format; # In order... s/^_(?=encoding$)// for @keys; # Reverse mangling by extract_data()... my %keys = map(($_,1), @keys); my @ekeys = grep !exists $keys{$_}, keys %$val; # Just in case... my @miss = grep(!exists $val->{$_}, @keys); @miss = map "$_".($optnl{$_} ? ' [optional]' : ''), @miss; my $miss = @miss ? "${fsep}missing fields: ".(join ', ', @miss)."." : ''; @keys = ( grep(exists $val->{$_}, @keys), sort @ekeys ); # grep: just in case my %ekeys = map(($_,''), @keys); @ekeys{@ekeys} = ('?') x @ekeys; return $pre_mult . (join $fsep, map "$ekeys{$_}".sprintf('%-14s',$_)."$val_sep$pre$val->{$_}$post", @keys) . $miss if $bin; $pre_mult . (join $fsep, map "$ekeys{$_}".sprintf('%-14s',$_)."$val_sep" . ( $_ =~ /^_(?!encoding)/ ? '__binary_DATA__ [len='.length($val->{$_}).']' : "$pre$val->{$_}$post" ), @keys) . $miss; } sub __f_long_name ($$) { my ($self,$fr) = (shift, shift); (my $short = $fr) =~ s/^(\w{4})\d{2,}/$1/; $long_names{$short} || '???'; } sub __frames_as_printable { my ($self,$fr_sep,$fn_sep) = (shift, shift, shift); my $h = $self->get_frame_ids(); join $fr_sep, map sprintf('%-40s', $self->get_frame_descr($_) . " (" . $self->__f_long_name($_) . ")") . $fn_sep . $self->__frame_as_printable($_,@_), sort keys %$h; } =pod =item get_frame_option() $options = get_frame_option($ID); Option is a hash reference, the hash contains all possible options. The value for each option is 0 or 1. groupid -- not supported yet encryption -- not supported yet compression -- Compresses frame before writing tag; compression/uncompression is done automatically read_only -- Ignored by this library, should be obeyed by application file_preserv -- Ignored by this library, should be obeyed by application tag_preserv -- Ignored by this library, should be obeyed by application =cut sub get_frame_option { my ($self, $fname)=@_; $self->get_frame_ids() unless exists $self->{frameIDs}; return undef unless exists $self->{frames}->{$fname}; return $self->{frames}->{$fname}->{flags}; } =pod =item set_frame_option() $options = set_frame_option($ID, $option, $value); Set $option to $value (0 or 1). If successfull the new set of options is returned, undef otherwise. groupid -- not supported yet encryption -- not supported yet compression -- Compresses frame before writing tag; compression/uncompression is done automatically read_only -- Ignored by this library, should be obeyed by application file_preserv -- Ignored by this library, should be obeyed by application tag_preserv -- Ignored by this library, should be obeyed by application =cut sub set_frame_option { my ($self, $fname,$option,$value)=@_; $self->get_frame_ids() unless exists $self->{frameIDs}; return undef unless exists $self->{frames}->{$fname}; if (exists $self->{frames}->{$fname}->{flags}->{$option}) { $self->{frames}->{$fname}->{flags}->{$option}=$value?1:0; } else { warn "Unknown option $option\n"; return undef; } return $self->{frames}->{$fname}->{flags}; } sub sort_with_apic { my ($a_APIC, $b_APIC) = map scalar(/^APIC/), $a, $b; $a_APIC cmp $b_APIC or $a cmp $b; } # build_tag() # create a string with the complete tag data sub build_tag { my ($self, $ignore_error) = @_; my $tag_data; # in which order should the frames be sorted? # with a simple sort the order of frames of one type is the order of adding them my @frames = sort sort_with_apic keys %{$self->{frames}}; for my $frameid (@frames) { my $frame = $self->{frames}->{$frameid}; if ($frame->{major} < 3) { #try to convert to ID3v2.3 or warn "Can't convert $frameid to ID3v2.3\n"; next if ($ignore_error); return undef; } my $data = $frame->{data}; my %flags = (); #compress data if this is wanted if ($frame->{flags}->{compression} || $self->{flags}->{compress_all}) { $flags{compression} = 1; $data = pack("N", length($data)) . compress $data unless $frame->{flags}->{unchanged}; } #encrypt data if this is wanted if ($frame->{flags}->{encryption} || $self->{flags}->{encrypt_all}) { if ($frame->{flags}->{unchanged}) { $flags{encryption} = 1; } else { # ... not supported yet return undef unless $ignore_error; warn "Encryption not supported yet\n"; } } # set groupid if ($frame->{flags}->{group_id}) { return undef unless $ignore_error; warn "Group ids are not supported in writing\n"; } # unsync my $extra = 0; if ( ($self->get_config('id3v23_unsync'))->[0] and ($self->{version} == 3 and ($self->get_config('id3v23_unsync_size_w'))->[0] or $self->{version} >= 4) ) { $extra++ while $data =~ /\xFF(?=[\x00\xE0-\xFF])/g; } #prepare header my $header = substr($frameid,0,4) . pack("Nn", $extra + length ($data), build_flags(%flags)); $tag_data .= $header . $data; } return $tag_data; } # insert_space() copies a mp3-file and can insert one or several areas # of free space for a tag. These areas are defined as # ($pos, $old_size, $new_size) # $pos says at which position of the mp3-file the space should be inserted # new_size gives the size of the space to insert and old_size can be used # to skip this size in the mp3-file (e.g if sub insert_space { my ($self, $insert) = @_; my $mp3obj = $self->{mp3}; # !! use a specific tmp-dir here my $tempfile = dirname($mp3obj->{filename}) . "/TMPxx"; my $count = 0; while (-e $tempfile . $count . ".tmp") { if ($count++ > 999) { warn "Problems with tempfile\n"; return undef; } } $tempfile .= $count . ".tmp"; unless (open (NEW, ">$tempfile")) { warn "Can't open '$tempfile' to insert tag\n"; return -1; } my ($buf, $pos_old); binmode NEW; $pos_old=0; $mp3obj->seek(0,0); local $\ = ''; foreach my $ins (@$insert) { if ($pos_old < $ins->[0]) { $pos_old += $ins->[0]; while ($mp3obj->read(\$buf,$ins->[0]<16384?$ins->[0]:16384)) { print NEW $buf; $ins->[0] = $ins->[0]<16384?0:$ins->[0]-16384; } } for (my $i = 0; $i<$ins->[2]; $i++) { print NEW chr(0); } if ($ins->[1]) { $pos_old += $ins->[1]; $mp3obj->seek($pos_old,0); } } while ($mp3obj->read(\$buf,16384)) { print NEW $buf; } close NEW; $mp3obj->close; # rename tmp-file to orig file unless (( rename $tempfile, $mp3obj->{filename})|| (system("mv",$tempfile,$mp3obj->{filename})==0)) { unlink($tempfile); warn "Couldn't rename temporary file $tempfile to $mp3obj->{filename}\n"; return -1; } return 0; } =pod =item get_frames() ($name, @info) = get_frames($ID); ($name, @info) = get_frames($ID, 'raw'); Same as get_frame() with different order of the returned values. $name and elements of the array @info have the same semantic as for get_frame(); each frame with id $ID produces one elements of array @info. =cut sub get_frames { my ($self, $fname, $raw) = @_; my ($info, $name, @rest) = $self->get_frame($fname, $raw) or return; return ($name, $info, @rest); } =item as_bin() $tag2 = $id3v2->as_bin($ignore_error, $update_file, $raw_ok); Returns the the current content of the ID3v2 tag as a string good to write to a file; it contains all the necessary footers and headers. If $ignore_error is TRUE, the frames the module does not know how to write are skipped; otherwise it is an error to have such a frame. Returns undef on error. If the optional argument $update_file is TRUE, an additional action is performed: if the audio file does not contain an ID3v2 tag, or the tag in the file is smaller than the built ID3v2 tag, the necessary 0-padding is inserted before the audio content of the file so that it is able to accommodate the build tag (and the C<tagsize> field of $id3v2 is updated correspondingly); in any case the header length of $tag2 is set to reflect the space in the beginning of the audio file. Unless $update_file has C<'padding'> as a substring, the actual length of the string $tag2 is not modified, so if it is smaller than the reserved space in the file, one needs to add some 0 padding at the end. Note that if the size of reserved space can shrink (as with C<id3v2_shrink> configuration option), then without this option it would be hard to calculate necessary padding by hand. If $raw_ok option is given, but not $update_file, the original contents is returned for unmodified tags. =item as_bin_raw() $tag2 = $id3v2->as_bin_raw($ignore_error, $update_file); same as as_bin() with $raw_ok flag. =item write_tag() $id3v2->write_tag($ignore_error); Saves all frames to the file. It tries to update the file in place, when the space of the old tag is big enough for the new tag. Otherwise it creates a temp file with a new tag (i.e. copies the whole mp3 file) and renames/moves it to the original file name. An extended header with CRC checksum is not supported yet. Encryption of frames and group ids are not supported. If $ignore_error is set, these options are ignored and the frames are saved without these options. If $ignore_error is not set and a tag with an unsupported option should be save, the tag is not written and a 0 is returned. If a tag with an encrypted frame is read, and the frame is not changed it can be saved encrypted again. ID3v2.2 tags are converted automatically to ID3v2.3 tags during writing. If a frame cannot be converted automatically (PIC; CMR), writing aborts and returns a 0. If $ignore_error is true, only not convertable frames are ignored and not written, but the rest of the tag is saved as ID3v2.3. At the moment the tag is automatically unsynchronized. If the tag is written successfully, 1 is returned. =cut sub as_bin_raw ($;$$) { my ($self, $ignore_error, $update_file) = @_; $self->as_bin($ignore_error, $update_file, 1); } sub as_bin ($;$$$) { my ($self, $ignore_error, $update_file, $raw_ok) = @_; return $self->{raw_data} if $raw_ok and $self->{raw_data} and not $self->{modified} and not $update_file; die "Writing of ID3v2.4 is not fully supported (prohibited now via `write_v24').\n" if $self->{major} == 4 and not $self->get_config1('write_v24'); if ($self->{major} > 4) { warn "Only writing of ID3v2.3 (and some tags of v2.4) is supported. Cannot convert ID3v". $self->{version}." to ID3v2.3 yet.\n"; return undef; } # which order should tags have? $self->get_frame_ids; my $tag_data = $self->build_tag($ignore_error); return unless defined $tag_data; # printing this will ruin flags if they are \x80 or above. die "panic: prepared raw tag contains wide characters" if $tag_data =~ /[^\x00-\xFF]/; # perhaps search for first mp3 data frame to check if tag size is not # too big and will override the mp3 data #ext header are not supported yet my $flags = chr(0); $flags = chr(128) if ($self->get_config('id3v23_unsync'))->[0] and $tag_data =~ s/\xFF(?=[\x00\xE0-\xFF])/\xFF\x00/g; # sync flag $tag_data .= "\0" # Terminated by 0xFF? if length $tag_data and chr(0xFF) eq substr $tag_data, -1, 1; my $n_tsize = length $tag_data; my $header = 'ID3' . chr(3) . chr(0); if ($update_file) { my $o_tsize = $self->{buggy_padding_size} + $self->{tagsize}; my $add_padding = 0; if ( $o_tsize < $n_tsize or ($self->get_config('id3v2_shrink'))->[0] ) { # if creating new tag / increasing size add at least 128b padding # add additional bytes to make new filesize multiple of 512b my $mp3obj = $self->{mp3}; my $filesize = (stat($mp3obj->{filename}))[7]; my $extra = ($self->get_config('id3v2_minpadding'))->[0]; my $n_filesize = ($filesize + $n_tsize - $o_tsize + $extra); my $round = ($self->get_config('id3v2_sizemult'))->[0]; $n_filesize = (($n_filesize + $round - 1) & ~($round - 1)); my $n_padding = $n_filesize - $filesize - ($n_tsize - $o_tsize); $n_tsize += $n_padding; if ($o_tsize != $n_tsize) { my @insert = [0, $o_tsize+10, $n_tsize + 10]; return undef unless insert_space($self, \@insert) == 0; } else { # Slot is not filled by 0; fill it manually $add_padding = $n_padding - $self->{buggy_padding_size}; } $self->{tagsize} = $n_tsize; } else { # Include current "padding" into n_tsize $add_padding = $self->{tagsize} - $n_tsize; $n_tsize = $self->{tagsize} = $o_tsize; } $add_padding = 0 if $add_padding < 0; $tag_data .= "\0" x $add_padding if $update_file =~ /padding/; } #convert size to header format specific size my $size = unpack('B32', pack ('N', $n_tsize)); substr ($size, -$_, 0) = '0' for (qw/28 21 14 7/); $size= pack('B32', substr ($size, -32)); return "$header$flags$size$tag_data"; } sub write_tag { my ($self,$ignore_error) = @_; $self->fix_frames_encoding() if $self->get_config1('id3v2_fix_encoding_on_write'); $self->get_frame_ids; # Ensure all the reading is done... # Need to do early, otherwise file size for calculation of "best" padding # may not take into account the added ID3v1 tag my $mp3obj = $self->{mp3}; $mp3obj->close; unless ($mp3obj->open("write")) { warn "Couldn't open file `",$mp3obj->filename(),"' to write tag!"; return undef; } my $tag = $self->as_bin($ignore_error, 'update_file, with_padding'); return 0 unless defined $tag; $mp3obj->close; unless ($mp3obj->open("write")) { # insert_space() could've closed the file warn "Couldn't open file `",$mp3obj->filename(),"' to write tag!"; return undef; } # actually write the tag $mp3obj->seek(0,0); $mp3obj->write($tag); $mp3obj->close; return 1; } =pod =item remove_tag() $id3v2->remove_tag(); Removes the whole tag from the file by copying the whole mp3-file to a temp-file and renaming/moving that to the original filename. Do not use remove_tag() if you only want to change a header, as otherwise the file is copied unnecessarily. Use write_tag() directly, which will override an old tag. =cut sub remove_tag { my $self = shift; my $mp3obj = $self->{mp3}; my $tempfile = dirname($mp3obj->{filename}) . "/TMPxx"; my $count = 0; local $\ = ''; while (-e $tempfile . $count . ".tmp") { if ($count++ > 999) { warn "Problems with tempfile\n"; return undef; } } $tempfile .= $count . ".tmp"; if (open (NEW, ">$tempfile")) { my $buf; binmode NEW; $mp3obj->seek($self->{tagsize}+10,0); while ($mp3obj->read(\$buf,16384)) { print NEW $buf; } close NEW; $mp3obj->close; unless (( rename $tempfile, $mp3obj->{filename})|| (system("mv",$tempfile,$mp3obj->{filename})==0)) { warn "Couldn't rename temporary file $tempfile\n"; } } else { warn "Couldn't write temp file\n"; return undef; } return 1; } =pod =item add_frame() $fn = $id3v2->add_frame($fname, @data); Add a new frame, identified by the short name $fname. The number of elements of array @data should be as described in the ID3v2.3 standard. (See also L<MP3::Tag::ID3v2_Data>.) There are two exceptions: if @data is empty, it is filled with necessary number of C<"">); if one of required elements is C<encoding>, it may be omitted or be C<undef>, meaning the arguments are in "Plain Perl (=ISOLatin-1 or Unicode) encoding". It returns the the short name $fn (which can differ from $fname, when an $fname frame already exists). If no other frame of this kind is allowed, an empty string is returned. Otherwise the name of the newly created frame is returned (which can have a 01 or 02 or ... appended). You have to call write_tag() to save the changes to the file. Examples (with C<$id3v2-E<gt>> omitted): $f = add_frame('TIT2', 0, 'Abba'); # $f='TIT2' $f = add_frame('TIT2', 'Abba'); # $f='TIT201', encoding=0 implicit $f = add_frame('COMM', 'ENG', 'Short text', 'This is a comment'); $f = add_frame('COMM'); # creates an empty frame $f = add_frame('COMM', 'ENG'); # ! wrong ! $f=undef, becaues number # of arguments is wrong $f = add_frame('RBUF', $n1, $n2, $n3); $f = add_frame('RBUF', $n1, $n2); # last field of RBUF is optional If a frame has optional fields I<and> C<encoding> (only C<COMR> frame as of ID3v2.4), there may be an ambiguity which fields are omitted. It is resolved this way: the C<encoding> field can be omitted only if all other optional frames are omitted too (set it to C<undef> instead). =item add_frame_split() The same as add_frame(), but if the number of arguments is unsufficient, would split() the last argument on C<;> to obtain the needed number of arguments. Should be avoided unless it is known that the fields do not contain C<;> (except for C<POPM RBUF RVRB SYTC>, where splitting may be done non-ambiguously). # No ambiguity, since numbers do not contain ";": $f = add_frame_split('RBUF', "$n1;$n2;$n3"); For C<COMR> frame, in case when the fields are C<join()>ed by C<';'>, C<encoding> field may be present only if all the other fields are present. =cut # 0 = latin1 (effectively: unknown) # 1 = UTF-16 with BOM (we always write UTF-16le to cowtow to M$'s bugs) # 2 = UTF-16be, no BOM # 3 = UTF-8 my @dec_types = qw( iso-8859-1 UTF-16 UTF-16BE utf8 ); my @enc_types = qw( iso-8859-1 UTF-16LE UTF-16BE utf8 ); my @tail_rex; # Actually, disable this code: it always triggers unsync... my $use_utf16le = $ENV{MP3TAG_USE_UTF_16LE}; @enc_types = @dec_types unless $use_utf16le; sub _add_frame { my ($self, $split, $fname, @data) = @_; $self->get_frame_ids() unless exists $self->{frameIDs}; my $format = get_format($fname); return undef unless defined $format; #prepare the data my $args = @$format; my $opt = 0; unless (@data) { @data = map {''} @$format; } my($encoding, $calc_enc, $e, $e_add) = (0,0); # Need to calculate encoding? # @data may be smaller than @args due to missing encoding, or due # to optional arguments. Both may be applicable for COMR frames. if (@data < $args) { $_->{optional} and $opt++ for @$format; $e_add++, unshift @data, undef # Encoding skipped if (@data == $args - 1 - $opt or $split and @data <= $args - 1 - $opt) and $format->[0]->{name} eq '_encoding'; if ($opt) { # encoding is present only for COMR, require it die "Data for `encoding' should be between 0 and 3" if $format->[0]->{name} eq "_encoding" and defined $data[0] and not $data[0] =~ /^[0-3]?$/; } } if ($split and @data < $args) { if ($back_splt{$fname}) { my $c = $args - @data; my $last = pop @data; my $rx = ($tail_rex[$c] ||= qr/((?:;[^;]*){0,$c})\z/); my($tail) = ($last =~ /$rx/); # Will always match push @data, substr $last, 0, length($last)-length($tail); if ($tail =~ s/^;//) { # matched >= 1 times push @data, split ';', $tail; } } else { my $last = pop @data; push @data, split /;/, $last, $args - @data; } # Allow for explicit specification of encoding shift @data if @data == $args + 1 and not defined $data[0] and $format->[0]->{name} eq '_encoding'; # Was auto-put there } die "Unexpected number of fields: ".@data.", expect $args, optional=$opt" unless @data <= $args and @data >= $args - $opt; if ($format->[0]->{name} eq "_encoding" and not defined $data[0]) { $calc_enc = 1; shift @data; } my ($datastring, $have_high) = ""; if ($calc_enc) { my @d = @data; foreach my $fs (@$format) { $have_high = 1 if $fs->{encoded} and $d[0] and $d[0] =~ /[^\x00-\xff]/; shift @d unless $fs->{name} eq "_encoding"; } } foreach my $fs (@$format) { next if $fs->{optional} and not @data; if ($fs->{name} eq "_encoding") { if ($calc_enc) { $encoding = ($have_high ? 1 : 0); # v2.3 only has 0, 1 } else { $encoding = shift @data; } $datastring .= chr($encoding); next; } my $d = shift @data; if ($fs->{isnum}) { ## store data as number my $num = int($d); $d=""; while ($num) { $d=pack("C",$num % 256) . $d; $num = int($num/256);} if ( exists $fs->{len} and $fs->{len}>0 ) { $d = substr $d, -$fs->{len}; $d = ("\x00" x ($fs->{len}-length($d))) . $d if length($d) < $fs->{len}; } if ( exists $fs->{mlen} and $fs->{mlen}>0 ) { $d = ("\x00" x ($fs->{mlen}-length($d))) . $d if length($d) < $fs->{mlen}; } } elsif ( exists $fs->{len} and not exists $fs->{func}) { if ($fs->{len}>0) { $d = substr $d, 0, $fs->{len}; $d .= " " x ($fs->{len}-length($d)) if length($d) < $fs->{len}; } elsif ($fs->{len}==0) { $d .= chr(0); } } elsif (exists $fs->{mlen} and $fs->{mlen}>0) { $d .= " " x ($fs->{mlen}-length($d)) if length($d) < $fs->{mlen}; } if (exists $fs->{re2b}) { while (my ($pat, $rep) = each %{$fs->{re2b}}) { $d =~ s/$pat/$rep/gis; } } if (exists $fs->{func_back}) { $d = $fs->{func_back}->($d); } elsif (exists $fs->{func}) { if ($fs->{small_max}) { # Allow the old way (byte) and a number # No conflict possible: byte is always smaller than ord '0' $d = pack 'C', $d if $d =~ /^\d+$/; } $d = $self->__format_field($fname, $fs->{name}, $d) } if ($fs->{encoded}) { if ($encoding) { # 0 = latin1 (effectively: unknown) # 1 = UTF-16 with BOM (we write UTF-16le to cowtow to M$'s bugs) # 2 = UTF-16be, no BOM # 3 = UTF-8 require Encode; if ($calc_enc or $encode_utf8) { # e_u8==1 by default $d = Encode::encode($enc_types[$encoding], $d); } elsif ($encoding < 3) { # Reencode from UTF-8 $d = Encode::decode('UTF-8', $d); $d = Encode::encode($enc_types[$encoding], $d); } $d = "\xFF\xFE$d" if $use_utf16le and $encoding == 1; } elsif (not $self->{fixed_encoding} # Now $encoding == 0... and $self->get_config1('id3v2_fix_encoding_on_edit') and $e = $self->botched_encoding() and do { require Encode; Encode::decode($e, $d) ne $d }) { # If the current string is interpreted differently # with botched_encoding, need to unbotch... $self->fix_frames_encoding(); } } $datastring .= $d; } return add_raw_frame($self, $fname, $datastring); } sub add_frame { my $self = shift; _add_frame($self, 0, @_) } sub add_frame_split { my $self = shift; _add_frame($self, 1, @_) } sub add_raw_frame ($$$$) { my($self, $fname, $datastring, $flags) = (shift,shift,shift,shift); #add frame to tag if (exists $self->{frames}->{$fname}) { my ($c, $ID) = (1, $fname); $fname .= '01'; while (exists $self->{frames}->{$fname}) { $fname++, $c++; } ++$self->{extra_frames}->{$ID} if $c > ($self->{extra_frames}->{$ID} || 0); } $self->{frames}->{$fname} = {flags => ($flags || $self->check_flags(0)), major => $self->{frame_major}, data => $datastring }; $self->{modified}++; return $fname; } =pod =item change_frame() $id3v2->change_frame($fname, @data); Change an existing frame, which is identified by its short name $fname eg as returned by get_frame_ids(). @data must be same as in add_frame(). If the frame $fname does not exist, undef is returned. You have to call write_tag() to save the changes to the file. =cut sub change_frame { my ($self, $fname, @data) = @_; $self->get_frame_ids() unless exists $self->{frameIDs}; return undef unless exists $self->{frames}->{$fname}; $self->remove_frame($fname); $self->add_frame($fname, @data); return 1; } =pod =item remove_frame() $id3v2->remove_frame($fname); Remove an existing frame. $fname is the short name of a frame, eg as returned by get_frame_ids(). You have to call write_tag() to save the changes to the file. =cut sub remove_frame { my ($self, $fname) = @_; $self->get_frame_ids() unless exists $self->{frameIDs}; return undef unless exists $self->{frames}->{$fname}; delete $self->{frames}->{$fname}; $self->{modified}++; return 1; } =item copy_frames($from, $to, $overwrite, [$keep_flags, $f_ids]) Copies specified frames between C<MP3::Tag::ID3v2> objects $from, $to. Unless $keep_flags, the copied frames have their flags cleared. If the array reference $f_ids is not specified, all the frames (but C<GRID> and C<TLEN>) are considered (subject to $overwrite), otherwise $f_ids should contain short frame ids to consider. Group ID flag is always cleared. If $overwrite is C<'delete'>, frames with the same descriptors (as returned by get_frame_descr()) in $to are deleted first, then all the specified frames are copied. If $overwrite is FALSE, only frames with descriptors not present in $to are copied. (If one of these two conditions is not met, the result may be not conformant to standards.) Returns count of copied frames. =cut sub copy_frames { my ($from, $to, $overwrite, $keep_flags, $f_ids) = @_; # return 0 unless $from->{ID3v2}; # No need to create it... my($cp, $expl) = (0, $f_ids); $f_ids ||= [keys %{$from->get_frame_ids}]; for my $fn (@$f_ids) { next if not $expl and $fn =~ /^(GRID|TLEN)/; if (($overwrite || 0) eq 'delete') { $to->frame_select_by_descr($from->get_frame_descr($fn), undef); # delete } elsif (not $overwrite) { next if $to->frame_have($from->get_frame_descr($fn)); } my $f = $from->{frames}->{$fn}; $fn =~ s/^(\w{4})\d+$/$1/; my $d = $f->{data}; my %fl = %{$f->{flags}}; (substr $d, 0, 1) = '' if delete $fl{groupid}; $to->add_raw_frame($fn, $d, $keep_flags ? \%fl : undef); $cp++; } return $cp } =item is_modified() $id3v2->is_modified; Returns true if the tag was modified after it was created. =cut sub is_modified { shift->{modified} } =pod =item supported_frames() $frames = $id3v2->supported_frames(); Returns a hash reference with all supported frames. The keys of the hash are the short names of the supported frames, the according values are the long (english) names of the frames. =cut sub supported_frames { my $self = shift; my (%tags, $fname, $lname); while ( ($fname, $lname) = each %long_names) { $tags{$fname} = $lname if get_format($fname, "quiet"); } return \%tags; } =pod =item what_data() ($data, $res_inp, $data_map) = $id3v2->what_data($fname); Returns an array reference with the needed data fields for a given frame. At this moment only the internal field names are returned, without any additional information about the data format of this field. Names beginning with an underscore (normally '_data') can contain binary data. (The C<_encoding> field is skipped in this list, since it is usually auto-deduced by this module.) $resp_inp is a reference to a hash (keyed by the field name) describing restrictions for the content of the data field. If the entry is undef, no restriction exists. Otherwise it is a hash. The keys of the hash are the allowed input, the correspodending value is the value which is actually stored in this field. If the value is undef then the key itself is valid for saving. If the hash contains an entry with "_FREE", the hash contains only suggestions for the input, but other input is also allowed. $data_map contains values of $resp_inp in the order of fields of a frame (including C<_encoding>). Example for picture types of the APIC frame: {"Other" => "\x00", "32x32 pixels 'file icon' (PNG only)" => "\x01", "Other file icon" => "\x02", ...} =cut sub what_data { my ($self, $fname) = @_; $fname = substr $fname, 0, 4; # delete 01 etc. at end return if length($fname)==3; #id3v2.2 tags are read-only and should never be written my $reswanted = wantarray; my $format = get_format($fname, "quiet"); return unless defined $format; my (@data, %res, @datares); foreach (@$format) { next unless exists $_->{name}; push @data, $_->{name} unless $_->{name} eq "_encoding"; next unless $reswanted; my $key = $fname . $_->{name}; $res{$_->{name}} = $field_map{$key} if exists $field_map{$key}; push @datares, $field_map{$key}; } return(\@data, \%res, \@datares) if $reswanted; return \@data; } sub __format_field { my ($self, $fname, $nfield, $v) = @_; # $v =~ s/^(\d+)$/chr $1/e if $is_small_int{"$fname$nfield"}; # Already done by caller my $m = $field_map_back{my $t = "$fname$nfield"} or return $v; # packed ==> Human return $v if exists $m->{$v}; # Already of a correct form my $m1 = $field_map{$t} or die; # Human ==> packed return $m1->{$v} if exists $m1->{$v}; # translate return $v if $m->{_FREE}; # Free-form allowed die "Unsupported value `$v' for field `$nfield' of frame `$fname'"; } =item title( [@new_title] ) Returns the title composed of the tags configured via C<MP3::Tag-E<gt>config('v2title')> call (with default 'Title/Songname/Content description' (TIT2)) from the tag. (For backward compatibility may be called by deprecated name song() as well.) Sets TIT2 frame if given the optional arguments @new_title. If this is an empty string, the frame is removed. =cut *song = \&title; sub v2title_order { my $self = shift; @{ $self->get_config('v2title') }; } sub title { my $self = shift; if (@_) { $self->remove_frame('TIT2'); # NOP if it is not there return if @_ == 1 and $_[0] eq ''; return $self->add_frame('TIT2', @_); } my @parts = grep defined && length, map scalar $self->get_frame($_), $self->v2title_order; return unless @parts; my $last = pop @parts; my $part; for $part (@parts) { $part =~ s(\0)(///)g; # Multiple strings $part .= ',' unless $part =~ /[.,;:\n\t]\s*$/; $part .= ' ' unless $part =~ /\s$/; } return join '', @parts, $last; } =item _comment([$language]) Returns the file comment (COMM with an empty 'Description') from the tag, or "Subtitle/Description refinement" (TIT3) frame (unless it is considered a part of the title). =cut sub _comment { my $self = shift; my $language; $language = lc shift if @_; my @info = get_frames($self, "COMM"); shift @info; for my $comment (@info) { next unless defined $comment; # Removed frames next unless exists $comment->{Description} and not length $comment->{Description}; next if defined $language and (not exists $comment->{Language} or lc $comment->{Language} ne $language); return $comment->{Text}; } return if grep $_ eq 'TIT3', $self->v2title_order; return scalar $self->get_frame("TIT3"); } =item comment() $val = $id3v2->comment(); $newframe = $id3v2->comment('Just a comment for freddy', 'personal', 'eng'); Returns the file comment (COMM frame with the 'Description' field in C<default_descr_c> configuration variable, defalting to C<''>) from the tag, or "Subtitle/Description refinement" (TIT3) frame (unless it is considered a part of the title). If optional arguments ($comment, $short, $language) are present, sets the comment frame. If $language is omited, uses the C<default_language> configuration variable (default is C<XXX>). If not C<XXX>, this should be lowercase 3-letter abbreviation according to ISO-639-2). If $short is not defined, uses the C<default_descr_c> configuration variable. If $comment is an empty string, the frame is removed. =cut sub comment { my $self = shift; my ($comment, $short, $language) = @_ or return $self->_comment(); my @info = get_frames($self, "COMM"); my $desc = ($self->get_config('default_descr_c'))->[0]; shift @info; my $c = -1; for my $comment (@info) { ++$c; next unless defined $comment; # Removed frames next unless exists $comment->{Description} and $comment->{Description} eq $desc; next if defined $language and (not exists $comment->{Language} or lc $comment->{Language} ne lc $language); $self->remove_frame($c ? sprintf 'COMM%02d', $c : 'COMM'); # $c--; # Not needed if only one frame is removed last; } return if @_ == 1 and $_[0] eq ''; $language = ($self->get_config('default_language'))->[0] unless defined $language; $short = $desc unless defined $short; $self->add_frame('COMM', $language, $short, $comment); } =item frame_select($fname, $descrs, $languages [, $newval1, ...]) Used to get/set/delete frames which may be not necessarily unique in a tag. # Select short-description='', prefere language 'eng', then 'rus', then # the third COMM frame, then any (in this case, the first or the second) # COMM frame $val = $id3v2->frame_select('COMM', '', ['eng', 'rus', '#2', '']); # Read $new = $id3v2->frame_select('COMM', '', ['eng', 'rus', '#2'], # Write 'Comment with empty "Description" and "eng"'); $new = $id3v2->frame_select('COMM', '', ['eng', 'rus', '#2'], # Delete undef); Returns the contents of the first frame named $fname with a 'Description' field in the specified array reference $descrs and the language in the list of specified languages $languages; empty return otherwise. If the frame is a "simple frame", the frame is returned as a string, otherwise as a hash reference; a "simple frame" should consist of one of Text/URL/_Data fields, with possible addition of Language and Description fields (if the corresponding arguments were defined). The lists $descrs and $languages of one element can be flattened to become this element (as with C<''> above). If the lists are not defined, no restriction is applied; to get the same effect with defined arguments, use $languages of C<''>, and/or $descrs a hash reference. Language of the form C<'#NUMBER'> selects the NUMBER's (0-based) frame with frame name $fname. If optional arguments C<$newval1...> are given, B<ALL> the found frames are removed; if only one such argument C<undef> is given, this is the only action. Otherwise, a new frame is created afterwards (the first elements of $descrs and $languages are used as the short description and the language, defaulting to C<''> and the C<default_language> configuration variable (which, in turn, defaults to C<XXX>; if not C<XXX>, this should be lowercase 3-letter abbreviation according to ISO-639-2). If new frame is created, the frame's name is returned; otherwise the count of removed frames is returned. As a generalization, APIC frames are handled too, using C<Picture Type> instead of C<Language>, and auto-calculating C<MIME type> for (currently) TIFF/JPEG/GIF/PNG/BMP and octet-stream. Only frames with C<MIME type> coinciding with the auto-calculated value are considered as "simple frames". One can use both the 1-byte format for C<Picture Type>, and the long names used in the ID3v2 documentation; the default value is C<'Cover (front)'>. # Choose APIC with empty description, picture_type='Leaflet page' my $data = $id3v2->frame_select('APIC', '', 'Leaflet page') or die "no expected APIC frame found"; my $format = ( ref $data ? $data->{'MIME type'} : $id3v2->_Data_to_MIME($data) ); # I know what to do with application/pdf only (sp?) and 'image/gif' die "Do not know what to do with this APIC format: `$format'" unless $format eq 'application/pdf' or $format eq 'image/gif'; $data = $data->{_Data} if ref $data; # handle non-simple frame # Set APIC frame with empty description (front cover if no other present) # from content of file.gif my $data = do { open my $f, '<', 'file.gif' and binmode $f or die; undef $/; <$f>}; my $new_frame = $id3v2->frame_select('APIC', '', undef, $data); Frames with multiple "content" fields may be set by providing multiple values to set. Alternatively, one can also C<join()> the values with C<';'> if the splitting is not ambiguous, e.g., for C<POPM RBUF RVRB SYTC>. (For frames C<GEOD> and C<COMR>, which have a C<Description> field, it should be specified among these values.) $id3v2->frame_select("RBUF", undef, undef, $n1, $n2, $n3); $id3v2->frame_select("RBUF", undef, undef, "$n1;$n2;$n3"); (By the way: consider using the method select_id3v2_frame() on the "parent" MP3::Tag object instead [see L<MP3::Tag/select_id3v2_frame>], or L<frame_select_by_descr()>.) =item _Data_to_MIME Internal method to extract MIME type from a string the image file content. Returns C<application/octet-stream> for unrecognized data (unless extra TRUE argument is given). $format = $id3v2->_Data_to_MIME($data); Currently, only the first 4 bytes of the string are inspected. =cut sub __to_lang($$) {my $l = shift; return $l if shift or $l eq 'XXX'; lc $l} my %as_lang = ('APIC', ['Picture Type', chr 3, 'small_int']); # "Cover (front)" my %MT = ("\xff\xd8\xff\xe0" => 'image/jpeg', "MM\0*" => 'image/tiff', "II*\0" => 'image/tiff', "\x89PNG", qw(image/png GIF8 image/gif BM image/bmp)); sub _Data_to_MIME ($$;$) { my($self, $data, $force) = (shift, shift, shift); # Fname, field name remain my $res = $MT{substr $data, 0, 4} || $MT{substr $data, 0, 2}; return $res if $res; return 'audio/mpeg' if $data =~ /^\xff[\xe0-\xff]/; # 11 bits are 1 return 'application/octet-stream' unless $force; return; } sub _frame_select { # if $extract_content false, return all found # "Quadratic" in number of comment frames and select-short/lang specifiers my ($self, $extract_content, $fname) = (shift, shift, shift); my ($descr, $languages) = (shift, shift); # or ($fname eq 'COMM' and return $self->_comment()); # ??? my $any_descr; if (ref $descr eq 'HASH') { # Special case $any_descr = 1; undef $descr; } elsif (defined $descr and not ref $descr) { $descr = [$descr]; } my $lang_special = $as_lang{$fname}; my $lang_field = ($lang_special ? $lang_special->[0] : 'Language'); my $languages_mangled; if (defined $languages) { $languages = [$languages] unless ref $languages; $languages = [@$languages]; # Make a copy: we edit the entries... if ($lang_special) { my $m = $field_map{"$fname$lang_field"}; if ($m) { # Below we assume that mapped values are not ''... # Need to duplicate the logic in add_frame() here, since # we need a normalized form to compare frames-to-select with... if ($lang_special->[2]) { # small_int s/^(\d+)$/chr $1/e for @$languages; } @$languages_mangled = map( (exists $m->{$_} ? $m->{$_} : $_), @$languages); my $m1 = $field_map_back{"$fname$lang_field"} or die; my $loose = $m->{_FREE}; @$languages = map( (exists $m1->{$_} ? $m1->{$_} : $_), @$languages_mangled); $_ eq '' or exists $m1->{$_} or $loose or not /\D/ or die "Unknown value `$_' for field `$lang_field' of frame $fname" for @$languages_mangled; } } else { @$languages = map __to_lang($_, 0), @$languages; } } my @found_frames = get_frames($self, $fname); shift @found_frames; my(@by_lang) = (0..$#found_frames); # Do it the slow way... if (defined $languages) { @by_lang = (); my %seen; for my $l (@$languages) { if ($l =~ /^#(\d+)$/) { push @by_lang, $1 if not $seen{$1}++ and $1 < @found_frames; } elsif (length $l > 3 and not $lang_special) { die "Language `$l' should not be more than 3-chars long"; } else { for my $c (0..$#found_frames) { my $f = $found_frames[$c] or next; # XXXX Needed? push @by_lang, $c if ($l eq '' or ref $f and defined $f->{$lang_field} and $l eq __to_lang $f->{$lang_field}, $lang_special) and not $seen{$c}++; } } } } my @select; for my $c (@by_lang) { my $f = $found_frames[$c]; my $cc = [$c, $f]; push(@select, $cc), next unless defined $descr; push @select, $cc if defined $f and ref $f and defined $f->{Description} and grep $_ eq $f->{Description}, @$descr; } return @select unless $extract_content; unless (@_) { # Read-only access return unless @select; my $res = $select[0][1]; # Only defined frames here... return $res unless ref $res; # TLEN my $ic = my $c = keys %$res; $c-- if exists $res->{Description} and (defined $descr or $any_descr); $c-- if exists $res->{$lang_field} and defined $languages; $c-- if exists $res->{encoding}; $c-- if $c == 2 and $ic == 5 and exists $res->{'MIME type'} and exists $res->{_Data} and $res->{'MIME type'} eq $self->_Data_to_MIME($res->{_Data}); if ($c <= 1) { return $res->{Text} if exists $res->{Text}; return $res->{URL} if exists $res->{URL}; return $res->{_Data} if exists $res->{_Data}; } return $res; } # Write or delete for my $f (reverse @select) { # Removal may break the numeration??? my ($c, $frame) = @$f; $self->remove_frame($c ? sprintf('%s%02d', $fname, $c) : $fname); } return scalar @select unless @_ > 1 or defined $_[0]; # Delete if (defined $languages) { $languages = $languages_mangled if defined $languages_mangled; } elsif ($lang_special) { $languages = [$lang_special->[1]]; } else { $languages = [@{$self->get_config('default_language')}]; # Copy to modify } my $format = get_format($fname); my $have_lang = grep $_->{name} eq $lang_field, @$format; $#$languages = $have_lang - 1; # Truncate unshift @$languages, $self->_Data_to_MIME($_[0]) if $lang_special and @_ == 1; # "MIME type" field $descr = [''] unless defined $descr; my $have_descr = grep $_->{name} eq 'Description', @$format; $have_descr = 0 if $embedded_Descr{$fname}; # Must be explicitly provided $#$descr = $have_descr - 1; # Truncate $self->add_frame_split($fname, @$languages, @$descr, @_) or die; } sub frame_select { my $self = shift; $self->_frame_select(1, @_); } =item frame_list() Same as frame_select(), but returns the list of found frames, each an array reference C<[$N, $f]> with $N the 0-based ordinal (among frames with the given short name), and $f the contents of a frame. =item frame_have() Same as frame_select(), but returns the count of found frames. =item frame_select_by_descr() =item frame_have_by_descr() =item frame_list_by_descr() $c = $id3v2->frame_select_by_descr("COMM(fre,fra,eng,#0)[]"); $t = $id3v2->frame_select_by_descr("TIT2"); $id3v2->frame_select_by_descr("TIT2", "MyT"); # Set/Change $id3v2->frame_select_by_descr("RBUF", $n1, $n2, $n3); # Set/Change $id3v2->frame_select_by_descr("RBUF", "$n1;$n2;$n3"); # Set/Change $id3v2->frame_select_by_descr("TIT2", undef); # Remove Same as frame_select(), frame_have(), frame_list(), but take one string argument instead of $fname, $descrs, $languages. The argument should be of the form NAME(langs)[descr] Both C<(langs)> and C<[descr]> parts may be omitted; I<langs> should contain comma-separated list of needed languages; no protection by backslashes is needed in I<descr>. frame_select_by_descr() will return a hash if C<(lang)> is omited, but the frame has a language field; likewise for C<[descr]>; see below for alternatives. Remember that when frame_select_by_descr() is used for modification, B<ALL> found frames are deleted before a new one is added. (By the way: consider using the method select_id3v2_frame_by_descr() on the "parent" MP3::Tag object instead; see L<MP3::Tag/select_id3v2_frame_by_descr>.) =item frame_select_by_descr_simple() Same as frame_select_by_descr(), but if no language is given, will not consider the frame as "complicated" frame even if it contains a language field. =item frame_select_by_descr_simpler() Same as frame_select_by_descr_simple(), but if no C<Description> is given, will not consider the frame as "complicated" frame even if it contains a C<Description> field. =cut sub frame_have { my $self = shift; scalar $self->_frame_select(0, @_); } sub frames_list { my $self = shift; $self->_frame_select(0, @_); } sub _frame_select_by_descr { my ($self, $what, $d) = (shift, shift, shift); my($l, $descr) = (''); if ( $d =~ s/^(\w{4})(?:\(([^()]*(?:\([^()]+\)[^()]*)*)\))?(?:\[(.*)\])?$/$1/ ) { $l = defined $2 ? [split /,/, $2, -1] : ($what > 1 && !@_ ? '' : undef); # Use special case in _frame_select: $descr = defined $3 ? $3 : ($what > 2 && !@_ ? {} : undef); # $descr =~ s/\\([\\\[\]])/$1/g if defined $descr; } return $self->_frame_select($what, $d, $descr, $l, @_); } sub frame_have_by_descr { my $self = shift; scalar $self->_frame_select_by_descr(0, @_); } sub frame_list_by_descr { my $self = shift; $self->_frame_select_by_descr(0, @_); } sub frame_select_by_descr { my $self = shift; $self->_frame_select_by_descr(1, @_); } sub frame_select_by_descr_simple { my $self = shift; $self->_frame_select_by_descr(2, @_); # 2 ==> prefer $languages eq ''... } sub frame_select_by_descr_simpler { my $self = shift; $self->_frame_select_by_descr(3, @_); # 2 ==> prefer $languages eq ''... } =item year( [@new_year] ) Returns the year (TYER/TDRC) from the tag. Sets TYER and TDRC frames if given the optional arguments @new_year. If this is an empty string, the frame is removed. The format is similar to timestamps of IDv2.4.0, but ranges can be separated by C<-> or C<-->, and non-contiguous dates are separated by C<,> (comma). If periods need to be specified via duration, then one needs to use the ISO 8601 C</>-notation (e.g., see http://www.mcs.vuw.ac.nz/technical/software/SGML/doc/iso8601/ISO8601.html ); the C<duration/end_timestamp> is not supported. On output, ranges of timestamps are converted to C<-> or C<--> separated format depending on whether the timestamps are years, or have additional fields. If configuration variable C<year_is_timestamp> is false, the return value is always the year only (of the first timestamp of a composite timestamp). Recall that ID3v2.4.0 timestamp has format 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 noncontiguous dates, use multiple strings, if allowed by the frame definition. =cut sub year { my $self = shift; if (@_) { $self->remove_frame('TYER') if defined $self->get_frame( "TYER"); $self->remove_frame('TDRC') if defined $self->get_frame( "TDRC"); return if @_ == 1 and $_[0] eq ''; my @args = @_; $args[-1] =~ s/^(\d{4}\b).*/$1/; $self->add_frame('TYER', @args); # Obsolete @args = @_; $args[-1] =~ s/-(-|(?=\d{4}\b))/\//g; # ranges are /-separated $args[-1] =~ s/,(?=\d{4}\b)/\0/g; # dates are \0-separated $args[-1] =~ s#([-/T:])(?=\d(\b|T))#${1}0#g; # %02d-format return $self->add_frame('TDRC', @args); # new; allows YYYY-MM-etc as well } my $y; ($y) = $self->get_frame( "TDRC", 'intact') or ($y) = $self->get_frame( "TYER") or return; return substr $y, 0, 4 unless ($self->get_config('year_is_timestamp'))->[0]; # Convert to human-readable form $y =~ s/\0/,/g; my $sep = ($y =~ /-/) ? '--' : '-'; $y =~ s#/(?=\d)#$sep#g; return $y; } =pod =item track( [$new_track] ) Returns the track number (TRCK) from the tag. Sets TRCK frame if given the optional arguments @new_track. If this is an empty string or 0, the frame is removed. =cut sub track { my $self = shift; if (@_) { $self->remove_frame('TRCK') if defined $self->get_frame("TRCK"); return if @_ == 1 and not $_[0]; return $self->add_frame('TRCK', @_); } return scalar $self->get_frame("TRCK"); } =pod =item artist( [ $new_artist ] ) Returns the artist name; it is the first existing frame from the list of TPE1 Lead artist/Lead performer/Soloist/Performing group TPE2 Band/Orchestra/Accompaniment TCOM Composer TPE3 Conductor TEXT Lyricist/Text writer Sets TPE1 frame if given the optional arguments @new_artist. If this is an empty string, the frame is removed. =cut sub artist { my $self = shift; if (@_) { $self->remove_frame('TPE1') if defined $self->get_frame( "TPE1"); return if @_ == 1 and $_[0] eq ''; return $self->add_frame('TPE1', @_); } my $a; ($a) = $self->get_frame("TPE1") and return $a; ($a) = $self->get_frame("TPE2") and return $a; ($a) = $self->get_frame("TCOM") and return $a; ($a) = $self->get_frame("TPE3") and return $a; ($a) = $self->get_frame("TEXT") and return $a; return; } =pod =item album( [ $new_album ] ) Returns the album name (TALB) from the tag. If none is found, returns the "Content group description" (TIT1) frame (unless it is considered a part of the title). Sets TALB frame if given the optional arguments @new_album. If this is an empty string, the frame is removed. =cut sub album { my $self = shift; if (@_) { $self->remove_frame('TALB') if defined $self->get_frame( "TALB"); return if @_ == 1 and $_[0] eq ''; return $self->add_frame('TALB', @_); } my $a; ($a) = $self->get_frame("TALB") and return $a; return if grep $_ eq 'TIT1', $self->v2title_order; return scalar $self->get_frame("TIT1"); } =item genre( [ $new_genre ] ) Returns the genre string from TCON frame of the tag. Sets TCON frame if given the optional arguments @new_genre. If this is an empty string, the frame is removed. =cut sub genre { my $self = shift; if (@_) { $self->remove_frame('TCON') if defined $self->get_frame( "TCON"); return if @_ == 1 and $_[0] eq ''; return $self->add_frame('TCON', @_); # XXX add genreID 0x00 ? } my $g = $self->get_frame('TCON'); return unless defined $g; $g =~ s/^\d+\0(?:.)//s; # XXX Shouldn't this be done in TCON()? $g; } =item version() $version = $id3v2->version(); ($major, $revision) = $id3v2->version(); Returns the version of the ID3v2 tag. It returns a formatted string like "3.0" or an array containing the major part (eg. 3) and revision part (eg. 0) of the version number. =cut sub version { my ($self) = @_; if (wantarray) { return ($self->{major}, $self->{revision}); } else { return $self->{version}; } } =item new() $tag = new($mp3fileobj); C<new()> needs as parameter a mp3fileobj, as created by C<MP3::Tag::File>. C<new> tries to find a ID3v2 tag in the mp3fileobj. If it does not find a tag it returns undef. Otherwise it reads the tag header, as well as an extended header, if available. It reads the rest of the tag in a buffer, does unsynchronizing if necessary, and returns a ID3v2-object. At this moment only ID3v2.3 is supported. Any extended header with CRC data is ignored, so no CRC check is done at the moment. The ID3v2-object can be used to extract information from the tag. Please use $mp3 = MP3::Tag->new($filename); $mp3->get_tags(); ## to find an existing tag, or $id3v2 = $mp3->new_tag("ID3v2"); ## to create a new tag instead of using this function directly =cut sub new { my ($class, $mp3obj, $create, $r_header) = @_; my $self={mp3=>$mp3obj}; my $header=0; bless $self, $class; if (defined $mp3obj) { # Not fake $mp3obj->open or return unless $mp3obj->is_open; $mp3obj->seek(0,0); $mp3obj->read(\$header, 10); $$r_header = $header if $r_header and 10 == length $header; } $self->{frame_start}=0; # default ID3v2 version $self->{major}=3; $self->{frame_major}=3; # major for new frames $self->{revision}=0; $self->{version}= "$self->{major}.$self->{revision}"; if (defined $mp3obj and $self->read_header($header)) { if ($create) { $self->{tag_data} = ''; $self->{data_size} = 0; } else { # sanity check: my $s = $mp3obj->size; my $s1 = $self->{tagsize} + $self->{footer_size}; if (defined $s and $s - 10 < $s1) { warn "Ridiculously large tag size: $s1; file size $s"; return; } $mp3obj->read(\$self->{tag_data}, $s1); $self->{data_size} = $self->{tagsize}; $self->{raw_data} = $header . $self->{tag_data}; # un-unsynchronize comes in all versions first if ($self->{flags}->{unsync}) { my $hits = $self->{tag_data} =~ s/\xFF\x00/\xFF/gs; $self->{data_size} -= $hits; } # in v2.2.x complete tag may be compressed, but compression isn't # described in tag specification, so get out if compression is found if ($self->{flags}->{compress_all}) { # can we test if it is simple zlib compression and use this? warn "ID3v".$self->{version}." [whole tag] compression isn't supported. Cannot read tag\n"; return undef; } # read the ext header if it exists if ($self->{flags}->{extheader}) { $self->{extheader} = substr ($self->{tag_data}, 0, 14); unless ($self->read_ext_header()) { return undef; # ext header not supported } } $self->{footer} = substr $self->{tag_data}, -$self->{footer_size} if $self->{footer_size}; # Treat (illegal) padding after the tag my($merge, $d, $z, $r) = ($mp3obj->get_config('id3v2_mergepadding'))->[0]; my $max0s = $merge ? 1e100 : 16*1024; while ($max0s and $mp3obj->read(\$d, 1024)) { $max0s -= length $d; ($z) = ($d =~ /^(\0*)/); $self->{buggy_padding_size} += length $z if $merge; ($r = substr $d, length $z), last unless length($z) == length($d); } $self->{tagend_offset} = $mp3obj->tell() - length $r; $mp3obj->read(\$d, 10 - length $r) and $r .= $d if defined $r and length $r < 10; $$r_header = $d if $r_header and 10 <= length $d; } $mp3obj->close; return $self; } else { $mp3obj->close if defined $mp3obj; if (defined $create && $create) { $self->{tag_data}=''; $self->{tagsize} = -10; $self->{data_size} = 0; $self->{buggy_padding_size} = 0; return $self; } } return undef; } sub new_with_parent { my ($class, $filename, $parent) = @_; my $header; my $new = $class->new($filename, undef, \$header); $parent->[0]{header} = $header if $header and $parent; return unless $new; $new->{parent} = $parent; $new; } ################## ## ## internal subs ## # This sub tries to read the header of an ID3v2 tag and checks for the right header # identification for the tag. It reads the version number of the tag, the tag size # and the flags. # Returns true if it finds a known ID3v2.x header, false otherwise. sub read_header { my ($self, $header) = @_; my %params; if (substr ($header,0,3) eq "ID3") { # flag meaning for all supported ID3v2.x versions my @flag_meaning=([],[], # v2.0 and v2.1 aren't supported yet # 2.2 ["unknown","unknown","unknown","unknown","unknown","unknown","compress_all","unsync"], # 2.3 ["unknown","unknown","unknown","unknown","unknown","experimental","extheader","unsync"], # 2.4 ["unknown","unknown","unknown","unknown","footer","experimental","extheader","unsync"], # ???? #["unknown","unknown","unknown","unknown","footer","experimental","extheader","unsync"], ); # extract the header data my ($major, $revision, $pflags) = unpack ("x3CCC", $header); # check the version if ($major > $#supported_majors or $supported_majors[$major] == 0) { my $warn = "Unknown ID3v2-Tag version: v2.$major.$revision\n"; $warn .= "| $major > ".($#supported_majors)." || $supported_majors[$major] == 0\n"; if($major > $#supported_majors) { $warn .= "| major $major > ".($#supported_majors)."\n"; } else { $warn .= "| \$supported_majors[major=$major] == 0\n"; } $warn .= "$_: \$supported_majors[$_] = $supported_majors[$_]\n" for (0..$#supported_majors); warn $warn; return 0; } if ($major == 4 and $self->get_config1('prohibit_v24')) { warn "Reading ID3v2-Tag version: v2.$major.$revision is prohibited via setting `prohibit_v24'\n"; return 0; } if ($revision != 0) { warn "Unknown ID3v2-Tag revision: v2.$major.$revision\nTrying to read tag\n"; } # check the flags my $flags={}; my $unknownFlag=0; my $i=0; foreach (split (//, unpack('b8',pack('v',$pflags)))) { $flags->{$flag_meaning[$major][$i]}=1 if $_; $i++; } $self->{version} = "$major.$revision"; $self->{major} = $major; $self->{revision} = $revision; # 2.3: includes extHeader, frames (as written), and the padding # excludes the header size (10) # 2.4: also excludes the footer (10 if present) $self->{tagsize} = un_syncsafe_4bytes substr $header, 6, 4; $self->{buggy_padding_size} = 0; # Fake so far $self->{flags} = $flags; $self->{footer_size} = ($self->{flags}->{footer} ? 10 : 0); return 1; } return 0; # no ID3v2-Tag found } # Reads the extended header and adapts the internal counter for the start of the # frame data. Ignores the rest of the ext. header (as CRC data). # v2.3: # Total size - 4 (4bytes, 6 or 10), flags (2bytes), padding size (4bytes), # OptionalCRC. # Flags: (subject to unsyncronization) # %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 # v2.4: Total size (4bytes, unsync), length of flags (=1), flags, Optional part. # 2.4 flags (with the corresponding "Optional part" format): # %0bcd0000 # b - Tag is an update # Flag data length $00 # c - CRC data present # Flag data length $05 # Total frame CRC 5 * %0xxxxxxx # d - Tag restrictions # Flag data length $01 # Restrictions %ppqrrstt sub read_ext_header { # XXXX in 2.3, it should be unsyncronized my $self = shift; my $ext_header = $self->{extheader}; # flags, padding and crc ignored at this time my $size; if ($self->{major}==4) { $size = un_syncsafe_4bytes substr $ext_header, 0, 4; } else { # 4 bytes extra for the size field itself $size = 4 + unpack("N", $ext_header); } $self->{frame_start} += $size; return 1; } sub extract_data { # Main sub for getting data from a frame my ($self, $data, $format, $noDecode, $arr) = @_; my ($rule, $found,$encoding, @result, $e); $encoding=0; $arr ||= 0; # 1: values only; 2: return array foreach $rule (@$format) { next if exists $rule->{v3name}; last if $rule->{optional} and not length $data; # get the data if ( exists $rule->{mlen} ) { # minlength, data is string ($found, $data) = ($data, ""); # Never with encoding } elsif ( $rule->{len} == 0 ) { # Till \0 if (exists $rule->{encoded} && ($encoding =~ /^[12]$/)) { ($found, $data) = ($data =~ /^((?:..)*?)(?:\0\0(.*)|\z)/s); } else { ($found, $data) = split /\x00/, $data, 2; } } elsif ($rule->{len} == -1) { # Till end ($found, $data) = ($data, ""); } else { $found = substr $data, 0,$rule->{len}; substr ($data, 0,$rule->{len}) = ''; } # was data found? unless (defined $found && $found ne "") { $found = ""; $found = $rule->{default} if exists $rule->{default}; } # work with data if ($rule->{name} eq "_encoding") { $encoding=unpack ("C", $found); push @result, 'encoding' unless $arr == 1; push @result, $encoding; } else { if (exists $rule->{encoded}) { # decode data if ( $encoding > 3 ) { warn "Encoding type '$encoding' not supported: found in $rule->{name}\n"; next; } elsif ($encoding and not $trustencoding) { warn "UTF encoding types disabled via MP3TAG_DECODE_UNICODE): found in $rule->{name}\n"; next; } elsif ($encoding) { # 0 = latin1 (effectively: unknown) # 1 = UTF-16 with BOM # 2 = UTF-16be, no BOM # 3 = UTF-8 require Encode; if ($decode_utf8) { $found = Encode::decode($dec_types[$encoding], $found); } elsif ($encoding < 3) { # Reencode in UTF-8 $found = Encode::decode($dec_types[$encoding], $found); $found = Encode::encode('UTF-8', $found); } } elsif (not $noDecode and $e = $self->botched_encoding) { require Encode; $found = Encode::decode( $e, $found ); } } $found = toNumber($found) if $rule->{isnum}; unless ($arr) { $found = $rule->{func}->($found) if exists $rule->{func}; unless (exists $rule->{data} || !defined $found) { $found =~ s/[\x00]+$//; # some progs pad text fields with \x00 $found =~ s![\x00]! / !g; # some progs use \x00 inside a text string to seperate text strings $found =~ s/ +$//; # no trailing spaces after the text } if (exists $rule->{re2}) { while (my ($pat, $rep) = each %{$rule->{re2}}) { $found =~ s/$pat/$rep/gis; } } } # store data push @result, $rule->{name} unless $arr == 1; push @result, $found; } } return {@result} unless $arr; return \@result; } sub botched_encoding ($) { my($self) = @_; return if $self->{fixed_encoding}; return unless my $enc = $self->get_config1('decode_encoding_v2'); # Don't recourse into TXXX[*] (inside-[] is encoded, # and frame_select() reads ALL TXXX frames...) local $self->{fixed_encoding} = 1; return unless $self->get_config1('ignore_trusted_encoding0_v2') or not $self->frame_select('TXXX', 'trusted_encoding0_v2'); $enc; } # Make editing in presence of decode_encoding_v2 more predictable: sub frames_need_fix_encoding ($) { my($self) = @_; return unless $self->botched_encoding; my($fname, $rule, %fix); for $fname (keys %{$self->{frames}}) { my $frame = $self->{frames}->{$fname}; next unless defined $frame; # XXXX Needed? my $fname4 = substr ($fname, 0, 4); my($result, $e) = $frame->{data}; my $format = get_format($fname4); next unless defined $format; foreach $rule (@$format) { next if exists $rule->{v3name}; # Otherwise _encoding is the first entry last if $rule->{name} ne '_encoding'; $e = unpack ("C", $result); } next unless defined $e and not $e; # The unfortunate "latin1" my $txts = $self->get_frame($fname, 'array_nokey'); my $raw_txts = $self->get_frame($fname, 'array_nodecode'); $fix{$fname} = $txts # Really need to fix: if join("\0\0\0", @$txts) ne join("\0\0\0", @$raw_txts); } return unless %fix; \%fix; } sub fix_frames_encoding ($) { # do not touch frames unless absolutely needed my($self) = @_; my($fix, $fname, $txt) = $self->frames_need_fix_encoding; while (($fname, $txt) = each %{$fix || {}}) { shift @$txt; # The 1st field is always _encoding; recalculate it $self->change_frame($fname, @$txt) or die; } $self->{fixed_encoding} = 1; $self->frame_select('TXXX', 'trusted_encoding0_v2', undef, 1) if $self->get_config1('id3v2_set_trusted_encoding0'); return($fix and keys %$fix); # Better be scalar context... } #Searches for a format string for a specified frame. format strings exist for #specific frames, or also for a group of frames. Specific format strings have #precedence over general ones. sub get_format { my $fname = shift; # to be quiet if called from supported_frames or what_data my $quiet = shift; my $fnamecopy = $fname; while ($fname ne "") { return $format{$fname} if exists $format{$fname}; substr ($fname, -1) =""; #delete last char } warn "Unknown Frame-Format found: $fnamecopy\n" unless defined $quiet; return undef; } #Reads the flags of a frame, and returns a hash with all flags as keys, and #0/1 as value for unset/set. sub check_flags { # how to detect unknown flags? my ($self, $flags)=@_; # %0abc0000 %0h00kmnp (this is byte1 byte2) my @flagmap4 = qw/data_length unsync encryption compression unknown_j unknown_i groupid 0 unknown_g unknown_f unknown_e unknown_d read_only file_preserv tag_preserv 0/; # %abc00000 %ijk00000 my @flagmap3 = qw/unknown_o unknown_n unknown_l unknown_m unknown_l groupid encryption compression unknown_h unknown_g unknown_f unknown_e unknown_d read_only file_preserv tag_preserv/; # flags were unpacked with 'n', so pack('v') gives byte2 byte1 # unpack('b16') puts more significant bits to the right, separately for # each byte; so the order is as specified above # 2.4: # %0abc0000 %0h00kmnp (this is byte1 byte2) # a - Tag alter preservation # b - File alter preservation # c - Read only # h - Grouping identity # k - Compression # m - Encryption # n - Unsynchronisation # p - Data length indicator # 2.3: # %abc00000 %ijk00000 # a - Tag alter preservation # b - File alter preservation # c - Read only # i - Compression # j - Encryption # k - Grouping identity my @flagmap = $self->{major} == 4 ? @flagmap4 : @flagmap3; my %flags = map { (shift @flagmap) => $_ } split (//, unpack('b16',pack('v',$flags))); $flags{unchanged}=1; return \%flags; } sub build_flags { my %flags=@_; my $flags=0; my %flagmap=(groupid=>32, encryption=>64, compression=>128, read_only=>8192, file_preserv=>16384, tag_preserv=>32768); while (my($flag,$set)=each %flags) { if ($set and exists $flagmap{$flag}) { $flags += $flagmap{$flag}; } elsif (not exists $flagmap{$flag}) { warn "Unknown flag during tag write: $flag\n"; } } return $flags; } sub DESTROY { } ################################## # # How to store frame formats? # # format{fname}=[{xxx},{xxx},...] # # array containing descriptions of the different parts of a frame. Each description # is a hash, which contains information, how to read the part. # # As Example: TCON # Text encoding $xx # Information <text string according to encoding # # TCON consist of two parts, so a array with two hashes is needed to describe this frame. # # A hash may contain the following keys. # # * len - says how many bytes to read for this part. 0 means read until \x00, -1 means # read until end of frame, any value > 0 specifies an exact length # * mlen - specifies a minimum length for the data, real length is until end of frame # (we assume it is not paired with encoding) # * name - the user sees this part of the frame under this name. If this part contains # binary data, the name should start with a _ # The name "_encoding" is reserved for the encoding part of a frame, which # is handled specifically to support encoding of text strings # (Is assumed to be the first entry, unless v3name) # * encoded - this part has to be encoded following to the encoding information # * func - a reference to a sub, which is called after the data is extracted. It gets # this data as argument and has to return some data, which is then returned # a result of this part # * isnum=1 - indicator that field stores a number as binary number # * re2 - hash with information for a replace: s/key/value/ # This is used after a call of `func' when reading a frame # * re2b - hash with information for a replace: s/key/value/ # This is used when adding a frame # * func_back - Translator function for add_frame (after re2b). # * data=1 - indicator that this part contains binary data # * default - default value, if data contains no information # # Name and exactly one of len or mlen are mandatory. # # TCON example: # # $format{TCON}=[{len=> 1, name=>"encoding", data=>1}, # {len=>-1, name=>"text", func=>\&TCON, re2=>{'\(RX\)'=>'Remix', '\(CR\)'=>'Cover'}] # ############################ sub toNumber { my $num = 0; $num = (256*$num)+unpack("C",$_) for split("",shift); return $num; } sub APIC { # MAX about 20 my $byte = shift; my $index = unpack ("C", $byte); my @pictypes = ("Other", "32x32 pixels 'file icon' (PNG only)", "Other file icon", "Cover (front)", "Cover (back)", "Leaflet page", "Media (e.g. label side of CD)", "Lead artist/lead performer/soloist", "Artist/performer", "Conductor", "Band/Orchestra", "Composer", "Lyricist/text writer", "Recording Location", "During recording", "During performance", "Movie/video screen capture", "A bright coloured fish", "Illustration", "Band/artist logotype", "Publisher/Studio logotype"); my $how = shift; if (defined $how) { # called by what_data die unless $how eq 1 and $byte eq 1; my $c=0; my %ret = map {$_, chr($c++)} @pictypes; return \%ret; } # called by extract_data return "Unknown... Error?" if $index > $#pictypes; return $pictypes[$index]; } sub COMR { # MAX about 9 my $data = shift; my $number = unpack ("C", $data); my @receivedas = ("Other","Standard CD album with other songs", "Compressed audio on CD","File over the Internet", "Stream over the Internet","As note sheets", "As note sheets in a book with other sheets", "Music on other media","Non-musical merchandise"); my $how = shift; if (defined $how) { die unless $how eq 1 and $data eq 1; my $c=0; my %ret = map {$_, chr($c++)} @receivedas; return \%ret; } return $number if ($number>8); return $receivedas[$number]; } sub PIC { # ID3v2.2 stores only 3 character Image format for pictures # and not mime type: Convert image format to mime type my $data = shift; my $how = shift; if (defined $how) { # called by what_data die unless $how eq 1 and $data eq 1; my %ret={}; return \%ret; } # called by extract_data if ($data eq "-->") { warn "ID3v2.2 PIC frame with link not supported\n"; $data = "text/plain"; } else { $data = "image/".(lc $data); } return $data; } sub TCON { my $data = shift; my $how = shift; if (defined $how) { # called by what_data die unless $how eq 1 and $data eq 1; my $c=0; my %ret = map {$_, "(".$c++.")"} @{MP3::Tag::ID3v1::genres()}; $ret{"_FREE"}=1; $ret{Remix}='(RX)'; $ret{Cover}="(CR)"; return \%ret; } # called by extract_data join ' / ', MP3::Tag::Implemenation::_massage_genres($data); } sub TCON_back { my $data = shift; $data = join ' / ', map MP3::Tag::Implemenation::_massage_genres($_, 'prefer_num'), split ' / ', $data; $data =~ s[(?:(?<=\(\d\))|(?<=\(\d\d\d\))|(?<=\((?:RX|CV|\d\d)\))) / ][]ig; $data =~ s[ / (?=\((?:RX|CV|\d{1,3})\))][]ig; $data; } sub TFLT { my $text = shift; my $how = shift; if (defined $how) { # called by what_data die unless $how eq 1 and $text eq 1; my %ret=("MPEG Audio"=>"MPG", "MPEG Audio MPEG 1/2 layer I"=>"MPG /1", "MPEG Audio MPEG 1/2 layer II"=>"MPG /2", "MPEG Audio MPEG 1/2 layer III"=>"MPG /3", "MPEG Audio MPEG 2.5"=>"MPG /2.5", "Transform-domain Weighted Interleave Vector Quantization"=>"VQF", "Pulse Code Modulated Audio"=>"PCM", "Advanced audio compression"=>"AAC", "_FREE"=>1, ); return \%ret; } #called by extract_data return "" if $text eq ""; $text =~ s/MPG/MPEG Audio/; $text =~ s/VQF/Transform-domain Weighted Interleave Vector Quantization/; $text =~ s/PCM/Pulse Code Modulated Audio/; $text =~ s/AAC/Advanced audio compression/; unless ($text =~ s!/1!MPEG 1/2 layer I!) { unless ($text =~ s!/2!MPEG 1/2 layer II!) { unless ($text =~ s!/3!MPEG 1/2 layer III!) { $text =~ s!/2\.5!MPEG 2.5!; } } } return $text; } sub TMED { #called by extract_data my $text = shift; return "" if $text eq ""; if ($text =~ /(?<!\() \( ([\w\/]*) \) /x) { my $found = $1; if ($found =~ s!DIG!Other digital Media! || $found =~ /DAT/ || $found =~ /DCC/ || $found =~ /DVD/ || $found =~ s!MD!MiniDisc! || $found =~ s!LD!Laserdisc!) { $found =~ s!/A!, Analog Transfer from Audio!; } elsif ($found =~ /CD/) { $found =~ s!/DD!, DDD!; $found =~ s!/AD!, ADD!; $found =~ s!/AA!, AAD!; } elsif ($found =~ s!ANA!Other analog Media!) { $found =~ s!/WAC!, Wax cylinder!; $found =~ s!/8CA!, 8-track tape cassette!; } elsif ($found =~ s!TT!Turntable records!) { $found =~ s!/33!, 33.33 rpm!; $found =~ s!/45!, 45 rpm!; $found =~ s!/71!, 71.29 rpm!; $found =~ s!/76!, 76.59 rpm!; $found =~ s!/78!, 78.26 rpm!; $found =~ s!/80!, 80 rpm!; } elsif ($found =~ s!TV!Television! || $found =~ s!VID!Video! || $found =~ s!RAD!Radio!) { $found =~ s!/!, !; } elsif ($found =~ s!TEL!Telephone!) { $found =~ s!/I!, ISDN!; } elsif ($found =~ s!REE!Reel! || $found =~ s!MC!MC (normal cassette)!) { $found =~ s!/4!, 4.75 cm/s (normal speed for a two sided cassette)!; $found =~ s!/9!, 9.5 cm/s!; $found =~ s!/19!, 19 cm/s!; $found =~ s!/38!, 38 cm/s!; $found =~ s!/76!, 76 cm/s!; $found =~ s!/I!, Type I cassette (ferric/normal)!; $found =~ s!/II!, Type II cassette (chrome)!; $found =~ s!/III!, Type III cassette (ferric chrome)!; $found =~ s!/IV!, Type IV cassette (metal)!; } $text =~ s/(?<!\() \( ([\w\/]*) \)/$found/x; } $text =~ s/\(\(/\(/g; $text =~ s/ / /g; return $text; } for my $elt ( qw( cddb_id cdindex_id ) ) { no strict 'refs'; *$elt = sub (;$) { my $self = shift; $self->frame_select('TXXX', $elt); } } BEGIN { # ID3v2.2, v2.3 are supported, v2.4 is very compatible... @supported_majors=(0,0,1,1,1); my $encoding ={len=>1, name=>"_encoding", data=>1}; my $text_enc ={len=>-1, name=>"Text", encoded=>1}; my $text ={len=>-1, name=>"Text"}; my $description ={len=>0, name=>"Description", encoded=>1}; my $url ={len=>-1, name=>"URL"}; my $url0 ={len=>0, name=>"URL"}; my $data ={len=>-1, name=>"_Data", data=>1}; my $language ={len=>3, name=>"Language"}; # this list contains all id3v2.2 frame names which can be matched directly to a id3v2.3 frame %v2names_to_v3 = ( BUF => "RBUF", CNT => "PCNT", COM => "COMM", CRA => "AENC", EQU => "EQUA", ETC => "ETCO", GEO => "GEOB", IPL => "IPLS", MCI => "MDCI", MLL => "MLLT", POP => "POPM", REV => "RVRB", RVA => "RVAD", SLT => "SYLT", STC => "SYTC", TFT => "TFLT", TMT => "TMED", UFI => "UFID", ULT => "USLT", TAL => "TALB", TBP => "TBPM", TCM => "TCOM", TCO => "TCON", TCR => "TCOP", TDA => "TDAT", TDY => "TDLY", TEN => "TENC", TIM => "TIME", TKE => "TKEY", TLA => "TLAN", TLE => "TLEN", TOA => "TOPE", TOF => "TOFN", TOL => "TOLY", TOR => "TORY", TOT => "TOAL", TP1 => "TPE1", TP2 => "TPE2", TP3 => "TPE3", TP4 => "TPE4", TPA => "TPOS", TPB => "TPUB", TRC => "TSRC", TRD => "TRDA", TRK => "TRCK", TSI => "TSIZ", TSS => "TSSE", TT1 => "TIT1", TT2 => "TIT2", TT3 => "TIT3", TXT => "TEXT", TXX => "TXXX", TYE => "TYER", WAF => "WOAF", WAR => "WOAR", WAS => "WOAS", WCM => "WCOM", WCP => "WCOP", WPB => "WPUB", WXX => "WXXX", ); %format = ( AENC => [$url0, {len=>2, name=>"Preview start", isnum=>1}, {len=>2, name=>"Preview length", isnum=>1}, $data], APIC => [$encoding, {len=>0, name=>"MIME type"}, {len=>1, name=>"Picture Type", small_max=>1, func=>\&APIC}, $description, $data], COMM => [$encoding, $language, $description, $text_enc], COMR => [$encoding, {len=>0, name=>"Price"}, {len=>8, name=>"Valid until"}, $url0, {len=>1, name=>"Received as", small_max=>1, func=>\&COMR}, {len=>0, name=>"Name of Seller", encoded=>1}, $description, {len=>0, name=>"MIME type", optional=>1}, {len=>-1, name=>"_Logo", data=>1, optional => 1}], CRM => [{v3name=>""},{len=>0, name=>"Owner ID"}, {len=>0, name=>"Content/explanation"}, $data], #v2.2 ENCR => [{len=>0, name=>"Owner ID"}, {len=>0, name=>"Method symbol"}, $data], #EQUA => [], #ETCO => [], GEOB => [$encoding, {len=>0, name=>"MIME type"}, {len=>0, name=>"Filename"}, $description, $data], GRID => [{len=>0, name=>"Owner"}, {len=>1, name=>"Symbol", isnum=>1}, $data], IPLS => [$encoding, $text_enc], # in 2.4 split into TMCL, TIPL LNK => [{len=>4, name=>"ID", func=>\&LNK}, {len=>0, name=>"URL"}, $text], LINK => [{len=>4, name=>"ID"}, {len=>0, name=>"URL"}, $text], MCDI => [$data], #MLLT => [], OWNE => [$encoding, {len=>0, name=>"Price payed"}, {len=>0, name=>"Date of purchase"}, $text_enc], PCNT => [{mlen=>4, name=>"Text", isnum=>1}], PIC => [{v3name => "APIC"}, $encoding, {len=>3, name=>"Image Format", func=>\&PIC}, {len=>1, name=>"Picture Type", func=>\&APIC}, $description, $data], #v2.2 POPM => [{len=>0, name=>"URL"},{len=>1, name=>"Rating", isnum=>1}, {mlen=>4, name=>"Counter", isnum=>1, optional=>1}], #POSS => [], PRIV => [{len=>0, name=>"Text"}, $data], RBUF => [{len=>3, name=>"Buffer size", isnum=>1}, {len=>1, name=>"Embedded info flag", isnum=>1}, {len=>4, name=>"Offset to next tag", isnum=>1, optional=>1}], #RVAD => [], RVRB => [{len=>2, name=>"Reverb left (ms)", isnum=>1}, {len=>2, name=>"Reverb right (ms)", isnum=>1}, {len=>1, name=>"Reverb bounces (left)", isnum=>1}, {len=>1, name=>"Reverb bounces (right)", isnum=>1}, {len=>1, name=>"Reverb feedback (left to left)", isnum=>1}, {len=>1, name=>"Reverb feedback (left to right)", isnum=>1}, {len=>1, name=>"Reverb feedback (right to right)", isnum=>1}, {len=>1, name=>"Reverb feedback (right to left)", isnum=>1}, {len=>1, name=>"Premix left to right", isnum=>1}, {len=>1, name=>"Premix right to left", isnum=>1},], SYTC => [{len=>1, name=>"Time Stamp Format", isnum=>1}, $data], #SYLT => [], T => [$encoding, $text_enc], TCON => [$encoding, {%$text_enc, func=>\&TCON, func_back => \&TCON_back, re2=>{'\(RX\)'=>'Remix', '\(CR\)'=>'Cover'}, # re2b=>{'\bRemix\b'=>'(RX)', '\bCover\b'=>'(CR)'} }], TCOP => [$encoding, {%$text_enc, re2 => {'^(?!\Z)'=>'(C) '}, re2b => {'^(Copyright\b)?\s*(\(C\)\s*)?' => ''}}], # TDRC => [$encoding, $text_enc, data => 1], TFLT => [$encoding, {%$text_enc, func=>\&TFLT}], TIPL => [{v3name => "IPLS"}, $encoding, $text_enc], TMCL => [{v3name => "IPLS"}, $encoding, $text_enc], TMED => [$encoding, {%$text_enc, func=>\&TMED}], # no what_data support TXXX => [$encoding, $description, $text_enc], UFID => [{%$description, name=>"Text"}, $data], USER => [$encoding, $language, $text_enc], USLT => [$encoding, $language, $description, $text_enc], W => [$url], WXXX => [$encoding, $description, $url], ); %long_names = ( AENC => "Audio encryption", APIC => "Attached picture", COMM => "Comments", COMR => "Commercial frame", ENCR => "Encryption method registration", EQUA => "Equalization", ETCO => "Event timing codes", GEOB => "General encapsulated object", GRID => "Group identification registration", IPLS => "Involved people list", LINK => "Linked information", MCDI => "Music CD identifier", MLLT => "MPEG location lookup table", OWNE => "Ownership frame", PRIV => "Private frame", PCNT => "Play counter", POPM => "Popularimeter", POSS => "Position synchronisation frame", RBUF => "Recommended buffer size", RVAD => "Relative volume adjustment", RVRB => "Reverb", SYLT => "Synchronized lyric/text", SYTC => "Synchronized tempo codes", TALB => "Album/Movie/Show title", TBPM => "BPM (beats per minute)", TCOM => "Composer", TCON => "Content type", TCOP => "Copyright message", TDAT => "Date", TDLY => "Playlist delay", TDRC => "Recording time", TENC => "Encoded by", TEXT => "Lyricist/Text writer", TFLT => "File type", TIME => "Time", 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", TOAL => "Original album/movie/show title", TOFN => "Original filename", TOLY => "Original lyricist(s)/text writer(s)", TOPE => "Original artist(s)/performer(s)", TORY => "Original release year", 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", TPUB => "Publisher", TRCK => "Track number/Position in set", TRDA => "Recording dates", TRSN => "Internet radio station name", TRSO => "Internet radio station owner", TSIZ => "Size", TSRC => "ISRC (international standard recording code)", TSSE => "Software/Hardware and settings used for encoding", TYER => "Year", TXXX => "User defined text information frame", UFID => "Unique file identifier", USER => "Terms of use", USLT => "Unsychronized lyric/text transcription", WCOM => "Commercial information", WCOP => "Copyright/Legal information", WOAF => "Official audio file webpage", WOAR => "Official artist/performer webpage", WOAS => "Official audio source webpage", WORS => "Official internet radio station homepage", WPAY => "Payment", WPUB => "Publishers official webpage", WXXX => "User defined URL link frame", # ID3v2.2 frames which cannot linked directly to a ID3v2.3 frame CRM => "Encrypted meta frame", PIC => "Attached picture", LNK => "Linked information", ); # these fields have restricted input (FRAMEfield) %res_inp=( "APICPicture Type" => \&APIC, "TCONText" => \&TCON, # Actually, has func_back()... "TFLTText" => \&TFLT, "COMRReceived as" => \&COMR, ); # have small_max %is_small_int = ("APICPicture Type" => 1, "COMRReceived as" => 1); for my $k (keys %res_inp) { my %h = %{ $field_map{$k} = $res_inp{$k}->(1,1) }; # Assign+make copy delete $h{_FREE}; %h = reverse %h; $field_map_back{$k} = \%h; } # Watch for 'lable': $field_map{'APICPicture Type'}{'Media (e.g. lable side of CD)'} = $field_map{'APICPicture Type'}{'Media (e.g. label side of CD)'}; %back_splt = qw(POPM 1); # Have numbers at end %embedded_Descr = qw(GEOD 1 COMR 1); # Have descr which is not leading } =pod =back =head1 BUGS Writing C<v2.4>-layout tags is not supported. Additionally, one should keep in mind that C<v2.3> and C<v2.4> have differences in two areas: =over 4 =item * layout of information in the byte stream (in other words, in a file considered as a string) is different; =item * semantic of frames is extended in C<v2.4> - more frames are defined, and more frame flags are defined too. =back MP3::Tag does not even try to I<write> frames in C<v2.4>-layout. However, when I<reading> the frames, MP3::Tag does not assume any restriction on the semantic of frames - it allows all the semantical extensions defined in C<v2.4> even for C<v2.3> (and, probably, for C<v2.2>) layout. C<[*]> (I expect, any sane program would do the same...) Likewise, when writing frames, there is no restriction imposed on semantic. If user specifies a frame the meaning of which is defined only in C<v2.4>, we would happily write it even when we use C<v2.3> layout. Same for frame flags. (And given the assumption C<[*]>, this is a correct thing to do...) =head1 SEE ALSO L<MP3::Tag>, L<MP3::Tag::ID3v1>, L<MP3::Tag::ID3v2_Data> ID3v2 standard - http://www.id3.org L<http://www.id3.org/id3v2-00>, L<http://www.id3.org/d3v2.3.0>, L<http://www.id3.org/id3v2.4.0-structure>, L<http://www.id3.org/id3v2.4.0-frames>, L<http://id3lib.sourceforge.net/id3/id3v2.4.0-changes.txt>. =head1 COPYRIGHT Copyright (c) 2000-2008 Thomas Geffert, Ilya Zakharevich. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the Artistic License, distributed with Perl. =cut 1; ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/MP3/Tag/ID3v2_Data.pod�������������������������������������������������������������0000700�0000000�0000000�00000016203�11304131316�013564� 0����������������������������������������������������������������������������������������������������ustar �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� =head1 NAME MP3::Tag::ID3v2_Data - get_frame() data format and supported frames =head1 SYNOPSIS $mp3 = MP3::Tag->new($filename); $mp3->get_tags(); $id3v2 = $mp3->{ID3v2} if exists $mp3->{id3v2}; ($info, $long) = $id3v2->get_frame($id); # or ($info, $long) = $id3v2->get_frame($id, 'raw'); =head1 DESCRIPTION This document describes how to use the results of the get_frame function of MP3::Tag::ID3v2, thus the data format of frames retrieved with MP3::Tag::ID3v2::get_frame(). It contains also a list of all supported ID3v2-Frames. =head2 get_frame() ($info, $long) = $id3v2->get_frame($id); # or ($info, $long) = $id3v2->get_frame($id, 'raw'); $id has to be a name of a frame like "APIC". For more variants of calling see L<get_frame()|MP3::Tag::ID3v2>. The names of all frames found in a tag can be retrieved with the L<get_frame_ids()|MP3::Tag::ID3v2> function. =head2 Using the returned data In the ID3v2.3 specifications 73 frames are defined, which can contain very different information. That means that get_frame returns the information of different frames also in different ways. =over 4 =item Simple Frames A lot of the tags contain only a text string and encoding information. If you call ($info, $long) = $id3v2->get_frame($id) for such a frame, $info will contain the text string and $long will contain the english name of the frame. Example: get_frame("TIT2"); # returns ("Birdhouse In Your Soul", "Title/songname/content description") =item Complex Frames For more complex frames the returned $info is a reference to a hash, where each entry of the hash decribes a part of the information found in the frame. The key of a hash entry contains the name of this part, the according value contains the information itself. Example: get_frame("APIC"); # returns ( { "Description" => "Flood", "MIME Type" => "/image/jpeg", "Picture Type" => "Cover (front)", "_Data" => "..data of jpeg picture (binary).." }, "Attached Picture"); =item Other Frames Some frames are not supported at the moment, ie the data found in the frame is not returned in a descriptive way. But you can read the data of this frames (and also of all other frames too) in raw mode. Then the complete data field of the frame is returned, without any modifications. This means that the returned data will be almost binary data. Example: get_frame("TIT2", 'raw'); # returns ("\x00Birdhouse In Your Soul", "Title/songname/content description") =back The frames which (in addition to C<Text>/C<URL>) contain only C<Description> and C<Language> fields are in some intermediate position between "simple" and "complex" frames. They can be handled very similarly to "simple" frames by using "long names", such as C<COMM[description]> or C<COMM(LANG)[description]>, and the corresponding "quick" API such as frame_select(). =head2 List of Simple Frames Following Frames are supported and return a single string (text). In the List you can find the frame IDs and the long names of the frames as returned by $id3v2->get_frame(): =over 4 =item IPLS : Involved people list =item MCDI : Music CD identifier =item PCNT : Play counter =item TALB : Album/Movie/Show title =item TBPM : BPM (beats per minute) =item TCOM : Composer =item TCON : Content type =item TCOP : Copyright message =item TDAT : Date =item TDLY : Playlist delay =item TDRC : Recording time =item TENC : Encoded by =item TEXT : Lyricist/Text writer =item TFLT : File type =item TIME : Time =item TIPL : Involved people list =item TIT1 : Content group description =item TIT2 : Title/songname/content description =item TIT3 : Subtitle/Description refinement =item TKEY : Initial key =item TLAN : Language(s) =item TLEN : Length =item TMCL : Musician credits list =item TMED : Media type =item TOAL : Original album/movie/show title =item TOFN : Original filename =item TOLY : Original lyricist(s)/text writer(s) =item TOPE : Original artist(s)/performer(s) =item TORY : Original release year =item TOWN : File owner/licensee =item TPE1 : Lead performer(s)/Soloist(s) =item TPE2 : Band/orchestra/accompaniment =item TPE3 : Conductor/performer refinement =item TPE4 : Interpreted, remixed, or otherwise modified by =item TPOS : Part of a set =item TPUB : Publisher =item TRCK : Track number/Position in set =item TRDA : Recording dates =item TRSN : Internet radio station name =item TRSO : Internet radio station owner =item TSIZ : Size =item TSRC : ISRC (international standard recording code) =item TSSE : Software/Hardware and settings used for encoding =item TYER : Year =item WCOM : Commercial information =item WCOP : Copyright/Legal information =item WOAF : Official audio file webpage =item WOAR : Official artist/performer webpage =item WOAS : Official audio source webpage =item WORS : Official internet radio station homepage =item WPAY : Payment =item WPUB : Publishers official webpage =back =head2 List of Complex Frames Following frames are supported and return a reference to a hash. The list shows which keys can be found in the returned hash: =over 4 =item AENC : Audio encryption Keys: URL, Preview start, Preview length, _Data =item APIC : Attached picture Keys: MIME type, Picture Type, Description, _Data =item COMM : Comments Keys: Language, Description, Text =item COMR : Commercial frame Keys: Price, Valid until, URL, Received as, Name of Seller, Description, MIME type, _Logo =item ENCR : Encryption method registration Keys: Owner ID, Method symbol, _Data =item GEOB : General encapsulated object Keys: MIME type, Filename, Description, _Data =item GRID : Group identification registration Keys: Owner, Symbol, _Data =item LINK : Linked information Keys: ID, URL, Text =item OWNE : Ownership frame Keys: Price payed, Date of purchase, Text =item POPM : Popularimeter Keys: URL, Rating, Counter =item PRIV : Private frame Keys: Text, _Data =item RBUF : Recommended buffer size Keys: Buffer size, Embedded info flag, Offset to next tag =item RVRB : Reverb Keys: Reverb left (ms), Reverb right (ms), Reverb bounces (left), Reverb bounces (right), Reverb feedback (left to left), Reverb feedback (left to right), Reverb feedback (right to right), Reverb feedback (right to left), Premix left to right, Premix right to left =item SYTC : Synchronized tempo codes Keys: Time Stamp Format, _Data =item TXXX : User defined text information frame Keys: Description, Text =item UFID : Unique file identifier Keys: Text, _Data =item USER : Terms of use Keys: Language, Text =item USLT : Unsychronized lyric/text transcription Keys: Language, Description, Text =item WXXX : User defined URL link frame Keys: Description, URL =back =head2 List of Other Frames Following frames are only supported in raw mode: =over 4 =item CRM : Encrypted meta frame =item EQUA : Equalization =item ETCO : Event timing codes =item LNK : Linked information =item MLLT : MPEG location lookup table =item PIC : Attached picture =item POSS : Position synchronisation frame =item RVAD : Relative volume adjustment =item SYLT : Synchronized lyric/text =back =head1 SEE ALSO L<MP3::Tag>, L<MP3::Tag::ID3v2> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/MP3/Tag/ImageExifTool.pm�����������������������������������������������������������0000700�0000000�0000000�00000005307�11201726512�014341� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package MP3::Tag::ImageExifTool; use strict; use File::Basename; #use File::Spec; use vars qw /$VERSION @ISA/; $VERSION="0.01"; @ISA = 'MP3::Tag::__hasparent'; =pod =head1 NAME MP3::Tag::ImageExifTool - extract size info from image files via L<Image::Size|Image::Size>. =head1 SYNOPSIS my $db = MP3::Tag::ImageExifTool->new($filename); # Name of multimedia file see L<MP3::Tag> =head1 DESCRIPTION MP3::Tag::ImageExifTool is designed to be called from the MP3::Tag module. It implements width(), height() and mime_type() methods (sizes in pixels). They return C<undef> if C<Image::Size> is not available, or does not return valid data. =cut # Constructor sub new_with_parent { my ($class, $f, $p, $e, %seen, @cue) = (shift, shift, shift); $f = $f->filename if ref $f; bless [$f], $class; } sub new { my ($class, $f) = (shift, shift); $class->new_with_parent($f, undef, @_); } # Destructor sub DESTROY {} sub __info ($) { my $self = shift; unless (defined $self->[1]) { my $v = eval { require Image::ExifTool; Image::ExifTool->new()->ImageInfo($self->[0], '-id3:*') }; # How to detect errors? $self->[1] = $v->{Error} ? '' : $v; } return $self->[1]; } my %tr = qw( mime_type MIMEType year Date width ImageWidth height ImageHeight bit_depth BitDepth ); for my $elt ( qw( title track artist album year genre comment mime_type width height ) ) { my $n = ($tr{$elt} or ucfirst $elt); my $is_genre = ($elt eq 'genre'); my $r = sub ($) { my $info = shift()->__info; return unless $info; my $v = $info->{$n}; $v =~ s/^None$// if $is_genre and $v; return $v; }; no strict 'refs'; *$elt = $r; } sub bit_depth ($) { my $info = shift()->__info; return unless $info; $info->{BitsPerSample} || $info->{Depth} || $info->{BitDepth} } sub field ($$) { my $info = shift()->__info; return unless $info; $info->{shift()} } sub _duration ($) { my $info = shift()->__info; return unless $info; my($d, $dd) = $info->{Duration}; if (defined $d and $d =~ /\d/) { $dd = 1; return $d if $d =~ /^\d*(\.\d*)?$/; } # Probably this is already covered by Duration? No, it is usually rounded... my($c, $r, $r1) = map $info->{$_}, qw(FrameCount VideoFrameRate FrameRate); unless (defined $c and $r ||= $r1) { # $d usually contains rounded value return $1*3600 + $2*60 + $3 if $dd and $d =~ /^(\d+):(\d+):(\d+(\.\d*)?)$/; return $1*60 + $2 if $dd and $d =~ /^(\d+):(\d+(\.\d*)?)$/; return; } $r = 30/1.001 if $r =~ /^29.97\d*^/; $r = 24/1.001 if $r =~ /^23.9(7\d*|8)$/; $c/$r } sub img_type ($) { my $self = shift; my $t = $self->mime_type; return uc $1 if $t =~ m(^image/(.*)); return; } 1; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/MP3/Tag/ImageSize.pm���������������������������������������������������������������0000700�0000000�0000000�00000003034�11201235342�013511� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package MP3::Tag::ImageSize; use strict; use File::Basename; #use File::Spec; use vars qw /$VERSION @ISA/; $VERSION="0.01"; @ISA = 'MP3::Tag::__hasparent'; =pod =head1 NAME MP3::Tag::ImageSize - extract size info from image files via L<Image::Size|Image::Size>. =head1 SYNOPSIS my $db = MP3::Tag::ImageSize->new($filename); # Name of multimedia file see L<MP3::Tag> =head1 DESCRIPTION MP3::Tag::ImageSize is designed to be called from the MP3::Tag module. It implements width(), height() and mime_type() methods (sizes in pixels). They return C<undef> if C<Image::Size> is not available, or does not return valid data. =head1 SEE ALSO L<Image::Size>, L<MP3::Tag> =cut # Constructor sub new_with_parent { my ($class, $f, $p, $e, %seen, @cue) = (shift, shift, shift); $f = $f->filename if ref $f; bless [$f], $class; } sub new { my ($class, $f) = (shift, shift); $class->new_with_parent($f, undef, @_); } # Destructor sub DESTROY {} my @fields = qw( 0 0 width height img_type mime_type ); for my $elt ( 2, 3, 4, 5 ) { # i_bitdepth my $r = sub (;$) { my $self = shift; unless ($self->[1]) { my ($w, $h, $t) = eval { require Image::Size; Image::Size::imgsize($self->[0]) }; defined $w or @$self[1..4] = (1,undef,undef,undef), return; my $tt = "image/\L$t"; @$self[1..5] = (1, $w, $h, $t, $tt); } return $self->[$elt]; }; no strict 'refs'; *{$fields[$elt]} = $r; } for my $elt ( qw( title track artist album year genre comment ) ) { no strict 'refs'; *$elt = sub (;$) { return }; } 1; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/MP3/Tag/Inf.pm���������������������������������������������������������������������0000700�0000000�0000000�00000007130�11171241532�012355� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package MP3::Tag::Inf; use strict; use vars qw /$VERSION @ISA/; $VERSION="1.00"; @ISA = 'MP3::Tag::__hasparent'; =pod =head1 NAME MP3::Tag::Inf - Module for parsing F<.inf> files associated with music tracks. =head1 SYNOPSIS my $mp3inf = MP3::Tag::Inf->new($filename); # Name of MP3 or .INF file # or an MP3::Tag::File object ($title, $artist, $album, $year, $comment, $track) = $mp3inf->parse(); see L<MP3::Tag> =head1 DESCRIPTION MP3::Tag::Inf is designed to be called from the MP3::Tag module. It parses the content of F<.inf> file (created, e.g., by cdda2wav). =over 4 =cut # Constructor sub new_with_parent { my ($class, $filename, $parent) = @_; my $self = bless {parent => $parent}, $class; $filename = $filename->filename if ref $filename; my $ext_rex = $self->get_config('extension')->[0]; $filename =~ s/($ext_rex)|$/.inf/; # replace extension return unless -f $filename; $self->{filename} = $filename; $self; } # Destructor sub DESTROY {} =item parse() ($title, $artist, $album, $year, $comment, $track) = $mp3inf->parse($what); parse_filename() extracts information about artist, title, track number, album and year from the F<.inf> file. $what is optional; it maybe title, track, artist, album, year or comment. If $what is defined parse() will return only this element. As a side effect of this call, $mp3inf->{info} is set to the hash reference with the content of particular elements of the F<.inf> file. Typically present are the following fields: CDINDEX_DISCID CDDB_DISCID MCN ISRC Albumperformer Performer Albumtitle Tracktitle Tracknumber Trackstart Tracklength Pre-emphasis Channels Copy_permitted Endianess Index The following fields are also recognized: Year Trackcomment =cut sub return_parsed { my ($self,$what) = @_; if (defined $what) { return $self->{parsed}{album} if $what =~/^al/i; return $self->{parsed}{artist} if $what =~/^a/i; return $self->{parsed}{track} if $what =~/^tr/i; return $self->{parsed}{year} if $what =~/^y/i; return $self->{parsed}{genre} if $what =~/^g/i; if ($what =~/^cddb_id/i) { my $o = $self->{parsed}{Cddb_discid}; $o =~ s/^0x//i if $o; return $o; } return $self->{parsed}{Cdindex_discid} if $what =~/^cdindex_id/i; return $self->{parsed}{comment}if $what =~/^c/i; return $self->{parsed}{title}; } return $self->{parsed} unless wantarray; return map $self->{parsed}{$_} , qw(title artist album year comment track); } sub parse { my ($self,$what) = @_; $self->return_parsed($what) if exists $self->{parsed}; local *IN; open IN, "< $self->{filename}" or die "Error opening `$self->{filename}': $!"; my $e; if ($e = $self->get_config('decode_encoding_inf') and $e->[0]) { eval "binmode IN, ':encoding($e->[0])'"; # old binmode won't compile... } my ($line, %info); for $line (<IN>) { $self->{info}{ucfirst lc $1} = $2 if $line =~ /^(\S+)\s*=\s*['"]?(.*?)['"]?\s*$/; } close IN or die "Error closing `$self->{filename}': $!"; my %parsed; @parsed{ qw( title artist album year comment track Cddb_discid Cdindex_discid ) } = @{ $self->{info} }{ qw( Tracktitle Performer Albumtitle Year Trackcomment Tracknumber Cddb_discid Cdindex_discid) }; $parsed{artist} = $self->{info}{Albumperformer} unless defined $parsed{artist}; $self->{parsed} = \%parsed; $self->return_parsed($what); } for my $elt ( qw( title track artist album comment year genre cddb_id cdindex_id ) ) { no strict 'refs'; *$elt = sub (;$) { my $self = shift; $self->parse($elt, @_); } } 1; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/MP3/Tag/LastResort.pm��������������������������������������������������������������0000700�0000000�0000000�00000001552�11304126510�013741� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package MP3::Tag::LastResort; use strict; use vars qw /$VERSION @ISA/; $VERSION="1.00"; @ISA = 'MP3::Tag::__hasparent'; =pod =head1 NAME MP3::Tag::LastResort - Module for using other fields to fill autoinfo fields. =head1 SYNOPSIS my $mp3extra = MP3::Tag::LastResort::new_with_parent($filename, $parent); $comment = $mp3inf->comment(); see L<MP3::Tag> =head1 DESCRIPTION MP3::Tag::LastResort is designed to be called from the MP3::Tag module. It uses the artist_collection() as comment() if comment() is not otherwise defined. =cut # Constructor sub new_with_parent { my ($class, $filename, $parent) = @_; bless {parent => $parent}, $class; } # Destructor sub DESTROY {} for my $elt ( qw( title track artist album year genre ) ) { no strict 'refs'; *$elt = sub (;$) { return }; } sub comment { shift->{parent}->artist_collection() } 1; ������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/MP3/Tag/ParseData.pm���������������������������������������������������������������0000700�0000000�0000000�00000017515�11316544506�013525� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package MP3::Tag::ParseData; use strict; use vars qw /$VERSION @ISA/; $VERSION="1.00"; @ISA = 'MP3::Tag::__hasparent'; =pod =head1 NAME MP3::Tag::ParseData - Module for parsing arbitrary data associated with music files. =head1 SYNOPSIS # parses the file name according to one of the patterns: $mp3->config('parse_data', ['i', '%f', '%t - %n - %a.%e', '%t - %y.%e']); $title = $mp3->title; see L<MP3::Tag> =head1 DESCRIPTION MP3::Tag::ParseData is designed to be called from the MP3::Tag module. Each option of configuration item C<parse_data> should be of the form C<[$flag, $string, $pattern1, ...]>. For each of the option, patterns of the option are matched agains the $string of the option, until one of them succeeds. The information obtained from later options takes precedence over the information obtained from earlier ones. The meaning of the patterns is the same as for parse() or parse_rex() methods of C<MP3::Tag>. Since the default for C<parse_data> is empty, by default this handler has no effect. $flag is split into 1-character-long flags (unknown flags are ignored): =over =item C<i> the string-to-parse is interpolated first; =item C<f> the string-to-parse is interpreted as the name of the file to read; =item C<F> added to C<f>, makes it non-fatal if the file does not exist; =item C<B> the file should be read in C<binary> mode; =item C<n> the string-to-parse is interpreted as collection of lines, one per track; =item C<l> the string-to-parse is interpreted as collection of lines, and the first matched is chosen; =item C<I> the resulting string is interpolated before parsing. =item C<b> Do not strip the leading and trailing blanks. (With output to file, the output is performed in binary mode too.) =item C<R> the patterns are considered as regular expressions. =item C<m> one of the patterns must match. =item C<o>, C<O>, C<D> With C<o> or C<O> interpret the pattern as a name of file to output parse-data to. With C<O> the name of output file is interpolated. When C<D> is present, intermediate directories are created. =item C<z> Do not ignore a field even if the result is a 0-length string. =back Unless C<b> option is given, the resulting values have starting and trailing whitespace trimmed. (Actually, split()ing into lines is done using the configuration item C<parse_split>; it defaults to C<"\n">.) If the configuration item C<parse_data> has multiple options, the $strings which are interpolated will use information set by preceding options; similarly, any interolated option may use information obtained by other handlers - even if these handers are later in the pecking order than C<MP3::Tag::ParseData> (which by default is the first handler). For example, with ['i', '%t' => '%t (%y)'], ['i', '%t' => '%t - %c'] and a local CDDB file which identifies title to C<'Merry old - another interpretation (1905)'>, the first field will interpolate C<'%t'> into this title, then will split it into the year and the rest. The second field will split the rest into a title-proper and comment. Note that one can use fields of the form ['mz', 'This is a forced title' => '%t'] to force particular values for parts of the MP3 tag. The usual methods C<artist>, C<title>, C<album>, C<comment>, C<year>, C<track>, C<year> can be used to access the results of the parse. It is possible to set individual id3v2 frames; use %{TIT1} or some such. Setting to an empty string deletes the frame if config parameter C<id3v2_frame_empty_ok> is false (the default value). Setting ID3v2 frames uses the same translation rules as select_id3v2_frame_by_descr(). =head2 SEE ALSO The flags C<i f F B l m I b> are identical to flags of the method interpolate_with_flags() of MP3::Tag (see L<MP3::Tag/"interpolate_with_flags">). Essentially, the other flags (C<R m o O D z>) are applied to the result of calling the latter method. =cut # Constructor sub new_with_parent { my ($class, $filename, $parent) = @_; $filename = $filename->filename if ref $filename; bless {filename => $filename, parent => $parent}, $class; } # Destructor sub DESTROY {} sub parse_one { my ($self, $in) = @_; my @patterns = @$in; # Apply shift to a copy, not original... my $flags = shift @patterns; my $data = shift @patterns; my @data = $self->{parent}->interpolate_with_flags($data, $flags); my $res; my @opatterns = @patterns; if ($flags =~ /[oO]/) { @patterns = map $self->{parent}->interpolate($_), @patterns if $flags =~ /O/; return unless length $data[0] or $flags =~ /z/; for my $file (@patterns) { if ($flags =~ /D/ and $file =~ m,(.*)[/\\],s) { require File::Path; File::Path::mkpath($1); } open OUT, "> $file" or die "open(`$file') for write: $!"; if ($flags =~ /b/) { binmode OUT; } else { my $e; if ($e = $self->get_config('encode_encoding_files') and $e->[0]) { eval "binmode OUT, ':encoding($e->[0])'"; # old binmode won't compile... } } local ($/, $,) = ('', ''); print OUT $data[0]; close OUT or die "close(`$file') for write: $!"; } return; } if ($flags =~ /R/) { @patterns = map $self->{parent}->parse_rex_prepare($_), @patterns; } else { @patterns = map $self->{parent}->parse_prepare($_), @patterns; } for $data (@data) { my $pattern; for $pattern (@patterns) { last if $res = $self->{parent}->parse_rex_match($pattern, $data); } last if $res; } { local $" = "' `"; die "Pattern(s) `@opatterns' did not succeed vs `@data'" if $flags =~ /m/ and not $res; } my $k; for $k (keys %$res) { unless ($flags =~ /b/) { $res->{$k} =~ s/^\s+//; $res->{$k} =~ s/\s+$//; } delete $res->{$k} unless length $res->{$k} or $flags =~ /z/; } return unless $res and keys %$res; return $res; } # XXX Two decisions: which entries can access results of which ones, # and which entries overwrite which ones; the user can reverse one of them # by sorting config('parse_data') in the opposite order; but not both. # Only practice can show whether our choice is correct... How to customize? sub parse { # Later recipies can access results of earlier ones. my ($self,$what) = @_; return $self->{parsed}->{$what} # Recalculate during recursive calls if not $self->{parsing} and exists $self->{parsed}; # Do not recalc after finish my $data = $self->get_config('parse_data'); return unless $data and @$data; my $parsing = $self->{parsing}; local $self->{parsing}; my (%res, $d, $c); for $d (@$data) { $c++; $self->{parsing} = $c; # Protect against recursion: later $d can access results of earlier ones last if $parsing and $parsing <= $c; my $res = $self->parse_one($d); # warn "Failure: [@$d]\n" unless $res; # Set user-scratch space data immediately for my $k (keys %$res) { if ($k eq 'year') { # Do nothing } elsif ($k =~ /^U(\d{1,2})$/) { $self->{parent}->set_user($1, delete $res->{$k}) } elsif (0 and $k =~ /^\w{4}(\d{2,})?$/) { if (length $res->{$k} or $self->get_config('id3v2_frame_empty_ok')->[0]) { $self->{parent}->set_id3v2_frame($k, delete $res->{$k}) } else { delete $res->{$k}; $self->{parent}->set_id3v2_frame($k); # delete } } elsif ($k =~ /^\w{4}(\d{2,}|(?:\(([^()]*(?:\([^()]+\)[^()]*)*)\))?(?:\[(\\.|[^]\\]*)\])?)$/) { my $r = delete $res->{$k}; $r = undef unless length $r or $self->get_config('id3v2_frame_empty_ok')->[0]; if (defined $r or $self->{parent}->_get_tag('ID3v2')) { $self->{parent}->select_id3v2_frame_by_descr($k, $r); } } } # later ones overwrite earlier %res = (%res, %$res) if $res; } $self->{parsed} = \%res; # return unless keys %res; return $self->{parsed}->{$what}; } for my $elt ( qw( title track artist album comment year genre ) ) { no strict 'refs'; *$elt = sub (;$) { my $self = shift; $self->parse($elt, @_); } } 1; �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/MP3/Tag.pm�������������������������������������������������������������������������0000700�0000000�0000000�00000340506�11417071736�011662� 0����������������������������������������������������������������������������������������������������ustar �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� package MP3::Tag; # Copyright (c) 2000-2004 Thomas Geffert. All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the Artistic License, distributed # with Perl. ################ # # provides a general interface for different modules, which can read tags # # at the moment MP3::Tag works with MP3::Tag::ID3v1 and MP3::Tag::ID3v2 use strict; { package MP3::Tag::__hasparent; sub parent_ok { my $self = shift; $self->{parent} and $self->{parent}->proxy_ok; } sub get_config { my $self = shift; return $MP3::Tag::config{shift()} unless $self->parent_ok; return $self->{parent}->get_config(@_); } *get_config1 = \&MP3::Tag::Implemenation::get_config1; } use MP3::Tag::ID3v1; use MP3::Tag::ID3v2; use MP3::Tag::File; use MP3::Tag::Inf; use MP3::Tag::CDDB_File; use MP3::Tag::Cue; use MP3::Tag::ParseData; use MP3::Tag::ImageSize; use MP3::Tag::ImageExifTool; use MP3::Tag::LastResort; use vars qw/$VERSION @ISA/; $VERSION="1.13"; @ISA = qw( MP3::Tag::User MP3::Tag::Site MP3::Tag::Vendor MP3::Tag::Implemenation ); # Make overridable *config = \%MP3::Tag::Implemenation::config; package MP3::Tag::Implemenation; # XXXX Old mispring... use vars qw/%config/; %config = ( autoinfo => [qw( ParseData ID3v2 ID3v1 ImageExifTool CDDB_File Inf Cue ImageSize filename LastResort )], cddb_files => [qw(audio.cddb cddb.out cddb.in)], v2title => [qw(TIT1 TIT2 TIT3)], composer => ['TCOM|a'], performer => ['TXXX[TPE1]|TPE1|a'], extension => ['\.(?!\d+\b)\w{1,4}$'], parse_data => [], parse_split => ["\n"], encoded_v1_fits => [0], parse_filename_ignore_case => [1], parse_filename_merge_dots => [1], parse_join => ['; '], year_is_timestamp => [1], comment_remove_date => [0], id3v2_frame_empty_ok => [0], id3v2_minpadding => [128], id3v2_sizemult => [512], id3v2_shrink => [0], id3v2_mergepadding => [0], id3v23_unsync_size_w => [0], id3v23_unsync => [1], parse_minmatch => [0], update_length => [1], default_language => ['XXX'], default_descr_c => [''], person_frames => [qw{ TEXT TCOM TXXX[TPE1] TPE1 TPE3 TOPE TOLY TMCL TIPL TENC TXXX[person-file-by] }], id3v2_frames_autofill => [qw{ TXXX[MCDI-fulltoc] 1 TXXX[cddb_id] 0 TXXX[cdindex_id] 0 }], id3v2_set_trusted_encoding0 => [1], id3v2_fix_encoding_on_edit => [1], name_for_field_normalization => ['%{composer}'], local_cfg_file => ['~/.mp3tagprc'], extra_config_keys => [], is_writable => ['writable_by_extension'], # ExifTool says: ID3 may be in MP3/MPEG/AIFF/OGG/FLAC/APE/RealAudio (MPC). writable_extensions => [qw(mp3 mp2 id3 tag ogg mpg mpeg mp4 aiff flac ape ram mpc)], ); { my %e; for my $t (qw(V1 V2 FILENAME FILES INF CDDB_FILE CUE)) { $e{$t} = $ENV{"MP3TAG_DECODE_${t}_DEFAULT"}; $e{$t} = $ENV{MP3TAG_DECODE_DEFAULT} unless defined $e{$t}; $config{"decode_encoding_" . lc $t} = [$e{$t}] if $e{$t}; } $e{eV1} = $ENV{MP3TAG_ENCODE_V1_DEFAULT}; $e{eV1} = $ENV{MP3TAG_ENCODE_DEFAULT} unless defined $e{eV1}; $e{eV1} = $e{V1} unless defined $e{eV1}; $config{encode_encoding_v1} = [$e{eV1}] if $e{eV1}; $e{eF} = $ENV{MP3TAG_ENCODE_FILES_DEFAULT}; $e{eF} = $ENV{MP3TAG_ENCODE_DEFAULT} unless defined $e{eF}; $e{eF} = $e{FILES} unless defined $e{eF}; $config{encode_encoding_files} = [$e{eF}] if $e{eF}; } =pod =head1 NAME MP3::Tag - Module for reading tags of MP3 audio files =head1 SYNOPSIS use MP3::Tag; $mp3 = MP3::Tag->new($filename); # get some information about the file in the easiest way ($title, $track, $artist, $album, $comment, $year, $genre) = $mp3->autoinfo(); # Or: $comment = $mp3->comment(); $dedicated_to = $mp3->select_id3v2_frame_by_descr('COMM(fre,fra,eng,#0)[dedicated to]'); $mp3->title_set('New title'); # Edit in-memory copy $mp3->select_id3v2_frame_by_descr('TALB', 'New album name'); # Edit in memory $mp3->select_id3v2_frame_by_descr('RBUF', $n1, $n2, $n3); # Edit in memory $mp3->update_tags({year => 1866}); # Edit in-memory, and commit to file $mp3->update_tags(); # Commit to file The following low-level access code is discouraged; better use title() etc., title_set() etc., update_tags(), select_id3v2_frame_by_descr() etc. methods on the wrapper $mp3: # scan file for existing tags $mp3->get_tags; if (exists $mp3->{ID3v1}) { # read some information from the tag $id3v1 = $mp3->{ID3v1}; # $id3v1 is only a shortcut for $mp3->{ID3v1} print $id3v1->title; # change the tag contents $id3v1->all("Song","Artist","Album",2001,"Comment",10,"Top 40"); $id3v1->write_tag; } if (exists $mp3->{ID3v2}) { # read some information from the tag ($name, $info) = $mp3->{ID3v2}->get_frame("TIT2"); # delete the tag completely from the file $mp3->{ID3v2}->remove_tag; } else { # create a new tag $mp3->new_tag("ID3v2"); $mp3->{ID3v2}->add_frame("TALB", "Album title"); $mp3->{ID3v2}->write_tag; } $mp3->close(); Please consider using the script F<mp3info2>; it allows simple access to most features of this module via command-line options; see L<mp3info2>. =head1 AUTHORS Thomas Geffert, thg@users.sourceforge.net Ilya Zakharevich, ilyaz@cpan.org =head1 DESCRIPTION C<MP3::Tag> is a wrapper module to read different tags of mp3 files. It provides an easy way to access the functions of separate modules which do the handling of reading/writing the tags itself. At the moment MP3::Tag::ID3v1 and MP3::Tag::ID3v2 are supported for read and write; MP3::Tag::ImageExifTool, MP3::Tag::Inf, MP3::Tag::CDDB_File, MP3::Tag::File, MP3::Tag::Cue, MP3::Tag::ImageSize, MP3::Tag::LastResort are supported for read access (the information obtained by L<Image::ExifTool|Image::ExifTool> (if present), parsing CDDB files, F<.inf> file, the filename, and F<.cue> file, and obtained via L<Image::Size|Image::Size>) (if present). =over 4 =item new() $mp3 = MP3::Tag->new($filename); Creates a mp3-object, which can be used to retrieve/set different tags. =cut sub rel2abs ($) { shift; if (eval {require File::Spec; File::Spec->can('rel2abs')}) { File::Spec->rel2abs(shift); } else { # require Cwd; # Cwd::abs_path(shift); shift; } } sub new { my $class = shift; my $filename = shift; my $mp3data; my $self = {}; bless $self, $class; my $proxy = MP3::Tag::__proxy->new($self); if (-f $filename or -c $filename) { $mp3data = MP3::Tag::File->new_with_parent($filename, $proxy); } # later it should hopefully possible to support also http/ftp sources # with a MP3::Tag::Net module or something like that if ($mp3data) { %$self = (filename => $mp3data, ofilename => $filename, abs_filename => $class->rel2abs($filename), __proxy => $proxy); return $self; } return undef; } { # Proxy class: to have only one place where to weaken/localize the reference # $obj->[0] must be settable to the handle (not needed if weakening succeeds) package MP3::Tag::__proxy; use vars qw/$AUTOLOAD/; my $skip_weaken = $ENV{MP3TAG_SKIP_WEAKEN}; sub new { my ($class, $handle) = (shift,shift); my $self = bless [$handle], $class; #warn("weaken() failed, falling back"), return bless [], $class if $skip_weaken or not eval {require Scalar::Util; Scalar::Util::weaken($self->[0]); 1}; $self; } sub DESTROY {} sub proxy_ok { shift->[0] } sub AUTOLOAD { my $self = shift; die "local_proxy not initialized" unless $self->[0]; (my $meth = $AUTOLOAD) =~ s/.*:://; my $smeth = $self->[0]->can($meth); die "proxy can't find the method $meth" unless $smeth; unshift @_, $self->[0]; goto &$smeth; } } sub proxy_ok { 1 } # We can always be a proxy to ourselves... ;-) =pod =item get_tags() [old name: getTags() . The old name is still available, but its use is not advised] @tags = $mp3->get_tags; Checks which tags can be found in the mp3-object. It returns a list @tags which contains strings identifying the found tags, like "ID3v1", "ID3v2", "Inf", or "CDDB_File" (the last but one if the F<.inf> information file with the same basename as MP3 file is found). Each found tag can then be accessed with $mp3->{tagname} , where tagname is a string returned by get_tags ; Use the information found in L<MP3::Tag::ID3v1>, L<MP3::Tag::ID3v2> and L<MP3::Tag::Inf>, L<MP3::Tag::CDDB_File>, L<MP3::Tag::Cue> to see what you can do with the tags. =cut ################ tag subs sub get_tags { my $self = shift; return @{$self->{gottags}} if exists $self->{gottags}; my (@IDs, $id); # Will not create a reference loop local $self->{__proxy}[0] = $self unless $self->{__proxy}[0] or $ENV{MP3TAG_TEST_WEAKEN}; for $id (qw(ParseData ID3v2 ID3v1 ImageExifTool Inf CDDB_File Cue ImageSize LastResort)) { my $ref = "MP3::Tag::$id"->new_with_parent($self->{filename}, $self->{__proxy}); next unless defined $ref; $self->{$id} = $ref; push @IDs, $id; } $self->{gottags} = [@IDs]; return @IDs; } sub _get_tag { my $self = shift; $self->{shift()}; } # keep old name for a while *getTags = \&get_tags; =item new_fake $obj = MP3::Tag->new_fake(); This method produces a "fake" MP3::Tag object which behaves as an MP3 file without tags. Give a TRUE optional argument if you want to set some properties of this object. =cut sub new_fake { my ($class, $settable) = (shift, shift); my %h = (gottags => []); my $self = bless \%h, $class; if ($settable) { $h{__proxy} = MP3::Tag::__proxy->new($self); $h{ParseData} = MP3::Tag::ParseData->new_with_parent(undef, $h{__proxy}); } \%h; } =pod =item new_tag() [old name: newTag() . The old name is still available, but its use is not advised] $tag = $mp3->new_tag($tagname); Creates a new tag of the given type $tagname. You can access it then with $mp3->{$tagname}. At the moment ID3v1 and ID3v2 are supported as tagname. Returns an tag-object: $mp3->{$tagname}. =cut sub new_tag { my $self = shift; my $whichTag = shift; if ($whichTag =~ /1/) { $self->{ID3v1}= MP3::Tag::ID3v1->new($self->{filename},1); return $self->{ID3v1}; } elsif ($whichTag =~ /2/) { $self->{ID3v2}= MP3::Tag::ID3v2->new($self->{filename},1); return $self->{ID3v2}; } } # keep old name for a while *newTag = \&new_tag; #only as a shortcut to {filename}->close to explicitly close a file =pod =item close() $mp3->close; You can use close() to explicitly close a file. Normally this is done automatically by the module, so that you do not need to do this. =cut sub close { my $self=shift; $self->{filename}->close; } =pod =item genres() $allgenres = $mp3->genres; $genreName = $mp3->genres($genreID); $genreID = $mp3->genres($genreName); Returns a list of all genres (reference to an array), or the according name or id to a given id or name. This function is only a shortcut to MP3::Tag::ID3v1->genres. This can be also called as MP3::Tag->genres; =cut sub genres { # returns all genres, or if a parameter is given, the according genre my $self=shift; return MP3::Tag::ID3v1::genres(shift); } =pod =item autoinfo() ($title, $track, $artist, $album, $comment, $year, $genre) = $mp3->autoinfo(); $info_hashref = $mp3->autoinfo(); autoinfo() returns information about the title, track number, artist, album name, the file comment, the year and genre. It can get this information from an ID3v1-tag, an ID3v2-tag, from CDDB file, from F<.inf>-file, and from the filename itself. It will as default first try to find a ID3v2-tag to get this information. If this cannot be found it tries to find a ID3v1-tag, then to read an CDDB file, an F<.inf>-file, and if these are not present either, it will use the filename to retrieve the title, track number, artist, album name. The comment, year and genre are found differently, via the C<comment>, C<year> and C<genre> methods. You can change the order of lookup with the config() command. autoinfo() returns an array with the information or a hashref. The hash has four keys 'title', 'track', 'artist' and 'album' where the information is stored. If comment, year or genre are found, the hash will have keys 'comment' and/or 'year' and/or 'genre' too. If an optional argument C<'from'> is given, the returned values (title, track number, artist, album name, the file comment, the year and genre) are array references with the first element being the value, the second the tag (C<ID3v2> or C<ID3v1> or C<Inf> or C<CDDB_File> or C<Cue> or C<filename>) from which it is taken. (Deprecated name 'song' can be used instead of 'title' as well.) =cut sub autoinfo() { my ($self, $from) = (shift, shift); my (@out, %out); for my $elt ( qw( title track artist album comment year genre ) ) { my $out = $self->$elt($from); if (wantarray) { push @out, $out; } elsif (defined $out and length $out) { $out{$elt} = $out; } } $out{song} = $out{title} if exists $out{title}; return wantarray ? @out : \%out; } =item comment() $comment = $mp3->comment(); # empty string unless found comment() returns comment information. It can get this information from an ID3v1-tag, or an ID3v2-tag (from C<COMM> frame with empty <short> field), CDDB file (from C<EXTD> or C<EXTT> fields), or F<.inf>-file (from C<Trackcomment> field). It will as default first try to find a ID3v2-tag to get this information. If no comment is found there, it tries to find it in a ID3v1-tag, if none present, will try CDDB file, then F<.inf>-file. It returns an empty string if no comment is found. You can change the order of this with the config() command. If an optional argument C<'from'> is given, returns an array reference with the first element being the value, the second the tag (ID3v2 or ID3v1) from which the value is taken. =cut =item year() $year = $mp3->year(); # empty string unless found year() returns the year information. It can get this information from an ID3v2-tag, or ID3v1-tag, or F<.inf>-file, or filename. It will as default first try to find a ID3v2-tag to get this information. If no year is found there, it tries to find it in a ID3v1-tag, if none present, will try CDDB file, then F<.inf>-file, then by parsing the file name. It returns an empty string if no year is found. You can change the order of this with the config() command. If an optional argument C<'from'> is given, returns an array reference with the first element being the value, the second the tag (ID3v2 or ID3v1 or filename) from which the value is taken. =item comment_collection(), comment_track(), title_track(). artist_collection() access the corresponding fields returned by parse() method of CDDB_File. =item cddb_id(), cdindex_id() access the corresponding methods of C<ID3v2>, C<Inf> or C<CDDB_File>. =item title_set(), artist_set(), album_set(), year_set(), comment_set(), track_set(), genre_set() $mp3->title_set($newtitle, [$force_id3v2]); Set the corresponding value in ID3v1 tag, and, if the value does not fit, or force_id3v2 is TRUE, in the ID3v2 tag. Changes are made to in-memory copy only. To propagate to the file, use update_tags() or similar methods. =item track1() Same as track(), but strips trailing info: if track() returns C<3/12> (which means track 3 of 12), this method returns C<3>. =item track2() Returns the second part of track number (compare with track1()). =item track0() Same as track1(), but pads with leading 0s to width of track2(); takes an optional argument (default is 2) giving the pad width in absense of track2(). =item disk1(), disk2() Same as track1(), track2(), but with disk-number instead of track-number (stored in C<TPOS> ID3v2 frame). =item disk_alphanum() Same as disk1(), but encodes a non-empty result as a letter (1 maps to C<a>, 2 to C<b>, etc). If number of disks is more than 26, falls back to numeric (e.g, C<3/888> will be encoded as C<003>). =cut sub track1 ($) { my $r = track(@_); $r =~ s(/.*)()s; $r; } sub track2 ($) { my $r = track(@_); return '' unless $r =~ s(^.*?/)()s; $r; } sub track0 ($) { my $self = shift; my $d = (@_ ? shift() : 2); my $r = $self->track(); return '' unless defined $r; (my $r1 = $r) =~ s(/.*)()s; $r = 'a' x $d unless $r =~ s(^.*?/)()s; my $l = length $r; sprintf "%0${l}d", $r1; } sub disk1 ($) { my $self = shift; my $r = $self->select_id3v2_frame('TPOS'); return '' unless defined $r; $r =~ s(/.*)()s; $r; } sub disk2 ($) { my $self = shift; my $r = $self->select_id3v2_frame('TPOS'); return '' unless defined $r; return '' unless $r =~ s(^.*?/)()s; $r; } sub disk_alphanum ($) { my $self = shift; my $r = $self->select_id3v2_frame('TPOS'); return '' unless defined $r; (my $r1 = $r) =~ s(/.*)()s; $r = $r1 unless $r =~ s(^.*?/)()s; # max(disk2, disk1) return chr(ord('a') - 1 + $r1) if $r <= 26; my $l = length $r; sprintf "%0${l}d", $r1; } my %ignore_0length = qw(ID3v1 1 CDDB_File 1 Inf 1 Cue 1 ImageSize 1 ImageExifTool 1); sub auto_field($;$) { my ($self, $elt, $from) = (shift, shift, shift); local $self->{__proxy}[0] = $self unless $self->{__proxy}[0] or $ENV{MP3TAG_TEST_WEAKEN}; my $parts = $self->get_config($elt) || $self->get_config('autoinfo'); $self->get_tags; my $do_can = ($elt =~ /^(cd\w+_id|height|width|bit_depth|mime_type|img_type|_duration)$/); foreach my $part (@$parts) { next unless exists $self->{$part}; next if $do_can and not $self->{$part}->can($elt); next unless defined (my $out = $self->{$part}->$elt()); # Ignore 0-length answers from ID3v1, ImageExifTool, CDDB_File, Cue, ImageSize, and Inf next if not length $out and $ignore_0length{$part}; # These return '' return [$out, $part] if $from; return $out; } return ''; } for my $elt ( qw( title track artist album comment year genre ) ) { no strict 'refs'; *$elt = sub (;$) { my $self = shift; my $translate = ($self->get_config("translate_$elt") || [])->[0] || sub {$_[1]}; return &$translate($self, $self->auto_field($elt, @_)); } } my %hide_meth = qw(mime_type _mime_type); for my $elt ( qw( cddb_id cdindex_id height width bit_depth mime_type img_type _duration ) ) { no strict 'refs'; *{$hide_meth{$elt} || $elt} = sub (;$) { my $self = shift; return $self->auto_field($elt, @_); } } for my $elt ( qw( comment_collection comment_track title_track artist_collection ) ) { no strict 'refs'; my ($tr) = ($elt =~ /^(\w+)_/); *$elt = sub (;$) { my $self = shift; local $self->{__proxy}[0] = $self unless $self->{__proxy}[0] or $ENV{MP3TAG_TEST_WEAKEN}; $self->get_tags; return unless exists $self->{CDDB_File}; my $v = $self->{CDDB_File}->parse($elt); return unless defined $v; my $translate = ($self->get_config("translate_$tr") || [])->[0] || sub {$_[1]}; return &$translate( $self, $v ); } } for my $elt ( qw(title artist album year comment track genre) ) { no strict 'refs'; *{"${elt}_set"} = sub ($$;$) { my ($mp3, $val, $force2) = (shift, shift, shift); $mp3->get_tags; $mp3->new_tag("ID3v1") unless exists $mp3->{ID3v1}; $mp3->{ID3v1}->$elt( $val ); return 1 if not $force2 and $mp3->{ID3v1}->fits_tag({$elt => $val}) and not exists $mp3->{ID3v2}; $mp3->new_tag("ID3v2") unless exists $mp3->{ID3v2}; $mp3->{ID3v2}->$elt( $val ); } } sub aspect_ratio ($) { my $self = shift; my ($w, $h) = ($self->width, $self->height); return unless $w and $h; $w/$h; } sub aspect_ratio_inverted ($) { my $r = shift->aspect_ratio or return; 1/$r; } sub aspect_ratio3 ($) { my $r = shift->aspect_ratio(); $r ? sprintf '%.3f', $r : $r; } =item mime_type( [$lazy] ) Returns the MIME type as a string. Returns C<application/octet-stream> for unrecognized types. If not $lazy, will try harder (via ExifTool, if needed). =item mime_Pretype( [$lazy] ) Returns uppercased first component of MIME type. =cut sub mime_Pretype ($;$) { my $r = shift->mime_type(shift); $r =~ s,/.*,,s; ucfirst lc $r } sub mime_type ($;$) { # _mime_type goes thru auto_field 'mime_type' my ($self, $lazy) = (shift, shift); $self->get_tags; my $h = $self->{header}; my $t = $h && $self->_Data_to_MIME($h, 1); return $t if $t; return((!$lazy && $self->_mime_type()) || 'application/octet-stream'); } =item genre() $genre = $mp3->genre(); # empty string unless found genre() returns the genre string. It can get this information from an ID3v2-tag or ID3v1-tag. It will as default first try to find a ID3v2-tag to get this information. If no genre is found there, it tries to find it in a ID3v1-tag, if none present, will try F<.inf>-file, It returns an empty string if no genre is found. You can change the order of this with the config() command. If an optional argument C<'from'> is given, returns an array reference with the first element being the value, the second the tag (ID3v2 or ID3v1 or filename) from which the value is taken. =item composer() $composer = $mp3->composer(); # empty string unless found composer() returns the composer. By default, it gets from ID3v2 tag, otherwise returns artist. You can change the inspected fields with the config() command. Subject to normalization via C<translate_composer> or C<translate_person> configuration variables. =item performer() $performer = $mp3->performer(); # empty string unless found performer() returns the main performer. By default, it gets from ID3v2 tag C<TXXX[TPE1]>, otherwise from ID3v2 tag C<TPE1>, otherwise returns artist. You can change the inspected fields with the config() command. Subject to normalization via C<translate_performer> or C<translate_person> configuration variables. =cut for my $elt ( qw( composer performer ) ) { no strict 'refs'; *$elt = sub (;$) { my $self = shift; my $translate = ($self->get_config("translate_$elt") || $self->get_config("translate_person") || [])->[0] || sub {$_[1]}; my $fields = ($self->get_config($elt))->[0]; return &$translate($self, $self->interpolate("%{$fields}")); } } =item config MP3::Tag->config(item => value1, value2...); # Set options globally $mp3->config(item => value1, value2...); # Set object options When object options are first time set or get, the global options are propagated into object options. (So if global options are changed later, these changes are not inherited.) Possible items are: =over =item autoinfo Configure the order in which ID3v1-, ID3v2-tag and filename are used by autoinfo. The default is C<ParseData, ID3v2, ID3v1, ImageExifTool, CDDB_File, Inf, Cue, ImageSize, filename, LastResort>. Options can be elements of the default list. The order in which they are given to config also sets the order how they are used by autoinfo. If an option is not present, it will not be used by autoinfo (and other auto-methods if the specific overriding config command were not issued). $mp3->config("autoinfo","ID3v1","ID3v2","filename"); sets the order to check first ID3v1, then ID3v2 and at last the Filename $mp3->config("autoinfo","ID3v1","filename","ID3v2"); sets the order to check first ID3v1, then the Filename and last ID3v2. As the filename will be always present ID3v2 will here never be checked. $mp3->config("autoinfo","ID3v1","ID3v2"); sets the order to check first ID3v1, then ID3v2. The filename will never be used. =item title artist album year comment track genre Configure the order in which ID3v1- and ID3v2-tag are used by the corresponding methods (e.g., comment()). Options can be the same as for C<autoinfo>. The order in which they are given to config also sets the order how they are used by comment(). If an option is not present, then C<autoinfo> option will be used instead. =item extension regular expression to match the file extension (including the dot). The default is to match 1..4 letter extensions which are not numbers. =item composer string to put into C<%{}> to interpolate to get the composer. Default is C<'TCOM|a'>. =item performer string to put into C<%{}> to interpolate to get the main performer. Default is C<'TXXX[TPE1]|TPE1|a'>. =item parse_data the data used by L<MP3::Tag::ParseData> handler; each option is an array reference of the form C<[$flag, $string, $pattern1, ...]>. All the options are processed in the following way: patterns are matched against $string until one of them succeeds; the information obtained from later options takes precedence over the information obtained from earlier ones. =item parse_split The regular expression to split the data when parsing with C<n> or C<l> flags. =item parse_filename_ignore_case If true (default), calling parse() and parse_rex() with match-filename escapes (such as C<%=D>) matches case-insensitively. =item parse_filename_merge_dots If true (default), calling parse() and parse_rex() with match-filename escapes (such as C<%=D>) does not distinguish a dot and many consequent dots. =item parse_join string to put between multiple occurences of a tag in a parse pattern; defaults to C<'; '>. E.g., parsing C<'1988-1992, Homer (LP)'> with pattern C<'%c, %a (%c)'> results in comment set to C<'1988-1992; LP'> with the default value of C<parse_join>. =item v2title Configure the elements of ID3v2-tag which are used by ID3v2::title(). Options can be "TIT1", "TIT2", "TIT3"; the present values are combined. If an option is not present, it will not be used by ID3v2::title(). =item cddb_files List of files to look for in the directory of MP3 file to get CDDB info. =item year_is_timestamp If TRUE (default) parse() will match complicated timestamps against C<%y>; for example, C<2001-10-23--30,2002-02-28> is a range from 23rd to 30th of October 2001, I<and> 28th of February of 2002. According to ISO, C<--> can be replaced by C</> as well. For convenience, the leading 0 can be omited from the fields which ISO requires to be 2-digit. =item comment_remove_date When extracting the date from comment fields, remove the recognized portion even if it is human readable (e.g., C<Recorded on 2014-3-23>) if TRUE. Current default: FALSE. =item default_language The language to use to select ID3v2 frames, and to choose C<COMM> ID3v2 frame accessed in comment() method (default is 'XXX'; if not C<XXX>, this should be lowercase 3-letter abbreviation according to ISO-639-2). =item default_descr_c The description field used to choose the C<COMM> ID3v2 frame accessed in comment() method. Defaults to C<''>. =item id3v2_frame_empty_ok When setting the individual id3v2 frames via ParseData, do not remove the frames set to an empty string. Default 0 (empty means 'remove'). =item id3v2_minpadding Minimal padding to reserve after ID3v2 tag when writing (default 128), =item id3v2_sizemult Additionally to C<id3v2_minpadding>, insert padding to make file size multiple of this when writing ID3v2 tag (default 512), Should be power of 2. =item id3v2_shrink If TRUE, when writing ID3v2 tag, shrink the file if needed (default FALSE). =item id3v2_mergepadding If TRUE, when writing ID3v2 tag, consider the 0-bytes following the ID3v2 header as writable space for the tag (default FALSE). =item update_length If TRUE, when writing ID3v2 tag, create a C<TLEN> tag if the duration is known (as it is after calling methods like C<total_secs>, or interpolation the duration value). If this field is 2 or more, force creation of ID3v2 tag by update_tags() if the duration is known. =item translate_* FALSE, or a subroutine used to munch a field C<*> (out of C<title track artist album comment year genre comment_collection comment_track title_track artist_collection person>) to some "normalized" form. Takes two arguments: the MP3::Tag object, and the current value of the field. The second argument may also have the form C<[value, handler]>, where C<handler> is the string indentifying the handler which returned the value. =item short_person Similar to C<translate_person>, but the intent is for this subroutine to translate a personal name field to a shortest "normalized" form. =item person_frames list of ID3v2 frames subject to normalization via C<translate_person> handler; current default is C<TEXT TCOM TXXX[TPE1] TPE1 TPE3 TOPE TOLY TMCL TIPL TENC TXXX[person-file-by]>. Used by select_id3v2_frame_by_descr(), frame_translate(), frames_translate(). =item id3v2_missing_fatal If TRUE, interpolating ID3v2 frames (e.g., by C<%{TCOM}>) when the ID3v2 tags is missing is a fatal error. If false (default), in such cases interpolation results in an empty string. =item id3v2_recalculate If TRUE, interpolating the whole ID3v2 tag (by C<%{ID3v2}>) will recalculate the tag even if its contents is not modified. =item parse_minmatch may be 0, 1, or a list of C<%>-escapes (matching any string) which should matched non-greedily by parse() and friends. E.g., parsing C<'Adagio - Andante - Piano Sonata'> via C<'%t - %l'> gives different results for the settings 0 and 1; note that greediness of C<%l> does not matter, thus the value of 1 is equivalent for the value of C<t> for this particular pattern. =item id3v23_unsync_size_w Old experimental flag to test why ITunes refuses to handle unsyncronized tags (does not help, see L<id3v23_unsync>). The idea was that version 2.3 of the standard is not clear about frame size field, whether it is the size of the frame after unsyncronization, or not. We assume that this size is one before unsyncronization (as in v2.2). Setting this value to 1 will assume another interpretation (as in v2.4) for write. =item id3v23_unsync Some broken MP3 players (e.g., ITunes, at least up to v6) refuse to handle unsyncronized (i.e., written as the standard requires it) tags; they may need this to be set to FALSE. Default: TRUE. (Some details: by definition, MP3 files should contain combinations of bytes C<FF F*> or C<FF E*> only at the start of audio frames ("syncronization" points). ID3v2 standards take this into account, and supports storing raw tag data in a format which does not contain these combinations of bytes [via "unsyncronization"]. Itunes etc do not only emit broken MP3 files [which cause severe hiccups in players which do not know how to skip ID3v2 tags, as most settop DVD players], they also refuse to read ID3v2 tags written in a correct, unsyncronized, format.) (Note also that the issue of syncronization is also applicable to ID3v1 tags; however, since this data is near the end of the file, many players are able to recognize that the syncronization points in ID3v1 tag cannot start a valid frame, since there is not enough data to read; some other players would hiccup anyway if ID3v1 contains these combinations of bytes...) =item encoded_v1_fits If FALSE (default), data containing "high bit characters" is considered to not fit ID3v1 tag if one of the following conditions hold: =over 4 =item 1. C<encode_encoding_v1> is set (so the resulting ID3v1 tag is not standard-complying, thus ambiguous without ID3v2), or =item 2. C<encode_encoding_v1> is not set, but C<decode_encoding_v1> is set (thus read+write operation is not idempotent for ID3v1 tag). =back With the default setting, these problems are resolved as far as (re)encoding of ID3v2 tag is non-ambiguous (which holds with the default settings for ID3v2 encodeing). =item decode_encoding_v1 =item encode_encoding_v1 =item decode_encoding_v2 =item decode_encoding_filename =item decode_encoding_inf =item decode_encoding_cddb_file =item decode_encoding_cue =item decode_encoding_files =item encode_encoding_files Encodings of C<ID3v1>, non-Unicode frames of C<ID3v2>, filenames, external files, F<.inf> files, C<CDDB> files, F<.cue> files, and user-specified files correspondingly. The value of 0 means "latin1". The default values for C<decode_encoding_*> are set from the corresponding C<MP3TAG_DECODE_*_DEFAULT> environment variable (here C<*> stands for the uppercased last component of the name); if this variable is not set, from C<MP3TAG_DECODE_DEFAULT>. Likewise, the default value for C<encode_encoding_v1> is set from C<MP3TAG_ENCODE_V1_DEFAULT> or C<MP3TAG_ENCODE_DEFAULT>; if not present, from the value for C<decode_encoding_v1>; similarly for C<encode_encoding_files>. Note that C<decode_encoding_v2> has no "encode" pair; it may also be disabled per tag via effects of C<ignore_trusted_encoding0_v2> and the corresponding frame C<TXXX[trusted_encoding0_v2]> in the tag. One should also keep in mind that the ID3v1 standard requires the encoding to be "latin1" (so does not store the encoding anywhere); this does not make a lot of sense, and a lot of effort of this module is spend to fix this unfortunate flaw. See L<"Problems with ID3 format">. =item ignore_trusted_encoding0_v2 If FALSE (default), and the frame C<TXXX[trusted_encoding0_v2]> is set to TRUE, the setting of C<decode_encoding_v2> is ignored. =item id3v2_set_trusted_encoding0 If TRUE (default), and frames are converted from the given C<decode_encoding_v2> to a standard-conforming encoding, a frame C<TXXX[trusted_encoding0_v2]> with a TRUE value is added. [The purpose is to make multi-step update in presence of C<decode_encoding_v2> possible; with C<id3v2_set_trusted_encoding0> TRUE, and C<ignore_trusted_encoding0_v2> FALSE (both are default values), editing of tags can be idempotent.] =item id3v2_fix_encoding_on_write If TRUE and C<decode_encoding_v2> is defined, the ID3v2 frames are converted to standard-conforming encodings on write. The default is FALSE. =item id3v2_fix_encoding_on_edit If TRUE (default) and C<decode_encoding_v2> is defined (and not disabled via a frame C<TXXX[trusted_encoding0_v2]> and the setting C<ignore_trusted_encoding0_v2>), a CYA action is performed when an edit may result in a confusion. More precise, adding an ID3v2 frame which is I<essentially> affected by C<decode_encoding_v2> would convert other frames to a standard-conforming encoding (and would set C<TXXX[trusted_encoding0_v2]> if required by C<id3v2_set_trusted_encoding0>). Recall that the added frames are always encoded in standard-conformant way; the action above avoids mixing non-standard-conformant frames with standard-conformant frames. Such a mix could not be cleared up by setting C<decode_encoding_v2>! One should also keep in mind that this does not affect frames which contain characters above C<0x255>; such frames are always written in Unicode, thus are not affected by C<decode_encoding_v2>. =item id3v2_frames_autofill Hash of suggested ID3v2 frames to autogenerate basing on extra information available; keys are frame descriptors (such as C<TXXX[cddb_id]>), values indicate whether ID3v2 tag should be created if it was not present. This variable is inspected by the method C<id3v2_frames_autofill>, which is not called automatically when the tag is accessed, but may be called by scripts using the module. The default is to force creation of tag for C<TXXX[MCDI-fulltoc]> frame, and do not force creation for C<TXXX[cddb_id]> and C<TXXX[cdindex_id]>. =item local_cfg_file Name of configuration file read at startup by the method parse_cfg(); is C<~>-substituted; defaults to F<~/.mp3tagprc>. =item prohibit_v24 If FALSE (default), reading of ID3v2.4 is allowed (it is not fully supported, but most things work acceptably). =item write_v24 If FALSE (default), writing of ID3v2.4 is prohibited (it is not fully supported; allow on your own risk). =item name_for_field_normalization interpolation of this string is used as a person name to normalize title-like fields. Defaults to C<%{composer}>. =item extra_config_keys List of extra config keys (default is empty); setting these would not cause warnings, and would not affect operation of C<MP3::Tag>. Applications using this module may add to this list to allow their configuration by the same means as configuration of C<MP3::Tag>. =item is_writable Contains a boolean value, or a method name and argument list to call whether the tag may be added to the file. Defaults to writable_by_extension(). =item writable_extensions Contains a list of extensions (case insensitive) for which the tag may be added to the file. Current default is C<mp3 mp2 id3 tag ogg mpg mpeg mp4 aiff flac ape ram mpc> (extracted from L<ExifTool> docs; may be tuned later). =item * Later there will be probably more things to configure. =back =cut my $conf_rex; sub config { my ($self, $item, @options) = @_; $item = lc $item; my $config = ref $self ? ($self->{config} ||= {%config}) : \%config; my @known = qw(autoinfo title artist album year comment track genre v2title cddb_files force_interpolate parse_data parse_split composer performer default_language default_descr_c update_length id3v2_fix_encoding_on_write id3v2_fix_encoding_on_edit extra_config_keys parse_join parse_filename_ignore_case encoded_v1_fits parse_filename_merge_dots year_is_timestamp comment_remove_date extension id3v2_missing_fatal id3v2_frame_empty_ok id3v2_minpadding id3v2_sizemult id3v2_shrink id3v2_mergepadding person_frames short_person parse_minmatch id3v23_unsync id3v23_unsync_size_w id3v2_recalculate ignore_trusted_encoding0_v2 id3v2_set_trusted_encoding0 write_v24 prohibit_v24 encode_encoding_files encode_encoding_v1 encode_encoding_cue decode_encoding_v1 decode_encoding_v2 decode_encoding_filename decode_encoding_files decode_encoding_inf decode_encoding_cddb_file name_for_field_normalization is_writable writable_extensions id3v2_frames_autofill local_cfg_file); my @tr = map "translate_$_", qw( title track artist album comment year genre comment_collection comment_track title_track composer performer artist_collection person ); my $e_known = $self->get_config('extra_config_keys'); $e_known = [map lc, @$e_known]; $conf_rex = '^(' . join('|', @known, @$e_known, @tr) . ')$' unless $conf_rex; if ($item =~ /^(force)$/) { return $config->{$item} = {@options}; } elsif ($item !~ $conf_rex) { warn "MP3::Tag::config(): Unknown option '$item' found; known options: @known @$e_known @tr\n REX = <<<$conf_rex>>>\n"; return; } undef $conf_rex if $item eq 'extra_config_keys'; $config->{$item} = \@options; } =item get_config $opt_array = $mp3->get_config("item"); When object options are first time set or get, the global options are propagated into object options. (So if global options are changed later, these changes are not inherited.) =item get_config1 $opt = $mp3->get_config1("item"); Similar to get_config(), but returns UNDEF if no config array is present, or the first entry of array otherwise. =cut sub get_config ($$) { my ($self, $item) = @_; my $config = ref $self ? ($self->{config} ||= {%config}) : \%config; $config->{lc $item}; } sub get_config1 { my $self = shift; my $c = $self->get_config(@_); $c and $c->[0]; } =item name_for_field_normalization $name = $mp3->name_for_field_normalization; Returns "person name" to use for normalization of title-like fields; it is the result of interpolation of the configuration variable C<name_for_field_normalization> (defaults to C<%{composer}> - which, by default, expands the same as C<%{TCOM|a}>). =cut sub name_for_field_normalization ($) { my $self = shift; $self->interpolate( $self->get_config1("name_for_field_normalization") ); } =item pure_filetags $data = $mp3->pure_filetags()->autoinfo; Configures $mp3 to not read anything except the pure ID3v2 or ID3v1 tags, and do not postprocess them. Returns the object reference itself to simplify chaining of method calls. =cut sub pure_filetags ($) { my $self = shift; for my $c (qw(autoinfo title artist album year comment track genre)) { $self->config($c,"ID3v2","ID3v1"); } $self->config('comment_remove_date', 0); for my $k (%{$self->{config}}) { delete $self->{config}->{$k} if $k =~ /^translate_/; } return $self; } =item get_user $data = $mp3->get_user($n); # n-th piece of user scratch space Queries an entry in a scratch array ($n=3 corresponds to C<%{U3}>). =item set_user $mp3->set_user($n, $data); # n-th piece of user scratch space Sets an entry in a scratch array ($n=3 corresponds to C<%{U3}>). =cut sub get_user ($$) { my ($self, $item) = @_; unless ($self->{userdata}) { local $self->{__proxy}[0] = $self unless $self->{__proxy}[0] or $ENV{MP3TAG_TEST_WEAKEN}; $self->{ParseData}->parse('track'); # Populate the hash if possible $self->{userdata} ||= []; } return unless defined (my $d = $self->{userdata}[$item]); $d; } sub set_user ($$$) { my ($self, $item, $val) = @_; $self->{userdata} ||= []; $self->{userdata}[$item] = $val; } =item set_id3v2_frame $mp3->set_id3v2_frame($name, @values); When called with only $name as the argument, removes the specified frame (if it existed). Otherwise sets the frame passing the specified @values to the add_frame() function of MP3::Tag::ID3v2. (The old value is removed.) =cut # With two elements, removes frame sub set_id3v2_frame ($$;@) { my ($self, $item) = (shift, shift); $self->get_tags; return if not @_ and not exists $self->{ID3v2}; $self->new_tag("ID3v2") unless exists $self->{ID3v2}; $self->{ID3v2}->remove_frame($item) if defined $self->{ID3v2}->get_frame($item); return unless @_; return $self->{ID3v2}->add_frame($item, @_); } =item get_id3v2_frames ($descr, @frames) = $mp3->get_id3v2_frames($fname); Returns the specified frame(s); has the same API as L<MP3::Tag::ID3v2::get_frames>, but also returns undef if no ID3v2 tag is present. =cut sub get_id3v2_frames ($$;$) { my ($self) = (shift); $self->get_tags; return if not exists $self->{ID3v2}; $self->{ID3v2}->get_frames(@_); } =item delete_tag $deleted = $mp3->delete_tag($tag); $tag should be either C<ID3v1> or C<ID3v2>. Deletes the tag if it is present. Returns FALSE if the tag is not present. =cut sub delete_tag ($$) { my ($self, $tag) = (shift, shift); $self->get_tags; die "Unexpected tag type '$tag'" unless $tag =~ /^ID3v[12]$/; return unless exists $self->{$tag}; my $res = $self->{$tag}->remove_tag(); $res = ($res >= 0) if $tag eq 'ID3v1'; # -1 on error $res or die "Error deleting tag `$tag'"; } =item is_id3v2_modified $frame = $mp3->is_id3v2_modified(); Returns TRUE if ID3v2 tag exists and was modified after creation. =cut sub is_id3v2_modified ($$;@) { my ($self) = (shift); return if not exists $self->{ID3v2}; $self->{ID3v2}->is_modified(); } =item select_id3v2_frame $frame = $mp3->select_id3v2_frame($fname, $descrs, $langs [, $VALUE]); Returns the specified frame(s); has the same API as L<MP3::Tag::ID3v2/frame_select> (args are the frame name, the list of wanted Descriptors, list of wanted Languages, and possibly the new contents - with C<undef> meaning deletion). For read-only access it returns empty if no ID3v2 tag is present, or no frame is found. If new contents is specified, B<ALL> the existing frames matching the specification are deleted. =item have_id3v2_frame $have_it = $mp3->have_id3v2_frame($fname, $descrs, $langs); Returns TRUE the specified frame(s) exist; has the same API as L<MP3::Tag::ID3v2::frame_have> (args are frame name, list of wanted Descriptors, list of wanted Languages). =item get_id3v2_frame_ids $h = $mp3->get_id3v2_frame_ids(); print " $_ => $h{$_}" for keys %$h; Returns a hash reference with the short names of ID3v2 frames present in the tag as keys (and long description of the meaning as values), or FALSE if no ID3v2 tag is present. See L<MP3::Tags::ID3v2::get_frame_ids> for details. =item id3v2_frame_descriptors Returns the list of human-readable "long names" of frames (such as C<COMM(eng)[lyricist birthdate]>), appropriate for interpolation, or for select_id3v2_frame_by_descr(). =item select_id3v2_frame_by_descr =item have_id3v2_frame_by_descr Similar to select_id3v2_frame(), have_id3v2_frame(), but instead of arguments $fname, $descrs, $langs take one string of the form NAME(langs)[descr] Both C<(langs)> and C<[descr]> parts may be omitted; langs should contain comma-separated list of needed languages. The semantic is similar to L<MP3::Tag::ID3v2::frame_select_by_descr_simpler|MP3::Tag::ID3v2/frame_select_by_descr_simpler>. It is allowed to have C<NAME> of the form C<FRAMnn>; C<nn>-th frame with name C<FRAM> is chosen (C<-1>-based: the first frame is C<FRAM>, the second C<FRAM00>, the third C<FRAM01> etc; for more user-friendly scheme, use C<langs> of the form C<#NNN>, with C<NNN> 0-based; see L<MP3::Tag::ID3v2/"get_frame_ids()">). $frame = $mp3->select_id3v2_frame_by_descr($descr [, $VALUE1, ...]); $have_it = $mp3->have_id3v2_frame_by_descr($descr); select_id3v2_frame_by_descr() will also apply the normalizer in config setting C<translate_person> if the frame name matches one of the elements of the configuration setting C<person_frames>. $c = $mp3->select_id3v2_frame_by_descr("COMM(fre,fra,eng,#0)[]"); $t = $mp3->select_id3v2_frame_by_descr("TIT2"); $mp3->select_id3v2_frame_by_descr("TIT2", "MyT"); # Set/Change $mp3->select_id3v2_frame_by_descr("RBUF", $n1, $n2, $n3); # Set/Change $mp3->select_id3v2_frame_by_descr("RBUF", "$n1;$n2;$n3"); # Set/Change $mp3->select_id3v2_frame_by_descr("TIT2", undef); # Remove Remember that when select_id3v2_frame_by_descr() is used for modification, B<ALL> found frames are deleted before a new one is added. For gory details, see L<MP3::Tag::ID3v2/frame_select>. =item frame_translate $mp3->frame_translate('TCOM'); # Normalize TCOM ID3v2 frame assuming that the frame value denotes a person, normalizes the value using personal name normalization logic (via C<translate_person> configuration value). Frame is updated, but the tag is not written back. The frame must be in the list of personal names frames (C<person_frames> configuration value). =item frames_translate Similar to frame_translate(), but updates all the frames in C<person_frames> configuration value. =cut sub select_id3v2_frame ($$;@) { my ($self) = (shift); $self->get_tags; if (not exists $self->{ID3v2}) { return if @_ <= 3 or not defined $_[3]; # Read access, or deletion $self->new_tag("ID3v2"); } $self->{ID3v2}->frame_select(@_); } sub _select_id3v2_frame_by_descr ($$$;@) { my ($self, $update) = (shift, shift); $self->get_tags; if (not exists $self->{ID3v2}) { return if @_ <= 1 or @_ <= 2 and not defined $_[1]; # Read or delete $self->new_tag("ID3v2"); } my $fname = $_[0]; $fname =~ s/^(\w{4})\d+/$1/; # if FRAMnn, convert to FRAM my $tr = ($self->get_config('translate_person') || [])->[0]; if ($tr) { my $translate = $self->get_config('person_frames'); unless (ref $translate eq 'HASH') { # XXXX Store the hash somewhere??? $translate = {map +($_, 1), @$translate}; #$self->config('person_frames', @translate); } my $do = $translate->{$fname}; $do = $translate->{$fname} # Remove language if not $do and $fname =~ s/^(\w{4})(?:\(([^()]*(?:\([^()]+\)[^()]*)*)\))/$1/; undef $tr unless $do; } return if $update and not $tr; $tr ||= sub {$_[1]}; return $self->{ID3v2}->frame_select_by_descr_simpler(@_) if @_ > 2 or @_ == 2 and not defined $_[1]; # Multi-arg write or delete return $self->{ID3v2}->frame_select_by_descr_simpler( $_[0], &$tr($self, $_[1]) ) if @_ == 2; # Write access with one arg my $val = $self->{ID3v2}->frame_select_by_descr_simpler(@_); my $nval; $nval = &$tr($self, $val) if defined $val; return $nval unless $update; # Update logic: return if not defined $val or $val eq $nval; $self->{ID3v2}->frame_select_by_descr_simpler($_[0], $nval); } sub select_id3v2_frame_by_descr ($$;@) { my ($self) = (shift); return $self->_select_id3v2_frame_by_descr(0, @_); } sub frame_translate ($@) { my ($self) = (shift); return $self->_select_id3v2_frame_by_descr(1, @_); } sub frames_translate ($) { my ($self) = (shift); for my $f (@{$self->get_config('person_frames') || []}) { $self->frame_translate($f); } } sub have_id3v2_frame ($$;@) { my ($self) = (shift); $self->get_tags; return if not exists $self->{ID3v2}; $self->{ID3v2}->frame_have(@_); } sub have_id3v2_frame_by_descr ($$) { my ($self) = (shift); $self->get_tags; return if not exists $self->{ID3v2}; $self->{ID3v2}->frame_have_by_descr(shift); } sub get_id3v2_frame_ids ($$) { my ($self) = (shift); $self->get_tags; return if not exists $self->{ID3v2}; $self->{ID3v2}->get_frame_ids(@_); } sub id3v2_frame_descriptors ($) { my ($self) = (shift); $self->get_tags; return if not exists $self->{ID3v2}; $self->{ID3v2}->get_frame_descriptors(@_); } =item copy_id3v2_frames($from, $to, $overwrite, [$keep_flags, $f_ids]) Copies specified frames between C<MP3::Tag> objects $from, $to. Unless $keep_flags, the copied frames have their flags cleared. If the array reference $f_ids is not specified, all the frames (but C<GRID> and C<TLEN>) are considered (subject to $overwrite), otherwise $f_ids should contain short frame ids to consider. Group ID flag is always cleared. If $overwrite is C<'delete'>, frames with the same descriptors (as returned by get_frame_descr()) in $to are deleted first, then all the specified frames are copied. If $overwrite is FALSE, only frames with descriptors not present in $to are copied. (If one of these two conditions is not met, the result may be not conformant to standards.) Returns count of copied frames. =cut sub copy_id3v2_frames { my ($from, $to, $overwrite, $keep_flags, $f_ids) = @_; $from->get_tags; return 0 unless $from = $from->{ID3v2}; # No need to create it... $f_ids ||= [keys %{$from->get_frame_ids}]; return 0 unless @$f_ids; $to->get_tags; $to->new_tag("ID3v2") if not exists $to->{ID3v2}; $from->copy_frames($to->{ID3v2}, $overwrite, $keep_flags, $f_ids); } sub _Data_to_MIME ($$;$) { goto &MP3::Tag::ID3v2::_Data_to_MIME } =item _Data_to_MIME Internal method to extract MIME type from a string the image file content. Returns C<application/octet-stream> for unrecognized data (unless extra TRUE argument is given). $format = $id3->_Data_to_MIME($data); Currently, only the first 4 bytes of the string are inspected. =cut =item shorten_person $string = $mp3->shorten_person($person_name); shorten $person_name as a personal name (according to C<short_person> configuration setting). =cut sub shorten_person ($$) { my $self = shift; my $tr = ($self->get_config('short_person') || [])->[0]; return shift unless $tr; return &$tr($self, shift); } =item normalize_person $string = $mp3->normalize_person($person_name); normalize $person_name as a personal name (according to C<translate_person> configuration setting). =cut sub normalize_person ($$) { my $self = shift; my $tr = ($self->get_config('translate_person') || [])->[0]; return shift unless $tr; return &$tr($self, shift); } =item id3v2_frames_autofill $mp3->id3v2_frames_autofill($force); Generates missing tags from the list specified in C<id3v2_frames_autofill> configuration variable. The tags should be from a short list this method knows how to deal with: TXXX[MCDI-fulltoc]: filled from file audio_cd.toc in directory of the audio file. [Create this file with readcd -fulltoc dev=0,1,0 -f=audio_cd >& nul modifying the dev (and redirection per your shell). ] TXXX[cddb_id] TXXX[cdindex_id]: filled from the result of the corresponding method (which may extract from .inf or cddb files). Existing frames are not modified unless $force option is specified; when $force is true, ID3v2 tag will be created even if it was not present. =cut sub id3v2_frames_autofill ($$) { my ($self, $forceframe) = (shift, shift); my %force = @{$self->get_config('id3v2_frames_autofill')}; $self->get_tags; unless ($self->{ID3v2} or $forceframe) { # first run: force ID3v2? for my $tag (keys %force) { next unless $force{$tag}; my $v; $v = $self->$1() or next if $tag =~ /^TXXX\[(cd(?:db|index)_id)\]$/; if ($tag eq 'TXXX[MCDI-fulltoc]') { my $file = $self->interpolate('%D/audio_cd.toc'); $v = -e $file; } $forceframe = 1, last if $v } } for my $tag (keys %force) { next if $self->have_id3v2_frame_by_descr($tag) and not $forceframe; next unless $force{$tag} or $self->{ID3v2} or $forceframe; my $v; $v = $self->$1() or next if $tag =~ /^TXXX\[(cd(?:db|index)_id)\]$/; if ($tag eq 'TXXX[MCDI-fulltoc]') { my $file = $self->interpolate('%D/audio_cd.toc'); next unless -e $file; warn(<<EOW), next unless $self->track; Could deduce MCDI info, but per id3v2.4 specs, must know the track number! EOW eval { open F, "< $file" or die "Can't open `$file' for read: $!"; binmode F or die "Can't binmode `$file' for read: $!"; local $/; $v = <F>; CORE::close F or die "Can't close `$file' for read: $!"; } or warn($@), next; } $self->select_id3v2_frame_by_descr($tag, $v), next if defined $v; die "id3v2_frames_autofill(): do not know how to create frame `$tag'"; } } =item interpolate $string = $mp3->interpolate($pattern) interpolates C<%>-escapes in $pattern using the information from $mp3 tags. The syntax of escapes is similar to this of sprintf(): % [ [FLAGS] MINWIDTH] [.MAXWIDTH] ESCAPE The only recognized FLAGS are C<-> (to denote left-alignment inside MINWIDTH- wide field), C<' '> (SPACE), and C<0> (denoting the fill character to use), as well as an arbitrary character in parentheses (which becomes the fill character). MINWIDTH and MAXWIDTH should be numbers. The short ESCAPEs are replaced by % => literal '%' t => title a => artist l => album y => year g => genre c => comment n => track f => filename without the directory path F => filename with the directory path D => the directory path of the filename E => file extension e => file extension without the leading dot A => absolute filename without extension B => filename without the directory part and extension N => filename as originally given without extension v mpeg_version L mpeg_layer_roman r bitrate_kbps q frequency_kHz Q frequency_Hz S total_secs_int M total_millisecs_int m total_mins mL leftover_mins H total_hours s leftover_secs SL leftover_secs_trunc ML leftover_msec SML leftover_secs_float C is_copyrighted_YN p frames_padded_YN o channel_mode u frames h height (these 3 for image files, Image::Size or Image::ExifData required) w width iT img_type mT mime_type mP mime_Pretype (the capitalized first part of mime_type) aR aspect_ratio (width/height) a3 aspect_ratio3 (3 decimal places after the dot) aI aspect_ratio_inverted (height/width) bD bit_depth aC collection artist (from CDDB_File) tT track title (from CDDB_File) cC collection comment (from CDDB_File) cT track comment (from CDDB_File) iC CDDB id iI CDIndex id (Multi-char escapes must be inclosed in braces, as in C<%{SML}> or C<%.5{aR}>. Additional multi-char escapes are interpretated is follows: =over 4 =item * Names of ID3v2 frames are replaced by their text values (empty for missing frames). =item * Strings C<n1> and C<n2> are replaced by "pure track number" and "max track number" (this allows for both formats C<N1> and C<N1/N2> of "track", the latter meaning track N1 of N2); use C<n0> to pad C<n1> with leading 0 to the width of C<n2> (in absense of n2, to 2). Likewise for C<m1>, C<m2> but with disk (media) number instead of track number; use C<mA> to encode C<m1> as a letter (see L<disk_alphanum()>). =item * Strings C<ID3v1> and C<ID3v2> are replaced by the whole ID3v1/2 tag (interpolation of C<ID3v2> for an unmodified tag is subject to C<id3v2_recalculate> configuration variable). (These may work as conditionals too, with C<:>.) =item * Strings of the form C<FRAM(list,of,languages)[description]'> are replaced by the first FRAM frame with the descriptor "description" in the specified comma-separated list of languages. Instead of a language (ID3v2 uses lowercase 3-char ISO-639-2 language notations) one can use a string of the form C<#Number>; e.g., C<#4> means 4th FRAM frame, or FRAM04. Empty string for the language means any language.) Works as a condition for conditional interpolation too. Any one of the list of languages and the disription can be omitted; this means that either the frame FRAM has no language or descriptor associated, or no restriction should be applied. Unknown language should be denoted as C<XXX> (in uppercase!). The language match is case-insensitive. =item * Several descriptors of the form C<FRAM(list,of,languages)[description]'> discussed above may be combined together with C<&>; the non-empty expansions are joined together with C<"; ">. Example: %{TXXX[pre-title]&TIT1&TIT2&TIT3&TXXX[post-title]} =item * C<d>I<NUMBER> is replaced by I<NUMBER>-th component of the directory name (with 0 corresponding to the last component). =item * C<D>I<NUMBER> is replaced by the directory name with NUMBER components stripped. =item * C<U>I<NUMBER> is replaced by I<NUMBER>-th component of the user scratch array. =item * If string starts with C<FNAME:>: if frame FNAME does not exists, the escape is ignored; otherwise the rest of the string is reinterpreted. =item * String starting with C<!FNAME:> are treated similarly with inverted test. =item * If string starts with C<FNAME||>: if frame FNAME exists, the part after C<||> is ignored; otherwise the part before C<||> is ignored, and the rest is reinterpreted. =item * If string starts with C<FNAME|>: if frame FNAME exists, the part after C<|> is ignored; otherwise the part before C<|> is ignored, and the rest is reinterpreted as if it started with C<%{>. =item * String starting with I<LETTER>C<:> or C<!>I<LETTER>C<:> are treated similarly to ID3v2 conditionals, but the condition is that the corresponding escape expands to non-empty string. Same applies to non-time related 2-char escapes and user variables. =item * Likewise for string starting with I<LETTER>C<|> or I<LETTER>C<||>. =item * For strings of the form C<nmP[VALUE]> or C<shP[VALUE]>, I<VALUE> is interpolated, then normalized or shortened as a personal name (according to C<translate_person> or C<short_person> configuration setting). =item * C<composer> or C<performer> is replaced by the result of calling the corresponding method. =item * C<frames> is replaced by space-separated list of "long names" of ID3v2 frames (see id3v2_frame_descriptors()). (To use a different separator, put it after slash, as in %{frames/, }, where separator is COMMA SPACE). =item * C<_out_frames[QQPRE//QQPOST]> is replaced by a verbose listing of frames. "simple" frames are output one-per-line (with the value surrounded by C<QQPRE> and C<QQPOST>); fields of other frames are output one-per-line. If one omits the leading C<_>, then C<__binary_DATA__> replaces the value of binary fields. =item * C<ID3v2-size>, C<ID3v2-pad>, and C<ID3v2-stripped> are replaced by size of ID3v2 tag in bytes, the amount of 0-padding at the end of the tag (not counting one extra 0 byte at the end of tag which may be needed for unsyncing if the last char is \xFF), and size without padding. Currently, for modified ID3v2 tag, what is returned reflect the size on disk (i.e., before modification). =item * C<ID3v2-modified> is replaced by C<'modified'> if ID3v2 is present and is modified in memory; otherwise is replaced by an empty string. =item * For strings of the form C<I(FLAGS)VALUE>, I<VALUE> is interpolated with flags in I<FLAGS> (see L<"interpolate_with_flags">). If FLAGS does not contain C<i>, VALUE should have C<{}> and C<\> backwacked. =item * For strings of the form C<T[FORMAT]>, I<FORMAT> is split on comma, and the resulting list of formats is used to convert the duration of the audio to a string using the method format_time(). (E.g., C<%{T[=E<gt>m,?H:,{mL}]}> would print duration in (optional) hours and minutes rounded to the closest minute.) =back The default for the fill character is SPACE. Fill character should preceed C<-> if both are given. Example: Title: %(/)-12.12t%{TIT3:; TIT3 is %\{TIT3\}}%{!TIT3:. No TIT3 is present} will result in Title: TITLE///////; TIT3 is Op. 16 if title is C<TITLE>, and TIT3 is C<Op. 16>, and Title: TITLE///////. No TIT3 is present if title is C<TITLE>, but TIT3 is not present. Fat content: %{COMM(eng,fra,fre,rus,)[FatContent]} will print the comment field with I<Description> C<FatContent> prefering the description in English to one in French, Russian, or any other language (in this order). (I do not know which one of terminology/bibliography codes for French is used, so for safety include both.) Composer: %{TCOM|a} will use the ID3v2 field C<TCOM> if present, otherwise uses C<%a> (this is similar to Composer: %{composer} but the latter may be subject to (different) normalization, and/or configuration variables). Interpolation of ID3v2 frames uses the minimal possible non-ambiguous backslashing rules: the only backslashes needed are to protect the innermost closing delimiter (C<]> or C<}>) appearing as a literal character, or to protect backslashes I<immediately> preceding such literal, or the closing delimiter. E.g., the pattern equal to %{COMM(eng)[a\b\\c\}\]end\\\]\\\\]: comment `a\b\\c\\\}]end\]\\' present} checks for the presence of comment with the descriptor C<a\b\\c\}]end\]\\>. Note that if you want to write this string as a Perl literal, a lot of extra backslashes may be needed (unless you use C<E<lt>E<lt>'FOO'> HERE-document). %{T[?Hh,?{mL}m,{SML}s]} for a file of duration 2345.62sec will result in C<39m05.62s>, while %{T[?H:,?{mL}:,{SL},?{ML}]}sec will result in C<39:05.620sec>. =cut my %trans = qw( t title a artist l album y year g genre c comment n track E filename_extension e filename_extension_nodot A abs_filename_noextension B filename_nodir_noextension N filename_noextension f filename_nodir D dirname F abs_filename aC artist_collection tT title_track cC comment_collection cT comment_track iD cddb_id iI cdindex_id n1 track1 n2 track2 n0 track0 mA disk_alphanum m1 disk1 m2 disk2 h height w width iT img_type mT mime_type mP mime_Pretype aR aspect_ratio a3 aspect_ratio3 aI aspect_ratio_inverted bD bit_depth v mpeg_version L mpeg_layer_roman ? is_stereo ? is_vbr r bitrate_kbps q frequency_kHz Q frequency_Hz ? size_bytes S total_secs_int M total_millisecs_int m total_mins mL leftover_mins H total_hours s leftover_secs ML leftover_msec SML leftover_secs_float SL leftover_secs_trunc ? time_mm_ss C is_copyrighted_YN p frames_padded_YN o channel_mode u frames ? frame_len ? vbr_scale ); # Different: %v is without trailing 0s, %q has fractional part, # %e, %E are for the extension, # %r is a number instead of 'Variable', %u is one less... # Missing: # %b Number of corrupt audio frames (integer) # %e Emphasis (string) # %E CRC Error protection (string) # %O Original material flag (string) # %G Musical genre (integer) my $frame_bra = # FRAM | FRAM03 | FRAM(lang)[ qr{\w{4}(?:(?:\d\d)|(?:\([^()]*(?:\([^()]+\)[^()]*)*\))?(?:(\[)|(?=[\}:|&])))}s; # 1 group for begin-descr # used with offset by 1: 2: fill, 3: same, 4: $left, 5..6 width, 7: key my $pat_rx = qr/^%(?:(?:\((.)\)|([^-.1-9%a-zA-Z]))?(-)?(\d+))?(?:\.(\d+))?([talgcynfFeEABDNvLrqQSmsCpouMHwh{%])/s; # XXXX Partially repeated below, search for `talgc'??? vLrqQSmsCpouMH miss??? my $longer_f = qr(a[3CRI]|tT|c[TC]|i[DIT]|n[012]|m[A12TP]|bD); # (a[CR]|tT|c[TC]|[mMS]L|SML|i[DIT]|n[012]|m[A12T]|bD) # a[CR]|tT|c[TC]|i[DIT]|n[012]|m[A12T]|bD # $upto TRUE: parse the part including $upto char # Very restricted backslashitis: only $upto and \ before $upto-or-end # $upto defined but FALSE: interpolate only one %-escape. # Anyway: $_[1] is modified to remove interpolated part. sub _interpolate ($$;$$) { # goto &interpolate_flags if @_ == 3; my ($self, undef, $upto, $skip) = @_; # pattern is modified, so is $_[1] $self->get_tags(); my $res = ""; my $ids; die "upto=`$upto' not supported" if $upto and $upto ne ']' and $upto ne'}'; die "upto=`$upto' not supported with skip" if $upto and not defined $upto and $skip; my $cnt = ($upto or not defined $upto) ? -1 : 1; # upto eq '': 1 escape while ($cnt-- and ($upto # undef and '' use the same code ? ($upto eq ']' ? $_[1] =~ s/^((?:[^%\\\]]|(?:\\\\)*\\\]|\\+[^\\\]]|\\\\)+)|$pat_rx//so : $_[1] =~ s/^((?:[^%\\\}]|(?:\\\\)*\\\}|\\+[^\\\}]|\\\\)+)|$pat_rx//so) : $_[1] =~ s/^([^%]+)|$pat_rx//so)) { if (defined $1) { my $str = $1; if ($upto and $upto eq ']') { $str =~ s<((?:\\\\)*)(?:\\(?=\])|(?!.))>< '\\' x (length($1)/2) >ges; } elsif ($upto and $upto eq '}') { $str =~ s<((?:\\\\)*)(?:\\(?=\})|(?!.))>< '\\' x (length($1)/2) >ges; } $res .= $str, next; } my ($fill, $left, $minwidth, $maxwidth, $what) = ((defined $2 ? $2 : $3), $4, $5, $6, $7); next if $skip and $what ne '{'; my $str; if ($what eq '{' and $_[1] =~ s/^([dD])(\d+)}//) { # Directory next if $skip; if ($1 eq 'd') { $str = $self->dir_component($2); } else { $str = $self->dirname($2); } } elsif ($what eq '{' and $_[1] =~ s/^U(\d+)}//) { # User data next if $skip; $str = $self->get_user($1); } elsif ($what eq '{' and $_[1] =~ s/^($longer_f|[mMS]L|SML)}//o) { # CDDB, IDs, or leftover times next if $skip; my $meth = $trans{$1}; $str = $self->$meth(); } elsif ($what eq '{' and # $frame_bra has 1 group, No. 5 # 2-char fields as above, except for [mMS]L|SML (XXX: vLrqQSmsCpouMH ???) $_[1] =~ s/^(!)?(([talgcynfFeEABDNvLrqQSmsCpouMHwh]|ID3v[12]|ID3v2-modified|$longer_f|U\d+)(:|\|\|?)|$frame_bra)//o) { # Alternation with simple/complicated stuff my ($neg, $id, $simple, $delim) = ($1, $2, $3, $4); if ($delim) { # Not a frame id... $id = $simple; } else { # Frame: maybe trailed by :, |, ||, maybe not $id .= ($self->_interpolate($_[1], ']', $skip) . ']') if $5; $_[1] =~ s/^(:|\|\|?)// and $delim = $1; unless ($delim) { die "Can't parse negated conditional: I see `$_[1]'" if $neg; my $nonesuch = 0; unless ($self->{ID3v2} or $neg) { die "No ID3v2 present" if $self->get_config('id3v2_missing_fatal'); $nonesuch = 1; } if ($_[1] =~ s/^}//) { # frame with optional (lang)/[descr] next if $skip or $nonesuch; $str = $self->select_id3v2_frame_by_descr($id); #$str = $str->{_Data} if $str and ref $str and exists $str->{_Data}; } elsif ($_[1] =~ /^&/o) { # join of frames with optional (language)/[descriptor] my @id = $id; while ($_[1] =~ s/^&($frame_bra)//o) { $id = $1; $id .= ($self->_interpolate($_[1], ']', $skip) . ']') if $2; next if $skip or $nonesuch; push @id, $id; } die "Can't parse &-list; I see `$_[1]'" unless $_[1] =~ s/^}//; next if $skip or $nonesuch; my @out; for my $in (@id) { $in = $self->select_id3v2_frame_by_descr($in); #$in = $in->{_Data} if $in and ref $in and exists $in->{_Data}; push @out, $in if defined $in and length $in; } $str = join '; ', @out; } else { die "unknown frame terminator; I see `$_[1]'"; } } } if ($delim) { # Conditional # $self->_interpolate($_[1], $upto, $skip), next if $skip; my $alt = ($delim ne ':') && $delim; # FALSE or $delim die "Negation and alternation incompatible in interpolation" if $alt and $neg; my $have; if ($simple and (2 >= length $simple or $simple =~ /^U/)) { my $s = (1 == length $simple ? $simple : "{$simple}"); $str = $self->interpolate("%$s"); $have = length($str); } elsif (($simple || '') eq 'ID3v2-modified') { # may be undef $have = ${ $self->{ID3v2} || {} }{modified} || ''; } elsif ($simple) { # ID3v2 or ID3v1 die "ID3v2 or ID3v1 as conditionals incompatible with $alt" if $alt; $have = !! $self->{$simple}; # Make logical } else { $have = $self->have_id3v2_frame_by_descr($id); } my $skipping = $skip || (not $alt and $1 ? $have : !$have); my $s; if ($alt and $alt ne '||') { # Need to prepend % if ($_[1] =~ s/^([^\\])}//) { # One-char escape $s = $self->interpolate("%$1") unless $skipping; } else { # Understood with {}; prepend %{ $_[1] =~ s/^/%\{/ or die; $s = $self->_interpolate($_[1], '', $skipping); } } else { $s = $self->_interpolate($_[1], '}', $skipping); } next if $skipping; $str = $self->select_id3v2_frame_by_descr($id) if $alt and $have and not $simple; $str = $s unless $have and $alt; $str = $str->{_Data} if $str and ref $str and exists $str->{_Data}; } } elsif ($what eq '{' and $_[1] =~ s/^ID3v1}//) { next if $skip; $str = $self->{ID3v1}->as_bin if $self->{ID3v1}; } elsif ($what eq '{' and $_[1] =~ s/^(sh|nm)P\[//s) { # (Short) personal name $what = $1; $str = $self->_interpolate($_[1], ']', $skip); $_[1] =~ s/^\}// or die "Can't find end of ${what}P escape; I see `$_[1]'"; next if $skip; my $meth = ($what eq 'sh' ? 'shorten_person' : 'normalize_person'); $str = $self->$meth($str); } elsif ($what eq '{' and $_[1] =~ s/^I\((\w+)\)//s) { # Interpolate my $flags = $1; if ($flags =~ s/i//) { $str = $self->_interpolate($_[1], '}', $skip); } else { $_[1] =~ s/^((?:[^\\\}]|(?:\\\\)*\\\}|\\+[^\\\}]|\\\\)*)\}//s # $_[1] =~ s/^((?:\\.|[^{}\\])*)}// or die "Can't find non-interpolated argument in `$_[1]'"; next if $skip; # ($str = $1) =~ s/\\([\\{}])/$1/g; ($str = $1) =~ s<((?:\\\\)*)(?:\\(?=\})|(?!.))>< '\\' x (length($1)/2) >ges; } next if $skip; ($str) = $self->interpolate_with_flags($str, $flags); } elsif ($what eq '{' and $_[1] =~ s/^T\[([^\[\]]*)\]\}//s) { # time next if $skip; $str = $self->format_time(undef, split /,/, $1); } elsif ($what eq '{') { #id3v2=whole, composer/performer/frames unless ($self->{ID3v2} or $skip) { die "No ID3v2 present" if $self->get_config('id3v2_missing_fatal'); $_[1] =~ s/^[^\}]*}//; # XXXX No error checking here... next; } if ($_[1] =~ s/ID3v2}//) { # Whole tag if (not $skip and $self->{ID3v2}) { if ($self->get_config('id3v2_recalculate')) { $str = $self->{ID3v2}->as_bin; } else { $str = $self->{ID3v2}->as_bin_raw; } } } elsif ($_[1] =~ s/^(composer|performer)}//) { $str = $self->$1() unless $skip; } elsif ($_[1] =~ s,^frames(?:/(.*?))?},,) { my $sep = (defined $1 ? $1 : ' '); $str = join $sep, $self->id3v2_frame_descriptors() unless $skip; } elsif ($_[1] =~ s,^(_)?out_frames\[(.*?)//(.*?)\]},,) { my($bin, $pre, $post) = ($1, $2, $3); my $v2 = $self->{ID3v2}; # $fr_sep, $fn_sep, $pre,$post,$fsep,$pre_mult,$val_sep # length "Picture Type" = 12 $str = ($v2 ? $v2->__frames_as_printable("\n", "\t==>\n ", $pre, # Tune tabbing for length=5..12 (_Data) $post, "\n ", "", " \t=\t", $bin) : '') unless $skip; } elsif ($_[1] =~ s,^ID3v2-size},,) { my $v2 = $self->{ID3v2}; $str = ($v2 ? 10 + $v2->{buggy_padding_size} + $v2->{tagsize} : 0) unless $skip; } elsif ($_[1] =~ s,^ID3v2-pad},,) { my $v2 = $self->{ID3v2}; $v2->get_frame_ids() if $v2 and not exists $v2->{frameIDs}; $str = ($v2 ? $v2->{padding} : 0) unless $skip; } elsif ($_[1] =~ s,^ID3v2-stripped},,) { my $v2 = $self->{ID3v2}; $v2->get_frame_ids() if $v2 and not exists $v2->{frameIDs}; $str = ($v2 ? 10 + $v2->{buggy_padding_size} + $v2->{tagsize} - $v2->{padding} : 0) unless $skip; } elsif ($_[1] =~ s,^ID3v2-modified},,) { my $v2 = $self->{ID3v2}; $str = ($v2 and $v2->{modified}) || '' unless $skip; } else { die "unknown escape; I see `$_[1]'"; } } elsif ($what eq '%') { $str = '%'; } else { my $meth = $trans{$what}; $str = $self->$meth(); } $str = '' unless defined $str; if (defined $maxwidth and length $str > $maxwidth) { if ($str =~ /^(?:\+|(\-))?(\d*)(\.\d*)?$/) { if (length($1 || '') + length $2 <= $maxwidth) { my $w = $maxwidth - length $2 - length($1 || ''); $w-- if $w; # Take into account decimal point... $str = sprintf '%.*f', $w, $str } else { # Might be a long integer benefiting from %g my($w, $s0) = ($maxwidth, $str); while ($w >= 1) { $str = sprintf '%.*g', $w, $s0; $str =~ s/(^|(?<=[-+]))0+|(?<=e)\+//gi; # 1e+07 to 1e7 last if length $str <= $maxwidth; $w-- } $str = $s0 if length $str > length $s0; # 12 vs 1e1 $str = substr $str, 0, $maxwidth; # 1e as a truncation of 1234 is better than 12... } } else { $str = substr $str, 0, $maxwidth; } } if (defined $minwidth) { $fill = ' ' unless defined $fill; if ($left) { $str .= $fill x ($minwidth - length $str); } else { $str = $fill x ($minwidth - length $str) . $str; } } $res .= $str; } if (defined $upto) { not $upto or ($upto eq ']' ? $_[1] =~ s/^\]// : $_[1] =~ s/^\}//) or die "Can't find final delimiter `$upto': I see `$_[1]'"; } else { die "Can't parse `$_[1]' during interpolation" if length $_[1]; } return $res; } sub interpolate ($$) { my ($self, $pattern) = @_; # local copy; $pattern is modified $self->_interpolate($pattern); } =item interpolate_with_flags @results = $mp3->interpolate_with_flags($text, $flags); Processes $text according to directives in the string $flags; $flags is split into separate flag characters; the meanings (and order of application) of flags are i interpolate via $mp3->interpolate f interpret (the result) as filename, read from file F if file does not exist, it is not an error B read is performed in binary mode (otherwise in text mode, modified per 'decode_encoding_files' configuration variable) l split result per 'parse_split' configuration variable n as l, using the track-number-th element (1-based) in the result I interpolate (again) via $mp3->interpolate b unless present, remove leading and trailing whitespace With C<l>, may produce multiple results. May be accessed via interpolation of C<%{I(flags)text}>. =cut sub interpolate_with_flags ($$$) { my ($self, $data, $flags) = @_; $data = $self->interpolate($data) if $flags =~ /i/; if ($flags =~ /f/) { local *F; my $e; unless (open F, "< $data") { return if $flags =~ /F/; die "Can't open file `$data' for parsing: $!"; } if ($flags =~ /B/) { binmode F; } else { my $e; if ($e = $self->get_config('decode_encoding_files') and $e->[0]) { eval "binmode F, ':encoding($e->[0])'"; # old binmode won't compile... } } local $/; my $d = <F>; CORE::close F or die "Can't close file `$data' for parsing: $!"; $data = $d; } my @data = $data; if ($flags =~ /[ln]/) { my $p = $self->get_config('parse_split')->[0]; @data = split $p, $data, -1; } if ($flags =~ /n/) { my $track = $self->track1 or return; @data = $data[$track - 1]; } for my $d (@data) { $d = $self->interpolate($d) if $flags =~ /I/; unless ($flags =~ /b/) { $d =~ s/^\s+//; $d =~ s/\s+$//; } } @data; } =item parse_rex($pattern, $string) Parse $string according to the regular expression $pattern with C<%>-escapes C<%%, %a, %t, %l, %y, %g, %c, %n, %e, %E>. The meaning of escapes is the same as for method L<"interpolate">(); but they are used not for I<expansion>, but for I<matching> a part of $string suitable to be a value for these fields. Returns false on failure, a hash reference with parsed fields otherwise. Some more escapes are supported: C<%=a, %=t, %=l, %=y, %=g, %=c, %=n, %=e, %=E, %=A, %=B, %=D, %=f, %=F, %=N, %={WHATEVER}> I<match> substrings which are I<current> values of artist/title/etc (C<%=n> also matches leading 0s; actual file-name matches ignore the difference between C</> and C<\>, between one and multiple consequent dots (if configuration variable C<parse_filename_merge_dots> is true (default)) and are case-insensitive if configuration variable C<parse_filename_ignore_case> is true (default); moreover, C<%n>, C<%y>, C<%=n>, C<%=y> will not match if the string-to-match is adjacent to a digit). The escapes C<%{UE<lt>numberE<gt>}> and escapes of the forms C<%{ABCD}>, C<%{ABCDE<lt>numberE<gt>}> match any string; the corresponding hash key in the result hash is what is inside braces; here C<ABCD> is a 4-letter word possibly followed by 2-digit number (as in names of ID3v2 tags), or what can be put in C<'%{FRAM(lang,list)[description]}'>. $res = $mp3->parse_rex( qr<^%a - %t\.\w{1,4}$>, $mp3->filename_nodir ) or die; $author = $res->{author}; 2-digit numbers, or I<number1/number2> with number1,2 up to 999 are allowed for the track number (the leading 0 is stripped); 4-digit years in the range 1000..2999 are allowed for year. Alternatively, if option year_is_timestamp is TRUE (default), year may be a range of timestamps in the format understood by ID3v2 method year() (see L<MP3::Tag::ID3v2/"year">). Currently the regular expressions with capturing parens are not supported. =item parse_rex_prepare($pattern) Returns a data structure which later can be used by parse_rex_match(). These two are equivalent: $mp3->parse_rex($pattern, $data); $mp3->parse_rex_match($mp3->parse_rex_prepare($pattern), $data); This call constitutes the "slow part" of the parse_rex() call; it makes sense to factor out this step if the parse_rex() with the same $pattern is called against multiple $data. =item parse_rex_match($prepared, $data) Matches $data against a data structure returned by parse_rex_prepare(). These two are equivalent: $mp3->parse_rex($pattern, $data); $mp3->parse_rex_match($mp3->parse_rex_prepare($pattern), $data); =cut sub _rex_protect_filename { my ($self, $filename, $what) = (shift, quotemeta shift, shift); $filename =~ s,\\[\\/],[\\\\/],g; # \ and / are interchangeable + backslashitis if ($self->get_config('parse_filename_merge_dots')->[0]) { # HPFS doesn't distinguish x..y and x.y $filename =~ s(\\\.+)(\\.+)g; $filename =~ s($)(\\.*) if $what =~ /[ABN]/; } my $case = $self->get_config('parse_filename_ignore_case')->[0]; return $filename unless $case; return "(?i:$filename)"; } sub _parse_rex_anything ($$) { my $c = shift->get_config('parse_minmatch'); my $min = $c->[0]; if ($min and $min ne '1') { my $field = shift; $min = grep $_ eq $field, @$c; } return $min ? '(.*?)' : '(.*)'; } sub __pure_track_rex ($) { my $t = shift()->track; $t =~ s/^0+//; $t =~ s,^(.*?)(/.*),\Q$1\E(?:\Q$2\E)?,; $t } sub _parse_rex_microinterpolate { # $self->idem($code, $groups, $ecount) my ($self, $code, $groups) = (shift, shift, shift); return '%' if $code eq '%'; # In these two, allow setting to '', and to 123/789 too... push(@$groups, $code), return '((?<!\d)\d{1,3}(?:/\d{1,3})?(?!\d)|\A\Z)' if $code eq 'n'; (push @$groups, $code), return '((?<!\d)[12]\d{3}(?:(?:--|[-:/T\0,])\d(?:|\d|\d\d\d))*(?!\d)|\A\Z)' if $code eq 'y' and ($self->get_config('year_is_timestamp'))->[0]; (push @$groups, $code), return '((?<!\d)[12]\d{3}(?!\d)|\A\Z)' if $code eq 'y'; # Filename parts ABDfFN and vLrqQSmsCpouMH not settable... (push @$groups, $code), return $self->_parse_rex_anything($code) if $code =~ /^[talgc]$/; $_[0]++, return $self->_rex_protect_filename($self->interpolate("%$1"), $1) if $code =~ /^=([ABDfFN]|{d\d+})$/; $_[0]++, return quotemeta($self->interpolate("%$1")) if $code =~ /^=([talgceEwhvLrqQSmsCpouMH]|{.*})$/; $_[0]++, return '(?<!\d)0*' . $self->__pure_track_rex . '(?!\d)' if $code eq '=n'; $_[0]++, return '(?<!\d)' . quotemeta($self->year) . '(?!\d)' if $code eq '=y'; (push @$groups, $1), return $self->_parse_rex_anything() if $code =~ /^{(U\d+|\w{4}(\d\d+|(?:\([^\)]*\))?(?:\[.*\])?)?)}$/s; # What remains is extension my $e = $self->get_config('extension')->[0]; (push @$groups, $code), return "($e)" if $code eq 'E'; (push @$groups, $code), return "(?<=(?=(?:$e)\$)\\.)(.*)" if $code eq 'e'; # Check whether '=' was omitted, as in %f $code =~ /^=/ or eval {my ($a, $b); $self->_parse_rex_microinterpolate("=$code", $a, $b)} and die "escape `%$code' can't be parsed; did you forget to put `='?"; die "unknown escape `%$code'"; } sub parse_rex_prepare { my ($self, $pattern) = @_; my ($codes, $exact, $p) = ([], 0, ''); my $o = $pattern; # (=? is correct! Group 4 is inside $frame_bra while ($pattern =~ s<^([^%]+)|%(=?{(?:($frame_bra)|[^}]+})|=?.)><>so) { if (defined $1) { $p .= $1; } else { my $group = $2; # description begins $group .= ($self->_interpolate($pattern, ']') . ']') if $4; if ($3) { $pattern =~ s/^}// or die "Can't find end of frame name, I see `$p'"; $group .= '}'; } $p .= $self->_parse_rex_microinterpolate($group, $codes, $exact); } } die "Can't parse pattern, I see `$pattern'" if length $pattern; #$pattern =~ s<%(=?{(?:[^\\{}]|\\[\\{}])*}|{U\d+}|=?.)> # (=? is correct! # ( $self->_parse_rex_microinterpolate($1, $codes, $exact) )seg; my @tags = map { length == 1 ? $trans{$_} : $_ } @$codes; return [$o, $p, \@tags, $exact]; } sub parse_rex_match { # pattern = [Original, Interpolated, Fields, NumExact] my ($self, $pattern, $data) = @_; return unless @{$pattern->[2]} or $pattern->[3]; my @vals = ($data =~ /$pattern->[1]()/s) or return; # At least 1 group my $cv = @vals - 1; die "Unsupported %-regular expression `$pattern->[0]' (catching parens? Got $cv vals) (converted to `$pattern->[1]')" unless $cv == @{$pattern->[2]}; my ($c, %h) = 0; for my $k ( @{$pattern->[2]} ) { $h{$k} ||= []; push @{ $h{$k} }, $vals[$c++]; # Support multiple occurences } my $j = $self->get_config('parse_join')->[0]; for $c (keys %h) { $h{$c} = join $j, grep length, @{ $h{$c} }; } $h{track} =~ s/^0+(?=\d)// if exists $h{track}; return \%h; } sub parse_rex { my ($self, $pattern, $data) = @_; $self->parse_rex_match($self->parse_rex_prepare($pattern), $data); } =item parse($pattern, $string) Parse $string according to the string $pattern with C<%>-escapes C<%%, %a, %t, %l, %y, %g, %c, %n, %e, %E>. The meaning of escapes is the same as for L<"interpolate">. See L<"parse_rex($pattern, $string)"> for more details. Returns false on failure, a hash reference with parsed fields otherwise. $res = $mp3->parse("%a - %t.mp3", $mp3->filename_nodir) or die; $author = $res->{author}; 2-digit numbers are allowed for the track number; 4-digit years in the range 1000..2999 are allowed for year. =item parse_prepare($pattern) Returns a data structure which later can be used by parse_rex_match(). This is a counterpart of parse_rex_prepare() used with non-regular-expression patterns. These two are equivalent: $mp3->parse($pattern, $data); $mp3->parse_rex_match($mp3->parse_prepare($pattern), $data); This call constitutes the "slow part" of the parse() call; it makes sense to factor out this step if the parse() with the same $pattern is called against multiple $data. =cut #my %unquote = ('\\%' => '%', '\\%\\=' => '%='); sub __unquote ($) { (my $k = shift) =~ s/\\(\W)/$1/g; $k } sub parse_prepare { my ($self, $pattern) = @_; $pattern = "^\Q$pattern\E\$"; # unquote %. and %=. and %={WHATEVER} and %{WHATEVER} $pattern =~ s<(\\%(?:\\=)?(\w|\\{(?:\w|\\[^\w\\{}]|\\\\\\[\\{}])*\\}|\\\W))> ( __unquote($1) )ge; # $pattern =~ s/(\\%(?:\\=)?)(\w|\\(\W))/$unquote{$1}$+/g; return $self->parse_rex_prepare($pattern); } sub parse { my ($self, $pattern, $data) = @_; $self->parse_rex_match($self->parse_prepare($pattern), $data); } =item filename() =item abs_filename() =item filename_nodir() =item filename_noextension() =item filename_nodir_noextension() =item abs_filename_noextension() =item dirname([$strip_levels]) =item filename_extension() =item filename_extension_nodot() =item dir_component([$level]) $filename = $mp3->filename(); $abs_filename = $mp3->abs_filename(); $filename_nodir = $mp3->filename_nodir(); $abs_dirname = $mp3->dirname(); $abs_dirname = $mp3->dirname(0); $abs_parentdir = $mp3->dirname(1); $last_dir_component = $mp3->dir_component(0); Return the name of the audio file: either as given to the new() method, or absolute, or directory-less, or originally given without extension, or directory-less without extension, or absolute without extension, or the directory part of the fullname only, or filename extension (with dot included, or not). The extension is calculated using the config() value C<extension>. The dirname() method takes an optional argument: the number of directory components to strip; the C<dir_component($level)> method returns one component of the directory (to get the last use 0 as $level; this is the default if no $level is specified). The configuration option C<decode_encoding_filename> can be used to specify the encoding of the filename; all these functions would use filename decoded from this encoding. =cut sub from_filesystem ($$) { my ($self, $f) = @_; my $e = $self->get_config('decode_encoding_filename'); return $f unless $e and $e->[0]; require Encode; Encode::decode($e->[0], $f); } sub filename { my $self = shift; $self->from_filesystem($self->{ofilename}); } sub abs_filename { my $self = shift; $self->from_filesystem($self->{abs_filename}); } sub filename_noextension { my $self = shift; my $f = $self->filename; my $ext_re = $self->get_config('extension')->[0]; $f =~ s/$ext_re//; return $f; } sub filename_nodir { require File::Basename; return scalar File::Basename::fileparse(shift->filename, ""); } sub dirname { require File::Basename; my ($self, $l) = (shift, shift); my $p = $l ? $self->dirname($l - 1) : $self->abs_filename; return File::Basename::dirname($p); } sub dir_component { require File::Basename; my ($self, $l) = (shift, shift); return scalar File::Basename::fileparse($self->dirname($l), ""); } sub filename_extension { my $self = shift; my $f = $self->filename_nodir; my $ext_re = $self->get_config('extension')->[0]; $f =~ /($ext_re)/ or return ''; return $1; } sub filename_nodir_noextension { my $self = shift; my $f = $self->filename_nodir; my $ext_re = $self->get_config('extension')->[0]; $f =~ s/$ext_re//; return $f; } sub abs_filename_noextension { my $self = shift; my $f = $self->abs_filename; my $ext_re = $self->get_config('extension')->[0]; $f =~ s/$ext_re//; return $f; } sub filename_extension_nodot { my $self = shift; my $e = $self->filename_extension; $e =~ s/^\.//; return $e; } =item mpeg_version() =item mpeg_layer() =item mpeg_layer_roman() =item is_stereo() =item is_vbr() =item bitrate_kbps() =item frequency_Hz() =item frequency_kHz() =item size_bytes() =item total_secs() =item total_secs_int() =item total_secs_trunc() =item total_millisecs_int() =item total_mins() =item leftover_mins() =item leftover_secs() =item leftover_secs_float() =item leftover_secs_trunc() =item leftover_msec() =item time_mm_ss() =item is_copyrighted() =item is_copyrighted_YN() =item frames_padded() =item frames_padded_YN() =item channel_mode_int() =item frames() =item frame_len() =item vbr_scale() These methods return the information about the contents of the MP3 file. If this information is not cached in ID3v2 tags (not implemented yet), using these methods requires that the module L<MP3::Info|MP3::Info> is installed. Since these calls are redirectoed to the module L<MP3::Info|MP3::Info>, the returned info is subject to the same restrictions as the method get_mp3info() of this module; in particular, the information about the frame number and frame length is only approximate. vbr_scale() is from the VBR header; total_secs() is not necessarily an integer, but total_secs_int() and total_secs_trunc() are (first is rounded, second truncated); time_mm_ss() has format C<MM:SS>; the C<*_YN> flavors return the value as a string Yes or No; mpeg_layer_roman() returns the value as a roman numeral; channel_mode() takes values in C<'stereo', 'joint stereo', 'dual channel', 'mono'>. =cut my %mp3info = qw( mpeg_version VERSION mpeg_layer LAYER is_stereo STEREO is_vbr VBR bitrate_kbps BITRATE frequency_kHz FREQUENCY size_bytes SIZE is_copyrighted COPYRIGHT frames_padded PADDING channel_mode_int MODE frames FRAMES frame_len FRAME_LENGTH vbr_scale VBR_SCALE total_secs_fetch SECS ); # Obsoleted: # total_mins MM # time_mm_ss TIME # leftover_secs SS # leftover_msec MS for my $elt (keys %mp3info) { no strict 'refs'; my $k = $mp3info{$elt}; *$elt = sub (;$) { # $MP3::Info::try_harder = 1; # Bug: loops infinitely if no frames my $self = shift; my $info = $self->{mp3info}; unless ($info) { require MP3::Info; $info = MP3::Info::get_mp3info($self->abs_filename); die "Didn't get valid data from MP3::Info for `".($self->abs_filename)."': $@" unless defined $info; } $info->{$k} } } sub frequency_Hz ($) { 1000 * (shift->frequency_kHz); } sub mpeg_layer_roman { eval { 'I' x (shift->mpeg_layer) } || '' } sub total_millisecs_int_fetch { int (0.5 + 1000 * shift->duration_secs) } sub frames_padded_YN { eval {shift->frames_padded() ? 'Yes' : 'No' } || '' } sub is_copyrighted_YN { eval {shift->is_copyrighted() ? 'Yes' : 'No' } || '' } sub total_millisecs_int { my $self = shift; my $ms = $self->{ms}; return $ms if defined $ms; (undef, $ms) = $self->get_id3v2_frames('TLEN'); $ms = $self->total_millisecs_int_fetch() unless defined $ms; $self->{ms} = $ms; return $ms; } sub total_secs_int { int (0.5 + 0.001 * shift->total_millisecs_int) } sub total_secs { 0.001 * shift->total_millisecs_int } sub total_secs_trunc { int (0.001 * (0.5 + shift->total_millisecs_int)) } sub total_mins { int (0.001/60 * (0.5 + shift->total_millisecs_int)) } sub leftover_mins { shift->total_mins() % 60 } sub total_hours { int (0.001/60/60 * (0.5 + shift->total_millisecs_int)) } sub leftover_secs { shift->total_secs_int() % 60 } sub leftover_secs_trunc { shift->total_secs_trunc() % 60 } sub leftover_msec { shift->total_millisecs_int % 1000 } sub leftover_secs_float { shift->total_millisecs_int % 60000 / 1000 } sub time_mm_ss { # Borrowed from MP3::Info my $self = shift; sprintf "%.2d:%.2d", $self->total_mins, $self->leftover_secs; } sub duration_secs { # Tricky: in which order to query MP3::Info and ExifTool? my $self = shift; my $d = $self->{duration}; return $d if defined $d; # Cached value return $self->{duration} = $self->total_secs_fetch # Have MP3::Info or a chance to work if $self->{mp3info} or $self->{filename} =~ /\.mp[23]$/i; my $r = $self->_duration; # Next: try ExifTool $r = $self->total_secs_fetch unless $r; # Try MP3::Info anyway return $r; } =item format_time $output = $mp3->format_time(67456.123, @format); formats time according to @format, which should be a list of format descriptors. Each format descriptor is either a simple letter, or a string in braces appropriate to be put after C<%> in an interpolated string. A format descriptor can be followed by a literal string to be put as a suffix, and can be preceded by a question mark, which says that this part of format should be printed only if needed. Leftover minutes, seconds are formated 0-padded to width 2 if they are preceded by more coarse units. Similarly, leftover milliseconds are printed with leading dot, and 0-padded to width 3. Two examples of useful C<@format>s are qw(?H: ?{mL}: {SML}) qw(?Hh ?{mL}m {SL} ?{ML}) Both will print hours, minutes, and milliseconds only if needed. The second one will use 3 digit-format after a point, the first one will not print the trailing 0s of milliseconds. The first one uses C<:> as separator of hours and minutes, the second one will use C<h m>. Optionally, the first element of the array may be of the form C<=E<gt>U>, here C<U> is one of C<h m s>. In this case, duration is rounded to closest hours, min or second before processing. (E.g., 1.7sec would print as C<1> with C<@format>s above, but would print as C<2> if rounded to seconds.) =cut my %Unit = qw( h 3600 m 60 s 1 ); sub format_time { my ($self, $time) = (shift, shift); $self = $self->new_fake() unless ref $self; local $self->{ms} = $self->{ms}; # Make modifiable local $self->{ms} = int($time * 1000 + 0.5) if defined $time; my ($out, %have, $c) = ''; for my $f (@_) { $have{$+}++ if $f =~ /^\??({([^{}]+)}|.)/; } for my $f (@_) { if (!$c++ and $f =~ /^=>(\w)$/) { my $u = $Unit{$1} or die "Unexpected unit of time for rounding: `$1'"; $time = $self->total_secs unless defined $time; $time = $u * int($time/$u + 0.5); $self->{ms} = 1000 * $time; next; } my $ff = $f; # Modifiable my $opt = ($ff =~ s/^\?//); $ff =~ s/^({[^{}]+}|\w)// or die "unexpected time format: <<$f>>"; my ($what, $format) = ($1, ''); if ($opt) { if ($what eq 'H') { $time = $self->total_secs unless defined $time; $opt = int($time / 3600) || !(grep $have{$_}, qw(m mL s S SL SML)); } elsif ($what eq 'm' or $what eq '{mL}') { $time = $self->total_secs unless defined $time; $opt = int($time / 60) || !(grep $have{$_}, qw(s S SL SML)); } elsif ($what eq '{ML}') { $opt = ($time != int $time); } else { $opt = 1; #die "Do not know how to treat optional `$what'"; } $what =~ /^(?:{(.*)}|(.))/ or die; (delete $have{$+}), next unless $opt; } $format = '02' if (($what eq 's' or $what eq '{SL}') and (grep $have{$_}, qw(H m mL))) or $what eq '{mL}' and $have{H}; $what = "%$format$what"; $what = ".%03{ML}" if $what eq '%{ML}' and grep $have{$_}, qw(H m mL s S SL); if ($what eq '%{SML}' and grep $have{$_}, qw(H m mL)) { # manual padding my $res = $self->interpolate($what); $res = "0$res" unless $res =~ /^\d\d/; $out .= "$res$ff"; } else { $out .= $self->interpolate($what) . $ff; } } $out; } my @channel_modes = ('stereo', 'joint stereo', 'dual channel', 'mono'); sub channel_mode { $channel_modes[shift->channel_mode_int] } =item can_write() checks permission to write per the configuration variable C<is_writable>. =item can_write_or_die($mess) as can_write(), but die()s on non-writable files with meaningful error message ($mess is prepended to the message). =item die_cant_write($mess) die() with the same message as can_write_or_die(). =item writable_by_extension() Checks that extension is (case-insensitively) in the list given by configuration variable C<writable_extensions>. =cut sub can_write ($) { my $self = shift; my @wr = @{ $self->get_config('is_writable') }; # Make copy return $wr[0] if @wr == 1 and not $wr[0] =~ /\D/; my $meth = shift @wr; $self->$meth(@wr); } sub writable_by_extension ($) { my $self = shift; my $wr = $self->get_config('writable_extensions'); # Make copy $self->extension_is(@$wr); } sub die_cant_write ($$) { my($self, $what) = (shift, shift); die $what, $self->interpolate("File %F is not writable per `is_writable' confuration variable, current value is `"), join(', ', @{$self->get_config('is_writable')}), "'"; } sub can_write_or_die ($$) { my($self, $what) = (shift, shift); my $wr = $self->can_write; return $wr if $wr; $self->die_cant_write($what); } =item update_tags( [ $data, [ $force2 ]] ) $mp3 = MP3::Tag->new($filename); $mp3->update_tags(); # Fetches the info, and updates tags $mp3->update_tags({}); # Updates tags if needed/changed $mp3->update_tags({title => 'This is not a song'}); # Updates tags This method updates ID3v1 and ID3v2 tags (the latter only if in-memory copy contains any data, or $data does not fit ID3v1 restrictions, or $force2 argument is given) with the the information about title, artist, album, year, comment, track, genre from the hash reference $data. The format of $data is the same as one returned from autoinfo() (with or without the optional argument 'from'). The fields which are marked as coming from ID3v1 or ID3v2 tags are not updated when written to the same tag. If $data is not defined or missing, C<autoinfo('from')> is called to obtain the data. Returns the object reference itself to simplify chaining of method calls. This is probably the simplest way to set data in the tags: populate $data and call this method - no further tinkering with subtags is needed. =cut sub update_tags { my ($mp3, $data, $force2, $wr2) = (shift, shift, shift); $mp3->get_tags; $data = $mp3->autoinfo('from') unless defined $data; # $mp3->new_tag("ID3v1") unless $wr1 = exists $mp3->{ID3v1}; unless (exists $mp3->{ID3v1}) { $mp3->can_write_or_die('update_tags() doing ID3v1: '); $wr2 = 1; $mp3->new_tag("ID3v1"); } my $elt; for $elt (qw/title artist album year comment track genre/) { my $d = $data->{$elt}; next unless defined $d; $d = [$d, ''] unless ref $d; $mp3->{ID3v1}->$elt( $d->[0] ) if $d->[1] ne 'ID3v1'; } # Skip what is already there... $mp3->{ID3v1}->write_tag; my $do_length = (defined $mp3->{ms}) ? ($mp3->get_config('update_length'))->[0] : 0; return $mp3 if not $force2 and $mp3->{ID3v1}->fits_tag($data) and not exists $mp3->{ID3v2} and $do_length < 2; # $mp3->new_tag("ID3v2") unless exists $mp3->{ID3v2}; unless (exists $mp3->{ID3v2}) { if (defined $wr2) { $mp3->die_cant_write('update_tags() doing ID3v2: ') unless $wr2; } else { $mp3->can_write_or_die('update_tags() doing ID3v2: '); } $mp3->new_tag("ID3v2"); } for $elt (qw/title artist album year comment track genre/) { my $d = $data->{$elt}; next unless defined $d; $d = [$d, ''] unless ref $d; $mp3->{ID3v2}->$elt( $d->[0] ) if $d->[1] ne 'ID3v2'; } # Skip what is already there... # $mp3->{ID3v2}->comment($data->{comment}->[0]); $mp3->set_id3v2_frame('TLEN', $mp3->{ms}) if $do_length and not $mp3->have_id3v2_frame('TLEN'); $mp3->{ID3v2}->write_tag; return $mp3; } sub _massage_genres ($;$) { # Thanks to neil verplank for the prototype require MP3::Tag::ID3v1; my($data, $how) = (shift, shift); my $firstnum = (($how || 0) eq 'num'); my $prefer_num = (($how || 0) eq 'prefer_num'); my (%seen, @genres); # find all genres in incoming data $data = $data->[0] if ref $data; # clean and split line on both null and parentheses $data =~ s/\s+/ /g; $data =~ s/\s*\0[\0\s]*/\0/g; $data =~ s/^[\s\0]+//; $data =~ s/[\s\0]+$//; my @data = split m<\0|\s+/\s+>, $data; @data = split /\( ( \d+ | rx | cr ) \)/xi, $data[0] if @data == 1; # review array, produce a clean, ordered list of unique genres for output foreach my $genre (@data) { next if $genre eq ""; # (12)(13) ==> in front, and between # convert text to number to eliminate collisions, and produce consistent output if ($genre =~ /\D/) {{ # Not a pure number # return id number my $genre_num = MP3::Tag::ID3v1::genres($genre); # 255 is "non-standard text" in ID3v1; pass the rest through last if $genre_num eq '255' or $genre_num eq ''; return $genre_num if $firstnum; $genre = $genre_num, last if $prefer_num; $genre_num = MP3::Tag::ID3v1::genres($genre_num); last unless defined $genre_num; $genre = $genre_num; }} # Now converted to a number - if possible unless ($prefer_num or $genre =~ /\D/) {{ # Here $genre is a number my $genre_str = MP3::Tag::ID3v1::genres($genre) or last; return $genre if $firstnum; $genre = $genre_str; }} # 2.4 defines these conversions $genre = "Remix" if lc $genre eq "rx"; $genre = "Cover" if lc $genre eq "cr"; $genre = "($genre)" if length $genre and not $genre =~ /\D/; # Only digits push @genres, $genre unless $seen{$genre}++; } return if $firstnum; @genres; } =item extension_is $mp3->extension_is(@EXT_LIST) returns TRUE if the extension of the filename coincides (case-insensitive) with one of the elements of the list. =cut sub extension_is ($@) { my ($self) = (shift); my $ext = lc($self->filename_extension_nodot()); return 1 if grep $ext eq lc, @_; return; } sub DESTROY { my $self=shift; if (exists $self->{filename} and defined $self->{filename}) { $self->{filename}->close; } } sub parse_cfg_line ($$$) { my ($self, $line, $data) = (shift,shift,shift); return if $line =~ /^\s*(#|$)/; die "Unrecognized configuration file line: <<<$line>>>" unless $line =~ /^\s*(\w+)\s*=\s*(.*?)\s*$/; push @{$data->{$1}}, $2; } =item C<parse_cfg( [$filename] )> Reads configuration information from the specified file (defaults to the value of configuration variable C<local_cfg_file>, which is C<~>-substituted). Empty lines and lines starting with C<#> are ignored. The remaining lines should have format C<varname=value>; leading and trailing whitespace is stripped; there may be several lines with the same C<varname>; this sets list-valued variables. =back =cut sub parse_cfg ($;$) { my ($self, $file) = (shift,shift); $file = ($self->get_config('local_cfg_file'))->[0] unless defined $file; return unless defined $file; $file =~ s,^~(?=[/\\]),$ENV{HOME}, if $ENV{HOME}; return unless -e $file; open F, "< $file" or die "Can't open `$file' for read: $!"; my $data = {}; while (defined (my $l = <F>)) { $self->parse_cfg_line($l, $data); } CORE::close F or die "Can't close `$file' for read: $!"; for my $k (keys %$data) { $self->config($k, @{$data->{$k}}); } } my @parents = qw(User Site Vendor); @MP3::Tag::User::ISA = qw( MP3::Tag::Site MP3::Tag::Vendor MP3::Tag::Implemenation ); # Make overridable @MP3::Tag::Site::ISA = qw( MP3::Tag::Vendor MP3::Tag::Implemenation ); @MP3::Tag::Vendor::ISA = qw( MP3::Tag::Implemenation ); sub load_parents { my $par; while ($par = shift @parents) { return 1 if eval "require MP3::Tag::$par; 1" } return; } load_parents() unless $ENV{MP3TAG_SKIP_LOCAL}; MP3::Tag->parse_cfg() unless $ENV{MP3TAG_SKIP_LOCAL}; 1; =pod =head1 ENVIRONMENT Some defaults for the operation of this module (and/or scripts distributed with this module) are set from environment. Assumed encodings (0 or encoding name): for read access: MP3TAG_DECODE_V1_DEFAULT MP3TAG_DECODE_V2_DEFAULT MP3TAG_DECODE_FILENAME_DEFAULT MP3TAG_DECODE_FILES_DEFAULT MP3TAG_DECODE_INF_DEFAULT MP3TAG_DECODE_CDDB_FILE_DEFAULT MP3TAG_DECODE_CUE_DEFAULT for write access: MP3TAG_ENCODE_V1_DEFAULT MP3TAG_ENCODE_FILES_DEFAULT (if not set, default to corresponding C<DECODE> options). Defaults for the above: MP3TAG_DECODE_DEFAULT MP3TAG_ENCODE_DEFAULT (if the second one is not set, the value of the first one is used). Value 0 for more specific variable will cancel the effect of the less specific variables. These variables set default configuration settings for C<MP3::Tag>; the values are read during the load time of the module. After load, one can use config()/get_config() methods to change/access these settings. See C<encode_encoding_*> and C<encode_decoding_*> in documentation of L<config|config> method. (Note that C<FILES> variant govern file read/written in non-binary mode by L<MP3/ParseData> module, as well as reading of control files of some scripts using this module, such as L<typeset_audio_dir>.) =over =item B<EXAMPLE> Assume that locally present CDDB files and F<.inf> files are in encoding C<cp1251> (this is not supported by "standard", but since the standard supports only a handful of languages, this is widely used anyway), and that one wants C<ID3v1> fields to be in the same encoding, but C<ID3v2> have an honest (Unicode, if needed) encoding. Then set MP3TAG_DECODE_INF_DEFAULT=cp1251 MP3TAG_DECODE_CDDB_FILE_DEFAULT=cp1251 MP3TAG_DECODE_V1_DEFAULT=cp1251 Since C<MP3TAG_DECODE_V1_DEFAULT> implies C<MP3TAG_ENCODE_V1_DEFAULT>, you will get the desired effect both for read and write of MP3 tags. =back Additionally, the following (unsupported) variables are currently recognized by ID3v2 code: MP3TAG_DECODE_UNICODE MP3TAG_DECODE_UTF8 MP3TAG_DECODE_UNICODE (default 1) enables decoding; the target of decoding is determined by MP3TAG_DECODE_UTF8: if 0, decoded values are byte-encoded UTF-8 (every Perl character contains a byte of UTF-8 encoded string); otherwise (default) it is a native Perl Unicode string. If C<MP3TAG_SKIP_LOCAL> is true, local customization files are not loaded. =head1 CUSTOMIZATION Many aspects of operation of this module are subject to certain subtle choices. A lot of effort went into making these choices customizable, by setting global or per-object configuration variables. A certain degree of customization of global configuration variables is available via the environment variables. Moreover, at startup the local customization file F<~/.mp3tagprc> is read, and defaults are set accordingly. In addition, to make customization as flexible as possible, I<ALL> aspects of operation of C<MP3::Tag> are subject to local override. Three customization modules MP3::Tag::User MP3::Tag::Site MP3::Tag::Vendor are attempted to be loaded if present. Only the first module (of those present) is loaded directly; if sequential load is desirable, the first thing a customization module should do is to call MP3::Tag->load_parents() method. The customization modules have an opportunity to change global configuration variables on load. To allow more flexibility, they may override any method defined in C<MP3::Tag>; as usual, the overriden method may be called using C<SUPER> modifier (see L<perlobj/"Method invocation">). E.g., it is recommended to make a local customization file with eval 'require Normalize::Text::Music_Fields'; for my $elt ( qw( title track artist album comment year genre title_track artist_collection person ) ) { no strict 'refs'; MP3::Tag->config("translate_$elt", \&{"Normalize::Text::Music_Fields::normalize_$elt"}) if defined &{"Normalize::Text::Music_Fields::normalize_$elt"}; } MP3::Tag->config("short_person", \&Normalize::Text::Music_Fields::short_person) if defined &Normalize::Text::Music_Fields::short_person; and install the (supplied, in the F<examples/modules>) module L<Normalize::Text::Music_Fields> which enables normalization of person names (to a long or a short form), and of music piece names to canonical forms. To simplify debugging of local customization, it may be switched off completely by setting MP3TAG_SKIP_LOCAL to TRUE (in environment). For example, putting id3v23_unsync = 0 into F<~/.mp3tagprc> will produce broken ID3v2 tags (but those required by ITunes). =head1 EXAMPLE SCRIPTS Some example scripts come with this module (either installed, or in directory F<examples> in the distribution); they either use this module, or provide data understood by this module: =over =item mp3info2 perform command line manipulation of audio tags (and more!); =item audio_rename rename audio files according to associated tags (and more!); =item typeset_mp3_dir write LaTeX files suitable for CD covers and normal-size sheet descriptions of hierarchy of audio files; =item mp3_total_time Calculate total duration of audio files; =item eat_wav_mp3_header remove WAV headers from MP3 files in WAV containers. =item fulltoc_2fake_cddb converts a CD's "full TOC" to a "fake" CDDB file (header only). Create this file with something like readcd -fulltoc dev=0,1,0 -f=audio_cd >& nul run similar to fulltoc_2fake_cddb < audio_cd.toc | cddb2cddb > cddb.out =item dir_mp3_2fake_cddb tries to convert a directory of MP3 files to a "fake" CDDB file (header only); assumes that files are a rip from a CD, and that alphabetical sort gives the track order (works only heuristically, since quantization of duration of MP3 files and of CD tracks is so different). Run similar to dir_mp3_2fake_cddb | cddb2cddb > cddb.out =item inf_2fake_cddb tries to convert a directory of F<.inf> files to a "fake" CDDB file (header only). (Still heuristic, since it can't guess the length of the leader.) Run similar to inf_2fake_cddb | cddb2cddb > cddb.out =item cddb2cddb Reads a (header of) CDDB file from STDIN, outputs (on STDOUT) the current version of the database record. Can be used to update a file, and/or to convert a fake CDDB file to a real one. =back (Last four do not use these modules!) Some more examples: # Convert from one (non-standard-conforming!) encoding to another perl -MMP3::Tag -MEncode -wle ' my @fields = qw(artist album title comment); for my $f (@ARGV) { print $f; my $t = MP3::Tag->new($f) or die; $t->update_tags( { map { $_ => encode "cp1251", decode "koi8-r", $t->$_() }, @fields } ); }' list_of_audio_files =head1 Problems with ID3 format The largest problem with ID3 format is that the first versions of these format were absolutely broken (underspecified). It I<looks> like the newer versions of this format resolved most of these problems; however, in reality they did not (due to unspecified backward compatibility, and grandfathering considerations). What are the problems with C<ID3v1>? First, one of the fields was C<artist>, which does not make any sense. In particular, different people/publishers would put there performer(s), composer, author of text/lyrics, or a combination of these. The second problem is that the only allowed encoding was C<iso-8859-1>; since most of languages of the world can't be expressed in this encoding, this restriction was completely ignored, thus the encoding is essentially "unknown". Newer versions of C<ID3> allow specification of encodings; however, since there is no way to specify that the encoding is "unknown", when a tag is automatically upgraded from C<ID3v1>, it is most probably assumed to be in the "standard" C<iso-8859-1> encoding. Thus impossibility to distinguish "unknown, assumed C<iso-8859-1>" from "known to be C<iso-8859-1>" in C<ID3v2>, essentially, makes any encoding specified in the tag "unknown" (or, at least, "untrusted"). (Since the upgrade [or a chain of upgrades] from the C<ID3v1> tag to the C<ID3v2> tag can result in any encoding of the "supposedly C<iso-8859-1>" tag, one cannot trust the content of C<ID3v2> tag even if it stored as Unicode strings.) This is why this module provides what some may consider only lukewarm support for encoding field in ID3v2 tags: if done fully automatic, it can allow instant propagation of wrong information; and this propagation is in a form which is quite hard to undo (but still possible to do with suitable settings to this module; see L<mp3info2/"Examples on dealing with broken encodings">). Likewise, the same happens with the C<artist> field in C<ID3v1>. Since there is no way to specify just "artist, type unknown" in C<ID3v2> tags, when C<ID3v1> tag is automatically upgraded to C<ID3v2>, the content would most probably be put in the "main performer", C<TPE1>, tag. As a result, the content of C<TPE1> tag is also "untrusted" - it may contain, e.g., the composer. In my opinion, a different field should be used for "known to be principal performer"; for example, the method performer() (and the script F<mp3info2> shipped with this module) uses C<%{TXXX[TPE1]}> in preference to C<%{TPE1}>. For example, interpolate C<%{TXXX[TPE1]|TPE1}> or C<%{TXXX[TPE1]|a}> - this will use the frame C<TXXX> with identifier C<TPE1> if present, if not, it will use the frame C<TPE1> (the first example), or will try to get I<artist> by other means (including C<TPE1> frame) (the second example). =head1 FILES There are many files with special meaning to this module and its dependent modules. =over 4 =item F<*.inf> Files with extension F<.inf> and the same basename as the audio file are read by module C<MP3::Tag::Inf>, and the extracted data is merged into the information flow according to configuration variable C<autoinfo>. It is assumed that these files are compatible in format to the files written by the program F<cdda2wav>. =item F<audio.cddb> F<cddb.out> F<cddb.in> in the same directory as the audio file are read by module C<MP3::Tag::CDDB_File>, and the extracted data is merged into the information flow according to configuration variable C<autoinfo>. (In fact, the list may be customized by configuration variable C<cddb_files>.) =item F<audio_cd.toc> in the same directory as the audio file may be read by the method id3v2_frames_autofill() (should be called explicitly) to fill the C<TXXX[MCDI-fulltoc]> frame. Depends on contents of configuration variable C<id3v2_frames_autofill>. =item F<~/.mp3tagprc> By default, this file is read on startup (may be customized by overriding the method parse_cfg()). By default, the name of the file is in the configuration variable C<local_cfg_file>. =back =head1 SEE ALSO L<MP3::Tag::ID3v1>, L<MP3::Tag::ID3v2>, L<MP3::Tag::File>, L<MP3::Tag::ParseData>, L<MP3::Tag::Inf>, L<MP3::Tag::CDDB_File>, L<MP3::Tag::Cue>, L<mp3info2>, L<typeset_audio_dir>. =head1 COPYRIGHT Copyright (c) 2000-2008 Thomas Geffert, Ilya Zakharevich. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the Artistic License, distributed with Perl. =cut ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/Normalize/�������������������������������������������������������������������������0000700�0000000�0000000�00000000000�11417072072�012131� 5����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/Normalize/Text/��������������������������������������������������������������������0000700�0000000�0000000�00000000000�11417072072�013055� 5����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/Normalize/Text/Music_Fields/�������������������������������������������������������0000700�0000000�0000000�00000000000�11417072072�015423� 5����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/Normalize/Text/Music_Fields/A_Dvor_k.comp������������������������������������������0000700�0000000�0000000�00000065537�10643570170�020014� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# format = mail-header # opus_rex \b(?:[Bb](?:\b|(?=\d))\.?|Op(?:us\b|\.))\s*\d+[a-d]?(?:[.,;\s]\s*No\.\s*\d+(?:\.\d+)*)? # opus_dup Op. 11 # opus_dup Op. 112 # opus_dup Op. 114 # opus_dup Op. 115 # opus_dup Op. 12 # opus_dup Op. 14 # opus_dup Op. 19a # opus_dup Op. 19b # opus_dup Op. 25 # opus_dup Op. 30 # opus_dup Op. 32 # opus_dup Op. 37 # opus_dup Op. 40 # opus_dup Op. 46 # opus_dup Op. 49 # opus_dup Op. 50 # opus_dup Op. 53 # opus_dup Op. 54 # opus_dup Op. 59 # opus_dup Op. 62 # opus_dup Op. 64 # opus_dup Op. 68, No. 5 # opus_dup Op. 71 # opus_dup Op. 72 # opus_dup Op. 79 # opus_dup Op. 8 # opus_dup Op. 84 # opus_dup Op. 86 # opus_dup Op. 94 # opus_dup Op. 99 Title-Name: Forget-me-not Polka Title-For: piano Title-Opus: B. 1 Title-Dates: 1856 Title-Type: Mass Title-Key: B flat major Title-Comment: [lost] Title-Opus: B. 2 Title-Dates: 1859 Title-Type: Polka Title-Key: E major Title-For: piano Title-Opus: B. 3 Title-Dates: 1860 Title-Name: The Woman Harpist Title-Comment: [lost] Title-Opus: B. 4 Title-Dates: 1860 Title-Type: Polka Title-Comment: [lost] Title-Opus: B. 5 Title-Dates: 1860 Title-Type: Gallop Title-Comment: [lost] Title-Opus: B. 6 Title-Dates: 1860 Title-Type: String Quintet Title-No: 1 Title-Key: A minor X-Title-Opus-Alt: B. 7 Title-Opus: Op. 1 Title-Dates: 1861 Title-Type: String Quartet Title-No: 1 Title-Key: A major X-Title-Opus-Alt: B. 8 Title-Opus: Op. 2 Title-Dates: 1862 Title-Type: Symphony Title-No: 1 Title-Key: C minor Title-Name: The Bells of Zlonice Title-Opus: B. 9 Title-Dates: 1865 Title-Type: Cello Concerto Title-Key: A major Title-Comment: [orchestrated by Jarmil Burghauser] Title-Opus: B. 10 Title-Dates: 1865 Title-Name: Cypresses Title-Comment: [18 songs on poems by G. Pfleger-Moravsky] Title-Opus: B. 11 Title-Dates: 1865 Title-Type: Symphony Title-No: 2 Title-Key: B flat major X-Title-Opus-Alt: B. 12 Title-Opus: Op. 4 Title-Dates: 1865 Title-Count: Two Title-Type: Songs Title-For: baritone Title-Opus: B. 13 Title-Dates: 1865 Title-Type: Clarinet Quintet Title-Key: B flat minor Title-Comment: [lost] Title-Opus: B. 14 Title-Dates: 1866 Title-Count: Seven Title-Type: Interludes Title-For: orchestra Title-Opus: B. 15 Title-Dates: 1867 Title-Type: Serenade Title-For: flute, violin, viola, and triangle Title-Opus: B. 15bis Title-Dates: 1867 Title-Name: Alfred Title-RAW: , heroic opera in 3 acts Title-Opus: B. 16 Title-Dates: 1870 Title-RAW: Tragic Overture Title-Related-How: from Title-Related-Name: Alfred Title-Opus: B. 16a Title-Dates: 1870 Title-Type: String Quartet Title-No: 2 Title-Key: B flat major Title-Opus: B. 17 Title-Dates: 1869 Title-Type: String Quartet Title-No: 3 Title-Key: D major Title-Opus: B. 18 Title-Dates: 1870 Title-Type: String Quartet Title-No: 4 Title-Key: E minor Title-Opus: B. 19 Title-Dates: 1870 Title-Type: Cello Sonata Title-Key: F minor Title-Comment: [lost] Title-Opus: B. 20 Title-Dates: 1871 Title-Name: The King and the Charcoalburner Title-Type-After-Name: comic opera Title-Comment: [1st version] X-Title-Opus-Alt: B. 21 Title-Opus: Op. 12 Title-Dates: 1871 Title-RAW: Concert Overture Title-Key: F major Title-Comment: [from 1st version of "The King and the Charcoalburner"] X-Title-Opus-Alt: B. 21a Title-Opus: Op. 12 Title-Dates: 1871 Title-Type: Potpourri Title-Related-How: from Title-Related-Name: The King and the Charcoalburner I Title-Opus: B. 22 Title-Dates: 1872 Title-RAW: Songs on words by Eliska Krásnohorská Title-Opus: B. 23 Title-Dates: 1871 Title-Name: Sirotek the Orphan Title-Comment: [after a ballad by K. J. Erben] Title-Opus: B. 24 Title-Dates: 1871 Title-Name: Rosemary Title-Comment: [after a poem by K. J. Erben] Title-Opus: B. 24a Title-Dates: 1871 Title-Type: Piano Trio Title-Comment: [lost] X-Title-Opus-Alt: B. 25 Title-Opus: Op. 13, No. 1 Title-Dates: 1871 Title-Type: Piano Trio Title-Comment: [lost] X-Title-Opus-Alt: B. 26 Title-Opus: Op. 13, No. 2 Title-Dates: 1872 Title-Name: The Heirs of the White Mountain Title-Comment: [hymn after a poem by V. Halék] Title-Opus: B. 27 Title-Dates: 1872 Title-Type: Piano Quintet Title-No: 1 Title-Key: A major X-Title-Opus-Alt: B. 28 Title-Opus: Op. 5 Title-Dates: 1872 Title-RAW: Four Songs on Serbian Folk Poems X-Title-Opus-Alt: B. 29 Title-Opus: Op. 6 Title-Dates: 1872 Title-Type: Songs Title-Related-How: from the Title-Related-Name: Dvur Králove Title-Punct: ( Title-Name: Queen's Court Title-RAW: ) Manuscript X-Title-Opus-Alt: B. 30 Title-Opus: Op. 7 Title-Dates: 1872 Title-Count: Three Title-Type: Nocturnes Title-Comment: [incomplete, except for No. 2 "May Night"] Title-Opus: B. 31 Title-Dates: 1872 Title-Name: Silhouettes Title-For: piano X-Title-Opus-Alt: B. 32 Title-Opus: Op. 8 Title-Dates: 1872 Title-Type: Violin Sonata Title-Key: A minor Title-Comment: [lost] Title-Opus: B. 33 Title-Dates: 1873 Title-Type: Symphony Title-No: 3 Title-Key: E flat major X-Title-Opus-Alt: B. 34 Title-Opus: Op. 10 Title-Dates: 1873 Title-Name: Romeo and Juliet Title-RAW: Overture Title-Comment: [lost] Title-Opus: B. 35 Title-Dates: 1873 Title-RAW: Octet Serenade Title-Comment: [lost] Title-Opus: B. 36 Title-Dates: 1873 Title-Type: String Quartet Title-No: 5 Title-Key: F minor X-Title-Opus-Alt: B. 37 Title-Opus: Op. 9 Title-Dates: 1873 Title-Type: Romance Title-Key: F minor Title-For: violin and piano X-Title-Opus-Alt: B. 38 Title-Opus: Op. 11 Title-Dates: 1877 Title-Type: Romance Title-Key: F minor Title-For: violin and orchestra Title-Comment: [arrangement of B. 38] X-Title-Opus-Alt: B. 39 Title-Opus: Op. 11 Title-Dates: 1877 Title-Type: String Quartet Title-No: 6 Title-Key: A minor X-Title-Opus-Alt: B. 40 Title-Opus: Op. 12 Title-Dates: 1873 Title-RAW: Andante appassionato Title-Key: A minor Title-For: string quartet Title-Opus: B. 40a Title-Dates: 1873 Title-Type: Symphony Title-No: 4 Title-Key: D minor X-Title-Opus-Alt: B. 41 Title-Opus: Op. 13 Title-Dates: 1874 Title-Name: The King and the Charcoalbuner Title-Comment: [2nd version of B. 21] X-Title-Opus-Alt: B. 42 Title-Opus: Op. 14 Title-Dates: 1874 Title-RAW: Porpourri on The King and the Charcoalburner Title-Comment: [2nd version of B. 22] Title-Opus: B. 43 Title-Dates: 1875 Title-RAW: Symphonic Poem (Rhapsody) Title-Key: A minor X-Title-Opus-Alt: B. 44 Title-Opus: Op. 14 Title-Dates: 1874 Title-Type: String Quartet Title-No: 7 Title-Key: A minor X-Title-Opus-Alt: B. 45 Title-Opus: Op. 16 Title-Dates: 1874 Title-Name: The Stubborn Lovers Title-Type-After-Name: comic opera X-Title-Opus-Alt: B. 46 Title-Opus: Op. 17 Title-Dates: 1874 Title-Type: Nocturne Title-Key: B major Title-For: strings X-Title-Opus-Alt: B. 47 Title-Opus: Op. 40 Title-Dates: 1883 Title-Type: Nocturne Title-Key: B major Title-For: violin and piano X-Title-Opus-Alt: B. 48a Title-Opus: Op. 40 Title-Dates: 1883 Title-Type: Nocturne Title-Key: B major Title-For: piano four hands X-Title-Opus-Alt: B. 48b Title-Opus: Op. 40 Title-Dates: 1875 Title-Type: String Quintet Title-No: 2 Title-Key: G major Title-Comment: [formerly Op. 18] X-Title-Opus-Alt: B. 49 Title-Opus: Op. 77 Title-Dates: 1875 Title-RAW: Moravian Duets X-Title-Opus-Alt: B. 50 Title-Opus: Op. 20 Title-Dates: 1875 Title-Type: Piano Trio Title-No: 1 Title-Key: B flat major X-Title-Opus-Alt: B. 51 Title-Opus: Op. 21 Title-Dates: 1875 Title-Type: Serenade Title-For: Strings Title-Key: E major X-Title-Opus-Alt: B. 52 Title-Opus: Op. 22 Title-Dates: 1875 Title-Type: Piano Quartet Title-No: 1 Title-Key: D major X-Title-Opus-Alt: B. 53 Title-Opus: Op. 23 Title-Dates: 1875 Title-Type: Symphony Title-No: 5 Title-Key: F major Title-Comment: [formerly Op. 24] X-Title-Opus-Alt: B. 54 Title-Opus: Op. 76 Title-Dates: 1875 Title-Name: Vanda Title-Type-After-Name: tragic opera X-Title-Opus-Alt: B. 55 Title-Opus: Op. 25 Title-Dates: 1875 Title-Type: Piano Trio Title-No: 2 Title-Key: G minor X-Title-Opus-Alt: B. 56 Title-Opus: Op. 26 Title-Dates: 1876 Title-Type: String Quartet Title-No: 8 Title-Key: E major Title-Comment: [formerly Op. 27] X-Title-Opus-Alt: B. 57 Title-Opus: Op. 80 Title-Dates: 1876 Title-Count: Two Title-Type: Minuets Title-For: piano X-Title-Opus-Alt: B. 58 Title-Opus: Op. 28 Title-Dates: 1876 Title-Count: Four Title-Type: Choruses Title-For: mixed choir X-Title-Opus-Alt: B. 59 Title-Opus: Op. 29 Title-Dates: 1876 Title-RAW: Moravian Duets X-Title-Opus-Alt: B. 60 Title-Opus: Op. 32 Title-Dates: 1876 Title-Name: Evening Songs Title-Comment: [after poems by V. Hálek] X-Title-Opus-Alt: B. 61 Title-Opus: Op. 31 Title-Dates: 1876 Title-RAW: Moravian Duets X-Title-Opus-Alt: B. 62 Title-Opus: Op. 32 Title-Dates: 1876 Title-Type: Piano Concerto Title-Key: G minor X-Title-Opus-Alt: B. 63 Title-Opus: Op. 33 Title-Dates: 1876 Title-RAW: Dumka Title-Key: D minor Title-For: piano X-Title-Opus-Alt: B. 64 Title-Opus: Op. 35 Title-Dates: 1876 Title-RAW: Theme and Variations Title-For: piano X-Title-Opus-Alt: B. 65 Title-Opus: Op. 36 Title-Dates: 1876 Title-RAW: Choral Songs Title-For: male voices Title-Opus: B. 66 Title-Dates: 1877 Title-Name: The Cunning Peasant Title-Type-After-Name: comic opera X-Title-Opus-Alt: B. 67 Title-Opus: Op. 37 Title-Dates: 1877 Title-Type: Overture Title-Related-How: to Title-Related-Name: The Cunning Peasant X-Title-Opus-Alt: B. 67a Title-Opus: Op. 37 Title-Dates: 1877 Title-RAW: Ave Maria X-Title-Opus-Alt: B. 68 Title-Opus: Op. 19b Title-Dates: 1877 Title-RAW: Moravian Duets X-Title-Opus-Alt: B. 69 Title-Opus: Op. 38 Title-Dates: 1877 Title-Type: Symphonic Variations Title-Comment: [formerly Op. 28] X-Title-Opus-Alt: B. 70 Title-Opus: Op. 78 Title-Dates: 1877 Title-RAW: Stabat Mater X-Title-Opus-Alt: B. 71 Title-Opus: Op. 58 Title-Dates: 1877 Title-Name: Bouquet of Czech Folksongs Title-Opus: B. 72 Title-Dates: 1877 Title-Name: Song of a Czech Title-Opus: B. 73 Title-Dates: 1877 Title-RAW: Scottish Dances Title-For: piano X-Title-Opus-Alt: B. 74 Title-Opus: Op. 41 Title-Dates: 1877 Title-Type: String Quartet Title-No: 9 Title-Key: D minor Title-Comment: [dedicated to Johannes Brahms] X-Title-Opus-Alt: B. 75 Title-Opus: Op. 34 Title-Dates: 1877 Title-Name: From a Bouquet of Czech Folksongs X-Title-Opus-Alt: B. 76 Title-Opus: Op. 43 Title-Dates: 1878 Title-Type: Serenade Title-Key: D minor Title-For: Wind Instruments X-Title-Opus-Alt: B. 77 Title-Opus: Op. 44 Title-Dates: 1878 Title-RAW: Slavonic Dances for piano four hands, 1st series X-Title-Opus-Alt: B. 78 Title-Opus: Op. 46 Title-Dates: 1878 Title-Type: Bagatelles Title-For: two violins, cello, and harmonium/piano X-Title-Opus-Alt: B. 79 Title-Opus: Op. 47 Title-Dates: 1878 Title-Type: String Sextet Title-Key: A major X-Title-Opus-Alt: B. 80 Title-Opus: Op. 48 Title-Dates: 1878 Title-Type: Capriccio Title-For: violin and piano X-Title-Opus-Alt: B. 81 Title-Opus: Op. 24 Title-Dates: 1878 Title-Name: Hymnus ad Laudus in festo Sanctae Trinitatis Title-Opus: B. 82 Title-Dates: 1878 Title-RAW: Slavonic Dances Title-For: orchestra Title-Comment: [orchestration of B. 78] X-Title-Opus-Alt: B. 83 Title-Opus: Op. 46 Title-Dates: 1878 Title-RAW: Three Modern Greek Songs X-Title-Opus-Alt: B. 84a Title-Opus: Op. 50 Title-Dates: 1878 Title-RAW: Three Modern Greek Songs X-Title-Opus-Alt: B. 84b Title-Opus: Op. 50 Title-Dates: 1878 Title-RAW: Furiants Title-For: piano X-Title-Opus-Alt: B. 85 Title-Opus: Op. 42 Title-Dates: 1878 Title-RAW: Slavonic Rhapsodies X-Title-Opus-Alt: B. 86 Title-Opus: Op. 45 Title-Dates: 1878 Title-RAW: Choral Songs Title-For: male voices X-Title-Opus-Alt: B. 87 Title-Opus: Op. 27 Title-Dates: 1878 Title-RAW: Festival March Title-For: orchestra X-Title-Opus-Alt: B. 88 Title-Opus: Op. 54 Title-Dates: 1879 Title-Type: Mazurek Title-For: violin and piano X-Title-Opus-Alt: B. 89 Title-Opus: Op. 49 Title-Dates: 1879 Title-Type: Mazurek Title-For: violin and orchestra X-Title-Opus-Alt: B. 90 Title-Opus: Op. 49 Title-Dates: 1879 Title-RAW: Psalm 149 Title-For: choir and orchestra Title-Comment: [1st version] X-Title-Opus-Alt: B. 91 Title-Opus: Op. 79 Title-Dates: 1879 Title-Type: String Quartet Title-No: 10 Title-Key: E flat major X-Title-Opus-Alt: B. 92 Title-Opus: Op. 51 Title-Dates: 1879 Title-RAW: Czech Suite Title-Key: D major Title-For: orchestra X-Title-Opus-Alt: B. 93 Title-Opus: Op. 39 Title-Dates: 1879 Title-Type: Polonaise Title-Key: A major Title-For: cello and piano Title-Opus: B. 94 Title-Dates: 1879 Title-Name: Ave maris stella X-Title-Opus-Alt: B. 95a Title-Opus: Op. 19b Title-Dates: 1879 Title-Name: O sanctissima dulcis Virgo Maria! Title-RAW: for alto, baritone, and organ X-Title-Opus-Alt: B. 95b Title-Opus: Op. 19a Title-Dates: 1879 Title-Name: O sanctissima dulcis Virgo Maria Title-Comment: [version of B. 95b, substituting soprano for baritone] X-Title-Opus-Alt: B. 95b bis Title-Opus: Op. 19a Title-Dates: 1879 Title-Type: Violin Concerto Title-Key: A minor X-Title-Opus-Alt: B. 96 Title-Opus: Op. 53 Title-Dates: 1880 Title-Name: Vanda Title-RAW: , concert overture X-Title-Opus-Alt: B. 97 Title-Opus: Op. 25 Title-Dates: 1879 Title-Name: Silhouettes Title-For: piano X-Title-Opus-Alt: B. 98 Title-Opus: Op. 8 Title-Dates: 1879 Title-RAW: Prague Waltzes Title-For: orchestra Title-Opus: B. 99 Title-Dates: 1879 Title-Type: Polonaise Title-Key: E flat major Title-For: orchestra Title-Opus: B. 100 Title-Dates: 1879 Title-Type: Waltzes Title-For: piano Title-Comment: [Orchestrated by Burghauser  ] X-Title-Opus-Alt: B. 101 Title-Opus: Op. 54 Title-Dates: 1880 Title-Name: The Heirs of the White Mountain Title-Comment: [after a poem by V. Halék] X-Title-Opus-Alt: B. 102 Title-Opus: Op. 30 Title-Dates: 1880 Title-RAW: Eclogues Title-For: piano X-Title-Opus-Alt: B. 103 Title-Opus: Op. (56) Title-Dates: 1880 Title-RAW: Gypsy Songs Title-Comment: [after poems by A. Heyduk] X-Title-Opus-Alt: B. 104 Title-Opus: Op. 55 Title-Dates: 1880 Title-Count: Two Title-Type: Waltzes Title-For: strings X-Title-Opus-Alt: B. 105 Title-Opus: Op. 54 Title-Dates: 1880 Title-Type: Violin Sonata Title-Key: F major X-Title-Opus-Alt: B. 106 Title-Opus: Op. 57 Title-Dates: 1880 Title-RAW: Moravian Duets X-Title-Opus-Alt: B. 107 Title-Opus: Op. 32 Title-Dates: 1880 Title-Type: Violin Concerto Title-Key: A minor Title-Comment: [final version of B. 96] X-Title-Opus-Alt: B. 108 Title-Opus: Op. 53 Title-Dates: 1880 Title-RAW: Album Leaves Title-For: piano Title-Opus: B. 109 Title-Dates: 1880 Title-Type: Piano Pieces X-Title-Opus-Alt: B. 110 Title-Opus: Op. 52 Title-Dates: 1880 Title-Type: Mazurkas Title-For: piano X-Title-Opus-Alt: B. 111 Title-Opus: Op. 56 Title-Dates: 1880 Title-Type: Symphony Title-No: 6 Title-Key: D major X-Title-Opus-Alt: B. 112 Title-Opus: Op. 60 Title-Dates: 1879 Title-Name: Child's Song Title-Opus: B. 113 Title-Dates: 1880 Title-Type: Polka Title-For: orchestra Title-Name: For the Prague Students X-Title-Opus-Alt: B. 114 Title-Opus: Op. 53A/1 Title-Dates: 1880 Title-Name: Ballad of King Matthias Title-Comment: [from 2nd version of The King and the Charcoalburner, B. 42] X-Title-Opus-Alt: B. 115 Title-Opus: Op. 14 Title-Dates: 1881 Title-Type: Moderato Title-Key: A major Title-For: piano Title-Opus: B. 116 Title-Dates: 1881 Title-Name: Legends Title-For: piano four hands X-Title-Opus-Alt: B. 117 Title-Opus: Op. 59 Title-Dates: 1881 Title-Name: There on our roof ... Title-Comment: [Moravian folk song] Title-Opus: B. 118 Title-Dates: 1881 Title-Type: Gallop Title-Key: E major Title-For: orchestra X-Title-Opus-Alt: B. 119 Title-Opus: Op. 53A/2 Title-Dates: 1881 Title-RAW: String Quartet Fragment Title-Key: F major Title-Opus: B. 120 Title-Dates: 1881 Title-Type: String Quartet Title-No: 11 Title-Key: C major X-Title-Opus-Alt: B. 121 Title-Opus: Op. 61 Title-Dates: 1881 Title-Name: Legends Title-For: orchestra Title-Comment: [orchestration of B. 117] X-Title-Opus-Alt: B. 122 Title-Opus: Op. 59 Title-Dates: 1881 Title-RAW: Songs on Poems by Pfleger-Moravsky Title-Opus: B. 123, No. 4 Title-Dates: 1881 Title-Name: Josef Kajetán Tyl Title-Comment: [incidental music for the play by F. F. Samberk] X-Title-Opus-Alt: B. 125 Title-Opus: Op. 62 Title-Dates: 1881 Title-Name: My Home Title-Type-After-Name: overture Title-Related-How: to Title-Related-Name: Josef Kajetán Tyl X-Title-Opus-Alt: B. 125a Title-Opus: Op. 62 Title-Dates: 1882 Title-Name: In Nature's Realm Title-Comment: [after poems by V. Hálek] X-Title-Opus-Alt: B. 126 Title-Opus: Op. 63 Title-Dates: 1882 Title-Name: Dmitrij Title-RAW: historical opera X-Title-Opus-Alt: B. 127 Title-Opus: Op. 64 Title-Dates: 1882 Title-Name: Dmitrij Title-Type-After-Name: overture X-Title-Opus-Alt: B. 127a Title-Opus: Op. 64 Title-Dates: 1882 Title-RAW: Two Evening Songs Title-Comment: [after poems by V. Hálek] X-Title-Opus-Alt: B. 128 Title-Opus: Op. 3 Title-Dates: 1882 Title-Type: Impromptu Title-Key: D minor Title-For: piano Title-Opus: B. 129 Title-Dates: 1883 Title-Type: Piano Trio Title-No: 3 Title-Key: F minor X-Title-Opus-Alt: B. 130 Title-Opus: Op. 65 Title-Dates: 1883 Title-RAW: Scherzo capriccioso Title-For: orchestra X-Title-Opus-Alt: B. 131 Title-Opus: Op. 66 Title-Dates: 1883 Title-Name: Hussite Title-RAW: Overture X-Title-Opus-Alt: B. 132 Title-Opus: Op. 67 Title-Dates: 1883 Title-Name: From the Bohemian Forest Title-For: piano four hands Title-Comment: [Orchestrated by Henk de Vlieger] X-Title-Opus-Alt: B. 133 Title-Opus: Op. 68 Title-Dates: 1883 Title-Name: The Heirs of the White Mountain Title-Comment: [revision of B. 102] X-Title-Opus-Alt: B. 134 Title-Opus: Op. 30 Title-Dates: 1883 Title-Name: The Specter's Bride Title-RAW: , dramatic cantata X-Title-Opus-Alt: B. 135 Title-Opus: Op. 69 Title-Dates: 1884 Title-RAW: Dumka Title-Key: C minor Title-For: piano X-Title-Opus-Alt: B. 136 Title-Opus: Op. 12, No. 1 Title-Dates: 1884 Title-RAW: Furiant Title-Key: G minor Title-For: piano X-Title-Opus-Alt: B. 137 Title-Opus: Op. 12, No. 2 Title-Dates: 1879 Title-Type: Humoresque Title-Key: F sharp major Title-For: piano Title-Opus: B. 138 Title-Dates: 1884 Title-Type: Ballade Title-Key: D minor Title-For: piano X-Title-Opus-Alt: B. 139 Title-Opus: Op. 15, No. 1 Title-Dates: 1884 Title-Name: The Wild Duck Title-RAW: , folk song Title-Opus: B. 140 Title-Dates: 1884 Title-Type: Symphony Title-No: 7 Title-Key: D minor X-Title-Opus-Alt: B. 141 Title-Opus: Op. 70 Title-Dates: 1885 Title-RAW: Two Czech Folk Poems Title-Opus: B. 142 Title-Dates: 1885 Title-Name: Hymn of the Czech Peasants Title-Opus: B. 143 Title-Dates: 1885 Title-Name: Saint Ludmila Title-Type-After-Name: oratorio X-Title-Opus-Alt: B. 144 Title-Opus: Op. 71 Title-Dates: 1886 Title-RAW: Slavonic Dances for piano four hands, 2nd series X-Title-Opus-Alt: B. 145 Title-Opus: Op. 72 Title-Dates: 1886 Title-Name: In Folk Tone Title-Comment: [collection of Slavonic and Czech folksongs] X-Title-Opus-Alt: B. 146 Title-Opus: Op. 73 Title-Dates: 1886 Title-RAW: Slavonic Dances Title-For: orchestra Title-Comment: [orchestration of B. 145] X-Title-Opus-Alt: B. 147 Title-Opus: Op. 72 Title-Dates: 1887 Title-RAW: Terzetto Title-Key: C major Title-For: two violins and 1 viola X-Title-Opus-Alt: B. 148 Title-Opus: Op. 74 Title-Dates: 1887 Title-RAW: Miniatures Title-For: two violins and violas X-Title-Opus-Alt: B. 149 Title-Opus: Op. 75a Title-Dates: 1887 Title-RAW: Romantic Pieces Title-For: violin and piano X-Title-Opus-Alt: B. 150 Title-Opus: Op. 75 Title-Dates: 1887 Title-Name: The King and the Charcoalburner Title-Comment: [3rd version of B. 21] X-Title-Opus-Alt: B. 151 Title-Opus: Op. 14 Title-Dates: 1887 Title-Name: Cypresses Title-Comment: [arrangements of a dozen pieces from B. 11 (Nos. 2-4, 6-9, 12, 14, 16-18)] Title-Opus: B. 152 Title-Dates: 1887 Title-Type: Mass Title-Key: D major Title-Comment: [1st version] X-Title-Opus-Alt: B. 153 Title-Opus: Op. 86 Title-Dates: 1887 Title-RAW: Psalm 149 Title-For: choir and orchestra Title-Comment: [2nd version of B. 91] X-Title-Opus-Alt: B. 154 Title-Opus: Op. 79 Title-Dates: 1887 Title-Type: Piano Quintet Title-No: 2 Title-Key: A major X-Title-Opus-Alt: B. 155 Title-Opus: Op. 81 Title-Dates: 1887 Title-Name: Two Little Pearls Title-For: piano Title-Opus: B. 156 Title-Dates: 1887 Title-RAW: Four Songs on Poems by O. Malybrok-Stieler X-Title-Opus-Alt: B. 157 Title-Opus: Op. 82 Title-Dates: 1888 Title-RAW: Album Leaf Title-For: piano Title-Opus: B. 158 Title-Dates: 1888 Title-Name: The Jacobin Title-Type-After-Name: opera X-Title-Opus-Alt: B. 159 Title-Opus: Op. 84 Title-Dates: 1888 Title-Name: Love Songs Title-Comment: [revision of Cypresses, B. 11] X-Title-Opus-Alt: B. 160 Title-Opus: Op. 83 Title-Dates: 1888 Title-Name: Poetic Tone Poems Title-For: piano X-Title-Opus-Alt: B. 161 Title-Opus: Op. 85 Title-Dates: 1889 Title-Type: Piano Quartet Title-No: 2 Title-Key: E flat major X-Title-Opus-Alt: B. 162 Title-Opus: Op. 87 Title-Dates: 1889 Title-Type: Symphony Title-No: 8 Title-Key: G major X-Title-Opus-Alt: B. 163 Title-Opus: Op. 88 Title-Dates: 1889 Title-Type: Gavotte Title-For: three violins Title-Opus: B. 164 Title-Dates: 1890 Title-Type: Requiem X-Title-Opus-Alt: B. 165 Title-Opus: Op. 89 Title-Dates: 1890 Title-Type: Piano Trio Title-No: 4 Title-Key: E minor Title-Name: Dumky X-Title-Opus-Alt: B. 166 Title-Opus: Op. 90 Title-Dates: 1891 Title-Type: Fanfares Title-For: four trumpets and timpani Title-Opus: B. 167 Title-Dates: 1891 Title-Name: In Nature's Realm Title-RAW: , concert overture X-Title-Opus-Alt: B. 168 Title-Opus: Op. 91 Title-Dates: 1891 Title-Name: Carnival Overture X-Title-Opus-Alt: B. 169 Title-Opus: Op. 92 Title-Dates: 1891 Title-RAW: Slavonic Dance Title-Key: E minor Title-For: violin and piano Title-Comment: [arrangement of B. 78 No. 2] X-Title-Opus-Alt: B. 170 Title-Opus: Op. 46, No. 2 Title-Dates: 1891 Title-Type: Rondo Title-Key: G minor Title-For: cello and piano X-Title-Opus-Alt: B. 171 Title-Opus: Op. 94 Title-Dates: 1891 Title-RAW: Slavonic Dances in A and G minor Title-For: cello and piano Title-Comment: [arrangement of B. 78 Nos. 3 and 8] X-Title-Opus-Alt: B. 172 Title-Opus: Op. 46, No. 3,8 Title-Dates: 1891 Title-Name: Silent Woods Title-RAW: for cello and piano (Published as Title-Name: Waldesruhe Title-Punct: ) Title-Comment: [arrangement of Ze Sumavy (From the Bohemian Forest) Op.68 / B.133 No.5] X-Title-Opus-Alt: B. 173 Title-Opus: Op. 68, No. 5 Title-Dates: 1891 Title-Name: Othello Title-RAW: Overture X-Title-Opus-Alt: B. 174 Title-Opus: Op. 93 Title-Dates: 1892 Title-Type: Mass Title-Key: D major Title-Comment: [2nd version of B. 153] X-Title-Opus-Alt: B. 175 Title-Opus: Op. 86 Title-Dates: 1892 Title-RAW: Te Deum X-Title-Opus-Alt: B. 176 Title-Opus: Op. 103 Title-Dates: 1892 Title-Name: The American Flag Title-Type-After-Name: cantata Title-Comment: [after a poem by J. R. Drake] X-Title-Opus-Alt: B. 177 Title-Opus: Op. 102 Title-Dates: 1893 Title-Type: Symphony Title-No: 9 Title-Key: E minor Title-Name: From the New World X-Title-Opus-Alt: B. 178 Title-Opus: Op. 95 Title-Dates: 1893 Title-Type: String Quartet Title-No: 12 Title-Key: F major Title-Name: American X-Title-Opus-Alt: B. 179 Title-Opus: Op. 96 Title-Dates: 1893 Title-Type: String Quintet Title-No: 3 Title-Key: E flat major Title-Name: American X-Title-Opus-Alt: B. 180 Title-Opus: Op. 97 Title-Dates: 1893 Title-Type: Rondo Title-Key: G minor Title-For: cello and orchestra Title-Comment: [arrangement of B. 171] X-Title-Opus-Alt: B. 181 Title-Opus: Op. 94 Title-Dates: 1893 Title-Name: Silent Woods Title-For: cello and orchestra Title-Comment: [arrangement of Ze Sumavy (From the Bohemian Forest) Op.68/B.173 No.5] X-Title-Opus-Alt: B. 182 Title-Opus: Op. 68, No. 5 Title-Dates: 1893 Title-Type: Violin Sonatina Title-Key: G major X-Title-Opus-Alt: B. 183 Title-Opus: Op. 100 Title-Dates: 1893 Title-Type: Suite Title-Key: A major Title-Name: American Title-For: piano X-Title-Opus-Alt: B. 184 Title-Opus: Op. 98 Title-Dates: 1894 Title-RAW: Biblical Songs X-Title-Opus-Alt: B. 185 Title-Opus: Op. 99 Title-Dates: 1894 Title-Name: Dmitrij Title-Comment: [2nd version of B. 127] X-Title-Opus-Alt: B. 186 Title-Opus: Op. 64 Title-Dates: 1894 Title-Type: Humoresques Title-For: piano X-Title-Opus-Alt: B. 187 Title-Opus: Op. 101 Title-Dates: 1894 Title-Count: Two Title-Type: Piano Pieces Title-Opus: B. 188 Title-Dates: 1894 Title-RAW: Biblical Songs Title-Comment: [orchestration of B. 185] X-Title-Opus-Alt: B. 189 Title-Opus: Op. 99 Title-Dates: 1895 Title-Type: Suite Title-Key: A major Title-Name: American Title-Comment: [orchestration of B. 184] X-Title-Opus-Alt: B. 190 Title-Opus: Op. 98b Title-Dates: 1895 Title-Type: Cello Concerto Title-Key: B minor X-Title-Opus-Alt: B. 191 Title-Opus: Op. 104 Title-Dates: 1895 Title-Type: String Quartet Title-No: 13 Title-Key: G major X-Title-Opus-Alt: B. 192 Title-Opus: Op. 106 Title-Dates: 1895 Title-Type: String Quartet Title-No: 14 Title-Key: A flat major X-Title-Opus-Alt: B. 193 Title-Opus: Op. 105 Title-Dates: 1895 Title-Name: Lullaby Title-Comment: [after a poem by F. L. Jelinek] Title-Opus: B. 194 Title-Dates: 1895 Title-Name: The Water Goblin Title-Type-After-Name: symphonic poem X-Title-Opus-Alt: B. 195 Title-Opus: Op. 107 Title-Dates: 1896 Title-Name: The Noon Witch Title-Type-After-Name: symphonic poem X-Title-Opus-Alt: B. 196 Title-Opus: Op. 108 Title-Dates: 1896 Title-Name: The Golden Spinning Wheel Title-Type-After-Name: symphonic poem X-Title-Opus-Alt: B. 197 Title-Opus: Op. 109 Title-Dates: 1896 Title-Name: The Wild Dove Title-Type-After-Name: symphonic poem X-Title-Opus-Alt: B. 198 Title-Opus: Op. 110 Title-Dates: 1896 Title-Name: A Hero's Song Title-Type-After-Name: symphonic poem X-Title-Opus-Alt: B. 199 Title-Opus: Op. 111 Title-Dates: 1897 Title-Name: Jacobin Title-Comment: [revision of B. 159] X-Title-Opus-Alt: B. 200 Title-Opus: Op. 84 Title-Dates: 1897 Title-Name: The Devil and Kate Title-Type-After-Name: opera X-Title-Opus-Alt: B. 201 Title-Opus: Op. 112 Title-Dates: 1899 Title-Type: Overture Title-Related-How: to Title-Related-Name: Kate and the Devil X-Title-Opus-Alt: B. 201a Title-Opus: Op. 112 Title-Dates: 1899 Title-Name: Festival Song Title-Comment: [after a poem by J. Vrchlicky] X-Title-Opus-Alt: B. 202 Title-Opus: Op. 113 Title-Dates: 1900 Title-Name: Rusalka Title-Type-After-Name: opera X-Title-Opus-Alt: B. 203 Title-Opus: Op. 114 Title-Dates: 1900 Title-Type: Overture Title-Related-How: to Title-Related-Name: Rusalka X-Title-Opus-Alt: B. 203a Title-Opus: Op. 114 Title-Dates: 1900 Title-Name: Song of the Smith of Lesetin Title-Opus: B. 204 Title-Dates: 1901 Title-Name: Saint Ludmila Title-Comment: [opera adapted from cantata B. 144] X-Title-Opus-Alt: B. 205 Title-Opus: Op. 71 Title-Dates: 1901 Title-Name: Armida Title-Type-After-Name: opera X-Title-Opus-Alt: B. 206 Title-Opus: Op. 115 Title-Dates: 1903 Title-Type: Overture Title-Related-How: to Title-Related-Name: Armida X-Title-Opus-Alt: B. 206a Title-Opus: Op. 115 Title-Dates: 1903 �����������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/Normalize/Text/Music_Fields/A_Schnittke.comp���������������������������������������0000700�0000000�0000000�00000262635�10641104532�020513� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# format = mail-header ## Raw data (and opus numbers) from http://home.wanadoo.nl/ovar/schnopus.htm ## <H3>Works</H3> ## In fact Alfred Schnittke didn't use opus numbers. ## Here opus numbers are used to facilitate indexing.<P> Title-Type: Concerto Title-For: accordion and orchestra Title-Opus: 1 Title-Dates: 1949 ## <DL><DD>Lost</DL> Title-Type: Incidental music Title-Related-How: to Title-Related-Name: Mayakovsky's Debut Title-Opus: 2 Title-Dates: 1950-1952 Title-RAW: Poème Title-For: piano and orchestra Title-Opus: 3 Title-Dates: 1953 Title-Name: The Passing Line of Clouds Grows Thinner Title-For: voice and piano Title-Opus: 4 Title-Dates: 1953 ## <DL><DD>To a poem by Alexander Pushkin.</DL> Title-Type: Fugue Title-For: violin solo Title-Opus: 5 Title-Dates: 1953 ## <DL><DD><I>CD <A HREF="/ovar/sovrev/kancheli/bis1392.htm">BIS CD 1392</A>: Vadim Gluzman (violin)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/naxos8554728.htm">Naxos 8.554728</A>: Mark Lubotsky (violin)</I></DL> Title-Count: Six Title-Type: Preludes Title-For: piano Title-Opus: 6 Title-Dates: 1953-1954 Title-Type: Variations Title-For: piano Title-Opus: 7 Title-Dates: 1954-1955 Title-Type: Sonata Title-For: violin and piano Title-Opus: 8 Title-Dates: 1954-1955 ## <DL><DD><I>CD Warner Classics 2564 61329-2: Daniel Hope (violin), Sebastian Knauer (piano)</I></DL> Title-Name: Dusk (Sumrak) Title-For: voice and piano Title-Opus: 9 Title-Dates: 1954-1955 ## <DL><DD>To a poem by Fyodor Tyutchev.</DL> Title-Name: Beggar (Nishchy) Title-For: voice and piano Title-Opus: 10 Title-Dates: 1954-1955 ## <DL><DD>To a poem by Mikhail Lermontov.</DL> Title-Name: Birch Tree (Beryozka) Title-For: voice and piano Title-Opus: 11 Title-Dates: 1954-1955 ## <DL><DD>To a poem by Stepan Shchiparev.</DL> Title-Count: Three Title-Type: Choruses Title-For: mixed chorus Title-Opus: 12 Title-Dates: 1954/1955 ## <DL><DD>To a poems by Alexander Prokofiev, Mikhail Isakovsky and Alexander Mashistov.<P> ## <I>CD Delos DE 3264: The Spiritual Revival Choir of Moscow's Schnittke Institute of Music, Lev Kontorovich (cond)</I></DL> Title-Type: Scherzo Title-For: piano quintet Title-Opus: 13 Title-Dates: 1954-1955 Title-Type: Intermezzo Title-For: piano quintet Title-Opus: 14 Title-Dates: 1954-1955 Title-Type: Suite Title-For: strings Title-Opus: 15 Title-Dates: 1954-1955 Title-Type: Overture Title-For: orchestra Title-Opus: 16 Title-Dates: 1954-1955 Title-Type: Symphony Title-No: 0 Title-Opus: 17 Title-Dates: 1956-1957 Title-Type: Concerto Title-No: 1 Title-For: violin and orchestra Title-Opus: 18 Title-Dates: 1957 ## <DL><DD>Revised in 1962.<BR> ## In four movements:<BR> ## 1. Allegro ma non troppo. Tempo iniziale - 13 min.<BR> ## 2. Presto (This movement may be omitted) - 5 min.<BR> ## 3. Andante - 10 min.<BR> ## 4. Allegro scherzando - 9 min.<P> ## <I>CD BIS CD 487: Malmö SO, Eri Klas (cond), Mark Lubotsky (violin)</I></DL> Title-Name: Nagasaki Title-Type-After-Name: oratorio Title-For: mezzo-soprano, mixed chorus and symphony orchestra Title-Opus: 19 Title-Dates: 1958 ## <DL><DD>On a text by Anatoly Sofronov, Georgi Fere, Eneda Eisaku and Simedziku Toson.<BR> ## In five movements:<BR> ## 1. Nagasaki: city of grief<BR> ## 2. Morning (attacca)<BR> ## 3. That fateful day ...<BR> ## 4. After the holocaust<BR> ## 5. The sun of peace</DL> Title-Name: Vocalise Title-For: mixed chorus a cappella Title-Opus: 20 Title-Dates: 1958 Title-RAW: Songs of War and Peace, cantata Title-For: soprano, mixed chorus and symphony orchestra Title-Opus: 21 Title-Dates: 1959 ## <DL><DD>On texts by Anatoly Leontyev and Andrei Pokrovsky, based on modern Russian folk songs.<BR> ## In four movements:<BR> ## 1. Golden grass on ancient burial mounds<BR> ## 2. War is rumbling in the fields<BR> ## 3. My heart moans<BR> ## 4. The storm has passed. The sky is clear</DL> Title-Type: String Quartet Title-Opus: 22 Title-Dates: 1959 ## <DL><DD>Unfinished.</DL> Title-Type: Concerto Title-For: piano and orchestra Title-Opus: 23 Title-Dates: 1960 ## <DL><DD>1. Allegro<BR> ## 2. Andante (attacca)<BR> ## 3. Allegro</DL> Title-Type: Concerto Title-For: electric instruments Title-Opus: 24 Title-Dates: 1960 ## <DL><DD>Unfinished.</DL> Title-RAW: Poem about Cosmos Title-For: orchestra Title-Opus: 25 Title-Dates: 1961 Title-Name: The Eleventh Commandment Title-Type-After-Name: opera in two acts Title-Opus: 26 Title-Dates: 1962 ## <DL><DD>Libretto by Marina Churova, Georgy Ansimov and Alfred Schnittke.</DL> Title-RAW: Children's Suite Title-For: small orchestra Title-Opus: 27 Title-Dates: 1962 ## <DL><DD>In six movements:<BR> ## 1. Moderato<BR> ## 2. Vivo<BR> ## 3. Moderato<BR> ## 4. Andantino<BR> ## 5. Allegro<BR> ## 6. Andantino</DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Introduction Title-Opus: 28 Title-Dates: 1962 Title-Type: Music Title-Related-How: to the TV-production Title-Related-Name: The Rose and the Cross Title-Opus: 29 Title-Dates: 1962 ## <DL><DD>After Alexander Blok</DL> Title-Type: Sonata Title-No: 1 Title-For: violin and piano Title-Opus: 30 Title-Dates: 1963 ## <DL><DD>In three movements:<BR> ## 1. Andante - 3 min.<BR> ## 2. Allegretto - 6 min.<BR> ## 3. Largo - 5 min.<BR> ## 4. Allegretto scherzando. Largo - 6 min.<P> ## <I>LP Chandos ABRD 1089: Rostislav Dubinsky (violin), Ljuba Edlina (piano)<BR> ## CD ASV CDDCA 868: Mateja Marinkovic (violin), Linn Hendry (piano)<BR> ## CD BIS CD 364: Christian Bergqvist (violin), Roland Pontinen (piano)<BR> ## CD BIS CD 527: U. Wallin (violin), Roland Pöntinen (piano)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/bridge9104.htm">Bridge 9104</A>: Joanna Kurkowicz (violin), Sergey Schepkin (piano)<BR> ## CD Catalyst 09026-62668-2: Maria Bachmann (violin), Jon Klibonoff (piano)<BR> ## CD Chandos CHAN 8343: Rostislav Dubinsky (violin), Ljuba Edlina (piano)<BR> ## CD Duchesne CD 71 532: I. Tseitlin (violin), P. Dheur (piano)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/hmn911701.htm">Harmonia Mundi HMN 911701</A>: Graf Mourja (violin), Elena Rozanova(piano)<BR> ## CD Live Classics LCL 191: Oleg Kagan (violin), Vassily Lobanov (piano)<BR> ## CD Ondine ODE 800-2: Mark Lubotsky (violin), Ralf Gothoni (piano)<BR> ## CD Ondine ODE 901-2: Pekka Kuusisto (violin), Raija Kerppo (piano)<BR> ## CD Proud Sound PROU CD 139: Rusne Mataityte (violin), Margrit-Julia Zimmermann (piano)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/sp22579.htm">Sonora Products SO 22579 CD</A>: Valery Gradow (violin), Inna Heifitz (piano)<BR> ## CD Stradivarius STR 33675: Francesco D'Orazio (violin), Giampaolo Nuti (piano)<BR> ## CD Vienna Modern Masters VMM 2025: Vasilij Meljnikov (violin), Aljoscha Starc (piano)</I></DL> Title-RAW: Prelude and Fugue Title-For: piano Title-Opus: 31 Title-Dates: 1963 ## <DL><DD>1. Andante<BR> ## 2. Allegretto<P> ## <I>CD Chandos CHAN 9704: Boris Berman (piano)</I></DL> Title-Type: Incidental music Title-Related-How: to Title-Related-Name: Caesar and Cleopatra Title-Opus: 32 Title-Dates: 1963 ## <DL><DD>Play in five acts by George Bernard Shaw.</DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Aim the Barrage at Us Title-Opus: 33 Title-Dates: 1963-1964 Title-Type: Music Title-For: piano and chamberorchestra Title-Opus: 34 Title-Dates: 1964 ## <DL><DD>In four movements:<BR> ## 1. Variazioni<BR> ## 2. Cantus firmus<BR> ## 3. Cadenza (attacca)<BR> ## 4. Basso ostinato<P> ## <I>CD Chandos CHAN 9466: Russian State SO, Gennadi Rozhdestvensky (cond), Vassily Lobanov, Vassily (piano)</I></DL> Title-Type: Music Title-For: chamberorchestra Title-Opus: 35 Title-Dates: 1964 Title-RAW: Three Verses of Marina Tsvetayeva Title-For: mezzo-soprano (or soprano) and piano Title-Opus: 36 Title-Dates: 1965 Title-Type: Dialogue Title-For: cello and seven instrumentalists Title-Opus: 37 Title-Dates: 1965 ## <DL><DD>Duration: 12 minutes.<P> ## <I>CD BIS CD 568: (Arrangement for trombone by Ch. Lindberg and A. Schnittke) Tapiola Sinfonietta, Osmo Vänskä (cond), Christian Lindberg (trombone)<BR> ## CD BIS CD 568: (Arrangement for trombone by Ch. Lindberg and A. Schnittke) Tapiola Sinfonietta, Osmo Vänskä (cond), Christian Lindberg (trombone)<BR> ## CD Live Classics LCL 202: Gnessin Chamber Orchestra, Yuri Nikolaevsky (cond), Natalia Gutman (cello)</I></DL> Title-RAW: Improvisation and Fugue Title-For: piano Title-Opus: 38 Title-Dates: 1965 ## <DL><DD>1. Lento<BR> ## 2. Vivo<P> ## <I>CD Chandos CHAN 9704: Boris Berman (piano)</I></DL> Title-RAW: Variations on One Chord Title-For: piano Title-Opus: 39 Title-Dates: 1965 ## <DL><DD>Grave / Lento / Allegretto / Andante / Agitato / Lento / Maestoso / Andante<P> ## <I>CD Chandos CHAN 9704: Boris Berman (piano)</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Adventures of a Dentist Title-Opus: 40 Title-Dates: 1965 Title-RAW: Charleston Title-For: stage orchestra/jazz ensemble Title-Related-How: from the Film Title-Related-Name: The Adventures of a Dentist Title-Opus: 40a Title-Dates: 1965 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Concealed Caballero Title-Opus: 41 Title-Dates: 1965 Title-Type: Concerto Title-No: 2 Title-For: violin and chamber orchestra Title-In-Movements: in a single movement Title-Opus: 42 Title-Dates: 1966 ## <DL><DD>Dedicated to Mark Lubotsky. ## Duration: 23 minutes.<P> ## <I>LP Philips 411 107-1: Basle Symphony Orchestra, Heinz Holliger (cond), Gidon Kremer (violin)<BR> ## CD BIS CD 487: Malmö SO, Eri Klas (cond), Mark Lubotsky (violin)<BR> ## CD Teldec 4509-94540-2: Chamber Orchestra of Europe, Gidon Kremer (violin)</I></DL> Title-Type: String Quartet Title-No: 1 Title-Opus: 43 Title-Dates: 1966 ## <DL><DD>Commissioned by Rostislav Dubinsky, Primarius of the Borodin Quartet.<BR> ## In three movements:<BR> ## 1. Sonata - 8 min.<BR> ## 2. Canon - 4 min.<BR> ## 3. Cadenza - 7 min.<P> ## <I>CD <A HREF="/ovar/sovrev/schnittke/arco54.htm">Arco Diva UP 0054-2 131</A>: Kapralova Quartet<BR> ## CD BIS CD 467: Tale Quartet<BR> ## CD Nonesuch 79500-2: Kronos Quartet</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Just a Little Joke Title-Opus: 44 Title-Dates: 1966 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Day Stars Title-Opus: 45 Title-Dates: 1966 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Commissar Title-Opus: 46 Title-Dates: 1967 ## <DL><DD><I>CD <A HREF="/ovar/sovrev/schnittke/cap71041.htm">Capriccio hybrid CD/SACD CC 71041</A>: Berlin Radio Symphony Orchestra, Frank Strobel (cond)</I></DL> Title-Name: Pianissimo Title-For: large symphony orchestra Title-Opus: 47 Title-Dates: 1968 ## <DL><DD>Duration: 9 minutes.<P> ## <I>CD BIS CD 427: Gothenburg SO, Neeme Jarvi (cond)</I></DL> Title-Type: Serenada Title-For: violin, clarinet, double-bass, piano and percussion Title-Opus: 48 Title-Dates: 1968 ## <DL><DD>1. -<BR> ## 2. Lento<BR> ## 3. Allegretto<P> ## <I>CD Hyperion CDA 66885: Capricorn, Timothy Mason (cond)</I></DL> Title-Type: Sonata Title-No: 2 Title-For: violin and piano Title-Name: Quasi una sonata Title-In-Movements: in a single movement Title-Opus: 49 Title-Dates: 1968 ## <DL><DD>Dedicated to Mark Lubotsky and Lyubov Yedlina.<P> ## <I>CD ASV CDDCA 868: Mateja Marinkovic (violin), Linn Hendry (piano)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/bridge9104.htm">Bridge 9104</A>: Joanna Kurkowicz (violin), Sergey Schepkin (piano)<BR> ## CD Duchesne CD 71 532: I. Tseitlin (violin), P. Dheur (piano)<BR> ## CD Northern Flowers NF 9908: Lidia Kovalenko (violin), Yuri Serov (piano)<BR> ## CD Ondine ODE 800-2: Mark Lubotsky (violin), Ralf Gothoni (piano)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/phoenix150.htm">Phoenix PHCD 150</A>: Levon Ambartsumian (violin), Anatoly Sheludyakov (piano)<BR> ## CD Russian Disc RDCD 10001: Levon Ambartsumian (violin), Anatoly Sheludyakov (piano)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/sp22579.htm">Sonora Products SO 22579 CD</A>: Valery Gradow (violin), Inna Heifitz (piano)<BR> ## CD Stradivarius STR 33675: Francesco D'Orazio (violin), Giampaolo Nuti (piano)</I></DL> Title-Type: Sonata Title-For: violin and chamber orchestra Title-Opus: 50 Title-Dates: 1968 ## <DL><DD>Arrangement of Violin Sonata no. 1 (1963).<BR> ## 1. Andante - 3 min.<BR> ## 2. Allegretto - 5 min. 30 sec.<BR> ## 3. Largo - 5 min. 30 sec.<BR> ## 4. Allegretto scherzando. Allegro - 5 min. 30 sec.<P> ## <I>CD BIS CD 537: Stockholm Chamber Orchestra, Lev Markiz (cond), Christian Bergqvist (violin)<BR> ## CD Capriccio 67 016: Moscow Virtuosi, V. Spivakov (cond)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/chan9891.htm">Chandos CHAN 9891</A>: Musici de Montreal, Yuli Turovsky (conductor), Stepan Arman (violin)<BR> ## CD Nimbus NI 5582: English SO, William Boughton (cond), Daniel Hope (violin)</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Angel Title-Opus: 51 Title-Dates: 1968 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: July the Sixth Title-Opus: 52 Title-Dates: 1968 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Day Stars Title-Opus: 53 Title-Dates: 1968 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Ownerlewss House Title-Opus: 54 Title-Dates: 1968 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Glass Harmonica Title-Opus: 55 Title-Dates: 1968 ## <DL><DD><I>CD <A HREF="/ovar/sovrev/schnittke/cap71061.htm">Capriccio 71061</A> (SACD): Rundfunk-Sinfonieorchester Berlin, Frank Strobel (cond)</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Used Cartridge Cases Title-Opus: 56 Title-Dates: 1968 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Night Call Title-Opus: 57 Title-Dates: 1968 Title-RAW: Magdalena's Song for voice and piano on words by Boris Pasternak Title-Opus: 58 Title-Dates: 1968 Title-RAW: "Potok" ("Stream"), electronic composition Title-Opus: 59 Title-Dates: 1969 ## <DL><DD><I>Melodiya C 30721</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Waltz Title-Opus: 60 Title-Dates: 1969 ## <DL><DD><I>CD <A HREF="/ovar/sovrev/schnittke/cap71061.htm">Capriccio 71061</A> (SACD): Rundfunk-Sinfonieorchester Berlin, Frank Strobel (cond)</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Sick at Heart Title-Opus: 61 Title-Dates: 1969 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: A Ballerina Aboard Title-Opus: 62 Title-Dates: 1969 Title-RAW: Incidental music to Alexander Pushkin's poem Title-Name: Boris Godunov Title-Opus: 63 Title-Dates: 1969 Title-Type: Concerto Title-For: oboe, harp and string orchestra Title-In-Movements: in a single movement Title-Opus: 64 Title-Dates: 1971 ## <DL><DD>Dedicated to Heinz Holliger, Ursula Holliger and the Zagreb Soloists Chamber Orchestra.<BR> ## Lento (one movement)<BR> ## Duration: 17 minutes.<P> ## <I>CD BIS CD 377: New Stockholm Chamber Orchestra, Lev Markiz (cond), Helen Jahren (oboe), Kjell Axel Lier (harp)</I></DL> Title-RAW: Canon in Memoriam Igor Strawinsky Title-For: string quartet Title-Opus: 65 Title-Dates: 1971 ## <DL><DD>Commissioned by the music magazine 'Tempo', London.<BR> ## Lento (one movement).<BR> ## Duration: 4 minutes 30 seconds.<P> ## <I>CD BIS CD 547: Tale Quartet<BR> ## CD DG 431 686-2: Hagen Quartet<BR> ## CD Etcetera KTC 1124: Mondriaan Quartet<BR> ## CD Nonesuch 79500-2: Kronos Quartet<BR> ## CD RCA-BMG Catalyst 82876 64283-2: Chilingirian Quartet: Levon Chilingirian (violin), Charles Sewart (violin), Simon Rowland-Jones (viola), Philip De Groote (cello)</I></DL> Title-Count: Eight Title-Type: Pieces Title-For: piano Title-Opus: 66 Title-Dates: 1971 ## <DL><DD>1. Folk Song Andantino<BR> ## 2. In the Mountains Moderato<BR> ## 3. Cuckoo and Woodpecker Vivo<BR> ## 4. Melody Andante<BR> ## 5. Tale Lento<BR> ## 6. Play Allegro<BR> ## 7. Children's Piece Andantino<BR> ## 8. March Allegretto<P> ## <I>CD Chandos CHAN 9704: (Four Pieces) Boris Berman (piano)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/mv06.htm">MV Productions SPPS CD06</A>: Svetlana Ponomarëva (piano)</I></DL> Title-Name: Labyrinths Title-Type-After-Name: ballet Title-In-Movements: in five episodes Title-Opus: 67 Title-Dates: 1971 ## <DL><DD>1. Moderato. Allegretto. Meno mosso. Adagio - 12 min. 30 sec.<BR> ## 2. Moderato - 3 min. 30 sec.<BR> ## 3. Allegretto - 2 min.<BR> ## 4. Agitato - 4 min.<BR> ## 5. Cadenza. Andante. Maestoso - 16 min.<P> ## <I>CD BIS CD 557: Malmö SO Chamber Ensemble, Lev Markiz (cond)</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Belorussian Station Title-Opus: 68 Title-Dates: 1971 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Uncle Vanya Title-Opus: 69 Title-Dates: 1971 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Sport, Sport, Sport Title-Opus: 70 Title-Dates: 1971 ## <DL><DD><I>CD <A HREF="/ovar/sovrev/schnittke/ocd606.htm">Olympia OCD 606</A>: USSR Cinematography SO, Emin Khachaturian (cond)</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Seagull Title-Opus: 71 Title-Dates: 1971 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Our Gagarin Title-Opus: 72 Title-Dates: 1971 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Last Flight of the Albatross Title-Opus: 73 Title-Dates: 1971 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: You and Me Title-Opus: 74 Title-Dates: 1971 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: A Cottage in Kolomna Title-Opus: 75 Title-Dates: 1971 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Wardrobe Title-Opus: 76 Title-Dates: 1971 Title-Name: Verses Written in the Sleeplessness of the Night Title-For: voice and piano Title-Opus: 77 Title-Dates: 1971 ## <DL><DD>On verses by Alexander Pushkin.<BR> ## Originally composed for a television production.</DL> Title-Type: Symphony Title-No: 1 Title-Opus: 78 Title-Dates: 1969-1972 ## <DL><DD>In four movements:<BR> ## 1. Senza tempo / Moderato / Allegro / Andante - 21 min.<BR> ## 2. Allegretto - 15 min.<BR> ## 3. Lento (attacca) - 9 min.<BR> ## 4. Lento - 27 min.<P> ## <I>CD BIS CD 577: Royal Stockholm PO, Leif Segerstam (cond), Ben Kallenberg (violin), Ake Lannerholm (trombone), Carl-Axel Dominique (piano)<BR> ## CD Chandos CHAN 9417: Russian State SO, Gennadi Rozhdestvensky (cond)<BR> ## CD Melodiya SUCD 10 00062: USSR Ministry of Culture SO, Gennadi Rozhdestvensky (cond)</I></DL> Title-RAW: "Voices of Nature" (without text) Title-For: ten female voices and vibraphone Title-Opus: 79 Title-Dates: 1972 ## <DL><DD>Lento (one movement).<BR> ## Duration: 5 minutes.<P> ## <I>CD <A HREF="/ovar/sovrev/schnittke/bis1157.htm">BIS 1157</A>: Swedish Radio Choir, Tonu Kaljuste (cond), Jonny Ronnlund (vibraphone)<BR> ## CD Chandos CHAN 9480: Danish National Radio Choir, Stefan Parkman (cond), Gert Sorensen (vibraphone)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/cda67297.htm">Hyperion CDA 67297</A>: Holst Singers, Stephen Layton (cond)</I></DL> Title-RAW: Suite in Old Style Title-For: violin and piano (or harpsichord) Title-Opus: 80 Title-Dates: 1972 ## <DL><DD>In five movements:<BR> ## 1.Pastorale - 4 min.<BR> ## 2. Ballet - 2 min.<BR> ## 3. Minuet - 4 min.<BR> ## 4. Fugue - 2 min. 30 sec.<BR> ## 5. Pantomime - 3 min. 30 sec.<P> ## <I>LP Chandos ABRD 1089: Rostislav Dubinsky (violin), Ljuba Edlina (piano)<BR> ## LP Melodiya C10 20223 005: V. Kafelnikov (trumpet), L. Grabko (piano)<BR> ## LP Melodiya C10 25671 007: E. Grach (violin), A. Maloletkova (piano)<BR> ## CD Arco Diva UP 0077-2: Ivana Tomaskova (violin), Renata Ardasevova (piano)<BR> ## CD ASV CDDCA 877: Mateja Marinkovic (violin), Linn Hendry (piano)<BR> ## CD BIS CD 527: U. Wallin (violin), R. Pöntinen (piano)<BR> ## CD <A HREF="/ovar/sovrev/kancheli/bis1392.htm">BIS CD 1392</A>: Vadim Gluzman (violin), Angela Yoffe (piano)<BR> ## CD Chandos CHAN 8343: Rostislav Dubinsky (violin), Ljuba Edlina (piano)<BR> ## CD Duchesne CD 71 532: I. Tseitlin (violin), P. Dheur (piano)<BR> ## CD Ondine ODE 800-2: Mark Lubotsky (violin), Ralf Gothoni (piano)<BR> ## CD Philips 456-016-2: (Arrangement by Gidon Kremer) Gidon Kremer (violin), Naoko Yoshino (harpsichord)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/sp22579.htm">Sonora Products SO 22579 CD</A>: Valery Gradow (violin), Inna Heifitz (piano)<BR> ## CD Stradivarius STR 33675: Francesco D'Orazio (violin), Giampaolo Nuti (piano)<BR> ## CD Talent DOM 2910 125: Daniel Rubenstein (violin), Muhiddin Dürrüoglu-Demiriz (piano)</I></DL> Title-RAW: Suite in Old Style Title-For: cello and piano Title-Opus: 80a ## <DL><DD>Arranged by Daniel Shafran.<BR> ## In five movements:<BR> ## 1.Pastorale - 4 min.<BR> ## 2. Ballet - 2 min.<BR> ## 3. Minuet - 4 min.<BR> ## 4. Fugue - 2 min. 30 sec.<BR> ## 5. Pantomime - 3 min. 30 sec.<P> ## <I>CD <A HREF="/ovar/sovrev/kancheli/asv4006.htm">ASV Gold GLD 4006</A>: Nikolai Demidenko (cello), Leonid Gorokhov (piano)<BR> ## CD Brilliant Classics 93096 (7 CD-set): Daniel Shafran (cello), Anton Ginzburg (piano)</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Chile in Struggle, Hope and Alarm Title-Opus: 81 Title-Dates: 1972 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Cheer Up, the Worst is Yet to Come Title-Opus: 82 Title-Dates: 1972 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Butterfly Title-Opus: 83 Title-Dates: 1972 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Strange Little Frog Title-Opus: 84 Title-Dates: 1972 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Where the Arbat crosses Bubulinas Street Title-Opus: 85 Title-Dates: 1972 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Hot Snow Title-Opus: 86 Title-Dates: 1972 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Right to Jump Title-Opus: 87 Title-Dates: 1972 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: In the World of the Fables Title-Opus: 88 Title-Dates: 1973 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Arduous Roads of Peace/The Balance of Terror Title-Opus: 89 Title-Dates: 1973 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: My Past and My Thoughts Title-Opus: 90 Title-Dates: 1973 ## <DL><DD><I>CD <A HREF="/ovar/sovrev/schnittke/cpo999796.htm">CPO 999 796-2</A>: Radio SO Berlin, Frank Strobel (cond)</I></DL> Title-RAW: Gratulationsrondo Title-Key: C major Title-For: violin and piano Title-Opus: 91 Title-Dates: 1973 ## <DL><DD>Dedicated to Rostislav Dubinsky on occasion of his 50th birthday.<BR> ## Allegro (one movement).<BR> ## Duration: 10 minutes.<P> ## <I>CD ASV CDDCA 877: Mateja Marinkovic (violin), Linn Hendry (piano)<BR> ## CD BIS CD 527: U. Wallin (violin), R. Pöntinen (piano)<BR> ## CD Northern Flowers NF 9908: Lidia Kovalenko (violin), Yuri Serov (piano)<BR> ## CD Stradivarius STR 33675: Francesco D'Orazio (violin), Giampaolo Nuti (piano)<BR> ## CD Teldec 4509-94540-2: Gidon Kremer (violin), Christoph Eschenbach (piano)</I></DL> Title-Type: Incidental music Title-Related-How: to Bertold Brecht's play Title-Related-Name: Turandot Title-Opus: 92 Title-Dates: 1973 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The World Today/And Yet I Believe Title-Opus: 93 Title-Dates: 1972-1974 Title-RAW: "Der Gelbe Klang" ("Yellow Sound"), scenic composition Title-For: pantomime, instrumental ensemble and tape (mixed chorus) Title-Opus: 94 Title-Dates: 1973-1974 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Agony of Death Title-Opus: 95 Title-Dates: 1973-1974 ## <DL><DD><I>CD <A HREF="/ovar/sovrev/schnittke/cpo999796.htm">CPO 999 796-2</A>: Radio SO Berlin, Frank Strobel (cond)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/ocd606.htm">Olympia OCD 606</A>: USSR Cinematography SO, Emin Khachaturian (cond)</I></DL> Title-RAW: Hymn I Title-For: cello, harp and timpani Title-Opus: 96 Title-Dates: 1974 ## <DL><DD>Duration: 11 minutes.<P> ## <I>LP Melodiya C10 28753 008: A. Ivashkin (cello), I. Pashinskaya (harp), V. Grishin (percussion)<BR> ## CD BIS CD 507: Torleif Thédéen (cello), Ingegerd Fredlund (harp), Anders Holdar (percussion)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/bis300507.htm">BIS CD 300507</A>: Torleif Thédéen (cello), Ingegerd Fredlund (harp), Anders Holdar (percussion)<BR> ## CD Melodiya SUCD 10 00061: A. Ivashkin (cello), I. Pashinskaya (harp), V. Grishin (percussion)</I></DL> Title-RAW: Hymn II Title-For: cello and double-bass Title-Opus: 97 Title-Dates: 1974 ## <DL><DD>Duration: 6 minutes.<P> ## <I>LP Melodiya C10 28753 008: A. Ivashkin (cello), V. Bartsalkin (double-bass)<BR> ## CD BIS CD 507: Torleif Thédéen (cello), Entcho Radoukanov (double-bass)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/bis300507.htm">BIS CD 300507</A>: Torleif Thédéen (cello), Entcho Radoukanov (double-bass)<BR> ## CD Melodiya SUCD 10 00061: A. Ivashkin (cello), V. Bartsalkin (double-bass)</I></DL> Title-RAW: Hymn III Title-For: cello, bassoon, harpsichord and bells or timpani Title-Opus: 98 Title-Dates: 1974 ## <DL><DD>Duration: 5 minutes.<P> ## <I>LP Melodiya C10 28753 008: A. Ivashkin (cello), Y. Rudometkin (bassoon), V. Chasovennaya (harpsichord), V. Grishin (bells)<BR> ## CD BIS CD 507: Torleif Thédéen (cello), Christian Davidsson (bassoon), Mayumi Kamata (harpsichord), Anders Loguin (percussion)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/bis300507.htm">BIS CD 300507</A>: Torleif Thédéen (cello), Christian Davidsson (bassoon), Mayumi Kamata (harpsichord), Anders Loguin (percussion)<BR> ## CD Melodiya SUCD 10 00061: A. Ivashkin (cello), Y. Rudometkin (bassoon), V. Chasovennaya (harpsichord), V. Grishin (bells)</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Cities and Years Title-Opus: 99 Title-Dates: 1974 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Captain's Daughter Title-Opus: 100 Title-Dates: 1974 Title-RAW: Requiem, music to Schiller's Drama Title-Name: Don Carlos Title-For: soloists, mixed chorus and instrumental ensemble Title-Opus: 101 Title-Dates: 1975 ## <DL><DD>In fourteen movements<BR> ## 1. Requiem - 3 min. 30 sec.<BR> ## 2. Kyrie - 2 min.<BR> ## 3. Dies irae - 1 min.<BR> ## 4. Tuba mirum - 4 min.<BR> ## 5. Rex tremendae majestatis - 1 min.<BR> ## 6. Recordare - 2 min. 30 sec.<BR> ## 7. Lacrimosa - 2 min. 30 sec.<BR> ## 8. Domine Jesu - 1 min. 30 sec.<BR> ## 9. Hostias - 1 min.<BR> ## 10. Sanctus - 4 min.<BR> ## 11. Benedictus - 2 min.<BR> ## 12. Agnus Dei - 2 min.<BR> ## 13. Credo - 4 min.<BR> ## 14. Requiem - 3 min. 30 sec.<P> ## <I>CD BIS CD 497: Stockholm Sinfonietta, Uppsala Academic Chamber Choir, Stefan Parkman (cond), Kristina Hjartsjo Salomonsson, Ingela Hogeras Sjoberg & Lisbeth Lindholm (soprano), Annika Finnila Eker (contralto), Nils Hogman (tenor)<BR> ## CD CHAN 9564: Russian State Symphony Orchestra, Russian State Symphonic Capella, Valery Polyansky (cond), Olga Sizova (soprano), Anaida Agadzhanian (soprano), Olga Tal (soprano), Tatiana Sharova (soprano), Ludmilla Kuznetsova (mezzo-soprano), Vsevolod Grivnov (tenor)<BR> ## CD Panton 81 1374-221: Prague Symphony Orchestra, Kuhn Mixed Chorus, Jiri Belohlavek (cond), Zdena Kloubova (soprano)</I></DL> Title-Name: Pantomime Title-Type-After-Name: suite Title-For: chamber orchestra Title-Opus: 102 Title-Dates: 1975 ## <DL><DD>After W.A. Mozart's Fragment KV 416d.<BR> ## 1. Pantalone and Colombine<BR> ## 2. The Dottore<BR> ## 3. Pierrot<BR> ## 4. The Turk<BR> ## 5. Pierrot chasing Harlekin<BR> ## 6. Harlekin's Death<BR> ## 7. Pierrot is terrified<BR> ## 8. Finale</DL> Title-Name: Cantus Perpetuus Title-For: keyboard instrument and percussion Title-Opus: 103 Title-Dates: 1975 Title-RAW: Preludium in Memoriam Dmitri Shostakovich Title-For: two violins (or solo violin and magnetic tape) Title-Opus: 104 Title-Dates: 1975 ## <DL><DD>Andante (one movement).<BR> ## Duration: 5 minutes.<P> ## <I>LP HMV-Melodiya ASD 3547: Gidon Kremer (violin)<BR> ## CD ASV CDDCA 877: Mateja Marinkovic & Thomas Bowes (violin)<BR> ## CD Aurophon AU 31899<BR> ## CD BIS CD 697: Oleg Krysa & Alexander Fischer (violin)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/bridge9104.htm">Bridge 9104</A>: Joanna Kurkowicz (violin), Sergey Schepkin (piano)<BR> ## CD Capriccio 67 115: V. Spivakov (violin)<BR> ## CD Col legno 0647 287: V. Spivakov (violin)<BR> ## CD DG 449 966-2: Gidon Kremer (violin)<BR> ## CD Duchesne CD 71 532: I. Tseitlin & P. Dheur (violin)<BR> ## CD RCA Victor 74321-24894-2: Sasha Rozhdestvensky & Vladimir Spivakov (violin)</I></DL> Title-RAW: Cadenza to Mozarts Piano Concerto in C minor KV 491 (first movement) Title-Opus: 105 Title-Dates: 1975 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Autumn Title-Opus: 106 Title-Dates: 1975 Title-RAW: Eight Songs from the incidental music to Schiller's Title-Name: Don Carlos Title-For: voice and piano or guitar Title-Opus: 107 Title-Dates: 1975 ## <DL><DD>1. Prelude<BR> ## 2. Hope<BR> ## 3. A Path in the Mountains<BR> ## 4. Evil Monks<BR> ## 5. Love Song<BR> ## 6. Abouth Theatre<BR> ## 7. To my Friends<BR> ## 8. Song of the Marauders</DL> Title-Type: Piano Quintet Title-Opus: 108 Title-Dates: 1972-1976 ## <DL><DD>In five movements:<BR> ## 1. Moderato (attacca) - 6 min.<BR> ## 2. Tempo di Valse - 5 min.<BR> ## 3. Andante - 6 min.<BR> ## 4. Lento (attacca) - 5 min.<BR> ## 5. Moderato pastorale - 3 min. 30 sec.<P> ## <I>LP Philips 411 107-1: Gidon Kremer (violin), Kathrin Rabus (violin), Gerard Causse (viola), Ko Iwasaki (cello), Elena Bashkirova (piano)<BR> ## CD Arabesque Z 6707: Lark Quartet, Gary Graffman (piano)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/asv6251.htm">ASV Quicksilva CD QS 6251</A>: Barbican Piano Trio, Jan Peter Schmolck (violin), James Boyd (viola)<BR> ## CD BIS CD 547: Tale Quartet, Roland Pöntinen (piano)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/bbm1093.htm">Black Box BBM 1093</A>: Barbican Piano Trio, Jan Peter Schmolck (violin), James Boyd (viola)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/ecm1755.htm">ECM New Series 1755 (461 815-2)</A>: Keller Quartet, Alexei Lubimov (piano)<BR> ## CD Etcetera KTC 1124: Mondriaan Quartet, Fred Oldenburg (piano)<BR> ## CD Hyperion CDA 66885: Capricorn, Timothy Mason (cond)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/naxos8554728.htm">Naxos 8.554728</A>: Mark Lubotsky & Dimity Hall (violin), Theodore Kuchar & Irini Morozova (viola), Alexander Ivashkin & Julian Smiles(cello), Irina Schnittke (piano)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/naxos8554830.htm">Naxos 8.554830</A>: Vermeer Quartet, Boris Berman (piano)<BR> ## CD Northern Flowers NF 9908: Lidia Kovalenko & Alexey Baev (violin), Alexey Popov (viola), Kirill Timofeev (cello), Yuri Serov (piano)<BR> ## CD Russian Disc RDCD 10 031: Moscow String Quartet, Constantine Orbelian (piano)<BR> ## CD Virgin Classics VC 7 91436-2: Borodin Quartet, Ludmilla Berlinsky (piano)<BR> ## CD <A HREF="/ovar/shosrev/naxos8554830.htm">Naxos 8.554830</A>: Vermeer Quartet, B. Berman (piano)</I></DL> Title-RAW: "Der Sonnengesang des Franz von Assisi" for two mixed choruses and six instrumentalists Title-Opus: 109 Title-Dates: 1976 ## <DL><DD><I>LP Melodiya: USSR State Chamber Chorus, Ensemble of Bolshoi Theatre Soloists, Valery Polyansky (cond)</I></DL> Title-RAW: Transcription of Shostakovich's Preludes nos. 1 & 2 (from "Five preludes") Title-For: small orchestra Title-Opus: 110 Title-Dates: 1976 Title-RAW: Moz-Art for two violins, after Mozart KV 416 Title-Opus: 111 Title-Dates: 1976 ## <DL><DD>Allegretto / Allegro (one movement)><BR> ## Duration: 5 minutes 30 seconds.<P> ## <I>CD BIS CD 697: Oleg Krysa & Alexander Fischer (violin)</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Selecting a Target Title-Opus: 112 Title-Dates: 1976 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Legend on How Tsar Peter Got the Negro Married Title-Opus: 113 Title-Dates: 1976 Title-Count: Two Title-Type: Fragments Title-For: small symphony orchestra Title-Related-How: from the music to the film Title-Related-Name: The Tale of Tsar Peter Title-Opus: 113a Title-Dates: 1976 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Lion-Trainers Title-Opus: 114 Title-Dates: 1976 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Clowns and Kids Title-Opus: 115 Title-Dates: 1976 ## <DL><DD><I>CD <A HREF="/ovar/sovrev/schnittke/cap71061.htm">Capriccio 71061</A> (SACD): Rundfunk-Sinfonieorchester Berlin, Frank Strobel (cond)</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Ricky-Ticky-Tari Title-Opus: 116 Title-Dates: 1976 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The White Steamer Title-Opus: 117 Title-Dates: 1976 Title-RAW: Two Cadenzas to Beethoven's Violin Concerto in D major Op. 61 Title-For: solo violin, 10 violins and timpani Title-Opus: 118 Title-Dates: 1975-1977 Title-Type: Concerto Grosso Title-No: 1 Title-For: two violins, prepared piano, harpsichord and 21 strings Title-Opus: 119 Title-Dates: 1977 ## <DL><DD>In six movements:<BR> ## 1. Preludio (attacca) - 6 min.<BR> ## 2. Toccata (attacca) - 5 min.<BR> ## 3. Recitativo - 9 min.<BR> ## 4. Cadenza (attacca) - 2 min. 30 sec.<BR> ## 5. Rondò (attacca) - 7 min. 30 sec.<BR> ## 6. Postludio - 3 min.<P> ## <I>CD BIS CD 377: New Stockholm Chamber Orchestra, Lev Markiz (cond), Christian Bergqvist (violin), P. Swedrup (violin), R. Pontinen (piano)<BR> ## CD BIS CD 1507: New Stockholm Chamber Orchestra, Lev Markiz (cond), Christian Bergqvist (violin), P. Swedrup (violin), R. Pontinen (piano)<BR> ## CD Chandos CHAN 9590: I Musici de Montreal, Yuli Turovsky (cond), Natalya Turovsky, Catherine Perrin<BR> ## CD DG 429 413-2: Chamber Orchestra of Europe, Heinrich Schiff (cond), Gidon Kremer (violin), Tatiana Grindenko (violin), Yuri Smirnov (harpsichord and piano)<BR> ## CD DG 439 452-2: Chamber Orchestra of Europe, Heinrich Schiff (cond), Gidon Kremer (violin), Tatiana Grindenko (violin), Yuri Smirnov (harpsichord and piano)<BR> ## CD DG 445 520-2: Chamber Orchestra of Europe, Heinrich Schiff (cond), Gidon Kremer (violin), Tatiana Grindenko (violin), Yuri Smirnov (harpsichord and piano)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/dg471626.htm">DG 471 626-2</A>: Chamber Orchestra of Europe, Heinrich Schiff (cond), Gidon Kremer (violin), Tatiana Grindenko (violin), Yuri Smirnov (harpsichord and piano)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/cdman175.htm">Manchester Classical Gallery CDMAN 175</A>: St. Petersburg Mozarteum Chamber Orchestra, Arcady Shteinlukht (cond), Victor Kuleshov (violin), Ilia Ioff (violin), Julia Lev (harpsichord and piano)<BR> ## CD RCA Victor Gold Seal GD 60957: London SO, Gennadi Rozhdestvensky (cond), Gidon Kremer & Tatiana Grindenko (violin)<BR> ## CD RCA Victor 74321-24894-2 (2 CD-set): London SO, Gennadi Rozhdestvensky (cond), Gidon Kremer & Tatiana Grindenko (violin)<BR> ## CD Sikorski SIK 7-003E: London SO, Gennadi Rozhdestvensky (cond), Gidon Kremer & Tatiana Grindenko (violin)</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Human Requitale Title-Opus: 120 Title-Dates: 1977 Title-RAW: "Magdalina" for voice and piano to a poem by Boris Pasternak Title-Opus: 121 Title-Dates: 1977 ## <DL><DD>Part of Hommage to Zhivago (1993)</DL> Title-RAW: Moz-Art a la Haydn Title-For: two violins and eleven strings Title-Opus: 122 Title-Dates: 1977 ## <DL><DD>For 6 violins, 2 violas, 2 cellos and 1 double-bass.<BR> ## Dedicated to Tatyana Grindenko and Gidon Kremer.<P> ## <I>CD ASV CDDCA 877: Mateja Marinkovic & Thomas Bowes (violin)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/bis1437.htm">BIS CD 1437</A>: Tapiola Sinfonietta, Ralf Gothoni (cond), Ulf Wallin & Meri Englund (violin)<BR> ## CD DG 429 413-2: Chamber Orchestra of Europe, Gidon Kremer (cond & violin), Tatiana Grindenko (violin)<BR> ## CD DG 445 520-2: Chamber Orchestra of Europe, Gidon Kremer (cond & violin), Tatiana Grindenko (violin)<BR> ## CD Nonesuch 7559-79633-2: Kremeratica Baltica, Gidon Kremer (violin)</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: My Memories Take Me To You Title-Opus: 123 Title-Dates: 1977 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Adventures of Travka Title-Opus: 124 Title-Dates: 1977 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Life-Story of an Unknown Actor Title-Opus: 125 Title-Dates: 1977 ## <DL><DD><I>CD <A HREF="/ovar/sovrev/schnittke/ocd606.htm">Olympia OCD 606</A>: USSR Cinematography SO, Emin Khachaturian (cond)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/cap71041.htm">Capriccio hybrid CD/SACD CC 71041</A>: Berlin Radio Symphony Orchestra, Frank Strobel (cond)</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Ascent Title-Opus: 126 Title-Dates: 1977 ## <DL><DD><I>CD <A HREF="/ovar/sovrev/schnittke/cap71061.htm">Capriccio 71061</A> (SACD): Rundfunk-Sinfonieorchester Berlin, Frank Strobel (cond)</I></DL> Title-Name: In Memoriam Title-For: symphony orchestra Title-Opus: 127 Title-Dates: 1972-1978 ## <DL><DD>Orchestral version of the piano quintet<BR> ## 1. Moderato (attacca) - 7 min.<BR> ## 2. Tempo di Valse - 6 min.<BR> ## 3. Andante (attacca) - 7 min.<BR> ## 4. Lento - 4 min. 30 sec.<BR> ## 4. Moderato pastorale - 3 min. 30 sec.<P> ## <I>CD BIS CD 447: Malmö SO, Lev Markiz (cond)<BR> ## CD Chandos CHAN 9466: Russian State Symphony Orchestra, Valery Polyansky (cond)<BR> ## CD Sony Classical CD 48241: London SO, Seiji Ozawa (cond)</I></DL> Title-Type: Concerto Title-No: 3 Title-For: violin and chamber orchestra Title-Opus: 128 Title-Dates: 1978 ## <DL><DD>In three movements:<BR> ## 1. Moderato - 10 min.<BR> ## 2. Agitato (attacca) - 6 min.<BR> ## 3. Moderato - 11 min.<P> ## <I>LP Eurodisc 201 234: Berlin Philharmonic Chamber Ensemble, W. Nelson (cond), Gidon Kremer (violin)<BR> ## LP Melodiya C10 15681 000: Soloists Ensemble, Y. Nikolayevsky (cond), Oleg Kagan (violin) ## CD BIS CD 517: Malmö SO, Eri Klas (cond), Oleg Krysa (violin)<BR> ## CD Ondine ODE 893-2: Virtuosi di Kuhmo, Sibelius Academy Wind Players, Ralf Gothoni (cond), Mark Lubotsky (violin)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/phoenix150.htm">Phoenix PHCD 150</A>: Moscow Tchaikovsky SO, Mikhail Kukushkin (cond), Levon Ambartsumian (violin)<BR> ## CD Teldec 4509-94540-2: Chamber Orchestra of Europe, Gidon Kremer (violin)</I></DL> Title-Type: Sonata Title-No: 1 Title-For: cello and piano Title-Opus: 129 Title-Dates: 1978 ## <DL><DD>In three movements:<BR> ## 1. Largo (attacca) - 4 min.<BR> ## 2. Presto (attacca) - 7 min.<BR> ## 3. Largo - 12 min.<P> ## <I>CD Aeon AECD 0636: Marc Coppey (cello), Peter Laul (piano)<BR> ## CD BIS CD 336: Torleif Thedéen (cello), Roland Pöntinen (piano)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/bbm11032.htm">Black Box BBM 1032</A>: Raphael Wallfisch (cello), John York (piano)<BR> ## CD Chandos CHAN 9705: Alexander Ivashkin (cello), Irina Schnittke (piano)<BR> ## CD <A HREF="/ovar/shosrev/cda67534.htm">Hyperion CDA 67534</A>: Alban Gerhardt (cello), Steven Osborne (piano)<BR> ## CD Globe GLO 5041: D. Ferschtmann (cello), M. Baslavskaya (piano)<BR> ## CD Harmonia Mundi HMN 91 1628: Xavier Phillips (cello), Huseyin Sermet (piano)<BR> ## CD Marco Polo 8.223334: Maria Kliegel (cello), Raimund Havenith (piano)<BR> ## CD Melodiya SUCD 10 00088: T. Hugh (cello), C. Tourocque (piano)<BR> ## CD Ode Manu CDMANU 1480: Alexander Ivashkin (cello), Tamas Vesmas (piano)<BR> ## CD <A HREF="/ovar/sovrev/kancheli/qtz2032.htm">Quartz QTZ 2032</A>: Matthew Barley (cello), Stephen De Pledge (piano)<BR> ## CD Thorofon CTH 2459: Sonja Schröder (cello), Peter Martin (piano)<BR> ## CD Unicorn Kanchana DKP CD 9083: Alexander Baillie (cello), Piers Lane (piano)<BR> ## CD United Recordings 88006-2: Paul Marleyn (cello), Sarah Morley (piano)</I></DL> Title-RAW: "Silent Night" ("Stille Nacht") arranged Title-For: violin and piano Title-Opus: 130 Title-Dates: 1978 ## <DL><DD>On motives of the like-named German Christmas carol.<BR> ## Lento (one movement).<BR> ## Employing the melody by Franz Xaver Gruber.<BR> ## Duration: 5 minutes<P> ## <I>CD ASV CDDCA 877: Mateja Marinkovic (violin), Linn Hendry (piano)<BR> ## CD BIS CD 527: U. Wallin (violin), Roland Pöntinen (piano)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/bbm1025.htm">Black Box BBM 1025</A>: Roman Mints (violin), Evgenia Chudinovich(piano)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/ni5631.htm">Nimbus NI 5631</A>: Daniel Hope (violin), Simon Mulligan (piano)<BR> ## CD Stradivarius STR 33675: Francesco D'Orazio (violin), Giampaolo Nuti (piano)<BR> ## CD Teldec 4509-94540-2: Gidon Kremer (violin), Christoph Eschenbach (piano)</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Father Sergei Title-Opus: 131 Title-Dates: 1978 Title-Type: Incidental music Title-Related-How: to Alexander Vampilov's play Title-Related-Name: A Duck Shooting Party Title-Opus: 132 Title-Dates: 1978 Title-Type: Incidental music Title-Related-How: to Nikolai Gogol's play Title-Related-Name: The Dead Soul Register (The Census List) Title-Opus: 133 Title-Dates: 1978 ## <DL><DD><I>CD <A HREF="/ovar/sovrev/schnittke/chan9885.htm">Chandos CHAN 9885</A>: Russian State SO, Valeri Polyansky (cond), Lev Butenin (reciter)</I></DL> Title-RAW: Hymn IV Title-For: cello, bassoon, double-bass, harpsichord, harp, timpani and bells Title-Opus: 134 Title-Dates: 1974-1979 ## <DL><DD>Duration: 3 minutes.<P> ## <I>LP Melodiya C10 28753 008: USSR Bolshoi Theatre Soloists Ensemble, A. Lazarev (cond)<BR> ## CD BIS CD 507: Torleif Thédéen (cello), Christian Davidsson (bassoon), Entcho Radoukanov (double-bass), Mayumi Kamata (harpsichord), Ingegerd Fredlund (harp), Anders Holdar & Anders Loguin (timpani & bells)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/bis300507.htm">BIS CD 300507</A>: Torleif Thédéen (cello), Christian Davidsson (bassoon), Entcho Radoukanov (double-bass), Mayumi Kamata (harpsichord), Ingegerd Fredlund (harp), Anders Holdar & Anders Loguin (timpani & bells)<BR> ## CD Melodiya SUCD 10 00061: USSR Bolshoi Theatre Soloists Ensemble, A. Lazarev (cond)</I></DL> Title-Type: Symphony Title-No: 2 Title-Name: St. Florian Title-For: vocal soloists, chamber choir and symphony orchestra Title-Opus: 135 Title-Dates: 1979 ## <DL><DD>In six movements:<BR> ## 1. Recitativo (Kyrie) - 11 min.<BR> ## 2. Maestoso (Gloria) - 5 min.<BR> ## 3. Moderato (Credo) - 9 min.<BR> ## 4. Pesante (Cucifixus) - 8 min. 30 sec.<BR> ## Coda: Agitato (Et resurrexit), Maestoso - 4 min.<BR> ## Introduction to V. Andante (Sanctus) - 4 min.<BR> ## 5. Andante (Sanctus. Benedictus) - 8 min.<BR> ## 6. Andante (Agnus Dei) - 11 min.<P> ## <I>LP Melodiya C10 23085 (2 LP-set): Leningrad PO, USSR Ministry of Culture Chamber Chorus, Gennadi Rozhdestvensky (cond)<BR> ## CD BBC Radio Classics 15656-9196-2: BBC Symphony Chorus and Orchestra, BBC Singers, Gennadi Rozhdestvensky (cond), Jean Temperley (contralto), ## Paul Esswood (alto), Neil Jenkins (tenor), Jonathan Robarts (bass)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/bis667.htm">BIS CD 667</A>: Royal Stockholm PO, Mikaeli Chamber Choir, Leif Segerstam (cond), Mikael Bellini (alto), ## Malena Ernman (mezzo-soprano), Goran Eliasson (tenor), Torkel Borelius (bass)<BR> ## CD Chandos CHAN 9519: Russian State Symphonic Cappella and SO, Valery Polyansky (cond), ## Marina Katsman contralto), Yaroslav Zdorov (alto), Oleg Dolgov (tenor), Sergei Veprintsev (bass)<BR> ## CD Melodiya SUCD 10 00063: Leningrad PO, USSR Ministry of Culture Chamber Chorus, Gennadi Rozhdestvensky (cond)</I></DL> Title-Type: Concerto Title-For: piano and string orchestra Title-In-Movements: in a single movement Title-Opus: 136 Title-Dates: 1979 ## <DL><DD>Duration: 21 minutes.<P> ## <I>LP Melodiya C10 22845 004: Lithuanian Chamber Orchestra, S. Sondeckis (cond), V. Krainev (cond)<BR> ## CD BIS CD 377: New Stockholm Chamber Orchestra, Lev Markiz (cond), Roland Pontinen (piano)<BR> ## CD <A HREF="/ovar/shosrev/brioso109.htm">Brioso BR 109</A>: Moscow PO, Vassily Sinaisky (cond), O. Volkov (piano)<BR> ## CD Capriccio 67 016: Moscow Virtuosi, V. Spivakov (cond)<BR> ## CD CHAN 9564: Russian State SO, Valery Polyansky (cond), Igor Khudolei (piano)<BR> ## CD <A HREF="/ovar/shosrev/delos3259.htm">Delos DE 3259</A>: Moscow Chamber Orchestra, Constantine Orbelian (pianist and conductor)<BR> ## CD Erato 2292-45742-2: London Sinfonietta, Gennadi Rozhdestvensky (cond), Victoria Postnikova (piano)<BR> ## CD Koch International Classics 37159-2: Moscow PO, Donald Barra (cond), Israela Margalit (piano)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/cdman175.htm">Manchester Classical Gallery CDMAN 175</A>: St. Petersburg Mozarteum Chamber Orchestra, Arcady Shteinlukht (cond), Veronica Reznikovskaya (piano)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/mv06.htm">MV Productions SPPS CD06</A>: Omsk Chamber Orchestra, Yuri Nikolaevsky (cond), Svetlana Ponomarëva (piano)<BR> ## CD Ondine ODE 893-2: Virtuosi di Kuhmo, Ralf Gothoni (cond & piano)<BR> ## CD RCA Victor Red Seal 09026- 60466-2: Moscow Virtuosi, Vladimir Spivakov (cond), Vladimir Krainev (piano)<BR> ## CD RCA Victor 74321-24894-2 (2 CD-set): Moscow Virtuosi, Vladimir Spivakov (cond), Vladimir Krainev (piano)<BR> ## CD Teldec 2292-45742-2: London Sinfonietta, Gennadi Rozhdestvensky (cond), Victoria Postnikova (piano)</I></DL> Title-RAW: Polyphonic Tango Title-For: ensemble Title-Opus: 137 Title-Dates: 1979 ## <DL><DD><I>Live Rec: Bolshoi Theatre Soloists Ensemble, A. Lazarev (cond)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/cpo999804.htm">CPO 999 804-2</A>: Radio-Philharmonie Hannover des NDR, Eiji Oue (cond)</I></DL> Title-RAW: Peaceful Music (Stille Musik) Title-For: violin and cello Title-Opus: 138 Title-Dates: 1979 ## <DL><DD>Lento (one movement).<BR> ## Duration: 6 minutes.<P> ## <I>CD ASV CDDCA 877: Mateja Marinkovic (violin), Timothy Hugh (cello)<BR> ## CD BIS CD 697: Oleg Krysa (violin), Torleif Thédéen (cello)<BR> ## CD Marco Polo 8.223334: Burkhard Godhoff (violin), Maria Kliegel (cello)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/naxos8554728.htm">Naxos 8.554728</A>: Mark Lubotsky & Dimity Hall (violin), Theodore Kuchar & Irini Morozova (viola), Alexander Ivashkin & Julian Smiles(cello), Irina Schnittke (piano)</I></DL> Title-RAW: In Memoriam Igor Strawinsky, Sergei Prokofiev and Dmitri Shostakovich Title-For: piano six hands Title-Opus: 139 Title-Dates: 1979 ## <DL><DD>Based on Chinese March from "The nightingale", Humoresque Scherzo and Polka from "The age of gold".<P> ## <I>Live Rec: Robert Nasveld, Polo de Haas & Nico de Vente (piano)<BR> ## Live Rec: Pianokwadraat (piano)</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Paradoxes of the Evolution Title-Opus: 140 Title-Dates: 1979 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Fantasies of Faryatyev Title-Opus: 141 Title-Dates: 1979 Title-Name: Passacaglia Title-For: large symphony orchestra Title-Opus: 142 Title-Dates: 1979-1980 ## <DL><DD>Duration: 19 minutes.<P> ## <I>CD BIS 437: Malmö SO, Leif Segerstam (cond)</I></DL> Title-RAW: Gogol Suite Title-For: orchestra Title-Related-How: from the spectacle Title-Related-Name: The Tale of the Inspector Title-Opus: 143 Title-Dates: 1980 ## <DL><DD>Suite from the music to a production of "The Dead Souls Register".<BR> ## Orchestral version by Gennadi Rozhdestvensky.<BR> ## In eight movements:<BR> ## 1. Overture - 1 min.<BR> ## 2. Chichikov's Childhood - 2 min. 30 sec.<BR> ## 3. The Portrait - 7 min.<BR> ## 4. The Overcoat - 2 min. 30 sec.<BR> ## 5. Ferdinand VIII - 1 min. 30 sec.<BR> ## 6. The Bureaucrats - 2 min. 30 sec.<BR> ## 7. The Ball - 6 min. 30 sec.<BR> ## 8. The Legacy - 6 min.<P> ## <I>LP Melodiya C10 18761-62: USSR Ministry of Culture SO, Gennadi Rozhdestvensky (cond)<BR> ## CD BIS CD 557: Malmö SO, Lev Markiz (cond)<BR> ## CD Pope Music PM 1007-2: Russian SO, Mark Gorenstein (cond)</I></DL> Title-RAW: "The Revisionist's Tale", transcription of five movements of the Gogol Suite Title-For: two pianos Title-Opus: 143a ## <DL><DD><I>CD <A HREF="/ovar/sovrev/schnittke/sp22566.htm">Sonora Products SO 22566 CD</A>: Natalia Zusman & Inna Heifetz (pianos)</I></DL> Title-RAW: Three Madrigals for soprano, violin, viola, double-bass, vibraphone and harpsichord on poems by Francisco Tanzer Title-Opus: 144 Title-Dates: 1980 ## <DL><DD>1. Sur une étoile (Andante)<BR> ## 2. Entfernung (Moderato)<BR> ## 3. Reflection (Andante)<P> ## <I>LP Melodiya C10 18403-4: Bolshoi Theatre Soloists Ensemble, A. Lazarev (cond), N. Lee (soprano)<BR> ## CD Hyperion CDA 66885: Capricorn, , Timothy Mason (cond), Sarah Leonard (soprano)</I></DL> Title-RAW: Three Scenes for soprano and ensemble without text Title-Opus: 145 Title-Dates: 1980 ## <DL><DD>Dedicated to Mark Pekarski and his ensemble.<BR> ## 1. Poco pesante<BR> ## 2. Moderato<BR> ## 3. Andante</DL> Title-RAW: Moz-Art, version Title-For: ensemble Title-Opus: 146 Title-Dates: 1980 ## <DL><DD>For oboe, harp, harpsichord, violin, cello and double bass.<BR> ## Allegretto (one movement)</DL> Title-Name: Minnesang Title-For: 52 voices Title-Opus: 147 Title-Dates: 1980-1981 ## <DL><DD>Duration: 19 minutes.<P> ## <I>CD Chandos CHAN 9126: Danish National Radio Choir, Stefan Parkman Cond)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/cda67297.htm">Hyperion CDA 67297</A>: Holst Singers, Stephen Layton (cond)</I></DL> Title-Type: String Quartet Title-No: 2 Title-Opus: 148 Title-Dates: 1980 ## <DL><DD>Commissioned by Universal Edition, Vienna.<BR> ## In four movements:<BR> ## 1. Moderato (attacca) - 3 min.<BR> ## 2. Agitato (attacca) - 6 min.<BR> ## 3. Mesto (attacca) - 6 min.<BR> ## 4. Moderato - 7 min.<P> ## <I>LP Panton 81 0752: Stamic Quartet<BR> ## CD Arabesque Z 6707: Lark Quartet<BR> ## CD BIS CD 467: Tale Quartet<BR> ## CD Claves CD 50-9504/5 (2 CD-set): (Arrangement by Rachlevsky) Kremlin Chamber Orchestra, Misha Rachlevsky (cond)<BR> ## CD Collins Classics 1450-2: Duke Quartet<BR> ## CD Gramavision GV 79439-2: Arditti Quartet<BR> ## CD Nonesuch 79500-2: Kronos Quartet<BR> ## CD Nonesuch 7559-79504-2 (10 CD-set): Kronos Quartet</I></DL> Title-RAW: Two Short Pieces Title-For: organ Title-Opus: 149 Title-Dates: 1980 ## <DL><DD><I>CD Melodiya SUCD 10 00009: O. Yanchenko (organ)<BR> ## CD Melodiya SUCD 10 00066: O. Yanchenko (organ)<BR> ## CD Aurophon AU 31840 <BR> ## CD Precosa-Aulos PRE 66 022 AUL: F. Herz (organ)</I></DL> Title-RAW: Three Cadenzas to Mozart's Piano Concerto in C major KV 467 Title-Opus: 150 Title-Dates: 1980 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Plane Crew Title-Opus: 151 Title-Dates: 1980 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Larisa Title-Opus: 152 Title-Dates: 1980 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Little Tragedies Title-Opus: 153 Title-Dates: 1980 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Farewell Title-Opus: 154 Title-Dates: composed with Artyomov, 1980 Title-Type: Symphony Title-No: 3 Title-Opus: 155 Title-Dates: 1981 ## <DL><DD>In four movements:<BR> ## 1. Moderato<BR> ## 2. Allegro<BR> ## 3. Allegro pesante (attacca)<BR> ## 4. Adagio<P> ## <I>LP Melodiya C10 25175: USSR Ministry of Culture SO, Gennadi Rozhdestvensky (cond)<BR> ## CD BIS CD 477: Stockholm PO, Eri Klas (cond)<BR> ## CD Melodiya SUCD 10 00064: USSR Ministry of Culture SO, Gennadi Rozhdestvensky (cond)</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Eugene Onegin Title-Opus: 156 Title-Dates: 1981 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: What Does Babirussya Need Tusks For? Title-Opus: 157 Title-Dates: 1981 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: I Am With You Again Title-Opus: 158 Title-Dates: 1981 Title-Type: Concerto Grosso Title-No: 2 Title-For: violin, cello and triple symphony orchestra Title-Opus: 159 Title-Dates: 1981-1982 ## <DL><DD>In four movements:<BR> ## 1. Andantino. Allegro - 6 min.<BR> ## 2. Pesante - 9 min.<BR> ## 3. Allegro - 5 min. 30 sec.<BR> ## 4. Andantino - 12 min.<P> ## <I>LP Melodiya A10 00509 005: USSR Ministry of Culture SO, Gennadi Rozhdestvensky (cond)<BR> ## CD BIS CD 567: Malmö SO, Lev Markiz (cond), Oleg Krysa (violin), Torleif Thédéen (cello)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/chan10180.htm">Chandos CHAN 10180</A>: Russian State SO, Valeri Polyansky (cond), Tatiana Grindenko (violin), Alexander Ivashkin (cello)<BR> ## CD Melodiya SUCD 10-00068: USSR Ministry of Culture SO, Gennadi Rozhdestvensky (cond)</I></DL> Title-Type: Septet Title-For: flute, two clarinets, violin, viola, cello and harpsichord or organ Title-Opus: 160 Title-Dates: 1981-1982 ## <DL><DD>Introduction. Moderato (attacca)<BR> ## 1. Perpetuum mobile. Allegretto<BR> ## 2. Choral. Moderato<P> ## <I>CD Chandos CHAN 9466: Bolshoi Theatre Orchestra Soloists Ensemble, Valery Polyansky (cond)</I></DL> Title-RAW: "Course of life" ("Lebenslauf") Title-For: four metronomes, piano and three percussionists Title-Opus: 161 Title-Dates: 1982 ## <DL><DD>Dedicated to Wilfried Brennecke and John Cage.</DL> Title-RAW: A Paganini Title-For: violin solo Title-Opus: 162 Title-Dates: 1982 ## <DL><DD>Andante (one movement).<BR> ## Duration: 11 minutes.<P> ## <I>LP DG 415 484-1: Gidon Kremer (violin)<BR> ## CD ASV CDDCA 877: Mateja Marinkovic (violin)<BR> ## CD BIS CD 697: Oleg Krysa (violin)<BR> ## CD BIS CD 1051: Ilya Gringolts (violin)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/bridge9104.htm">Bridge 9104</A>: Joanna Kurkowicz (violin)<BR> ## CD DG 415 484-2: Gidon Kremer (violin)<BR> ## CD DG 445 520-2: Gidon Kremer (violin)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/phoenix150.htm">Phoenix PHCD 150</A>: Levon Ambartsumian (violin)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/sp22579.htm">Sonora Products SO 22579 CD</A>: Valery Gradow (violin)</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Sturdy Boy Title-Opus: 163 Title-Dates: 1982 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Autumn Title-Opus: 164 Title-Dates: 1982 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Pencil and Eraser Title-Opus: 165 Title-Dates: 1982 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Meteoric Shower/Star Fall Title-Opus: 166 Title-Dates: 1982 Title-Name: Seid Nüchtern und Wachet Title-Type-After-Name: cantata Title-For: soloists (counter-tenor, counter-alto, tenor and bass), mixed chorus and orchestra Title-Opus: 167 Title-Dates: 1983 ## <DL><DD>Text from "The History of D. Johann Faustus".<BR> ## Parts:<BR> ## 1. Folget nun - 3 min.<BR> ## 2. Die vierundzwanzig Jahre - 2 min.<BR> ## 3. Gehen also miteinander - 2 min.<BR> ## 4. Meine liebe - 5 min.<BR> ## 5. Ach, mein Herr Fauste - 5 min.<BR> ## 6. Doktor Faustus klagte - 3 min.<BR> ## 7. Es geschah - 5 min.<BR> ## 8. Diese gemeldete Magistri - 3 min.<BR> ## 9. Also endet sich - 4 min.<BR> ## 10. Seid nüchtern und wachet - 3 min.<P> ## <I>CD BIS 437: Malmö SO & Chorus, James DePreist (cond), Inger Blom (mezzo-soprano), Mikael Bellini (alto), Louis Devos (tenor), Ulrik Cold (bass)</I></DL> ## <B>Opus 168: String Quartet no. 3 (1983)</B><BR> ## <DL><DD>Commissioned by Society for New Music, Mannheim.<BR> ## In three movements:<BR> ## 1. Andante - 5 min.<BR> ## 2. Agitato - 7 min.<BR> ## 3. Pesante - 7 min.<P> ## <I>CD Arabesque Z 6707: Lark Quartet<BR> ## CD <A HREF="/ovar/sovrev/schnittke/arco54.htm">Arco Diva UP 0054-2 131</A>: Kapralova Quartet<BR> ## CD BIS CD 467: Tale Quartet<BR> ## CD Etcetera KTC 1124: Mondriaan Quartet<BR> ## CD LDR CD1008: Britten Quartet<BR> ## CD Virgin Classics VC 7 91436-2: Borodin Quartet<BR> ## CD Nonesuch 79500-2: Kronos Quartet</I></DL> Title-RAW: Cadenza to Mozart's Piano Concerto in C major KV 503 (first movement) Title-Opus: 169 Title-Dates: 1983 Title-RAW: Two Cadenzas to Mozart's Bassoon Concerto in B flat major KV 191 Title-Opus: 170 Title-Dates: 1983 Title-Name: Sound and Echo (Schall und Hall) Title-For: trombone and organ Title-Opus: 171 Title-Dates: 1983 ## <DL><DD>Lento (one movement).<BR> ## Duration: 12 minutes.<P> ## <I>CD BIS CD 488: Christian Lindberg (trombone), Gunnar Idenstam (organ)<BR> ## CD Chandos CHAN 9466: Anatoly Skobelev (trombone), Ludmilla Golub (organ)</I></DL> Title-RAW: "A Streetcar Named Desire (Endstation Sehnsucht)", ballet in two acts by John Neumeier after the play by Tennessee Williams (from Symphony No. 1) Title-Opus: 172 Title-Dates: 1983 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Leave-Taking Title-Opus: 173 Title-Dates: 1983 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: A Fairy Tale of Travels Title-Opus: 174 Title-Dates: 1983 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Darling of the Audience Title-Opus: 175 Title-Dates: 1983 Title-Type: Symphony Title-No: 4 Title-For: two vocal soloists, piano, chamber chorus and chamber orchestra Title-In-Movements: in a single movement Title-Opus: 176 Title-Dates: 1984 ## <DL><DD>Duration: 41 minutes.<P> ## <I>LP Melodiya A10 00271 005: USSR Ministry of Culture Orchestra SO, Gennadi Rozhdestvensky (cond), Viktoria Postnikova (piano) ## CD BIS CD 497: Stockholm Sinfonietta, Uppsala Academic Chamber Choir, Okko Kamu (cond), Mikael Bellini (alto)<BR> ## CD Chandos CHAN 9463: Russian State SO, Russian State Symphonic Cappella, Valery Polyansky (cond), Iaroslav Zdorov (alto), Dmitri Pianov (tenor)<BR> ## CD Melodiya SUCD 10-00065: USSR Ministry of Culture Orchestra SO, Gennadi Rozhdestvensky (cond), Viktoria Postnikova (cond) (Recording: 1986)</I></DL> Title-Type: Concerto Title-No: 4 Title-For: violin and orchestra Title-Opus: 177 Title-Dates: 1984 ## <DL><DD>In four movements:<BR> ## 1. Andante (attacca) - 5 min.<BR> ## 2. Vivo (attacca) - 7 min.<BR> ## 3. Adagio (attacca) - 9 min. 30 sec.<BR> ## 4. Lento - 12 min. 30 sec.<P> ## <I>CD BIS CD517: Malmö SO, Eri Klas (cond), Oleg Krysa (violin)<BR> ## CD Teldec 4509-98440-2: Members of Philharmonia Orchestra, Gidon Kremer (violin)</I></DL> Title-RAW: Transcription of Scott Joplin's Ragtime Title-For: orchestra Title-Opus: 178 Title-Dates: 1984 Title-RAW: Transcription of Adolf Jensen's Serenada Title-For: mezzo-soprano and orchestra Title-Opus: 179 Title-Dates: 1984 Title-RAW: Transcription of Friedrich Nietsche's Incantation Title-For: mezzo-soprano and orchestra Title-Opus: 180 Title-Dates: 1984 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The White Poodle Title-Opus: 181 Title-Dates: 1984 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Dead Souls Title-Opus: 182 Title-Dates: 1984 Title-Type: Suite Title-Related-How: from Title-Related-Name: Dead Souls Title-For: orchestra Title-Opus: 182a ## <DL><DD><I>Live Rec: Royal Concertgebouw Orchestra, Gennadi Rozhdestvensky (cond), Viktoria Postnikova (piano)</I></DL> Title-Name: Ritual Title-For: large symphony orchestra Title-Opus: 183 Title-Dates: 1984-1985 ## <DL><DD>In memory of the victims of the Second World War (on occasion of the 40th Anniversary of the liberation of Belgrade). ## Moderato (one movement). ## Duration: 9 minutes.<P> ## <I>CD BIS 437: Malmö SO, Leif Segerstam (cond)</I></DL> Title-Type: Concerto Title-For: soprano and mixed chorus Title-Opus: 184 Title-Dates: 1984-1985 ## <DL><DD>On verses from the "Book of Mournful Songs" by Gregory of Narek.<BR> ## Commissioned by Moscow Chamber Choir.<BR> ## Part One - 15 min.<BR> ## Part Two - 7 min. 30 sec.<BR> ## Part Three - 13 min.<BR> ## Part Four - 4 min. 30 sec.<P> ## <I>CD <A HREF="/ovar/sovrev/schnittke/bis1157.htm">BIS 1157</A>: Swedish Radio Choir, Tonu Kaljuste (cond)<BR> ## CD Chandos CHAN 9126: Danish National Radio Choir, Stefan Parkman Cond)<BR> ## CD Chandos CHAN 9332: Russian State Symphonic Cappella, Valery Polyansky (cond)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/cda67297.htm">Hyperion CDA 67297</A>: Holst Singers, Stephen Layton (cond)<BR> ## CD Melodiya SUCD 10 00066: USSR Ministry of Culture Chamber Chorus, Valery Polyansky (cond), E. Dof-Donskaya (soprano)<BR> ## CD Nonesuch 79500-2: Kronos Quartet (arrangement)</I></DL> Title-RAW: Othello, ballet in two acts by John Neumeier after the tragedy by William Shakespeare Title-Opus: 185 Title-Dates: 1985 Title-RAW: "Sketches", choreographic phantasy on a theme by Nikolai Gogol, ballet Title-In-Movements: in one movement Title-Opus: 186 Title-Dates: 1985 ## <DL><DD><I>CD CdM Russian Season RUS 288 155: Bolshoi Theatre Orchestra, Andrey Chistiakov (cond)</I></DL> Title-RAW: "(K)ein Sommernachtstraum", subtitled Title-Name: Not after Shakespeare Title-For: symphony orchestra Title-Opus: 187 Title-Dates: 1985 ## <DL><DD>Duration: 11 minutes.<P> ## <I>CD BIS 437: Malmö SO, Leif Segerstam (cond)<BR> ## CD Chandos CHAN 9722: Russian State SO, Valery Polyansky (cond)<BR> ## CD Pope Music Pm 1007-2: Russian SO, Mark Gorenstein (cond)</I></DL> Title-Type: Concerto Grosso Title-No: 3 Title-For: two violins, harpsichord, celesta, piano and fourteen strings Title-Opus: 188 Title-Dates: 1985 ## <DL><DD>In five movements:<BR> ## 1. Allegro - 2 min.<BR> ## 2. Risoluto - 3 min.<BR> ## 3. Pesante - 8 min.<BR> ## 4. Adagio - 8 min.<BR> ## 5. Moderato - 3 min.<P> ## <I>CD Decca 430 698-2: Royal Concertgebouw Orchestra, Riccardo Chailly (cond), R. Brautigam (harpsichord/piano), Viktor Lieberman & Jaap van Zweden (violin)<BR> ## CD London 430 698-2: Royal Concertgebouw Orchestra, Riccardo Chailly (cond), R. Brautigam (harpsichord/piano), Viktor Lieberman & Jaap van Zweden (violin)<BR> ## CD BIS CD 537: Stockholm Chamber Orchestra, Lev Markiz (cond), Patrik Swedrup & Tale Olsson (violin)</I></DL> Title-Type: Concerto Title-For: viola and orchestra Title-Opus: 189 Title-Dates: 1985 ## <DL><DD>In three movements:<BR> ## 1. Largo - 5 min.<BR> ## 2. Allegro molto - 12 min.<BR> ## 3. Largo - 17 min.<P> ## <I>LP Melodiya A10 00499 007: USSR Ministry of Culture SO, Gennadi Rozhdestvensky (cond), Yuri Bashmet (viola)<BR> ## CD BIS CD 447: Malmö SO, Lev Markiz (cond), Nobuko Imai (viola)<BR> ## CD EMI CDC 5 55107-2: Jerusalem SO, David Shallon (cond), Tabea Zimmermann (viola)<BR> ## CD Koch Schwann 31523-2: Philharmonia Orchestra, Heinrich Schiff (cond), Isabelle van Keulen (viola)<BR> ## CD Melodiya SUCD 10-00068: USSR Ministry of Culture SO, Gennadi Rozhdestvensky (cond), Yuri Bashmet (viola)<BR> ## CD Regis RRC 1141: USSR Ministry of Culture SO, Gennadi Rozhdestvensky (cond), Yuri Bashmet (viola)<BR> ## CD RCA Victor Red Seal RD 60446: London SO, Mstislav Rostropovich (cond), Yuri Bashmet (viola)<BR> ## CD RCA Victor 74321-24894-2 (2 CD-set): London SO, Mstislav Rostropovich (cond), Yuri Bashmet (viola)<BR> ## CD <A HREF="/ovar/sovrev/kancheli/ecm1471.htm">ECM New Series 1471</A> (437 199-2): Saarbrücken Radio SO, Dennis Russell Davies (cond), Kim Kashkashian (viola)<BR> ## DVD TDK DV-VPOVG: Vienna Philharmonic Orchestra, Valery Gergiev (cond), Yuri Bashmet (viola) (Live recording, 2000)</I></DL> Title-RAW: Music to an Imagined Play Title-For: ensemble Title-Opus: 190 Title-Dates: 1985 ## <DL><DD>In three movements:<BR> ## 1. Winter Road<BR> ## 2. Budding Song<BR> ## 3. March<P> ## <I>CD <A HREF="/ovar/sovrev/schnittke/ocd606.htm">Olympia OCD 606</A>: USSR Cinematography SO, Emin Khachaturian (cond)</I></DL> Title-Type: String Trio Title-Opus: 191 Title-Dates: 1985 ## <DL><DD>Commissioned by the Alban Berg Society in commemoration of the composer's 100th Anniversary.<BR> ## In two movements:<BR> ## 1. Moderato - 12 min. 30 sec.<BR> ## 2. Adagio - 13 min.<P> ## <I>CD ASV CDDCA 868: Mateja Marinkovic (violin), Paul Silverthorne (viola), Timothy Hugh (cello)<BR> ## CD BIS CD 547: Patrik Swedrup (violin), Ingegerd Kierkegaard (viola), Helena Nilsson (cello)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/emi55627.htm">EMI CDC 5 55627 2</A>: Gidon Kremer (violin), Yuri Bashmet (viola), M. Rostropovich (cello)<BR> ## CD Hyperion CDA 66885: Capricorn<BR> ## CD <A HREF="/ovar/sovrev/schnittke/naxos8554728.htm">Naxos 8.554728</A>: Mark Lubotsky & Dimity Hall (violin), Theodore Kuchar & Irini Morozova (viola), Alexander Ivashkin & Julian Smiles(cello), Irina Schnittke (piano)</I></DL> Title-RAW: Transcription of Alban Berg's Canon, arrangement Title-For: nine strings Title-Opus: 192 Title-Dates: 1985 Title-RAW: Transcription of Alban Berg's Canon, arrangement Title-For: violin and chamberorchestra Title-Opus: 192a Title-Dates: 1987 ## <DL><DD><I>CD <A HREF="/ovar/sovrev/schnittke/emi55627.htm">EMI CDC 5 55627 2</A>: Moscow Soloists, Gidon Kremer (violin)</I></DL> Title-Type: Concerto Title-No: 1 Title-For: cello and orchestra Title-Opus: 193 Title-Dates: 1985-1986 ## <DL><DD>In four movements:<BR> ## 1. Pesante: Moderato - 14 min.<BR> ## 2. Largo - 10 min.<BR> ## 3. Allegro vivace - 4 min.<BR> ## 4. Largo - 12 min.<P> ## <I>CD BIS CD 507: Danish National Radio SO, Leif Segerstam (cond), Torleif Thédéen (cello)<BR> ## CD BIS CD 1507: Danish National Radio SO, Leif Segerstam (cond), Torleif Thédéen (cello)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/bis300507.htm">BIS CD 300507</A>: Danish National Radio SO, Leif Segerstam (cond), Torleif Thédéen (cello)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/chan9852.htm">Chandos CHAN 9852</A>: Russian State Symphony Orchestra, Valery Polyansky (cond), Alexander Ivashkin (cello)<BR> ## CD EMI CDC 7 54443-2: London PO, Kurt Masur (cond), Natalia Gutman (cello)<BR> ## CD Marco Polo 8.223334: Saarbrucken Radio SO, Gerhard Markson (cond), Maria Kliegel (cello)<BR> ## CD Regis RRC 1141: USSR Ministry of Culture SO, Gennadi Rozhdestvensky (cond), Natalia Gutman (cello)<BR> ## CD Sikorski SIK 7-003E: USSR Ministry of Culture SO, Gennadi Rozhdestvensky (cond), Natalia Gutman (cello)</I></DL> Title-RAW: "Peer Gynt", ballet in three acts by John Neumeier based on Henrik Ibsen's drama Title-Opus: 194 Title-Dates: 1986 ## <DL><DD>Parts:<BR> ## Prologue<BR> ## 1. Into the World - 4 min.<BR> ## <I>Act I: Norway</I><BR> ## 2. Peer and his mother Åse - 2 min.<BR> ## 3. Peer´s Imagination - 4 min.<BR> ## 4. Peer at Ingrid´s wedding celebration - 2 min.<BR> ## 5. Appearance of Solveig and her parents - 4 min.<BR> ## 6. Pas de deux: Solveig-Peer - 4 min.<BR> ## 7. The World of the small-minded Locals - 1 min.<BR> ## 8. In the mountains with Ingrid - 5 min.<BR> ## 9. The Troll-world - 5 min.<BR> ## 10. The Böyg - 3 min. 30 sec.<BR> ## 11. Peer´s Solitude - 1 min. 30 sec.<BR> ## 12. Solveig comes to Peer (pas de deux) - 6 min. 30 sec.<BR> ## 13. The Woman in Green - 2 min. 30 sec.<BR> ## 14. Åse´s Death (pas de deux) - 5 min. 30 sec.<BR> ## <I>Act II: Out in the World - Illusions</I><BR> ## 15. Overture - 3 min. 30 sec.<BR> ## 16. Auditions - 4 min.<BR> ## 17. Rainbow Sextet - 3 min.<BR> ## 18. Peer as Slavedealer - 1 min.<BR> ## 19. Scene and Opening Night Party - 4 min. 30 sec.<BR> ## 20. Emperor of the World - 2 min. 30 sec.<BR> ## 21. Peer´s dance with the whip - 2 min.<BR> ## 22. Solveig´s dance - 2 min. 30 sec.<BR> ## 23. Peer´s mad dance - 2 min. 30 sec.<BR> ## 24. Peer´s coronation - 1 min.<BR> ## 25. Finale - 2 min.<BR> ## <I>Act III: Return</I><BR> ## 26. Mesto - 2 min.<BR> ## 27. Andante - 4 min.<BR> ## 28. Peer´s memories - 2 min.<BR> ## 29. Ingrid´s burial - 3 min.<BR> ## 30. Scene with Solveig - 6 min. 30 sec.<BR> ## 31. Peer surrounded by his aspects - 1 min.<BR> ## 32. Song of the World - 30 sec.<BR> ## 33. The Onion - 3 min.<BR> ## 34. Despair and escape - 40 sec.<BR> ## 35. Deliverance - 2 min.<BR> ## <I>Epilogue:</I><BR> ## 36. Out of the world - 24 min.<BR> ## 37. Appendix - 2 min.<P> ## <I>CD BIS CD 677/8: Stockholm Royal Opera Orchestra, Eri Klas (cond)</I></DL> Title-Type: Epilogue Title-Related-How: from Title-Related-Name: Peer Gynt Title-For: symphony orchestra and tape (mixed chorus) Title-Opus: 194a Title-Dates: 1987 ## <DL><DD><I>Live Rec: Oslo PO, Mariss Yansons (cond)</I></DL> Title-Type: Epilogue Title-Related-How: from Title-Related-Name: Peer Gynt Title-For: cello, piano and tape Title-Opus: 194b Title-Dates: 1993 ## <DL><DD><I>CD Chandos CHAN 9705: Alexander Ivashkin (cello), Irina Schnittke (piano)</I></DL> Title-Type: Trio Sonata Title-For: chamber orchestra Title-Opus: 195 Title-Dates: 1987 ## <DL><DD>Arrangement after the String Trio (1985).<BR> ## 1. Moderato - 18 min.<BR> ## 2. Adagio - 16 min.<P> ## <I>CD BIS CD 537: Stockholm Chamber Orchestra, Lev Markiz (cond)<BR> ## CD Col legno WWE 8CD20041 (9 CD-set)<BR> ## CD ECM New Series 453-512-2: Stuttgart Chamber Orchestra, Dennis Russell Davies (cond)<BR> ## CD RCA Victor Red Seal RD 60446: Moscow Soloists, Yuri Bashmet (cond)</I></DL> Title-Name: Quasi Una Sonata Title-For: violin and chamber orchestra Title-Opus: 196 Title-Dates: 1987 ## <DL><DD>Arrangement of Violin Sonata no. 2 (1967).<P> ## <I>CD <A HREF="/ovar/sovrev/schnittke/bis1437.htm">BIS CD 1437</A>: Tapiola Sinfonietta, Ralf Gothoni (cond), Ulf Wallin (violin)<BR> ## CD DG 429 413-2: Chamber Orchestra of Europe, Gidon Kremer (cond & violin), Yuri Smirnov (piano)<BR> ## CD DG 445 520-2: Chamber Orchestra of Europe, Gidon Kremer (cond & violin), Yuri Smirnov (piano)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/dg471626.htm">DG 471 626-2</A>: Chamber Orchestra of Europe, Gidon Kremer (violin)<BR> ## CD Sony Classical SK 53 271: English Chamber Orchestra, Mstislav Rostropovich (cond), Mark Lubotsky (violin)</I></DL> Title-Type: Piano Sonata Title-No: 1 Title-Opus: 197 Title-Dates: 1987-1988 ## <DL><DD>Dedicated to Vladimir Feltsman.<BR> ## 1. Lento<BR> ## 2. Allegretto<BR> ## 3. Lento<BR> ## 4. Allegro<P> ## <I>CD <A HREF="/ovar/sovrev/schnittke/bc17292.htm">Berlin Classics 0017292BC</A>: Ragna Schirmer (piano)<BR> ## CD Chandos CHAN 8962: Boris Berman (piano)<BR> ## CD Consonance 81-0009: Vasily Lobanov (piano)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/mv06.htm">MV Productions SPPS CD06</A>: Svetlana Ponomareva (piano)</I></DL> Title-Count: Four Title-Type: Aphorisms Title-For: chamber orchestra Title-Opus: 198 Title-Dates: 1988 ## <DL><DD>In four movements:<BR> ## 1. Lento<BR> ## 2. Moderato<BR> ## 3. Allegretto<BR> ## 4. Lento<P> ## <I>LP Melodiya: Soloists Ensemble of Bolshoi Theatre Orchestra, A. Lazarev (cond)</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Balcony Title-Opus: 199 Title-Dates: 1988 Title-RAW: Symphony No. 5 / Concerto Grosso Title-No: 4 Title-Opus: 200 Title-Dates: 1988 ## <DL><DD>In four movements:<BR> ## 1. Allegro - 5 min.<BR> ## 2. Allegretto - 9 min.<BR> ## 3. Lento (attacca). Allegro - 17 min.<BR> ## 4. Lento - 9 min.<P> ## <I>CD BIS CD 427: Gothenburg SO, Neeme Järvi (cond)<BR> ## CD Decca 430 698-2: Royal Concertgebouw Orchestra, Riccardo Chailly (cond)<BR> ## CD London 430 698-2: Royal Concertgebouw Orchestra, Riccardo Chailly (cond)</I></DL> Title-Type: Concerto Title-For: piano four hands and chamber orchestra Title-In-Movements: in a single movement Title-Opus: 201 Title-Dates: 1988 ## <DL><DD>Dedicated to Viktoria Postnikova and Irina Schnittke.<P> ## <I>CD <A HREF="/ovar/sovrev/schnittke/cpo999804.htm">CPO 999 804-2</A>: Radio-Philharmonie Hannover des NDR, Eiji Oue (cond), Genova and Dimitrov Piano Duo<BR> ## CD Erato 2292-45742-2: London Sinfonietta, Gennadi Rozhdestvensky (cond), Irina Schnittke & Victoria Postnikova (piano)<BR> ## CD Revelation RV 10009: Gennadi Rozhdestvensky (cond), Irina Schnittke & Victoria Postnikova (piano)</I></DL> Title-RAW: Three Verses of Viktor Schnittke Title-For: tenor and piano Title-Opus: 202 Title-Dates: 1988 ## <DL><DD>1. Wer Gedichte macht, ...<BR> ## 2. Der Geiger<BR> ## 3. Dein Schweigen</DL> Title-RAW: "Twelve Penitential Psalms", verses of repentance Title-For: mixed chorus a cappella Title-Opus: 203 Title-Dates: 1988 ## <DL><DD>In twelve movements><P> ## <I>CD Chandos CHAN 9480: Danish National Radio Choir, Stefan Parkman (cond)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/ecm4535132.htm">ECM 453-513-2 (1583)</A>: Swedish Radio Choir, Tonu Kaljuste (cond)</I></DL> Title-RAW: Piano Quartet in A minor (after Mahler) Title-Opus: 204 Title-Dates: 1988 ## <DL><DD>Dedicated to Oleg Krysa.<BR> ## Quoting a fragment for piano quartet by the 16-year old Gustav Mahler.<BR> ## Allegro (one movement). ## Duration: 6 minutes.<P> ## <I>CD BIS CD 547: Tale Olsson (violin), Ingegerd Kierkegaard (viola), Helena Nilsson (cello), Roland Pöntinen (piano)<BR> ## CD Virgin Classics VC 7 91436-2: Borodin Quartet, Ludmilla Berlinsky (piano)</I></DL> Title-Name: Sounding Letters (Klingende Buchstaben) Title-For: solo cello Title-Opus: 205 Title-Dates: 1988 ## <DL><DD>Dedicated to Alexander Ivashkin on the occasion of his 40th birthday.<BR> ## Andantino (one movement)<BR> ## Duration: 4 minutes.<P> ## <I>CD BIS CD 507: Torleif Thédéen (cello)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/bis300507.htm">BIS CD 300507</A>: Torleif Thédéen (cello)<BR> ## CD <A HREF="/ovar/shosrev/cda67534.htm">Hyperion CDA 67534</A>: Alban Gerhardt (cello)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/naxos8554728.htm">Naxos 8.554728</A>: Alexander Ivashkin (cello)<BR> ## CD Ode Manu CDMANU 1480: Alexander Ivashkin (cello)</I></DL> Title-Type: Monologue Title-For: viola and chamberorchestra Title-Opus: 206 Title-Dates: 1989 ## <DL><DD>Largo (one movement).<P> ## <I>CD EMI CDC 5 55107-2: Jerusalem SO, David Shallon (cond), Tabea Zimmermann (viola)<BR> ## CD Melodiya SUCD 10 00492: Moscow Soloists, Yuri Bashmet (cond & viola)<BR> ## CD RCA Victor RD 60464: Moscow Soloists, Yuri Bashmet (cond & viola)<BR> ## CD RCA Victor 74321-24894-2: Moscow Soloists, Yuri Bashmet (cond & viola)</I></DL> Title-RAW: Eröffnungsvers zum Ersten Festspielsonntag Title-For: mixed chorus and organ Title-Opus: 207 Title-Dates: 1989 ## <DL><DD>Ihr Völker alle, klatscht in die Hände... (from Psalm 47)</DL> Title-Name: 3 x 7 Title-For: seven instruments Title-Opus: 208 Title-Dates: 1989 ## <DL><DD>For clarinet, horn, trombone, harpsichord, violin, violoncello and double bass.<BR> ## Moderato (one movement)</DL> Title-Type: String Quartet Title-No: 4 Title-Opus: 209 Title-Dates: 1989 ## <DL><DD>Commissioned by Vienna Concert Hall Society.<BR> ## 1. Lento<BR> ## 2. Allegro<BR> ## 3. Lento<BR> ## 4. Vivace<BR> ## 5. Lento<P> ## <I>CD <A HREF="/ovar/sovrev/schnittke/arco54.htm">Arco Diva UP 0054-2 131</A>: Kapralova Quartet<BR> ## CD EMI CDC 7 54660-2 : Alban Berg Quartet<BR> ## CD EMI CMS 5 65765-2 (4 CD-set): Alban Berg Quartet<BR> ## CD Nonesuch 79500-2: Kronos Quartet<BR> ## CD Nonesuch 7559-79504-2 (10 CD-set): Kronos Quartet</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Visitor of a Museum Title-Opus: 210 Title-Dates: 1989 Title-Type: Concerto Title-No: 2 Title-For: cello and orchestra Title-Opus: 211 Title-Dates: 1990 ## <DL><DD>Dedicated to Mstislav Rostropovich.<BR> ## 1. Moderato (attaca) - 4 min.<BR> ## 2. Allegro (attaca) - 10 min.<BR> ## 3. Lento (attaca) - 10 min.<BR> ## 4. Allegretto vivo (attaca) - 5 min.<BR> ## 5. Grave - 16 min.<P> ## <I>CD BIS CD 567: Malmö SO, Lev Markiz (cond), Torleif Thédéen (cello)<BR> ## CD Chandos CHAN 9722: Russian State SO, Valery Polyansky (cond), Alexander Ivashkin (cello)<BR> ## CD Sony Classical CD 48241: London SO, Seiji Ozawa (cond), Mstislav Rostropovich (cello)</I></DL> Title-RAW: Moz-Art à la Mozart Title-For: harp and eight flutes Title-Opus: 212 Title-Dates: 1990 Title-RAW: Madrigal in Memoriam Oleg Kagan Title-For: violin or solo Title-Opus: 213 Title-Dates: 1990 ## <DL><DD>Lento (one movement).<BR> ## Duration: 10 minutes.<P> ## <I>CD ASV CDDCA 877: Mateja Marinkovic (violin)<BR> ## CD BIS CD 697: Oleg Krysa (violin)</I></DL> Title-RAW: Madrigal in Memoriam Oleg Kagan Title-For: cello solo Title-Opus: 213a Title-Dates: 1990 ## <DL><DD>Lento (one movement)<BR> ## Duration: 12 minutes.<P> ## <I>CD BIS CD 697: Torleif Thédéen (cello)<BR> ## CD <A HREF="/ovar/shosrev/cda67534.htm">Hyperion CDA 67534</A>: Alban Gerhardt (cello)<BR> ## CD Ode Manu CDMANU 1480: Alexander Ivashkin (cello)</I></DL> Title-Count: Three Title-Type: Fragments Title-For: harpsichord Title-Opus: 214 Title-Dates: 1990 ## <DL><DD>1. Andante<BR> ## 2. Vivo<BR> ## 3. Lento</DL> Title-Count: Five Title-Type: Aphorisms Title-For: piano Title-Opus: 215 Title-Dates: 1990 ## <DL><DD>Dedicated to Joseph Brodsky and Alexander Slobodyanik.<BR> ## 1. Moderato assai<BR> ## 2. Allegretto<BR> ## 3. Lento<BR> ## 4. Senza tempo<BR> ## 5. Grave<P> ## <I>CD Chandos CHAN 9704: Boris Berman (piano)<BR> ## CD Ode Manu CDMANU 1480: Tamas Vesmas (piano)<BR> ## CD Thorofon CTH 2459: Peter Martin (piano)</I></DL> Title-Type: Piano Sonata Title-No: 2 Title-Opus: 216 Title-Dates: 1990 ## <DL><DD>Dedicated to Irina Schnittke.<BR> ## 1. Moderato<BR> ## 2. Lento<BR> ## 3. Allegro moderato<P> ## <I>CD <A HREF="/ovar/sovrev/schnittke/bc17292.htm">Berlin Classics 0017292BC</A>: Ragna Schirmer (piano)<BR> ## CD Chandos CHAN 9704: Boris Berman (piano)<BR> ## CD Sony Classical SK 53 271: Irina Schnittke (piano)</I></DL> Title-RAW: Cadenzas to Mozart's Piano Concerto in B flat major KV 39 Title-Opus: 217 Title-Dates: 1990 Title-Type: Music Title-Related-How: to the Film Title-Related-Name: Russia: Love For This Country Title-Opus: 218 Title-Dates: 1990 Title-RAW: Life with an Idiot, opera in 2 acts (4 scenes) by V. Yerofeyev after his likenamed short story Title-Opus: 219 Title-Dates: 1990-1991 ## <DL><DD><I>Sony S2K 52 495: Rotterdam PO, Vocal Ensemble, Mstislav Rostropovich (cond), Dale Duesing (baritone), ## Romain Bischkoff (bass), Teresa Ringholz (soprano), Howard Haskin (tenor), Leonid Zimnenko ## (bass), Robin Leggate (tenor)</I></DL> Title-Name: Sutartines Title-For: percussion, organ and string orchestra Title-Opus: 220 Title-Dates: 1991 Title-RAW: Suite in Old Style Title-For: chamberorchestra Title-Opus: 221 Title-Dates: 1991 ## <DL><DD>Arrangement of opus 51 by Vladimir Spivakov and Vladimir Milman.<BR> ## 1. Pastorale<BR> ## 2. Ballet<BR> ## 3. Minuet<BR> ## 4. Fugue<BR> ## 5. Pantomime<P> ## <I>LP Melodiya A10 00483 007: USSR Bolshoi Theatre Soloists Ensemble, Gennadi Rozhdestvensky (cond)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/bis1437.htm">BIS CD 1437</A>: Tapiola Sinfonietta, Ralf Gothoni (cond)<BR> ## CD Capriccio 67 016: Moscow Virtuosi, V. Spivakov (cond)<BR> ## CD Consonance 81-0009: Igor Bogulavsky (viola d’amore), Viktor Grislin (vibraphone), Alla Litvienko (harpsichord), Viktor Gabinsky (marimba), Vadim Vasilykov (bells)<BR> ## CD Melodiya SUCD 10 00010: (parts 1 & 2) USSR Bolshoi Theatre Soloists Ensemble, Igor Boguslavsky (viole d'amour)<BR> ## CD RCA Victor Red Seal RD 60370: Moscow Virtuosi, Vladimir Spivakov (cond)<BR> ## CD RCA Victor 74321-24894-2: Moscow Virtuosi, Vladimir Spivakov (cond)<BR> ## CD RCA Victor Artistes Repertoire 82876 502682: Moscow Virtuosi, Vladimir Spivakov (cond)</I></DL> Title-RAW: Festive Cantus Title-For: violin, piano, mixed chorus and orchestra Title-Opus: 222 Title-Dates: 1991 ## <DL><DD>Dedicated to Gennadi Rozhdestvensky on the occasion of his 60th birthday.</DL> Title-Type: Concerto Grosso Title-No: 5 Title-For: violin, piano and orchestra Title-Opus: 223 Title-Dates: 1991 ## <DL><DD>Commissioned by the Carnegie Hall Corporation for the Cleveland Orchestra on the occasion of the Carnegie Hall Centenary.<BR> ## 1. Allegretto<BR> ## 2. -<BR> ## 3. Allegro vivace<P> ## <I>CD DG 437 091 2: Vienna PO, Christoph von Dohnanyi (cond), Gidon Kremer (violin), Rainer Keuschnig (piano)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/dg471626.htm">DG 471 626-2</A>: Vienna PO, Christoph von Dohnanyi (cond), Gidon Kremer (violin), Rainer Keuschnig (piano)</I></DL> Title-RAW: For the 90th Birthday of Alfred Schlee Title-For: viola solo Title-Opus: 224 Title-Dates: 1991 Title-Type: Symphony Title-No: 6 Title-Opus: 225 Title-Dates: 1992 ## <DL><DD>Dedicated to Mstislav Rostropovich and The National Symphony Orchestra of Washington.<BR> ## Commissioned by The National Symphony Orchestra of Washington, D. C., and Mstislav Rostropovich.<BR> ## 1. Allegro moderato - 14 min.<BR> ## 2. Presto - 4 min.<BR> ## 3. Adagio (attaca) - 10 min.<BR> ## 4. Allegro vivace - 5 min.<P> ## <I>CD BIS CD 747: BBC National Orchestra of Wales, Tadaaki Otaka (cond)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/chan10180.htm">Chandos CHAN 10180</A>: Russian State SO, Valeri Polyansky (cond)</I></DL> Title-RAW: Agnus Dei Title-For: two sopranos, female chorus and chamber orchestra Title-Opus: 226 Title-Dates: 1992 ## <DL><DD>Part of the coope-rative work "Mass for Peace".<BR> ## Lento (one movement)</DL> Title-Type: Trio Title-For: violin, cello and piano Title-Opus: 227 Title-Dates: 1992 ## <DL><DD>Arrangement of the String Trio (1985).<BR> ## 1. Moderato - 14 min.<BR> ## 2. Adagio - 13 min.<P> ## <I>CD <A HREF="/ovar/sovrev/schnittke/asv6251.htm">ASV Quicksilva CD QS 6251</A>: Barbican Piano Trio: Gaby Lester (violin), Robert Max (cello), James Kirby (piano)<BR> ## CD BIS CD 697: Oleg Krysa (violin), Torleif Thédéen (cello), Tatiana Tchekina (piano)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/bbm1093.htm">Black Box BBM 1093</A>: Barbican Piano Trio: Gaby Lester (violin), Robert Max (cello), James Kirby (piano)<BR> ## CD Cascavelle VEL 3071: Trio Animae: Tomas Dratva (piano), Jean-Christophe Gawrysiak (violin), Dieter Hilpert (cello)<BR> ## CD Nimbus NI 5572: Vienna Piano Trio (Wolfgang Redik, violin; Marcus Trefny, cello; Stefan Mendl, piano)<BR> ## CD Sony Classical SK 53 271: Mark Lubotsky (violin), Mstislav Rostropovich (cello), Irina Schnittke (piano)</I></DL> Title-RAW: Musica Nostalgica Title-For: cello and piano Title-Opus: 228 Title-Dates: 1992 ## <DL><DD><I>CD Chandos CHAN 9705: Alexander Ivashkin (cello), Irina Schnittke (piano)<BR> ## CD <A HREF="/ovar/sovrev/kancheli/qtz2032.htm">Quartz QTZ 2032</A>: Matthew Barley (cello), Stephen De Pledge (piano)</I></DL> Title-Type: Piano Sonata Title-No: 3 Title-Opus: 229 Title-Dates: 1992 ## <DL><DD>1. Lento<BR> ## 2. Allegro<BR> ## 3. Largo<BR> ## 4. Allegro<P> ## <I>CD <A HREF="/ovar/sovrev/schnittke/bc17292.htm">Berlin Classics 0017292BC</A>: Ragna Schirmer (piano)<BR> ## CD Chandos CHAN 9704: Boris Berman (piano)</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Last Days of St. Petersburg Title-Opus: 230 Title-Dates: 1992 ## <DL><DD><I>CD <A HREF="/ovar/sovrev/schnittke/cpo999796.htm">CPO 999 796-2</A>: Radio SO Berlin, Frank Strobel (cond)</I></DL> Title-RAW: Hommage to Dr. Zhivago, musical allegory Title-Related-How: on motives of B. Pasternak's novel Title-Related-Name: Doctor Zhivago Title-Opus: 231 Title-Dates: 1993 Title-RAW: "Gesualdo", opera in seven acts, a prologue and an epilogue by Richard Bletschacher Title-Opus: 232 Title-Dates: 1993 Title-RAW: Hommage to Grieg Title-For: orchestra Title-Opus: 233 Title-Dates: 1993 ## <DL><DD>Arrangement of a fragment from the ballet "Peer Gynt" for orchestra.<BR> ## Adagio (one movement)><P> ## <I>CD <A HREF="/ovar/sovrev/schnittke/cpo999804.htm">CPO 999 804-2</A>: Radio-Philharmonie Hannover des NDR, Eiji Oue (cond)</I></DL> Title-Type: Symphony Title-No: 7 Title-Opus: 234 Title-Dates: 1993 ## <DL><DD>Dedicated to Kurt Masur.<BR> ## Commissioned by the New York Philharmonic Orchestra.<BR> ## 1. Andante (attaca) - 5 min.<BR> ## 2. Largo - 2 min. 30 sec.<BR> ## 3. Allegro - 14 min.<P> ## <I>CD BIS CD 747: BBC National Orchestra of Wales, Tadaaki Otaka (cond)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/chan9852.htm">Chandos CHAN 9852</A>: Russian State Symphony Orchestra, Valery Polyansky (cond)<BR></I></DL> Title-Type: Concerto Grosso Title-No: 6 Title-For: piano, violin and string orchestra Title-Opus: 235 Title-Dates: 1993 ## <DL><DD><I>Antes Edition BM-CD31.9187: Bulgarian Orpheus Chamber Orchestra, Raitscho Christov (cond), Falko Steinbach (piano), Yosif Raduinov (violin)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/bis1437.htm">BIS CD 1437</A>: Tapiola Sinfonietta, Ralf Gothoni (cond & piano), Ulf Wallin (violin)<BR> ## CD Chandos CHAN 9359: Stockholm PO, Gennadi Rozhdestvensky (cond), Sasha Rozhdestvensky (violin), Viktoria Postnikova (piano)<BR> ## CD Nimbus NI 5582: English SO, William Boughton (cond), Daniel Hope (violin), Simon Mulligan (piano)</I></DL> Title-RAW: "Mother" for mezzo-soprano and piano after verses by Else Lasker-Schüler Title-Opus: 236 Title-Dates: 1993 ## <DL><DD>Dedicated to Ulrich Eckhardt on occasion of his 60th birthday.<BR> ## Lento (one movement)</DL> Title-RAW: Improvisation Title-For: cello solo Title-Opus: 237 Title-Dates: 1993 ## <DL><DD>Commissioned by the 'Acanthes' contest, October 1994.<BR> ## Dedicated to Mstislav Rostropovich.<BR> ## Andante poco rubato (one movement)<P> ## <I>CD Ode Manu CDMANU 1480: Alexander Ivashkin (cello)<BR> ## CD Thorofon CTH 2459: Sonja Schröder (cello)</I></DL> Title-Type: Music Title-Related-How: to the Film Title-Related-Name: The Master and Margarita Title-Opus: 238 Title-Dates: 1993 ## <DL><DD><I>CD <A HREF="/ovar/sovrev/schnittke/cpo999796.htm">CPO 999 796-2</A>: Radio SO Berlin, Frank Strobel (cond)</I></DL> Title-RAW: "The History of Dr. Johann Faustus", opera in three acts, a prologue and an epilogue by Jörg Morgener and Alfred Schnittke Title-Opus: 239 Title-Dates: 1994 ## <DL><DD>Based on the likenamed book published by Johann Spies in 1587<P> ## <I>CD RCA Victor Red Seal 09026-68413-2: Hamburg State Opera Chorus and Orchestra, Gerd Albrecht (cond), ## Jurgen Freier (baritone), Eberhard Lorenz (tenor), Arno Raunig (alto), ## Hanna Schwarz (mezzo-soprano), Eberhard Buchner (tenor), Jonathan Barreto-Ramos (tenor), Christoph Johannes Wendel (baritone), Jurgen Fersch (bass)</I></DL> Title-Type: Symphonic Prelude Title-Opus: 240 Title-Dates: 1994 ## <DL><DD>Commissioned by the Hamburg Philharmonic State Orchestra.<BR> ## Andante (one movement).<BR> ## Duration: 17 min. 30 sec.<P> ## <I>CD <A HREF="/ovar/sovrev/schnittke/bis1217.htm">BIS CD 1217</A>: Norrkoping Symphony Orchestra, Lü Jia (cond)</I></DL> Title-Type: Symphony Title-No: 8 Title-Opus: 241 Title-Dates: 1994 ## <DL><DD>Dedicated to Gennadi Rozhdestvensky and the Royal Stockholm Philharmonic Orchestra.<BR> ## Commissioned by Stockholm Concert Hall Foundation.<BR> ## 1. Moderato - 8 min. 30 sec.<BR> ## 2. Allegro moderato - 4 min. 30 sec.<BR> ## 3. Lento - 16 min.<BR> ## 4. Allegro moderato - 5 min.<BR> ## 5. Lento - 2 min.<P> ## <I>CD <A HREF="/ovar/sovrev/schnittke/bis1217.htm">BIS CD 1217</A>: Norrkoping Symphony Orchestra, Lü Jia (cond)<BR> ## CD Chandos CHAN 9359: Stockholm PO, Gennadi Rozhdestvensky (cond)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/chan9885.htm">Chandos CHAN 9885</A>: Russian State SO, Valeri Polyansky (cond)</I></DL> Title-Name: For Liverpool Title-For: orchestra Title-Opus: 242 Title-Dates: 1994 ## <DL><DD>Commissioned by The Royal Liverpool Philharmonic Society with funds provided by the Art Council of England.<BR> ## Duration: 12 min. 30 sec.<P> ## <I>CD <A HREF="/ovar/sovrev/schnittke/bis1217.htm">BIS CD 1217</A>: Norrkoping Symphony Orchestra, Lü Jia (cond)</I></DL> Title-Type: Triple concerto Title-For: violin, viola, cello and string orchestra Title-Name: Concerto for Three Title-Opus: 243 Title-Dates: 1994 ## <DL><DD>1. Moderato - 6 min.<BR> ## 2. Larghetto - 4 min. 30 sec.<BR> ## 3. Largo - 4 min. 30 sec.<BR> ## 4. Attaca - Minuet - 5 min.<P> ## <I>CD BIS CD 1379/80 (2 CD-set): Toho Gakuen Orchestra, Koichiro Harada (cond), Yasushi Toyoshima (violin), Noboru Kamimura (cello), Nobuko Imai (viola)<BR> ## CD <A HREF="/ovar/sovrev/schnittke/emi55627.htm">EMI CDC 5 55627 2</A>: Moscow Soloists, Gidon Kremer (violin), Yuri Bashmet (viola), Mstislav Rostropovich (cello)<BR> ## CD Quartz QTZ 2052: West Kazakhstan Philharmonic Orchestra, Mikel Toms (cond), Roman Mints (violin), Maxim Rysanov (viola), Kristine Blaumane (cello)</I></DL> Title-RAW: Five Fragments to Pictures of Hieronymus Bosch Title-For: tenor, violin, trombone, harpsichord, timpani and string orchestra Title-Opus: 244 Title-Dates: 1994 ## <DL><DD>On texts by Aeshylus.<BR> ## Dedicated to Vladimir Spivakov.<BR> ## 1. Lento<BR> ## 2. Moderato<BR> ## 3. Andantino<BR> ## 4. Agitato<BR> ## 5. Senza tempo<P> ## <I>CD Capriccio 67 016: Moscow Virtuosi, V. Spivakov (cond)</I></DL> Title-Name: Lux Aeterna Title-For: mixed chorus and orchestra Title-Opus: 245 Title-Dates: 1994 ## <DL><DD>Orchestrated by Gennadi Rozhdestvensky.<BR> ## Commissioned by the International Bach Academy Stuttgart as part of the cooperative work 'Requiem of Reconciliation' for Europãisches Musikfest Stuttgart.<BR> ## Andante (one movement)</DL> Title-Type: Sonata Title-No: 2 Title-For: cello and piano Title-Opus: 246 Title-Dates: 1994 ## <DL><DD>Dedicated to Mstislav Rostropovich.<BR> ## 1. Senza tempo<BR> ## 2. Allegro<BR> ## 3. Largo<BR> ## 4. Allegro<BR> ## 5. Lento<P> ## <I>CD <A HREF="/ovar/sovrev/schnittke/bbm11032.htm">Black Box BBM 1032</A>: Raphael Wallfisch (cello), John York (piano)<BR> ## CD Chandos CHAN 9705: Alexander Ivashkin (cello), Irina Schnittke (piano)<BR> ## CD <A HREF="/ovar/sovrev/kabalevsky/emi5720162.htm">EMI CZS 572016-2</A> (13 CD-set): Mstislav Rostropovich (cello), Aza Amintayeva (piano)<BR> ## CD Thorofon CTH 2459: Sonja Schröder (cello), Peter Martin (piano)</I></DL> Title-Type: Quartet Title-For: four percussionists Title-Opus: 247 Title-Dates: 1994 ## <DL><DD>Andante (one movement)</DL> Title-Type: Sonata Title-No: 3 Title-For: violin and piano Title-Opus: 248 Title-Dates: 1994 ## <DL><DD>Dedicated to Mark Lubotsky.<BR> ## 1. Andante<BR> ## 2. Allegro (molto)<BR> ## 3. Adagio<BR> ## 4. Senza tempo<P> ## <I>CD <A HREF="/ovar/sovrev/schnittke/ni5631.htm">Nimbus NI 5631</A>: Daniel Hope (violin), Simon Mulligan (piano)<BR> ## CD Ondine ODE 893-2: Mark Lubotsky (violin), Irina Schnittke (piano)<BR> ## CD Stradivarius STR 33675: Francesco D'Orazio (violin), Giampaolo Nuti (piano)</I></DL> Title-Type: Minuet Title-For: violin, viola and cello Title-Opus: 249 Title-Dates: 1994 ## <DL><DD>Originally composed as an encore for the first performance of the Concerto for violin, viola and cello.<BR> ## Dedicated to Gidon Kremer, Yuri Bashmet and Mstislav Rostropovich.<P> ## <I>CD <A HREF="/ovar/sovrev/schnittke/emi55627.htm">EMI CDC 5 55627 2</A>: Gidon Kremer (violin), Yuri Bashmet (viola), Mstislav Rostropovich (cello)</I></DL> Title-Type: Sonatina Title-For: piano four hands Title-Opus: 250 Title-Dates: 1995 ## <DL><DD>Allegro moderato (one movement)</DL> Title-Type: Concerto Title-For: viola and small orchestra Title-Opus: 251 Title-Dates: 1997 Title-Type: Variations Title-For: string quartet Title-Opus: 252 Title-Dates: 1997 Title-Type: Symphony Title-No: 9 Title-Opus: 253 Title-Dates: 1996-1998 ## <DL><DD>Unfinished.</DL> ## <hr width=40% align=center size=3><P> ���������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/Normalize/Text/Music_Fields/D_Shostakovich.comp������������������������������������0000700�0000000�0000000�00000120446�10730440050�021215� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# format = mail-header # opus_rex \bOp(?:us\b|\.)\s*\d+[a-i]?(?:[.,;\s]\s*No\.\s*\d+(?:\.\d+)*)? Title-Type: Scherzo Title-Key: F sharp minor Title-For: orchestra Title-Opus: 1 Title-Dates: 1919 Title-Count: Eight Title-Type: Preludes Title-For: piano Title-Opus: 2 Title-Dates: 1919-1920 Title-RAW: Minuet, Prelude and Intermezzo Title-For: piano Title-Opus: 2a Title-Dates: 1919-1920 Title-RAW: Murzilka Title-For: piano Title-Opus: 2b Title-Dates: 1920 Title-Count: Five Title-Type: Preludes Title-For: piano Title-Opus: 2c Title-Dates: 1920-1921 Title-Type: Orchestration Title-Related-How: of Title-Related-Name: I Waited in the Grotto Title-Related-By: by Rimsky-Korsakov Title-For: soprano and orchestra Title-Opus: 2d Title-Dates: 1921 Title-Type: Theme and Variations Title-Key: B flat major Title-For: orchestra Title-Opus: 3 Title-Dates: 1921-1922 Title-Type: Transcription of Theme and Variations Title-Key: B flat major Title-For: solo piano Title-Opus: 3a Title-Dates: 1921-1922 Title-Name: Two Fables of Krilov Title-For: mezzo-soprano, female chorus and chamber orchestra Title-Opus: 4 Title-Dates: 1922 Title-Type: Transcription Title-Related-How: of Title-Related-Name: Two Fables of Krilov Title-For: mezzo-soprano and piano Title-Opus: 4a Title-Dates: 1922 Title-RAW: Three Fantastic Dances Title-For: piano Title-Opus: 5 Title-Dates: 1922 Title-Type: Suite Title-Key: F sharp minor Title-For: two pianos Title-Opus: 6 Title-Dates: 1922 Title-Type: Scherzo Title-Key: E flat major Title-For: orchestra Title-Opus: 7 Title-Dates: 1923-1924 Title-Type: Transcription of Scherzo Title-Key: E flat major Title-For: solo piano Title-Opus: 7a Title-Dates: 1923-1924 Title-Type: Piano Trio Title-No: 1 Title-Key: C minor Title-Opus: 8 Title-Dates: 1923 Title-Count: Three Title-Type: Pieces Title-For: cello and piano Title-Opus: 9 Title-Dates: 1923-1924 Title-Type: Symphony Title-No: 1 Title-Key: F minor Title-Opus: 10 Title-Dates: 1924-1925 Title-Type: Prelude and Scherzo Title-For: string octet/orchestra Title-Opus: 11 Title-Dates: 1924-1925 Title-Type: Piano Sonata Title-No: 1 Title-Opus: 12 Title-Dates: 1926 Title-Name: Aphorisms Title-Punct: , Title-Count: ten Title-Type: pieces Title-For: piano Title-Opus: 13 Title-Dates: 1927 Title-Type: Symphony Title-No: 2 Title-Key: B flat major Title-Name: To October Title-RAW: with chorus Title-Opus: 14 Title-Dates: 1927 Title-Type: Reduction of the choral score Title-Related-How: of Title-Related-Name: Symphony No. 2 Title-For: voices and piano Title-Opus: 14a Title-Dates: 1927 Title-Name: The Nose Title-Type-After-Name: opera in three acts Title-Related-By: after Gogol Title-Opus: 15 Title-Dates: 1927-1928 Title-Type: Suite Title-Related-How: from Title-Related-Name: The Nose Title-Punct: , Title-For: tenor, baritone and orchestra Title-Opus: 15a Title-Dates: 1927-1928 Title-Type: Reduction of the accompaniment Title-Related-How: of Title-Related-Name: The Nose Title-For: piano Title-Opus: 15b Title-Dates: 1927-1928 Title-Name: Tahiti-Trot Title-For: orchestra Title-Opus: 16 Title-Dates: 1928 Title-RAW: Two Pieces by Scarlatti Title-For: wind orchestra Title-Opus: 17 Title-Dates: 1928 Title-Type: Music Title-Related-How: to the silent film Title-Related-Name: New Babylon Title-For: small orchestra Title-Opus: 18 Title-Dates: 1928-1929 Title-Type: Suite Title-Related-How: from Title-Related-Name: New Babylon Title-For: orchestra Title-Opus: 18a Title-Dates: 1976 Title-Type: Music Title-Related-How: to the comedy Title-Related-Name: The Bedbug Title-Related-By: by Mayakovsky Title-Opus: 19 Title-Dates: 1929 Title-Type: Suite Title-Related-How: from Title-Related-Name: The Bedbug Title-For: orchestra Title-Opus: 19a Title-Dates: 1929 Title-Type: Arrangement of Music Title-Related-How: to Title-Related-Name: The Bedbug Title-For: piano Title-Opus: 19b Title-Dates: 1929 Title-Type: Symphony Title-No: 3 Title-Key: E flat major Title-Name: The First of May Title-RAW: with chorus Title-Opus: 20 Title-Dates: 1929 Title-Type: Reduction Title-Related-How: of Title-Related-Name: Symphony No. 3 Title-RAW: for solo piano including a vocal score of the final chorus Title-Opus: 20a Title-Dates: 1929 Title-Name: Six Romances on Texts by Japanese Poets Title-For: tenor and orchestra Title-Opus: 21 Title-Dates: 1928-1932 Title-Name: Six Romances on Texts by Japanese Poets Title-For: tenor and piano Title-Opus: 21a Title-Dates: 1928-1932 Title-Name: The Age of Gold Title-Type-After-Name: ballet in three acts Title-Opus: 22 Title-Dates: 1929-1930 Title-Type: Suite Title-Related-How: from Title-Related-Name: The Age of Gold Title-For: orchestra Title-Opus: 22a Title-Dates: 1929-1930 Title-Type: Polka Title-Related-How: from Title-Related-Name: The Age of Gold Title-For: solo piano Title-Opus: 22b Title-Dates: 1935 Title-Type: Polka Title-Related-How: from Title-Related-Name: The Age of Gold Title-For: piano four hands Title-Opus: 22c Title-Dates: 1962 Title-Count: Two Title-Type: Pieces Title-Related-How: for Erwin Dressel's Opera Title-Related-Name: Armer Columbus Title-For: orchestra Title-Opus: 23 Title-Dates: 1929 Title-Type: Music Title-Related-How: to the play Title-Related-Name: The Gunshot Title-Related-By: by Bezymensky Title-Opus: 24 Title-Dates: 1929 Title-Type: Music Title-Related-How: to the play Title-Related-Name: Virgin Soil Title-Related-By: by Gorbenko and L'vov Title-Opus: 25 Title-Dates: 1930 Title-Type: Transcription Title-Related-How: of Title-Related-Name: Symphony of Psalms Title-Related-By: by Stravinsky Title-For: two pianos Title-Opus: 25a Title-Dates: 1930 Title-Type: Music Title-Related-How: to the film Title-Related-Name: Alone Title-Punct: ( Title-Name: Odna Title-Punct: ) Title-Opus: 26 Title-Dates: 1930-1931 Title-Type: Suite Title-Related-How: from Title-Related-Name: Alone Title-For: orchestra Title-Opus: 26a Title-Dates: 1930-1931 Title-Name: The Bolt Title-Type-After-Name: ballet in three acts Title-Opus: 27 Title-Dates: 1930-1931 Title-Type: Suite Title-Related-How: from Title-Related-Name: The Bolt Title-For: orchestra Title-Opus: 27a Title-Dates: 1931 Title-Type: Music Title-Related-How: to the play Title-Related-Name: Rule, Britannia! Title-Related-By: by Piotrovsky Title-Opus: 28 Title-Dates: 1931 Title-Name: Lady Macbeth of the Mtsensk District Title-Type-After-Name: opera in four acts Title-Related-By: after Leskov Title-Opus: 29 Title-Dates: 1930-1932 Title-Type: Suite Title-Related-How: from Title-Related-Name: Lady Macbeth of the Mtsensk District Title-For: orchestra Title-Opus: 29a Title-Dates: 1930-1932 Title-RAW: Passacaglia from an Entr'acte Title-Related-How: to Title-Related-Name: Lady Macbeth of the Mtsensk District Title-For: organ Title-Opus: 29b Title-Dates: 1930-1932 Title-Type: Music Title-Related-How: to the film Title-Related-Name: Golden Mountains Title-Opus: 30 Title-Dates: 1931 Title-Type: Suite Title-Related-How: from Title-Related-Name: Golden Mountains Title-For: orchestra Title-Opus: 30a Title-Dates: 1931 Title-Count: Two Title-Type: Pieces Title-For: string quartet Title-Opus: 30b Title-Dates: 1931 Title-Name: The Green Company Title-Type-After-Name: overture Title-Opus: 30c Title-Dates: 1931 Title-Type: Music Title-Related-How: to the stage revue Title-Related-Name: Hypothetically Murdered Title-Related-By: by Voyevodin and Riss Title-Opus: 31 Title-Dates: 1931 Title-Type: Orchestration Title-Related-How: of Title-Related-Name: Hypothetically Murdered Title-Opus: 31a Title-Dates: 1932 Title-RAW: Reduction of Four Movements of the Music Title-Related-How: to Title-Related-Name: Hypothetically Murdered Title-For: piano Title-Opus: 31b Title-Dates: 1931 Title-Type: Music Title-Related-How: to the play Title-Related-Name: Hamlet Title-Related-By: by Shakespeare Title-Opus: 32 Title-Dates: 1931-1932 Title-Type: Suite Title-Related-How: from Title-Related-Name: Hamlet Title-For: small orchestra Title-Opus: 32a Title-Dates: 1932 Title-Name: From Karl Marx to Our Own Days Title-Type-After-Name: symphonic poem Title-For: solo voices, chorus and orchestra Title-Opus: 32b Title-Dates: 1932 Title-Name: The Big Lightning Title-RAW: , unfinished comic opera Title-Opus: 32c Title-Dates: 1932 Title-Type: Music Title-Related-How: to the film Title-Related-Name: Counterplan Title-Opus: 33 Title-Dates: 1932 Title-RAW: "Song about the Oncoming Train" and Title-Name: My Heart's Aching and Moaning Title-Related-How: from Title-Related-Name: Counterplan Title-For: voice and piano Title-Opus: 33a Title-Dates: 1956 Title-Name: We meet this Morning (The Song of the Young Workers) Title-Related-How: from Title-Related-Name: Counterplan Title-For: voice and piano Title-Opus: 33b Title-Dates: 1956 Title-Count: Twenty-Four Title-Type: Preludes Title-For: piano Title-Opus: 34 Title-Dates: 1932-1933 Title-Type: Transcription of Twenty-Four Preludes Title-For: violin and piano Title-Opus: 34b Title-Dates: 1932-1933 Title-Type: Transcription of Twenty-Four Preludes Title-For: orchestra Title-Opus: 34c Title-Dates: 1932-1933 Title-RAW: Transcription of Prelude Opus 34 Title-No: 14 Title-For: orchestra Title-Opus: 34d Title-Dates: 1932-1933 Title-RAW: Concerto in C minor for piano, trumpet and strings, also known as Piano Concerto Title-No: 1 Title-Opus: 35 Title-Dates: 1933 Title-Type: Reduction of Piano Concerto Title-No: 1 Title-For: two pianos Title-Opus: 35a Title-Dates: 1933 Title-Type: Music Title-Related-How: to the animated film Title-Related-Name: The Tale of the Priest and His Worker Balda Title-For: chamber orchestra Title-Opus: 36 Title-Dates: 1933-1934 Title-Type: Suite Title-Related-How: from Title-Related-Name: The Tale of the Priest and his Worker Balda Title-Opus: 36a Title-Dates: 1935 Title-Type: Music Title-Related-How: to the play Title-Related-Name: The Human Comedy Title-Related-By: after Balzac Title-For: small orchestra Title-Opus: 37 Title-Dates: 1933-1934 Title-Type: Music Title-Related-How: to the film Title-Related-Name: Love and Hate Title-Opus: 38 Title-Dates: 1934 Title-Name: Suite for Jazz Orchestra No. 1 Title-Opus: 38a Title-Dates: 1934 Title-Name: The Limpid Stream Title-Type-After-Name: ballet in three acts Title-Opus: 39 Title-Dates: 1934-1935 Title-Type: Suite Title-Related-How: from Title-Related-Name: The Limpid Stream Title-For: orchestra Title-Opus: 39a Title-Dates: 1934-1935 Title-Type: Moderato Title-Related-How: from Title-Related-Name: The Limpid Stream Title-For: cello and piano Title-Opus: 39b Title-Dates: 1934-1935 Title-Type: Sonata Title-Key: D minor Title-For: cello and piano Title-Opus: 40 Title-Dates: 1934 Title-Type: Moderato Title-For: cello and piano Title-Opus: 40a Title-Dates: 1934 Title-Type: Music Title-Related-How: to the film Title-Related-Name: The Youth of Maxim Title-Opus: 41 Title-Dates: 1934-1935 Title-Type: Music Title-Related-How: to the film Title-Related-Name: Girl Friends Title-Opus: 41a Title-Dates: 1934-1935 Title-Count: Five Title-Type: Fragments Title-For: small orchestra Title-Opus: 42 Title-Dates: 1935 Title-Type: Symphony Title-No: 4 Title-Key: C minor Title-Opus: 43 Title-Dates: 1935-1936 Title-Type: Reduction Title-Related-How: of Title-Related-Name: Symphony No. 4 Title-For: two pianos Title-Opus: 43a Title-Dates: 1935-1936 Title-Type: Music Title-Related-How: to the play Title-Related-Name: Hail, Spain Title-Related-By: by Afinogenov Title-Opus: 44 Title-Dates: 1936 Title-Type: Music Title-Related-How: to the film Title-Related-Name: The Return of Maxim Title-Opus: 45 Title-Dates: 1936-1937 Title-Name: Four Romances on Verses by Pushkin Title-For: bass and piano Title-Opus: 46 Title-Dates: 1936-1937 Title-Type: Orchestration Title-Related-How: of Title-Related-Name: Four Romances on Verses by Pushkin Title-For: bass and orchestra Title-Opus: 46a Title-Dates: 1936-1937 Title-RAW: Arrangement of Nos. 1, 2 and 3 Title-Related-How: of Title-Related-Name: Four Romances on Verses by Pushkin Title-For: bass and string orchestra Title-Opus: 46b Title-Dates: 1936-1937 Title-Type: Symphony Title-No: 5 Title-Key: D minor Title-Opus: 47 Title-Dates: 1937 Title-RAW: Reduction of Scherzo (Allegretto) Title-Related-How: of Title-Related-Name: Symphony No. 5 Title-For: solo piano Title-Opus: 47a Title-Dates: 1937 Title-Type: Music Title-Related-How: to the film Title-Related-Name: Volochayev Days Title-Opus: 48 Title-Dates: 1936-1937 Title-Type: Orchestration Title-Related-How: of Title-Related-Name: Internationale Title-Related-By: by Degeyter Title-Opus: 48a Title-Dates: 1937 Title-Name: The Twelve Chairs Title-Type-After-Name: operetta Title-Opus: 48b Title-Dates: 1937-1938 Title-Type: String Quartet Title-No: 1 Title-Key: C major Title-Opus: 49 Title-Dates: 1938 Title-Type: Music Title-Related-How: to the film Title-Related-Name: The Vyborg District Title-Opus: 50 Title-Dates: 1938 Title-Type: Suite Title-Related-How: from Title-Related-Name: The Maxim film-Trilogy Title-For: orchestra and chorus Title-Opus: 50a Title-Dates: 1938 Title-Name: Suite for Jazz Orchestra No. 2 Title-Opus: 50b Title-Dates: 1938 Title-Type: Music Title-Related-How: to the film Title-Related-Name: Friends Title-Opus: 51 Title-Dates: 1938 Title-RAW: Vocalise Title-Related-How: from Title-Related-Name: Friends Title-RAW: for unaccompanied chorus Title-Opus: 51a Title-Dates: 1938 Title-Type: Music Title-Related-How: to the film Title-Related-Name: The Great Citizen Title-RAW: , first part Title-Opus: 52 Title-Dates: 1938 Title-Type: Music Title-Related-How: to the film Title-Related-Name: The Man with a Gun Title-Opus: 53 Title-Dates: 1938 Title-Name: Lenin Symphony Title-For: soli, chorus and orchestra Title-Opus: 53a Title-Dates: 1938-1939 Title-Type: Symphony Title-No: 6 Title-Key: B minor Title-Opus: 54 Title-Dates: 1939 Title-Type: Music Title-Related-How: to the film Title-Related-Name: The Great Citizen Title-RAW: , second part Title-Opus: 55 Title-Dates: 1939 Title-Type: Music Title-Related-How: to the animated film Title-Related-Name: The Silly Little Mouse Title-Opus: 56 Title-Dates: 1939 Title-Name: Seven Finnish Folk Songs Title-For: soprano, tenor and small orchestra Title-Opus: 56a Title-Dates: 1939 Title-Type: Piano Quintet Title-Key: G minor Title-Opus: 57 Title-Dates: 1940 Title-Type: Orchestration Title-Related-How: of the Opera Title-Related-Name: Boris Godunov Title-Related-By: by Mussorgsky Title-Opus: 58 Title-Dates: 1939-1940 Title-Type: Music Title-Related-How: to the play Title-Related-Name: King Lear Title-Related-By: by Shakespeare Title-Opus: 58a Title-Dates: 1940 Title-Type: Reduction Title-Related-How: of Title-Related-Name: King Lear Title-For: piano Title-Opus: 58b Title-Dates: 1940 Title-RAW: "Songs of the Fool" and Title-Name: Ballad of Cordelia Title-Related-How: from Title-Related-Name: King Lear Title-For: voice and piano Title-Opus: 58c Title-Dates: 1940 Title-RAW: Orchestration of "Wiener Blut" by Johann Strauss II Title-Opus: 58d Title-Dates: 1940 Title-RAW: Orchestration of "The Excursion Train Polka" by Johann Strauss II Title-Opus: 58e Title-Dates: 1940 Title-Type: Orchestration Title-Related-How: of Title-Related-Name: 27 Romances and Songs Arrangements Title-Opus: 58f Title-Dates: 1941 Title-Name: The Oath to the People's Commissar Title-For: bass, chorus and piano Title-Opus: 58g Title-Dates: 1941 Title-RAW: "Songs of a Guard's Division" ("The Fearless Regiments Are On the Move"), marching song for bass and mixed chorus with simple accompaniment for bayan or piano Title-Opus: 58h Title-Dates: 1941 Title-Type: Polka Title-For: harp duet Title-Key: F sharp minor Title-Opus: 58i Title-Dates: 1941 Title-Type: Music Title-Related-How: to the film Title-Related-Name: The Adventures of Korzinkina Title-Opus: 59 Title-Dates: 1940 Title-Type: Suite Title-Related-How: from Title-Related-Name: The Adventures of Korzinkina Title-Opus: 59a Title-Dates: 1940 Title-Count: Three Title-Type: Pieces Title-For: solo violin Title-Opus: 59b Title-Dates: 1940 Title-Name: Katyusha Maslova Title-Type-After-Name: opera Title-Related-How: after Tolsty's novel Title-Related-Name: Resurrection Title-Opus: 59c Title-Dates: 1940 Title-Type: Symphony Title-No: 7 Title-Key: C major Title-Name: Leningrad Title-Opus: 60 Title-Dates: 1941 Title-Type: Piano Sonata Title-No: 2 Title-Key: B minor Title-Opus: 61 Title-Dates: 1943 Title-Name: Six Romances on Verses by English Poets Title-For: bass and piano Title-Opus: 62 Title-Dates: 1942 Title-Name: Six Romances on Verses by English Poets Title-For: bass and orchestra Title-Opus: 62a Title-Dates: 1943 Title-Type: Music Title-Related-How: to the spectacle Title-Related-Name: Native Country Title-Type-After-Name: suite Title-Name: Native Leningrad Title-Opus: 63 Title-Dates: 1942 Title-Type: Piece Title-Related-How: of the Opera Title-Related-Name: The Gamblers Title-Related-By: after Gogol Title-Opus: 63a Title-Dates: 1941-1942 Title-Name: Solemn March Title-For: military band/wind orchestra Title-Opus: 63b Title-Dates: 1942 Title-RAW: Patriotic Song Title-Related-By: after Dolmatovsky Title-For: voices Title-Opus: 63c Title-Dates: 1943 Title-Name: Song About the Red Army Title-Related-By: after Golodny Title-Opus: 63d Title-Dates: 1943 Title-Type: Orchestration Title-Related-How: of Title-Related-Name: Eight British and American Folk Songs Title-For: voice(s) and orchestra Title-Opus: 63e Title-Dates: 1943 Title-Name: Russian Folk Songs Title-For: chorus Title-Opus: 63f Title-Dates: 1943 Title-Name: Three Russian Folk Songs Title-RAW: for two soloists and chorus with piano accompaniment Title-Opus: 63g Title-Dates: 1943 Title-Type: Music Title-Related-How: to the film Title-Related-Name: Zoya Title-Opus: 64 Title-Dates: 1944 Title-Type: Suite Title-Related-How: from Title-Related-Name: Zoya Title-For: chorus and orchestra Title-Opus: 64a Title-Dates: 1944 Title-Name: She Was Born a Brave Girl in Her Homeland Title-Related-How: from Title-Related-Name: Zoya Title-For: voice and piano Title-Opus: 64b Title-Dates: 1944 Title-Type: Symphony Title-No: 8 Title-Key: C minor Title-Opus: 65 Title-Dates: 1943 Title-Type: Music Title-Related-How: to the spectacle Title-Related-Name: Russian River Title-For: soloists, choir and orchestra Title-Opus: 66 Title-Dates: 1944 Title-Type: Orchestration Title-Related-How: of Fleishman's Chamber-Opera Title-Related-Name: Rothschild's Violin Title-Related-By: after Chekhov Title-Opus: 66a Title-Dates: 1944 Title-Type: Piano Trio Title-No: 2 Title-Key: E minor Title-Opus: 67 Title-Dates: 1944 Title-Type: String Quartet Title-No: 2 Title-Key: A major Title-Opus: 68 Title-Dates: 1944 Title-Name: Children's Notebook Title-Punct: , Title-Count: six Title-Type: pieces Title-For: piano Title-Opus: 69 Title-Dates: 1944-1945 Title-Type: Symphony Title-No: 9 Title-Key: E flat major Title-Opus: 70 Title-Dates: 1945 Title-Type: Reduction Title-Related-How: of Title-Related-Name: Symphony No. 9 Title-For: piano four hands Title-Opus: 70a Title-Dates: 1945 Title-Type: Music Title-Related-How: to the film Title-Related-Name: Simple People Title-Opus: 71 Title-Dates: 1945 Title-Count: Two Title-Type: Songs Title-Related-How: to the spectacle Title-Related-Name: Victorious Spring Title-Related-By: after Svetlov Title-For: voices and orchestra Title-Opus: 72 Title-Dates: 1945 Title-RAW: Accompaniment of Nos. 1 and 2 Title-Related-How: of Title-Related-Name: Victorious Spring Title-RAW: , arranged Title-For: piano Title-Opus: 72a Title-Dates: 1945 Title-Type: String Quartet Title-No: 3 Title-Key: F major Title-Opus: 73 Title-Dates: 1946 Title-Type: Transcription of String Quartet Title-No: 3 Title-For: strings and woodwinds Title-Opus: 73a Title-Dates: 1946 Title-Type: Reduction of String Quartet Title-No: 3 Title-For: two pianos Title-Opus: 73b Title-Dates: 1946 Title-Name: Poem of the Motherland Title-Type-After-Name: cantata Title-For: mezzosoprano, tenor, two baritones, chorus and orchestra Title-Opus: 74 Title-Dates: 1947 Title-Type: Music Title-Related-How: to the film Title-Related-Name: The Young Guards Title-RAW: after Fadeyev's novel Title-Opus: 75 Title-Dates: 1947-1948 Title-Type: Suite Title-Related-How: from Title-Related-Name: The Young Guards Title-Opus: 75a Title-Dates: 1951 Title-Type: Music Title-Related-How: to the film Title-Related-Name: Pirogov Title-Related-By: after German Title-Opus: 76 Title-Dates: 1947 Title-Type: Suite Title-Related-How: from Title-Related-Name: Pirogov Title-For: orchestra Title-Opus: 76a Title-Dates: 1947 Title-Count: Three Title-Type: Pieces Title-For: orchestra Title-Opus: 76b Title-Dates: 1947-1948 Title-Type: Violin Concerto Title-No: 1 Title-Key: A minor Title-Opus: 77 Title-Dates: 1947-1948 Title-Type: Reduction of Violin Concerto Title-No: 1 Title-For: violin and piano Title-Opus: 77a Title-Dates: 1947-1948 Title-Type: Music Title-Related-How: to the film Title-Related-Name: Michurin Title-Opus: 78 Title-Dates: 1948 Title-Type: Suite Title-Related-How: from Title-Related-Name: Michurin Title-For: chorus and orchestra Title-Opus: 78a Title-Dates: 1964 Title-Name: Rayok Title-RAW: (Little Paradise) Title-For: four voices, chorus and piano Title-Opus: 78b Title-Dates: 1948 Title-Name: From Jewish Folk Poetry Title-RAW: , song cycle Title-For: soprano, contralto, tenor and piano Title-Opus: 79 Title-Dates: 1948 Title-Name: From Jewish Folk Poetry Title-RAW: , song cycle Title-For: soprano, contralto, tenor and small orchestra Title-Opus: 79a Title-Dates: 1948 Title-Type: Music Title-Related-How: to the film Title-Related-Name: Meeting on the Elbe Title-For: voices and piano Title-Opus: 80 Title-Dates: 1948 Title-Type: Suite Title-Related-How: from Title-Related-Name: Meeting on the Elbe Title-For: voices and orchestra Title-Opus: 80a Title-Dates: 1948 Title-Count: Three Title-Type: Songs Title-Related-How: from Title-Related-Name: Meeting on the Elbe Title-For: voice and piano Title-Opus: 80b Title-Dates: 1956 Title-Name: Song of the Forests Title-Type-After-Name: oratorio Title-Related-By: after Dolmatovsky Title-For: tenor, basssoli, mixed & boys' chorus and orchestra Title-Opus: 81 Title-Dates: 1949 Title-Name: In the Fields Stand the Collective Farms Title-Related-How: from Title-Related-Name: Song of the Forests Title-For: children's chorus and mixed chorus Title-Opus: 81a Title-Dates: 1960 Title-Name: A Walk into the Future Title-Related-How: from Title-Related-Name: Song of the Forests Title-For: voice and piano Title-Opus: 81b Title-Dates: 1962 Title-Type: Music Title-Related-How: to the film Title-Related-Name: The Fall of Berlin Title-Opus: 82 Title-Dates: 1949 Title-Type: Suite Title-Related-How: from Title-Related-Name: The Fall of Berlin Title-For: chorus and orchestra Title-Opus: 82a Title-Dates: 1950 Title-Name: Beautiful Day Title-Related-How: from Title-Related-Name: The Fall of Berlin Title-RAW: , song for two-part children's chorus and piano Title-Opus: 82b Title-Dates: 1950 Title-Name: Vocalise Title-Related-How: from Title-Related-Name: The Fall of Berlin Title-RAW: , song for s.a.t.b. chorus a cappella Title-Opus: 82c Title-Dates: 1950 Title-Type: String Quartet Title-No: 4 Title-Key: D major Title-Opus: 83 Title-Dates: 1949 Title-Type: Transcription of String Quartet Title-No: 4 Title-For: orchestra Title-Opus: 83a Title-Dates: 1949, arranged by Rudolf Barshai Title-Type: Reduction of String Quartet Title-No: 4 Title-For: two pianos four hands Title-Opus: 83b Title-Dates: 1949 Title-Name: Two Romances on Verses by Lermontov Title-For: male voice and piano Title-Opus: 84 Title-Dates: 1950 Title-Name: Ballet Suite No. 1 Title-For: orchestra Title-Opus: 84a Title-Dates: 1949 Title-RAW: Merry March Title-For: two pianos Title-Opus: 84b Title-Dates: 1949 Title-Type: Music Title-Related-How: to the film Title-Related-Name: Byelinsky Title-For: orchestra and chorus Title-Opus: 85 Title-Dates: 1950 Title-Type: Suite Title-Related-How: from Title-Related-Name: Byelinsky Title-For: chorus and orchestra Title-Opus: 85a Title-Dates: 1960, assembled by L. Atovmian Title-RAW: Four Choruses from the Music Title-Related-How: to Title-Related-Name: Byelinsky Title-RAW: for s.a.t.b. chorus a cappella Title-Opus: 85b Title-Dates: 1950 Title-Name: Four Songs to Words by Dolmatovsky Title-For: voice and piano Title-Opus: 86 Title-Dates: 1951 Title-Name: The Homeland Hears Title-RAW: for chorus and tenor soloist with wordless chorus Title-Opus: 86a Title-Dates: 1951 Title-Name: Ten Russian Folk Song Arrangements Title-For: soloists, mixed chorus and piano Title-Opus: 86b Title-Dates: 1951 Title-Count: Twenty-Four Title-Type: Preludes and Fugues Title-For: piano Title-Opus: 87 Title-Dates: 1950-1951 Title-RAW: Arrangement of No. 15 of Twenty-Four Preludes and Fugues Title-For: two pianos Title-Opus: 87a Title-Dates: 1963 Title-RAW: Arrangement of No. 8 of Twenty-Four Preludes and Fugues Title-For: orchestra Title-Opus: 87b Title-Dates: 1990 Title-Type: Transcription of Twenty-Four Preludes and Fugues Title-For: violin and piano Title-Opus: 87c Title-Dates: 1990 Title-Name: Ten Poems on Texts by Revolutionary Poets Title-For: chorus and boys' chorus a cappella Title-Opus: 88 Title-Dates: 1951 Title-Type: Music Title-Related-How: to the film Title-Related-Name: The Unforgettable Year 1919 Title-Opus: 89 Title-Dates: 1951 Title-Type: Suite Title-Related-How: from Title-Related-Name: The Unforgettable Year 1919 Title-For: orchestra Title-Opus: 89a Title-Dates: 1953, assembled by L. Atovmian Title-Name: Ballet Suite No. 2 Title-For: orchestra Title-Opus: 89b Title-Dates: 1951, assembled by L. Atovmian Title-Count: Two Title-Type: Pieces Title-Related-How: from Title-Related-Name: Ballet Suite No. 2 Title-For: cello and piano Title-Opus: 89c Title-Dates: 1951 Title-Name: The Sun Shines on Our Motherland Title-Type-After-Name: cantata Title-Related-By: after Dolmatovsky Title-For: mixed & boys' chorus and orchestra Title-Opus: 90 Title-Dates: 1952 Title-Type: Reduction of the Accompaniment Title-Related-How: of Title-Related-Name: The Sun Shines on Our Motherland Title-For: piano Title-Opus: 90a Title-Dates: 1952 Title-Name: Four Monologues on Verses by Pushkin Title-For: bass and piano Title-Opus: 91 Title-Dates: 1952 Title-Type: Orchestration Title-Related-How: of Title-Related-Name: Four Monologues on Verses by Pushkin Title-For: bass and orchestra Title-Opus: 91a Title-Dates: 1952 Title-Name: Seven Doll's Dances Title-For: piano Title-Opus: 91b Title-Dates: 1952 Title-Name: Ballet Suite No. 3 Title-For: orchestra Title-Opus: 91c Title-Dates: 1952 Title-RAW: Greek Songs Title-For: voice and piano Title-Opus: 91d Title-Dates: 1952-1953 Title-Name: Ballet Suite No. 4 Title-For: orchestra Title-Opus: 91e Title-Dates: 1953 Title-Type: String Quartet Title-No: 5 Title-Key: B flat major Title-Opus: 92 Title-Dates: 1952 Title-Type: Symphony Title-No: 10 Title-Key: E minor Title-Opus: 93 Title-Dates: 1953 Title-Type: Reduction Title-Related-How: of Title-Related-Name: Symphony No. 10 Title-For: piano four hands Title-Opus: 93a Title-Dates: 1953 Title-RAW: Concertino Title-For: two pianos Title-Key: A minor Title-Opus: 94 Title-Dates: 1953 Title-Type: Music Title-Related-How: to the film Title-Related-Name: Song of the Great Rivers Title-Opus: 95 Title-Dates: 1954 Title-Name: Poem of Labour Title-Related-How: from Title-Related-Name: Unity Title-RAW: , arranged Title-For: mixed chorus and orchestra Title-Opus: 95a Title-Dates: 1954 Title-Count: Two Title-Type: Songs Title-Related-How: from Title-Related-Name: Unity Title-RAW: ("A Song of Unity" and "Peaceful Labour"), arranged Title-For: voice and piano Title-Opus: 95b Title-Dates: 1954 Title-Type: Waltz Title-Related-How: from Title-Related-Name: Unity Title-For: orchestra Title-Opus: 95c Title-Dates: 1954 Title-Type: Music Title-Related-How: to the play Title-Related-Name: Hamlet Title-Related-By: by Shakespeare Title-Opus: 95d Title-Dates: 1954 Title-RAW: "Pendozalis", Greek Song Title-For: voice and piano Title-Opus: 95e Title-Dates: 1954 Title-Name: October Dawn Title-Type-After-Name: song Title-For: soloists and chorus Title-Opus: 95f Title-Dates: 1954 Title-Name: Festive Overture Title-Key: A major Title-For: orchestra Title-Opus: 96 Title-Dates: 1954 Title-Type: Music Title-Related-How: to the film Title-Related-Name: The Gadfly Title-RAW: , based on the novel by Voynich Title-Opus: 97 Title-Dates: 1955 Title-Type: Suite Title-Related-How: from Title-Related-Name: The Gadfly Title-For: orchestra Title-Opus: 97a Title-Dates: 1955 Title-Name: Tarantella Title-Related-How: from Title-Related-Name: The Gadfly Title-For: two pianos Title-Opus: 97b Title-Dates: 1955 Title-Count: Four Title-Type: Waltzes Title-For: flute, clarinet and piano Title-Opus: 97c Title-Dates: 1955 Title-RAW: Three Violin Duets for two violins with piano accompaniment Title-Opus: 97d Title-Dates: 1955 Title-RAW: Five Romances on Verses by Dolmatovsky Title-For: bass and piano Title-Opus: 98 Title-Dates: 1954 Title-Name: There Were Kisses Title-Type-After-Name: song Title-Related-By: after Dolmatovsky Title-For: voice and piano Title-Opus: 98a Title-Dates: 1954 Title-Type: Music Title-Related-How: to the film Title-Related-Name: The First Echelon Title-Opus: 99 Title-Dates: 1955-1956 Title-Type: Suite Title-Related-How: from Title-Related-Name: The First Echelon Title-For: chorus and orchestra Title-Opus: 99a Title-Dates: 1956 Title-RAW: Two Songs from the Music Title-Related-How: to Title-Related-Name: The First Echelon Title-For: voice and piano Title-Opus: 99b Title-Dates: 1956 Title-RAW: Spanish Songs for (mezzo)soprano and piano Title-Opus: 100 Title-Dates: 1956 Title-Type: String Quartet Title-No: 6 Title-Key: G major Title-Opus: 101 Title-Dates: 1956 Title-Type: Piano Concerto Title-No: 2 Title-Key: F major Title-Opus: 102 Title-Dates: 1957 Title-Type: Reduction of Piano Concerto Title-No: 2 Title-For: two pianos Title-Opus: 102a Title-Dates: 1957 Title-Type: Symphony Title-No: 11 Title-Key: G minor Title-Name: The Year 1905 Title-Opus: 103 Title-Dates: 1957 Title-Type: Reduction Title-Related-How: of Title-Related-Name: Symphony No. 11 Title-For: piano four hands Title-Opus: 103a Title-Dates: 1957 Title-Name: Cultivation: Two Russian Folk Song Arrangements Title-For: chorus a cappella Title-Opus: 104 Title-Dates: 1957 Title-Count: Eleven Title-Type: Variations Title-Related-On: a Theme by Glinka Title-For: piano Title-Opus: 104a Title-Dates: 1957 Title-Name: Moscow, Cheryomushki Title-Type-After-Name: operetta in three acts Title-Opus: 105 Title-Dates: 1958 Title-Type: Music Title-Related-How: to the film Title-Related-Name: Cheryomushki Title-Opus: 105a Title-Dates: 1962 Title-Type: Re-orchestration Title-Related-How: of Mussorgsky's Opera Title-Related-Name: Khovanshchina Title-Opus: 106 Title-Dates: 1959 Title-Type: Cello Concerto Title-No: 1 Title-Key: E flat major Title-Opus: 107 Title-Dates: 1959 Title-Type: Reduction of Cello Concerto Title-No: 1 Title-For: cello and piano Title-Opus: 107a Title-Dates: 1959 Title-Type: String Quartet Title-No: 7 Title-Key: F sharp minor Title-Opus: 108 Title-Dates: 1960 Title-Name: Satires (Pictures of the Past) Title-RAW: , Five Romances on Verses by Chorny Title-For: soprano and piano Title-Opus: 109 Title-Dates: 1960 Title-Type: Orchestration Title-Related-How: of Title-Related-Name: Satires (Pictures of the Past) Title-RAW: , Five Romances on Verses by Chorny for soprano and orchestra (Orchestration by Boris Tishchenko) Title-Opus: 109a Title-Type: String Quartet Title-No: 8 Title-Key: C minor Title-Opus: 110 Title-Dates: 1960 Title-RAW: Chamber Symphony in C minor (Arr. Rudolf Barshai) Title-Opus: 110a Title-Type: Music Title-Related-How: to the film Title-Related-Name: Five Days - Five Nights Title-Opus: 111 Title-Dates: 1960 Title-Type: Suite Title-Related-How: from Title-Related-Name: Five Days - Five Nights Title-For: orchestra Title-Opus: 111a Title-Dates: 1961 Title-Name: Novorossiisk Chimes, the Flame of Eternal Glory Title-For: orchestra Title-Opus: 111b Title-Dates: 1960 Title-Type: Symphony Title-No: 12 Title-Key: D minor Title-Name: The Year 1917 Title-Opus: 112 Title-Dates: 1961 Title-Type: Reduction Title-Related-How: of Title-Related-Name: Symphony No. 12 Title-For: two pianos, four hands Title-Opus: 112a Title-Dates: 1961 Title-Type: Symphony Title-No: 13 Title-Key: B flat minor Title-Name: Babi-Yar Title-For: bass, bass chorus and orchestra Title-Opus: 113 Title-Dates: 1962 Title-Type: Reduction Title-Related-How: of Title-Related-Name: Symphony No. 13 Title-For: two pianos four hands Title-Opus: 113a Title-Dates: 1962 Title-Name: Katerina Izmailova Title-Type-After-Name: opera in four acts Title-Related-By: after Leskov Title-Opus: 114 Title-Dates: 1956-1963 Title-Type: Suite of Five Fragments Title-Related-How: from the Opera Title-Related-Name: Katarina Izmailova Title-For: orchestra Title-Opus: 114a Title-Dates: 1963 Title-Type: Music Title-Related-How: to the film Title-Related-Name: Katerina Izmailova Title-Opus: 114b Title-Dates: 1966 Title-Type: Passacaglia Title-Related-How: from the Opera Title-Related-Name: Katerina Izmailova Title-For: organ Title-Opus: 114c Title-Name: Overture on Russian and Khirghiz Folk Themes Title-For: orchestra Title-Opus: 115 Title-Dates: 1963 Title-Type: Music Title-Related-How: to the film Title-Related-Name: Hamlet Title-Related-By: after Shakespeare Title-For: orchestra Title-Opus: 116 Title-Dates: 1963-1964 Title-Type: Suite Title-Related-How: from Title-Related-Name: Hamlet Title-For: orchestra Title-Opus: 116a Title-Dates: 1964 Title-Type: String Quartet Title-No: 9 Title-Key: E flat major Title-Opus: 117 Title-Dates: 1964 Title-Type: String Quartet Title-No: 10 Title-Key: A flat major Title-Opus: 118 Title-Dates: 1964 Title-Name: Symphony for Strings Title-RAW: in A-flat major (Arr. Rudolf Barshai) Title-Opus: 118a Title-Name: The Execution of Stepan Razin Title-Type-After-Name: cantata Title-Related-By: after Yevtushenko Title-For: bass, mixed chorus and orchestra Title-Opus: 119 Title-Dates: 1964 Title-Type: Reduction Title-Related-How: of Title-Related-Name: The Execution of Stepan Razin Title-For: voices and piano Title-Opus: 119a Title-Dates: 1964 Title-Type: Music Title-Related-How: to the film Title-Related-Name: A Year Is Like a Lifetime Title-For: orchestra Title-Opus: 120 Title-Dates: 1965 Title-Type: Suite Title-Related-How: from Title-Related-Name: A Year Is Like a Lifetime Title-For: orchestra Title-Opus: 120a Title-Dates: 1965 Title-RAW: Five Romances on Texts from the Magazine Title-Name: Krokodil Title-For: bass and piano Title-Opus: 121 Title-Dates: 1965 Title-RAW: Orchestration of Five Romances on texts from the Magazine Title-Name: Krokodil Title-RAW: (Orcestration by Boris Tishchenko) Title-Opus: 121a Title-Type: String Quartet Title-No: 11 Title-Key: F minor Title-Opus: 122 Title-Dates: 1966 Title-Name: Preface to the Complete Collection of My Works and Brief Reflections on this Preface Title-For: bass and piano Title-Opus: 123 Title-Dates: 1966 Title-Count: Two Title-Type: Choruses Title-Related-By: after Davidenko Title-For: chorus and orchestra Title-Opus: 124 Title-Dates: 1962 Title-Type: Transcription Title-Related-How: of Title-Related-Name: Songs and Dances of Death Title-Related-By: by Mussorgsky Title-For: voice and piano Title-Opus: 124a Title-Dates: 1962 Title-Name: The Lady and the Hooligan Title-Type-After-Name: ballet in one act Title-Opus: 124b Title-RAW: Instrumentation of Schumann's Cello Concerto Title-Key: A minor Title-Opus: 125 Title-Dates: 1963 Title-Type: Cello Concerto Title-No: 2 Title-Key: G minor Title-Opus: 126 Title-Dates: 1966 Title-Type: Reduction of Cello Concerto Title-No: 2 Title-For: cello and piano Title-Opus: 126a Title-Dates: 1966 Title-Name: Seven Romances on Poems by Blok Title-For: soprano, violin, cello and piano Title-Opus: 127 Title-Dates: 1967 Title-RAW: Romance "Spring, Spring" to Verses by Pushkin Title-For: bass and piano Title-Opus: 128 Title-Dates: 1967 Title-RAW: Orchestration of the Romance "Spring, Spring" to Verses by Pushkin Title-For: bass and orchestra Title-Opus: 128a Title-Dates: 1967 Title-Type: Violin Concerto Title-No: 2 Title-Key: C sharp minor Title-Opus: 129 Title-Dates: 1967 Title-Type: Reduction of Violin Concerto Title-No: 2 Title-For: violin and piano Title-Opus: 129a Title-Dates: 1967 Title-Name: Funeral-Triumphal Prelude Title-For: orchestra Title-Opus: 130 Title-Dates: 1967 Title-Name: October Title-Type-After-Name: symphonic poem Title-Key: C minor Title-For: orchestra Title-Opus: 131 Title-Dates: 1967 Title-Type: Music Title-Related-How: to the film Title-Related-Name: Sofya Perovskaya Title-Opus: 132 Title-Dates: 1967 Title-Type: String Quartet Title-No: 12 Title-Key: D flat major Title-Opus: 133 Title-Dates: 1968 Title-Type: Sonata Title-For: violin and piano Title-Opus: 134 Title-Dates: 1968 Title-RAW: Reorchestration of Tishchenko's Cello Concerto Title-No: 1 Title-Opus: 134a Title-Dates: 1969 Title-Type: Symphony Title-No: 14 Title-For: soprano, bass, string orchestra and percussion Title-Opus: 135 Title-Dates: 1969 Title-Type: Reduction Title-Related-How: of Title-Related-Name: Symphony No. 14 Title-For: voices and piano Title-Opus: 135a Title-Dates: 1969 Title-Name: Loyalty Title-RAW: , eight ballads after Dolmatovsky for unaccompanied male chorus Title-Opus: 136 Title-Dates: 1970 Title-Type: Music Title-Related-How: to the film Title-Related-Name: King Lear Title-Related-By: after Shakespeare Title-Opus: 137 Title-Dates: 1970 Title-Name: People's Lamentation Title-Related-How: from Title-Related-Name: King Lear Title-RAW: , arranged Title-For: voice and piano Title-Opus: 137a Title-Dates: 1970 Title-Type: String Quartet Title-No: 13 Title-Key: B flat minor Title-Opus: 138 Title-Dates: 1970 Title-Name: March of the Soviet Militia Title-For: military band/wind orchestra Title-Opus: 139 Title-Dates: 1970 Title-Name: Six Romances on Verses by English Poets Title-For: bass and chamber orchestra Title-Opus: 140 Title-Dates: 1971 Title-Type: Symphony Title-No: 15 Title-Key: A major Title-Opus: 141 Title-Dates: 1971 Title-Type: Reduction Title-Related-How: of Title-Related-Name: Symphony No. 15 Title-For: two pianos Title-Opus: 141a Title-Dates: 1971 Title-Type: Transcription Title-Related-How: of Title-Related-Name: Serenada Title-Related-By: by Braga Title-For: soprano, mezzo-soprano, violin and piano Title-Opus: 141b Title-Dates: 1972 Title-Type: String Quartet Title-No: 14 Title-Key: F sharp major Title-Opus: 142 Title-Dates: 1972-1973 Title-Name: Six Poems by Marina Tsvetayeva Title-Type-After-Name: suite Title-For: contralto and piano Title-Opus: 143 Title-Dates: 1973 Title-Name: Six Poems by Marina Tsvetayeva Title-Type-After-Name: suite Title-For: contralto and chamber orchestra Title-Opus: 143a Title-Dates: 1974 Title-Type: String Quartet Title-No: 15 Title-Key: E flat minor Title-Opus: 144 Title-Dates: 1974 Title-Name: Epilogue Title-Type-After-Name: Transcription of String Quartet Title-No: 15 Title-For: string orchestra Title-Opus: 144a Title-Dates: 1994 Title-RAW: Suite on Verses by Buonarrotti Title-For: bass and piano Title-Opus: 145 Title-Dates: 1974 Title-RAW: Suite on Verses by Buonarrotti Title-For: bass and orchestra Title-Opus: 145a Title-Dates: 1975 Title-Name: Four Verses of Captain Lebyadkin to Texts by Dostoyevsky Title-For: bass and piano Title-Opus: 146 Title-Dates: 1975 ## SubNumbers for Opus 146 from: http://home.wanadoo.nl/ovar/shosopus/shoslast.htm Title-Type: Orchestration Title-Related-How: of Title-Related-Name: Four Verses by Captain Lebyadkin Title-RAW: (Orchestration by Boris Tishchenko) Title-Opus: 146a Title-RAW: Orchestration of Beethoven's Arrangement (Opus 75 No. 3) Title-Related-How: of Mephistopheles's Title-Related-Name: Song of the Flea Title-Opus: 146b Title-Dates: 1975 Title-Name: The Dreamers Title-Type-After-Name: ballet in four acts Title-Opus: 146c Title-Dates: 1975 Title-Type: Sonata Title-For: viola and piano Title-Opus: 147 Title-Dates: 1975 Title-RAW: Transcription of Opus 147 Title-For: cello and piano Title-Opus: 147a Title-Dates: 1975 Title-RAW: Transcription of Opus 147 Title-For: viola, strings and celesta Title-Opus: 147b Title-Dates: 1990 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/Normalize/Text/Music_Fields/G_Gershwin.comp����������������������������������������0000700�0000000�0000000�00000060673�10641052300�020344� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# format = mail-header # no_opus_no ## Note: All orchestral/operatic pieces are orchestrated by Gershwin unless otherwise specified. ## * Lullaby (1919), a meditative piece for string quartet. Originally, a class assignment from his music theory teacher. ## * Blue Monday, a one-act opera featured in George White's Scandals of 1922, orchestrated by Will Vodery. ## + A Suite from Blue Monday for two pianos was later arranged and has been recorded. ## + Reorchestrated by Ferde Grofe and retitled 135th Street in 1925. ## * Rhapsody in Blue, (1924), his most famous work, a symphonic jazz composition for Paul Whiteman's jazz band & piano , better known in the form orchestrated for full ## symphonic orchestra by Ferde Grofe. Featured in numerous films and commercials. Title-RAW: Short Story, Title-For: violin and piano Title-Dates: 1925 ##, an arrangement of two other short pieces originally intended to be included with the Three Preludes. ## * Concerto in F, (1925), three movements, for piano and orchestra ## * Three Preludes, (1926), for piano ## * An American In Paris (1928), a symphonic poem with elements of jazz and realistic Parisian sound effects ## * Second Rhapsody for Piano and Orchestra (1931), for Piano and Orchestra, based on the score for a musical sequence from Delicious. Working title for the work was Rhapsody ## in Rivets. ## + The form most commonly heard today is a re-orchestrated version by Robert McBride; most of Gershwin's orchestrations have been simplified. Also, eight measures not by the composer ## were added to the recapitulation. Michael Tilson Thomas has been a promulgator of Gershwin's original version. ## * Cuban Overture (1932), originally titled Rumba, a tone poem featuring elements of native Cuban dance and folk music; score specifies usage of native Cuban instruments Title-RAW: Piano Transcriptions of Eight Songs Title-Dates: 1932 ## * I Got Rhythm Variations (1934), a set of interesting variations on his famous song, for piano and orchestra ## + Includes a waltz, an atonal fugue, and experimentation with Asian and jazz influences ## * Porgy And Bess, a folk opera (1935) (from the book by DuBose Heyward) about African-American life, now considered a definitive work of the American theater. ## + Contains the famous aria "Summertime", in addition to hits like "I Got Plenty of Nothin'" and "It Ain't Necessarily So". ## + Porgy and Bess has also been heard in the concert hall, mostly in two orchestral suites, one by Gershwin himself entitled Catfish Row; another suite by Robert Russell ## Bennett, Porgy and Bess: A Symphonic Picture is also relatively popular. Title-RAW: Walking the Dog Title-Dates: 1937 ##, a humorous piece for orchestra featuring the clarinet. Originally a musical sequence entitled Promenade from the movie Shall We Dance for piano ## and chamber orchestra. ## + Many other incidental sequences from Shall We Dance were written and (for the most part) orchestrated by Gershwin, among them: Waltz of the Red Balloons and a final ## extended 8-minute orchestral passage based on the title song with an intruiging coda hinting at Gershwin forging a new musical path. It is unknown why any of these ## compositions have not seen the light of day in the concert hall. ## + Most of the musicals Gershwin wrote are also known for their instrumental music, among them the March from Strike Up The Band and overtures to many of his later ## shows. Title-RAW: Impromptu in Two Keys Title-Dates: publ. posth. in 1973 ##, for piano Title-Count: Two Title-Type: Waltzes Title-Key: C major Title-Dates: publ. posth. in 1975 ##, for piano ## + Originally a two-piano interlude in Pardon My English on Broadway. ## Musical theater credits ## Note: All works are musicals produced on Broadway unless specified otherwise. Title-RAW: Half Past Eight (lyrics by Ira Gershwin and Edward B. Perkins). Premiered in Syracuse. Title-Dates: 1919 Title-RAW: La La Lucille Title-Lyrics-By: Arthur Jackson, B. G. DeSylva and Irving Caesar Title-Dates: 1919 Title-RAW: Morris Gest Title-Name: Midnight Whirl Title-Lyrics-By: B. G. DeSylva and John Henry Mears Title-Dates: 1919 Title-RAW: Limehouse Nights Title-Lyrics-By: B. G. DeSylva and John Henry Mears Title-Dates: 1919 Title-RAW: Poppyland Title-Lyrics-By: B. G. DeSylva and John Henry Mears Title-Dates: 1920 Title-RAW: George White's Scandals of 1920 Title-Lyrics-By: Arthur Jackson Title-Dates: 1920 Title-RAW: A Dangerous Maid (lyrics by Ira Gershwin). Premiered in Atlantic City. Title-Dates: 1921 Title-RAW: The Broadway Whirl (co-composed with Harry Tierney, lyrics by Buddy DeSylva, Joseph McCarthy, Richard Carle and John Henry Mears Title-Dates: 1921 Title-RAW: George White's Scandals of 1921 Title-Lyrics-By: Arthur Jackson Title-Dates: 1921 Title-RAW: George White's Scandals of 1922 Title-Lyrics-By: E. Ray Goetz, Ira Gershwin and B. G. DeSylva Title-Dates: 1922 ## + The premiere performance featured the one-act opera Blue Monday with libretto and lyrics by B. G. DeSylva, set in Harlem in a jazz idiom. However, after only one ## performance, the opera was withdrawn from the show. Gershwin also wrote seven other songs for the show. Title-RAW: Our Nell (co-composed with William Daly, lyrics co-written by Gershwin and Daly) Title-Dates: 1922 Title-RAW: By and By Title-Lyrics-By: Brian Hooker Title-Dates: 1922 Title-RAW: Innocent Ingenue Baby (co-composed with William Daly, lyrics by Brian Hooker) Title-Dates: 1923 Title-RAW: Walking Home with Angeline Title-Lyrics-By: Brian Hooker Title-Dates: 1923 Title-RAW: The Rainbow (lyrics by Clifford Grey and Brian Hooker). Premiered in London. Title-Dates: 1923 Title-RAW: George White's Scandals of 1923 Title-Lyrics-By: E. Ray Goetz, B. G. DeSylva and Ballard MacDonald Title-Dates: 1923 Title-RAW: Sweet Little Devil Title-Lyrics-By: B. G. DeSylva Title-Dates: 1924 Title-RAW: George White's Scandals of 1924 Title-Lyrics-By: B. G. DeSylva and Ballard MacDonald Title-Dates: 1924 Title-RAW: Primrose (lyrics by Desmond Carter and Ira Gershwin). Premiered in London. Title-Dates: 1924 Title-RAW: Lady, Be Good! Title-Lyrics-By: Ira Gershwin Title-Dates: 1924 Title-RAW: Tell Me More! Title-Lyrics-By: Ira Gershwin and B. G. DeSylva Title-Dates: 1925 Title-RAW: Tip-Toes Title-Lyrics-By: Ira Gershwin Title-Dates: 1925 Title-RAW: Song of the Flame (operetta, lyrics by Otto Harbach and Oscar Hammerstein II, and musical collaboration by Herbert Stothart) Title-Dates: 1925 Title-RAW: Oh, Kay! Title-Lyrics-By: Ira Gershwin and Howard Dietz Title-Dates: 1926 ## + Includes the famous song, "Someone to Watch Over Me" ## + Revived in 1928 and 1990 (the latter with an all-Black cast) Title-RAW: Strike Up The Band Title-Lyrics-By: Ira Gershwin Title-Dates: premiered in Philadelphia 1927, revised Broadway in 1930, revised 1936 for U.C.L.A # prev_aka Strike Up The Band ## + Revised and produced on Broadway in 1930 Title-RAW: Funny Face Title-Lyrics-By: Ira Gershwin Title-Dates: 1927 Title-RAW: Rosalie Title-Lyrics-By: Ira Gershwin and P. G. Wodehouse, co-composed with Sigmund Romberg Title-Dates: 1928 Title-RAW: Treasure Girl Title-Lyrics-By: Ira Gershwin Title-Dates: 1928 Title-RAW: Show Girl Title-Lyrics-By: Ira Gershwin and Gus Kahn Title-Dates: 1929 Title-RAW: Girl Crazy Title-Lyrics-By: Ira Gershwin Title-Dates: 1930 Title-RAW: Of Thee I Sing Title-Lyrics-By: Ira Gershwin Title-Dates: 1931 ## + Awarded the Pulitzer Prize for Drama for 1932 and was the first musical to win that award, although only Ira Gershwin and the bookwriters were awarded the Prize and ## not George Gershwin ## + Revived in 1933 and 1952 Title-RAW: Pardon My English Title-Lyrics-By: Ira Gershwin Title-Dates: 1933 Title-RAW: Let 'Em Eat Cake Title-Lyrics-By: Ira Gershwin Title-Dates: 1933 ## Let 'Em Eat Cake (lyrics by Ira Gershwin), sequel to Of Thee I Sing (1933) Title-RAW: Porgy and Bess Title-Lyrics-By: Ira Gershwin and DuBose Heyward Title-Dates: 1935 ## + Revived on Broadway in 1942, 1943, 1953, 1976 (Houston Grand Opera winner of the Tony Award for Most Innovative Revival of a Musical), and 1983 ## Works featuring original Gershwin songs for shows by other composers Title-RAW: The Passing Show of 1916 - "Making of a Girl" co-composed with Sigmund Romberg, lyrics by Harold Atteridge Title-Dates: 1916 Title-RAW: Hitchy-Koo of 1918 - "You-Oo just You", lyrics by Irving Caesar Title-Dates: 1918 Title-RAW: Ladies First - "(The Real) American Folk Song (is a Rag)", lyrics by Ira Gershwin and "Some Wonderful Sort of Someone", lyrics by Schuyler Greene Title-Dates: 1918 ## * 1919 - Good Morning, Judge - "I was so young (you were so beautiful)", lyrics by Irvine Caesar and Alfred Bryan and "here's more to the kiss than the x-x-x", lyrics by ## Irving Caesar Title-RAW: The Lady in Red - "Some Wonderful Sort of Someone", lyrics by Schyler Greene and "Something about Love", lyrics by L. Paley Title-Dates: 1919 Title-RAW: The Capitol Revue - "Come to the Moon", lyrics by L. Paley and Ned Wayburn, "Swanee", lyrics by Irvine Caesar Title-Dates: 1919 Title-RAW: Dear Mabel - "We're pals", lyrics by Irving Caesar, first performed in Baltimore Title-Dates: 1920 Title-RAW: Ed Wynn's Carnival - "Oo, how I love you to be loved by you", lyrics by L. Paley Title-Dates: 1920 Title-RAW: The Sweetheart Shop - "Waiting for the Sun to Come Out", lyrics by Ira Gershwin Title-Dates: 1920 Title-RAW: Sinbad - "Swanee" (as performed by Al Jolson) Title-Dates: 1920 Title-RAW: Broadway Brevities of 1920 - "Lu Lu" and "Snowflakes", lyrics by Arthur Jackson and "Spanish love", lyrics by Irving Caesar Title-Dates: 1920 Title-RAW: Piccadilly to Broadway, songs unpublished Title-Dates: 1920 Title-RAW: Blue Eyes, songs unpublished Title-Dates: 1921 Title-RAW: Selwyn's Snapshots of 1921, songs unpublished Title-Dates: 1921 Title-RAW: The Perfect Fool - "My Log-Cabin Home", lyrics by Irving Caesar and Buddy De Sylva, "No One Else but that Girl of Mine", lyrics by Irving Caesar Title-Dates: 1921 Title-RAW: The French Doll - "Do it again!", lyrics by Buddy De Sylva Title-Dates: 1922 Title-RAW: For Goodness Sake - "Someone" and "Tra-la-la", lyrics by Ira Gershwin Title-Dates: 1922 Title-RAW: The Dancing Girl - "That American Boy of Mine", lyrics by Irving Caesar Title-Dates: 1922 Title-RAW: Spice of 1922 - "The Yankee Doodle Blues", lyrics by Irving Caesar and Buddy De Sylva Title-Dates: 1922 Title-RAW: Little Miss Bluebeard (play) - "I won't say I will but I won't say I won't", lyrics by Ira Gershwin and Buddy De Sylva Title-Dates: 1923 Title-RAW: Nifties of 1923 - "At Half Past Seven", lyrics by Buddy De Sylva, and "Nashville Nightingale", lyrics by Irving Caesar Title-Dates: 1923 Title-RAW: Americana of 1926 - Title-Name: That Lost Barber Shop Chord Title-Dates: 1926 Title-RAW: 9:15 Revue Title-Dates: 1930 Title-RAW: The Show is On - Title-Name: By Strauss Title-Dates: 1936 ## + Revived in 1937 ## Works interpolating Gershwin songs posthumously: Title-RAW: At Home With Ethel Waters - Title-Name: Lady Be Good Title-Dates: 1953 Title-RAW: Mr. Wonderful Title-Dates: 1956 ### "I Got Rhythm" a hit single for pop vocal group The Happenings (1967) ### My One And Only - an adaptation of the music from Funny Face (1983) ### Uptown...It's Hot! - "Lady Be Good" (1986) ### Crazy For You - musical adapting George and Ira Gershwin Tin Pan Alley and Broadway songs (1992) ## + Awarded the Tony Award for Best Musical ### The Gershwins' Fascinating Rhythm - revue with songs by George and Ira Gershwin (1999) ## * 2001 - George Gershwin Alone - one-man play by Hershey Felder, who portrayed Gershwin, incorporating "Swanee" from Sinbad (lyrics by Irving Caesar), "Embraceable You" from ## Girl Crazy (lyrics by Ira Gershwin), "Someone to Watch Over Me" from Oh, Kay! (lyrics by Ira Gershwin), "Bess, You is My Woman Now" from Porgy and Bess ## (lyrics by DuBose Heyward and Ira Gershwin), An American in Paris and Rhapody in Blue. ### Elaine Stritch at Liberty - But Not For Me (2002) ### Back From Broadway - one-time concert featuring songs by George Gershwin (2002) ## Musical films Title-RAW: The Sunshine Trail - theme song of same title (lyrics by Ira Gershwin), as well as accompaniment music for silent film Title-Dates: 1923 Title-RAW: Delicious Title-Lyrics-By: Ira Gershwin Title-Dates: 1931 Title-RAW: Shall We Dance? Title-Lyrics-By: Ira Gershwin Title-Dates: 1937 Title-RAW: A Damsel in Distress Title-Lyrics-By: Ira Gershwin Title-Dates: 1937 Title-RAW: Goldwyn Follies Title-Lyrics-By: Ira Gershwin Title-Dates: 1938 ## + Gershwin died during the filming. Vernon Duke completed and adapted Gerhwin's songs, and composed some additional ones. Title-RAW: The Shocking Miss Pilgrim (Kay Swift adapted a number of unpublished Gershwin melodies and Ira Gershwin wrote the lyrics.) Title-Dates: 1947 Title-RAW: Kiss Me, Stupid (adaptations of unpublished Gershwin songs with lyrics by Ira Gershwin.) Title-Dates: 1964 ## Miscellaneous Songs Title-RAW: When you want 'em, you can't get 'em, when you've got 'em, you don't want 'em Title-Lyrics-By: M. Roth Title-Dates: 1916 Title-RAW: The Love of a Wife Title-Lyrics-By: Arthur Jackson and B. G. De Sylva Title-Dates: 1919 Title-RAW: Yan-Kee Title-Lyrics-By: Irving Caesar Title-Dates: 1920 Title-RAW: Dixie Rose Title-Lyrics-By: Irving Caesar and B. G. De Sylva Title-Dates: 1921 Title-RAW: In the Heart of a Geisha Title-Lyrics-By: Fred Fisher Title-Dates: 1921 Title-RAW: Swanee Rose Title-Lyrics-By: Irving Caesar and B. G. De Sylva Title-Dates: 1921 Title-RAW: Tomale (I'm hot for you) Title-Lyrics-By: B. G. De Sylva Title-Dates: 1921 Title-RAW: Harlem River Chanty and It's a great little world! Title-Lyrics-By: Ira Gershwin, originally composed for Tip-Toes on Broadway but not used Title-Dates: 1925 Title-RAW: Murderous Monty (and Light-Fingered Jane) Title-Lyrics-By: Desmond Carter, composed for London production of Tell Me More. Title-Dates: 1925 Title-RAW: I'd rather charleston Title-Lyrics-By: Desmond Carter, composed for London production of Lady Be Good. Title-Dates: 1926 Title-RAW: Beautiful gypsy and Rosalie (originally composed for Rosalie on Broadway, but not used) Title-Dates: 1928 Title-RAW: Feeling Sentimental (originally composed for Show Girl on Broadway, but not used) Title-Dates: 1929 Title-RAW: In the Mandarin's Orchid Garden Title-Dates: 1929 Title-RAW: Mischa, Yascha, Toscha, Sascha (originally composed for the musical film Delicious, but not used. Title-Dates: 1931 ## + This is Gershwin's only finished work based on a Jewish theme, and the title is a reference to the first names of four Jewish-Russian violinists, Mischa Elman, ## Jascha Heifetz, Toscha Seidel and Sascha Jacobsen. Title-RAW: You've got what gets me (for 1st film version of Girl Crazy) Title-Dates: 1932 Title-RAW: Till Then Title-Dates: 1933 Title-RAW: King of Swing Title-Lyrics-By: Al Stillman Title-Dates: 1936 Title-RAW: Strike up the band for U.C.L.A (to the same music as the song Strike Up The Band) Title-Dates: 1936 Title-RAW: Hi-Ho! Title-Lyrics-By: Ira Gershwin Title-Dates: 1937 ## originally composed for Shall We Dance, but not used Title-RAW: Just Another Rhumba Title-Lyrics-By: Ira Gershwin, originally composed for The Goldwyn Follies, but not used Title-Dates: 1938 Title-RAW: Dawn of a New Day Title-Dates: 1938 ## Commercial Works for Piano Title-RAW: Rialto Ripples - rag Title-Dates: 1918 ## * early 1920s - Three-Quarter Blues (Irish Waltz) Title-RAW: Swiss Miss (arrangement of a song from Lady Be Good) Title-Dates: 1926 Title-RAW: Merry Andrew (arrangement of a dance piece from Rosalie) Title-Dates: 1928 Title-RAW: George Gershwin's Song-Book (arrangements of refrains from Gershwin songs) Title-Dates: 1932 ### ==### PIANO // Title: 3 Preludes for Piano (1926) (transcr. for vn. and pf. by Heifetz) Title: Three Preludes for Piano (1926) (transcr. for vn. and pf. by Heifetz) Title-Count: 3 Title-Type: Preludes Title-For: Piano Title-Dates: 1926 Title-Count: Three Title-Type: Preludes Title-For: Piano Title-Dates: 1926 Title: Prelude No. 1 for Piano (1926) - Allegro ben ritmato e deciso Title: Prelude No. 2 for Piano (1926) - Andante con moto e poco rubato Title: Prelude No. 3 for Piano (1926) - Allegro ben ritmato e deciso ### ==### MUSICALS // ### The Passing Show of 1916 ### ###MUSICALS // ### La La Lucille (1919) ### ###MUSICALS // ### George White's Scandals (1920--1924) ### ###MUSICALS // ### A Dangerous Maid (1921) ### ###MUSICALS // ### Sweet Little Devil (1924) ### ###MUSICALS // ### Primrose (1924) ### ###MUSICALS // ### Lady, Be Good! (1924) ### ###MUSICALS // ### Song of the Flame (1925) ### ###MUSICALS // ### Tell Me More (1925) ### ###MUSICALS // ### Tip Toes (1925) ### ###MUSICALS // ### Oh, Kay! (1926, lyrics by P. G. Wodehouse) ### ###MUSICALS // ### Strike up the Band (1927, 2nd vers. 1930) ### ###MUSICALS // ### Funny Face (1927) ### ###MUSICALS // ### Rosalie (1928) ### ###MUSICALS // ### Treasure Girl (1928) ### ###MUSICALS // ### Show Girl (1929) ### ###MUSICALS // ### Girl Crazy (1930) ### ###MUSICALS // ### Of Thee I Sing (1931, lyrics by George F. Kaufman) ### ###MUSICALS // ### Pardon my English (1933) ### ###MUSICALS // ### Let 'em eat Cake (1933) ### ==### SONGS // ### ## Among the best of hundreds of songs are Swanee; The Man I Love; Embraceable You; I Got Rhythm; Fascinating Rhythm; 'S Wonderful; Lady Be Good; and Love Walked In. The popular Summertime is from Porgy and Bess ### ###FILMS // ### Delicious (1931) ### ###FILMS // ### Shall We Dance?; A Damsel in Distress (1937) ### ###FILMS // ### The Goldwyn Follies (1938) ### ###FILMS // ### The Shocking Miss Pilgrim (1946) ### ###FILMS // ### Kiss Me, Stupid (1964) ### ###OPERAS // Title-RAW: Blue Monday Title-Dates: 1-act; item in George White's Scandals 1922 but withdrawn after 1 perf.; retitled 135th Street and revived Miami 1970 ### ###OPERAS // ### Porgy and Bess (1934--1935) ### ###ORCH. // Title-RAW: Rhapsody in Blue (pf. and orch.) Title-Dates: 1924 ### ###ORCH. // Title-Type: Piano concerto Title-Key: F major Title-Dates: 1925 ### ###ORCH. // Title-RAW: An American in Paris Title-Dates: 1928 ### ###ORCH. // Title-RAW: Second Rhapsody for Piano and orch. (working title "Rhapsody in Rivets") Title-Dates: 1931 ### ###ORCH. // Title-RAW: Cuban Overture Title-Dates: 1932 ### ###ORCH. // Title-RAW: "I Got Rhythm" Variations for Piano and orch. Title-Dates: 1934 # prev_aka Variations on "I Got Rhythm" ### ### ### ## http://www.gershwinfan.com/works.html ### ### Since I Found You (1913) ### When You Want 'Em, You Can't Get 'Em; When You've Got 'Em, You Don't Want 'Em (1916) ### ## The Passing Show of 1916 (1916) ### Rialto Ripples (1917) ### Beautiful Bird (1917) ### You Are Not the Girl (1917) ### Hitchy Koo of 1918 (1918) ### The Real American Folk Song (Is a Rag) (1918) ### Kitchenette (1918) ### If You Only Knew (1918) ### There's Magic in the Air (1918) ### When There's a Chance to Dance (1918) ### ## La, La, Lucille (1919) ### Morris Gest Midnight Whirl (1919) ### Lullaby (1919) ### Good Morning, Judge (1919) ### The Lady in Red (1919) ### Capitol Revue (1919) ### George White's Scandals of 1920 (1920) ### Piccadilly to Broadway (1920) ### For No Reason at All (1920) ### Mischa, Jascha, Toscha, Sascha (1920) ### Waiting for the Sun to Come Out (1920) ### Back Home (1920) ### I Want to Be Wanted by You (1920) ### Ed Wynn's Carnival (1920) ### Sinbad (1920) ### Broadway Brevities of 1920 (1920) ### George White's Scandals of 1921 (1921) ### The Perfect Fool (1921) ### Blue Eyes (1921) ### Selwyn's Snapshots of 1921 (1921) ### George White's Scandals of 1922 (1922) ### ## Blue Monday (one-act opera) (1922) ### Molly on the Shore (1922) ### For Goodness Sake (1922) ### A New Step Ev'ry Day a/k/a Stairway to Paradise (1922) ### Our Nell (1922) ### The French Doll (1922) ### ## A Dangerous Maid (1921) ### Phoebe (1921) ### Spice of 1922 (1922) ### The Rainbow (1923) ### George White's Scandals of 1923 (1923) ### The Dancing Girl (1923) ### Nifties of 1923 (1923) ### I Won't Say I Will but I Won't Say I Won't (1923) ### The Sunshine Trail (1923) ### ## Rhapsody in Blue (1924) ### George White's Scandals of 1924 (1924) ### ## Lady, Be Good! (1924) ### ## Sweet Little Devil (1924) ### ## Primrose (1924) ### ## Concerto in F (1925) ### ## Song of the Flame (1925) ### Short Story (1925) ### ## Tell Me More (1925) ### ## Tip-Toes (1925) ### Preludes for Piano (1926) ### Americana (1926) ### ## Oh, Kay! (1926) ### ## Strike Up the Band (1927) ### ## Funny Face (1927) ### ## Treasure Girl (1928) ### ## An American in Paris (1928) ### ## Rosalie (1928) ### ## Show Girl (1929) ### Impromptu in Two Keys (1929) ### Three-Quarter Blues (1929) ### East is West (1929) ### 9:15 Review (1930) ### ## Girl Crazy (1930) ### Strike Up the Band (revision) (1930) ### ## Of Thee I Sing (1931) ### ## Delicious (1931) ### George Gershwin's Song-Book (1932) ### ## Second Rhapsody (1932) ### ## Cuban Overture (1932) ### Girl Crazy (1932) ### ## Pardon My English (1933) ### ## Let 'Em Eat Cake (1933) ### ## Variations on I Got Rhythm (1934) ### ## Porgy and Bess (1935) ### The Show is On (1936) ### Suite from Porgy and Bess (1936) ### ## Shall We Dance? (1937) ### A Damsel in Distress (1937) ### ## The Goldwyn Follies (1937) Title: Summertime (Act I Scene 1) Title: A Woman is a Sometime Thing (Act I Scene 1) Title: My Man's Gone Now (Act I Scene 2) # prev_aka MY MANS GONE NOW Title: It Take a Long Pull to Get There (Act II Scene 1) Title: I Got Plenty o' Nuttin' (Act II Scene 1) Title: Buzzard Keep on Flyin' (Act II Scene 1) # prev_aka THE BUZZARD SONG Title: Bess, You Is My Woman Now (Act II Scene 1) Title: Oh, I Can't Sit Down (Act II Scene 1) Title: It Ain't Necessarily So (Act II Scene 2) ## Misprints: # prev_aka It Aint Necessarily So (Act II Scene 2) # prev_aka It Aint Necessarily So Title: What you want wid Bess (Act II Scene 2) Title: Oh, Doctor Jesus (Act II Scene 3) Title: A Red-Haired Woman (Act II Scene 4) Title: There's a Boat Dat's Leavin' Soon for New York (Act III Scene 2) Title: Bess, O Where's My Bess? (Act III Scene 3) # prev_aka Where is my Bess Title: I'm on my way (Act III Scene 3) ### From Ella's record Title: The Half Of It Dearie Blues Title: 'S Wonderful Title: Aren't You Kind Of Glad We Did? Title: (I've Got) Beginner's Luck Title: Bidin' My Time Title: Boy Wanted Title: Boy! What Love Has Done To Me! Title: But Not For Me Title: By Strauss Title: Cheerful Little Earful Title: Clap Yo' Hands Title: Embraceable You Title: Fascinating Rhythm Title: Fascinatin' Rhythm Title: Fidgety Feet Title: A Foggy Day Title: For You, For Me, For Evermore ## Funny Face Title: He Loves And She Loves Title: How Long Has This Been Going On? Title: I Can't Be Bothered Now Title: I Got Rhythm Title: I Was Doing All Right Title: I've Got A Crush On You Title: Isn't It A Pity? Title: Just Another Rhumba Title: Let's Call The Whole Thing Off Title: Let's Kiss And Make Up Title: Looking For A Boy Title: Lorelei ### Lorelei - (alternate take) Title: Love Is Here To Stay ### Love Is Here To Stay - (alternate take) Title: Love Is Sweeping The Country Title: Love Walked In Title: The Man I Love Title: March Of The Swiss Soldiers Title: My Cousin In Milwaukee Title: My One And Only Title: Nice Work If You Can Get It ### Of Thee I Sing Title: Oh, Lady, Be Good! ### Oh, Lady, Be Good! - (alternate take) Title: Oh, So Nice! Title: Prelude I Title: Prelude II Title: Prelude III Title: Promenade (Walking The Dog) Title: The Real American Folk Song (Is A Rag) Title: Sam And Delilah ### Shall We Dance? Title: Slap That Bass Title: Somebody From Somewhere Title: Somebody Loves Me Title: Someone To Watch Over Me Title: Soon Title: Stiff Upper Lip ### Strike Up The Band Title: That Certain Feeling Title: They All Laughed Title: They Can't Take That Away From Me Title: Things Are Looking Up Title: Treat Me Rough Title: Who Cares? ### You've Got What Gets Me ### From Gershwin plays Gershwin Title: Hang on to Me Title: Sweet and low down Title: Then do we dance? Title: Maybe Title: Do-do-do ## From Porgy and Bess Title: I wants to stay here Title: Medley: Here come de honey man crab man oh ���������������������������������������������������������������������MP3-Tag-1.13/lib/Normalize/Text/Music_Fields/J_Brahms.comp������������������������������������������0000700�0000000�0000000�00000023326�10646105262�020002� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# format = mail-header ## This is a very primitive list, not taking into account sub-opuses Title-Type: Piano Sonata Title-No: 1 Title-Key: C major Title-Opus: 1 Title-Dates: 1852 Title-Type: Piano Sonata Title-No: 2 Title-Key: F sharp minor Title-Opus: 2 Title-Dates: 1852 Title-Count: Six Title-Type: Songs Title-Opus: 3 Title-Dates: 1853 Title-Type: Scherzo Title-Key: E flat minor Title-For: piano Title-Opus: 4 Title-Dates: 1851 Title-Type: Piano Sonata Title-No: 3 Title-Key: F minor Title-Opus: 5 Title-Dates: 1853 Title-Count: Six Title-Type: Songs Title-Opus: 6 Title-Count: Six Title-Type: Songs Title-Opus: 7 Title-Type: Piano Trio Title-No: 1 Title-Key: B major Title-Opus: 8 Title-Dates: 1854 Title-RAW: Variations on a theme by Robert Schumann Title-Key: F sharp minor Title-For: piano Title-Opus: 9 Title-Dates: 1854 Title-Count: Four Title-Type: Ballades Title-For: piano Title-Opus: 10 Title-Dates: 1854 Title-Type: Serenade Title-No: 1 Title-Key: D major Title-For: orchestra Title-Opus: 11 Title-Dates: 1857 Title-Name: Ave Maria Title-Opus: 12 Title-Name: Begräbnisgesang Title-Opus: 13 Title-Count: Eight Title-Type: Songs and Romances Title-Opus: 14 Title-Type: Piano Concerto Title-No: 1 Title-Key: D minor Title-Opus: 15 Title-Dates: 1859 Title-Type: Serenade Title-No: 2 Title-Key: A major Title-For: orchestra Title-Opus: 16 Title-Dates: 1859 Title-Count: Four Title-Type: Songs Title-For: female voices, two horns and harp Title-Opus: 17 Title-Type: String Sextet Title-No: 1 Title-Key: B flat major Title-Opus: 18 Title-Dates: 1860 Title-Count: Five Title-Type: Poems Title-Opus: 19 Title-Count: Three Title-Type: Duets Title-Opus: 20 Title-RAW: Two Sets of Variations Title-For: piano Title-Opus: 21 Title-Name: Marienlieder Title-Opus: 22 Title-RAW: Variations on a Theme by Robert Schumann Title-For: piano, four hands Title-Opus: 23 Title-Dates: 1861 Title-RAW: Variations and Fugue on a Theme by Handel Title-For: piano Title-Opus: 24 Title-Dates: 1861 Title-Type: Piano Quartet Title-No: 1 Title-Key: G minor Title-Opus: 25 Title-Dates: 1861 Title-Type: Piano Quartet Title-No: 2 Title-Key: A major Title-Opus: 26 Title-Dates: 1861 Title-RAW: Psalm 13 Title-Opus: 27 Title-Count: Four Title-Type: Duets Title-Opus: 28 Title-Count: Two Title-Type: Motets Title-Opus: 29 Title-Dates: 1860, published 1864 Title-RAW: Geistliches Lied Title-Opus: 30 Title-Count: Three Title-Type: Vocal Quartets Title-Opus: 31 Title-Count: Nine Title-Type: Songs Title-Opus: 32 Title-Count: Fifteen Title-Type: Romances Title-Related-How: from Tieck's Title-Related-Name: Liebesgeschichte der schönen Magelone Title-Opus: 33 Title-Type: Piano Quintet Title-Key: F minor Title-Opus: 34 Title-Dates: 1864 Title-Type: Sonata Title-For: 2 Pianos Title-Key: F minor Title-Opus: 34b Title-RAW: Variations on a Theme by Paganini Title-For: Piano Title-Opus: 35 Title-Dates: 1862-1863 Title-Type: String Sextet Title-No: 2 Title-Opus: 36 Title-Count: Three Title-Type: Sacred Choruses Title-Opus: 37 Title-Type: Cello Sonata Title-No: 1 Title-Key: E minor Title-Opus: 38 Title-Dates: 1862-65 Title-Count: Sixteen Title-Type: Waltzes Title-For: piano, four hands Title-Opus: 39 Title-Dates: 1865 Title-Type: Trio Title-For: Horn, Violin and Piano Title-Key: E flat major Title-Opus: 40 Title-Dates: 1865 Title-Count: Five Title-Type: Songs Title-For: male voices Title-Opus: 41 Title-Count: Three Title-Type: secular songs Title-For: choir Title-Opus: 42 Title-Count: Four Title-Type: Songs Title-Opus: 43 Title-Count: Twelve Title-Type: Songs and Romances Title-Opus: 44 Title-Name: Ein deutsches Requiem Title-Opus: 45 Title-Dates: 1868 Title-Count: Four Title-Type: Songs Title-Opus: 46 Title-Count: Five Title-Type: Songs Title-Opus: 47 Title-Count: Seven Title-Type: Songs Title-Opus: 48 Title-RAW: Five Songs -- (#4, "Wiegenlied", is also known as "Brahms' Lullaby") Title-Opus: 49 Title-Name: Rinaldo Title-Opus: 50 Title-Count: Two Title-Type: String Quartets Title-Opus: 51 Title-Count: Eighteen Title-Name: Liebeslieder-Waltzer Title-For: piano, four hands and vocal quartet Title-Name: ad libitum Title-Opus: 52 Title-Dates: 1874 Title-RAW: Alto Rhapsody Title-Opus: 53 Title-Name: Schicksalslied Title-Opus: 54 Title-Name: Triumphlied Title-Opus: 55 Title-RAW: Variations on a Theme by Joseph Haydn Title-Opus: 56 Title-Dates: 1873 Title-Count: Eight Title-Type: Songs Title-Opus: 57 Title-Count: Eight Title-Type: Songs Title-Opus: 58 Title-Count: Eight Title-Type: Songs Title-Opus: 59 Title-Type: Piano Quartet Title-No: 3 Title-Key: C minor Title-Opus: 60 Title-Count: Four Title-Type: Duets Title-Opus: 61 Title-Count: Seven Title-Type: secular songs Title-For: choir Title-Opus: 62 Title-Count: Nine Title-Type: Songs Title-Opus: 63 Title-Count: Three Title-Type: Vocal Quartets Title-Opus: 64 Title-RAW: Neue Liebeslieder - 15 Waltzes Title-Opus: 65 Title-Count: Five Title-Type: Duets Title-Opus: 66 Title-Type: String Quartet Title-No: 3 Title-Key: B flat major Title-Opus: 67 Title-Dates: 1876 Title-Type: Symphony Title-No: 1 Title-Key: C minor Title-Opus: 68 Title-Dates: 1876 première Title-Count: Nine Title-Type: Songs Title-Opus: 69 Title-Count: Four Title-Type: Songs Title-Opus: 70 Title-Count: Five Title-Type: Songs Title-Opus: 71 Title-Count: Five Title-Type: Songs Title-Opus: 72 Title-Type: Symphony Title-No: 2 Title-Key: D major Title-Opus: 73 Title-Dates: 1877 Title-Count: Two Title-Type: Motets Title-Opus: 74 Title-Count: Four Title-Type: Ballads and Romances Title-Opus: 75 Title-Count: Eight Title-Type: Pieces Title-For: piano Title-Opus: 76 Title-Dates: 1878 Title-Type: Violin Concerto Title-Key: D major Title-Opus: 77 Title-Dates: 1878 Title-Type: Violin Sonata Title-No: 1 Title-Key: G major Title-Opus: 78 Title-Count: Two Title-Type: Rhapsodies Title-For: piano Title-Opus: 79 Title-Dates: 1879 Title-Name: Academic Festival Overture Title-For: orchestra Title-Opus: 80 Title-Dates: 1880 Title-Name: Tragic Overture Title-For: orchestra Title-Opus: 81 Title-Dates: 1880 Title-Name: Nänie Title-Opus: 82 Title-Dates: 1881 Title-Type: Piano Concerto Title-No: 2 Title-Key: B flat major Title-Opus: 83 Title-Dates: 1881 Title-Type: Romances and Songs Title-Opus: 84 Title-Count: Six Title-Type: Songs Title-Opus: 85 Title-Count: Six Title-Type: Songs Title-Opus: 86 Title-Type: Piano Trio Title-No: 2 Title-Key: C major Title-Opus: 87 Title-Type: String Quintet Title-No: 1 Title-Key: F major Title-Opus: 88 Title-Dates: 1882 Title-Name: Gesang der Parzen Title-Opus: 89 Title-Type: Symphony Title-No: 3 Title-Key: F major Title-Opus: 90 Title-Dates: 1883 Title-Count: Two Title-Type: Songs Title-For: Voice, Viola & Piano Title-Opus: 91 Title-Count: Four Title-Type: Vocal Quartets Title-Opus: 92 Title-Count: Six Title-Type: Songs and Romances Title-For: choir Title-Opus: 93 Title-Count: Five Title-Type: Songs Title-Opus: 94 Title-Count: Seven Title-Type: Songs Title-Opus: 95 Title-Count: Four Title-Type: Songs Title-Opus: 96 Title-Count: Six Title-Type: Songs Title-Opus: 97 Title-Type: Symphony Title-No: 4 Title-Key: E minor Title-Opus: 98 Title-Dates: 1885 Title-Type: Cello Sonata Title-No: 2 Title-Key: F major Title-Opus: 99 Title-Dates: 1886 Title-Type: Violin Sonata Title-No: 2 Title-Key: A major Title-Opus: 100 Title-Dates: 1886 Title-Type: Piano Trio Title-No: 3 Title-Key: C minor Title-Opus: 101 Title-Dates: 1886 Title-Type: Double Concerto Title-For: Violin and Cello Title-Key: A minor Title-Opus: 102 Title-Dates: 1887 Title-Name: Zigeunerlieder Title-Opus: 103 Title-Count: Five Title-Type: songs Title-For: choir Title-Opus: 104 Title-Count: Five Title-Type: Songs Title-Opus: 105 Title-Count: Five Title-Type: Songs Title-Opus: 106 Title-Count: Five Title-Type: Songs Title-Opus: 107 Title-Type: Violin Sonata Title-No: 3 Title-Key: D minor Title-Opus: 108 Title-Name: Fest- und Gedenksprüche Title-For: choir Title-Opus: 109 Title-Count: Three Title-Type: Motets Title-Opus: 110 Title-Type: String Quintet Title-No: 2 Title-Key: G major Title-Name: Prater Title-Opus: 111 Title-Dates: 1890 Title-Count: Six Title-Type: Vocal Quartets Title-Opus: 112 Title-Count: Thirteen Title-Type: Canons Title-For: female choir Title-Opus: 113 Title-Type: Trio Title-For: Piano, Clarinet, and Cello Title-Key: A minor Title-Opus: 114 Title-Dates: 1891 Title-Type: Quintet Title-For: Clarinet and Strings Title-Key: B minor Title-Opus: 115 Title-Dates: 1891 Title-Count: Seven Title-Type: Fantasias Title-For: piano Title-Opus: 116 Title-Dates: 1892 Title-Count: Three Title-Type: Intermezzi Title-For: piano Title-Opus: 117 Title-Dates: 1892 Title-Count: Six Title-Type: Pieces Title-For: Piano Title-Opus: 118 Title-Dates: 1893 Title-Count: Four Title-Type: Pieces Title-For: piano Title-Opus: 119 Title-Dates: 1893 Title-Count: Two Title-Type: Clarinet Sonatas Title-Opus: 120 Title-Name: Vier ernste Gesänge Title-RAW: ("Four Serious Songs") Title-Opus: 121 Title-Dates: 1896 Title-Count: Eleven Title-Type: Chorale Preludes Title-For: organ Title-Opus: 122 Title-Dates: 1896 Title-RAW: Hungarian Dances (1869) (Brahms considered these adaptations, not original works, and so he did not assign an Opus #) [1] Title-Opus: WoO 1 Title-RAW: Chorale Prelude and Fugue on „O Traurigkeit, o Herzeleid“ Title-For: organ Title-Opus: WoO 7 Title-Type: Fugue Title-Key: A flat minor Title-For: organ Title-Opus: WoO 8 Title-Type: Prelude and Fugue Title-Key: A minor Title-For: organ Title-Opus: WoO 9 Title-Type: Prelude and Fugue Title-Key: G minor Title-For: organ Title-Opus: WoO 10 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/Normalize/Text/Music_Fields/L_van_Beethoven.comp�����������������������������������0000700�0000000�0000000�00000311424�10730432724�021352� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# format = mail-header ## Format: "title; opus (date)" ## The dates are taken from CODM, beethovenlv, or wikipedia ## The most expanded date is taken; if there is a conflict, the first ## available date (in the order above) is taken # dup_opus_rex ^(?i)WoO\s+(15|74|105|116|158)\b # opus_prefix Op. ## These titles are taken from wikipedia site: Title-Count: Three Title-Type: Piano Trios Title-Opus: 1 Title-Dates: 1792--1794 Title-Type: Piano Trio Title-No: 1 Title-Key: E flat major Title-Opus: 1-1 Title-Dates: 1792--1793 Title-Type: Piano Trio Title-No: 2 Title-Key: G major Title-Opus: 1-2 Title-Dates: 1792--1794 Title-Type: Piano Trio Title-No: 3 Title-Key: C minor Title-Opus: 1-3 Title-Dates: 1792--1794 Title-Count: Three Title-Type: Piano Sonatas Title-Opus: 2 Title-Dates: 1794--1795 Title-Type: Piano Sonata Title-No: 1 Title-Key: F minor Title-Opus: 2-1 Title-Dates: 1793--1795 Title-Type: Piano Sonata Title-No: 2 Title-Key: A major Title-Opus: 2-2 Title-Dates: 1794--1795 Title-Type: Piano Sonata Title-No: 3 Title-Key: C major Title-Opus: 2-3 Title-Dates: 1794--1795 Title-Type: String Trio Title-No: 1 Title-Key: E flat major Title-Opus: 3 Title-Dates: pre-1794 Title-Type: String Quintet Title-Key: E flat major Title-Opus: 4 Title-Dates: 1795 Title-Count: Two Title-Type: Cello Sonatas Title-Opus: 5 Title-Dates: 1796 Title-Type: Sonata Title-For: Piano and Cello Title-No: 1 Title-Key: F major Title-Opus: 5-1 Title-Dates: 1796 Title-Type: Sonata Title-For: Piano and Cello Title-No: 2 Title-Key: G minor Title-Opus: 5-2 Title-Dates: 1796 Title-Type: Piano Sonata Title-For: four hands Title-Opus: 6 Title-Dates: 1797 Title-Type: Piano Sonata Title-No: 4 Title-Key: E flat major Title-Opus: 7 Title-Dates: 1796 Title-Type: Serenade Title-Key: D major Title-For: string trio Title-Opus: 8 Title-Dates: 1797 Title-Count: Three Title-Type: String Trios Title-Opus: 9 Title-Dates: 1798 Title-Type: String Trio Title-No: 2 Title-Key: G major Title-Opus: 9-1 Title-Dates: 1798 Title-Type: String Trio Title-No: 3 Title-Key: D major Title-Opus: 9-2 Title-Dates: 1798 Title-Type: String Trio Title-No: 4 Title-Key: C minor Title-Opus: 9-3 Title-Dates: 1798 Title-Count: Three Title-Type: Piano Sonatas Title-Opus: 10 Title-Dates: 1798 Title-Type: Piano Sonata Title-No: 5 Title-Key: C minor Title-Opus: 10-1 Title-Dates: 1795--1797 Title-Type: Piano Sonata Title-No: 6 Title-Key: F major Title-Opus: 10-2 Title-Dates: 1796--1797 Title-Type: Piano Sonata Title-No: 7 Title-Key: D major Title-Opus: 10-3 Title-Dates: 1797--1798 Title-Type: Piano Trio Title-No: 4 Title-Key: B flat major Title-Opus: 11 Title-Dates: 1797 Title-Count: Three Title-Type: Violin Sonatas Title-Opus: 12 Title-Dates: 1798 Title-Type: Violin Sonata Title-No: 1 Title-Key: D major Title-Opus: 12-1 Title-Dates: 1798 Title-Type: Violin Sonata Title-No: 2 Title-Key: A major Title-Opus: 12-2 Title-Dates: 1798 Title-Type: Violin Sonata Title-No: 3 Title-Key: E flat major Title-Opus: 12-3 Title-Dates: 1798 Title-Type: Piano Sonata Title-No: 8 Title-Key: C minor Title-Name: Pathetique Title-Opus: 13 Title-Dates: 1799 Title-Count: Two Title-Type: Piano Sonatas Title-Opus: 14 Title-Dates: 1799 Title-Type: Piano Sonata Title-No: 9 Title-Key: E major Title-Opus: 14-1 Title-Dates: 1798--1799 Title-Type: Piano Sonata Title-No: 10 Title-Key: G major Title-Opus: 14-2 Title-Dates: 1799 Title-Type: Piano Concerto Title-No: 1 Title-Key: C major Title-Punct: ; Title-Comment: Op. 15 (comp. 1795--1798, f.p. (presumed) Vienna, 1800-4-2, soloist Beethoven, cond. Wranitzky; pubd. 1801-3) Title-Type: Quintet Title-For: Piano and Winds Title-Opus: 16 Title-Dates: 1796, pubd. 1801 Title-Type: Horn Sonata Title-Key: F major Title-Opus: 17 Title-Dates: 1800 Title-Count: Six Title-Type: String Quartets Title-Opus: 18 Title-Dates: 1800 Title-Type: String Quartet Title-No: 1 Title-Key: F major Title-Opus: 18-1 Title-Dates: 1800 Title-Type: String Quartet Title-No: 2 Title-Key: G major Title-Opus: 18-2 Title-Dates: 1800 Title-Type: String Quartet Title-No: 3 Title-Key: D major Title-Opus: 18-3 Title-Dates: 1800 Title-Type: String Quartet Title-No: 4 Title-Key: C minor Title-Opus: 18-4 Title-Dates: 1800 Title-Type: String Quartet Title-No: 5 Title-Key: A major Title-Opus: 18-5 Title-Dates: 1800 Title-Type: String Quartet Title-No: 6 Title-Key: B flat major Title-Opus: 18-6 Title-Dates: 1800 Title-Type: Piano Concerto Title-No: 2 Title-Key: B flat major Title-Opus: 19 Title-Dates: comp. 1794--1795, f.p. Vienna, 1795-03-29, soloist Beethoven; pubd. 1801-12 Title-Type: Septet Title-Key: E flat major Title-Opus: 20 Title-Dates: 1799 Title-Type: Symphony Title-No: 1 Title-Key: C major Title-Opus: 21 Title-Dates: comp. 1799--1800, f.p. Vienna, 1800-04-02, cond. P. Wranitzky; pubd. 1801 Title-Type: Piano Sonata Title-No: 11 Title-Key: B flat major Title-Opus: 22 Title-Dates: 1800 Title-Type: Violin Sonata Title-No: 4 Title-Key: A minor Title-Opus: 23 Title-Dates: 1800 Title-Type: Violin Sonata Title-No: 5 Title-Key: F major Title-Name: Spring Title-Opus: 24 Title-Dates: 1801 Title-Type: Serenade Title-Key: D major Title-For: Flute, Violin and Viola Title-Opus: 25 Title-Dates: 1801 Title-Type: Piano Sonata Title-No: 12 Title-Key: A flat major Title-Opus: 26 Title-Dates: 1801 Title-Count: Two Title-Type: Piano Sonatas Title-Opus: 27 Title-Dates: 1801 Title-Type: Piano Sonata Title-No: 13 Title-Key: E flat major Title-Opus: 27-1 Title-Dates: 1801 Title-Type: Piano Sonata Title-No: 14 Title-Key: C sharp minor Title-Name: Moonlight Title-Opus: 27-2 Title-Dates: 1800--1801 Title-Type: Piano Sonata Title-No: 15 Title-Key: D major Title-Opus: 28 Title-Dates: 1801 Title-Type: String Quintet Title-Key: C major Title-Opus: 29 Title-Dates: 1801 Title-Count: Three Title-Type: Violin Sonatas Title-Opus: 30 Title-Dates: 1801--1802 Title-Type: Violin Sonata Title-No: 6 Title-Key: A major Title-Opus: 30-1 Title-Dates: 1801--1802 Title-Type: Violin Sonata Title-No: 7 Title-Key: C minor Title-Opus: 30-2 Title-Dates: 1801--1802 Title-Type: Violin Sonata Title-No: 8 Title-Key: G major Title-Opus: 30-3 Title-Dates: 1801--1802 Title-Count: Three Title-Type: Piano Sonatas Title-Opus: 31 Title-Dates: 1802 Title-Type: Piano Sonata Title-No: 16 Title-Key: G major Title-Opus: 31-1 Title-Dates: 1802 Title-Type: Piano Sonata Title-No: 17 Title-Key: D minor Title-Name: Tempest Title-Opus: 31-2 Title-Dates: 1802 Title-Type: Piano Sonata Title-No: 18 Title-Key: E flat major Title-Name: The Hunt Title-Opus: 31-3 Title-Dates: 1802 Title-RAW: Song - An die Hoffnung Title-Opus: 32 Title-Dates: 1805 Title-Count: Seven Title-Type: Bagatelles Title-For: piano Title-Opus: 33 Title-Dates: 1802 Title-Count: Six Title-Type: variations Title-For: piano Title-Related-On: an original theme Title-Key: F major Title-Opus: 34 Title-Dates: 1802 Title-RAW: Fifteen variations and a fugue for piano on an original theme Title-Key: E flat major Title-Name: Eroica Title-Opus: 35 Title-Dates: 1802 Title-Type: Symphony Title-No: 2 Title-Key: D major Title-Opus: 36 Title-Dates: comp. 1801--1802, f.p. Vienna, 1803-04-05, cond. Beethoven; pubd. 1804 Title-Type: Piano Concerto Title-No: 3 Title-Key: C minor Title-Opus: 37 Title-Dates: comp. 1800--1801, f.p. Vienna, 1803-04-05, soloist Beethoven; pubd. 1804 Title-RAW: Piano Trio No. 8 (Arrangement of the Septet; Op. 20)) Title-Opus: 38 Title-Dates: 1820--1823 Title-RAW: Two Preludes through all twelve major keys Title-For: piano Title-Opus: 39 Title-Dates: 1789 Title-Type: Romance Title-For: Violin Title-Key: G major Title-Opus: 40 Title-Dates: 1802 Title-Type: Serenade Title-For: Piano and Flute or Violin Title-Key: D major Title-Opus: 41 Title-Dates: 1803 Title-Type: Notturno Title-For: Viola and Piano Title-Key: D major Title-Opus: 42 Title-Dates: 1803 Title-RAW: The Creatures of Prometheus, Overture and Ballet music Title-Opus: 43 Title-Dates: 1801 Title-RAW: Piano Trio No. 10 (Variations on an original theme in E flat major) Title-Opus: 44 Title-Dates: 1802--1803 Title-Count: Three Title-Type: Marches Title-For: Piano, 4 hands Title-Opus: 45 Title-Dates: 1803 Title-RAW: Song - Adelaide Title-Opus: 46 Title-Dates: 1795 Title-Type: Violin Sonata Title-No: 9 Title-Key: A major Title-Name: Kreutzer Title-Opus: 47 Title-Dates: 1802 Title-Count: Six Title-Type: Songs Title-Opus: 48 Title-Dates: 1802 Title-RAW: Bitten Title-Opus: 48-1 Title-Dates: 1802 Title-RAW: Die Liebe des Nächsten Title-Opus: 48-2 Title-Dates: 1802 Title-RAW: Vom Tode Title-Opus: 48-3 Title-Dates: 1802 Title-RAW: Die Ehre Gottes aus der Natur Title-Opus: 48-4 Title-Dates: 1802 Title-RAW: Gottes Macht und Vorsehung, Bußlied Title-Opus: 48-5 Title-Dates: 1802 Title-Count: Two Title-Type: Piano Sonatas Title-Opus: 49 Title-Dates: 1802 Title-Type: Piano Sonata Title-No: 19 Title-Key: G minor Title-Opus: 49-1 Title-Dates: 1797 Title-Type: Piano Sonata Title-No: 20 Title-Key: G major Title-Opus: 49-2 Title-Dates: 1795--1796 Title-Type: Romance Title-For: Violin Title-Key: F major Title-Opus: 50 Title-Dates: 1798 Title-Count: Two Title-Type: Rondos Title-For: Piano Title-Opus: 51 Title-Dates: 1797 Title-Type: Rondo Title-Key: C major Title-Opus: 51-1 Title-Dates: 1796--1797 Title-Type: Rondo Title-Key: G major Title-Opus: 51-2 Title-Dates: 1798 Title-Count: Eight Title-Type: Songs Title-Opus: 52 Title-Dates: 1785--1793 Title-RAW: Urians Reise um die Welt Title-Opus: 52-1 Title-Dates: 1785--1793 Title-RAW: Feuerfab Title-Opus: 52-2 Title-Dates: 1785--1793 Title-RAW: Das Liedchen von der Ruhe Title-Opus: 52-3 Title-Dates: 1785--1793 Title-RAW: Maigesang Title-Opus: 52-4 Title-Dates: 1785--1793 Title-RAW: Mollys Abschied Title-Opus: 52-5 Title-Dates: 1785--1793 Title-RAW: Die Liebe Title-Opus: 52-6 Title-Dates: 1785--1793 Title-RAW: Marmotte Title-Opus: 52-7 Title-Dates: 1785--1793 Title-RAW: Das Blümchen Wunderhold Title-Opus: 52-8 Title-Dates: 1785--1793 Title-Type: Piano Sonata Title-No: 21 Title-Key: C major Title-Name: Waldstein Title-Opus: 53 Title-Dates: 1804 Title-Type: Piano Sonata Title-No: 22 Title-Key: F major Title-Opus: 54 Title-Dates: 1804 Title-Type: Symphony Title-No: 3 Title-Key: E flat major Title-Name: Eroica Title-Opus: 55 Title-Dates: comp. 1803--1804, f.pub.p. Vienna, 1805-04-07; pubd. 1806 Title-Type: Triple Concerto Title-Key: C major Title-Opus: 56 Title-Dates: comp. 1804, f.p. 1808; pubd. 1807 Title-Type: Piano Sonata Title-No: 23 Title-Key: F minor Title-Name: Appassionata Title-Opus: 57 Title-Dates: 1805 Title-Type: Piano Concerto Title-No: 4 Title-Key: G major Title-Opus: 58 Title-Dates: comp. 1805--1806, f.p. Vienna, 1808-12-22, soloist Beethoven; pubd. 1808 Title-RAW: Three String Quartets, the Rasumovsky quartets Title-Opus: 59 Title-Dates: comp. 1806 Title-Type: String Quartet Title-No: 7 Title-Key: F major Title-Opus: 59-1 Title-Dates: comp. 1806 Title-Type: String Quartet Title-No: 8 Title-Key: E minor Title-Opus: 59-2 Title-Dates: comp. 1806 Title-Type: String Quartet Title-No: 9 Title-Key: C major Title-Opus: 59-3 Title-Dates: comp. 1806 Title-Type: Symphony Title-No: 4 Title-Key: B flat major Title-Opus: 60 Title-Dates: comp. 1806, f.pub.p. Vienna, 1807-11-15, cond. Clement; pubd. 1808 Title-Type: Concerto Title-For: Violin and Orchestra Title-Key: D major Title-Opus: 61 Title-Dates: arr. for pf. by Beethoven 1807, pubd. 1808 / comp. 1806, f.p. Vienna, 1806-12-23, soloist Franz Clement; pub. 1809 Title-RAW: Overture - Coriolan Title-Opus: 62 Title-Dates: 1807 Title-RAW: Arrangement of String Quintet (Op. 4) Title-For: Piano Trio Title-Opus: 63 Title-Dates: 1806 Title-RAW: Arrangement of String Trio (Op. 3) Title-For: Piano and Cello Title-Opus: 64 Title-Dates: 1807 Title-RAW: Aria - Ah perfido! Title-Opus: 65 Title-Dates: comp. 1796 Title-RAW: Variations for Cello on Mozart's Ein Mädchen oder Weibchen Title-Opus: 66 Title-Dates: 1796 Title-Type: Symphony Title-No: 5 Title-Key: C minor Title-Opus: 67 Title-Dates: comp. 1804--1808, f.p. Vienna, 1808-12-22, cond. Beethoven; pubd. 1809 Title-Type: Symphony Title-No: 6 Title-Key: F major Title-Name: Pastoral Title-Opus: 68 Title-Dates: comp. 1807--1808, f.p. Vienna, 1808-12-22, cond. Beethoven; pubd. 1809 Title-Type: Sonata Title-For: Piano and Violoncello Title-No: 3 Title-Key: A major Title-Opus: 69 Title-Dates: 1808 Title-Count: Two Title-Type: Piano Trios Title-Opus: 70 Title-Dates: 1808 Title-Type: Piano Trio Title-No: 5 Title-Key: D major Title-Name: Ghost Title-Opus: 70-1 Title-Dates: 1808 Title-Type: Piano Trio Title-No: 6 Title-Key: E flat major Title-Opus: 70-2 Title-Dates: 1808 Title-Type: Wind sextet Title-Key: E flat major Title-Opus: 71 Title-Dates: 1796 Title-RAW: Opera - Fidelio (c. 1803-5; Fidelio Overture composed 1814) Title-Opus: 72 Title-Dates: 1805, rev. 1806 and 1814 / 1814 Title-RAW: Opera - Leonore (earlier version of Fidelio, with Leonore No. 2 Overture) Title-Opus: 72a Title-Dates: 1805 Title-RAW: Opera - Leonore (earlier version of Fidelio, with Leonore No. 3 Overture) Title-Opus: 72b Title-Dates: 1806 Title-Type: Piano Concerto Title-No: 5 Title-Key: E flat major Title-Name: Emperor Title-Opus: 73 Title-Dates: comp. 1809, f.p. Leipzig, 1810-12, soloist F. Schneider, f. Vienna p. 1812-02-12, soloist Czerny; pubd. 1811 Title-Type: String Quartet Title-No: 10 Title-Key: E flat major Title-Name: Harp Title-Opus: 74 Title-Dates: 1809 Title-Count: Six Title-Type: Songs Title-Opus: 75 Title-Dates: 1809 Title-Type: Mignon Title-Opus: 75-1 Title-Dates: 1809 Title-RAW: Neue Liebe neues Leben Title-Opus: 75-2 Title-Dates: 1809 Title-RAW: Aus Goethes Faust: Es war einmal ein König Title-Opus: 75-3 Title-Dates: 1809 Title-RAW: Gretels Warnung Title-Opus: 75-4 Title-Dates: 1809 Title-RAW: An die fernen Geliebten Title-Opus: 75-5 Title-Dates: 1809 Title-RAW: Der Zufriedene Title-Opus: 75-6 Title-Dates: 1809 Title-Count: Six Title-Type: variations Title-For: piano Title-Related-On: an original theme Title-Key: D major Title-Opus: 76 Title-Dates: 1810 Title-Type: Piano Fantasia Title-Opus: 77 Title-Dates: 1810 Title-Type: Piano Sonata Title-No: 24 Title-Key: F sharp major Title-Opus: 78 Title-Dates: 1809 Title-Type: Piano Sonata Title-No: 25 Title-Key: G major Title-Opus: 79 Title-Dates: 1809 Title-Type: Fantasy Title-Key: C minor Title-For: piano, chorus, and orchestra Title-Opus: 80 Title-Dates: 1808 Title-Type: Piano Sonata Title-No: 26 Title-Key: E flat major Title-Name: Les Adieux Title-Opus: 81a Title-Dates: 1809 Title-Type: Sextet Title-Key: E flat major Title-Opus: 81b Title-Dates: ?1795 Title-RAW: Four Ariettas and a Duet Title-Opus: 82 Title-Dates: 1809 Title-RAW: Dimmi, ben mio, che m'ami Title-Opus: 82-1 Title-Dates: 1809 Title-RAW: T'intendo si, mio cor Title-Opus: 82-2 Title-Dates: 1809 Title-RAW: L'amante impaziente (first version) Title-Opus: 82-3 Title-Dates: 1809 Title-RAW: L'amante impatiente (second version) Title-Opus: 82-4 Title-Dates: 1809 Title-RAW: Duet: Odi 'laura che dolce sospira Title-Opus: 82-5 Title-Dates: 1809 Title-Count: Three Title-Type: Songs Title-Opus: 83 Title-Dates: 1810 Title-RAW: Wonne der Wehmut Title-Opus: 83-1 Title-Dates: 1810 Title-RAW: Sehnsucht Title-Opus: 83-2 Title-Dates: 1810 Title-RAW: Mit einem gemalten Band Title-Opus: 83-3 Title-Dates: 1810 Title-RAW: Egmont (Overture and Incidental Music) Title-Opus: 84 Title-Dates: 1810 Title-RAW: Christus am Ölberge or Christ on the Mount of Olives Title-Opus: 85 Title-Dates: 1803 Title-Type: Mass Title-Key: C major Title-Opus: 86 Title-Dates: 1807 Title-Type: Trio Title-For: two Oboes and English Horn Title-Key: C major Title-Opus: 87 Title-Dates: 1795 Title-RAW: Song - Das Gluck der Freundschaft Title-Opus: 88 Title-Dates: 1803 Title-Type: Polonaise Title-Key: C major Title-Opus: 89 Title-Dates: 1814 Title-Type: Piano Sonata Title-No: 27 Title-Key: E minor Title-Opus: 90 Title-Dates: 1814 Title-RAW: Wellington's Victory ("Battle" Symphony) Title-Opus: 91 Title-Dates: comp. 1813, f.p. Vienna, 1813-12-08, cond. Beethoven; pubd. 1816 Title-Type: Symphony Title-No: 7 Title-Key: A major Title-Opus: 92 Title-Dates: comp. 1811--1812, f.p. Vienna, 1813-12-08, cond. Beethoven; pubd. 1816 Title-Type: Symphony Title-No: 8 Title-Key: F major Title-Opus: 93 Title-Dates: comp. 1812, f.p. Vienna, 1814-02-27, cond. Beethoven; pubd. 1816 Title-RAW: Song - An die Hoffnung Title-Opus: 94 Title-Dates: 1813--1815 Title-Type: String Quartet Title-No: 11 Title-Key: F minor Title-Name: Serioso Title-Opus: 95 Title-Dates: 1810 Title-Type: Violin Sonata Title-No: 10 Title-Key: G major Title-Opus: 96 Title-Dates: 1812, rev. 1815 Title-Type: Piano Trio Title-No: 7 Title-Key: B flat major Title-Name: Archduke Title-Opus: 97 Title-Dates: 1810--1811 Title-RAW: Song Cycle - An die ferne Geliebte Title-Opus: 98 Title-Dates: 1816 Title-RAW: Song - Der Mann von Wort Title-Opus: 99 Title-Dates: 1816 Title-RAW: Song - Merkenstein Title-Opus: 100 Title-Dates: 1815 Title-Type: Piano Sonata Title-No: 28 Title-Key: A major Title-Opus: 101 Title-Dates: 1816 Title-Count: Two Title-Type: Cello Sonatas Title-Opus: 102 Title-Dates: 1815 Title-Type: Sonata Title-For: Piano and Cello Title-No: 4 Title-Key: C major Title-Opus: 102-1 Title-Dates: 1815 Title-Type: Sonata Title-For: Piano and Cello Title-No: 5 Title-Key: D minor Title-Opus: 102-2 Title-Dates: 1815 Title-Type: Wind octet Title-Key: E flat major Title-Opus: 103 Title-Dates: 1792 Title: String Quintet (arrangement of Piano Trio No. 3, 1817); Op. 104 (arr. by Beethoven in 1817 of his pf. trio (1792-4)) Title-Count: Six Title-Type: sets of variations Title-For: Piano and Flute Title-Opus: 105 Title-Dates: 1819 Title-Type: Piano Sonata Title-No: 29 Title-Key: B flat major Title-Name: Hammerklavier Title-Opus: 106 Title-Dates: 1818 Title-Count: Ten Title-Type: sets of variations Title-For: Piano and Flute Title-Opus: 107 Title-Dates: 1820 Title-RAW: Twenty-Five Scottish Songs Title-Opus: 108 Title-Dates: 1815-1816 Title-Type: Piano Sonata Title-No: 30 Title-Key: E major Title-Opus: 109 Title-Dates: 1820 Title-Type: Piano Sonata Title-No: 31 Title-Key: A flat major Title-Opus: 110 Title-Dates: 1821 Title-Type: Piano Sonata Title-No: 32 Title-Key: C minor Title-Opus: 111 Title-Dates: 1822 Title-RAW: Meeresstille und glückliche Fahrt (for chorus and orchestra) Title-Opus: 112 Title-Dates: 1815 Title-RAW: Overture and incidental music for Die Ruinen von Athen (The ruins of Athens) Title-Opus: 113 Title-Dates: 1811 Title-RAW: March and Chorus - Die Weihe des Hauses ("The Consecration of the House"; 1822) Title-Opus: 114 Title-Dates: 1822 Title-RAW: Overture - Zur Namensfeier Title-Opus: 115 Title-Dates: 1815 Title-RAW: Vocal Trio with Orchestra - Tramte, empi tremate Title-Opus: 116 Title-Dates: 1802 Title-RAW: Overture to King Stephen Title-Opus: 117 Title-Dates: 1811 Title-RAW: Elegischer Gesang (for chorus and orchestra) Title-Opus: 118 Title-Dates: 1814 Title-RAW: Eleven new Bagatelles Title-For: piano Title-Opus: 119 Title-Dates: 1821 Title-RAW: Thirty-three variations for piano on a waltz by Diabelli, C major (Diabelli Variations) Title-Opus: 120 Title-Dates: 1823 Title-RAW: Piano Trio No. 11 (Variations on Ich bin der Schneider Kakadu) Title-Opus: 121 Title-Dates: 1803 Title-RAW: Opferlied (for chorus and orchestra) Title-Opus: 121b Title-Dates: 1822 Title-RAW: Bundeslied (for chorus and orchestra) Title-Opus: 122 Title-Dates: 1824 Title-RAW: Mass in D major (Missa Solemnis) Title-Opus: 123 Title-Dates: 1819--1822 Title-RAW: Overture - Die Weihe des Hauses (Consecration of the House) Title-Opus: 124 Title-Dates: 1822 Title-Type: Symphony Title-No: 9 Title-Key: D minor Title-Name: Choral Title-Opus: 125 Title-Dates: comp. 1817--1823, f.p. Vienna, 1824-05-07, cond. Beethoven; pubd. 1826 Title-Count: Six Title-Type: Bagatelles Title-For: piano Title-Opus: 126 Title-Dates: 1824 Title-Type: String Quartet Title-No: 12 Title-Key: E flat major Title-Opus: 127 Title-Dates: 1825 Title-RAW: Song - Der Kuss Title-Opus: 128 Title-Dates: 1822 Title-RAW: Rondo Capriccio for piano in G major (Rage over a lost penny) Title-Opus: 129 Title-Dates: 1825--1826 Title-Type: String Quartet Title-No: 13 Title-Key: B flat major Title-Opus: 130 Title-Dates: 1825 Title-Type: String Quartet Title-No: 14 Title-Key: C sharp minor Title-Opus: 131 Title-Dates: 1826 Title-Type: String Quartet Title-No: 15 Title-Key: A minor Title-Opus: 132 Title-Dates: 1825 Title-RAW: Große Fuge Title-Key: B flat major Title-For: string quartet Title-Opus: 133 Title-Dates: 1825 Title-RAW: Piano arrangement (4 hands) of Große Fuge Title-Opus: 134 Title-Dates: 1826 Title-Type: String Quartet Title-No: 16 Title-Key: F major Title-Opus: 135 Title-Dates: 1826 Title-RAW: Cantata - Der glorreiche Augenblick Title-Opus: 136 Title-Dates: 1814 Title-RAW: String Quintet (fugue) Title-Key: D major Title-Opus: 137 Title-Dates: 1817 Title-RAW: Opera - Leonore (earlier version of Fidelio, with Leonore No. 1 Overture) Title-Opus: 138 Title-Dates: 1807 ## Music for a Ritterballett; WoO 1 (1790--1791) ## Triumphal March for orchestra for Christoph Kuffner's tragedy Tarpeja; WoO 2a ## Prelude to Act II of Tarpeja; WoO 2b ## "Gratulations-Menuett", minuet for orchestra; WoO 3 (1822) ## Piano Concerto in E flat major (solo part only with indications of orchestration); WoO 4 (1784) ## Violin Concerto movement in C major, fragment; WoO 5 (1790--1792) ## Rondo in B flat major for piano and orchestra, fragment, possibly part of initial version of the Piano Concerto No. 2; WoO 6 (1793) ## Twelve minuets for orchestra; WoO 7 (1795) ## Twelve German Dances for orchestra (later arranged for piano); WoO 8 (1795) ## Six minuets for two violins and cello; WoO 9 (1795) ## Six minuets for orchestra (original version lost, only an arrangement for piano is extant); WoO 10 (1795) ## Seven Ländler for two violins and cello (original version lost, only an arrangement for piano is extant); WoO 11 (1799) ## Twelve minuets for orchestra (probably spurious, actually by Beethoven's brother Carl); WoO 12 (1799) ## Twelve German Dances for orchestra (only a version for piano is extant); WoO 13 (1792--1797) ## Twelve contredanses for orchestra; WoO 14 ## Six Laendler for two violins and cello (also arranged for piano); WoO 15 (1802) ## Twelve Ecossaises for orchestra (probably spurious); WoO 16 ## Eleven "Mödlinger Tänze" for seven instruments (probably spurious); WoO 17 (1819) ## March for Military Band (trio added later); WoO 18 (1809--1810) ## March for Military Band (trio added later); WoO 19 (1810) ## March for Military Band (trio added later); WoO 20 (1809--1822) ## Polonaise for Military Band; WoO 21 (1810) ## Ecossaise for Military Band; WoO 22 (1809--1810) ## Ecossaise for Military Band (only a piano arrangement by Carl Czerny is extant); WoO 23 (1810) ## March for Military Band; WoO 24 (1816) ## Rondo for two oboes, two clarinets, two French horns and two bassoons (original finale of the Octet, opus 103; WoO 25 (1792--1793) ## Duo for two flutes; WoO 26 (1792) ## Three duets for clarinet and bassoon (possibly spurious); WoO 27 ## Variations for two oboes and cor anglais on "Là ci darem la mano" from Wolfgang Amadeus Mozart's opera Don Giovanni; WoO 28 (1795) ## March for wind; WoO 29 (1797--1798) ## Three Equali for four trombones (also arranged for four male voices); WoO 30 (1812) ## Fugue for organ; WoO 31 (1783) ## Duo for viola and cello, "mit zwei obligaten Augengläsern" ("with two obbligato eyeglasses"); WoO 32 (1796--1797) ## Five pieces for mechanical clock or flute; WoO 33 ## Duet for two violins; WoO 34 (1822) ## Canon for two violins; WoO 35 (1825) ## Three piano quartets; WoO 36 (1785) ## Trio in G major for piano, flute and bassoon; WoO 37 (1786) ## Piano Trio No. 8 in E flat major; WoO 38 (1785--1791) ## Allegretto in B flat major for piano trio; WoO 39 (1812) ## Twelve variations for piano and violin on "Se vuol ballare" from Mozart's The Marriage of Figaro; WoO 40 (1792--1793) ## Rondo in G major for piano and violin; WoO 41 (1793--1794) ## Six German Dances for violin and piano; WoO 42 (1796) ## Sonatina for mandolin and harpsichord; WoO 43a ## Adagio for mandolin and harpsichord; WoO 43b ## Sonatina for mandolin and piano; WoO 44a ## Andante and variations for mandolin and harpsichord; WoO 44b ## Twelve variations for cello and piano "See the conqu'ring hero comes" from George Frideric Händel's oratorio Judas Maccabaeus; WoO 45 (1796) ## Seven variations for cello and piano on "Bei Männern, welche Liebe fühlen" from Mozart's opera Die Zauberflöte; WoO 46 (1801) ## Andante Favori - Original middle movement from Piano Sonata No. 21 (Waldstein); WoO 57 (1803--1804) ## Für Elise - Bagatelle in A minor for solo piano; WoO 59 (1808) ## String Quintet in C major (Fragment, Piano Transcription); WoO 62 (1826) ## Nine variations for piano on a march by Ernst Christoph Dressler; WoO 63 (1782) ## Six variations for piano or harp on a Swiss song; WoO 64 (1790--1792) ## Twenty-four variations for piano on Vincenzio Righini's aria "Venni Amore"; WoO 65 (1790--1791) ## Thirteen variations for piano on the aria "Es war einmal ein alter Mann" from Carl Ditters von Dittersdorf's opera Das rote Käppchen; WoO 66 (1792) ## Eight variations for piano four hands on a theme by Count Waldstein; WoO 67 (1792) ## Twelve variations for piano on the "Menuet a la Vigano" from Jakob Haibel's ballet La nozza disturbate; WoO 68 (1795) ## Nine variations for piano on "Quant'e piu bello" from Giovanni Paisiello's opera La Molinara; WoO 69 (1795) ## Six variations for piano on "Nel cor piu non mi sento" from Giovanni Paisiello's opera La Molinara; WoO 70 (1795) ## Twelve variations for piano on the Russian dance from Paul Wranitzky's ballet Das Waldmädchen; WoO 71 (1796--1797) ## Eight variations for piano on "Mich brennt ein heisses Fieber" from André-Ernest-Modeste Grétry's opera Richard Löwenherz; WoO 72 (1795--1798) ## Ten variations for piano on "La stessa, la stessissima" from Antonio Salieri's opera Falstaff; WoO 73 (1799) ## "Ich denke dein" - song with six variations for piano four hands; WoO 74 (1799--1803) ## Seven variations for piano on "Kind, willst du ruhig schlafen" from Peter Winter's opera Das unterbrochene Opferfest; WoO 75 (1792--1799) ## Eight variations for piano on "Tandeln und scherzen" from Franz Xaver Süssmayr's opera Soliman II; WoO 76 (1799) ## Six easy variations for piano on an original theme; WoO 77 (1800) ## Seven variations for piano on "God Save the King"; WoO 78 (1802--1803) ## Five variations for piano on "Rule Britannia"; WoO 79 (1803) ## Thirty-two variations in C minor on an original theme; WoO 80 (1806) ## These titles are taken from beethovenlv site: Title-RAW: Ballet Music "Ritterballet", music for a ballet of knights Title-Opus: WoO 1 Title-Dates: 1790--1791 Title-RAW: Triumphal March Title-For: Orchestra Title-Key: C major Title-Name: Tarpeja Title-Opus: WoO 2-1 Title-Dates: 1813 Title-RAW: Entr’acte Title-Key: D major Title-Name: Tarpeja Title-Comment: (now thought to be from the opening of Act II from Leonore 1805) Title-Opus: WoO 2-2 Title-Dates: 1813 Title-Type: Minuet Title-For: Orchestra Title-Key: E flat major Title-Name: Gratulations - Menuett Title-Opus: WoO 3 Title-Dates: 1822 Title-Type: Concerto Title-For: Piano & Orchestra Title-Key: E flat major Title-Punct: ; Title-Comment: authorship: reconstruction Title-Opus: WoO 4 Title-Dates: 1784 Title-Type: Concerto Title-For: Violin & Orchestra Title-Key: C major Title-Punct: : Title-Comment: fragment of first movement; three completions made; authorship: unfinished Title-Opus: WoO 5 Title-Dates: 1790--1792 Title-Type: Rondo Title-For: Piano & Orchestra Title-Key: B flat major Title-Punct: : Title-Comment: fragment, completed by Carl Czerny Title-Opus: WoO 6 Title-Dates: 1793 Title-Type: Minuet Title-For: Orchestra Title-Key: D major Title-Punct: : Title-Comment: No. 1 of 12 Minuets for Orchestra; piano version is Hess 101 Title-Opus: WoO 7-1 Title-Dates: 1795 Title-Type: Minuet Title-For: Orchestra Title-Key: B flat major Title-Punct: : Title-Comment: No. 2 of 12 Minuets for Orchestra; piano version is Hess 101 Title-Opus: WoO 7-2 Title-Dates: 1795 Title-Type: Minuet Title-For: Orchestra Title-Key: G major Title-Punct: : Title-Comment: No. 3 of 12 Minuets for Orchestra; piano version is Hess 101 Title-Opus: WoO 7-3 Title-Dates: 1795 Title-Type: Minuet Title-For: Orchestra Title-Key: E flat major Title-Punct: : Title-Comment: No. 4 of 12 Minuets for Orchestra; piano version is Hess 101 Title-Opus: WoO 7-4 Title-Dates: 1795 Title-Type: Minuet Title-For: Orchestra Title-Key: C major Title-Punct: : Title-Comment: No. 5 of 12 Minuets for Orchestra; piano version is Hess 101 Title-Opus: WoO 7-5 Title-Dates: 1795 Title-Type: Minuet Title-For: Orchestra Title-Key: A major Title-Punct: : Title-Comment: No. 6 of 12 Minuets for Orchestra; piano version is Hess 101 Title-Opus: WoO 7-6 Title-Dates: 1795 Title-Type: Minuet Title-For: Orchestra Title-Key: D major Title-Punct: : Title-Comment: No. 7 of 12 Minuets for Orchestra; piano version is Hess 101 Title-Opus: WoO 7-7 Title-Dates: 1795 Title-Type: Minuet Title-For: Orchestra Title-Key: B flat major Title-Punct: : Title-Comment: No. 8 of 12 Minuets for Orchestra; piano version is Hess 101 Title-Opus: WoO 7-8 Title-Dates: 1795 Title-Type: Minuet Title-For: Orchestra Title-Key: G major Title-Punct: : Title-Comment: No. 9 of 12 Minuets for Orchestra; piano version is Hess 101 Title-Opus: WoO 7-9 Title-Dates: 1795 Title-Type: Minuet Title-For: Orchestra Title-Key: E flat major Title-Punct: : Title-Comment: No. 10 of 12 Minuets for Orchestra; piano version is Hess 101 Title-Opus: WoO 7-10 Title-Dates: 1795 Title-Type: Minuet Title-For: Orchestra Title-Key: C major Title-Punct: : Title-Comment: No. 11 of 12 Minuets for Orchestra; piano version is Hess 101 Title-Opus: WoO 7-11 Title-Dates: 1795 Title-Type: Minuet Title-For: Orchestra Title-Key: F major Title-Punct: : Title-Comment: No. 12 of 12 Minuets for Orchestra; piano version is Hess 101 Title-Opus: WoO 7-12 Title-Dates: 1795 Title-Type: Dance Title-For: Orchestra Title-Key: C major Title-Punct: : Title-Comment: No. 1 of 12 German Dances Title-Opus: WoO 8-1 Title-Dates: 1795 Title-Type: Dance Title-For: Orchestra Title-Key: A major Title-Punct: : Title-Comment: No. 2 of 12 German Dances Title-Opus: WoO 8-2 Title-Dates: 1795 Title-Type: Dance Title-For: Orchestra Title-Key: F major Title-Punct: : Title-Comment: No. 3 of 12 German Dances Title-Opus: WoO 8-3 Title-Dates: 1795 Title-Type: Dance Title-For: Orchestra Title-Key: B flat major Title-Punct: : Title-Comment: No. 4 of 12 German Dances Title-Opus: WoO 8-4 Title-Dates: 1795 Title-Type: Dance Title-For: Orchestra Title-Key: E flat major Title-Punct: : Title-Comment: No. 5 of 12 German Dances Title-Opus: WoO 8-5 Title-Dates: 1795 Title-Type: Dance Title-For: Orchestra Title-Key: G major Title-Punct: : Title-Comment: No. 6 of 12 German Dances Title-Opus: WoO 8-6 Title-Dates: 1795 Title-Type: Dance Title-For: Orchestra Title-Key: C major Title-Punct: : Title-Comment: No. 7 of 12 German Dances Title-Opus: WoO 8-7 Title-Dates: 1795 Title-Type: Dance Title-For: Orchestra Title-Key: A major Title-Punct: : Title-Comment: No. 8 of 12 German Dances Title-Opus: WoO 8-8 Title-Dates: 1795 Title-Type: Dance Title-For: Orchestra Title-Key: F major Title-Punct: : Title-Comment: No. 9 of 12 German Dances Title-Opus: WoO 8-9 Title-Dates: 1795 Title-Type: Dance Title-For: Orchestra Title-Key: D major Title-Punct: : Title-Comment: No. 10 of 12 German Dances Title-Opus: WoO 8-10 Title-Dates: 1795 Title-Type: Dance Title-For: Orchestra Title-Key: G major Title-Punct: : Title-Comment: No. 11 of 12 German Dances Title-Opus: WoO 8-11 Title-Dates: 1795 Title-Type: Dance Title-For: Orchestra Title-Key: C major Title-Punct: : Title-Comment: No. 12 of 12 German Dances Title-Opus: WoO 8-12 Title-Dates: 1795 Title-Type: Minuet Title-For: Violins & Cello Title-Key: E flat major Title-Punct: : Title-Comment: No. 1 of 6 Minuets Title-Opus: WoO 9-1 Title-Dates: 1795 Title-Type: Minuet Title-For: Violins & Cello Title-Key: G major Title-Punct: : Title-Comment: No. 2 of 6 Minuets Title-Opus: WoO 9-2 Title-Dates: 1795 Title-Type: Minuet Title-For: Violins & Cello Title-Key: C major Title-Punct: : Title-Comment: No. 3 of 6 Minuets Title-Opus: WoO 9-3 Title-Dates: 1795 Title-Type: Minuet Title-For: Violins & Cello Title-Key: F major Title-Punct: : Title-Comment: No. 4 of 6 Minuets Title-Opus: WoO 9-4 Title-Dates: 1795 Title-Type: Minuet Title-For: Violin & Cello Title-Key: D major Title-Punct: : Title-Comment: No. 5 of 6 Minuets Title-Opus: WoO 9-5 Title-Dates: 1795 Title-Type: Minuet Title-For: Violins & Cello Title-Key: G major Title-Punct: : Title-Comment: No. 6 of 6 Minuets Title-Opus: WoO 9-6 Title-Dates: 1795 Title-Type: Minuet Title-For: Orchestra Title-Key: C major Title-Punct: : Title-Comment: No. 1 of 6 minuets; surviving piano version Title-Opus: WoO 10-1 Title-Dates: 1795 Title-Type: Minuet Title-For: Orchestra Title-Key: G major Title-Punct: : Title-Comment: No. 2 of 6 minuets; surviving piano version Title-Opus: WoO 10-2 Title-Dates: 1795 Title-Type: Minuet Title-For: Orchestra Title-Key: E flat major Title-Punct: : Title-Comment: No. 3 of 6 minuets; surviving piano version Title-Opus: WoO 10-3 Title-Dates: 1795 Title-Type: Minuet Title-For: Orchestra Title-Key: B flat major Title-Punct: : Title-Comment: No. 4 of 6 minuets; surviving piano version Title-Opus: WoO 10-4 Title-Dates: 1795 Title-Type: Minuet Title-For: Orchestra Title-Key: D major Title-Punct: : Title-Comment: No. 5 of 6 minuets; surviving piano version Title-Opus: WoO 10-5 Title-Dates: 1795 Title-Type: Minuet Title-For: Orchestra Title-Key: C major Title-Punct: : Title-Comment: No. 6 of 6 minuets; surviving piano version Title-Opus: WoO 10-6 Title-Dates: 1795 Title-Count: 7 Title-Type: Ländler Title-For: Violins & Cello Title-Key: D major Title-Punct: : Title-Comment: surviving piano version Title-Opus: WoO 11 Title-Dates: 1799 Title-RAW: 12 Minuets for Orchestra; authorship: not certain Title-Opus: WoO 12 Title-Dates: 1799 Title-Type: Dance Title-For: Orchestra Title-Key: D major Title-Punct: : Title-Comment: No. 1 of 12 German Dances; surviving piano version Title-Opus: WoO 13-1 Title-Dates: 1792--1797 Title-Type: Dance Title-For: Orchestra Title-Key: B flat major Title-Punct: : Title-Comment: No. 2 of 12 German Dances; surviving piano version Title-Opus: WoO 13-2 Title-Dates: 1792--1797 Title-Type: Dance Title-For: Orchestra Title-Key: G major Title-Punct: : Title-Comment: No. 3 of 12 German Dances; surviving piano version Title-Opus: WoO 13-3 Title-Dates: 1792--1797 Title-Type: Dance Title-For: Orchestra Title-Key: D major Title-Punct: : Title-Comment: No. 4 of 12 German Dances; surviving piano version Title-Opus: WoO 13-4 Title-Dates: 1792--1797 Title-Type: Dance Title-For: Orchestra Title-Key: F major Title-Punct: : Title-Comment: No. 5 of 12 German Dances; surviving piano version Title-Opus: WoO 13-5 Title-Dates: 1792--1797 Title-Type: Dance Title-For: Orchestra Title-Key: B flat major Title-Punct: : Title-Comment: No. 6 of 12 German Dances; surviving piano version Title-Opus: WoO 13-6 Title-Dates: 1792--1797 Title-Type: Dance Title-For: Orchestra Title-Key: D major Title-Punct: : Title-Comment: No. 7 of 12 German Dances; surviving piano version Title-Opus: WoO 13-7 Title-Dates: 1792--1797 Title-Type: Dance Title-For: Orchestra Title-Key: G major Title-Punct: : Title-Comment: No. 8 of 12 German Dances; surviving piano version Title-Opus: WoO 13-8 Title-Dates: 1792--1797 Title-Type: Dance Title-For: Orchestra Title-Key: E flat major Title-Punct: : Title-Comment: No. 9 of 12 German Dances; surviving piano version Title-Opus: WoO 13-9 Title-Dates: 1792--1797 Title-Type: Dance Title-For: Orchestra Title-Key: C major Title-Punct: : Title-Comment: No. 10 of 12 German Dances; surviving piano version Title-Opus: WoO 13-10 Title-Dates: 1792--1797 Title-Type: Dance Title-For: Orchestra Title-Key: A major Title-Punct: : Title-Comment: No. 11 of 12 German Dances; surviving piano version Title-Opus: WoO 13-11 Title-Dates: 1792--1797 Title-Type: Dance Title-For: Orchestra Title-Key: D major Title-Punct: : Title-Comment: No. 12 of 12 German Dances; surviving piano version Title-Opus: WoO 13-12 Title-Dates: 1792--1797 Title-Type: Contredanse Title-For: Small Orchestra Title-Key: C major Title-Punct: : Title-Comment: No. 1 of 12 Contredanses Title-Opus: WoO 14-1 Title-Dates: 1791--1802 Title-Type: Contredanse Title-For: Small Orchestra Title-Key: A major Title-Punct: : Title-Comment: No. 2 of 12 Contredanses Title-Opus: WoO 14-2 Title-Dates: 1801--1802 Title-Type: Contredanse Title-For: Small Orchestra Title-Key: D major Title-Punct: : Title-Comment: No. 3 of 12 Contredanses Title-Opus: WoO 14-3 Title-Dates: 1795--1802 Title-Type: Contredanse Title-For: Small Orchestra Title-Key: B flat major Title-Punct: : Title-Comment: No. 4 of 12 Contredanses Title-Opus: WoO 14-4 Title-Dates: 1795--1802 Title-Type: Contredanse Title-For: Small Orchestra Title-Key: E flat major Title-Punct: : Title-Comment: No. 5 of 12 Contredanses Title-Opus: WoO 14-5 Title-Dates: 1791--1802 Title-Type: Contredanse Title-For: Small Orchestra Title-Key: C major Title-Punct: : Title-Comment: No. 6 of 12 Contredanses Title-Opus: WoO 14-6 Title-Dates: 1795--1802 Title-Type: Contredanse Title-For: Small Orchestra Title-Key: E flat major Title-Punct: : Title-Comment: No. 7 of 12 Contredanses Title-Opus: WoO 14-7 Title-Dates: 1800--1802 Title-Type: Contredanse Title-For: Small Orchestra Title-Key: C major Title-Punct: : Title-Comment: No. 8 of 12 Contredanses Title-Opus: WoO 14-8 Title-Dates: 1791--1802 Title-Type: Contredanse Title-For: Small Orchestra Title-Key: A major Title-Punct: : Title-Comment: No. 9 of 12 Contredanses Title-Opus: WoO 14-9 Title-Dates: 1801--1802 Title-Type: Contredanse Title-For: Small Orchestra Title-Key: C major Title-Punct: : Title-Comment: No. 10 of 12 Contredanses Title-Opus: WoO 14-10 Title-Dates: 1801--1802 Title-Type: Contredanse Title-For: Small Orchestra Title-Key: G major Title-Punct: : Title-Comment: No. 11 of 12 Contredanses Title-Opus: WoO 14-11 Title-Dates: 1800--1802 Title-Type: Contredanse Title-For: Small Orchestra Title-Key: E flat major Title-Punct: : Title-Comment: No. 12 of 12 Contredanses Title-Opus: WoO 14-12 Title-Dates: 1791--1802 Title-Type: Dance Title-For: Violins & Bass Title-Key: D major Title-Punct: : Title-Comment: No. 1 of 6 ländlerische Tänze Title-Opus: WoO 15-1 Title-Dates: 1802 Title-Type: Dance Title-For: Piano Title-Key: D major Title-Punct: : Title-Comment: No. 1 of 6 ländlerische Tänze, piano version Title-Opus: WoO 15-1 Title-Dates: 1802 Title-Type: Dance Title-For: Violins & Bass Title-Key: D major Title-Punct: : Title-Comment: No. 2 of 6 ländlerische Tänze Title-Opus: WoO 15-2 Title-Dates: 1802 Title-Type: Dance Title-For: Piano Title-Key: D major Title-Punct: : Title-Comment: No. 2 of 6 ländlerische Tänze, piano version Title-Opus: WoO 15-2 Title-Dates: 1802 Title-Type: Dance Title-For: Violins & Bass Title-Key: D major Title-Punct: : Title-Comment: No. 3 of 6 ländlerische Tänze Title-Opus: WoO 15-3 Title-Dates: 1802 Title-Type: Dance Title-For: Piano Title-Key: D major Title-Punct: : Title-Comment: No. 3 of 6 ländlerische Tänze, piano version Title-Opus: WoO 15-3 Title-Dates: 1802 Title-Type: Dance Title-For: Violins & Bass Title-Key: D minor Title-Punct: : Title-Comment: No. 4 of 6 ländlerische Tänze Title-Opus: WoO 15-4 Title-Dates: 1802 Title-Type: Dance Title-For: Piano Title-Key: D minor Title-Punct: : Title-Comment: No. 4 of 6 ländlerische Tänze, piano version Title-Opus: WoO 15-4 Title-Dates: 1802 Title-Type: Dance Title-For: Violins & Bass Title-Key: D major Title-Punct: : Title-Comment: No. 5 of 6 ländlerische Tänze Title-Opus: WoO 15-5 Title-Dates: 1802 Title-Type: Dance Title-For: Piano Title-Key: D major Title-Punct: : Title-Comment: No. 5 of 6 ländlerische Tänze, piano version Title-Opus: WoO 15-5 Title-Dates: 1802 Title-Type: Dance Title-For: Violins & Bass Title-Key: D major Title-Punct: : Title-Comment: No. 6 of 6 ländlerische Tänze Title-Opus: WoO 15-6 Title-Dates: 1802 Title-Type: Dance Title-For: Piano Title-Key: D major Title-Punct: : Title-Comment: No. 6 of 6 ländlerische Tänze, piano version Title-Opus: WoO 15-6 Title-Dates: 1802 Title-RAW: 12 Ecossaises for Piano; authorship: fraudulent Title-Opus: WoO 16 Title-Type: Waltz Title-For: Instrument(s) Title-Key: E flat major Title-Punct: : Title-Comment: No. 1 of 11 Mödlinger Tänze for 7 String & Wind Instruments; authorship: probably spurious Title-Opus: WoO 17-1 Title-Dates: 1819 Title-Type: Minuet Title-For: Instrument(s) Title-Key: B flat major Title-Punct: : Title-Comment: No. 2 of 11 Mödlinger Tänze for 7 String & Wind Instruments; authorship: probably spurious Title-Opus: WoO 17-2 Title-Dates: 1819 Title-Type: Waltz Title-For: Instrument(s) Title-Key: B flat major Title-Punct: : Title-Comment: No. 3 of 11 Mödlinger Tänze for 7 String & Wind Instruments; authorship: probably spurious Title-Opus: WoO 17-3 Title-Dates: 1819 Title-Type: Minuet Title-For: Instrument(s) Title-Key: E flat major Title-Punct: : Title-Comment: No. 4 of 11 Mödlinger Tänze for 7 String & Wind Instruments; authorship: probably spurious Title-Opus: WoO 17-4 Title-Dates: 1819 Title-Type: Minuet Title-For: Instrument(s) Title-Key: E flat major Title-Punct: : Title-Comment: No. 5 of 11 Mödlinger Tänze for 7 String & Wind Instruments; authorship: probably spurious Title-Opus: WoO 17-5 Title-Dates: 1819 Title-Type: Ländler Title-For: Instrument(s) Title-Key: E flat major Title-Punct: : Title-Comment: No. 6 of 11 Mödlinger Tänze for 7 String & Wind Instruments; authorship: probably spurious Title-Opus: WoO 17-6 Title-Dates: 1819 Title-Type: Minuet Title-For: Instrument(s) Title-Key: B flat major Title-Punct: : Title-Comment: No. 7 of 11 Mödlinger Tänze for 7 String & Wind Instruments; authorship: probably spurious Title-Opus: WoO 17-7 Title-Dates: 1819 Title-Type: Ländler Title-For: Instrument(s) Title-Key: B flat major Title-Punct: : Title-Comment: No. 8 of 11 Mödlinger Tänze for 7 String & Wind Instruments; authorship: probably spurious Title-Opus: WoO 17-8 Title-Dates: 1819 Title-Type: Minuet Title-For: Instrument(s) Title-Key: G major Title-Punct: : Title-Comment: No. 9 of 11 Mödlinger Tänze for 7 String & Wind Instruments; authorship: probably spurious Title-Opus: WoO 17-9 Title-Dates: 1819 Title-Type: Waltz Title-For: Instrument(s) Title-Key: D major Title-Punct: : Title-Comment: No. 10 of 11 Mödlinger Tänze for 7 String & Wind Instruments; authorship: probably spurious Title-Opus: WoO 17-10 Title-Dates: 1819 Title-Type: Waltz Title-For: Instrument(s) Title-Key: D major Title-Punct: : Title-Comment: No. 11 of 11 Mödlinger Tänze for 7 String & Wind Instruments; authorship: probably spurious Title-Opus: WoO 17-11 Title-Dates: 1819 Title-RAW: March & Trio Title-For: Wind Band Title-Key: F major Title-Name: York’scher (Yorkscher) March für die bohmische Landwehr Title-Opus: WoO 18 Title-Dates: 1809--1810 Title-RAW: March & Trio Title-For: Wind Band Title-Key: F major Title-Opus: WoO 19 Title-Dates: 1810 Title-RAW: March & Trio Title-For: Wind Band Title-Key: C major Title-Name: Zapfenstreich Title-Comment: (The Tattoo) Title-Opus: WoO 20 Title-Dates: 1809--1822 Title-Type: Polonaise Title-For: Wind Band Title-Key: D major Title-Opus: WoO 21 Title-Dates: 1810 Title-Type: Ecossaise Title-For: Wind Band Title-Key: D major Title-Opus: WoO 22 Title-Dates: 1809--1810 Title-Type: Ecossaise Title-For: Wind Band Title-Key: G major Title-Punct: ; Title-Comment: authorship: reconstruction Title-Opus: WoO 23 Title-Dates: 1810 Title-Type: March Title-For: Wind Band Title-Key: D major Title-Opus: WoO 24 Title-Dates: 1816 Title-Type: Rondino Title-For: Oboes, Clarinets, Horns & Bassoons Title-Key: E flat major Title-Opus: WoO 25 Title-Dates: 1792--1793 Title-Type: Duo Title-For: Flutes Title-Key: G major Title-Punct: : Title-Comment: Allegro and Minuet Title-Opus: WoO 26 Title-Dates: 1792 Title-Type: Duo Title-No: 1 Title-For: Clarinet & Bassoon Title-Key: C major Title-Punct: ; Title-Comment: authorship: doubtful Title-Opus: WoO 27-1 Title-Type: Duo Title-No: 2 Title-For: Clarinet & Bassoon Title-Key: F major Title-Punct: ; Title-Comment: authorship: doubtful Title-Opus: WoO 27-2 Title-Type: Duo Title-No: 3 Title-For: Clarinet & Bassoon Title-Key: B flat major Title-Punct: ; Title-Comment: authorship: doubtful Title-Opus: WoO 27-3 Title-Type: Variations Title-For: Oboes & English Horn Title-Key: C major Title-Punct: : Title-Comment: on "Là ci darem la mano" from Mozart’s opera Title-Name: Don Giovanni Title-Opus: WoO 28 Title-Dates: 1795 Title-Type: March Title-For: Clarinets, Horns & Bassoons Title-Key: B flat major Title-Name: Grenadiermarsch Title-Opus: WoO 29 Title-Dates: 1797--1798 Title-Type: Equali Title-For: Trombones Title-Key: D minor Title-Opus: WoO 30-1 Title-Dates: 1812 Title-Type: Equali Title-For: Trombones Title-Key: D major Title-Opus: WoO 30-2 Title-Dates: 1812 Title-Type: Equali Title-For: Trombones Title-Key: B flat major Title-Opus: WoO 30-3 Title-Dates: 1812 Title-Type: Fugue Title-For: Organ Title-Key: D major Title-Opus: WoO 31 Title-Dates: 1783 Title-Type: Duo Title-For: Viola & Cello Title-Key: E flat major Title-Name: mit zwei obligaten Augengläsern Title-Comment: (with 2 obbligato eyeglasses) Title-Opus: WoO 32 Title-Dates: 1796--1797 Title-RAW: Piece for Musical Clock Title-Key: F major Title-Punct: : Title-Comment: or for flute; Adagio assai; No. 1 of 5 pieces Title-Opus: WoO 33-1 Title-Dates: 1799 Title-RAW: Piece for Musical Clock Title-Key: G major Title-Punct: : Title-Comment: or for flute; Scherzo-Allegro; No. 2 of 5 pieces Title-Opus: WoO 33-2 Title-Dates: 1799--1800 Title-RAW: Piece for Musical Clock Title-Key: G major Title-Punct: : Title-Comment: or for flute; Allegro; No. 3 of 5 pieces Title-Opus: WoO 33-3 Title-Dates: 1799 Title-RAW: Piece for Musical Clock Title-Key: C major Title-Punct: : Title-Comment: or for flute; Allegro non piu molto; No. 4 of 5 pieces Title-Opus: WoO 33-4 Title-Dates: 1794 Title-RAW: Piece for Musical Clock Title-Key: C major Title-Punct: : Title-Comment: or for flute; Minuet-Allegretto; No. 5 of 5 pieces Title-Opus: WoO 33-5 Title-Dates: 1794 Title-Type: Duo Title-For: Violins Title-Key: A major Title-Punct: : Title-Comment: for Alexandre Boucher Title-Opus: WoO 34 Title-Dates: 1822 Title-Type: Canon Title-Key: A major Title-Punct: : Title-Comment: for 2 violins or for 2 cellos; for Samson de Boer Title-Opus: WoO 35 Title-Dates: 1825 Title-Type: Piano Quartet Title-Key: E flat major Title-Opus: WoO 36-1 Title-Dates: 1785 Title-Type: Piano Quartet Title-Key: D major Title-Opus: WoO 36-2 Title-Dates: 1785 Title-Type: Piano Quartet Title-Key: C major Title-Opus: WoO 36-3 Title-Dates: 1785 Title-Type: Trio Title-For: Piano, Flute & Bassoon Title-Key: G major Title-Opus: WoO 37 Title-Dates: 1786 Title-Type: Piano Trio Title-No: 8 Title-Key: E flat major Title-Opus: WoO 38 Title-Dates: 1785--1791 Title-Type: Allegretto Title-For: Piano Trio Title-Key: B flat major Title-Opus: WoO 39 Title-Dates: 1812 Title-Count: 12 Title-Type: Variations Title-For: Piano & Violin Title-Key: F major Title-Punct: : Title-Comment: on the theme "Se vuol ballare" from Mozart’s opera Title-Name: The Marriage of Figaro Title-Opus: WoO 40 Title-Dates: 1792--1793 Title-Type: Rondo Title-For: Piano & Violin Title-Key: G major Title-Opus: WoO 41 Title-Dates: 1793--1794 Title-RAW: 6 German Dances for Piano & Violin: Allemandes in F Title-Key: D major Title-Key: F major Title-Key: A major Title-Key: D major Title-Key: G major Title-Opus: WoO 42 Title-Dates: 1796 Title-Type: Sonatina Title-For: Mandolin & Harpsichord Title-Key: C minor Title-Punct: : Title-Comment: (WoO 43a) Title-Opus: WoO 43-1 Title-Dates: 1796 Title-RAW: Adagio Title-For: Mandolin & Harpsichord Title-Key: E flat major Title-Punct: : Title-Comment: (WoO 43b) Title-Opus: WoO 43-2 Title-Dates: 1796 Title-Type: Sonatina Title-For: Mandolin & Harpsichord Title-Key: C major Title-Punct: : Title-Comment: (WoO 44a) Title-Opus: WoO 44-1 Title-Dates: 1796 Title-RAW: Andante con Variazioni Title-For: Mandolin & Harpsichord Title-Key: D major Title-Punct: : Title-Comment: (WoO 44b) Title-Opus: WoO 44-2 Title-Dates: 1796 Title-Count: 12 Title-Type: Variations Title-For: Piano & Cello Title-Key: G major Title-Punct: : Title-Comment: on "See the conquering hero comes" fm Handel’s oratorio Title-Name: Judas Maccabaeus Title-Opus: WoO 45 Title-Dates: 1796 Title-Count: 7 Title-Type: Variations Title-For: Piano & Cello Title-Key: E flat major Title-Punct: : Title-Comment: on the duet "Bei Männern, welche Liebe fühlen" from Mozart’s Title-Name: Zauberflöte Title-Opus: WoO 46 Title-Dates: 1801 Title-Type: Piano Sonata Title-Key: E flat major Title-Name: Kurfürstensonate Title-Comment: (Electoral) Title-No: 1 Title-Opus: WoO 47-1 Title-Dates: 1783 Title-Type: Piano Sonata Title-Key: F minor Title-Name: Kurfürstensonate Title-Comment: (Electoral) Title-No: 2 Title-Opus: WoO 47-2 Title-Dates: 1783 Title-Type: Piano Sonata Title-Key: D major Title-Name: Kurfürstensonate Title-Comment: (Electoral) Title-No: 3 Title-Opus: WoO 47-3 Title-Dates: 1783 Title-Type: Rondo Title-For: Piano Title-Key: C major Title-Opus: WoO 48 Title-Dates: 1783 Title-Type: Rondo Title-For: Piano Title-Key: A major Title-Opus: WoO 49 Title-Dates: 1783 Title-RAW: 2 Sonata Movements Title-For: Piano Title-Key: F major Title-Opus: WoO 50 Title-Dates: 1790--1792 Title-RAW: Easy Piano Sonata Title-Key: C major Title-Opus: WoO 51 Title-Dates: 1791--1798 Title-Type: Bagatelle Title-For: Piano Title-Key: C minor Title-Punct: : Title-Comment: presto Title-Opus: WoO 52 Title-Dates: 1795--1822 Title-Type: Allegretto Title-For: Piano Title-Key: C minor Title-Opus: WoO 53 Title-Dates: 1796--1797 Title-Type: Piece Title-For: Piano Title-Key: C major Title-Name: Lustig - traurig Title-Comment: (Merry - Sad) in C & c Title-Opus: WoO 54 Title-Dates: 1802 Title-Type: Prelude Title-For: Piano Title-Key: F minor Title-Opus: WoO 55 Title-Dates: 1803 Title-Type: Bagatelle Title-For: Piano Title-Key: C major Title-Punct: : Title-Comment: Allegretto Title-Opus: WoO 56 Title-Dates: 1803--1822 Title-Type: Andante Title-For: Piano Title-Key: F major Title-Name: Andante favori Title-Opus: WoO 57 Title-Dates: 1803--1804 Title-Type: Cadenza Title-For: Piano Title-Key: D minor Title-Punct: : Title-Comment: for Mozart’s piano concerto in d minor K 466 1st mvmt Title-Opus: WoO 58-1 Title-Dates: 1809 Title-Type: Cadenza Title-For: Piano & Orchestra Title-Key: D minor Title-Punct: : Title-Comment: for Mozart’s piano concerto in d minor K 466 3rd mvmt Title-Opus: WoO 58-2 Title-Dates: 1809 Title-Type: Bagatelle Title-For: Piano Title-Key: A minor Title-Name: Für Elise Title-Opus: WoO 59 Title-Dates: 1808 Title-Type: Bagatelle Title-For: Piano Title-Key: B flat major Title-Name: Zeimlich lebhaft Title-Comment: (A little lively) Title-Opus: WoO 60 Title-Dates: 1818 Title-Type: Allegretto Title-For: Piano Title-Key: B minor Title-Opus: WoO 61 Title-Dates: 1821 Title-Type: Bagatelle Title-For: Piano Title-Key: G minor Title-Punct: : Title-Comment: called Title-Opus: WoO 61a Title-Opus: WoO 61-1 Title-Dates: 1825 Title-Type: String Quintet Title-Key: C major Title-Name: Letzter musikalischer Gedanke Title-Comment: (last musical thought); authorship: unfinished at death Title-Opus: WoO 62 Title-Dates: 1826 Title-Count: 9 Title-Type: Variations Title-For: Piano Title-Key: C minor Title-Punct: : Title-Comment: on a march by Ernst Christoph Dressler Title-Opus: WoO 63 Title-Dates: 1782 Title-Count: 6 Title-Type: Variations Title-For: Piano or Harp Title-Key: F major Title-Punct: : Title-Comment: on a Swiss air Title-Opus: WoO 64 Title-Dates: 1790--1792 Title-Count: 24 Title-Type: Variations Title-For: Piano Title-Key: D major Title-Punct: : Title-Comment: on Vincenzo Righini’s arietta Title-Name: Venni Amore Title-Opus: WoO 65 Title-Dates: 1790--1791 Title-Count: 13 Title-Type: Variations Title-For: Piano Title-Key: A major Title-Punct: : Title-Comment: on Dittersdorf’s arietta Title-Name: Es war einmal ein alter Mann Title-Related-How: from Title-Related-Name: Das rote Käppchen Title-Opus: WoO 66 Title-Dates: 1792 Title-Count: 8 Title-Type: Variations Title-For: Piano Four Hands Title-Key: C major Title-Punct: : Title-Comment: on a theme by Count Ferdinand von Waldstein Title-Opus: WoO 67 Title-Dates: 1792 Title-Count: 12 Title-Type: Variations Title-For: Piano Title-Key: C major Title-Punct: : Title-Comment: on the "Menuett à la Viganò" from Jakob Haibel’s ballet Title-Name: Le nozze disturbate Title-Opus: WoO 68 Title-Dates: 1795 Title-Count: 9 Title-Type: Variations Title-For: Piano Title-Key: A major Title-Punct: : Title-Comment: on Giovanni Paisello’s air Title-Name: Quant’ è più bello Title-Related-How: from Title-Related-Name: La Molinara Title-Opus: WoO 69 Title-Dates: 1795 Title-Count: 6 Title-Type: Variations Title-For: Piano Title-Key: G major Title-Punct: : Title-Comment: on the duet "Nel cor più non mi sento" from Giovanni Paisello’s opera Title-Name: La Molinara Title-Opus: WoO 70 Title-Dates: 1795 Title-Count: 12 Title-Type: Variations Title-For: Piano Title-Key: A major Title-Punct: : Title-Comment: on a Russian dance from Paul Wranitsky’s ballet Title-Name: Das Waldmädchen Title-Opus: WoO 71 Title-Dates: 1796--1797 Title-Count: 8 Title-Type: Variations Title-For: Piano Title-Key: C major Title-Punct: : Title-Comment: on the Romance "Une fièvre brûlante" from Grétry’s opera Title-Name: Richard Cœur de Lion Title-Opus: WoO 72 Title-Dates: 1795--1798 Title-Count: 10 Title-Type: Variations Title-For: Piano Title-Key: B major Title-Punct: : Title-Comment: on Salieri’s duet Title-Name: La stessa la stessissima Title-Related-How: from Title-Related-Name: Falstaff Title-Opus: WoO 73 Title-Dates: 1799 Title-Type: Song Title-Name: Ich denke dein Title-Alternative-Name: Ich denke dein, wenn mir der Sonne Schimmer Title-Opus: WoO 74 Title-Dates: 1799--1803 Title-Count: 6 Title-Type: Variations Title-For: Piano Four Hands Title-Key: D major Title-Punct: : Title-Related-How: on Title-Related-Name: Ich denke dein Title-Opus: WoO 74 Title-Dates: 1799--1803 Title-Count: 7 Title-Type: Variations Title-For: Piano Title-Key: F major Title-Punct: : Title-Comment: on the Quartet "Kind, willst du ruhig schlafen?" from Peter Winter’s opera Title-Name: Das unterbrochene Opferfest Title-Opus: WoO 75 Title-Dates: 1792--1799 Title-Count: 8 Title-Type: Variations Title-For: Piano Title-Key: F major Title-Punct: : Title-Comment: on the Trio "Tändeln und Scherzen" from Franz Xaver Süssmayr’s opera Title-Name: Soliman II Title-Opus: WoO 76 Title-Dates: 1799 Title-Count: 6 Title-Type: Easy Variations Title-For: Piano Title-Key: G major Title-Punct: : Title-Comment: on an original theme Title-Opus: WoO 77 Title-Dates: 1800 Title-Count: 7 Title-Type: Variations Title-For: Piano Title-Key: C major Title-Punct: : Title-Comment: on the English folk song Title-Name: God Save the King Title-Opus: WoO 78 Title-Dates: 1802--1803 Title-Count: 5 Title-Type: Variations Title-For: Piano Title-Key: D major Title-Punct: : Title-Comment: on the English song "Rule Britannia" from Arne’s Title-Name: Alfred Title-Opus: WoO 79 Title-Dates: 1803 Title-Count: 32 Title-Type: Variations Title-For: Piano Title-Key: C minor Title-Punct: : Title-Comment: on an original theme Title-Opus: WoO 80 Title-Dates: 1806 Title-Type: Allemande Title-For: Piano Title-Key: A major Title-Opus: WoO 81 Title-Dates: 1793--1822 Title-Type: Minuet Title-For: Piano Title-Key: E flat major Title-Opus: WoO 82 Title-Dates: 1803 Title-Count: 6 Title-Type: Ecossaises Title-For: Piano Title-Key: E flat major Title-Opus: WoO 83 Title-Dates: 1806 Title-Type: Waltz Title-For: Piano Title-Key: E flat major Title-Opus: WoO 84 Title-Dates: 1824 Title-Type: Waltz Title-For: Piano Title-Key: D major Title-Opus: WoO 85 Title-Dates: 1825 Title-Type: Ecossaise Title-For: Piano Title-Key: E flat major Title-Opus: WoO 86 Title-Dates: 1825 Title-RAW: Cantata "Trauerkantate auf den Tod Joseph II" (on the Death of Joseph II) Title-Opus: WoO 87 Title-Dates: 1790 Title-RAW: Cantata "Kantate auf die Erhebung Leopold II zur Kaiserwürde" (on the Elevation of Leopold II to the Imperial Dignity) Title-Opus: WoO 88 Title-Dates: 1790 Title-Type: Aria Title-Key: F major Title-Name: Prüfung des Küssens Title-Punct: ; Title-For: bass & orchestra Title-Name: Meine weise Mutter spricht Title-Opus: WoO 89 Title-Dates: 1790--1792 Title-Type: Aria Title-Key: D major Title-Name: Mit Mädeln sich vertragen Title-Punct: ; Title-For: bass & orchestra Title-Opus: WoO 90 Title-Dates: 1790--1792 Title-Type: Aria Title-Key: F major Title-Name: O welch ein Leben! Title-Punct: ; Title-Comment: for tenor & orchestra for Umlauf’s Singspiel "Die schöne Schusterin" (The Beautiful Shoemaker’s Wife) Title-Opus: WoO 91-1 Title-Dates: 1795 Title-Type: Aria Title-Key: B flat major Title-Name: Soll ein Schuh nicht drücken? Title-Punct: ; Title-Comment: for soprano & orchestra for Umlauf’s Singspiel "Die schöne Schusterin" (The Beautiful Shoemaker’s Wife) Title-Opus: WoO 91-2 Title-Dates: 1795 Title-RAW: Scene & Aria Title-Key: A major Title-Name: Primo amore Title-Punct: ; Title-Comment: for soprano w/ orchestra Title-Opus: WoO 92 Title-Dates: 1791--1792 Title-RAW: Scene & Aria "No, non turbarti"; for soprano w/ string orchestra (WoO 92a) Title-Opus: WoO 92-1 Title-Dates: 1802 Title-RAW: Duo for Solo Voice(s) & Orchestra "Ne’ giorni tuoi felici"; for soprano & tenor w/ orchestra Title-Opus: WoO 93 Title-Dates: 1802 Title-RAW: Music for a drama Title-For: Orchestra, Chorus & Solo Voice(s) Title-Key: B flat major Title-Name: Germania Title-Punct: , Title-Comment: finale for Trietschke’s Singspiel "Die gute Nachricht" (The Good News) Title-Opus: WoO 94 Title-Dates: 1814 Title-Type: Chorus Title-For: Orchestra & Chorus Title-Key: A major Title-Name: Chor auf die verbündeten Fürsten Title-Comment: (Chorus for the Allied Princes) Title-Name: Ihr weisen Gründer Title-Opus: WoO 95 Title-Dates: 1814 Title-RAW: Music for a drama Title-For: Orchestra, Chorus & Solo Voice(s) Title-Name: Leonore Prohaska Title-Opus: WoO 96 Title-Dates: 1815 Title-Type: Chorus Title-For: Orchestra, Chorus & Solo Voice(s) Title-Key: D major Title-Name: Es ist vollbracht Title-Comment: (It is fulfilled), final chorus from Treitschke’s Singspiel "Die Ehrenpforten" (The Triumphal Arches) Title-Opus: WoO 97 Title-Dates: 1815 Title-RAW: Chorus for Orchestra, Chorus & Solo Voice(s) "Wo sich die Pulse jugendlich jagen" chorus with soprano solo for the festive play Title-Name: Die Weihe des Hauses Title-Opus: WoO 98 Title-Dates: 1822 Title-Type: Duet Title-Name: Bei labbri, che Amore Title-Opus: WoO 99-1 Title-Dates: 1801--1803 Title-Type: Trio Title-For: Solo Voice(s) Title-Name: Chi mai di questo core Title-Opus: WoO 99-2 Title-Dates: 1801--1803 Title-RAW: Duet "Fra tutte le pene" (WoO 99, No. 3a) 1 of 3 settings Title-Opus: WoO 99-3.1 Title-Dates: 1801--1803 Title-RAW: Trio for Solo Voice(s) "Fra tutte le pene" (WoO 99, No. 3b); 2 of 3 settings Title-Opus: WoO 99-3.2 Title-Dates: 1801--1803 Title-RAW: Quartet for Solo Voice(s) "Fra tutte le pene" (WoO 99, No. 3c) 3 of 3 settings Title-Opus: WoO 99-3.3 Title-Dates: 1801--1803 Title-RAW: Quartet for Solo Voice(s) "Già la notte s’avvicina" (WoO 99, No. 4a) 1 of 2 settings Title-Opus: WoO 99-4.1 Title-Dates: 1801--1803 Title-RAW: Trio for Solo Voice(s) "Già la notte s’avvicina" (WoO 99, No. 4b) 2 of 2 settings Title-Opus: WoO 99-4.2 Title-Dates: 1801--1803 Title-RAW: Quartet for Solo Voice(s) "Giura il nocchier" (WoO 99, No. 5a) 1 of 2 settings Title-Opus: WoO 99-5.1 Title-Dates: 1801--1803 Title-RAW: Trio for Solo Voice(s) "Giura il nocchier" (WoO 99, No. 5b) 2 of 2 settings Title-Opus: WoO 99-5.2 Title-Dates: 1801--1803 Title-Type: Trio Title-For: Solo Voice(s) Title-Name: Ma tu tremi Title-Opus: WoO 99-6 Title-Dates: 1801--1803 Title-RAW: Quartet for Solo Voice(s) "Nei campi e nelle selve" (WoO 99, No. 7a) 1 of 2 settings Title-Opus: WoO 99-7.1 Title-Dates: 1801--1803 Title-RAW: Quartet for Solo Voice(s) "Nei campi e nelle selve" (WoO 99, No. 7b) 2 of 2 settings Title-Opus: WoO 99-7.2 Title-Dates: 1801--1803 Title-Type: Song Title-Key: G major Title-Name: O care selve Title-Punct: , Title-Comment: with unison chorus Title-Opus: WoO 99-8 Title-Dates: 1794--1795 Title-Type: Trio Title-For: Solo Voice(s) Title-Name: Per te d’amico aprile Title-Opus: WoO 99-9 Title-Dates: 1801--1803 Title-RAW: Quartet for Solo Voice(s) "Quella cetra ah pur tu sei" (WoO 99, No. 10a) 1 of 3 settings Title-Opus: WoO 99-10.1 Title-Dates: 1801--1803 Title-RAW: Trio for Solo Voice(s) "Quella cetra ah pur tu sei" (WoO 99, No. 10b) 2 of 3 settings Title-Opus: WoO 99-10.2 Title-Dates: 1801--1803 Title-RAW: Quartet for Solo Voice(s) "Quella cetra ah pur tu sei" (WoO 99, No. 10c) 3 of 3 settings Title-Opus: WoO 99-10.3 Title-Dates: 1801--1803 Title-Type: Duet Title-Name: Scrivo in te Title-Opus: WoO 99-11 Title-Dates: 1801--1803 Title-Type: Quartet Title-For: Solo Voice(s) Title-Key: C major Title-Name: Silvio, amante disperato Title-Opus: WoO 99-12 Title-Dates: 1801--1802 Title-Type: Joke Title-Key: G major Title-Name: Lob auf den Dicken Title-Name: Schuppanzigh ist ein Lump Title-Opus: WoO 100 Title-Dates: 1801 Title-Type: Joke Title-Key: E flat major Title-Name: Graf, liebster Graf, liebstes Schaf Title-Opus: WoO 101 Title-Dates: 1802 Title-RAW: Song "Abschiedsgesang" (Song of Farewell) Title-For: tenor & 2 basses Title-Name: Die Stunde schlägt Title-Opus: WoO 102 Title-Dates: 1814 Title-Type: Cantata Title-Key: B flat major Title-Name: Un lieto brindisi: Cantata campestre Title-Comment: for 4 voices & piano acc Title-Name: Johannisfeier begehn wir heute Title-Opus: WoO 103 Title-Dates: 1814 Title-RAW: Song "Gesang der Mönche" (Song of the Monks) from Schiller’s Title-Name: William Tell Title-Alternative-Name: Rasch tritt der Tod den Menchen an Title-Opus: WoO 104 Title-Dates: 1817 Title-Type: Song Title-Key: C major Title-Name: Hochzeitslied Title-For: bass, chorus & piano Title-Name: Auf Freunde, singt dem Gott der Ehen! Title-Opus: WoO 105 Title-Dates: 1819 Title-Type: Song Title-Key: A major Title-Name: Hochzeitslied Title-Comment: unison version of Hess 124 Title-Opus: WoO 105 Title-Dates: 1819 Title-Type: Cantata Title-Name: Lobkowitz-Kantate Title-For: soprano, chorus & piano Title-Opus: WoO 106 Title-Dates: 1823 Title-Type: Song Title-Name: Schilderung eines Mädchens Title-Alternative-Name: Schildern, willst du Freund, soll ich dir Elisen? Title-Opus: WoO 107 Title-Dates: 1782 Title-Type: Song Title-Key: A major Title-Name: An einen Säugling Title-Alternative-Name: Noch weisst du nicht, wes Kind du bist Title-Opus: WoO 108 Title-Dates: 1783 Title-Type: Song Title-Key: C major Title-Name: Trinklied (beim Abschied zu singen) Title-Alternative-Name: Erhebt das Glas mit froher Hand Title-Opus: WoO 109 Title-Dates: 1791--1792 Title-Type: Song Title-Key: F minor Title-Name: Elegie auf den Tod eines Pudels Title-Alternative-Name: Stirb immerhin, es welken ja so viele der Freuden Title-Opus: WoO 110 Title-Dates: 1790 Title-Type: Song Title-Key: G major Title-Name: Punschlied Title-Alternative-Name: Wer nicht, wenn warm von Hand zu Hand Title-Opus: WoO 111 Title-Dates: 1791--1792 Title-Type: Song Title-Key: G major Title-Name: An Laura Title-Alternative-Name: Freud’ umblühe dich auf allen Wegen Title-Opus: WoO 112 Title-Dates: 1792 Title-Type: Song Title-Key: E major Title-Name: Klage Title-Alternative-Name: Dein Silver schien durch Eichengrün Title-Opus: WoO 113 Title-Dates: 1790 Title-Type: Song Title-Key: E major Title-Name: Ein Selbstgespräch Title-Alternative-Name: Ich, der mit flatterndem Sinn Title-Opus: WoO 114 Title-Dates: 1793 Title-Type: Song Title-Key: D major Title-Name: An Minna Title-Alternative-Name: Nur bei dir, an deinem Herzen Title-Opus: WoO 115 Title-Dates: 1792 Title-Type: Song Title-Key: C minor Title-Name: Que le temps me dure Title-Comment: 1st version Title-Opus: WoO 116 Title-Dates: 1793 Title-Type: Song Title-Key: C major Title-Name: Que le temps me dure Title-Comment: 2nd version Title-Opus: WoO 116 Title-Dates: 1793 Title-Type: Song Title-Key: C major Title-Name: Der freie Mann Title-Punct: ; Title-Comment: "Wer ist ein freier Mann?"; with unison chorus Title-Opus: WoO 117 Title-Dates: 1792--1794 Title-RAW: Song "Seufzer eines Ungeliebten"; "Hast du nicht Liebe zugemessen", and Title-Name: Gegenliebe Title-Alternative-Name: Wüsst’ ich, wüsst’ ich Title-Opus: WoO 118 Title-Dates: 1794--1795 Title-Type: Song Title-Key: G major Title-Name: O care selve Title-Punct: , Title-Comment: with unison chorus Title-Opus: WoO 119 Title-Dates: 1794--1795 Title-Type: Song Title-Key: F major Title-Name: Man strebt, die Flamme zu verhehlen Title-Opus: WoO 120 Title-Dates: 1800--1802 Title-Type: Song Title-Key: G major Title-Name: Abschiedsgesang an Wiens Bürger Title-Alternative-Name: Keine Klage soll erschallen Title-Opus: WoO 121 Title-Dates: 1796 Title-Type: Song Title-Key: C major Title-Name: Kriegslied der Osterreicher Title-Punct: ; Title-Comment: "Ein grosses deutches Volk sind wir" (with unison chorus) Title-Opus: WoO 122 Title-Dates: 1797 Title-Type: Song Title-Key: G major Title-Name: Zärtliche Liebe Title-Comment: (Tender Love) Title-Alternative-Name: Ich liebe dich, so wie du mich Title-Opus: WoO 123 Title-Dates: 1795 Title-Type: Song Title-Key: A major Title-Name: La partenza Title-Comment: (Der Abschied) Title-Alternative-Name: Ecco quel fiero istante! Title-Opus: WoO 124 Title-Dates: 1795 Title-Type: Song Title-Key: E flat major Title-Name: La tiranna Title-Punct: , Title-Comment: Canzonetta Title-Alternative-Name: Ah grief to think! Title-Opus: WoO 125 Title-Dates: 1798--1799 Title-Type: Song Title-Key: E major Title-Name: Opferlied Title-Comment: (Sacrificial Song) Title-Alternative-Name: Die Flamme lodert Title-Opus: WoO 126 Title-Dates: 1794--1795 Title-Type: Song Title-Key: C major Title-Name: Neue Liebe, neues Leben Title-Comment: (New love, new life); "Herz, mein Herz, was soll das geben?" (1st setting) Title-Opus: WoO 127 Title-Dates: 1798--1799 Title-Type: Song Title-Key: G major Title-Name: Plaisir d’aimer Title-Name: Plaisir d’aimer besoin d’une âme tendre Title-Opus: WoO 128 Title-Dates: 1798--1799 Title-Type: Song Title-Key: F major Title-Name: Der Wachtelschlag Title-Comment: (The Quail’s Call) Title-Name: Ach, wie schalt’s dorten so lieblich hervor Title-Opus: WoO 129 Title-Dates: 1803 Title-Type: Song Title-Key: C minor Title-Name: Gedenke mein Title-Alternative-Name: Gedenke mein, ich denke dein Title-Opus: WoO 130 Title-Dates: 1804--1820 Title-RAW: Sketch Title-For: Solo Voice(s) & Instrument(s) Title-Key: D minor Title-Name: Erlkönig Title-Comment: "Wer reitet so spat durch Nacht und Wind?"; authorship: unfinished Title-Opus: WoO 131 Title-Dates: 1794--1796 Title-Type: Song Title-Key: E flat major Title-Name: Als die Geliebte sich trennen wollte Title-Alternative-Name: Der Hoffnung letzter Schimmer sinkt dahin Title-Opus: WoO 132 Title-Dates: 1806 Title-Type: Song Title-Key: A flat major Title-Name: In questa tomba oscura Title-Punct: , Title-Comment: Arietta (In this dark Tomb) Title-Opus: WoO 133 Title-Dates: 1806--1807 Title-Type: Song Title-Key: G minor Title-Name: Sehnsucht Title-Punct: ; Title-Comment: "Nur wer die Sehnsucht kennt" (1st setting) Title-Opus: WoO 134-1 Title-Dates: 1807--1808 Title-Type: Song Title-Key: G minor Title-Name: Sehnsucht Title-Punct: ; Title-Comment: "Nur wer die Sehnsucht kennt" (2nd setting) Title-Opus: WoO 134-2 Title-Dates: 1807--1808 Title-Type: Song Title-Key: E flat major Title-Name: Sehnsucht Title-Punct: ; Title-Comment: "Nur wer die Sehnsucht kennt" (3rd setting) Title-Opus: WoO 134-3 Title-Dates: 1807--1808 Title-Type: Song Title-Key: G minor Title-Name: Sehnsucht Title-Punct: ; Title-Comment: "Nur wer die Sehnsucht kennt" (4th setting) Title-Opus: WoO 134-4 Title-Dates: 1807--1808 Title-Type: Song Title-Key: C minor Title-Name: Die laute Klage Title-Alternative-Name: Turteltaube, du klagtest so laut Title-Opus: WoO 135 Title-Dates: 1814--1815 Title-Type: Song Title-Key: D major Title-Name: Andenken Title-Comment: (Memories) Title-Alternative-Name: Ich denke dein Title-Opus: WoO 136 Title-Dates: 1808 Title-Type: Song Title-Key: B flat major Title-Name: Gesang aus der Ferne Title-Comment: (Song from far away); "Als mir noch die Träne" (2nd setting) Title-Opus: WoO 137 Title-Dates: 1809 Title-Type: Song Title-Key: B flat major Title-Name: Der Jüngling in der Fremde Title-Alternative-Name: Der Früling entblühet dem Schoß der Natur Title-Opus: WoO 138 Title-Dates: 1809 Title-Type: Song Title-Key: D major Title-Name: Der Liebende Title-Alternative-Name: Welch ein wunderbares Leben Title-Opus: WoO 139 Title-Dates: 1809 Title-RAW: Song "An die Geliebte"; "O daß ich dir vom stillen Auge" (1st setting) Title-Opus: WoO 140-1 Title-Dates: 1811 Title-RAW: Song "An die Geliebte"; "O daß ich dir vom stillen Auge" (2nd setting) Title-Opus: WoO 140-2 Title-Dates: 1814 Title-Type: Song Title-Key: C major Title-Name: Der Gesang der Nachtigall Title-Alternative-Name: Höre, die Nachtigall singt Title-Opus: WoO 141 Title-Dates: 1813 Title-Type: Song Title-Key: E minor Title-Name: Der Bardengeist Title-Alternative-Name: Dort auf dem hohen Felsen sang Title-Opus: WoO 142 Title-Dates: 1813 Title-Type: Song Title-Key: E flat major Title-Name: Des Kriegers Abschied Title-Alternative-Name: Ich zieh’ ins Feld von Lieb’entbrannt Title-Opus: WoO 143 Title-Dates: 1814 Title-Type: Song Title-Key: E flat major Title-Name: Merkenstein Title-Comment: (1st setting) Title-Opus: WoO 144 Title-Dates: 1814 Title-Type: Song Title-Key: G major Title-Name: Das Geheimnis (Liebe and Wahrheit) Title-Alternative-Name: Wo blüht das Blümchen, das nie verblüht? Title-Opus: WoO 145 Title-Dates: 1815 Title-Type: Song Title-Key: E major Title-Name: Sehnsucht Title-Comment: (Longing) Title-Alternative-Name: Die stille Nacht umdunkelt Title-Opus: WoO 146 Title-Dates: 1816 Title-Type: Song Title-Key: A major Title-Name: Ruf vom Berge Title-Alternative-Name: Wenn ich ein Vöglein wär Title-Opus: WoO 147 Title-Dates: 1816 Title-Type: Song Title-Key: F major Title-Name: So oder so Title-Alternative-Name: Nord oder Süd! Title-Opus: WoO 148 Title-Dates: 1817 Title-Type: Song Title-Key: D major Title-Name: Resignation Title-Alternative-Name: Lisch aus, mein Licht! Title-Opus: WoO 149 Title-Dates: 1817 Title-Type: Song Title-Key: E major Title-Name: Abendlied unter’m gestirnten Himmel Title-Comment: (Evening Song beneath the Starry Sky) Title-Alternative-Name: Wenn die Sonne nieder sinket Title-Opus: WoO 150 Title-Dates: 1820 Title-Type: Song Title-Key: G major Title-Name: Der edle Mensch sei hülfreich und gut Title-Opus: WoO 151 Title-Dates: 1823 Title-RAW: Folksong Setting "The Return to Ulster", "Once again, but how chang’d"; No. 1 of 25 Irish songs Title-Opus: WoO 152-1 Title-Dates: 1810 Title-RAW: Folksong Setting "Sweet Power of Song!"; No. 2 of 25 Irish songs Title-Opus: WoO 152-2 Title-Dates: 1810 Title-RAW: Folksong Setting "Once more I hail thee"; No. 3 of 25 Irish songs Title-Opus: WoO 152-3 Title-Dates: 1810 Title-RAW: Folksong Setting "The morning air plays on my face"; No. 4 of 25 Irish songs Title-Opus: WoO 152-4 Title-Dates: 1810 Title-RAW: Folksong Setting "On the massacre of Glencoe", "O! tell me, harper"; No. 5 of 25 Irish songs Title-Opus: WoO 152-5 Title-Dates: 1810 Title-RAW: Folksong Setting "What shall I do to shew how much I love her?"; No. 6 of 25 Irish songs Title-Opus: WoO 152-6 Title-Dates: 1810 Title-RAW: Folksong Setting "His boat comes on the sunny tide"; No. 7 of 25 Irish songs Title-Opus: WoO 152-7 Title-Dates: 1810 Title-RAW: Folksong Setting "Come draw we round a cheerful ring"; No. 8 of 25 Irish songs Title-Opus: WoO 152-8 Title-Dates: 1810 Title-RAW: Folksong Setting "The Soldier’s Dream", "Our bugles sung truce"; No. 9 of 25 Irish songs Title-Opus: WoO 152-9 Title-Dates: 1810 Title-RAW: Folksong Setting "The Deserter" (The evening previous to his execution), "If sadly thinking and spirits sinking"; No. 10 of 25 Irish songs Title-Opus: WoO 152-10 Title-Dates: 1812 Title-RAW: Folksong Setting "Thou emblem of faith" (Upon returning a ring); No. 11 of 25 Irish songs Title-Opus: WoO 152-11 Title-Dates: 1812 Title-RAW: Folksong Setting "English Bulls, or The Irishman in London", "Och! have you not heard, Pat"; No. 12 of 25 Irish songs Title-Opus: WoO 152-12 Title-Dates: 1810 Title-RAW: Folksong Setting "Musing on the roaring ocean"; No. 13 of 25 Irish songs Title-Opus: WoO 152-13 Title-Dates: 1812 Title-RAW: Folksong Setting "Dermot and Shelah", "O who sits so sadly"; No. 14 of 25 Irish songs Title-Opus: WoO 152-14 Title-Dates: 1810 Title-RAW: Folksong Setting "Let brainspinning swains"; No. 15 of 25 Irish songs Title-Opus: WoO 152-15 Title-Dates: 1810 Title-RAW: Folksong Setting "Hide not thy anguish"; No. 16 of 25 Irish songs Title-Opus: WoO 152-16 Title-Dates: 1810 Title-RAW: Folksong Setting "In vain to this desert my fate I deplore"; No. 17 of 25 Irish songs Title-Opus: WoO 152-17 Title-Dates: 1810 Title-RAW: Folksong Setting "They bid me slight my Dermot dear"; No. 18 of 25 Irish songs Title-Opus: WoO 152-18 Title-Dates: 1810 Title-RAW: Folksong Setting "Wife, Children and Friends", "When the blackletter’d list to the gods"; No. 19 of 25 Irish songs Title-Opus: WoO 152-19 Title-Dates: 1812 Title-RAW: Folksong Setting "Farewell bliss and farewell Nancy"; No. 20 of 25 Irish songs Title-Opus: WoO 152-20 Title-Dates: 1810 Title-RAW: Folksong Setting "Morning a cruel turmoiler is"; No. 21 of 25 Irish songs Title-Opus: WoO 152-21 Title-Dates: 1812 Title-RAW: Folksong Setting "From Garyone, my happy home", air: "Garyone"; No. 22 of 25 Irish songs Title-Opus: WoO 152-22 Title-Dates: 1812 Title-RAW: Folksong Setting "A wand’ring gypsey, Sirs, am I"; No. 23 of 25 Irish songs Title-Opus: WoO 152-23 Title-Dates: 1810 Title-RAW: Folksong Setting "The Traugh Welcome", "Shall a son of O’Donnell be cheerless and cold"; No. 24 of 25 Irish songs Title-Opus: WoO 152-24 Title-Dates: 1812 Title-RAW: Folksong Setting "O harp of Erin", "O harp of Erin thou art now laid low"; air: "I once had a true love"; No. 25 of 25 Irish songs Title-Opus: WoO 152-25 Title-Dates: 1812 Title-RAW: Folksong Setting "When eve’s last rays in twilight die"; No. 1 of 20 Irish songs Title-Opus: WoO 153-1 Title-Dates: 1810 Title-RAW: Folksong Setting "No. riches from his scanty store"; No. 2 of 20 Irish songs Title-Opus: WoO 153-2 Title-Dates: 1810 Title-RAW: Folksong Setting "The British Light Dragoons, or The Plain of Badajos", "’Twas a Marechal of France"; No. 3 of 20 Irish songs Title-Opus: WoO 153-3 Title-Dates: 1810 Title-RAW: Folksong Setting "Since greybeards inform us that youth will decay"; No. 4 of 20 Irish songs Title-Opus: WoO 153-4 Title-Dates: 1810 Title-RAW: Folksong Setting "I dream’d I lay where flow’rs were springing"; No. 5 of 20 Irish songs Title-Opus: WoO 153-5 Title-Dates: 1813 Title-RAW: Folksong Setting "Sad and luckless was the season"; No. 6 of 20 Irish songs Title-Opus: WoO 153-6 Title-Dates: 1815 Title-RAW: Folksong Setting "O soothe me, my lyre"; No. 7 of 20 Irish songs Title-Opus: WoO 153-7 Title-Dates: 1813 Title-RAW: Folksong Setting "Norah of Balamagairy", "Farewell mirth and hilarity"; No. 8 of 20 Irish songs Title-Opus: WoO 153-8 Title-Dates: 1812--1813 Title-RAW: Folksong Setting "The kiss, dear maid, thy lip has left"; No. 9 of 20 Irish songs Title-Opus: WoO 153-9 Title-Dates: 1813 Title-RAW: Folksong Setting "Oh, thou hapless soldier"; No. 10 of 20 Irish songs Title-Opus: WoO 153-10 Title-Dates: 1810 Title-RAW: Folksong Setting "When far from the home"; No. 11 of 20 Irish songs Title-Opus: WoO 153-11 Title-Dates: 1813 Title-RAW: Folksong Setting "I’ll praise the saints with early song"; No. 12 of 20 Irish songs Title-Opus: WoO 153-12 Title-Dates: 1813 Title-RAW: Folksong Setting: ‘"Tis sunshine at last"; No. 13 of 20 Irish songs Title-Opus: WoO 153-13 Title-Dates: 1815 Title-RAW: Folksong Setting "Paddy O’Rafferty"; No. 14 of 20 Irish songs Title-Opus: WoO 153-14 Title-Dates: 1810 Title-RAW: Folksong Setting "‘Tis but in vain, for nothing thrives"; No. 15 of 20 Irish songs Title-Opus: WoO 153-15 Title-Dates: 1813 Title-RAW: Folksong Setting "O might I but my Patrick love!" (English); No. 16 of 20 Irish songs Title-Opus: WoO 153-16 Title-Dates: 1813 Title-RAW: Folksong Setting "Come, Darby dear! easy, be easy"; No. 17 of 20 Irish songs Title-Opus: WoO 153-17 Title-Dates: 1813 Title-RAW: Folksong Setting "No. more, my Mary, I sigh for splendour"; No. 18 of 20 Irish songs Title-Opus: WoO 153-18 Title-Dates: 1813 Title-RAW: Folksong Setting "Judy, lovely, matchless creature"; No. 19 of 20 Irish songs Title-Opus: WoO 153-19 Title-Dates: 1813 Title-RAW: Folksong Setting "Thy ship must sail, my Henry dear"; No. 20 of 20 Irish songs Title-Opus: WoO 153-20 Title-Dates: 1813 Title-RAW: Folksong Setting "The Elfin Fairies", "We fairy elves in secret dells", air: "Planxty Kelly"; No. 1 of 12 Irish songs Title-Opus: WoO 154-1 Title-Dates: 1813 Title-RAW: Folksong Setting "O harp of Erin", "O harp of Erin thou art now laid low", air: "I once had a true love"; No. 2 of 12 Irish songs Title-Opus: WoO 154-2 Title-Dates: 1813 Title-RAW: Folksong Setting "The Farewell Song", "O Erin", air: "The old woman"; No. 3 of 12 Irish songs Title-Opus: WoO 154-3 Title-Dates: 1813 Title-RAW: Folksong Setting "The pulse of a Irishman", air: "St Patrick’s Day"; No. 4 of 12 Irish songs Title-Opus: WoO 154-4 Title-Dates: 1813 Title-RAW: Folksong Setting "O who, my dear Dermot", air: "Crooghan a Venee"; No. 5 of 12 Irish songs Title-Opus: WoO 154-5 Title-Dates: 1813 Title-RAW: Folksong Setting "Put round the bright wine", air: "Chiling O’Guiry"; No. 6 of 12 Irish songs Title-Opus: WoO 154-6 Title-Dates: 1813 Title-RAW: Folksong Setting "From Garyone, my happy home", air: "Garyone"; No. 7 of 12 Irish songs Title-Opus: WoO 154-7 Title-Dates: 1813 Title-RAW: Folksong Setting "Save me from the grave and wise", air: "Nora Creina"; No. 8 of 12 Irish songs Title-Opus: WoO 154-8 Title-Dates: 1813 Title-RAW: Folksong Setting "O would I were but that sweet linnet!", air: "The pretty girl milking the cows"; No. 9 of 12 Irish songs Title-Opus: WoO 154-9 Title-Dates: 1813 Title-RAW: Folksong Setting "The hero may perish", air: "The fox’s sleep"; No. 10 of 12 Irish songs Title-Opus: WoO 154-10 Title-Dates: 1813 Title-RAW: Folksong Setting "The Soldier in a Foreign Land", "The piper who sat on his low mossy seat", air: "The Brown Maid"; No. 11 of 12 Irish songs Title-Opus: WoO 154-11 Title-Dates: 1813 Title-RAW: Folksong Setting "He promis’d me at parting", air: "Killeavy"; No. 12 of 12 Irish songs Title-Opus: WoO 154-12 Title-Dates: 1813 Title-RAW: Folksong Setting "Chase of the Wolf" or "Sion, the Son of Evan", "Hear the shouts of Evan’s son"; No. 1 of 26 Welsh songs Title-Opus: WoO 155-1 Title-Dates: 1810 Title-RAW: Folksong Setting "The Monks of Bangor’s March", "When the heathen trumpet’s clang"; No. 2 of 26 Welsh songs Title-Opus: WoO 155-2 Title-Dates: 1810 Title-RAW: Folksong Setting "The Cottage Maid", "I envy not the splendour fine"; No. 3 of 26 Welsh songs Title-Opus: WoO 155-3 Title-Dates: 1810 Title-RAW: Folksong Setting "Love without Hope", "Her features speak the warmest heart"; No. 4 of 26 Welsh songs Title-Opus: WoO 155-4 Title-Dates: 1810 Title-RAW: Folksong Setting "The Golden Robe", "A golden robe my Love shall wear"; No. 5 of 26 Welsh songs Title-Opus: WoO 155-5 Title-Dates: 1810 Title-RAW: Folksong Setting "The Fair Maid of Mona", "How, my love, could hapless doubts o’ertake thee"; No. 6 of 26 Welsh songs Title-Opus: WoO 155-6 Title-Dates: 1810 Title-RAW: Folksong Setting "O let the night my blushes hide"; No. 7 of 26 Welsh songs Title-Opus: WoO 155-7 Title-Dates: 1810 Title-RAW: Folksong Setting "Farewell, thou noisy town"; No. 8 of 26 Welsh songs Title-Opus: WoO 155-8 Title-Dates: 1810 Title-RAW: Folksong Setting "To the Aeolian Harp", "Harp of the winds"; No. 9 of 26 Welsh songs Title-Opus: WoO 155-9 Title-Dates: 1810 Title-RAW: Folksong Setting "Ned Pugh’s Farewell", "To leave my dear girl, my country, and friends"; No. 10 of 26 Welsh songs Title-Opus: WoO 155-10 Title-Dates: 1810 Title-RAW: Folksong Setting "Merch Megan, or Peggy’s Daughter"1 "In the white cot where Peggy dwells"; No. 11 of 26 Welsh songs Title-Opus: WoO 155-11 Title-Dates: 1810 Title-RAW: Folksong Setting "Waken, lords and ladies gay"; No. 12 of 26 Welsh songs Title-Opus: WoO 155-12 Title-Dates: 1810 Title-RAW: Folksong Setting "Helpless Woman", "How cruel are the parents"; No. 13 of 26 Welsh songs Title-Opus: WoO 155-13 Title-Dates: 1810 Title-RAW: Folksong Setting "The Dream", "Last night worn with anguish"; No. 14 of 26 Welsh songs Title-Opus: WoO 155-14 Title-Dates: 1810 Title-RAW: Folksong Setting "When mortals all to rest retire" (English); No. 15 of 26 Welsh songs Title-Opus: WoO 155-15 Title-Dates: 1813 Title-RAW: Folksong Setting "The Damsels of Cardigan", "Fair Tivy"; No. 16 of 26 Welsh songs Title-Opus: WoO 155-16 Title-Dates: 1810 Title-RAW: Folksong Setting "The Dairy House", "A spreading hawthorn shades the seat"; No. 17 of 26 Welsh songs Title-Opus: WoO 155-17 Title-Dates: 1810 Title-RAW: Folksong Setting "Sweet Richard", "Yes, thou art chang’d since first we met"; No. 18 of 26 Welsh songs Title-Opus: WoO 155-18 Title-Dates: 1810 Title-RAW: Folksong Setting "The Vale of Clwyd", "Think not I’ll leave"; No. 19 of 26 Welsh songs Title-Opus: WoO 155-19 Title-Dates: 1810 Title-RAW: Folksong Setting "To the Blackbird", "Sweet warbler of a strain divine"; No. 20 of 26 Welsh songs Title-Opus: WoO 155-20 Title-Dates: 1813 Title-RAW: Folksong Setting "Cupid’s Kindness", "Dear brother"; No. 21 of 26 Welsh songs Title-Opus: WoO 155-21 Title-Dates: 1810 Title-RAW: Folksong Setting "Constancy", "Tho’ cruel fate should bid us part"; No. 22 of 26 Welsh songs Title-Opus: WoO 155-22 Title-Dates: 1810 Title-RAW: Folksong Setting "The Old Strain", "My pleasant home beside the Dee!"; No. 23 of 26 Welsh songs Title-Opus: WoO 155-23 Title-Dates: 1810 Title-RAW: Folksong Setting "Three Hundred Pounds", "In yonder snug cottage"; No. 24 of 26 Welsh songs Title-Opus: WoO 155-24 Title-Dates: 1810 Title-RAW: Folksong Setting "The Parting Kiss", "Laura, thy sighs must now No. more"; No. 25 of 26 Welsh songs Title-Opus: WoO 155-25 Title-Dates: 1815 Title-RAW: Folksong Setting "Good Night", "Ere yet we slumber seek"; No. 26 of 26 Welsh songs Title-Opus: WoO 155-26 Title-Dates: 1810 Title-RAW: Folksong Setting "The Banner of Buccleuch", "From the brown crest of Newark"; No. 1 of 12 Scottish songs Title-Opus: WoO 156-1 Title-Dates: 1819 Title-RAW: Folksong Setting "Duncan Gray", "Duncan Gray came here to; woo"; No. 2 of 12 Scottish songs Title-Opus: WoO 156-2 Title-Dates: 1818 Title-RAW: Folksong Setting "Up! quit thy bower"; No. 3 of 12 Scottish songs Title-Opus: WoO 156-3 Title-Dates: 1819 Title-RAW: Folksong Setting "Ye shepherds of this pleasant vale"; No. 4 of 12 Scottish songs Title-Opus: WoO 156-4 Title-Dates: 1818 Title-RAW: Folksong Setting "Cease your funning"; No. 5 of 12 Scottish songs Title-Opus: WoO 156-5 Title-Dates: 1817 Title-RAW: Folksong Setting "Highland Harry", "My Harry was a gallant gay"; No. 6 of 12 Scottish songs Title-Opus: WoO 156-6 Title-Dates: 1815 Title-RAW: Folksong Setting "Polly Stewart", "O lovely Polly Stewart"; No. 7 of 12 Scottish songs Title-Opus: WoO 156-7 Title-Dates: 1818 Title-RAW: Folksong Setting "Womankind", "The hero may perish his country to save"; No. 8 of 12 Scottish songs Title-Opus: WoO 156-8 Title-Dates: 1818 Title-RAW: Folksong Setting "Lochnagar", "Away ye gay landscapes"; No. 9 of 12 Scottish songs Title-Opus: WoO 156-9 Title-Dates: 1818 Title-RAW: Folksong Setting "Glencoe", "O tell us, Harper"; No. 10 of 12 Scottish songs Title-Opus: WoO 156-10 Title-Dates: 1819 Title-RAW: Folksong Setting "Auld Lang Syne" "Should auld acquaintance be forgot"; No. 11 of 12 Scottish songs Title-Opus: WoO 156-11 Title-Dates: 1818 Title-RAW: Folksong Setting "The Quaker’s Wife", "Dark was the morn and black the sea"; No. 12 of 12 Scottish songs Title-Opus: WoO 156-12 Title-Dates: 1818 Title-RAW: Folksong Setting "God Save the King", "God save our Lord the King" (English); No. 1 of 12 assorted folk songs Title-Opus: WoO 157-1 Title-Dates: 1817 Title-RAW: Folksong Setting "The Soldier", "Then, Soldier! come" (Irish); No. 2 of 12 assorted folk songs Title-Opus: WoO 157-2 Title-Dates: 1815 Title-RAW: Folksong Setting "Charlie is my darling" (Scottish); No. 3 of 12 assorted folk songs Title-Opus: WoO 157-3 Title-Dates: 1819 Title-RAW: Folksong Setting "O sanctissima" (Sicilian); No. 4 of 12 assorted folk songs Title-Opus: WoO 157-4 Title-Dates: 1817 Title-RAW: Folksong Setting "The Miller of Dee", "There was a jolly miller once" (English); No. 5 of 12 assorted folk songs Title-Opus: WoO 157-5 Title-Dates: 1819 Title-RAW: Folksong Setting "A health to the brave" (Irish); No. 6 of 12 assorted folk songs Title-Opus: WoO 157-6 Title-Dates: 1815 Title-RAW: Folksong Setting "Robin Adair", "Since all thy vows, false maid" (Irish); No. 7 of 12 assorted folk songs Title-Opus: WoO 157-7 Title-Dates: 1815 Title-RAW: Folksong Setting "By the side of the Shannon" (Irish); No. 8 of 12 assorted folk songs Title-Opus: WoO 157-8 Title-Dates: 1815 Title-RAW: Folksong Setting "Highlander’s Lament", "My Harry was a gallant gay" (Scottish); No. 9 of 12 assorted folk songs Title-Opus: WoO 157-9 Title-Dates: 1820 Title-RAW: Folksong Setting "Sir Johnnie Cope" (Scottish); No. 10 of 12 assorted folk songs Title-Opus: WoO 157-10 Title-Dates: 1817 Title-RAW: Folksong Setting "The Wandering Minstrel", "I am bow’d down" (Irish); No. 11 of 12 assorted folk songs Title-Opus: WoO 157-11 Title-Dates: 1815 Title-RAW: Folksong Setting "La gondoletta", "La biondina in gondoletta" (Venetian); No. 12 of 12 assorted folk songs Title-Opus: WoO 157-12 Title-Dates: 1816 Title-RAW: Folksong Setting "Ridder Stigs Runer" (Danish); WoO 158a, No. 1 of 23 continental folk songs Title-Opus: WoO 158-1 Title-Dates: 1813,1817 Title-RAW: Folksong Setting "Horch auf, mein Liebchen" (German); WoO 158a, No. 2 of 23 continental folk songs Title-Opus: WoO 158-2 Title-Dates: 1813,1816,1817 Title-RAW: Folksong Setting "Wegen meiner blieb d’Fräula" (German); WoO 158a, No. 3 of 23 continental folk songs Title-Opus: WoO 158-3 Title-Dates: 1816,1817,1820 Title-RAW: Folksong Setting "Wann i in der Früh aufsteh" (Tyrolean); WoO 158a, No. 4 of 23 continental folk songs Title-Opus: WoO 158-4 Title-Dates: 1816,1817,1820 Title-RAW: Folksong Setting "Teppichkrämer-Lied"; "I bin a Tyroler Bua" (Tyrolean); WoO 158a, No. 5 of 23 continental folk songs Title-Opus: WoO 158-5 Title-Dates: 1815,1816,1818 Title-RAW: Folksong Setting "A Madel, ja a Madel" (Tyrolean); WoO 158a, No. 6 of 23 continental folk songs Title-Opus: WoO 158-6 Title-Dates: 1810,1815,1816 Title-RAW: Folksong Setting "Wer solche Buema afipackt" (Tyrolean); WoO 158a, No. 7 of 23 continental folk songs Title-Opus: WoO 158-7 Title-Dates: 1810,1817 Title-RAW: Folksong Setting "Ih mag di nit nehma, du töppeter Hecht" (Tyrolean); WoO 158a, No. 8 of 23 continental folk songs Title-Opus: WoO 158-8 Title-Dates: 1817 Title-RAW: Folksong Setting "Oj, oj upilem sie w karczmie" (Polish); WoO 158a, No. 9 of 23 continental folk songs Title-Opus: WoO 158-9 Title-Dates: 1816 Title-RAW: Folksong Setting "Poszla baba po popiol" (Polish); WoO 158a, No. 10 of 23 continental folk songs Title-Opus: WoO 158-10 Title-Dates: 1816 Title-RAW: Folksong Setting "Yo No. quiero embarcarme" (Iberian); WoO 158a, No. 11 of 23 continental folk songs Title-Opus: WoO 158-11 Title-Dates: 1816 Title-RAW: Folksong Setting "Seus lindos olhos" (Portuguese); WoO 158a, No. 12 of 23 continental folk songs Title-Opus: WoO 158-12 Title-Dates: 1816 Title-RAW: Folksong Setting "Vo lesocke komarockov mnogo urodilos" (Im Walde sind viele Mücklein geboren) (Russian); WoO 158a, No. 13 of 23 continental folk songs Title-Opus: WoO 158-13 Title-Dates: 1816 Title-RAW: Folksong Setting "Akh, recen’ki, recen’ki" (Ach Bächlein, Bächlein, kühle Wasser) (Russian); WoO 158a, No. 14 of 23 continental folk songs Title-Opus: WoO 158-14 Title-Dates: 1816 Title-RAW: Folksong Setting "Kak Posli nasi podruzki" (As They Went) (Unsere Mädchen gingen in den Wald) (Russian); WoO 158a, No. 15 of 23 continental folk songs Title-Opus: WoO 158-15 Title-Dates: 1816 Title-RAW: Folksong Setting "Schöne Minka, ich muß scheiden" (Ukrainian-Cossack); WoO 158a, No. 16 of 23 continental folk songs Title-Opus: WoO 158-16 Title-Dates: 1816 Title-RAW: Folksong Setting "Lilla Carl", Vaggvisa (Swedish lullaby); WoO 158a, No. 17 of 23 continental folk songs Title-Opus: WoO 158-17 Title-Dates: 1817 Title-RAW: Folksong Setting "An ä Bergli bin i gesässe" (Swiss); WoO 158a, No. 18 of 23 continental folk songs Title-Opus: WoO 158-18 Title-Dates: 1816 Title-RAW: Folksong Setting "Una paloma blanca" (Spanish Bolero a Solo); WoO 158a, No. 19 of 23 continental folk songs Title-Opus: WoO 158-19 Title-Dates: 1816 Title-RAW: Folksong Setting "Como la mariposa soy" (Spanish Bolero a due); WoO 158a, No. 20 of 23 continental folk songs Title-Opus: WoO 158-20 Title-Dates: 1816 Title-RAW: Folksong Setting "Tiranilla Española" (Spanish); "La tiranna se embarca"; WoO 158a, No. 21 of 23 continental folk songs Title-Opus: WoO 158-21 Title-Dates: 1816 Title-RAW: Folksong Setting "Édes kinos emlékezet", "Magyar Szüretötö Enek" (Hungarian grape harvest song); WoO 158a, No. 22 of 23 continental folk songs Title-Opus: WoO 158-22 Title-Dates: 1817 Title-RAW: Folksong Setting "Da brava, Catina" (Venetian); WoO 158a, No. 23 of 23 continental folk songs Title-Opus: WoO 158-23 Title-Dates: 1816 Title-RAW: Folksong Setting Title-Key: A major Title-Name: Adieu, my lov’d harp Title-Comment: (Irish); WoO 158b, No. 1 of 7 British folk songs Title-Opus: WoO 158-1 Title-Dates: 1813,1817 Title-RAW: Folksong Setting Title-Key: E flat major Title-Name: Castle O’Neill Title-Comment: (Irish), without words; WoO 158b, No. 2 of 7 British folk songs Title-Opus: WoO 158-2 Title-Dates: 1813,1816,1817 Title-RAW: Folksong Setting "Oh ono chri!" (Scottish) "O was not I a weary wight"; WoO 158b, No. 3 of 7 British folk songs Title-Opus: WoO 158-3 Title-Dates: 1816,1817,1820 Title-RAW: Folksong Setting "Red gleams the sun on yon hill tap" (Scottish); WoO 158b, No. 4 of 7 British folk songs Title-Opus: WoO 158-4 Title-Dates: 1816,1817,1820 Title-RAW: Folksong Setting Title-Key: G major Title-Name: Erin! O Erin! Title-Comment: (Scottish-Irish) "Like the bride lamp that lay"; WoO 158b, No. 5 of 7 British folk songs Title-Opus: WoO 158-5 Title-Dates: 1815,1816,1818 Title-RAW: Folksong Setting "O Mary ye’s be clad in silk" (Scottish); WoO 158b, No. 6 of 7 British folk songs Title-Opus: WoO 158-6 Title-Dates: 1810,1815,1816 Title-RAW: Folksong Setting "Lament for Owen Roe O’Neill" (Irish) without words; WoO 158b, No. 7 of 7 British folk songs Title-Opus: WoO 158-7 Title-Dates: 1810,1817 Title-RAW: Folksong Setting Title-Key: E minor Title-Name: When my Hero in court appears Title-Punct: ; Title-Comment: WoO 158c, No. 1 of 6 assorted folk songs Title-Opus: WoO 158-1 Title-Dates: 1813,1817 Title-RAW: Folksong Setting Title-Key: B major Title-Name: Air de Colin Title-Comment: from Rousseau’s "Le Devin du Village"; "Non, non, Colette n’est point trompeuse"; WoO 158c, No. 2 of 6 assorted folk songs Title-Opus: WoO 158-2 Title-Dates: 1813,1816,1817 Title-RAW: Folksong Setting Title-Key: B major Title-Name: Mark yonder pomp of costly fashion Title-Comment: (Scottish); WoO 158c, No. 3 of 6 assorted folk songs Title-Opus: WoO 158-3 Title-Dates: 1816,1817,1820 Title-RAW: Folksong Setting Title-Key: A major Title-Name: Bonnie wee thing Title-Comment: (Scottish) for 3 voices and piano; WoO 158c, No. 4 of 6 assorted folk songs Title-Opus: WoO 158-4 Title-Dates: 1816,1817,1820 Title-RAW: Folksong Setting Title-Key: B flat major Title-Name: From thee, Eliza, I must go Title-Comment: (Scottish) for 3 voices and piano trio; WoO 158c, No. 5 of 6 assorted folk songs Title-Opus: WoO 158-5 Title-Dates: 1815,1816,1818 Title-RAW: Folksong Setting Title-Key: E minor Title-Punct: : Title-Comment: Untitled (Scottish) without words; WoO 158c, No. 6 of 6 assorted folk songs Title-Opus: WoO 158-6 Title-Dates: 1810,1815,1816 Title-RAW: Folksong Setting Title-Key: F major Title-Name: Air Français Title-Comment: (French) Title-Opus: WoO 158d Title-Opus: WoO 158 Title-Type: 3-part Canon Title-Key: F major Title-Name: Im Arm der Liebe ruht sich’s wohl Title-Opus: WoO 159 Title-Dates: 1795 Title-Type: 3-part Canon Title-Key: G major Title-Punct: : Title-Comment: for Three Unison Voices (Allegretto) without Text Title-Opus: WoO 160-1 Title-Dates: 1795 Title-RAW: 4-part Canon: for Four Unison Voices (Moderato) without Text Title-Opus: WoO 160-2 Title-Dates: 1795 Title-Type: 3-part Canon Title-Key: C major Title-Name: Ewig dein! Title-Comment: (Forever thine!) Title-Opus: WoO 161 Title-Dates: 1811 Title-Type: 4-part Canon Title-Key: B flat major Title-Name: Ta ta ta, lieber Mälzel Title-Punct: ; Title-Comment: authorship: spurious Title-Opus: WoO 162 Title-Dates: 1812 Title-Type: 3-part Canon Title-Key: F minor Title-Name: Kurz ist der Schmerz, und ewig ist die Freude Title-Comment: (Suffering is transitory, but joy is eternal) (1st setting) Title-Opus: WoO 163 Title-Dates: 1813 Title-Type: 3-part Canon Title-Key: C major Title-Name: Freundschaft ist die Quelle wahrer Glückseligkeit Title-Comment: (Friendship is the source of true bliss) Title-Opus: WoO 164 Title-Dates: 1814 Title-RAW: 4-part Canon "Glück zum neuen Jahr!" (1st setting) Title-Opus: WoO 165 Title-Dates: 1815 Title-Type: 3-part Canon Title-Key: F major Title-Name: Kurz ist der Schmerz, und ewig ist die Freude Title-Comment: (Suffering is transitory, but joy is eternal) (2nd setting) Title-Opus: WoO 166 Title-Dates: 1815 Title-Type: 3-part Canon Title-Key: C major Title-Name: Brauchle, Linke Title-Opus: WoO 167 Title-Dates: 1815 Title-Type: 3-part Riddle Canon Title-Key: F major Title-Name: Das Schweigen Title-Alternative-Name: Lerne Schweigen, o Freund Title-Opus: WoO 168-1 Title-Dates: 1816 Title-Type: 3-part Canon Title-Key: F major Title-Name: Das Reden Title-Alternative-Name: Rede, wenn’s um einen Freund dir gilt Title-Opus: WoO 168-2 Title-Dates: 1816 Title-Type: 2-part Riddle Canon Title-Key: C major Title-Name: Ich küße Sie, drücke Sie an mein Herz Title-Opus: WoO 169 Title-Dates: 1816 Title-Type: 2-part Canon Title-Key: C major Title-Name: Ars longa, vita brevis Title-Comment: (first version) Title-Opus: WoO 170 Title-Dates: 1816 Title-Type: 4-part Canon Title-Key: G major Title-Name: Glück fehl’ dir vor allem! Title-Punct: ; Title-Comment: authorship: spurious Title-Opus: WoO 171 Title-Dates: 1817 Title-Type: 3-part Canon Title-Key: E flat major Title-Name: Ich bitt’ dich, schreib’ mir die Es-Scala auf Title-Opus: WoO 172 Title-Dates: 1818 Title-Type: 2-part Riddle Canon Title-Key: B flat major Title-Name: Hol’ euch der Teufel! B’hüt euch Gott! Title-Opus: WoO 173 Title-Dates: 1819 Title-Type: 4-part Canon Title-Key: B flat major Title-Name: Glaube und hoffe Title-Opus: WoO 174 Title-Dates: 1819 Title-Type: 4-part Riddle Canon Title-Name: Sankt Petrus war ein Fels/ Bernardus war ein Sankt Title-Opus: WoO 175 Title-Dates: 1820 Title-Type: 3-part Canon Title-Key: F major Title-Name: Glück zum neuen Jahr! Title-Comment: (2nd setting) Title-Opus: WoO 176 Title-Dates: 1819 Title-Type: Canon Title-Key: E major Title-Name: Bester Magistrat, Ihr friert Title-Punct: ; Title-Comment: canon for 2 male voices & 2 double-basses Title-Opus: WoO 177 Title-Dates: 1820 Title-Type: 3-part Canon Title-Key: B flat major Title-Name: Signor Abate Title-Opus: WoO 178 Title-Dates: 1820 Title-RAW: Intro & 4-part Canon Title-Key: C major Title-Name: Seiner Kaiserlichen Hoheit...alles Gute, alles Schöne Title-Opus: WoO 179 Title-Dates: 1819 Title-Type: 2-part Canon Title-Key: C major Title-Name: Auf einen, welcher Hoffmann geheißen Title-Alternative-Name: Hoffmann, sei ja kein Hofmann Title-Opus: WoO 180 Title-Dates: 1820 Title-Type: 4-part Canon Title-Key: C major Title-Name: Gedenket heute an Baden Title-Opus: WoO 181-1 Title-Dates: 1820 Title-Type: 4-part Canon Title-Key: C major Title-Name: Gehabt euch wohl Title-Opus: WoO 181-2 Title-Dates: 1820 Title-Type: 3-part Canon Title-Key: C major Title-Name: Tugend ist kein leerer Name Title-Opus: WoO 181-3 Title-Dates: 1820 Title-Type: 3-part Canon Title-Key: D minor Title-Name: O Tobias! Title-Opus: WoO 182 Title-Dates: 1821 Title-Type: 4-part Canon Title-Key: F major Title-Name: Bester Herr Graf, Sie sind ein Schaf Title-Opus: WoO 183 Title-Dates: 1823 Title-Type: 5-part Canon Title-Key: G major Title-Name: Falstafferel, lass dich sehen! Title-Opus: WoO 184 Title-Dates: 1823 Title-Type: 6-part Canon Title-Name: Edel sei der Mensch, hülfreich und gut Title-Opus: WoO 185 Title-Dates: 1823 Title-Type: 2-part Canon Title-Key: E flat major Title-Name: Te solo adoro Title-Opus: WoO 186 Title-Dates: 1824 Title-Type: 4-part Canon Title-Key: F major Title-Name: Auf einen, welcher Schwenke geheißen Title-Alternative-Name: Schwenke dich ohne Schwänke Title-Opus: WoO 187 Title-Dates: 1824 Title-Type: 2-part Riddle Canon Title-Key: B flat major Title-Name: Gott ist eine feste Burg Title-Opus: WoO 188 Title-Dates: 1825 Title-Type: 4-part Canon Title-Key: C major Title-Name: Doktor, sperrt das Tor dem Tod Title-Opus: WoO 189 Title-Dates: 1825 Title-Type: 2-part Canon Title-Key: C major Title-Name: Ich war hier, Doktor, ich war hier Title-Opus: WoO 190 Title-Dates: 1825 Title-Type: 3-part Canon Title-Key: B flat major Title-Name: Kühl, nicht lau Title-Opus: WoO 191 Title-Dates: 1825 Title-Type: 4-part Riddle Canon Title-Key: F major Title-Name: Ars longa, vita brevis Title-Comment: (second version) Title-Opus: WoO 192 Title-Dates: 1825 Title-Type: Riddle Canon Title-Key: C major Title-Name: Ars longa, vita brevis Title-Comment: (third version) Title-Opus: WoO 193 Title-Dates: 1825 Title-Type: Riddle Canon Title-Key: F major Title-Name: Si non per portas, per muros Title-Opus: WoO 194 Title-Dates: 1825 Title-Type: 2-part Canon Title-Key: A minor Title-Name: Freu dich des Lebens Title-Opus: WoO 195 Title-Dates: 1825 Title-Type: 4-part Riddle Canon Title-Key: F major Title-Name: Es muß sein! Title-Opus: WoO 196 Title-Dates: 1826 Title-Type: 5-part Canon Title-Key: C major Title-Name: Das ist das Werk, sorgt um das Geld! Title-Opus: WoO 197 Title-Dates: 1826 Title-Type: 2-part Riddle Canon Title-Key: C major Title-Name: Wir irren allesamt Title-Opus: WoO 198 Title-Dates: 1826 Title-Type: Joke Title-Key: D major Title-Name: Ich bin der Herr von zu Title-Comment: (I am the man for you) Title-Opus: WoO 199 Title-Dates: 1814 Title-Type: Piece Title-For: Piano Title-Key: G major Title-Name: O Hoffnung! Title-Opus: WoO 200 Title-Dates: 1818 Title-Type: Joke Title-Key: C major Title-Name: Ich bin bereit! Amen Title-Comment: (I am ready), beginning of a double fugue Title-Opus: WoO 201 Title-Dates: 1818 Title-Type: Riddle Canon Title-Key: F major Title-Name: Das Schöne zum Guten! Title-Punct: , Title-Comment: musical motto (1st version) Title-Opus: WoO 202 Title-Dates: 1823 Title-Type: Riddle Canon Title-Key: A major Title-Name: Das Schöne zu dem Guten! Title-Punct: , Title-Comment: musical motto (2nd version) Title-Opus: WoO 203 Title-Dates: 1825 Title-Type: Joke Title-Key: D minor Title-Name: Holz, Holz, geigt die Quartette so Title-Comment: (Holz, Holz, you play the quartets as if you were chopping cabbage) Title-Opus: WoO 204 Title-Dates: 1825 Title-Type: Musical greetings Title-For: Solo Voice(s) Title-Key: C major Title-Name: Baron, Baron, Baron Title-Punct: , Title-Comment: No. 1 of 10 musical greetings Title-Opus: WoO 205-1 Title-Dates: 1798 Title-Type: Musical greetings Title-For: Solo Voice(s) Title-Key: C major Title-Name: Allein, allein, allein, jedoch. Silentium!! Title-Punct: , Title-Comment: No. 2 of 10 musical greetings Title-Opus: WoO 205-2 Title-Dates: 1814 Title-Type: Musical greetings Title-For: Solo Voice(s) Title-Key: A minor Title-Name: O Adjutant Title-Punct: , Title-Comment: No. 3 of 10 musical greetings Title-Opus: WoO 205-3 Title-Dates: 1817 Title-Type: Musical greetings Title-For: Solo Voice(s) Title-Key: C major Title-Name: Wo? Wo? Title-Punct: , Title-Comment: No. 4 of 10 musical greetings Title-Opus: WoO 205-4 Title-Dates: 1817 Title-Type: Musical greetings Title-For: Solo Voice(s) Title-Key: D major Title-Name: Erfüllung, Erfüllung Title-Punct: , Title-Comment: No. 5 of 10 musical greetings Title-Opus: WoO 205-5 Title-Dates: 1819 Title-Type: Musical greetings Title-For: Solo Voice(s) Title-Key: F major Title-Name: Scheut euch nicht Title-Punct: , Title-Comment: No. 6 of 10 musical greetings Title-Opus: WoO 205-6 Title-Dates: 1822 Title-Type: Musical greetings Title-For: Solo Voice(s) Title-Key: E major Title-Name: Tobias! Paternostergäßler. Tobias! Paternostergäßlerischer, Bierhäuslerischer musikalischer Philister! Title-Punct: , Title-Comment: No. 7 of 10 musical greetings Title-Opus: WoO 205-7 Title-Dates: 1824 Title-Type: Musical greetings Title-For: Solo Voice(s) Title-Key: D major Title-Name: Tobias Tobias Title-Punct: , Title-Comment: No. 8 of 10 musical greetings Title-Opus: WoO 205-8 Title-Dates: 1825 Title-Type: Musical greetings Title-For: Solo Voice(s) Title-Key: C major Title-Name: Bester To----(bias) Title-Punct: , Title-Comment: No. 9 of 10 musical greetings Title-Opus: WoO 205-9 Title-Dates: 1826 Title-Type: Musical greetings Title-For: Solo Voice(s) Title-Key: C major Title-Name: Erster aller Tobiasse Title-Punct: , Title-Comment: No. 10 of 10 musical greetings Title-Opus: WoO 205-10 Title-Dates: 1826 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/Normalize/Text/Music_Fields/Music_Fields-rus.lst�����������������������������������0000700�0000000�0000000�00000015162�11216655640�021341� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# charset = cp1251 ### Aliases should be at the front; correct => misspell1, misspell2... # alias ׸ðíûé => ×åðíûé # alias Êóøåë¸â-Áåçáîðîäêî => Êóøåëåâ-Áåçáîðîäêî # alias Êóøåë¸â-Áåçáîðîäêî => Êóøåëåâ-Áåçáîðîäêî # alias Ãóìèë¸â => Ãóìèëåâ Àëåêñàíäð Äîëüñêèé Àëåêñàíäð Ìèðçàÿí Àëåêñàíäð Ñóõàíîâ Àëåêñàíäð Âåðòèíñêèé (1889-03-21--1957-05-21) Àëåêñàíäð Ãîðîäíèöêèé Áóëàò Îêóäæàâà (1924-5-9--1997-6-12) Åëåíà Êàìáóðîâà Ìèõàèë Ùåðáàêîâ Íîâåëëà Ìàòâååâà Âåðà Ìàòâååâà (1945-10-23--1976-8-11) Àäà (Àðèàäíà Àäàìîâíà) ßêóøåâà Âëàäèìèð Ñåðãååâè÷ Äàøêåâè÷ ## First prefered: Ñåðãåé è Òàòüÿíà Íèêèòèíû ##Òàòüÿíà è Ñåðãåé Íèêèòèíû Ñåðãåé Íèêèòèí Âàëåðèé Àãàôîíîâ (1941-10-3-1984-9-5) Âèêòîð Áåðêîâñêèé (1932-07-13--2005-07-22) Âëàäèìèð Âûñîöêèé (1938-1-25--1980-7-25) Þëèé Êèì ôîëüêëîð ÌÃÓ Äìèòðèé Ñóõàðåâ Àðñåíèé Òàðêîâñêèé (1907-6-24--1989-5-27) Àãíèÿ Áàðòî (1906-2-17--1981-4-1) Âàäèì Âëàäèìèðîâè÷ Åãîðîâ Èâàí Ñåìåíîâè÷ Êèóðó Ãðèãîðèé Ïîæåíÿí Ëàðèñà Êðèòñêàÿ Àíäðåé Âîçíåñåíñêèé Àëåêñàíäð Êóøíåð Áîðèñ Ëåîíèäîâè÷ Ïàñòåðíàê (1890-2-10--1960-5-30) Îñèï Ýìèëüåâè÷ Ìàíäåëüøòàì (1891-1-15--1938-12-27) Âëàäèìèð Âëàäèìèðîâè÷ Ìàÿêîâñêèé (1893-07-19--1930-4-14) Àíäðåé Ìèðîíîâ (1941-3-8--1987-8-16) Àíäðåé Ïåòðîâ Àëåêñàíäð Áëîê (1880-11-28--1921-8-7) Áîðèñ Âëàäèìèðîâè÷ Çàõîäåð (1918-9-9-2000-11-7) Áîðèñ Íàòàíîâè÷ Ñòðóãàöêèé Áîðèñ Ñëóöêèé (1919-5-7--1986-2-22) Þðèé Âèçáîð (1934-6-20--1984-9-17) Þðèé Äàâûäîâè÷ Ëåâèòàíñêèé (1922-1-21--1996-1-24) Àäà ßêóøåâà Àëåêñàíäð Ãàëè÷ (1918-10-19--1977-12-15) Àëåêñàíäð Äóëîâ Âåðîíèêà Òóøíîâà (1915-1965) Àíäðåé Ìàêàðåâè÷ Áîðèñ Ãðåáåíùèêîâ Àëåêñàíäð Êàðïîâ Àíäðåé Êîðô Äîìèíî Èãîðü Áåëûé Êèðèëë Âîëîøèí Ëåîíèä Ñåðãååâ Íàòàëüÿ Ïðèåçæåâà Îëåã Ìèòÿåâ Ñêàé Ñâåòëàíà Âåòðîâà Òàíÿ Êîðîëåâà Òèìóð Øàîâ Òðè Ñó÷êà Âàäèì è Âàëåðèé Ìèùóêè Âåðîíèêà Äîëèíà Âëàäèìèð Ëàíöáåðã ## This causes infinite loop of expansion Èâàùåíêî => Âàñèëüåâ è Èâàùåíêî => Èâàùåíêî ## Âàñèëüåâ è Èâàùåíêî Ìèõàèë Ñâåòëîâ (1903-6-17--1964-7-28) Ýäóàðä Áàãðèöêèé (1895-11-4--1939-2-16) Âèêòîð Ñîñíîðà Íèêîëàé Çàáîëîöêèé (1903-5-7--1958-10-14) Ãåîðãèé Èâàíîâ (1894-11-10--1958-8-26) Äàíèèë Õàðìñ (1905-12-30--1942-2-2) Äîí Àìèíàäî (1888-5-7--1957-11-14) Åâãåíèé Åâòóøåíêî Åâãåíèé Êëÿ÷êèí (1934-3-23--1994-7-30) Èîñèô Àëåêñàíäðîâè÷ Áðîäñêèé (1940-5-24--1996-1-28) Îëåã ×óõîíöåâ Ïàâåë Êîãàí (1918-7-7--1942) Ïîëü Âåðëåí (1844-3-30--1896-1-8) Þðèé Àäåëóíã (1945-4-3--1993-1-6) Äàâèä Ñàìóèëîâè÷ Ñàìîéëîâ (1920-6-1--1990-2-23) Ãåíðèõ Âåíèàìèíîâè÷ Ñàïãèð (1928-11-20--1999-10-7) Îâñåé Äðèç (1908-5-16--1971-2-14) Èâàí Êðûëîâ (1769-2-13--1844-11-21) Ñàìóèë ßêîâëåâè÷ Ìàðøàê (1887-11-3--1964-7-4) Êîíñòàíòèí Áèáë (1898-2-26--1951-11-12) Ìàðèíà Èâàíîâíà Öâåòàåâà (1992-10-8--1941-8-31) Ñàøà ׸ðíûé (1880-10-13--1932-7-5) Âåíèàìèí Áîðèñîâè÷ Ñìåõîâ Ìàêñèìèëèàí Àëåêñàíäðîâè÷ Âîëîøèí (1877-5-28--1932-8-11) Íèêîëàé Ñòåïàíîâè÷ Ãóìèë¸â (1886-4-15--1921-8) Ãðèãîðèé Àëåêñàíäðîâè÷ Êóøåë¸â-Áåçáîðîäêî (1832-2-1--1870-5-13), ϸòð Àíäðååâè÷ Âÿçåìñêèé (1792-7-23--1878-11-22) Ìèõàèë Ëüâîâè÷ Ìàòóñîâñêèé (1915-7-23--1990-7-16) Àëåêñàíäð Àëåêñàíäðîâè÷ Àëÿáüåâ (1787-8-15--1851-3-6) Áîðèñ Àëåêñååâè÷ Ïðîçîðîâñêèé (1891-6-30--1937) ## Maurice Car^eme Ìîðèñ Êàðåì (1899-5-12--1978-1-13) ## dates not checked Ìèõàèë Êóäèìîâ Áîðèñ Íîñèê Âàäèì Åãîðîâ Òèì Ñîáàêèí Þðèé Êóçíåöîâ Àãíåøêà Îñååöêàÿ À. Í. Îñòðîâñêèé Àíòîí Ïàâëîâè÷ ×åõîâ (1860-1-29--1904-7-15) Àëåêñàíäð Ñåðãååâè÷ Ãðèáîåäîâ (1790(5?)-1-15-1829-2-11) Àëåêñàíäð Ñåðãååâè÷ Ïóøêèí (1799-6-6-1837-2-10) Àíäðå Ìîðóà (1885-1967) Àðêàäèé Àâåð÷åíêî (1881-3-18--1925-3-18) Âàñèëü Áûêîâ (1924-6-19--2003-6-22) Âëàäèìèð Àëåêñååâè÷ Ãèëÿðîâñêèé (1853-12-8---1935-10-1) Äåíèñ Èâàíîâè÷ Ôîíâèçèí (1744(5?)-4-3--1792) Äæåê Ëîíäîí (1876-1-12--1916-11-22) Èâàí Àëåêñååâè÷ Áóíèí (1870-10-22--1953-11-8) Èëüÿ Èëüô (1897-10-15--1937-4-12) Åâãåíèé Ïåòðîâ (1903-12-13--1942-7) Èâàí Ñåðãååâè÷ Òóðãåíåâ (1818-10-28--1883-11-22) Èñààê Áàáåëü (1894-7-13-1940-1-27) Ëåâ Íèêîëàåâè÷ Òîëñòîé (1828-9-9--1910-11-20) Ìèõàèë Àôàíàñüåâè÷ Áóëãàêîâ (1891-5-15--1940-3-10) Ìèõàèë Þðüåâè÷ Ëåðìîíòîâ (1814-10-15--1841-7-27) Íèêîëàé Âàñèëüåâè÷ Ãîãîëü (1809-4-1--1852-3-4) Íèêîëàé Ñåì¸íîâè÷ Ëåñêîâ (1831-2-16--1895-3-5) Î. Ãåíðè (1862-7-11--1910-6-5) Îñêàð Óàéëüä (1984-10-16--1900-11-13) Ô¸äîð Ìèõàéëîâè÷ Äîñòîåâñêèé (1821-11-11--1881-2-9) Ãàíñ Õðèñòèàí Àíäåðñåí (1805-4-2--1875-8-4) ßðîñëàâ Ãàøåê (1883-4-30--1923-3-3) Äæîçåô Ðåäüÿðä Êèïëèíã (1865-12-30--1936-1-18) Þðèé Àäåëóíã (1945-4-3--1993-1-6) Àëåêñåé Íèêîëàåâè÷ Àïóõòèí (1840(1?)-11-27--1893-8-29) Àôàíàñèé Àôàíàñüåâè÷ Ôåò (1820-12-5--1892-12-3) Àëåêñåé Êîíñòàíòèíîâè÷ Òîëñòîé (1817-9-5--1875-10-10) Âëàäèìèð Âëàäèìèðîâè÷ Íàáîêîâ (1899-4-24--1977-7-2) Ðîáåðò Èâàíîâè÷ Ðîæäåñòâåíñêèé (1932-6-20--1994-3-20) Âëàäèìèð Òóðèÿíñêèé Âàäèì Ñåðãååâè÷ Øåôíåð (1915-2002) Èííîêåíòèé Ô¸äîðîâè÷ Àííåíñêèé (1855-9-1--1909-12-11) Èëüÿ Ëüâîâè÷ Ñåëüâèíñêèé (1899-10-24--1968-3-22) Èñààê Èîñèôîâè÷ Øâàðö Èëüÿ Ãðèãîðüåâè÷ Ýðåíáóðã (1891-1-27--1967-8-31) Ìèõàèë Ñåðãååâè÷ Áîÿðñêèé Ìàêñèìèëèàí Àëåêñàíäðîâè÷ Âîëîøèí (1877-5-28-1932-8-11) Ìèêàýë Òàðèâåðäèåâ (1931-8-15--1996-6-24) Íèêîëàé Ìèõàéëîâè÷ Ðóáöîâ (1936-1-3--1971-1-19) Ôåäîð Èâàíîâè÷ Òþò÷åâ (1803-12-5--1873-7-27) Íèêîëîç Ìåëèòîíîâè÷ Áàðàòàøâèëè (1817-12-27--1845-10-21) Âåðà Èëüèíè÷íà Ìàòâååâà (1945-10-23--1976-8-11) Ãåííàäèé Ô¸äîðîâè÷ Øïàëèêîâ (1937-09-06--1974-11-01) Àëåêñàíäð Ìîèñååâè÷ Âîëîäèí (1919-2-10--2001-12-17) Þðèé Àáðàìîâè÷ Ëåâèòèí (1912-12-18--1993-8-2) Ñåðãåé Àëåêñàíäðîâè÷ Åñåíèí (1895-10-3--1925-12-28) ## Â. Áåðåñòîâ ## Â. Çîëîòóõèí ## Â. Ñìåõîâ Îëåã Àíîôðèåâ Îëåã Èâàíîâè÷ Äàëü (1941-5-25--1981-5-3) Ïåðñè Áèøè Øåëëè (1792-8-4--1822-7-8) Ðèììà Ô¸äîðîâíà Êàçàêîâà Ýëüäàð Ðÿçàíîâ Ýäóàðä Íèêîëàåâè÷ Óñïåíñêèé Þðèé Àëåêñååâè÷ Êóêèí Þííà Ïåòðîâíà Ìîðèö ## ß. Ïðèãîæèé ### These should be at the end ### Possible misspellings of the first name # fix_firstname ôîëêëîð ÌÃÓ ### Misspelling of older versions fixed: misspelled => correct_shortcut # fix Ivan Krylov (1769-1844) => Êðûëîâ # fix Samuil Marshak (1887-1964) => Ìàðøàê # fix À. Ñ. Ãðèáîåäîâ (1790(5?)-1829) => Àëåêñàíäð Ñåðãååâè÷ Ãðèáîåäîâ (1790(5?)-1-15-1829-2-11) # fix Ä. È. Ôîíâèçèí (1744(5?)-1792) => Äåíèñ Èâàíîâè÷ Ôîíâèçèí (1744(5?)-4-3--1792) # fix Õ. Ê. Àíäåðñåí => Àíäåðñåí # fix Ï.-Á. Øåëëè => Øåëëè # fix Ýäóàðä Áàãðèöêèé (1922-1942) => Áàãðèöêèé # fix Èîñèô Àëåêñàíäðîâè÷ Áðîäñêèé (19405-24--1996-1-28) => Áðîäñêèé # fix Òàòüÿíà è Ñåðãåé Íèêèòèíû => Ñåðãåé è Òàòüÿíà Íèêèòèíû # fix Ð. Êèïëèíã => Êèïëèíã ## Non-automatic shortnames # shortname Äì. Ñóõàðåâ # shortname Íèêèòèíû # shortname Àðñ. Òàðêîâñêèé # shortname È. Êèóðó # shortname À. Àïóõòèí # shortname Äæ. Êèïëèíã # shortname Äæ. Ëîíäîí ## This might be wrongly expanded when combinations like ## Ñåðãåé è Òàòüÿíà Íèêèòèíû, Âèêòîð Áåðêîâñêèé ## are broken over "," and "è" # keep Òàòüÿíà Íèêèòèíû # keep Ñåðãåé Íèêèòèíû ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/lib/Normalize/Text/Music_Fields.pm�����������������������������������������������������0000700�0000000�0000000�00000135304�11201130514�015755� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Normalize::Text::Music_Fields; # Music_Normalize_Fields $VERSION = '0.02'; use strict; use Config; #use utf8; # Needed for 5.005... my %tr; my %short; sub translate_dots ($) { my $a = shift; $a =~ s/^\s+//; $a =~ s/\s+$//; $a =~ s/\s+/ /g; $a =~ s/\b(\w)\.\s*/$1 /g; $a =~ s/(\w\.)\s*/$1 /g; lc $a } sub translate_tr ($) { my $a = shift; $a = $tr{translate_dots $a} or return; return $a; } sub strip_years ($) { # strip dates my ($a) = (shift); my @rest; return $a unless $a =~ s/\s+((?:\([-\d,]+\)(\s+|$))+)$//; @rest = split /\s+/, $1; return $a, @rest; } sub strip_duplicate_dates { # Remove $d[0] if it matches $d_r my ($d_r, @d) = @_; return unless @d; $d_r = substr $d_r, 1, length($d_r) - 2; # Parens my $dd = substr $d[0], 1, length($d[0]) - 2; # Parens my @dates_r = split /,|--|-(?=\d\d\d\d)/, $d_r; my @dates = split /,|--|-(?=\d\d\d\d)/, $dd; for my $d (@dates) { return @d unless grep /^\Q$d\E(-|$)/, @dates_r; } return @d[1..$#d]; } sub __split_person ($) { # Non-conflicting ANDs (0x438 is cyrillic "i", word is cyrillic "per") split /([,;:]\s+(?:\x{043f}\x{0435}\x{0440}\.\s+)?|\s+(?:[-&\x{0438}ei]|and|et)\s+|\x00)/, shift; } sub _translate_person ($$$); sub _translate_person ($$$) { my ($self, $aa, $with_year) = (shift, shift, shift); my $fail = ($with_year & 2); $with_year &= 1; my $ini_a = $aa; $aa = $aa->[0] if ref $aa; # [value, handler] $aa =~ s/\s+$//; load_lists() unless %tr; # Try early fixing: my $a1 = translate_tr $aa; return ref $ini_a ? [$a1, $ini_a->[1]] : $a1 if $a1 and $with_year; my ($a, @date) = strip_years($aa); my $tr_a = translate_tr $a; if (not defined $tr_a and $a =~ /(.*?)\s*,\s*(.*)/s) { # Schumann, Robert $tr_a = translate_tr "$2 $1"; } if (not defined $tr_a) { return if $fail; my $ini = $aa; # Normalize "translated" to "transl." # echo "¯¥à¥¢®¤" | perl -wnle 'BEGIN{binmode STDIN, q(encoding(cp866))}printf qq(\\x{%04x}), ord $_ for split //' $aa =~ s/(\s\x{043f}\x{0435}\x{0440})\x{0435}\x{0432}\x{043e}\x{0434}\x{0435}?(\s)/$1.$2/g; $aa =~ s/(\s+)\x{0432}\s+(?=\x{043f}\x{0435}\x{0440}\.)/;$1/g; # v per. ==> , per. $aa =~ s/[,;.]\s+(\x{043f}\x{0435}\x{0440}\.)\s*/; $1 /g; # normalize space, punct $aa =~ s/\b(transl)ated\b/$1./g; my @parts = __split_person $aa; if (@parts <= 1) { # At least normalize spacing: # Add dots after initials $aa =~ s/\b(\w)\s+(?=(\w))/ ($1 ne lc $1 and $2 ne lc $2) ? "$1." : "$1 " /eg; # Separate initials by spaces unless in a group of initials $aa =~ s/\b(\w\.)(?!$|[-\s]|\w\.)/$1 /g; return ref $ini_a ? [$aa, $ini_a->[1]] : $aa; } for my $i (0..$#parts) { next if $i % 2; # Separator my $val = _translate_person($self, $parts[$i], $with_year | 2); # fail # Deal with cases (currently, in Russian only, after "transl.") if (not defined $val and $i and $parts[$i-1] =~ /^;\s+\x{043f}\x{0435}\x{0440}\.\s+$/ # per and $parts[$i] =~ /(.*)\x{0430}$/s) { $val = _translate_person($self, "$1", $with_year | 2); # fail } $val ||= _translate_person($self, $parts[$i], $with_year); # cosmetic too $parts[$i] = $val if defined $val; } $tr_a = join '', @parts; return $ini_a if $tr_a eq $ini; @date = (); # Already taken into account... } my ($short, @date_r) = strip_years($tr_a); # Real date @date = strip_duplicate_dates($date_r[0], @date) if @date_r == 1 and @date; $tr_a = $short unless $with_year; $a = join ' ', $tr_a, @date; return ref $ini_a ? [$a, $ini_a->[1]] : $a; } sub normalize_person ($$) { return _translate_person(shift, shift, 1); } for my $field (qw(artist artist_collection)) { no strict 'refs'; *{"normalize_$field"} = \&normalize_person; } sub short_person ($$); sub short_person ($$) { my ($self, $a) = (shift, shift); my $ini_a = $a; $a = $a->[0] if ref $a; # [value, handler] $a = _translate_person($self, $a, 0); # Normalize, no dates of life $a =~ s/\s+$//; ($a, my @date) = strip_years($a); my @parts; if (exists $short{$a}) { $a = $short{$a}; } elsif (@parts = __split_person $a and @parts > 1) { for my $i (0..$#parts) { next if $i % 2; # Separator $parts[$i] = short_person($self, $parts[$i]); } $a = join '', @parts; } else { # Drop years of life shift @date if @date and $date[0] =~ /^\(\d{4}-[-\d,]*\d{4,}[-\d,]*\)$/; # Add dots after initials $a =~ s/\b(\w)\s+(?=(\w))/ ($1 ne lc $1 and $2 ne lc $2) ? "$1." : "$1 " /eg; # Separate initials by spaces unless in a group of initials $a =~ s/\b(\w\.)(?!$|[-\s]|\w\.)/$1 /g; my @a = split /\s+/, $a; # Skip shorting if there are strange non upcased parts (e.g., "-") or '()') my @check = @a; my $von = (@a > 2 and $a[-2] =~ /^[a-z]+$/); splice @check, $#a - 1, 1 if $von; # Ignore mid parts (skip if there are non upcased parts (e.g., "-") or '()') unless (grep lc eq $_, @check or @a <= 1 or $a =~ /\(|[,;]\s/) { my $i = substr($a[0], 0, 1); $a[0] = "$i." if $a[0] =~ /^\w\w/ and lc($i) ne $i; # Keep "from" in L. van Beethoven, M. di Falla, I. von Held, J. du Pre @a = @a[0,($von ? -2 : ()),-1]; } $a = join ' ', @a; } $a = join ' ', $a, @date; return ref $ini_a ? [$a, $ini_a->[1]] : $a; } my %comp; sub normalize_file_lines ($$) { # Normalizing speeds up load_composer() my ($self, $fn) = @_; open my $f, '<', $fn or die "Can't open file $fn for read"; local $_; print "# normalized\n"; while (<$f>) { next if /^#\s*normalized\s*$/; chomp; $_ = normalize_piece($self, $_) unless /^\s*#/; print "$_\n"; } close $f or die "Can't close file $fn for read"; } sub _significant ($$$) { # Try to extract "actual name" of the piece my ($tbl, $l, $r) = (shift, shift, shift); my ($pre, $opus); if ($tbl->{no_opus_no}) { # Remove year-like comment ($pre) = ($l =~ /^(.*\S)\s*\(\d{4}\b[^()]*\)$/s); } else { ($pre, $opus) = ($l =~ /$r/); } $pre = $l unless $pre; my ($significant) = ($pre =~ /^(.*?\bNo[.]?\s*\d+)/is); # Up to No. NN ($significant) = ($pre =~ /^(.*?);/s) unless $significant; ($significant) = $pre unless $significant; (lc $significant, $opus); } my $def_opus_rx = qr/\b(?:Op(?:us\b|\.)|WoO)\s*\d+[a-d]?(?:[.,;\s]\s*No\.\s*\d+(?:\.\d+)*)?/; sub _read_composer_file ($$*$$) { my($self, $f, $fh, $tbl, $aka) = (shift,shift,shift,shift,shift); my($normalized, $l, @works, %aka, $opened); my $opus_rx = $tbl->{opus_rx} || $def_opus_rx; my $opus_pref = $tbl->{opus_prefix} || 'Op.'; local $/ = "\n"; # allow customization if (defined $fh) { $f |= "composer's file" . (eval {' for ' . $self->name_for_field_normalization} || ''); } else { open COMP, "< $f" or die "Can't read $f: $!"; $fh = \*COMP; $f = "`$f'"; $opened = 1; } while (defined ($l = <$fh>)) { next if $l =~ /^\s*(?:##|$)/; if ($l =~ /^#\s*normalized\s*$/) { $normalized++; # Very significant optimization (unless mail-header) } elsif ($l =~ /^#\s*opus_rex\s(.*?)\s*$/) { $opus_rx = $tbl->{opus_rx} = qr/$1/; } elsif ($l =~ /^#\s*dup_opus_rex\s(.*?)\s*$/) { $tbl->{dup_opus_rx} = qr/$1/; } elsif ($l =~ /^#\s*opus_prefix\s(.*?)\s*$/) { $opus_pref = $tbl->{opus_prefix} = $1; } elsif ($l =~ /^#\s*no_opus_no\s*$/) { $tbl->{no_opus_no} = 1; } elsif ($l =~ /^#\s*opus_dup\s+(.*?)\s*$/) { $tbl->{dup_opus}{lc $1} = 1; } elsif ($l =~ /^#\s*prev_aka\s+(.*?)\s*$/) { $aka->{$1} = $works[-1]; # recognize also alternative names } elsif ($l =~ /^#\s*format\s*=\s*(line|mail-header)\s*$/) { $/ = ($1 eq 'line' ? "\n" : ''); } elsif ($l =~ /^#[^#]/) { warn "Unrecognized line of $f: $l" } elsif ($l !~ /^##/) { # Recursive call to ourselves... if ($normalized) { $l =~ s/\s*$//; # chomp... } elsif ($/) { $l = normalize_piece($self, $l); } else { $l = normalize_piece_mail_header($self, $l, $opus_rx, $opus_pref); } push @works, $l; } } not $opened or close $fh or die "Error reading $f: $!"; @works; } sub read_composer_file ($$;*) { my($self, $f, $fh) = (shift,shift,shift); $self = prepare_tag_object_comp($self) unless ref $self; _read_composer_file($self, $f, $fh,{},{}); } my @path; @path = ("$ENV{HOME}/.music_fields") if defined $ENV{HOME} and -d "$ENV{HOME}/.music_fields"; push @path, '-'; @path = split /\Q$Config{path_sep}/, $ENV{MUSIC_FIELDS_PATH} if defined $ENV{MUSIC_FIELDS_PATH}; sub set_path { @path = @_; } (my $myself = __PACKAGE__) =~ s,::,/,g; # 'Normalize/Text/Music_Fields.pm' my @f = $INC{"$myself.pm"}; warn("panic: can't find myself"), @f = () unless -r $f[0]; s(\.pm$)()i or (@f=(), warn "panic: misformed myself") for @f; sub get_path () { map +($_ eq '-' ? @f : $_), @path; } sub load_composer ($$) { my ($self, $c) = @_; eval {$c = $self->shorten_person($c)}; my $ini = $c; return $comp{$ini} if exists $comp{$ini}; $c =~ s/[^-\w]/_/g; $c =~ s/__/_/g; # XXX See Wikipedia "Opus number" for more complete logic $comp{$ini}{opus_rx} = $def_opus_rx; $comp{$ini}{opus_prefix} = 'Op.'; my @dirs = get_path(); my @files = grep -r $_, map "$_/$c.comp", @dirs or return 0; my $f = $files[0]; # $f = $c =~ tr( ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\x80-\x9F) # ( !cLXY|S"Ca<__R~o+23'mP.,1o>...?AAAAAAACEEEEIIIIDNOOOOOx0UUUUYpbaaaaaaaceeeeiiiidnooooo:ouuuuyPy_) # unless -r $f; #warn "file looked up is $f"; return $comp{$ini} unless -r $f; my $tbl = $comp{$ini}; my ($normalized); my @works = _read_composer_file($self, $f, undef, $tbl, \my %aka); return unless @works; # Piano Trio No. 8 (Arrangement of the Septet; Op. 20)); Op. 38 (1820--1823) # so can't m/.*?/ my $r = qr/^(.*($tbl->{opus_rx}))/s; # Name "as in Wikipedia:Naming conventions (pieces of music)" my (%opus, %name, %dup, %dupop); for my $l (@works) { my ($significant, $opus) = _significant($tbl, $l, $r); if ($significant and $name{$significant}) { $dup{$significant}++; warn "Duplicate name `$significant': <$l> <$name{$significant}>" if $ENV{MUSIC_DEBUG_TABLE}; } $name{$significant} = $l if $significant; $opus or next; $opus = lc $opus; if ($opus{$opus}) { $dupop{$opus}++; warn "Duplicate opus number `$opus': <$l> <$opus{$opus}>" unless $tbl->{dup_opus_rx} and $opus =~ /$tbl->{dup_opus_rx}/ or $tbl->{dup_opus}{$opus}; } $opus{$opus} = $l; } delete $name{$_} for keys %dup; delete $opus{$_} for keys %dupop; for my $s (keys %aka) { my ($n) = _significant($tbl, $s, $r); warn "Duplicate and/or unnecessary A.K.A. name `$s' for <$aka{$s}>" if $name{$n}; $name{$n} = $aka{$s}; $name{"\0$s"} = "\0$n"; # put into values(), see normalize_person() } $tbl->{works} = \@works; $tbl->{opus} = \%opus if %opus; $tbl->{name} = \%name if %name; $tbl; } sub translate_signature ($$$$) { # One should be able to override this shift; join '', @_; } $Normalize::Text::Music_Fields::translate_signature = \&translate_signature; my %alteration = (dur => 'major', moll => 'minor'); my %mod = (is => 'sharp', es => 'flat', s => 'flat', # since Es means Ees '#' => 'sharp', b => 'flat'); # XXXX German ==> English (nontrivial): H ==> B, His ==> B sharp, B ==> B flat # XXXX Do not touch B (??? Check "Klavier" etc to detect German???) my %key = (H => 'B'); sub normalize_signature ($$$$) { my ($self, $key, $mod, $alteration) = @_; $alteration ||= ($key =~ /[A-Z]/) ? ' major' : ' minor'; $alteration = lc $alteration; $alteration =~ s/^-?\s*/ /; $alteration =~ s/(\w+)/ $alteration{$1} || $1 /e; $mod =~ s/^-?\s*/ / if $mod; # E-flat, Cb $mod = lc $mod; $mod =~ s/(\w+|#)/ $mod{$1} || $1 /e; $key = uc $key; $key = $key{$key} || $key; &$Normalize::Text::Music_Fields::translate_signature($self,$key,$mod,$alteration); } my $post_opus_rex = qr/(?:[\-\/](?=\d)|(?:[,;.]?|\s)\s*(?:\bN(?:[or]|(?=\d))\.?|#|\x{2116}\.?))\s*(?=\d)/; sub normalize_opus ($$$) { my ($self, $op, $no) = (shift, shift, shift); my $have_no = ( $op =~ s/\b(?:[,;.]?|\s)\s*(?=No\.\s*\d+)/, / ); $no = '' unless defined $no; # nr12 n12 12 -12 #12 Numero_Sign 12 - but only if $op has no number already! $no =~ s/^$post_opus_rex/, No. / unless $have_no; # Now the tricky part: normalize the stuff in unknown format; # XXXX Now support only "B. NNN" stuff $op =~ s/^(\w)(\b|(?=\d))\.?\s*/\U$1. /; "$op$no" } # 1: prefix ("in" etc.), 2: letter, 3: modifier ("b" etc), 4: alteration: minor etc. my $signature_rex = qr/(\s*(?:\bin\b|[,;.:]|^|\((?:in\s+)?(?=[-a-zA-Z#\s]+\)))\s*)([a-h])(\s*[b#]|(?:\s+|-)(?:flat|sharp)|[ie]s|(?<=e)s|)((?:(?:\s+|-)(?:major|minor|dur|moll))?)\)?(?=\s*[-;":]|$)/i; # All these should match in # mp3info2 -D -a beethoven -t "# 28" "" # (should give the same results): "wind in C" "tattoo" "WoO 20" # "sonata in F#" "piano in F#" "op78" "Op. 10-2" "Op. 10, #2" "sonata #22" "WoO 205-1" sub find_person ($) { my $self = shift; eval {$self->name_for_field_normalization} || eval {$self->composer} || $self->artist; } # See test_normalize_piece() sub _normalize_piece ($$$$) { my ($self, $n, $improve_opus, $by_opus) = (shift, shift, shift, shift); my $ini_n = $n; $n = $n->[0] if ref $n; # [value, handler] return $ini_n unless $n; $n =~ s/^\s+//; $n =~ s/\s+$//; return $ini_n unless $n; $n =~ s/\s{2,}/ /g; # Opus numbers $n =~ s/\bOp(us\s+(?=\d)|[.\s]\s*|\.?(?=\d))/Op. /gi; # XXXX posth.??? $n =~ s/\bN(?:[or]|(?=\d))\.?\s*(?=\d)/No. /gi; # nr12 n12 $n =~ s/(?<!\w)[#\x{2116}]\s*(?=\d)/No. /gi; # #12, Numero Sign 12 my $c = find_person $self; my $tbl = ($c and load_composer($self, $c)) || {}; my $opus_rx = $tbl->{opus_rx} || $def_opus_rx; # XXXX Is this `?' for good? $n =~ s/(?<=[^(.,;\s])(\s*[.,;])?\s*\b(?=$opus_rx)/; /gi if $improve_opus; # punctuation before Op. # punctuation between Op. and No (as in Wikipedia for most expanded listings) # $n =~ s/\b((Op\.|WoO)\s+\d+[a-d]?)(?:[,;.]?|\s)\s*(?=No\.\s*\d+)/$1, /gi; $n =~ s/($opus_rx)($post_opus_rex\d+)?/ normalize_opus($self, $1, $2) /gie; # Tricky part: normalize "In b#"; allow just b# after punctuation too $n =~ s/$signature_rex/ ((not $1 or 'i' eq substr($1,0,1)) ? '' : ' ') . "in " . normalize_signature($self,"$2","$3","$4")/ie; my $canon; { $tbl or last; # Convert Op. 23-3 to Op. and No # my ($o, $no) = ($n =~ /\b(Op\.\s+\d+[a-d]?[-\/]\d+[a-d]?)((?:[,;.]?|\s)\s*(?:No\.\s*\d+))?/); # $n =~ s/\b(Op\.\s+\d+[a-d]?)[-\/](\d+[a-d]?)/$1, No. $2/i # if $o and not $no and $o !~ /^$opus_rx$/; $tbl->{works} or last; # XXX See Wikipedia "Opus number" for more complete logic my ($opus) = ($n =~ /^.*($opus_rx)/); # at the end (one not in comments!) if ($opus and $by_opus) { $canon = $tbl->{opus}{lc $opus} or last; } else { # $significant: Up to the first "No. NNN.N", or to the first ";" my ($significant, $pre, $no, $post) = ($n =~ /^((.*?)\bNo\b[.]?\s*(\d+(?:\.\d+)*))\s*(.*)/is); ($significant) = ($n =~ /^(.*?);/s) unless $significant; $significant ||= $n; $canon = $tbl->{name}{lc $significant}; # Try exact match if (not $canon) { # Try harder: match word-for-word my ($ton, $rx_pre, $rx_post) = ('') x 3; my $nn = $n; if ($nn =~ s/\b(in\s+[A-H](?:\s+(?:flat|sharp))?\s+(?:minor|major))\b//) { $ton = $1; ($significant, $pre, $no, $post) = # Redo with $nn ($nn =~ /^((.*?)\bNo\b[.]?\s*(\d+(?:\.\d+)*))\s*(.*)/is); ($significant) = ($nn =~ /^(.*?);/s) unless $significant; $significant ||= $nn; $ton = '.*\b' . (quotemeta $ton) . '\b'; } $pre = $significant unless defined $pre; # Same with No removed # my @parts2 = split '\W+', $post; if ($pre and $pre =~ /\w/) { $rx_pre = '\b' . join('\b.*\b', split /\W+/, $pre) . '\b'; } if ($post and $post =~ /\w/) { $rx_post = '.*' . join '\b.*\b', split /\W+/, $post; } # warn "<$no> <$n> <$nn> <$ton> <$rx_pre> <$rx_post>"; $no = '.*\bNo\.\s*' . (quotemeta $no) . '\b(?!\.\d)' if $no; $no = '' unless defined $no; last unless "$rx_pre$no$ton$rx_post"; my $sep = $tbl->{no_opus_no} ? '' : '.*;'; my $rx = qr/$rx_pre$no$ton$rx_post$sep/is; my @matches = grep /$rx/, values %{$tbl->{name}}; if (@matches == 1) { $canon = $matches[0]; } elsif (!@matches) { last; } else { # Many matches; maybe the shortest is substr of the rest? my ($l, $s, $diff) = 1e100; $l > length and ($s = $_, $l = length) for @matches; $s eq substr $_, 0, $l or ($diff = 1, last) for @matches; last if $diff; $canon = $s; } $canon = $tbl->{name}{$canon} if $canon =~ s/^\0//s; # short name } } # if ($canon) { # my (%w, %w1); # for my $w (split /[-.,;\s]+/, $canon) { # $w{lc $w}++; # } # for my $w (split /[-.,;\s]+/, $n) { # $w1{lc $w}++ unless $w{lc $w}; # } # if (%w1) { # warn "Unknown words in title: `", join("` '", sort keys %w1), "'" # unless $ENV{MUSIC_TRANSLATE_FIELDS_SKIP_WARNINGS}; # last # } # } $n = $canon; # XXXX Simple try (need to compare word-for-word) } return ref $ini_n ? [$n, $ini_n->[1]] : $n; } sub normalize_piece ($$) { _normalize_piece(shift, shift, 'improve opus', 'by opus'); } sub opus_parser ($) { my $tag = shift; my $c = find_person $tag; my $tbl = ($c and load_composer($tag, $c)); my $opus_rx = $tbl->{opus_rx} || $def_opus_rx; my $opus_pre = $tbl->{opus_prefix} || 'Op.'; ($opus_rx, $opus_pre, $c) } sub full_opus ($$;$$) { my ($tag, $short, $opus_rx, $opus_pref) = (shift, shift, shift, shift); ($opus_rx, $opus_pref) = opus_parser($tag) unless $opus_rx; $short = "$opus_pref $short" if $short =~ /^\d/ and not $short =~ /$opus_rx/; $short =~ s/^($opus_rx)($post_opus_rex\d+)?/ normalize_opus($tag, $1, $2) /gie; $short } # Currently used Title-* fields: RAW, Opus, Dates, Key, Name, Related-Name, # Alternative-Name, Punct, Type, Count, For, Type-After-Name, In-Movements # Related-On, Comment, Related-After, Name-By-First-Row ## [When new added, change also the "merging" logic in merge_info().] sub normalize_mail_header_line ($$;$$) { my ($tag, $in, $opus_rx, $opus_pref) = (shift, shift, shift, shift); my ($t, $v) = $in =~ /^([-\w]+):\s*(.*)$/s or die; $v = "($v)" if $t eq 'Title-Dates'; $v = full_opus $tag, $v, $opus_rx, $opus_pref if $t eq 'Title-Opus' and $v =~ /(^\d|[\-\/])/; $v = "; $v" if $t eq 'Title-Opus'; $v = qq("$v") if $t =~ /^Title(-Related)?-Name$/; $v = qq(["$v"]) if $t =~ /^Title-Name-By-First-Row$/; $v = qq(; "$v") if $t eq 'Title-Alternative-Name'; $v =~ s/^(in\s+)?/in /i if $t =~ 'Title-Key'; $v = "No. $v" if $t eq 'Title-No'; $v = "for $v" if $t eq 'Title-For'; $v = "on $v" if $t eq 'Title-Related-On'; $v = "(lyrics by $v)" if $t eq 'Title-Lyrics-By'; $v = ", $v" if $t eq 'Title-Type-After-Name'; $v; } ## perl -wple "BEGIN {print q(# format = mail-header)} s/#\s*normalized\s*$//; $_ = qq(Title: $_) unless /^\s*(#|$)/; $_ = qq(\n$_) if $p and not /^##/; $_ .= qq(\n) unless $p = /^##/" Normalize::Text::Music_Fields-G_Gershwin.comp >Music_Fields-G_Gershwin.comp-mail sub normalize_piece_mail_header ($$;$$) { my ($tag, $in, $opus_rx, $opus_pref) = (shift, shift, shift, shift); return $1 if $in =~ /^Title:\s*(.*?)\s*$/m; my @pieces = map normalize_mail_header_line($tag, $_, $opus_rx, $opus_pref), grep /^Title-[-\w]+:\s/, split /\n/, $in; for my $i (1 .. @pieces - 1) { $pieces[$i-1] .= ' ' unless $pieces[$i-1] =~ /[\(\[\{]$/ or $pieces[$i] =~ /^[\)\]\}.,;:?!]/; } return join '', @pieces; } sub shorten_opus ($$$$) { # $mp3, $str, $pre my ($tag, $op, $pref, $rx) = (shift, shift, shift, shift); my ($out, $cut) = ($op, ''); if ($out =~ s/^\Q$pref\E\s*(?=\d)//) { if ($out =~ $rx) { # back up if shortened version causes confusion $out = $op; } else { $cut = $pref; } } my $out1 = $out; if ($out =~ s/(\d[a-i]?),\s+No\.\s*(?=\d)/$1-/) { my $o = full_opus($tag, $out, $rx, $pref); if ($op ne $o or $out =~ /^$rx$/) { # check again $out = $out1; unless ($out eq $op) { # Extra sanity check $o = full_opus($tag, $out, $rx, $pref); $out = $op unless $op eq $o; } } } $out } my $main_instr = join '|', qw(Piano Violin Viola Cello Horn String Wind Harp Instrument Clarinet Alto); my $for_instr = join '|', qw(Mandolin Harpsichord chorus soprano alt bass basses tenor mezzo-soprano \(mezzo\)soprano baritone contralto hand soli soloists woodwinds celesta accordion instrumentalists large small double violoncello clarinet oboe english french bassoon trombone organ flute voice orchestra military band chamber symphonic symphony electric percussion double-bass vibraphone pantomime instrumental ensemble tape timpani bells keyboard guitar triple percussionist counter-tenor alto counter-alto male female children's boys' mixed a capella cappella choir basssoli chamberorchestra metronome triangle harmonium trumpet); my $multiplets = join '|', qw(solo duo duet trio quartet quintet sextet septet octet); my $pieces = join '|', qw(Serenada Serenade Romance Song Notturno Aria Mass Allemande Chorus Allegretto Rondo Opera Fantasia Polonaise Contredanse Prelude Andante Cadenza Bagatelle Cantata Aria Joke Waltz Waltzes Minuet Ländler March Rondino Variations Equali Fugue Piece Symphony Sonata Concerto Sonatina Dance Mignon Fantasy Scherzo Polka Moderato Fragment Transcription Orchestration Suite Music Reduction Passacaglia Arrangement accompaniment choral score Operetta Ballet oratorio Choruses Intermezzo Overture Dialogue Epilogue Aphorism Monologue Gallop Interlude Re-orchestration Reorchestration Cycle Potpourri Nocturne Capriccio Mazurek Mazurka Impromptu Humoresque Ballade Ballads Gavotte Requiem Fanfares Motet Rhapsodies Rhapsody Intermezzi Poem Marches Theme Melody); my $numb_rx = qr/one|two|three|four|five|six|seven|eight|nine/i; my $count_rx = qr/ \d+ | (?:$numb_rx)(?:teen)? | ten|eleven|twelve|thirteen|fifteen|eighteen | (?:twenty|thirty|fourty|fifty|sixty|seventy|eighty|ninety) (?: (?:\s+ | -) (?:$numb_rx) )? /ix; #no utf8; # `use' is needed by 5.005 my $for_rx = qr/ (?:\s+|^) for (?: (?:\s+|(?<=\/)) \(? (?:and|or|&|vocal\s+soloist|$main_instr|$for_instr|prepared\s+piano|magnetic\s+tape|stage\s+orchestra|jazz\s+ensemble|(?:vocal\s+)?(?:$multiplets)|$count_rx|[23456789]|[12345]\d|Große Fuge) (?:s|\(s\))? \)? [,\/]? )+ /ix; my $piece_rx = qr/ (?: (?:Transcription|Orchestration|Reduction|Arrangement|Suite|Instrumentation|Re-?orchestration) \s+ of (?: \s+ (?: $main_instr | the | $count_rx ) )? \s+ )? # Mod (?: (?: $main_instr | Vocal | secular | sacred | Double | Triple | Easy | Trio | Symphonic ) \s+ )? # Prefix (?:Concerto\s+grosso | $multiplets | Ecossaise? | (?:[123456]-part\s+)? (?:riddle\s+)? Canon | (?:sets\s+of\s+)? (?: chorale\s+preludes? | $pieces ) (?: s? \s* (?:\band\b|&) \s* (?:$pieces))? | Incidental\s+music | electronic\s+composition | chorale\s+prelude | Musical\s+greetings? | choral\s+score | vocal\s+quartet | (?:heroic|comic|tragic|historical)\s+opera | scenic\s+composition | symphonic\s+poem ) # Main type (?: s? \s+ in \s+ (?:$numb_rx) \s+ act )? /ix; #use utf8; # needed by 5.005 my $name_rx = qr/ (?: [A-Z]\w* \.? \s+)* [A-Z][-\'\w]+ /x; my $rel_piece_rx = # Two Pieces for Erwin Dressel's Opera "Armer Columbus" qr/ \b (?:to|from|of|a\s+fter|for|on(?:\s+motives\s+of)?) (?: \s+ (?: \s+ music \s+ to)? (?: the | $name_rx\'s ) # Erwin Dressel's (?: \s+ (?: (?:(?:silent|animated)\s+)? film | spectacle | comedy | TV[-\s]+production | music\s+to\s+the\s+film | play | (?:Chamber-?\s*)? opera | stage \s+ revue | novel))?)? \b /ix; sub strip_known_from_end ($$$) { my ($tag, $in, $try_key, @tail) = (shift, shift, shift); # E.g., when the second name is based on the first line of lyrics: unshift @tail, "Title-Lyrics-By: $1" if $in =~ s/\s+\(lyrics\s+by\s+([^()]+)\)$//; unshift @tail, "Title-Alternative-Name: $4" while $in =~ s/^(.*?".*?".*)(\s*[.:,;])?\s+(?(2)|(?=\())(\()?"([^\"]+)"(?(3)\)|)$/$1/; # Too much recognized as this if ??? while ( $in =~ s/ \s* ( $rel_piece_rx | (?!$) [.:,;]? ) (?: \s+ ( (\[)? ["\x{201E}]([^\"\x{201C}\x{201E}]+)["\x{201C}] (?(3) \] | ) | \(["\x{201E}]([^\"\x{201C}\x{201E}]+)["\x{201C}]\) )) $ //xo ) { if (length $1 <= 1) { unshift @tail, "Title-Name: $+"; } else { unshift @tail, "Title-Related-Name: $+" if $2; unshift @tail, "Title-Related-How: $1"; } } unshift @tail, "Title-Related-By: after $1" if $in =~ s/ \s* after \s+ ($name_rx) $//xo; unshift @tail, "Title-Related-On: $+" # Variation and Fugue if $in =~ s/ ( \b variations? (?: \s+ and \s+ $piece_rx)? (?:$for_rx)? ) \s+ on \s+ # on a Hungarian melody (an? \s+ (?: (?: $name_rx | original ) \s+)? $piece_rx (?: \s+ by \s+ $name_rx)? )$/$1/xio; # XXXX Why $+ needed? unshift @tail, "Title-In-Movements: $1" if $in =~ s/\s*(in\s+(a\s+single|$numb_rx|\d)\s+(movement|episode)s?)$//; unshift @tail, "Title-Key: " . normalize_signature($tag, "$2", "$3", "$4") if $in =~ s/\s*$signature_rex$//; if ($in =~ s/\s*([.,;:])?\s+No\.\s*(\d+[a-d]?(\.\d+)?)$//i) { unshift @tail, "Title-No: $2"; unshift @tail, "Title-Punct: $1" if $1; } unshift @tail, "Title-Key: " . normalize_signature($tag, "$2", "$3", "$4") if $try_key and $in =~ s/[:;,]?\s*$signature_rex$//; my $f; ($f = $1) =~ s/^\s*for\s*//, unshift @tail, "Title-For: $f" if $in =~ s/($for_rx)$//io; # XXXX: foo arranged for piano ??? if ($in =~ s/\s*([.,;:])?\s+No.\s*(\d+[a-d]?(\.\d+)?)$//i) { # Repeat unshift @tail, "Title-No: $2"; unshift @tail, "Title-Punct: $1" if $1; } ($in, @tail); } sub parse_piece ($$$$$$$); # Predeclaration for recursive call without () sub parse_piece ($$$$$$$) { my ($after_name, $at_end, $at_start, $tag, $in, $opus_pref, $opus_rx, @tail) = (shift, shift, shift, shift, shift, shift, shift); if ($at_end) { unshift @tail, "Title-Dates: $2" if $in =~ s/(.*\S)\s*\(([^()]*\b\d{4}\b[^()]*)\)$/$1/ # $1 makes greedy or $at_end and not $at_start and $in =~ s/^()\s*\(([^()]*\b\d{4}\b[^()]*)\)$/$1/; # $1 makes greedy unshift @tail, "Title-Opus: " . shorten_opus($tag, "$2", $opus_pref, $opus_rx) while $in =~ s/(.*);\s+($opus_rx)\s*$/$1/; unshift @tail, "Title-Key: " . normalize_signature($tag, "$2", "$3", "$4") if $in =~ s/\s*$signature_rex$//; } ($in, my @r) = strip_known_from_end($tag, $in, 'look for key'); unshift @tail, @r; # Now recognize comment as everything after a key (except, maybe, name) if ($in =~ /^(.*\S)\s*$signature_rex\s*(?:"([^\"]+)"\s*)?(?:([.,:;])\s)?(.*)$/) { $in = $1; my $k = normalize_signature($tag, "$3", "$4", "$5"); my($n,$rest) = ($6, $8); if (length $rest) {{ # Localize match unshift @tail, 'Title-'. ($8 =~ /^[^\s\w]$/ ? 'Punct' : 'Comment'). ": $rest"; }} unshift @tail, "Title-Punct: $7" if $7; my $alt = ($in =~ /".*"/ ? '-Alternative' : ''); unshift @tail, "Title$alt-Name: $n" if defined $n and length $n; unshift @tail, "Title-Key: $k"; } # Now repeat looking for known fields ($in, @r) = strip_known_from_end($tag, $in, not 'look for key'); unshift @tail, @r; if ($at_start) { # and (@tail or not $at_end) unshift @tail, "Title-Type: $1" if $in =~ s/^($piece_rx s?)\s*$//iox; unshift @tail, "Title-Count: $1" , "Title-Type: $2" if $in =~ s/^($count_rx)\s+( $piece_rx s?)\s*$//iox; unshift @tail, "Title-Count: $1" if $in =~ s/^($count_rx)\s*$//iox; } if (not @tail and $at_start and $at_end) { unshift @tail, "Title: $in"; } elsif (not length $in) { # Do nothing } elsif ($in =~ /^\s*[-,:;.()\[\]{}]\s*$/) { unshift @tail, "Title-Punct: $in"; } elsif ($after_name and $in =~ /^(by|after)((\s+and)?\s+[A-Z][-\'\w]+)+\s*$/) { unshift @tail, "Title-Related-By: $in"; } elsif ($after_name and $in =~ /^([-,;:])\s+($piece_rx s?)\s*$/iox) { unshift @tail, "Title-Type-After-Name: $2"; } elsif ($at_start and $in =~ /^"([^\"]+)"\s*$/iox) { unshift @tail, "Title-Name: $1"; } else { if ($at_start and $in =~ /^"([^\"]+)"[,.;:]\s*(\S.*?)\s*$/) { my $name = $1; # Pretend we are at start: my @rest = parse_piece 'after_name', ($at_end and not @tail), 'start', $tag, "$2", $opus_pref, $opus_rx; unshift @rest, "Title-Punct: ," unless $rest[0] =~ s/^Title-Type:/Title-Type-After-Name:/; return("Title-Name: $name", @rest, @tail) unless (join "\n", '', @rest) =~ /\nTitle-RAW:/; } unshift @tail, "Title-RAW: $in"; } @tail; } my %html_esc = qw( amp & lt < gt > ); sub naive_format ($$$) { # Used to find glaring errors in conversion only my ($tag, $in, $opus_rx, $opus, @out) = (shift,shift,shift); $in =~ s/^($opus_rx)\n/$1: /; my @in = split /\s*\n\s*/, $in; if ($in[0] =~ s/^($opus_rx)[:,]\s*/Title-RAW: /) { ($opus = $1) =~ s/^Opus\b/Op./; } for my $l (@in) { if ($l =~ s/^Title-Bold:\s*//) { push @out, qq("$l"); } elsif ($l =~ s/^Title-Opus:\s*//) { push @out, '; ' . full_opus $tag, "$l"; } elsif ($l =~ s/^Title-Dates:\s*//) { push @out, "($l)"; } elsif ($l =~ s/^X-\w[-\w]*:\s*//) { # Do nothing } elsif ($l =~ s/^Title-(RAW|Comment):\s*//) { push @out, $l if length $l; } else { warn "Naive formatting: Unknown line format `$l'" } } if (defined $opus) { my @year; @year = $1 if @out and $out[-1] =~ s/\s*(\([^()]*\b\d{4}\b[^()]*\))$//; pop @out unless @out and length $out[-1]; push @out, "; $opus", @year; } for my $n (1..$#out) { $out[$n] =~ s/^(?![.,;:])/ /; } join '', @out } # Convert from line-format to mail-header format: ## perl -MNormalize::Text::Music_Fields -wlne "BEGIN {$tag = Normalize::Text::Music_Fields::prepare_tag_object_comp(shift @ARGV); print q(# format = mail-header)} print Normalize::Text::Music_Fields::emit_as_mail_header($tag,$_, 0,$pre)" gershwin Music_Fields-G_Gershwin.comp-line >Music_Fields-G_Gershwin.comp-mail1 # (inverse transformation:) Dump pieces listed in mail-header format ## perl -MNormalize::Text::Music_Fields -wle "print for Normalize::Text::Music_Fields::read_composer_file(shift, shift)" gershwin Music_Fields-G_Gershwin.comp-mail > o # ## perl -MNormalize::Text::Music_Fields -00wnle "BEGIN {$tag = Normalize::Text::Music_Fields::prepare_tag_object_comp(shift @ARGV); print q(# format = mail-header)} print Normalize::Text::Music_Fields::emit_as_mail_header($tag,$_, q(bold,xml,opus),$pre)" shostakovich o-xslt-better >Music_Fields-D_Shostakovich.comp-mail1 ## perl -MNormalize::Text::Music_Fields -wnle "BEGIN {$tag = Normalize::Text::Music_Fields::prepare_tag_object_comp(shift @ARGV); print qq(# format = mail-header\n)} print Normalize::Text::Music_Fields::emit_as_mail_header($tag,$_, q(opus), $pre)" schnittke o-schnittke-better >Music_Fields-A_Schnittke.comp-mail2 sub emit_as_mail_header ($$$$) { # $mp3, $str, $has_bold_parts_etc, $pre [R/W] my ($tag, $in, $preformatted) = (shift, shift, shift); $in =~ s/#\s*normalized\s*$//; #return "\n" if $in =~ /^\s*$/; my @out; unless ($in =~ /^\s*(#|$)/) { return "\n\n" if $preformatted and $in =~ /^<\?xml\b/; my $ini = my $ini_raw = $in; $in =~ s/&(amp|lt|gt);/$html_esc{$1}/g if $preformatted =~ /\bxml\b/; $in =~ s/&#x([\da-f]+);/chr hex $1/gei if $preformatted =~ /\bxml\b/; my ($opus_rx, $opus_pre) = opus_parser($tag); my $have_op = ($in =~ /^$opus_rx:/); # When $use_only_opus, all the text but Opus-No is ignored; bad for update my $use_only_opus = ($preformatted =~ /\bonly_by_opus\b/); $in = _normalize_piece($tag, $in, !$have_op, $use_only_opus) unless $preformatted =~ /\bbold\b/; $ini = naive_format($tag, $in, $opus_rx) if $preformatted =~ /\b(opus|bold)\b/; my @op; my $prefix = ($preformatted =~ /\bbold\b/ ? 'Title-RAW: ' : ''); if ($in =~ s/^($opus_rx)(?:[:,](?:[ \t]+|(?=\n))|\n\s*)/$prefix/) { my $op = $1; my $o_pre = $opus_pre; $o_pre = 'Opus' if $op =~ /^Opus\b/; @op = "Title-Opus: " . shorten_opus($tag, $op, $o_pre, $opus_rx); } elsif ($preformatted =~ /\bopus\b/) { warn "Expected to start with `Opus NUMBER: ': <<<$in>>>"; } if ($preformatted =~ /\bbold\b/) { my @parts = split /\s*\n\s*/, $in; my ($after_for, $after_name); for my $n (0..$#parts) { my $p = $parts[$n]; $p =~ s/\s+$//; if ($p =~ s/^Title-Bold:\s*//) { my $rel = $after_for ? '-Related' : ''; push @out, "Title$rel-Name: $p"; $after_for = 0, $after_name = 1; next; } elsif ($p =~ /^Title-RAW:\s*$/) { # Do nothing next; } elsif ($after_for = ($n != $#parts and $parts[$n+1] =~ /^Title-Bold:\s*/ and $parts[$n] =~ /^Title-RAW:\s*/ # Title-RAW: Two Pieces for Erwin Dressel's Opera "Armer Columbus" and $p =~ s/ \s* ( $rel_piece_rx \s*$ )//ixo)) { my $how = $1; $p =~ s/^Title-RAW:\s+// or warn "Expected to start with Title-RAW: <<<$p>>>"; push @out, parse_piece $after_name,!'end', !$n, $tag, $p, $opus_pre, $opus_rx; push @out, "Title-Related-How: $how"; } elsif ($p =~ s/^Title-Opus:\s+// ) { push @out, 'Title-Opus: ' . full_opus $tag, $p, $opus_rx, $opus_pre; $after_name = 0; } elsif ($p =~ /^(Title-(Opus|Comment|Dates)|X-Title-Opus-Alt):\s+/ ) { # Keep intact push @out, $p; $after_name = 0; } else { $p =~ s/^Title-RAW:\s+// or warn "Expected to start with `Title-RAW: ': <<<$p>>>"; push @out, parse_piece $after_name, $n==$#parts, !$n, $tag, $p, $opus_pre, $opus_rx; $after_name = 0; } } } else { @out = parse_piece 0, 'at_end', 'at_start', $tag, $in, $opus_pre, $opus_rx; } my @y; unshift @y, pop @out while $out[-1] =~ /^Title-Dates:\s/; push @out, @op, @y; $out[0] =~ s/^Title:/Title-RAW:/ if @out > 1; # Opus 1: foo $in = join "\n", @out, ($preformatted =~ /\bbold\b/ ? ('','') : ()); # \n\n my $res = normalize_piece_mail_header($tag, $in, $opus_rx, $opus_pre); warn "# Mismatch:\n# in = $ini\n# out = $res\n#rawin= $ini_raw\n" unless $res eq $ini; } $in = "\n$in" if $in !~ /^\s*##/ and $_[0] and not $preformatted =~ /\bbold\b/; $in .= qq(\n) unless $preformatted =~ /\bbold\b/ or $_[0] = ($in =~ /^##/); $in; # Caller appends extra \n } ## perl -MNormalize::Text::Music_Fields -wnle "BEGIN {$tag = Normalize::Text::Music_Fields::prepare_tag_object_comp(shift @ARGV); print qq(# format = mail-header\n)} next unless s/^\s*\+\+\s*//; print Normalize::Text::Music_Fields::merge_info($tag,$_, q(opus))" brahms o-brahms-op-no1-xslt sub merge_info ($$$;$$) { # $update not fully implemented my ($tag, $in, $preformatted, $soft, $update) = (shift, shift, shift, shift, shift); my $parsed = emit_as_mail_header($tag, $in, $preformatted, my $pre); my $op_n = ($parsed =~ /^Title-Opus: (.*)/m and $1); die "Can't find opus number in `$in'" unless defined $op_n; my $op_no = full_opus $tag, $op_n; $parsed =~ s/^Title-Punct:\s*-\nTitle-Name:/Title-Name-By-First-Row:/; $soft ||= qr(^(?!)); # Never match warn "Opus [$op_n]: Type `$1' interpreted as Title-Name\n" if $op_n =~ $soft and $parsed =~ s/^Title-Type:/Title-Name:/m and $parsed =~ /^Title-Name:\s*(.*)/; warn("Too many fields in `$parsed', skipping"), return '' if $parsed =~ /^(?=.)(?!Title-(?:Opus|RAW|Name(?:-By-First-Row)?|Key|Dates):)/m; my $name = normalize_piece $tag, $op_no; # expand opus+no to the full name if ($name eq $op_no) { # No current information my ($opus_rx, $opus_pre) = opus_parser($tag); die "No subopus number in `$op_no' (from `$in')" unless $op_no =~ /^($opus_rx)\s*[.,:;]\s*No/; my $op = $1; $name = normalize_piece $tag, $op; # Expands opus to the full name $update = 0; } elsif (not $update) { die "Opus `$op_no' already known: `$name'"; } my $parsed_op = emit_as_mail_header($tag, $name, 'only_by_opus', my $pre1); warn("Prior knowledge not found for `$in'\n"), return $parsed if $parsed_op =~ /^Title:/; # Not found, or not parsable unless ($update) { # Handling "a group name" $parsed_op =~ s/^Title-Count:.*\n//; # Four ballades for piano if ($parsed_op =~ /^Title-Type:\s*(.*)\n/) { # Strip the plural my $type = $1; $type =~ s/^ Sets \s+ of \s+/Set of /x or $type =~ s/^ ($piece_rx) (?:s | es) $/$1/x; # Strip the plural $parsed_op =~ s/^.*/Title-Type: $type/; } $parsed_op =~ s/^Title-Opus:.*/Title-Opus: $op_n/m or die "Can't find Opus: `$parsed_op'"; } if ($parsed =~ /^Title-Dates:\s*(.*)/m) { my $d = $1; # (?<!.) does as /^/m, but matches at end too $parsed_op =~ s/(?<!.)(Title-Dates:.*\n|\Z)/Title-Dates: $d\n/ or die; } if ($parsed =~ /^Title-Key:\s*(.*)/m) { my $k = $1; die "Key mismatch: $k vs $1" if $parsed_op =~ /^Title-Key:\s*(.*)/m and $1 ne $k; # XXXX Where put the key? STD orders: Type/No/Key/For or Type/For/No/Key # There is also (beeth) Type/For/Related-On/Key??? Type/For/Key??? $parsed_op =~ s/(?<!.)(?=Title-(?!(?:Type|For|Related-On|No):)|\Z)/Title-Key: $k\n/ or die; } if ($parsed =~ s/^Title-RAW:/Title-Name:/m) { (my $n) = ($parsed =~ /^Title-Name:\s*(.*)/m); warn "Title-RAW `$n' interpreted as Title-Name in `$in'\n"; } if ($parsed =~ /^(Title-Name(?:[-\w]*):\s*.*)/m) { # pre: Type-After-Name, In-Movements my $n = $1; # Related-On, Comment, Related-After $parsed_op =~ s/(?<!.)(?=Title-(?:Type-After-Name|In-Movements|Related-On|Comment|Related-After|Opus|Dates):|\Z)/$n\n/ or die; } $parsed_op } for my $field (qw(album title title_track)) { no strict 'refs'; *{"normalize_$field"} = \&normalize_piece; } # perl -Ii:/zax/bin -MNormalize::Text::Music_Fields -wle "BEGIN{binmode $_, ':encoding(cp866)' for \*STDIN, \*STDOUT, \*STDERR}print Normalize::Text::Music_Fields->check_persons" sub check_persons ($) { my $self = shift; my %seen; $seen{$_}++ for values %tr; for my $l (keys %seen) { my $s = short_person($self, $l); my $ll = normalize_person($self, $s); warn "`$l' => `$s' => `$ll'" unless $ll eq $l; } %seen = (); $seen{$_}++ for values %short; for my $s (values %seen) { my $l = normalize_person($self, $s); my $ss = short_person($self, $l); warn "`$s' => `$l' => `$ss'" unless $ss eq $s; } } my %aliases; sub load_lists () { my @dirs = get_path(); my @lists = map <$_/*.lst>, @dirs; #warn "dirs=`@dirs', lists=`@lists'\n"; warn("panic: can't find name lists in `@dirs'"), return 0 unless @lists; for my $f (@lists) { local $/ = "\n"; open F, "< $f" or warn("Can't open `$f' for read: $!"), next; my @in = <F>; close F or warn("Can't close `$f' for read: $!"), next; my $charset; for (@in) { next if /^\s*$/; if ( /^ \s* \# \s* (?:charset|encoding) \s* = \s* ("?) (.*?) \1 \s* $/ix) { $charset = $2; require Encode; next; } $_ = Encode::decode($charset, $_) if $charset; # Make empty to disable s/^\s+//, s/\s+$//, s/\s+/ /g; next if /^##/; if (/^ \# \s* (alias|fix|shortname_for) \s+ (.*?) \s* => \s* (.*)/x) { if ($1 eq 'alias') { $aliases{$2} = [split /\s*,\s*/, $3]; } elsif ($1 eq 'fix') { my ($old, $ok) = ($2, $3); $tr{translate_dots $old} = $tr{translate_dots $ok} || $ok; #print "translating `",translate_dots $old,"' to `",translate_dots $ok,"'\n"; } elsif ($1 eq 'shortname_for') { my ($long, $short) = ($2, $3); $tr{translate_dots $short} = $long; ($long) = strip_years($long); $short{$long} = $short; } next; } if (/^ \# \s* fix_firstname \s+ (.*\s(\S+))$/x) { $tr{translate_dots $1} = $tr{translate_dots $2}; next; } if (/^ \# \s* keep \s+ (.*?) \s* $/x) { $tr{translate_dots $1} = $1; next; } if (/^ \# \s* shortname \s+ (.*?) \s* $/x) { my $in = $1; my $full = __PACKAGE__->_translate_person($in, 0); unless (defined $full and $full ne $in) { my @parts = split /\s+/, $in; $full = __PACKAGE__->_translate_person($parts[-1], 0); warn("Can't find translation for `@parts'"), next unless defined $full and $full ne $parts[-1]; # Add the normalization my $f = __PACKAGE__->normalize_person($parts[-1]); $tr{translate_dots $in} = $f; } $short{$full} = $in; ($full) = strip_years($full); $short{$full} = $in; next; } warn("Do not understand directive: `$_'"), next if /^#/; #warn "Doing `$_'"; my ($pre, $post) = /^(.*?)\s*(\(.*\))?$/; my @f = split ' ', $pre or warn("`$pre' won't split"), die; my $last = pop @f; my @last = $last; # no utf8; # `use' is needed by 5.005 (my $ascii = $last) =~ tr( ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\x80-\x9F) ( !cLXY|S"Ca<__R~o+23'mP.,1o>...?AAAAAAACEEEEIIIIDNOOOOOx0UUUUYpbaaaaaaaceeeeiiiidnooooo:ouuuuyPy_); push @last, $ascii unless $ascii eq $last; my $a = $aliases{$last[0]} ? $aliases{$last[0]} : []; $a = [$a] unless ref $a; push @last, @$a; for my $last (@last) { my @comp = (@f, $last); $tr{"\L@comp"} ||= $_; $tr{lc $last} ||= $_; # Two Bach's if (@f) { $tr{"\L$f[0] $last"} ||= $_; # With the first of pre-names only my @ini = map substr($_, 0, 1), @f; $tr{"\L$ini[0] $last"} ||= $_; # One initial $tr{"\L@ini $last"} ||= $_; # All initials } } } } } #$tr{lc 'Tchaikovsky, Piotyr Ilyich'} = $tr{lc 'Tchaikovsky'}; sub prepare_tag_object_comp ($;$) { my ($comp, $piece) = @_; require MP3::Tag; my $tag = MP3::Tag->new_fake('settable'); for my $elt ( qw( title track artist album comment year genre title_track artist_collection person ) ) { no strict 'refs'; MP3::Tag->config("translate_$elt", \&{"Normalize::Text::Music_Fields::normalize_$elt"}) if defined &{"Normalize::Text::Music_Fields::normalize_$elt"}; # This is needed to expand albums, since pieces file is named so... MP3::Tag->config("short_person", \&Normalize::Text::Music_Fields::short_person) if defined &Normalize::Text::Music_Fields::short_person; } $tag->config('parse_data', ['mi', $comp, '%a'], ($piece ? ['mi', $piece, '%l'] : () )); $tag; } ## perl -MNormalize::Text::Music_Fields -e Normalize::Text::Music_Fields::test_normalize_piece sub test_normalize_piece { for (split /\n/, <<EOS) { beethoven # 28 beethoven wind in C beethoven tattoo beethoven WoO 20 beethoven sonata in F# beethoven piano in F# beethoven op78 beethoven Op. 10-2 beethoven Op. 10, #2 beethoven sonata #22 beethoven WoO 205-1 beethoven WoO 205, No 1 beethoven WoO 205, No. 1 beethoven WoO 205, no 1 beethoven WoO 205;#1 beethoven WoO 205, no1 beethoven WoO 205 #1 beethoven WoO 205#1 beethoven WoO 205. #1 - beethoven WoO 205,-1 - beethoven WoO 205, -1 - beethoven WoO 205 -1 - beethoven WoO 205 1 - beethoven WoO 205;1 EOS my $match = (s/^-\s*// ? '-' : '+'); s/^(\w+)\s+//; my $tag = prepare_tag_object_comp("$1", $_); print "$match ", find_person($tag), " ", $tag->album, "\n"; } } for my $elt ( qw( title track artist album comment year genre title_track artist_collection person ) ) { no strict 'refs'; # backward compatibility layer: *{"translate_$elt"} = \&{"normalize_$elt"} if defined &{"normalize_$elt"}; } 1; =head1 NAME Normalize::Text::Music_Fields - normalize names of people's and (musical) works. =head1 SYNOPSIS $name = $obj->Normalize::Text::Music_Fields::normalize_person($name); $work = $obj->Normalize::Text::Music_Fields::normalize_piece($work); # $obj should have methods `name_for_field_normalization', 'shorted_person' =head1 DESCRIPTION Databases of names and of works-per-name are taken from plain-text files (optionally in mail-header format). Names are stored in F<*.lst> files. Works are stored in F<.comp> files named after the shortened name of the composer. The directories of these files are looked in the environment variable C<MUSIC_FIELDS_PATH> (if defined, split the same way as C<PATH>), or in C<$ENV{HOME}/.music_fields>, and C<-> (and C<-> is replaced by the directory named as the module file with F<.pm> dropped). At runtime, one can replace the list by calling function Normalize::Text::Music_Fields::set_path() with the list of directories as the argument. (Since parsed files are cached, replacing the directory list should be done as early as possible.) Files may be managed with utility subroutines provided with the module: # Translate from one-per-line to mail-header format: perl -wple "BEGIN {print q(# format = mail-header)} s/#\s*normalized\s*$//; $_ = qq(Title: $_) unless /^\s*(#|$)/; $_ = qq(\n$_) if $p and not /^##/; $_ .= qq(\n) unless $p = /^##/" Normalize::Text::Music_Fields-G_Gershwin.comp >Music_Fields-G_Gershwin.comp-mail # (inverse transformation:) Dump pieces listed in mail-header format perl -MNormalize::Text::Music_Fields -wle "print for Normalize::Text::Music_Fields::read_composer_file(shift, shift)" gershwin Music_Fields-G_Gershwin.comp-mail > o # Normalize data in 1-line-per piece format perl -MNormalize::Text::Music_Fields -wle "Normalize::Text::Music_Fields::prepare_tag_object_comp(shift)->Normalize::Text::Music_Fields::normalize_file_lines(shift)" # Create a mail-header file from a semi-processed (with "bold" fields) # mail-header file (with xml escapes, preceded by opus number) perl -MNormalize::Text::Music_Fields -00wnle "BEGIN {$tag = Normalize::Text::Music_Fields::prepare_tag_object_comp(shift @ARGV); print q(# format = mail-header)} print Normalize::Text::Music_Fields::emit_as_mail_header($tag,$_, q(bold,xml,opus),$pre)" shostakovich o-xslt-better >Music_Fields-D_Shostakovich.comp-mail1 # Likewise, from work-per-line with opus-numbers: perl -MNormalize::Text::Music_Fields -wnle "BEGIN {$tag = Normalize::Text::Music_Fields::prepare_tag_object_comp(shift @ARGV); print qq(# format = mail-header\n)} print Normalize::Text::Music_Fields::emit_as_mail_header($tag,$_, q(opus), $pre)" schnittke o-schnittke-better >Music_Fields-A_Schnittke.comp-mail2 # A primitive tool for merging additional info into the database: perl -MNormalize::Text::Music_Fields -wnle "BEGIN {$tag = Normalize::Text::Music_Fields::prepare_tag_object_comp(shift @ARGV); print qq(# format = mail-header\n)} next unless s/^\s*\+\+\s*//; print Normalize::Text::Music_Fields::merge_info($tag,$_, q(opus,xml), qr(^(58|70|76|116|118|119)($|-)))" brahms o-brahms-op-no1-xslt # Minimal consistency check of persons database. perl -MNormalize::Text::Music_Fields -wle "BEGIN{binmode $_, ':encoding(cp866)' for \*STDIN, \*STDOUT, \*STDERR} print Normalize::Text::Music_Fields->check_persons" # Minimal testing code: perl -MNormalize::Text::Music_Fields -e Normalize::Text::Music_Fields::test_normalize_piece It may be easier to type these examples if one uses C<manage_M_N_F.pm>, which exports the mentioned subroutines to the main namespace (available in F<examples> directory of a distribution of C<MP3::Tag>). E.g., the last example becomes: perl -Mmanage_M_N_F -e test_normalize_piece =cut ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/Makefile.PL����������������������������������������������������������������������������0000700�0000000�0000000�00000003354�11304131106�011372� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������use ExtUtils::MakeMaker; use Config; use strict; my @programs_to_install = qw(mp3info2 typeset_audio_dir audio_rename); my $lib_only = (grep /^LIB=/, @ARGV and not grep /^INSTALLSCRIPT=/, @ARGV); my @scr = grep /^INSTALLSCRIPT=/, @ARGV; (my $scr = pop @scr) =~ s/^INSTALLSCRIPT=//; $scr = $Config{installscript} unless defined $scr; if ( grep $_ eq '-n', @ARGV or $lib_only ) { @ARGV = grep $_ ne '-n', @ARGV; warn "I see LIB= but not no INSTALLSCRIPT=\n" if $lib_only; warn "Skipping installation of scripts...\n"; @programs_to_install = (); } else { warn <<EOW; This program comes with several scripts which I would try to install in directory $scr. To skip, rerun with option -n given to Makefile.PL. EOW } # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. WriteMakefile( 'NAME' => 'MP3::Tag', 'VERSION_FROM' => 'lib/MP3/Tag.pm', # finds $VERSION 'EXE_FILES' => [ map "examples/$_", @programs_to_install ], # 'PMLIBDIRS' => ['Tag', 'MP3::Tag'], 'AUTHOR' => '"Thomas Geffert" <thg@users.sourceforge.net>, "Ilya Zakharevich" ilyaz@cpan.org', 'PREREQ_PM' => { # Compress::Zlib => 0, }, 'PL_FILES' => {'data_pod.PL'=>'lib/MP3/Tag/ID3v2_Data.pod'}, # 'clean' => {FILES => 'ID3v2_Data.pod'}, # is included! ); # Tell MakeMaker about manifying ID3v2_Data.pod package MY; sub manifypods { my $self = shift; $self->{MAN3PODS}->{'lib/MP3/Tag/ID3v2_Data.pod'} = '$(INST_MAN3DIR)/MP3::Tag::ID3v2_Data.$(MAN3EXT)'; $self->SUPER::manifypods(@_); } sub postamble { # Not good enough: is done after .pod is moved to INST_LIB... ' lib/MP3/Tag/ID3v2_Data.pod :: lib/MP3/Tag/ID3v2.pm # pm_to_blib '; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/MANIFEST�������������������������������������������������������������������������������0000700�0000000�0000000�00000003161�11304131122�010543� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Changes MANIFEST Makefile.PL README.txt TODO lib/MP3/Tag.pm lib/MP3/Tag/File.pm lib/MP3/Tag/ID3v1.pm lib/MP3/Tag/ID3v2.pm lib/MP3/Tag/Inf.pm lib/MP3/Tag/CDDB_File.pm lib/MP3/Tag/Cue.pm lib/MP3/Tag/LastResort.pm lib/MP3/Tag/ParseData.pm lib/Encode/transliterate_win1251.pm lib/Normalize/Text/Music_Fields.pm lib/Normalize/Text/Music_Fields/A_Dvor_k.comp lib/Normalize/Text/Music_Fields/A_Schnittke.comp lib/Normalize/Text/Music_Fields/D_Shostakovich.comp lib/Normalize/Text/Music_Fields/G_Gershwin.comp lib/Normalize/Text/Music_Fields/J_Brahms.comp lib/Normalize/Text/Music_Fields/L_van_Beethoven.comp lib/Normalize/Text/Music_Fields/Music_Fields-rus.lst cddb.tmp cddb.tm data_pod.PL examples/README.txt examples/audio_rename examples/mod/Music_Translate_Fields.pm examples/mod/Music_Normalize_Fields.pm examples/src/HOWTO examples/src/Beethoven.all_y examples/mod/manage_M_N_F.pm examples/empty.mp3 examples/extractID3v2.pl examples/mp3info.pl examples/mp3info2 examples/tagged.pl examples/tagit.pl examples/typeset_audio_dir examples/mp3_total_time.pl examples/eat_wav_mp3_header.pl examples/fetch_composer_wiki.pl examples/fetch_composer_codm.pl examples/empty_10sec.mp3 examples/extract-y.pl examples/dir_mp3_2fake_cddb.pl examples/fulltoc_2fake_cddb.pl examples/inf_2fake_cddb.pl examples/cddb2cddb.pl examples/Music_Normalize_Fields-normalize.pl test.mp3 t/mp3tag.t t/set_v2.t t/v2_comments.t t/update_tags.t t/parser.t t/interpolate.t tk-tag/README tk-tag/tk-tag.pl lib/MP3/Tag/ID3v2_Data.pod META.yml Module meta-data (added by MakeMaker) README.shrink lib/MP3/Tag/ImageSize.pm lib/MP3/Tag/ImageExifTool.pm ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/META.yml�������������������������������������������������������������������������������0000700�0000000�0000000�00000001043�11417072076�010701� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������--- #YAML:1.0 name: MP3-Tag version: 1.13 abstract: ~ author: - "Thomas Geffert" <thg@users.sourceforge.net>, "Ilya Zakharevich" ilyaz@cpan.org license: unknown distribution_type: module configure_requires: ExtUtils::MakeMaker: 0 build_requires: ExtUtils::MakeMaker: 0 requires: {} no_index: directory: - t - inc generated_by: ExtUtils::MakeMaker version 6.54 meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/README.shrink��������������������������������������������������������������������������0000700�0000000�0000000�00000001425�10361053130�011574� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Due to a bug (fixed in version 0.9701) when multiple edits were done on the same file, several K of zero bytes may have been inserted between the ID3v2 tag and the body of audio. These bytes were not included in the count of bytes of the tag. To fix files with this unwanted padding, one can do the following: a) include the byte count of the padding into the tag size (quick); b) Remove the padding (will require rewriting of the file, so may take some time). To do "a", enable the option 'id3v2_mergepadding' when you update tags. To do "b" enable the option 'id3v2_shrink' when you update tags. (With method 'update_tag', you may want to specify the $force2 option; with mp3info2, use -2 option). Example: mp3info2 -C "~id3v2_shrink=1~id3v2_mergepadding=1" -u2 -R . �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/README.txt�����������������������������������������������������������������������������0000700�0000000�0000000�00000002727�11304131132�011120� 0����������������������������������������������������������������������������������������������������ustar �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� MP3::Tag ============================================================ This is a perl module to read/write ID3v1, ID3v1.1 and ID3v2.3 tags of mp3-files. (Other tags hopefully to follow). To install the MP3::TAG module you simply do: perl Makefile.PL make make test make install (as root) If you find some errors while doing this, please send me an email describing the problems. You need to have the modules Compress::Zlib and File::Basename installed. If you are missing one of these (perl Makefile.PL should warn you) you can find them on CPAN (www.cpan.org). In the directory examples, you find 4 examples, how to use the module. You can read the documentation of this module with man MP3::Tag man MP3::Tag::ID3v1 man MP3::Tag::ID3v2 man MP3::Tag::ID3v2_Data More information about this project, new releases and so on, can be found at: http://tagged.sourceforge.net Success with this Thomas <thg@users.sourceforge.net> tk-tag.pl ============================================================== In the directory tk-tag you can find a graphical interface for MP3::Tag. See the README file in that directory. Copyright ============================================================== Copyright (c) 2000-2004 Thomas Geffert. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the Artistic License, distributed with Perl. �����������������������������������������MP3-Tag-1.13/t/�������������������������������������������������������������������������������������0000700�0000000�0000000�00000000000�11417072072�007666� 5����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/t/interpolate.t������������������������������������������������������������������������0000700�0000000�0000000�00000010074�10750464250�012410� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl test.pl' ######################### We start with some black magic to print on failure. # Change 1..1 below to 1..last_test_to_print . # (It may become useful if the test is moved to ./t subdirectory.) BEGIN { $| = 1; print "1..24\n"; $ENV{MP3TAG_SKIP_LOCAL} = 1} END {print "MP3::Tag not loaded :(\n" unless $loaded;} use MP3::Tag; $loaded = 1; $count = 0; ok(1,"MP3::Tag initialized"); ######################### End of black magic. # Insert your test code below (better if it prints "ok 13" # (correspondingly "not ok 13") depending on the success of chunk 13 # of the test code): {local *F; open F, '>test12.mp3' or warn; print F 'empty'} {local *F; open F, '>xxxtest12.mp3' or warn; print F 'content'} $mp3 = MP3::Tag->new("test12.mp3"); ok( $mp3->interpolate('%{TCOM: %{TCOM} }') eq '', 'false conditional'); ok( $mp3->interpolate('%{TCOM|| <%{TCOM}> }') eq ' <> ', 'false ||-interpolation'); my $res = $mp3->interpolate('aa%{I(f)xxxtest12.mp3}bb'); print "# `$res'\n"; ok($res eq 'aacontentbb', "I(f) interpolates"); $res = $mp3->interpolate('aa%{I(if)xxx%f}bb'); ok($res eq 'aacontentbb', "I(fi) interpolates"); $res = $mp3->interpolate('aa%{I(if)xxx%{f||not_present}}bb'); ok($res eq 'aacontentbb', "I(fi) interpolates with choice"); $res = $mp3->interpolate('aa%{I(if)%{!y:xxx%{f||not_present}}}bb'); ok($res eq 'aacontentbb', "I(fi) interpolates with conditional with choice "); $res = $mp3->interpolate('aa%{COMM03:%{I(if)%{!y:xxx%{f||not_present}}}}bb'); ok($res eq 'aabb', "I(fi) interpolates in conditional"); $res = $mp3->interpolate('aa%{ID3v2:%{frames/, }--}bb'); ok($res eq 'aabb', "%{frames} interpolates in conditional"); $res = $mp3->format_time(56345.62, qw(?Hh ?{mL}m {SL} ?{ML})); print "# `$res'\n"; ok($res eq '15h39m05.620', "format time 56345.62"); $res = $mp3->format_time(56345, qw(?Hh ?{mL}m {SL} ?{ML})); print "# `$res'\n"; ok($res eq '15h39m05', "format time 56345"); $res = $mp3->format_time(2345.62, qw(?Hh ?{mL}m {SL} ?{ML})); print "# `$res'\n"; ok($res eq '39m05.620', "format time 2345.62"); $res = $mp3->format_time(2345, qw(?Hh ?{mL}m {SL} ?{ML})); print "# `$res'\n"; ok($res eq '39m05', "format time 2345"); $res = $mp3->format_time(5.62, qw(?Hh ?{mL}m {SL} ?{ML})); print "# `$res'\n"; ok($res eq '5.620', "format time 5.62"); $res = $mp3->format_time(5, qw(?Hh ?{mL}m {SL} ?{ML})); print "# `$res'\n"; ok($res eq '5', "format time 5"); $res = $mp3->format_time(56345.62, qw(?Hh ?{mL}m {SML}s)); print "# `$res'\n"; ok($res eq '15h39m05.62s', "format time 56345.62"); $res = $mp3->format_time(56345, qw(?Hh ?{mL}m {SML}s)); print "# `$res'\n"; ok($res eq '15h39m05s', "format time 56345"); $res = $mp3->format_time(2345.62, qw(?Hh ?{mL}m {SML}s)); print "# `$res'\n"; ok($res eq '39m05.62s', "format time 2345.62"); $res = $mp3->format_time(2345, qw(?Hh ?{mL}m {SML}s)); print "# `$res'\n"; ok($res eq '39m05s', "format time 2345"); $res = $mp3->format_time(5.62, qw(?Hh ?{mL}m {SML}s)); print "# `$res'\n"; ok($res eq '5.62s', "format time 5.62"); $res = $mp3->format_time(5, qw(?Hh ?{mL}m {SML}s)); print "# `$res'\n"; ok($res eq '5s', "format time 5"); $res = MP3::Tag->format_time(5.62, qw(?Hh ?{mL}m {SML}s)); print "# `$res'\n"; ok($res eq '5.62s', "format time 5.62"); { my $f = MP3::Tag->new_fake; $res = $f->format_time(5.62, qw(?Hh ?{mL}m {SML}s)); print "# `$res'\n"; ok($res eq '5.62s', "format time 5.62"); } { local $mp3->{ms} = 2345.62 * 1000; # Pretend the length is as given $res = $mp3->interpolate("%{T[?Hh,?{mL}m,{SML}s]}"); print "# `$res'\n"; ok($res eq '39m05.62s', "format time 2345.62"); } my @failed; #@failed ? die "Tests @failed failed.\n" : print "All tests successful.\n"; sub ok_test { my ($result, $test) = @_; printf ("Test %2d %s %s", ++$count, $test, '.' x (45-length($test))); (push @failed, $count), print " not" unless $result; print " ok\n"; } sub ok { my ($result, $test) = @_; (push @failed, $count), print "not " unless $result; printf "ok %d # %s\n", ++$count, $test; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/t/mp3tag.t�����������������������������������������������������������������������������0000700�0000000�0000000�00000037562�11100222026�011247� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl test.pl' ######################### We start with some black magic to print on failure. # Change 1..1 below to 1..last_test_to_print . # (It may become useful if the test is moved to ./t subdirectory.) BEGIN { $| = 1; print "1..137\n"; $ENV{MP3TAG_SKIP_LOCAL} = 1} END {print "MP3::Tag not loaded :(\n" unless $loaded;} use MP3::Tag; $loaded = 1; $count = 0; ok(1,"MP3::Tag initialized"); ######################### End of black magic. # Insert your test code below (better if it prints "ok 13" # (correspondingly "not ok 13") depending on the success of chunk 13 # of the test code): #test - getting the tags $mp3 = MP3::Tag->new("test.mp3"); $mp3->get_tags; $v1 = $mp3->{ID3v1}; ok($v1,"Detecting ID3v1"); $v2 = $mp3->{ID3v2}; ok($v2,"Detecting ID3v2"); #test - reading ID3v1 ok(($v1 && ($v1->song eq "Song") && ($v1->track == 10)),"Reading ID3v1"); ok(($mp3->title eq "Only a test with a ID3v1 and ID3v2 tag") && ($mp3->track == 10),"Reading ID3v1/2 via Tag"); ok($mp3->comment eq "test", "Reading ID3v1 comment via Tag"); ok($mp3->year eq 2000, "Reading ID3v1 year via Tag"); ok($mp3->genre eq 'Ska', "Reading ID3v1 genre via Tag"); #test - reading ID3v2 ok($v2 && $v2->get_frame("COMM")->{Description} eq "Test!","Reading ID3v2"); {local *F; open F, '>test2.mp3' or warn; print F 'empty'} $mp3 = MP3::Tag->new("test2.mp3"); $mp3->new_tag("ID3v1"); $v1 = $mp3->{ID3v1}; $mp3->new_tag("ID3v2"); $v2 = $mp3->{ID3v2}; #test - creating/changing/writing ID3v1 ok($v1 && join("",$v1->all("New","a","a",2000,"c",10,"Ska")), "Creating new ID3v1"); ok($v1 && $v1->write_tag,"Writing ID3v1"); ok($v1 && $v1->artist("Artist"), "Changing ID3v1"); ok($v1 && $v1->write_tag,"Writing ID3v1"); #test - creating/changing/writing ID3v2 ok($v2 && $v2->add_frame("TLAN","ENG"),"Creating new ID3v2"); ok($v2 && $v2->write_tag,"Writing ID3v2"); ok($v2 && $v2->add_frame("TLAN","GER"),"Changing ID3v2"); ok($v2 && $v2->year('1848-9,1864-1872'),"Writing ID3v2 complex timestamp"); ok($v2 && $v2->write_tag,"Writing ID3v2"); $mp3=$v1=$v2=undef; # Close the file... ok(((stat("test2.mp3"))[7] % 512) == 0," ID3v2 rounding size"); $mp3 = MP3::Tag->new("test2.mp3"); $mp3->get_tags; $v1 = $mp3->{ID3v1}; $v2 = $mp3->{ID3v2}; #test 10 - reading new ID3v1 ok($v1 && $v1->song eq "New" && $v1->artist eq "Artist","Checking new ID3v1"); ok($v1 && $v1->title eq "New","Checking new ID3v1"); ok($v1 && $mp3->autoinfo->{title} eq "New","Checking new ID3v1"); #test 11 - reading new ID3v2 ok($v2 && $v2->get_frame("TLAN") eq "ENG" && $v2->get_frame("TLAN01") eq "GER","Checking new ID3v2"); #test 16 - reading new ID3v2 ok($v2 && (@f = $v2->get_frame("TLAN")) && @f == 3 && "@f[0,2]" eq "ENG GER", "Checking multi-frame ID3v2"); ok($v2 && (@f = $v2->get_frames("TLAN")) && @f == 3 && "@f[1,2]" eq "ENG GER", "Checking multi-frame ID3v2"); #test 18 - comment ok($v2 && !defined $v2->comment(), "Checking no comment"); # year ok($v2 && (@f = $v2->get_frames("TDRC", 'intact')) && @f == 2 && $f[-1] eq "1848-09\0001864/1872", "Checking timestamp(s) in ID3v2"); ok($v2 && ($y = $v2->year) && $y eq "1848-09,1864--1872", "Checking ID3v2 year"); ok($v2 && $v2->add_frame("COMM", "ENG", '', 'Testing...'), "Changing ID3v2 ''-comment"); ok($v2 && $v2->write_tag,"Writing ID3v2"); $mp3 = MP3::Tag->new("test2.mp3"); $mp3->get_tags; $v2 = $mp3->{ID3v2}; ok($v2 && $v2->comment() eq 'Testing...', "Checking any-language comment"); ok($v2 && $v2->comment('Another test...', '', "ENG"), "Setting ID3v2-comment"); ok($v2 && $v2->write_tag,"Writing ID3v2"); $mp3 = MP3::Tag->new("test2.mp3"); $mp3->get_tags; $v1 = $mp3->{ID3v1}; $v2 = $mp3->{ID3v2}; ok($v2 && $v2->comment() eq 'Another test...', "Checking any-language comment"); ok($v2 && !defined $v2->_comment('GER'), "Checking no GER comment"); ok($v2 && $v2->_comment('ENG') eq 'Another test...', "Checking ENG comment"); ok($v2 && $mp3->comment() eq 'Another test...', "Checking ID3 comment"); my $s = $mp3->interpolate('%%02t_Title: `%012.12t\'; %{TLAN} %{TLAN01: have %{TLAN01}} %{!TLAN02:, do not have TLAN02}'); print "# `$s'\n"; # %02t_Title: `000000000New'; ENG have ENG , do not have TLAN02 ok($s && $s eq "%02t_Title: `000000000New'; ENG have GER , do not have TLAN02", "Checking ID3 interpolation"); #back to original tag open (FH, ">test2.mp3") or warn; binmode FH; print FH "empty"; close FH or warn; # Check the .inf parsing open FH, ">test2.inf" or warn; print FH <<EOP; #created by test script # CDINDEX_DISCID= 'nFif1ufKKowDai0uJk8E7b_B1cw-' CDDB_DISCID= 0xe60ca011 MCN= ISRC= # Albumperformer= 'Some Choir' Performer= 'Bach (2110)' Albumtitle= 'Liturgy of St. John; Op 31' Tracktitle= 'It Is Truly Meet' Tracknumber= 11 Trackstart= 174717 # track length in sectors (1/75 seconds each), rest samples Tracklength= 10533, 0 Pre-emphasis= no Channels= 2 Copy_permitted= once (copyright protected) Endianess= little # index list Index= 0 Year= 1988 Trackcomment= 'Chiribim conducts Some Choir; recorded in Mariann' EOP close FH or warn; $mp3 = MP3::Tag->new("./test2.mp3"); $mp3->get_tags; my $inf = $mp3->{Inf}; #my @a = %$mp3; #warn "@a"; ok($inf, ".inf file parsed"); ok($inf && $mp3->autoinfo->{title} eq 'It Is Truly Meet', "Checking .inf title"); ok($inf && $mp3->autoinfo->{artist} eq 'Bach (2110)', "Checking .inf artist"); ok($inf && $mp3->autoinfo->{track} eq 11, "Checking .inf track"); ok($inf && $mp3->autoinfo->{album} eq 'Liturgy of St. John; Op 31', "Checking .inf album"); ok($inf && $mp3->autoinfo->{year} eq 1988, "Checking .inf year"); ok($inf && $mp3->autoinfo->{comment} eq 'Chiribim conducts Some Choir; recorded in Mariann', "Checking .inf comment"); ok($inf && $mp3->autoinfo('from')->{comment}[0] eq 'Chiribim conducts Some Choir; recorded in Mariann', "Checking .inf comment+source"); ok($inf && $mp3->autoinfo('from')->{comment}[1] eq 'Inf', "Checking .inf comment source"); use File::Spec; require File::Basename; my $i = $mp3->interpolate('file=%(_)-12f, File=%F, %%comment="%c", dir="%{d0}"'); # Skip a good test on 5.005 my $checkname = (File::Spec->can('rel2abs') ? 'test2.mp3' : './test2.mp3'); my $ii = 'file=test2.mp3___, File=' . MP3::Tag->rel2abs($checkname) . ', %comment="Chiribim conducts Some Choir; recorded in Mariann"' . ', dir="' . scalar(File::Basename::fileparse(File::Basename::dirname(MP3::Tag->rel2abs('test2.mp3')),"")) . '"'; #warn "$i\n$ii\n"; ok($inf && $i eq $ii, "Checking interpolation: `$i' eq `$ii'"); ok($mp3->filename_nodir eq "test2.mp3", "Checking filename method:"); ok($mp3 && $mp3->interpolate("%A.%e") eq $mp3->interpolate("%F"), "interpolate %A"); # Check CDDB_File... ok(MP3::Tag->config('cddb_files', qw(cddb.tm1 cddb.tm cddb.tm2)), "Configuring list of cddb_files"); open NH, '>audio07.mp3' or warn; close NH; $mp3 = MP3::Tag->new("./audio07.mp3"); ok($mp3 && $mp3->title eq 'Makrokosmos III - I. Nocturnal Sounds (The Awakening)', "Title via CDDB_File"); ok($mp3 && $mp3->artist eq 'Crumb Piece', "Artist via CDDB_File"); ok($mp3 && $mp3->album eq 'Ancient Voices', "Album via CDDB_File"); ok($mp3 && $mp3->year eq '1234', "Year via CDDB_File"); ok($mp3 && $mp3->comment eq 'comment7; Fake entry', "Comment via CDDB_File"); # print STDERR "# Genre=", $mp3->genre, "\n"; ok($mp3 && $mp3->genre eq 'Vocal', "Genre via CDDB_File"); ok($mp3 && $mp3->track eq '7', "Track no with CDDB_File"); ok($mp3 && (not defined $mp3->artist_collection), "artist_collection"); open NH, '>audio08.mp3' or warn; close NH; $mp3 = MP3::Tag->new("./audio08.mp3"); ok($mp3 && $mp3->year eq '2001-10-23--30,2002-02-28', "Year via CDDB_File"); ok($mp3 && $mp3->comment_collection eq 'Fake entry', "comment_collection"); # print STDERR "# cT=", $mp3->comment_track, "\n"; ok($mp3 && $mp3->comment_track eq 'comment8; Recorded on 2001-10-23--30,2002-02-28', "comment_track"); ok($mp3 && $mp3->artist_collection eq 'Crumb Piece', "artist_collection"); ok($mp3 && $mp3->artist eq 'Piece of Crumb', "artist"); ok($mp3 && $mp3->interpolate('%{aC}') eq 'Crumb Piece', "artist_collection via %{aC}"); ok(MP3::Tag->config('comment_remove_date', 1), "Configuring comment_remove_date"); $mp3 = MP3::Tag->new("./audio08.mp3"); # print STDERR "# cT=", $mp3->comment_track, "\n"; ok($mp3 && $mp3->comment_track eq 'comment8', "comment_track with removal"); ok(MP3::Tag->config('cddb_files', qw(cddb.tmp1 cddb.tmp cddb.tmp2)), "Configuring2 list of cddb_files"); open NH, '>audio07.mp3' or warn; close NH; $mp3 = MP3::Tag->new("./audio07.mp3"); ok($mp3 && $mp3->title eq 'Makrokosmos III - I. Nocturnal Sounds (The Awakening)', "Title via CDDB_File"); ok($mp3 && $mp3->artist eq 'Crumb Piece', "Artist via CDDB_File"); ok($mp3 && $mp3->album eq 'Ancient Voices', "Album via CDDB_File"); ok($mp3 && $mp3->year eq '1234', "Year via CDDB_File"); ok($mp3 && $mp3->comment eq 'comment7; Fake entry', "Comment via CDDB_File"); ok($mp3 && $mp3->genre eq 'A special genre', "Genre via CDDB_File"); ok($mp3 && $mp3->track eq '7', "Track no with CDDB_File"); open NH, '>audio_07.mp3' or warn; close NH; $mp3 = MP3::Tag->new("./audio_07.mp3"); $mp3->config(parse_data => ['m', 'no comment', '%c']); ok($mp3 && $mp3->title eq 'Makrokosmos III - I. Nocturnal Sounds (The Awakening)', "Title via CDDB_File with force"); ok($mp3 && $mp3->comment eq 'no comment', "Forced comment"); $mp3 = MP3::Tag->new("./audio_07.mp3"); $mp3->config(parse_data => ['im', '<%c>', '%t']); ok($mp3 && $mp3->artist eq 'Crumb Piece', "Artist via CDDB_File with force/interpolate"); ok($mp3 && $mp3->title eq '<comment7; Fake entry>', "Force/interpolated title"); $mp3 = MP3::Tag->new("./audio_07.mp3"); $mp3->config(parse_data => ['im', '[%t]' => '%t'], ['im', '<%t>' => '%c']); ok($mp3 && $mp3->comment eq '<[Makrokosmos III - I. Nocturnal Sounds (The Awakening)]>', "Force/interpolated recursive comment"); ok($mp3 && $mp3->title eq '[Makrokosmos III - I. Nocturnal Sounds (The Awakening)]', "Force/interpolated recursive title"); $mp3 = MP3::Tag->new("audio_07.mp3"); $mp3->config(parse_data => ['im', '%f', '%c_%n.mp3'], ['mz', '' => '%g']); ok($mp3 && $mp3->comment eq 'audio', "comment via parse"); ok($mp3 && $mp3->track eq '7', "track via parse"); ok($mp3 && $mp3->comment eq 'audio', "comment via cached parse"); ok($mp3 && $mp3->title eq 'Makrokosmos III - I. Nocturnal Sounds (The Awakening)', "title with parse"); $s = $mp3->interpolate("%03n_%{!g: Have only comment=<%c>}<%g>%c"); ok($mp3 && $s eq '007_ Have only comment=<audio><>audio', "conditional interpolation"); $mp3 = MP3::Tag->new("./audio_07.mp3"); $mp3->config(parse_data => ['iRm', 'my/dir/%f', '/%c/%c%E']); ok($mp3 && $mp3->comment eq 'dir; audio_07', "multi-%c via parse/interpolate"); $mp3 = MP3::Tag->new("./audio_07.mp3"); $mp3->config(parse_data => ['iRm', 'my/dir/%f', '/%c/%c%=E']); ok($mp3 && $mp3->comment eq 'dir; audio_07', "multi-%c and %=E via parse/interpolate"); $mp3 = MP3::Tag->new("./audio_07.mp3"); $mp3->config(parse_data => ['im', 'my/dir/%f', '%t/%c/%c.%e']); $i = $mp3->comment; #warn "<$i>\n"; ok($mp3 && $i eq 'dir; audio_07', "multi-%c and %e via parse/interpolate"); $mp3 = MP3::Tag->new("./audio_07.mp3"); ok($mp3 && $mp3->genre_set('foobar / baz'), "set genre to something complicated to trigger v2"); ok($mp3 && $mp3->update_tags({genre => '(18)'}), "set genre to (18)"); $mp3 = MP3::Tag->new("./audio_07.mp3"); ok($mp3 && $mp3->genre() eq 'Techno', "get genre"); ok($mp3 && $mp3->{ID3v1}->genre() eq 'Techno', "get v1 genre"); ok($mp3 && $mp3->{ID3v2}, "v2 was set"); $mp3 = MP3::Tag->new("./audio_07.mp3"); ok($mp3 && $mp3->update_tags({genre => 18}), "set genre to 18"); $mp3 = MP3::Tag->new("./audio_07.mp3"); ok($mp3 && $mp3->genre() eq 'Techno', "get genre"); ok($mp3 && $mp3->{ID3v1}->genre() eq 'Techno', "get v1 genre"); $mp3 = MP3::Tag->new("./audio_07.mp3"); # max=147 SynthPop ok($mp3 && $mp3->update_tags({genre => '(147)'}), "set genre to (147)"); $mp3 = MP3::Tag->new("./audio_07.mp3"); ok($mp3 && $mp3->genre() eq 'SynthPop', "get genre"); ok($mp3 && $mp3->{ID3v1}->genre() eq 'SynthPop', "get v1 genre"); $mp3 = MP3::Tag->new("./audio_07.mp3"); # max=147 SynthPop ok($mp3 && $mp3->update_tags({genre => '147'}), "set genre to 147"); $mp3 = MP3::Tag->new("./audio_07.mp3"); ok($mp3 && $mp3->genre() eq 'SynthPop', "get genre"); ok($mp3 && $mp3->{ID3v1}->genre() eq 'SynthPop', "get v1 genre"); $mp3 = MP3::Tag->new("./audio_07.mp3"); # max=149 SynthPop ok($mp3 && $mp3->update_tags({genre => '(149)'}), "set genre to (149)"); $mp3 = MP3::Tag->new("./audio_07.mp3"); ok($mp3 && $mp3->genre() eq '(149)', "get genre"); my $g = $mp3->{ID3v1}->genre(); print "# g=<$g>\n"; ok($mp3 && $g eq '', "get v1 genre"); $mp3 = MP3::Tag->new("./audio_07.mp3"); # max=149 SynthPop ok($mp3 && $mp3->update_tags({genre => '149'}), "set genre to 149"); $mp3 = MP3::Tag->new("./audio_07.mp3"); ok($mp3 && $mp3->genre() eq '(149)', "get genre"); $g = $mp3->{ID3v1}->genre(); print "# g=<$g>\n"; ok($mp3 && $g eq '', "get v1 genre"); open NH, '>audio_07.mp3' or warn; close NH; $mp3 = MP3::Tag->new("./audio_07.mp3"); ok($mp3->interpolate("%{ID3v1:have v1}/%{!ID3v1:no v1}; %{ID3v2:have v2}/%{!ID3v2:no v2}") eq '/no v1; /no v2', 'conditional interpolate'); ok($mp3 && $mp3->update_tags({genre => '(18)'}), "set genre to (18)"); $mp3 = MP3::Tag->new("./audio_07.mp3"); ok($mp3 && $mp3->genre() eq 'Techno', "get genre"); ok($mp3 && $mp3->{ID3v1}->genre() eq 'Techno', "get v1 genre"); ok($mp3->interpolate("%{ID3v1:have v1}/%{!ID3v1:no v1}; %{ID3v2:have v2}/%{!ID3v2:no v2}") eq 'have v1/; /no v2', 'conditional interpolate'); $mp3 = MP3::Tag->new("./audio_07.mp3"); ok($mp3 && $mp3->update_tags({genre => 18}), "set genre to 18"); $mp3 = MP3::Tag->new("./audio_07.mp3"); ok($mp3 && $mp3->genre() eq 'Techno', "get genre"); ok($mp3 && $mp3->{ID3v1}->genre() eq 'Techno', "get v1 genre"); $mp3 = MP3::Tag->new("./audio_07.mp3"); # max=147 SynthPop ok($mp3 && $mp3->update_tags({genre => '(147)'}), "set genre to (147)"); $mp3 = MP3::Tag->new("./audio_07.mp3"); ok($mp3 && $mp3->genre() eq 'SynthPop', "get genre"); ok($mp3 && $mp3->{ID3v1}->genre() eq 'SynthPop', "get v1 genre"); $mp3 = MP3::Tag->new("./audio_07.mp3"); # max=147 SynthPop ok($mp3 && $mp3->update_tags({genre => '147'}), "set genre to 147"); $mp3 = MP3::Tag->new("./audio_07.mp3"); ok($mp3 && $mp3->genre() eq 'SynthPop', "get genre"); ok($mp3 && $mp3->{ID3v1}->genre() eq 'SynthPop', "get v1 genre"); ok($mp3 && !$mp3->{ID3v2}, "no v2 was set"); $mp3 = MP3::Tag->new("./audio_07.mp3"); # max=147 SynthPop ok($mp3 && $mp3->update_tags({genre => '(149)'}), "set genre to (149)"); $mp3 = MP3::Tag->new("./audio_07.mp3"); ok($mp3 && $mp3->genre() eq '(149)', "get genre"); $g = $mp3->{ID3v1}->genre(); print "# g=<$g>\n"; ok($mp3 && $g eq '', "get v1 genre"); $mp3 = MP3::Tag->new("./audio_07.mp3"); # max=147 SynthPop ok($mp3 && $mp3->update_tags({genre => '149', track => '2/4'}), "set genre to 149, track"); $mp3 = MP3::Tag->new("./audio_07.mp3"); ok($mp3 && $mp3->genre() eq '(149)', "get genre"); $g = $mp3->{ID3v1}->genre(); print "# g=<$g>\n"; ok($mp3 && $g eq '', "get v1 genre"); ok($mp3 && $mp3->track() eq '2/4', "get track"); ok($mp3 && $mp3->{ID3v1}->track() eq '2', "get v1 track"); ok(0 == ((-s "./audio_07.mp3") % 512), "padding to sector"); ok($mp3->interpolate("%{ID3v1:have v1}/%{!ID3v1:no v1}; %{ID3v2:have v2}/%{!ID3v2:no v2}") eq 'have v1/; have v2/', 'conditional interpolate'); open NH, '>audio_07.mp3' or warn; close NH; $mp3 = MP3::Tag->new("./audio_07.mp3"); # max=147 SynthPop ok($mp3 && ($mp3->genre_set(113) or 1), 'set genre'); ok($mp3 && $mp3->{ID3v1} && $mp3->{ID3v1}->genre() eq 'Tango', 'get v1 genre'); ok($mp3 && $mp3->genre() eq 'Tango', 'get genre'); my @failed; #@failed ? die "Tests @failed failed.\n" : print "All tests successful.\n"; sub ok_test { my ($result, $test) = @_; printf ("Test %2d %s %s", ++$count, $test, '.' x (45-length($test))); (push @failed, $count), print " not" unless $result; print " ok\n"; } sub ok { my ($result, $test) = @_; (push @failed, $count), print "not " unless $result; printf "ok %d # %s\n", ++$count, $test; } ����������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/t/parser.t�����������������������������������������������������������������������������0000700�0000000�0000000�00000004776�10431330050�011354� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl test.pl' ######################### We start with some black magic to print on failure. # Change 1..1 below to 1..last_test_to_print . # (It may become useful if the test is moved to ./t subdirectory.) BEGIN { $| = 1; print "1..20\n"; $ENV{MP3TAG_SKIP_LOCAL} = 1} END {print "MP3::Tag not loaded :(\n" unless $loaded;} use MP3::Tag; $loaded = 1; $count = 0; ok(1,"MP3::Tag initialized"); ######################### End of black magic. # Insert your test code below (better if it prints "ok 13" # (correspondingly "not ok 13") depending on the success of chunk 13 # of the test code): {local *F; open F, '>test12.mp3' or warn; print F 'empty'} $mp3 = MP3::Tag->new("test12.mp3"); ok($res = $mp3->parse('%t - %l', 'abc - def - xyz'), "Parsed greedily"); ok($res->{title} eq 'abc - def', "Parsed before correct"); ok($res->{album} eq 'xyz', "Parsed after correct"); ok($mp3->config(parse_minmatch => 1), 'Set parse_minmatch'); ok($res = $mp3->parse('%t - %l', 'abc - def - xyz'), "Parsed nongreedily"); ok($res->{title} eq 'abc', "Parsed before correct"); ok($res->{album} eq 'def - xyz', "Parsed after correct"); ok($res = $mp3->parse('%{COMM(rus,EN,#1,)[foo]}', 'abc - def - xyz'), "Parsed COMM(rus,EN,#1,)[foo]"); ok($res->{'COMM(rus,EN,#1,)[foo]'} eq 'abc - def - xyz', "Parsed after correct"); {local *F; open F, '>t12.mp3' or warn; print F 'empty'} $mp3 = MP3::Tag->new("t12.mp3"); ok($mp3, "Got tag"); ok(scalar ($mp3->config('parse_data',['bOD', "foo\n", '%B%B/%B%B.xxx']), 1), "config parsedata"); ok(!-e 't12t12', 'temporary directory not there'); ok(!-e 't12t12/t12t12.xxx', 'temporary output file not there'); unlink 't12t12/t12t12.xxx'; rmdir 't12t12'; ok(scalar ($mp3->title, 1), "Run the parser"); ok(-d 't12t12', 'Output directory created'); ok(-e 't12t12/t12t12.xxx', 'Output file created'); ok(4 == -s 't12t12/t12t12.xxx', 'Output file of correct size'); ok(unlink('t12t12/t12t12.xxx'), 'Remove output file'); ok(rmdir('t12t12'), 'Remove output directory'); my @failed; #@failed ? die "Tests @failed failed.\n" : print "All tests successful.\n"; sub ok_test { my ($result, $test) = @_; printf ("Test %2d %s %s", ++$count, $test, '.' x (45-length($test))); (push @failed, $count), print " not" unless $result; print " ok\n"; } sub ok { my ($result, $test) = @_; (push @failed, $count), print "not " unless $result; printf "ok %d # %s\n", ++$count, $test; } ��MP3-Tag-1.13/t/set_v2.t�����������������������������������������������������������������������������0000700�0000000�0000000�00000047641�11100456504�011267� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl test.pl' ######################### We start with some black magic to print on failure. # Change 1..1 below to 1..last_test_to_print . # (It may become useful if the test is moved to ./t subdirectory.) BEGIN { $| = 1; print "1..208\n"; $ENV{MP3TAG_SKIP_LOCAL} = 1} END {print "MP3::Tag not loaded :(\n" unless $loaded;} use MP3::Tag; $loaded = 1; $count = 0; ok(1,"MP3::Tag initialized"); ######################### End of black magic. # Insert your test code below (better if it prints "ok 13" # (correspondingly "not ok 13") depending on the success of chunk 13 # of the test code): {local *F; open F, '>test12.mp3' or warn; print F 'empty'} $mp3 = MP3::Tag->new("test12.mp3"); ok($mp3, "Got tag"); ok(scalar ($mp3->set_id3v2_frame('TIT1','this is my tit1'),1), "set_id3v2_frame"); ok(scalar ($mp3->set_id3v2_frame('TCON','(254)(102)CHANSON(101)'),1), "set_id3v2_frame TCON"); ok(scalar ($mp3->set_id3v2_frame('TCOP','Copyright (C) foobar'),1), "set_id3v2_frame TCOP"); ok($mp3->{ID3v2}, "ID3v2 tag autocreated"); ok(($mp3->{ID3v2} and $mp3->{ID3v2}->write_tag),"Writing ID3v2"); $mp3 = MP3::Tag->new("test12.mp3"); ok($mp3->title() eq 'this is my tit1',"Got ID3v2"); my $g = $mp3->genre(); print "# <genre>=<$g>\n"; # Chanson --> 102 ok($g eq '(254) / Chanson / Speech',"ID3v2 TCON parsed"); $g = ($mp3->{ID3v2}->get_frames('TCON', 'raw'))[1]; print "# <rawgenre>=<$g>\n"; # Starts with encoding=\0 ok($g eq "\0(254)(102)(101)","ID3v2 TCON stored right"); $g = $mp3->select_id3v2_frame('TCOP'); print "# <TCOP>=<$g>\n"; ok($g eq '(C) foobar',"ID3v2 TCOP parsed"); $g = ($mp3->{ID3v2}->get_frames('TCOP', 'raw'))[1]; print "# <rawTCOP>=<$g>\n"; # Starts with encoding=\0 ok($g eq "\0foobar","ID3v2 TCOP stored right"); my $res = $mp3->interpolate('aa%{ID3v2:<<%{frames/, }>>}bb'); ok($res =~ /^aa<<(\w{4}, )+\w{4}>>bb$/, "%{frames} interpolates in conditional"); {local *F; open F, '>test12.mp3' or warn; print F 'empty'} $mp3 = MP3::Tag->new("test12.mp3"); ok($mp3, "Got tag"); ok(($mp3->update_tags({genre => '(254)CHANSONNETTE(102)'})),"Update tags"); $mp3 = MP3::Tag->new("test12.mp3"); ok($mp3->genre() eq '(254) / CHANSONNETTE / Chanson',"ID3v2 TCON parsed"); ok($mp3->{ID3v2}, "ID3v2 tag autocreated"); ok($mp3->{ID3v1}, "ID3v1 tag autocreated"); #warn "<<", $mp3->{ID3v1}->genre(), ">>\n"; ok($mp3->{ID3v1}->genre() eq 'Chanson',"ID3v1 genre was set"); {local *F; open F, '>test12.mp3' or warn; print F 'empty'} $mp3 = MP3::Tag->new("test12.mp3"); ok($mp3, "Got tag"); $res = $mp3->parse('%{TIT2}', 'another tit1'); ok($res, "parse %{TIT2}"); ok(1 == scalar keys %$res, "1 key"); #warn keys %$res; ok($res->{TIT2} eq 'another tit1', "key TIT2"); {local *F; open F, '>test12.mp3' or warn; print F 'empty'} $mp3 = MP3::Tag->new("test12.mp3"); ok($mp3, "Got tag"); ok(scalar ($mp3->config('parse_data',['mz', 'another tit1', '%{TIT2}']), 1), "config parsedata"); $t = $mp3->title; #warn "tit '$t'"; ok($t, "checking a field"); ok($mp3->{ID3v2}, "ID3v2 tag autocreated"); ok(($mp3->{ID3v2} and $mp3->{ID3v2}->write_tag),"Writing ID3v2"); $mp3 = MP3::Tag->new("test12.mp3"); ok($mp3->title() eq 'another tit1',"Got ID3v2"); {local *F; open F, '>test12.mp3' or warn; print F 'empty'} $mp3 = MP3::Tag->new("test12.mp3"); ok($mp3, "Got tag"); ok(scalar ($mp3->config('parse_data',['mz', 'another text', '%{TXXX[foo]}']), 1), "config parsedata TXXX[foo]"); ok($mp3->title, "prepare the data"); ok($mp3->interpolate('%{TXXX}'), "have TXXX"); ok(!$mp3->interpolate('%{TXXX01}'), "no TXXX01"); ok(!$mp3->interpolate('%{TXXX02}'), "no TXXX02"); ok($mp3->update_tags, 'update'); ok($mp3 = MP3::Tag->new("test12.mp3"), 'reinit ourselves'); ok($mp3->title, "prepare the data"); ok($mp3->interpolate('%{TXXX}'), "have TXXX"); ok(!$mp3->interpolate('%{TXXX01}'), "no TXXX01"); ok(!$mp3->interpolate('%{TXXX02}'), "no TXXX02"); ok($mp3 = MP3::Tag->new("test12.mp3"), 'reinit ourselves'); ok($mp3->select_id3v2_frame('COMM', 'short', 'yyy', 'this is my COMM(yyy)[short]'), "select_id3v2_frame for write"); ok($mp3->select_id3v2_frame_by_descr('TXXX[with[]]', 'this is my TXXX[with[]]'), "select_id3v2_frame_by_descr for write"); ok($mp3->update_tags, 'update'); ok($mp3 = MP3::Tag->new("test12.mp3"), 'reinit ourselves'); ok($mp3->select_id3v2_frame('COMM', 'short', 'yyy') eq 'this is my COMM(yyy)[short]', "select_id3v2_frame for read"); ok($mp3->select_id3v2_frame('COMM', 'short', 'yYy') eq 'this is my COMM(yyy)[short]', "select_id3v2_frame for read"); ok($mp3->select_id3v2_frame('COMM', 'short', '') eq 'this is my COMM(yyy)[short]', "select_id3v2_frame for read"); ok($mp3->select_id3v2_frame('TXXX', 'with[]') eq 'this is my TXXX[with[]]', "select_id3v2_frame for read, TXXX with []"); ok($mp3->select_id3v2_frame_by_descr('TXXX[with[]]') eq 'this is my TXXX[with[]]', "select_id3v2_frame_by_descr for read, TXXX with []"); # these returns hash ok($mp3->select_id3v2_frame('COMM', 'short', undef)->{Text} eq 'this is my COMM(yyy)[short]', "select_id3v2_frame for read, lang=undef"); ok($mp3->select_id3v2_frame('COMM', 'short')->{Text} eq 'this is my COMM(yyy)[short]', "select_id3v2_frame for read"); ok(! defined ($mp3->select_id3v2_frame('COMM', 'short', [])), "select_id3v2_frame for read, empty lang"); ok($mp3->select_id3v2_frame('COMM', undef, 'yyy')->{Text} eq 'this is my COMM(yyy)[short]', "select_id3v2_frame for read"); ok($mp3->select_id3v2_frame('COMM', undef, 'yYy')->{Text} eq 'this is my COMM(yyy)[short]', "select_id3v2_frame for read"); ok($mp3->select_id3v2_frame('COMM', undef, '')->{Text} eq 'this is my COMM(yyy)[short]', "select_id3v2_frame for read"); ok($mp3->select_id3v2_frame('COMM', undef, undef)->{Text} eq 'this is my COMM(yyy)[short]', "select_id3v2_frame for read, Lang/descr are undef"); ok($mp3->select_id3v2_frame('COMM', undef)->{Text} eq 'this is my COMM(yyy)[short]', "select_id3v2_frame for read"); ok($mp3->select_id3v2_frame('COMM')->{Text} eq 'this is my COMM(yyy)[short]', "select_id3v2_frame for read"); ok((not defined $mp3->select_id3v2_frame('COMM', '', [])), "select_id3v2_frame for read"); # Check growth of tags ok($mp3 = MP3::Tag->new("test12.mp3"), 'reinit ourselves'); $mp3->get_tags; my $osize = $mp3->{ID3v2}->{tagsize}; ok($osize>0, 'got size'); ok($mp3->select_id3v2_frame('COMM', 'short', 'yyy', 'my COMM(yyy)[short]'), "select_id3v2_frame for write, make shorter"); ok($mp3->update_tags, 'update'); ok($mp3 = MP3::Tag->new("test12.mp3"), 'reinit ourselves'); $mp3->get_tags; my $nsize = $mp3->{ID3v2}->{tagsize}; ok($nsize>0, 'got size'); ok($nsize <= $osize, 'size did not grow'); ok($mp3->select_id3v2_frame('COMM', 'short', 'yyy', 'this is my COMM(yyy)[short]'), "select_id3v2_frame for write, make longer - as it was"); ok($mp3->update_tags, 'update'); ok($mp3 = MP3::Tag->new("test12.mp3"), 'reinit ourselves'); $mp3->get_tags; my $nnsize = $mp3->{ID3v2}->{tagsize}; ok($nnsize>0, 'got size'); ok($nnsize >= $nsize, 'size did not shrink'); ok($nnsize <= $osize, 'size did not grow w.r.t. initial size'); #print STDERR "sizes: $osize, $nsize, $nnsize\n"; MP3::Tag->config(id3v2_shrink => 1); MP3::Tag->config(id3v2_mergepadding => 1); {local *F; open F, '>test13.mp3' or warn; print F "\0" x 1500, "\1", "\0" x 1499} sub has_1 ($;$) { my ($f, $has_v1) = (shift, shift) or die; open F, "<$f" or die; binmode F; seek F, 0, 2 or die "seek-end: $!"; my $p = tell F; my $off = 1500 + ($has_v1 ? 128 : 0); # print STDERR "off=$p ($f) ", -s $f, "\n"; return 0 unless $p >= $off; seek F, -$off, 2 or die "seek -$off: $!"; my $in; read F, $in, 1 or die; close F or die; return $in eq "\1"; } ok(has_1("test13.mp3"), "has 1"); ok($mp3 = MP3::Tag->new("test13.mp3"), 'reinit from new file'); $mp3->get_tags; ok($mp3->update_tags({title => "Some very very very very very very long title"}), 'update'); ok(int((511 + -s 'test13.mp3')/512) == int((511 + 3000 + 10 + $mp3->{ID3v2}->{tagsize})/512), 'size ok (but this may change if we look for padding more aggressively)'); my $ts = $mp3->{ID3v2}->{tagsize}; ok(has_1("test13.mp3", 1), "has 1"); ok($mp3 = MP3::Tag->new("test13.mp3"), 'reinit'); $mp3->get_tags; ok($mp3->{ID3v2}->{tagsize} == $ts, 'tagsize the same'); ok($mp3->{ID3v2}->{buggy_padding_size} == 1500, 'padding_size the same'); #print STDERR "padding_found: $mp3->{ID3v2}->{padding_size}\n"; ok($mp3->update_tags({title => "Another very very very very very very long title"}), 'update'); ok($mp3->{ID3v2}->{tagsize} <= 512+128+100, 'tagsize small enough'); ok(has_1("test13.mp3", 1), "has 1"); ok($mp3->title =~ /Another/, "Title preserved"); ok($mp3 = MP3::Tag->new("test13.mp3"), 'reinit'); $mp3->get_tags; #print STDERR "padding_found: $mp3->{ID3v2}->{padding_size}\n"; ok($mp3->update_tags({title => "Other very very very very very very long title"}), 'update'); ok($mp3->{ID3v2}->{tagsize} <= 512+128+100, 'tagsize small enough'); #undef $mp3; ok(has_1("test13.mp3", 1), "has 1"); ok($mp3->title =~ /Other/, "Title preserved"); {local *F; open F, '>test12.mp3' or warn; print F 'empty'} $mp3 = MP3::Tag->new("test12.mp3"); ok($mp3, "Got tag"); my $id = 'a|a||a|||a}|}||}|||}|]|||]|||||]||||'; my $id1 = 'a|a||a|||a}|}||}|||}]|]||]||'; my $id0 = 'a|a||a|||a|}|||}|||||}|||||||}]|]||]||||'; $id =~ s/\|/\\/g; $id1 =~ s/\|/\\/g; $id0 =~ s/\|/\\/g; $mp3->select_id3v2_frame_by_descr("TXXX[$id1]", 'Val'); ok($mp3, "Set frame"); ok($mp3->select_id3v2_frame('TXXX', $id1) eq 'Val', "Frame is set indeed"); ok($mp3->select_id3v2_frame_by_descr("TXXX[$id1]") eq 'Val', "Frame is selectable"); ok($mp3->interpolate("%{TXXX[$id]}") eq 'Val', "Frame is interpolatable"); ok($mp3->interpolate("%{TXXX[$id]:Foo}") eq 'Foo', "Frame is conditionally interpolatable"); ok($mp3->interpolate("%{TXXX[$id]:$id0}") eq $id1, "Frame is conditionally interpolatable with complicated expansion"); ok($mp3->interpolate("%{!TXXX[o$id]:Foo}") eq 'Foo', "Frame is neg-conditionally interpolatable"); ok($mp3->interpolate("%{TXXX[$id]|TXXX[o$id]}") eq 'Val', "Frame is |-interpolatable"); ok($mp3->interpolate("%{TXXX[o$id]|TXXX[$id]}") eq 'Val', "Frame is |-interpolatable"); ok($mp3->interpolate("%{TXXX[o$id]||$id0}") eq $id1, "Frame is ||-interpolatable with complicated expansion"); ok($mp3->interpolate("%{TXXX[$id]||$id0}") eq 'Val', "Frame is ||-interpolatable with complicated expansion"); ok($mp3->interpolate("%{TXXX[o$id]||%{TXXX[$id]}}") eq 'Val', "Frame is ||-interpolatable with a frame in expansion"); ok($mp3->interpolate("%{TXXX[$id]&TXXX[$id]&TXXX[o$id]&TXXX[$id]}") eq 'Val; Val; Val', "Frame is &-interpolatable"); ok($mp3->update_tags(), 'update'); my $gif = <<EOF; 47 49 46 38 37 61 04 00 04 00 F0 00 00 02 02 02 00 00 00 2C 00 00 00 00 04 00 04 00 00 02 04 84 8F 09 05 00 3B EOF $gif = join '', map chr hex, split /\s+/, $gif; ok(37 == length $gif, 'correct gif data'); {local *F; open F, '>t_gif.mp3' or warn; print F 'empty'} $mp3 = MP3::Tag->new("t_gif.mp3"); ok($mp3, "Got tag"); ok($mp3->select_id3v2_frame_by_descr('APIC[try]', $gif), 'APIC set'); ok($mp3->update_tags(), 'update'); ok($mp3 = MP3::Tag->new("t_gif.mp3"), 'reinit'); ok($gif eq $mp3->select_id3v2_frame_by_descr('APIC[try]'), 'APIC read'); ok($gif eq $mp3->select_id3v2_frame_by_descr('APIC(Cover (front))[try]'), 'APIC read with picture type'); ok(!defined $mp3->select_id3v2_frame_by_descr('APIC(Cover (back))[try]'), 'APIC with missing picture type'); ok($gif eq $mp3->select_id3v2_frame_by_descr('APIC(Cover (front),Composer)[try]'), 'APIC read with 3 picture types'); ok($gif eq $mp3->select_id3v2_frame_by_descr('APIC(Artist/performer,Cover (front),Composer)[try]'), 'APIC read with 3 picture types'); ok($gif eq $mp3->select_id3v2_frame('APIC','try',['Artist/performer','Cover (front)','Composer']), 'APIC read lower-level with 3 picture types'); {local *F; open F, '>t_gif1.mp3' or warn; print F 'empty'} $mp3 = MP3::Tag->new("t_gif1.mp3"); ok($mp3, "Got tag"); ok($mp3->select_id3v2_frame_by_descr('APIC(Cover (back))[try]', $gif), 'APIC set'); ok($mp3->update_tags(), 'update'); ok($mp3 = MP3::Tag->new("t_gif1.mp3"), 'reinit'); ok($gif eq $mp3->select_id3v2_frame_by_descr('APIC'), 'APIC read'); ok($gif eq $mp3->select_id3v2_frame_by_descr('APIC[try]'), 'APIC read'); ok($gif eq $mp3->select_id3v2_frame_by_descr('APIC(Cover (back))[try]'), 'APIC read with picture type'); ok($gif eq $mp3->select_id3v2_frame_by_descr('APIC(Cover (back))'), 'APIC read with picture type, no descr'); ok(!defined $mp3->select_id3v2_frame_by_descr('APIC(Cover (front))[try]'), 'APIC with missing picture type'); ok(!defined $mp3->select_id3v2_frame_by_descr('APIC(Cover (front))[none-try]'), 'APIC with missing picture type and descr'); ok(!defined $mp3->select_id3v2_frame_by_descr('APIC(Cover (front))'), 'APIC with missing picture type and descr'); ok(!defined $mp3->select_id3v2_frame_by_descr('APIC(Cover (back))[none-try]'), 'APIC with missing descr'); ok(!defined $mp3->select_id3v2_frame_by_descr('APIC[none-try]'), 'APIC with missing descr'); ok($gif eq $mp3->select_id3v2_frame_by_descr('APIC(Cover (back),Composer)[try]'), 'APIC read with 3 picture types'); ok($gif eq $mp3->select_id3v2_frame_by_descr('APIC(Artist/performer,Cover (back),Composer)[try]'), 'APIC read with 3 picture types'); ok($gif eq $mp3->select_id3v2_frame('APIC','try',['Artist/performer','Cover (back)','Composer']), 'APIC read lower-level with 3 picture types'); {local *F; open F, '>t_gif2.mp3' or warn; print F 'empty'} $mp3 = MP3::Tag->new("t_gif2.mp3"); ok($mp3, "Got tag"); ok($mp3->select_id3v2_frame('APIC', 'try', 'Cover (back)', $gif), 'APIC set'); ok($mp3->update_tags(), 'update'); ok($mp3 = MP3::Tag->new("t_gif2.mp3"), 'reinit'); ok($gif eq $mp3->select_id3v2_frame_by_descr('APIC[try]'), 'APIC read'); ok($gif eq $mp3->select_id3v2_frame_by_descr('APIC(Cover (back))[try]'), 'APIC read with picture type'); ok(!defined $mp3->select_id3v2_frame_by_descr('APIC(Cover (front))[try]'), 'APIC with missing picture type'); ok($gif eq $mp3->select_id3v2_frame_by_descr('APIC(Cover (back),Composer)[try]'), 'APIC read with 3 picture types'); ok($gif eq $mp3->select_id3v2_frame_by_descr('APIC(Artist/performer,Cover (back),Composer)[try]'), 'APIC read with 3 picture types'); ok($gif eq $mp3->select_id3v2_frame('APIC','try',['Artist/performer','Cover (back)','Composer']), 'APIC read lower-level with 3 picture types'); ok($gif eq $mp3->select_id3v2_frame_by_descr('APIC(4)[try]'), 'APIC read with picture type 4'); ok($gif eq $mp3->select_id3v2_frame_by_descr("APIC(\x04)[try]"), 'APIC read with picture type \x04'); ok(!defined $mp3->select_id3v2_frame_by_descr('APIC(3)[try]'), 'APIC with missing picture type 3'); ok(!defined $mp3->select_id3v2_frame_by_descr("APIC(\x03)[try]"), 'APIC with missing picture type \x03'); my @descr = $mp3->id3v2_frame_descriptors(); print "# descr = @descr\n"; ok(scalar grep($_ eq 'APIC(Cover (back))[try]', @descr), 'descriptor found'); ok($mp3 = MP3::Tag->new("t_gif2.mp3"), 'reinit'); ok($mp3->select_id3v2_frame('APIC', 'try5', 5, $gif), 'APIC set'); ok($mp3->update_tags(), 'update'); ok($mp3 = MP3::Tag->new("t_gif2.mp3"), 'reinit'); @descr = $mp3->id3v2_frame_descriptors(); ok(scalar grep($_ eq 'APIC(Cover (back))[try]', @descr), 'descriptor found'); ok(scalar grep($_ eq 'APIC(Leaflet page)[try5]', @descr), 'another descriptor found'); ok( 0 <= index($mp3->interpolate('%{frames}'), 'APIC(Cover (back))[try]'), 'interpolate frames'); ok( 0 <= index($mp3->interpolate('%{frames}'), 'APIC(Leaflet page)[try5]'), 'interpolate frames try5'); ok( 0 <= index($mp3->interpolate('%{frames/, }'), 'APIC(Cover (back))[try], '), 'interpolate frames'); ok( 0 <= index($mp3->interpolate('%{frames/, }'), 'APIC(Leaflet page)[try5], '), 'interpolate frames try5'); ok($mp3->select_id3v2_frame_by_descr("APIC", undef), 'delete APIC'); ok(!$mp3->select_id3v2_frame_by_descr("APIC"), 'APIC deleted'); ok($mp3->{ID3v2}->add_frame("APIC", 'image/gif', 'Leaflet page', 'zzz', $gif), 'add APIC human'); ok($gif eq $mp3->select_id3v2_frame_by_descr('APIC(Leaflet page)[zzz]'), 'APIC read'); ok($mp3->select_id3v2_frame_by_descr("APIC", undef), 'delete APIC'); ok(!$mp3->select_id3v2_frame_by_descr("APIC"), 'APIC deleted'); ok($mp3->{ID3v2}->add_frame("APIC", 'image/gif', 5, 'zzz', $gif), 'add APIC number'); ok($gif eq $mp3->select_id3v2_frame_by_descr('APIC(Leaflet page)[zzz]'), 'APIC read'); ok($mp3->select_id3v2_frame_by_descr("APIC", undef), 'delete APIC'); ok(!$mp3->select_id3v2_frame_by_descr("APIC"), 'APIC deleted'); ok($mp3->{ID3v2}->add_frame("APIC", 'image/gif', "\x05", 'zzz', $gif), 'add APIC byte'); ok($gif eq $mp3->select_id3v2_frame_by_descr('APIC(Leaflet page)[zzz]'), 'APIC read'); ok($mp3->{ID3v2}->add_frame("TFLT", 'MPEG Audio MPEG 1/2 layer III'), 'add TFLT'); my $b = ($mp3->{ID3v2}->get_frames('TFLT', 'array_nokey'))[1]; ok($b->[1] eq "MPG /3", 'raw TFLT'); ok('MPEG Audio MPEG 1/2 layer III' eq $mp3->select_id3v2_frame_by_descr('TFLT'), 'TFLT read'); ok($mp3->select_id3v2_frame_by_descr("RBUF", 12, 14, 15), '3-arg RBUF'); #my $b = ($mp3->{ID3v2}->get_frames('RBUF', 'raw'))[1]; #print "# RBUF=<$b>\n"; $b = ($mp3->{ID3v2}->get_frames('RBUF', 'array_nokey'))[1]; print "# RBUF=<@$b>\n"; ok("@$b" eq "12 14 15", 'get array RBUF'); ok($mp3->select_id3v2_frame_by_descr("RBUF", "13;18"), '2-arg joined RBUF'); #my $b = ($mp3->{ID3v2}->get_frames('RBUF', 'raw'))[1]; #print "# RBUF=<$b>\n"; $b = ($mp3->{ID3v2}->get_frames('RBUF', 'array_nokey'))[1]; print "# RBUF=<@$b>\n"; ok("@$b" eq "13 18", 'get array RBUF'); ok($mp3->select_id3v2_frame_by_descr("TRCK", "13/118"), 'TRCK'); ok($mp3->update_tags(), 'update'); ok($mp3 = MP3::Tag->new("t_gif2.mp3"), 'reinit'); $mp3->get_tags; ok($mp3->{ID3v1}->track() eq '13', 'v1 track'); ok($mp3->track1() eq '13', 'track1'); ok($mp3->track0() eq '013', 'track0'); ok($mp3->track2() eq '118', 'track2'); ok($mp3->disk2() eq '', 'disk2'); ok($mp3->disk1() eq '', 'disk1'); ok($mp3->disk_alphanum() eq '', 'disk1'); ok($mp3->interpolate("%{TRCK};%n;%{n1};%{n2};%{n0} %{TPOS};%{m1};%{m2};%{mA}") eq '13/118;13/118;13;118;013 ;;;', 'interpolate track/disk'); $b = $mp3->interpolate("%{n1:have n1}/%{!n1:no n1}; %{m1:have m1}/%{!m1:no m1}"); print "# i --> <$b>\n"; ok($mp3->interpolate("%{n1:have n1}/%{!n1:no n1}; %{m1:have m1}/%{!m1:no m1}") eq 'have n1/; /no m1', 'conditional interpolate'); ok($mp3->select_id3v2_frame_by_descr("TPOS", "4/11"), 'TPOS'); ok($mp3->disk2() eq '11', 'disk2'); ok($mp3->disk1() eq '4', 'disk1'); ok($mp3->disk_alphanum() eq 'd', 'disk1'); $b = $mp3->interpolate("%{TRCK};%n;%{n1};%{n2};%{n0} %{TPOS};%{m1};%{m2};%{mA}"); print "# i --> <$b>\n"; ok($mp3->interpolate("%{TRCK};%n;%{n1};%{n2};%{n0} %{TPOS};%{m1};%{m2};%{mA}") eq '13/118;13/118;13;118;013 4/11;4;11;d', 'interpolate track/disk'); ok($mp3->select_id3v2_frame_by_descr("TPOS", "5/101"), 'TPOS'); ok($mp3->disk2() eq '101', 'disk2'); ok($mp3->disk1() eq '5', 'disk1'); ok($mp3->disk_alphanum() eq '005', 'disk1'); ok($mp3->interpolate("%{TRCK};%n;%{n1};%{n2};%{n0} %{TPOS};%{m1};%{m2};%{mA}") eq '13/118;13/118;13;118;013 5/101;5;101;005', 'interpolate track/disk'); ok($mp3 = MP3::Tag->new_fake('settable'), 'reinit to fake'); ok($mp3->genre_set(113), 'set genre'); ok($mp3->genre() eq 'Tango', 'get genre'); ok($mp3->genre_set(193), 'set genre'); ok($mp3->genre() eq '(193)', 'get genre'); ok($mp3->select_id3v2_frame_by_descr('POPM',"long;url;12;25"), 'set POPM'); $b = ($mp3->{ID3v2}->get_frames('POPM', 'array_nokey'))[1]; print "# POPM=<@$b>\n"; ok("@$b" eq "long;url 12 25", 'get array POPM'); ok($mp3->select_id3v2_frame_by_descr('COMR','$12;2011;google.com;2;Gooogle;small junklet'), 'set COMR'); $b = ($mp3->{ID3v2}->get_frames('COMR', 'array_nokey'))[1]; $b = join '/', @$b; print "# COMR=<$b>\n"; ok($b eq "0/\$12/2011 /google.com/\x02/Gooogle/small junklet", 'get array COMR'); my @failed; #@failed ? die "Tests @failed failed.\n" : print "All tests successful.\n"; sub ok_test { my ($result, $test) = @_; printf ("Test %2d %s %s", ++$count, $test, '.' x (45-length($test))); (push @failed, $count), print " not" unless $result; print " ok\n"; } sub ok { my ($result, $test) = @_; (push @failed, $count), print "not " unless $result; printf "ok %d # %s\n", ++$count, $test; } �����������������������������������������������������������������������������������������������MP3-Tag-1.13/t/update_tags.t������������������������������������������������������������������������0000700�0000000�0000000�00000003236�11201126110�012342� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl test.pl' ######################### We start with some black magic to print on failure. # Change 1..1 below to 1..last_test_to_print . # (It may become useful if the test is moved to ./t subdirectory.) BEGIN { $| = 1; print "1..8\n"; $ENV{MP3TAG_SKIP_LOCAL} = 1} END {print "MP3::Tag not loaded :(\n" unless $loaded;} use MP3::Tag; $loaded = 1; $count = 0; ok(1,"MP3::Tag initialized"); ######################### End of black magic. # Insert your test code below (better if it prints "ok 13" # (correspondingly "not ok 13") depending on the success of chunk 13 # of the test code): {local *F; open F, '>test14.MP3' or warn; print F 'empty'} $mp3 = MP3::Tag->new("test14.MP3"); ok($mp3, "Got tag"); ok($mp3->update_tags({'title' => 'foobar'}), 'update_tags() called'); ok($mp3 = MP3::Tag->new("test14.MP3"), "Regot tag"); my $t = $mp3->title; print "# t=<$t>\n"; ok($mp3->title eq 'foobar', 'Tag correctly written'); {local *F; open F, '>test15.mp7' or warn; print F 'empty'} $mp3 = MP3::Tag->new("test15.mp7"); ok($mp3, "Got tag from empty .mp7"); ok(! eval {$mp3->update_tags({'title' => 'foobar'})}, 'update_tags() failing'); ok(scalar($@ =~ /`is_writable'/), 'is_writable triggering failure'); my @failed; sub ok_test { my ($result, $test) = @_; printf ("Test %2d %s %s", ++$count, $test, '.' x (45-length($test))); (push @failed, $count), print " not" unless $result; print " ok\n"; } sub ok { my ($result, $test) = @_; (push @failed, $count), print "not " unless $result; printf "ok %d # %s\n", ++$count, $test; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/t/v2_comments.t������������������������������������������������������������������������0000700�0000000�0000000�00000021746�10431330064�012315� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl test.pl' ######################### We start with some black magic to print on failure. # Change 1..1 below to 1..last_test_to_print . # (It may become useful if the test is moved to ./t subdirectory.) BEGIN { $| = 1; print "1..105\n"; $ENV{MP3TAG_SKIP_LOCAL} = 1} END {print "MP3::Tag not loaded :(\n" unless $loaded;} use MP3::Tag; $loaded = 1; $count = 0; ok(1,"MP3::Tag initialized"); ######################### End of black magic. # Insert your test code below (better if it prints "ok 13" # (correspondingly "not ok 13") depending on the success of chunk 13 # of the test code): {local *F; open F, '>test12.mp3' or warn; print F 'empty'} $mp3 = MP3::Tag->new("test12.mp3"); ok($mp3, "Got tag"); ok(scalar $mp3->set_id3v2_frame("COMM", "eng", 'foo', 'Testing...'), "Changing ID3v2 ''-comment"); ok($mp3->{ID3v2}, "ID3v2 tag autocreated"); ok(($mp3->{ID3v2} and $mp3->{ID3v2}->write_tag),"Writing ID3v2"); $mp3 = MP3::Tag->new("test12.mp3"); ok($mp3->interpolate('%{COMM}'),'have COMM frame'); ok(!$mp3->interpolate('%{COMM01}'),'no COMM01 frame'); ok(!$mp3->interpolate('%{COMM02}'),'no COMM02 frame'); ok($mp3->interpolate('%{COMM(RUS,eng)[foo]}') eq 'Testing...', "Got tag via %{COMM(RUS,eng)[foo]}"); ok($mp3->interpolate('%{COMM(rus,ENG)[foo]}') eq 'Testing...', "Got tag via %{COMM(rus,ENG)[foo]}"); ok($mp3->interpolate('%{COMM(rus,EN,#0)[foo]}') eq 'Testing...', "Got tag via %{COMM(rus,EN,#0)[foo]}"); ok($mp3->interpolate('%{COMM(rus,EN,#1,)[foo]}') eq 'Testing...', "Got tag via %{COMM(rus,EN,#1,)[foo]}"); ok($mp3->interpolate('%{COMM[foo]}') eq 'Testing...', "Got tag via %{COMM[foo]}"); ok($mp3->interpolate('%{COMM(rus,EN,#1)[foo]}') eq '', "No tag via %{COMM(rus,EN,#1)[foo]}"); #my $r = $mp3->interpolate('%{!COMM(rus,EN,#1)[foo]:<%\{COMM(rus,EN,#1,)[foo]\}>}'); #warn "'$r'\n"; ok($mp3->interpolate('%{!COMM(rus,EN,#1)[foo]:<%{COMM(rus,EN,#1,)[foo]}>}') eq '<Testing...>', "Conditional via %{COMM(rus,EN,#1)[foo]}"); ok($mp3->interpolate('%{COMM(rus,EN,#1)[foo]||<%{COMM(rus,EN,#1,)[foo]}>}') eq '<Testing...>', "Alternative via %{COMM(rus,EN,#1)[foo]}"); ok($mp3->interpolate('%{COMM(rus,EN,#1,)[foo]||<%{COMM(rus,EN,#1,)[foo]}>}') eq 'Testing...', "Alternative via %{COMM(rus,EN,#1,)[foo]}"); ok($mp3->interpolate('%{COMM(rus,EN,#1)[foo]|COMM(rus,EN,#1,)[foo]}') eq 'Testing...', "Alternative via %{COMM(rus,EN,#1)[foo]}"); ok($mp3->interpolate('%{COMM(rus,EN,#1,)[foo]|COMM02}') eq 'Testing...', "Alternative via %{COMM(rus,EN,#1,)[foo]}"); ok($mp3->interpolate('%{COMM(rus,EN,#1)[foo]|f}') eq 'test12.mp3', "Alternative via %{COMM(rus,EN,#1)[foo]}"); ok($mp3->interpolate('%{COMM01||<%{COMM(rus,EN,#1,)[foo]}>}') eq '<Testing...>', "Alternative via %{COMM01}"); ok($mp3->set_id3v2_frame('TLEN', 123456), 'Set TLEN frame'); ok($mp3->interpolate('%{TLEN||<%{COMM(rus,EN,#1,)[foo]}>}') eq '123456', "Alternative via %{TLEN}"); ok($mp3->interpolate('%{COMM01|COMM(rus,EN,#1,)[foo]}') eq 'Testing...', "Alternative via %{COMM01}"); ok($mp3->interpolate('%{TLEN|COMM(rus,EN,#1,)[foo]}') eq '123456', "Alternative via %{TLEN}"); ok($mp3->interpolate('%{COMM01|f}') eq 'test12.mp3', "Alternative via %{TLEN}"); ok($mp3->interpolate('%{y||<%{COMM(rus,EN,#1,)[foo]}>}') eq '<Testing...>', "Alternative via %{y}"); print "# res=`", $mp3->interpolate('%{f||<%{COMM(rus,EN,#1,)[foo]}>}'), "'\n"; ok($mp3->interpolate('%{f||<%{COMM(rus,EN,#1,)[foo]}>}') eq 'test12.mp3', "Alternative via %{f}"); ok($mp3->interpolate('%{y|COMM(rus,EN,#1,)[foo]}') eq 'Testing...', "Alternative via %{y}"); ok($mp3->interpolate('%{f|COMM(rus,EN,#1,)[foo]}') eq 'test12.mp3', "Alternative via %{f}"); ok($mp3->interpolate('%{y|f}') eq 'test12.mp3', "Alternative via %{y}"); ok($res = $mp3->parse('%{U1}%={COMM(rus,EN,#2,)[foo]}%{U2}', '<Testing...>'), "Parsed %={COMM(rus,EN,#1,)[foo]}"); ok($res->{U1} eq '<', "Parsed before %={COMM(rus,EN,#2,)[foo]}"); ok($res->{U2} eq '>', "Parsed after %={COMM(rus,EN,#2,)[foo]}"); ok(scalar $mp3->set_id3v2_frame("COMM", "fra", 'foo', '????'), "Changing ID3v2 ''-comment"); ok($mp3->interpolate('%{COMM(rus,EN,fra)[foo]|COMM(rus,EN,#1,)[foo]}') eq '????', "Alternative via %{COMM(rus,EN,fra)[foo]}"); ok($mp3->interpolate('%{COMM01|COMM(rus,EN,#1,)[foo]}') eq '????', "Alternative via %{COMM01}"); ok($mp3->{ID3v2}->year(1993), 'Set year'); ok($mp3->interpolate('%{y|COMM(rus,EN,#1,)[foo]}') eq '1993', "Alternative via %{y}"); ok($mp3 = MP3::Tag->new("test12.mp3"), 'reget tags'); ok($mp3->config(parse_data => ['m', 'my/dir/', '%{COMM[directory]}']), 'config parse_data'); ok($mp3->title, 'Get the machinery started'); ok($mp3->interpolate('%{COMM(XXX)[directory]}') eq 'my/dir/', 'have it parsed'); ok($mp3->update_tags(), 'update tags'); ok($mp3 = MP3::Tag->new("test12.mp3"), 'reget tags'); ok($mp3->interpolate('%{COMM(XXX)[directory]}') eq 'my/dir/', 'have it stored'); {local *F; open F, '>test12.mp3' or warn; print F 'empty'} ok($mp3 = MP3::Tag->new("test12.mp3"), 'init ourselves'); ok($mp3->config(parse_data => ['m', 'bar', '%{COMM[ini-fname]}']), 'config parse_data'); ok($mp3->title, "prepare the data"); ok($mp3->interpolate('%{COMM}'), "have COMM"); ok(!$mp3->interpolate('%{COMM01}'), "no COMM01"); ok(!$mp3->interpolate('%{COMM02}'), "no COMM02"); ok($mp3->update_tags, 'update'); ok($mp3 = MP3::Tag->new("test12.mp3"), 'reinit ourselves'); ok($mp3->interpolate('%{COMM}'), "have COMM"); ok(!$mp3->interpolate('%{COMM01}'), "no COMM01"); ok(!$mp3->interpolate('%{COMM02}'), "no COMM02"); ok($mp3->config(parse_data => ['m', 'bar', '%{COMM[ini-fname]}']), 'config parse_data'); ok($mp3->title, "prepare the data"); ok($mp3->interpolate('%{COMM}'), "have COMM"); ok(!$mp3->interpolate('%{COMM01}'), "no COMM01"); ok(!$mp3->interpolate('%{COMM02}'), "no COMM02"); {local *F; open F, '>test12.mp3' or warn; print F 'empty'} ok($mp3 = MP3::Tag->new("test12.mp3"), 'init ourselves'); ok($mp3->config(parse_data => ['m', 'bar', '%{COMM[a]}'], ['m', 'baz', '%{COMM[b]}'], ['m', 'foo', '%{COMM[c]}']), 'config multiple parse_data to create COMMs'); ok($mp3->title, "prepare the data"); ok($mp3->interpolate('%{COMM}'), "have COMM"); ok($mp3->interpolate('%{COMM01}'), "have COMM01"); ok($mp3->interpolate('%{COMM02}'), "have COMM02"); ok(!$mp3->interpolate('%{COMM03}'), "no COMM03"); my $r = $mp3->interpolate('%{COMM[a]&COMM[aa]&COMM[b]&COMM[c]}'); print "# `$r'\n"; ok($r eq 'bar; baz; foo', "parse COMM[a]&COMM[aa]&COMM[b]&COMM[c]"); ok($mp3->update_tags, 'update'); ok($mp3 = MP3::Tag->new("test12.mp3"), 'reinit ourselves'); ok($mp3->config(parse_data => ['mz', '', '%{COMM[c]}'], ['mz', '', '%{COMM[b]}'], ['mz', '', '%{COMM[a]}']), 'config multiple parse_data to delete COMMs'); ok($mp3->title, "prepare the data"); ok(!$mp3->interpolate('%{COMM}'), "no COMM"); ok(!$mp3->interpolate('%{COMM01}'), "no COMM01"); ok(!$mp3->interpolate('%{COMM02}'), "no COMM02"); ok(!$mp3->interpolate('%{COMM03}'), "no COMM03"); ok($mp3 = MP3::Tag->new("test12.mp3"), 'reinit ourselves'); ok($mp3->config(parse_data => ['mz', '', '%{COMM[a]}'], ['mz', '', '%{COMM[c]}'], ['mz', '', '%{COMM[b]}']), 'config multiple parse_data to delete COMMs'); ok($mp3->title, "prepare the data"); ok(!$mp3->interpolate('%{COMM}'), "no COMM"); ok(!$mp3->interpolate('%{COMM01}'), "no COMM01"); ok(!$mp3->interpolate('%{COMM02}'), "no COMM02"); ok(!$mp3->interpolate('%{COMM03}'), "no COMM03"); ok($mp3 = MP3::Tag->new("test12.mp3"), 'reinit ourselves'); ok($mp3->config(parse_data => ['mz', '', '%{COMM[b]}'], ['mz', '', '%{COMM[c]}'], ['mz', '', '%{COMM[a]}']), 'config multiple parse_data to delete COMMs'); ok($mp3->title, "prepare the data"); ok(!$mp3->interpolate('%{COMM}'), "no COMM"); ok(!$mp3->interpolate('%{COMM01}'), "no COMM01"); ok(!$mp3->interpolate('%{COMM02}'), "no COMM02"); ok(!$mp3->interpolate('%{COMM03}'), "no COMM03"); ok($mp3 = MP3::Tag->new("test12.mp3"), 'reinit ourselves'); ok($mp3->config(parse_data => ['mz', '', '%{COMM[a]}'], ['mz', '', '%{COMM[b]}'], ['mz', '', '%{COMM[c]}']), 'config multiple parse_data to delete COMMs'); ok($mp3->title, "prepare the data"); ok(!$mp3->interpolate('%{COMM}'), "no COMM"); ok(!$mp3->interpolate('%{COMM01}'), "no COMM01"); ok(!$mp3->interpolate('%{COMM02}'), "no COMM02"); ok(!$mp3->interpolate('%{COMM03}'), "no COMM03"); ok($mp3->update_tags, 'update'); ok($mp3 = MP3::Tag->new("test12.mp3"), 'reinit ourselves'); ok(!$mp3->interpolate('%{COMM}'), "no COMM"); ok(!$mp3->interpolate('%{COMM01}'), "no COMM01"); ok(!$mp3->interpolate('%{COMM02}'), "no COMM02"); ok(!$mp3->interpolate('%{COMM03}'), "no COMM03"); #ok($mp3, "Got tag"); my @failed; #@failed ? die "Tests @failed failed.\n" : print "All tests successful.\n"; sub ok_test { my ($result, $test) = @_; printf ("Test %2d %s %s", ++$count, $test, '.' x (45-length($test))); (push @failed, $count), print " not" unless $result; print " ok\n"; } sub ok { my ($result, $test) = @_; (push @failed, $count), print "not " unless $result; printf "ok %d # %s\n", ++$count, $test; } ��������������������������MP3-Tag-1.13/test.mp3�������������������������������������������������������������������������������0000700�0000000�0000000�00000001300�07307730140�011020� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ID3�€��6COMM���z���ENGTest!�This is a test file for the MP3::Tag perl module. It contains only a ID3v1.1 and a ID3v2.3 tag, but no mp3 data.COMM���‹���ENGContact�To find out more about MP3::Tag go to http://sourceforge.net/projects/tagged or contact the author at thg@users.sourceforge.netTIT2���'���Only a test with a ID3v1 and ID3v2 tag��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������TAGSong��������������������������Artist������������������������Album�������������������������2000test������������������������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/tk-tag/��������������������������������������������������������������������������������0000700�0000000�0000000�00000000000�11417072072�010612� 5����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/tk-tag/README��������������������������������������������������������������������������0000700�0000000�0000000�00000006450�07247476370�011521� 0����������������������������������������������������������������������������������������������������ustar �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� tk-tag.pl 0.10 ==================================================================== This program is a graphical interface for the MP3::Tag library. Some buttons do not have a function yet. But is shows you already the contents of the ID3v1/ID3v1.1 and ID3v2.3 tags and let you change and save these tags. If you change something, the changes will only be saved if you click on 'Save changes' or press Control-S. This is the same after using the remove button. Without saving the changes, nothing will be removed permanently. A undo function is not implemented yet, but if you didn't save any changes you can reload the original tags by selecting the file in the filelist again. Following functions work already: - open a directory (sorry for the bad open directory interface) - select a file form the filelist with a double click or by pressing Enter - You can load/save binary data in ID3v2 tags, like the APIC tag. - Filter the filelist with the buttons below the filelist. In the Entry-field you can type a regular expression, which will be used for filtering. - You can edit all fields of the ID3v1 and ID3v2 tags and the filename. During editing you can use Control-U, Control-u, Control-l to switch a whole field to UPPERCASE, Uppercase First or lowercase. In the filename field you can use Control-Space to switch spaces to underscores or vice versa (toggling). - You can use the information of a ID3v1 tag to set the filename (see Options->Fileformat and documentation below). You can also use the Artist (TPE1), Album (TALB) and Song (TIT2) information of an ID3v2 tag to set the filename. - You can remove the whole ID3v1 or ID3v2 Tag. And even the whole file with the remove button, or by deleting the filename. (Remember to save the changes to really delete it). - You can remove or add frames to a ID3v2 frame - If you enter data in a tag which doesn't exist yet (red background color) it will be automatically created when you save the changes. - Use Control-n to advance to the next file. - Copy all ID3v1 tag information to the ID3v2 tag Warnings will mostly printed to the console and not to a tk-window. To use this program, you must have the Tk and Tk::JPEG modules from CPAN installed. And of course MP3::Tag. Thomas <thg@users.sourceforge.net> Filename format =============== To set the filename from the data a tag, you can specify a format string: %a - will be replaced with artist %s - will be replaced with song %l - will be replaced with album %t - will be replaced with track (not with ID3v2 at the moment) %y - will be replaced with year (not with ID3v2 at the moment) %g - will be replaced with genre (not with ID3v2 at the moment) %c - will be replaced with comment (not with ID3v2 at the moment) options for %x: (where x is one of a,s,l,t,y,g,c) %nx => use only first n characters eg. artist="artist name" %5a = "artis" %n:cx => use at least n characters, if %x is shorter, fill it with character c eg. track=3 %2:0t = '03' , artist="abc" %5:_a = "__abc" %n!:cx => same as %n:cx, but if %x is longer than n, cut it at n eg. artist="abc" %5:_a = "__abc" artist="abcdefg" %5:_a = "abcde" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/tk-tag/tk-tag.pl�����������������������������������������������������������������������0000700�0000000�0000000�00000113203�07247476370�012360� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w use Tk; use Tk::JPEG; use Tk::DialogBox; use Tk::BrowseEntry; use Tk::FBox; use Tk::TextUndo; #use POSIX; use strict; use MP3::Tag; ### TODO # # make all buttons alive # # view binary data (as hex dump) # # delete/add more than one frame at once # # better open function # # ... # # doing changes automatically to all selected files # (like setting filename, applying Control-u to all filename, # deleting all ID3v2 frames or whatsoever) # # Sorry, but I know that this is ugly code... # use vars qw/$tktag $VERSION %element %var/; $VERSION="0.15"; $var{dir}= shift || "./"; #/mp3/unsorted"; $var{v2specent}={TIT2=>{name=>"Song"}, TPE1=>{name=>"Artist"}, TALB=>{name=>"Album"}}; $var{filter}=""; $var{fnformat} = "%a - %s.mp3"; $var{autoinfo} = ["ID3v2","ID3v1","filename"]; formatstr($var{fnformat}); $tktag = MainWindow->new(); #main window create_window(); #&load_filelist; &scan_dir; &filter_and_show; MainLoop; exit 0; #################################### ###### tk subs sub key_bindings { $tktag->bind('<Control-U>'=>\&use_ucase); $tktag->bind('<Control-u>'=>\&use_ucase_first); $tktag->bind('<Control-l>'=>\&use_lcase); $tktag->bind('<Control-space>'=>\&use_spaces); $tktag->bind('<Control-S>'=>\&save); $tktag->bind('<Control-f>'=>\&autosetfilename); $tktag->bind('<Control-n>'=> sub { $element{filelist}->activate($element{filelist} ->index("active")+1); &select; }); } sub create_window { my ($l); &key_bindings; $element{leftFrame} = $tktag->Frame(); $element{rightFrame} = $tktag->Frame(); $element{rightFrameMul} = $tktag->Frame(); # create menu $element{menu} = create_menu($tktag); # special fonts $tktag->fontCreate('C_big', -family => 'courier', -weight => 'bold', -size => 18); # create Filelist $element{filelist} = $element{leftFrame}->Scrolled("Listbox", -width=>25, -selectmode=>"extended", -scrollbars=>"osre", -takefocus=>1, -exportselection=>0); $element{filelist}->bind("<Double-1>" => \&select); $element{filelist}->bind("<Key-Return>" => \&select); $var{simple}=1; $element{filelist}->bind("<1>" => \&check_multi); $element{filelist}->bind("<Control-a>" => [$element{filelist}, 'selectionSet',"0","end"]); my $filter=$element{leftFrame}->Frame(); my $cbline=$filter->Frame(); my $illine=$filter->Frame(); $cbline->Checkbutton(-text => 'ID3v1', -variable => \$var{v1filter}, -command => \&filter_and_show, -relief => 'flat')->pack(-side=>"left"); $cbline->Checkbutton(-text => 'ID3v2', -variable => \$var{v2filter}, -command => \&filter_and_show, -relief => 'flat')->pack(-side=>"left"); $illine->Checkbutton(-text => 'Inverse', -variable => \$var{filter_inv}, -command => \&filter_and_show, -relief => 'flat')->pack(-side=>"left"); $illine->Label(-textvariable=>\$var{visiblefiles}, -relief=>"sunken", width=>5)->pack(-side=>"left", -anchor=>"e"); $cbline->pack(-side=>"top"); $l=$filter->Entry(-textvariable=>\$var{filter}, -width=>25)->pack(-side=>"top"); $l->bind("<FocusOut>" => \&filter_and_show); $l->bind("<KeyPress-Return>" => \&filter_and_show); $illine->pack(-side=>"top"); $element{rightFrameMul}->Label(-text=>"Options for automatic processing ". "of serveral files") ->pack(-side=>"top", -expand=>1, -fill=>"x"); # create filename-area my $fn1area = $element{rightFrame}->Frame(Name=>"fnframe"); my $fn2area = $element{rightFrame}->Frame(); $element{filenamelabel} = $fn1area->Label(-text=>"Filename:") ->pack(-side=>"left", anchor=>"w"); $var{bgcolor}=$element{filenamelabel}->cget("-background"); $l=$fn1area->Entry(-textvariable=>\$var{filename}, -validate=>"key", -vcmd=>val_fn($element{filenamelabel})) ->pack(-side=>"left", -fill=>"x", -expand=>"yes", -anchor=>"w"); $fn1area->Button(-text=>"Save changes", -command=>\&save) ->pack(-side=>"left", -anchor=>"w"); $element{setfilename} = $fn2area->Menubutton(qw/-underline 0 -relief raised/, -text => "Set Filename --", -direction => "below"); $element{setfilename}->configure(-menu => $element{setfilename}->menu); $element{setfilename}->command(-label => "from ID3v1 Tag", -command=>[\&setfilename, "ID3v1"]); $element{setfilename}->command(-label => "from ID3v2 Tag", -command=>[\&setfilename, "ID3v2"]); $element{setfilename}->pack(-side=>"left", anchor=>"n", -padx=>15); $element{setid3v1} = $fn2area->Menubutton(qw/-underline 0 -relief raised/, -text => "Set ID3v1 --", -direction => "below"); $element{setid3v1}->configure(-menu => $element{setid3v1}->menu); $element{setid3v1}->command(-label => "from Filename", -command=>[\©fntov1]); $element{setid3v1}->command(-label => "from ID3v2 Tag", -command=>[\©v2tov1]); $element{setid3v1}->pack(-side=>"left", anchor=>"n", -padx=>15); $element{setid3v2} = $fn2area->Menubutton(qw/-underline 0 -relief raised/, -text => "Set ID3v2 --", -direction => "below"); $element{setid3v2}->configure(-menu => $element{setid3v2}->menu); $element{setid3v2}->command(-label => "from Filename", -command=>[\©fntov2]); $element{setid3v2}->command(-label => "from ID3v1 Tag", -command=>\©v1tov2); $element{setid3v2}->pack(-side=>"left", anchor=>"n", -padx=>15); $element{removebutton} = $fn2area->Menubutton(qw/-underline 0 -relief raised/, -text => "Remove --", -direction => "below"); $element{removebutton}->configure(-menu => $element{removebutton}->menu); $element{removebutton}->command(-label => "File", -command=>sub{ $var{filename}=""; $element{filenamelabel} ->configure(-background=>"red");}); $element{removebutton}->command(-label => "ID3v1 Tag", -command=>sub{ if (exists $var{v1}) { &remove_id3v1; $element{v1caption} ->configure(-background=>"red");}}); $element{removebutton}->command(-label => "ID3v2 Tag", -command=>sub{ if (exists $var{v2}) { &remove_id3v2; $element{v2caption} ->configure(-background=>"red");}}); $element{removebutton}->pack(-side=>"left", anchor=>"n", -padx=>15); # create ID3v1 area my $v1area = $element{rightFrame}->Frame(Name=>"v1frame"); my $v1caption = $v1area->Frame(-background=>"red"); my $v1labels = $v1area->Frame(); my $v1entries = $v1area->Frame(); my $v2apic = $v1area->Frame(); my $a=0; $element{v1caption}=$v1caption->Label(-text=>"ID3v1.1", -font=>"C_big", -background=>"red", -relief=>"ridge") ->pack(-side=>"top", -anchor=>"c", -expand=>"yes", -fill=>"x"); $l = $v1labels->Label(-text=>"Song:")->pack(-side=>"top", -anchor=>"e"); $v1entries->Entry(-textvariable=>\$var{v1song}, -width=>30, -validate=>"key", -vcmd=>val_text($l,30)) ->pack(-side=>"top", -anchor=>"w"); $l = $v1labels->Label(-text=>"Artist:")->pack(-side=>"top", -anchor=>"e", -pady=>2); $v1entries->Entry(-textvariable=>\$var{v1artist}, -width=>30, -validate=>"key", -vcmd=>&val_text($l,30)) ->pack(-side=>"top", -anchor=>"w"); $l = $v1labels->Label(-text=>"Album:")->pack(-side=>"top", -anchor=>"e"); $v1entries->Entry(-textvariable=>\$var{v1album}, -width=>30, -validate=>"key", -vcmd=>&val_text($l,30)) ->pack(-side=>"top", -anchor=>"w"); $l = $v1labels->Label(-text=>"Comment:")->pack(-side=>"top", -anchor=>"e", -pady=>1); $v1entries->Entry(-textvariable=>\$var{v1comment}, -width=>30, -validate=>"key", -vcmd=>val_text($l,28)) ->pack(-side=>"top", -anchor=>"w"); $element{genrelabel} = $v1labels->Label(-text=>"Genre:")->pack(-side=>"top", -anchor=>"e"); my $genres = ["", sort @{MP3::Tag::genres()}]; $v1entries->BrowseEntry(-variable => \$var{v1genre}, -choices => $genres, -state=>"readonly", -browsecmd=> sub { $element{genrelabel} ->configure(-background=>"yellow"); $element{v1caption} ->configure(-background=>"yellow"); push @{$var{labels}}, $element{genrelabel} }) ->pack(-side=>"top", -anchor=>"w"); $l = $v1labels->Label(-text=>"Year:")->pack(-side=>"top", -anchor=>"e"); $v1entries->Entry(-textvariable=>\$var{v1year}, -width=>4, -validate=>"key", -vcmd=>val_num($l,4)) ->pack(-side=>"top", -anchor=>"w"); $l = $v1labels->Label(-text=>"Track:")->pack(-side=>"top", -anchor=>"e", -pady=>2); $v1entries->Entry(-textvariable=>\$var{v1track}, -width=>4, -validate=>"key", -vcmd=>val_num($l,3)) ->pack(-side=>"top", -anchor=>"w"); $element{apic} = $v2apic->Photo('apic'); $element{apic}->blank; $v2apic->Label(-image=>'apic', -height=>150, -width=>180) ->pack(-side=>"top", -anchor=>"center"); $element{apictext}=$v2apic->Label(-text=>"")->pack(-side=>"top", -anchor=>"center"); $v1caption->pack(-side=>"top", -expand=>"yes", -fill=>"x"); $v1labels->pack(-side=>"left"); $v1entries->pack(-side=>"left"); $v2apic->pack(-side=>"right"); # ID3v2 area my $v2area = $element{rightFrame}->Frame(Name=>"v2frame"); my $v2caption = $v2area->Frame(); my $v2top = $v2area->Frame(); my $v2right = $v2area->Frame(); my $v2buttons = $v2top->Frame(); my $v2labent = $v2top->Frame(); my $v2labels = $v2labent->Frame(); my $v2entries = $v2labent->Frame(); my $v2parea = $v2right->Frame(); my $v2iarea = $v2right->Frame(); $element{v2caption}=$v2caption->Label(-text=>"ID3v2.3", -font=>"C_big", -background=>"red", -relief=>"ridge") ->pack(-side=>"top", -anchor=>"c", -fill=>"x", -expand=>"yes"); while (my ($fname,$val) = each %{$var{v2specent}}) { $val->{label} = $v2labels->Label(-text=>$val->{name})->pack(-side=>"top", -anchor=>"e"); $val->{entry} = $v2entries->Entry(-textvariable=>\$var{"v2$fname"}, -width=>40, -validate=>"key", -vcmd=>[\&val_v2,$element{v2caption}, $val->{label}, $a]) ->pack(-side=>"top", -anchor=>"w"); } $v2buttons->Button(-text=>"Add Frame", -command=>\&add_frame) ->pack(-side=>"left", -anchor=>"c"); $v2buttons->Button(-text=>"Delete Frame(s)", -command=>\&del_frame) ->pack(-side=>"left", -anchor=>"c"); $element{frames} = $v2iarea->Scrolled("Listbox", -scrollbars=>"re", -width=>"8", -selectmode=>"browse", -takefocus=>1) ->pack(-side=>"left"); $element{frames}->bind("<Double-1>" => \&show_frame); $element{frames}->bind("<Key-Return>" => \&show_frame); $v2parea->Label(-textvariable=>\$var{longname})->pack(-side=>"top", -anchor=>"n", -pady=>5); $element{frameinfo} = $v2parea; $v2labels->pack(-side=>"left"); $v2entries->pack(-side=>"left"); $v2labent->pack(-side=>"left", -anchor=>"nw"); $v2buttons->pack(-side=>"left", -fill=>"y", -expand=>"yes"); $v2iarea->pack(-side=>"left", -anchor=>"nw"); $v2parea->pack(-side=>"left", -fill=>"both", -expand=>"yes", -anchor=>"nw"); $v2caption->pack(-side=>"top", -expand=>"yes", -fill=>"x"); $v2top->pack(-side=>"top", -fill=>"x", -expand=>"yes", -anchor=>"n"); $v2right->pack(-side=>"left", -fill=>"both", -expand=>"yes", -anchor=>"n"); # pack left Frame $element{filelist}->pack(-side=>"top", -fill=>"y", -expand=>"yes"); $filter->pack(-side=>"bottom"); # pack right Frame $fn1area->pack(-side=>"top", -anchor=>"nw", -expand=>"yes", -fill=>"x"); $fn2area->pack(-side=>"top", -anchor=>"nw", -expand=>"yes", -fill=>"x"); $v1area->pack(-side=>"top", -anchor=>"nw", -expand=>"yes", -fill=>"x", -pady=>10); $v2area->pack(-side=>"top", -anchor=>"nw", -expand=>"yes", -fill=>"x"); $v2parea->pack(-side=>"top", -anchor=>"nw", -expand=>"yes", -fill=>"x"); # pack main window $element{menu}->pack(-side=>"top", -fill=>"x", -anchor=>"nw"); $element{leftFrame}->pack(-side=>"left", -fill=>"y", -anchor=>"w"); $element{rightFrame}->pack(-side=>"left",-fill=>"both",-expand=>"yes", -anchor=>"nw"); #prepare open dialog window $element{open} = $tktag->DialogBox(-title=>"Open directory", -buttons=>["Open", "Cancel"]); $element{open}->add("Label", -text=>"Directory:")->pack(); $element{open}->add("Entry", -width=>35, -textvariable=>\$var{dir})->pack(); $element{fnformat} = $tktag->DialogBox(-title=>"Set filename format", -buttons=>["Set", "Cancel"]); $element{fnformat}->add("Label", -text=>"Format")->pack(); $element{fnformat}->add("Entry", -width=>35, -textvariable=>\$var{fnformat}) ->pack(); $element{fnformat}->add("Label", -text=>"%s - Song %l - Album\n%a - Artist". " %t - Track\n %c - Comment ". "%g - Genre\n %y - Year". "\nSee also README file")->pack(); $element{addelframe} = $tktag->DialogBox(-buttons=>["Ok", "Cancel"]); $element{addelabel} = $element{addelframe}->add("Label") ->pack(); $element{addelistbox} = $element{addelframe}->add("Scrolled", "Listbox", -scrollbars=>"osre", -width=>50) ->pack(-fill=>"both", -expand=>1); } sub val_v2 { my ($label1, $label2, $change) = @_; if ($_[7] == -1) { $change=0; return 1; } return 1 if $change++; $label1->configure(-background=>"yellow"); if (defined $label2) { $label2->configure(-background=>"yellow"); push @{$var{labels}}, $label2; } return 1; } sub val_fn { my ($label1, $change) = @_; return sub { if ($_[4] == -1) { $change=0; return 1; } return 1 if $change++; $label1->configure(-background=>"yellow"); return 1; } } sub val_text { my ($label, $length, $change) = @_; return sub { my ($pro, undef, undef, undef, $type) = @_; return 0 if $type == 1 && length($pro) > $length; if ($type == -1) { $change=0; return 1; } return 1 if $change++; $label->configure(-background=>"yellow"); $element{v1caption}->configure(-background=>"yellow"); push @{$var{labels}}, $label; return 1; } } sub val_num { my ($label, $length, $change) = @_; return sub { my ($pro, undef, undef, undef, $type) = @_; return 0 if $type == 1 && !($pro =~ /^\d{0,$length}$/o); if ($type == -1) { $change=0; return 1; } return 1 if $change++; $label->configure(-background=>"yellow"); $element{v1caption}->configure(-background=>"yellow"); push @{$var{labels}}, $label; return 1; } } sub create_menu { my $menu = shift->Menu(-type=>"menubar"); my $file=$menu->cascade(-label=>"File"); $file->command(-label=>"Open", -command=>\&load_filelist); $file->separator(); $file->command(-label=>"Exit", -command=>sub {exit 0;}); my $opts=$menu->cascade(-label=>"Options"); $opts->command(-label=>"Filename format", -command=>\&filename_format); return $menu; } sub tk_question { my $text = shift; my $ans = $tktag->messageBox(-text=>$text, -title=>"Question", -type=>"YesNo", -default=>"No", -icon=>"question" ); return $ans eq "Yes" ? 1 : 0; } sub tk_warning { my $text = shift; $tktag->messageBox(-text=>$text, -title=>"Warning", -buttons=>"Ok", -icon=>"warning" ); } ####### file subs sub load_filelist { $element{open}->configure(-title=>"Open directory"); my $answer = $element{open}->Show; if ($answer eq "Open") { $var{dir} =~ s!/$!!; $var{dir} .= "/"; &scan_dir; &filter_and_show; } } sub scan_dir { delete $var{files}; opendir(DIR, $var{dir}) or return; $tktag->Busy; $tktag->update; my $file; while (defined($file = readdir(DIR))) { if (-d $var{dir} . $file) { $var{files}->{$file}={dir=>$var{dir}, isDir=>1}; } elsif (-f _) { my $mp3=MP3::Tag->new($var{dir}.$file); $var{files}->{$file}={dir=>$var{dir}}; foreach ($mp3->getTags) { $var{files}->{$file}->{$_}=1; } } } closedir DIR; $tktag->Unbusy; } sub filter_and_show { my (@files, @dirs, $name, $val, $filter); $element{filelist}->delete("0", "end"); if ($var{filter} ne "") { $filter = qr/$var{filter}/i; } while (($name, $val)=each %{$var{files}}) { if (exists $val->{isDir}) { push @dirs, "[$name]"; } else { if (!$var{v1filter} || (!$var{filter_inv} && $val->{ID3v1}) || ($var{filter_inv} && !$val->{ID3v1})) { if (!$var{v2filter} || (!$var{filter_inv} && $val->{ID3v2}) || ($var{filter_inv} && !$val->{ID3v2})) { if (!$filter || (!$var{filter_inv} && $name =~ $filter) || ($var{filter_inv} && !($name =~ $filter))) { push @files, $name; } } } } } $element{filelist}->insert("end", sort @dirs); $element{filelist}->insert("end", sort @files); $var{visiblefiles}=$#files+1; } sub select { my $active =$element{filelist}->get("active","active"); if ($active =~ /^\[(.*)\]$/) { if (exists $var{files}->{$1}->{isDir}) { $var{dir}.="$1/"; &scan_dir; &filter_and_show; return; } } $var{filename}=$active; $var{oldfilename} = $var{filename}; my $filename = $var{dir}.$var{filename}; $var{mp3}->close if exists $var{mp3}; delete $var{v1}; delete $var{v2}; $element{filenamelabel}->configure(-background=>$var{bgcolor}); $var{mp3} = MP3::Tag->new($filename); $var{mp3}->getTags; # remove old things foreach (@{$var{fpack}}) { $_->packForget(); } $var{fpack} = []; $var{longname}=""; if (exists $var{labels}) { while (my $label = shift @{$var{labels}}) { $label->configure(-background=>$var{bgcolor}); } } # set information of ID3v1 tag if (exists $var{mp3}->{ID3v1}) { $element{v1caption}->configure(-background=>"green"); $var{v1}=$var{mp3}->{ID3v1}; ($var{v1song},$var{v1artist},$var{v1album},$var{v1year},$var{v1comment},$var{v1track},$var{v1genre}) = $var{v1}->all(); } else { ($var{v1song},$var{v1artist},$var{v1album},$var{v1year},$var{v1comment},$var{v1track},$var{v1genre}) = ("","","","","","",""); $element{v1caption}->configure(-background=>"red"); } #set information of ID3v2 tag if (exists $var{mp3}->{ID3v2}) { $element{v2caption}->configure(-background=>"green"); $var{v2}=$var{mp3}->{ID3v2}; my $frames = $var{v2}->getFrameIDs; while (my ($fname, $val) = each %{$var{v2specent}}) { if (exists $frames->{$fname}) { $var{"v2$fname"} = $var{v2}->getFrame($fname); $val->{entry}->bind('<FocusOut>' => v2_specent_change($fname)); } else { $var{"v2$fname"} = ""; $val->{entry}->bind('<FocusOut>' => v2_specent_create($fname)); } } if (exists $frames->{"APIC"}) { show_picture("APIC"); } else { $element{apic}->blank; $element{apictext}->configure(-text=>""); } my @frames = sort keys %$frames; $element{frames}->delete("0","end"); $element{frames}->insert("end", @frames); show_frame($frames[0],1); } else { while (my ($fname, $val) = each %{$var{v2specent}}) { $var{"v2$fname"}=""; $val->{entry}->bind('<FocusOut>' => v2_specent_create($fname)); } $element{v2caption}->configure(-background=>"red"); $element{frames}->delete("0","end"); $element{apic}->blank; $element{apictext}->configure(-text=>""); } } sub v2_specent_change { my $fname = shift; return sub { $var{v2}->change_frame($fname, $var{"v2$fname"}); if (exists $var{current_frame} && $var{current_frame} eq $fname) { $var{frame_Text}->delete("0.1", "end"); $var{frame_Text}->insert("0.1", $var{"v2$fname"}); } }; } sub v2_specent_create { my $fname = shift; return sub { return if $var{"v2$fname"} eq ""; $var{v2}=$var{mp3}->newTag("ID3v2") unless exists $var{v2}; $var{v2}->add_frame($fname, $var{"v2$fname"}); my @allframes = $element{frames}->get(0,"end"); push @allframes, $fname; $element{frames}->delete(0,"end"); $element{frames}->insert("end", sort @allframes); $var{v2specent}->{$fname}->{entry}->bind('<FocusOut>' => v2_specent_change($fname)); }; } sub show_frame { my ($fname, $tagchanged) = @_; if (ref $fname) { # called from listbox-bind $fname = $fname->get("active"); } save_frameinfo() unless defined $tagchanged; # delete last info foreach (@{$var{fpack}}) { $_->packForget(); } $var{fpack} = []; $var{current_frame}=""; return unless length($fname) == 4 || length($fname) == 6; my $info; $var{current_frame} = $fname; ($info, $var{longname}) = $var{v2}->getFrame($fname); (undef, $var{restrinp}) = $var{v2}->what_data($fname); if (ref $info) { my ($key, $value); foreach $key (sort keys %$info) { $value = $info->{$key}; my $f = $element{frameinfo}->Frame(); push @{$var{fpack}}, $f; if ($key =~ s/^_//) { my $l=$f->Label(-text=>"$key:", -justify=>"left") ->pack(-side=>"left", -anchor=>"w"); my $b=$f->Menubutton(qw/-underline 0 -relief raised/, -text => "Data of " .length($value) . " Bytes --", -direction => "below"); $b->configure(-menu => $b->menu); $var{"data_$key"}=$value; $b->command(-label => "Save Data", -command=>[\&save_data,$key]); $b->command(-label => "Load Data", -command=>[\&load_data,$key,$b, $l]); $b->command(-label => "View Data", -command=>[\&view_data,$key]); $b->pack(-side=>"left", anchor=>"n"); } else { my $l=$f->Label(-text=>"$key:", -justify=>"left") ->pack(-side=>"left", -anchor=>"w"); if (exists $var{restrinp}->{$key}) { my $state="readonly"; if (exists $var{restrinp}->{$key}->{"_FREE"}) { $state="normal"; delete $var{restrinp}->{$key}->{"_FREE"}; } $var{"frame_$key"}=$value; my @inps=sort keys %{$var{restrinp}->{$key}}; $f->BrowseEntry(-variable => \$var{"frame_$key"}, -choices => \@inps, -state=>$state, -width=>37, -browsecmd=> sub { $l->configure(-background=>"yellow"); $element{v2caption} ->configure(-background=>"yellow");}) ->pack(-side=>"right", -anchor=>"e"); } else { $var{"frame_$key"} = $f->TextUndo(-height=>2, -width=>40, -wrap=>"word") ->pack(-side=>"right", -anchor=>"e"); $var{"frame_$key"}->insert("0.0", $value); $var{"frame_$key"}->bind('<Double-1>' => sub{ $var{"frame_$key"} ->configure(-disable=>1);}); push @{$var{fpack}}, $var{"frame_$key"}; $var{"frame_$key"}->bind('<Key>'=>[\&yyy, $l]); } } $f->pack(-side=>"top", -anchor=>"w", -fill=>"x", -expand=>"yes"); } } else { if (exists $var{restrinp}->{Text}) { my $state="readonly"; if (exists $var{restrinp}->{Text}->{"_FREE"}) { $state="normal"; delete $var{restrinp}->{Text}->{"_FREE"}; } $var{"frame_Text"}=$info; my @inps=sort keys %{$var{restrinp}->{Text}}; push @{$var{fpack}},$element{frameinfo}->BrowseEntry( -variable => \$var{"frame_Text"}, -choices => \@inps, -width=>37, -state=>$state, -browsecmd=> sub { $element{v2caption} ->configure(-background=>"yellow")}) ->pack(-side=>"top", -anchor=>"n"); } else { $var{frame_Text} = $element{frameinfo}->TextUndo(-height=>2, -width=>40, -wrap=>"word") ->pack(-side=>"top", -anchor=>"n"); $var{frame_Text}->insert("0.0", $info); push @{$var{fpack}}, $var{frame_Text}; if (exists $var{v2specent}->{$fname}) { $var{frame_Text}->bind('<Key>'=>[\&xxx, $fname]); } } } } sub save_frameinfo { # save last info return unless exists $var{current_frame}; my ($format, $resinp) = $var{v2}->what_data($var{current_frame}); my @data=(); foreach (@$format) { if (/^_/) { push @data, $var{"data$_"}; next; } my $d; if ($var{"frame_$_"} =~ /Text/) { $d= $var{"frame_$_"}->get("0.1","end"); } else { $d= $var{"frame_$_"}; $d= $resinp->{$_}->{$d} if (defined $resinp->{$_}->{$d}); }; chomp $d; $d =~ s/ +$//; push @data, $d; } $var{v2}->change_frame($var{current_frame}, @data); } sub xxx { my ($textobject, $fname) = @_; my $text = $textobject->get("0.1","end"); chomp($text); $var{"v2$fname"} = $text; $var{v2specent}->{$fname}->{label}->configure(-background=>"yellow"); $element{v2caption}->configure(-background=>"yellow"); } sub yyy { $_[1]->configure(-background=>"yellow"); $element{v2caption}->configure(-background=>"yellow"); } sub show_picture { my $fname = shift; my $frame = $var{v2}->getFrame($fname); if ($frame->{"MIME type"} =~ /image\/(gif|jpe?g|bmp)/i) { my $type = lc $1; $type =~ s/jpg/jpeg/; open(TMP, ">/tmp/_tk_temp.jpg"); binmode TMP; print TMP $frame->{_Data}; close TMP; $element{apic}->configure(-format=>$1, -file=>"/tmp/_tk_temp.jpg"); my $text .= $frame->{"Picture Type"}; $text = $frame->{Description} if $text eq "Other"; $text = "ID3v2.3-" . uc($fname) . ": ". $text; $element{apictext}->configure(-text=>$text); unlink "/tmp/_tk_temp.jpg"; } } sub save { # save changes to filename if ($var{filename} ne $var{oldfilename}) { return 0 unless change_filename(); } $element{filenamelabel}->configure(-background=>$var{bgcolor}); #save changes of ID3v1 tag if ($element{v1caption}->cget("-background") eq "yellow") { my @fields; for (qw/v1song v1artist v1album v1year v1comment v1track v1genre/) { push @fields, $var{$_}; } $var{v1}=$var{mp3}->newTag("ID3v1") unless exists $var{v1}; $var{v1}->all(@fields); $var{v1}->writeTag; $var{v1genre}=$var{v1}->genre(); $element{v1caption}->configure(-background=>"green"); } elsif (exists $var{v1} && $element{v1caption}->cget("-background") eq "red") { $var{v1}->removeTag; delete $var{v1}; } #save changes of ID3v2 tag if ($element{v2caption}->cget("-background") eq "yellow") { $var{v2}=$var{mp3}->newTag("ID3v2") unless exists $var{v2}; save_frameinfo(); while (my ($fname, $val)=each %{$var{v2specent}}) { if ($var{"v2$fname"} ne "" && $val->{label}->cget("-background") eq "yellow") { my $d=$var{"v2$fname"}; $d=~s/ +$//; if (defined $var{v2}->getFrame($fname)) { $var{v2}->change_frame($fname, $d); } else { $var{v2}->add_frame($fname, $d); } $val->{label}->configure(-background=>$var{bgcolor}); } } $var{v2}->write_tag; $element{v2caption}->configure(-background=>"green"); foreach my $v2e ( @{$var{fpack}}) { foreach ($v2e->children) { $_->configure(-background=>$var{bgcolor}) if /Label/; } } } elsif (exists $var{v2} && $element{v2caption}->cget("-background") eq "red") { $var{v2}->remove_tag; delete $var{v2}; } if (exists $var{labels}) { while (my $label = shift @{$var{labels}}) { $label->configure(-background=>$var{bgcolor}); } } } sub change_filename { my $success = 0; if ($var{filename} eq "") { if (tk_question("Filename is empty. Do you want to ". "delete $var{oldfilename}?")) { $var{mp3}->close; if (unlink $var{dir}.$var{oldfilename}) { remove_id3v1(); remove_id3v2(); $element{v1caption}->configure(-background=>"red"); $element{v2caption}->configure(-background=>"red"); $element{filelist}->delete("active","active"); delete $var{files}->{$var{oldfilename}}; $success = 1; } else { tk_warning("Cannot delete file."); } } } elsif (-e $var{dir}.$var{filename}) { if (-f _) { if (tk_question("Files $var{filename} exists. Do you want". " to overwrite it?")) { goto re; } } else { tk_warning("$var{filename} exists and it isn't a plain file.". " Can't rename $var{oldfilename}!"); } } else { re: if (rename($var{dir}.$var{oldfilename}, $var{dir}.$var{filename}) || (system("mv", $var{dir}.$var{oldfilename}, $var{dir}.$var{filename})==0)) { $var{files}->{$var{filename}}=$var{files}->{$var{oldfilename}}; delete $var{files}->{$var{oldfilename}}; $var{oldfilename} = $var{filename}; my $index=$element{filelist}->index("active"); filter_and_show; $element{filelist}->see($index); $element{filelist}->activate($index); $var{mp3}->{filename}=$var{dir}.$var{filename}; $success = 1; } else { tk_warning("Couldn't rename $var{oldfilename} to $var{filename}"); } } return $success; } sub remove_id3v1 { for (qw/v1song v1artist v1album v1year v1comment v1track v1genre/) { $var{$_} = ""; } } sub remove_id3v2 { for (keys %{$var{v2specent}}) { $var{"v2$_"} = ""; } $element{frames}->delete("0","end"); foreach (@{$var{fpack}}) { $_->packForget(); } $var{fpack} = []; $var{longname}=""; } sub add_frame { return unless exists $var{mp3}; $element{addelframe}->configure(-title=>"Add Frame"); $element{addelabel}->configure(-text=>"Select a frame to add:"); $element{addelistbox}->delete("0","end"); $var{v2}=$var{mp3}->newTag("ID3v2") unless exists $var{v2}; my $list = $var{v2}->supported_frames; my @list = map {"$_ - $list->{$_}"} sort keys %$list; $element{addelistbox}->insert("end",@list); if ($element{addelframe}->Show() eq "Ok") { $element{v2caption}->configure(-background=>"yellow"); my $fname= substr $element{addelistbox}->get("active","active"),0,4; $fname = $var{v2}->add_frame($fname); my $index=0; my $maxindex = $element{frames}->index("end"); while ($index<$maxindex && $element{frames}->get($index,$index) lt $fname) { $index++; } $element{frames}->insert($index, $fname); $element{frames}->activate($index); $element{frames}->selectionSet($index); show_frame($fname); } } sub del_frame { return unless exists $var{v2}; $element{addelframe}->configure(-title=>"Delete Frame"); $element{addelabel}->configure(-text=>"Select a frame to delete:"); $element{addelistbox}->delete("0","end"); my $list = $var{v2}->getFrameIDs; my @list = map {"$_ - $list->{$_}"} sort keys %$list; $element{addelistbox}->insert("end",@list); if ($element{addelframe}->Show() eq "Ok") { $element{v2caption}->configure(-background=>"yellow"); my $fname= $element{addelistbox}->get("active","active"); $fname =~ s/^([A-Z0-9]+) .*/$1/; $var{v2}->remove_frame($fname); my $index=0; my $maxindex = $element{frames}->index("end"); while ($index<$maxindex && $element{frames}->get($index,$index) ne $fname) { $index++; } $element{frames}->delete($index,$index); if ($var{current_frame} eq $fname) { show_frame($element{frames}->get("active"),1); } if (exists $var{v2specent}->{$fname}) { $var{"v2$fname"}=""; $var{v2specent}->{$fname}->{label}->configure(-background=>"yellow"); } } } sub check_multi { my ($h,$w,%info); my @sel = $element{filelist}->curselection(); if ($#sel >0 && $var{simple}) { $element{rightFrame}->packForget; $element{rightFrameMul}->pack(-side=>"left",-fill=>"both", -expand=>"yes", -anchor=>"nw"); $var{simple}=0; } elsif ($#sel == 0 && ! $var{simple}) { $element{rightFrameMul}->packForget; $element{rightFrame}->pack(-side=>"left",-fill=>"both", -expand=>"yes", -anchor=>"nw"); $var{simple}=1; } } sub setfilename { my $tag = shift; # may be 'ID3v1', 'ID3v2' or 'filename' $tag =~ s/ID3//; return undef unless exists $var{$tag} || $tag eq "filename"; my $new = $var{setfilename}->{stencil}; my $i=0; foreach my $part (@{$var{setfilename}->{details}}) { my $code=$part->{tag}; if ($tag eq "v2") { $code =~ s/song/TIT2/ || $code =~ s/album/TALB/ || $code =~ s/artist/TPE1/; } my $txt=""; if ($tag eq "filename") { if ($code eq "song" || $code eq "artist" || $code eq "album" || $code eq "track") { $txt = $var{mp3}->{filename}->$code($var{dir}.$var{filename}); } } else { $txt = $var{"$tag$code"}; } $txt =~ s/ *$//; $txt = substr $txt, 0, $part->{length} if exists $part->{length} && ((! exists $part->{fill}) || exists $part->{precise} ); $txt = $part->{fill} x ($part->{length}-length($txt)) . $txt if exists $part->{fill}; $new =~ s/%$i/$txt/; $i++; } $new =~ s/ /_/g if exists $var{setfilename}->{nospaces}; $var{filename}=$new; $element{filenamelabel}->configure(-background=>"yellow"); return 1; } sub formatstr { my $format = shift; my %tags = (s=>"song", a=>"artist", l=>"album", y=>"year", g=>"genre", t=>"track"); my @fmt; while ($format =~ /%([0-9]*)(?:(!)?:(.))?([salygt])/g) { my $t; $t->{length}=$1 if defined $1 && $1 ne ""; $t->{precise}=1 if defined $2 && $2 ne ""; $t->{fill}=$3 if defined $3 && $3 ne ""; $t->{tag} = $tags{$4} if defined $4 && $4 ne ""; push @fmt, $t if defined $4 && $4 ne ""; } my $i=0; $format =~ s/%([0-9]*)(?:(!)?:(.))?([salygt])/"%".$i++/eg; $var{setfilename}->{stencil}=$format; $var{setfilename}->{details}=\@fmt; } sub element_change { my $element = shift; if ($element->PathName =~/v1/) { $element{v1caption}->configure(-background=>"yellow"); } elsif ($element->PathName =~/v2/) { $element{v2caption}->configure(-background=>"yellow"); } elsif ($element->PathName =~/fn/) { $element{filenamelabel}->configure(-background=>"yellow"); } } sub use_ucase { my $element=shift; if ($element =~ /Entry/) { my $evar = $element->cget(-textvariable); my $ovar = $$evar; $$evar =~ tr/[a-z]/[A-Z]/; $$evar =~ s/\.MP3$/.mp3/; if ($ovar ne $$evar) { element_change($element); } } elsif ($element =~ /Text/) { my $text = $element->get("0.1","end"); chomp $text; return if $text eq ""; if ($text =~ tr/[a-z]/[A-Z]/) { element_change($element); $element->delete("0.1","end"); $element->insert("end",$text); } } } sub use_lcase { my $element=shift; if ($element =~ /Entry/) { my $evar = $element->cget(-textvariable); return if $$evar eq ""; if ($$evar =~ tr/[A-Z]/[a-z]/) { element_change($element); } } elsif ($element =~ /Text/) { my $text = $element->get("0.1","end"); chomp $text; return if $text eq ""; if ($text =~ tr/[A-Z]/[a-z]/) { element_change($element); $element->delete("0.1","end"); $element->insert("end",$text); } } } sub use_ucase_first { my $element=shift; if ($element =~ /Entry/) { my $evar = $element->cget(-textvariable); return if $$evar eq ""; my $ovar=$$evar; $$evar=ucase_first($$evar); if ($ovar ne $$evar) { element_change($element); } } elsif ($element =~ /Text/) { my $text = $element->get("0.1","end"); chomp $text; return if $text eq ""; my $otext = ucase_first($text); if ($otext ne $text) { $element->delete("0.1","end"); $element->insert("end",$otext); element_change($element); } } } sub ucase_first { my $text=shift; $text =~ s/(\w+)/\L\u$1/gm; $text =~ s/('[A-Z])/\L$1/g; #'); $text =~ s/\.Mp3$/.mp3/; return $text; } sub use_spaces { my $element=shift; if ($element =~ /Entry/) { my $evar = $element->cget(-textvariable); return if $$evar eq ""; my $ovar=$$evar; $$evar=spaces($$evar); if ($ovar ne $$evar) { element_change($element); } } } sub spaces { my $text=shift; $text =~ s/%20/_/g; if ((($text =~ tr/././) >2)) { $text =~ s/\./ /g; $text =~ s/ (mp3)$/.$1/i; $text =~ s/(\w)-(\w)/$1 - $2/g; } elsif ($text =~ /_/) { $text =~ s/_/ /g; $text =~ s/(\w)-(\w)/$1 - $2/g; } else { $text =~ s/ /_/g; $text =~ s/_-_/-/g; } $text=~ s/ +/ /g; return $text; } sub filename_format { if ( $element{fnformat}->Show eq "Set") { formatstr($var{fnformat}); } } sub view_data { my $key= shift; warn "Viewing of $key of $var{current_frame} not supported yet\n"; } sub load_data { my ($key, $button,$label)= @_; my @types=(["All files", "*"]); my $file = $tktag->getOpenFile(-filetypes => \@types); if (open (LOAD, $file)) { binmode LOAD; local $/; undef $/; $var{"data_$key"}=<LOAD>; close LOAD; warn "Please set mime type according to new data\n"; $button->configure(-text => "Data of " .length($var{"data_$key"}) . " Bytes --"); $label->configure(-background=>"yellow"); $element{v2caption}->configure(-background=>"yellow"); } else { warn "Couldn't open $file for saving $var{current_frame}.$key"; } } sub save_data { my $key= shift; my @types=(["All files", "*"]); my $file = $tktag->getSaveFile(-filetypes => \@types, -initialfile => 'Untitled', -defaultextension => '.dat'); if (open (SAVE, ">$file")) { binmode SAVE; print SAVE $var{"data_$key"}; close SAVE; } else { warn "Couldn't open $file for saving $var{current_frame}.$key"; } } sub copyv1tov2 { my %v1tov2 = (artist=>"TPE1", album=>"TALB", song=>"TIT2", year=>"TYER", track=>"TRCK", comment=>"COMM", genre=>"TCON"); return unless exists $var{v1}; $var{v2}=$var{mp3}->newTag("ID3v2") unless exists $var{v2}; my $frames = $var{v2}->getFrameIDs; while (my ($v1,$v2) = each %v1tov2) { next if $var{"v1$v1"} eq ""; my ($data, $restr) = $var{v2}->what_data($v2); my @newdata; if ($#{$data}==0) { @newdata = ($var{"v1$v1"}); } else { foreach (@$data) { if ($_ eq "Text") { push @newdata, $var{"v1$v1"}; } else { push @newdata, ""; } } } if (exists $frames->{$v2}) { $var{v2}->change_frame($v2, @newdata); } else { $var{v2}->add_frame($v2, @newdata); add2framelist($v2); } if (exists $var{v2specent}->{$v2}) { $var{"v2$v2"}=$var{"v1$v1"}; $var{v2specent}->{$v2}->{label}->configure(-background=>"yellow"); } } $element{v2caption}->configure(-background=>"yellow"); } sub copyv2tov1 { } sub copyfntov1 { $var{v1}=$var{mp3}->newTag("ID3v1") unless exists $var{v1}; foreach (qw/song artist album track/) { $var{"v1$_"}=$var{mp3}->{filename}->$_($var{dir}.$var{filename}); } } sub copyfntov2 { } sub add2framelist { my $fname = shift; my $index=0; my $maxindex = $element{frames}->index("end"); while ($index<$maxindex && $element{frames}->get($index,$index) lt $fname) { $index++; } $element{frames}->insert($index, $fname); $element{frames}->activate($index); $element{frames}->selectionSet($index); } sub autosetfilename { foreach (@{$var{autoinfo}}) { last if setfilename($_); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3-Tag-1.13/TODO�����������������������������������������������������������������������������������0000700�0000000�0000000�00000002440�11230715704�010115� 0����������������������������������������������������������������������������������������������������ustar ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP3::Tag.pm =========== * perhaps restructuring of this wrapper module, as it should be easier to say which Tag::modules should be used * more testing MP3::Tag::ID3v1.pm ================== * more testing MP3::Tag::ID3v2.pm ================== * Encryption of frames (read and write) * Only first tag in front of file is read, tags inside mp3-data are ignored * Following frames are only supported in RAW mode: - EQUA -> Equalization - ETCO -> Event timing codes - MLLT -> MPEG location lookup table - POSS -> Position synchronisation frame - SYLT -> Synchronized tempo codes - RVAD -> Relative volume adjustment * more testing * reading of frames is very strict following rules of ID3v2 definition, but a lot of programs seem to do this not, so some frames are not read right (eg TMED of contains 'DIG, MD' instead of '(DIG/MD)', but only '(DIG/MD)' is returned correctly as 'Other Digital Media, MiniDisc' 'DIG, MD' is returned as 'DIG, MD' * frames like TMED who have to be specially encoded, are doing this only when the frame is read, not when it is written * Support for v2.4 output (is input fully supported now?) * http://gabriel.mp3-tech.org/mp3infotag.html http://web.archive.org/web/20010827032505/privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������