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 */
030package org.openimaj.audio.generation;
031
032import org.openimaj.audio.AudioFormat;
033import org.openimaj.audio.SampleChunk;
034import org.openimaj.audio.generation.Synthesizer.FMOptions;
035import org.openimaj.audio.generation.Synthesizer.OscillatorOptions;
036import org.openimaj.audio.samples.SampleBuffer;
037import org.openimaj.audio.samples.SampleBufferFactory;
038
039/**
040 *      The oscillator implementations for the synthesiser.
041 *
042 *  @author David Dupplaw (dpd@ecs.soton.ac.uk)
043 *
044 *      @created 2 May 2012
045 */
046public interface Oscillator
047{
048        /**
049         *      Oscillator that produces pure sine waves.
050         *
051         *      @author David Dupplaw (dpd@ecs.soton.ac.uk)
052         *  @created 21 Feb 2013
053         *      @version $Author$, $Revision$, $Date$
054         */
055        public static class SineOscillator implements Oscillator
056        {
057                @Override
058        public SampleChunk getSampleChunk( final int length, final double time,
059                                final double freq, final int gain, final AudioFormat format )
060        {
061                        // Work out how many samples per frequency wave
062                        final double samplesPerWave = format.getSampleRateKHz()*1000d/freq;
063
064                        // Phase offset in samples. (f*t)-floor(f*t) is the part number
065                        // of waves at this point (assuming the first wave starts at a
066                        // phase of zero).
067                        final double p = 2*Math.PI*((freq*time)-Math.floor(freq*time));
068
069                        // Create an appropriate sample buffer
070                        final SampleBuffer sb = SampleBufferFactory.createSampleBuffer(
071                                        format, length );
072
073                        // Fill it with sin waves
074                        final double z = 2*Math.PI/samplesPerWave;
075                        for( int i = 0; i < length; i++ )
076                                sb.set( i, (float)(Math.sin( i*z+p )*gain) );
077
078            return sb.getSampleChunk();
079        }
080
081                @Override
082                public OscillatorOptions getOptions()
083                {
084                        return null;
085                }
086        };
087
088        /**
089         *      Oscillator that produces pure square waves.
090         *
091         *      @author David Dupplaw (dpd@ecs.soton.ac.uk)
092         *  @created 21 Feb 2013
093         *      @version $Author$, $Revision$, $Date$
094         */
095        public static class SquareOscillator implements Oscillator
096        {
097                @Override
098        public SampleChunk getSampleChunk( final int length, final double time,
099                                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}