com.neophob.sematrix.core.glue.Collector.java Source code

Java tutorial

Introduction

Here is the source code for com.neophob.sematrix.core.glue.Collector.java

Source

/**
 * Copyright (C) 2011-2013 Michael Vogt <michu@neophob.com>
 *
 * This file is part of PixelController.
 *
 * PixelController is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * PixelController is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with PixelController.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.neophob.sematrix.core.glue;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.imageio.ImageIO;

import org.apache.commons.lang3.StringUtils;

import com.neophob.sematrix.core.color.ColorSet;
import com.neophob.sematrix.core.effect.Effect;
import com.neophob.sematrix.core.effect.Effect.EffectName;
import com.neophob.sematrix.core.effect.PixelControllerEffect;
import com.neophob.sematrix.core.fader.Fader.FaderName;
import com.neophob.sematrix.core.fader.IFader;
import com.neophob.sematrix.core.fader.PixelControllerFader;
import com.neophob.sematrix.core.generator.Generator;
import com.neophob.sematrix.core.generator.Generator.GeneratorName;
import com.neophob.sematrix.core.generator.PixelControllerGenerator;
import com.neophob.sematrix.core.glue.helper.InitHelper;
import com.neophob.sematrix.core.jmx.PixelControllerStatus;
import com.neophob.sematrix.core.jmx.TimeMeasureItemGlobal;
import com.neophob.sematrix.core.listener.MessageProcessor;
import com.neophob.sematrix.core.mixer.Mixer;
import com.neophob.sematrix.core.mixer.Mixer.MixerName;
import com.neophob.sematrix.core.mixer.PixelControllerMixer;
import com.neophob.sematrix.core.osc.PixelControllerOscServer;
import com.neophob.sematrix.core.output.IOutput;
import com.neophob.sematrix.core.output.PixelControllerOutput;
import com.neophob.sematrix.core.properties.ApplicationConfigurationHelper;
import com.neophob.sematrix.core.properties.ConfigConstant;
import com.neophob.sematrix.core.properties.ValidCommands;
import com.neophob.sematrix.core.resize.PixelControllerResize;
import com.neophob.sematrix.core.resize.Resize.ResizeName;
import com.neophob.sematrix.core.sound.ISound;
import com.neophob.sematrix.core.sound.SoundDummy;
import com.neophob.sematrix.core.sound.SoundMinim;

/**
 * The Class Collector.
 */
public class Collector extends Observable {

    private static final Logger LOG = Logger.getLogger(Collector.class.getName());

    /** The Constant EMPTY_CHAR. */
    private static final String EMPTY_CHAR = " ";

    /** The Constant NR_OF_PRESENT_SLOTS. */
    public static final int NR_OF_PRESET_SLOTS = 144;

    /** The singleton instance. */
    private static Collector instance = new Collector();

    /** The random mode. */
    private boolean randomMode = false;

    /** The random mode. */
    private boolean randomPresetMode = false;

    /** The initialized. */
    private boolean initialized;

    /** The matrix. */
    private MatrixData matrix;

    /** all input elements. */
    private List<Visual> allVisuals;

    /** fx to screen mapping. */
    private List<OutputMapping> ioMapping;

    /** The nr of screens. */
    private int nrOfScreens;

    /** The fps. */
    private int fps;

    /** The current visual. */
    private int currentVisual;

    /** The current output. */
    private int currentOutput;

    /** present settings. */
    private int selectedPreset;

    /** The present. */
    private List<PresetSettings> presets;

    /** The pixel controller generator. */
    private PixelControllerGenerator pixelControllerGenerator;

    /** The pixel controller mixer. */
    private PixelControllerMixer pixelControllerMixer;

    /** The pixel controller effect. */
    private PixelControllerEffect pixelControllerEffect;

    /** The pixel controller resize. */
    private PixelControllerResize pixelControllerResize;

    /** The pixel controller output. */
    private PixelControllerOutput pixelControllerOutput;

    /** The pixel controller shuffler select. */
    private PixelControllerShufflerSelect pixelControllerShufflerSelect;

    private PixelControllerFader pixelControllerFader;

    private PixelControllerOscServer oscServer;

    private ApplicationConfigurationHelper ph;

    /** The is loading present. */
    private boolean isLoadingPresent = false;

    private PixelControllerStatus pixConStat;

    private List<ColorSet> colorSets;

    /** The random mode. */
    private boolean inPauseMode = false;

    private boolean internalVisualsVisible = true;

    private IOutput output;

    private ISound sound;

    private FileUtils fileUtils;

    /**
     * Instantiates a new collector.
     */
    private Collector() {
        allVisuals = new CopyOnWriteArrayList<Visual>();

        this.nrOfScreens = 0;
        ioMapping = new CopyOnWriteArrayList<OutputMapping>();
        initialized = false;

        selectedPreset = 0;
        presets = PresetSettings.initializePresetSettings(NR_OF_PRESET_SLOTS);

        pixelControllerShufflerSelect = new PixelControllerShufflerSelect();
        pixelControllerShufflerSelect.initAll();
    }

    /**
     * initialize the collector.
     *
     * @param papplet the PApplet
     * @param ph the PropertiesHelper
     */
    public synchronized void init(FileUtils fileUtils, ApplicationConfigurationHelper ph) {
        LOG.log(Level.INFO, "Initialize collector");
        if (initialized) {
            return;
        }

        this.fileUtils = fileUtils;
        this.nrOfScreens = ph.getNrOfScreens();
        this.ph = ph;
        this.fps = ph.parseFps();

        this.colorSets = InitHelper.getColorPalettes(fileUtils);

        //choose sound implementation
        if (ph.isAudioAware()) {
            try {
                sound = new SoundMinim(ph.getSoundSilenceThreshold());
            } catch (Exception e) {
                LOG.log(Level.WARNING, "FAILED TO INITIALIZE SOUND INSTANCE. Disable sound input.");
            } catch (Error e) {
                LOG.log(Level.WARNING, "FAILED TO INITIALIZE SOUND INSTANCE (Error). Disable sound input.", e);
            }
        }

        if (sound == null) {
            sound = new SoundDummy();
        }

        //create the device with specific size
        this.matrix = new MatrixData(ph.getDeviceXResolution(), ph.getDeviceYResolution());

        pixelControllerResize = new PixelControllerResize();
        pixelControllerResize.initAll();

        //create generators
        pixelControllerGenerator = new PixelControllerGenerator(ph, fileUtils, matrix, this.fps, sound,
                pixelControllerResize.getResize(ResizeName.PIXEL_RESIZE));
        pixelControllerGenerator.initAll();

        pixelControllerEffect = new PixelControllerEffect(matrix, sound);
        pixelControllerEffect.initAll();

        pixelControllerMixer = new PixelControllerMixer(matrix, sound);
        pixelControllerMixer.initAll();

        pixelControllerFader = new PixelControllerFader(ph, matrix, this.fps);

        //create visuals
        int additionalVisuals = 1 + ph.getNrOfAdditionalVisuals();
        LOG.log(Level.INFO, "Initialize " + (nrOfScreens + additionalVisuals) + " Visuals");
        try {
            Generator genPassThru = pixelControllerGenerator.getGenerator(GeneratorName.PASSTHRU);
            Effect effPassThru = pixelControllerEffect.getEffect(EffectName.PASSTHRU);
            Mixer mixPassThru = pixelControllerMixer.getMixer(MixerName.PASSTHRU);
            for (int i = 1; i < nrOfScreens + additionalVisuals + 1; i++) {
                Generator g = pixelControllerGenerator
                        .getGenerator(GeneratorName.values()[i % (GeneratorName.values().length)]);
                if (g == null) {
                    //its possible we select an inactive generator, in this case just ignore it...
                    additionalVisuals++;
                    LOG.log(Level.INFO, "Ignore null Visual, take next...");
                } else {
                    allVisuals.add(
                            new Visual(g, genPassThru, effPassThru, effPassThru, mixPassThru, colorSets.get(0)));
                }
            }

        } catch (IndexOutOfBoundsException e) {
            LOG.log(Level.SEVERE, "Failed to initialize Visual, maybe missing palette files?\n");
            throw new IllegalArgumentException("Failed to initialize Visuals, maybe missing palette files?");
        }

        LOG.log(Level.INFO, "Initialize output");
        pixelControllerOutput = new PixelControllerOutput();
        pixelControllerOutput.initAll();

        this.presets = fileUtils.loadPresents(NR_OF_PRESET_SLOTS);

        //create an empty mapping
        ioMapping.clear();
        for (int n = 0; n < nrOfScreens; n++) {
            ioMapping.add(new OutputMapping(pixelControllerFader.getVisualFader(FaderName.SWITCH), n));
        }

        pixConStat = new PixelControllerStatus(this, fps);

        initialized = true;
    }

    /**
     * start tcp and osc server
     * 
     * @param papplet
     * @param ph
     */
    public synchronized void initDaemons(ApplicationConfigurationHelper ph) {
        //Start OSC Server (OSC Interface)
        try {
            int listeningOscPort = Integer.parseInt(ph.getProperty(ConfigConstant.NET_OSC_LISTENING_PORT, "9876"));
            oscServer = new PixelControllerOscServer(listeningOscPort);
            oscServer.startServer();
            //register osc server in the statistic class
            this.pixConStat.setOscServerStatistics(oscServer);
        } catch (Exception e) {
            LOG.log(Level.SEVERE, "failed to start OSC Server", e);
        }
    }

    /**
     * update the whole system:
     * -generators
     * -effects
     * -outputs
     * 
     * update the generators, if the sound is
     * louder, update faster.
     */
    public void updateSystem() {
        //do not update system if presents are loading
        if (isLoadingPresent()) {
            return;
        }

        //update the current value of frames per second
        /*      if (papplet!=null) {
           pixConStat.setCurrentFps(papplet.frameRate);
           pixConStat.setFrameCount(papplet.frameCount);         
        }*/

        long l = System.currentTimeMillis();
        //update generator depending on the input sound
        pixelControllerGenerator.update();
        pixConStat.trackTime(TimeMeasureItemGlobal.GENERATOR, System.currentTimeMillis() - l);

        l = System.currentTimeMillis();
        pixelControllerEffect.update();
        pixConStat.trackTime(TimeMeasureItemGlobal.EFFECT, System.currentTimeMillis() - l);

        l = System.currentTimeMillis();
        pixelControllerOutput.update();
        pixConStat.trackTime(TimeMeasureItemGlobal.OUTPUT_SCHEDULE, System.currentTimeMillis() - l);

        //cleanup faders
        l = System.currentTimeMillis();
        for (OutputMapping om : ioMapping) {
            IFader fader = om.getFader();
            if (fader != null && fader.isStarted() && fader.isDone()) {
                //fading is finished, cleanup
                fader.cleanUp();

                if (fader.getScreenOutput() >= 0) {
                    mapInputToScreen(fader.getScreenOutput(), fader.getNewVisual());
                    LOG.log(Level.INFO, "Cleanup {0}, new visual: {1}, output screen: {2}",
                            new Object[] { fader.getFaderName(), fader.getNewVisual(), fader.getScreenOutput() });
                } else {
                    LOG.log(Level.INFO, "Cleanup preset {0}, new visual: {1}",
                            new Object[] { fader.getFaderName(), fader.getNewVisual() });
                }
            }
        }
        pixConStat.trackTime(TimeMeasureItemGlobal.FADER, System.currentTimeMillis() - l);

        if (randomMode) {
            Shuffler.shuffleStuff(sound);
        } else if (randomPresetMode) {
            Shuffler.randomPresentModeShuffler(sound);
        }
    }

    /**
     * Gets the single instance of Collector.
     *
     * @return single instance of Collector
     */
    public static Collector getInstance() {
        return instance;
    }

    /**
     * Gets the nr of screens.
     *
     * @return the nr of screens
     */
    public int getNrOfScreens() {
        return nrOfScreens;
    }

    private void saveImage(Visual v, String filename, int[] data) {
        try {
            int w = v.getGenerator1().getInternalBufferXSize();
            int h = v.getGenerator1().getInternalBufferYSize();
            BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
            bi.setRGB(0, 0, w, h, data, 0, w);
            File outputfile = new File(filename);
            ImageIO.write(bi, "png", outputfile);
        } catch (IOException e) {
            LOG.log(Level.SEVERE, "Failed to save screenshot " + filename, e);
        }
    }

    /**
     * create screenshot
     */
    public void saveScreenshot() {
        int ofs = 0;
        int frames = pixelControllerGenerator.getFrames();
        String suffix = ".png";
        File f = new File("screenshot" + File.separator);
        if (!f.exists()) {
            LOG.log(Level.INFO, "Create directory " + f.getAbsolutePath());
            boolean result = f.mkdir();
            LOG.log(Level.INFO, "Result: " + result);
        }
        for (Visual v : allVisuals) {
            String prefix = "screenshot" + File.separator + frames + "-" + ofs + "-";
            ColorSet cs = v.getColorSet();
            saveImage(v, prefix + "gen1" + suffix, cs.convertToColorSetImage(v.getGenerator1().internalBuffer));
            saveImage(v, prefix + "gen2" + suffix, cs.convertToColorSetImage(v.getGenerator2().internalBuffer));

            saveImage(v, prefix + "fx1" + suffix, cs.convertToColorSetImage(v.getEffect1Buffer()));
            saveImage(v, prefix + "fx2" + suffix, cs.convertToColorSetImage(v.getEffect2Buffer()));

            saveImage(v, prefix + "mix" + suffix, v.getMixerBuffer());
            ofs++;
        }
    }

    /**
     * which fx for screenOutput?.
     *
     * @param screenOutput the screen output
     * @return fx nr.
     */
    public int getFxInputForScreen(int screenOutput) {
        return ioMapping.get(screenOutput).getVisualId();
    }

    /**
     * define which fx is shown on which screen, without fading.
     *
     * @param screenOutput which screen nr
     * @param visualInput which visual
     */
    public void mapInputToScreen(int screenOutput, int visualInput) {
        OutputMapping o = ioMapping.get(screenOutput);
        o.setVisualId(visualInput);
        ioMapping.set(screenOutput, o);
    }

    /**
     * get all screens with a specific visual
     * used for crossfading.
     *
     * @param oldVisual the old visual
     * @return the all screens with visual
     */
    public List<Integer> getAllScreensWithVisual(int oldVisual) {
        List<Integer> ret = new ArrayList<Integer>();
        int ofs = 0;
        for (OutputMapping o : ioMapping) {
            if (o.getVisualId() == oldVisual) {
                ret.add(ofs);
            }
            ofs++;
        }
        return ret;
    }

    /**
     * Gets the fps.
     *
     * @return the fps
     */
    public int getFps() {
        return fps;
    }

    /**
     * Checks if is random mode.
     *
     * @return true, if is random mode
     */
    public boolean isRandomMode() {
        return randomMode;
    }

    /**
     * Sets the random mode.
     *
     * @param randomMode the new random mode
     */
    public void setRandomMode(boolean randomMode) {
        this.randomMode = randomMode;
    }

    public boolean isRandomPresetMode() {
        return randomPresetMode;
    }

    public void setRandomPresetMode(boolean randomPresetMode) {
        this.randomPresetMode = randomPresetMode;
    }

    public void savePresets() {
        fileUtils.savePresents(presets);
    }

    /**
     * load a saved preset.
     *
     * @param preset the new current status
     */
    public void setCurrentStatus(List<String> preset) {
        LOG.log(Level.FINEST, "--------------");
        long start = System.currentTimeMillis();
        setLoadingPresent(true);
        for (String s : preset) {
            s = StringUtils.trim(s);
            s = StringUtils.removeEnd(s, ";");
            LOG.log(Level.FINEST, "LOAD PRESET: " + s);
            MessageProcessor.processMsg(StringUtils.split(s, ' '), false, null);
        }
        setLoadingPresent(false);
        long needed = System.currentTimeMillis() - start;
        LOG.log(Level.INFO, "Preset loaded in " + needed + "ms");
    }

    /**
     * get current state of visuals/outputs
     * as string list - used to save current settings.
     *
     * @return the current status
     */
    public List<String> getCurrentStatus() {
        List<String> ret = new ArrayList<String>();

        //get visual status
        int n = 0;
        for (Visual v : allVisuals) {
            ret.add(ValidCommands.CURRENT_VISUAL + EMPTY_CHAR + n++);
            ret.add(ValidCommands.CHANGE_GENERATOR_A + EMPTY_CHAR + v.getGenerator1Idx());
            ret.add(ValidCommands.CHANGE_GENERATOR_B + EMPTY_CHAR + v.getGenerator2Idx());
            ret.add(ValidCommands.CHANGE_EFFECT_A + EMPTY_CHAR + v.getEffect1Idx());
            ret.add(ValidCommands.CHANGE_EFFECT_B + EMPTY_CHAR + v.getEffect2Idx());
            ret.add(ValidCommands.CHANGE_MIXER + EMPTY_CHAR + v.getMixerIdx());
            ret.add(ValidCommands.CURRENT_COLORSET + EMPTY_CHAR + v.getColorSet().getName());
        }

        //get output status
        int ofs = 0;
        for (OutputMapping om : ioMapping) {
            ret.add(ValidCommands.CURRENT_OUTPUT + EMPTY_CHAR + ofs);
            ret.add(ValidCommands.CHANGE_OUTPUT_FADER + EMPTY_CHAR + om.getFader().getId());
            ret.add(ValidCommands.CHANGE_OUTPUT_VISUAL + EMPTY_CHAR + om.getVisualId());
            ofs++;
        }

        //add element status
        ret.addAll(pixelControllerEffect.getCurrentState());
        ret.addAll(pixelControllerGenerator.getCurrentState());
        ret.addAll(pixelControllerShufflerSelect.getCurrentState());

        ret.add(ValidCommands.CHANGE_PRESET + EMPTY_CHAR + selectedPreset);
        if (inPauseMode) {
            ret.add(ValidCommands.FREEZE + EMPTY_CHAR);
        }
        return ret;
    }

    /*
     * MATRIX ======================================================
     */

    /**
     * Gets the matrix.
     *
     * @return the matrix
     */
    public MatrixData getMatrix() {
        return matrix;
    }

    /**
     * Sets the matrix.
     *
     * @param matrix the new matrix
     */
    public void setMatrix(MatrixData matrix) {
        this.matrix = matrix;
    }

    /*
     * VISUAL ======================================================
     */

    /**
     * Adds the visual.
     *
     * @param visual the visual
     */
    public void addVisual(Visual visual) {
        allVisuals.add(visual);
    }

    /**
     * Gets the all visuals.
     *
     * @return the all visuals
     */
    public List<Visual> getAllVisuals() {
        return allVisuals;
    }

    /**
     * Gets the visual.
     *
     * @param index the index
     * @return the visual
     */
    public Visual getVisual(int index) {
        if (index >= 0 && index < allVisuals.size()) {
            return allVisuals.get(index);
        }
        return allVisuals.get(0);
    }

    /**
     * Sets the all visuals.
     *
     * @param allVisuals the new all visuals
     */
    public void setAllVisuals(List<Visual> allVisuals) {
        this.allVisuals = allVisuals;
    }

    /* 
     * PRESENT ======================================================
     */

    /**
     * Gets the selected present.
     *
     * @return the selected present
     */
    public int getSelectedPreset() {
        return selectedPreset;
    }

    /**
     * Sets the selected present.
     *
     * @param selectedPresent the new selected present
     */
    public void setSelectedPreset(int selectedPreset) {
        this.selectedPreset = selectedPreset;
    }

    /**
     * Gets the present.
     *
     * @return the present
     */
    public List<PresetSettings> getPresets() {
        return presets;
    }

    /**
     * Sets the present.
     *
     * @param present the new present
     */
    public void setPresets(List<PresetSettings> preset) {
        this.presets = preset;
    }

    /*
     * OUTPUT MAPPING ======================================================
     */

    /**
     * Gets the all output mappings.
     *
     * @return the all output mappings
     */
    public List<OutputMapping> getAllOutputMappings() {
        return ioMapping;
    }

    /**
     * Gets the output mappings.
     *
     * @param index the index
     * @return the output mappings
     */
    public OutputMapping getOutputMappings(int index) {
        return ioMapping.get(index);
    }

    /**
     * Gets the current visual.
     *
     * @return the current visual
     */
    public int getCurrentVisual() {
        return currentVisual;
    }

    /**
     * Sets the current visual.
     *
     * @param currentVisual the new current visual
     */
    public void setCurrentVisual(int currentVisual) {
        if (currentVisual >= 0 && currentVisual < allVisuals.size()) {
            this.currentVisual = currentVisual;
        }
    }

    /**
     * 
     * @return
     */
    public int getCurrentOutput() {
        return currentOutput;
    }

    /**
     * 
     * @param currentOutput
     */
    public void setCurrentOutput(int currentOutput) {
        if (currentOutput >= 0 && currentOutput < ioMapping.size()) {
            this.currentOutput = currentOutput;
        }
    }

    /**
     * Checks if is loading present.
     *
     * @return true, if is loading present
     */
    public synchronized boolean isLoadingPresent() {
        return isLoadingPresent;
    }

    /**
     * Sets the loading present.
     *
     * @param isLoadingPresent the new loading present
     */
    public synchronized void setLoadingPresent(boolean isLoadingPresent) {
        this.isLoadingPresent = isLoadingPresent;
    }

    /**
     * Gets the shuffler select.
     *
     * @param ofs the ofs
     * @return the shuffler select
     */
    public boolean getShufflerSelect(ShufflerOffset ofs) {
        return pixelControllerShufflerSelect.getShufflerSelect(ofs);
    }

    /**
     * Gets the pixel controller shuffler select.
     *
     * @return the pixel controller shuffler select
     */
    public PixelControllerShufflerSelect getPixelControllerShufflerSelect() {
        return pixelControllerShufflerSelect;
    }

    /**
     * Gets the pixel controller mixer.
     *
     * @return the pixel controller mixer
     */

    public PixelControllerMixer getPixelControllerMixer() {
        return pixelControllerMixer;
    }

    /**
     * Gets the pixel controller effect.
     *
     * @return the pixel controller effect
     */
    public PixelControllerEffect getPixelControllerEffect() {
        return pixelControllerEffect;
    }

    /**
     * Gets the pixel controller generator.
     *
     * @return the pixel controller generator
     */
    public PixelControllerGenerator getPixelControllerGenerator() {
        return pixelControllerGenerator;
    }

    /**
     * Gets the pixel controller resize.
     *
     * @return the pixel controller resize
     */
    public PixelControllerResize getPixelControllerResize() {
        return pixelControllerResize;
    }

    /**
     * Gets the pixel controller output.
     *
     * @return the pixel controller output
     */
    public PixelControllerOutput getPixelControllerOutput() {
        return pixelControllerOutput;
    }

    /**
     * 
     * @return
     */
    public PixelControllerFader getPixelControllerFader() {
        return pixelControllerFader;
    }

    /**
     * @return the ph
     */
    public ApplicationConfigurationHelper getPh() {
        return ph;
    }

    /**
     * 
     * @return
     */
    public int getFrames() {
        return pixelControllerGenerator.getFrames();
    }

    /**
     * 
     * @return
     */
    public PixelControllerStatus getPixConStat() {
        return pixConStat;
    }

    /**
     * 
     * @return
     */
    public List<ColorSet> getColorSets() {
        return colorSets;
    }

    /**
     * 
     * @param colorSets
     */
    public void setColorSets(List<ColorSet> colorSets) {
        this.colorSets = colorSets;
    }

    /**
     * 
     */
    public void togglePauseMode() {
        if (inPauseMode) {
            inPauseMode = false;
        } else {
            inPauseMode = true;
        }
    }

    /**
     * 
     */
    public void toggleInternalVisual() {
        if (internalVisualsVisible) {
            internalVisualsVisible = false;
        } else {
            internalVisualsVisible = true;
        }
    }

    public boolean isInternalVisualsVisible() {
        return internalVisualsVisible;
    }

    /**
     * 
     * @return
     */
    public boolean isInPauseMode() {
        return inPauseMode;
    }

    /**
     * @param output the output to set
     */
    public void setOutput(IOutput output) {
        this.output = output;
    }

    /**
     * 
     * @return
     */
    public String getOutputDeviceName() {
        if (this.output == null) {
            return "";
        }
        return output.getType().toString();
    }

    /**
     * 
     * @return
     */
    public Boolean isOutputDeviceConnected() {
        if (this.output == null || !this.output.isSupportConnectionState()) {
            return null;
        }

        return this.output.isSupportConnectionState() && this.output.isConnected();
    }

    /**
     * 
     * @return
     */
    public IOutput getOutputDevice() {
        return this.output;
    }

    /**
     * sound implementation
     * @return
     */
    public ISound getSound() {
        return sound;
    }

    private List<String> getGuiState() {
        List<String> ret = new ArrayList<String>();

        Visual v = allVisuals.get(currentVisual);
        ret.add(ValidCommands.CURRENT_VISUAL + EMPTY_CHAR + currentVisual);
        ret.add(ValidCommands.CHANGE_GENERATOR_A + EMPTY_CHAR + v.getGenerator1Idx());
        ret.add(ValidCommands.CHANGE_GENERATOR_B + EMPTY_CHAR + v.getGenerator2Idx());
        ret.add(ValidCommands.CHANGE_EFFECT_A + EMPTY_CHAR + v.getEffect1Idx());
        ret.add(ValidCommands.CHANGE_EFFECT_B + EMPTY_CHAR + v.getEffect2Idx());
        ret.add(ValidCommands.CHANGE_MIXER + EMPTY_CHAR + v.getMixerIdx());
        ret.add(ValidCommands.CURRENT_COLORSET + EMPTY_CHAR + v.getColorSet().getName());

        //get output status
        int ofs = 0;
        for (OutputMapping om : ioMapping) {
            ret.add(ValidCommands.CURRENT_OUTPUT + EMPTY_CHAR + ofs);
            ret.add(ValidCommands.CHANGE_OUTPUT_FADER + EMPTY_CHAR + om.getFader().getId());
            ret.add(ValidCommands.CHANGE_OUTPUT_VISUAL + EMPTY_CHAR + om.getVisualId());
            ofs++;
        }

        ret.addAll(pixelControllerEffect.getCurrentState());
        ret.addAll(pixelControllerGenerator.getCurrentState());
        ret.addAll(pixelControllerShufflerSelect.getCurrentState());

        ret.add(ValidCommands.CHANGE_PRESET + EMPTY_CHAR + selectedPreset);
        ret.add(ValidCommands.FREEZE + EMPTY_CHAR + inPauseMode);

        return ret;
    }

    /**
     * 
     */
    public void notifyGuiUpdate() {
        setChanged();
        notifyObservers(getGuiState());
    }

}