Interhaptics SDK for Unity 1.6
Loading...
Searching...
No Matches
iOSUtilities.cs
Go to the documentation of this file.
1/* ​
2* Copyright (c) 2023 Go Touch VR SAS. All rights reserved. ​
3* ​
4*/
5
6using System;
7using System.Collections;
8using System.Collections.Generic;
9using System.Linq;
10using UnityEngine;
11
13{
14 public static class iOSUtilities
15 {
16 const int PARAMETER_CURVE_MAX_SIZE = 16;
17 const double D_EPSILON = 0.00001;
18
19 private static bool IsEqual(double _a, double _b, double _epsilon = D_EPSILON)
20 {
21 return Mathf.Abs((float)(_a - _b)) <= _epsilon;
22 }
23
24 // check if the ramp between two values is going up, down or is constant
25 private static int GetCurrentRamp(double _a, double _b)
26 {
27 if (IsEqual(_a, _b)) // constant
28 {
29 return 0;
30 }
31
32 if (_a > _b) // going down
33 {
34 return -1;
35 }
36
37 return 1; // going up
38 }
39
40 // converts HAR output buffers to simplified data for AHAP construction
41 // HAR == [value, value, value....] each value separated by _step seconds
42 // out == [time, value, time, value....] with linear interpolation between each pair
43 private static List<double> ProcessBuffer(double [] _buffer, double _step)
44 {
45 List<double> outBuffer = new List<double>();
46
47 double currentTimeStamp = 0.0;
48 int lastRamp = -2; //set to -2 to always get the first value
49
50 for (int i = 0; i < _buffer.Count(); i++)
51 {
52 if (i == _buffer.Count() - 1) //if last value, stores a pair
53 {
54 outBuffer.Add(currentTimeStamp);
55 outBuffer.Add(_buffer[i]);
56 break;
57 }
58
59 int currentRamp = GetCurrentRamp(_buffer[i], _buffer[i+1]); //check if curve is going up or down
60
61 // if ramp changes, stores a pair
62 if (currentRamp != lastRamp) //will trigger at first iteration
63 {
64 lastRamp = currentRamp;
65 outBuffer.Add(currentTimeStamp);
66 outBuffer.Add(_buffer[i]);
67 }
68
69 currentTimeStamp += _step;
70 }
71
72 return outBuffer;
73 }
74
75 public static string BufferToAHAP(float[] _buffer, float[] _frequencies, float[] _transientTimer, float[] _transientGain, float _duration, float _timestep)
76 {
77 double[] transient = new double[_transientTimer.Length * 4];
78
79 for (int j = 0; j < transient.Length; j += 4)
80 {
81 transient[j] = _transientTimer[j / 4];
82 transient[j + 1] = _transientGain[j / 4];
83 transient[j + 2] = 0;
84 transient[j + 3] = 0;
85 }
86
87 double[] buffer = new double[_buffer.Length];
88 double[] frequencies = new double[_frequencies.Length];
89
90 for (int j = 0; j < _frequencies.Length; j++)
91 {
92 frequencies[j] = _frequencies[j];
93 }
94 for (int j = 0; j < _buffer.Length; j++)
95 {
96 buffer[j] = _buffer[j];
97 }
98 return BufferToAHAP(buffer, frequencies, transient, _duration, _timestep);
99 }
100
101 public static string BufferToAHAP(double[] _buffer, double[] _frequencies, double[] _transients, double _duration, double _timestep)
102 {
103 if (_buffer == null)
104 {
105 return null;
106 }
107
108 //UnityEngine.Debug.Log(_buffer.Length);
109
110 AHAP ahap = new AHAP();
111 ahap.Pattern = new List<PatternObject>();
112
113 // transients ------------------------------------------------------------
114 if (_transients != null)
115 {
116 for (int i = 0; i + 3 < _transients.Length; i += 4)
117 {
118 EventParameter intensityTrans = new EventParameter
119 {
120 parameterID = ParameterID.HapticIntensity,
121 parameterValue = (float)_transients[i + 1]
122 };
123 EventParameter sharpnessTrans = new EventParameter
124 {
125 parameterID = ParameterID.HapticSharpness,
126 parameterValue = 1.0f
127 };
128
129 Event CurrentTransient = new Event
130 {
131 Time = (float)_transients[i],
132 eventType = EventType.HapticTransient,
133 eventParameters = new EventParameter[2] { intensityTrans, sharpnessTrans }
134 };
135 ahap.Pattern.Add(CurrentTransient);
136
137 //puting the amplitude modulation at 1 for the transients ---
138
139 float currentTransStart = CurrentTransient.Time;
140 float currentTransEnd = currentTransStart + 0.022f;
141
142 int startIndex = (int)(currentTransStart / _timestep);
143 int endIndex = Math.Max((int)(currentTransEnd / _timestep), startIndex + 1);
144
145 int end = Math.Min(endIndex, _buffer.Length);
146 for (int index = startIndex; index < end; index++)
147 {
148 _buffer[index] = (float)_transients[i + 1];
149 }
150
151 }
152 }
153 // -----------------------------------------------------------------------
154
155 // Convert Amplitude and Frequency pattern to have less values
156 List<double> ampPattern = ProcessBuffer(_buffer, _timestep);
157 List<double> freqPattern = ProcessBuffer(_frequencies, _timestep);
158
159 EventParameter intensity = new EventParameter
160 {
161 parameterID = ParameterID.HapticIntensity,
162 parameterValue = 1
163 };
164 EventParameter sharpness = new EventParameter
165 {
166 parameterID = ParameterID.HapticSharpness,
167 parameterValue = 1
168 };
169
170 /*
171 // remove amp modulation when there are only transients
172 if (ampPattern.Count() == 2 && IsEqual(ampPattern[0], ampPattern[1]))
173 {
174 intensity.parameterValue = ampPattern[0];
175 ampPattern.Clear();
176 }
177
178 //remove freq modulation when there are only transients
179 if (freqPattern.Count() == 2 && IsEqual(freqPattern[0], freqPattern[1]))
180 {
181 sharpness.parameterValue = freqPattern[0];
182 freqPattern.Clear();
183 }*/
184
185 Event e = new Event
186 {
187 Time = 0,
188 eventType = EventType.HapticContinuous,
189 eventDuration = (float)_duration,
190 eventParameters = new EventParameter[2] { intensity, sharpness }
191 };
192
193 ahap.Pattern.Add(e);
194
195 //Amplitude pattern ------------------------------------------------------
196 for (int j = 0; j < ampPattern.Count(); j += PARAMETER_CURVE_MAX_SIZE * 2)
197 {
198 List<ParameterCurveControlPoint> controlPoints = new List<ParameterCurveControlPoint>();
199 for (int k = 0; k < Math.Min(ampPattern.Count() - j, PARAMETER_CURVE_MAX_SIZE * 2); k+=2)
200 {
201 controlPoints.Add(new ParameterCurveControlPoint
202 {
203 Time = ampPattern[k + j] - ampPattern[j],
204 ParameterValue = Mathf.Max(0, (float)ampPattern[k + j + 1])
205 });
206 }
207
208 ParameterCurve parameterCurve = new ParameterCurve
209 {
210 parameterID = ParameterID.HapticIntensityControl,
211 Time = ampPattern[j],
212 parameterCurveControlPoints = controlPoints
213 };
214 ahap.Pattern.Add(parameterCurve);
215 }
216 // -----------------------------------------------------------------------
217
218 // Frequency pattern ------------------------------------------------------
219 for (int j = 0; j < freqPattern.Count(); j += PARAMETER_CURVE_MAX_SIZE * 2)
220 {
221 List<ParameterCurveControlPoint> controlPoints = new List<ParameterCurveControlPoint>();
222 for (int k = 0; k < Math.Min(freqPattern.Count() - j, PARAMETER_CURVE_MAX_SIZE * 2); k+=2)
223 {
224 controlPoints.Add(new ParameterCurveControlPoint
225 {
226 Time = freqPattern[k + j] - freqPattern[j],
227 ParameterValue = Mathf.Max(0, (Mathf.Clamp((float)freqPattern[k + j + 1], 65.0f, 300.0f) - 300.0f) / 235.0f + 1) - 1
228 });
229 }
230
231 ParameterCurve parameterCurve = new ParameterCurve
232 {
233 parameterID = ParameterID.HapticSharpnessControl,
234 Time = freqPattern[j],
235 parameterCurveControlPoints = controlPoints
236 };
237 ahap.Pattern.Add(parameterCurve);
238 }
239 // -----------------------------------------------------------------------
240 //Debug.Log(ahap.ToJson());
241 return ahap.ToJson();
242 }
243 }
244
245 public class AHAP
246 {
247 public int Version = 1;
248 public MetaData Metadata = new MetaData();
249 public List<PatternObject> Pattern;
250
251 public string ToJson()
252 {
253 string json = "{" +
254 "\"Version\": " + Version + ","
255 + Metadata.ToJson();
256
257 json += "\"Pattern\": [";
258 json += string.Join(",", Pattern.Select(e => e.ToJson()));
259 json += "]}";
260 return json;
261 }
262 }
263
264 public class MetaData
265 {
266 public string Project = "";
267 public string Created = "";
268
269 public string ToJson()
270 {
271 return "\"Metadata\": {" +
272 "\"Project\": \"" + Project + "\"," +
273 "\"Created\": \"" + Created + "\"" +
274 "},";
275 }
276 }
277
278
279 public abstract class PatternObject
280 {
281 public abstract string ToJson();
282 }
283
284 public class Event : PatternObject
285 {
286 public override string ToJson()
287 {
288 string json = "";
289 string eventParametersStr = "\"EventParameters\": [";
290
291 if (eventParameters.Length > 0)
292 {
293 for (int i = 0; i < eventParameters.Length; i++)
294 {
295 if (i > 0)
296 {
297 eventParametersStr += ", ";
298 }
299 eventParametersStr += "{" + "" +
300 "\"ParameterID\": \"" + eventParameters[i].parameterID.ToString() + "\"," +
301 "\"ParameterValue\": " + eventParameters[i].parameterValue +
302 "}";
303 }
304 }
305 eventParametersStr += "]";
306
307 json += "{" +
308 "\"Event\": {" +
309 "\"Time\": " + Time + "," +
310 "\"EventType\": \"" + eventType.ToString() + "\"," +
311 "\"EventDuration\": " + eventDuration + "," +
312 eventParametersStr +
313 "}" +
314 "}";
315
316 return json;
317 }
318
319 public float Time = 0;
320 public EventType eventType = EventType.HapticContinuous;
321 public float eventDuration = 0;
323
324 }
325
327 {
328
329 public override string ToJson()
330 {
331 string json = "";
332
333 string pccp = "\"ParameterCurveControlPoints\": [";
334
335 if (parameterCurveControlPoints.Count > 0)
336 {
337 for (int i = 0; i < parameterCurveControlPoints.Count; i++)
338 {
339 if (i > 0)
340 {
341 pccp += ", ";
342 }
343
344 pccp += "{" + "" +
345 "\"Time\": " + parameterCurveControlPoints[i].Time + "," +
346 "\"ParameterValue\": " + parameterCurveControlPoints[i].ParameterValue +
347 "}";
348 }
349 }
350 pccp += "]";
351
352 json += "{" +
353 "\"ParameterCurve\": {" +
354 "\"ParameterID\": \"" + parameterID.ToString() + "\"," +
355 "\"Time\": " + Time + "," +
356 pccp +
357 "}" +
358 "}";
359
360 return json;
361 }
362
363 public ParameterID parameterID = ParameterID.HapticIntensityControl;
364 public double Time = 0;
365 public List<ParameterCurveControlPoint> parameterCurveControlPoints;
366
367 }
368
369 public enum EventType
370 {
373 }
374
382
383 public struct EventParameter
384 {
386 public double parameterValue;
387 }
388
390 {
391 public double Time;
392 public double ParameterValue;
393 }
394
395}
List< ParameterCurveControlPoint > parameterCurveControlPoints