/* * Jajuk * Copyright (C) The Jajuk Team * http://jajuk.info * * This program 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 any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.jajuk.services.dj; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.collections.CollectionUtils; import org.jajuk.base.File; import org.jajuk.base.FileManager; import org.jajuk.base.Genre; import org.jajuk.util.Const; import org.jajuk.util.UtilFeatures; import org.jajuk.util.filters.JajukPredicates; /** * Type description. */ public class TransitionDigitalDJ extends DigitalDJ { /** List of transitions, need to be a list, not a set for offset. */ private List<Transition> transitions; /** * The Constructor. * * @param sID */ public TransitionDigitalDJ(String sID) { super(sID); this.transitions = new ArrayList<Transition>(10); } /** * Gets the transitions. * * @return DJ transitions */ public List<Transition> getTransitions() { return this.transitions; } /** * Delete a transition at given offset. * * @param offset */ public void deleteTransition(int offset) { this.transitions.remove(offset); } /** * Add a transition. * * @param transition * @param offset */ public void addTransition(Transition transition, int offset) { this.transitions.add(offset, transition); } /** * Gets the transition. * * @param ambience * * @return transition mapping this FROM ambience or null if none maps it */ public Transition getTransition(Ambience ambience) { for (Transition transition : transitions) { if (CollectionUtils.containsAny(transition.getFrom().getGenres(), ambience.getGenres())) { return transition; } } return null; } /* * Generate the playlist @return the playlist */ /* (non-Javadoc) * @see org.jajuk.services.dj.DigitalDJ#generatePlaylist() */ @Override public List<File> generatePlaylist() { List<File> out = new ArrayList<File>(500); // get a global shuffle selection List<File> global = FileManager.getInstance().getGlobalShufflePlaylist(); // Select by rate if needed filterFilesByRate(global); // None element, leave if (global.size() == 0) { return out; } // Build a ambience -> files map Map<Ambience, List<File>> hmAmbienceFiles = getAmbienceFilesList(global); // compute number of items to add int items = Math.min(global.size(), Const.NB_TRACKS_ON_ACTION); if (!bUnicity && items < Const.MIN_TRACKS_NUMBER_WITHOUT_UNICITY) { // under a limit, if collection is too small and no unicity, use // several times the same files items = Const.MIN_TRACKS_NUMBER_WITHOUT_UNICITY; } // Get first track for (File file : global) { if (transitions.get(0).getFrom().getGenres().contains(file.getTrack().getGenre())) { out.add(file); // Unicity in selection, remove it from this ambience if (bUnicity) { List<File> files = hmAmbienceFiles.get(getAmbience(file.getTrack().getGenre())); files.remove(file); } items--; break; } } // none matching track? return if (out.size() == 0) { return out; } // initialize current ambience with first track ambience (can be null for // unsorted tracks) Ambience currentAmbience = getAmbience(out.get(0).getTrack().getGenre()); // start transition applying while (items > 0) { // A genre can be in only one transition Transition currentTransition = getTransition(currentAmbience); List<File> files = hmAmbienceFiles.get(currentAmbience); int nbTracks = 2; if (currentTransition != null) { nbTracks = currentTransition.getNbTracks(); } // We remove one item as it has already been added through the first track if (out.size() == 1) { nbTracks--; } if (files != null && files.size() >= nbTracks) { for (int i = 0; i < nbTracks && files.size() > 0; i++) { File file = UtilFeatures.getShuffleItem(files); out.add(file); items--; // Unicity in selection, remove it from this ambience if (bUnicity) { files.remove(file); } } } else { // no more tracks for this ambience ? leave // finally ensure that we don't select more than the max number of tracks filterFilesByMaxTrack(out); return out; } if (currentTransition != null) { currentAmbience = currentTransition.getTo(); } else { break; } } // finally ensure that we don't select more than the max number of tracks filterFilesByMaxTrack(out); return out; } /** * Returns a map ambience -> set of files. * * @param global initial set of files to consider * * @return a map ambience -> set of files */ @SuppressWarnings("unchecked") private Map<Ambience, List<File>> getAmbienceFilesList(List<File> global) { // Create a map ambience -> set of files Map<Ambience, List<File>> hmAmbienceFiles = new HashMap<Ambience, List<File>>(5); // For performance, we find unique ambiences in from and to transitions Set<Ambience> ambiences = new HashSet<Ambience>(5); for (Transition tr : transitions) { ambiences.add(tr.getFrom()); ambiences.add(tr.getTo()); } // Fill null key hmAmbienceFiles.put(null, (List<File>) ((ArrayList<File>) global).clone()); // Fill all ambiences for (Ambience ambience : ambiences) { List<File> all = (List<File>) ((ArrayList<File>) global).clone(); CollectionUtils.filter(all, new JajukPredicates.AmbiencePredicate(ambience)); hmAmbienceFiles.put(ambience, all); } return hmAmbienceFiles; } /** * Gets the ambience. * * @param genre * * @return ambience associated with a genre known in transitions or null if * none */ private Ambience getAmbience(Genre genre) { for (Transition transition : transitions) { if (transition.getFrom().getGenres().contains(genre)) { return transition.getFrom(); } } return null; } /** * (non-Javadoc). * * @return the string * * @see dj.DigitalDJ#toXML() */ @Override public String toXML() { StringBuilder sb = new StringBuilder(2000); sb.append(toXMLGeneralParameters()); sb.append("\t<" + Const.XML_DJ_TRANSITIONS + ">\n"); for (Transition transition : transitions) { sb.append("\t\t<" + Const.XML_DJ_TRANSITION + " " + Const.XML_DJ_FROM + "='" + transition.getFrom().toXML() + "' " + Const.XML_DJ_TO + "='" + transition.getTo().toXML() + "' " + Const.XML_DJ_NUMBER + "='" + transition.getNbTracks() + "'/>\n"); } sb.append("\t</" + Const.XML_DJ_TRANSITIONS + ">\n"); sb.append("</" + Const.XML_DJ_DJ + ">\n"); return sb.toString(); } /** * Sets the transitions. * * @param transitions the new transitions */ public void setTransitions(List<Transition> transitions) { this.transitions = transitions; } }