com.teotigraphix.caustk.sound.SoundSource.java Source code

Java tutorial

Introduction

Here is the source code for com.teotigraphix.caustk.sound.SoundSource.java

Source

////////////////////////////////////////////////////////////////////////////////
// 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.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

import org.androidtransfuse.event.EventObserver;
import org.apache.commons.io.FileUtils;

import com.teotigraphix.caustk.application.Dispatcher;
import com.teotigraphix.caustk.application.IDispatcher;
import com.teotigraphix.caustk.controller.ICaustkController;
import com.teotigraphix.caustk.controller.SubControllerBase;
import com.teotigraphix.caustk.controller.SubControllerModel;
import com.teotigraphix.caustk.core.CausticException;
import com.teotigraphix.caustk.core.osc.RackMessage;
import com.teotigraphix.caustk.library.LibraryScene;
import com.teotigraphix.caustk.library.SoundMixerState;
import com.teotigraphix.caustk.tone.BasslineTone;
import com.teotigraphix.caustk.tone.BeatboxTone;
import com.teotigraphix.caustk.tone.EightBitSynth;
import com.teotigraphix.caustk.tone.FMSynthTone;
import com.teotigraphix.caustk.tone.ModularTone;
import com.teotigraphix.caustk.tone.OrganTone;
import com.teotigraphix.caustk.tone.PCMSynthTone;
import com.teotigraphix.caustk.tone.PadSynthTone;
import com.teotigraphix.caustk.tone.SubSynthTone;
import com.teotigraphix.caustk.tone.Tone;
import com.teotigraphix.caustk.tone.ToneDescriptor;
import com.teotigraphix.caustk.tone.ToneType;
import com.teotigraphix.caustk.tone.ToneUtils;
import com.teotigraphix.caustk.tone.VocoderTone;
import com.teotigraphix.caustk.utils.RuntimeUtils;

public class SoundSource extends SubControllerBase implements ISoundSource {

    private int maxNumTones = 14;

    @Override
    protected Class<? extends SubControllerModel> getModelType() {
        return SoundSourceModel.class;
    }

    SoundSourceModel getModel() {
        return (SoundSourceModel) getInternalModel();
    }

    //--------------------------------------------------------------------------
    // Public Property API
    //--------------------------------------------------------------------------

    //----------------------------------
    // dispatcher
    //----------------------------------

    private final IDispatcher dispatcher;

    @Override
    public IDispatcher getDispatcher() {
        return dispatcher;
    }

    //----------------------------------
    // transpose
    //----------------------------------

    private int transpose;

    @Override
    public int getTranspose() {
        return transpose;
    }

    @Override
    public void setTranspose(int value) {
        transpose = value;
    }

    //----------------------------------
    // tones
    //----------------------------------

    @Override
    public int getToneCount() {
        return getModel().getTones().size();
    }

    @Override
    public Collection<Tone> getTones() {
        return Collections.unmodifiableCollection(getModel().getTones().values());
    }

    @Override
    public Tone getTone(int index) {
        return getModel().getTones().get(index);
    }

    @Override
    public Tone getToneByName(String value) {
        for (Tone tone : getModel().getTones().values()) {
            if (tone.getName().equals(value))
                return tone;
        }
        return null;
    }

    public SoundSource(ICaustkController controller) {
        super(controller);

        dispatcher = new Dispatcher();

        getDispatcher().register(OnSoundSourceInitialValue.class, new EventObserver<OnSoundSourceInitialValue>() {
            @Override
            public void trigger(OnSoundSourceInitialValue object) {
                System.out.println("Original value:" + object.getValue());
            }
        });
    }

    //--------------------------------------------------------------------------
    // Public Method API
    //--------------------------------------------------------------------------

    @Override
    public List<Tone> findToneStartsWith(String name) {
        List<Tone> result = new ArrayList<Tone>();
        for (Tone tone : getTones()) {
            if (tone.getName().startsWith(name))
                result.add(tone);
        }
        return result;
    }

    @Override
    public void createScene(LibraryScene scene) throws CausticException {
        // make tones
        for (ToneDescriptor descriptor : scene.getSoundSourceState().getDescriptors().values()) {
            createTone(descriptor);
        }
        SoundMixerState mixerState = scene.getSoundMixerState();

        SoundMixerModel model = getController().getSerializeService().fromString(mixerState.getData(),
                SoundMixerModel.class);
        model.update();
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends Tone> T createTone(String data) throws CausticException {
        Tone tone = null;
        try {
            tone = getController().getSerializeService().fromString(data, ToneUtils.getToneClass(data));
        } catch (IOException e) {
            e.printStackTrace();
        }

        int index = nextIndex();
        tone.setIndex(index);

        RackMessage.CREATE.send(getController(), tone.getToneType().getValue(), tone.getName(), index);

        toneAdd(index, tone);

        return (T) tone;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends Tone> T createTone(int index, String name, Class<? extends Tone> toneClass)
            throws CausticException {
        T tone = null;
        try {
            Constructor<? extends Tone> constructor = toneClass.getConstructor(ICaustkController.class);
            tone = (T) constructor.newInstance(getController());
            initializeTone(tone, name, tone.getToneType(), index);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        SoundSourceUtils.setup(toneClass.cast(tone));

        RackMessage.CREATE.send(getController(), tone.getToneType().getValue(), tone.getName(), tone.getIndex());

        toneAdd(index, tone);

        return tone;
    }

    @Override
    public <T extends Tone> T createTone(String name, Class<? extends Tone> toneClass) throws CausticException {
        return createTone(nextIndex(), name, toneClass);
    }

    @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;
    }

    @Override
    public void destroyTone(int index) {
        destroyTone(getTone(index));
    }

    public void destroyTone(Tone tone) {
        int index = tone.getIndex();
        RackMessage.REMOVE.send(getController(), index);
        toneRemove(tone);
    }

    @Override
    public void clearAndReset() {
        getController().getDispatcher().trigger(new OnSoundSourceClear());

        ArrayList<Tone> remove = new ArrayList<Tone>(getModel().getTones().values());
        for (Tone tone : remove) {
            //RackMessage.REMOVE.send(getController(), tone.getIndex());
            toneRemove(tone);
        }

        RackMessage.BLANKRACK.send(getController());

        getController().getDispatcher().trigger(new OnSoundSourceReset());
    }

    //--------------------------------------------------------------------------
    // Protected Method API
    //--------------------------------------------------------------------------

    Tone createSynthChannel(int index, String toneName, ToneType toneType) throws CausticException {
        if (index > 13)
            throw new CausticException("Only 14 machines allowed in a rack");

        if (getModel().getTones().containsKey(index))
            throw new CausticException("{" + index + "} tone is already defined");

        RackMessage.CREATE.send(getController(), toneType.getValue(), toneName, index);

        Tone tone = null;
        switch (toneType) {
        case Bassline:
            tone = new BasslineTone(getController());
            initializeTone(tone, toneName, toneType, index);
            SoundSourceUtils.setup((BasslineTone) tone);
            break;
        case Beatbox:
            tone = new BeatboxTone(getController());
            initializeTone(tone, toneName, toneType, index);
            SoundSourceUtils.setup((BeatboxTone) tone);
            break;
        case PCMSynth:
            tone = new PCMSynthTone(getController());
            initializeTone(tone, toneName, toneType, index);
            SoundSourceUtils.setup((PCMSynthTone) tone);
            break;
        case SubSynth:
            tone = new SubSynthTone(getController());
            initializeTone(tone, toneName, toneType, index);
            SoundSourceUtils.setup((SubSynthTone) tone);
            break;
        case PadSynth:
            tone = new PadSynthTone(getController());
            initializeTone(tone, toneName, toneType, index);
            SoundSourceUtils.setup((PadSynthTone) tone);
            break;
        case Organ:
            tone = new OrganTone(getController());
            initializeTone(tone, toneName, toneType, index);
            SoundSourceUtils.setup((OrganTone) tone);
            break;
        case Vocoder:
            tone = new VocoderTone(getController());
            initializeTone(tone, toneName, toneType, index);
            SoundSourceUtils.setup((VocoderTone) tone);
            break;
        case EightBitSynth:
            tone = new EightBitSynth(getController());
            initializeTone(tone, toneName, toneType, index);
            SoundSourceUtils.setup((EightBitSynth) tone);
            break;
        case Modular:
            tone = new ModularTone(getController());
            initializeTone(tone, toneName, toneType, index);
            SoundSourceUtils.setup((ModularTone) tone);
            break;
        case FMSynth:
            tone = new FMSynthTone(getController());
            initializeTone(tone, toneName, toneType, index);
            SoundSourceUtils.setup((FMSynthTone) tone);
            break;
        default:
            break;
        }

        toneAdd(index, tone);

        return tone;
    }

    private void initializeTone(Tone tone, String toneName, ToneType toneType, int index) {
        tone.setId(UUID.randomUUID());
        tone.setName(toneName);
        tone.setIndex(index);
    }

    private void toneAdd(int index, Tone tone) {
        getModel().getTones().put(index, tone);
        getController().getDispatcher().trigger(new OnSoundSourceToneAdd(tone));
    }

    private void toneRemove(Tone tone) {
        getModel().getTones().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 < 15; index++) {
            if (!getModel().getTones().containsKey(index))
                break;
        }
        return index;
    }

    @Override
    public void loadSong(File causticFile) throws CausticException {
        clearAndReset();

        RackMessage.LOAD_SONG.send(getController(), causticFile.getAbsolutePath());

        restore();
        getController().getSoundMixer().restore();

        getDispatcher().trigger(new OnSoundSourceSongLoad());
    }

    @Override
    public File saveSong(String name) {
        RackMessage.SAVE_SONG.send(getController(), name);
        return RuntimeUtils.getCausticSongFile(name);
    }

    @Override
    public File saveSongAs(File file) throws IOException {
        File song = saveSong(file.getName().replace(".caustic", ""));
        FileUtils.copyFileToDirectory(song, file.getParentFile(), true);
        song.delete();
        return file;
    }

    @Override
    public void restore() {
        for (int i = 0; i < maxNumTones; i++) {
            String name = RackMessage.QUERY_MACHINE_NAME.queryString(getController(), i);
            String type = RackMessage.QUERY_MACHINE_TYPE.queryString(getController(), i);
            if (name == null || name.equals(""))
                continue;

            ToneType toneType = ToneType.fromString(type);
            Tone tone = null;
            try {
                tone = createTone(i, name, toneType);
            } catch (CausticException e) {
                e.printStackTrace();
            }
            tone.restore();
        }
    }

}