wiki:HelloWorld

Hello world

Back to Building emotion-oriented systems

The “Hello” example realises a simple text-based interactive system. The user types arbitrary text; an analyser component spots keywords, and deduces an affective state from them; and a rendering component outputs an emoticon corresponding to this text. Despite its simplicity, the example is instructive because it displays the main elements of an emotion-oriented system.

The input component simply reads one line of text at a time, and sends it on. It has an input device (line 4) and a Sender writing TEXT data to the Topic semaine.data.hello.text (line 3). In its constructor, the component registers itself as an input component (l. 7), and registers its sender (l. 8). Its act() method, which is automatically called every 100 ms while the system is running, checks for new input (l. 12), reads it (l. 13), and sends it to the Topic (l. 14).

 1 public class HelloInput extends Component {
 2
 3   private Sender textSender = new Sender("semaine.data.hello.text", "TEXT", getName());;
 4   private BufferedReader inputReader = new BufferedReader(new InputStreamReader(System.in));
 5
 6   public HelloInput() throws JMSException {
 7     super("HelloInput", true/*is input*/, false);
 8     senders.add(textSender);
 9   }
10
11   @Override protected void act() throws IOException, JMSException {
12     if (inputReader.ready()) {
13       String line = inputReader.readLine();
14       textSender.sendTextMessage(line, meta.getTime());
15     }
16   }
17 }

As a simplistic central processing component, the HelloAnalyser makes emotional judgements about the input. It registers a Receiver (l. 7) for the Topic that HelloInput writes to, and sets up (l. 3) and registers (l. 8) an XML Sender producing data of type EmotionML. Whenever a message is received, the method react() is called (l. 11). It receives (l. 13) and analyses (l. 14-17) the input text, and computes values for the emotion dimensions arousal and valence from the text. Finally, it creates an EmotionML document (l. 18) and sends it (l. 19).

 1 public class HelloAnalyser extends Component {
 2 
 3   private XMLSender emotionSender = 
         new XMLSender("semaine.data.hello.emotion", "EmotionML", getName());
 4 
 5   public HelloAnalyser() throws JMSException {
 6     super("HelloAnalyser");
 7     receivers.add(new Receiver("semaine.data.hello.text"));
 8     senders.add(emotionSender);
 9   }
10 	
11   @Override protected void react(SEMAINEMessage m) throws JMSException {
12     int arousalValue = 0, valenceValue = 0;
13     String input = m.getText();
14     if (input.contains("very")) arousalValue = 1;
15     else if (input.contains("a bit")) arousalValue = 0;
16     if (input.contains("happy")) valenceValue = 1;
17     else if (input.contains("sad")) valenceValue = 0;
18     Document emotionML = createEmotionML(arousalValue, valenceValue);
19     emotionSender.sendXML(emotionML, meta.getTime());
20   }
21 
22   private Document createEmotionML(int arousalValue, int valenceValue) {
23     Document emotionML = XMLTool.newDocument(EmotionML.ROOT_TAGNAME,
                   EmotionML.namespaceURI);
24     Element emotion = XMLTool.appendChildElement(
                   emotionML.getDocumentElement(), EmotionML.E_EMOTION);
25     emotion.setAttribute(EmotionML.A_DIMENSION_VOCABULARY,
                   EmotionML.VOC_FSRE_DIMENSION_DEFINITION);
26     Element arousal = XMLTool.appendChildElement(emotion,
                   EmotionML.E_DIMENSION);
27     arousal.setAttribute(EmotionML.A_NAME,
                   EmotionML.VOC_FSRE_DIMENSION_AROUSAL);
28     arousal.setAttribute(EmotionML.A_VALUE, String.valueOf(arousalValue));
29     Element valence = XMLTool.appendChildElement(emotion,
                   EmotionML.E_DIMENSION);
30     valence.setAttribute(EmotionML.A_NAME,
                   EmotionML.VOC_FSRE_DIMENSION_VALENCE);
31     valence.setAttribute(EmotionML.A_VALUE, String.valueOf(valenceValue));
32     return emotionML;
33   }
34 }

As the SEMAINE API does not yet provide built-in support for standalone EmotionML documents, the component uses a generic XMLSender (l. 3) and uses the XMLTool to build up the EmotionML document (l. 23-30).

Valence
-0+
Arousal+8-(8-| 8-)
0:-(:-| :-)
-*-(*-| *-)

The output of the Hello system should be an emoticon representing an area in the arousal-valence plane as shown in the Table above. The EmoticonOutput component registers an XML Receiver (l. 5) to the Topic that the HelloAnalyser sends to. Whenever a message is received, the react() method is called (l. 8), which analyses the XML document in terms of EmotionML markup (l. 10-12), and extracts the arousal and valence values (l. 14-15). The emotion display is rendered as a function of these values (l. 17-19).

 1 public class EmoticonOutput extends Component {
 2 
 3   public EmoticonOutput() throws JMSException {
 4     super("EmoticonOutput", false, true /*is output*/);
 5     receivers.add(new XMLReceiver("semaine.data.hello.emotion"));
 6   }
 7 
 8   @Override protected void react(SEMAINEMessage m)
                   throws MessageFormatException {
 9     SEMAINEXMLMessage xm = (SEMAINEXMLMessage) m;
10     Element emotion = (Element) xm.getDocument().getElementsByTagNameNS(
                   EmotionML.namespaceURI, EmotionML.E_EMOTION).item(0);
11     String vocabularyURI = emotion.getAttribute(
                   EmotionML.A_DIMENSION_VOCABULARY);
12     if (!vocabularyURI.equals(EmotionML.VOC_FSRE_DIMENSION_DEFINITION))
                   return;
13     List<Element> dimensions = XMLTool.getChildrenByLocalNameNS(emotion,
                   EmotionML.E_DIMENSION, EmotionML.namespaceURI);
14     float a = 0.5f, v = 0.5f; // neutral values
15     boolean haveSomething = false;
16     for (Element dim : dimensions) {
17       String name = dim.getAttribute(EmotionML.A_NAME);
18       float value = Float.parseFloat(dim.getAttribute(EmotionML.A_VALUE));
19       if (name.equals(EmotionML.VOC_FSRE_DIMENSION_AROUSAL)) {
20         a = value;
21         haveSomething = true;
22       } else if (name.equals(EmotionML.VOC_FSRE_DIMENSION_VALENCE)) {
23         v = value;
24         haveSomething = true;
25       }
26     }
27     if (!haveSomething) return;
28     String eyes  = a > 0.66 ? "8" /*active*/   : 
                      a < 0.33 ? "*" /*passive*/  :
                                 ":" /*neutral*/;
29     String mouth = v > 0.66 ? ")" /*positive*/ : 
                      v < 0.33 ? "(" /*negative*/ : 
                                 "|" /*neutral*/;
30     System.out.println(eyes+"-"+mouth);
31   }
32 }

In order to build a system from the components, a configuration file is created. It includes the SystemManager component as well as the three newly created components. Furthermore, it requests a visible system manager GUI providing a message flow graph.

semaine.components = \
    |eu.semaine.components.meta.SystemManager| \
    |eu.semaine.examples.hello.HelloInput| \
    |eu.semaine.examples.hello.HelloAnalyser| \
    |eu.semaine.examples.hello.EmoticonOutput|

semaine.systemmanager.gui = true

The system is started in the same way as all Java-based SEMAINE API systems:

java -cp 'lib/*' eu.semaine.system.ComponentRunner config/example-hello.config

The following figure shows a screenshot of the resulting message flow graph. As the communication passes via the middleware ActiveMQ, the system would behave in the exact same way if the four components were started as separate processes, on different machines, or if some of them were written in C++ rather than Java.

Back to Building emotion-oriented systems

Last modified 6 years ago Last modified on 03/24/11 09:38:29

Attachments (1)

Download all attachments as: .zip