Android Open Source - OpenSynth Controller






From Project

Back to project page OpenSynth.

License

The source code is released under:

Apache License

If you think the Android project OpenSynth listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright 2013 Daisuke Fuji <daisuke@indigo-lab.com>
 * /*  w  ww  .  ja v a2s. c om*/
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.thebends.synth;

/**
 * The controller module returns samples and drives the oscillator and envelope.
 */
public class Controller {
    // A shift in frequency by the specified amount.  The frequency gets
    // multiplied by 2^n
    public enum OctaveShift {
        OCTAVE_1(1),  // No shift
        OCTAVE_2(2),
        OCTAVE_4(4),
        OCTAVE_8(8),
        OCTAVE_16(16);

        private int mIntValue;

        OctaveShift(final int intValue) {
            mIntValue = intValue;
        }

        public int getIntValue() {
            return mIntValue;
        }
    };

    public enum ModulationSource {
        LFO_SRC_SQUARE,
        LFO_SRC_TRIANGLE,
        LFO_SRC_SAWTOOTH,
        LFO_SRC_REVERSE_SAWTOOTH,
        // TODO(allen): How do you determine the sustain length?
        // Is it sustain = period - (attack + decay + release)?
        //        LFO_FILTER_ENVELOPE,
        // TODO(allen): OSC2 needs a manual frequency control for this to work, i think.
        //        OSC2,
    };

    public enum ModulationDestination {
        LFO_DEST_WAVE,  // Tremelo
        LFO_DEST_PITCH,  // Vibrato
        LFO_DEST_FILTER,
        // TODO(allen): Is this Ring modulation?
        // LFO_DEST_OSC2,
    };

    private KeyStack mKeyStack = new KeyStack();
    private MutableParameter mKeyFrequency = new MutableParameter();

    private boolean mArpeggioEnabled;
    private Arpeggio mArpeggio;
    private LagProcessor mKeyLagProcessor;

    private Oscillator mOsc1 = new Oscillator();
    private Oscillator mOsc2 = new Oscillator();

    // The two oscillators combined
    private KeyboardOscillator mCombinedOsc;
    private Volume mVolume = new Volume();

    private ModulationSource mModulationSource;
    private ModulationDestination mModulationDestination;
    private MutableParameter mModulationFrequency;
    private Oscillator mModulationOsc = new Oscillator();
    private MutableParameter mModulationAmount;
    private LFO mModulation = new LFO();

    private FilterCutoff mFilterCutoff = new FilterCutoff();
    private LowPassFilter mLowpassFilter = new LowPassFilter();
    private ResonantFilter mResonantFilter = new ResonantFilter();

    public Controller() {
        mKeyFrequency = new MutableParameter();
        mArpeggioEnabled = false;
        mArpeggio = new Arpeggio(mKeyStack);
        mKeyLagProcessor = new LagProcessor(mArpeggio);
        mCombinedOsc = new KeyboardOscillator(mOsc1, mOsc2, mKeyLagProcessor);
        mModulationSource = ModulationSource.LFO_SRC_SQUARE;
        mModulationDestination = ModulationDestination.LFO_DEST_WAVE;
        mModulationFrequency = new MutableParameter();
        mModulationAmount = new MutableParameter();
        mModulationOsc.setFrequency(mModulationFrequency);
        mModulation.setOscillator(mModulationOsc);
        mModulation.setLevel(mModulationAmount);

        mLowpassFilter.setCutoff(mFilterCutoff);
        mResonantFilter.setCutoff(mFilterCutoff);

        resetRouting();
    }

    // Volume [0, 1.0]
    public void setVolume(float volume) {
        mVolume.setLevel(volume);
    }

    /**
     * Start/Stop playing a note.  These may trigger the Attack and Release of the
     * volume and filter envelopes, depending on the order of the on/off events.
     * It is an error to call NoteOff() for a note that was never the argument of
     * noteOn();
     */
    public void noteOn(int note) {
        assert note >= 1;
        assert note <= 88;
        mKeyStack.noteOn(note);
        if (mKeyStack.size() == 1) {
            // This is the first note played, so start attacking
            mKeyLagProcessor.reset();
            mArpeggio.reset();
            volumeEnvelope().noteOn();
            filterEnvelope().noteOn();
        }
        double frequency = KeyStack.toFrequency(mKeyStack.getCurrentNote());
        mKeyFrequency.setValue(frequency);
    }

    public void noteOff(int note) {
        mKeyStack.noteOff(note);
        if (mKeyStack.size() == 0) {
            // All notes were release, so start the release phase of the envelope
            noteOff();
        } else {
            // There are still notes on key stack -- switch!
            double frequency = KeyStack.toFrequency(mKeyStack.getCurrentNote());
            mKeyFrequency.setValue(frequency);
        }
    }

    /**
     * For testing
     */
    public void noteOnFrequency(float frequency) {
        mKeyFrequency.setValue(frequency);
        volumeEnvelope().noteOn();
        filterEnvelope().noteOn();
    }

    /**
     * Invoked when all notes have been released as a fallback
     */
    public void noteOff() {
        mKeyStack.clear();
        volumeEnvelope().noteOff();
        filterEnvelope().noteOff();
    }

    /**
     * True when nothing is playing
     */
    public boolean isReleased() {
        return (volumeEnvelope().isReleased() || filterEnvelope().isReleased());
    }

    /**
     * For testing
     */
    public void setSampleRate(float sample_rate) {
        mOsc1.setSampleRate(sample_rate);
        mOsc2.setSampleRate(sample_rate);
    }


    // OSC 1

    /**
     * Set the volume of oscillator
     */
    public void setOsc1Level(double level) {
        mCombinedOsc.setOsc1Level(level);
    }

    /**
     * Set the wave form of oscillator
     */
    public void setOsc1WaveForm(Oscillator.WaveForm waveForm) {
        mOsc1.setWaveForm(waveForm);
    }

    /**
     * The oscillator frequency is shifted by the specified amount.
     */
    public void setOsc1Octave(OctaveShift octave) {
        mCombinedOsc.setOsc1Octave(octave.getIntValue());
    }


    // OSC 2

    public void setOsc2Level(double level) {
        mCombinedOsc.setOsc2Level(level);
    }

    public void setOsc2WaveForm(Oscillator.WaveForm waveForm) {
        mOsc2.setWaveForm(waveForm);
    }

    public void setOsc2Octave(OctaveShift octave) {
        mCombinedOsc.setOsc2Octave(octave.getIntValue());
    }

    public void setOsc2Shift(int cents) {
        mCombinedOsc.setOsc2Shift(cents);
    }

    public void setOscSync(boolean sync) {
        mCombinedOsc.setOscSync(sync);
    }

    public Envelope volumeEnvelope() {
        return mVolume.envelope();
    }

    public Envelope filterEnvelope() {
        return mFilterCutoff.envelope();
    }

    public void setModulationSource(ModulationSource source) {
        mModulationSource = source;
        resetRouting();
    }

    public void setModulationDestination(ModulationDestination dest) {
        mModulationDestination = dest;
        resetRouting();
    }

    public void setModulationAmount(float amount) {
        mModulationAmount.setValue(amount);
    }

    public void setModulationFrequency(float frequency) {
        mModulationFrequency.setValue(frequency);
    }

    public void setFilterCutoff(float frequency) {
        mFilterCutoff.setCutoff(frequency);
    }

    /**
     *  [0.0, 1.0]
     */
    public void setFilterResonance(float value) {
        mResonantFilter.setResonance(value);
    }

    public void setGlideSamples(long samples) {
        mKeyLagProcessor.setSamples(samples);
    }

    public void setArpeggioEnabled(boolean enabled) {
        mArpeggioEnabled = enabled;
        resetRouting();
    }

    public void setArpeggioSamples(long samples) {
        mArpeggio.setSamplesPerNote(samples);
    }

    public void setArpeggioOctaves(int octaves) {
        mArpeggio.setOctaves(octaves);
    }

    public void setArpeggioStep(Arpeggio.Step step) {
        mArpeggio.setStep(step);
    }

    /**
     * Get a single sample
     */
    public double getSample() {
        if (volumeEnvelope().isReleased() || filterEnvelope().isReleased()) {
            return 0;
        }

        // Combined oscillators, volume/envelope/modulation
        double value = mCombinedOsc.getValue();
        // Clip!
        value = Math.max(-1.0f, value);
        value = Math.min(1.0f, value);
        // Combined filter with envelope/modulation
        value = mLowpassFilter.getValue(value);
        value = mResonantFilter.getValue(value);
        // Clip!
        value = Math.max(-1.0f, value);
        value = Math.min(1.0f, value);
        // Adjust volume
        value *= mVolume.getValue();
        return value;
    }

    public void getSamples(double[] buffer, int size) {
        for (int i = 0; i < size; ++i) {
            buffer[i] = getSample();
        }
    }

    public void getSamples(short[] buffer, int size) {
        for (int i = 0; i < size; ++i) {
            buffer[i] = (short)(getSample() * Short.MAX_VALUE);
        }
    }

    /*
     * Invoked when one of the routing parameters changes, such as the source
     * or destination of modulation.
     */
    private void resetRouting() {
        switch (mModulationSource) {
        case LFO_SRC_SQUARE:
            mModulationOsc.setWaveForm(Oscillator.WaveForm.SQUARE);
            break;
        case LFO_SRC_TRIANGLE:
            mModulationOsc.setWaveForm(Oscillator.WaveForm.TRIANGLE);
            break;
        case LFO_SRC_SAWTOOTH:
            mModulationOsc.setWaveForm(Oscillator.WaveForm.SAWTOOTH);
            break;
        case LFO_SRC_REVERSE_SAWTOOTH:
            mModulationOsc.setWaveForm(Oscillator.WaveForm.REVERSE_SAWTOOTH);
         break;
        default:
            assert false;
        }

        // Reset the destinations
        mVolume.setModulation(null);
        mFilterCutoff.setModulation(null);
        mCombinedOsc.setFrequencyModulation(null);

        // Route modulation into the correct pipeline
        switch (mModulationDestination) {
        case LFO_DEST_WAVE:
            // Modulate the volume (tremelo)
            mVolume.setModulation(mModulation);
            break;
        case LFO_DEST_PITCH:
            // Modulate the frequency (vibrato)
            mCombinedOsc.setFrequencyModulation(mModulation);
            break;
        case LFO_DEST_FILTER:
            // Modulate the cutoff frequency
            mFilterCutoff.setModulation(mModulation);
            break;
        default:
            assert false;
        }

        if (mArpeggioEnabled) {
            mKeyLagProcessor.setParam(mArpeggio);
        } else {
            mKeyLagProcessor.setParam(mKeyFrequency);
        }
    }
}




Java Source Code List

com.google.synthesizer.android.widgets.piano.BlackPianoKey.java
com.google.synthesizer.android.widgets.piano.NotePianoKey.java
com.google.synthesizer.android.widgets.piano.OctavePianoKey.java
com.google.synthesizer.android.widgets.piano.PianoKey.java
com.google.synthesizer.android.widgets.piano.PianoViewListener.java
com.google.synthesizer.android.widgets.piano.PianoView.java
com.google.synthesizer.android.widgets.piano.WhitePianoKey.java
com.google.synthesizer.core.midi.MidiListener.java
com.google.synthesizer.core.music.Note.java
com.indigo_lab.android.opensynth.OpenSynthActivity.java
com.indigo_lab.android.opensynth.ViewPagerAdapter.java
com.indigo_lab.android.opensynth.view.ArpeggioView.java
com.indigo_lab.android.opensynth.view.ControllerView.java
com.indigo_lab.android.opensynth.view.EnvelopeView.java
com.indigo_lab.android.opensynth.view.FilterEnvelopeView.java
com.indigo_lab.android.opensynth.view.FilterView.java
com.indigo_lab.android.opensynth.view.ImageRadioButton.java
com.indigo_lab.android.opensynth.view.ModulationView.java
com.indigo_lab.android.opensynth.view.OscillatorDetailView.java
com.indigo_lab.android.opensynth.view.OscillatorView.java
com.indigo_lab.android.opensynth.view.RadioButton.java
com.indigo_lab.android.opensynth.view.VolumeEnvelopeView.java
org.thebends.synth.Arpeggio.java
org.thebends.synth.Controller.java
org.thebends.synth.Envelope.java
org.thebends.synth.FilterCutoff.java
org.thebends.synth.Filter.java
org.thebends.synth.FixedParameter.java
org.thebends.synth.KeyStack.java
org.thebends.synth.KeyboardOscillator.java
org.thebends.synth.LFO.java
org.thebends.synth.LagProcessor.java
org.thebends.synth.LowPassFilter.java
org.thebends.synth.MutableParameter.java
org.thebends.synth.Oscillator.java
org.thebends.synth.Parameter.java
org.thebends.synth.ResonantFilter.java
org.thebends.synth.SynthJni.java
org.thebends.synth.SynthTrack.java
org.thebends.synth.Volume.java