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}