/* * Created on Mar 6, 2007 * * Copyright (c) 2007 Jens Gulden * * http://www.frinika.com * * This file is part of Frinika. * * Frinika 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 2 of the License, or * (at your option) any later version. * Frinika 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 Frinika; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.frinika.sequencer.midi.groovepattern; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import com.frinika.global.FrinikaConfig; import com.frinika.sequencer.model.MidiPart; /** * <<singleton>> * Globally handles groove-patterns for the groove-quantization feature. * * TODO Currently stores user-defined or imported patterns as .mid files in a directory * .frinika-groove-patterns/. This is very simple, persistent storage of patterns should * be configurable via user-options. * * @see com.frinika.sequencer.gui.menu.midi.MidiQuantizeAction * @author Jens Gulden */ public class GroovePatternManager { public final static String PRESETS_PACKAGE = "groovepatterns"; public final static String[] PRESETS = new String[] { "test1" }; // TODO // --- static --- private static GroovePatternManager instance; // singleton instance private static Map<String, GroovePattern> presetPatterns; private static Map<String, GroovePatternFromSequence> userPatterns; public static GroovePatternManager getInstance() { if (instance == null) { initPresets(); loadUserPatterns(); instance = new GroovePatternManager(); } return instance; } private static void initPresets() { presetPatterns = new HashMap<String, GroovePattern>(); for (int i = 0; i < PRESETS.length; i++) { String name = PRESETS[i]; InputStream res = ClassLoader.getSystemResourceAsStream(PRESETS_PACKAGE + "/" + name + ".mid"); try { loadPresetPattern(presetPatterns, name, res); } catch (IOException ioe) { ioe.printStackTrace(); } } } private static void loadUserPatterns() { userPatterns = new HashMap<String, GroovePatternFromSequence>(); File dir = FrinikaConfig.GROOVE_PATTERN_DIRECTORY; //new File( FrinikaConfig.GROOVE_PATTERN_DIRECTORY ); if ( dir.exists() ) { File[] files = dir.listFiles(); for (int i = 0; i < files.length; i++) { File file = files[i]; String name = file.getName(); if (name.endsWith(".mid")) { name = name.substring(0, name.length() - 4); try { FileInputStream in = new FileInputStream(file); loadPresetPattern(userPatterns, name, in); in.close(); } catch (IOException ioe) { ioe.printStackTrace(); } } } } } private static void loadPresetPattern(Map map, String name, InputStream in) throws IOException { GroovePatternFromSequence gp = new GroovePatternFromSequence(); gp.importFromMidiFile(name, in); map.put(name, gp); } private static void addUserPattern(GroovePatternFromSequence gp) { userPatterns.put(gp.getName(), gp); } private static void removeUserPattern(GroovePatternFromSequence gp) { userPatterns.remove(gp.getName()); } private static boolean hasUserPattern(String name) { return (userPatterns != null) && (userPatterns.get(name) != null); } private static void storeUserPattern(GroovePatternFromSequence groovePattern) throws IOException { // ensure storage dir exists File dir = FrinikaConfig.GROOVE_PATTERN_DIRECTORY; //new File(STORAGE_DIRECTORY); dir.mkdir(); // save groovePattern.saveAsMidiFile( storageFile(groovePattern) ); } public static String normalizeName(String name) { // replace all blanks and non-filename-like characters return name.replace(' ', '_').replace('\t','_').replace('.','_').replace(':','_').replace('/','_').replace('\\','_').replace('?','_').replace('*','_'); } // --- instance --- private GroovePatternManager() { // singleton constructor } public Collection<GroovePattern> getGroovePatterns() { ArrayList<GroovePattern> l = new ArrayList<GroovePattern>(); l.addAll( getPresetGroovePatterns() ); l.addAll( getUserGroovePatterns() ); return l; } /** * Finds a groove pattern by name. * * @param name * @return a groove pattern with the requested unique name, or null if ot found */ public GroovePattern getGroovePattern(String name) { for (GroovePattern gp : getGroovePatterns()) { if (gp.getName().equals(name)) { return gp; } } return null; } public Collection<GroovePattern> getPresetGroovePatterns() { return presetPatterns.values(); } public Collection<GroovePatternFromSequence> getUserGroovePatterns() { return userPatterns.values(); } public GroovePatternFromSequence importUserGroovePattern(File midiFile) throws IOException { GroovePatternFromSequence gp = new GroovePatternFromSequence(); gp.importFromMidiFile(midiFile); ensureUniqueName(gp.getName()); addUserPattern(gp); storeUserPattern(gp); return gp; } public GroovePatternFromSequence importUserGroovePattern(String name, MidiPart part) throws IOException { ensureUniqueName(name); GroovePatternFromSequence gp = new GroovePatternFromSequence(); gp.importFromMidiPart(name, part); addUserPattern(gp); storeUserPattern(gp); return gp; } public void removeUserGroovePattern(GroovePatternFromSequence gp) throws IOException { if (hasUserPattern(gp.getName())) { removeUserPattern(gp); // delete file File file = storageFile(gp); if ( ! file.delete() ) { throw new IOException("unable to delete file " + file.getAbsolutePath()); } } } private static File storageFile(GroovePatternFromSequence gp) { return new File(FrinikaConfig.GROOVE_PATTERN_DIRECTORY, normalizeName(gp.getName()) + ".mid" ); } private static void ensureUniqueName(String name) throws IOException { if ( hasUserPattern( name ) ) { throw new IOException("a groove pattern named '"+name+"' already exists"); } } }