com.teotigraphix.caustk.library.LibraryManager.java Source code

Java tutorial

Introduction

Here is the source code for com.teotigraphix.caustk.library.LibraryManager.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.library;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;

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

import com.teotigraphix.caustic.core.CausticException;
import com.teotigraphix.caustic.core.XMLMemento;
import com.teotigraphix.caustic.desktop.RuntimeUtils;
import com.teotigraphix.caustk.controller.ICaustkController;
import com.teotigraphix.caustk.core.components.SynthComponent;
import com.teotigraphix.caustk.library.vo.EffectRackInfo;
import com.teotigraphix.caustk.library.vo.MetadataInfo;
import com.teotigraphix.caustk.library.vo.MixerPanelInfo;
import com.teotigraphix.caustk.library.vo.RackInfo;
import com.teotigraphix.caustk.project.IProjectManager;
import com.teotigraphix.caustk.project.IProjectManager.OnProjectManagerChange;
import com.teotigraphix.caustk.project.IProjectManager.ProjectManagerChangeKind;
import com.teotigraphix.caustk.tone.Tone;

public class LibraryManager implements ILibraryManager {

    //--------------------------------------------------------------------------
    // Variables
    //--------------------------------------------------------------------------

    private LibraryRegistry registry;

    private ICaustkController controller;

    private File librariesDirectory;

    private Library selectedLibrary;

    //--------------------------------------------------------------------------
    // API
    //--------------------------------------------------------------------------

    //----------------------------------
    // selectedLibrary
    //----------------------------------

    @Override
    public Library getSelectedLibrary() {
        return selectedLibrary;
    }

    @Override
    public void setSelectedLibrary(Library value) {
        if (value == selectedLibrary)
            return;

        selectedLibrary = value;
        controller.getDispatcher().trigger(new OnLibraryManagerSelectedLibraryChange(selectedLibrary));
    }

    //----------------------------------
    // librariesDirectory
    //----------------------------------

    @Override
    public File getLibrariesDirectory() {
        return librariesDirectory;
    }

    //----------------------------------
    // libraries
    //----------------------------------

    @Override
    public int getLibraryCount() {
        return registry.getLibraries().size();
    }

    @Override
    public Collection<Library> getLibraries() {
        return registry.getLibraries();
    }

    public LibraryManager(ICaustkController controller) {
        this.controller = controller;

        File root = controller.getConfiguration().getApplicationRoot();
        if (!root.exists())
            throw new RuntimeException("Application root not specified");

        librariesDirectory = new File(root, "libraries");
        if (!librariesDirectory.exists())
            librariesDirectory.mkdirs();

        registry = new LibraryRegistry(librariesDirectory);

        controller.getDispatcher().register(OnProjectManagerChange.class,
                new EventObserver<OnProjectManagerChange>() {
                    @Override
                    public void trigger(OnProjectManagerChange object) {
                        if (object.getKind() == ProjectManagerChangeKind.CREATE) {
                            //onProjectManagerCreateHandler();
                        } else if (object.getKind() == ProjectManagerChangeKind.LOAD) {
                            onProjectManagerLoadHandler();
                        } else if (object.getKind() == ProjectManagerChangeKind.SAVE) {
                            onProjectManagerSaveHandler();
                        } else if (object.getKind() == ProjectManagerChangeKind.EXIT) {
                            onProjectManagerExitHandler();
                        }
                    }
                });
    }

    protected void onProjectManagerSaveHandler() {
        saveSessionProperties();
    }

    protected void onProjectManagerLoadHandler() {
        load();
        String id = controller.getProjectManager().getSessionPreferences().getString("selectedLibrary");
        if (id != null) {
            selectedLibrary = registry.getLibrary(id);
        }
    }

    private void saveSessionProperties() {
        // if the project has selected a library, save it
        if (selectedLibrary != null) {
            controller.getProjectManager().getSessionPreferences().put("selectedLibrary", selectedLibrary.getId());
        }
    }

    protected void onProjectManagerExitHandler() {
        registry = new LibraryRegistry(librariesDirectory);
        selectedLibrary = null;
    }

    @Override
    public Library getLibraryById(UUID id) {
        return registry.getLibrary(id);
    }

    @Override
    public Library getLibraryByName(String name) {
        for (Library library : registry.getLibraries()) {
            if (library.getName().equals(name))
                return library;
        }
        return null;
    }

    public List<LibraryPatch> findPatchesByTag(String tag) {
        List<LibraryPatch> result = new ArrayList<LibraryPatch>();
        for (Library library : registry.getLibraries()) {
            for (LibraryPatch item : library.getPatches()) {
                if (item.hasTag(tag)) {
                    result.add(item);
                }
            }
        }
        return result;
    }

    public List<LibraryPhrase> findPhrasesByTag(String tag) {
        List<LibraryPhrase> result = new ArrayList<LibraryPhrase>();
        for (Library library : registry.getLibraries()) {
            for (LibraryPhrase item : library.getPhrases()) {
                if (item.hasTag(tag)) {
                    result.add(item);
                }
            }
        }
        return result;
    }

    public List<LibraryScene> findScenesByTag(String tag) {
        List<LibraryScene> result = new ArrayList<LibraryScene>();
        for (Library library : registry.getLibraries()) {
            for (LibraryScene item : library.getScenes()) {
                if (item.hasTag(tag)) {
                    result.add(item);
                }
            }
        }
        return result;
    }

    /**
     * Saves the {@link Library}s to disk using the libraries directory
     * location.
     * <p>
     * Note: This DOES NOT get called from the {@link IProjectManager#save()}
     * method. The client needs to save a library by calling {@link #save()}.
     * 
     * @throws IOException
     */
    @Override
    public void save() throws IOException {
        for (Library library : registry.getLibraries()) {
            saveLibrary(library);
        }
    }

    /**
     * Loads the entire <code>libraries</code> directory into the manager.
     * <p>
     * Each sub directory located within the <code>libraries</code> directory
     * will be created as a {@link Library} instance.
     */
    @Override
    public void load() {
        Collection<File> dirs = FileUtils.listFilesAndDirs(librariesDirectory, new IOFileFilter() {
            @Override
            public boolean accept(File arg0, String arg1) {
                return false;
            }

            @Override
            public boolean accept(File arg0) {
                return false;
            }
        }, new IOFileFilter() {
            @Override
            public boolean accept(File file, String name) {
                if (file.getParentFile().getName().equals("libraries"))
                    return true;
                return false;
            }

            @Override
            public boolean accept(File file) {
                if (file.getParentFile().getName().equals("libraries"))
                    return true;
                return false;
            }
        });

        for (File directory : dirs) {
            if (directory.equals(librariesDirectory))
                continue;

            loadLibrary(directory.getName());
        }
    }

    @Override
    public void importSong(Library library, File causticFile) throws IOException {
        //        //----------------------------------------------------------------------
        //        // clear the core rack
        //        RackMessage.BLANKRACK.send(controller);
        //
        //        Rack rack = new Rack(controller.getFactory(), true);
        //        rack.setEngine(controller);
        //
        //        // load the file into the rack.
        //        try {
        //            rack.loadSong(causticFile.getAbsolutePath());
        //        } catch (CausticException e) {
        //            e.printStackTrace();
        //            throw new IOException(e);
        //        }
        //
        //        // restore the rack
        //        //rack.restore();
        //        rack.getOutputPanel().restore();
        //        rack.getMixerPanel().restore();
        //        rack.getEffectsRack().restore();
        //
        //        //loadLibraryPatches(library, rack);
        //        loadLibraryScene(library, causticFile, rack);
        //        loadLibraryPhrases(library, rack);
        //
        //        // clear the core rack
        //        RackMessage.BLANKRACK.send(controller);
    }

    private LibraryScene createDefaultScene() throws CausticException {
        //        RackMessage.BLANKRACK.send(controller);
        //
        //        Rack rack = new Rack(controller.getFactory(), true);
        //        rack.setEngine(controller);
        //
        //        // create the default setup
        //        rack.addMachine("SubSynth", MachineType.SUBSYNTH);
        //        rack.addMachine("PCMSynth1", MachineType.PCMSYNTH);
        //        rack.addMachine("PCMSynth2", MachineType.PCMSYNTH);
        //        rack.addMachine("Bassline1", MachineType.BASSLINE);
        //        rack.addMachine("Bassline2", MachineType.BASSLINE);
        //        rack.addMachine("Beatbox", MachineType.BEATBOX);
        //
        //        //rack.populateMachines();
        //        rack.getOutputPanel().restore();
        //        rack.getMixerPanel().restore();
        //        rack.getEffectsRack().restore();
        //
        //        LibraryScene libraryScene = new LibraryScene();
        //        libraryScene.setId(UUID.randomUUID());
        //        MetadataInfo metadataInfo = new MetadataInfo();
        //        metadataInfo.addTag("DefaultScene");
        //        libraryScene.setMetadataInfo(metadataInfo);
        //        libraryScene.setRackInfo(LibrarySerializerUtils.createRackInfo(rack));
        //        libraryScene.setMixerInfo(LibrarySerializerUtils.createMixerPanelInfo(controller
        //                .getSoundMixer().getMixerPanel()));
        //        libraryScene.setEffectRackInfo(LibrarySerializerUtils.createEffectRackInfo(controller
        //                .getSoundMixer().getEffectsRack()));
        //
        //        RackMessage.BLANKRACK.send(controller);
        //        return libraryScene;
        return null;
    }

    @Override
    public Library createLibrary(String name) throws IOException {
        File newDirectory = new File(librariesDirectory, name);
        //if (newDirectory.exists())
        //    throw new CausticException("Library already exists " + newDirectory.getAbsolutePath());
        newDirectory.mkdir();

        // create a default scene for every new Library
        LibraryScene defaultScene = null;
        try {
            defaultScene = createDefaultScene();
        } catch (CausticException e) {
            e.printStackTrace();
        }

        Library library = new Library();
        library.setId(UUID.randomUUID());
        library.setDirectory(newDirectory);
        library.mkdirs();

        registry.addLibrary(library);
        library.addScene(defaultScene);

        return library;
    }

    @Override
    public LibraryScene createLibraryScene(MetadataInfo info) {
        LibraryScene scene = new LibraryScene();
        scene.setMetadataInfo(info);
        scene.setId(null);

        //--------------------------------------
        RackInfo rackInfo = new RackInfo();
        XMLMemento memento = XMLMemento.createWriteRoot("rack");
        for (int i = 0; i < 6; i++) {
            Tone tone = controller.getSoundSource().getTone(i);
            if (tone != null) {
                //                IMemento child = memento.createChild("machine");
                //                child.putInteger("index", i);
                //                //child.putInteger("active", machine != null ? 1 : 0);
                //                child.putString("id", tone.getId());
                //                child.putString("type", tone.getType().getValue());
            }
        }

        rackInfo.setData(memento.toString());

        scene.setRackInfo(rackInfo);

        MixerPanelInfo mixerPanelInfo = LibrarySerializerUtils
                .createMixerPanelInfo(controller.getSoundMixer().getMixerPanel());
        scene.setMixerInfo(mixerPanelInfo);

        EffectRackInfo effectRackInfo = LibrarySerializerUtils
                .createEffectRackInfo(controller.getSoundMixer().getEffectsRack());
        scene.setEffectRackInfo(effectRackInfo);

        //TagUtils.addDefaultTags(name, rack, scene);

        return scene;
    }

    //    private void loadLibraryScene(Library library, File causticFile, Rack rack) throws IOException {
    //        String name = causticFile.getName().replace(".caustic", "");
    //        LibraryScene scene = new LibraryScene();
    //        scene.setMetadataInfo(new MetadataInfo());
    //
    //        scene.setId(UUID.randomUUID());
    //        library.addScene(scene);
    //
    //        //--------------------------------------
    //        RackInfo rackInfo = new RackInfo();
    //        XMLMemento memento = XMLMemento.createWriteRoot("rack");
    //        for (int i = 0; i < 6; i++) {
    //            IMachine machine = rack.getMachine(i);
    //            LibraryPatch patch = null;
    //
    //            if (machine != null) {
    //                patch = new LibraryPatch();
    //                patch.setMachineType(machine.getType());
    //                patch.setMetadataInfo(new MetadataInfo());
    //                patch.setId(UUID.randomUUID());
    //                TagUtils.addDefaultTags(machine, patch);
    //                relocatePresetFile(machine, library, patch);
    //                library.addPatch(patch);
    //
    //                IMemento child = memento.createChild("machine");
    //                child.putInteger("index", i);
    //                child.putInteger("active", machine != null ? 1 : 0);
    //
    //                if (patch != null)
    //                    child.putString("patchId", patch.getId().toString());
    //
    //                child.putString("id", machine.getId());
    //                child.putString("type", machine.getType().getValue());
    //            }
    //        }
    //
    //        rackInfo.setData(memento.toString());
    //
    //        scene.setRackInfo(rackInfo);
    //
    //        MixerPanelInfo mixerPanelInfo = LibrarySerializerUtils.createMixerPanelInfo(rack);
    //        scene.setMixerInfo(mixerPanelInfo);
    //
    //        EffectRackInfo effectRackInfo = LibrarySerializerUtils.createEffectRackInfo(rack);
    //        scene.setEffectRackInfo(effectRackInfo);
    //
    //        TagUtils.addDefaultTags(name, rack, scene);
    //    }

    //    private void loadLibraryPhrases(Library library, Rack rack) {
    //        for (int i = 0; i < 6; i++) {
    //            IMachine machine = rack.getMachine(i);
    //            if (machine != null) {
    //
    //                String result = PatternSequencerMessage.QUERY_PATTERNS_WITH_DATA.queryString(
    //                        controller, i);
    //
    //                if (result == null)
    //                    continue;
    //
    //                for (String patternName : result.split(" ")) {
    //                    int bankIndex = PatternUtils.toBank(patternName);
    //                    int patternIndex = PatternUtils.toPattern(patternName);
    //
    //                    // set the current bank and pattern of the machine to query
    //                    // the string pattern data
    //                    PatternSequencerMessage.BANK.send(controller, i, bankIndex);
    //                    PatternSequencerMessage.PATTERN.send(controller, i, patternIndex);
    //
    //                    //----------------------------------------------------------------
    //                    // Load Pattern
    //                    //----------------------------------------------------------------
    //
    //                    // load one phrase per pattern; load ALL patterns
    //                    // as caustic machine patterns
    //                    int length = (int)PatternSequencerMessage.NUM_MEASURES.query(controller, i);
    //                    float tempo = OutputPanelMessage.BPM.query(controller);
    //                    String noteData = PatternSequencerMessage.QUERY_NOTE_DATA.queryString(
    //                            controller, i);
    //
    //                    LibraryPhrase phrase = new LibraryPhrase();
    //                    phrase.setMetadataInfo(new MetadataInfo());
    //                    phrase.setId(UUID.randomUUID());
    //                    phrase.setLength(length);
    //                    phrase.setTempo(tempo);
    //                    phrase.setMachineType(machine.getType());
    //                    phrase.setNoteData(noteData);
    //                    phrase.setResolution(calculateResolution(noteData));
    //                    TagUtils.addDefaultTags(phrase);
    //                    library.addPhrase(phrase);
    //                }
    //            }
    //        }
    //    }

    private Resolution calculateResolution(String data) {
        // TODO This is totally inefficient, needs to be lazy loaded
        // push the notes into the machines sequencer
        float smallestGate = 1f;
        String[] notes = data.split("\\|");
        for (String noteData : notes) {
            String[] split = noteData.split(" ");

            float start = Float.parseFloat(split[0]);
            float end = Float.parseFloat(split[3]);
            float gate = end - start;
            smallestGate = Math.min(smallestGate, gate);
        }

        Resolution result = Resolution.SIXTEENTH;
        if (smallestGate <= Resolution.SIXTYFOURTH.getValue() * 4)
            result = Resolution.SIXTYFOURTH;
        else if (smallestGate <= Resolution.THIRTYSECOND.getValue() * 4)
            result = Resolution.THIRTYSECOND;
        else if (smallestGate <= Resolution.SIXTEENTH.getValue() * 4)
            result = Resolution.SIXTEENTH;

        return result;
    }

    protected void relocatePresetFile(Tone tone, Library library, LibraryPatch patch) throws IOException {
        String id = patch.getId().toString();
        tone.getComponent(SynthComponent.class).savePreset(id);
        File presetFile = RuntimeUtils.getCausticPresetsFile(tone.getToneType().getValue(), id);
        if (!presetFile.exists())
            throw new IOException("Preset file does not exist");

        File presetsDirectory = library.getPresetsDirectory();
        File destFile = new File(presetsDirectory, presetFile.getName());
        FileUtils.copyFile(presetFile, destFile);
        FileUtils.deleteQuietly(presetFile);
    }

    @Override
    public void saveLibrary(Library library) throws IOException {
        String data = controller.getSerializeService().toString(library);
        File file = new File(library.getDirectory(), "library.ctk");
        FileUtils.writeStringToFile(file, data);
    }

    @Override
    public Library loadLibrary(String name) {
        File directory = new File(librariesDirectory, name);
        File file = new File(directory, "library.ctk");

        Library library = controller.getSerializeService().fromFile(file, Library.class);
        registry.addLibrary(library);

        controller.getDispatcher().trigger(new OnLibraryManagerLoadComplete(library));

        return library;
    }

    @Override
    public void clear() {
        registry = new LibraryRegistry(librariesDirectory);
    }

    @Override
    public void delete() throws IOException {
        for (Library library : registry.getLibraries()) {
            library.delete();
        }
        clear();
    }

}

/*
    
    
<?xml version="1.0" encoding="UTF-8"?>
<mixer>
<master eq_bass="0.19565237" eq_high="0.100000024" eq_mid="0.0" id="-1" index="-1" volume="1.1478264"/>
<channels>
    <channel delay_send="0.504348" eq_bass="-0.008696437" eq_high="0.06956482" eq_mid="1.1920929E-7" id="0" index="0" mute="0" pan="0.0" reverb_send="0.05869567" solo="0" stereo_width="0.99609375" volume="1.2782611"/>
    <channel delay_send="0.5000001" eq_bass="-1.0" eq_high="0.02666676" eq_mid="0.034783125" id="1" index="1" mute="0" pan="0.0" reverb_send="0.0" solo="0" stereo_width="0.0" volume="1.3652176"/>
    <channel delay_send="0.0" eq_bass="1.0" eq_high="-0.008695722" eq_mid="0.0" id="2" index="2" mute="0" pan="0.0" reverb_send="0.0" solo="0" stereo_width="0.0" volume="1.2782608"/>
    <channel delay_send="0.52173936" eq_bass="-1.0" eq_high="-2.9802322E-7" eq_mid="-0.026086628" id="3" index="3" mute="0" pan="0.18260896" reverb_send="0.07173912" solo="0" stereo_width="0.0" volume="1.4383385"/>
    <channel delay_send="0.0" eq_bass="-0.017391145" eq_high="0.38260782" eq_mid="0.0" id="4" index="4" mute="0" pan="0.0" reverb_send="0.07391316" solo="0" stereo_width="0.53269273" volume="1.3913045"/>
    <channel delay_send="0.0" eq_bass="1.0" eq_high="5.9604645E-7" eq_mid="-0.008694708" id="5" index="5" mute="0" pan="0.0" reverb_send="0.0" solo="0" stereo_width="0.99609375" volume="0.99999934"/>
</channels>
<delay feedback="0.7565215" stereo="1" time="5"/>
<reverb damping="0.0" room="0.9" stereo="1"/>
</mixer>
    
    
<?xml version="1.0" encoding="UTF-8"?>
<rack>
<machine active="1" id="LOWLEAD" index="0" type="subsynth"/>
<machine active="1" id="HIGHLEAD" index="1" type="subsynth"/>
<machine active="1" id="BASS" index="2" type="subsynth"/>
<machine active="1" id="MELODY" index="3" type="bassline"/>
<machine active="1" id="STRINGS" index="4" type="pcmsynth"/>
<machine active="1" id="DRUMSIES" index="5" type="beatbox"/>
</rack>
    
    
<?xml version="1.0" encoding="UTF-8"?>
<effects>
<channel id="LOWLEAD" index="0">
    <effect channel="0" depth="0.95" feedback="0.25" rate="0.43333432" type="5" wet="1.0"/>
</channel>
<channel id="HIGHLEAD" index="1"/>
<channel id="BASS" index="2">
    <effect attack="0.011313666" channel="2" ratio="1.0" release="0.098769695" sidechain="5" threshold="0.19565232" type="3"/>
</channel>
<channel id="MELODY" index="3">
    <effect channel="3" depth="1" jitter="1.0" rate="0.31891286" type="4" wet="1.0"/>
    <effect attack="0.005227146" channel="3" ratio="0.7956521" release="0.105373904" sidechain="5" threshold="0.58260775" type="3"/>
</channel>
<channel id="STRINGS" index="4">
    <effect channel="4" depth="0.95" feedback="0.25" rate="0.05869563" type="5" wet="1.0"/>
    <effect attack="0.066963136" channel="4" ratio="1.0" release="0.03214792" sidechain="5" threshold="0.0" type="3"/>
</channel>
<channel id="DRUMSIES" index="5"/>
</effects>
    
    
*/