ruby-ogginfo 0.3.2 → 0.4

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/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