/* * Created on Sep 30, 2004 * * Copyright (c) 2005 Peter Johan Salomonsen (http://www.petersalomonsen.com) * * http://www.frinika.com * * This file is part of Frinika. * * Frinika is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frinika is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Frinika; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.frinika.synth.synths; import java.io.Serializable; import javax.sound.midi.MidiDevice; import javax.sound.midi.MidiMessage; import javax.sound.midi.MidiSystem; import javax.sound.midi.Receiver; import javax.sound.midi.ShortMessage; import com.frinika.voiceserver.VoiceServer; import com.frinika.audio.Decibel; import com.frinika.synth.*; import com.frinika.synth.envelope.VolumeEnvelope; import com.frinika.synth.filters.LoPass; import com.frinika.synth.synths.analogika.AnalogikaGUI; import com.frinika.synth.synths.analogika.settings.AnalogikaSettings; import com.frinika.synth.synths.analogika.settings.analogikasettingsversions.AnalogikaSettings20050303; /** * @author Peter Johan Salomonsen * */ public final class Analogika extends Synth { static final float panLeft(final double position) { return Decibel.getAmplitudeRatio(-(float)(-20f*Math.log10(Math.sqrt(1-position)))); } static final float panRight(final double position) { return Decibel.getAmplitudeRatio(-(float)(-20f*Math.log10(Math.sqrt(position)))); } static final float PAN_LEFT_0 = panLeft(0.00); static final float PAN_LEFT_15 = panLeft(0.15); static final float PAN_LEFT_20 = panLeft(0.2); static final float PAN_LEFT_25 = panLeft(0.25); static final float PAN_LEFT_30 = panLeft(0.30); static final float PAN_LEFT_40 = panLeft(0.40); static final float PAN_LEFT_45 = panLeft(0.45); static final float PAN_LEFT_50 = panLeft(0.5); static final float PAN_LEFT_55 = panLeft(0.55); static final float PAN_LEFT_60 = panLeft(0.60); static final float PAN_LEFT_70 = panLeft(0.70); static final float PAN_LEFT_75 = panLeft(0.75); static final float PAN_LEFT_80 = panLeft(0.80); static final float PAN_LEFT_85 = panLeft(0.85); static final float PAN_RIGHT_15 = panRight(0.15); static final float PAN_RIGHT_20 = panRight(0.2); static final float PAN_RIGHT_25 = panRight(0.25); static final float PAN_RIGHT_30 = panRight(0.30); static final float PAN_RIGHT_40 = panRight(0.40); static final float PAN_RIGHT_45 = panRight(0.45); static final float PAN_RIGHT_50 = panRight(0.5); static final float PAN_RIGHT_55 = panRight(0.55); static final float PAN_RIGHT_60 = panRight(0.60); static final float PAN_RIGHT_70 = panRight(0.70); static final float PAN_RIGHT_75 = panRight(0.75); static final float PAN_RIGHT_80 = panRight(0.80); static final float PAN_RIGHT_85 = panRight(0.85); static final float PAN_RIGHT_100 = panRight(1.0); AnalogikaSettings20050303 settings = new AnalogikaSettings20050303(); /** * @param voiceServer */ public Analogika(SynthRack synth) { super(synth); } public AnalogikaSettings getAnalogikaSettings() { return settings; } @Override public void loadSettings(Serializable settings) { AnalogikaSettings newSettings = (AnalogikaSettings)settings; this.settings.setFreqSpread(newSettings.getFreqSpread()); this.settings.setLayers(newSettings.getLayers()); this.settings.setLoPassAttack(newSettings.getLoPassAttack()); this.settings.setLoPassDecay(newSettings.getLoPassDecay()); this.settings.setLoPassSustain(newSettings.getLoPassSustain()); this.settings.setLoPassRelease(newSettings.getLoPassRelease()); this.settings.setLoPassMax(newSettings.getLoPassMax()); this.settings.setVolAttack(newSettings.getVolAttack()); this.settings.setVolDecay(newSettings.getVolDecay()); this.settings.setVolSustain(newSettings.getVolSustain()); this.settings.setVolRelease(newSettings.getVolRelease()); this.settings.setWaveform(newSettings.getWaveform()); if(newSettings.getInstrumentName()==null) setInstrumentName("analogika"); else setInstrumentName(newSettings.getInstrumentName()); } @Override public Serializable getSettings() { this.settings.setInstrumentName(getInstrumentName()); return settings; } /* (non-Javadoc) * @see javax.sound.midi.MidiChannel#noteOn(int, int) */ public void noteOn(int noteNumber, int velocity) { Oscillator osc = new AnalogikaOscillator(this); osc.setNoteNumber(noteNumber); osc.setVelocity(velocity); addOscillator(noteNumber,osc); } /* (non-Javadoc) * @see com.petersalomonsen.mystudio.mysynth.Synth#showGUI() */ public void showGUI() { new AnalogikaGUI(this); } final class AnalogikaOscillator extends Oscillator { final LoPass leftLoPass = new LoPass(); final LoPass rightLoPass = new LoPass(); final VolumeEnvelope volEnvelope = new VolumeEnvelope(getAudioOutput().getSampleRate(),-100f,0f); final VolumeEnvelope loPassEnvelope = new VolumeEnvelope(getAudioOutput().getSampleRate(),-100f,-(settings.getLoPassMax()/10f)); final int layers = settings.getLayers(); final float freqSpread = settings.getFreqSpread(); final float[] waveform = settings.getWaveform(); final float positionMultiplier = (float)(waveform.length / (2 * Math.PI)); final float layerMiddle = layers/2f; final float freqBase = 1f+((0.5f-layerMiddle)/layerMiddle)*freqSpread; final float spreadMiddle = freqSpread / layerMiddle; /** * @param synth */ public AnalogikaOscillator(Synth synth) { super(synth); volEnvelope.setAttack(settings.getVolAttack()); volEnvelope.setDecay(settings.getVolDecay()); volEnvelope.setSustain(settings.getVolSustain()); volEnvelope.setRelease(settings.getVolRelease()); loPassEnvelope.setAttack(settings.getLoPassAttack()); loPassEnvelope.setDecay(settings.getLoPassDecay()); loPassEnvelope.setSustain(settings.getLoPassSustain()); loPassEnvelope.setRelease(settings.getLoPassRelease()); } final float getSample(final float layerIndex) { return waveform[(int)(position * (layerIndex*spreadMiddle + freqBase)) % waveform.length]; } public final void fillBuffer(int startBufferPos, int endBufferPos, float[] buffer) { if(release) volEnvelope.release(); for(int n=startBufferPos;n<endBufferPos;) { float left = 0; float right = 0; float tmpSample; switch(layers) { case 1: left=getSample(0) * PAN_LEFT_50; right=left; break; case 2: left=getSample(0) * PAN_LEFT_0; right=getSample(1) * PAN_RIGHT_100; break; case 3: left=getSample(0) * PAN_LEFT_50; right=left; left+=getSample(1) * PAN_LEFT_0; right+=getSample(2) * PAN_RIGHT_100; break; case 4: left=getSample(0) * PAN_LEFT_0; right=getSample(1) * PAN_RIGHT_100; tmpSample = getSample(2); left+=tmpSample * PAN_LEFT_25; right+=tmpSample * PAN_RIGHT_25; tmpSample = getSample(3); left+=tmpSample * PAN_LEFT_75; right+=tmpSample * PAN_RIGHT_75; break; case 5: left=getSample(0) * PAN_LEFT_50; right=left; left+=getSample(1) * PAN_LEFT_0; right+=getSample(2) * PAN_RIGHT_100; tmpSample = getSample(3); left+=tmpSample * PAN_LEFT_25; right+=tmpSample * PAN_RIGHT_25; tmpSample = getSample(4); left+=tmpSample * PAN_LEFT_75; right+=tmpSample * PAN_RIGHT_75; break; case 6: left=getSample(0) * PAN_LEFT_0; right=getSample(1) * PAN_RIGHT_100; tmpSample = getSample(2); left+=tmpSample * PAN_LEFT_20; right+=tmpSample * PAN_RIGHT_20; tmpSample = getSample(3); left+=tmpSample * PAN_LEFT_80; right+=tmpSample * PAN_RIGHT_80; tmpSample = getSample(4); left+=tmpSample * PAN_LEFT_40; right+=tmpSample * PAN_RIGHT_40; tmpSample = getSample(5); left+=tmpSample * PAN_LEFT_60; right+=tmpSample * PAN_RIGHT_60; break; case 7: left=getSample(0) * PAN_LEFT_50; right=left; left+=getSample(1) * PAN_LEFT_0; right+=getSample(2) * PAN_RIGHT_100; tmpSample = getSample(3); left+=tmpSample * PAN_LEFT_20; right+=tmpSample * PAN_RIGHT_20; tmpSample = getSample(4); left+=tmpSample * PAN_LEFT_80; right+=tmpSample * PAN_RIGHT_80; tmpSample = getSample(5); left+=tmpSample * PAN_LEFT_40; right+=tmpSample * PAN_RIGHT_40; tmpSample = getSample(6); left+=tmpSample * PAN_LEFT_60; right+=tmpSample * PAN_RIGHT_60; break; case 8: left=getSample(0) * PAN_LEFT_0; right=getSample(1) * PAN_RIGHT_100; tmpSample = getSample(2); left+=tmpSample * PAN_LEFT_15; right+=tmpSample * PAN_RIGHT_15; tmpSample = getSample(3); left+=tmpSample * PAN_LEFT_85; right+=tmpSample * PAN_RIGHT_85; tmpSample = getSample(4); left+=tmpSample * PAN_LEFT_30; right+=tmpSample * PAN_RIGHT_30; tmpSample = getSample(5); left+=tmpSample * PAN_LEFT_70; right+=tmpSample * PAN_RIGHT_70; tmpSample = getSample(6); left+=tmpSample * PAN_LEFT_45; right+=tmpSample * PAN_RIGHT_45; tmpSample = getSample(7); left+=tmpSample * PAN_LEFT_55; right+=tmpSample * PAN_RIGHT_55; break; } position+=increment * positionMultiplier * preOscillator.getLfoBuffer()[n / 2]; float volEnvelopeAttenuation = volEnvelope.getAttenuation(); float loPassEnvelopeSample = loPassEnvelope.getAttenuation(); // Filter ----------------------- leftLoPass.setCutOff(loPassEnvelopeSample); left = leftLoPass.filter(left); rightLoPass.setCutOff(loPassEnvelopeSample); right = rightLoPass.filter(right); /* leftHiPass.setCutOff(hiFilterSustain); left = leftHiPass.filter(left) / (filterCompensate[127-hiMidiFilterSustain]); rightHiPass.setCutOff(hiFilterSustain) ; right = rightHiPass.filter(right) / (filterCompensate[127-hiMidiFilterSustain]); */ //----------------------------------- preOscillator.sampleBuffer[n++]+=left * level * volEnvelopeAttenuation; preOscillator.sampleBuffer[n++]+=right * level * volEnvelopeAttenuation; if(this.release && volEnvelopeAttenuation<0.01) { getAudioOutput().removeTransmitter(this); break; } } } } // For direct testing public static void main(String[] args) throws Exception { final VoiceServer audioOutput = new com.frinika.voiceserver.JavaSoundVoiceServer(); final SynthRack synth = new SynthRack(audioOutput); synth.open(); synth.setSynth(0,new Analogika(synth)); System.out.println("Initializing midi, please wait..."); MidiDevice midiIn = MidiSystem.getMidiDevice(MidiSystem.getMidiDeviceInfo()[0]); midiIn.open(); class Recv implements Receiver { public void send(MidiMessage message, long timeStamp) { try { ShortMessage shm = (ShortMessage)message; shm.setMessage(shm.getCommand(),0,shm.getData1(),shm.getData2()); synth.getReceiver().send(shm,-1); } catch(Exception e) {} } public void close() { } } midiIn.getTransmitter().setReceiver(new Recv()); System.out.println("Ready to play!!!"); AnalogikaGUI gui = new AnalogikaGUI((Analogika)synth.getSynth(0)); gui.setDefaultCloseOperation(AnalogikaGUI.EXIT_ON_CLOSE); } /** * */ public String toString() { //TODO count ETC return "Analogika: "+getInstrumentName(); } }