class MpcInfo
Constants
- FREQUENCIES
- PROFILES_NAMES
- SV4_6_HEADER
Attributes
id3v2_tag[R]
infos[R]
Public Class Methods
new(filename)
click to toggle source
# File lib/audioinfo/mpcinfo.rb, line 39 def initialize(filename) @file = File.open(filename, 'rb') @infos = {} @infos['raw'] = {} parse_infos end
Private Instance Methods
encoder_version(encoderversion)
click to toggle source
# File lib/audioinfo/mpcinfo.rb, line 183 def encoder_version(encoderversion) # Encoder version * 100 (106 = 1.06) # EncoderVersion % 10 == 0 Release (1.0) # EncoderVersion % 2 == 0 Beta (1.06) # EncoderVersion % 2 == 1 Alpha (1.05a...z) if encoderversion.zero? # very old version, not known exactly which 'Buschmann v1.7.0-v1.7.9 or Klemm v0.90-v1.05' elsif (encoderversion % 10).zero? # release version format('%.2f', encoderversion / 100.0) elsif encoderversion.even? format('%.2f beta', encoderversion / 100.0) else format('%.2f alpha', encoderversion / 100.0) end end
parse_infos()
click to toggle source
# File lib/audioinfo/mpcinfo.rb, line 49 def parse_infos mpc_header = @file.read(3) case mpc_header when 'MP+' # this is SV7+ header = StringIO.new(@file.read(25)) header_size = 28 # stream_version_byte = header.read(4).unpack("V").first stream_version_byte = header.read(1)[0].ord # .unpack("c").first @infos['stream_major_version'] = (stream_version_byte & 0x0F) @infos['stream_minor_version'] = (stream_version_byte & 0xF0) >> 4 @infos['frame_count'] = read32(header) raise(MpcInfoError, 'Only Musepack SV7 supported') if @infos['stream_major_version'] != 7 flags_dword1 = read32(header) @infos['intensity_stereo'] = ((flags_dword1 & 0x80000000) >> 31) == 1 @infos['mid_side_stereo'] = ((flags_dword1 & 0x40000000) >> 30) == 1 @infos['max_subband'] = (flags_dword1 & 0x3F000000) >> 24 @infos['raw']['profile'] = (flags_dword1 & 0x00F00000) >> 20 @infos['begin_loud'] = ((flags_dword1 & 0x00080000) >> 19) == 1 @infos['end_loud'] = ((flags_dword1 & 0x00040000) >> 18) == 1 @infos['raw']['sample_rate'] = (flags_dword1 & 0x00030000) >> 16 @infos['max_level'] = (flags_dword1 & 0x0000FFFF) @infos['raw']['title_peak'] = read16(header) @infos['raw']['title_gain'] = read16(header) @infos['raw']['album_peak'] = read16(header) @infos['raw']['album_gain'] = read16(header) flags_dword2 = read32(header) @infos['true_gapless'] = ((flags_dword2 & 0x80000000) >> 31) == 1 @infos['last_frame_length'] = (flags_dword2 & 0x7FF00000) >> 20 not_sure_what = read32(header, 3) @infos['raw']['encoder_version'] = read8(header) @infos['profile'] = PROFILES_NAMES[@infos['raw']['profile']] || 'invalid' @infos['sample_rate'] = FREQUENCIES[@infos['raw']['sample_rate']] raise(MpcInfoError, 'Corrupt MPC file: frequency == zero') if (@infos['sample_rate']).zero? sample_rate = @infos['sample_rate'] channels = 2 # appears to be hardcoded @infos['samples'] = (((@infos['frame_count'] - 1) * 1152) + @infos['last_frame_length']) * channels @infos['length'] = (((@infos['frame_count'] - 1) * 1152) + @infos['last_frame_length']) * channels @infos['length'] = (@infos['samples'] / channels) / @infos['sample_rate'].to_f raise(MpcInfoError, 'Corrupt MPC file: playtime_seconds == zero') if (@infos['length']).zero? # add size of file header to avdataoffset - calc bitrate correctly + MD5 data avdataoffset = header_size # FIXME: is $ThisFileInfo['avdataend'] == File.size ???? @infos['bitrate'] = ((@file.stat.size - avdataoffset) * 8) / @infos['length'] @infos['title_peak'] = @infos['raw']['title_peak'] @infos['title_peak_db'] = @infos['title_peak'].zero? ? 0 : peak_db(@infos['title_peak']) @infos['title_gain_db'] = if (@infos['raw']['title_gain']).negative? (32_768 + @infos['raw']['title_gain']) / -100.0 else @infos['raw']['title_gain'] / 100.0 end @infos['album_peak'] = @infos['raw']['album_peak'] @infos['album_peak_db'] = @infos['album_peak'].zero? ? 0 : peak_db(@infos['album_peak']) @infos['album_gain_db'] = if (@infos['raw']['album_gain']).negative? (32_768 + @infos['raw']['album_gain']) / -100.0 else @infos['raw']['album_gain'] / 100.0 end @infos['encoder_version'] = encoder_version(@infos['raw']['encoder_version']) # #FIXME # $ThisFileInfo['replay_gain']['track']['adjustment'] = @infos['title_gain_db']; # $ThisFileInfo['replay_gain']['album']['adjustment'] = @infos['album_gain_db']; # if @infos['title_peak'] > 0 # #$ThisFileInfo['replay_gain']['track']['peak'] = @infos['title_peak'] # elsif round(@infos['max_level'] * 1.18) > 0) # // why? I don't know - see mppdec.c # # ThisFileInfo['replay_gain']['track']['peak'] = getid3_lib::CastAsInt(round(@infos['max_level'] * 1.18)); # end # # if @infos['album_peak'] > 0 # #$ThisFileInfo['replay_gain']['album']['peak'] = @infos['album_peak']; # end # # #ThisFileInfo['audio']['encoder'] = # # 'SV'.@infos['stream_major_version'].'.'.@infos['stream_minor_version'].', '.@infos['encoder_version']; # #$ThisFileInfo['audio']['encoder'] = @infos['encoder_version']; # #$ThisFileInfo['audio']['encoder_options'] = @infos['profile']; when SV4_6_HEADER # this is SV4 - SV6, handle seperately header_size = 8 when 'ID3' @id3v2_tag = ID3v2.new @id3v2_tag.from_io(@file) @file.seek(@id3v2_tag.io_position) # very dirty hack to allow parsing of mpc infos after id3v2 tag while @file.read(1) != 'M'; end if @file.read(2) == 'P+' @file.seek(-3, IO::SEEK_CUR) # we need to reparse the tag, since we have the beggining of the mpc file parse_infos else raise(MpcInfoError, 'cannot find MPC header after id3 tag') end else raise(MpcInfoError, 'cannot find MPC header') end end
peak_db(i)
click to toggle source
# File lib/audioinfo/mpcinfo.rb, line 179 def peak_db(i) ((Math.log10(i) / Math.log10(2)) - 15) * 6 end
read16(io)
click to toggle source
# File lib/audioinfo/mpcinfo.rb, line 171 def read16(io) io.read(2).unpack1('v') end
read32(io, size = 4)
click to toggle source
# File lib/audioinfo/mpcinfo.rb, line 175 def read32(io, size = 4) io.read(size).unpack1('V') end
read8(io)
click to toggle source
# File lib/audioinfo/mpcinfo.rb, line 167 def read8(io) io.read(1)[0].ord end