Audio-Scan
view release on metacpan or search on metacpan
- Added Opus codec support. (Jeff Muizelaar)
- ADTS: RT #124525, fixed incorrect duration calcuation due to off-by-one error
in frame counter.
- Added missing license meta name. (manwar)
1.00 2018-04-21
- Fixed parsing of DSF files with more than 2 channels.
0.99 2017-11-24
- Fixed a bug where an incorrect audio offset was returned when using
NO_ARTWORK and reading a file with an ID3v2.2 or v2.3 unsynchronization bit
set. The artwork in this type of file is only available when scanning in
normal mode (without NO_ARTWORK).
- Added id3_was_unsynced => 1 to the info section for v2.2/v2.3 files with the
whole-tag unsync bit set. This type of tag is generally harmful to parser performance,
so this flag might be useful if you want to find and upgrade this type of tag to v2.4.
- DSF: WAV_BLOCK_SIZE was being used instead of DSF_BLOCK_SIZE. (Kimmo Taskinen)
- ID3: Support for reading ID3 tags located >4GB into a file, such as in very large
DSF files. (Kimmo Taskinen)
- WavPack: Read correct samplerate and bits_per_sample for DSD files. (Kimmo Taskinen)
- DSF/DFF: added bitrate to info hash.
- Win32: Added binmode for all open files and filehandles.
0.82 2010-05-24 13:15:00
- RT 57664: ID3: Add checks to avoid reading too much inside an invalid frame.
- FLAC: Seeking bug fixes.
0.81 2010-05-15 10:40:00
- Fixed broken boolean values.
0.80 2010-05-14 23:15:00
- ID3: Fixed unsynchronisation of v2.2/v2.3 tags.
- FLAC: Fixed several seeking bugs and improved seeking algorithm.
- MP3: Subtract LAME encoder delay/padding from total samples to obtain more
accurate song duration value.
0.79 2010-04-27 14:30:00
- ID3: Bug 16079, fixed crash when reading empty TCON tag.
- Bug 16095, workaround Win32+filehandle bug where file pos could sometimes
be off-by-one after a PerlIO_read.
0.78 2010-04-16 15:45:00
- ID3: Really fixed empty text field bug this time.
0.74 2010-04-02 12:15:00
- ID3: Fixed bug where an empty text field could contain the text from the
previous frame.
0.73 2010-03-31 12:30:00
- Added find_frame_fh_return_info method for MP4 seeking from a filehandle.
0.72 2010-03-31 10:50:00
- Fixed bug in reading unsynchronized APIC frames, the APIC data returned was not
the correct size.
- Fixed Mac hints file to work properly on OSX Server 10.6.
0.71 2010-03-30 00:00:00
- Added find_frame_return_info method for MP4 that returns the seek offset as well
as a rewritten header that can be placed before the seeked audio to construct a
valid bitstream.
0.70 2010-03-23 19:00:00
- Replaced libid3tag with a custom ID3 tag parser. libid3tag would read an entire
lib/Audio/Scan.pm view on Meta::CPAN
without the environment variable set.
One limitation that currently exists is that memory for embedded images is still
allocated for ASF and Ogg Vorbis files.
This information is returned in different ways depending on the format:
ID3 (MP3, AAC, WAV, AIFF):
$tags->{APIC}->[3]: image length
$tags->{APIC}->[4]: image offset (unless APIC would need unsynchronization)
MP4:
$tags->{COVR}: image length
$tags->{COVR_offset}: image offset (always available)
Ogg Vorbis:
$tags->{ALLPICTURES}->[0]->{image_data}: image length
Image offset is not supported with Vorbis because the data is always base64-encoded.
lib/Audio/Scan.pm view on Meta::CPAN
$tags->{'COVER ART (FRONT)'}: image length
$tags->{'COVER ART (FRONT)_offset'}: image offset (always available)
=head1 MP3
=head2 INFO
The following metadata about a file may be returned:
id3_version (i.e. "ID3v2.4.0")
id3_was_unsynced (if a v2.2/v2.3 file needed whole-tag unsynchronization)
song_length_ms (duration in milliseconds)
layer (i.e. 3)
stereo
samples_per_frame
padding
audio_size (size of all audio frames)
audio_offset (byte offset to first audio frame)
bitrate (in bps, determined using Xing/LAME/VBRI if possible, or average in the worst case)
samplerate (in kHz)
vbr (1 if file is VBR)
// increased memory usage but the only way to do it, as frame size values only
// indicate the post-unsync size, so it's not possible to unsync each frame individually
// tested with v2.3-unsync.mp3
if ( !_check_buf(id3->infile, id3->buf, id3->size, id3->size) ) {
ret = 0;
goto out;
}
id3->size_remain = _id3_deunsync( buffer_ptr(id3->buf), id3->size );
DEBUG_TRACE(" Un-synchronized tag, new_size %d\n", id3->size_remain);
my_hv_store( id3->info, "id3_was_unsynced", newSVuv(1) );
}
else {
DEBUG_TRACE(" Ignoring v2.4 tag un-synchronize flag\n");
}
}
if (id3->flags & ID3_TAG_FLAG_EXTENDEDHEADER) {
uint32_t ehsize;
// If the tag is v2.2, this bit is actually the compression bit and the tag should be ignored
if (id3->version_major == 2) {
ret = 0;
goto out;
id3->size_remain -= 4;
size -= 4;
DEBUG_TRACE(" data length indicator, size %d\n", decoded_size);
}
if (flags & ID3_FRAME_FLAG_V24_UNSYNCHRONISATION) {
// Special case, do not unsync an APIC frame if not reading artwork,
// FF's are not likely to appear in the part we care about anyway
if ( !strcmp(id, "APIC") && _env_true("AUDIO_SCAN_NO_ARTWORK") ) {
DEBUG_TRACE(" Would un-synchronize APIC frame, but ignoring because of AUDIO_SCAN_NO_ARTWORK\n");
// Reset decoded_size to 0 since we aren't actually decoding.
// XXX this would break if we have a compressed + unsync APIC frame but not very likely in the real world
decoded_size = 0;
id3->tag_data_safe = 0;
}
else {
// tested with v2.4-unsync.mp3
if ( !_check_buf(id3->infile, id3->buf, size, ID3_BLOCK_SIZE) ) {
ret = 0;
goto out;
}
decoded_size = _id3_deunsync( buffer_ptr(id3->buf), size );
unsync_extra = size - decoded_size;
DEBUG_TRACE(" Un-synchronized frame, new_size %d\n", decoded_size);
}
}
if (flags & ID3_FRAME_FLAG_V24_COMPRESSION) {
// tested with v2.4-compressed-frame.mp3
// XXX need test for compressed + unsync
unsigned long tmp_size;
if ( !_check_buf(id3->infile, id3->buf, size, ID3_BLOCK_SIZE) ) {
ret = 0;
// Special handling for APIC tags when in skip_art mode
if (skip_art) {
av_push( framedata, newSVuv(size - read) );
// I don't think it's possible to obtain an APIC offset when a tag has been unsync'ed,
// so we can't support skip_art mode in this case. See v2.3-unsync-apic-bad-offset.mp3
if (id3->flags & ID3_TAG_FLAG_UNSYNCHRONISATION && id3->version_major < 4) {
DEBUG_TRACE(" cannot obtain APIC offset due to v2.3 unsync tag\n");
}
else {
// Record offset of APIC image data too, unless the data needs to be unsynchronized or is empty
if (id3->tag_data_safe && (size - read) > 0)
av_push( framedata, newSVuv(id3->offset + (id3->size - id3->size_remain) + read) );
}
_id3_skip(id3, size - read);
read = size;
}
// Special buffering mode for APIC data, avoids a large buffer allocation
else if (buffer_art) {
src/id3_compat.c view on Meta::CPAN
{"MCI", EQ(MCDI) /* Music CD identifier */},
#line 71 "id3_compat.gperf"
{"TIME", OBSOLETE /* Time [obsolete] */},
#line 67 "id3_compat.gperf"
{"TDY", EQ(TDLY) /* Playlist delay */},
#line 94 "id3_compat.gperf"
{"TSS", EQ(TSSE) /* Software/hardware and settings used for encoding */},
#line 92 "id3_compat.gperf"
{"TSI", OBSOLETE /* Size [obsolete] */},
#line 103 "id3_compat.gperf"
{"ULT", EQ(USLT) /* Unsynchronised lyric/text transcription */},
#line 76 "id3_compat.gperf"
{"TOA", EQ(TOPE) /* Original artist(s)/performer(s) */},
#line 40 "id3_compat.gperf"
{"COM", EQ(COMM) /* Comments */},
#line 81 "id3_compat.gperf"
{"TOT", EQ(TOAL) /* Original album/movie/show title */},
#line 64 "id3_compat.gperf"
{"TCR", EQ(TCOP) /* Copyright message */},
#line 62 "id3_compat.gperf"
{"TCO", TX(TCON) /* Content type */},
src/id3_compat.gperf view on Meta::CPAN
TSIZ, OBSOLETE /* Size [obsolete] */
TSS, EQ(TSSE) /* Software/hardware and settings used for encoding */
TT1, EQ(TIT1) /* Content group description */
TT2, EQ(TIT2) /* Title/songname/content description */
TT3, EQ(TIT3) /* Subtitle/description refinement */
TXT, EQ(TEXT) /* Lyricist/text writer */
TXX, EQ(TXXX) /* User defined text information frame */
TYE, EQ(TYER) /* Year [obsolete] */
TYER, OBSOLETE /* Year [obsolete] */
UFI, EQ(UFID) /* Unique file identifier */
ULT, EQ(USLT) /* Unsynchronised lyric/text transcription */
WAF, EQ(WOAF) /* Official audio file webpage */
WAR, EQ(WOAR) /* Official artist/performer webpage */
WAS, EQ(WOAS) /* Official audio source webpage */
WCM, EQ(WCOM) /* Commercial information */
WCP, EQ(WCOP) /* Copyright/legal information */
WPB, EQ(WPUB) /* Publishers official webpage */
WXX, EQ(WXXX) /* User defined URL link frame */
TCP, EQ(TCMP) /* non-standard iTunes compilation flag */
TST, EQ(TSOT) /* non-standard iTunes track sort */
TSA, EQ(TSOA) /* non-standard iTunes album sort */
src/id3_frametype.c view on Meta::CPAN
{"TCOM", FRAME(text), "Composer"},
#line 267 "id3_frametype.gperf"
{"COMR", FRAME(COMR), "Commercial frame"},
#line 266 "id3_frametype.gperf"
{"COMM", FRAME(COMM), "Comments"},
#line 291 "id3_frametype.gperf"
{"TCON", FRAME(text), "Content type"},
#line 277 "id3_frametype.gperf"
{"PCNT", FRAME(PCNT), "Play counter"},
#line 279 "id3_frametype.gperf"
{"POSS", FRAME(POSS), "Position synchronisation frame"},
#line 270 "id3_frametype.gperf"
{"ETCO", FRAME(ETCO), "Event timing codes"},
#line 318 "id3_frametype.gperf"
{"TPE2", FRAME(text), "Band/orchestra/accompaniment"},
#line 306 "id3_frametype.gperf"
{"TKEY", FRAME(text), "Initial key"},
#line 299 "id3_frametype.gperf"
{"TENC", FRAME(text), "Encoded by"},
#line 295 "id3_frametype.gperf"
{"TDOR", FRAME(text), "Original release time"},
src/id3_frametype.c view on Meta::CPAN
{"TRCK", FRAME(text), "Track number/position in set"},
#line 320 "id3_frametype.gperf"
{"TPE4", FRAME(text), "Interpreted, remixed, or otherwise modified by"},
#line 294 "id3_frametype.gperf"
{"TDLY", FRAME(text), "Playlist delay"},
#line 282 "id3_frametype.gperf"
{"RVA2", FRAME(RVA2), "Relative volume adjustment (2)"},
#line 296 "id3_frametype.gperf"
{"TDRC", FRAME(text), "Recording time"},
#line 336 "id3_frametype.gperf"
{"USLT", FRAME(USLT), "Unsynchronised lyric/text transcription"},
#line 339 "id3_frametype.gperf"
{"WOAF", FRAME(url), "Official audio file webpage"},
#line 298 "id3_frametype.gperf"
{"TDTG", FRAME(text), "Tagging time"},
#line 285 "id3_frametype.gperf"
{"SIGN", FRAME(SIGN), "Signature frame"},
#line 341 "id3_frametype.gperf"
{"WOAS", FRAME(url), "Official audio source webpage"},
#line 300 "id3_frametype.gperf"
{"TEXT", FRAME(text), "Lyricist/text writer"},
src/id3_frametype.gperf view on Meta::CPAN
EQU2, FRAME(EQU2), "Equalisation (2)"
ETCO, FRAME(ETCO), "Event timing codes"
GEOB, FRAME(GEOB), "General encapsulated object"
GRID, FRAME(GRID), "Group identification registration"
LINK, FRAME(LINK), "Linked information"
MCDI, FRAME(MCDI), "Music CD identifier"
MLLT, FRAME(MLLT), "MPEG location lookup table"
OWNE, FRAME(OWNE), "Ownership frame"
PCNT, FRAME(PCNT), "Play counter"
POPM, FRAME(POPM), "Popularimeter"
POSS, FRAME(POSS), "Position synchronisation frame"
PRIV, FRAME(PRIV), "Private frame"
RBUF, FRAME(RBUF), "Recommended buffer size"
RVA2, FRAME(RVA2), "Relative volume adjustment (2)"
RVRB, FRAME(RVRB), "Reverb"
SEEK, FRAME(SEEK), "Seek frame"
SIGN, FRAME(SIGN), "Signature frame"
SYLT, FRAME(SYLT), "Synchronised lyric/text"
SYTC, FRAME(SYTC), "Synchronised tempo codes"
TALB, FRAME(text), "Album/movie/show title"
TBPM, FRAME(text), "BPM (beats per minute)"
src/id3_frametype.gperf view on Meta::CPAN
TRSO, FRAME(text), "Internet radio station owner"
TSOA, FRAME(text), "Album sort order"
TSOP, FRAME(text), "Performer sort order"
TSOT, FRAME(text), "Title sort order"
TSRC, FRAME(text), "ISRC (international standard recording code)"
TSSE, FRAME(text), "Software/hardware and settings used for encoding"
TSST, FRAME(text), "Set subtitle"
TXXX, FRAME(TXXX), "User defined text information frame"
UFID, FRAME(UFID), "Unique file identifier"
USER, FRAME(USER), "Terms of use"
USLT, FRAME(USLT), "Unsynchronised lyric/text transcription"
WCOM, FRAME(url), "Commercial information"
WCOP, FRAME(url), "Copyright/legal information"
WOAF, FRAME(url), "Official audio file webpage"
WOAR, FRAME(url), "Official artist/performer webpage"
WOAS, FRAME(url), "Official audio source webpage"
WORS, FRAME(url), "Official Internet radio station homepage"
WPAY, FRAME(url), "Payment"
WPUB, FRAME(url), "Publishers official webpage"
WXXX, FRAME(WXXX), "User defined URL link frame"
XSOP, FRAME(text), "Performer sort order (v2.3)"
my $tags = $s->{tags};
is( ref $tags->{RGAD}, 'HASH', 'RGAD frame is a hash' );
is( $tags->{RGAD}->{peak}, '0.999020', 'RGAD peak ok' );
is( $tags->{RGAD}->{track_originator}, 3, 'RGAD track originator ok' );
is( $tags->{RGAD}->{track_gain}, '-5.700000 dB', 'RGAD track gain ok' );
is( $tags->{RGAD}->{album_originator}, 3, 'RGAD album originator ok' );
is( $tags->{RGAD}->{album_gain}, '-5.600000 dB', 'RGAD album gain ok' );
}
# v2.4 per-frame unsynchronisation
{
my $s = Audio::Scan->scan( _f('v2.4-unsync.mp3') );
my $tags = $s->{tags};
is( $tags->{TALB}, 'Album', 'v2.4 unsync TALB ok' );
is( $tags->{TDRC}, 2009, 'v2.4 unsync TDRC ok' );
is( $tags->{TIT2}, 'Title', 'v2.4 unsync TIT2 ok' );
is( $tags->{TPE1}, 'Artist', 'v2.4 unsync TPE1 ok' );
}
# v2.3 whole tag unsynchronisation
{
my $s = Audio::Scan->scan( _f('v2.3-unsync.mp3') );
my $tags = $s->{tags};
is( $tags->{TALB}, 'Hydroponic Garden', 'v2.3 unsync TALB ok' );
is( $tags->{TCON}, 'Ambient', 'v2.3 unsync TCON ok' );
is( $tags->{TPE1}, 'Carbon Based Lifeforms', 'v2.3 unsync TPE1 ok' );
is( $tags->{TPE2}, 'Carbon Based Lifeforms', 'v2.3 unsync TPE2 ok' );
is( $tags->{TRCK}, 4, 'v2.3 unsync TRCK ok' );
}
# v2.4 with UTF-8 encoded comment with empty null description
{
my $s = Audio::Scan->scan( _f('v2.4-utf8-null-comment.mp3') );
my $tags = $s->{tags};
is( $tags->{COMM}->[0], 'eng', 'v2.4 UTF-8 null comment lang ok' );
is( $tags->{COMM}->[1], '', 'v2.4 UTF-8 null comment description ok' );
is( $tags->{COMM}->[2], 'Test 123', 'v2.4 UTF-8 null comment value ok' );
}
# v2.4 with unsynchronized APIC frame, check that the correct length is read
# in both artwork and no-artwork modes
{
local $ENV{AUDIO_SCAN_NO_ARTWORK} = 1;
my $s = Audio::Scan->scan( _f('v2.4-apic-unsync.mp3') );
my $tags = $s->{tags};
# This is not the actual length but it's OK since we don't unsync in no-artwork mode
is( $tags->{APIC}->[3], 46240, 'v2.4 APIC unsync no-artwork length ok' );
is( !defined $tags->{APIC}->[4], 1, 'v2.4 APIC unsync no-artwork has no offset ok' );
}
( run in 0.304 second using v1.01-cache-2.11-cpan-0d8aa00de5b )