diff --git a/README.md b/README.md
index 6f94fa9..e92443f 100644
--- a/README.md
+++ b/README.md
@@ -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.
@@ -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.
diff --git a/build_helper/overview.html b/build_helper/overview.html
index f350d3d..75f026e 100644
--- a/build_helper/overview.html
+++ b/build_helper/overview.html
@@ -5,7 +5,7 @@
The source code can be found in the Midica project on Github.
- End user documentation can be found on midica.org.
+ End user documentation can be found on midica.org.
Midica can be used for the following tasks:
diff --git a/build_helper/precommit.pl b/build_helper/precommit.pl
index f7bdd75..226bcfd 100644
--- a/build_helper/precommit.pl
+++ b/build_helper/precommit.pl
@@ -255,7 +255,7 @@
. " -notimestamp"
. " -windowtitle 'Midica $version - Javadoc'"
. " -doctitle 'Midica - the MIDI Processing and Programming Tool'"
- . " -header 'Midica
$version'"
+ . " -header 'Midica
$version'"
;
my $status = system $cmd;
if ($status) {
diff --git a/img/karaoke.png b/img/karaoke.png
index 422b77a..3685c4b 100644
Binary files a/img/karaoke.png and b/img/karaoke.png differ
diff --git a/img/main.png b/img/main.png
index 073af0a..ff3b2f2 100644
Binary files a/img/main.png and b/img/main.png differ
diff --git a/img/player.png b/img/player.png
index 2e9a3c6..66083c4 100644
Binary files a/img/player.png and b/img/player.png differ
diff --git a/midica.jar b/midica.jar
index db6b623..e3cfd03 100644
Binary files a/midica.jar and b/midica.jar differ
diff --git a/src/org/midica/Midica.java b/src/org/midica/Midica.java
index b581b7b..2c34646 100644
--- a/src/org/midica/Midica.java
+++ b/src/org/midica/Midica.java
@@ -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";
@@ -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;
diff --git a/src/org/midica/file/Foreign.java b/src/org/midica/file/Foreign.java
index f4df473..911962e 100644
--- a/src/org/midica/file/Foreign.java
+++ b/src/org/midica/file/Foreign.java
@@ -27,6 +27,8 @@
*/
public class Foreign {
+ private static String lastStdOut = null;
+
/**
* Creates a temporary directory.
*
@@ -193,6 +195,7 @@ public static void execute(String[] cmd, String programName, boolean acceptAllEx
*/
public static void execute(List cmd, String programName, boolean acceptAllExitCodes) throws ForeignException {
ProcessBuilder pb = new ProcessBuilder(cmd);
+ lastStdOut = null;
try {
Process process = pb.start();
@@ -208,6 +211,7 @@ public static void execute(List cmd, String programName, boolean acceptA
while ((line = outReader.readLine()) != null) {
stdOut += line + "
";
}
+ lastStdOut = stdOut;
try {
int exitCode = process.waitFor();
@@ -260,4 +264,13 @@ public static void execute(List 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;
+ }
}
diff --git a/src/org/midica/file/read/AldaImporter.java b/src/org/midica/file/read/AldaImporter.java
index 1461fd7..8b41b10 100644
--- a/src/org/midica/file/read/AldaImporter.java
+++ b/src/org/midica/file/read/AldaImporter.java
@@ -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
@@ -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();
@@ -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));
}
diff --git a/src/org/midica/file/read/MidicaPLParser.java b/src/org/midica/file/read/MidicaPLParser.java
index c79de87..3cab800 100644
--- a/src/org/midica/file/read/MidicaPLParser.java
+++ b/src/org/midica/file/read/MidicaPLParser.java
@@ -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)) + ".*");
@@ -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)
diff --git a/src/org/midica/file/write/AldaExporter.java b/src/org/midica/file/write/AldaExporter.java
index 4e0c69d..bd8a452 100644
--- a/src/org/midica/file/write/AldaExporter.java
+++ b/src/org/midica/file/write/AldaExporter.java
@@ -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 + ")";
}
}
diff --git a/src/org/midica/file/write/Decompiler.java b/src/org/midica/file/write/Decompiler.java
index 77b6539..29f552f 100644
--- a/src/org/midica/file/write/Decompiler.java
+++ b/src/org/midica/file/write/Decompiler.java
@@ -1029,7 +1029,19 @@ private void groupNotes() {
for (Entry 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;
@@ -1152,19 +1164,20 @@ private void addNotesToSlices() {
// create notes structure for this tick
TreeMap> notesStruct = new TreeMap<>();
- // NOTE:
+ NOTE:
for (Entry 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
diff --git a/src/org/midica/file/write/MidicaPLExporter.java b/src/org/midica/file/write/MidicaPLExporter.java
index 9d5aa6a..b32b768 100644
--- a/src/org/midica/file/write/MidicaPLExporter.java
+++ b/src/org/midica/file/write/MidicaPLExporter.java
@@ -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();
@@ -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) {
@@ -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
diff --git a/test/org/midica/file/read/MidicaPLParserTest.java b/test/org/midica/file/read/MidicaPLParserTest.java
index de24957..b633160 100644
--- a/test/org/midica/file/read/MidicaPLParserTest.java
+++ b/test/org/midica/file/read/MidicaPLParserTest.java
@@ -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());
@@ -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() );
diff --git a/test/org/midica/file/write/ExporterTest.java b/test/org/midica/file/write/ExporterTest.java
index 21bc5cf..644c08b 100644
--- a/test/org/midica/file/write/ExporterTest.java
+++ b/test/org/midica/file/write/ExporterTest.java
@@ -10,16 +10,17 @@
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map.Entry;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.midica.TestUtil;
import org.midica.file.Foreign;
import org.midica.file.ForeignException;
+import org.midica.file.read.AldaImporter;
+import org.midica.file.read.MidiParser;
import org.midica.file.read.MidicaPLParser;
import org.midica.file.read.ParseException;
+import org.midica.file.read.SequenceParser;
import org.midica.file.write.MidicaPLExporter;
/**
@@ -41,13 +42,15 @@ static void setUpBeforeClass() throws InvocationTargetException, InterruptedExce
}
/**
- * Tests MidicaPL files, if they can be parsed and then exported without exception.
+ * Tests MidicaPL files, if they can be imported, exported, and then imported again
+ * without exception.
*
* The directories to be tested are:
*
* - the example files
* - the working unit test files for the {@link MidicaPLParser}
* - the MidicaPL test files for the decompiler
+ * - real-world midi files that are not committed because of copyright
*
* The exporters to be tested with each file are:
*
@@ -58,39 +61,75 @@ static void setUpBeforeClass() throws InvocationTargetException, InterruptedExce
* @throws ParseException if something went wrong.
*/
@Test
- void testParseDecompileMplDirectories() throws ParseException, ForeignException, ExportException {
- MidicaPLParser parser = new MidicaPLParser(true);
+ void testImportExportReimportDirectories() throws ParseException, ForeignException, ExportException {
// get directories to be searched
ArrayList directories = new ArrayList<>();
directories.add(System.getProperty("user.dir") + File.separator + "examples");
directories.add(TestUtil.getTestfileDirectory() + File.separator + "working");
directories.add(TestUtil.getTestfileDirectory() + File.separator + "exporter");
+ directories.add(TestUtil.getTestfileDirectory() + File.separator + "midi-real-world");
- // get exporters
- HashMap exporters = new HashMap<>();
- exporters.put("mid", new MidiExporter());
- exporters.put("mpl", new MidicaPLExporter());
- exporters.put("alda", new AldaExporter());
+ // types of export formats to test
+ String[] targetTypes = new String[] {"mpl", "mid", "alda"};
for (String dirStr : directories) {
File dir = new File(dirStr);
for (File file : dir.listFiles()) {
+ SequenceParser importer;
- // parse
+ // choose importer
+ String sourceType;
if (!file.isFile())
continue;
- if (!file.getName().endsWith(".midica") && !file.getName().endsWith(".midicapl") && !file.getName().endsWith(".mpl"))
+ if (file.getName().endsWith(".midica") || file.getName().endsWith(".midicapl") || file.getName().endsWith(".mpl"))
+ sourceType = "mpl";
+ else if (file.getName().endsWith(".alda"))
+ sourceType = "alda";
+ else if (file.getName().endsWith(".mid") || file.getName().endsWith(".midi") || file.getName().endsWith(".kar"))
+ sourceType = "mid";
+ else
continue;
- parser.parse(file);
+ importer = getImporter(sourceType);
- // export
- for (Entry entry : exporters.entrySet()) {
- String extension = entry.getKey();
- Exporter exporter = entry.getValue();
+ // import source file
+ try {
+ importer.parse(file);
+ }
+ catch (Exception e) {
+ System.err.println("failed to parse source file: " + file.getAbsolutePath());
+ throw e;
+ }
+
+ // export and import again
+ for (String targetType : targetTypes) {
+
+ // export to target format
+ String extension = targetType;
+ Exporter exporter = getExporter(targetType);
File decompiledFile = Foreign.createTempFile(extension, null);
- exporter.export(decompiledFile);
+ try {
+ exporter.export(decompiledFile);
+ }
+ catch (Exception e) {
+ System.err.println("failed to export file."
+ + " Source: " + file.getAbsolutePath()
+ + " Target: " + decompiledFile.getAbsolutePath());
+ throw e;
+ }
+
+ // re-import
+ importer = getImporter(targetType);
+ try {
+ importer.parse(decompiledFile);
+ }
+ catch (Exception e) {
+ System.err.println("failed to re-import exported file."
+ + " Source: " + file.getAbsolutePath()
+ + " Target: " + decompiledFile.getAbsolutePath());
+ throw e;
+ }
// clean up
Foreign.deleteTempFile(decompiledFile);
@@ -98,4 +137,42 @@ void testParseDecompileMplDirectories() throws ParseException, ForeignException,
}
}
}
+
+ /**
+ * Creates and returns an exporter for the given file type.
+ *
+ * @param type mpl, mid or alda (for MidicaPL, MIDI or ALDA)
+ * @return the exporter.
+ * @throws IllegalArgumentException if an unknown file type is given.
+ */
+ private Exporter getExporter(String type) throws IllegalArgumentException {
+
+ if ("mid".equals(type))
+ return new MidiExporter();
+ if ("mpl".equals(type))
+ return new MidicaPLExporter();
+ if ("alda".equals(type))
+ return new AldaExporter();
+
+ throw new IllegalArgumentException("unknown exporter type: " + type);
+ }
+
+ /**
+ * Creates and returns an importer for the given file type.
+ *
+ * @param type mpl, mid or alda (for MidicaPL, MIDI or ALDA)
+ * @return the importer.
+ * @throws IllegalArgumentException if an unknown file type is given.
+ */
+ private SequenceParser getImporter(String type) throws IllegalArgumentException {
+
+ if ("mid".equals(type))
+ return new MidiParser();
+ if ("mpl".equals(type))
+ return new MidicaPLParser(true);
+ if ("alda".equals(type))
+ return new AldaImporter();
+
+ throw new IllegalArgumentException("unknown importer type: " + type);
+ }
}
diff --git a/test/org/midica/testfiles/failing/soundbank-url-404.midica b/test/org/midica/testfiles/failing/soundbank-url-404.midica
index c057f5c..0f40c29 100644
--- a/test/org/midica/testfiles/failing/soundbank-url-404.midica
+++ b/test/org/midica/testfiles/failing/soundbank-url-404.midica
@@ -1,5 +1,5 @@
INCLUDE inc/instruments.midica
-SOUNDBANK http:/ /midica.org/assets/sound/404.sf2
+SOUNDBANK https:/ /midica.org/assets/sound/404.sf2
0 c /4
diff --git a/test/org/midica/testfiles/failing/soundbank-url-invalid.midica b/test/org/midica/testfiles/failing/soundbank-url-invalid.midica
index d61b583..5f25075 100644
--- a/test/org/midica/testfiles/failing/soundbank-url-invalid.midica
+++ b/test/org/midica/testfiles/failing/soundbank-url-invalid.midica
@@ -1,5 +1,5 @@
INCLUDE inc/instruments.midica
-SOUNDBANK http:/ /midica.org/assets/sound/invalid.sf2
+SOUNDBANK https:/ /midica.org/assets/sound/invalid.sf2
0 c /4
diff --git a/test/org/midica/testfiles/midi-real-world/.gitkeep b/test/org/midica/testfiles/midi-real-world/.gitkeep
new file mode 100644
index 0000000..a2e6f14
--- /dev/null
+++ b/test/org/midica/testfiles/midi-real-world/.gitkeep
@@ -0,0 +1,2 @@
+This directory is for real-world midi files that cannot be committed because of copyright issues.
+These files are used for unit tests only.
diff --git a/test/org/midica/testfiles/working/soundbank-url.midica b/test/org/midica/testfiles/working/soundbank-url.midica
index d2c86f0..39af6a1 100644
--- a/test/org/midica/testfiles/working/soundbank-url.midica
+++ b/test/org/midica/testfiles/working/soundbank-url.midica
@@ -1,2 +1,2 @@
-SOUNDBANK http:/ /midica.org/assets/sound/soundbank-emg.sf2
+SOUNDBANK https:/ /midica.org/assets/sound/soundbank-emg.sf2