/* * 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.FileNotFoundException; import java.io.Serializable; import java.util.HashMap; import java.util.Vector; import com.frinika.global.FrinikaConfig; import com.frinika.synth.*; import com.frinika.synth.envelope.VolumeEnvelope; import com.frinika.synth.importers.soundfont.SoundFontImporter; import com.frinika.synth.importers.soundfont.SoundFontImporterGUI; import com.frinika.synth.synths.sampler.SamplerGUI; import com.frinika.synth.synths.sampler.SamplerOscillator; import com.frinika.synth.synths.sampler.settings.SampledSoundSettings; import com.frinika.synth.synths.sampler.settings.SamplerSettings; import com.frinika.synth.synths.sampler.settings.sampledsoundsettingversions.SampledSound20050403; import com.frinika.synth.synths.sampler.settings.samplersettingversions.Sampler20050227; /** * @author Peter Johan Salomonsen * */ public class MySampler extends Synth { protected HashMap<Integer,Vector<SoundFontOscillator>> exclusiveClasses = new HashMap<Integer,Vector<SoundFontOscillator>>(); public SoundFontImporter sfi; public SamplerOscillator samplerOscillator = new SamplerOscillator(this); public SamplerGUI gui; public SampledSoundSettings[][] sampledSounds = new SampledSoundSettings[200][128]; public int recordMode; SamplerSettings samplerSettings = new Sampler20050227(); static public final int RECORDMODE_SINGLE_KEY = 0; static public final int RECORDMODE_ALL_KEYS = 1; static public final int RECORDMODE_SELECTION = 2; public static final int SAMPLEMODE_NO_LOOP = 0; public static final int SAMPLEMODE_LOOP_CONTINOUSLY = 1; public static final int SAMPLEMODE_LOOP_UNTIL_RELEASE = 3; public MySampler(SynthRack synth) { super(synth); sfi = new SoundFontImporter(this); getAudioOutput().addTransmitter(samplerOscillator); } public SoundFontImporter getImporter() { return(sfi); } public void insertSample(SampledSoundSettings snd, int noteNumber, int velocity) { /* if(!samplePool.contains(snd)) samplePool.add(snd); */ sampledSounds[noteNumber][velocity] = snd; } public void loadSettings(Serializable settings) { try { samplerSettings = (SamplerSettings)settings; setInstrumentName(samplerSettings.getInstrumentName()); if(samplerSettings.getSoundFontName()!=null) { try { sfi.getSoundFont(new java.io.File(samplerSettings.getSoundFontName())); } catch(FileNotFoundException fne) { SoundFontImporterGUI.getMissingSoundFont(new java.io.File(samplerSettings.getSoundFontName()),sfi); } System.out.println("InstrumentIndex: "+samplerSettings.getInstrumentIndex()); sfi.getInstrument(samplerSettings.getInstrumentIndex()); } else { sampledSounds = samplerSettings.getSampledSounds(); } } catch(Exception e) { e.printStackTrace(); } } public Serializable getSettings() { Sampler20050227 settings = new Sampler20050227(); try { if(MySampler.this.getFrinikaSynth().isSaveReferencedData()) throw new Exception("Also saving referenced data"); settings.setSoundFontName(sfi.file.getAbsolutePath()); settings.setInstrumentIndex(sfi.getInstrumentIndex()); settings.setInstrumentName(getInstrumentName()); } catch(Exception e) { settings.setInstrumentName(getInstrumentName()); settings.setSampledSounds(sampledSounds); } settings.setFreqSpread(samplerSettings.getFreqSpread()); settings.setLayers(samplerSettings.getLayers()); return(settings); } public SamplerSettings getSamplerSettings() { return samplerSettings; } /* (non-Javadoc) * @see com.petersalomonsen.mystudio.mysynth.Synth#close() */ public void close() { super.close(); getAudioOutput().removeTransmitter(samplerOscillator); } /* (non-Javadoc) * @see javax.sound.midi.MidiChannel#noteOn(int, int) */ public synchronized void noteOn(int noteNumber, int velocity) { if( samplerOscillator.isMonitoring() && !samplerOscillator.isRecording()) { switch(recordMode) { case RECORDMODE_SELECTION: if(!gui.isNoteInSelection(noteNumber,velocity)) break; default: samplerOscillator.startRecording(); Oscillator osc = new SamplingOscillator(this,noteNumber,velocity); addOscillator(noteNumber,osc); } } else { switch(samplerSettings.getLayers()) { case 2: SoundFontOscillator osc = new SoundFontOscillator(this,noteNumber,velocity); osc.setNoteNumber(noteNumber,-samplerSettings.getFreqSpread(),0); osc.setVelocity(velocity); addOscillator(noteNumber,osc); SoundFontOscillator layerOsc = new SoundFontOscillator(this,noteNumber,velocity); layerOsc.setNoteNumber(noteNumber,samplerSettings.getFreqSpread() ,1); layerOsc.setVelocity(velocity); osc.addLayer(layerOsc); break; case 3: osc = new SoundFontOscillator(this,noteNumber,velocity); osc.setNoteNumber(noteNumber,-samplerSettings.getFreqSpread(),0); osc.setVelocity(velocity); addOscillator(noteNumber,osc); layerOsc = new SoundFontOscillator(this,noteNumber,velocity); layerOsc.setNoteNumber(noteNumber,0,0.5f); layerOsc.setVelocity(velocity); osc.addLayer(layerOsc); layerOsc = new SoundFontOscillator(this,noteNumber,velocity); layerOsc.setNoteNumber(noteNumber,samplerSettings.getFreqSpread() ,1); layerOsc.setVelocity(velocity); osc.addLayer(layerOsc); break; case 4: osc = new SoundFontOscillator(this,noteNumber,velocity); osc.setNoteNumber(noteNumber,-samplerSettings.getFreqSpread(),0); osc.setVelocity(velocity); addOscillator(noteNumber,osc); layerOsc = new SoundFontOscillator(this,noteNumber,velocity); layerOsc.setNoteNumber(noteNumber,-samplerSettings.getFreqSpread()*0.33f,0.33f); layerOsc.setVelocity(velocity); osc.addLayer(layerOsc); layerOsc = new SoundFontOscillator(this,noteNumber,velocity); layerOsc.setNoteNumber(noteNumber,samplerSettings.getFreqSpread()*0.33f,0.66f); layerOsc.setVelocity(velocity); osc.addLayer(layerOsc); layerOsc = new SoundFontOscillator(this,noteNumber,velocity); layerOsc.setNoteNumber(noteNumber,samplerSettings.getFreqSpread() ,1); layerOsc.setVelocity(velocity); osc.addLayer(layerOsc); break; case 5: osc = new SoundFontOscillator(this,noteNumber,velocity); osc.setNoteNumber(noteNumber,-samplerSettings.getFreqSpread(),0); osc.setVelocity(velocity); addOscillator(noteNumber,osc); layerOsc = new SoundFontOscillator(this,noteNumber,velocity); layerOsc.setNoteNumber(noteNumber,-samplerSettings.getFreqSpread()*0.33f,0.33f); layerOsc.setVelocity(velocity); osc.addLayer(layerOsc); layerOsc = new SoundFontOscillator(this,noteNumber,velocity); layerOsc.setNoteNumber(noteNumber,0,0.5f); layerOsc.setVelocity(velocity); osc.addLayer(layerOsc); layerOsc = new SoundFontOscillator(this,noteNumber,velocity); layerOsc.setNoteNumber(noteNumber,samplerSettings.getFreqSpread()*0.33f,0.66f); layerOsc.setVelocity(velocity); osc.addLayer(layerOsc); layerOsc = new SoundFontOscillator(this,noteNumber,velocity); layerOsc.setNoteNumber(noteNumber,samplerSettings.getFreqSpread() ,1); layerOsc.setVelocity(velocity); osc.addLayer(layerOsc); break; case 6: osc = new SoundFontOscillator(this,noteNumber,velocity); osc.setNoteNumber(noteNumber,-samplerSettings.getFreqSpread(),0); osc.setVelocity(velocity); addOscillator(noteNumber,osc); layerOsc = new SoundFontOscillator(this,noteNumber,velocity); layerOsc.setNoteNumber(noteNumber,-samplerSettings.getFreqSpread()*0.60f,0.33f); layerOsc.setVelocity(velocity); osc.addLayer(layerOsc); layerOsc = new SoundFontOscillator(this,noteNumber,velocity); layerOsc.setNoteNumber(noteNumber,-samplerSettings.getFreqSpread()*0.20f,0.33f); layerOsc.setVelocity(velocity); osc.addLayer(layerOsc); layerOsc = new SoundFontOscillator(this,noteNumber,velocity); layerOsc.setNoteNumber(noteNumber,samplerSettings.getFreqSpread()*0.20f,0.66f); layerOsc.setVelocity(velocity); osc.addLayer(layerOsc); layerOsc = new SoundFontOscillator(this,noteNumber,velocity); layerOsc.setNoteNumber(noteNumber,samplerSettings.getFreqSpread()*0.60f,0.66f); layerOsc.setVelocity(velocity); osc.addLayer(layerOsc); layerOsc = new SoundFontOscillator(this,noteNumber,velocity); layerOsc.setNoteNumber(noteNumber,samplerSettings.getFreqSpread() ,1); layerOsc.setVelocity(velocity); osc.addLayer(layerOsc); break; default: osc = new SoundFontOscillator(this,noteNumber,velocity); osc.setNoteNumber(noteNumber,0,0.5f); osc.setVelocity(velocity); addOscillator(noteNumber,osc); } } } /* (non-Javadoc) * @see com.petersalomonsen.mystudio.mysynth.Synth#showGUI() */ public void showGUI() { gui = new SamplerGUI(this); } public class SamplingOscillator extends Oscillator{ int velocity; int noteNumber; boolean releaseTriggered = false; public SamplingOscillator(Synth synth, int noteNumber, int velocity) { super(synth); System.out.println(velocity); this.velocity = velocity; this.noteNumber = noteNumber; } /* (non-Javadoc) * @see com.petersalomonsen.mystudio.mysynth.Oscillator#release() */ public void release() { if(!releaseTriggered) { releaseTriggered = true; SampledSoundSettings sampledSound = new SampledSound20050403(); short[][] samples = ((MySampler)synth).samplerOscillator.stopRecording(); sampledSound.setLeftSamples(samples[0]); sampledSound.setRightSamples(samples[1]==null ? samples[0] : samples[1]); sampledSound.setRootKey(noteNumber); sampledSound.setSampleRate((int) FrinikaConfig.sampleRate); java.text.DateFormat dateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); sampledSound.setSampleName(dateFormat.format(new java.util.Date())); switch(recordMode) { case RECORDMODE_SINGLE_KEY: for(int v=0;v<128;v++) ((MySampler)synth).insertSample(sampledSound,noteNumber,v); break; case RECORDMODE_ALL_KEYS: for(int n = 0;n<96;n++) for(int v=0;v<128;v++) ((MySampler)synth).insertSample(sampledSound,n,v); break; case RECORDMODE_SELECTION: gui.insertSampleToSelection(sampledSound); break; } ((MySampler)synth).samplerOscillator.stopMonitor(); System.out.println("Record stored"); getAudioOutput().removeTransmitter(this); } } /* (non-Javadoc) * @see com.petersalomonsen.mystudio.audio.IAudioOutputGenerator#fillBuffer(int, int, float[]) */ public final void fillBuffer(int startBufferPos, int endBufferPos, float[] buffer) { } } public class SoundFontOscillator extends Oscillator{ SampledSoundSettings sampledSound; float samplesPerPeriod = (float)871.74; float samplePos = 0; float sampleIncrement; float dBFactor = 1; int velocity; int noteNumber; float attenuationPerSample = (float)0.1; short[] leftSamples; short[] rightSamples; int loopStart; int loopEnd; int sampleMode; float leftLevel; float rightLevel; VolumeEnvelope envelope = new VolumeEnvelope(sampleRate,-100f,0); Vector<SoundFontOscillator> layers = new Vector<SoundFontOscillator>(); public SoundFontOscillator(Synth synth, int noteNumber, int velocity) { super(synth); this.velocity = velocity; this.noteNumber = noteNumber; } public void addLayer(SoundFontOscillator osc) { layers.add(osc); } public void terminate() { //Force immediate release attenuationPerSample = envelope.getAttenuationPerSample(-100,-32768); release(); } /* (non-Javadoc) * @see com.petersalomonsen.mystudio.mysynth.OscillatorAdapter#setNoteNumber(int) */ public void setNoteNumber(int noteNumber, float customFineTune, float customPan) { this.noteNumber = noteNumber; sampledSound = sampledSounds[noteNumber][velocity]; leftSamples = sampledSound.getLeftSamples(); rightSamples = sampledSound.getRightSamples(); loopStart = sampledSound.getLoopStart(); loopEnd = sampledSound.getLoopEnd(); sampleMode = sampledSound.getSampleMode(); attenuationPerSample = envelope.getAttenuationPerSample(-100,sampledSound.getRelease()); float rootFreq = getFrequency(sampledSound.getRootKey()); samplesPerPeriod = (sampledSound.getSampleRate()/rootFreq) * PitchCents.getPitchCent(sampledSound.getFineTune()) * PitchCents.getRealPitchCent(customFineTune); frequency = rootFreq + ((getFrequency(noteNumber)-rootFreq) * (sampledSound.getScaleTune()/100f)); updateIncrement(); sampleIncrement = (float)(( increment / (2*Math.PI))*samplesPerPeriod); Pan pan = new Pan(customPan); leftLevel = pan.getLeftLevel(); rightLevel = pan.getRightLevel(); if(sampledSound.getExclusiveClass()>0) { int exclusiveClass = sampledSound.getExclusiveClass(); if(exclusiveClasses.containsKey(exclusiveClass)) { Vector<SoundFontOscillator>exOscillators = exclusiveClasses.get(exclusiveClass); if(exOscillators.get(0).noteNumber == noteNumber) exOscillators.add(this); else { for(SoundFontOscillator exOscillator : exOscillators) exOscillator.terminate(); exclusiveClasses.remove(exclusiveClass); } } if(!exclusiveClasses.containsKey(exclusiveClass)) { Vector<SoundFontOscillator> exOscillators = new Vector<SoundFontOscillator>(); exOscillators.add(this); exclusiveClasses.put(exclusiveClass,exOscillators); } } } public final void fillBuffer(int startBufferPos, int endBufferPos, float[] buffer) { for(int n=startBufferPos;n<endBufferPos;) { float left=0; float right=0; if(samplePos<(rightSamples.length-1)) { //linear interpolation int x1 = (int)samplePos; int x2 = x1+1; float posMinusX1 = samplePos - x1; short y1 = rightSamples[x1]; float dydx = (rightSamples[x2] - y1); right = (((dydx * posMinusX1 + y1) * level) / 32768f); y1 = leftSamples[x1]; dydx = (leftSamples[x2] - y1); left = (((dydx * posMinusX1 + y1) * level) / 32768f); if(dBFactor > 0.01) { left *= dBFactor * leftLevel; right *= dBFactor * rightLevel; if(release) dBFactor *= attenuationPerSample; samplePos+=(sampleIncrement * preOscillator.getLfoBuffer()[n / 2]); preOscillator.sampleBuffer[n++]+=left; preOscillator.sampleBuffer[n++]+=right; // Handle looping if(sampleMode == SAMPLEMODE_LOOP_CONTINOUSLY && samplePos>=loopEnd) samplePos = loopStart + (samplePos-loopEnd); } else { getAudioOutput().removeTransmitter(this); break; } } else { getAudioOutput().removeTransmitter(this); break; } } // Convert to array to avoid concurrent mod exception (don't use synchronized to avoid blocking) for(Object o : layers.toArray()) { SoundFontOscillator osc = (SoundFontOscillator)o; osc.release = this.release; osc.fillBuffer(startBufferPos,endBufferPos,buffer); } } } /** * */ public String toString() { return "MS: "+getInstrumentName(); } }