org.openimaj.audio.generation.Oscillator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of audio-processing Show documentation
Show all versions of audio-processing Show documentation
Implementations of various audio processors (e.g. multichannel conversion, volume change, ...).
/**
* Copyright (c) 2011, The University of Southampton and the individual contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the University of Southampton nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.openimaj.audio.generation;
import org.openimaj.audio.AudioFormat;
import org.openimaj.audio.SampleChunk;
import org.openimaj.audio.generation.Synthesizer.FMOptions;
import org.openimaj.audio.generation.Synthesizer.OscillatorOptions;
import org.openimaj.audio.samples.SampleBuffer;
import org.openimaj.audio.samples.SampleBufferFactory;
/**
* The oscillator implementations for the synthesiser.
*
* @author David Dupplaw ([email protected])
*
* @created 2 May 2012
*/
public interface Oscillator
{
/**
* Oscillator that produces pure sine waves.
*
* @author David Dupplaw ([email protected])
* @created 21 Feb 2013
* @version $Author$, $Revision$, $Date$
*/
public static class SineOscillator implements Oscillator
{
@Override
public SampleChunk getSampleChunk( final int length, final double time,
final double freq, final int gain, final AudioFormat format )
{
// Work out how many samples per frequency wave
final double samplesPerWave = format.getSampleRateKHz()*1000d/freq;
// Phase offset in samples. (f*t)-floor(f*t) is the part number
// of waves at this point (assuming the first wave starts at a
// phase of zero).
final double p = 2*Math.PI*((freq*time)-Math.floor(freq*time));
// Create an appropriate sample buffer
final SampleBuffer sb = SampleBufferFactory.createSampleBuffer(
format, length );
// Fill it with sin waves
final double z = 2*Math.PI/samplesPerWave;
for( int i = 0; i < length; i++ )
sb.set( i, (float)(Math.sin( i*z+p )*gain) );
return sb.getSampleChunk();
}
@Override
public OscillatorOptions getOptions()
{
return null;
}
};
/**
* Oscillator that produces pure square waves.
*
* @author David Dupplaw ([email protected])
* @created 21 Feb 2013
* @version $Author$, $Revision$, $Date$
*/
public static class SquareOscillator implements Oscillator
{
@Override
public SampleChunk getSampleChunk( final int length, final double time,
final double freq, final int gain, final AudioFormat format )
{
final SampleBuffer sb = SampleBufferFactory.createSampleBuffer(
format, length );
final double samplesPerWave = format.getSampleRateKHz()*1000d/freq;
// phase offset in samples
final int p = (int)( samplesPerWave *
((freq*time)-Math.floor(freq*time)));
for( int i = 0; i < length; i++ )
{
final int x = (i+p) % (int)samplesPerWave;
if( x > samplesPerWave/2 )
sb.set( i, gain );
else sb.set( i, -gain );
}
return sb.getSampleChunk();
}
@Override
public OscillatorOptions getOptions()
{
return null;
}
};
/**
* Oscillator that produces saw waves.
*
* @author David Dupplaw ([email protected])
* @created 21 Feb 2013
* @version $Author$, $Revision$, $Date$
*/
public static class SawOscillator implements Oscillator
{
@Override
public SampleChunk getSampleChunk( final int length, final double time,
final double freq, final int gain, final AudioFormat format )
{
final SampleBuffer sb = SampleBufferFactory.createSampleBuffer(
format, length );
final double samplesPerWave = format.getSampleRateKHz()*1000d/freq;
// phase offset in samples
final int p = (int)( samplesPerWave *
((freq*time)-Math.floor(freq*time)));
for( int i = 0; i < length; i++ )
{
final int x = (i+p) % (int)samplesPerWave;
sb.set( i, (float)(x*(gain/samplesPerWave)) );
}
return sb.getSampleChunk();
}
@Override
public OscillatorOptions getOptions()
{
return null;
}
};
/**
* White noise generator
*
* @author David Dupplaw ([email protected])
* @created 21 Feb 2013
* @version $Author$, $Revision$, $Date$
*/
public static class NoiseOscillator implements Oscillator
{
@Override
public SampleChunk getSampleChunk( final int length, final double time,
final double freq, final int gain, final AudioFormat format )
{
final SampleBuffer sb = SampleBufferFactory.createSampleBuffer(
format, length );
for( int i = 0; i < sb.size(); i++ )
sb.set( i, (float)(Math.random() * Integer.MAX_VALUE) );
return sb.getSampleChunk();
}
@Override
public OscillatorOptions getOptions()
{
return null;
}
};
/**
* Generates an empty sample chunk
*
* @author David Dupplaw ([email protected])
* @created 21 Feb 2013
* @version $Author$, $Revision$, $Date$
*/
public static class DummyOscillator implements Oscillator
{
@Override
public SampleChunk getSampleChunk( final int length, final double time,
final double freq, final int gain, final AudioFormat format )
{
final SampleBuffer sb = SampleBufferFactory.createSampleBuffer(
format, length );
return sb.getSampleChunk();
}
@Override
public OscillatorOptions getOptions()
{
return null;
}
};
/**
* Frequency modulation of wave types. We use a carrier wave that generates a
* tone at the required frequency and modulate with the wave that is the
* modulation wave by altering the time (phase) offset of the carrier using
* the modulation wave.
*
* @author David Dupplaw ([email protected])
* @created 21 Feb 2013
* @version $Author$, $Revision$, $Date$
*/
public static class FrequencyModulatedOscillator implements Oscillator
{
/** The options for this synth */
private final FMOptions options = new FMOptions();
@Override
public SampleChunk getSampleChunk(final int length, final double time,
final double freq, final int gain, final AudioFormat format)
{
// System.out.println( "======================================================" );
// System.out.println( "Getting signal at time "+time );
// Get the modulator signal
final SampleBuffer sb = this.getOptions().modulator.nextSampleChunk().getSampleBuffer();
// work out the maximum and minimum time signal needed from the carrier
// so that we can retrieve the appropriate time chunk from it
double minTime = time;
double maxTime = time + length;
final double step = format.getSampleRateKHz();
final double amp = this.getOptions().modulatorAmplitude;
for( int i = 0; i < sb.size(); i++ )
{
final float f = sb.get(i);
final double t = time + step*i + f*amp;
minTime = Math.min( t, minTime );
maxTime = Math.max( t, maxTime );
}
// The start and length of the carrier signal required
final int cl = (int)(maxTime - minTime);
final double ct = minTime;
// System.out.println( "Carrier buffer: "+ct+" -> "+cl );
// Get the carrier signal
final SampleBuffer carrierBuffer = this.getOptions().carrier.getOscillator()
.getSampleChunk( cl, ct, freq, gain, format ).getSampleBuffer();
// We'll side-affect the modulator signal, as it's the right length already.
for( int i = 0; i < sb.size(); i++ )
{
// The time to retrieve in the carrier signal
final float f = sb.get(i);
final double t = time + step*i + f*amp;
// Convert the time back to an index into the carrier buffer
final int index = (int)((t - minTime)/step);
sb.set( i, carrierBuffer.get( index ) );
// System.out.println( "------ "+i+" ------");
// System.out.println( "Value in modulator: "+f+" (scaled to "+(f*amp)+")" );
// System.out.println( "Time at position: "+(time + step*i) );
// System.out.println( "Modulated time: "+t );
// System.out.println( "Index into carrier: "+index );
// System.out.println( "Value in carrier: "+carrierBuffer.get(index) );
}
return sb.getSampleChunk();
}
@Override
public FMOptions getOptions()
{
return this.options;
}
};
/**
*
* @param length The length of the sample chunk to generate
* @param time The time at which the sample chunk should start
* @param freq The frequency of wave to generate
* @param gain The gain of the wave to generate (0 <= gain <= MAX_INT)
* @param format The format of the sample chunk
* @return The sample chunk
*/
public abstract SampleChunk getSampleChunk( int length, double time,
double freq, int gain, AudioFormat format );
/**
* Returns the options for the particular oscillator type.
* @return The options object
*/
public abstract OscillatorOptions getOptions();
}