altosdroid: Get rid of a couple of startup messages
[fw/altos] / altosdroid / src / org / altusmetrum / AltosDroid / AltosVoice.java
1 /*\r
2  * Copyright © 2011 Keith Packard <keithp@keithp.com>\r
3  * Copyright © 2012 Mike Beattie <mike@ethernal.org>\r
4  *\r
5  * This program is free software; you can redistribute it and/or modify\r
6  * it under the terms of the GNU General Public License as published by\r
7  * the Free Software Foundation; version 2 of the License.\r
8  *\r
9  * This program is distributed in the hope that it will be useful, but\r
10  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
12  * General Public License for more details.\r
13  *\r
14  * You should have received a copy of the GNU General Public License along\r
15  * with this program; if not, write to the Free Software Foundation, Inc.,\r
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.\r
17  */\r
18 \r
19 package org.altusmetrum.AltosDroid;\r
20 \r
21 import android.speech.tts.TextToSpeech;\r
22 import android.speech.tts.TextToSpeech.OnInitListener;\r
23 \r
24 import org.altusmetrum.altoslib_1.*;\r
25 \r
26 public class AltosVoice {\r
27 \r
28         private TextToSpeech tts         = null;\r
29         private boolean      tts_enabled = false;\r
30 \r
31         private IdleThread   idle_thread = null;\r
32 \r
33         private AltosState   old_state   = null;\r
34 \r
35         public AltosVoice(AltosDroid a) {\r
36 \r
37                 tts = new TextToSpeech(a, new OnInitListener() {\r
38                         public void onInit(int status) {\r
39                                 if (status == TextToSpeech.SUCCESS) tts_enabled = true;\r
40                                 if (tts_enabled) {\r
41                                         idle_thread = new IdleThread();\r
42                                 }\r
43                         }\r
44                 });\r
45 \r
46         }\r
47 \r
48         public void speak(String s) {\r
49                 if (!tts_enabled) return;\r
50                 tts.speak(s, TextToSpeech.QUEUE_ADD, null);\r
51         }\r
52 \r
53         public void stop() {\r
54                 if (tts != null) tts.shutdown();\r
55                 if (idle_thread != null) {\r
56                         idle_thread.interrupt();\r
57                         idle_thread = null;\r
58                 }\r
59         }\r
60 \r
61         public void tell(AltosState state) {\r
62                 if (!tts_enabled) return;\r
63 \r
64                 boolean spoke = false;\r
65                 if (old_state == null || old_state.state != state.state) {\r
66                         speak(state.data.state());\r
67                         if ((old_state == null || old_state.state <= AltosLib.ao_flight_boost) &&\r
68                             state.state > AltosLib.ao_flight_boost) {\r
69                                 speak(String.format("max speed: %d meters per second.", (int) (state.max_speed() + 0.5)));\r
70                                 spoke = true;\r
71                         } else if ((old_state == null || old_state.state < AltosLib.ao_flight_drogue) &&\r
72                                    state.state >= AltosLib.ao_flight_drogue) {\r
73                                 speak(String.format("max height: %d meters.", (int) (state.max_height + 0.5)));\r
74                                 spoke = true;\r
75                         }\r
76                 }\r
77                 if (old_state == null || old_state.gps_ready != state.gps_ready) {\r
78                         if (state.gps_ready) {\r
79                                 speak("GPS ready");\r
80                                 spoke = true;\r
81                         } else if (old_state != null) {\r
82                                 speak("GPS lost");\r
83                                 spoke = true;\r
84                         }\r
85                 }\r
86                 old_state = state;\r
87                 idle_thread.notice(state, spoke);\r
88         }\r
89 \r
90 \r
91         class IdleThread extends Thread {\r
92                 boolean            started;\r
93                 private AltosState state;\r
94                 int                reported_landing;\r
95                 int                report_interval;\r
96                 long               report_time;\r
97 \r
98                 public synchronized void report(boolean last) {\r
99                         if (state == null)\r
100                                 return;\r
101 \r
102                         /* reset the landing count once we hear about a new flight */\r
103                         if (state.state < AltosLib.ao_flight_drogue)\r
104                                 reported_landing = 0;\r
105 \r
106                         /* Shut up once the rocket is on the ground */\r
107                         if (reported_landing > 2) {\r
108                                 return;\r
109                         }\r
110 \r
111                         /* If the rocket isn't on the pad, then report height */\r
112                         if (AltosLib.ao_flight_drogue <= state.state &&\r
113                             state.state < AltosLib.ao_flight_landed &&\r
114                             state.range >= 0)\r
115                         {\r
116                                 speak(String.format("Height %d, bearing %s %d, elevation %d, range %d.\n",\r
117                                                     (int) (state.height + 0.5),\r
118                                         state.from_pad.bearing_words(\r
119                                               AltosGreatCircle.BEARING_VOICE),\r
120                                                     (int) (state.from_pad.bearing + 0.5),\r
121                                                     (int) (state.elevation + 0.5),\r
122                                                     (int) (state.range + 0.5)));\r
123                         } else if (state.state > AltosLib.ao_flight_pad) {\r
124                                 speak(String.format("%d meters", (int) (state.height + 0.5)));\r
125                         } else {\r
126                                 reported_landing = 0;\r
127                         }\r
128 \r
129                         /* If the rocket is coming down, check to see if it has landed;\r
130                          * either we've got a landed report or we haven't heard from it in\r
131                          * a long time\r
132                          */\r
133                         if (state.state >= AltosLib.ao_flight_drogue &&\r
134                             (last ||\r
135                              System.currentTimeMillis() - state.report_time >= 15000 ||\r
136                              state.state == AltosLib.ao_flight_landed))\r
137                         {\r
138                                 if (Math.abs(state.baro_speed) < 20 && state.height < 100)\r
139                                         speak("rocket landed safely");\r
140                                 else\r
141                                         speak("rocket may have crashed");\r
142                                 if (state.from_pad != null)\r
143                                         speak(String.format("Bearing %d degrees, range %d meters.",\r
144                                                             (int) (state.from_pad.bearing + 0.5),\r
145                                                             (int) (state.from_pad.distance + 0.5)));\r
146                                 ++reported_landing;\r
147                         }\r
148                 }\r
149 \r
150                 long now () {\r
151                         return System.currentTimeMillis();\r
152                 }\r
153 \r
154                 void set_report_time() {\r
155                         report_time = now() + report_interval;\r
156                 }\r
157 \r
158                 public void run () {\r
159                         try {\r
160                                 for (;;) {\r
161                                         set_report_time();\r
162                                         for (;;) {\r
163                                                 synchronized (this) {\r
164                                                         long sleep_time = report_time - now();\r
165                                                         if (sleep_time <= 0)\r
166                                                                 break;\r
167                                                         wait(sleep_time);\r
168                                                 }\r
169                                         }\r
170                                         report(false);\r
171                                 }\r
172                         } catch (InterruptedException ie) {\r
173                         }\r
174                 }\r
175 \r
176                 public synchronized void notice(AltosState new_state, boolean spoken) {\r
177                         AltosState old_state = state;\r
178                         state = new_state;\r
179                         if (!started && state.state > AltosLib.ao_flight_pad) {\r
180                                 started = true;\r
181                                 start();\r
182                         }\r
183 \r
184                         if (state.state < AltosLib.ao_flight_drogue)\r
185                                 report_interval = 10000;\r
186                         else\r
187                                 report_interval = 20000;\r
188                         if (old_state != null && old_state.state != state.state) {\r
189                                 report_time = now();\r
190                                 this.notify();\r
191                         } else if (spoken)\r
192                                 set_report_time();\r
193                 }\r
194 \r
195                 public IdleThread() {\r
196                         state = null;\r
197                         reported_landing = 0;\r
198                         report_interval = 10000;\r
199                 }\r
200         }\r
201 \r
202 }\r