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 }