mp3file 1.1.5 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -1
- data/bin/mp3info +2 -2
- data/lib/mp3file/id3v1_tag.rb +10 -10
- data/lib/mp3file/id3v2/frame_header.rb +11 -11
- data/lib/mp3file/id3v2/header.rb +3 -3
- data/lib/mp3file/mp3_file.rb +39 -12
- data/lib/mp3file/mp3_header.rb +35 -9
- data/lib/mp3file/version.rb +1 -1
- data/lib/mp3file/xing_header.rb +9 -9
- data/mp3file.gemspec +1 -1
- data/spec/files/bret_96_no_xing.mp3 +0 -0
- data/spec/files/bret_id3v1_no_xing.mp3 +0 -0
- data/spec/mp3file/id3v1_tag_spec.rb +4 -4
- data/spec/mp3file/mp3_file_spec.rb +72 -22
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9ad0218bd03684169d8f12f4bbe90a47388ea4a
|
4
|
+
data.tar.gz: 345deef9fcc2f91c5cdd0e233cc07973fdab9b49
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c45bef09f17af0e2fa3f2d42329a1a3fc1267c123478182a680fc0ad696835892786f6e62464880c6f08387028fbdb7ad3ba87a14d68f58ca69c560a50a64c4
|
7
|
+
data.tar.gz: 79c80620a99932962e312a0343b4e8c090ae3ded864201b32924b91fca302758bad64c292109829e5868853e7ac954d9d9c1b810930a7347702efffb16348728
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Mp3file
|
2
2
|
|
3
3
|
This is a pure-Ruby MP3 metadata extractor. We use it to identify the
|
4
|
-
bitrate and duration of songs that people upload to
|
4
|
+
bitrate and duration of songs that people upload to http://www.reverbnation.com .
|
5
5
|
|
6
6
|
It handles a bunch of things:
|
7
7
|
* ID3v1 tags
|
@@ -12,3 +12,5 @@ It handles a bunch of things:
|
|
12
12
|
Don't think of this as an example of good code. This is some of the
|
13
13
|
most terrible, ugly, brutally hacky code I think I've ever written. I
|
14
14
|
apologize to anyone who looks at it.
|
15
|
+
|
16
|
+
[![Build Status](https://travis-ci.org/ahwatts/mp3file.svg?branch=master)](https://travis-ci.org/ahwatts/mp3file)
|
data/bin/mp3info
CHANGED
@@ -17,7 +17,7 @@ alias :h :humanize
|
|
17
17
|
|
18
18
|
ARGV.each do |file|
|
19
19
|
begin
|
20
|
-
mp3 = Mp3file::MP3File.new(file, scan_all_headers:
|
20
|
+
mp3 = Mp3file::MP3File.new(file, scan_all_headers: false)
|
21
21
|
audio_percent = mp3.audio_size.to_f / mp3.file_size.to_f * 100.0
|
22
22
|
|
23
23
|
puts("File: #{mp3.file.path}")
|
@@ -39,7 +39,7 @@ ARGV.each do |file|
|
|
39
39
|
if mp3.xing_header
|
40
40
|
xing = mp3.xing_header
|
41
41
|
puts(" Xing header:")
|
42
|
-
print(" ")
|
42
|
+
print(" Name: #{xing.name.inspect} ")
|
43
43
|
|
44
44
|
if xing.frames
|
45
45
|
print("Frames: #{h(xing.frames)} ")
|
data/lib/mp3file/id3v1_tag.rb
CHANGED
@@ -5,12 +5,12 @@ module Mp3file
|
|
5
5
|
attr_accessor(:title, :artist, :album, :year, :comment, :track, :genre_id, :genre)
|
6
6
|
|
7
7
|
class ID3v1TagFormat < BinData::Record
|
8
|
-
string(:tag_id, :
|
9
|
-
string(:title, :
|
10
|
-
string(:artist, :
|
11
|
-
string(:album, :
|
12
|
-
string(:year, :
|
13
|
-
string(:comment, :
|
8
|
+
string(:tag_id, read_length: 3, asserted_value: "TAG")
|
9
|
+
string(:title, length: 30, trim_padding: true)
|
10
|
+
string(:artist, length: 30, trim_padding: true)
|
11
|
+
string(:album, length: 30, trim_padding: true)
|
12
|
+
string(:year, length: 4, trim_padding: true)
|
13
|
+
string(:comment, length: 30)
|
14
14
|
uint8(:genre_id)
|
15
15
|
end
|
16
16
|
|
@@ -52,12 +52,12 @@ module Mp3file
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def load_format(tag_data)
|
55
|
-
@title = tag_data.title
|
56
|
-
@artist = tag_data.artist
|
57
|
-
@album = tag_data.album
|
55
|
+
@title = tag_data.title
|
56
|
+
@artist = tag_data.artist
|
57
|
+
@album = tag_data.album
|
58
58
|
@year = tag_data.year
|
59
59
|
split_comment = tag_data.comment.split("\x00").reject { |s| s == '' }
|
60
|
-
@comment = split_comment.first
|
60
|
+
@comment = split_comment.first || ""
|
61
61
|
if split_comment.size > 1
|
62
62
|
@track = split_comment.last.bytes.first
|
63
63
|
end
|
@@ -1,38 +1,38 @@
|
|
1
1
|
module Mp3file::ID3v2
|
2
2
|
class FrameHeader
|
3
3
|
class ID3v220FrameHeaderFormat < BinData::Record
|
4
|
-
string(:frame_id, :
|
4
|
+
string(:frame_id, length: 3)
|
5
5
|
uint24be(:frame_size)
|
6
6
|
end
|
7
7
|
|
8
8
|
class ID3v230FrameHeaderFormat < BinData::Record
|
9
|
-
string(:frame_id, :
|
9
|
+
string(:frame_id, length: 4)
|
10
10
|
uint32be(:frame_size)
|
11
11
|
bit1(:tag_alter_preserve)
|
12
12
|
bit1(:file_alter_preserve)
|
13
13
|
bit1(:read_only)
|
14
|
-
bit5(:unused1)
|
14
|
+
bit5(:unused1)
|
15
15
|
bit1(:compression)
|
16
16
|
bit1(:encryption)
|
17
17
|
bit1(:has_group)
|
18
|
-
bit5(:unused2)
|
19
|
-
uint8(:encryption_type, :
|
20
|
-
uint8(:group_id, :
|
18
|
+
bit5(:unused2)
|
19
|
+
uint8(:encryption_type, onlyif: -> { encryption == 1 })
|
20
|
+
uint8(:group_id, onlyif: -> { has_group == 1 })
|
21
21
|
end
|
22
22
|
|
23
23
|
class ID3v240FrameHeaderFormat < BinData::Record
|
24
|
-
string(:frame_id, :
|
24
|
+
string(:frame_id, length: 4)
|
25
25
|
uint32be(:frame_size)
|
26
|
-
bit1(:unused1)
|
26
|
+
bit1(:unused1)
|
27
27
|
bit1(:tag_alter_preserve)
|
28
28
|
bit1(:file_alter_preserve)
|
29
29
|
bit1(:read_only)
|
30
30
|
|
31
|
-
bit4(:unused2)
|
31
|
+
bit4(:unused2)
|
32
32
|
|
33
|
-
bit1(:unused3)
|
33
|
+
bit1(:unused3)
|
34
34
|
bit1(:group)
|
35
|
-
bit2(:unused4)
|
35
|
+
bit2(:unused4)
|
36
36
|
|
37
37
|
bit1(:compression)
|
38
38
|
bit1(:encryption)
|
data/lib/mp3file/id3v2/header.rb
CHANGED
@@ -10,15 +10,15 @@ module Mp3file::ID3v2
|
|
10
10
|
:tag_size)
|
11
11
|
|
12
12
|
class ID3v2HeaderFormat < BinData::Record
|
13
|
-
string(:tag_id, :
|
14
|
-
uint8(:vmaj, :
|
13
|
+
string(:tag_id, read_length: 3, asserted_value: "ID3")
|
14
|
+
uint8(:vmaj, assert: -> { (2..4).include?(value) })
|
15
15
|
uint8(:vmin)
|
16
16
|
|
17
17
|
bit1(:unsynchronized)
|
18
18
|
bit1(:extended_header)
|
19
19
|
bit1(:experimental)
|
20
20
|
bit1(:footer)
|
21
|
-
bit4(:unused
|
21
|
+
bit4(:unused)
|
22
22
|
|
23
23
|
uint32be(:size_padded)
|
24
24
|
end
|
data/lib/mp3file/mp3_file.rb
CHANGED
@@ -125,8 +125,25 @@ module Mp3file
|
|
125
125
|
# repeat.
|
126
126
|
@extra_id3v2_tags = []
|
127
127
|
begin
|
128
|
-
# Try to find the first MP3
|
129
|
-
|
128
|
+
# Try to find the first two MP3 headers.
|
129
|
+
loop do
|
130
|
+
@first_header_offset, @first_header = get_next_header(@file)
|
131
|
+
@file.seek(@first_header_offset + @first_header.frame_size, IO::SEEK_SET)
|
132
|
+
second_header_offset = @file.tell - 4
|
133
|
+
second_header =
|
134
|
+
begin
|
135
|
+
MP3Header.new(@file)
|
136
|
+
rescue InvalidMP3HeaderError
|
137
|
+
nil
|
138
|
+
end
|
139
|
+
|
140
|
+
# Pretend we didn't read the second header.
|
141
|
+
@file.seek(@first_header_offset + 4)
|
142
|
+
|
143
|
+
if second_header && @first_header.same_header?(second_header)
|
144
|
+
break
|
145
|
+
end
|
146
|
+
end
|
130
147
|
rescue InvalidMP3FileError
|
131
148
|
if @id3v2_tag
|
132
149
|
end_of_tags = @id3v2_tag.size + @extra_id3v2_tags.map(&:last).map(&:size).reduce(:+).to_i
|
@@ -206,22 +223,33 @@ module Mp3file
|
|
206
223
|
@vbr = !@xing_header.nil? && @xing_header.name == "Xing"
|
207
224
|
end
|
208
225
|
|
209
|
-
# puts "@num_frames = #{@num_frames.inspect}"
|
210
|
-
# puts "@
|
226
|
+
# puts "@num_frames (1) = #{@num_frames.inspect}"
|
227
|
+
# puts "@bitrate (1) = #{@bitrate.inspect}"
|
228
|
+
# puts "@vbr = #{@vbr.inspect}"
|
229
|
+
# puts "@xing_header.frames = #{@xing_header && @xing_header.frames.inspect}"
|
230
|
+
# puts "@xing_header.bytes = #{@xing_header && @xing_header.bytes.inspect}"
|
211
231
|
# puts "@audio_size = #{@audio_size.inspect}"
|
212
232
|
# puts "@first_header size = #{@first_header.frame_size.inspect}"
|
213
233
|
|
214
|
-
# Find the number of frames
|
215
|
-
#
|
216
|
-
#
|
217
|
-
|
218
|
-
|
234
|
+
# Find the number of frames, in this order:
|
235
|
+
# 1. The frame count from the Xing (or Info) header.
|
236
|
+
# 2. The number of frames we actually counted by scanning the
|
237
|
+
# whole file, which we might not have done.
|
238
|
+
# 3. Assume it's CBR and divide the file size by the frame size.
|
239
|
+
@num_frames =
|
240
|
+
(@xing_header && @xing_header.frames) ||
|
241
|
+
@num_frames ||
|
219
242
|
(@audio_size / @first_header.frame_size)
|
220
243
|
|
244
|
+
# puts "@num_frames (2) = #{@num_frames.inspect}"
|
245
|
+
|
221
246
|
# Figure out the total samples and the time duration.
|
222
247
|
@total_samples = @num_frames * @first_header.samples
|
223
248
|
@length = @total_samples.to_f / @samplerate.to_f
|
224
249
|
|
250
|
+
# puts "@total_samples = #{@total_samples.inspect}"
|
251
|
+
# puts "@length = #{@length.inspect}"
|
252
|
+
|
225
253
|
# If the file looks like it's a VBR file, do an averate bitrate
|
226
254
|
# calculation, either using the Xing header's idea of the file
|
227
255
|
# size or the one we found.
|
@@ -229,8 +257,7 @@ module Mp3file
|
|
229
257
|
@bitrate = ((@xing_header && @xing_header.bytes) || @audio_size) / @length.to_f * 8 / 1000
|
230
258
|
end
|
231
259
|
|
232
|
-
# puts "@
|
233
|
-
# puts "@bitrate = #{@bitrate.inspect}"
|
260
|
+
# puts "@bitrate (2) = #{@bitrate.inspect}"
|
234
261
|
|
235
262
|
@file.close
|
236
263
|
end
|
@@ -247,7 +274,7 @@ module Mp3file
|
|
247
274
|
while header.nil?
|
248
275
|
begin
|
249
276
|
header = MP3Header.new(file)
|
250
|
-
header_offset = file.tell - 4
|
277
|
+
header_offset = file.tell - 4
|
251
278
|
rescue InvalidMP3HeaderError
|
252
279
|
header_offset += 1
|
253
280
|
if header_offset - initial_header_offset > 4096
|
data/lib/mp3file/mp3_header.rb
CHANGED
@@ -5,18 +5,18 @@ module Mp3file
|
|
5
5
|
attr_reader(:version, :layer, :has_crc, :bitrate,
|
6
6
|
:samplerate, :has_padding, :mode, :mode_extension,
|
7
7
|
:copyright, :original, :emphasis, :samples, :frame_size,
|
8
|
-
:side_bytes)
|
8
|
+
:side_bytes, :raw)
|
9
9
|
|
10
10
|
class MP3HeaderFormat < BinData::Record
|
11
|
-
uint8(:sync1, :
|
11
|
+
uint8(:sync1, asserted_value: 255)
|
12
12
|
|
13
|
-
bit3(:sync2, :
|
14
|
-
bit2(:version, :
|
15
|
-
bit2(:layer, :
|
13
|
+
bit3(:sync2, asserted_value: 7)
|
14
|
+
bit2(:version, assert: -> { value != 1 })
|
15
|
+
bit2(:layer, assert: -> { value != 0 })
|
16
16
|
bit1(:crc)
|
17
17
|
|
18
|
-
bit4(:bitrate, :
|
19
|
-
bit2(:samplerate, :
|
18
|
+
bit4(:bitrate, assert: -> { value != 15 && value != 0 })
|
19
|
+
bit2(:samplerate, assert: -> { value != 3 })
|
20
20
|
bit1(:padding)
|
21
21
|
bit1(:private)
|
22
22
|
|
@@ -24,7 +24,7 @@ module Mp3file
|
|
24
24
|
bit2(:mode_extension)
|
25
25
|
bit1(:copyright)
|
26
26
|
bit1(:original)
|
27
|
-
bit2(:emphasis, :
|
27
|
+
bit2(:emphasis, assert: -> { value != 2 })
|
28
28
|
end
|
29
29
|
|
30
30
|
MPEG_VERSIONS = [ 'MPEG 2.5', nil, 'MPEG 2', 'MPEG 1' ]
|
@@ -78,10 +78,22 @@ module Mp3file
|
|
78
78
|
raise InvalidMP3HeaderError, ve.message
|
79
79
|
end
|
80
80
|
|
81
|
+
@raw = head
|
81
82
|
@version = MPEG_VERSIONS[head.version]
|
82
83
|
@layer = LAYERS[head.layer]
|
83
84
|
@has_crc = head.crc == 0
|
84
|
-
|
85
|
+
|
86
|
+
if @version.nil? || @layer.nil?
|
87
|
+
raise InvalidMP3HeaderError, "Bad MPEG version or layer."
|
88
|
+
end
|
89
|
+
|
90
|
+
kbitrate = BITRATES[head.version][head.layer][head.bitrate - 1]
|
91
|
+
|
92
|
+
if kbitrate.nil?
|
93
|
+
raise InvalidMP3HeaderError, "Bad bitrate."
|
94
|
+
end
|
95
|
+
|
96
|
+
@bitrate = kbitrate * 1000
|
85
97
|
@samplerate = SAMPLERATES[head.version][head.samplerate]
|
86
98
|
@has_padding = head.padding == 1
|
87
99
|
@mode = MODES[head.mode]
|
@@ -105,5 +117,19 @@ module Mp3file
|
|
105
117
|
@frame_size = (((samples.to_f * bitrate.to_f) / (8 * slot_size.to_f * samplerate.to_f)) + pad_slots).to_i * slot_size
|
106
118
|
@side_bytes = SIDE_BYTES[head.version][head.mode]
|
107
119
|
end
|
120
|
+
|
121
|
+
def to_i
|
122
|
+
@raw.to_binary_s
|
123
|
+
.reverse
|
124
|
+
.each_byte
|
125
|
+
.each_with_index
|
126
|
+
.inject(0) { |m, (b, i)| m += b << (i*8) }
|
127
|
+
end
|
128
|
+
|
129
|
+
def same_header?(other)
|
130
|
+
# Borrowed from ffmpeg.
|
131
|
+
same_header_mask = 0xffe00000 | (3 << 17) | (3 << 10) | (3 << 19)
|
132
|
+
(to_i & same_header_mask) == (other.to_i & same_header_mask)
|
133
|
+
end
|
108
134
|
end
|
109
135
|
end
|
data/lib/mp3file/version.rb
CHANGED
data/lib/mp3file/xing_header.rb
CHANGED
@@ -5,21 +5,21 @@ module Mp3file
|
|
5
5
|
attr_reader(:name, :frames, :bytes, :toc, :quality)
|
6
6
|
|
7
7
|
class XingHeaderFormat < BinData::Record
|
8
|
-
string(:vbr_id, :
|
8
|
+
string(:vbr_id, read_length: 4, assert: -> { value == 'Xing' || value == 'Info' })
|
9
9
|
|
10
|
-
uint8(:unused1, :
|
11
|
-
uint8(:unused2, :
|
12
|
-
uint8(:unused3, :
|
13
|
-
bit4(:unused4, :
|
10
|
+
uint8(:unused1, asserted_value: 0)
|
11
|
+
uint8(:unused2, asserted_value: 0)
|
12
|
+
uint8(:unused3, asserted_value: 0)
|
13
|
+
bit4(:unused4, asserted_value: 0)
|
14
14
|
bit1(:quality_present)
|
15
15
|
bit1(:toc_present)
|
16
16
|
bit1(:bytes_present)
|
17
17
|
bit1(:frames_present)
|
18
18
|
|
19
|
-
uint32be(:frames, :
|
20
|
-
uint32be(:bytes, :
|
21
|
-
array(:toc, :
|
22
|
-
uint32be(:quality, :
|
19
|
+
uint32be(:frames, onlyif: -> { frames_present == 1 })
|
20
|
+
uint32be(:bytes, onlyif: -> { bytes_present == 1 })
|
21
|
+
array(:toc, type: :uint8, read_until: -> { index == 99 }, onlyif: -> { toc_present == 1 })
|
22
|
+
uint32be(:quality, onlyif: -> { quality_present == 1 })
|
23
23
|
end
|
24
24
|
|
25
25
|
def initialize(io)
|
data/mp3file.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.add_development_dependency('rake')
|
20
20
|
s.add_development_dependency('pry')
|
21
21
|
|
22
|
-
s.add_dependency('bindata', '~>
|
22
|
+
s.add_dependency('bindata', '~> 2.3.0')
|
23
23
|
|
24
24
|
s.files = `git ls-files`.split("\n")
|
25
25
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
Binary file
|
Binary file
|
@@ -52,10 +52,10 @@ describe Mp3file::ID3v1Tag do
|
|
52
52
|
|
53
53
|
describe "When created with a blank ID3v1 tag" do
|
54
54
|
subject { Mp3file::ID3v1Tag.parse(StringIO.new("TAG" + ("\x00" * 125))) }
|
55
|
-
its(:title) { should ==
|
56
|
-
its(:artist) { should ==
|
57
|
-
its(:album) { should ==
|
58
|
-
its(:comment) { should ==
|
55
|
+
its(:title) { should == "" }
|
56
|
+
its(:artist) { should == "" }
|
57
|
+
its(:album) { should == "" }
|
58
|
+
its(:comment) { should == "" }
|
59
59
|
its(:track) { should == nil }
|
60
60
|
its(:genre) { should == 'Blues' }
|
61
61
|
end
|
@@ -4,47 +4,97 @@ require File.dirname(__FILE__) + '/../common_helpers'
|
|
4
4
|
include CommonHelpers
|
5
5
|
|
6
6
|
describe Mp3file::MP3File do
|
7
|
-
describe "A 96 kbps 32 kHz Joint Stereo CBR file without any ID3 tags" do
|
8
|
-
|
7
|
+
describe "A 96 kbps 32 kHz Joint Stereo CBR file without a Xing header or any ID3 tags" do
|
8
|
+
path = fixture_file("bret_96_no_xing.mp3")
|
9
|
+
subject { Mp3file::MP3File.new(path) }
|
10
|
+
|
9
11
|
its(:id3v2tag?) { should == false }
|
10
12
|
its(:id3v1tag?) { should == false }
|
11
|
-
its("file.path") { should ==
|
13
|
+
its("file.path") { should == path.to_s }
|
12
14
|
its("file.closed?") { should == true }
|
13
|
-
its(:file_size) { should ==
|
14
|
-
its(:audio_size) { should ==
|
15
|
+
its(:file_size) { should == path.size }
|
16
|
+
its(:audio_size) { should == path.size }
|
15
17
|
its(:first_header_offset) { should == 0 }
|
16
18
|
its(:mpeg_version) { should == 'MPEG 1' }
|
17
19
|
its(:layer) { should == 'Layer III' }
|
18
20
|
its(:bitrate) { should == 96 }
|
19
21
|
its(:samplerate) { should == 32000 }
|
20
22
|
its(:mode) { should == 'Joint Stereo' }
|
21
|
-
its(:num_frames) { should ==
|
22
|
-
its(:total_samples) { should ==
|
23
|
-
its(:length) { should == 5 }
|
23
|
+
its(:num_frames) { should == 140 }
|
24
|
+
its(:total_samples) { should == 161280 }
|
25
|
+
its(:length) { should == 5.04 }
|
24
26
|
its(:vbr?) { should == false }
|
25
27
|
end
|
26
28
|
|
27
|
-
describe "
|
28
|
-
|
29
|
+
describe "A 96 kbps 32 kHz Joint Stereo CBR file with a Xing \"Info\" header and no ID3 tags" do
|
30
|
+
path = fixture_file("bret_96.mp3")
|
31
|
+
subject { Mp3file::MP3File.new(path) }
|
32
|
+
|
29
33
|
its(:id3v2tag?) { should == false }
|
30
34
|
its(:id3v1tag?) { should == false }
|
31
|
-
its("file.path") { should ==
|
35
|
+
its("file.path") { should == path.to_s }
|
32
36
|
its("file.closed?") { should == true }
|
33
|
-
its(:file_size) { should ==
|
37
|
+
its(:file_size) { should == path.size }
|
38
|
+
its(:audio_size) { should == path.size }
|
39
|
+
its(:first_header_offset) { should == 0 }
|
40
|
+
its(:mpeg_version) { should == 'MPEG 1' }
|
41
|
+
its(:layer) { should == 'Layer III' }
|
42
|
+
its(:bitrate) { should == 96 }
|
43
|
+
its(:samplerate) { should == 32000 }
|
44
|
+
its(:mode) { should == 'Joint Stereo' }
|
45
|
+
its(:num_frames) { should == 140 }
|
46
|
+
its(:total_samples) { should == 161280 }
|
47
|
+
its(:length) { should == 5.04 }
|
48
|
+
its(:vbr?) { should == false }
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "a 44.1 kHz Stereo VBR file with an average bitrate of 130 kbps without any ID3 tags" do
|
52
|
+
path = fixture_file("bret_vbr_6.mp3")
|
53
|
+
subject { Mp3file::MP3File.new(path) }
|
54
|
+
|
55
|
+
its(:id3v2tag?) { should == false }
|
56
|
+
its(:id3v1tag?) { should == false }
|
57
|
+
its("file.path") { should == path.to_s }
|
58
|
+
its("file.closed?") { should == true }
|
59
|
+
its(:file_size) { should == path.size }
|
34
60
|
its(:audio_size) { should == 81853 }
|
35
61
|
its(:first_header_offset) { should == 0 }
|
36
62
|
its(:mpeg_version) { should == 'MPEG 1' }
|
37
63
|
its(:layer) { should == 'Layer III' }
|
38
|
-
its(:bitrate) { should
|
64
|
+
its(:bitrate) { should be_within(0.005).of(129.88) }
|
39
65
|
its(:samplerate) { should == 44100 }
|
40
66
|
its(:mode) { should == 'Stereo' }
|
41
67
|
its(:num_frames) { should == 193 }
|
42
68
|
its(:total_samples) { should == 222336 }
|
43
|
-
its(:length) { should
|
69
|
+
its(:length) { should be_within(0.005).of(5.04) }
|
44
70
|
its(:vbr?) { should == true }
|
45
71
|
end
|
46
72
|
|
47
|
-
describe "A 96 kbps 32 kHz CBR file with only an ID3v1 tag" do
|
73
|
+
describe "A 96 kbps 32 kHz CBR file with no Xing header and only an ID3v1 tag" do
|
74
|
+
subject { Mp3file::MP3File.new(fixture_file('bret_id3v1_no_xing.mp3')) }
|
75
|
+
its(:id3v2tag?) { should == false }
|
76
|
+
its(:id3v1tag?) { should == true }
|
77
|
+
its(:audio_size) { should == 60480 }
|
78
|
+
its(:first_header_offset) { should == 0 }
|
79
|
+
its(:mpeg_version) { should == 'MPEG 1' }
|
80
|
+
its(:layer) { should == 'Layer III' }
|
81
|
+
its(:bitrate) { should == 96 }
|
82
|
+
its(:samplerate) { should == 32000 }
|
83
|
+
its(:mode) { should == 'Joint Stereo' }
|
84
|
+
its(:num_frames) { should == 140 }
|
85
|
+
its(:total_samples) { should == 161280 }
|
86
|
+
its(:length) { should be_within(0.005).of(5.04) }
|
87
|
+
its(:vbr?) { should == false }
|
88
|
+
its('id3v1_tag.artist') { should == 'Cracker' }
|
89
|
+
its(:title) { should == 'Hey Bret (You Know What Time I' }
|
90
|
+
its(:album) { should == 'Sunrise in the Land of Milk an' }
|
91
|
+
its(:comment) { should == 'For testing the mp3file gem' }
|
92
|
+
its(:year) { should == '2009' }
|
93
|
+
its(:track) { should == 9 }
|
94
|
+
its(:genre) { should == 'Rock' }
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "A 96 kbps 32 kHz CBR file with a Xing \"Info\" header and only an ID3v1 tag" do
|
48
98
|
subject { Mp3file::MP3File.new(fixture_file('bret_id3v1.mp3')) }
|
49
99
|
its(:id3v2tag?) { should == false }
|
50
100
|
its(:id3v1tag?) { should == true }
|
@@ -55,9 +105,9 @@ describe Mp3file::MP3File do
|
|
55
105
|
its(:bitrate) { should == 96 }
|
56
106
|
its(:samplerate) { should == 32000 }
|
57
107
|
its(:mode) { should == 'Joint Stereo' }
|
58
|
-
its(:num_frames) { should ==
|
59
|
-
its(:total_samples) { should ==
|
60
|
-
its(:length) { should
|
108
|
+
its(:num_frames) { should == 140 }
|
109
|
+
its(:total_samples) { should == 161280 }
|
110
|
+
its(:length) { should be_within(0.005).of(5.04) }
|
61
111
|
its(:vbr?) { should == false }
|
62
112
|
its('id3v1_tag.artist') { should == 'Cracker' }
|
63
113
|
its(:title) { should == 'Hey Bret (You Know What Time I' }
|
@@ -68,7 +118,7 @@ describe Mp3file::MP3File do
|
|
68
118
|
its(:genre) { should == 'Rock' }
|
69
119
|
end
|
70
120
|
|
71
|
-
describe "A 96 kbps 34 kHz Joint Stereo CBR file with an ID3v2 tag" do
|
121
|
+
describe "A 96 kbps 34 kHz Joint Stereo CBR file with a Xing \"Info\" header and an ID3v2 tag" do
|
72
122
|
subject { Mp3file::MP3File.new(fixture_file('bret_id3v2.mp3')) }
|
73
123
|
its(:id3v2tag?) { should == true }
|
74
124
|
its(:id3v1tag?) { should == false }
|
@@ -82,9 +132,9 @@ describe Mp3file::MP3File do
|
|
82
132
|
its(:bitrate) { should == 96 }
|
83
133
|
its(:samplerate) { should == 32000 }
|
84
134
|
its(:mode) { should == 'Joint Stereo' }
|
85
|
-
its(:num_frames) { should ==
|
86
|
-
its(:total_samples) { should ==
|
87
|
-
its(:length) { should == 5 }
|
135
|
+
its(:num_frames) { should == 140 }
|
136
|
+
its(:total_samples) { should == 161280 }
|
137
|
+
its(:length) { should == be_within(0.001).of(5.04) }
|
88
138
|
its(:vbr?) { should == false }
|
89
139
|
end
|
90
140
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mp3file
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Watts
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-09-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -64,14 +64,14 @@ dependencies:
|
|
64
64
|
requirements:
|
65
65
|
- - "~>"
|
66
66
|
- !ruby/object:Gem::Version
|
67
|
-
version:
|
67
|
+
version: 2.3.0
|
68
68
|
type: :runtime
|
69
69
|
prerelease: false
|
70
70
|
version_requirements: !ruby/object:Gem::Requirement
|
71
71
|
requirements:
|
72
72
|
- - "~>"
|
73
73
|
- !ruby/object:Gem::Version
|
74
|
-
version:
|
74
|
+
version: 2.3.0
|
75
75
|
description: Reads MP3 headers and returns their information.
|
76
76
|
email:
|
77
77
|
- ahwatts@gmail.com
|
@@ -107,7 +107,9 @@ files:
|
|
107
107
|
- mp3file.gemspec
|
108
108
|
- spec/common_helpers.rb
|
109
109
|
- spec/files/bret_96.mp3
|
110
|
+
- spec/files/bret_96_no_xing.mp3
|
110
111
|
- spec/files/bret_id3v1.mp3
|
112
|
+
- spec/files/bret_id3v1_no_xing.mp3
|
111
113
|
- spec/files/bret_id3v2.mp3
|
112
114
|
- spec/files/bret_vbr_6.mp3
|
113
115
|
- spec/files/zeroes.mp3
|
@@ -146,7 +148,9 @@ summary: Reads MP3 headers and returns their information.
|
|
146
148
|
test_files:
|
147
149
|
- spec/common_helpers.rb
|
148
150
|
- spec/files/bret_96.mp3
|
151
|
+
- spec/files/bret_96_no_xing.mp3
|
149
152
|
- spec/files/bret_id3v1.mp3
|
153
|
+
- spec/files/bret_id3v1_no_xing.mp3
|
150
154
|
- spec/files/bret_id3v2.mp3
|
151
155
|
- spec/files/bret_vbr_6.mp3
|
152
156
|
- spec/files/zeroes.mp3
|