flacinfo-rb 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README +4 -2
- data/{lib/flacinfo.rb → flacinfo.rb} +226 -45
- metadata +5 -4
data/README
CHANGED
@@ -17,8 +17,9 @@ seektable :: hash of arrays of seek points
|
|
17
17
|
comment :: array of VORBIS COMMENT block metadata
|
18
18
|
tags :: user-friendly hash of Vorbis comment metadata key=value pairs
|
19
19
|
application :: hash of APPLICATION block metadata
|
20
|
-
padding :: hash of PADDING block metadata
|
21
|
-
cuesheet :: hash of CUESHEET block metadata
|
20
|
+
padding :: hash of PADDING block metadata
|
21
|
+
cuesheet :: hash of CUESHEET block metadata
|
22
|
+
picture :: hash of PICTURE block metadata
|
22
23
|
flac_file :: hash of APPLICATION Id 0x41544348 (Flac File) metadata if present
|
23
24
|
|
24
25
|
== Public methods ==
|
@@ -30,6 +31,7 @@ print_seektable :: pretty-print seektable hash
|
|
30
31
|
meta_flac :: prints all META BLOCKS. (Mostly) equivelant to 'metaflac --list'
|
31
32
|
raw_data_dump(?) :: if passed a filename it will dump flac_file['raw_data'] to that file,
|
32
33
|
otherwise it will dump it to the console (even if binary!)
|
34
|
+
write_picture(?) :: write image from PICTURE block(s) to optional file
|
33
35
|
|
34
36
|
For more/different documentation see http://badcomputer.org/unix/code/flacinfo/
|
35
37
|
|
@@ -1,27 +1,34 @@
|
|
1
1
|
# = Description
|
2
2
|
#
|
3
3
|
# flacinfo-rb gives you access to low level information on Flac files.
|
4
|
-
# * It parses stream information (
|
5
|
-
# * It parses Vorbis comments (
|
6
|
-
# * It parses the seek table (
|
7
|
-
# * It parses the 'application metadata block' (
|
8
|
-
# * If
|
4
|
+
# * It parses stream information (METADATA_BLOCK_STREAMINFO).
|
5
|
+
# * It parses Vorbis comments (METADATA_BLOCK_VORBIS_COMMENT).
|
6
|
+
# * It parses the seek table (METADATA_BLOCK_SEEKTABLE).
|
7
|
+
# * It parses the 'application metadata block' (METADATA_BLOCK_APPLICATION).
|
8
|
+
# * If application is ID 0x41544348 (Flac File)
|
9
9
|
# then we can parse that too.
|
10
|
-
# * It recognizes (but does not yet parse) the cue sheet (
|
10
|
+
# * It recognizes (but does not yet parse) the cue sheet (METADATA_BLOCK_CUESHEET).
|
11
|
+
# * It parses zero or more picture blocks (METADATA_BLOCK_PICTURE)
|
12
|
+
# * It allows you to write embedded images to a file.
|
13
|
+
#
|
14
|
+
# My plans are to eventually have this library write as well as read data to/from Flac files,
|
15
|
+
# so in the future it should become quite a nice native Ruby library interface which will allow
|
16
|
+
# the user to mimic most functionality of the 'metaflac' binary programmatically.
|
11
17
|
#
|
12
18
|
# = Copyright and Disclaimer
|
19
|
+
#
|
13
20
|
# Copyright:: (c) 2006 Darren Kirby
|
14
21
|
# FlacInfo is free software.
|
15
22
|
# No warranty is provided and the author cannot accept responsibility
|
16
23
|
# for lost or damaged files.
|
17
24
|
# License:: Ruby
|
18
25
|
# Author:: Darren Kirby (mailto:bulliver@badcomputer.org)
|
19
|
-
# Website:: http://badcomputer.org/unix/code/flacinfo/
|
26
|
+
# Website:: http://badcomputer.org/unix/code/flacinfo/index.bot
|
20
27
|
#
|
21
28
|
# = More information
|
22
29
|
#
|
23
30
|
# * There is an example irb session that shows typical usage at
|
24
|
-
# http://badcomputer.org/unix/code/flacinfo/
|
31
|
+
# http://badcomputer.org/unix/code/flacinfo/index.bot
|
25
32
|
# * The Flac spec is at:
|
26
33
|
# http://flac.sourceforge.net/format.html
|
27
34
|
# * The Vorbis Comment spec is at:
|
@@ -33,39 +40,86 @@
|
|
33
40
|
class FlacInfoError < StandardError
|
34
41
|
end
|
35
42
|
|
36
|
-
# Note: STREAMINFO is the only block guaranteed to be present.
|
37
|
-
#
|
43
|
+
# Note: STREAMINFO is the only block guaranteed to be present in the Flac file.
|
44
|
+
# All attributes will be present, but empty if the associated block is not present in the Flac file.
|
38
45
|
class FlacInfo
|
39
46
|
|
40
|
-
# Hash of values extracted from the STREAMINFO block. Keys are
|
41
|
-
# '
|
47
|
+
# Hash of values extracted from the STREAMINFO block. Keys are:
|
48
|
+
# 'offset':: The STREAMINFO block's offset from the beginning of the file (not including the block header).
|
49
|
+
# 'block_size':: The size of the STREAMINFO block (not including the block header).
|
50
|
+
# 'minimum_block':: The minimum block size (in samples) used in the stream.
|
51
|
+
# 'maximum_block':: The maximum block size (in samples) used in the stream.
|
52
|
+
# 'minimum_frame':: The minimum frame size (in bytes) used in the stream.
|
53
|
+
# 'maximum_frame':: The maximum frame size (in bytes) used in the stream.
|
54
|
+
# 'samplerate':: Sample rate in Hz.
|
55
|
+
# 'channels':: The number of channels used in the stream.
|
56
|
+
# 'bits_per_sample':: The number of bits per sample used in the stream.
|
57
|
+
# 'total_samples':: The total number of samples in stream.
|
58
|
+
# 'md5':: MD5 signature of the unencoded audio data.
|
42
59
|
attr_reader :streaminfo
|
43
60
|
|
44
|
-
# Hash of values extracted from the SEEKTABLE block. Keys are
|
45
|
-
# '
|
46
|
-
#
|
61
|
+
# Hash of values extracted from the SEEKTABLE block. Keys are:
|
62
|
+
# 'offset':: The SEEKTABLE block's offset from the beginning of the file (not including the block header).
|
63
|
+
# 'block_size':: The size of the SEEKTABLE block (not including the block header).
|
64
|
+
# 'seek_points':: The number of seek points in the block.
|
65
|
+
# 'points':: Another hash whose keys start at 0 and end at ('seek_points' - 1). Each "seektable['points'][n]" hash
|
66
|
+
# contains an array whose (integer) values are:
|
67
|
+
# '0':: Sample number of first sample in the target frame, or 0xFFFFFFFFFFFFFFFF for a placeholder point.
|
68
|
+
# '1':: Offset (in bytes) from the first byte of the first frame header to the first byte of the target frame's header.
|
69
|
+
# '2':: Number of samples in the target frame.
|
47
70
|
attr_reader :seektable
|
48
71
|
|
49
72
|
# Array of "name=value" strings extracted from the VORBIS_COMMENT block. This is just the contents, metadata is in 'tags'.
|
50
73
|
attr_reader :comment
|
51
74
|
|
52
|
-
# Hash of the 'comment' values separated into "key => value" pairs as well as
|
75
|
+
# Hash of the 'comment' values separated into "key => value" pairs as well as the keys:
|
76
|
+
# 'offset':: The VORBIS_COMMENT block's offset from the beginning of the file (not including the block header).
|
77
|
+
# 'block_size':: The size of the VORBIS_COMMENT block (not including the block header).
|
78
|
+
# 'vendor_tag':: Typically, the name and version of the software that encoded the file.
|
53
79
|
attr_reader :tags
|
54
80
|
|
55
|
-
# Hash of values extracted from the APPLICATION block. Keys are
|
81
|
+
# Hash of values extracted from the APPLICATION block. Keys are:
|
82
|
+
# 'offset':: The APPLICATION block's offset from the beginning of the file (not including the block header).
|
83
|
+
# 'block_size':: The size of the APPLICATION block (not including the block header).
|
84
|
+
# 'ID':: Registered application ID. See http://flac.sourceforge.net/id.html
|
85
|
+
# 'name':: Name of the registered application ID.
|
56
86
|
attr_reader :application
|
57
87
|
|
58
|
-
# Hash of values extracted from the PADDING block.
|
88
|
+
# Hash of values extracted from the PADDING block. Keys are:
|
89
|
+
# 'offset':: The PADDING block's offset from the beginning of the file (not including the block header).
|
90
|
+
# 'block_size':: The size of the PADDING block (not including the block header).
|
59
91
|
attr_reader :padding
|
60
92
|
|
61
|
-
# Hash of values extracted from the CUESHEET block.
|
93
|
+
# Hash of values extracted from the CUESHEET block. Keys are:
|
94
|
+
# 'offset':: The CUESHEET block's offset from the beginning of the file (not including the block header).
|
95
|
+
# 'block_size':: The size of the CUESHEET block (not including the block header).
|
62
96
|
attr_reader :cuesheet
|
63
97
|
|
64
|
-
# Hash of values extracted from
|
65
|
-
#
|
98
|
+
# Hash of values extracted from one or more PICTURE blocks. This hash always includes the key 'n' which is the number of
|
99
|
+
# PICTURE blocks found, else '0'. For each block found there will be an integer key starting from 1. Each of these is a
|
100
|
+
# hash which contains the keys:
|
101
|
+
# 'offset':: The PICTURE block's offset from the beginning of the file (not including the block header).
|
102
|
+
# 'block_size':: The size of the PICTURE block (not including the block header).
|
103
|
+
# 'type_int':: The picture type according to the ID3v2 APIC frame.
|
104
|
+
# 'type_string':: A text value representing the picture type.
|
105
|
+
# 'description_string':: A text description of the picture.
|
106
|
+
# 'mime_type':: The MIME type string. May be '-->' to signify that the data part is a URL of the picture.
|
107
|
+
# 'colour_depth':: The color depth of the picture in bits-per-pixel.
|
108
|
+
# 'n_colours':: For indexed-color pictures (e.g. GIF), the number of colors used, or 0 for non-indexed pictures.
|
109
|
+
# 'width':: The width of the picture in pixels.
|
110
|
+
# 'height':: The height of the picture in pixels.
|
111
|
+
# 'raw_data_offset':: The raw picture data's offset from the beginning of the file.
|
112
|
+
# 'raw_data_length':: The length of the picture data in bytes.
|
113
|
+
attr_reader :picture
|
114
|
+
|
115
|
+
# Hash of values extracted from an APPLICATION block if it is type 0x41544348 (Flac File).
|
116
|
+
# Keys are:
|
117
|
+
# 'description':: A brief text description of the contents.
|
118
|
+
# 'mime_type':: The Mime type of the contents.
|
119
|
+
# 'raw_data':: The contents. May be binary.
|
66
120
|
attr_reader :flac_file
|
67
121
|
|
68
|
-
# FlacInfo is the
|
122
|
+
# FlacInfo is the class for parsing Flac files.
|
69
123
|
#
|
70
124
|
# :call-seq:
|
71
125
|
# FlacInfo.new(file) -> FlacInfo instance
|
@@ -75,7 +129,7 @@ class FlacInfo
|
|
75
129
|
parse_flac_meta_blocks
|
76
130
|
end
|
77
131
|
|
78
|
-
# Returns true if @tags[
|
132
|
+
# Returns true if @tags[tag] has a value, false otherwise.
|
79
133
|
#
|
80
134
|
# :call-seq:
|
81
135
|
# FlacInfo.hastag?(tag) -> bool
|
@@ -84,7 +138,7 @@ class FlacInfo
|
|
84
138
|
@tags["#{tag}"] ? true : false
|
85
139
|
end
|
86
140
|
|
87
|
-
#
|
141
|
+
# Pretty print comment hash.
|
88
142
|
#
|
89
143
|
# :call-seq:
|
90
144
|
# FlacInfo.print_tags -> nil
|
@@ -94,7 +148,7 @@ class FlacInfo
|
|
94
148
|
nil
|
95
149
|
end
|
96
150
|
|
97
|
-
#
|
151
|
+
# Pretty print streaminfo hash.
|
98
152
|
#
|
99
153
|
# :call-seq:
|
100
154
|
# FlacInfo.print_streaminfo -> nil
|
@@ -104,7 +158,7 @@ class FlacInfo
|
|
104
158
|
nil
|
105
159
|
end
|
106
160
|
|
107
|
-
#
|
161
|
+
# Pretty print the seektable.
|
108
162
|
#
|
109
163
|
# :call-seq:
|
110
164
|
# FlacInfo.print_seektable -> nil
|
@@ -121,13 +175,14 @@ class FlacInfo
|
|
121
175
|
nil
|
122
176
|
end
|
123
177
|
|
124
|
-
# This method produces output
|
178
|
+
# This method produces output similar to 'metaflac --list'.
|
125
179
|
#
|
126
180
|
# :call-seq:
|
127
181
|
# FlacInfo.meta_flac -> nil
|
128
182
|
#
|
129
183
|
def meta_flac
|
130
184
|
n = 0
|
185
|
+
pictures_seen = 0
|
131
186
|
@metadata_blocks.each do |block|
|
132
187
|
puts "METADATA block ##{n}"
|
133
188
|
puts " type: #{block[1]} (#{block[0].upcase})"
|
@@ -145,6 +200,9 @@ class FlacInfo
|
|
145
200
|
meta_vorb
|
146
201
|
when 5
|
147
202
|
meta_cue
|
203
|
+
when 6
|
204
|
+
pictures_seen += 1
|
205
|
+
meta_pict(pictures_seen)
|
148
206
|
end
|
149
207
|
n += 1
|
150
208
|
end
|
@@ -154,7 +212,7 @@ class FlacInfo
|
|
154
212
|
# Dumps the contents of flac_file['raw_data']
|
155
213
|
#
|
156
214
|
# :call-seq:
|
157
|
-
# FlacInfo.raw_data_dump()
|
215
|
+
# FlacInfo.raw_data_dump() -> nil
|
158
216
|
# FlacInfo.raw_data_dump(outfile) -> nil
|
159
217
|
#
|
160
218
|
# If passed with 'outfile', the data will be written to a file with that name
|
@@ -179,6 +237,57 @@ class FlacInfo
|
|
179
237
|
end
|
180
238
|
end
|
181
239
|
|
240
|
+
# Writes embedded images to a file
|
241
|
+
#
|
242
|
+
# :call-seq:
|
243
|
+
# FlacInfo.write_picture() -> nil
|
244
|
+
# FlacInfo.write_picture(:outfile=>"str") -> nil
|
245
|
+
# FlacInfo.write_picture(:n=>int) -> nil
|
246
|
+
# FlacInfo.write_picture(:outfile=>"str", :n=>int) -> nil
|
247
|
+
#
|
248
|
+
# If passed with ':outfile', the image will be written to a file with that name
|
249
|
+
# otherwise it is written to the value of the 'album' tag if it exists, otherwise it
|
250
|
+
# is written to 'flacimage'. All three of these will have a dot plus the relevant file
|
251
|
+
# extension appended. The argument to ':n' is which image to write in case of multiples.
|
252
|
+
#
|
253
|
+
def write_picture(args = {})
|
254
|
+
if @picture["n"] == 0
|
255
|
+
raise FlacInfoError, "There is no METADATA_BLOCK_PICTURE"
|
256
|
+
end
|
257
|
+
|
258
|
+
if args.has_key?(:n)
|
259
|
+
n = args[:n]
|
260
|
+
else
|
261
|
+
n = 1
|
262
|
+
end
|
263
|
+
|
264
|
+
# "image/jpeg" => "jpeg"
|
265
|
+
extension = @picture[n]["mime_type"].split("/")[1]
|
266
|
+
|
267
|
+
if not args.has_key?(:outfile)
|
268
|
+
if @tags["album"] == nil or @tags["album"] == ""
|
269
|
+
outfile = "flacimage#{n}.#{extension}"
|
270
|
+
else
|
271
|
+
outfile = "#{@tags["album"]}#{n}.#{extension}"
|
272
|
+
end
|
273
|
+
else
|
274
|
+
outfile = "#{args[:outfile]}.#{extension}"
|
275
|
+
end
|
276
|
+
|
277
|
+
in_p = File.new(@filename, "rb")
|
278
|
+
out_p = File.new(outfile, "wb")
|
279
|
+
|
280
|
+
out_p.binmode
|
281
|
+
|
282
|
+
in_p.seek(@picture[n]['raw_data_offset'], IO::SEEK_CUR)
|
283
|
+
raw_data = in_p.read(@picture[n]['raw_data_length'])
|
284
|
+
out_p.write(raw_data)
|
285
|
+
|
286
|
+
in_p.close
|
287
|
+
out_p.close
|
288
|
+
|
289
|
+
nil
|
290
|
+
end
|
182
291
|
|
183
292
|
private
|
184
293
|
# The following six methods are just helpers for meta_flac
|
@@ -226,7 +335,7 @@ class FlacInfo
|
|
226
335
|
|
227
336
|
def meta_vorb
|
228
337
|
puts " length: #{@tags['block_size']}"
|
229
|
-
puts "
|
338
|
+
puts " vendor string: #{@tags['vendor_tag']}"
|
230
339
|
puts " comments: #{@comment.size}"
|
231
340
|
n = 0
|
232
341
|
@comment.each do |c|
|
@@ -239,6 +348,19 @@ class FlacInfo
|
|
239
348
|
puts " length: #{@cuesheet['block_size']}"
|
240
349
|
end
|
241
350
|
|
351
|
+
def meta_pict(n)
|
352
|
+
puts " length: #{@picture[n]['block_size']}"
|
353
|
+
puts " type: #{@picture[n]['type_int']} => #{@picture[n]['type_string']}"
|
354
|
+
puts " mimetype: #{@picture[n]['mime_type']}"
|
355
|
+
puts " description: #{@picture[n]['description_string']}"
|
356
|
+
puts " image width: #{@picture[n]['width']}"
|
357
|
+
puts " image height: #{@picture[n]['height']}"
|
358
|
+
puts " colour depth: #{@picture[n]['colour_depth']}"
|
359
|
+
puts " number of colours: #{@picture[n]['n_colours']}"
|
360
|
+
puts " image size: #{@picture[n]['raw_data_length']} bytes"
|
361
|
+
end
|
362
|
+
|
363
|
+
|
242
364
|
# This is where the 'real' parsing starts.
|
243
365
|
def parse_flac_meta_blocks
|
244
366
|
@fp = File.new(@filename, "rb")
|
@@ -248,9 +370,7 @@ class FlacInfo
|
|
248
370
|
@padding = {}
|
249
371
|
@application = {}
|
250
372
|
@cuesheet = {}
|
251
|
-
|
252
|
-
typetable = { 0 => "streaminfo", 1 => "padding", 2 => "application",
|
253
|
-
3 => "seektable", 4 => "vorbis_comment", 5 => "cuesheet" }
|
373
|
+
@picture = {"n" => 0}
|
254
374
|
|
255
375
|
header = @fp.read(4)
|
256
376
|
# First 4 bytes must be 0x66, 0x4C, 0x61, and 0x43
|
@@ -258,9 +378,14 @@ class FlacInfo
|
|
258
378
|
raise FlacInfoError, "#{@filename} does not appear to be a valid Flac file"
|
259
379
|
end
|
260
380
|
|
381
|
+
typetable = { 0 => "streaminfo", 1 => "padding", 2 => "application",
|
382
|
+
3 => "seektable", 4 => "vorbis_comment", 5 => "cuesheet",
|
383
|
+
6 => "picture" }
|
384
|
+
|
261
385
|
@metadata_blocks = []
|
262
386
|
lastheader = 0
|
263
387
|
n = 0
|
388
|
+
|
264
389
|
until lastheader == 1
|
265
390
|
# first bit = Last-metadata-block flag
|
266
391
|
# bits 2-7 = BLOCK_TYPE. See typetable above
|
@@ -268,17 +393,26 @@ class FlacInfo
|
|
268
393
|
lastheader = block_header[0].to_i & 1
|
269
394
|
type = sprintf("%u", "0b#{block_header[1..7]}").to_i
|
270
395
|
@metadata_blocks[n] = ["#{typetable[type]}", type, lastheader]
|
396
|
+
|
397
|
+
if type >= typetable.size
|
398
|
+
raise FlacInfoError, "Invalid block header type"
|
399
|
+
end
|
400
|
+
|
271
401
|
self.send "parse_#{typetable[type]}"
|
272
402
|
n += 1
|
273
403
|
end
|
404
|
+
|
405
|
+
@fp.close
|
274
406
|
end
|
275
407
|
|
276
408
|
def parse_seektable
|
277
409
|
begin
|
278
410
|
@seektable['block_size'] = @fp.read(3).reverse.unpack("v*")[0]
|
411
|
+
@seektable['offset'] = @fp.tell
|
279
412
|
@seektable['seek_points'] = @seektable['block_size'] / 18
|
280
413
|
n = 0
|
281
414
|
@seektable['points'] = {}
|
415
|
+
|
282
416
|
@seektable['seek_points'].times do
|
283
417
|
pt_arr = []
|
284
418
|
pt_arr << @fp.read(8).reverse.unpack("V*")[0]
|
@@ -287,23 +421,61 @@ class FlacInfo
|
|
287
421
|
@seektable['points'][n] = pt_arr
|
288
422
|
n += 1
|
289
423
|
end
|
424
|
+
|
290
425
|
rescue
|
291
|
-
raise FlacInfoError, "Could not parse
|
426
|
+
raise FlacInfoError, "Could not parse METADATA_BLOCK_SEEKTABLE"
|
292
427
|
end
|
293
428
|
end
|
294
429
|
|
295
430
|
# Not parsed yet, I have no flacs with a cuesheet!
|
296
431
|
def parse_cuesheet
|
297
|
-
|
298
|
-
|
432
|
+
begin
|
433
|
+
@cuesheet['block_size'] = @fp.read(3).reverse.unpack("v*")[0]
|
434
|
+
@cuesheet['offset'] = @fp.tell
|
435
|
+
@fp.seek(@cuesheet['block_size'], IO::SEEK_CUR)
|
436
|
+
rescue
|
437
|
+
raise FlacInfoError, "Could not parse METADATA_BLOCK_CUESHEET"
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
def parse_picture
|
442
|
+
n = @picture["n"] + 1
|
443
|
+
@picture["n"] = n
|
444
|
+
@picture[n] = {}
|
445
|
+
|
446
|
+
picture_type = ["Other", "32x32 pixels file icon", "Other file icon", "Cover (front)", "Cover (back)",
|
447
|
+
"Leaflet page", "Media", "Lead artist/lead performer/soloist", "Artist/performer",
|
448
|
+
"Conductor", "Band/Orchestra", "Composer", "Lyricist/text writer", "Recording Location",
|
449
|
+
"During recording", "During performance", "Movie/video screen capture", "A bright
|
450
|
+
coloured fish", "Illustration", "Band/artist logotype", "Publisher/Studio logotype"]
|
451
|
+
|
452
|
+
begin
|
453
|
+
@picture[n]['block_size'] = @fp.read(3).reverse.unpack("v*")[0]
|
454
|
+
@picture[n]['offset'] = @fp.tell
|
455
|
+
@picture[n]['type_int'] = @fp.read(4).reverse.unpack("v*")[0]
|
456
|
+
@picture[n]['type_string'] = picture_type[@picture[n]['type_int']]
|
457
|
+
mime_length = @fp.read(4).reverse.unpack("v*")[0]
|
458
|
+
@picture[n]['mime_type'] = @fp.read(mime_length).unpack("a*")[0]
|
459
|
+
description_length = @fp.read(4).reverse.unpack("v*")[0]
|
460
|
+
@picture[n]['description_string'] = @fp.read(description_length).unpack("M*")[0]
|
461
|
+
@picture[n]['width'] = @fp.read(4).reverse.unpack("v*")[0]
|
462
|
+
@picture[n]['height'] = @fp.read(4).reverse.unpack("v*")[0]
|
463
|
+
@picture[n]['colour_depth'] = @fp.read(4).reverse.unpack("v*")[0]
|
464
|
+
@picture[n]['n_colours'] = @fp.read(4).reverse.unpack("v*")[0]
|
465
|
+
@picture[n]['raw_data_length'] = @fp.read(4).reverse.unpack("V*")[0]
|
466
|
+
@picture[n]['raw_data_offset'] = @fp.tell
|
467
|
+
@fp.seek((@picture[n]['raw_data_length']), IO::SEEK_CUR)
|
468
|
+
rescue
|
469
|
+
raise FlacInfoError, "Could not parse METADATA_BLOCK_PICTURE"
|
470
|
+
end
|
299
471
|
end
|
300
472
|
|
301
473
|
def parse_application
|
302
474
|
begin
|
303
475
|
@application['block_size'] = @fp.read(3).reverse.unpack("v*")[0]
|
476
|
+
@application['offset'] = @fp.tell
|
304
477
|
@application['ID'] = @fp.read(4).unpack("H*")[0]
|
305
478
|
|
306
|
-
# See http://flac.sourceforge.net/id.html
|
307
479
|
app_id = {"41544348" => "Flac File", "43756573" => "GoldWave Cue Points",
|
308
480
|
"4D754D4C" => "MusicML", "46696361" => "CUE Splitter",
|
309
481
|
"46746F6C" => "flac-tools", "5346464C" => "Sound Font FLAC",
|
@@ -319,30 +491,33 @@ class FlacInfo
|
|
319
491
|
@application['raw_data'] = @fp.read(@application['block_size'] - 4)
|
320
492
|
end
|
321
493
|
rescue
|
322
|
-
raise FlacInfoError, "Could not parse
|
494
|
+
raise FlacInfoError, "Could not parse METADATA_BLOCK_APPLICATION"
|
323
495
|
end
|
324
496
|
end
|
325
497
|
|
326
498
|
# Unlike most values in the Flac header
|
327
|
-
# the Vorbis comments are in LSB order
|
499
|
+
# the Vorbis comments are in LSB order
|
328
500
|
#
|
329
501
|
# @comment is an array of values according to the official spec implementation
|
330
|
-
# @tags is a more user-friendly data structure with the values
|
331
|
-
# separated into key=value pairs
|
502
|
+
# @tags is a more user-friendly data structure with the values
|
503
|
+
# separated into key=value pairs
|
332
504
|
def parse_vorbis_comment
|
333
505
|
begin
|
334
506
|
@tags = {}
|
335
507
|
@tags['block_size'] = @fp.read(3).reverse.unpack("v*")[0]
|
508
|
+
@tags['offset'] = @fp.tell
|
336
509
|
vendor_length = @fp.read(4).unpack("V")[0]
|
337
510
|
@tags['vendor_tag'] = @fp.read(vendor_length)
|
338
511
|
user_comment_list_length = @fp.read(4).unpack("V")[0]
|
339
512
|
@comment = []
|
340
513
|
n = 0
|
514
|
+
|
341
515
|
user_comment_list_length.times do
|
342
516
|
length = @fp.read(4).unpack("V")[0]
|
343
517
|
@comment[n] = @fp.read(length)
|
344
518
|
n += 1
|
345
519
|
end
|
520
|
+
|
346
521
|
@comment.each do |c|
|
347
522
|
k,v = c.split("=")
|
348
523
|
# Vorbis spec says we can have more than one identical comment ie:
|
@@ -355,21 +530,27 @@ class FlacInfo
|
|
355
530
|
@tags[k] = v
|
356
531
|
end
|
357
532
|
end
|
533
|
+
|
358
534
|
rescue
|
359
|
-
raise FlacInfoError, "Could not parse
|
535
|
+
raise FlacInfoError, "Could not parse METADATA_BLOCK_VORBIS_COMMENT"
|
360
536
|
end
|
361
537
|
end
|
362
538
|
|
363
539
|
# padding is just a bunch of '0' bytes
|
364
540
|
def parse_padding
|
365
|
-
|
366
|
-
|
541
|
+
begin
|
542
|
+
@padding['block_size'] = @fp.read(3).reverse.unpack("v*")[0]
|
543
|
+
@padding['offset'] = @fp.tell
|
544
|
+
@fp.seek(@padding['block_size'], IO::SEEK_CUR)
|
545
|
+
rescue
|
546
|
+
raise FlacInfoError, "Could not parse METADATA_BLOCK_PADDING"
|
547
|
+
end
|
367
548
|
end
|
368
549
|
|
369
550
|
def parse_streaminfo
|
370
551
|
begin
|
371
|
-
# Length (in bytes) of metadata to follow (not including header)
|
372
552
|
@streaminfo['block_size'] = @fp.read(3).reverse.unpack("v*")[0]
|
553
|
+
@streaminfo['offset'] = @fp.tell
|
373
554
|
@streaminfo['minimum_block'] = @fp.read(2).reverse.unpack("v*")[0]
|
374
555
|
@streaminfo['maximum_block'] = @fp.read(2).reverse.unpack("v*")[0]
|
375
556
|
@streaminfo['minimum_frame'] = @fp.read(3).reverse.unpack("v*")[0]
|
@@ -389,7 +570,7 @@ class FlacInfo
|
|
389
570
|
# 128 bits :: MD5 signature of the unencoded audio data.
|
390
571
|
@streaminfo['md5'] = @fp.read(16).unpack("H32")[0]
|
391
572
|
rescue
|
392
|
-
raise FlacInfoError, "Could not parse
|
573
|
+
raise FlacInfoError, "Could not parse METADATA_BLOCK_STREAMINFO"
|
393
574
|
end
|
394
575
|
end
|
395
576
|
|
metadata
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.
|
2
|
+
rubygems_version: 0.9.4
|
3
3
|
specification_version: 1
|
4
4
|
name: flacinfo-rb
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: "0.
|
7
|
-
date:
|
6
|
+
version: "0.2"
|
7
|
+
date: 2007-06-23 00:00:00 -06:00
|
8
8
|
summary: Pure Ruby lib for accessing metadata (including Vorbis tags) from Flac files
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -25,11 +25,12 @@ required_ruby_version: !ruby/object:Gem::Version::Requirement
|
|
25
25
|
platform: ruby
|
26
26
|
signing_key:
|
27
27
|
cert_chain:
|
28
|
+
post_install_message:
|
28
29
|
authors:
|
29
30
|
- Darren Kirby
|
30
31
|
files:
|
31
32
|
- README
|
32
|
-
-
|
33
|
+
- flacinfo.rb
|
33
34
|
test_files: []
|
34
35
|
|
35
36
|
rdoc_options: []
|