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