Mercurial > hg > cmdp
view src/uk/ac/qmul/eecs/depic/daw/referencesound/BeadsSequenceMapping.java @ 2:c0412c81d274
Added documentation
author | Fiore Martin <f.martin@qmul.ac.uk> |
---|---|
date | Thu, 18 Feb 2016 18:35:26 +0000 |
parents | 3074a84ef81e |
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.referencesound; import net.beadsproject.beads.core.AudioContext; import net.beadsproject.beads.core.Bead; import net.beadsproject.beads.ugens.Gain; 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.Sequence.Value; import uk.ac.qmul.eecs.depic.patterns.SequenceMapping; /** * Implements {@code uk.ac.qmul.eecs.depic.patterns.SequenceMapping} using the Beads sound engine * It's a slightly modified version of {@code uk.ac.qmul.eecs.depic.daw.beads.BeadsSequenceMapping} * that includes the one ref and multiple ref sonification, used in GridSound. * * */ abstract class BeadsSequenceMapping implements SequenceMapping { public static final float DEFAULT_VALUE_LEN_MS = 50.0f; public static final float DEFAULT_VALUE_REF_LEN_MS = 50.0f; public static final float DEFAULT_VALUE_AMP = .5f; public static boolean verboseMode = true; protected static final float RATE_STEP = 0.1f; protected AudioContext ac; /* the master gain */ protected Gain masterGain; private PitchedUGen renderCurveAtUGen; private Gain renderCurveGain; private float renderValueLen; private float renderValueRefLen; private float renderValueAmp; private Bead lastTrail; /** * * @param ac an audio context. The audio context must be started outside this class */ protected BeadsSequenceMapping(AudioContext ac){ this.ac = ac; masterGain = new Gain(ac, 1, 1.0f); ac.out.addInput(masterGain); renderValueLen = DEFAULT_VALUE_LEN_MS; renderValueRefLen = DEFAULT_VALUE_REF_LEN_MS; renderValueAmp = DEFAULT_VALUE_AMP; } /** * * returns the ugen used to render a sequence value * * @return */ protected abstract PitchedUGen createRenderValueUGen(); /** * returns a * @return */ protected abstract PitchedUGen createRenderCurveUGen(); protected abstract Range<Float> getRenderCurvePitchSpan(); protected abstract Range<Float> getRenderValuePitchSpan(); public void setRenderValueRefLen(float renderValueRefLen) { this.renderValueRefLen = renderValueRefLen; } public float getRenderValueRefLen() { return renderValueRefLen; } public void setRenderValueLen(float milliseconds){ renderValueLen = milliseconds; } public float getRenderValueLen(){ return renderValueLen; } public float getRenderValueAmp() { return renderValueAmp; } public void setRenderValueAmp(float renderValueAmp) { this.renderValueAmp = renderValueAmp; } @Override public void renderValue(Value val) { if(lastTrail != null){ lastTrail.kill(); lastTrail = null; } float v = val.getValue(); Sequence sequence = val.getSequence(); Range<Float> range = sequence.getRange(); /* Map the value to the pitch: */ MathUtils.Scale scale = new MathUtils.Scale( range, getRenderValuePitchSpan() ); float pitch = scale.exponential(v); PitchedUGen valuePlayer = createRenderValueUGen(); valuePlayer.setLen(renderValueAmp, renderValueLen); valuePlayer.setPitch(pitch); masterGain.addInput(valuePlayer); } public void renderValueOneRef(Value val){ if(lastTrail != null){ lastTrail.kill(); lastTrail = null; } Range<Float> r = val.getSequence().getRange(); float v = r.getStart() + (r.getEnd() - r.getStart())/2 ; Sequence sequence = val.getSequence(); Range<Float> range = sequence.getRange(); /* Map the value to the pitch: */ MathUtils.Scale scale = new MathUtils.Scale( range, getRenderValuePitchSpan() ); float pitch = scale.exponential(v); PitchedUGen valuePlayer = createRenderValueUGen(); valuePlayer.setLen(renderValueAmp, renderValueLen); valuePlayer.setPitch(pitch); masterGain.addInput(valuePlayer); } public void renderValueMulRef(Value val, float resolution) { if(lastTrail != null){ lastTrail.kill(); lastTrail = null; } Range<Float> r = val.getSequence().getRange(); float reference = r.getStart() + (r.getEnd() - r.getStart())/2; float v = val.getValue(); Sequence sequence = val.getSequence(); Range<Float> range = sequence.getRange(); if(!rangeContains(range,v) || !rangeContains(range,reference)){ throw new IllegalArgumentException("One or more values not in range val:"+ v + " reference:" +reference); } if(Float.compare(resolution, 0) <= 0){ throw new IllegalArgumentException("Resolution must be positive. resolution:"+resolution); } /* if the step is bigger than the start and end make the step = end-start */ if(resolution > Math.abs(v-reference) ){ resolution = Math.abs(v-reference); } /* Map the value to the pitch: */ MathUtils.Scale scale = new MathUtils.Scale( range, getRenderValuePitchSpan() ); final PitchedUGen valuePlayer = createRenderValueUGen(); boolean onePitchOnly = true; if(v > reference){ // scale from v down to reference /* goes all the way from v-resolution to the last value before reaching the * reference, each step is of resolution */ for(float i=v-resolution; i>=reference; i = i-resolution){ final float nextPitch = scale.exponential(i); if(i == v-resolution){ // first run valuePlayer.setPitch(nextPitch); }else{ onePitchOnly = false; valuePlayer.addLen(renderValueAmp, renderValueRefLen,new Bead(){ @Override public void messageReceived(Bead bead){ valuePlayer.setPitch(nextPitch); } }); } } /* add the last bit of envelope that will kill the UGen */ /* if the envelope has many pitches already used addLen which, if env is != null * will bring it to 0. Otherwise use setLen that brings the envelope to 0 */ if(onePitchOnly){ valuePlayer.setLen(renderValueAmp, renderValueRefLen); }else{ valuePlayer.addLen(renderValueAmp, renderValueRefLen); } lastTrail = valuePlayer; masterGain.addInput(valuePlayer); }else if(v < reference){ // scale from v up to reference for(float i=v+resolution; i<=reference; i = i+resolution){ final float nextPitch = scale.exponential(i); if(i == v+resolution){ // first run valuePlayer.setPitch(nextPitch); }else{ onePitchOnly = false; valuePlayer.addLen(renderValueAmp, renderValueRefLen,new Bead(){ @Override public void messageReceived(Bead bead){ valuePlayer.setPitch(nextPitch); } }); } } /* add the last bit of envelope that will kill the UGen */ /* if the envelope has many pitches already used addLen which, if env is != null * will bring it to 0. Otherwise use setLen that brings the envelope to 0 */ if(onePitchOnly){ valuePlayer.setLen(renderValueAmp, renderValueRefLen); }else{ valuePlayer.addLen(renderValueAmp, renderValueRefLen); } lastTrail = valuePlayer; masterGain.addInput(valuePlayer); } } private static boolean rangeContains(Range<Float> range, float f){ return (Float.compare(f, range.getStart()) >= 0 && Float.compare(f, range.getEnd()) <= 0); } @Override public void renderCurveAt(Sequence sequence, float time, float duration){ /* negative value means stop now */ if(duration < 0.0f ){ if(renderCurveAtUGen != null) renderCurveAtUGen.kill(); renderCurveAtUGen = null; return; } /* look for the two sequence values, whose times contain time in between */ float valueAtTime = new MathUtils.Interpolate(sequence).linear(time); float pitch = new MathUtils.Scale(sequence.getRange(),getRenderCurvePitchSpan()).linear(valueAtTime); /* infinite duration means play until renderCurveAt is called again with duration = -1 */ if(Float.isInfinite(duration)){ if(renderCurveAtUGen == null){ renderCurveAtUGen = createRenderCurveUGen(); renderCurveAtUGen.setPitch(pitch); masterGain.addInput(renderCurveAtUGen); renderCurveAtUGen.start(); }else{ renderCurveAtUGen.setPitch(pitch); } }else if(duration > 0.0f){ if(renderCurveAtUGen == null){ renderCurveAtUGen = createRenderCurveUGen(); renderCurveAtUGen.setPitch(pitch); renderCurveAtUGen.setLen(duration*0.8f, duration*0.2f); ac.out.addInput(renderCurveAtUGen); renderCurveAtUGen.start(); }else{ renderCurveAtUGen.setLen(duration*0.8f, duration*0.2f); } } } }