//////////////////////////////////////////////////////////////////////////////// // Copyright 2013 Michael Schmalle - Teoti Graphix, LLC // // 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 // // Author: Michael Schmalle, Principal Architect // mschmalle at teotigraphix dot com //////////////////////////////////////////////////////////////////////////////// package com.teotigraphix.caustk.tone.components; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import com.teotigraphix.caustk.core.osc.PatternSequencerMessage; import com.teotigraphix.caustk.tone.ToneComponent; public class PatternSequencerComponent extends ToneComponent { //-------------------------------------------------------------------------- // // IPatternSequencer API :: Properties // //-------------------------------------------------------------------------- //---------------------------------- // length //---------------------------------- public int getLength() { int measures = (int)PatternSequencerMessage.NUM_MEASURES.query(getEngine(), getToneIndex()); return measures; } public void setLength(int value) { PatternSequencerMessage.NUM_MEASURES.send(getEngine(), getToneIndex(), value); } public int getLength(int bankIndex, int patternIndex) { int lastBank = getSelectedBank(); int lastIndex = getSelectedIndex(); setSelectedPattern(bankIndex, patternIndex); int measures = (int)PatternSequencerMessage.NUM_MEASURES.query(getEngine(), getToneIndex()); setSelectedPattern(lastBank, lastIndex); return measures; } public void setLength(int bankIndex, int patternIndex, int value) { int lastBank = getSelectedBank(); int lastIndex = getSelectedIndex(); setSelectedPattern(bankIndex, patternIndex); PatternSequencerMessage.NUM_MEASURES.send(getEngine(), getToneIndex(), value); setSelectedPattern(lastBank, lastIndex); } //---------------------------------- // selectedBank //---------------------------------- public void setSelectedPattern(int bank, int pattern) { setSelectedBank(bank); setSelectedIndex(pattern); } //---------------------------------- // selectedBank //---------------------------------- private int selectedBank = 0; public int getSelectedBank() { return selectedBank; } public int getSelectedBank(boolean restore) { return (int)PatternSequencerMessage.BANK.query(getEngine(), getToneIndex()); } void setSelectedBank(int value) { if (value < 0 || value > 15) throw newRangeException("bank", "0..15", value); selectedBank = value; sendBankOSC(selectedBank); } //---------------------------------- // selectedIndex //---------------------------------- private int selectedIndex = 0; public int getSelectedIndex() { return selectedIndex; } public int getSelectedIndex(boolean restore) { return (int)PatternSequencerMessage.PATTERN.query(getEngine(), getToneIndex()); } void setSelectedIndex(int value) { if (value < 0 || value > 15) throw newRangeException("pattern", "0..15", value); selectedIndex = value; sendcPatternOSC(selectedIndex); } //---------------------------------- // patternListing //---------------------------------- public List<String> getPatternListing() { String patterns = PatternSequencerMessage.QUERY_PATTERNS_WITH_DATA.queryString(getEngine(), getToneIndex()); if (patterns == null || "".equals(patterns)) return new ArrayList<String>(); return Arrays.asList(patterns.split(" ")); } public void initializeData(String data) { if (data.equals("")) return; // push the notes into the machines sequencer String[] notes = data.split("\\|"); for (String noteData : notes) { String[] split = noteData.split(" "); float start = Float.valueOf(split[0]); int pitch = Float.valueOf(split[1]).intValue(); float velocity = Float.valueOf(split[2]); float end = Float.valueOf(split[3]); int flags = Float.valueOf(split[4]).intValue(); addNote(pitch, start, end, velocity, flags); } } //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- public PatternSequencerComponent() { } //-------------------------------------------------------------------------- // // IPatternSequencer API :: Methods // //-------------------------------------------------------------------------- public void clear() { for (int bank = 0; bank < 4; bank++) { clearBank(bank); } } public void clearBank(int index) { PatternSequencerMessage.CLEAR_BANK.send(getEngine(), getToneIndex(), index); } /** * Clears the bank and index while holding the selected bank and pattern * index. * <p> * Use this is the selected bank and pattern cannot change. * * @param bank The bank to clear. * @param index The pattern to clear. */ public void clearIndex(int bank, int index) { int oldBank = getSelectedBank(true); sendBankOSC(bank); PatternSequencerMessage.CLEAR_PATTERN.send(getEngine(), getToneIndex(), index); sendBankOSC(oldBank); } /** * Clears the pattern using the current bank selected. * * @param index The pattern index to clear. */ public void clearIndex(int index) { PatternSequencerMessage.CLEAR_PATTERN.send(getEngine(), getToneIndex(), index); } final void sendBankOSC(int bank) { PatternSequencerMessage.BANK.send(getEngine(), getToneIndex(), bank); } final void sendcPatternOSC(int pattern) { PatternSequencerMessage.PATTERN.send(getEngine(), getToneIndex(), pattern); } public void addNote(int pitch, float start, float end, float velocity, int flags) { PatternSequencerMessage.NOTE_DATA.send(getEngine(), getToneIndex(), start, pitch, velocity, end, flags); } public void addNote(int bankIndex, int patternIndex, int pitch, float start, float end, float velocity, int flags) { int lastBank = getSelectedBank(); int lastIndex = getSelectedIndex(); setSelectedPattern(bankIndex, patternIndex); addNote(pitch, start, end, velocity, flags); setSelectedPattern(lastBank, lastIndex); } public void removeNote(int pitch, float start) { PatternSequencerMessage.NOTE_DATA_REMOVE.send(getEngine(), getToneIndex(), start, pitch); } public void removeNote(int bankIndex, int patternIndex, int pitch, float start) { int lastBank = getSelectedBank(); int lastIndex = getSelectedIndex(); setSelectedPattern(bankIndex, patternIndex); removeNote(pitch, start); setSelectedPattern(lastBank, lastIndex); } public void triggerOn(Resolution resolution, int step, int pitch, float gate, float velocity, int flags) { float start = Resolution.toBeat(step, resolution); float end = start + gate; addNote(pitch, start, end, velocity, flags); } public void triggerOn(int bankIndex, int patternIndex, Resolution resolution, int step, int pitch, float gate, float velocity, int flags) { int lastBank = getSelectedBank(); int lastIndex = getSelectedIndex(); setSelectedPattern(bankIndex, patternIndex); triggerOn(resolution, step, pitch, gate, velocity, flags); setSelectedPattern(lastBank, lastIndex); } public void triggerOff(Resolution resolution, int step, int pitch) { float start = Resolution.toBeat(step, resolution); removeNote(pitch, start); } public void triggerOff(int bankIndex, int patternIndex, Resolution resolution, int step, int pitch) { int lastBank = getSelectedBank(); int lastIndex = getSelectedIndex(); setSelectedPattern(bankIndex, patternIndex); triggerOff(resolution, step, pitch); setSelectedPattern(lastBank, lastIndex); } @Override public void restore() { } /** * @author Michael Schmalle * @copyright Teoti Graphix, LLC * @since 1.0 */ public enum Resolution { /** * A whole note. */ WHOLE(1f), // 1 /** * A half note. */ HALF(0.5f), // 2 /** * A quarter note. */ QUATER(0.25f), // 4 /** * An eighth note. */ EIGHTH(0.125f), // 8 /** * A sixteenth note. */ SIXTEENTH(0.0625f), // 16 /** * A thirtysecond note. */ THIRTYSECOND(0.03125f), // 32 /** * A sixtyfourth note. */ SIXTYFOURTH(0.015625f); // 1 / 0.015625f = 64 Resolution(float value) { mValue = value; } private float mValue; /** * Returns the amount of steps in a measure for the given phrase * resolution. * * @param resolution The note resolution. * @return The number of steps in a measure for the given phrase * resolution. */ public final static int toSteps(Resolution resolution) { return (int)(1 / resolution.getValue()); } public float getValue() { return mValue; } private static int beatsInMeasure = 4; public static int toStep(float beat, Resolution resolution) { // (beat(5) / 0.0625) / 4 return (int)(beat / resolution.getValue()) / beatsInMeasure; } public static float toBeat(int step, Resolution resolution) { return (step * resolution.getValue()) * beatsInMeasure; } } }