// http://www.solomonsmusic.net/vartech.htm
// http://dolmetsch.com/form.pdf
logger.debug("Generating piece with ctx=" + ctx);
Part mainPart = ctx.getParts().get(PartType.MAIN);
MainPartContext lCtx = initLocalContext(score, ctx);
// used for representing the structure of the phrase, for debugging purposes
StringBuilder structureItem = new StringBuilder();
// some empty initial measures
addInitialMeasures(lCtx, mainPart);
// TODO add upBeat
while(lCtx.getTotalMeasures() + lCtx.getCurrentPhraseMeasuresCounter() < ctx.getMeasures()) {
if (lCtx.getCurrentPhrase() == null) {
createNewPhrase(lCtx, mainPart);
}
initializeMeasure(lCtx);
boolean downBeat = lCtx.isNextNoteDownBeat(); //getting here, because it will get overridden when we calculate the length
if (lCtx.getCurrentPhraseMeasuresCounter() >= lCtx.getPhraseMeasures() && lCtx.getCurrentMeasureSize() == 0 && lCtx.getTotalLength() > 0) {
//stop repeating the theme and end the phrase
lCtx.getCurrentPhrase().setNotes(lCtx.getPitches().size());
resetPhrase(lCtx);
continue;
}
// in-phrase repetitions (motifs)
if (lCtx.getCurrentPhraseMeasuresCounter() > 0 && lCtx.getCurrentPhraseMeasuresCounter() < lCtx.getPhraseMeasures()) {
if (!lCtx.getCurrentPhraseMotifs().isEmpty() && Chance.test(40) && lCtx.getCurrentMeasureSize() == 0) {
lCtx.getCurrentPhrase().getStructure().add(structureItem.toString());
structureItem = new StringBuilder();
repeatExistingMotifWithinPhrase(lCtx);
lCtx.setMeasuresSinceLastMotif(0);
} else if (shouldRepeatNewMotif(lCtx)) {
lCtx.getCurrentPhrase().getStructure().add(structureItem.toString());
structureItem = new StringBuilder();
repeatNewMotifWithinPhrase(lCtx);
lCtx.setMeasuresSinceLastMotif(0);
} else {
// count the number of measures since the last motif repetition
if (lCtx.getCurrentMeasureSize() == 0) {
lCtx.setMeasuresSinceLastMotif(lCtx.getMeasuresSinceLastMotif() + 1);
}
if (Chance.test(17)) {
repeatNotesWithinPhrase(lCtx);
structureItem.append("--Repeat--");
}
}
}
handleSpecialNotes(lCtx);
handleContour(lCtx);
boolean useInterval = false;
boolean terraceContourChange = lCtx.getContour() == Contour.TERRACE && lCtx.getCurrentMeasureSize() == 0 && lCtx.getCurrentPhraseMeasuresCounter() % 2 == 0 && lCtx.getCurrentPhraseMeasuresCounter() != 0;
if (terraceContourChange) {
useInterval = true;
lCtx.setDirectionUp(!lCtx.isDirectionUp());
}
//TODO add two-note chords (simultaneous intervals). The logic should contain resolving dissonant and unstable sim.intervals to consonant and stable.
lCtx.setNotePitch(getNextNotePitch(lCtx, useInterval));
// restore the direction after the terrace jump
if (terraceContourChange) {
lCtx.setDirectionUp(!lCtx.isDirectionUp());
}
//TODO climax - around 2/3 and 3/4
double length = getLength(lCtx);
lCtx.setLength(length);
lCtx.getPitches().add(lCtx.getNotePitch());
lCtx.getUniquePitches().add(lCtx.getNotePitch());
// use pause in a couple of cases at random, and also in the middle and at the end of the theme
boolean usePause = Chance.test(11)
|| (lCtx.isEndOfMeasure() == true
&& lCtx.getCurrentPhraseMeasuresCounter() == lCtx.getPhraseMeasures() / 2 && Chance
.test(70))
|| (lCtx.getCurrentPhraseMeasuresCounter() == lCtx.getPhraseMeasures() && Chance
.test(75));
if (usePause) {
lCtx.getCurrentPhrase().addRest(new Rest(length));
structureItem.append("-R-");
} else {
Note note = createNote(lCtx, downBeat, length);
if (lCtx.isAllowOrnaments() && Chance.test(6)) {
addOrnamentedNote(lCtx, length, note);
} else {
lCtx.getCurrentPhrase().addNote(note);
}
structureItem.append("-" + note.getPitch() + "-");
}
}
// add the cadences to the final phrase, if the above loop has been interrupted due to the end of the piece
if (lCtx.getCurrentPhrase() != null) {
lCtx.getCurrentPhrase().getStructure().add(structureItem.toString());
int endingMeasures = addEnding(lCtx.getCurrentPhrase(), ctx);
lCtx.setCurrentPhraseMeasuresCounter(lCtx.getCurrentPhraseMeasuresCounter() + endingMeasures);
lCtx.setTotalMeasures(lCtx.getTotalMeasures() + lCtx.getCurrentPhraseMeasuresCounter());
lCtx.getCurrentPhrase().setMeasures(lCtx.getCurrentPhraseMeasuresCounter());
}
List<ExtendedPhrase> phrases = repeatPhrases(lCtx);
// override with the possibly changed list of phrases
lCtx.setPhrases(phrases);
// if any (slight) modification has been carried out to the measure count, update it here
ctx.setMeasures(lCtx.getTotalMeasures());
// add the phrases to the main part
mainPart.addPhraseList(lCtx.getPhrases().toArray(new ExtendedPhrase[lCtx.getPhrases().size()]));
// transpose to the desired key
Mod.transpose(mainPart, ctx.getKeyNote());
if (ctx.getParts().containsKey(PartType.MAIN_DUPLICATE)) {
Part duplicateMainPart = ctx.getParts().get(PartType.MAIN_DUPLICATE);
duplicateMainPart.addPhraseList(mainPart.getPhraseArray());
if (Chance.test(45) || duplicateMainPart.getInstrument() == mainPart.getInstrument()) {
Mod.transpose(duplicateMainPart, -12); // an octave lower
}
}
applyExtras(ctx);