blob: 5e8f7c177c1d6d51b4a7b5f053ceeb9ee1d65256 [file] [log] [blame]
Thomas Martitzdc521b52010-10-18 18:55:31 +00001#!/usr/bin/ruby
2# (c) 2010 by Thomas Martitz
3#
4# This program is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 2 of the License, or
7# (at your option) any later version.
8
9#
10# parse test codec output files and give wiki or spreadsheet formatted output
11#
12class CodecResult
13 include Comparable
14private
15
16 attr_writer :codec
17 attr_writer :decoded_frames
18 attr_writer :max_frames
19 attr_writer :decode_time
20 attr_writer :file_duration
21 attr_writer :percent_realtime
22 attr_writer :mhz_needed
23
24 def get_codec(filename)
25 case filename
Michael Giacomelli77268ec2011-04-12 20:24:43 +000026 when /nero_hev2_.+/
27 self.codec = "Nero AAC-HEv2 (SBR+PS)"
Thomas Martitzdc521b52010-10-18 18:55:31 +000028 when /.+aache.+/, /nero_he_.+/
Michael Giacomelli77268ec2011-04-12 20:24:43 +000029 self.codec = "Nero AAC-HE (SBR)"
Thomas Martitzdc521b52010-10-18 18:55:31 +000030 when /a52.+/
31 self.codec = "AC3 (A52)"
32 when /ape_.+/
33 self.codec = "Monkey Audio"
34 when /lame_.+/
35 self.codec = "MP3"
36 when /.+\.m4a/
37 self.codec = "AAC-LC"
38 when /vorbis.+/
39 self.codec = "Vorbis"
40 when /wma_.+/
41 self.codec = "WMA Standard"
42 when /wv_.+/
43 self.codec = "WAVPACK"
44 when /applelossless.+/
45 self.codec = "Apple Lossless"
46 when /mpc_.+/
47 self.codec = "Musepack"
48 when /flac_.+/
49 self.codec = "FLAC"
50 when /cook_.+/
51 self.codec = "Cook (RA)"
52 when /atrac3.+/
53 self.codec = "Atrac3"
54 when /true.+/
55 self.codec = "True Audio"
Nils Wallméniusfcb9ea42010-12-22 13:27:55 +000056 when /toolame.+/, /pegase_l2.+/
Thomas Martitzdc521b52010-10-18 18:55:31 +000057 self.codec = "MP2"
58 when /atrack1.+/
59 self.codec = "Atrac1"
60 when /wmapro.+/
61 self.codec = "WMA Professional"
62 when /wmal.+/
63 self.codec = "WMA Lossless"
64 when /speex.+/
65 self.codec = "Speex"
Nils Wallméniusfcb9ea42010-12-22 13:27:55 +000066 when /pegase_l1.+/
67 self.codec = "MP1"
Cástor Muñozab3581c2015-04-28 18:53:36 +020068 when /opus.+/
69 self.codec = "Opus"
Thomas Martitzdc521b52010-10-18 18:55:31 +000070 else
71 self.codec = "CODEC UNKNOWN (#{name})"
72 end
73 end
74
75 def file_name=(name)
76 @file_name = name
77 get_codec(name)
78 end
79
80public
81
82 attr_reader :file_name
83 attr_reader :codec
84 attr_reader :decoded_frames
85 attr_reader :max_frames
86 attr_reader :decode_time
87 attr_reader :file_duration
88 attr_reader :percent_realtime
89 attr_reader :mhz_needed
90
91 # make results comparable, allows for simple faster/slower/equal
92 def <=>(other)
93 if self.file_name != other.file_name
94 raise ArgumentError, "Cannot compare different files"
95 end
96 return self.decode_time <=> other.decode_time
97 end
98
99 def initialize(text_block, cpu_freq = nil)
100 # we need an Array
101 c = text_block.class
102 if (c != Array && c.superclass != Array)
103 raise ArgumentError,
104 "Argument must be an array but is " + text_block.class.to_s
105 end
106
107 #~ lame_192.mp3
108 #~ 175909 of 175960
109 #~ Decode time - 8.84s
110 #~ File duration - 175.96s
111 #~ 1990.49% realtime
112 #~ 30.14MHz needed for realtime (not there in RaaA)
113
114 # file name
115 self.file_name = text_block[0]
116
117 # decoded & max frames
118 test = Regexp.new(/(\d+) of (\d+)/)
119 res = text_block[1].match(test)
120 self.decoded_frames = res[1].to_i
121 self.max_frames = res[2].to_i
122
123 # decode time, in centiseconds
124 test = Regexp.new(/Decode time - ([.\d]+)s/)
125 self.decode_time = text_block[2].match(test)[1].to_f
126
127 # file duration, in centiseconds
128 test = Regexp.new(/File duration - ([.\d]+)s/)
129 self.file_duration = text_block[3].match(test)[1].to_f
130
131 # % realtime
132 self.percent_realtime = text_block[4].to_f
133
134 # MHz needed for rt
135 test = Regexp.new(/[.\d]+MHz needed for realtime/)
136 self.mhz_needed = nil
137 if (text_block[5] != nil && text_block[5].length > 0)
Nils Wallméniusfcb9ea42010-12-22 13:27:55 +0000138 self.mhz_needed = text_block[5].match(test)[0].to_f
Thomas Martitzdc521b52010-10-18 18:55:31 +0000139 elsif (cpu_freq)
140 # if not given, calculate it as per passed cpu frequency
141 # duration to microseconds
142 speed = self.file_duration / self.decode_time
143 self.mhz_needed = cpu_freq / speed
144 end
145 end
146end
147
148class TestCodecResults < Array
149 def initialize(file_name, cpu_freq)
150 super()
151 temp = self.clone
152 # go through the results, create a CodecResult for each block
153 # of text (results for the codecs are seperated by an empty line)
154 File.open(file_name, File::RDONLY) do |file|
155 file.each_chomp do |line|
156 if (line.length == 0) then
157 self << CodecResult.new(temp, cpu_freq);temp.clear
158 else
159 temp << line
160 end
161 end
162 end
163 end
164
165 # sort the results by filename (so files of the same codec are near)
166 def sort
167 super { |x, y| x.file_name <=> y.file_name }
168 end
169end
170
171class File
172 # walk through each line but have the \n removed
173 def each_chomp
174 self.each_line do |line|
175 yield(line.chomp)
176 end
177 end
178end
179
180class Float
181 alias_method(:old_to_s, :to_s)
182 # add the ability to use a different decimal seperator in to_s
183 def to_s
184 string = old_to_s
185 string.sub!(/[.]/ , @@dec_sep) if @@dec_sep
186 string
187 end
188
189 @@dec_sep = nil
190 def self.decimal_seperator=(sep)
191 @@dec_sep=sep
192 end
193end
194
195#files is an Array of TestCodecResultss
196def for_calc(files)
197 files[0].each_index do |i|
198 string = files[0][i].file_name + "\t"
199 for f in files
200 string += f[i].percent_realtime.to_s + "%\t"
201 end
202 puts string
203 end
204end
205
206#files is an Array of TestCodecResultss
207def for_wiki(files)
208 basefile = files.shift
209 codec = nil
210 basefile.each_index do |i| res = basefile[i]
211 # make a joined row for each codec
212 if (codec == nil || res.codec != codec) then
213 codec = res.codec
214 puts "| *%s* ||||%s" % [codec, "|"*files.length]
215 end
216 row = sprintf("| %s | %.2f%%%% realtime | Decode time - %.2fs |" %
217 [res.file_name, res.percent_realtime, res.decode_time])
218 if (res.mhz_needed != nil) # column for mhz needed, | - | if unknown
219 row += sprintf(" %.2fMHz |" % res.mhz_needed.to_s)
220 else
221 row += " - |"
222 end
223 for f in files # calculate speed up compared to the rest files
224 delta = (res.percent_realtime / f[i].percent_realtime)*100
225 row += sprintf(" %.2f%%%% |" % delta)
226 end
227 puts row
228 end
229end
230
231# for_xml() anyone? :)
232
233def help
234 puts "#{$0} [OPTIONS] FILE [FILES]..."
235 puts "Options:\t-w\tOutput in Fosswiki format (default)"
236 puts "\t\t-c\tOutput in Spreadsheet-compatible format (tab-seperated)"
237 puts "\t\t-s=MHZ\tAssume MHZ cpu frequency for \"MHz needed for realtime\" calculation"
238 puts "\t\t\t(if not given by the log files, e.g. for RaaA)"
239 puts "\t\t-d=CHAR\tUse CHAR as decimal seperator in the -c output"
240 puts "\t\t\t(if your spreadsheed tool localized and making problems)"
241 puts
242 puts "\tOne file is needed. This is the basefile."
243 puts "\tIn -c output, the % realtime values of each"
244 puts "\tcodec from each file is printed on the screen onto the screen"
245 puts "\tIn -w output, a wiki table is made from the basefile with one column"
246 puts "\tfor each additional file representing relative speed of the basefile"
247 exit
248end
249
250to_call = method(:for_wiki)
251mhz = nil
252files = []
253
254help if (ARGV.length == 0)
255
256ARGV.each do |e|
257 a = e.chars.to_a
258 if (a[0] == '-') # option
259 case a[1]
260 when 'c'
261 to_call = method(:for_calc)
262 when 'w'
263 to_call = method(:for_wiki)
264 when 'd'
265 if (a[2] == '=')
266 sep = a[3]
267 else
268 sep = a[2]
269 end
270 Float.decimal_seperator = sep
271 when 's'
272 if (a[2] == '=')
273 mhz = a[3..-1].join.to_i
274 else
275 mhz = a[2..-1].join.to_i
276 end
277 else
278 help
279 end
280 else # filename
281 files << e
282 end
283end
284
285
286tmp = []
287for file in files do
288 tmp << TestCodecResults.new(file, mhz).sort
289end
290to_call.call(tmp) # invoke selected method