JScore's raw text file importer
Introduction
JScore is JMSL's music notation package. JMSL can generate notes and send
them to JScore directly. But JScore can import music that was generated
by other languages, too. A simple text file that specifies musical content
can be parsed by a utility class presented here. This text file could be
generated by a C program, a Perl script, a CGI program on the web, or typed
in by hand.
Usage
To import a textfile into JScore, use the class com.softsynth.jmsl.score.util.RawJScoreFileImporter,
passing it the input filename and the output filename.
Example:
java -classpath %CLASSES%\jmsl.jar;%CLASSES%\jscore.jar;%CLASSES%\jsynclasses.jar com.softsynth.jmsl.score.util.RawJScoreFileImporter inputfile.txt outputscore.xml
You can also load the file from a URL and create a JScore directly
in an applet (see examples below).
The class responsible for parsing the textfile is called RawJScoreFileParser,
which we will use for our examples at the end of this document.
Two ways of working
You may either create a file that adds measures and notes in a free
flow style or by specifying measure numbers.
1) In a freeflow style, you specify zero or more MEASURE tags. Specify
-1 as the measure number of all NOTE tags. These NOTE's are simply poured
into the score in the order they are encountered in the textfile. As notes
fill measures, more measures are added.
2) When specifying measure numbers, MEASURE tags and NOTE tags are passed
measure numbers. NOTE's will be added to their specified measures. Gaps
in the score will be filled in with empty measures.
You may use one method for measures and the other method for notes,
but it is not recommended that you mix methods for the same tag.
Comparing both methods
We jump right into showing you examples here, and explain details of tags
and parameters later. Notice that measures are numbered beginning with
0, so for example the fourth measure is 3.
EXAMPLE 1, if a score began in 4/4 time and changed to 5/4 time
in measure 3,
-
Free Flow Style: you need to include three MEASURE 4 4 tags, and one MEASURE
5 4 tag.
MEASURE 4 4
MEASURE 4 4
MEASURE 4 4
MEASURE 5 4
Specifying measure numbers: you need to specify only two MEASURE tags,
one for the first measure of the score, and another with the measure number
of the time signature change.
MEASURE 0 4 4
MEASURE 3 5 4
In both methods above, as notes are added and overflow measure 3, more
measures of 5/4 will be added.
EXAMPLE 2, if a score had one whole note in measure 0 and one
whole note in measure 2,
-
Free Flow Style: you need to include three NOTE tags: one for the note
in the first measure, one to insert a rest in the second measure, and one
to insert a note in the third measure. Notice that -1 is passed as measure
numbers.
NOTE -1 0 960 60 120 0 0 0
NOTE -1 0 960 0 0 0 0 0
NOTE -1 0 960 60 120 0 0 0
Specifying measure numbers: you need to specify only two NOTE tags, one
for the note in the first measure (measure 0), and one for the note in
the third (measure 2). The measure in between will automatically be added
as a blank.
NOTE 0 0 960 60 120 0 0 0
NOTE 2 0 960 60 120 0 0 0
TWO COMPLETE EXAMPLES COMPARING METHODS
Example of a freeflow input file that inputs the first 8 notes of
a C major scale using quarter notes:
TICKSPERQUARTER 240
MAXVEL 127
STAFFS 1
MEASURE 4 4
NOTE -1 0 240 60 100 0 0 0
NOTE -1 0 240 62 100 0 0 0
NOTE -1 0 240 64 100 0 0 0
NOTE -1 0 240 65 100 0 0 0
NOTE -1 0 240 67 100 0 0 0
NOTE -1 0 240 69 100 0 0 0
NOTE -1 0 240 71 100 0 0 0
NOTE -1 0 240 72 100 0 0 0
Example of the same musical content as above, specifying measure numbers:
TICKSPERQUARTER 240
MAXVEL 127
STAFFS 1
MEASURE 0 4 4
NOTE 0 0 240 60 100 0 0 0
NOTE 0 0 240 62 100 0 0 0
NOTE 0 0 240 64 100 0 0 0
NOTE 0 0 240 65 100 0 0 0
NOTE 1 0 240 67 100 0 0 0
NOTE 1 0 240 69 100 0 0 0
NOTE 1 0 240 71 100 0 0 0
NOTE 1 0 240 72 100 0 0 0
Tags and their parameters
Here we specify tags and their parameters.
-
TICKSPERQUARTER ticks required
JScore's quantizer will compare the duration of each NOTE tag to the
value specified here. If TICKSPERQUARTER is 240, then 120 will be an eighth
note. If TICKSPERQUARTER is 1.0, then 0.5 will be an eighth note. If TICKSPERQUARTER
is 240, then 34.28 will be a sixteenth note septuplet.
Only one such tag is allowed per score
-
MAXVEL vel required
JScore's will compare the loudness of each NOTE tag to the value specified
here. If MAXVEL is 127, then 127 will generate a JScore amplitude of 1.0.
Only one such tag is allowed per score
-
STAFFS number required
The number of staves in this score. Intentionally misspelled for unjustifiable
historical reasons.
Only one such tag is allowed per score
-
MEASURE timesig_numer timesig_denom optional
Each MEASURE tag that is encountered will result in one new measure
being added to the score with the specified time signature. If no MEASURE
tag is encountered, one measure of 4/4 is automatically added, and new
measures of 4/4 are added as notes flow in. If you are writing a waltz,
simply specifying MEASURE 3 4 once will do fine for the rest of the score.
If the number of notes fill and exceed the number of measures added by
MEASURE tags, more measures with the last measure's time signature are
added. You may of course, specify a MEASURE tag for every measure in your
score, and change time signatures as often as you like.
IMPORTANT: MEASURE tags are parsed completely independently of NOTE
tags. Their location in the text file has no significance compared to the
location of NOTE tags before or after them. They are not delimiters.
-
MEASURE measure_number timesig_numer timesig_denom optional
This form of the MEASURE tag specifies which measure number gets the
specified time signature. This allows the user to, for example, specify
the following...
MEASURE 0 4 4
MEASURE 10 7 8
...which would result in ten measures of 4/4 (measures 0..9), followed
by one measure of 7/8. Notes that are added beyond measure 10 will automatically
cause more measures of 7/8 to be added.
CLEF measure staff clef_name optional
The CLEF tag allows you to specify clef changes for any staff of any
measure. Measures are numbered starting at 0. Staves are also numbered
starting at 0, where 0 is the top staff.
CLEF names can be any of the following: TREBLE, ALTO, TENOR, BASS,
PERCUSSION
Example:
CLEF 0 0 TREBLE
CLEF 0 1 BASS
...specifies the first measure's top staff shall have a treble clef, and
its second staff shall have a bass clef.
Specifying a clef with a measure number greater than the number of measures
built by MEASURE tags results in measures being added to the score which
inherit the clefs of the last measure.
TEMPO measure bpm optional
The TEMPO tag specifies tempo changes for a given measure, in quarter
notes per minute.
Example:
TEMPO 0 120
TEMPO 10 90
... results in a score which begins at a tempo of 120 bpm and changes to
90 bpm in the 11th bar.
Specifying a tempo with a measure number greater than the number of
measures built by MEASURE tags results in measures being added to the score,
which inherit the tempo of the last measure.
NOTE measure track dur midi_pitch vel tied_out? beamed_out? play_styleoptional
The NOTE tag is used to enter notes into a score. We examine every
parameter below.
-
measure: the measure number into which the note should be added. If -1
is specified, the note will be added on to the end of the melody (free
flow style). If a measure is specified, notes will be added to the end
of that measure's current content. In both cases, as notes overflow the
current or intended measure, the note will either spill into the next available
measure, or a new measure will be added if there is no free measure. It
is the user's responsibility to ensure that if a note's measure is specified,
that the total duration for that measure is not exceeded (ie you cannot
add 5 quarters to a 4/4 measure; thefifth will spill into the next measure,
and the results will be as confusing as the intention of the input file's
creator).
-
track: a value of 0 places a note in the top staff. A value of 2 places
it in the next staff down. A value of 4 places it in the third staff from
the top. In the future, JScore will support two tracks per staff. Currently,
only one track per staff is supported, so this value must be an even number
(track = 2 * staff)
-
dur: the duration of the note, which is compared to the TICKSPERQUARTER
tag's parameter. JScore currently supports all core durations WHOLE ..
128th, their dotted values, and tuplets 3, 5, 7, and 11. The duration specified
will be compared to the supported durations, and the closest value will
be chosen. So if TICKSPERQUARTER were 1.0, then 0.33333 would be an 8th
note triplet, 0.66666 would be a quarter note triplet, and 0.090909 would
be a 32nd note 11-tuplet.
-
midi_pitch: an integer 0..127 where 0 is a rest, and 60 is MIDDLE C.
-
vel: a loudness value, which will be scaled to 0..1, compared to the MAXVEL
tag's parameter.
-
tied_out? Specifies whether or not the note should be tied to the following
note. 0 = false, 1 = true
-
beamed_out? Specifies whether or not the note should be beamed to the following
note. 0 = false, 1 = true
-
play_style: unsupported. Future versions will support values for staccato,
legato, accent, etc
INTERVAL measure track dur midi_pitch vel tied_out? beamed_out? play_styleoptional
Used to build chords, the INTERVAL tag behaves similarly to the note
tag, except it adds an interval above the most recently added NOTE. Measure
number, track number, and duration parameters are ignored!!! The interval
will be added to the most recently handled NOTE tag.
Examples
RawJScoreFileParser can read from a disk file or from a URL. We use the
latter feature to demonstrate some examples (see applet source below).
-
C Scale, free flow
-
C Scale, measures specified
-
C Scale, measures specified, measure skipped, timesig
change
-
Two staves, clefs, beaming, ties, chords, tempo
-
11-tuplets, triplets, and quintuplets
Applet source
package jmslexamples;
import com.softsynth.jmsl.*;
import com.softsynth.jmsl.score.*;
import com.softsynth.jmsl.score.util.*;
import java.net.*;
import java.awt.*;
import java.io.*;
/** Read a URL that points to a raw JScore input text file. Print it to a TextArea, parse it,
and open the score in a ScoreFrame
@author Nick Didkovsky, (c) 2000 Nick Didkovsky, all rights reserved */
public class RawJScoreImportDemo extends java.applet.Applet {
ScoreFrame scoreFrame;
TextArea textArea;
public void init() {
}
public void start() {
try {
JMSL.setIsApplet(true);
JMSL.clock = new DefaultMusicClock();
JMSL.clock.setAdvance(0.1);
JMSL.scheduler = new EventScheduler();
JMSL.scheduler.start();
String urlName = getParameter("URL");
System.out.println(urlName);
URL url = new URL(getCodeBase(), urlName);
System.out.println(url + "");
add(textArea = new TextArea(40, 40));
readScoreToTextArea(url);
// parse the textfile and build a score
RawJScoreFileParser parser = new RawJScoreFileParser(url);
RawJScoreFileImporter importer = new RawJScoreFileImporter();
parser.addRawJScoreFileListener(importer);
parser.parse();
parser.close();
// display it
scoreFrame = new ScoreFrame();
scoreFrame.addScore(importer.getScore(800, 600));
scoreFrame.setSize(850, 600);
scoreFrame.setVisible(true);
}
catch (Exception e) {
System.out.println("ERR: " + e);
}
}
public void stop() {
JMSL.closeMusicDevices();
JMSL.scheduler.stop();
removeAll();
if (scoreFrame != null) {
scoreFrame.setVisible(false);
scoreFrame.dispose();
Score.deleteCanvas();
}
}
/** textarea.append() left deprecated to work in all browsers */
void readScoreToTextArea(URL url) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
textArea.setText("");
String line = in.readLine();
while (line != null) {
textArea.appendText(line + "\n");
line = in.readLine();
}
in.close();
}
}
More JMSL Demos