source: orbit/iOS/Orbit/Orbit/SignalConverter.m @ e37a889

ServoTab_Interfacepyramid
Last change on this file since e37a889 was e37a889, checked in by Steve Castellotti <sc@…>, 7 years ago

Orbit:

  • Operating on WAV sound
  • Property mode set to 100644
File size: 10.4 KB
RevLine 
[ab9d63b]1//
2//  SignalConverter.m
3//  orbit
4//
5//  Created by Jonathon Horsman on 11/07/2013.
6//  Copyright (c) 2013 Puzzlebox Productions, LLC. All rights reserved.
7//
8
9#import "SignalConverter.h"
10
11#define AUDIO_FILE_NAME @"throttle_hover_ios.wav" // @"iOS_noflip.wav"
12#define POOR_SIGNAL_KEY @"poorSignal"
13#define ATTENTION_KEY @"eSenseAttention"
14#define MEDITATION_KEY @"eSenseMeditation"
15
[9015b1e]16#define CHANNEL_A 1
17
[ab9d63b]18// Converts signals received from the EEG headset to the audio played to fly the helicopter.
19//
20@implementation SignalConverter {
21    // these 2 arrays hold the values of the thrust which should be applied to the helicopter
22    // (by way of volume level through the headphones) at each level of attention and meditation.
23    // this could be calculated on the fly, but because it happens ~20 times per second, the
24    // better option is to store all the values in arrays, and recalculate when the sliders are changed.
25    float attentionPower[101];
26    float meditationPower[101];
27   
28    int signalStrength, attentionLevel, meditationLevel; // the latest readings from the headset
[ce0a7ee]29    int yaw, throttle, pitch;
[ab9d63b]30}
31
[de10cbc]32@synthesize attentionThreshold, meditationThreshold, running, testing;
[ab9d63b]33
[9015b1e]34
35- (id) init
36{
37    self = [super init];
38    if (self)
39    {
40        [[TGAccessoryManager sharedTGAccessoryManager] setupManagerWithInterval:0.05];
41        [[TGAccessoryManager sharedTGAccessoryManager] setDelegate:self];
[ad40faa]42        // Generate Signal
43        //        audioPlayer = [[AudioGenerator alloc] init];
[ce0a7ee]44       
45        // defaults
46        throttle = 80;
47        yaw = 78;
48        pitch = 31;
[359a504]49       
50        // initialise the audio session - this should only be done once
51        AudioSessionInitialize(NULL, NULL, NULL, NULL);
[7945ecb]52        AudioSessionSetActive(YES);
[9015b1e]53    }
54    return self;
55}
56
[ab9d63b]57- (void) setValuesForAttention:(float) attention meditation:(float) meditation
58{
59    attentionThreshold = attention;
60    meditationThreshold = meditation;
61    [self calculatePowerValues];
62}
63
[9015b1e]64- (void) appStopped
[ab9d63b]65{
[9015b1e]66    running = NO;
67    if ([TGAccessoryManager sharedTGAccessoryManager].accessory != nil) {
[ab9d63b]68        [[TGAccessoryManager sharedTGAccessoryManager] stopStream];
69    }
[9015b1e]70    [audioPlayer stop];
71    if (_delegate != nil) {
72        [_delegate appStopped];
73    }
[7945ecb]74    AudioSessionSetActive(NO);
[ab9d63b]75}
76
77#pragma mark - TGAccessoryDelegate methods
78
79- (void)dataReceived:(NSDictionary *)data {
80    [self performSelectorOnMainThread:@selector(updatedSignalReceived:)
81                           withObject:data
82                        waitUntilDone:NO];
83}
84
[d249b65]85// Updated signal received from the EEG headset.
[ab9d63b]86- (void) updatedSignalReceived:(id) data
87{
88    [self setValuesFromData:data];
89    // notify listening delegates of updated values so the UI can be updated
90    if (_delegate != nil)
91    {
92        [_delegate updatedValuesForSignal: signalStrength
[d249b65]93                                attention: (float)attentionLevel / 100
94                               meditation: (float)meditationLevel / 100
95                                    power: [self currentPowerLevel]];
[ab9d63b]96    }
97    [self playAudio];
98}
99
100- (void) setValuesFromData:(id) data
101{
102    [self setSignalStrength:(NSNumber *)[data valueForKey:POOR_SIGNAL_KEY]];
103    [self setAttentionLevel:(NSNumber *)[data valueForKey:ATTENTION_KEY]];
[d249b65]104    [self setMeditationLevel:(NSNumber *)[data valueForKey:MEDITATION_KEY]];
[ab9d63b]105}
106
107- (void) setSignalStrength:(NSNumber *) value
108{
109    if (value != nil) {
[d249b65]110       
[ad40faa]111        //        NSLog(@"value: %i", [value intValue]);
[d249b65]112       
113        if ([value intValue] == 0) {
114            signalStrength = 100;
115        }
116       
117        else if ([value intValue] == 200) {
118            signalStrength = 0;
119        }
120       
121        else {
122            signalStrength = [value intValue];
123        }
124       
[ad40faa]125        //        NSLog(@"signalStrength: %i", signalStrength);
[d249b65]126       
[ab9d63b]127    }
128}
129
130- (void) setAttentionLevel:(NSNumber *) value
131{
132    if (value != nil) {
133        attentionLevel = [value intValue];
134    }
135}
136
137- (void) setMeditationLevel:(NSNumber *) value
138{
139    if (value != nil) {
140        meditationLevel = [value intValue];
141    }
142}
143
144// The headset was switched on, start the data stream
145- (void)accessoryDidConnect:(EAAccessory *)accessory {
146}
147
148// The headset was switched off (or the Bluetooth signal was dropped).
149- (void)accessoryDidDisconnect {
150    if (_delegate != nil)
151    {
152        [_delegate notifyHeadsetDisconnect];
153    }
154}
155
[9015b1e]156- (BOOL) isBluetoothReady
157{
158    return [[TGAccessoryManager sharedTGAccessoryManager] accessory] != NULL;
159}
160
161- (BOOL) isVolumeMax
162{
[7945ecb]163    Float32 volume;
164    UInt32 dataSize;
165    AudioSessionGetPropertySize(kAudioSessionProperty_CurrentHardwareOutputVolume, &dataSize);
166    AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputVolume, &dataSize, &volume);
[359a504]167    NSLog(@"Volume is %f", volume);
168    return 1.0 == volume;
169}
170
171- (BOOL) isAudioJackPlugged
172{
173    UInt32 routeSize;
174   
175    // oddly, without calling this method caused an error.
176    AudioSessionGetPropertySize(kAudioSessionProperty_AudioRouteDescription, &routeSize);
177    CFDictionaryRef desc; // this is the dictionary to contain descriptions
178   
179    // make the call to get the audio description and populate the desc dictionary
[7945ecb]180    AudioSessionGetProperty(kAudioSessionProperty_AudioRouteDescription, &routeSize, &desc);
[d249b65]181   
[359a504]182    // the dictionary contains 2 keys, for input and output. Get output array
183    CFArrayRef outputs = CFDictionaryGetValue(desc, kAudioSession_AudioRouteKey_Outputs);
184   
185    // the output array contains 1 element - a dictionary
186    CFDictionaryRef dict = CFArrayGetValueAtIndex(outputs, 0);
187   
188    // get the output description from the dictionary
189    CFStringRef output = CFDictionaryGetValue(dict, kAudioSession_AudioRouteKey_Type);
190   
191    /**
192     These are the possible output types:
193     kAudioSessionOutputRoute_LineOut
194     kAudioSessionOutputRoute_Headphones
195     kAudioSessionOutputRoute_BluetoothHFP
196     kAudioSessionOutputRoute_BluetoothA2DP
197     kAudioSessionOutputRoute_BuiltInReceiver
198     kAudioSessionOutputRoute_BuiltInSpeaker
199     kAudioSessionOutputRoute_USBAudio
200     kAudioSessionOutputRoute_HDMI
201     kAudioSessionOutputRoute_AirPlay
202     */
[d249b65]203   
[359a504]204    return CFStringCompare(output, kAudioSessionOutputRoute_Headphones, 0) == kCFCompareEqualTo;
[9015b1e]205}
206
[ab9d63b]207#pragma mark start / stop methods
208
[9015b1e]209- (BOOL) startProcessing
[ab9d63b]210{
[de10cbc]211    if (testing) return NO;
[ab9d63b]212    EAAccessory *accessory = [[TGAccessoryManager sharedTGAccessoryManager] accessory];
213    if (accessory != nil) {
[9015b1e]214        running = YES;
215        if (_delegate != nil) {
[ab9d63b]216            [_delegate notifyDeviceConnected: accessory.name];
217        }
218        [[TGAccessoryManager sharedTGAccessoryManager] startStream];
219    }
[9015b1e]220    return running;
[ab9d63b]221}
222
223- (void) stopProcessing
224{
[de10cbc]225    if (running) [self appStopped];
226}
227
228- (void) playTestSound
229{
230    if (!running && !testing) {
231        testing = YES;
[ad40faa]232        // Generate Sound
233        //        [audioPlayer playWithThrottle:throttle yaw:yaw pitch:pitch];
[e37a889]234       
235        // Play WAV
236        [self prepareAudio];
237        [audioPlayer play];
[de10cbc]238    }
239}
240
241- (void) stopTestSound
242{
243    if (testing) {
244        testing = NO;
245        [audioPlayer stop];
246    }
[ab9d63b]247}
248
249#pragma mark internal processing methods
250
251// calculate the total power level to output through the headphones, a value between zero and 1.
252// this is the attention signal level and meditation signal level added together
253- (float) currentPowerLevel
254{
[d249b65]255   
[ba2b6ff]256    float attentionScore = attentionPower[attentionLevel];
257    float meditationScore = meditationPower[meditationLevel];
258   
[89934ea]259    if (attentionThreshold == 0.0) {
[ba2b6ff]260        attentionScore = 0;
261    }
262   
263    if (meditationThreshold == 0.0) {
264        meditationScore = 0;
265    }
[d249b65]266   
[ba2b6ff]267    float powerLevel = attentionScore + meditationScore;
[ab9d63b]268    if (powerLevel > 1) {
269        return 1.0;
270    }
271    return powerLevel;
272}
273
[ce0a7ee]274- (void) setYaw:(int)y throttle:(int)t pitch:(int)p
275{
276    yaw = y;
277    throttle = t;
278    pitch = p;
279}
280
[ad40faa]281//- (void) playAudio
282//{
283//    if ([self currentPowerLevel] > 0) {
284//        // Generate Signal
285//        [audioPlayer playWithThrottle:throttle yaw:yaw pitch:pitch];
286//    } else {
287//        [audioPlayer stop];
[e37a889]288//
[ad40faa]289//    }
290//}
291
292
[ab9d63b]293- (void) playAudio
294{
[ad40faa]295   
[e37a889]296    NSLog(@"currentPowerLevel: %f", [self currentPowerLevel]);
[ad40faa]297   
[e37a889]298//    [self prepareAudio];
299//    [audioPlayer play];
300//   
301//    return;
[ad40faa]302   
303    //   audioPlayer.volume = [self currentPowerLevel];
[ab9d63b]304    if ([self currentPowerLevel] > 0) {
[e37a889]305       
[ad40faa]306        audioPlayer.volume = 1.0;
[e37a889]307       
308        // Generate Signal
309//        [audioPlayer playWithThrottle:throttle yaw:yaw pitch:pitch];
310       
311        // Play WAV
312        [self prepareAudio];
[ad40faa]313        [audioPlayer play];
[e37a889]314       
[ab9d63b]315    } else {
316        [audioPlayer stop];
[ad40faa]317        [self prepareAudio];
[ab9d63b]318       
319    }
320}
321
322#pragma mark - display calculation and update methods
323
324// when the user adjusts one of the 2 threshold sliders we recalculate the power values at each of the 101 possible levels
325- (void) calculatePowerValues
326{
327    for (int i = 0; i < 101; i++) {
328        attentionPower[i] = [self calculatePowerAt:(float)i/100 withThreshold:attentionThreshold];
329    }
330    for (int i = 0; i < 101; i++) {
331        meditationPower[i] = [self calculatePowerAt:(float)i/100 withThreshold:meditationThreshold];
332    }
333}
334
335// Convert a value (attention or meditation) into the corresponding power output (which is the volume level)
336// given the threshold is set at specified value.
337// The threshold is the value set by the user as the minimum value (attention or meditation) to be reached
338// before the helicopter is given the signal to fly.
339// If the value if below the threshold, the value will be zero (threshold not yet met).
340// Otherwise the returned power value will be between 0 and 1.
341- (float) calculatePowerAt:(float)value withThreshold:(float)threshold
342{
343    if (value < threshold) { // threshold not met
344        return 0;
345    }
346    // e.g. if the threshold is 0.55 and the current value is 0.7:
347    // there is 0.45 remaining of the threshold slider, and the value is 0.15 past the threshold (0.7 - 0.55), which is 0.33 (0.15 / 0.45)
348    return (value - threshold) / (1 - threshold);
349}
350
[ad40faa]351- (void) prepareAudio
352{
353    NSURL *audioFilePath = [[NSBundle mainBundle] URLForResource:AUDIO_FILE_NAME withExtension:nil];
354   
355    NSError *error;
356    audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFilePath error:&error];
357    if (!audioPlayer) {
358        //        [self logMessage:[NSString stringWithFormat:@"Failed to initialize audio player: %@", [error debugDescription]]];
359        NSLog(@"Failed to initialize audio player: %@", [error debugDescription]);
360    } else {
361        [audioPlayer prepareToPlay];
362    }
363}
364
[ab9d63b]365@end
Note: See TracBrowser for help on using the repository browser.