f@0
|
1 /*
|
f@0
|
2 Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
|
f@0
|
3
|
f@0
|
4 Copyright (C) 2015 Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
|
f@0
|
5
|
f@0
|
6 This program is free software: you can redistribute it and/or modify
|
f@0
|
7 it under the terms of the GNU General Public License as published by
|
f@0
|
8 the Free Software Foundation, either version 3 of the License, or
|
f@0
|
9 (at your option) any later version.
|
f@0
|
10
|
f@0
|
11 This program is distributed in the hope that it will be useful,
|
f@0
|
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
f@0
|
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
f@0
|
14 GNU General Public License for more details.
|
f@0
|
15
|
f@0
|
16 You should have received a copy of the GNU General Public License
|
f@0
|
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
f@0
|
18 */
|
f@0
|
19 package uk.ac.qmul.eecs.depic.daw;
|
f@0
|
20
|
f@0
|
21 import java.util.ArrayList;
|
f@0
|
22 import java.util.List;
|
f@0
|
23
|
f@0
|
24 import uk.ac.qmul.eecs.depic.patterns.MathUtils;
|
f@0
|
25 import uk.ac.qmul.eecs.depic.patterns.Range;
|
f@0
|
26 import uk.ac.qmul.eecs.depic.patterns.Sequence;
|
f@0
|
27 import uk.ac.qmul.eecs.depic.patterns.SequenceListener;
|
f@0
|
28
|
f@1
|
29 /**
|
f@1
|
30 *
|
f@1
|
31 * A Wave that displays chucks in DB scale
|
f@1
|
32 *
|
f@1
|
33 */
|
f@0
|
34 public class DbWave implements Wave {
|
f@0
|
35 private Wave soundWave ;
|
f@0
|
36 private Sequence peakLevelSequence;
|
f@0
|
37
|
f@0
|
38 public DbWave(Wave soundWave){
|
f@0
|
39 this.soundWave = soundWave;
|
f@0
|
40 }
|
f@0
|
41
|
f@0
|
42 @Override
|
f@0
|
43 public int getChunkNum() {
|
f@0
|
44 return soundWave.getChunkNum();
|
f@0
|
45 }
|
f@0
|
46
|
f@0
|
47 @Override
|
f@0
|
48 public Chunk getChunkAt(int i) {
|
f@0
|
49 return new DBChunk(soundWave.getChunkAt(i));
|
f@0
|
50 }
|
f@0
|
51
|
f@0
|
52 @Override
|
f@0
|
53 public void setScaleFactor(int scaleFactor) {
|
f@0
|
54 soundWave.setScaleFactor(scaleFactor);
|
f@0
|
55 }
|
f@0
|
56
|
f@0
|
57 @Override
|
f@0
|
58 public int getScaleFactor() {
|
f@0
|
59 return soundWave.getScaleFactor();
|
f@0
|
60 }
|
f@0
|
61
|
f@0
|
62 @Override
|
f@0
|
63 public int getMaxScaleFactor() {
|
f@0
|
64 return soundWave.getMaxScaleFactor();
|
f@0
|
65 }
|
f@0
|
66
|
f@0
|
67 @Override
|
f@0
|
68 public float getWaveTime() {
|
f@0
|
69 return soundWave.getWaveTime();
|
f@0
|
70 }
|
f@0
|
71
|
f@0
|
72 @Override
|
f@0
|
73 public float getMillisecPerChunk() {
|
f@0
|
74 return soundWave.getMillisecPerChunk();
|
f@0
|
75 }
|
f@0
|
76
|
f@0
|
77 /**
|
f@0
|
78 * Returns a sequence representing the peak meter line, if any
|
f@0
|
79 *
|
f@0
|
80 * @return the peak level sequence or {@code null} if this wave doesn't have a sequence
|
f@0
|
81 */
|
f@0
|
82 @Override
|
f@0
|
83 public Sequence getSequence(){
|
f@0
|
84 return peakLevelSequence;
|
f@0
|
85 }
|
f@0
|
86
|
f@0
|
87 public void createNewSequence(){
|
f@0
|
88 peakLevelSequence = new PeakMeterSequence(this);
|
f@0
|
89 }
|
f@0
|
90
|
f@0
|
91 public void deleteSequence(){
|
f@0
|
92 peakLevelSequence = null;
|
f@0
|
93 }
|
f@0
|
94
|
f@0
|
95 @Override
|
f@0
|
96 public boolean hasSequence(){
|
f@0
|
97 return (peakLevelSequence != null);
|
f@0
|
98 }
|
f@0
|
99
|
f@0
|
100
|
f@0
|
101 private static class PeakMeterSequence implements Sequence {
|
f@0
|
102 private static final float TAN_THETA = 1/5000.0f;
|
f@0
|
103 float previousPeak;
|
f@0
|
104 float previousPeakTime;
|
f@0
|
105 Range<Float> range;
|
f@0
|
106 List<Sequence.Value> values;
|
f@0
|
107 private float len;
|
f@0
|
108
|
f@0
|
109 PeakMeterSequence(Wave w){
|
f@0
|
110 previousPeak = 0.0f;
|
f@0
|
111 previousPeakTime = 0.0f;
|
f@0
|
112 values = new ArrayList<>(w.getChunkNum()/5);
|
f@0
|
113 range = new Range<Float>(0.0f,1.0f);
|
f@0
|
114 len = w.getChunkNum()*w.getMillisecPerChunk();
|
f@0
|
115
|
f@0
|
116 for(int i=0; i<w.getChunkNum();i++){
|
f@0
|
117 Chunk c = w.getChunkAt(i);
|
f@0
|
118 final float time = i*w.getMillisecPerChunk();
|
f@0
|
119
|
f@0
|
120 /* decayTime is the time where the previous peak was, plus the time *
|
f@0
|
121 * that the peak line will take, to decay to zero */
|
f@0
|
122 float decayTime = previousPeakTime +(previousPeak / TAN_THETA);
|
f@0
|
123
|
f@0
|
124 float peakLineDecayPoint = 0.0f;
|
f@0
|
125 /* if the peak line has already decayed to zero in the past, *
|
f@0
|
126 * peakLineDecayPoint will just be left to 0 */
|
f@0
|
127 if(time < decayTime || MathUtils.equal(time, decayTime,w.getMillisecPerChunk()/2)){
|
f@0
|
128 /* timeLeftToZero is always > 0, otherwise would not be in this block */
|
f@0
|
129 float timeLeftToZero = decayTime - time;
|
f@0
|
130 /* returns the y value of the peak line at timePoistion */
|
f@0
|
131 peakLineDecayPoint = timeLeftToZero * TAN_THETA;
|
f@0
|
132 }
|
f@0
|
133
|
f@0
|
134 if(c.getNormEnd() > peakLineDecayPoint) {
|
f@0
|
135 final int index = values.size();
|
f@0
|
136 final float value = c.getNormEnd();
|
f@0
|
137
|
f@0
|
138 /* this chunk was higher than the peak line at time *
|
f@0
|
139 * position therefore it becomes the new peak value */
|
f@0
|
140 previousPeak = value;
|
f@0
|
141 previousPeakTime = time;
|
f@0
|
142
|
f@0
|
143 values.add(new Sequence.Value() {
|
f@0
|
144 @Override
|
f@0
|
145 public int index() {
|
f@0
|
146 return index;
|
f@0
|
147 }
|
f@0
|
148
|
f@0
|
149 @Override
|
f@0
|
150 public float getValue() {
|
f@0
|
151 return value;
|
f@0
|
152 }
|
f@0
|
153
|
f@0
|
154 @Override
|
f@0
|
155 public float getTimePosition() {
|
f@0
|
156 return time;
|
f@0
|
157 }
|
f@0
|
158
|
f@0
|
159 @Override
|
f@0
|
160 public Sequence getSequence() {
|
f@0
|
161 return PeakMeterSequence.this;
|
f@0
|
162 }
|
f@0
|
163
|
f@0
|
164 @Override
|
f@0
|
165 public String toString(){
|
f@0
|
166 return "Peak meter sequence value. Value:"+getValue()+" time pos:"+getTimePosition();
|
f@0
|
167 }
|
f@0
|
168 });
|
f@0
|
169 }else if( MathUtils.equal(time, decayTime, w.getMillisecPerChunk())){
|
f@0
|
170 previousPeak = 0.0f;
|
f@0
|
171 previousPeakTime = time;
|
f@0
|
172 final int index = values.size();
|
f@0
|
173 values.add(new Sequence.Value() {
|
f@0
|
174 @Override
|
f@0
|
175 public int index() {
|
f@0
|
176 return index;
|
f@0
|
177 }
|
f@0
|
178
|
f@0
|
179 @Override
|
f@0
|
180 public float getValue() {
|
f@0
|
181 return 0.0f;
|
f@0
|
182 }
|
f@0
|
183
|
f@0
|
184 @Override
|
f@0
|
185 public float getTimePosition() {
|
f@0
|
186 return time;
|
f@0
|
187 }
|
f@0
|
188
|
f@0
|
189 @Override
|
f@0
|
190 public Sequence getSequence() {
|
f@0
|
191 return PeakMeterSequence.this;
|
f@0
|
192 }
|
f@0
|
193
|
f@0
|
194 @Override
|
f@0
|
195 public String toString(){
|
f@0
|
196 return "Peak meter sequence value. Value:"+getValue()+" time pos:"+getTimePosition();
|
f@0
|
197 }
|
f@0
|
198 });
|
f@0
|
199 }
|
f@0
|
200 }
|
f@0
|
201 }
|
f@0
|
202
|
f@0
|
203 /**
|
f@0
|
204 * Calculates the point on the time line (x-axis) where the peak curve will be completely decayed
|
f@0
|
205 * to zero.
|
f@0
|
206 *
|
f@0
|
207 * @param time the time of the decay point
|
f@0
|
208 * @param previousPeak the previous peak value. that is where the line has started decaying
|
f@0
|
209 * @param previousPeakTime the time of the previous peak value
|
f@0
|
210 *
|
f@0
|
211 * @return
|
f@0
|
212 */
|
f@0
|
213 float calculateDecayLineAtTime(float time,float previousPeak, float previousPeakTime){
|
f@0
|
214 /* decayTime is the time where the previous peak was plus the time *
|
f@0
|
215 * that the peak line will take to decay to zero */
|
f@0
|
216 float decayTime = previousPeakTime +(previousPeak / TAN_THETA);
|
f@0
|
217
|
f@0
|
218 /* the peak line has already decayed to zero in the past */
|
f@0
|
219 if(time > decayTime){
|
f@0
|
220 return 0.0f;
|
f@0
|
221 }
|
f@0
|
222
|
f@0
|
223 /* always > 0, otherwise would have returned already */
|
f@0
|
224 float timeLeftToZero = decayTime - time;
|
f@0
|
225 /* returns the y value of the peak line at timePoistion */
|
f@0
|
226 return timeLeftToZero * TAN_THETA;
|
f@0
|
227 }
|
f@0
|
228
|
f@0
|
229 @Override
|
f@0
|
230 public float getBegin() {
|
f@0
|
231 return 0.0f;
|
f@0
|
232 }
|
f@0
|
233
|
f@0
|
234 @Override
|
f@0
|
235 public float getEnd() {
|
f@0
|
236 return values.isEmpty() ? getBegin() : values.get(values.size()-1).getValue();
|
f@0
|
237 }
|
f@0
|
238
|
f@0
|
239 @Override
|
f@0
|
240 public int getValuesNum() {
|
f@0
|
241 return values.size();
|
f@0
|
242 }
|
f@0
|
243
|
f@0
|
244 @Override
|
f@0
|
245 public Value getValueAt(int index) {
|
f@0
|
246 return values.get(index);
|
f@0
|
247 }
|
f@0
|
248
|
f@0
|
249 @Override
|
f@0
|
250 public Range<Float> getRange() {
|
f@0
|
251 return range;
|
f@0
|
252 }
|
f@0
|
253
|
f@0
|
254 @Override
|
f@0
|
255 public void addSequenceListener(SequenceListener l) {
|
f@0
|
256 // this sequence is immutable, therefore listeners would never be notfied
|
f@0
|
257 }
|
f@0
|
258
|
f@0
|
259 @Override
|
f@0
|
260 public void removeSequenceListener(SequenceListener l) {
|
f@0
|
261 // this sequence is immutable, therefore listeners would never be notfied
|
f@0
|
262 }
|
f@0
|
263
|
f@0
|
264 @Override
|
f@0
|
265 public float getLen() {
|
f@0
|
266 return len;
|
f@0
|
267 }
|
f@0
|
268
|
f@0
|
269 }
|
f@0
|
270
|
f@0
|
271 }
|
f@0
|
272
|
f@0
|
273 class DBChunk extends Chunk {
|
f@0
|
274 float normStart;
|
f@0
|
275 float normEnd;
|
f@0
|
276
|
f@0
|
277 DBChunk(Chunk c) {
|
f@0
|
278 super((short)0,(short)0);
|
f@0
|
279 start = MathUtils.toDb(Math.abs(c.getStart()));
|
f@0
|
280 if(Float.isInfinite(start)){
|
f@0
|
281 start = 0.0f;
|
f@0
|
282 }
|
f@0
|
283 end = MathUtils.toDb(Math.abs(c.getEnd()));
|
f@0
|
284 if(Float.isInfinite(end)){
|
f@0
|
285 end = 0.0f;
|
f@0
|
286 }
|
f@0
|
287
|
f@0
|
288 normStart = 0.0f;
|
f@0
|
289
|
f@0
|
290 float max = Math.max(Math.abs(c.getStart()),Math.abs(c.getEnd()));
|
f@0
|
291 float db = MathUtils.toDb(max);
|
f@0
|
292 if(Float.isInfinite(db)){
|
f@0
|
293 normEnd = 0;
|
f@0
|
294 }else if(db < -60){
|
f@0
|
295 normEnd = 0;
|
f@0
|
296 }else{
|
f@0
|
297 normEnd = new MathUtils.Scale(-60.0f, 0.0f, 0.0f, 1.0f).linear(db);
|
f@0
|
298 }
|
f@0
|
299 }
|
f@0
|
300
|
f@0
|
301 @Override
|
f@0
|
302 public float getNormStart() {
|
f@0
|
303 return normStart;
|
f@0
|
304 }
|
f@0
|
305
|
f@0
|
306 @Override
|
f@0
|
307 public float getNormEnd() {
|
f@0
|
308 return normEnd;
|
f@0
|
309 }
|
f@0
|
310
|
f@0
|
311 }
|
f@0
|
312
|