ruby-ogginfo 0.3.2 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,9 @@
1
+ === 0.4 / 2009-12-04
2
+
3
+ * new ogg frame reading implementation
4
+ * better tag reading (thanks to Sven)
5
+ * now assume utf-8 by default to decode tags
6
+
1
7
  === 0.3.2 / 2009-06-22
2
8
 
3
9
  * added setup.rb for tarball distribution
data/lib/ogginfo.rb CHANGED
@@ -1,7 +1,6 @@
1
- # $Id: ogginfo.rb 39 2008-03-15 17:21:31Z moumar $
2
- #
3
1
  # see http://www.xiph.org/ogg/vorbis/docs.html for documentation on vorbis format
4
- # http://www.xiph.org/ogg/vorbis/doc/v-comment.html
2
+ # http://www.xiph.org/vorbis/doc/v-comment.html
3
+ # http://www.xiph.org/vorbis/doc/framing.html
5
4
  #
6
5
  # License: ruby
7
6
 
@@ -24,25 +23,91 @@ end
24
23
  class OggInfoError < StandardError ; end
25
24
 
26
25
  class OggInfo
27
- VERSION = "0.3.2"
26
+ VERSION = "0.4"
27
+ CHECKSUM_TABLE = [
28
+ 0x00000000,0x04c11db7,0x09823b6e,0x0d4326d9,
29
+ 0x130476dc,0x17c56b6b,0x1a864db2,0x1e475005,
30
+ 0x2608edb8,0x22c9f00f,0x2f8ad6d6,0x2b4bcb61,
31
+ 0x350c9b64,0x31cd86d3,0x3c8ea00a,0x384fbdbd,
32
+ 0x4c11db70,0x48d0c6c7,0x4593e01e,0x4152fda9,
33
+ 0x5f15adac,0x5bd4b01b,0x569796c2,0x52568b75,
34
+ 0x6a1936c8,0x6ed82b7f,0x639b0da6,0x675a1011,
35
+ 0x791d4014,0x7ddc5da3,0x709f7b7a,0x745e66cd,
36
+ 0x9823b6e0,0x9ce2ab57,0x91a18d8e,0x95609039,
37
+ 0x8b27c03c,0x8fe6dd8b,0x82a5fb52,0x8664e6e5,
38
+ 0xbe2b5b58,0xbaea46ef,0xb7a96036,0xb3687d81,
39
+ 0xad2f2d84,0xa9ee3033,0xa4ad16ea,0xa06c0b5d,
40
+ 0xd4326d90,0xd0f37027,0xddb056fe,0xd9714b49,
41
+ 0xc7361b4c,0xc3f706fb,0xceb42022,0xca753d95,
42
+ 0xf23a8028,0xf6fb9d9f,0xfbb8bb46,0xff79a6f1,
43
+ 0xe13ef6f4,0xe5ffeb43,0xe8bccd9a,0xec7dd02d,
44
+ 0x34867077,0x30476dc0,0x3d044b19,0x39c556ae,
45
+ 0x278206ab,0x23431b1c,0x2e003dc5,0x2ac12072,
46
+ 0x128e9dcf,0x164f8078,0x1b0ca6a1,0x1fcdbb16,
47
+ 0x018aeb13,0x054bf6a4,0x0808d07d,0x0cc9cdca,
48
+ 0x7897ab07,0x7c56b6b0,0x71159069,0x75d48dde,
49
+ 0x6b93dddb,0x6f52c06c,0x6211e6b5,0x66d0fb02,
50
+ 0x5e9f46bf,0x5a5e5b08,0x571d7dd1,0x53dc6066,
51
+ 0x4d9b3063,0x495a2dd4,0x44190b0d,0x40d816ba,
52
+ 0xaca5c697,0xa864db20,0xa527fdf9,0xa1e6e04e,
53
+ 0xbfa1b04b,0xbb60adfc,0xb6238b25,0xb2e29692,
54
+ 0x8aad2b2f,0x8e6c3698,0x832f1041,0x87ee0df6,
55
+ 0x99a95df3,0x9d684044,0x902b669d,0x94ea7b2a,
56
+ 0xe0b41de7,0xe4750050,0xe9362689,0xedf73b3e,
57
+ 0xf3b06b3b,0xf771768c,0xfa325055,0xfef34de2,
58
+ 0xc6bcf05f,0xc27dede8,0xcf3ecb31,0xcbffd686,
59
+ 0xd5b88683,0xd1799b34,0xdc3abded,0xd8fba05a,
60
+ 0x690ce0ee,0x6dcdfd59,0x608edb80,0x644fc637,
61
+ 0x7a089632,0x7ec98b85,0x738aad5c,0x774bb0eb,
62
+ 0x4f040d56,0x4bc510e1,0x46863638,0x42472b8f,
63
+ 0x5c007b8a,0x58c1663d,0x558240e4,0x51435d53,
64
+ 0x251d3b9e,0x21dc2629,0x2c9f00f0,0x285e1d47,
65
+ 0x36194d42,0x32d850f5,0x3f9b762c,0x3b5a6b9b,
66
+ 0x0315d626,0x07d4cb91,0x0a97ed48,0x0e56f0ff,
67
+ 0x1011a0fa,0x14d0bd4d,0x19939b94,0x1d528623,
68
+ 0xf12f560e,0xf5ee4bb9,0xf8ad6d60,0xfc6c70d7,
69
+ 0xe22b20d2,0xe6ea3d65,0xeba91bbc,0xef68060b,
70
+ 0xd727bbb6,0xd3e6a601,0xdea580d8,0xda649d6f,
71
+ 0xc423cd6a,0xc0e2d0dd,0xcda1f604,0xc960ebb3,
72
+ 0xbd3e8d7e,0xb9ff90c9,0xb4bcb610,0xb07daba7,
73
+ 0xae3afba2,0xaafbe615,0xa7b8c0cc,0xa379dd7b,
74
+ 0x9b3660c6,0x9ff77d71,0x92b45ba8,0x9675461f,
75
+ 0x8832161a,0x8cf30bad,0x81b02d74,0x857130c3,
76
+ 0x5d8a9099,0x594b8d2e,0x5408abf7,0x50c9b640,
77
+ 0x4e8ee645,0x4a4ffbf2,0x470cdd2b,0x43cdc09c,
78
+ 0x7b827d21,0x7f436096,0x7200464f,0x76c15bf8,
79
+ 0x68860bfd,0x6c47164a,0x61043093,0x65c52d24,
80
+ 0x119b4be9,0x155a565e,0x18197087,0x1cd86d30,
81
+ 0x029f3d35,0x065e2082,0x0b1d065b,0x0fdc1bec,
82
+ 0x3793a651,0x3352bbe6,0x3e119d3f,0x3ad08088,
83
+ 0x2497d08d,0x2056cd3a,0x2d15ebe3,0x29d4f654,
84
+ 0xc5a92679,0xc1683bce,0xcc2b1d17,0xc8ea00a0,
85
+ 0xd6ad50a5,0xd26c4d12,0xdf2f6bcb,0xdbee767c,
86
+ 0xe3a1cbc1,0xe760d676,0xea23f0af,0xeee2ed18,
87
+ 0xf0a5bd1d,0xf464a0aa,0xf9278673,0xfde69bc4,
88
+ 0x89b8fd09,0x8d79e0be,0x803ac667,0x84fbdbd0,
89
+ 0x9abc8bd5,0x9e7d9662,0x933eb0bb,0x97ffad0c,
90
+ 0xafb010b1,0xab710d06,0xa6322bdf,0xa2f33668,
91
+ 0xbcb4666d,0xb8757bda,0xb5365d03,0xb1f740b4
92
+ ]
93
+
28
94
  attr_reader :channels, :samplerate, :bitrate, :nominal_bitrate, :length
29
95
 
30
96
  # +tag+ is a hash containing the vorbis tag like "Artist", "Title", and the like
31
97
  attr_reader :tag
32
98
 
33
99
  # create new instance of OggInfo, using +charset+ to convert tags to
34
- def initialize(filename, charset = "iso-8859-1")
100
+ def initialize(filename, charset = "utf-8")
35
101
  @filename = filename
36
102
  @charset = charset
37
103
  @file = File.new(@filename, "rb")
38
104
 
39
- find_next_page
40
- extract_infos
41
- find_next_page
42
- extract_tag
105
+ frames = (1..2).collect { |i| OggInfo.read_frame(@file) }
106
+ extract_vorbis_infos(frames[0])
107
+ extract_tag(frames[1])
43
108
  convert_tag_charset("utf-8", @charset)
44
- @saved_tag = @tag.dup
45
- extract_end
109
+ @original_tag = @tag.dup
110
+ @length = get_length
46
111
  @bitrate = @file.stat.size.to_f*8/@length
47
112
  @file.close
48
113
  end
@@ -63,9 +128,9 @@ class OggInfo
63
128
  ret
64
129
  end
65
130
 
66
- # write any tags to file
131
+ # commits any tags to file
67
132
  def close
68
- if @tag != @saved_tag
133
+ if @tag != @original_tag
69
134
  cmd = %w{vorbiscomment -w}
70
135
  convert_tag_charset(@charset, "utf-8")
71
136
 
@@ -86,63 +151,98 @@ class OggInfo
86
151
  "channels #{@channels} samplerate #{@samplerate} bitrate #{@nominal_bitrate} bitrate #{@bitrate} length #{@length} #{@tag.inspect}"
87
152
  end
88
153
 
89
- private
90
- def find_next_page
91
- header = 'OggS' # 0xf4 . 0x67 . 0x 67 . 0x53
92
- bytes = @file.read(4)
93
- bytes_read = 4
94
-
95
- while header != bytes
96
- #raise OggInfoError if bytes_read > 4096 or @file.eof? #bytes.nil?
97
- raise OggInfoError if @file.eof? #bytes.nil?
98
- bytes.slice!(0)
99
- char = @file.read(1)
100
- bytes_read += 1
101
- bytes << char
154
+ # read an ogg frame from the +file+
155
+ def self.read_frame(file)
156
+ frame = {}
157
+
158
+ frame[:file_position] = file.pos
159
+
160
+ return nil if file.eof?
161
+ chunk = file.read(27)
162
+
163
+ capture_pattern,
164
+ frame[:version],
165
+ frame[:header_type],
166
+ frame[:granule_pos],
167
+ frame[:bitstream_serial_number],
168
+ frame[:page_sequence_number],
169
+ frame[:checksum],
170
+ frame[:page_segments] = chunk.unpack("a4CCQNNNC")
171
+
172
+ if capture_pattern != "OggS"
173
+ raise(OggInfoError, "bad magic number '#{capture_pattern}'")
174
+ end
175
+
176
+ segment_sizes = file.read(frame[:page_segments]).unpack("C*")
177
+ frame[:header_size] = 27 + frame[:page_segments]
178
+ frame[:body_size] = segment_sizes.inject(0) { |sum, i| sum += i }
179
+ frame[:size] = frame[:header_size] + frame[:body_size]
180
+ file.seek(frame[:body_size], IO::SEEK_CUR)
181
+ frame
182
+ end
183
+
184
+ # compute the checksum of a given +frame+ from a given +file+, you can compare it with frame[:checksum].
185
+ def self.checksum(file, frame)
186
+ original_pos = file.pos
187
+ file.seek(frame[:file_position])
188
+ data = file.read(frame[:size])
189
+ data[22] = data[23] = data[24] = data[25] = 0
190
+
191
+ crc = 0
192
+ data.each_byte do |byte|
193
+ crc = (crc << 8)^CHECKSUM_TABLE[((crc >> 24)&0xff) ^ byte]
194
+ crc = crc & 0xffffffff
102
195
  end
196
+
197
+ # "reverse" it
198
+ crc = [crc].pack("V").unpack("N").first
199
+ file.seek(original_pos)
200
+ crc
103
201
  end
104
202
 
105
- def extract_infos
106
- @file.seek(35, IO::SEEK_CUR) # seek after "vorbis"
107
- @channels, @samplerate, up_br, @nominal_bitrate, down_br = @file.read(17).unpack("CV4")
203
+ private
204
+ def extract_vorbis_infos(frame)
205
+ @file.seek(frame[:file_position] + frame[:header_size] + 1 ) # seek after "vorbis"
206
+ vorbis_string, vorbis_version, @channels, @samplerate, upper_bitrate, @nominal_bitrate, lower_bitrate = @file.read(27).unpack("a6VCV4")
108
207
  if @nominal_bitrate == 0
109
- if up == 2**32 - 1 or down == 2**32 - 1
208
+ if upper_bitrate == 2**32 - 1 or lower_bitrate == 2**32 - 1
110
209
  @nominal_bitrate = 0
111
210
  else
112
- @nominal_bitrate = (up_br + down_br)/2
211
+ @nominal_bitrate = (upper_bitrate + lower_bitrate)/2
113
212
  end
114
213
  end
115
214
  end
116
215
 
117
- def extract_tag
216
+ def extract_tag(frame)
217
+ @file.seek(frame[:file_position] + frame[:header_size] + 1 + "vorbis".size)
218
+ vendor_length = @file.read(4).unpack("V").first
219
+ @vendor = @file.read(vendor_length)
220
+
118
221
  @tag = {}
119
- @file.seek(22, IO::SEEK_CUR)
120
- segs = @file.read(1).unpack("C")[0]
121
- @file.seek(segs + 7, IO::SEEK_CUR)
122
- size = @file.read(4).unpack("V")[0]
123
- @file.seek(size, IO::SEEK_CUR)
124
222
  tag_size = @file.read(4).unpack("V")[0]
125
223
 
126
224
  tag_size.times do |i|
127
225
  size = @file.read(4).unpack("V")[0]
128
226
  comment = @file.read(size)
129
- key, val = comment.split(/=/)
227
+ key, val = comment.split(/=/, 2)
130
228
  @tag[key.downcase] = val
131
229
  end
132
230
  end
133
231
 
134
- def extract_end
135
- begin #Errno::EINVAL
136
- @file.seek(-5000, IO::SEEK_END) #FIXME size of seeking
137
- find_next_page
138
- pos = @file.read(6).unpack("x2 V")[0] #FIXME pos is int64
139
- @length = pos.to_f / @samplerate
140
- rescue Errno::EINVAL
141
- @length = 0
232
+ def get_length
233
+ @file.seek(0)
234
+ last_frame = nil
235
+ begin
236
+ while f = OggInfo.read_frame(@file)
237
+ last_frame = f
238
+ end
239
+ rescue OggInfoError
142
240
  end
241
+ last_frame[:granule_pos].to_f / @samplerate
143
242
  end
144
243
 
145
244
  def convert_tag_charset(from_charset, to_charset)
245
+ return if from_charset == to_charset
146
246
  Iconv.open(to_charset, from_charset) do |ic|
147
247
  @tag.each do |k, v|
148
248
  @tag[k] = ic.iconv(v)
@@ -117,7 +117,7 @@ class OggInfoTest < Test::Unit::TestCase
117
117
  assert_equal 64000, ogg.nominal_bitrate
118
118
  assert_equal 2, ogg.channels
119
119
  assert_equal 44100, ogg.samplerate
120
- assert_equal 0, ogg.length
120
+ assert_in_delta(0.5, ogg.length, 1)
121
121
  #average_bitrate
122
122
  end
123
123
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-ogginfo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: "0.4"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Guillaume Pierronnet
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-30 00:00:00 +02:00
12
+ date: 2009-12-04 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -22,7 +22,10 @@ dependencies:
22
22
  - !ruby/object:Gem::Version
23
23
  version: 1.9.0
24
24
  version:
25
- description: ruby-ogginfo gives you access to low level information on ogg files (bitrate, length, samplerate, encoder, etc... ), as well as tag. It is written in pure ruby.
25
+ description: |-
26
+ ruby-ogginfo gives you access to low level information on ogg files
27
+ (bitrate, length, samplerate, encoder, etc... ), as well as tag.
28
+ It is written in pure ruby.
26
29
  email: moumar@rubyforge.org
27
30
  executables: []
28
31
 
@@ -42,6 +45,8 @@ files:
42
45
  - test/test_ruby-ogginfo.rb
43
46
  has_rdoc: true
44
47
  homepage: http://ruby-ogginfo.rubyforge.org/
48
+ licenses: []
49
+
45
50
  post_install_message:
46
51
  rdoc_options:
47
52
  - --main
@@ -63,9 +68,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
63
68
  requirements: []
64
69
 
65
70
  rubyforge_project: ruby-ogginfo
66
- rubygems_version: 1.3.1
71
+ rubygems_version: 1.3.5
67
72
  signing_key:
68
- specification_version: 2
73
+ specification_version: 3
69
74
  summary: ruby-ogginfo is a pure-ruby library that gives low level informations on ogg files
70
75
  test_files:
71
76
  - test/test_ruby-ogginfo.rb