////////////////////////////////////////////////////////////////////////////////
// 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.system;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import com.teotigraphix.caustk.controller.ICaustkController;
import com.teotigraphix.caustk.library.Library;
import com.teotigraphix.caustk.library.LibraryPatch;
import com.teotigraphix.caustk.library.LibraryPattern;
import com.teotigraphix.caustk.library.LibraryPhrase;
import com.teotigraphix.caustk.pattern.Part;
import com.teotigraphix.caustk.pattern.Patch;
import com.teotigraphix.caustk.pattern.Pattern;
import com.teotigraphix.caustk.pattern.Phrase;
import com.teotigraphix.caustk.tone.BasslineTone;
/**
* Represents an abstract memory bank, USER, PRESET etc.
* <p>
* The {@link MemoryManager} will use the {@link MemoryLoader} to load the
* {@link MemorySlotItem}s into the memory bank's category.
*/
public abstract class Memory {
protected ICaustkController controller;
//----------------------------------
// currentLibrary
//----------------------------------
public Library getCurrentLibrary() {
return controller.getLibraryManager().getSelectedLibrary();
}
private Map<Category, MemorySlot> slots = new HashMap<Category, MemorySlot>();
/**
* The {@link MemoryLoader} will load these slots at startup.
*
* @param category The category type.
* @param slot The {@link MemorySlot} to fill the category.
*/
public void put(Category category, MemorySlot slot) {
slots.put(category, slot);
}
private MemorySlot getMemorySlot(Category category) {
return slots.get(category);
}
public MemorySlot getPatternSlot() {
return getMemorySlot(Category.PATTERN);
}
public MemorySlot getPhraseSlot() {
return getMemorySlot(Category.PHRASE);
}
public MemorySlot getPatchSlot() {
return getMemorySlot(Category.PATCH);
}
//----------------------------------
// currentMemorySlot
//----------------------------------
private MemorySlot currentMemorySlot;
public MemorySlot getCurrentMemorySlot() {
return currentMemorySlot;
}
//----------------------------------
// type
//----------------------------------
private Type type;
public Type getType() {
return type;
}
public void getType(Type value) {
if (type == value)
return;
type = value;
}
//----------------------------------
// category
//----------------------------------
private Category category;
public Category getCategory() {
return category;
}
public void setCategory(Category value) {
if (category == value)
return;
category = value;
// instead of have a bunch of subclasses of memory bank
// I can just have a strategy provider that was loaded by the loader
// so I would have PatchProvider, PatternProvider, PhraseProvider, SongProvider
// The loader would load the machines of a preset .caustic file
// populate all machines, patterns and patches.
// it will then create each strategy and populate it with MemoryBankItems
// such as PatchItem in a LINEAR list that will forever more be used in the
// duration of the application. So the linear list index is established
// by the loader when it is creating the memory bank items
//================
// So, the loader will have to load each type of bank and have items for each bank
// IE USER, PRESET etc. So PRESET will have its own lists of PatchItem etc
currentMemorySlot = slots.get(category);
}
public Memory(ICaustkController systemController, Type type) {
this.controller = systemController;
this.type = type;
}
/**
* Returns the number of patterns that have been loaded into the banks'
* memory.
*/
public int getPatternCount() {
return getPatternSlot().size();
}
/**
* Loads the {@link Library} for the specific memory bank.
*
* @param library TODO
* @throws IOException
*/
public void load(Library library) throws IOException {
//XXX this needs to be run in a service
// this list for now is ONLY used to create the bank and pattern names
// for the UI display
// seems like the most logical way is to have
// A[0], B[1], C[2], D[3], E[4], F[5]
// where the index is the machine
// each bank has 64 possible patterns from the machine
// the algorithm will automatically fill all 64 with either existing patterns
// or empty, if the machine does not exist, the bank will not be created
getPhraseSlot().addAll(library.getPhrases());
getPatchSlot().addAll(library.getPatches());
getPatternSlot().addAll(library.getScenes());
}
/**
* Copies a pattern data from the bank into a NEW {@link Pattern} instance
* and returns it.
* <p>
* This will work out to a pattern having the string id of <code>U14</code>
* or <code>U01</code> all the way up to <code>U63</code>.
* <p>
* Until more complex logic is implemented, 64 patterns per {@link Pattern}
* is the most easiest to manager right now.
*
* @param index The index (0-63) of the patter. Note, the linear value must
* be mapped from the Caustic bank/pattern paradigm of 4 banks
* and 16 patterns in a bank.
* @return A new instance of a {@link Pattern} initialized with the
* serialized full pattern data OR initialized with the default
* configurator.
*/
public Pattern copyPattern(int index) {
Pattern pattern = getPattern(index);
controller.getPatternManager().configure(pattern);
return pattern;
}
/**
* Always returns a NEW instance with {@link Part} and {@link Phrase} data
* loaded.
* <p>
* It looks like the implementation is going to change a bit, since this
* needs to return a fully loaded Pattern with parts and phrase data.
* <p>
* The commit() method of the temporary memory will actually set the part
* and phrase data INTO the tone.
*
* @param index The index of the {@link Pattern} to return based on the
* selection in the UI that was based off what this provider
* returned as the initial list of patterns.
*/
Pattern getPattern(int index) {
// XXX If this works with libraries, it might be better to add the items
// to the slods in the USER, SYSTEM banks so there is no ref to a library in this class
//LibraryItem item = (LibraryItem)getPatternSlot().getItem(index);
LibraryPattern item = getCurrentLibrary().getPatterns().get(index);
Pattern pattern = new Pattern(controller, item);
return pattern;
}
public Phrase copyPhrase(Part part, int index) {
Phrase phrase = getPhrase(part);
//phrase.configure();
return phrase;
}
/**
* Copies a {@link Patch} from memory into a new {@link Patch} instance.
*
* @param part
* @param index The index of the patch to copy form memory.
* @return A new instance of the {@link Patch}.
*/
public Patch copyPatch(Part part, int index) {
Patch patch = getPatch(part);
patch.configure();
return patch;
}
/**
* Creates a {@link Phrase} for the {@link Part}.
*
* @param part The {@link Part} needing a {@link Phrase}.
*/
Phrase getPhrase(Part part) {
int index = part.getIndex();
UUID id = part.getPattern().getPatternItem().getPhrase(index);
LibraryPhrase libraryPhrase = getCurrentLibrary().findPhraseById(id);
if (libraryPhrase == null) {
libraryPhrase = new LibraryPhrase();
}
Phrase phrase = new Phrase(part, libraryPhrase);
return phrase;
}
/**
* Creates and returns a {@link Patch} based on the {@link Part} passed.
* <p>
* An example is a {@link Part#getTone()} being an instance of
* {@link BasslineTone}, the method would return a {@link BasslinePatch}.
*
* @param part The {@link Part} needing a {@link Patch}.
*/
Patch getPatch(Part part) {
int index = part.getIndex();
UUID patchId = part.getPattern().getPatternItem().getToneSet().getDescriptors().get(index)
.getPatchId();
LibraryPatch item = getCurrentLibrary().findPatchById(patchId);
//if (part.getTone() instanceof BasslineTone)
return new Patch(part, item);
//return null;
}
public enum Type {
PRESET, USER, EXTERNAL, TEMPORARY;
}
public enum Category {
PATTERN_SET, PATTERN, SONG, RPSSET, PATCH, PHRASE;
}
}