package com.jsyn.examples;
import java.io.IOException;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import com.jsyn.JSyn;
import com.jsyn.Synthesizer;
import com.jsyn.devices.javasound.MidiDeviceTools;
import com.jsyn.instruments.SubtractiveSynthVoice;
import com.jsyn.midi.MessageParser;
import com.jsyn.midi.MidiConstants;
import com.jsyn.unitgen.LineOut;
import com.jsyn.unitgen.PowerOfTwo;
import com.jsyn.unitgen.SineOscillator;
import com.jsyn.unitgen.UnitOscillator;
import com.jsyn.util.VoiceAllocator;
import com.softsynth.shared.time.TimeStamp;
/**
* Connect a USB MIDI Keyboard to the internal MIDI Synthesizer
* using JavaSound.
*
* @author Phil Burk (C) 2010 Mobileer Inc
*
*/
public class UseMidiKeyboard
{
private static final int MAX_VOICES = 8;
private Synthesizer synth;
private VoiceAllocator allocator;
private LineOut lineOut;
private double vibratoRate = 5.0;
private double vibratoDepth = 0.0;
private UnitOscillator lfo;
private PowerOfTwo powerOfTwo;
private MessageParser messageParser;
private SubtractiveSynthVoice[] voices;
public static void main( String[] args )
{
UseMidiKeyboard app = new UseMidiKeyboard();
try
{
app.test();
} catch( MidiUnavailableException e )
{
e.printStackTrace();
} catch( IOException e )
{
e.printStackTrace();
} catch( InterruptedException e )
{
e.printStackTrace();
}
}
// Write a Receiver to get the messages from a Transmitter.
class CustomReceiver implements Receiver
{
public void close()
{
System.out.print( "Closed." );
}
public void send( MidiMessage message, long timeStamp )
{
byte[] bytes = message.getMessage();
messageParser.parse( bytes );
}
}
public int test() throws MidiUnavailableException, IOException,
InterruptedException
{
setupSynth();
messageParser = new MyParser();
int result = 2;
// I know that my keyboard has "Usb in the description".
// There is another device that does not have Usb that I want to avoid.
MidiDevice keyboard = MidiDeviceTools.findKeyboard( "usb" );
Receiver receiver = new CustomReceiver();
// Just use default synthesizer.
if( keyboard != null )
{
// If you forget to open them you will hear no sound.
keyboard.open();
// Put the receiver in the transmitter.
// This gives fairly low latency playing.
keyboard.getTransmitter().setReceiver( receiver );
System.out.println( "Play MIDI keyboard: " + keyboard.getDeviceInfo().getDescription() );
result = 0;
}
else
{
System.out.println( "Could not find a keyboard." );
}
return result;
}
class MyParser extends MessageParser
{
@Override
public void controlChange( int channel, int index, int value )
{
// Mod Wheel
if( index == 1 )
{
vibratoDepth = 0.1 * value / 128.0;
// System.out.println( "vibratoDepth = " + vibratoDepth );
lfo.amplitude.set( vibratoDepth );
}
// 102 is the index of the first knob on my Axiom 25
else if( index == 102 )
{
final double bump = 0.95;
if( value < 64 )
{
vibratoRate *= bump;
}
else
{
vibratoRate *= 1.0 / bump;
}
System.out.println( "vibratoRate = " + vibratoRate );
lfo.frequency.set( vibratoRate );
}
}
@Override
public void noteOff( int channel, int noteNumber, int velocity )
{
allocator.noteOff( noteNumber, synth.createTimeStamp() );
}
@Override
public void noteOn( int channel, int noteNumber, int velocity )
{
double frequency = convertPitchToFrequency( noteNumber );
double amplitude = velocity / (4 * 128.0);
TimeStamp timeStamp = synth.createTimeStamp();
allocator.noteOn( noteNumber, frequency, amplitude, timeStamp );
}
public void pitchBend( int channel, int bend )
{
double fraction = (bend - MidiConstants.PITCH_BEND_CENTER) / ((double)MidiConstants.PITCH_BEND_CENTER);
System.out.println( "bend = " + bend + ", fraction = " + fraction );
}
}
/**
* Calculate frequency in Hertz based on MIDI pitch. Middle C is 60.0. You
* can use fractional pitches so 60.5 would give you a pitch half way
* between C and C#.
*/
double convertPitchToFrequency( double pitch )
{
final double concertA = 440.0;
return concertA * Math.pow( 2.0, ((pitch - 69) * (1.0 / 12.0)) );
}
private void setupSynth()
{
synth = JSyn.createSynthesizer();
// Add an output.
synth.add( lineOut = new LineOut() );
synth.add( powerOfTwo = new PowerOfTwo() );
synth.add( lfo = new SineOscillator() );
// Sums pitch modulation.
lfo.output.connect( powerOfTwo.input );
lfo.amplitude.set( vibratoDepth );
lfo.frequency.set( vibratoRate );
voices = new SubtractiveSynthVoice[MAX_VOICES];
for( int i = 0; i < MAX_VOICES; i++ )
{
SubtractiveSynthVoice voice = new SubtractiveSynthVoice();
synth.add( voice );
powerOfTwo.output.connect( voice.pitchModulation );
voice.getOutput().connect( 0, lineOut.input, 0 );
voice.getOutput().connect( 0, lineOut.input, 1 );
voices[i] = voice;
}
allocator = new VoiceAllocator( voices );
// Start synthesizer using default stereo output at 44100 Hz.
synth.start();
// We only need to start the LineOut. It will pull data from the
// oscillator.
lineOut.start();
// Get synthesizer time in seconds.
double timeNow = synth.getCurrentTime();
// Advance to a near future time so we have a clean start.
double time = timeNow + 0.5;
}
}