/* * FluidSynthesizer.java * * This file is part of Tritonus: http://www.tritonus.org/ */ /* * Copyright (c) 2006 by Henri Manson * Copyright (c) 2006 by Matthias Pfisterer * 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. * * 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. */ /* |<--- this code is formatted to fit into 80 columns --->| */ package org.tritonus.midi.device.fluidsynth; import javax.sound.midi.*; import org.tritonus.share.TDebug; import org.tritonus.share.midi.TMidiChannel; import org.tritonus.share.midi.TDirectSynthesizer; import org.tritonus.midi.sb.fluidsynth.FluidSoundbank; public class FluidSynthesizer extends TDirectSynthesizer implements Synthesizer { private MidiChannel channels[]; private FluidSoundbank defaultSoundbank; private int defaultbankSfontID; // native pointers 64 bit maximum private long settingsPtr; private long synthPtr; private long audioDriverPtr; static { loadNativeLibrary(); } /** Load the native library for fluidsynth. */ private static void loadNativeLibrary() { if (TDebug.TraceFluidNative) TDebug.out("FluidSynthesizer.loadNativeLibrary(): loading native library tritonusfluid"); try { System.loadLibrary("tritonusfluid"); // only reached if no exception occures setTrace(TDebug.TraceFluidNative); } catch (Error e) { if (TDebug.TraceFluidNative || TDebug.TraceAllExceptions) { TDebug.out(e); } // throw e; } if (TDebug.TraceFluidNative) TDebug.out("FluidSynthesizer.loadNativeLibrary(): loaded"); } /** * Constructor. */ public FluidSynthesizer(MidiDevice.Info info) throws Exception { super(info); } protected void openImpl() throws MidiUnavailableException { if (newSynth() < 0) { throw new MidiUnavailableException("Low-level initialization of the synthesizer failed"); } if (TDebug.TraceSynthesizer) TDebug.out("FluidSynthesizer: " + Long.toHexString(synthPtr)); channels = new MidiChannel[16]; for (int i = 0; i < 16; i++) { channels[i] = new NewFluidMidiChannel(i); } String sfontFile = System.getProperty("tritonus.fluidsynth.defaultsoundbank"); if (sfontFile != null && ! sfontFile.equals("")) { int sfontID = loadSoundFont(sfontFile); setDefaultSoundBank(sfontID); String strBankOffset = System.getProperty("tritonus.fluidsynth.defaultsoundbankoffset"); if (strBankOffset != null && ! strBankOffset.equals("")) { setBankOffset(sfontID, Integer.parseInt(strBankOffset)); } } } protected void closeImpl() { if (TDebug.TraceSynthesizer) TDebug.out("FluidSynthesizer.closeImpl(): " + Long.toHexString(synthPtr)); deleteSynth(); super.closeImpl(); } public void setDefaultSoundBank(int sfontID) { defaultSoundbank = new FluidSoundbank(this, sfontID); defaultbankSfontID = sfontID; } protected void finalize(){ if (TDebug.TraceSynthesizer) TDebug.out("finalize: " + Long.toHexString(synthPtr)); close(); } public native int loadSoundFont(String filename); public native void setBankOffset(int sfontID, int offset); public native void setGain(float gain); /* $$mp: currently not functional because fluid_synth_set_reverb_preset() * is not present in fluidsynth 1.0.6. */ public native void setReverbPreset(int reverbPreset); public native int getMaxPolyphony(); protected native int newSynth(); protected native void deleteSynth(); /** * Turns a note on. * * The implementation calls fluid_synth_noteoff(). * * @param nChannel the channel * @param nNoteNumber the note * @param nVelocity the velocity */ native void noteOn(int nChannel, int nNoteNumber, int nVelocity); /** * Turns a note off. * * The implementation calls fluid_synth_noteon(). * * @param nChannel the channel * @param nNoteNumber the note * @param nVelocity the velocity */ native void noteOff(int nChannel, int nNoteNumber, int nVelocity); /** * Changes a controller on the synthesizer. * * The implementation calls fluid_synth_cc(). * * @param nChannel the channel * @param nController the controller number * @param nValue the controller value */ native void controlChange(int nChannel, int nController, int nValue); /** * Obtains the value of a controller. * * The implementation calls fluid_synth_get_cc(). * * @param nChannel the channel * @param nController the controller number * @return the controller value */ native int getController(int nChannel, int nController); /** * Sets the program for a channel. * * The implementation calls fluid_synth_program_change(). * * @param nChannel the channel * @param nProgram the program number */ native void programChange(int nChannel, int nProgram); /** * Obtains the program set for a channel. * * The implementation calls fluid_synth_get_program(). * * @param nChannel the channel * @return the program number set for this channel */ native int getProgram(int nChannel); /** * Sets the pitch bend for a channel. * * The implementation calls fluid_synth_pitch_bend(). * * @param nChannel the channel * @param nBend the pitch bend value */ native void setPitchBend(int nChannel, int nBend); /** * Obtains the pitch bend for a channel. * * The implementations calls fluid_synth_get_pitch_bend(). * * @param nChannel the channel * @return the pitch bend value. */ native int getPitchBend(int nChannel); /** Sets tracing in the native code. * Note that this method can either be called directly or (recommended) * the system property "tritonus.TraceFluidNative" can be set to true. * * @see org.tritonus.share.TDebug */ public static native void setTrace(boolean bTrace); public boolean isSoundbankSupported(Soundbank soundbank) { return (soundbank instanceof FluidSoundbank); } public boolean loadAllInstruments(Soundbank soundbank) { checkSoundbank(soundbank); return true; } public void unloadAllInstruments(Soundbank soundbank) { checkSoundbank(soundbank); } public void unloadInstruments(Soundbank soundbank, Patch[] patchList) { checkSoundbank(soundbank); } public boolean loadInstruments(Soundbank soundbank, Patch[] patchList) { checkSoundbank(soundbank); return true; } public void unloadInstrument(Instrument instrument) { checkInstrument(instrument); } public boolean loadInstrument(Instrument instrument) { checkInstrument(instrument); return true; } public Instrument[] getAvailableInstruments() { return null; } public MidiChannel[] getChannels() { return channels; } public Soundbank getDefaultSoundbank() { return defaultSoundbank; } public long getLatency() { return 0L; } public Instrument[] getLoadedInstruments() { return null; } public VoiceStatus[] getVoiceStatus() { return new VoiceStatus[0]; } public boolean remapInstrument(Instrument from, Instrument to) { checkInstrument(from); checkInstrument(to); return true; } /** Checks if the soundbank is supported by this synthesizer implementation. * * @param sb the soundbank to check * @throws IllegalArgumentException if the soundbank is not supported */ private void checkSoundbank(Soundbank sb) { if (! isSoundbankSupported(sb)) throw new IllegalArgumentException("soundbank is not supported"); } /** * Checks if the instrument belongs to a soundbank that is supported by this * synthesizer implementation. * * @param instr the instrument to check * @throws IllegalArgumentException if the instrument's soundbank * is not supported */ private void checkInstrument(Instrument instr) { checkSoundbank(instr.getSoundbank()); } private class NewFluidMidiChannel extends TMidiChannel { public NewFluidMidiChannel(int nChannel) { super(nChannel); } public void noteOn(int nNoteNumber, int nVelocity) { FluidSynthesizer.this.noteOn(getChannel(), nNoteNumber, nVelocity); } public void noteOff(int nNoteNumber, int nVelocity) { FluidSynthesizer.this.noteOff(getChannel(), nNoteNumber, nVelocity); } public void noteOff(int nNoteNumber) { noteOff(nNoteNumber, 0); } /** * Fluidsynth does not implement poly pressure (aftertouch). Therefore, * this method does nothing. */ public void setPolyPressure(int nNoteNumber, int nPressure) { } /** * Fluidsynth does not implement poly pressure (aftertouch). Therefore, * this method always return 0. */ public int getPolyPressure(int nNoteNumber) { return 0; } /** * Fluidsynth does not implement channel pressure. Therefore, * this method does nothing. */ public void setChannelPressure(int nPressure) { } /** * Fluidsynth does not implement channel pressure. Therefore, * this method always returns 0. */ public int getChannelPressure() { return 0; } public void controlChange(int nController, int nValue) { FluidSynthesizer.this.controlChange(getChannel(), nController, nValue); } public int getController(int nController) { return FluidSynthesizer.this.getController(getChannel(), nController); } public void programChange(int nProgram) { FluidSynthesizer.this.programChange(getChannel(), nProgram); } public int getProgram() { return FluidSynthesizer.this.getProgram(getChannel()); } public void setPitchBend(int nBend) { FluidSynthesizer.this.setPitchBend(getChannel(), nBend); } public int getPitchBend() { return FluidSynthesizer.this.getPitchBend(getChannel()); } // TODO: emulate by manipulating volume public void setMute(boolean bMute) { } public boolean getMute() { return false; } public void setSolo(boolean bSolo) { } public boolean getSolo() { return false; } } } /* FluidSynthesizer.java */