Skip to content

Commit

Permalink
added support for ALDA 2
Browse files Browse the repository at this point in the history
- alda import: checking ALDA version on the system
- alda export: exporting only ALDA 2
- converted all midica.org links from http to https
- bugfixes in the decompiler
- bugfixes in the MidicaPLParser
- fixed #73
  • Loading branch information
truj committed Aug 9, 2023
1 parent 22bf1d5 commit 441cdfe
Show file tree
Hide file tree
Showing 20 changed files with 219 additions and 87 deletions.
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ If you prefer to write your music in ALDA or ABC, you need to:

# Screenshots

You can find a lot of screenshots here: http://www.midica.org/screenshots.html
You can find a lot of screenshots here: https://www.midica.org/screenshots.html

I will not repeat them all in this Readme. But at least here are three screenshots.
The first one shows the main window.
Expand All @@ -115,20 +115,20 @@ Midica has its own Music Programming Language: MidicaPL. But alternatively you c

For learning, each language has its own resources:

- [MidicaPL tutorial](http://www.midica.org/tutorial.html)
- [MidicaPL tutorial](https://www.midica.org/tutorial.html)
- [ALDA documentation](https://github.com/alda-lang/alda/blob/master/doc/index.md)
- [ABC tutorials](https://abcnotation.com/learn)

Here we focus on MidicaPL. For a quick reference, here are the links to the main
chapters of the MidicaPL tutorial:

- [Preparation](http://www.midica.org/tutorial.html)
- [Chapter 1: Basics](http://www.midica.org/tutorial-1.html)
- [Chapter 2: Improving](http://www.midica.org/tutorial-2.html)
- [Chapter 3: Functions](http://www.midica.org/tutorial-3.html)
- [Chapter 4: Blocks](http://www.midica.org/tutorial-4.html)
- [Chapter 5: Tweaking](http://www.midica.org/tutorial-5.html)
- [Chapter 6: Patterns](http://www.midica.org/tutorial-6.html)
- [Preparation](https://www.midica.org/tutorial.html)
- [Chapter 1: Basics](https://www.midica.org/tutorial-1.html)
- [Chapter 2: Improving](https://www.midica.org/tutorial-2.html)
- [Chapter 3: Functions](https://www.midica.org/tutorial-3.html)
- [Chapter 4: Blocks](https://www.midica.org/tutorial-4.html)
- [Chapter 5: Tweaking](https://www.midica.org/tutorial-5.html)
- [Chapter 6: Patterns](https://www.midica.org/tutorial-6.html)

Examples of complete songs can be found in the [examples directory](examples/).
In this Readme we just show some short examples to get an impression of the language.
Expand Down
2 changes: 1 addition & 1 deletion build_helper/overview.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
The source code can be found in the <a href="https://github.com/truj/midica">Midica project on Github</a>.
</p>
<p>
End user documentation can be found on <a href="http://www.midica.org/">midica.org</a>.
End user documentation can be found on <a href="https://www.midica.org/">midica.org</a>.
</p>
<p>
Midica can be used for the following tasks:
Expand Down
2 changes: 1 addition & 1 deletion build_helper/precommit.pl
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@
. " -notimestamp"
. " -windowtitle 'Midica $version - Javadoc'"
. " -doctitle 'Midica - the MIDI Processing and Programming Tool'"
. " -header '<a href=\"http://www.midica.org/\" target=\"_top\"><b>Midica</b></a><br>$version'"
. " -header '<a href=\"https://www.midica.org/\" target=\"_top\"><b>Midica</b></a><br>$version'"
;
my $status = system $cmd;
if ($status) {
Expand Down
Binary file modified img/karaoke.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified img/main.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified img/player.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified midica.jar
Binary file not shown.
6 changes: 3 additions & 3 deletions src/org/midica/Midica.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ public class Midica {
* After switching to a new major version, this has to be set to "-1" manually, so that
* precommit.pl starts with "0" again.
*/
private static final int VERSION_MINOR = 5;
private static final int VERSION_MINOR = 6;

/** UNIX timestamp of the last commit */
public static final int COMMIT_TIME = 1690393042;
public static final int COMMIT_TIME = 1691601094;

/** Branch name. Automatically changed by precommit.pl */
public static final String BRANCH = "master";
Expand All @@ -51,7 +51,7 @@ public class Midica {
public static final String SOURCE_URL = "https://github.com/truj/midica";

/** Website URL */
public static final String URL = "http://midica.org/";
public static final String URL = "https://midica.org/";

/** Controller of the main window */
public static UiController uiController;
Expand Down
13 changes: 13 additions & 0 deletions src/org/midica/file/Foreign.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
*/
public class Foreign {

private static String lastStdOut = null;

/**
* Creates a temporary directory.
*
Expand Down Expand Up @@ -193,6 +195,7 @@ public static void execute(String[] cmd, String programName, boolean acceptAllEx
*/
public static void execute(List<String> cmd, String programName, boolean acceptAllExitCodes) throws ForeignException {
ProcessBuilder pb = new ProcessBuilder(cmd);
lastStdOut = null;
try {
Process process = pb.start();

Expand All @@ -208,6 +211,7 @@ public static void execute(List<String> cmd, String programName, boolean acceptA
while ((line = outReader.readLine()) != null) {
stdOut += line + "<br>";
}
lastStdOut = stdOut;

try {
int exitCode = process.waitFor();
Expand Down Expand Up @@ -260,4 +264,13 @@ public static void execute(List<String> cmd, String programName, boolean acceptA
throw new ForeignException(msg);
}
}

/**
* Returns whatever the last executed program wrote to STDOUT.
*
* @return the last STDOUT string
*/
public static String getLastOutput() {
return lastStdOut;
}
}
29 changes: 25 additions & 4 deletions src/org/midica/file/read/AldaImporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
*
* The process contains the following steps:
*
* - Start the ALDA server, if not yet done
* - Checks which ALDA version is used
* - If ALDA 1 is used, starts the ALDA server, if not yet done
* - Convert ALDA to a MIDI tempfile, using the alda executable
* - Parse the MIDI file using the parent class
* - Delete the MIDI file
Expand Down Expand Up @@ -60,9 +61,26 @@ public void parse(Object fileAsObj) throws ParseException {
try {
String execPath = Config.get(Config.EXEC_PATH_IMP_ALDA);

// get alda version
int version = -1;
String[] aldaVersion = {execPath, "version"};
Foreign.execute(aldaVersion, execPath, true);
String versionStr = Foreign.getLastOutput();
if (versionStr != null) {
versionStr = versionStr.toLowerCase();
if (versionStr.startsWith("alda 2")) {
version = 2;
}
else if (versionStr.startsWith("client version: 1")) {
version = 1;
}
}

// alda up
String[] aldaUp = {execPath, "up"};
Foreign.execute(aldaUp, programName, true);
if (1 == version) {
String[] aldaUp = {execPath, "up"};
Foreign.execute(aldaUp, programName, true);
}

// get a temp file path
File tempfile = Foreign.createTempMidiFile();
Expand All @@ -72,7 +90,10 @@ public void parse(Object fileAsObj) throws ParseException {
String[] aldaConvert = {execPath, "export", "-f", file.getAbsolutePath(), "-o", tempfile.getAbsolutePath()};
Foreign.execute(aldaConvert, programName, false);

// due to an ALDA bug sometimes the exit code is successul even if no MIDI file was created
// Due to a bug in alda 1, sometimes the exit code was successul even if
// no MIDI file was created.
// I don't know if that bug still exists in alda 2 but this workaround doesn't
// hurt either.
if (!tempfile.exists()) {
throw new ParseException(Dict.get(Dict.ERROR_ALDA_NO_MIDI_FILE));
}
Expand Down
8 changes: 5 additions & 3 deletions src/org/midica/file/read/MidicaPLParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ private void compilePatterns() {
condInPattern = Pattern.compile("\\s*" + Pattern.quote(COND_IN_SEP) + "\\s*");

// find forbidden \r or \n in a soft karaoke field or syllable
crlfSkPattern = Pattern.compile(Pattern.quote(LYRICS_CR) + "|" + Pattern.quote(LYRICS_LF));
crlfSkPattern = Pattern.compile("^.+(" + Pattern.quote(LYRICS_CR) + "|" + Pattern.quote(LYRICS_LF) + ")");

// find sharp(s) or flat(s) in a note name
sharpPattern = Pattern.compile("^.+" + Pattern.quote(Config.getConfiguredSharpOrFlat(true)) + ".*");
Expand Down Expand Up @@ -3113,9 +3113,11 @@ else if (iPat == 2)
*/
private void applySyllable(String syllable, long tick) throws ParseException {
syllable = syllable.replaceAll( Pattern.quote(LYRICS_SPACE), " " );
syllable = syllable.replaceAll( Pattern.quote(LYRICS_CR), "\r" );
syllable = syllable.replaceAll( Pattern.quote(LYRICS_LF), "\n" );
syllable = syllable.replaceAll( Pattern.quote(LYRICS_COMMA), "," );
if (!isSoftKaraoke) {
syllable = syllable.replaceAll( Pattern.quote(LYRICS_CR), "\r" );
syllable = syllable.replaceAll( Pattern.quote(LYRICS_LF), "\n" );
}

try {
if (isSoftKaraoke)
Expand Down
62 changes: 31 additions & 31 deletions src/org/midica/file/write/AldaExporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -1078,45 +1078,45 @@ private String getKeySignature() {

if (isMajor) {
tonalityStr = "major";
if (0 == sharpsOrFlats) noteStr = "c"; // C maj
else if (1 == sharpsOrFlats) noteStr = "g"; // G maj
else if (2 == sharpsOrFlats) noteStr = "d"; // D maj
else if (3 == sharpsOrFlats) noteStr = "a"; // A maj
else if (4 == sharpsOrFlats) noteStr = "e"; // E maj
else if (5 == sharpsOrFlats) noteStr = "b"; // B maj
else if (6 == sharpsOrFlats) noteStr = "f :sharp"; // F# maj
else if (7 == sharpsOrFlats) noteStr = "c :sharp"; // C# maj
else if (-1 == sharpsOrFlats) noteStr = "f"; // F maj
else if (-2 == sharpsOrFlats) noteStr = "b :flat"; // Bb maj
else if (-3 == sharpsOrFlats) noteStr = "e :flat"; // Eb maj
else if (-4 == sharpsOrFlats) noteStr = "a :flat"; // Ab maj
else if (-5 == sharpsOrFlats) noteStr = "d :flat"; // Db maj
else if (-6 == sharpsOrFlats) noteStr = "g :flat"; // Gb maj
else if (-7 == sharpsOrFlats) noteStr = "c :flat"; // Cb maj
if (0 == sharpsOrFlats) noteStr = "c"; // C maj
else if (1 == sharpsOrFlats) noteStr = "g"; // G maj
else if (2 == sharpsOrFlats) noteStr = "d"; // D maj
else if (3 == sharpsOrFlats) noteStr = "a"; // A maj
else if (4 == sharpsOrFlats) noteStr = "e"; // E maj
else if (5 == sharpsOrFlats) noteStr = "b"; // B maj
else if (6 == sharpsOrFlats) noteStr = "f sharp"; // F# maj
else if (7 == sharpsOrFlats) noteStr = "c sharp"; // C# maj
else if (-1 == sharpsOrFlats) noteStr = "f"; // F maj
else if (-2 == sharpsOrFlats) noteStr = "b flat"; // Bb maj
else if (-3 == sharpsOrFlats) noteStr = "e flat"; // Eb maj
else if (-4 == sharpsOrFlats) noteStr = "a flat"; // Ab maj
else if (-5 == sharpsOrFlats) noteStr = "d flat"; // Db maj
else if (-6 == sharpsOrFlats) noteStr = "g flat"; // Gb maj
else if (-7 == sharpsOrFlats) noteStr = "c flat"; // Cb maj
}
else if (isMinor) {
tonalityStr = "minor";
if (0 == sharpsOrFlats) noteStr = "a"; // A min
else if (1 == sharpsOrFlats) noteStr = "e"; // E min
else if (2 == sharpsOrFlats) noteStr = "b"; // B min
else if (3 == sharpsOrFlats) noteStr = "f :sharp"; // F# min
else if (4 == sharpsOrFlats) noteStr = "c :sharp"; // C# min
else if (5 == sharpsOrFlats) noteStr = "g :sharp"; // G# min
else if (6 == sharpsOrFlats) noteStr = "d :sharp"; // D# min
else if (7 == sharpsOrFlats) noteStr = "a :sharp"; // A# min
else if (-1 == sharpsOrFlats) noteStr = "d"; // D min
else if (-2 == sharpsOrFlats) noteStr = "g"; // G min
else if (-3 == sharpsOrFlats) noteStr = "c"; // C min
else if (-4 == sharpsOrFlats) noteStr = "f"; // F min
else if (-5 == sharpsOrFlats) noteStr = "b :flat"; // Bb min
else if (-6 == sharpsOrFlats) noteStr = "e :flat"; // Eb min
else if (-7 == sharpsOrFlats) noteStr = "a :flat"; // Ab min
if (0 == sharpsOrFlats) noteStr = "a"; // A min
else if (1 == sharpsOrFlats) noteStr = "e"; // E min
else if (2 == sharpsOrFlats) noteStr = "b"; // B min
else if (3 == sharpsOrFlats) noteStr = "f sharp"; // F# min
else if (4 == sharpsOrFlats) noteStr = "c sharp"; // C# min
else if (5 == sharpsOrFlats) noteStr = "g sharp"; // G# min
else if (6 == sharpsOrFlats) noteStr = "d sharp"; // D# min
else if (7 == sharpsOrFlats) noteStr = "a sharp"; // A# min
else if (-1 == sharpsOrFlats) noteStr = "d"; // D min
else if (-2 == sharpsOrFlats) noteStr = "g"; // G min
else if (-3 == sharpsOrFlats) noteStr = "c"; // C min
else if (-4 == sharpsOrFlats) noteStr = "f"; // F min
else if (-5 == sharpsOrFlats) noteStr = "b flat"; // Bb min
else if (-6 == sharpsOrFlats) noteStr = "e flat"; // Eb min
else if (-7 == sharpsOrFlats) noteStr = "a flat"; // Ab min
}

// invalid?
if (null == noteStr || null == tonalityStr)
return null;

return "[:" + noteStr + " :" + tonalityStr + "]";
return "'(" + noteStr + " " + tonalityStr + ")";
}
}
21 changes: 17 additions & 4 deletions src/org/midica/file/write/Decompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -1029,7 +1029,19 @@ private void groupNotes() {
for (Entry<Byte, Byte> tickEntry: tickStructClone.entrySet()) {
byte note = tickEntry.getKey();
byte velocity = tickEntry.getValue();
long offTick = channelOnOffClone.get(note).ceilingKey(tick + 1);
Long offTick = channelOnOffClone.get(note).ceilingKey(tick + 1);

// In rare cases the following sequence is possible for a given note:
// - note-ON
// - note-ON and note-OFF in the same tick
// - end of sequence
// This results in channelOnOffClone.get(note) ending with two
// false values in a row.
// Then offTick is null.
// Probably this also happens if a note-ON has no note-OFF at all...
// In these cases: ignore the note completely.
if (null == offTick)
continue NOTE;

String chordId = offTick + "/" + velocity;

Expand Down Expand Up @@ -1152,19 +1164,20 @@ private void addNotesToSlices() {
// create notes structure for this tick
TreeMap<String, TreeMap<Byte, String>> notesStruct = new TreeMap<>();

// NOTE:
NOTE:
for (Entry<Byte, Byte> noteSet : tickStruct.entrySet()) {
byte note = noteSet.getKey();
byte velocity = noteSet.getValue();
Long offTick = sliceOnOff.get(channel).get(note).ceilingKey(tick + 1);

// TODO: test this
// handle the case that there is no offTick at all
// can happen if the MIDI is corrupt or uses all-notes-off / all-sounds-off
// instead of note-off or note-on with velocity=0
if (null == offTick) {
exportResult.addWarning(null, tick, channel, Dict.get(Dict.WARNING_OFF_NOT_FOUND));
exportResult.setDetailsOfLastWarning(Dict.get(Dict.ERROR_NOTE) + ": " + note);
String noteName = Dict.getNoteOrPercussionName(note, 9 == channel);
exportResult.setDetailsOfLastWarning(Dict.get(Dict.ERROR_NOTE) + ": " + noteName + " (" + note + ")");
continue NOTE;
}

// create structure for this note
Expand Down
18 changes: 11 additions & 7 deletions src/org/midica/file/write/MidicaPLExporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public String createOutput() {
// in MPL we calculate measure lengths based on the TARGET sequence
// so we need to overwrite the structure from the parent class
measureLengthHistory.clear();
measureLengthHistory.put(0L, 4L * sourceResolution); // MIDI default is 4/4
measureLengthHistory.put(0L, 4L * targetResolution); // MIDI default is 4/4

// META block
createMetaBlock();
Expand Down Expand Up @@ -1032,11 +1032,10 @@ private void createBarlineIfNeeded(byte channel) {
long totalTicks = currentTgtTicks - lastTimeSigTick;

// get delta
long srcDelta = totalTicks % measureLength;
long srcDelta2 = measureLength - srcDelta;
if (srcDelta2 < srcDelta)
srcDelta = srcDelta2;
long tgtDelta = (srcDelta * targetResolution * 10 + sourceResolution * 5) / (sourceResolution * 10);
long tgtDelta = totalTicks % measureLength;
long tgtDelta2 = measureLength - tgtDelta;
if (tgtDelta2 < tgtDelta)
tgtDelta = tgtDelta2;

// no bar line at all?
if (tgtDelta > MAX_BARLINE_TOL) {
Expand Down Expand Up @@ -1196,7 +1195,12 @@ private String escapeSyllable(String syllable) {
}

// escape space and comma
return syllable.replaceAll(" ", "_").replaceAll(",", "\\\\c");
syllable = syllable.replaceAll(" ", "_").replaceAll(",", "\\\\c");

// escape comment symbols
syllable = syllable.replaceAll(MidicaPLParser.COMMENT, "/\\\\/");

return syllable;
}

@Override
Expand Down
6 changes: 3 additions & 3 deletions test/org/midica/file/read/MidicaPLParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1012,7 +1012,7 @@ void testParseFilesWorking() throws ParseException {
}

parse(getWorkingFile("soundbank-url"));
assertEquals("http://midica.org/assets/sound/soundbank-emg.sf2", SoundbankParser.getFullPath());
assertEquals("https://midica.org/assets/sound/soundbank-emg.sf2", SoundbankParser.getFullPath());
assertEquals(Dict.get(Dict.SOUND_FROM_URL) + "soundbank-emg.sf2", SoundbankParser.getShortName());
assertEquals(SoundbankParser.SOUND_FORMAT_SF2, SoundbankParser.getSoundFormat());
assertEquals(SoundbankParser.FROM_URL, SoundbankParser.getSource());
Expand Down Expand Up @@ -2285,13 +2285,13 @@ void testParseFilesFailing() {

e = assertThrows( ParseException.class, () -> parse(getFailingFile("soundbank-url-invalid")) );
assertEquals( 3, e.getLineNumber() );
assertEquals( "SOUNDBANK http:/ /midica.org/assets/sound/invalid.sf2", e.getLineContent() );
assertEquals( "SOUNDBANK https:/ /midica.org/assets/sound/invalid.sf2", e.getLineContent() );
assertTrue( e.getMessage().contains(Dict.get(Dict.INVALID_RIFF)) );

e = assertThrows( ParseException.class, () -> parse(getFailingFile("soundbank-url-404")) );
assertEquals( 3, e.getLineNumber() );
assertTrue( e.getMessage().contains(Dict.get(Dict.DOWNLOAD_PROBLEM)) );
assertEquals( "SOUNDBANK http:/ /midica.org/assets/sound/404.sf2", e.getLineContent() );
assertEquals( "SOUNDBANK https:/ /midica.org/assets/sound/404.sf2", e.getLineContent() );

e = assertThrows( ParseException.class, () -> parse(getFailingFile("define-not-enough-args")) );
assertEquals( 3, e.getLineNumber() );
Expand Down
Loading

0 comments on commit 441cdfe

Please sign in to comment.