Java tutorial
/* * 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.Collections; import java.util.List; import java.util.Random; 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 org.apache.commons.lang3.ArrayUtils; import com.music.model.Chord; import com.music.model.ExtendedPhrase; import com.music.model.InstrumentGroups; import com.music.model.PartType; import com.music.model.Scale; import com.music.model.SpecialNoteType; import com.music.model.ToneGroups; import com.music.model.ToneType; import com.music.util.music.Chance; import com.music.util.music.ChordUtils; import com.music.util.music.NoteFactory; import com.music.util.music.ToneResolver; public class Arpeggiator implements ScoreManipulator { private Random random = new Random(); @Override public void handleScore(Score score, ScoreContext ctx) { Part part = ctx.getParts().get(PartType.ARPEGGIO); if (part == null) { return; } Part mainPart = ctx.getParts().get(PartType.MAIN); double currentMeasureSize = 0; double normalizedMeasureSize = ctx.getNormalizedMeasureSize(); SpecialNoteType specialNoteType = null; for (Phrase phrase : mainPart.getPhraseArray()) { if (Chance.test(20)) { // change the special note type if (Chance.test(60)) { // to a new value specialNoteType = SpecialNoteType.values()[random.nextInt(SpecialNoteType.values().length)]; } else { // reset specialNoteType = null; } } Phrase arpeggioPhrase = new Phrase(); arpeggioPhrase.setTitle("Arpeggio phrase"); Scale currentScale = ((ExtendedPhrase) phrase).getScale(); // get copies of the static ones, so that we can shuffle them without affecting the original List<Chord> scaleChords = new ArrayList<Chord>(ChordUtils.chords.get(currentScale)); Collections.shuffle(scaleChords, random); Note[] notes = phrase.getNoteArray(); List<ToneType> firstToneTypes = new ArrayList<>(); int measures = 0; Note[] currentNotes = null; boolean useTwoNoteChords = Chance.test(14); Chord chord = null; for (int i = 0; i < notes.length; i++) { Note currentNote = notes[i]; if (currentNote.getRhythmValue() == 0) { continue; // rhythm value is 0 for the first notes of a (main-part) chord. So progress to the next } boolean lastMeasure = measures == ctx.getMeasures() - 1; if (currentMeasureSize == 0 && !currentNote.isRest() && !lastMeasure) { boolean preferStable = ToneResolver.needsContrastingChord(firstToneTypes, ToneGroups.UNSTABLE); boolean preferUnstable = ToneResolver.needsContrastingChord(firstToneTypes, ToneGroups.STABLE); // change the chord only in 1/4 of the cases if (currentNotes == null || Chance.test(25)) { // no alternatives for now - only 3-note chords Chord previous = chord; chord = ChordUtils.getChord(ctx, currentNote.getPitch(), previous, scaleChords, scaleChords, scaleChords, preferStable, preferUnstable); if (chord != null) { int[] pitches = chord.getPitches(); //remove the middle note in some cases (but make it possible to have three-note chords in a generally two-note phrase) if (pitches.length == 3 && useTwoNoteChords && Chance.test(90)) { pitches = ArrayUtils.remove(pitches, 1); } int count = Chance.test(90) ? (Chance.test(80) ? 4 : 2) : (Chance.test(80) ? 3 : 5); currentNotes = new Note[count]; double length = normalizedMeasureSize / count; for (int k = 0; k < count; k++) { Note note = NoteFactory.createNote(pitches[random.nextInt(pitches.length)], length); note.setDynamic(InstrumentGroups.getInstrumentSpecificDynamics( 65 + random.nextInt(10), part.getInstrument())); if (specialNoteType != null) { note.setDuration(note.getRhythmValue() * specialNoteType.getValue()); } currentNotes[k] = note; } } } if (Chance.test(85) && currentNotes != null) { for (Note note : currentNotes) { arpeggioPhrase.addNote(note); } } else { arpeggioPhrase.addRest(new Rest(normalizedMeasureSize)); } } else if (currentMeasureSize == 0 && (currentNote.isRest() || lastMeasure)) { arpeggioPhrase.addRest(new Rest(normalizedMeasureSize)); } currentMeasureSize += currentNote.getRhythmValue(); if (currentMeasureSize >= normalizedMeasureSize) { currentMeasureSize = 0; measures++; } } part.add(arpeggioPhrase); } } }