//////////////////////////////////////////////////////////////////////////////// // 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.sound; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.UUID; import org.androidtransfuse.event.EventObserver; import com.teotigraphix.caustic.core.CausticException; import com.teotigraphix.caustic.core.Dispatcher; import com.teotigraphix.caustic.core.ICausticEngine; import com.teotigraphix.caustic.core.IDispatcher; import com.teotigraphix.caustic.osc.RackMessage; import com.teotigraphix.caustk.controller.ICaustkController; import com.teotigraphix.caustk.core.components.SynthComponent; import com.teotigraphix.caustk.tone.BasslineTone; import com.teotigraphix.caustk.tone.SubSynthTone; import com.teotigraphix.caustk.tone.Tone; import com.teotigraphix.caustk.tone.ToneDescriptor; import com.teotigraphix.caustk.tone.ToneType; public class CaustkSoundSource implements ICaustkSoundSource { //-------------------------------------------------------------------------- // Public Property API //-------------------------------------------------------------------------- //---------------------------------- // soundMode //---------------------------------- private SoundMode soundMode; @Override public SoundMode getSoundMode() { return soundMode; } @Override public void setSoundMode(SoundMode value) { soundMode = value; } //---------------------------------- // octave //---------------------------------- private int octave; @Override public int getOctave() { return octave; } @Override public void setOctave(int value) { octave = value; } //---------------------------------- // dispatcher //---------------------------------- private final IDispatcher dispatcher; @Override public IDispatcher getDispatcher() { return dispatcher; } //---------------------------------- // controller //---------------------------------- private ICaustkController controller; protected ICaustkController getController() { return controller; } protected ICausticEngine getEngine() { return controller.getSoundGenerator(); } //---------------------------------- // tones //---------------------------------- private Map<Integer, Tone> tones = new HashMap<Integer, Tone>(); @Override public int getToneCount() { return tones.size(); } @Override public Collection<Tone> getTones() { return Collections.unmodifiableCollection(tones.values()); } @Override public Tone getTone(int index) { return tones.get(index); } public CaustkSoundSource(ICaustkController controller) { this.controller = controller; controller.registerAPI(SoundMixerAPI.class, new SoundMixerAPI(controller)); dispatcher = new Dispatcher(); tones = new HashMap<Integer, Tone>(); getDispatcher().register(OnSoundSourceInitialValue.class, new EventObserver<OnSoundSourceInitialValue>() { @Override public void trigger(OnSoundSourceInitialValue object) { System.out.println("Original value:" + object.getValue()); } }); } //-------------------------------------------------------------------------- // Public Method API //-------------------------------------------------------------------------- /** * Triggers a note on for the part's tone. * <p> * Using this method automatically adjusts the pitch of the tone based on * the current {@link #getOctave()} of the sound source. * * @param part * @param pitch * @param velocity */ @Override public void noteOn(Tone tone, int pitch, float velocity) { int semitones = getOctave() * 12; tone.getComponent(SynthComponent.class).noteOn(pitch + semitones, velocity); } @Override public void noteOff(Tone tone, int pitch) { int semitones = getOctave() * 12; tone.getComponent(SynthComponent.class).noteOff(pitch + semitones); } @Override public Tone createTone(String name, ToneType toneType) throws CausticException { return createTone(new ToneDescriptor(nextIndex(), name, toneType)); } @Override public Tone createTone(int index, String name, ToneType toneType) throws CausticException { return createTone(new ToneDescriptor(index, name, toneType)); } @Override public Tone createTone(ToneDescriptor descriptor) throws CausticException { Tone tone = createSynthChannel(nextIndex(), descriptor.getName(), descriptor.getToneType()); return tone; } public void destroyTone(int index) { destroyTone(getTone(index)); } public void destroyTone(Tone tone) { int index = tone.getIndex(); RackMessage.REMOVE.send(getEngine(), index); toneRemove(tone); } public void clearAndReset() { getController().getDispatcher().trigger(new OnSoundSourceClear()); ArrayList<Tone> remove = new ArrayList<Tone>(tones.values()); for (Tone tone : remove) destroyTone(tone); RackMessage.BLANKRACK.send(getEngine()); getController().getDispatcher().trigger(new OnSoundSourceReset()); } //-------------------------------------------------------------------------- // Protected Method API //-------------------------------------------------------------------------- Tone createSynthChannel(int index, String toneName, ToneType toneType) throws CausticException { if (tones.containsKey(index)) { throw new CausticException("{" + index + "} tone is already defined"); } RackMessage.CREATE.send(getEngine(), toneType.getValue(), toneName, index); Tone tone = null; switch (toneType) { case Bassline: tone = new BasslineTone(getController()); initializeTone(tone, toneName, toneType); SoundSourceUtils.setup((BasslineTone)tone); break; case Beatbox: break; case PCMSynth: break; case SubSynth: tone = new SubSynthTone(getController()); initializeTone(tone, toneName, toneType); SoundSourceUtils.setup((SubSynthTone)tone); break; case PadSynth: break; case Organ: break; case Vocoder: break; case EightBitSynth: break; case Modular: break; case FMSynth: break; default: break; } toneAdd(index, tone); return tone; } private void initializeTone(Tone tone, String toneName, ToneType toneType) { tone.setId(UUID.randomUUID()); tone.setName(toneName); tone.setToneType(toneType); } private void toneAdd(int index, Tone tone) { tones.put(index, tone); getController().getDispatcher().trigger(new OnSoundSourceToneAdd(tone)); } private void toneRemove(Tone tone) { tones.remove(tone.getIndex()); getController().getDispatcher().trigger(new OnSoundSourceToneRemove(tone)); } //-------------------------------------------------------------------------- // Public Observer API //-------------------------------------------------------------------------- public static class OnSoundSourceToneAdd { private Tone tone; public Tone getTone() { return tone; } public OnSoundSourceToneAdd(Tone tone) { this.tone = tone; } } public static class OnSoundSourceToneRemove { private Tone tone; public Tone getTone() { return tone; } public OnSoundSourceToneRemove(Tone tone) { this.tone = tone; } } public static class OnSoundSourceInitialValue { private Object value; public Object getValue() { return value; } public OnSoundSourceInitialValue(Object value) { this.value = value; } } public static class OnSoundSourceInitialValueReset { } public static class OnSoundSourceClear { } public static class OnSoundSourceReset { } //-------------------------------------------------------------------------- // Public Observer //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- // Private Methods //-------------------------------------------------------------------------- private int nextIndex() { int index = 0; for (index = 0; index < 10; index++) { if (!tones.containsKey(index)) break; } return index; } }