Mercurial > hg > cmdp
view src/uk/ac/qmul/eecs/depic/daw/DbWave.java @ 4:473da40f3d39 tip
added html formatting to Daw/package-info.java
author | Fiore Martin <f.martin@qmul.ac.uk> |
---|---|
date | Thu, 25 Feb 2016 17:50:09 +0000 |
parents | 629262395647 |
children |
line wrap: on
line source
/* Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation. Copyright (C) 2015 Queen Mary University of London (http://depic.eecs.qmul.ac.uk/) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package uk.ac.qmul.eecs.depic.daw; import java.util.ArrayList; import java.util.List; import uk.ac.qmul.eecs.depic.patterns.MathUtils; import uk.ac.qmul.eecs.depic.patterns.Range; import uk.ac.qmul.eecs.depic.patterns.Sequence; import uk.ac.qmul.eecs.depic.patterns.SequenceListener; /** * * A Wave that displays chucks in DB scale * */ public class DbWave implements Wave { private Wave soundWave ; private Sequence peakLevelSequence; public DbWave(Wave soundWave){ this.soundWave = soundWave; } @Override public int getChunkNum() { return soundWave.getChunkNum(); } @Override public Chunk getChunkAt(int i) { return new DBChunk(soundWave.getChunkAt(i)); } @Override public void setScaleFactor(int scaleFactor) { soundWave.setScaleFactor(scaleFactor); } @Override public int getScaleFactor() { return soundWave.getScaleFactor(); } @Override public int getMaxScaleFactor() { return soundWave.getMaxScaleFactor(); } @Override public float getWaveTime() { return soundWave.getWaveTime(); } @Override public float getMillisecPerChunk() { return soundWave.getMillisecPerChunk(); } /** * Returns a sequence representing the peak meter line, if any * * @return the peak level sequence or {@code null} if this wave doesn't have a sequence */ @Override public Sequence getSequence(){ return peakLevelSequence; } public void createNewSequence(){ peakLevelSequence = new PeakMeterSequence(this); } public void deleteSequence(){ peakLevelSequence = null; } @Override public boolean hasSequence(){ return (peakLevelSequence != null); } private static class PeakMeterSequence implements Sequence { private static final float TAN_THETA = 1/5000.0f; float previousPeak; float previousPeakTime; Range<Float> range; List<Sequence.Value> values; private float len; PeakMeterSequence(Wave w){ previousPeak = 0.0f; previousPeakTime = 0.0f; values = new ArrayList<>(w.getChunkNum()/5); range = new Range<Float>(0.0f,1.0f); len = w.getChunkNum()*w.getMillisecPerChunk(); for(int i=0; i<w.getChunkNum();i++){ Chunk c = w.getChunkAt(i); final float time = i*w.getMillisecPerChunk(); /* decayTime is the time where the previous peak was, plus the time * * that the peak line will take, to decay to zero */ float decayTime = previousPeakTime +(previousPeak / TAN_THETA); float peakLineDecayPoint = 0.0f; /* if the peak line has already decayed to zero in the past, * * peakLineDecayPoint will just be left to 0 */ if(time < decayTime || MathUtils.equal(time, decayTime,w.getMillisecPerChunk()/2)){ /* timeLeftToZero is always > 0, otherwise would not be in this block */ float timeLeftToZero = decayTime - time; /* returns the y value of the peak line at timePoistion */ peakLineDecayPoint = timeLeftToZero * TAN_THETA; } if(c.getNormEnd() > peakLineDecayPoint) { final int index = values.size(); final float value = c.getNormEnd(); /* this chunk was higher than the peak line at time * * position therefore it becomes the new peak value */ previousPeak = value; previousPeakTime = time; values.add(new Sequence.Value() { @Override public int index() { return index; } @Override public float getValue() { return value; } @Override public float getTimePosition() { return time; } @Override public Sequence getSequence() { return PeakMeterSequence.this; } @Override public String toString(){ return "Peak meter sequence value. Value:"+getValue()+" time pos:"+getTimePosition(); } }); }else if( MathUtils.equal(time, decayTime, w.getMillisecPerChunk())){ previousPeak = 0.0f; previousPeakTime = time; final int index = values.size(); values.add(new Sequence.Value() { @Override public int index() { return index; } @Override public float getValue() { return 0.0f; } @Override public float getTimePosition() { return time; } @Override public Sequence getSequence() { return PeakMeterSequence.this; } @Override public String toString(){ return "Peak meter sequence value. Value:"+getValue()+" time pos:"+getTimePosition(); } }); } } } /** * Calculates the point on the time line (x-axis) where the peak curve will be completely decayed * to zero. * * @param time the time of the decay point * @param previousPeak the previous peak value. that is where the line has started decaying * @param previousPeakTime the time of the previous peak value * * @return */ float calculateDecayLineAtTime(float time,float previousPeak, float previousPeakTime){ /* decayTime is the time where the previous peak was plus the time * * that the peak line will take to decay to zero */ float decayTime = previousPeakTime +(previousPeak / TAN_THETA); /* the peak line has already decayed to zero in the past */ if(time > decayTime){ return 0.0f; } /* always > 0, otherwise would have returned already */ float timeLeftToZero = decayTime - time; /* returns the y value of the peak line at timePoistion */ return timeLeftToZero * TAN_THETA; } @Override public float getBegin() { return 0.0f; } @Override public float getEnd() { return values.isEmpty() ? getBegin() : values.get(values.size()-1).getValue(); } @Override public int getValuesNum() { return values.size(); } @Override public Value getValueAt(int index) { return values.get(index); } @Override public Range<Float> getRange() { return range; } @Override public void addSequenceListener(SequenceListener l) { // this sequence is immutable, therefore listeners would never be notfied } @Override public void removeSequenceListener(SequenceListener l) { // this sequence is immutable, therefore listeners would never be notfied } @Override public float getLen() { return len; } } } class DBChunk extends Chunk { float normStart; float normEnd; DBChunk(Chunk c) { super((short)0,(short)0); start = MathUtils.toDb(Math.abs(c.getStart())); if(Float.isInfinite(start)){ start = 0.0f; } end = MathUtils.toDb(Math.abs(c.getEnd())); if(Float.isInfinite(end)){ end = 0.0f; } normStart = 0.0f; float max = Math.max(Math.abs(c.getStart()),Math.abs(c.getEnd())); float db = MathUtils.toDb(max); if(Float.isInfinite(db)){ normEnd = 0; }else if(db < -60){ normEnd = 0; }else{ normEnd = new MathUtils.Scale(-60.0f, 0.0f, 0.0f, 1.0f).linear(db); } } @Override public float getNormStart() { return normStart; } @Override public float getNormEnd() { return normEnd; } }