diff --git a/CHANGELOG.md b/CHANGELOG.md
index 982363f..7fc56bb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,19 @@
# Changes
+## 11.7.0
+
+* New: Writing of samples can now be cancelled as well.
+* Fixed: Logger text is now cleared regularily to prevent a crash. To have the log still available, all messages are now logged into a file ConvertWithMoss.log which is created in the output directory.
+* Kontakt
+ * Fixed: Regression: Reading Kontakt 5-7 file lists were broken.
+ * Fixed: NCW files are now only read when needed for writing and the memory is freed up directly afterwards to support NKIs which reference a very large amounts of NCW files.
+* Korg KMP
+ * New: KSF files which reference another KSF file are now read properly.
+ * New: Reading: Applied +12dB option.
+ * New: KMP/KSF files which contain SKIPPEDSAMPLE as a filename are now ignored (conversion was canceled previously).
+* Sample Files
+ * New: Notify about the number of sample files found in a folder before the mapping starts.
+
## 11.6.0
* EXS24
diff --git a/maven-local-repository/de/mossgrabers/uitools/1.5.2/uitools-1.5.2.jar.md5 b/maven-local-repository/de/mossgrabers/uitools/1.5.2/uitools-1.5.2.jar.md5
deleted file mode 100644
index a4cb325..0000000
--- a/maven-local-repository/de/mossgrabers/uitools/1.5.2/uitools-1.5.2.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-1f82009d0a3586c0808d7aed1428215b
\ No newline at end of file
diff --git a/maven-local-repository/de/mossgrabers/uitools/1.5.2/uitools-1.5.2.jar.sha1 b/maven-local-repository/de/mossgrabers/uitools/1.5.2/uitools-1.5.2.jar.sha1
deleted file mode 100644
index 6f3f4ad..0000000
--- a/maven-local-repository/de/mossgrabers/uitools/1.5.2/uitools-1.5.2.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-765c5b28d03a453b90367a2dd76b497aa13bf129
\ No newline at end of file
diff --git a/maven-local-repository/de/mossgrabers/uitools/1.5.2/uitools-1.5.2.pom.md5 b/maven-local-repository/de/mossgrabers/uitools/1.5.2/uitools-1.5.2.pom.md5
deleted file mode 100644
index 6fe1718..0000000
--- a/maven-local-repository/de/mossgrabers/uitools/1.5.2/uitools-1.5.2.pom.md5
+++ /dev/null
@@ -1 +0,0 @@
-d68a627f4ac72078abfce0c389567b29
\ No newline at end of file
diff --git a/maven-local-repository/de/mossgrabers/uitools/1.5.2/uitools-1.5.2.pom.sha1 b/maven-local-repository/de/mossgrabers/uitools/1.5.2/uitools-1.5.2.pom.sha1
deleted file mode 100644
index 3e9febc..0000000
--- a/maven-local-repository/de/mossgrabers/uitools/1.5.2/uitools-1.5.2.pom.sha1
+++ /dev/null
@@ -1 +0,0 @@
-4c307a1103fd9ca401332545214fda01ff85e4bc
\ No newline at end of file
diff --git a/maven-local-repository/de/mossgrabers/uitools/1.5.2/uitools-1.5.2.jar b/maven-local-repository/de/mossgrabers/uitools/1.5.3/uitools-1.5.3.jar
similarity index 81%
rename from maven-local-repository/de/mossgrabers/uitools/1.5.2/uitools-1.5.2.jar
rename to maven-local-repository/de/mossgrabers/uitools/1.5.3/uitools-1.5.3.jar
index 2a190ad..3bf1f62 100644
Binary files a/maven-local-repository/de/mossgrabers/uitools/1.5.2/uitools-1.5.2.jar and b/maven-local-repository/de/mossgrabers/uitools/1.5.3/uitools-1.5.3.jar differ
diff --git a/maven-local-repository/de/mossgrabers/uitools/1.5.3/uitools-1.5.3.jar.md5 b/maven-local-repository/de/mossgrabers/uitools/1.5.3/uitools-1.5.3.jar.md5
new file mode 100644
index 0000000..77d381d
--- /dev/null
+++ b/maven-local-repository/de/mossgrabers/uitools/1.5.3/uitools-1.5.3.jar.md5
@@ -0,0 +1 @@
+972610b7530023e1a5b75b8e6b46fe6a
\ No newline at end of file
diff --git a/maven-local-repository/de/mossgrabers/uitools/1.5.3/uitools-1.5.3.jar.sha1 b/maven-local-repository/de/mossgrabers/uitools/1.5.3/uitools-1.5.3.jar.sha1
new file mode 100644
index 0000000..78c4e43
--- /dev/null
+++ b/maven-local-repository/de/mossgrabers/uitools/1.5.3/uitools-1.5.3.jar.sha1
@@ -0,0 +1 @@
+677ce031293c57c3229b55c17ad498490d7251a2
\ No newline at end of file
diff --git a/maven-local-repository/de/mossgrabers/uitools/1.5.2/uitools-1.5.2.pom b/maven-local-repository/de/mossgrabers/uitools/1.5.3/uitools-1.5.3.pom
similarity index 95%
rename from maven-local-repository/de/mossgrabers/uitools/1.5.2/uitools-1.5.2.pom
rename to maven-local-repository/de/mossgrabers/uitools/1.5.3/uitools-1.5.3.pom
index 2d16212..b84894e 100644
--- a/maven-local-repository/de/mossgrabers/uitools/1.5.2/uitools-1.5.2.pom
+++ b/maven-local-repository/de/mossgrabers/uitools/1.5.3/uitools-1.5.3.pom
@@ -7,7 +7,7 @@
de.mossgrabers
jar
UiTools
- 1.5.2
+ 1.5.3
UTF-8
@@ -32,15 +32,10 @@
javafx-web
23.0.1
-
- org.fxmisc.richtext
- richtextfx
- 0.11.3
-
org.junit.jupiter
junit-jupiter-api
- 5.11.3
+ 5.11.4
test
diff --git a/maven-local-repository/de/mossgrabers/uitools/1.5.3/uitools-1.5.3.pom.md5 b/maven-local-repository/de/mossgrabers/uitools/1.5.3/uitools-1.5.3.pom.md5
new file mode 100644
index 0000000..bbf7cf1
--- /dev/null
+++ b/maven-local-repository/de/mossgrabers/uitools/1.5.3/uitools-1.5.3.pom.md5
@@ -0,0 +1 @@
+f9aa23a4bdeb813432459f86bcdedadc
\ No newline at end of file
diff --git a/maven-local-repository/de/mossgrabers/uitools/1.5.3/uitools-1.5.3.pom.sha1 b/maven-local-repository/de/mossgrabers/uitools/1.5.3/uitools-1.5.3.pom.sha1
new file mode 100644
index 0000000..c58d42e
--- /dev/null
+++ b/maven-local-repository/de/mossgrabers/uitools/1.5.3/uitools-1.5.3.pom.sha1
@@ -0,0 +1 @@
+2ebe38dea9dd275dacf8d264cab4fecb9474fd6b
\ No newline at end of file
diff --git a/maven-local-repository/de/mossgrabers/uitools/maven-metadata.xml b/maven-local-repository/de/mossgrabers/uitools/maven-metadata.xml
index 9c5cc49..987e6a1 100644
--- a/maven-local-repository/de/mossgrabers/uitools/maven-metadata.xml
+++ b/maven-local-repository/de/mossgrabers/uitools/maven-metadata.xml
@@ -3,10 +3,10 @@
de.mossgrabers
uitools
- 1.5.2
+ 1.5.3
- 1.5.2
+ 1.5.3
- 20241118164256
+ 20250103142649
diff --git a/maven-local-repository/de/mossgrabers/uitools/maven-metadata.xml.md5 b/maven-local-repository/de/mossgrabers/uitools/maven-metadata.xml.md5
index fc982ba..d34befa 100644
--- a/maven-local-repository/de/mossgrabers/uitools/maven-metadata.xml.md5
+++ b/maven-local-repository/de/mossgrabers/uitools/maven-metadata.xml.md5
@@ -1 +1 @@
-ad0ddfd3b1da31526f04a4f60f100e6e
\ No newline at end of file
+99fe126702a5f194ca99e9925f671db9
\ No newline at end of file
diff --git a/maven-local-repository/de/mossgrabers/uitools/maven-metadata.xml.sha1 b/maven-local-repository/de/mossgrabers/uitools/maven-metadata.xml.sha1
index 26f3242..2d36216 100644
--- a/maven-local-repository/de/mossgrabers/uitools/maven-metadata.xml.sha1
+++ b/maven-local-repository/de/mossgrabers/uitools/maven-metadata.xml.sha1
@@ -1 +1 @@
-5bf395d679ed1decc83d2c358543f9ee768a839f
\ No newline at end of file
+a981d4efab038fc6a085c8a661aa0a2098d032e1
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 50a4886..3798007 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
4.0.0
de.mossgrabers
convertwithmoss
- 11.6.0
+ 11.7.0
jar
ConvertWithMoss
@@ -49,7 +49,7 @@
uitools
de.mossgrabers
- 1.5.2
+ 1.5.3
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/core/AbstractCoreTask.java b/src/main/java/de/mossgrabers/convertwithmoss/core/AbstractCoreTask.java
index d0b42af..0e95d1a 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/core/AbstractCoreTask.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/core/AbstractCoreTask.java
@@ -81,10 +81,14 @@ public void shutdown ()
}
- /** {@inheritDoc} */
- @Override
- public void cancel ()
+ protected void notifyProgress ()
{
- // Intentionally empty
+ this.notifier.log ("IDS_NOTIFY_PROGRESS");
+ }
+
+
+ protected void notifyNewline ()
+ {
+ this.notifier.log ("IDS_NOTIFY_LINE_FEED");
}
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/core/creator/AbstractCreator.java b/src/main/java/de/mossgrabers/convertwithmoss/core/creator/AbstractCreator.java
index 80fcea9..a38667d 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/core/creator/AbstractCreator.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/core/creator/AbstractCreator.java
@@ -92,6 +92,8 @@ public abstract class AbstractCreator extends AbstractCoreTask implements ICreat
private boolean updateSampleChunk = false;
private boolean removeJunkChunks = false;
+ protected boolean isCancelled = false;
+
// Combine into library options
protected CheckBox combineIntoOneLibrary = null;
protected TextField combinationFilename = null;
@@ -126,6 +128,22 @@ public boolean wantsMultipleFiles ()
}
+ /** {@inheritDoc} */
+ @Override
+ public void cancel ()
+ {
+ this.isCancelled = true;
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ public void clearCancelled ()
+ {
+ this.isCancelled = false;
+ }
+
+
/**
* Add the UI elements for the combination of several multi-sample source into 1 library.
*
@@ -579,6 +597,9 @@ protected List writeSamples (final File sampleFolder, final IMultisampleSo
final List sampleZones = group.getSampleZones ();
for (int zoneIndex = 0; zoneIndex < sampleZones.size (); zoneIndex++)
{
+ if (this.isCancelled)
+ return writtenFiles;
+
final ISampleZone zone = sampleZones.get (zoneIndex);
final File file = new File (sampleFolder, this.createSampleFilename (zone, zoneIndex, fileEnding));
@@ -644,6 +665,9 @@ protected List writeSamples (final File sampleFolder, final IMultisampleSo
final List sampleZones = group.getSampleZones ();
for (int zoneIndex = 0; zoneIndex < sampleZones.size (); zoneIndex++)
{
+ if (this.isCancelled)
+ return writtenFiles;
+
final ISampleZone zone = sampleZones.get (zoneIndex);
final File file = new File (sampleFolder, this.createSampleFilename (zone, zoneIndex, extension));
@@ -929,18 +953,6 @@ private static void putUncompressedEntry (final ZipOutputStream zipOutputStream,
}
- private void notifyProgress ()
- {
- this.notifier.log ("IDS_NOTIFY_PROGRESS");
- }
-
-
- private void notifyNewline ()
- {
- this.notifier.log ("IDS_NOTIFY_LINE_FEED");
- }
-
-
/**
* Adds options add or update certain WAV chunks.
*
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/core/creator/ICreator.java b/src/main/java/de/mossgrabers/convertwithmoss/core/creator/ICreator.java
index 7a54512..428d206 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/core/creator/ICreator.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/core/creator/ICreator.java
@@ -45,4 +45,10 @@ public interface ICreator extends ICoreTask
* @return Returns true if the creator wants to combine several files
*/
boolean wantsMultipleFiles ();
+
+
+ /**
+ * Clears the cancelled state. Call before each run.
+ */
+ void clearCancelled ();
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/core/detector/AbstractDetectorTask.java b/src/main/java/de/mossgrabers/convertwithmoss/core/detector/AbstractDetectorTask.java
index 92d93a2..ea88138 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/core/detector/AbstractDetectorTask.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/core/detector/AbstractDetectorTask.java
@@ -159,7 +159,7 @@ protected Boolean call () throws Exception
/**
- * Wait a bit.
+ * Check for task cancellation.
*
* @return The thread was cancelled if true
*/
@@ -636,4 +636,16 @@ private File findSampleFileRecursively (final File folder, final String fileName
return null;
}
+
+
+ protected void notifyProgress ()
+ {
+ this.notifier.log ("IDS_NOTIFY_PROGRESS");
+ }
+
+
+ protected void notifyNewline ()
+ {
+ this.notifier.log ("IDS_NOTIFY_LINE_FEED");
+ }
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/core/model/implementation/DefaultSampleZone.java b/src/main/java/de/mossgrabers/convertwithmoss/core/model/implementation/DefaultSampleZone.java
index 6f50513..5e283fa 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/core/model/implementation/DefaultSampleZone.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/core/model/implementation/DefaultSampleZone.java
@@ -368,7 +368,7 @@ public void setVelocityCrossfadeHigh (final int crossfadeHigh)
@Override
public void setGain (final double gain)
{
- this.gain = Math.clamp (gain, -12.0, 12.0);
+ this.gain = Math.clamp (gain, 0.125, 24.0);
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/file/StreamUtils.java b/src/main/java/de/mossgrabers/convertwithmoss/file/StreamUtils.java
index 573f921..2737733 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/file/StreamUtils.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/file/StreamUtils.java
@@ -889,6 +889,38 @@ public static void padBytes (final OutputStream out, final int numBytes, final i
}
+ /**
+ * Reads a certain number of bytes from the input stream. The number of bytes is determined from
+ * the first 4 bytes (32-bit value).
+ *
+ * @param in The input stream to read from
+ * @param isBigEndian True if bytes are stored big-endian otherwise little-endian
+ * @return The data block without the size bytes
+ * @throws IOException Could not read
+ */
+ public static byte [] readDataBlock (final InputStream in, final boolean isBigEndian) throws IOException
+ {
+ final int size = (int) StreamUtils.readUnsigned32 (in, isBigEndian);
+ return in.readNBytes (size);
+ }
+
+
+ /**
+ * Reads a certain number of bytes from the input stream. The number of bytes is determined from
+ * the first 4 bytes (32-bit value).
+ *
+ * @param out The output stream to write to
+ * @param data The data block without the size bytes
+ * @param isBigEndian True if bytes are stored big-endian otherwise little-endian
+ * @throws IOException Could not read
+ */
+ public static void writeDataBlock (final OutputStream out, final byte [] data, final boolean isBigEndian) throws IOException
+ {
+ StreamUtils.writeUnsigned32 (out, data.length, isBigEndian);
+ out.write (data);
+ }
+
+
/**
* Skip exactly N bytes.
*
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/file/ncw/NcwFile.java b/src/main/java/de/mossgrabers/convertwithmoss/file/ncw/NcwFile.java
index 5a2e380..fddabc9 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/file/ncw/NcwFile.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/file/ncw/NcwFile.java
@@ -47,6 +47,8 @@ public class NcwFile
private int [] [] channelData;
private float [] [] channelDataFloat;
+ private final File ncwFile;
+ private final Object lazyLoadingLock = new Object ();
/**
@@ -57,10 +59,7 @@ public class NcwFile
*/
public NcwFile (final File ncwFile) throws IOException
{
- try (final FileInputStream stream = new FileInputStream (ncwFile))
- {
- this.read (stream);
- }
+ this.ncwFile = ncwFile;
}
@@ -72,6 +71,7 @@ public NcwFile (final File ncwFile) throws IOException
*/
public NcwFile (final InputStream inputStream) throws IOException
{
+ this.ncwFile = null;
this.read (inputStream);
}
@@ -80,9 +80,11 @@ public NcwFile (final InputStream inputStream) throws IOException
* Get the number of channels.
*
* @return The number of channels
+ * @throws IOException Could not read the file
*/
- public int getChannels ()
+ public int getChannels () throws IOException
{
+ this.lazyLoading ();
return this.channels;
}
@@ -91,9 +93,11 @@ public int getChannels ()
* Get the number of samples of one channel.
*
* @return The number of samples
+ * @throws IOException Could not read the file
*/
- public int getNumberOfSamples ()
+ public int getNumberOfSamples () throws IOException
{
+ this.lazyLoading ();
return this.numberOfSamples;
}
@@ -102,9 +106,11 @@ public int getNumberOfSamples ()
* Get the bits per sample.
*
* @return Bits per sample
+ * @throws IOException Could not read the file
*/
- public int getBitsPerSample ()
+ public int getBitsPerSample () throws IOException
{
+ this.lazyLoading ();
return this.bitsPerSample;
}
@@ -113,9 +119,11 @@ public int getBitsPerSample ()
* Get the sample rate.
*
* @return The sample rate
+ * @throws IOException Could not read the file
*/
- public int getSampleRate ()
+ public int getSampleRate () throws IOException
{
+ this.lazyLoading ();
return this.sampleRate;
}
@@ -126,27 +134,51 @@ public int getSampleRate ()
* @param outputStream Where to write the WAV file
* @throws IOException Could not write
*/
- public void writeWAV (final OutputStream outputStream) throws IOException
+ public synchronized void writeWAV (final OutputStream outputStream) throws IOException
{
- final boolean isFloat = this.channelDataFloat != null;
+ synchronized (this.lazyLoadingLock)
+ {
+ this.lazyLoading ();
- final WaveFile wavFile = new WaveFile (this.channels, this.sampleRate, this.bitsPerSample, this.numberOfSamples);
- if (isFloat)
- wavFile.getFormatChunk ().setCompressionCode (FormatChunk.WAVE_FORMAT_IEEE_FLOAT);
- final DataChunk dataChunk = wavFile.getDataChunk ();
+ final boolean isFloat = this.channelDataFloat != null;
+
+ final WaveFile wavFile = new WaveFile (this.channels, this.sampleRate, this.bitsPerSample, this.numberOfSamples);
+ if (isFloat)
+ wavFile.getFormatChunk ().setCompressionCode (FormatChunk.WAVE_FORMAT_IEEE_FLOAT);
+ final DataChunk dataChunk = wavFile.getDataChunk ();
+
+ final ByteArrayOutputStream bout = new ByteArrayOutputStream (this.channels * (this.bitsPerSample / 8) * this.numberOfSamples);
+ if (isFloat)
+ for (int i = 0; i < this.numberOfSamples; i++)
+ for (int channel = 0; channel < this.channels; channel++)
+ StreamUtils.writeFloatLE (bout, this.channelDataFloat[channel][i]);
+ else
+ for (int i = 0; i < this.numberOfSamples; i++)
+ for (int channel = 0; channel < this.channels; channel++)
+ StreamUtils.writeUnsigned (bout, this.channelData[channel][i], this.bitsPerSample, false);
+
+ dataChunk.setData (bout.toByteArray ());
+ wavFile.write (outputStream);
+
+ // Dirty workaround to allow garbage collection
+ this.channelData = null;
+ this.channelDataFloat = null;
+ }
+ }
- final ByteArrayOutputStream bout = new ByteArrayOutputStream (this.channels * (this.bitsPerSample / 8) * this.numberOfSamples);
- if (isFloat)
- for (int i = 0; i < this.numberOfSamples; i++)
- for (int channel = 0; channel < this.channels; channel++)
- StreamUtils.writeFloatLE (bout, this.channelDataFloat[channel][i]);
- else
- for (int i = 0; i < this.numberOfSamples; i++)
- for (int channel = 0; channel < this.channels; channel++)
- StreamUtils.writeUnsigned (bout, this.channelData[channel][i], this.bitsPerSample, false);
- dataChunk.setData (bout.toByteArray ());
- wavFile.write (outputStream);
+ private void lazyLoading () throws IOException
+ {
+ synchronized (this.lazyLoadingLock)
+ {
+ if (this.channelData != null || this.channelDataFloat != null)
+ return;
+
+ try (final FileInputStream stream = new FileInputStream (this.ncwFile))
+ {
+ this.read (stream);
+ }
+ }
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/file/ncw/NcwFileSampleData.java b/src/main/java/de/mossgrabers/convertwithmoss/file/ncw/NcwFileSampleData.java
index 4891f67..ac1856f 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/file/ncw/NcwFileSampleData.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/file/ncw/NcwFileSampleData.java
@@ -37,26 +37,26 @@ public NcwFileSampleData (final File file) throws IOException
{
super (file);
- this.handleNcwFile (new NcwFile (file));
+ this.ncwFile = new NcwFile (file);
}
/**
- * Constructor.
+ * Constructor. Used for monoliths.
*
* @param inputStream The stream from which the file content can be read
* @throws IOException Could not read the file
*/
public NcwFileSampleData (final InputStream inputStream) throws IOException
{
- this.handleNcwFile (new NcwFile (inputStream));
+ this.ncwFile = new NcwFile (inputStream);
}
- private void handleNcwFile (final NcwFile ncwFile) throws IOException
+ /** {@inheritDoc} */
+ @Override
+ protected void createAudioMetadata () throws IOException
{
- this.ncwFile = ncwFile;
-
final int channels = this.ncwFile.getChannels ();
if (channels > 2)
throw new IOException (Functions.getMessage ("IDS_NOTIFY_ERR_MONO", Integer.toString (channels), this.sampleFile.getAbsolutePath ()));
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/kmp/KMPFile.java b/src/main/java/de/mossgrabers/convertwithmoss/format/kmp/KMPFile.java
index 204a64d..a7aaf53 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/kmp/KMPFile.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/kmp/KMPFile.java
@@ -12,6 +12,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -186,6 +187,16 @@ public void read (final InputStream inputStream) throws IOException, ParseExcept
if (in.available () == 0)
break;
}
+
+ // Remove all 'skipped sample' zones
+ final List cleanedZones = new ArrayList<> ();
+ for (final ISampleZone zone: this.zones)
+ {
+ if (!SAMPLE_SKIPPED.equals (zone.getName ()))
+ cleanedZones.add (zone);
+ }
+ this.zones.clear ();
+ this.zones.addAll (cleanedZones);
}
@@ -226,23 +237,30 @@ private void readParameterChunk1 (final DataInputStream in) throws IOException,
// Filter Cutoff - unused in KMP itself
in.readByte ();
- final byte [] nBytes = in.readNBytes (12);
- final String sampleFilename = new String (nBytes);
+ String sampleFilename = new String (in.readNBytes (12));
- if (SAMPLE_SKIPPED.equals (sampleFilename))
- this.notifier.logError ("IDS_KMP_ERR_SKIPPED_SAMPLE");
- else if (sampleFilename.startsWith (SAMPLE_INTERNAL))
- try
- {
- final int internalIndex = Integer.parseInt (sampleFilename.substring (SAMPLE_INTERNAL.length ()));
- this.notifier.logError ("IDS_KMP_ERR_INTERNAL_SAMPLE", Integer.toString (internalIndex));
- }
- catch (final NumberFormatException ex)
+ while (sampleFilename != null)
+ {
+ if (SAMPLE_SKIPPED.equals (sampleFilename))
{
- // All good, not a reference to internal sample memory
+ zone.setName (SAMPLE_SKIPPED);
+ this.notifier.log ("IDS_KMP_SKIPPED_SAMPLE");
+ break;
}
- this.readKSFZone (zone, sampleFilename);
+ if (sampleFilename.startsWith (SAMPLE_INTERNAL))
+ try
+ {
+ final int internalIndex = Integer.parseInt (sampleFilename.substring (SAMPLE_INTERNAL.length ()));
+ throw new IOException (Functions.getMessage ("IDS_KMP_ERR_INTERNAL_SAMPLE", Integer.toString (internalIndex)));
+ }
+ catch (final NumberFormatException ex)
+ {
+ // All good, not a reference to internal sample memory
+ }
+
+ sampleFilename = this.readKSFZone (zone, sampleFilename);
+ }
}
}
@@ -261,7 +279,7 @@ private void readParameterChunk2 (final DataInputStream in) throws IOException
}
- private void readKSFZone (final ISampleZone zone, final String filename) throws IOException, ParseException
+ private String readKSFZone (final ISampleZone zone, final String filename) throws IOException, ParseException
{
File ksfFile = new File (this.sampleFolder1, filename);
if (!ksfFile.exists ())
@@ -273,7 +291,7 @@ private void readKSFZone (final ISampleZone zone, final String filename) throws
try (final FileInputStream stream = new FileInputStream (ksfFile))
{
- KSFFile.read (stream, zone);
+ return KSFFile.read (stream, zone);
}
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/kmp/KSFFile.java b/src/main/java/de/mossgrabers/convertwithmoss/format/kmp/KSFFile.java
index 40db025..07c05ae 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/kmp/KSFFile.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/kmp/KSFFile.java
@@ -78,10 +78,11 @@ private KSFFile ()
*
* @param inputStream Where to read the file from
* @param zone The zone to which to add the KSF data
+ * @return The referenced KSF file, if any
* @throws IOException Could not read the file
* @throws ParseException Error during parsing
*/
- public static void read (final InputStream inputStream, final ISampleZone zone) throws IOException, ParseException
+ public static String read (final InputStream inputStream, final ISampleZone zone) throws IOException, ParseException
{
final DataInputStream in = new DataInputStream (inputStream);
@@ -138,8 +139,10 @@ public static void read (final InputStream inputStream, final ISampleZone zone)
// Bit 7 (0x40): 1 = reverse, 0 = forward
// Bit 8 (0x80): 1 = loop off, 0 = loop on
final int attributes = in.read ();
+
+ // Boost by 12dB
if ((attributes & 1) > 0)
- System.out.println ("Boosted!");
+ zone.setGain (boostDecibels (zone.getGain (), 12.0));
if ((attributes & 0x10) > 0)
throw new ParseException (Functions.getMessage ("IDS_KMP_COMPRESSED_DATA_NOT_SUPPORTED"));
@@ -168,12 +171,12 @@ public static void read (final InputStream inputStream, final ISampleZone zone)
case KSF_SAMPLE_NAME_ID:
assertSize (id, dataSize, KSF_SAMPLE_NAME_SIZE);
- combinedName = new String (in.readNBytes (24));
+ combinedName = new String (in.readNBytes (KSF_SAMPLE_NAME_SIZE));
break;
case KSF_SAMPLE_FILENAME_ID:
assertSize (id, dataSize, KSF_SAMPLE_FILENAME_SIZE);
- throw new ParseException (Functions.getMessage ("IDS_KMP_ERR_REFERENCED_KSF_NOT_SUPPORTED"));
+ return new String (in.readNBytes (KSF_SAMPLE_FILENAME_SIZE));
case KSF_SAMPLE_DIVIDED_PARAM_ID, KSF_SAMPLE_DIVIDED_DATA_ID:
throw new ParseException (Functions.getMessage ("IDS_KMP_ERR_DISTRIBUTED_KSF_NOT_SUPPORTED"));
@@ -190,6 +193,8 @@ public static void read (final InputStream inputStream, final ISampleZone zone)
final InMemorySampleData sampleData = new InMemorySampleData (audioMetadata, data);
zone.setName (combinedName.trim ());
zone.setSampleData (sampleData);
+
+ return null;
}
@@ -348,4 +353,20 @@ private static String createSafeFilename (final String filename)
final String name = filename.replaceAll ("[\\\\/:*?\"<>|&\\.#]", "_").trim ();
return name.length () == 1 ? "NAME" + name : name;
}
+
+
+ private static double boostDecibels (final double originalDb, final double boostDb)
+ {
+ // Convert original dB to linear scale
+ double originalLinear = Math.pow (10, originalDb / 10);
+
+ // Convert boost dB to linear scale
+ double boostLinear = Math.pow (10, boostDb / 10);
+
+ // Add the linear values
+ double newLinear = originalLinear + boostLinear;
+
+ // Convert back to dB
+ return 10 * Math.log10 (newLinear);
+ }
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/nki/type/kontakt5/FileList.java b/src/main/java/de/mossgrabers/convertwithmoss/format/nki/type/kontakt5/FileList.java
index 02f7931..467f1eb 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/nki/type/kontakt5/FileList.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/nki/type/kontakt5/FileList.java
@@ -46,15 +46,15 @@ public void parse (final PresetChunk chunk) throws IOException
if (version < 2 || version > 3)
throw new IOException (Functions.getMessage ("IDS_NKI5_UNSUPPORTED_FILELIST_EX_VERSION", Integer.toString (version)));
- final List files = readFiles (in, version);
if (version == 3)
{
+ final List files = readFilesV3 (in);
this.specialFiles = files;
this.sampleFiles = files;
return;
}
- this.specialFiles = files;
+ this.specialFiles = readFilesV2 (in);
}
else
{
@@ -67,7 +67,7 @@ public void parse (final PresetChunk chunk) throws IOException
readFile (in);
}
- this.sampleFiles = readFiles (in, -1);
+ this.sampleFiles = readFilesV2 (in);
this.readMetadata (in, chunkID);
}
@@ -90,7 +90,7 @@ private void readMetadata (final ByteArrayInputStream in, final int chunkID) thr
for (int i = 0; i < numFiles; i++)
StreamUtils.readUnsigned32 (in, false);
- this.otherFiles = readFiles (in, -1);
+ this.otherFiles = readFilesV2 (in);
}
else
// Final padding
@@ -99,14 +99,33 @@ private void readMetadata (final ByteArrayInputStream in, final int chunkID) thr
/**
- * Read several file paths.
+ * Read several file paths in version 2.
*
* @param in The input stream to read from
- * @param version The version of the Filename List structure
* @return The read file paths
* @throws IOException Could not read
*/
- private static List readFiles (final ByteArrayInputStream in, final int version) throws IOException
+ private static List readFilesV2 (final ByteArrayInputStream in) throws IOException
+ {
+ final List files = new ArrayList<> ();
+ if (in.available () > 0)
+ {
+ final int size = StreamUtils.readSigned32 (in, false);
+ for (int i = 0; i < size; i++)
+ files.add (readFile (in));
+ }
+ return files;
+ }
+
+
+ /**
+ * Read several file paths in version 3.
+ *
+ * @param in The input stream to read from
+ * @return The read file paths
+ * @throws IOException Could not read
+ */
+ private static List readFilesV3 (final ByteArrayInputStream in) throws IOException
{
final List files = new ArrayList<> ();
if (in.available () > 0)
@@ -114,14 +133,13 @@ private static List readFiles (final ByteArrayInputStream in, final int
final int size = StreamUtils.readSigned32 (in, false);
// 00 padding
- if (version == 3)
- in.skipNBytes (8);
+ in.skipNBytes (8);
for (int i = 0; i < size - 1; i++)
{
files.add (readFile (in));
- if (version == 3 && in.available () > 0)
+ if (in.available () > 0)
{
// Padding - always zero
in.skipNBytes (4);
@@ -133,8 +151,7 @@ private static List readFiles (final ByteArrayInputStream in, final int
}
// The last empty entry - 00 padding
- if (version == 3)
- in.skipNBytes (24);
+ in.skipNBytes (24);
}
return files;
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/samplefile/SampleFileDetectorTask.java b/src/main/java/de/mossgrabers/convertwithmoss/format/samplefile/SampleFileDetectorTask.java
index 87a0a9c..7ba182b 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/samplefile/SampleFileDetectorTask.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/samplefile/SampleFileDetectorTask.java
@@ -109,7 +109,10 @@ protected List readFile (final File folder)
if (files.length == 0)
continue;
+ this.notifier.log ("IDS_NOTIFY_FOUND_RAW_FILES", Integer.toString (files.length), sampleFileType.getName ());
+
// Analyze all files
+ int outputCount = 0;
final List sampleData = new ArrayList<> (files.length);
for (final File file: files)
{
@@ -120,6 +123,10 @@ protected List readFile (final File folder)
try
{
sampleData.add (this.createSampleData (file));
+ this.notifyProgress ();
+ outputCount++;
+ if (outputCount % 80 == 0)
+ this.notifyNewline ();
}
catch (final IOException ex)
{
@@ -128,6 +135,8 @@ protected List readFile (final File folder)
}
}
+ this.notifyNewline ();
+
sources.addAll (this.createMultisample (sampleFileType, folder, sampleData));
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YamahaYsfcDetectorTask.java b/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YamahaYsfcDetectorTask.java
index b5c4d1d..033ac23 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YamahaYsfcDetectorTask.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YamahaYsfcDetectorTask.java
@@ -59,7 +59,9 @@ public class YamahaYsfcDetectorTask extends AbstractDetectorTask
".x7a",
".x8u", // MODX / MODX+
".x8l",
- ".x8a"
+ ".x8a",
+ ".y2l", // Montage M
+ ".y2u"
};
private static final int SAMPLE_RESOLUTION = 16;
@@ -88,8 +90,20 @@ protected List readFile (final File file)
try
{
final YsfcFile ysfcFile = new YsfcFile (file);
- this.notifier.log ("IDS_YSFC_FOUND_TYPE", getWorkstationName (ysfcFile), ysfcFile.getVersionStr ());
- return this.createMultisample (ysfcFile);
+ this.notifier.log ("IDS_YSFC_FOUND_TYPE", ysfcFile.getVersion ().getTitle (), ysfcFile.getVersionStr ());
+
+ final List keyGroups = this.createMultisamplesFromKeygroups (ysfcFile);
+
+ // If there are no Performances, create directly from key-groups
+ final YamahaYsfcChunk epfmChunk = ysfcFile.getChunks ().get (YamahaYsfcChunk.ENTRY_LIST_PERFORMANCE);
+ if (epfmChunk == null)
+ {
+ this.notifier.log ("IDS_YSFC_NO_PERFORMANCES");
+ return keyGroups;
+ }
+
+ // TODO
+ return keyGroups;
}
catch (final IOException ex)
{
@@ -99,29 +113,14 @@ protected List readFile (final File file)
}
- private static String getWorkstationName (final YsfcFile ysfcFile)
- {
- final int version = ysfcFile.getVersion ();
- if (version <= 101)
- return "Motif XS";
- if (version == 102)
- return "Motif XF";
- if (version == 103)
- return "MOXF";
- if (version >= 500)
- return "MODX";
- return "Montage";
- }
-
-
/**
- * Create a multi-sample from the chunk data.
+ * Create multi-samples from the key-group/wave chunk data.
*
* @param ysfcFile The YSFC source file
* @return The multi-sample(s)
* @throws IOException COuld not read the multi-sample
*/
- private List createMultisample (final YsfcFile ysfcFile) throws IOException
+ private List createMultisamplesFromKeygroups (final YsfcFile ysfcFile) throws IOException
{
final Map chunks = ysfcFile.getChunks ();
@@ -177,7 +176,7 @@ private List createMultisample (final YsfcFile ysfcFile) thr
}
- private static IGroup createSampleZones (final List keyBanks, final List waveDataItems, final String name, final int version) throws IOException
+ private static IGroup createSampleZones (final List keyBanks, final List waveDataItems, final String name, final YamahaYsfcVersion version) throws IOException
{
final DefaultGroup group = new DefaultGroup ("Layer");
@@ -230,7 +229,7 @@ private static IGroup createSampleZones (final List keyBanks,
else
sampleData.setSampleData (WaveFile.interleaveChannels (data, dataRight, SAMPLE_RESOLUTION));
- keybankIndex += version < 400 ? 1 : 2;
+ keybankIndex += version.isVersion1 () ? 1 : 2;
waveDataIndex += 2;
}
}
@@ -319,17 +318,16 @@ private static List readWaveData (final byte [] dwimDataArra
* Reads all the key-bank data items from the given data array.
*
* @param dwfmDataArray The array to read from
- * @param version The format version of the key-bank, e.g. 404 for version 4.0.4
+ * @param version The format version of the YSFC file
* @return The parsed wave metadata items
* @throws IOException Could not read the data
*/
- private static List readKeyBanks (final byte [] dwfmDataArray, final int version) throws IOException
+ private static List readKeyBanks (final byte [] dwfmDataArray, final YamahaYsfcVersion version) throws IOException
{
final List keyBanks = new ArrayList<> ();
final ByteArrayInputStream dwfmContentStream = new ByteArrayInputStream (dwfmDataArray);
- final boolean isVersion1 = version < 400;
// numberOfKeyBank
- StreamUtils.readUnsigned16 (dwfmContentStream, isVersion1);
+ StreamUtils.readUnsigned16 (dwfmContentStream, version.isVersion1 ());
dwfmContentStream.skipNBytes (2);
while (dwfmContentStream.available () > 0)
keyBanks.add (new YamahaYsfcKeybank (dwfmContentStream, version));
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YamahaYsfcKeybank.java b/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YamahaYsfcKeybank.java
index 93765b9..1c99eba 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YamahaYsfcKeybank.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YamahaYsfcKeybank.java
@@ -56,7 +56,7 @@ public YamahaYsfcKeybank ()
* @param version The format version of the key-bank, e.g. 404 for version 4.0.4
* @throws IOException Could not read the entry item
*/
- public YamahaYsfcKeybank (final InputStream in, final int version) throws IOException
+ public YamahaYsfcKeybank (final InputStream in, final YamahaYsfcVersion version) throws IOException
{
this.read (in, version);
}
@@ -69,10 +69,10 @@ public YamahaYsfcKeybank (final InputStream in, final int version) throws IOExce
* @param version The format version of the key-bank, e.g. 404 for version 4.0.4
* @throws IOException Could not read the entry item
*/
- public void read (final InputStream in, final int version) throws IOException
+ public void read (final InputStream in, final YamahaYsfcVersion version) throws IOException
{
- final boolean isVersion1 = version < 400;
- final boolean isMotif = version < 103;
+ final boolean isVersion1 = version.isVersion1 ();
+ final boolean isMotif = version.isMotif ();
final boolean isBigEndian = isVersion1 && !isMotif;
this.keyRangeLower = in.read ();
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YamahaYsfcPartElement.java b/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YamahaYsfcPartElement.java
new file mode 100644
index 0000000..ef21186
--- /dev/null
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YamahaYsfcPartElement.java
@@ -0,0 +1,65 @@
+// Written by Jürgen Moßgraber - mossgrabers.de
+// (c) 2019-2024
+// Licensed under LGPLv3 - http://www.gnu.org/licenses/lgpl-3.0.txt
+
+package de.mossgrabers.convertwithmoss.format.yamaha.ysfc;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import de.mossgrabers.convertwithmoss.file.StreamUtils;
+
+
+/**
+ * An element of a part in a performance.
+ *
+ * @author Jürgen Moßgraber
+ */
+public class YamahaYsfcPartElement
+{
+ private byte [] dataBlock;
+
+
+ /**
+ * Constructor which reads the performance from the input stream.
+ *
+ * @param in The input stream
+ * @param version The format version of the YSFC file
+ * @throws IOException Could not read the entry item
+ */
+ public YamahaYsfcPartElement (final InputStream in, final YamahaYsfcVersion version) throws IOException
+ {
+ this.read (in, version);
+ }
+
+
+ /**
+ * Read a performance from the input stream.
+ *
+ * @param in The input stream
+ * @param version The format version of the YSFC file
+ * @throws IOException Could not read the entry item
+ */
+ public void read (final InputStream in, final YamahaYsfcVersion version) throws IOException
+ {
+ // TODO ...
+ this.dataBlock = StreamUtils.readDataBlock (in, true);
+ final InputStream elementData = new ByteArrayInputStream (dataBlock);
+ // TODO ...
+ }
+
+
+ /**
+ * Write a performance to the output stream.
+ *
+ * @param out The output stream
+ * @throws IOException Could not write the entry item
+ */
+ public void write (final OutputStream out) throws IOException
+ {
+ // TODO
+ StreamUtils.writeDataBlock (out, this.dataBlock, true);
+ }
+}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YamahaYsfcPerformance.java b/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YamahaYsfcPerformance.java
new file mode 100644
index 0000000..76c2820
--- /dev/null
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YamahaYsfcPerformance.java
@@ -0,0 +1,312 @@
+// Written by Jürgen Moßgraber - mossgrabers.de
+// (c) 2019-2024
+// Licensed under LGPLv3 - http://www.gnu.org/licenses/lgpl-3.0.txt
+
+package de.mossgrabers.convertwithmoss.format.yamaha.ysfc;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import de.mossgrabers.convertwithmoss.file.StreamUtils;
+import de.mossgrabers.tools.StringUtils;
+import de.mossgrabers.tools.ui.Functions;
+
+
+/**
+ * A performance which is the metadata description of a sample in YSFC terms.
+ *
+ * @author Jürgen Moßgraber
+ */
+public class YamahaYsfcPerformance
+{
+ private String name;
+ private final List parts = new ArrayList<> ();
+ private final YamahaYsfcVersion sourceVersion;
+
+ private byte [] reverbBlock;
+ private byte [] variationBlock;
+ private byte [] masterEqBlock;
+ private byte [] masterEffectBlock;
+ private byte [] commonParameters;
+ private byte [] sceneData;
+ private byte [] controlBoxes;
+ private byte [] adPart;
+ private byte [] digitalInputPart;
+ private byte [] playSettings;
+ private final String [] assignableKnobs = new String [8];
+ final List allParts = new ArrayList<> ();
+
+
+ /**
+ * Default constructor.
+ *
+ * @param version The version to use
+ * @throws IOException Could not read the default parameters for the requested version
+ */
+ public YamahaYsfcPerformance (final YamahaYsfcVersion version) throws IOException
+ {
+ this.sourceVersion = version;
+ if (version != YamahaYsfcVersion.MONTAGE)
+ throw new IOException (Functions.getMessage ("IDS_YSFC_VERSION_NOT_SUPPORTED", version.getTitle ()));
+ final byte [] data = Functions.rawFileFor ("de/mossgrabers/convertwithmoss/templates/ysfc/MontageDPFM.bin");
+ this.read (new ByteArrayInputStream (data));
+ }
+
+
+ /**
+ * Constructor which reads the performance from the input stream.
+ *
+ * @param in The input stream
+ * @param version The format version of the YSFC file
+ * @throws IOException Could not read the entry item
+ */
+ public YamahaYsfcPerformance (final InputStream in, final YamahaYsfcVersion version) throws IOException
+ {
+ this.sourceVersion = version;
+ this.read (in);
+ }
+
+
+ /**
+ * Read a performance from the input stream.
+ *
+ * @param in The input stream
+ * @throws IOException Could not read the entry item
+ */
+ public void read (final InputStream in) throws IOException
+ {
+ this.readCommon (new ByteArrayInputStream (StreamUtils.readDataBlock (in, true)));
+ this.readEffects (in);
+ this.readParts (in);
+ }
+
+
+ private void readCommon (final InputStream in) throws IOException
+ {
+ this.name = StreamUtils.readASCII (in, 21).trim ();
+ final int pos = this.name.indexOf (0);
+ if (pos >= 0)
+ this.name = this.name.substring (0, pos);
+
+ // Common Parameters
+ this.commonParameters = in.readNBytes (43);
+
+ // Scene 1-8
+ if (this.sourceVersion == YamahaYsfcVersion.MONTAGE)
+ this.sceneData = in.readNBytes (8 * 11);
+ else // MODX
+ this.sceneData = in.readNBytes (8 * 21);
+
+ // Assignable Knob 1-8
+ for (int i = 0; i < 8; i++)
+ this.assignableKnobs[i] = StreamUtils.readASCII (in, 17).trim ();
+
+ // Control Box 1-16
+ this.controlBoxes = in.readNBytes (16 * 9);
+ }
+
+
+ private void readEffects (final InputStream in) throws IOException
+ {
+ this.reverbBlock = StreamUtils.readDataBlock (in, true);
+ this.variationBlock = StreamUtils.readDataBlock (in, true);
+ this.masterEqBlock = StreamUtils.readDataBlock (in, true);
+ this.masterEffectBlock = StreamUtils.readDataBlock (in, true);
+ }
+
+
+ private void readParts (final InputStream in) throws IOException
+ {
+ final int numberOfParts = (int) StreamUtils.readUnsigned32 (in, true);
+
+ for (int i = 0; i < numberOfParts; i++)
+ this.allParts.add (new YamahaYsfcPerformancePart (new ByteArrayInputStream (StreamUtils.readDataBlock (in, true)), this.sourceVersion));
+
+ // Skip AD + Digital input parts
+ this.adPart = StreamUtils.readDataBlock (in, true);
+ this.digitalInputPart = StreamUtils.readDataBlock (in, true);
+
+ // Read all elements
+
+ // Only keep sample based parts (0 = AWM, 1 = AWM Drum, 2 = FM)
+ for (int i = 0; i < numberOfParts; i++)
+ {
+ final int partType = (int) StreamUtils.readUnsigned32 (in, true);
+ switch (partType)
+ {
+ // AWM
+ case 0, 1:
+ // 8 for plain AWM or 73 for drums
+ final int numberOfElements = (int) StreamUtils.readUnsigned32 (in, true);
+ final YamahaYsfcPerformancePart part = this.allParts.get (i);
+ this.parts.add (part);
+ for (int el = 0; el < numberOfElements; el++)
+ part.addElement (new YamahaYsfcPartElement (in, this.sourceVersion));
+ break;
+
+ // FM - not used
+ case 2:
+ final int numberOfOperators = (int) StreamUtils.readUnsigned32 (in, true);
+ StreamUtils.readDataBlock (in, true);
+ for (int op = 0; op < numberOfOperators; op++)
+ StreamUtils.readDataBlock (in, true);
+ break;
+
+ default:
+ throw new IOException (Functions.getMessage ("IDS_YSFC_UNKNOWN_PART_TYPE", Integer.toString (partType)));
+ }
+ }
+
+ // Super knob & ARP settings
+ this.playSettings = in.readAllBytes ();
+ }
+
+
+ /**
+ * Write a performance to the output stream.
+ *
+ * @param out The output stream
+ * @throws IOException Could not write the entry item
+ */
+ public void write (final OutputStream out) throws IOException
+ {
+ this.writeCommon (out);
+ this.writeEffects (out);
+ this.writeParts (out);
+ }
+
+
+ private void writeCommon (final OutputStream out) throws IOException
+ {
+ final ByteArrayOutputStream arrayOut = new ByteArrayOutputStream ();
+
+ StreamUtils.writeASCII (arrayOut, StringUtils.optimizeName (this.name, 20), 21);
+
+ arrayOut.write (this.commonParameters);
+ arrayOut.write (this.sceneData);
+
+ // Assignable Knob 1-8
+ for (int i = 0; i < 8; i++)
+ StreamUtils.writeASCII (arrayOut, StringUtils.rightPadSpaces (StringUtils.optimizeName (this.assignableKnobs[i], 16), 16), 17);
+
+ // Control Box 1-16
+ arrayOut.write (this.controlBoxes);
+
+ StreamUtils.writeDataBlock (out, arrayOut.toByteArray (), true);
+ }
+
+
+ private void writeEffects (final OutputStream out) throws IOException
+ {
+ StreamUtils.writeDataBlock (out, this.reverbBlock, true);
+ StreamUtils.writeDataBlock (out, this.variationBlock, true);
+ StreamUtils.writeDataBlock (out, this.masterEqBlock, true);
+ StreamUtils.writeDataBlock (out, this.masterEffectBlock, true);
+ }
+
+
+ private void writeParts (final OutputStream out) throws IOException
+ {
+ StreamUtils.writeUnsigned32 (out, this.allParts.size (), true);
+
+ for (final YamahaYsfcPerformancePart part: this.allParts)
+ part.write (out);
+
+ StreamUtils.writeDataBlock (out, this.adPart, true);
+ StreamUtils.writeDataBlock (out, this.digitalInputPart, true);
+
+ for (final YamahaYsfcPerformancePart part: this.allParts)
+ {
+ StreamUtils.writeUnsigned32 (out, part.type, true);
+
+ // Always 8 AWM elements!
+ StreamUtils.writeUnsigned32 (out, 8, true);
+ for (final YamahaYsfcPartElement element: part.getElements ())
+ element.write (out);
+ }
+
+ out.write (this.playSettings);
+ }
+
+
+ public static void main (final String [] args)
+ {
+ try (final FileOutputStream out = new FileOutputStream (new File ("C:\\Users\\mos\\Desktop\\result.bin")))
+ {
+ final YamahaYsfcPerformance performance = new YamahaYsfcPerformance (YamahaYsfcVersion.MONTAGE);
+ performance.write (out);
+ }
+ catch (final IOException e)
+ {
+ e.printStackTrace ();
+ }
+ }
+
+
+ public static void main2 (final String [] args)
+ {
+ final String filenameX7U = "C:\\Privat\\Programming\\ConvertWithMoss\\Testdateien\\YamahaYSFC\\X7 - Montage\\01W Atmosphere.X7U";
+ final String filenameX8L = "C:\\Privat\\Programming\\ConvertWithMoss\\Testdateien\\YamahaYSFC\\X8 - MODX\\Arilyn.X8L";
+
+ YsfcFile ysfcFile;
+ try
+ {
+ ysfcFile = new YsfcFile (new File (filenameX7U));
+ final Map chunks = ysfcFile.getChunks ();
+ final YamahaYsfcChunk epfmChunk = chunks.get (YamahaYsfcChunk.ENTRY_LIST_PERFORMANCE);
+ final YamahaYsfcChunk dpfmChunk = chunks.get (YamahaYsfcChunk.DATA_LIST_PERFORMANCE);
+ if (epfmChunk == null || dpfmChunk == null)
+ // TODO
+ // this.notifier.logError ("IDS_YSFC_NO_MULTISAMPLE_DATA");
+ // return Collections.emptyList ();
+ return;
+
+ final List epfmListChunks = epfmChunk.getEntryListChunks ();
+ final List dpfmListChunks = dpfmChunk.getDataArrays ();
+
+ if (epfmListChunks.size () != dpfmListChunks.size ())
+ // TODO
+ // this.notifier.logError ("IDS_YSFC_DIFFERENT_NUMBER_OF_WAVEFORM_CHUNKS");
+ // return Collections.emptyList ();
+ return;
+
+ for (int i = 0; i < epfmListChunks.size (); i++)
+ {
+ final YamahaYsfcEntry yamahaYsfcEntry = epfmListChunks.get (i);
+ final byte [] performanceData = dpfmListChunks.get (i);
+
+ final YamahaYsfcPerformance yamahaYsfcPerformance = new YamahaYsfcPerformance (new ByteArrayInputStream (performanceData), ysfcFile.getVersion ());
+ }
+
+ }
+ catch (final IOException e)
+ {
+ e.printStackTrace ();
+ }
+ }
+
+
+ private static void dump (final YamahaYsfcEntry yamahaYsfcEntry, final byte [] performanceData, final int version)
+ {
+ // TODO Auto-generated method stub
+ try
+ {
+ Files.write (new File ("C:\\Users\\mos\\Desktop\\Data\\" + yamahaYsfcEntry.getItemTitle () + "DPFM.bin").toPath (), performanceData);
+ }
+ catch (final IOException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace ();
+ }
+
+ }
+}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YamahaYsfcPerformancePart.java b/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YamahaYsfcPerformancePart.java
new file mode 100644
index 0000000..401f3fc
--- /dev/null
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YamahaYsfcPerformancePart.java
@@ -0,0 +1,93 @@
+// Written by Jürgen Moßgraber - mossgrabers.de
+// (c) 2019-2024
+// Licensed under LGPLv3 - http://www.gnu.org/licenses/lgpl-3.0.txt
+
+package de.mossgrabers.convertwithmoss.format.yamaha.ysfc;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import de.mossgrabers.convertwithmoss.file.StreamUtils;
+import de.mossgrabers.tools.StringUtils;
+
+
+/**
+ * A part in a performance.
+ *
+ * @author Jürgen Moßgraber
+ */
+public class YamahaYsfcPerformancePart
+{
+ String name;
+ int type;
+ private List elements = new ArrayList<> ();
+ private byte [] theRest;
+
+
+ /**
+ * Constructor which reads the performance from the input stream.
+ *
+ * @param in The input stream
+ * @param version The format version of the YSFC file
+ * @throws IOException Could not read the entry item
+ */
+ public YamahaYsfcPerformancePart (final InputStream in, final YamahaYsfcVersion version) throws IOException
+ {
+ this.read (in, version);
+ }
+
+
+ /**
+ * Read a performance from the input stream.
+ *
+ * @param in The input stream
+ * @param version The format version of the YSFC file
+ * @throws IOException Could not read the entry item
+ */
+ public void read (final InputStream in, final YamahaYsfcVersion version) throws IOException
+ {
+ this.name = StreamUtils.readASCII (in, 21).trim ();
+ final int pos = this.name.indexOf (0);
+ if (pos >= 0)
+ this.name = this.name.substring (0, pos);
+
+ this.type = in.read ();
+ // TODO ...
+ this.theRest = in.readAllBytes ();
+ }
+
+
+ /**
+ * Write a performance to the output stream.
+ *
+ * @param out The output stream
+ * @throws IOException Could not write the entry item
+ */
+ public void write (final OutputStream out) throws IOException
+ {
+ final ByteArrayOutputStream arrayOut = new ByteArrayOutputStream ();
+ StreamUtils.writeASCII (arrayOut, StringUtils.rightPadSpaces (StringUtils.optimizeName (this.name, 20), 20), 21);
+
+ arrayOut.write (this.type);
+ // TODO
+ arrayOut.write (this.theRest);
+
+ StreamUtils.writeDataBlock (out, arrayOut.toByteArray (), true);
+ }
+
+
+ public void addElement (final YamahaYsfcPartElement element)
+ {
+ this.elements.add (element);
+ }
+
+
+ public List getElements ()
+ {
+ return this.elements;
+ }
+}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YamahaYsfcVersion.java b/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YamahaYsfcVersion.java
new file mode 100644
index 0000000..37f13ca
--- /dev/null
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YamahaYsfcVersion.java
@@ -0,0 +1,94 @@
+// Written by Jürgen Moßgraber - mossgrabers.de
+// (c) 2019-2024
+// Licensed under LGPLv3 - http://www.gnu.org/licenses/lgpl-3.0.txt
+
+package de.mossgrabers.convertwithmoss.format.yamaha.ysfc;
+
+/**
+ * Possible YSFC versions.
+ *
+ * @author Jürgen Moßgraber
+ */
+public enum YamahaYsfcVersion
+{
+ /** 1.0.1 */
+ MOTIF_XS("Motif XS"),
+ /** 1.0.2 */
+ MOTIF_XF("Motif XF"),
+ /** 1.0.3 */
+ MOXF("MOXF"),
+ /** 4.0.4 */
+ MONTAGE("Montage"),
+ /** 4.1.0 */
+ MONTAGE_M("Montage M"),
+ /** 5.0.1 */
+ MODX("MODX"),
+ /** Unknown version. */
+ UNKNOWN("Unknown");
+
+
+ private final String title;
+
+
+ private YamahaYsfcVersion (final String title)
+ {
+ this.title = title;
+ }
+
+
+ /**
+ * Get the title.
+ *
+ * @return The title
+ */
+ public String getTitle ()
+ {
+ return this.title;
+ }
+
+
+ /**
+ * Returns true if this is a version 1.0.x version.
+ *
+ * @return True if version 1
+ */
+ public boolean isVersion1 ()
+ {
+ return this.isMotif () || this == MOXF;
+ }
+
+
+ /**
+ * Returns true if this is a Motif 1.0.x version.
+ *
+ * @return True if Motif version
+ */
+ public boolean isMotif ()
+ {
+ return this == MOTIF_XF || this == MOTIF_XS;
+ }
+
+
+ /**
+ * Get the constant from the 3 digit version number.
+ *
+ * @param version The version, e.g. version 4.0.5 is 405.
+ * @return The enumeration constant
+ */
+ public static YamahaYsfcVersion get (final int version)
+ {
+ if (version <= 101)
+ return MOTIF_XS;
+ if (version == 102)
+ return MOTIF_XF;
+ if (version == 103)
+ return MOXF;
+ if (version >= 400 && version < 410)
+ return MONTAGE;
+ if (version >= 410 && version < 420)
+ return MONTAGE_M;
+ if (version >= 500 && version < 510)
+ return MODX;
+ return UNKNOWN;
+ }
+}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YsfcFile.java b/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YsfcFile.java
index 3b3f871..3a19a26 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YsfcFile.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/yamaha/ysfc/YsfcFile.java
@@ -100,18 +100,19 @@ public String getVersionStr ()
*/
public void setVersionStr (final String versionStr)
{
- this.versionStr = versionStr;
+ this.versionStr = versionStr.trim ();
+ this.version = parseVersion (this.versionStr);
}
/**
- * Get the version number as an integer, e.g. 404.
+ * Get the YSFC version.
*
- * @return The version number
+ * @return The version
*/
- public int getVersion ()
+ public YamahaYsfcVersion getVersion ()
{
- return this.version;
+ return YamahaYsfcVersion.get (this.version);
}
@@ -146,8 +147,7 @@ private void read (final InputStream inputStream) throws IOException
// The version in the form of 'A.B.C', e.g. '1.0.2'. Older versions may have appended 0xFF
// instead of 0x00
- this.versionStr = createAsciiString (inputStream.readNBytes (16));
- this.version = parseVersion (this.versionStr.trim ());
+ this.setVersionStr (createAsciiString (inputStream.readNBytes (16)));
// The size of the chunk catalog block
final int catalogSize = (int) StreamUtils.readUnsigned32 (inputStream, true);
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/templates/ysfc/package-info.java b/src/main/java/de/mossgrabers/convertwithmoss/templates/ysfc/package-info.java
new file mode 100644
index 0000000..ba45131
--- /dev/null
+++ b/src/main/java/de/mossgrabers/convertwithmoss/templates/ysfc/package-info.java
@@ -0,0 +1,10 @@
+// Written by Jürgen Moßgraber - mossgrabers.de
+// (c) 2019-2024
+// Licensed under LGPLv3 - http://www.gnu.org/licenses/lgpl-3.0.txt
+
+/**
+ * Resources folder for YSFC template files.
+ *
+ * @author Jürgen Moßgraber
+ */
+package de.mossgrabers.convertwithmoss.templates.ysfc;
\ No newline at end of file
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/ui/ConvertWithMossApp.java b/src/main/java/de/mossgrabers/convertwithmoss/ui/ConvertWithMossApp.java
index 7729b72..7238c16 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/ui/ConvertWithMossApp.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/ui/ConvertWithMossApp.java
@@ -6,6 +6,7 @@
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
@@ -148,10 +149,12 @@ public class ConvertWithMossApp extends AbstractFrame implements INotifier, Cons
private Button renameFilePathSelectButton;
private final CSVRenameFile csvRenameFile = new CSVRenameFile ();
- private final LoggerBoxWeb loggingArea = new LoggerBoxWeb ();
+ private final LoggerBoxWeb loggingArea = new LoggerBoxWeb (4000);
private final TraversalManager traversalManager = new TraversalManager ();
private final List collectedSources = new ArrayList<> ();
+ private FileWriter logWriter;
+
/**
* Main-method.
@@ -349,8 +352,7 @@ public void initialise (final Stage stage, final Optional baseTitleOptio
this.closeButton = setupButton (exButtonPanel, "Close", "@IDS_EXEC_CLOSE", "@IDS_EXEC_CLOSE_TOOLTIP");
this.closeButton.setOnAction (event -> this.closeExecution ());
- final Parent loggerComponent = this.loggingArea.getComponent ();
- this.executePane.setCenter (loggerComponent);
+ this.executePane.setCenter (this.loggingArea.getComponent ());
this.executePane.setRight (exButtonPanel.getPane ());
this.executePane.setVisible (false);
@@ -539,7 +541,12 @@ private void execute (final boolean onlyAnalyse)
if (selectedDetector < 0 || !this.detectors[selectedDetector].checkSettings () || !this.detectors[selectedDetector].validateParameters ())
return;
- this.loggingArea.clear ();
+ final int selectedCreator = this.destinationTabPane.getSelectionModel ().getSelectedIndex ();
+ if (selectedCreator < 0)
+ return;
+ this.creators[selectedCreator].clearCancelled ();
+
+ this.clearLog ();
this.mainPane.setVisible (false);
this.executePane.setVisible (true);
@@ -559,6 +566,10 @@ private void cancelExecution ()
final int selectedDetector = this.sourceTabPane.getSelectionModel ().getSelectedIndex ();
if (selectedDetector >= 0)
this.detectors[selectedDetector].cancel ();
+
+ final int selectedCreator = this.destinationTabPane.getSelectionModel ().getSelectedIndex ();
+ if (selectedCreator >= 0)
+ this.creators[selectedCreator].cancel ();
}
@@ -753,11 +764,36 @@ private void applyRenaming (final IMultisampleSource multisampleSource)
}
+ private void clearLog ()
+ {
+ this.loggingArea.clear ();
+
+ try
+ {
+ this.logWriter = new FileWriter (new File (this.outputFolder, "ConvertWithMoss.log"));
+ }
+ catch (final IOException ex)
+ {
+ this.loggingArea.notifyError ("IDS_NOTIFY_ERR_NO_LOG_FILE", ex);
+ this.logWriter = null;
+ }
+ }
+
+
/** {@inheritDoc} */
@Override
public void log (final String messageID, final String... replaceStrings)
{
- this.loggingArea.notify (Functions.getMessage (messageID, replaceStrings));
+ this.logText (Functions.getMessage (messageID, replaceStrings));
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ public void logText (final String text)
+ {
+ this.loggingArea.notify (text);
+ this.logToFile (text);
}
@@ -765,7 +801,9 @@ public void log (final String messageID, final String... replaceStrings)
@Override
public void logError (final String messageID, final String... replaceStrings)
{
- this.loggingArea.notifyError (Functions.getMessage (messageID, replaceStrings));
+ final String message = Functions.getMessage (messageID, replaceStrings);
+ this.loggingArea.notifyError (message);
+ this.logToFile (message);
}
@@ -773,7 +811,9 @@ public void logError (final String messageID, final String... replaceStrings)
@Override
public void logError (final String messageID, final Throwable throwable)
{
- this.loggingArea.notifyError (Functions.getMessage (messageID, throwable));
+ final String message = Functions.getMessage (messageID, throwable);
+ this.loggingArea.notifyError (message);
+ this.logToFile (message);
}
@@ -796,14 +836,21 @@ public void logError (final Throwable throwable, final boolean logExceptionStack
this.loggingArea.notifyError (message, throwable);
else
this.loggingArea.notifyError (message);
+ this.logToFile (message);
}
- /** {@inheritDoc} */
- @Override
- public void logText (final String text)
+ private void logToFile (final String message)
{
- this.loggingArea.notify (text);
+ if (this.logWriter != null)
+ try
+ {
+ this.logWriter.append (message);
+ }
+ catch (final IOException ex)
+ {
+ // Ignore
+ }
}
@@ -854,6 +901,22 @@ public void updateButtonStates (final boolean canClose)
loggerComponent.setAccessibleText (Functions.getMessage ("IDS_NOTIFY_FINISHED"));
}
+ if (canClose)
+ {
+ if (this.logWriter != null)
+ {
+ try
+ {
+ this.logWriter.close ();
+ }
+ catch (final IOException ex)
+ {
+ // Ignore
+ }
+ this.logWriter = null;
+ }
+ }
+
});
}
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index af1e47a..d13b3c3 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -13,8 +13,6 @@
requires transitive de.mossgrabers.uitools;
requires javafx.graphics;
requires com.github.trilarion.sound;
- requires org.fxmisc.richtext;
- requires org.fxmisc.flowless;
exports de.mossgrabers.convertwithmoss.ui;
@@ -35,4 +33,5 @@
opens de.mossgrabers.convertwithmoss.images;
opens de.mossgrabers.convertwithmoss.templates.nki;
opens de.mossgrabers.convertwithmoss.templates.adv;
+ opens de.mossgrabers.convertwithmoss.templates.ysfc;
}
diff --git a/src/main/resources/Strings.properties b/src/main/resources/Strings.properties
index 20aae65..9a009d3 100644
--- a/src/main/resources/Strings.properties
+++ b/src/main/resources/Strings.properties
@@ -1,4 +1,4 @@
-TITLE=ConvertWithMoss 11.6.0
+TITLE=ConvertWithMoss 11.7.0
##################################################################################
#
@@ -39,7 +39,7 @@ IDS_NOTIFY_CANCELLED=\nCancelled.\n\n
IDS_NOTIFY_FINISHED=\nFinished.\n\n
IDS_NOTIFY_SKIPPED=Skipped folder '%1'\n because it contains WAV file(s) which cannot processed: %2\nError: %3
IDS_NOTIFY_NO_NAME=No common name detected. Using name of the first sample.\n
-IDS_NOTIFY_DETECTED_GROUPS=Detected %1 group(s).\n
+IDS_NOTIFY_DETECTED_GROUPS=Created %1 group(s).\n
IDS_NOTIFY_SAVE_FAILED=Could not create multi-sample: %1\n
IDS_NOTIFY_FILE_NOT_FOUND=The following file could not be found: %1\n
IDS_NOTIFY_COMMA=Values are comma separated.
@@ -89,6 +89,8 @@ IDS_NOTIFY_ERR_MISSING_SAMPLE_DATA=\nZone '%1' does not have sample data (maybe
IDS_NOTIFY_SEARCH_SAMPLE_IN=Looking for sample in '%1'...\n
IDS_NOTIFY_SEARCH_SAMPLE_IN_FOUND=Found.\n
IDS_NOTIFY_ERR_NO_TYPE_SELECTED=Please select at least one sample file type.
+IDS_NOTIFY_FOUND_RAW_FILES=Detected %1 %2 files.\nProcessing
+IDS_NOTIFY_ERR_NO_LOG_FILE=Could not create log file: %1\n
IDS_NOTIFY_PROCESSING=Processing
IDS_NOTIFY_FINISHED=Finished
@@ -138,10 +140,9 @@ IDS_EXS_NO_SAMPLES=The file does not reference any samples but has %1 zones.\n
IDS_KMP_UNKNOWN_CHUNK=Found unknown chunk: %1
IDS_KMP_WRONG_CHUNK_LENGTH=Wrong length of chunk '%1': was %2 but expected %3.\n
-IDS_KMP_ERR_SKIPPED_SAMPLE=Cannot handle skipped sample.\n
+IDS_KMP_SKIPPED_SAMPLE=Zone with 'skipped sample' reference found (will be removed).\n
IDS_KMP_ERR_INTERNAL_SAMPLE=Cannot handle internal sample: %1\n
IDS_KMP_ERR_KSF_NOT_FOUND=KSF file not found: %1\n
-IDS_KMP_ERR_REFERENCED_KSF_NOT_SUPPORTED=Cross-referenced KSF files are currently not supported. Please get in touch and send me the file for analysis.\n
IDS_KMP_ERR_DISTRIBUTED_KSF_NOT_SUPPORTED=Distributed KSF files are currently not supported. Please get in touch and send me the file for analysis.\n
IDS_KMP_COMPRESSED_DATA_NOT_SUPPORTED=Compressed sample data is currently not supported. Please get in touch and send me the file for analysis.\n
IDS_KMP_BIT_SIZE_NOT_SUPPORTED=KSF format only supports 8 or 16 bit samples but found %1.\n
@@ -242,6 +243,9 @@ IDS_YSFC_DIFFERENT_NUMBER_OF_WAVEFORM_CHUNKS=Different number of waveform chunks
IDS_YSFC_WAVE_FORMAT_NOT_SUPPORTED=Unsupported sample format: %1. Cannot decode the wave form(s).\n
IDS_YSFC_ENCRYPTED_SAMPLES=Samples are in compressed format which is not supported.\n
IDS_YSFC_MISSING_RIGHT_SAMPLE=Missing right stereo sample.\n
+IDS_YSFC_UNKNOWN_PART_TYPE=Unknown part type: %1\n
+IDS_YSFC_VERSION_NOT_SUPPORTED=Version '%1' not supported for performances.\n
+IDS_YSFC_NO_PERFORMANCES=File does not contain performances. Multi-samples are created directly from key-banks/wave data.\n
###################################################################################
# UI
diff --git a/src/main/resources/de/mossgrabers/convertwithmoss/templates/ysfc/MontageDPFM.bin b/src/main/resources/de/mossgrabers/convertwithmoss/templates/ysfc/MontageDPFM.bin
new file mode 100644
index 0000000..0aeeed6
Binary files /dev/null and b/src/main/resources/de/mossgrabers/convertwithmoss/templates/ysfc/MontageDPFM.bin differ