001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030/** 031 * 032 */ 033package org.openimaj.vis.audio; 034 035import org.openimaj.audio.AudioFormat; 036import org.openimaj.audio.samples.FloatSampleBuffer; 037import org.openimaj.audio.samples.SampleBuffer; 038import org.openimaj.image.MBFImage; 039import org.openimaj.util.array.ArrayUtils; 040import org.openimaj.vis.VisualisationImpl; 041 042/** 043 * A visualisation for signals. It utilises the {@link SampleBuffer} class 044 * to store the samples to be displayed. It will display multi-channel signals 045 * as given by the audio format of the SampleBuffer. 046 * <p> 047 * A method for accepting sample chunks is implemented so that a "live" display 048 * of audio waveform can be displayed. 049 * <p> 050 * If you prefer to display an overview of the complete audio of a resource, 051 * use the {@link AudioOverviewVisualisation}. 052 * 053 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 054 * @created 13 Jul 2012 055 * @version $Author$, $Revision$, $Date$ 056 */ 057public class AudioWaveform extends VisualisationImpl<SampleBuffer> 058{ 059 /** */ 060 private static final long serialVersionUID = 1L; 061 062 /** Whether to have a decay on the waveform */ 063 private boolean decay = false; 064 065 /** The decay amount if decay is set to true */ 066 private float decayAmount = 0.3f; 067 068 /** The maximum signal value */ 069 private float maxValue = 100f; 070 071 /** The scalar in the x direction */ 072 private float xScale = 1f; 073 074 /** Whether to automatically determine the x scalar */ 075 private boolean autoFit = true; 076 077 /** Whether to automatically determine the y scalar */ 078 private boolean autoScale = true; 079 080 /** The colour to draw the waveform */ 081 private Float[] colour = new Float[]{1f,1f,1f,1f}; 082 083 /** 084 * Create an audio waveform display of the given width and height 085 * @param w The width of the image 086 * @param h The height of the image 087 */ 088 public AudioWaveform( final int w, final int h ) 089 { 090 super( w, h ); 091 } 092 093 /** 094 * Create an audio waveform that overlays the given visualisation. 095 * @param v The visualisation to overlay 096 */ 097 public AudioWaveform( final VisualisationImpl<?> v ) 098 { 099 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}