1 | |
---|
2 | |
---|
3 | |
---|
4 | |
---|
5 | |
---|
6 | |
---|
7 | |
---|
8 | |
---|
9 | |
---|
10 | |
---|
11 | |
---|
12 | #import "ViewController.h" |
---|
13 | #import <AVFoundation/AVFoundation.h> |
---|
14 | |
---|
15 | |
---|
16 | #define LOGGING false // set to false to stop each received signal being logged |
---|
17 | |
---|
18 | #define AUDIO_FILE_NAME @"throttle_hover_ios.wav" // @"iOS_noflip.wav" // |
---|
19 | #define POOR_SIGNAL_KEY @"poorSignal" |
---|
20 | #define ATTENTION_KEY @"eSenseAttention" |
---|
21 | #define MEDITATION_KEY @"eSenseMeditation" |
---|
22 | |
---|
23 | @implementation ViewController { |
---|
24 | |
---|
25 | |
---|
26 | |
---|
27 | |
---|
28 | float attentionPower[101]; |
---|
29 | float meditationPower[101]; |
---|
30 | |
---|
31 | int currentAttentionLevel, currentMeditationLevel; |
---|
32 | BOOL processingDevice; |
---|
33 | BOOL demoRunning; |
---|
34 | } |
---|
35 | |
---|
36 | @synthesize log, attention, meditation, signal, power, attentionThreshold, meditationThreshold, connectButton, demoButton; |
---|
37 | |
---|
38 | - (void)viewDidLoad |
---|
39 | { |
---|
40 | [super viewDidLoad]; |
---|
41 | [self prepareAudio]; |
---|
42 | [self.attentionThreshold addTarget:self action:@selector(calculatePowerValues) forControlEvents:UIControlEventValueChanged]; |
---|
43 | [self.meditationThreshold addTarget:self action:@selector(calculatePowerValues) forControlEvents:UIControlEventValueChanged]; |
---|
44 | [self calculatePowerValues]; |
---|
45 | |
---|
46 | } |
---|
47 | |
---|
48 | - (void)didReceiveMemoryWarning |
---|
49 | { |
---|
50 | [super didReceiveMemoryWarning]; |
---|
51 | |
---|
52 | [self stopDemo]; |
---|
53 | } |
---|
54 | |
---|
55 | |
---|
56 | - (void) appForegrounded |
---|
57 | { |
---|
58 | [self resetViews]; |
---|
59 | } |
---|
60 | |
---|
61 | |
---|
62 | - (void) resetViews |
---|
63 | { |
---|
64 | [self resetOutputToZero]; |
---|
65 | self.log.text = @""; |
---|
66 | [self printInstructions]; |
---|
67 | |
---|
68 | } |
---|
69 | |
---|
70 | - (void) printInstructions |
---|
71 | { |
---|
72 | [self logMessage:@"\nWelcome to Puzzlebox Orbit!\n\nInstructions for Use:\n1. Pair with EEG headset under Bluetooth Settings\n2. Attach infrared dongle to headphone port\n3. Raise volume to maximum level\n4. Press Connect button below to begin processing\n5. When Attention or Meditation levels cross the indicated threshold, the helicopter will take off\n\nPlease visit http://orbit.puzzlebox.info for more details"]; |
---|
73 | } |
---|
74 | |
---|
75 | - (void) resetOutputToZero |
---|
76 | { |
---|
77 | [self showSignalStrength:[NSNumber numberWithInt:200]]; |
---|
78 | [self setAttentionLevel:[NSNumber numberWithInt:0]]; |
---|
79 | [self setMeditationLevel:[NSNumber numberWithInt:0]]; |
---|
80 | [self showPowerLevel]; |
---|
81 | } |
---|
82 | |
---|
83 | - (void) appClosed |
---|
84 | { |
---|
85 | [self stopDemo]; |
---|
86 | [self resetViews]; |
---|
87 | if ([[TGAccessoryManager sharedTGAccessoryManager] connected]) { |
---|
88 | [[TGAccessoryManager sharedTGAccessoryManager] stopStream]; |
---|
89 | } |
---|
90 | } |
---|
91 | |
---|
92 | - (void) prepareAudio |
---|
93 | { |
---|
94 | NSURL *audioFilePath = [[NSBundle mainBundle] URLForResource:AUDIO_FILE_NAME withExtension:nil]; |
---|
95 | |
---|
96 | NSError *error; |
---|
97 | audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFilePath error:&error]; |
---|
98 | if (!audioPlayer) { |
---|
99 | [self logMessage:[NSString stringWithFormat:@"Failed to initialize audio player: %@", [error debugDescription]]]; |
---|
100 | } else { |
---|
101 | [audioPlayer prepareToPlay]; |
---|
102 | } |
---|
103 | } |
---|
104 | |
---|
105 | - (void) logMessage:(NSString *) str |
---|
106 | { |
---|
107 | log.text = [[NSString alloc] initWithFormat:@"%@\n%@", str, log.text]; |
---|
108 | } |
---|
109 | |
---|
110 | - (void) logDataReceived:(NSDictionary *) data |
---|
111 | { |
---|
112 | NSNumber *sig = [data valueForKey:POOR_SIGNAL_KEY]; |
---|
113 | if (sig != NULL) { |
---|
114 | int val = (200 - [sig intValue]) / 2; |
---|
115 | [self logMessage:[NSString stringWithFormat:@"Signal strength: %u%%, Attention: %@%%, Meditation: %@%%", |
---|
116 | val, [data valueForKey:ATTENTION_KEY], [data valueForKey:MEDITATION_KEY]]]; |
---|
117 | } |
---|
118 | |
---|
119 | } |
---|
120 | |
---|
121 | #pragma mark - TGAccessoryDelegate methods |
---|
122 | |
---|
123 | - (void)dataReceived:(NSDictionary *)data { |
---|
124 | [self performSelectorOnMainThread:@selector(updatedSignalReceived:) |
---|
125 | withObject:data |
---|
126 | waitUntilDone:NO]; |
---|
127 | } |
---|
128 | |
---|
129 | - (void) updatedSignalReceived:(id) data |
---|
130 | { |
---|
131 | if (LOGGING) { |
---|
132 | [self logDataReceived: data]; |
---|
133 | } |
---|
134 | [self showSignalStrength:(NSNumber *)[data valueForKey:POOR_SIGNAL_KEY]]; |
---|
135 | [self setAttentionLevel:(NSNumber *)[data valueForKey:ATTENTION_KEY]]; |
---|
136 | [self setMeditationLevel:(NSNumber *)[data valueForKey:MEDITATION_KEY]]; |
---|
137 | [self showPowerLevel]; |
---|
138 | [self playAudio]; |
---|
139 | } |
---|
140 | |
---|
141 | |
---|
142 | - (void)accessoryDidConnect:(EAAccessory *)accessory { |
---|
143 | [self logMessage:[NSString stringWithFormat:@"%@ connected to this device", [accessory name]]]; |
---|
144 | if ([[TGAccessoryManager sharedTGAccessoryManager] accessory] != nil) { |
---|
145 | [[TGAccessoryManager sharedTGAccessoryManager] startStream]; |
---|
146 | } |
---|
147 | } |
---|
148 | |
---|
149 | |
---|
150 | |
---|
151 | - (void)accessoryDidDisconnect { |
---|
152 | [self logMessage:@"Accessory was disconnected"]; |
---|
153 | [self resetOutputToZero]; |
---|
154 | } |
---|
155 | |
---|
156 | #pragma mark - display calculation and update methods |
---|
157 | |
---|
158 | |
---|
159 | - (void) calculatePowerValues |
---|
160 | { |
---|
161 | float threshold = self.attentionThreshold.value; |
---|
162 | for (int i = 0; i < 101; i++) { |
---|
163 | attentionPower[i] = [self calculatePowerAt:(float)i/100 withThreshold:threshold]; |
---|
164 | } |
---|
165 | threshold = self.meditationThreshold.value; |
---|
166 | for (int i = 0; i < 101; i++) { |
---|
167 | meditationPower[i] = [self calculatePowerAt:(float)i/100 withThreshold:threshold]; |
---|
168 | } |
---|
169 | } |
---|
170 | |
---|
171 | |
---|
172 | |
---|
173 | |
---|
174 | |
---|
175 | |
---|
176 | |
---|
177 | - (float) calculatePowerAt:(float)value withThreshold:(float)threshold |
---|
178 | { |
---|
179 | if (value < threshold) { |
---|
180 | return 0; |
---|
181 | } |
---|
182 | |
---|
183 | |
---|
184 | return (value - threshold) / (1 - threshold); |
---|
185 | } |
---|
186 | |
---|
187 | - (void) showSignalStrength:(NSNumber *) value |
---|
188 | { |
---|
189 | if (value != nil) { |
---|
190 | self.signal.progress = (200.0 - [value intValue]) / 200.0; |
---|
191 | } |
---|
192 | } |
---|
193 | |
---|
194 | - (void) setAttentionLevel:(NSNumber *) value |
---|
195 | { |
---|
196 | if (value != nil) { |
---|
197 | currentAttentionLevel = [value intValue]; |
---|
198 | self.attention.progress = (float)currentAttentionLevel / 100; |
---|
199 | } |
---|
200 | } |
---|
201 | |
---|
202 | - (void) setMeditationLevel:(NSNumber *) value |
---|
203 | { |
---|
204 | if (value != nil) { |
---|
205 | currentMeditationLevel = [value intValue]; |
---|
206 | self.meditation.progress = (float)currentMeditationLevel / 100; |
---|
207 | } |
---|
208 | } |
---|
209 | |
---|
210 | |
---|
211 | - (void) showPowerLevel |
---|
212 | { |
---|
213 | self.power.progress = [self currentPowerLevel]; |
---|
214 | } |
---|
215 | |
---|
216 | |
---|
217 | |
---|
218 | - (float) currentPowerLevel |
---|
219 | { |
---|
220 | float powerLevel = attentionPower[currentAttentionLevel] + meditationPower[currentMeditationLevel]; |
---|
221 | if (powerLevel > 1) { |
---|
222 | return 1.0; |
---|
223 | } |
---|
224 | return powerLevel; |
---|
225 | } |
---|
226 | |
---|
227 | - (void) playAudio |
---|
228 | { |
---|
229 | |
---|
230 | if ([self currentPowerLevel] > 0) { |
---|
231 | audioPlayer.volume = 1.0; |
---|
232 | [audioPlayer play]; |
---|
233 | } else { |
---|
234 | [audioPlayer stop]; |
---|
235 | } |
---|
236 | } |
---|
237 | |
---|
238 | #pragma mark - button press events |
---|
239 | |
---|
240 | |
---|
241 | - (IBAction) demoButtonPressed:(id) sender |
---|
242 | { |
---|
243 | if (demoRunning) { |
---|
244 | [self stopDemo]; |
---|
245 | } else { |
---|
246 | [self startDemo]; |
---|
247 | } |
---|
248 | } |
---|
249 | |
---|
250 | - (void) stopDemo |
---|
251 | { |
---|
252 | demoRunning = NO; |
---|
253 | [self resetOutputToZero]; |
---|
254 | [demoButton setTitle:@"Demo" forState:UIControlStateNormal]; |
---|
255 | if (audioPlayer != NULL && [audioPlayer isPlaying]) { |
---|
256 | [audioPlayer stop]; |
---|
257 | } |
---|
258 | [self logMessage:@"Demo stopped"]; |
---|
259 | } |
---|
260 | |
---|
261 | - (void) startDemo |
---|
262 | { |
---|
263 | demoRunning = YES; |
---|
264 | [self logMessage:@"Running demo"]; |
---|
265 | [demoButton setTitle:@"Stop" forState:UIControlStateNormal]; |
---|
266 | [self performSelectorInBackground:@selector(generateDemoData) withObject:nil]; |
---|
267 | } |
---|
268 | |
---|
269 | |
---|
270 | |
---|
271 | - (void) generateDemoData |
---|
272 | { |
---|
273 | while (demoRunning) { |
---|
274 | for (int i = 0; i < 100; i++) { |
---|
275 | if (!demoRunning) { |
---|
276 | break; |
---|
277 | } |
---|
278 | NSDictionary *values = [NSDictionary dictionaryWithObjectsAndKeys: |
---|
279 | [NSNumber numberWithInt:i], ATTENTION_KEY, |
---|
280 | [NSNumber numberWithInt:i * 0.8], MEDITATION_KEY, |
---|
281 | [NSNumber numberWithInt:0], POOR_SIGNAL_KEY, |
---|
282 | nil]; |
---|
283 | [self dataReceived:values]; |
---|
284 | [NSThread sleepForTimeInterval:0.1]; |
---|
285 | } |
---|
286 | |
---|
287 | for (int i = 100; i > 0; i--) { |
---|
288 | if (!demoRunning) { |
---|
289 | break; |
---|
290 | } |
---|
291 | NSDictionary *values = [NSDictionary dictionaryWithObjectsAndKeys: |
---|
292 | [NSNumber numberWithInt:i], ATTENTION_KEY, |
---|
293 | [NSNumber numberWithInt:i * 0.8], MEDITATION_KEY, |
---|
294 | [NSNumber numberWithInt:0], POOR_SIGNAL_KEY, |
---|
295 | nil]; |
---|
296 | [self dataReceived:values]; |
---|
297 | [NSThread sleepForTimeInterval:0.1]; |
---|
298 | } |
---|
299 | |
---|
300 | } |
---|
301 | } |
---|
302 | |
---|
303 | - (void) startProcessing |
---|
304 | { |
---|
305 | processingDevice = YES; |
---|
306 | [self logMessage:@"Connecting to EEG Headset"]; |
---|
307 | [connectButton setTitle:@"Disconnect" forState:UIControlStateNormal]; |
---|
308 | |
---|
309 | EAAccessory *accessory = [[TGAccessoryManager sharedTGAccessoryManager] accessory]; |
---|
310 | if (accessory != nil) { |
---|
311 | [self logMessage:[NSString stringWithFormat:@"EEG device %@ connected", accessory.name]]; |
---|
312 | [[TGAccessoryManager sharedTGAccessoryManager] startStream]; |
---|
313 | } |
---|
314 | |
---|
315 | } |
---|
316 | |
---|
317 | - (void) stopProcessing |
---|
318 | { |
---|
319 | processingDevice = NO; |
---|
320 | [self logMessage:@"Disconnecting from EEG Headset"]; |
---|
321 | if ([[TGAccessoryManager sharedTGAccessoryManager] connected]) { |
---|
322 | [[TGAccessoryManager sharedTGAccessoryManager] stopStream]; |
---|
323 | } |
---|
324 | [connectButton setTitle:@"Connect" forState:UIControlStateNormal]; |
---|
325 | |
---|
326 | [self resetOutputToZero]; |
---|
327 | [audioPlayer stop]; |
---|
328 | |
---|
329 | } |
---|
330 | |
---|
331 | - (IBAction) connectButtonPressed:(id) sender { |
---|
332 | |
---|
333 | if (processingDevice) { |
---|
334 | [self stopProcessing]; |
---|
335 | } else { |
---|
336 | [self startProcessing]; |
---|
337 | } |
---|
338 | |
---|
339 | } |
---|
340 | @end |
---|