Class: Mp3file::MP3Header

Inherits:
Object
  • Object
show all
Defined in:
lib/mp3file/mp3_header.rb

Defined Under Namespace

Classes: MP3HeaderFormat

Constant Summary collapse

MPEG_VERSIONS =
[ 'MPEG 2.5', nil, 'MPEG 2', 'MPEG 1' ]
LAYERS =
[ nil, 'Layer III', 'Layer II', 'Layer I' ]
BITRATES =
[
  # MPEG 2.5
  [ nil,
    [  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160 ],    # Layer III
    [  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160 ],    # Layer II
    [ 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256 ], ], # Layer I
  # reserved
  nil,
  # MPEG 2
  [ nil,
    [  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160 ],    # Layer III
    [  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160 ],    # Layer II
    [ 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256 ], ], # Layer I
  # MPEG 1
  [ nil,
    [ 32, 40, 48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320 ],   # Layer III
    [ 32, 48, 56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384 ],   # Layer II
    [ 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 ], ] # Layer I
]
SAMPLERATES =
[
  [ 11025, 12000,  8000 ], # MPEG 2.5
  nil,
  [ 22050, 24000, 16000 ], # MPEG 2
  [ 44100, 48000, 32000 ], # MPEG 1
]
MODES =
[ 'Stereo', 'Joint Stereo', 'Dual Channel', 'Mono' ]
SAMPLE_COUNTS =
[
  [ nil,  576, 1152, 384 ], # MPEG 2.5, III / II / I
  nil,
  [ nil,  576, 1152, 384 ], # MPEG 2, III / II / I
  [ nil, 1152, 1152, 384 ], # MPEG 1, III / II / I
]
SIDE_BYTES =
[
  [ 17, 17, 17,  9 ], # MPEG 2.5, Stereo, J-Stereo, Dual Channel, Mono
  nil,
  [ 17, 17, 17,  9 ], # MPEG 2, Stereo, J-Stereo, Dual Channel, Mono
  [ 32, 32, 32, 17 ], # MPEG 1, Stereo, J-Stereo, Dual Channel, Mono
]
MODE_EXTENSIONS_LAYER_I_II =
[ 'bands 4 to 31', 'bands 8 to 31', 'bands 12 to 31', 'bands 16 to 31' ]
MODE_EXTENSIONS_LAYER_III =
[ nil, 'Intensity Stereo', 'M/S Stereo', [ 'Intensity Stereo', 'M/S Stereo' ] ]
EMPHASES =
[ 'none', '50/15 ms', nil, 'CCIT J.17' ]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(io) ⇒ MP3Header

Returns a new instance of MP3Header.



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/mp3file/mp3_header.rb', line 74

def initialize(io)
  begin
    head = MP3HeaderFormat.read(io)
  rescue BinData::ValidityError => ve
    raise InvalidMP3HeaderError, ve.message
  end

  @raw = head
  @version = MPEG_VERSIONS[head.version]
  @layer = LAYERS[head.layer]
  @has_crc = head.crc == 0

  if @version.nil? || @layer.nil?
    raise InvalidMP3HeaderError, "Bad MPEG version or layer."
  end

  kbitrate = BITRATES[head.version][head.layer][head.bitrate - 1]

  if kbitrate.nil?
    raise InvalidMP3HeaderError, "Bad bitrate."
  end

  @bitrate = kbitrate * 1000
  @samplerate = SAMPLERATES[head.version][head.samplerate]
  @has_padding = head.padding == 1
  @mode = MODES[head.mode]

  @mode_extension = nil
  if @mode == 'Joint Stereo'
    if [ 'Layer I', 'Layer II' ].include?(@layer)
      @mode_extension = MODE_EXTENSIONS_LAYER_I_II[head.mode_extension]
    elsif @layer == 'Layer III'
      @mode_extension = MODE_EXTENSIONS_LAYER_III[head.mode_extension]
    end
  end

  @copyright = head.copyright == 1
  @original = head.original == 1
  @emphasis = EMPHASES[head.emphasis]
  @samples = SAMPLE_COUNTS[head.version][head.layer]

  slot_size = layer == 'Layer I' ? 4 : 1
  pad_slots = has_padding ? 1 : 0
  @frame_size = (((samples.to_f * bitrate.to_f) / (8 * slot_size.to_f * samplerate.to_f)) + pad_slots).to_i * slot_size
  @side_bytes = SIDE_BYTES[head.version][head.mode]
end

Instance Attribute Details

#bitrateObject (readonly)

Returns the value of attribute bitrate.



5
6
7
# File 'lib/mp3file/mp3_header.rb', line 5

def bitrate
  @bitrate
end

Returns the value of attribute copyright.



5
6
7
# File 'lib/mp3file/mp3_header.rb', line 5

def copyright
  @copyright
end

#emphasisObject (readonly)

Returns the value of attribute emphasis.



5
6
7
# File 'lib/mp3file/mp3_header.rb', line 5

def emphasis
  @emphasis
end

#frame_sizeObject (readonly)

Returns the value of attribute frame_size.



5
6
7
# File 'lib/mp3file/mp3_header.rb', line 5

def frame_size
  @frame_size
end

#has_crcObject (readonly)

Returns the value of attribute has_crc.



5
6
7
# File 'lib/mp3file/mp3_header.rb', line 5

def has_crc
  @has_crc
end

#has_paddingObject (readonly)

Returns the value of attribute has_padding.



5
6
7
# File 'lib/mp3file/mp3_header.rb', line 5

def has_padding
  @has_padding
end

#layerObject (readonly)

Returns the value of attribute layer.



5
6
7
# File 'lib/mp3file/mp3_header.rb', line 5

def layer
  @layer
end

#modeObject (readonly)

Returns the value of attribute mode.



5
6
7
# File 'lib/mp3file/mp3_header.rb', line 5

def mode
  @mode
end

#mode_extensionObject (readonly)

Returns the value of attribute mode_extension.



5
6
7
# File 'lib/mp3file/mp3_header.rb', line 5

def mode_extension
  @mode_extension
end

#originalObject (readonly)

Returns the value of attribute original.



5
6
7
# File 'lib/mp3file/mp3_header.rb', line 5

def original
  @original
end

#rawObject (readonly)

Returns the value of attribute raw.



5
6
7
# File 'lib/mp3file/mp3_header.rb', line 5

def raw
  @raw
end

#samplerateObject (readonly)

Returns the value of attribute samplerate.



5
6
7
# File 'lib/mp3file/mp3_header.rb', line 5

def samplerate
  @samplerate
end

#samplesObject (readonly)

Returns the value of attribute samples.



5
6
7
# File 'lib/mp3file/mp3_header.rb', line 5

def samples
  @samples
end

#side_bytesObject (readonly)

Returns the value of attribute side_bytes.



5
6
7
# File 'lib/mp3file/mp3_header.rb', line 5

def side_bytes
  @side_bytes
end

#versionObject (readonly)

Returns the value of attribute version.



5
6
7
# File 'lib/mp3file/mp3_header.rb', line 5

def version
  @version
end

Instance Method Details

#durationObject



121
122
123
# File 'lib/mp3file/mp3_header.rb', line 121

def duration
  @samples.to_f / @samplerate
end

#same_header?(other) ⇒ Boolean

Returns:

  • (Boolean)


133
134
135
136
137
# File 'lib/mp3file/mp3_header.rb', line 133

def same_header?(other)
  # Borrowed from ffmpeg.
  same_header_mask = 0xffe00000 | (3 << 17) | (3 << 10) | (3 << 19)
  (to_i & same_header_mask) == (other.to_i & same_header_mask)
end

#to_iObject



125
126
127
128
129
130
131
# File 'lib/mp3file/mp3_header.rb', line 125

def to_i
  @raw.to_binary_s
    .reverse
    .each_byte
    .each_with_index
    .inject(0) { |m, (b, i)| m += b << (i*8) }
end