Skip to content

Commit

Permalink
+ james
Browse files Browse the repository at this point in the history
  • Loading branch information
Florian Hanke committed Mar 31, 2008
1 parent 7d1b9a6 commit 00ab79d
Show file tree
Hide file tree
Showing 62 changed files with 49,565 additions and 0 deletions.
3 changes: 3 additions & 0 deletions config/README
@@ -0,0 +1,3 @@
This folder contains configuration files for james itself.

The files are in YAML format (http://de.wikipedia.org/wiki/YAML).
3 changes: 3 additions & 0 deletions config/names.yml
@@ -0,0 +1,3 @@
# names for the two voices
female: jamie
male: james
19 changes: 19 additions & 0 deletions config/sentences.yml
@@ -0,0 +1,19 @@
# with this file it is possible to configure your james's core
# commands are denoted with an exclamation mark
# it is not possible to use multiple command words
sleep!: sleep
return from dialogue!: thank you
# when james ... he says one of ...
wakes up:
- yes?
- sir?
- ready!
- ok!
- tell me!
- yes, sir?
- your wish is my command.
- at your service.
goes to sleep:
- good night!
- good bye!
- hasta la vista, baby!
8 changes: 8 additions & 0 deletions config/voices.yml
@@ -0,0 +1,8 @@
# Commented voices are Apple built-in voices. Can be changed by replacing the last part e.g.'Vicki' with e.g.'Laura'
# much better female voice from iVox:
# female: com.acapela.iVox.voice.iVoxHeather22k
# much better male voice from iVox:
# male: com.acapela.iVox.voice.iVoxRyan22k
female: com.apple.speech.synthesis.voice.Vicki
# male: com.apple.speech.synthesis.voice.Bruce
male: com.apple.speech.synthesis.voice.Alex
38 changes: 38 additions & 0 deletions configurator.rb
@@ -0,0 +1,38 @@
# TODO all

# configures
class Configurator
def self.configure

end
end

require 'osx/cocoa'

...

def initialize
# get and configure a recognizer interface
@recognizer = OSX::NSSpeechRecognizer.alloc.init
@recognizer.setDelegate(self)
@recognizer.startListening
# get a synthesizer interface
@synthesizer = OSX::NSSpeechSynthesizer.alloc.init
end

# callback method from the speech interface
def speechRecognizer_didRecognizeCommand( sender, command )
# do something with the command
# command needs to be converted to a proper ruby string: command.to_s
end

def speak(text)
# say something using the speech synthesizer
@synthesizer.startSpeakingString(text)
end

state :to, {
'back' => :from,
'next train' => :result,
'nowhere' => :result
}.merge(CITIES)
8 changes: 8 additions & 0 deletions dialogue.rb
@@ -0,0 +1,8 @@
class Dialogue

# choose one reply randomly from the given replies
def random_reply(replies)
replies[rand(replies.size)]
end

end
119 changes: 119 additions & 0 deletions dialogue_extension.rb
@@ -0,0 +1,119 @@
# require 'cocoa'

# TODO make ['HB','berne','geneva'] => :from possible using the splat operator
# add ability to chain dialogues a la chain_dialogue :state, <dialogue_name>

# superclass for dialogue modules
# dialogues move along the moves
# if a state is entered, enter_#{state_name} is called
# if a state is exited, exit_#{state_name} is called
class DialogueExtension < Dialogue

attr_reader :state

alias :old_initialize :initialize

# every subclass of this class automatically has its state set to :entry on creation
# def initialize(*args)
# puts "Resetting ", self.name, "\n"
# reset
# old_initialize(*args)
# end

# # automatically adds a hook phrase
# # meaning: adds a move from :awake to this hook word
# # and also a method
def initialize
# reset
end
#
# # TODO improve this such that reset doesn't need to be called in each initializer!
def reset
@state = :entry
end

# TODO think about saying something after each method call though like this it is kept simple which is good
def hear(phrase)
# if next state
return nil unless next_state(phrase)
# call exit method
send("exit_#{@state}".intern, phrase) if respond_to?("exit_#{@state}")
# TODO say(response)
# set actual state
@state = self.next_state(phrase)
# call entry method
send("enter_#{@state}".intern) if respond_to?("enter_#{@state}")
end

# next possible phrases
# TODO splat
def expects
self.class.moves[@state].keys
end

def next_state(phrase)
self.class.moves[@state][phrase] if self.class.moves[@state]
end

# returns the possible states of this dialogue
def self.possible_states
self.moves.keys
end

# metaprog

# hook words - these define when this dialogue is entered
# adds a hooks method
# TODO get hooks from yaml file
def self.hook_words(*hooks)
self.class_eval do
# set entry state correctly
entry = {}
hooks.each do |hook|
entry[hook] = self.initial
end
# add moves class variable
class <<self
attr_accessor :moves
end
self.moves ||= {}
self.moves[:entry] = entry
# define an instance method
define_method(:hooks) do
hooks
end
end
end

# initial state
def self.initial_state(initial)
self.class_eval do
# add accessor for
class <<self
attr_accessor :initial
end
self.initial = initial
end
end

# state definitions like
# state :name, { moves }
def self.state(name, moves)
self.class_eval do
self.moves ||= {}
self.moves[name] ||= {}
# split arrays here instead of handling later specifically
# can change the implementation later if needed
moves.each do |words,state|
words.each do |word|
self.moves[name][word] = state
end
end
#
# self.moves[name] = moves
# puts "moves for #{self.name} are #{self.moves.inspect}"
end
# puts "#{self.name} === #{self.moves.inspect}"
end

end
108 changes: 108 additions & 0 deletions dialogue_frontend.rb
@@ -0,0 +1,108 @@
require 'osx/cocoa'
require 'main_dialogue'

# TODO move some stuff in the dialogue
# TODO extract cocoa connection
# TODO implement callback

# debug
USE_TEXTUAL_INTERFACE = false

class DialogueFrontend

attr_reader :dialogue

def initialize
# load voices
load_voices

# get a dialogue
@dialogue = MainDialogue.new(self)

# get and configure a recognizer interface
start_recognizer

# get a synthesizer interface
start_synthesizer

# default voice
male
end

# callback method from the speech interface
def speechRecognizer_didRecognizeCommand( sender, command )
command = command.to_s
# call the dialogue system
@dialogue.hear(command)
# set actual commands
self.commands = @dialogue.expects
end

# callback method from dialogue
def say(text)
@synthesizer.startSpeakingString(text)
end

# callback from dialogue
def male
self.voice = @male_voice
end

# callback from dialogue
def female
self.voice = @female_voice
end

# wrapper for the cocoa setCommands
def commands=(commands)
@recognizer.setCommands(commands)
puts "expects: #{commands.join(', ')}"
end

private

# specialized setter for voice
def voice=(voice)
@synthesizer.setVoice(voice)
end

# start recognizing words
def start_recognizer
@recognizer = OSX::NSSpeechRecognizer.alloc.init
@recognizer.setBlocksOtherRecognizers(true)
@recognizer.setListensInForegroundOnly(false)
@recognizer.setDelegate(self)
self.commands = @dialogue.expects
@recognizer.startListening
end

# start speaking
def start_synthesizer
@synthesizer = OSX::NSSpeechSynthesizer.alloc.init
end

# load voices from yaml
def load_voices
yaml_voices = ''
File.open('config/voices.yml') do |f| yaml_voices << f.read end
voices = YAML.load(yaml_voices)
@male_voice = voices['male']
@female_voice = voices['female']
end

end

controller = DialogueFrontend.new

# code to use a textual interface
while USE_TEXTUAL_INTERFACE
exit_words = ['quit','exit']
puts "'#{exit_words.join("' or '")}' to quit. Expects: #{controller.dialogue.expects.join(', ')}"
input = gets.chomp
if exit_words.include?(input)
break
end
controller.dialogue.hear(input)
end

OSX::NSApplication.sharedApplication.run
65 changes: 65 additions & 0 deletions dialogue_plugin.rb
@@ -0,0 +1,65 @@
require 'rubycocoa'

# superclass for dialogue modules
# dialogues move along the moves
# if a state is entered, enter_#{state_name} is called
# if a state is exited, exit_#{state_name} is called
class DialoguePlugin

# automatically adds a hook phrase
# meaning: adds a move from :awake to this hook word
# and also a method
def initialize
# actual state in this module
@state = nil
# defines possible moves from one state to another
@moves = {

}
end

# returns the possible states of this dialogue
def possible_states
@moves.keys
end

# next possible phrases
def expects_phrases
@moves[@state].keys
end

def next_state(phrase)
@moves[@state][phrase]
end

def hear(phrase)
# if next state
return unless next_state(phrase)
# call exit method
response = send("exit_#{@state}".intern, phrase)
say response
# set actual state
@state = next_state(phrase)
# call entry method
response = send("enter_#{@state}")
say response
end

def say(text)
# TODO blah
end

# hook words - these define when this dialogue is entered
def hook_words(names = nil)
names.each do |name|
# TODO
end
end

def initial_state(initial)
define_method(:reset) do
@state = initial
end
end

end
Binary file added dialogues/.DS_Store
Binary file not shown.
2 changes: 2 additions & 0 deletions dialogues/joke/README
@@ -0,0 +1,2 @@
TODO: load the jokes from somewhere
With this dialogue part, james can tell jokes!
1 change: 1 addition & 0 deletions dialogues/joke/init.rb
@@ -0,0 +1 @@
require 'joke_dialogue'

0 comments on commit 00ab79d

Please sign in to comment.