1 /** 2 * Copyright (c) 2011, The University of Southampton and the individual contributors. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without modification, 6 * are permitted provided that the following conditions are met: 7 * 8 * * Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 11 * * Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * * Neither the name of the University of Southampton nor the names of its 16 * contributors may be used to endorse or promote products derived from this 17 * software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 /** 31 * 32 */ 33 package org.openimaj.vis.audio; 34 35 import org.openimaj.audio.AudioFormat; 36 import org.openimaj.audio.samples.FloatSampleBuffer; 37 import org.openimaj.audio.samples.SampleBuffer; 38 import org.openimaj.image.MBFImage; 39 import org.openimaj.util.array.ArrayUtils; 40 import org.openimaj.vis.VisualisationImpl; 41 42 /** 43 * A visualisation for signals. It utilises the {@link SampleBuffer} class 44 * to store the samples to be displayed. It will display multi-channel signals 45 * as given by the audio format of the SampleBuffer. 46 * <p> 47 * A method for accepting sample chunks is implemented so that a "live" display 48 * of audio waveform can be displayed. 49 * <p> 50 * If you prefer to display an overview of the complete audio of a resource, 51 * use the {@link AudioOverviewVisualisation}. 52 * 53 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 54 * @created 13 Jul 2012 55 * @version $Author$, $Revision$, $Date$ 56 */ 57 public class AudioWaveform extends VisualisationImpl<SampleBuffer> 58 { 59 /** */ 60 private static final long serialVersionUID = 1L; 61 62 /** Whether to have a decay on the waveform */ 63 private boolean decay = false; 64 65 /** The decay amount if decay is set to true */ 66 private float decayAmount = 0.3f; 67 68 /** The maximum signal value */ 69 private float maxValue = 100f; 70 71 /** The scalar in the x direction */ 72 private float xScale = 1f; 73 74 /** Whether to automatically determine the x scalar */ 75 private boolean autoFit = true; 76 77 /** Whether to automatically determine the y scalar */ 78 private boolean autoScale = true; 79 80 /** The colour to draw the waveform */ 81 private Float[] colour = new Float[]{1f,1f,1f,1f}; 82 83 /** 84 * Create an audio waveform display of the given width and height 85 * @param w The width of the image 86 * @param h The height of the image 87 */ 88 public AudioWaveform( final int w, final int h ) 89 { 90 super( w, h ); 91 } 92 93 /** 94 * Create an audio waveform that overlays the given visualisation. 95 * @param v The visualisation to overlay 96 */ 97 public AudioWaveform( final VisualisationImpl<?> v ) 98 { 99 super( v ); 100 } 101 102 /** 103 * Draw the given sample chunk into an image and returns that image. 104 * The image is reused, so if you want to keep it you must clone 105 * the image afterwards. 106 * 107 * @param sb The sample chunk to draw 108 * @return The image drawn 109 */ 110 public MBFImage drawWaveform( final SampleBuffer sb ) 111 { 112 synchronized( this.visImage ) 113 { 114 // If decay is set we lower the values of the data already in place 115 if( !this.clearBeforeDraw && this.decay ) 116 this.visImage.multiplyInplace( this.decayAmount ); 117 118 // Work out the y scalar 119 float m = this.maxValue; 120 if( this.autoScale ) 121 m = (float)Math.max( 122 Math.abs( ArrayUtils.minValue( sb.asDoubleArray() ) ), 123 Math.abs( ArrayUtils.maxValue( sb.asDoubleArray() ) ) ); 124 125 final int nc = sb.getFormat().getNumChannels(); 126 final int channelHeight = this.visImage.getHeight()/nc; 127 final float scalar = this.visImage.getHeight() / (m*2*nc); 128 final int h = this.getHeight(); 129 130 // Work out the xscalar 131 if( this.autoFit ) 132 this.xScale = this.visImage.getWidth() / (sb.size()/(float)nc); 133 134 // Plot the wave form 135 for( int c = 0; c < nc; c++ ) 136 { 137 final int yOffset = channelHeight * c + channelHeight/2; 138 int lastX = 0; 139 int lastY = yOffset; 140 for( int i = 1; i < sb.size()/nc; i += nc ) 141 { 142 final int x = (int)(i*this.xScale); 143 final int y = (int)(sb.get(i*nc+c)/Integer.MAX_VALUE*scalar+yOffset); 144 this.visImage.drawLine( lastX, lastY, x, h-y, this.colour ); 145 lastX = x; 146 lastY = h-y; 147 } 148 } 149 } 150 151 return this.visImage; 152 } 153 154 /** 155 * {@inheritDoc} 156 * @see org.openimaj.vis.VisualisationImpl#update() 157 */ 158 @Override 159 public void update() 160 { 161 if( this.data != null ) 162 synchronized( this.data ) 163 { 164 this.drawWaveform( this.data ); 165 } 166 } 167 168 /** 169 * If the samples are represented as a set of doubles, you can set them 170 * here. The assumed format will be a single channel at 44.1KHz. 171 * @param samples The sample data. 172 */ 173 public void setData( final double[] samples ) 174 { 175 final FloatSampleBuffer fsb = new FloatSampleBuffer( samples, 176 new AudioFormat( -1, 44.1, 1 ) ); 177 super.setData( fsb ); 178 } 179 180 /** 181 * Samples represented as a set of doubles. 182 * @param samples The samples. 183 * @param format The format of the samples 184 */ 185 public void setData( final double[] samples, final AudioFormat format ) 186 { 187 final FloatSampleBuffer fsb = new FloatSampleBuffer( samples, 188 format.clone().setNBits( -1 ) ); 189 super.setData( fsb ); 190 } 191 192 /** 193 * Set the maximum value that the signal can achieve. This method also 194 * disables autoScale. 195 * 196 * @param f The maximum that a signal can achieve. 197 */ 198 public void setMaximum( final float f ) 199 { 200 this.maxValue = f; 201 this.autoScale = false; 202 } 203 204 /** 205 * Get the maximum value in use 206 * @return The maximum value 207 */ 208 public float getMaximum() 209 { 210 return this.maxValue; 211 } 212 213 /** 214 * Set the x-scale at which to draw the waveform. This method also disables 215 * auto fit. 216 * @param f The scale at which to draw the waveform. 217 */ 218 public void setXScale( final float f ) 219 { 220 this.xScale = f; 221 this.autoFit = false; 222 } 223 224 /** 225 * Whether to auto fit the x-axis 226 * @param tf TRUE to auto-fit the x-axis 227 */ 228 public void setAutoFit( final boolean tf ) 229 { 230 this.autoFit = tf; 231 } 232 233 /** 234 * Whether to auto fit the y-axis 235 * @param tf TRUE to auto-fit the y-axis 236 */ 237 public void setAutoScale( final boolean tf ) 238 { 239 this.autoScale = tf; 240 } 241 242 /** 243 * Returns whether decay is set 244 * @return TRUE if decay is set 245 */ 246 public boolean isDecay() 247 { 248 return this.decay; 249 } 250 251 /** 252 * Set whether decay is to be used 253 * @param decay TRUE for decay, FALSE otherwise 254 */ 255 public void setDecay( final boolean decay ) 256 { 257 this.decay = decay; 258 } 259 260 /** 261 * Get the amount of decay in use 262 * @return The decay amount 263 */ 264 public float getDecayAmount() 265 { 266 return this.decayAmount; 267 } 268 269 /** 270 * Set the amount of decay to use 271 * @param decayAmount the decay amount 272 */ 273 public void setDecayAmount( final float decayAmount ) 274 { 275 this.decayAmount = decayAmount; 276 } 277 278 /** 279 * Set the colour to draw the signal 280 * @param colour The colour 281 */ 282 public void setColour( final Float[] colour ) 283 { 284 this.colour = colour; 285 } 286 }