package eu.bandm.music.haken ;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import eu.bandm.tools.annotations.Opt ;
import eu.bandm.tools.util.Rational ;
import eu.bandm.tools.message.SimpleMessage;
import eu.bandm.tools.message.MessageReceiver;
import eu.bandm.tools.message.MessageCounter;
import eu.bandm.tools.message.MessageFormatter;
import eu.bandm.tools.message.MessagePrinter;
import eu.bandm.tools.message.MessageTee;
import eu.bandm.tools.message.XMLDocumentIdentifier;
import eu.bandm.tscore.model.TimeScape ;
import eu.bandm.tscore.model.Part ;
import eu.bandm.tscore.model.Tp ;
import eu.bandm.tscore.model.Vox ;
import eu.bandm.tscore.model.Event ;
import eu.bandm.tscore.base.Util ;
import eu.bandm.music.entities.RationalDuration.DottedBaseDuration ;
import eu.bandm.music.entities.QualifiedRational ;
import eu.bandm.music.entities.MetricConsumer ;
import eu.bandm.music.entities.MTree ;
import eu.bandm.music.entities.StemEnd ;
/** A first version of a haken score for realization by a solo vocalist.
* This is the production version for one single vocal output, singing just
* vowels and semi-vowels on one fixed pitch.
* With one K-voices per parameter "lightness" and "soundness".
* Usage: create an Instance (with an already raw-parsed TimeScape)
* and call "update(... score.findVoice(..)..).
* The {@link #main(String[])} method takes filenames and voice names from the command
* line parameters.
*/
public class Score_hkn_vox_v00 extends Score_hkn {
// ---------------------------------------------------------------------
// lilypond conversions
// ---------------------------------------------------------------------
/** Evident*/
protected static final Rational rat_3_4 = Rational.valueOf(3,4);
/** Evident*/
protected static final QualifiedRational sound_3_4
= new QualifiedRational(rat_3_4, true);
/** Evident*/
protected static final Rational rat_1_4 = Rational.valueOf(1,4);
/** Evident*/
protected static final QualifiedRational sound_1_4
= new QualifiedRational(rat_1_4, true);
/** Auxiliary module for generating a LilyPond source text as output.*/
protected final LilypondGenerator lilypondGenerator ;
// ---------------------------------------------------------------------
/** Only constructor.
* @param name of the score, also used to derive output file names
* @param ts the raw parsed tscore input data.
* @param msg the drain of all messages.
* @param parameters control parsing, if left out, derfaults to "new {@link Parameters}".
*/
public Score_hkn_vox_v00 (final String name,
final Part ts,
final MessageReceiver> msg,
final @Opt Parameters parameters){
super(name, ts, msg, parameters);
lilypondGenerator = new LilypondGenerator(msg);
}
/** FIXME DOC*/
public static Map fromTimeScape
(final TimeScape ts,
final String topName,
final MessageReceiver> msg,
final @Opt Parameters parameters){
return Util.partsToScores(ts, topName, "%s_%s", msg, parameters, Score_hkn_vox_v00::new);
}
/** The list of RelWert used for the "height" of the sung sound.*/
List seq_height ;
/** The list of RelWert used for the "soundness" of the sung sound.*/
List seq_vowelness ;
/** Constant used for the pitch height for all generated V-Events.*/
public static final String lilypond_constant_pitch = "e''" ; // "h'" ;
/** List of the length of the pauses from p0=quarter note to p4=brevis.*/
final public QualifiedRational[] pauseGrades
= { QualifiedRational.pause(1,4),
QualifiedRational.pause(1,2),
QualifiedRational.pause(3,4),
QualifiedRational.pause(1,1),
QualifiedRational.pause(2,1)
};
/** Highest valid index into {@link #pauseGrades}.*/
final public int maxPause = pauseGrades.length-1 ;
/** Get the duration of the pause to be insert before the event on the
* i-th K-time point.
* @return null if not pause is specified in the selected voice.
*/
protected @Opt QualifiedRational getPauseByIndex(final List tps,
final @Opt Vox vpause,
final int i
/*GLO event2pause */){
final int g = getPauseIndexByIndex (tps, vpause, i, maxPause );
return (g>noPauseSelected) ? pauseGrades[g] : null ;
}
/** Index into the list of durations for the MetricConsumer write out method.
* Must be a class field, not a method variable, for mere technical (Java) reasons.
* ("external fields referred to from a inner class must be final", etc.)
*/
int accu_index_writeout ;
/** The character representation of the sounds to sing, indexed first by
* vowel-ness (U to O), then by brightness (U to O).
*/
public static final String[][] lyricSquare
= {
{ "\u03C7", "\u0283", "\u00E7" },
{ "m", "n", "l" },
{ "u", "a", "i" }
};
/**
* Generate the lilyPond commands for the sequence of K-Evenst.
* @param max number of K-Events to process
* @param tps all time points after expansion
* @param vpause voice selected for the additional articulation pauses
* @param checkLastForPause whether the last pause ????? FIXME
*/
protected void writeout(final int max,
final List tps,
final @Opt Vox vpause,
final boolean checkLastForPause
/*GLO IN accu_index, etc.*/ ){
final @Opt QualifiedRational lastPause
= checkLastForPause ? getPauseByIndex(tps, vpause, max) : null ;
// Step through all time points and mix K-Events (with a fixed duration) and
// pauses into one list of durations.
// The list internal in the LilypondGenerator grows IN PARALLEL and
// maps the SUM of standard V-Event duration and articulation pauses to the K-events:
final List durations = new ArrayList<>();
for (int i = 0; i> msg
= new MessageTee<>(new MessageFormatter<>(new MessagePrinter<>()),
mcnt);
if (args.length<6)
msg.receive(SimpleMessage.error("not enough command line parameters."));
mcnt.terminateApplicationOnErrors();
String message = null ;
final String inputfilename = (args[0]);
msg.receive(SimpleMessage.logStart("parsing input file %s to raw data", inputfilename));
final File inputfile = new File(inputfilename) ;
final TimeScape ts = Util.parseTimeScape(inputfile, modifiers, msg);
msg.receive(SimpleMessage.logEnd("parsing input file"));
mcnt.terminateApplicationOnErrors();
// ====================================================================
// convert raw model into Score_haken semantic interpretation
// ====================================================================
msg.receive(SimpleMessage.logStart(message="semantic interpretation"));
final String scoreNameStem = args[1];
final Map scores
= fromTimeScape(ts,scoreNameStem,msg,null);
@Opt String subtitle = args[2];
if (noVoiceSelected.equals(subtitle))
subtitle = null ;
for (final Score_hkn_vox_v00 score : scores.values()){
final @Opt Vox v_ps = score.findVoice (args[3], true/*=errorNotWarning on spelling error.*/);
// from here different from "vla":
final @Opt Vox v_sound = score.findVoice (args[4], true);
final @Opt Vox v_height = score.findVoice (args[5], true);
if (v_sound==null || v_height==null)
msg.receive(SimpleMessage.error("both voices (for soundness and height) must be given."));
mcnt.terminateApplicationOnErrors();
// ==========================================
score.update(inputfilename, score.name, subtitle, v_ps, v_sound, v_height);
// ==========================================
msg.receive(SimpleMessage.logEnd(message));
// ====================================================================
// write lilypond format objects to output file
// ====================================================================
final String outputfilename = score.name+".ly";
score.lilypondGenerator.writeToFile(outputfilename);
msg.receive(SimpleMessage.log("result written to %s", outputfilename));
}// for scores
}//main
}