package jp.sourceforge.ocmml.android; import java.nio.DoubleBuffer; import java.util.ArrayList; public class Track { public static final int TEMPO_TRACK = 0; public static final int FIRST_TRACK = 1; public static final int DEFAULT_BPM = 120; public static final int EVENT_ALLOCATE_MIN = 2; public static final int EVENT_TYPE = 0; public static final int EVENT_DELTA = 1; public static final int EVENT_TYPE_EOT = 0; public static final int EVENT_TYPE_NOP = 1; public static final int EVENT_TYPE_NOTE_ON = 2; public static final int EVENT_TYPE_NOTE_OFF = 3; public static final int EVENT_TYPE_TEMPO = 4; public static final int EVENT_TYPE_VOLUME = 5; public static final int EVENT_TYPE_NOTE = 6; public static final int EVENT_TYPE_FORM = 7; public static final int EVENT_TYPE_ENVELOPE_FOR_VCO = 8; public static final int EVENT_TYPE_NOISE_FREQUENCY = 9; public static final int EVENT_TYPE_PWM = 10; public static final int EVENT_TYPE_PAN = 11; public static final int EVENT_TYPE_FORMANT = 12; public static final int EVENT_TYPE_DETUNE = 13; public static final int EVENT_TYPE_LFO = 14; public static final int EVENT_TYPE_LPF = 15; public static final int EVENT_TYPE_CLOSE = 16; public static final int EVENT_TYPE_VOLUME_MODE = 17; public static final int EVENT_TYPE_ENVELOPE_FOR_VCF = 18; public static final int EVENT_TYPE_INPUT = 19; public static final int EVENT_TYPE_OUTPUT = 20; public static final int EVENT_TYPE_EXPRESSION = 21; public Track() { mEnd = false; mChannel = new Channel(); mEvents = new ArrayList(); mIndex = 0; mDelta = 0; mGlobalTick = 0; mNeedle = 0.0; mDuration = 0; setBPM(DEFAULT_BPM); recordGate(15.0 / 16.0); recordGate(0); } public void getSamples(DoubleBuffer samples, int start, int end, Boolean update) { if (mEnd) return; int eventCount = mEvents.size(), i = start; while (i < end) { Boolean loop = false; double delta = 0; do { loop = false; if (mIndex < eventCount) { int[] e = mEvents.get(mIndex); delta = e[EVENT_DELTA] * mSPT; if (mNeedle >= delta) { loop = true; switch (e[EVENT_TYPE]) { case EVENT_TYPE_NOTE_ON: mChannel.enableNote(e); break; case EVENT_TYPE_NOTE_OFF: mChannel.disableNote(); break; case EVENT_TYPE_NOTE: mChannel.setNoteIndex(e[EVENT_DELTA + 1]); break; case EVENT_TYPE_TEMPO: mBPM = e[EVENT_DELTA + 1]; break; case EVENT_TYPE_FORM: mChannel.setForm(e); break; case EVENT_TYPE_ENVELOPE_FOR_VCO: mChannel.setASDRForVCO(e); break; case EVENT_TYPE_ENVELOPE_FOR_VCF: mChannel.setASDRForVCF(e); break; case EVENT_TYPE_NOISE_FREQUENCY: mChannel.setNoiseFrequency(e[EVENT_DELTA + 1]); break; case EVENT_TYPE_PWM: mChannel.setPWM(e[EVENT_DELTA + 1]); break; case EVENT_TYPE_PAN: mChannel.setPan(e[EVENT_DELTA + 1]); break; case EVENT_TYPE_FORMANT: mChannel.setFormantVowel(e[EVENT_DELTA + 1]); break; case EVENT_TYPE_DETUNE: mChannel.setDetune(e[EVENT_DELTA + 1]); break; case EVENT_TYPE_LFO: double width = e[EVENT_DELTA + 4] * mSPT; e[EVENT_DELTA + 5] = (int) (e[EVENT_DELTA + 5] * mSPT); e[EVENT_DELTA + 6] = (int) (e[EVENT_DELTA + 6] * width); mChannel.setLFO(e, Sample.RATE / width); case EVENT_TYPE_LPF: mChannel.setLPF(e); break; case EVENT_TYPE_VOLUME_MODE: mChannel.setVolumeMode(e[EVENT_DELTA + 1]); break; case EVENT_TYPE_INPUT: mChannel.setInput(e); break; case EVENT_TYPE_OUTPUT: mChannel.setOutput(e); break; case EVENT_TYPE_EXPRESSION: mChannel.setExpression(e[EVENT_DELTA + 1]); break; case EVENT_TYPE_CLOSE: mChannel.close(); break; case EVENT_TYPE_EOT: mEnd = true; default: break; } mNeedle -= delta; mIndex++; } } } while (loop); int di = 0; if (mIndex < eventCount) { int[] e = mEvents.get(mIndex); delta = e[EVENT_DELTA] * mSPT; di = (int) Math.ceil(delta - mNeedle); if (i + di >= end) di = end - i; mNeedle += di; if (update) mChannel.getSamples(samples, i, di, end); i += di; } else break; } } public void seek() { mGlobalTick = 0; } public void seek(int delta) { mDelta += delta; mGlobalTick += delta; } public void recordNote(int index, int length, int velocity, Boolean keyOn, Boolean keyOff) { int[] e; if (keyOn) { e = new int[EVENT_ALLOCATE_MIN + 2]; e[EVENT_TYPE] = EVENT_TYPE_NOTE_ON; e[EVENT_DELTA + 1] = index; e[EVENT_DELTA + 2] = velocity; } else { e = new int[EVENT_ALLOCATE_MIN + 1]; e[EVENT_TYPE] = EVENT_TYPE_NOTE; e[EVENT_DELTA + 1] = index; } setDeltaAndAddEvent(e); if (keyOff) { int gate = Math.max((int) (length * mGate - mGate2), 0); seek(gate); e = new int[EVENT_ALLOCATE_MIN + 2]; e[EVENT_TYPE] = EVENT_TYPE_NOTE_OFF; e[EVENT_DELTA + 1] = index; e[EVENT_DELTA + 2] = velocity; setDeltaAndAddEvent(e); seek(length - gate); } else seek(length); } public void recordRest(int length) { seek(length); } public void recordRest(long msec) { seek((int) (msec * Sample.RATE / (mSPT * 1000))); } public void recordVolume(int volume) { int[] e = new int[EVENT_ALLOCATE_MIN + 1]; e[EVENT_TYPE] = EVENT_TYPE_VOLUME; ; e[EVENT_DELTA + 1] = volume; setDeltaAndAddEvent(e); } public void recordTempo(int tempo, long globalTick) { int[] e = new int[EVENT_ALLOCATE_MIN + 1]; e[EVENT_TYPE] = EVENT_TYPE_TEMPO; ; e[EVENT_DELTA + 1] = tempo; recordGlobalTick(globalTick, e); } public void recordEOT() { int[] e = new int[EVENT_ALLOCATE_MIN]; e[EVENT_TYPE] = EVENT_TYPE_EOT; setDeltaAndAddEvent(e); } public void recordGate(double gate) { mGate = gate; } public void recordGate(int gate) { mGate2 = Math.max(gate, 0); } public void recordForm(int mainForm, int subForm) { int[] e = new int[EVENT_ALLOCATE_MIN + 2]; e[EVENT_TYPE] = EVENT_TYPE_FORM; e[EVENT_DELTA + 1] = mainForm; e[EVENT_DELTA + 2] = subForm; setDeltaAndAddEvent(e); } public void recordNoiseFrequency(int frequency) { int[] e = new int[EVENT_ALLOCATE_MIN + 1]; e[EVENT_TYPE] = EVENT_TYPE_NOISE_FREQUENCY; e[EVENT_DELTA + 1] = frequency; setDeltaAndAddEvent(e); } public void recordPWM(int pwm) { int[] e = new int[EVENT_ALLOCATE_MIN + 1]; e[EVENT_TYPE] = EVENT_TYPE_PWM; e[EVENT_DELTA + 1] = pwm; setDeltaAndAddEvent(e); } public void recordPan(int pan) { int[] e = new int[EVENT_ALLOCATE_MIN + 1]; e[EVENT_TYPE] = EVENT_TYPE_PAN; e[EVENT_DELTA + 1] = pan; setDeltaAndAddEvent(e); } public void recordFormantVowel(int vowel) { int[] e = new int[EVENT_ALLOCATE_MIN + 1]; e[EVENT_TYPE] = EVENT_TYPE_VOLUME; e[EVENT_DELTA + 1] = vowel; setDeltaAndAddEvent(e); } public void recordVolumeMode(int mode) { int[] e = new int[EVENT_ALLOCATE_MIN + 1]; e[EVENT_TYPE] = EVENT_TYPE_VOLUME_MODE; e[EVENT_DELTA + 1] = mode; setDeltaAndAddEvent(e); } public void recordDetune(int detune) { int[] e = new int[EVENT_ALLOCATE_MIN + 1]; e[EVENT_TYPE] = EVENT_TYPE_DETUNE; e[EVENT_DELTA + 1] = detune; setDeltaAndAddEvent(e); } public void recordLFO(int mainForm, int subForm, int depth, int width, int delay, int time, int reverse) { int[] e = new int[EVENT_ALLOCATE_MIN + 7]; e[EVENT_TYPE] = EVENT_TYPE_LPF; e[EVENT_DELTA + 1] = mainForm; e[EVENT_DELTA + 2] = subForm; e[EVENT_DELTA + 3] = depth; e[EVENT_DELTA + 4] = width; e[EVENT_DELTA + 5] = delay; e[EVENT_DELTA + 6] = time; e[EVENT_DELTA + 7] = reverse; setDeltaAndAddEvent(e); } public void recordLPF(int sw, int amount, int frequency, int resonance) { int[] e = new int[EVENT_ALLOCATE_MIN + 4]; e[EVENT_TYPE] = EVENT_TYPE_LPF; e[EVENT_DELTA + 1] = sw; e[EVENT_DELTA + 2] = amount; e[EVENT_DELTA + 3] = frequency; e[EVENT_DELTA + 4] = resonance; setDeltaAndAddEvent(e); } public void recordInput(int inSens, int pipe) { int[] e = new int[EVENT_ALLOCATE_MIN + 2]; e[EVENT_TYPE] = EVENT_TYPE_OUTPUT; e[EVENT_TYPE + 1] = inSens; e[EVENT_TYPE + 2] = pipe; setDeltaAndAddEvent(e); } public void recordOutput(int mode, int pipe) { int[] e = new int[EVENT_ALLOCATE_MIN + 2]; e[EVENT_TYPE] = EVENT_TYPE_OUTPUT; e[EVENT_TYPE + 1] = mode; e[EVENT_TYPE + 2] = pipe; setDeltaAndAddEvent(e); } public void recordExpression(int expression) { int[] e = new int[EVENT_ALLOCATE_MIN + 1]; e[EVENT_TYPE] = EVENT_TYPE_EXPRESSION; e[EVENT_DELTA + 1] = expression; setDeltaAndAddEvent(e); } public void recordClose() { int[] e = new int[EVENT_ALLOCATE_MIN]; e[EVENT_TYPE] = EVENT_TYPE_CLOSE; setDeltaAndAddEvent(e); } public void recordEnvelopeADSR(int attack, int decay, int sustain, int release, Boolean isVCO) { int[] e = new int[EVENT_ALLOCATE_MIN + 4]; e[EVENT_TYPE] = isVCO ? EVENT_TYPE_ENVELOPE_FOR_VCO : EVENT_TYPE_ENVELOPE_FOR_VCF; e[EVENT_DELTA + 1] = attack; e[EVENT_DELTA + 2] = decay; e[EVENT_DELTA + 3] = sustain; e[EVENT_DELTA + 4] = release; setDeltaAndAddEvent(e); } public void conductTracks(ArrayList tracks) { int ni = mEvents.size(), nj = tracks.size(); long globalSample = 0, globalTick = 0; double spt = calculateSPT(DEFAULT_BPM); for (int i = 0; i < ni; i++) { int[] e = mEvents.get(i); int delta = e[EVENT_DELTA]; globalTick += delta; globalSample += (delta * spt); int eventType = e[EVENT_TYPE]; if (eventType == EVENT_TYPE_TEMPO) { int tempoValue = e[EVENT_DELTA + 1]; for (int j = FIRST_TRACK; j < nj; j++) { Track track = tracks.get(j); track.recordTempo(tempoValue, globalTick); spt = calculateSPT(tempoValue); } } } long maxGlobalTick = 0; for (int j = FIRST_TRACK; j < nj; j++) { Track track = tracks.get(j); long trackGlobalTick = track.getGlobalTick(); if (maxGlobalTick < trackGlobalTick) maxGlobalTick = trackGlobalTick; } int[] close = new int[2]; recordGlobalTick(maxGlobalTick, close); globalSample += (maxGlobalTick - globalTick) * spt; recordRest((long) 3000); recordEOT(); globalSample += 3 * Sample.RATE; mDuration = (long) (globalSample * (1000.0 / Sample.RATE)); } public Boolean isEnd() { return mEnd; } public long getGlobalTick() { return mGlobalTick; } public long getDuration() { return mDuration; } public double getBPM() { return mBPM; } public void setBPM(double value) { mBPM = value; mSPT = calculateSPT(value); } public int getEventCount() { return mEvents.size(); } private void recordGlobalTick(long globalTick, int[] e) { int eventCount = mEvents.size(); long preGlobalTick = 0; for (int i = 0; i < eventCount; i++) { int[] ev = mEvents.get(i); long nextTick = preGlobalTick + ev[EVENT_DELTA]; if (nextTick >= globalTick) { ev[EVENT_DELTA] = (int) (nextTick - globalTick); e[EVENT_DELTA] = (int) (globalTick - preGlobalTick); mEvents.add(i, e); } preGlobalTick = nextTick; } e[EVENT_DELTA] = (int) (globalTick - preGlobalTick); mEvents.add(e); } private void setDeltaAndAddEvent(int[] e) { e[EVENT_DELTA] = mDelta; mDelta = 0; mEvents.add(e); } private double calculateSPT(double bpm) { return Sample.RATE / (bpm * 96.0 / 60.0); } private Channel mChannel; private ArrayList mEvents; private int mIndex; private int mDelta; private double mBPM; private double mSPT; private double mNeedle; private double mGate; private double mGate2; private Boolean mEnd; private long mGlobalTick; private long mDuration; }