flacinfo-rb 0.1 → 0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|