/* * 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 static com.music.model.InstrumentGroups.ACCOMPANIMENT_INSTRUMENTS; import static com.music.model.InstrumentGroups.ARPEGGIO_INSTRUMENTS; import static com.music.model.InstrumentGroups.BASS_INSTRUMENTS; import static com.music.model.InstrumentGroups.DRONE_INSTRUMENTS; import static com.music.model.InstrumentGroups.ELECTRONIC_MAIN_PART_ONLY_INSTRUMENTS; import static com.music.model.InstrumentGroups.MAIN_PART_ONLY_INSTRUMENTS; import static com.music.model.InstrumentGroups.PAD_INSTRUMENTS; import static com.music.model.InstrumentGroups.SIMPLE_BEAT_INSTRUMENTS; import static com.music.model.InstrumentGroups.SPORADIC_EFFECTS_INSTRUMENTS; import java.util.Random; import jm.constants.Instruments; import jm.music.data.Part; import jm.music.data.Score; import com.music.model.InstrumentGroups; import com.music.model.PartType; import com.music.util.music.Chance; public class PartConfigurer implements ScoreManipulator { private Random random = new Random(); @Override public void handleScore(Score score, ScoreContext ctx) { boolean accompaniment = Chance.test(70); int partIdx = 0; int mainInstrument = 0; boolean electronic = false; boolean classical = Chance.test(5); //ensure some classical-sounding pieces boolean counterpoint = false; if (classical) { accompaniment = true; } if (Chance.test(1) || (classical && Chance.test(12))) { counterpoint = true; accompaniment = false; } if (!accompaniment) { if (Chance.test(35)) { // electronic electronic = true; ctx.setElectronic(true); mainInstrument = ELECTRONIC_MAIN_PART_ONLY_INSTRUMENTS[random.nextInt(ELECTRONIC_MAIN_PART_ONLY_INSTRUMENTS.length)]; } else { mainInstrument = MAIN_PART_ONLY_INSTRUMENTS[random.nextInt(MAIN_PART_ONLY_INSTRUMENTS.length)]; } } else { mainInstrument = InstrumentGroups.MAIN_PART_INSTRUMENTS[random.nextInt(InstrumentGroups.MAIN_PART_INSTRUMENTS.length)]; } if (classical) { mainInstrument = Instruments.PIANO; } Part main = new Part(PartType.MAIN.getTitle(), mainInstrument , partIdx++); score.add(main); ctx.getParts().put(PartType.MAIN, main); if (Chance.test(12)) { Part duplicateMainPart = new Part(PartType.MAIN_DUPLICATE.getTitle(), MAIN_PART_ONLY_INSTRUMENTS[random.nextInt(MAIN_PART_ONLY_INSTRUMENTS.length)], partIdx++); score.add(duplicateMainPart); ctx.getParts().put(PartType.MAIN_DUPLICATE, duplicateMainPart); } if (accompaniment) { if (Chance.test(55) && !electronic) { Part accompanimentPart = new Part(PartType.ACCOMPANIMENT.getTitle(), ACCOMPANIMENT_INSTRUMENTS[random.nextInt(ACCOMPANIMENT_INSTRUMENTS.length)], partIdx++); score.add(accompanimentPart); ctx.getParts().put(PartType.ACCOMPANIMENT, accompanimentPart); } else if (isRegularMetre(ctx)){ Part arpegioPart = new Part(PartType.ARPEGGIO.getTitle(), ARPEGGIO_INSTRUMENTS[random.nextInt(ARPEGGIO_INSTRUMENTS.length)], partIdx++); score.add(arpegioPart); ctx.getParts().put(PartType.ARPEGGIO, arpegioPart); } } boolean hasBass = Chance.test(22) && !classical; if (hasBass) { Part bassPart = new Part(PartType.BASS.getTitle(), BASS_INSTRUMENTS[random.nextInt(BASS_INSTRUMENTS.length)], partIdx++); score.add(bassPart); ctx.getParts().put(PartType.BASS, bassPart); } if (!hasBass && isRegularMetre(ctx) && !classical && Chance.test(16)) { Part dronePart = new Part(PartType.DRONE.getTitle(), DRONE_INSTRUMENTS[random.nextInt(DRONE_INSTRUMENTS.length)], partIdx++); score.add(dronePart); ctx.getParts().put(PartType.DRONE, dronePart); } boolean useSimpleBeat = Chance.test(33) && !classical; if (useSimpleBeat) { Part extraPart = new Part(PartType.SIMPLE_BEAT.getTitle(), SIMPLE_BEAT_INSTRUMENTS[random.nextInt(SIMPLE_BEAT_INSTRUMENTS.length)], partIdx++); score.add(extraPart); ctx.getParts().put(PartType.SIMPLE_BEAT, extraPart); } boolean usePercussions = Chance.test(46); if (!useSimpleBeat && usePercussions && isRegularMetre(ctx) && !classical) { Part extraPart = new Part(PartType.PERCUSSIONS.getTitle(), 0, 9); extraPart.setDynamic(35); //quieter, in the background score.add(extraPart); ctx.getParts().put(PartType.PERCUSSIONS, extraPart); } //TODO beats (ala drum & bass). no cymbals? if (!classical && Chance.test(30)) { Part extraPart = new Part(PartType.EFFECTS.getTitle(), SPORADIC_EFFECTS_INSTRUMENTS[random.nextInt(SPORADIC_EFFECTS_INSTRUMENTS.length)], partIdx++); score.add(extraPart); ctx.getParts().put(PartType.EFFECTS, extraPart); } if (Chance.test(30) && !classical) { Part padPart1 = new Part(PartType.PAD1.getTitle(), PAD_INSTRUMENTS[random.nextInt(PAD_INSTRUMENTS.length)], partIdx++); score.add(padPart1); ctx.getParts().put(PartType.PAD1, padPart1); if (Chance.test(46)) { Part padPart2 = new Part(PartType.PAD2.getTitle(), PAD_INSTRUMENTS[random.nextInt(PAD_INSTRUMENTS.length)], partIdx++); score.add(padPart2); ctx.getParts().put(PartType.PAD2, padPart2); } } // TODO add more dramatic features other than the timpani if (Chance.test(6) && isRegularMetre(ctx) && !classical) { Part timpaniPart = new Part(PartType.TIMPANI.getTitle(), Instruments.TIMPANI, partIdx++); score.add(timpaniPart); ctx.getParts().put(PartType.TIMPANI, timpaniPart); } } public static boolean isRegularMetre(ScoreContext ctx) { // check if power of 2 return ((ctx.getMetre()[0]-1) & ctx.getMetre()[0]) == 0; } }