f@0
|
1 package uk.ac.qmul.eecs.depic.daw.haptics;
|
f@0
|
2
|
f@0
|
3 import uk.ac.qmul.eecs.depic.daw.Chunk;
|
f@0
|
4 import uk.ac.qmul.eecs.depic.daw.SoundWave;
|
f@0
|
5 import uk.ac.qmul.eecs.depic.daw.SoundWaveEvent;
|
f@0
|
6 import uk.ac.qmul.eecs.depic.daw.SoundWaveListener;
|
f@0
|
7 import uk.ac.qmul.eecs.depic.jhapticgui.Haptics;
|
f@0
|
8 import uk.ac.qmul.eecs.depic.patterns.MathUtils;
|
f@0
|
9 import uk.ac.qmul.eecs.depic.patterns.Range;
|
f@0
|
10 import uk.ac.qmul.eecs.depic.patterns.Sequence;
|
f@0
|
11 import uk.ac.qmul.eecs.depic.patterns.Sequence.Value;
|
f@0
|
12 import uk.ac.qmul.eecs.depic.patterns.SequenceEvent;
|
f@0
|
13 import uk.ac.qmul.eecs.depic.patterns.SequenceListener;
|
f@0
|
14 import uk.ac.qmul.eecs.depic.patterns.SequenceMapping;
|
f@0
|
15
|
f@2
|
16 /**
|
f@2
|
17 *
|
f@2
|
18 * Listens to elements of the DAW and sends commands to the haptic device in order to
|
f@2
|
19 * render such elements in haptics. It implements all the listeners interfaces because it reacts
|
f@2
|
20 * to changes and mirrors them in haptics.
|
f@2
|
21 *
|
f@2
|
22 * It is used to render sound waves and automations (sequences) in haptics
|
f@2
|
23 *
|
f@2
|
24 */
|
f@0
|
25 public class HapticTrigger implements SequenceMapping, SequenceListener, SoundWaveListener {
|
f@0
|
26 public enum DisplayType {
|
f@0
|
27 DISPLAY_SEQUENCE,
|
f@0
|
28 DISPLAY_NONE,
|
f@0
|
29 DISPLAY_PEAKS,
|
f@0
|
30 DISPLAY_RENDER_CURVE_AT,
|
f@0
|
31 DISPLAY_RENDER_VALUE
|
f@0
|
32 }
|
f@0
|
33
|
f@0
|
34 public abstract class Command {
|
f@0
|
35 /**
|
f@0
|
36 * Message to display a graph
|
f@0
|
37 *
|
f@0
|
38 * Arguments: intitial y value, max y value, min y value, max x value, x viewport size
|
f@0
|
39 */
|
f@0
|
40 public static final String DISPLAY_SEQUENCE = "display.seq";
|
f@0
|
41 public static final String DISPLAY_RENDER_CURVE_AT = "display.curve_at";
|
f@0
|
42 public static final String DISPLAY_RENDER_VALUE = "display.value";
|
f@0
|
43 /**
|
f@0
|
44 * display nothing. takes no args
|
f@0
|
45 */
|
f@0
|
46 public static final String DISPLAY_NONE = "display.none";
|
f@0
|
47 public static final String DISPLAY_PEAKS = "display.peaks";
|
f@0
|
48
|
f@0
|
49 /**
|
f@0
|
50 * Arguments : x time position, y in normalized form
|
f@0
|
51 */
|
f@0
|
52 public static final String SEQUENCE_VALUE_ADD = "seq.value.add";
|
f@0
|
53 public static final String SEQUENCE_VALUE_CHANGE = "seq.value.change";
|
f@0
|
54 public static final String SEQUENCE_VALUE_REMOVE = "seq.value.rem";
|
f@0
|
55 public static final String SEQUENCE_VALUE_FIND = "seq.value.find";
|
f@0
|
56 public static final String SEQUENCE_SHIFT = "seq.shift";
|
f@0
|
57 public static final String SEQUENCE_BEGIN = "seq.begin";
|
f@0
|
58
|
f@0
|
59 public static final String RENDER_VALUE = "render_value";
|
f@0
|
60 public static final String RENDER_CURVE_AT = "render_curve_at";
|
f@0
|
61 /**
|
f@0
|
62 * Message to rotate the viscosity scrub line in the haptic space
|
f@0
|
63 *
|
f@0
|
64 * Argument is the degree of rotation
|
f@0
|
65 */
|
f@0
|
66 public static final String ROTATE_Z = "rotate.z";
|
f@0
|
67 public static final String ROTATE_Y = "rotate.y";
|
f@0
|
68 public static final String ROTATE_X = "rotate.x";
|
f@0
|
69 }
|
f@0
|
70
|
f@0
|
71 private DisplayType displayType;
|
f@0
|
72 private Haptics haptics;
|
f@0
|
73
|
f@0
|
74 public HapticTrigger(Haptics haptics, DisplayType type){
|
f@0
|
75 if(haptics == null)
|
f@0
|
76 throw new IllegalArgumentException("haptics cannot be null");
|
f@0
|
77 this.haptics = haptics;
|
f@0
|
78 displayType = type;
|
f@0
|
79 haptics.sendMessage(Command.DISPLAY_NONE,"",0);
|
f@0
|
80 }
|
f@0
|
81
|
f@0
|
82 @Override
|
f@0
|
83 public void renderValue(Value val) {
|
f@0
|
84 /* sends a normalized value of the chunk size */
|
f@0
|
85 haptics.sendMessage(Command.RENDER_VALUE, Float.toString(val.getValue()) , val.hashCode());
|
f@0
|
86 }
|
f@0
|
87
|
f@0
|
88 @Override
|
f@0
|
89 public void renderCurve(Sequence m, float startTime) {
|
f@0
|
90 throw new UnsupportedOperationException("Only update implemented");
|
f@0
|
91 }
|
f@0
|
92
|
f@0
|
93 @Override
|
f@0
|
94 public void renderCurveAt(Sequence sequence, float time, float duration) {
|
f@0
|
95 float val = new MathUtils.Interpolate(sequence).linear(time);
|
f@0
|
96
|
f@0
|
97 /* normalize the value to [0-1] range */
|
f@0
|
98 float normalizedValue = new MathUtils.Scale(sequence.getRange(), new Range<Float>(0.0f,1.0f)).linear(val);
|
f@0
|
99 haptics.sendMessage(Command.RENDER_CURVE_AT, ""+normalizedValue, sequence.hashCode());
|
f@0
|
100 }
|
f@0
|
101
|
f@0
|
102 @Override
|
f@0
|
103 public <T extends SequenceEvent> void sequenceUpdated(T t) {
|
f@0
|
104 SequenceEvent.What evtType = t.getWhat();
|
f@0
|
105
|
f@0
|
106 if(displayType == DisplayType.DISPLAY_SEQUENCE) {
|
f@0
|
107 if(SequenceEvent.What.VALUE_ADDED.equals(evtType)){
|
f@0
|
108 Sequence.Value aVal = t.getValue();
|
f@0
|
109
|
f@0
|
110 /* since the haptic graph y value are in normalized form normalize first */
|
f@0
|
111 float verticalValue = new MathUtils.Scale(t.getSource().getRange(), Range.NORMALIZED_RANGE_F).linear(aVal.getValue());
|
f@0
|
112
|
f@0
|
113 haptics.sendMessage(Command.SEQUENCE_VALUE_ADD, aVal.getTimePosition()+" "+verticalValue, aVal.hashCode());
|
f@0
|
114 }else if(SequenceEvent.What.VALUE_CHANGED.equals(evtType)){
|
f@0
|
115 Sequence.Value aVal = t.getValue();
|
f@0
|
116
|
f@0
|
117 float verticalValue = new MathUtils.Scale(t.getSource().getRange(), Range.NORMALIZED_RANGE_F).linear(aVal.getValue());
|
f@0
|
118
|
f@0
|
119 haptics.sendMessage(Command.SEQUENCE_VALUE_CHANGE, aVal.getTimePosition() +" "+verticalValue, aVal.hashCode());
|
f@0
|
120 }else if(SequenceEvent.What.VALUE_REMOVED.equals(evtType)){
|
f@0
|
121 Sequence.Value aVal = t.getValue();
|
f@0
|
122 haptics.sendMessage(Command.SEQUENCE_VALUE_REMOVE, "", aVal.hashCode());
|
f@0
|
123 }else if(SequenceEvent.What.BEGIN_CHANGED.equals(evtType)){
|
f@0
|
124 Sequence sequence = t.getSource();
|
f@0
|
125
|
f@0
|
126 haptics.sendMessage(Command.SEQUENCE_BEGIN,
|
f@0
|
127 ""+new MathUtils.Scale(sequence.getRange(),Range.NORMALIZED_RANGE_F).linear(sequence.getBegin()),
|
f@0
|
128 0
|
f@0
|
129 );
|
f@0
|
130 }
|
f@0
|
131 }
|
f@0
|
132
|
f@0
|
133 }
|
f@0
|
134
|
f@0
|
135 /**
|
f@0
|
136 * Gets an update from a {@code SoundWave} this class is a listener of, and send a message to the {@code HapticDevice}
|
f@0
|
137 * thread accordingly.
|
f@0
|
138 *
|
f@0
|
139 * @param evt the new event
|
f@0
|
140 */
|
f@0
|
141 @Override
|
f@0
|
142 public void update(SoundWaveEvent evt) {
|
f@0
|
143 String evtType = evt.getType();
|
f@0
|
144
|
f@0
|
145 if(SoundWaveEvent.POSITION_CHANGED.equals(evtType)){
|
f@0
|
146 SoundWave wave = evt.getSource();
|
f@0
|
147 int pos = (Integer)evt.getArgs();
|
f@0
|
148
|
f@0
|
149 if(pos < wave.getChunkNum()){
|
f@0
|
150 Chunk chunk = wave.getChunkAt(pos);
|
f@0
|
151 float chunkSize = chunk.getNormEnd() - chunk.getNormStart();
|
f@0
|
152 /* sends a normalized value of the chunk size */
|
f@0
|
153 haptics.sendMessage(Command.RENDER_VALUE, Float.toString(chunkSize) , chunk.hashCode());
|
f@0
|
154 }else{
|
f@0
|
155 /* if the scub goes past the audio wave just send wave amp = 0 */
|
f@0
|
156 haptics.sendMessage(Command.RENDER_VALUE, "0", 0);
|
f@0
|
157 }
|
f@0
|
158 }else if(SoundWaveEvent.CLOSE.equals(evtType)){
|
f@0
|
159 /* removes itself from listeners */
|
f@0
|
160 evt.getSource().removeSoundWaveListener(this);
|
f@0
|
161 }
|
f@0
|
162 }
|
f@0
|
163
|
f@0
|
164
|
f@0
|
165
|
f@0
|
166 }
|