View Javadoc

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  package org.openimaj.audio.generation;
31  
32  import org.openimaj.audio.AudioFormat;
33  import org.openimaj.audio.SampleChunk;
34  import org.openimaj.audio.generation.Synthesizer.FMOptions;
35  import org.openimaj.audio.generation.Synthesizer.OscillatorOptions;
36  import org.openimaj.audio.samples.SampleBuffer;
37  import org.openimaj.audio.samples.SampleBufferFactory;
38  
39  /**
40   * 	The oscillator implementations for the synthesiser.
41   *
42   *  @author David Dupplaw (dpd@ecs.soton.ac.uk)
43   *
44   *	@created 2 May 2012
45   */
46  public interface Oscillator
47  {
48  	/**
49  	 * 	Oscillator that produces pure sine waves.
50  	 *
51  	 *	@author David Dupplaw (dpd@ecs.soton.ac.uk)
52  	 *  @created 21 Feb 2013
53  	 *	@version $Author$, $Revision$, $Date$
54  	 */
55  	public static class SineOscillator implements Oscillator
56  	{
57  		@Override
58          public SampleChunk getSampleChunk( final int length, final double time,
59  				final double freq, final int gain, final AudioFormat format )
60          {
61  			// Work out how many samples per frequency wave
62  			final double samplesPerWave = format.getSampleRateKHz()*1000d/freq;
63  
64  			// Phase offset in samples. (f*t)-floor(f*t) is the part number
65  			// of waves at this point (assuming the first wave starts at a
66  			// phase of zero).
67  			final double p = 2*Math.PI*((freq*time)-Math.floor(freq*time));
68  
69  			// Create an appropriate sample buffer
70  			final SampleBuffer sb = SampleBufferFactory.createSampleBuffer(
71  					format, length );
72  
73  			// Fill it with sin waves
74  			final double z = 2*Math.PI/samplesPerWave;
75  			for( int i = 0; i < length; i++ )
76  				sb.set( i, (float)(Math.sin( i*z+p )*gain) );
77  
78              return sb.getSampleChunk();
79          }
80  
81  		@Override
82  		public OscillatorOptions getOptions()
83  		{
84  			return null;
85  		}
86  	};
87  
88  	/**
89  	 * 	Oscillator that produces pure square waves.
90  	 *
91  	 *	@author David Dupplaw (dpd@ecs.soton.ac.uk)
92  	 *  @created 21 Feb 2013
93  	 *	@version $Author$, $Revision$, $Date$
94  	 */
95  	public static class SquareOscillator implements Oscillator
96  	{
97  		@Override
98          public SampleChunk getSampleChunk( final int length, final double time,
99  				final double freq, final int gain, final AudioFormat format )
100         {
101 			final SampleBuffer sb = SampleBufferFactory.createSampleBuffer(
102 					format, length );
103 
104 			final double samplesPerWave = format.getSampleRateKHz()*1000d/freq;
105 
106 			// phase offset in samples
107 			final int p = (int)( samplesPerWave *
108 				((freq*time)-Math.floor(freq*time)));
109 
110 			for( int i = 0; i < length; i++ )
111 			{
112 				final int x = (i+p) % (int)samplesPerWave;
113 				if( x > samplesPerWave/2 )
114 						sb.set( i, gain );
115 				else	sb.set( i, -gain );
116 			}
117 
118             return sb.getSampleChunk();
119         }
120 
121 		@Override
122 		public OscillatorOptions getOptions()
123 		{
124 			return null;
125 		}
126 	};
127 
128 	/**
129 	 * 	Oscillator that produces saw waves.
130 	 *
131 	 *	@author David Dupplaw (dpd@ecs.soton.ac.uk)
132 	 *  @created 21 Feb 2013
133 	 *	@version $Author$, $Revision$, $Date$
134 	 */
135 	public static class SawOscillator implements Oscillator
136 	{
137 		@Override
138         public SampleChunk getSampleChunk( final int length, final double time,
139 				final double freq, final int gain, final AudioFormat format )
140         {
141 			final SampleBuffer sb = SampleBufferFactory.createSampleBuffer(
142 					format, length );
143 
144 			final double samplesPerWave = format.getSampleRateKHz()*1000d/freq;
145 
146 			// phase offset in samples
147 			final int p = (int)( samplesPerWave *
148 				((freq*time)-Math.floor(freq*time)));
149 
150 			for( int i = 0; i < length; i++ )
151 			{
152 				final int x = (i+p) % (int)samplesPerWave;
153 				sb.set( i, (float)(x*(gain/samplesPerWave)) );
154 			}
155 
156             return sb.getSampleChunk();
157         }
158 
159 		@Override
160 		public OscillatorOptions getOptions()
161 		{
162 			return null;
163 		}
164 	};
165 
166 	/**
167 	 * 	White noise generator
168 	 *
169 	 *	@author David Dupplaw (dpd@ecs.soton.ac.uk)
170 	 *  @created 21 Feb 2013
171 	 *	@version $Author$, $Revision$, $Date$
172 	 */
173 	public static class NoiseOscillator implements Oscillator
174 	{
175 		@Override
176 		public SampleChunk getSampleChunk( final int length, final double time,
177 				final double freq, final int gain, final AudioFormat format )
178 		{
179 			final SampleBuffer sb = SampleBufferFactory.createSampleBuffer(
180 					format, length );
181 			for( int i = 0; i < sb.size(); i++ )
182 				sb.set( i, (float)(Math.random() * Integer.MAX_VALUE) );
183 			return sb.getSampleChunk();
184 		}
185 
186 		@Override
187 		public OscillatorOptions getOptions()
188 		{
189 			return null;
190 		}
191 	};
192 
193 	/**
194 	 * 	Generates an empty sample chunk
195 	 *
196 	 *	@author David Dupplaw (dpd@ecs.soton.ac.uk)
197 	 *  @created 21 Feb 2013
198 	 *	@version $Author$, $Revision$, $Date$
199 	 */
200 	public static class DummyOscillator implements Oscillator
201 	{
202 		@Override
203 		public SampleChunk getSampleChunk( final int length, final double time,
204 				final double freq, final int gain, final AudioFormat format )
205 		{
206 			final SampleBuffer sb = SampleBufferFactory.createSampleBuffer(
207 					format, length );
208 			return sb.getSampleChunk();
209 		}
210 
211 		@Override
212 		public OscillatorOptions getOptions()
213 		{
214 			return null;
215 		}
216 	};
217 
218 	/**
219 	 * Frequency modulation of wave types. We use a carrier wave that generates a
220 	 * tone at the required frequency and modulate with the wave that is the
221 	 * modulation wave by altering the time (phase) offset of the carrier using
222 	 * the modulation wave.
223 	 *
224 	 *	@author David Dupplaw (dpd@ecs.soton.ac.uk)
225 	 *  @created 21 Feb 2013
226 	 *	@version $Author$, $Revision$, $Date$
227 	 */
228 	public static class FrequencyModulatedOscillator implements Oscillator
229 	{
230 		/** The options for this synth */
231 		private final FMOptions options = new FMOptions();
232 
233 		@Override
234 		public SampleChunk getSampleChunk(final int length, final double time,
235 				final double freq, final int gain, final AudioFormat format)
236 		{
237 //			System.out.println( "======================================================" );
238 //			System.out.println( "Getting signal at time "+time );
239 
240 			// Get the modulator signal
241 			final SampleBuffer sb = this.getOptions().modulator.nextSampleChunk().getSampleBuffer();
242 
243 			// work out the maximum and minimum time signal needed from the carrier
244 			// so that we can retrieve the appropriate time chunk from it
245 			double minTime = time;
246 			double maxTime = time + length;
247 			final double step = format.getSampleRateKHz();
248 			final double amp = this.getOptions().modulatorAmplitude;
249 			for( int i = 0; i < sb.size(); i++ )
250 			{
251 				final float f = sb.get(i);
252 				final double t = time + step*i + f*amp;
253 				minTime = Math.min( t, minTime );
254 				maxTime = Math.max( t, maxTime );
255 			}
256 
257 			// The start and length of the carrier signal required
258 			final int cl = (int)(maxTime - minTime);
259 			final double ct = minTime;
260 
261 //			System.out.println( "Carrier buffer: "+ct+" -> "+cl );
262 
263 			// Get the carrier signal
264 			final SampleBuffer carrierBuffer = this.getOptions().carrier.getOscillator()
265 					.getSampleChunk( cl, ct, freq, gain, format ).getSampleBuffer();
266 
267 			// We'll side-affect the modulator signal, as it's the right length already.
268 			for( int i = 0; i < sb.size(); i++ )
269 			{
270 				// The time to retrieve in the carrier signal
271 				final float f = sb.get(i);
272 				final double t = time + step*i + f*amp;
273 
274 				// Convert the time back to an index into the carrier buffer
275 				final int index = (int)((t - minTime)/step);
276 
277 				sb.set( i, carrierBuffer.get( index ) );
278 
279 //				System.out.println( "------ "+i+" ------");
280 //				System.out.println( "Value in modulator: "+f+" (scaled to "+(f*amp)+")" );
281 //				System.out.println( "Time at position: "+(time + step*i) );
282 //				System.out.println( "Modulated time: "+t );
283 //				System.out.println( "Index into carrier: "+index );
284 //				System.out.println( "Value in carrier: "+carrierBuffer.get(index) );
285 			}
286 
287 			return sb.getSampleChunk();
288 		}
289 
290 		@Override
291 		public FMOptions getOptions()
292 		{
293 			return this.options;
294 		}
295 	};
296 
297 	/**
298 	 *
299 	 *  @param length The length of the sample chunk to generate
300 	 *  @param time The time at which the sample chunk should start
301 	 *  @param freq The frequency of wave to generate
302 	 *  @param gain The gain of the wave to generate (0 <= gain <= MAX_INT)
303 	 *  @param format The format of the sample chunk
304 	 *  @return The sample chunk
305 	 */
306 	public abstract SampleChunk getSampleChunk( int length, double time,
307 			double freq, int gain, AudioFormat format );
308 
309 	/**
310 	 * 	Returns the options for the particular oscillator type.
311 	 *	@return The options object
312 	 */
313 	public abstract OscillatorOptions getOptions();
314 }