From: Leslie Viljoen Date: 2006-06-14T23:06:11+09:00 Subject: Re: [QUIZ part solution] Running Coach (#82) On 6/14/06, Adam Shelly wrote: > Here's my attempt: Here's a quick version that is closer to having speech synth. It's not a real synthesiser, but if you can provide the corresponding ogg files it can look for certain phrases and play them. The result should sound a bit better than a real synthesiser since the sections will be spoken fairly naturally. Only 53 files to record! Well maybe the strings you use could be made a bit more similar to shorten the list a bit ;-) Les ------------------------ $CheerThreshold = 6 #decrease to get more random encouragement $LongThreshold = 120 #minimum time to be considered a "long" run class SpeechSynth def initialize @known = ["minutes left in this phase", "you are done, rest now.", "until the next activity", "OK, you can walk now", "seconds more to walk", "seconds more to run", "to go and there are", "OK, you can run now", "You are almost done", "are you ready, go!", "get ready to walk", "get ready to run", "short run left", "in this phase", "to exercise", "Keep it up!", "to exersize", "Way to go!", "of walking", "short runs", "minute and", "There are", "Good job!", "long runs", "of runing", "You have", "walk for", "long run", "minutes", "seconds", "to walk", "run for", "to run", "walks", "left", "only", "more", "for", "and", "60", "30", "15", "18", "55", "7", "6", "5", "9", "3", "2", "1", "8", "4"] end def playFile(fileName) puts "PLAY: '" +fileName + "'" #on linux this can be: #`play #{filename}` end def known(searchPhrase) @known.each do |phrase| return $~[1] if searchPhrase =~ /^(#{phrase}).*/i end puts "UNKNOWN PHRASE: #{searchPhrase}" return nil end def say(sentence) while sentence.length > 0 knownPhrase = known(sentence) if knownPhrase playFile(knownPhrase + ".ogg") sentence = sentence[knownPhrase.length+1..-1] sentence = "" if !sentence else sentence = "" end sentence.strip! end end end class Phase attr_reader :action, :seconds def initialize action, time @action = action.downcase @seconds = time.to_i end end class Coach def initialize filename @synth = SpeechSynth.new File.open(filename) {|f| @rawdata = f.read.split("\n") } @duration = 0 @runs = @longs = @walks = 0 @encouragometer = 0 @step = [30,15,10,5,5] end def coach build_timeline say summarize(2) say start_prompt @time = Time.now @target_time = @time while (phase = @phases.shift) update_summary phase narrate_phase phase if @phases.size > 0 say transition(@phases[0].action) say summarize(rand(2)) end end say finish_line end def narrate_phase phase say what_to_do_for(phase) @target_time += phase.seconds delta = (@target_time - Time.now).to_i stepidx = 0 while (delta > 0) stepidx+=1 if delta < @step[stepidx]+1 wait_time = delta%@step[stepidx] wait_time += @step[stepidx] if wait_time <= 0 wait(wait_time) delta = (@target_time - Time.now).to_i encourage_maybe say whats_left(phase.action,delta) if delta > 0 end end def update_summary phase @duration -= phase.seconds @runs -= 1 if phase.action == 'run' @longs -= 1 if phase.action == 'run' and phase.seconds >= $LongThreshold @walks -= 1 if phase.action == 'walk' end def build_timeline @phases = @rawdata.map {|command| p = Phase.new(*command.split) @duration += p.seconds @runs += 1 if p.action == 'run' @longs += 1 if p.action == 'run' and p.seconds >= $LongThreshold @walks += 1 if p.action == 'walk' p } end def say s @synth.say(s) end def wait n if $DEBUG puts "...waiting #{n} seconds..." @target_time -= n else $stdout.flush sleep(n) end end def encourage_maybe @encouragometer += rand(3) if (@encouragometer > $CheerThreshold) say cheer @encouragometer = 0 end end def timesay secs secs = secs.to_i s = "" if secs > 60 min = secs/60 secs -= min*60 s += "#{min} minute" s += 's' if min > 1 s += ' and ' if secs > 0 end if secs > 0 s += "#{secs} second" s += 's' if secs > 1 end s end # All the phrases should be below this line, not mixed up in the logic def what_to_do_for phase s = "#{phase.action} for #{timesay(phase.seconds)} \n" #s += "You are almost done" if @phases.size == 1 s end def whats_left act, time timestr = timesay(time) s = [ "You have #{timestr} more to #{act}", "#{act} for #{timestr} more", "only #{timestr} left of #{act}ing", "You have #{timestr} more to #{act}", "#{timestr} left in this phase", "There are #{timestr} until the next activity" ] s[rand(s.size)] end def start_prompt "are you ready, go!" end def transition next_act s = ["OK, you can #{next_act} now", "get ready to #{next_act}"] s[rand(s.size)] end def finish_line "you are done, rest now." end def cheer c = ["Keep it up!", "Way to go!", "Good Job!"] c[rand(c.size)] end def summarize degree shorts = @runs - @longs s = "you have #{timesay(@duration)}" if degree > 0 if degree > 1 s+= " for " else s+= " to go and there are " if @runs > 0 end s+="#{@longs} long run" if @longs > 0 s+="s" if @longs > 1 s+=" and" if @longs > 0 and shorts > 0 and degree <=1 s+=" #{shorts} short run" if shorts > 0 s+="s" if shorts > 1 if degree >1 s+=" and" if @longs+shorts > 0 s+=" #{@walks} walk" if @walks > 0 s+="s" if @walks > 1 else s+=" left" end else s+= " left to exercise" end s end end Coach.new(ARGV[0]||"week3.txt").coach