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.gui;
|
f@0
|
20
|
f@0
|
21 import java.awt.Color;
|
f@0
|
22 import java.awt.Dimension;
|
f@0
|
23 import java.awt.Graphics;
|
f@0
|
24 import java.awt.Graphics2D;
|
f@0
|
25 import java.awt.Point;
|
f@0
|
26 import java.awt.Rectangle;
|
f@0
|
27 import java.awt.event.ActionEvent;
|
f@0
|
28 import java.awt.event.ActionListener;
|
f@0
|
29 import java.awt.geom.Line2D;
|
f@0
|
30 import java.awt.geom.Point2D;
|
f@0
|
31
|
f@0
|
32 import javax.swing.JPanel;
|
f@0
|
33 import javax.swing.Timer;
|
f@0
|
34 import javax.swing.event.ChangeEvent;
|
f@0
|
35 import javax.swing.event.ChangeListener;
|
f@0
|
36
|
f@0
|
37 import uk.ac.qmul.eecs.depic.daw.Automation;
|
f@0
|
38 import uk.ac.qmul.eecs.depic.daw.Chunk;
|
f@0
|
39 import uk.ac.qmul.eecs.depic.daw.Daw;
|
f@0
|
40 import uk.ac.qmul.eecs.depic.daw.Selection;
|
f@0
|
41 import uk.ac.qmul.eecs.depic.daw.SoundType;
|
f@0
|
42 import uk.ac.qmul.eecs.depic.daw.SoundWave;
|
f@0
|
43 import uk.ac.qmul.eecs.depic.daw.SoundWaveEvent;
|
f@0
|
44 import uk.ac.qmul.eecs.depic.daw.SoundWaveListener;
|
f@0
|
45 import uk.ac.qmul.eecs.depic.daw.Wave;
|
f@0
|
46 import uk.ac.qmul.eecs.depic.daw.haptics.HapticViewPort;
|
f@0
|
47 import uk.ac.qmul.eecs.depic.patterns.Sequence;
|
f@0
|
48 import uk.ac.qmul.eecs.depic.patterns.SequenceMapping;
|
f@0
|
49
|
f@0
|
50 /**
|
f@1
|
51 * An audio track widget.
|
f@0
|
52 *
|
f@0
|
53 *
|
f@0
|
54 *
|
f@0
|
55 * This class has the following bound properties :
|
f@0
|
56 * <ul>
|
f@0
|
57 * <li><b>scaleFactor:</b> the scale factor of each pixel of the sound wave representation. The bigger the
|
f@0
|
58 * scale factor, the lower the resolution. a scale factor ranges from 1, highest resolution supported by
|
f@0
|
59 * the {@code SoundWave} instance, to the max scale factor supported by the backing {@code SoundWave} model - {@code int}</li>
|
f@0
|
60 * <li><b>cursorPos:</b> the position of the cursor - {@code int} </li>
|
f@0
|
61 * <li><b>mouseDragSelection:</b> the range of the selection on this track as the user drags the mouse - {@code SelectionRange} </li>
|
f@0
|
62 * <li><b><preferredSize:</b> the preferred size - {@code Dimension}
|
f@0
|
63 * </ul>
|
f@0
|
64 *
|
f@0
|
65 *
|
f@0
|
66 *
|
f@0
|
67 */
|
f@0
|
68 public class AudioTrack extends JPanel implements SoundWaveListener {
|
f@0
|
69 private static final long serialVersionUID = 1L;
|
f@0
|
70
|
f@0
|
71 public static final int MAX_TRACK_HEIGHT = 130;
|
f@0
|
72 public static final int MAX_TRACK_WIDTH = Integer.MAX_VALUE;
|
f@0
|
73
|
f@0
|
74 public static final Color WAVE_COLOR = new Color(7,47,140);
|
f@0
|
75 public static final Color CURSOR_COLOR = new Color(44,47,56);
|
f@0
|
76 public static final Color SELECTION_COLOR = new Color(218,218,240);
|
f@0
|
77 public static final Color OVERLAY_BG_COLOR = new Color(222,222,235);
|
f@0
|
78 public static final Color VIEW_PORT_COLOR = Color.GREEN;
|
f@0
|
79
|
f@0
|
80 /* these fields are used by friend classes *
|
f@0
|
81 * they're final to protect them from being changed */
|
f@0
|
82 final AudioTrackInput trackInteraction;
|
f@0
|
83 final AudioTrackSonification trackSonification;
|
f@0
|
84
|
f@0
|
85 private SequenceGraph automationGraph;
|
f@0
|
86 private SequenceGraph peakMeterGraph;
|
f@0
|
87 private final CursorUpdaterOnPlay clipInteraction;
|
f@0
|
88 protected SoundWave soundWave;
|
f@0
|
89
|
f@0
|
90 private HapticViewPort hapticViewPort;
|
f@0
|
91
|
f@0
|
92 private Selection currentSelection;
|
f@0
|
93 private int scaleFactor; // bound property
|
f@0
|
94 private int cursorPos; // bound property
|
f@0
|
95 private float secondsPerPixel;
|
f@0
|
96 private boolean showHapticViewPort;
|
f@0
|
97 boolean showAutomationSound;
|
f@0
|
98 boolean showPeakLevelSound;
|
f@0
|
99 private boolean showDbWave;
|
f@0
|
100
|
f@0
|
101 public AudioTrack(SoundWave soundWave){
|
f@0
|
102 if(soundWave == null)
|
f@0
|
103 throw new IllegalArgumentException("soundWave cannot be null");
|
f@0
|
104
|
f@0
|
105 this.soundWave = soundWave;
|
f@0
|
106 soundWave.addSoundWaveListener(this);
|
f@0
|
107
|
f@0
|
108
|
f@0
|
109 setBackground(Color.WHITE);
|
f@0
|
110
|
f@0
|
111 scaleFactor = 1;
|
f@0
|
112 currentSelection = new Selection(0,scaleFactor);
|
f@0
|
113
|
f@0
|
114 /* set up sequence graphs */
|
f@0
|
115 ChangeListener sequenceGraphListener = new ChangeListener(){
|
f@0
|
116 @Override
|
f@0
|
117 public void stateChanged(ChangeEvent e) {
|
f@0
|
118 repaint();
|
f@0
|
119 }
|
f@0
|
120 };
|
f@0
|
121
|
f@0
|
122 automationGraph = new SequenceGraph(Color.WHITE, getSize());
|
f@0
|
123 automationGraph.addChangeListener(sequenceGraphListener);
|
f@0
|
124 peakMeterGraph = new SequenceGraph(Color.GRAY, getSize());
|
f@0
|
125 peakMeterGraph.addChangeListener(sequenceGraphListener);
|
f@0
|
126
|
f@0
|
127 clipInteraction = new CursorUpdaterOnPlay();
|
f@0
|
128 trackInteraction = new AudioTrackInput(this);
|
f@0
|
129 trackSonification = new AudioTrackSonification(this);
|
f@0
|
130 hapticViewPort = new HapticViewPort(1);
|
f@0
|
131
|
f@0
|
132 setMaximumSize(new Dimension(MAX_TRACK_WIDTH,MAX_TRACK_HEIGHT));
|
f@0
|
133 //setMinimumSize(new Dimension(100,TRACK_HEIGHT));
|
f@0
|
134 setPreferredSize(new Dimension(0,MAX_TRACK_HEIGHT));
|
f@0
|
135
|
f@0
|
136 this.setFocusable(true);
|
f@0
|
137
|
f@0
|
138 addPropertyChangeListener(trackSonification);
|
f@0
|
139 addMouseListener(trackInteraction);
|
f@0
|
140 addMouseMotionListener(trackInteraction);
|
f@0
|
141 addKeyListener(trackInteraction);
|
f@0
|
142 }
|
f@0
|
143
|
f@0
|
144 @Override
|
f@0
|
145 public void update(SoundWaveEvent evt) { // FIXME fare file closed
|
f@0
|
146 String evtType = evt.getType();
|
f@0
|
147 if(SoundWaveEvent.OPEN.equals(evtType)) {
|
f@0
|
148 SoundWave wave = evt.getSource();
|
f@0
|
149 hapticViewPort.setTrackSize(wave.getChunkNum()); // FIXME check
|
f@0
|
150 _setScaleFactor(wave.getScaleFactor());
|
f@0
|
151 setCursorPos(0);
|
f@0
|
152 } else if(SoundWaveEvent.SCALE_FACTOR_CHANGED.equals(evtType)){
|
f@0
|
153 _setScaleFactor((Integer)evt.getArgs());
|
f@0
|
154 }else if (SoundWaveEvent.SELECTION_CHANGED.equals(evtType)){
|
f@0
|
155 Selection oldSelection = currentSelection;
|
f@0
|
156 currentSelection = (Selection)evt.getArgs();
|
f@0
|
157 /* update the mouse interaction object accordingly */
|
f@0
|
158 trackInteraction.setMouseSelection(currentSelection.getStart(), currentSelection.getEnd());
|
f@0
|
159 firePropertyChange("mouseDragSelection",oldSelection,currentSelection);
|
f@0
|
160 repaint();
|
f@0
|
161
|
f@0
|
162 }else if(SoundWaveEvent.POSITION_CHANGED.equals(evtType)){
|
f@0
|
163 /* update the position according to the Selection object returned by evt.getArgs() *
|
f@0
|
164 * the selection is open. The Selection's scale factor is also taken into account *
|
f@0
|
165 * when calculating the new position. In particular the ratio between the *
|
f@0
|
166 * selction's scale factor and this object current scale factor */
|
f@0
|
167 setCursorPos((Integer)evt.getArgs());
|
f@0
|
168
|
f@0
|
169
|
f@0
|
170 }else if(SoundWaveEvent.START.equals(evtType) ||
|
f@0
|
171 SoundWaveEvent.STOP.equals(evtType) ||
|
f@0
|
172 SoundWaveEvent.PAUSE.equals(evtType)){
|
f@0
|
173 clipInteraction.update(evt);
|
f@0
|
174 }else if (SoundWaveEvent.AUTOMATION_CHANGED.equals(evtType)){
|
f@0
|
175 Automation automation = (Automation)evt.getArgs();
|
f@0
|
176
|
f@0
|
177 /* if it's an automation different from NONE, paint the overlay: make the bg darker */
|
f@0
|
178 if(Automation.NONE_AUTOMATION.equals(automation)){
|
f@0
|
179 setBackground(Color.WHITE);
|
f@0
|
180 automationGraph.removeChangeListener(trackSonification);
|
f@0
|
181 }else{
|
f@0
|
182 setBackground(OVERLAY_BG_COLOR);
|
f@0
|
183 }
|
f@0
|
184
|
f@0
|
185 /* resize automation graph, if currently showing any */
|
f@0
|
186 automationGraph.setMillisecPerPixel(evt.getSource().getMillisecPerChunk());
|
f@0
|
187 automationGraph.setColor(automation.getColor());
|
f@0
|
188 automationGraph.setSize(getSize());
|
f@0
|
189 automationGraph.setSequence(automation);
|
f@0
|
190 automationGraph.addChangeListener(trackSonification);
|
f@0
|
191
|
f@0
|
192 }else if(SoundWaveEvent.CLOSE.equals(evtType) ||
|
f@0
|
193 SoundWaveEvent.CUT.equals(evtType) ||
|
f@0
|
194 SoundWaveEvent.PASTE.equals(evtType) ||
|
f@0
|
195 SoundWaveEvent.INSERT.equals(evtType)){
|
f@0
|
196 hapticViewPort.setTrackSize(evt.getSource().getChunkNum());
|
f@0
|
197 repaint();
|
f@0
|
198 }else if(SoundWaveEvent.PEAK_METER.equals(evtType)){
|
f@0
|
199
|
f@0
|
200 peakMeterGraph.setMillisecPerPixel(evt.getSource().getMillisecPerChunk());
|
f@0
|
201 peakMeterGraph.setSize(getSize());
|
f@0
|
202 /* listens to the changes, so it will repaint after setSequence */
|
f@0
|
203 peakMeterGraph.setSequence((Sequence)evt.getArgs());
|
f@0
|
204
|
f@0
|
205 }
|
f@0
|
206 }
|
f@0
|
207
|
f@0
|
208 @Override
|
f@0
|
209 public void paintComponent(Graphics g){
|
f@0
|
210 super.paintComponent(g);
|
f@0
|
211 Graphics2D g2 = (Graphics2D)g;
|
f@0
|
212 Color oldColor = g2.getColor();
|
f@0
|
213
|
f@0
|
214 /* get height and width. needed for painting */
|
f@0
|
215 int height = getHeight();
|
f@0
|
216 int width = getWidth();
|
f@0
|
217
|
f@0
|
218 /* paint the selection grey background*/
|
f@0
|
219 Selection mouseSelection = trackInteraction.getMouseSelection();
|
f@0
|
220 if(!mouseSelection.isOpen()){
|
f@0
|
221 g2.setColor(SELECTION_COLOR);
|
f@0
|
222 g2.fillRect(mouseSelection.getStart(), 0, Math.abs(mouseSelection.getStart()-mouseSelection.getEnd()), height);
|
f@0
|
223 }
|
f@0
|
224
|
f@0
|
225 /* paint the central line */
|
f@0
|
226 g2.setColor(CURSOR_COLOR);
|
f@0
|
227 g2.draw(new Line2D.Float(0f, height/2,width,height/2));
|
f@0
|
228
|
f@0
|
229 /* paint the sound wave, if any */
|
f@0
|
230 if(soundWave != null ){
|
f@0
|
231 Wave wave = showDbWave ? soundWave.getDbWave() : soundWave;
|
f@0
|
232 g2.setColor(WAVE_COLOR);
|
f@0
|
233 int horizontalPixel = 0;
|
f@0
|
234 for(int i=0; i<wave.getChunkNum(); i++){
|
f@0
|
235 Chunk chunk = wave.getChunkAt(i);
|
f@0
|
236 g2.draw(new Line2D.Float(
|
f@0
|
237 horizontalPixel,
|
f@0
|
238 normToHeightConvert(chunk.getNormStart()),
|
f@0
|
239 horizontalPixel,
|
f@0
|
240 normToHeightConvert(chunk.getNormEnd())));
|
f@0
|
241 horizontalPixel++;
|
f@0
|
242 }
|
f@0
|
243 }
|
f@0
|
244
|
f@0
|
245 paintHapticViewPort(g2, width, height);
|
f@0
|
246 /* paint the sequence graphs, if any */
|
f@0
|
247 automationGraph.draw(g);
|
f@0
|
248 peakMeterGraph.draw(g);
|
f@0
|
249
|
f@0
|
250
|
f@0
|
251 g2.setColor(CURSOR_COLOR);
|
f@0
|
252 /* draw cursor */
|
f@0
|
253 g2.drawLine(cursorPos, 0, cursorPos, height);
|
f@0
|
254
|
f@0
|
255 if(mouseSelection.isOpen()){
|
f@0
|
256 /* paints one line as the selection has no length */
|
f@0
|
257 g2.drawLine(mouseSelection.getStart(), 0, mouseSelection.getStart(), height);
|
f@0
|
258 }else{
|
f@0
|
259 /* paint the selection left and right boundaries */
|
f@0
|
260 /* draw the boundaries of the selection with CURSOR_COLOR */
|
f@0
|
261 g2.drawLine(mouseSelection.getStart(), 0, mouseSelection.getStart(), height);
|
f@0
|
262 g2.drawLine(mouseSelection.getEnd(), 0, mouseSelection.getEnd(), height);
|
f@0
|
263 }
|
f@0
|
264 g2.setColor(oldColor);
|
f@0
|
265 }
|
f@0
|
266
|
f@0
|
267 protected void paintHapticViewPort(Graphics g2, int width, int height){
|
f@0
|
268 if(!showHapticViewPort)
|
f@0
|
269 return;
|
f@0
|
270
|
f@0
|
271 Color oldColor = g2.getColor();
|
f@0
|
272 g2.setColor(VIEW_PORT_COLOR);
|
f@0
|
273
|
f@0
|
274 int leftBound = hapticViewPort.getPosition(0.0f);
|
f@0
|
275 if(leftBound == 0) // make it visible if it's at 0
|
f@0
|
276 leftBound = 1;
|
f@0
|
277
|
f@0
|
278 g2.drawLine(leftBound-1, height/4, leftBound-1, height*3/4);
|
f@0
|
279 g2.drawLine(leftBound, 0, leftBound, height); // little bar on the left to give the idea of direction
|
f@0
|
280 int rightBound = hapticViewPort.getPosition(1.0f);
|
f@0
|
281 g2.drawLine(rightBound, 0, rightBound, height);// little bar on the right to give the idea of direction
|
f@0
|
282 g2.drawLine(rightBound+1, height/4, rightBound+1, height*3/4);
|
f@0
|
283 g2.setColor(oldColor);
|
f@0
|
284 }
|
f@0
|
285
|
f@0
|
286 public SoundWave getSoundWave(){
|
f@0
|
287 return soundWave;
|
f@0
|
288 }
|
f@0
|
289
|
f@0
|
290 public int getScaleFactor() {
|
f@0
|
291 return scaleFactor;
|
f@0
|
292 }
|
f@0
|
293
|
f@0
|
294 public void setScaleFactor(int factor) {
|
f@0
|
295 /* sets the scale factor in the SoundWave to scaleFactor *
|
f@0
|
296 * _setScaleFactor will be called right after as a SoundWaveEvent *
|
f@0
|
297 * will be triggered by SoundWave. see update() */
|
f@0
|
298 soundWave.setScaleFactor(factor);
|
f@0
|
299 }
|
f@0
|
300
|
f@0
|
301 /* implements scaleFactor body. It's called by update() after the model's scale factor
|
f@0
|
302 * gets changed */
|
f@0
|
303 private void _setScaleFactor(int factor) {
|
f@0
|
304 if(factor < 1 || factor > getSoundWave().getMaxScaleFactor() ){
|
f@0
|
305 Daw.getSoundEngineFactory().getSharedSonification().play(SoundType.ERROR);
|
f@0
|
306 return;
|
f@0
|
307 }
|
f@0
|
308
|
f@0
|
309 int oldScaleFactor = scaleFactor;
|
f@0
|
310 scaleFactor = factor;
|
f@0
|
311
|
f@0
|
312 secondsPerPixel = soundWave.getMillisecPerChunk()/1000;
|
f@0
|
313
|
f@0
|
314 /* update haptic view port */
|
f@0
|
315 hapticViewPort.setScaleFactor(scaleFactor);
|
f@0
|
316
|
f@0
|
317 /* resize the selection after zooming in/out */
|
f@0
|
318 Selection oldSelection = currentSelection;
|
f@0
|
319 int start = (int) (oldSelection.getStart() * Math.pow(2,oldScaleFactor - factor));
|
f@0
|
320 if(oldSelection.isOpen())
|
f@0
|
321 currentSelection = new Selection(start,factor);
|
f@0
|
322 else{
|
f@0
|
323 int end = (int) (oldSelection.getEnd() * Math.pow(2,oldScaleFactor - factor));
|
f@0
|
324 currentSelection = new Selection(start,end,factor);
|
f@0
|
325 }
|
f@0
|
326
|
f@0
|
327 /* if the selection is open, it will be painted as a one-pixel selection */
|
f@0
|
328 trackInteraction.setMouseSelection(currentSelection.getStart(), currentSelection.getEnd());
|
f@0
|
329
|
f@0
|
330 /* make listeners aware the mouse selection has changed */
|
f@0
|
331 firePropertyChange("mouseDragSelection",oldSelection,currentSelection);
|
f@0
|
332
|
f@0
|
333 setPreferredSize(new Dimension(soundWave.getChunkNum()+30,MAX_TRACK_HEIGHT));
|
f@0
|
334 revalidate();
|
f@0
|
335
|
f@0
|
336 setCursorPos(soundWave.getCurrentChunkPosition());
|
f@0
|
337 /* update the sequences */
|
f@0
|
338 automationGraph.setSize(getSize());
|
f@0
|
339 automationGraph.setMillisecPerPixel(AudioTrack.this.soundWave.getMillisecPerChunk());
|
f@0
|
340 automationGraph.updateSequence();
|
f@0
|
341
|
f@0
|
342 peakMeterGraph.setSize(getSize());
|
f@0
|
343 peakMeterGraph.setMillisecPerPixel(AudioTrack.this.soundWave.getMillisecPerChunk());
|
f@0
|
344 peakMeterGraph.updateSequence();
|
f@0
|
345
|
f@0
|
346 repaint();
|
f@0
|
347 firePropertyChange("scaleFactor", oldScaleFactor, scaleFactor);
|
f@0
|
348 }
|
f@0
|
349
|
f@0
|
350 public Selection getSelection(){
|
f@0
|
351 return currentSelection;
|
f@0
|
352 }
|
f@0
|
353
|
f@0
|
354 /**
|
f@0
|
355 * Doesn't change the cursor position of the underlying soundwave
|
f@0
|
356 * @param position
|
f@0
|
357 */
|
f@0
|
358 public void setCursorPos(int position) {
|
f@0
|
359 int oldCursorPos = cursorPos;
|
f@0
|
360 cursorPos = position;
|
f@0
|
361
|
f@0
|
362 repaint();
|
f@0
|
363 scrollRectToVisible(new Rectangle(new Point(position,0)));
|
f@0
|
364 firePropertyChange("cursorPos", oldCursorPos, position);
|
f@0
|
365 }
|
f@0
|
366
|
f@0
|
367 public int getCursorPos(){
|
f@0
|
368 return cursorPos;
|
f@0
|
369 }
|
f@0
|
370
|
f@0
|
371 public float getSecondsPerPixel(){
|
f@0
|
372 return secondsPerPixel;
|
f@0
|
373 }
|
f@0
|
374
|
f@0
|
375 public SequenceGraph getAutomationGraph(){
|
f@0
|
376 return automationGraph;
|
f@0
|
377 }
|
f@0
|
378
|
f@0
|
379 public SequenceGraph getPeakLevelGraph(){
|
f@0
|
380 return peakMeterGraph;
|
f@0
|
381 }
|
f@0
|
382
|
f@0
|
383 /**
|
f@0
|
384 * Makes it available to {@code Rule} class to notify listeners after changing
|
f@0
|
385 * the mouseDragSelection.
|
f@0
|
386 *
|
f@0
|
387 * @param property the programmatic name of the property that was changed
|
f@0
|
388 * @param oldValue the old value of the property
|
f@0
|
389 * @param newValue the new value of the property
|
f@0
|
390 */
|
f@0
|
391 @Override
|
f@0
|
392 protected void firePropertyChange(String property, Object oldValue, Object newValue){
|
f@0
|
393 super.firePropertyChange(property, oldValue, newValue);
|
f@0
|
394 }
|
f@0
|
395
|
f@0
|
396 public HapticViewPort getHapticViewPort(){
|
f@0
|
397 return hapticViewPort;
|
f@0
|
398 }
|
f@0
|
399
|
f@0
|
400 public void showHapticViewPort(boolean show){
|
f@0
|
401 showHapticViewPort = show;
|
f@0
|
402 repaint();
|
f@0
|
403 }
|
f@0
|
404
|
f@0
|
405 public void showAutomationSound(boolean show){
|
f@0
|
406 showAutomationSound = show;
|
f@0
|
407
|
f@0
|
408 SequenceMapping sonificationSeqMapping = Daw.getSoundEngineFactory().getSharedSonification()
|
f@0
|
409 .getSequenceMapping(SoundType.AUTOMATION);
|
f@0
|
410 if(show){
|
f@0
|
411 sonificationSeqMapping.renderCurveAt(
|
f@0
|
412 soundWave.getSequence(),
|
f@0
|
413 soundWave.getMillisecPerChunk() * cursorPos,
|
f@0
|
414 SequenceMapping.DURATION_INF);
|
f@0
|
415 }else{
|
f@0
|
416 sonificationSeqMapping.renderCurveAt(null, 0.0f, SequenceMapping.DURATION_STOP);
|
f@0
|
417 }
|
f@0
|
418
|
f@0
|
419 }
|
f@0
|
420
|
f@0
|
421 public void showPeakLevelSound(boolean show){
|
f@0
|
422 showPeakLevelSound = show;
|
f@0
|
423
|
f@0
|
424 SequenceMapping sonificationSeqMapping = Daw.getSoundEngineFactory().getSharedSonification()
|
f@0
|
425 .getSequenceMapping(SoundType.PEAK_LEVEL);
|
f@0
|
426 if(show){
|
f@0
|
427 sonificationSeqMapping.renderCurveAt(
|
f@0
|
428 soundWave.getDbWave().getSequence(),
|
f@0
|
429 soundWave.getMillisecPerChunk() * cursorPos,
|
f@0
|
430 SequenceMapping.DURATION_INF);
|
f@0
|
431 }else{
|
f@0
|
432 sonificationSeqMapping.renderCurveAt(null, 0.0f, SequenceMapping.DURATION_STOP);
|
f@0
|
433 }
|
f@0
|
434 }
|
f@0
|
435
|
f@0
|
436 public void showDbWave(boolean show){
|
f@0
|
437 showDbWave = show;
|
f@0
|
438 repaint();
|
f@0
|
439 }
|
f@0
|
440
|
f@0
|
441
|
f@0
|
442 public int normToWidthconvert(float f){
|
f@0
|
443 return hapticViewPort.getPosition(f);
|
f@0
|
444 }
|
f@0
|
445 /**
|
f@0
|
446 *
|
f@0
|
447 * @param s a value sample value ranging from Short.MIN_VALUE to Short.MAX_VALUE
|
f@0
|
448 * @param height the height of the graphic component where the track is to be drawn
|
f@0
|
449 * @return the Y value in the graphic component resulted from mapping the sample to the graphic component height
|
f@0
|
450 */
|
f@0
|
451 public int normToHeightConvert(float f){
|
f@0
|
452 /* in a JPanel pixel y coordinate increases from top to bottom, whereas the *
|
f@0
|
453 * in the signed short PCM format y value increased from bottom to top *
|
f@0
|
454 * so the sign of the value is reversed to match it with the JPanel space */
|
f@0
|
455 int height = getHeight();
|
f@0
|
456 return height - (int)( f *height);
|
f@0
|
457 }
|
f@0
|
458
|
f@0
|
459 /**
|
f@0
|
460 * Returns a point in the {@code Automation} coordinates - x = length in millis,
|
f@0
|
461 * y = range of the automation - corresponding to the point on {@code track}
|
f@0
|
462 * which has coordinates {@code (mouseX, mouseY) }
|
f@0
|
463 *
|
f@0
|
464 *
|
f@0
|
465 * @param track
|
f@0
|
466 * @param mouseX the {@code x} coordinate of the point on {@code track}
|
f@0
|
467 * @param mouseY the {@code y} coordinate of the point on {@code track}
|
f@0
|
468 * @return the point in the {@code Automation} coordinates corresponding to (mouseX,mouseY)
|
f@0
|
469 * or {@code null} if mouseX is bigger than the automation length (in chunks of the track's {@code SoundWave})
|
f@0
|
470 * or mouseY is bigger than the track's height, or if either is lower than zero.
|
f@0
|
471 *
|
f@0
|
472 */
|
f@0
|
473 public static Point2D.Float getAutomationCoord(AudioTrack track, int mouseX, int mouseY ){
|
f@0
|
474 SoundWave wave = track.getSoundWave();
|
f@0
|
475 Automation autom = wave.getParametersControl().getCurrentAutomation();
|
f@0
|
476
|
f@0
|
477 /* get height and width of the track panel */
|
f@0
|
478 float height = track.getSize().height;
|
f@0
|
479
|
f@0
|
480 float width = track.getSoundWave().getChunkNum();
|
f@0
|
481
|
f@0
|
482 if(mouseX >= width || mouseY > height || mouseX < 0 || mouseY < 0 ){
|
f@0
|
483 return null;
|
f@0
|
484 }
|
f@0
|
485
|
f@0
|
486 /* calculate the automation x (a.k.a. position over time) from *
|
f@0
|
487 * the mouse click according following relation: *
|
f@0
|
488 * mouse x / width of the panel = automation x position / length of sample */
|
f@0
|
489 float automationX = (float)(autom.getLen()*(mouseX/width));
|
f@0
|
490 /* calculate the automation y (a.k.a. value int he parameter range) from *
|
f@0
|
491 * the mouse click according following relation: *
|
f@0
|
492 * mouse y / height of the panel = automation y position / value range *
|
f@0
|
493 * since in swing y has 0 at the top the y value is reversed height-mouseY */
|
f@0
|
494 float automationY = (float)(autom.getRange().lenght() *((height-mouseY)/height));
|
f@0
|
495 return new Point2D.Float(automationX,automationY+autom.getRange().getStart());
|
f@0
|
496 }
|
f@0
|
497
|
f@0
|
498
|
f@0
|
499
|
f@0
|
500 private class CursorUpdaterOnPlay implements ActionListener {
|
f@0
|
501 private static final int REFRESH_DELAY = 50;
|
f@0
|
502 Timer timer = new Timer(REFRESH_DELAY,this);;
|
f@0
|
503
|
f@0
|
504 public void update(SoundWaveEvent evt) {
|
f@0
|
505 if(SoundWaveEvent.START.equals(evt.getType())){
|
f@0
|
506 timer.start();
|
f@0
|
507 }else if(SoundWaveEvent.STOP.equals(evt.getType()) ||
|
f@0
|
508 SoundWaveEvent.PAUSE.equals(evt.getType())){
|
f@0
|
509 timer.stop();
|
f@0
|
510 }
|
f@0
|
511 }
|
f@0
|
512
|
f@0
|
513 @Override
|
f@0
|
514 public void actionPerformed(ActionEvent e) {
|
f@0
|
515 int cursorPos = Math.round(soundWave.getTransportControl().getPlayPosition()/soundWave.getMillisecPerChunk());
|
f@0
|
516 setCursorPos(cursorPos);
|
f@0
|
517 }
|
f@0
|
518 } // class ClipInteraction
|
f@0
|
519
|
f@0
|
520 }
|