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 }