/* * Computoser is a music-composition algorithm and a website to present the results * Copyright (C) 2012-2014 Bozhidar Bozhanov * * Computoser is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * Computoser 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Computoser. If not, see <http://www.gnu.org/licenses/>. */ package com.music; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.Vector; import jm.music.data.CPhrase; import jm.music.data.Note; import jm.music.data.Part; import jm.music.data.Phrase; import jm.music.data.Rest; import jm.music.data.Score; import com.music.model.ExtendedPhrase; import com.music.model.PartType; import com.music.util.music.Chance; import com.music.util.music.NoteFactory; public class PercussionGenerator implements ScoreManipulator { private Random random = new Random(); private int[] PRIMARY_DRUM_PERCENTAGES = { 90, 10, 25, 50, 80, 10, 25, 50 }; private int[] SECONDARY_DRUM_PERCENTAGES = { 15, 5, 80, 5, 20, 15, 80, 15 }; private int[] AVAILABLE_PERCUSSIONS = {36, 38, 42, 43, 44, 46, 48, 49, 51}; @Override public void handleScore(Score score, ScoreContext ctx) { Part drumPart = ctx.getParts().get(PartType.PERCUSSIONS); if (drumPart == null) { return; } Part mainPart = ctx.getParts().get(PartType.MAIN); Phrase[] phrases = mainPart.getPhraseArray(); for (Phrase phrase : phrases) { if (!(phrase instanceof ExtendedPhrase)) { continue; } int[] percussions = getRandomPercussionPattern(); // TODO allow for -..-..-., and generally allow more strictly accented beats boolean useMiddleBeats = Chance.test(5); ExtendedPhrase ePhrase = (ExtendedPhrase) phrase; double beatNoteLength = ctx.getNormalizedMeasureSize() / 8; if (useMiddleBeats) { beatNoteLength = ctx.getNormalizedMeasureSize() / 16; } for (int i = 0; i < ePhrase.getMeasures(); i++) { List<Phrase> fullKit = new ArrayList<Phrase>(); // sometimes alternate the pattern per measure if (Chance.test(8)) { percussions = getRandomPercussionPattern(); } // don't use the drums in some measures if (Chance.test(6)) { Phrase phr = new Phrase(0.0); phr.addRest(new Rest(ctx.getNormalizedMeasureSize())); fullKit.add(phr); continue; } for (int j = 0; j < percussions.length; j++) { Phrase phr = new Phrase(0.0); for (short k = 0; k < 16; k++) { int[] chances = PRIMARY_DRUM_PERCENTAGES; if (j % 2 == 1 || percussions[j] == 46) { chances = SECONDARY_DRUM_PERCENTAGES; } if ((useMiddleBeats && k % 2 == 1 && Chance.test(6)) || Chance.test(chances[k / 2])) { Note note = NoteFactory.createNote(percussions[j], beatNoteLength); if (k % 2 == 1) { note.setDynamic(35 + random.nextInt(8)); } else { note.setDynamic(45 + random.nextInt(10)); } phr.addNote(note); } else { phr.addRest(new Rest(beatNoteLength)); } } fullKit.add(phr); } // add phrases to the instrument (part) CPhrase cp = new CPhrase(0.0); cp.setPhraseList(new Vector<Phrase>(fullKit)); cp.setStartTime(i * ctx.getNormalizedMeasureSize()); drumPart.addCPhrase(cp); } } } private int[] getRandomPercussionPattern() { int[] percussions = new int[2 + random.nextInt(3)]; for (int i = 0; i < percussions.length; i++) { percussions[i] = AVAILABLE_PERCUSSIONS[random.nextInt(AVAILABLE_PERCUSSIONS.length)]; } return percussions; } }