diff --git a/CHANGELOG.md b/CHANGELOG.md
index f03718f..d238e56 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,9 @@
# Changes
-## 10.5.0 (unreleased)
+## 10.2.0
-* Added support for Waldorf Quantum MkI/MkII, Iridium, Iridium Core sample format.
+* Kontakt 1-4, MPC Keygroups, Soundfont 2, TAL Sampler, TX16Wx
+ * New: Added support for amplitude and filter velocity modulation.
* Kontakt - Writing
* New: Improved pitch envelope.
* Kontakt 4.2-7 - Reading
diff --git a/documentation/SupportedFeaturesSampleFormats.ods b/documentation/SupportedFeaturesSampleFormats.ods
index 1ea083e..0e4b29e 100644
Binary files a/documentation/SupportedFeaturesSampleFormats.ods and b/documentation/SupportedFeaturesSampleFormats.ods differ
diff --git a/pom.xml b/pom.xml
index 2632f27..1e9c1f1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
4.0.0
de.mossgrabers
convertwithmoss
- 10.5.0
+ 10.2.0
jar
ConvertWithMoss
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/core/MathUtils.java b/src/main/java/de/mossgrabers/convertwithmoss/core/MathUtils.java
index 4407fe1..60c584c 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/core/MathUtils.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/core/MathUtils.java
@@ -23,34 +23,6 @@ private MathUtils ()
}
- /**
- * Limit the given value to the minimum/maximum range including minimum/maximum values.
- *
- * @param value The value to clamp
- * @param minimum The minimum value
- * @param maximum The maximum value
- * @return The value clamped to the minimum/maximum range
- */
- public static double clamp (final double value, final double minimum, final double maximum)
- {
- return Math.max (minimum, Math.min (value, maximum));
- }
-
-
- /**
- * Limit the given value to the minimum/maximum range including minimum/maximum values.
- *
- * @param value The value to clamp
- * @param minimum The minimum value
- * @param maximum The maximum value
- * @return The value clamped to the minimum/maximum range
- */
- public static int clamp (final int value, final int minimum, final int maximum)
- {
- return Math.max (minimum, Math.min (value, maximum));
- }
-
-
/**
* Converts a signed integer into a two complement short value.
*
@@ -122,7 +94,7 @@ public static double dBToDouble (final double dBValue)
*/
public static double normalizeFrequency (final double frequency, final double maxFrequency)
{
- return clamp (log2 (frequency) / log2 (maxFrequency), 0, 1);
+ return Math.clamp (log2 (frequency) / log2 (maxFrequency), 0, 1);
}
@@ -147,7 +119,7 @@ public static double denormalizeFrequency (final double normalizedFrequency, fin
*/
public static double normalizeCutoff (final double cutoffInHertz)
{
- return MathUtils.clamp ((log2 (cutoffInHertz / (2 * 440.0)) * 12.0 + 57) / 140.0, 0, 1);
+ return Math.clamp ((log2 (cutoffInHertz / (2 * 440.0)) * 12.0 + 57) / 140.0, 0, 1);
}
@@ -159,7 +131,7 @@ public static double normalizeCutoff (final double cutoffInHertz)
*/
public static double denormalizeCutoff (final double normalizedValue)
{
- return MathUtils.clamp (2.0 * 440.0 * Math.pow (2, (normalizedValue * 140.0 - 57.0) / 12.0), 32.7, 106300);
+ return Math.clamp (2.0 * 440.0 * Math.pow (2, (normalizedValue * 140.0 - 57.0) / 12.0), 32.7, 106300);
}
@@ -186,7 +158,7 @@ public static double normalize (final double value, final double maximum)
*/
public static double normalize (final double value, final double minimum, final double maximum)
{
- return clamp (value, minimum, maximum) / maximum;
+ return Math.clamp (value, minimum, maximum) / maximum;
}
@@ -200,7 +172,7 @@ public static double normalize (final double value, final double minimum, final
*/
public static double denormalize (final double value, final double minimum, final double maximum)
{
- return clamp (value * maximum, minimum, maximum);
+ return Math.clamp (value * maximum, minimum, maximum);
}
@@ -267,7 +239,7 @@ public static int normalizeTimeAsInt (final double value, final double maxValue)
public static double normalizeTime (final double value, final double maxValue)
{
// value is negative if not set but 0 is fine then!
- final double clamped = MathUtils.clamp (value, 0, maxValue);
+ final double clamped = Math.clamp (value, 0, maxValue);
return Math.log (clamped + 1) / Math.log (maxValue + 1);
}
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 569b96d..4651b56 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/core/creator/AbstractCreator.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/core/creator/AbstractCreator.java
@@ -33,7 +33,6 @@
import de.mossgrabers.convertwithmoss.core.AbstractCoreTask;
import de.mossgrabers.convertwithmoss.core.IMultisampleSource;
import de.mossgrabers.convertwithmoss.core.INotifier;
-import de.mossgrabers.convertwithmoss.core.MathUtils;
import de.mossgrabers.convertwithmoss.core.model.IGroup;
import de.mossgrabers.convertwithmoss.core.model.IMetadata;
import de.mossgrabers.convertwithmoss.core.model.ISampleData;
@@ -650,7 +649,7 @@ private void rewriteFile (final IMetadata metadata, final ISampleZone zone, fina
// Update information chunks
if (this.isUpdateBroadcastAudioChunk ())
updateBroadcastAudioChunk (metadata, wavFile);
- final int unityNote = MathUtils.clamp (zone.getKeyRoot (), 0, 127);
+ final int unityNote = Math.clamp (zone.getKeyRoot (), 0, 127);
if (this.isUpdateInstrumentChunk ())
updateInstrumentChunk (zone, wavFile, unityNote);
if (this.isUpdateSampleChunk ())
@@ -665,7 +664,7 @@ private void rewriteFile (final IMetadata metadata, final ISampleZone zone, fina
/**
* Trims the data of the wave file to the part from the zone start and zone end. The zone is
* updated accordingly.
- *
+ *
* @param wavFile The WAV file to trim
* @param zone The zone
*/
@@ -746,12 +745,12 @@ private static void updateInstrumentChunk (final ISampleZone zone, final WaveFil
}
instrumentChunk.setUnshiftedNote (unityNote);
- instrumentChunk.setFineTune (MathUtils.clamp ((int) (zone.getTune () * 100), -50, 50));
- instrumentChunk.setGain (MathUtils.clamp ((int) zone.getGain (), -127, 127));
- instrumentChunk.setLowNote (MathUtils.clamp (zone.getKeyLow (), 0, 127));
- instrumentChunk.setHighNote (MathUtils.clamp (limitToDefault (zone.getKeyHigh (), 127), 0, 127));
- instrumentChunk.setLowVelocity (MathUtils.clamp (zone.getVelocityLow (), 0, 127));
- instrumentChunk.setHighVelocity (MathUtils.clamp (limitToDefault (zone.getVelocityHigh (), 127), 0, 127));
+ instrumentChunk.setFineTune (Math.clamp ((int) (zone.getTune () * 100), -50, 50));
+ instrumentChunk.setGain (Math.clamp ((int) zone.getGain (), -127, 127));
+ instrumentChunk.setLowNote (Math.clamp (zone.getKeyLow (), 0, 127));
+ instrumentChunk.setHighNote (Math.clamp (limitToDefault (zone.getKeyHigh (), 127), 0, 127));
+ instrumentChunk.setLowVelocity (Math.clamp (zone.getVelocityLow (), 0, 127));
+ instrumentChunk.setHighVelocity (Math.clamp (limitToDefault (zone.getVelocityHigh (), 127), 0, 127));
}
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 f1d4c4c..64675e7 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/core/detector/AbstractDetectorTask.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/core/detector/AbstractDetectorTask.java
@@ -30,7 +30,6 @@
import de.mossgrabers.convertwithmoss.core.IMultisampleSource;
import de.mossgrabers.convertwithmoss.core.INotifier;
-import de.mossgrabers.convertwithmoss.core.MathUtils;
import de.mossgrabers.convertwithmoss.core.model.IFileBasedSampleData;
import de.mossgrabers.convertwithmoss.core.model.IGroup;
import de.mossgrabers.convertwithmoss.core.model.IMetadata;
@@ -348,7 +347,7 @@ protected String loadTextFile (final File file) throws IOException
*/
protected static double denormalizeValue (final double value, final double minimum, final double maximum)
{
- return minimum + MathUtils.clamp (value, 0, 1) * (maximum - minimum);
+ return minimum + Math.clamp (value, 0, 1) * (maximum - minimum);
}
@@ -558,7 +557,7 @@ protected File findSampleFile (final File folder, final File previousFolder, fin
}
// ... and search recursively...
- final File found = findSampleFileRecursively (startDirectory, sampleFile.getName ());
+ final File found = this.findSampleFileRecursively (startDirectory, sampleFile.getName ());
// Returning the original file triggers the expected error...
if (found == null)
return sampleFile;
@@ -600,7 +599,7 @@ private File findSampleFileRecursively (final File folder, final String fileName
if (children != null)
for (final File subFolder: children)
{
- sampleFile = findSampleFileRecursively (subFolder, fileName);
+ sampleFile = this.findSampleFileRecursively (subFolder, fileName);
if (sampleFile != null)
return sampleFile;
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/core/model/implementation/DefaultFilter.java b/src/main/java/de/mossgrabers/convertwithmoss/core/model/implementation/DefaultFilter.java
index 31a9058..ecc594b 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/core/model/implementation/DefaultFilter.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/core/model/implementation/DefaultFilter.java
@@ -22,8 +22,8 @@ public class DefaultFilter implements IFilter
protected double cutoff;
protected double resonance;
protected int envelopeDepth;
- protected IEnvelopeModulator cutoffEnvelopeModulator = new DefaultEnvelopeModulator (1);
- protected IModulator cutoffVelocityModulator = new DefaultModulator (1);
+ protected IEnvelopeModulator cutoffEnvelopeModulator = new DefaultEnvelopeModulator (0);
+ protected IModulator cutoffVelocityModulator = new DefaultModulator (0);
/**
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/core/model/implementation/DefaultModulator.java b/src/main/java/de/mossgrabers/convertwithmoss/core/model/implementation/DefaultModulator.java
index f0bf13c..a761d35 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/core/model/implementation/DefaultModulator.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/core/model/implementation/DefaultModulator.java
@@ -4,7 +4,6 @@
package de.mossgrabers.convertwithmoss.core.model.implementation;
-import de.mossgrabers.convertwithmoss.core.MathUtils;
import de.mossgrabers.convertwithmoss.core.model.IModulator;
@@ -25,7 +24,7 @@ public class DefaultModulator implements IModulator
*/
public DefaultModulator (final double depth)
{
- this.depth = MathUtils.clamp (depth, -1, 1);
+ this.depth = Math.clamp (depth, -1, 1);
}
@@ -41,7 +40,7 @@ public double getDepth ()
@Override
public void setDepth (final double depth)
{
- this.depth = MathUtils.clamp (depth, -1.0, 1.0);
+ this.depth = Math.clamp (depth, -1.0, 1.0);
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/core/model/implementation/DefaultSampleLoop.java b/src/main/java/de/mossgrabers/convertwithmoss/core/model/implementation/DefaultSampleLoop.java
index 953f3a4..2afb29c 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/core/model/implementation/DefaultSampleLoop.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/core/model/implementation/DefaultSampleLoop.java
@@ -4,7 +4,6 @@
package de.mossgrabers.convertwithmoss.core.model.implementation;
-import de.mossgrabers.convertwithmoss.core.MathUtils;
import de.mossgrabers.convertwithmoss.core.model.ISampleLoop;
import de.mossgrabers.convertwithmoss.core.model.enumeration.LoopType;
@@ -101,7 +100,7 @@ public int getCrossfadeInSamples ()
@Override
public void setCrossfade (final double crossfade)
{
- this.crossfade = MathUtils.clamp (crossfade, 0.0, 1.0);
+ this.crossfade = Math.clamp (crossfade, 0.0, 1.0);
}
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 89c22d5..8e9547b 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
@@ -8,7 +8,6 @@
import java.util.List;
import java.util.Optional;
-import de.mossgrabers.convertwithmoss.core.MathUtils;
import de.mossgrabers.convertwithmoss.core.model.IEnvelopeModulator;
import de.mossgrabers.convertwithmoss.core.model.IFilter;
import de.mossgrabers.convertwithmoss.core.model.IModulator;
@@ -340,7 +339,7 @@ public void setVelocityCrossfadeHigh (final int crossfadeHigh)
@Override
public void setGain (final double gain)
{
- this.gain = MathUtils.clamp (gain, -12.0, 12.0);
+ this.gain = Math.clamp (gain, -12.0, 12.0);
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/file/AudioFileUtils.java b/src/main/java/de/mossgrabers/convertwithmoss/file/AudioFileUtils.java
index 8b99bd3..4f1ddba 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/file/AudioFileUtils.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/file/AudioFileUtils.java
@@ -276,12 +276,10 @@ public static WaveFile convertToWav (final ISampleData sampleData, final Destina
// AudioSystem handles 32bit float values incorrect. We need our own implementation.
if (is32BitFloat)
- {
try (AudioInputStream convertedAudioInputStream = convertAudioStreamFrom32BitFloatTo16BitPCM (audioInputStream, audioFormat, newAudioFormat))
{
return doConvertToWav (convertedAudioInputStream, newAudioFormat);
}
- }
return doConvertToWav (audioInputStream, newAudioFormat);
}
@@ -303,7 +301,7 @@ private static AudioInputStream convertAudioStreamFrom32BitFloatTo16BitPCM (fina
for (int i = 0; i < sourceData.length; i += 4)
{
- float floatValue = inputBuffer.getFloat (i);
+ final float floatValue = inputBuffer.getFloat (i);
// Convert float to 16-bit PCM
outputBuffer.putShort ((short) (floatValue * Short.MAX_VALUE));
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/file/sf2/Sf2Modulator.java b/src/main/java/de/mossgrabers/convertwithmoss/file/sf2/Sf2Modulator.java
index 8e43137..eb4f25f 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/file/sf2/Sf2Modulator.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/file/sf2/Sf2Modulator.java
@@ -15,6 +15,8 @@
*/
public class Sf2Modulator
{
+ /** The ID for a Velocity modulator. */
+ public static final Integer MODULATOR_VELOCITY = Integer.valueOf (2);
/** The ID for a Pitch Bend modulator. */
public static final Integer MODULATOR_PITCH_BEND = Integer.valueOf (14);
@@ -30,7 +32,7 @@ public class Sf2Modulator
* The controller source to be used is the velocity value which is sent from the MIDI
* note-on command which generated the given sound.
*/
- MODULATOR_NAMES.put (Integer.valueOf (2), "Note-On Velocity");
+ MODULATOR_NAMES.put (MODULATOR_VELOCITY, "Note-On Velocity");
/**
* The controller source to be used is the key number value which was sent from the MIDI
* note-on command which generated the given sound.
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/file/wav/FormatChunk.java b/src/main/java/de/mossgrabers/convertwithmoss/file/wav/FormatChunk.java
index 667f438..e097be1 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/file/wav/FormatChunk.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/file/wav/FormatChunk.java
@@ -302,7 +302,7 @@ public void setSignificantBitsPerSample (final int bitsPerSample)
*/
public int calculateLength (final byte [] data)
{
- return data.length / calculateBytesPerSample ();
+ return data.length / this.calculateBytesPerSample ();
}
@@ -314,14 +314,14 @@ public int calculateLength (final byte [] data)
*/
public int calculateDataSize (final int lengthInSamples)
{
- return lengthInSamples * calculateBytesPerSample ();
+ return lengthInSamples * this.calculateBytesPerSample ();
}
/**
* Calculate the number of bytes which are used for one sample depending on the significant bite
* per sample and the number of channels.
- *
+ *
* @return The number of bytes
*/
public int calculateBytesPerSample ()
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/file/wav/SampleChunk.java b/src/main/java/de/mossgrabers/convertwithmoss/file/wav/SampleChunk.java
index cc54169..184a720 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/file/wav/SampleChunk.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/file/wav/SampleChunk.java
@@ -150,7 +150,7 @@ public int getMIDIUnityNoteRaw ()
public int getMIDIUnityNote ()
{
final int unityNote = this.getFourBytesAsInt (0x0C);
- return getMIDIPitchFractionAsCents () < 0 ? unityNote + 1 : unityNote;
+ return this.getMIDIPitchFractionAsCents () < 0 ? unityNote + 1 : unityNote;
}
@@ -192,7 +192,7 @@ public long getMIDIPitchFraction ()
*/
public int getMIDIPitchFractionAsCents ()
{
- long midiPitchFraction = this.getMIDIPitchFraction ();
+ final long midiPitchFraction = this.getMIDIPitchFraction ();
final int value = (int) Math.round (midiPitchFraction * 50.0 / 0x80000000L);
return value > 50 ? value - 100 : value;
}
@@ -201,7 +201,7 @@ public int getMIDIPitchFractionAsCents ()
/**
* Sets the unity note and the pitch fraction. If the cents are negative the unity note and
* fraction are adapted accordingly.
- *
+ *
* @param unityNote The unity note to set
* @param cent The pitch adjustment in cents
*/
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/file/wav/WaveFile.java b/src/main/java/de/mossgrabers/convertwithmoss/file/wav/WaveFile.java
index 39b1118..cfb399f 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/file/wav/WaveFile.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/file/wav/WaveFile.java
@@ -132,7 +132,7 @@ public void setBroadcastAudioExtensionChunk (final BroadcastAudioExtensionChunk
{
this.broadcastAudioExtensionChunk = broadcastAudioExtensionChunk;
this.chunkStack.clear ();
- fillChunkStack ();
+ this.fillChunkStack ();
}
@@ -156,7 +156,7 @@ public void setInstrumentChunk (final InstrumentChunk instrumentChunk)
{
this.instrumentChunk = instrumentChunk;
this.chunkStack.clear ();
- fillChunkStack ();
+ this.fillChunkStack ();
}
@@ -180,7 +180,7 @@ public void setSampleChunk (final SampleChunk sampleChunk)
{
this.sampleChunk = sampleChunk;
this.chunkStack.clear ();
- fillChunkStack ();
+ this.fillChunkStack ();
}
@@ -204,7 +204,7 @@ public void setDataChunk (final DataChunk dataChunk)
{
this.dataChunk = dataChunk;
this.chunkStack.clear ();
- fillChunkStack ();
+ this.fillChunkStack ();
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/ableton/AbletonCreator.java b/src/main/java/de/mossgrabers/convertwithmoss/format/ableton/AbletonCreator.java
index 3f42a13..a4f8798 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/ableton/AbletonCreator.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/ableton/AbletonCreator.java
@@ -18,7 +18,6 @@
import de.mossgrabers.convertwithmoss.core.IMultisampleSource;
import de.mossgrabers.convertwithmoss.core.INotifier;
-import de.mossgrabers.convertwithmoss.core.MathUtils;
import de.mossgrabers.convertwithmoss.core.creator.AbstractCreator;
import de.mossgrabers.convertwithmoss.core.model.IAudioMetadata;
import de.mossgrabers.convertwithmoss.core.model.IEnvelope;
@@ -335,7 +334,7 @@ else if (cents < -50)
cents += 100;
}
- zoneContent = zoneContent.replace ("%ROOT_KEY%", Integer.toString (MathUtils.clamp (limitToDefault (zone.getKeyRoot (), keyLow) - semitones, 0, 127)));
+ zoneContent = zoneContent.replace ("%ROOT_KEY%", Integer.toString (Math.clamp (limitToDefault (zone.getKeyRoot (), keyLow) - semitones, 0, 127)));
zoneContent = zoneContent.replace ("%DETUNE%", Integer.toString (cents));
zoneContent = zoneContent.replace ("%TUNE_SCALE%", Integer.toString ((int) (zone.getKeyTracking () * 100)));
zoneContent = zoneContent.replace ("%PANORAMA%", formatDouble (zone.getPanorama ()));
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/ableton/AbletonDetectorTask.java b/src/main/java/de/mossgrabers/convertwithmoss/format/ableton/AbletonDetectorTask.java
index 111dbca..a703bb0 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/ableton/AbletonDetectorTask.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/ableton/AbletonDetectorTask.java
@@ -27,7 +27,6 @@
import de.mossgrabers.convertwithmoss.core.IMultisampleSource;
import de.mossgrabers.convertwithmoss.core.INotifier;
-import de.mossgrabers.convertwithmoss.core.MathUtils;
import de.mossgrabers.convertwithmoss.core.detector.AbstractDetectorTask;
import de.mossgrabers.convertwithmoss.core.detector.DefaultMultisampleSource;
import de.mossgrabers.convertwithmoss.core.model.IEnvelope;
@@ -322,7 +321,7 @@ private static void readZone (final ISampleZone zone, final Element multiSampleP
zone.setKeyRoot (AbletonDetectorTask.getIntegerValueAttribute (multiSamplePartElement, AbletonTag.TAG_ROOT_KEY, 60));
zone.setTune (AbletonDetectorTask.getIntegerValueAttribute (multiSamplePartElement, AbletonTag.TAG_DETUNE, 0) / 100.0);
- zone.setKeyTracking (MathUtils.clamp (AbletonDetectorTask.getIntegerValueAttribute (multiSamplePartElement, AbletonTag.TAG_TUNE_SCALE, 0) / 100.0, 0, 1));
+ zone.setKeyTracking (Math.clamp (AbletonDetectorTask.getIntegerValueAttribute (multiSamplePartElement, AbletonTag.TAG_TUNE_SCALE, 0) / 100.0, 0, 1));
zone.setPanorama (AbletonDetectorTask.getDoubleValueAttribute (multiSamplePartElement, AbletonTag.TAG_PANORAMA, 0));
final double volumeVal = AbletonDetectorTask.getDoubleValueAttribute (multiSamplePartElement, AbletonTag.TAG_VOLUME, 1);
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/akai/MPCKeygroupCreator.java b/src/main/java/de/mossgrabers/convertwithmoss/format/akai/MPCKeygroupCreator.java
index ef8e8a6..09a462c 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/akai/MPCKeygroupCreator.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/akai/MPCKeygroupCreator.java
@@ -226,7 +226,7 @@ private static Element createLayerElement (final Document document, final int la
XMLUtils.addTextElement (document, layerElement, MPCKeygroupTag.LAYER_ACTIVE, MPCKeygroupTag.TRUE);
XMLUtils.addTextElement (document, layerElement, MPCKeygroupTag.LAYER_VOLUME, Double.toString (convertGain (zone.getGain ())));
- final double pan = (MathUtils.clamp (zone.getPanorama (), -1.0d, 1.0d) + 1.0d) / 2.0d;
+ final double pan = (Math.clamp (zone.getPanorama (), -1.0d, 1.0d) + 1.0d) / 2.0d;
XMLUtils.addTextElement (document, layerElement, MPCKeygroupTag.LAYER_PAN, String.format (Locale.US, "%.6f", Double.valueOf (pan)));
final double tuneCent = zone.getTune ();
@@ -334,6 +334,9 @@ private static Optional getKeygroup (final Map>
instrumentElement.setAttribute ("number", Integer.toString (calcInstrumentNumber (keygroupsMap)));
instrumentsElement.appendChild (instrumentElement);
+ /////////////////////////////////////////////////////////////
+ // Filter
+
final Optional optFilter = zone.getFilter ();
if (optFilter.isPresent ())
{
@@ -359,12 +362,26 @@ private static Optional getKeygroup (final Map>
setEnvelopeCurveAttribute (document, instrumentElement, MPCKeygroupTag.INSTRUMENT_FILTER_DECAY_CURVE, filterEnvelope.getDecaySlope ());
setEnvelopeCurveAttribute (document, instrumentElement, MPCKeygroupTag.INSTRUMENT_FILTER_RELEASE_CURVE, filterEnvelope.getReleaseSlope ());
}
+
+ final double filterCutoffVelocityAmount = filter.getCutoffVelocityModulator ().getDepth ();
+ if (filterCutoffVelocityAmount > 0)
+ XMLUtils.addTextElement (document, instrumentElement, MPCKeygroupTag.INSTRUMENT_VELOCITY_TO_FILTER_AMOUNT, formatDouble (filterCutoffVelocityAmount, 2));
}
+ /////////////////////////////////////////////////////////////
+ // Range
+
XMLUtils.addTextElement (document, instrumentElement, MPCKeygroupTag.INSTRUMENT_LOW_NOTE, Integer.toString (keyLow));
XMLUtils.addTextElement (document, instrumentElement, MPCKeygroupTag.INSTRUMENT_HIGH_NOTE, Integer.toString (keyHigh));
XMLUtils.addTextElement (document, instrumentElement, MPCKeygroupTag.INSTRUMENT_IGNORE_BASE_NOTE, zone.getKeyTracking () == 0 ? "True" : "False");
+ /////////////////////////////////////////////////////////////
+ // Amplitude
+
+ final double ampVelocityAmount = zone.getAmplitudeVelocityModulator ().getDepth ();
+ if (ampVelocityAmount > 0)
+ XMLUtils.addTextElement (document, instrumentElement, MPCKeygroupTag.INSTRUMENT_VELOCITY_TO_AMP_AMOUNT, formatDouble (ampVelocityAmount, 2));
+
final IEnvelope amplitudeEnvelope = zone.getAmplitudeEnvelopeModulator ().getSource ();
setEnvelopeAttribute (document, instrumentElement, MPCKeygroupTag.INSTRUMENT_VOLUME_ATTACK, amplitudeEnvelope.getAttackTime (), MPCKeygroupConstants.MIN_ENV_TIME_SECONDS, MPCKeygroupConstants.MAX_ENV_TIME_SECONDS, MPCKeygroupConstants.DEFAULT_ATTACK_TIME, true);
setEnvelopeAttribute (document, instrumentElement, MPCKeygroupTag.INSTRUMENT_VOLUME_HOLD, amplitudeEnvelope.getHoldTime (), MPCKeygroupConstants.MIN_ENV_TIME_SECONDS, MPCKeygroupConstants.MAX_ENV_TIME_SECONDS, MPCKeygroupConstants.DEFAULT_HOLD_TIME, true);
@@ -375,6 +392,9 @@ private static Optional getKeygroup (final Map>
setEnvelopeCurveAttribute (document, instrumentElement, MPCKeygroupTag.INSTRUMENT_VOLUME_DECAY_CURVE, amplitudeEnvelope.getDecaySlope ());
setEnvelopeCurveAttribute (document, instrumentElement, MPCKeygroupTag.INSTRUMENT_VOLUME_RELEASE_CURVE, amplitudeEnvelope.getReleaseSlope ());
+ /////////////////////////////////////////////////////////////
+ // Pitch
+
final IEnvelopeModulator pitchModulator = zone.getPitchModulator ();
final double pitchDepth = pitchModulator.getDepth ();
// Only positive modulation values are supported with MPC
@@ -458,7 +478,7 @@ private static double convertGain (final double volumeDB)
*/
private static double normalizeLogarithmicEnvTimeValue (final double value, final double minimum, final double maximum)
{
- return Math.log (MathUtils.clamp (value, minimum, maximum) / minimum) / Math.log (maximum / minimum);
+ return Math.log (Math.clamp (value, minimum, maximum) / minimum) / Math.log (maximum / minimum);
}
@@ -478,7 +498,7 @@ private static void setEnvelopeAttribute (final Document document, final Element
private static void setEnvelopeCurveAttribute (final Document document, final Element element, final String curveTag, final double slopeValue)
{
- final double value = MathUtils.clamp ((slopeValue + 1.0) / 2.0, 0, 1);
+ final double value = Math.clamp ((slopeValue + 1.0) / 2.0, 0, 1);
XMLUtils.addTextElement (document, element, curveTag, String.format (Locale.US, "%.6f", Double.valueOf (value)));
}
}
\ No newline at end of file
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/akai/MPCKeygroupDetectorTask.java b/src/main/java/de/mossgrabers/convertwithmoss/format/akai/MPCKeygroupDetectorTask.java
index c11a312..677c63e 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/akai/MPCKeygroupDetectorTask.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/akai/MPCKeygroupDetectorTask.java
@@ -22,13 +22,12 @@
import de.mossgrabers.convertwithmoss.core.IMultisampleSource;
import de.mossgrabers.convertwithmoss.core.INotifier;
-import de.mossgrabers.convertwithmoss.core.MathUtils;
import de.mossgrabers.convertwithmoss.core.detector.AbstractDetectorTask;
import de.mossgrabers.convertwithmoss.core.detector.DefaultMultisampleSource;
import de.mossgrabers.convertwithmoss.core.model.IEnvelope;
+import de.mossgrabers.convertwithmoss.core.model.IEnvelopeModulator;
import de.mossgrabers.convertwithmoss.core.model.IFilter;
import de.mossgrabers.convertwithmoss.core.model.IGroup;
-import de.mossgrabers.convertwithmoss.core.model.IEnvelopeModulator;
import de.mossgrabers.convertwithmoss.core.model.ISampleData;
import de.mossgrabers.convertwithmoss.core.model.ISampleZone;
import de.mossgrabers.convertwithmoss.core.model.enumeration.PlayLogic;
@@ -268,30 +267,40 @@ private List parseGroups (final File basePath, final int numKeygroups, f
final int velStart = XMLUtils.getChildElementIntegerContent (layerElement, MPCKeygroupTag.LAYER_VEL_START, 0);
final int velEnd = XMLUtils.getChildElementIntegerContent (layerElement, MPCKeygroupTag.LAYER_VEL_END, 0);
- final DefaultSampleZone sampleMetadata = this.parseSampleData (layerElement, basePath, keyLow, keyHigh, velStart, velEnd, zonePlay, ignoreBaseNote, triggerType);
- if (sampleMetadata == null)
+ final DefaultSampleZone zone = this.parseSampleData (layerElement, basePath, keyLow, keyHigh, velStart, velEnd, zonePlay, ignoreBaseNote, triggerType);
+ if (zone == null)
continue;
- samples.add (sampleMetadata);
+ samples.add (zone);
- final IEnvelopeModulator amplitudeModulator = sampleMetadata.getAmplitudeEnvelopeModulator ();
+ /////////////////////////////////////////////////////////////
+ // Amplitude
+
+ final IEnvelopeModulator amplitudeModulator = zone.getAmplitudeEnvelopeModulator ();
amplitudeModulator.setDepth (1.0);
amplitudeModulator.getSource ().set (volumeEnvelope);
+ final double ampVelocityAmount = XMLUtils.getChildElementDoubleContent (instrumentElement, MPCKeygroupTag.INSTRUMENT_VELOCITY_TO_AMP_AMOUNT, 0);
+ if (ampVelocityAmount > 0)
+ zone.getAmplitudeVelocityModulator ().setDepth (ampVelocityAmount);
+
+ /////////////////////////////////////////////////////////////
+ // Pitch
+
final double pitchEnvAmount = XMLUtils.getChildElementDoubleContent (instrumentElement, MPCKeygroupTag.INSTRUMENT_PITCH_ENV_AMOUNT, 0.5);
if (pitchEnvAmount != 0.5)
{
- final IEnvelopeModulator pitchModulator = sampleMetadata.getPitchModulator ();
+ final IEnvelopeModulator pitchModulator = zone.getPitchModulator ();
pitchModulator.setDepth ((pitchEnvAmount - 0.5) * 2.0);
pitchModulator.getSource ().set (pitchEnvelope);
}
// No loop if it is a one-shot
if (!isOneShot)
- parseLoop (layerElement, sampleMetadata);
+ parseLoop (layerElement, zone);
- sampleMetadata.setFilter (filter);
+ zone.setFilter (filter);
- this.readMissingData (isDrum, isOneShot, sampleMetadata);
+ this.readMissingData (isDrum, isOneShot, zone);
}
}
@@ -323,6 +332,11 @@ private static IFilter parseFilter (final Element instrumentElement)
cutoffModulator.setDepth (filterAmount);
cutoffModulator.getSource ().set (parseEnvelope (instrumentElement, MPCKeygroupTag.INSTRUMENT_FILTER_ATTACK, MPCKeygroupTag.INSTRUMENT_FILTER_HOLD, MPCKeygroupTag.INSTRUMENT_FILTER_DECAY, MPCKeygroupTag.INSTRUMENT_FILTER_SUSTAIN, MPCKeygroupTag.INSTRUMENT_FILTER_RELEASE, MPCKeygroupTag.INSTRUMENT_FILTER_ATTACK_CURVE, MPCKeygroupTag.INSTRUMENT_FILTER_DECAY_CURVE, MPCKeygroupTag.INSTRUMENT_FILTER_RELEASE_CURVE));
}
+
+ final double filterCutoffVelocityAmount = XMLUtils.getChildElementDoubleContent (instrumentElement, MPCKeygroupTag.INSTRUMENT_VELOCITY_TO_FILTER_AMOUNT, 0);
+ if (filterCutoffVelocityAmount > 0)
+ filter.getCutoffVelocityModulator ().setDepth (filterCutoffVelocityAmount);
+
return filter;
}
@@ -390,7 +404,7 @@ private DefaultSampleZone parseSampleData (final Element layerElement, final Fil
final String panStr = XMLUtils.getChildElementContent (layerElement, MPCKeygroupTag.LAYER_PAN);
if (panStr != null && !panStr.isBlank ())
- sampleMetadata.setPanorama (MathUtils.clamp (Double.parseDouble (panStr) * 2.0d - 1.0d, -1.0d, 1.0d));
+ sampleMetadata.setPanorama (Math.clamp (Double.parseDouble (panStr) * 2.0d - 1.0d, -1.0d, 1.0d));
final String pitchStr = XMLUtils.getChildElementContent (layerElement, MPCKeygroupTag.LAYER_PITCH);
if (pitchStr != null && !pitchStr.isBlank ())
@@ -576,9 +590,9 @@ private static IEnvelope parseEnvelope (final Element element, final String atta
envelope.setSustainLevel (getEnvelopeAttribute (element, sustainTag, 0, 1, 1, false));
- envelope.setAttackSlope (MathUtils.clamp (XMLUtils.getChildElementDoubleContent (element, attackCurveTag, 0.5) * 2.0 - 1.0, -1.0, 1.0));
- envelope.setDecaySlope (MathUtils.clamp (XMLUtils.getChildElementDoubleContent (element, decayCurveTag, 0.5) * 2.0 - 1.0, -1.0, 1.0));
- envelope.setReleaseSlope (MathUtils.clamp (XMLUtils.getChildElementDoubleContent (element, releaseCurveTag, 0.5) * 2.0 - 1.0, -1.0, 1.0));
+ envelope.setAttackSlope (Math.clamp (XMLUtils.getChildElementDoubleContent (element, attackCurveTag, 0.5) * 2.0 - 1.0, -1.0, 1.0));
+ envelope.setDecaySlope (Math.clamp (XMLUtils.getChildElementDoubleContent (element, decayCurveTag, 0.5) * 2.0 - 1.0, -1.0, 1.0));
+ envelope.setReleaseSlope (Math.clamp (XMLUtils.getChildElementDoubleContent (element, releaseCurveTag, 0.5) * 2.0 - 1.0, -1.0, 1.0));
return envelope;
}
@@ -595,6 +609,6 @@ private static double getEnvelopeAttribute (final Element element, final String
private static double denormalizeLogarithmicEnvTimeValue (final double value, final double minimum, final double maximum)
{
- return minimum * Math.exp (MathUtils.clamp (value, 0, 1) * Math.log (maximum / minimum));
+ return minimum * Math.exp (Math.clamp (value, 0, 1) * Math.log (maximum / minimum));
}
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/akai/MPCKeygroupTag.java b/src/main/java/de/mossgrabers/convertwithmoss/format/akai/MPCKeygroupTag.java
index da26586..939af1a 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/akai/MPCKeygroupTag.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/akai/MPCKeygroupTag.java
@@ -15,206 +15,211 @@ public class MPCKeygroupTag
// Elements
/** The root element. */
- public static final String ROOT = "MPCVObject";
+ public static final String ROOT = "MPCVObject";
/** The program element. */
- public static final String ROOT_PROGRAM = "Program";
+ public static final String ROOT_PROGRAM = "Program";
/** The version element. */
- public static final String ROOT_VERSION = "Version";
+ public static final String ROOT_VERSION = "Version";
/** The file version element. */
- public static final String VERSION_FILE_VERSION = "File_Version";
+ public static final String VERSION_FILE_VERSION = "File_Version";
/** The program name element. */
- public static final String PROGRAM_NAME = "ProgramName";
+ public static final String PROGRAM_NAME = "ProgramName";
/** The program instrument element. */
- public static final String PROGRAM_INSTRUMENTS = "Instruments";
+ public static final String PROGRAM_INSTRUMENTS = "Instruments";
/** The program pad note map element. */
- public static final String PROGRAM_PAD_NOTE_MAP = "PadNoteMap";
+ public static final String PROGRAM_PAD_NOTE_MAP = "PadNoteMap";
/** The program number of keygroups element. */
- public static final String PROGRAM_NUM_KEYGROUPS = "KeygroupNumKeygroups";
+ public static final String PROGRAM_NUM_KEYGROUPS = "KeygroupNumKeygroups";
/** The program pads setup element. */
- public static final String PROGRAM_PADS = "ProgramPads-";
+ public static final String PROGRAM_PADS = "ProgramPads-";
/** The program keygroup pitch bend range element. */
- public static final String PROGRAM_PITCHBEND_RANGE = "KeygroupPitchBendRange";
+ public static final String PROGRAM_PITCHBEND_RANGE = "KeygroupPitchBendRange";
/** The program keygroup wheel to LFO element. */
- public static final String PROGRAM_WHEEL_TO_LFO = "KeygroupWheelToLfo";
+ public static final String PROGRAM_WHEEL_TO_LFO = "KeygroupWheelToLfo";
/** The instruments instrument element. */
- public static final String INSTRUMENTS_INSTRUMENT = "Instrument";
+ public static final String INSTRUMENTS_INSTRUMENT = "Instrument";
/** The low note element of the instrument element. */
- public static final String INSTRUMENT_LOW_NOTE = "LowNote";
+ public static final String INSTRUMENT_LOW_NOTE = "LowNote";
/** The high note element of the instrument element. */
- public static final String INSTRUMENT_HIGH_NOTE = "HighNote";
+ public static final String INSTRUMENT_HIGH_NOTE = "HighNote";
/** The ignore base note element of the instrument element. */
- public static final String INSTRUMENT_IGNORE_BASE_NOTE = "IgnoreBaseNote";
+ public static final String INSTRUMENT_IGNORE_BASE_NOTE = "IgnoreBaseNote";
/** The zone play element of the instrument element. */
- public static final String INSTRUMENT_ZONE_PLAY = "ZonePlay";
+ public static final String INSTRUMENT_ZONE_PLAY = "ZonePlay";
/** The trigger mode element of the instrument element. */
- public static final String INSTRUMENT_TRIGGER_MODE = "TriggerMode";
+ public static final String INSTRUMENT_TRIGGER_MODE = "TriggerMode";
/** The one-shot element of the instrument element. */
- public static final String INSTRUMENT_ONE_SHOT = "OneShot";
+ public static final String INSTRUMENT_ONE_SHOT = "OneShot";
/** The layers element of the instrument element. */
- public static final String INSTRUMENT_LAYERS = "Layers";
+ public static final String INSTRUMENT_LAYERS = "Layers";
/** The filter type element of the instrument element. */
- public static final String INSTRUMENT_FILTER_TYPE = "FilterType";
+ public static final String INSTRUMENT_FILTER_TYPE = "FilterType";
/** The filter cutoff element of the instrument element. */
- public static final String INSTRUMENT_FILTER_CUTOFF = "Cutoff";
+ public static final String INSTRUMENT_FILTER_CUTOFF = "Cutoff";
/** The filter resonance element of the instrument element. */
- public static final String INSTRUMENT_FILTER_RESONANCE = "Resonance";
+ public static final String INSTRUMENT_FILTER_RESONANCE = "Resonance";
/** The filter envelope amount element of the instrument element. */
- public static final String INSTRUMENT_FILTER_ENV_AMOUNT = "FilterEnvAmt";
+ public static final String INSTRUMENT_FILTER_ENV_AMOUNT = "FilterEnvAmt";
+ /** The cutoff velocity amount of the instrument element. */
+ public static final String INSTRUMENT_VELOCITY_TO_FILTER_AMOUNT = "VelocityToFilter";
/** The filter attack element of the instrument element. */
- public static final String INSTRUMENT_FILTER_ATTACK = "FilterAttack";
+ public static final String INSTRUMENT_FILTER_ATTACK = "FilterAttack";
/** The filter hold element of the instrument element. */
- public static final String INSTRUMENT_FILTER_HOLD = "FilterHold";
+ public static final String INSTRUMENT_FILTER_HOLD = "FilterHold";
/** The filter decay element of the instrument element. */
- public static final String INSTRUMENT_FILTER_DECAY = "FilterDecay";
+ public static final String INSTRUMENT_FILTER_DECAY = "FilterDecay";
/** The filter sustain element of the instrument element. */
- public static final String INSTRUMENT_FILTER_SUSTAIN = "FilterSustain";
+ public static final String INSTRUMENT_FILTER_SUSTAIN = "FilterSustain";
/** The filter release element of the instrument element. */
- public static final String INSTRUMENT_FILTER_RELEASE = "FilterRelease";
+ public static final String INSTRUMENT_FILTER_RELEASE = "FilterRelease";
/** The filter attack curve element of the instrument element. */
- public static final String INSTRUMENT_FILTER_ATTACK_CURVE = "FilterAttackCurve";
+ public static final String INSTRUMENT_FILTER_ATTACK_CURVE = "FilterAttackCurve";
/** The filter decay curve element of the instrument element. */
- public static final String INSTRUMENT_FILTER_DECAY_CURVE = "FilterDecayCurve";
+ public static final String INSTRUMENT_FILTER_DECAY_CURVE = "FilterDecayCurve";
/** The filter release curve element of the instrument element. */
- public static final String INSTRUMENT_FILTER_RELEASE_CURVE = "FilterReleaseCurve";
+ public static final String INSTRUMENT_FILTER_RELEASE_CURVE = "FilterReleaseCurve";
+
+ /** The amplitude velocity amount element of the instrument element. */
+ public static final String INSTRUMENT_VELOCITY_TO_AMP_AMOUNT = "VelocitySensitivity";
/** The volume attack element of the instrument element. */
- public static final String INSTRUMENT_VOLUME_ATTACK = "VolumeAttack";
+ public static final String INSTRUMENT_VOLUME_ATTACK = "VolumeAttack";
/** The volume hold element of the instrument element. */
- public static final String INSTRUMENT_VOLUME_HOLD = "VolumeHold";
+ public static final String INSTRUMENT_VOLUME_HOLD = "VolumeHold";
/** The volume decay element of the instrument element. */
- public static final String INSTRUMENT_VOLUME_DECAY = "VolumeDecay";
+ public static final String INSTRUMENT_VOLUME_DECAY = "VolumeDecay";
/** The volume sustain element of the instrument element. */
- public static final String INSTRUMENT_VOLUME_SUSTAIN = "VolumeSustain";
+ public static final String INSTRUMENT_VOLUME_SUSTAIN = "VolumeSustain";
/** The volume release element of the instrument element. */
- public static final String INSTRUMENT_VOLUME_RELEASE = "VolumeRelease";
+ public static final String INSTRUMENT_VOLUME_RELEASE = "VolumeRelease";
/** The volume attack curve element of the instrument element. */
- public static final String INSTRUMENT_VOLUME_ATTACK_CURVE = "VolumeAttackCurve";
+ public static final String INSTRUMENT_VOLUME_ATTACK_CURVE = "VolumeAttackCurve";
/** The volume decay curve element of the instrument element. */
- public static final String INSTRUMENT_VOLUME_DECAY_CURVE = "VolumeDecayCurve";
+ public static final String INSTRUMENT_VOLUME_DECAY_CURVE = "VolumeDecayCurve";
/** The volume release curve element of the instrument element. */
- public static final String INSTRUMENT_VOLUME_RELEASE_CURVE = "VolumeReleaseCurve";
+ public static final String INSTRUMENT_VOLUME_RELEASE_CURVE = "VolumeReleaseCurve";
/** The pitch attack element of the instrument element. */
- public static final String INSTRUMENT_PITCH_ATTACK = "PitchAttack";
+ public static final String INSTRUMENT_PITCH_ATTACK = "PitchAttack";
/** The pitch hold element of the instrument element. */
- public static final String INSTRUMENT_PITCH_HOLD = "PitchHold";
+ public static final String INSTRUMENT_PITCH_HOLD = "PitchHold";
/** The pitch decay element of the instrument element. */
- public static final String INSTRUMENT_PITCH_DECAY = "PitchDecay";
+ public static final String INSTRUMENT_PITCH_DECAY = "PitchDecay";
/** The pitch sustain element of the instrument element. */
- public static final String INSTRUMENT_PITCH_SUSTAIN = "PitchSustain";
+ public static final String INSTRUMENT_PITCH_SUSTAIN = "PitchSustain";
/** The pitch release element of the instrument element. */
- public static final String INSTRUMENT_PITCH_RELEASE = "PitchRelease";
+ public static final String INSTRUMENT_PITCH_RELEASE = "PitchRelease";
/** The pitch attack curve element of the instrument element. */
- public static final String INSTRUMENT_PITCH_ATTACK_CURVE = "PitchAttackCurve";
+ public static final String INSTRUMENT_PITCH_ATTACK_CURVE = "PitchAttackCurve";
/** The pitch decay curve element of the instrument element. */
- public static final String INSTRUMENT_PITCH_DECAY_CURVE = "PitchDecayCurve";
+ public static final String INSTRUMENT_PITCH_DECAY_CURVE = "PitchDecayCurve";
/** The pitch release curve element of the instrument element. */
- public static final String INSTRUMENT_PITCH_RELEASE_CURVE = "PitchReleaseCurve";
+ public static final String INSTRUMENT_PITCH_RELEASE_CURVE = "PitchReleaseCurve";
/** The pitch envelope amount element of the instrument element. */
- public static final String INSTRUMENT_PITCH_ENV_AMOUNT = "PitchEnvAmount";
+ public static final String INSTRUMENT_PITCH_ENV_AMOUNT = "PitchEnvAmount";
/** The layer element of the layers element. */
- public static final String LAYERS_LAYER = "Layer";
+ public static final String LAYERS_LAYER = "Layer";
/** The sample name element of the layer element. */
- public static final String LAYER_SAMPLE_NAME = "SampleName";
+ public static final String LAYER_SAMPLE_NAME = "SampleName";
/** The active element of the layer element. */
- public static final String LAYER_ACTIVE = "Active";
+ public static final String LAYER_ACTIVE = "Active";
/** The volume element of the layer element. */
- public static final String LAYER_VOLUME = "Volume";
+ public static final String LAYER_VOLUME = "Volume";
/** The panorama element of the layer element. */
- public static final String LAYER_PAN = "Pan";
+ public static final String LAYER_PAN = "Pan";
/** The pitch element of the layer element. */
- public static final String LAYER_PITCH = "Pitch";
+ public static final String LAYER_PITCH = "Pitch";
/** The coarse tune element of the layer element. */
- public static final String LAYER_COARSE_TUNE = "TuneCoarse";
+ public static final String LAYER_COARSE_TUNE = "TuneCoarse";
/** The fine tune element of the layer element. */
- public static final String LAYER_FINE_TUNE = "TuneFine";
+ public static final String LAYER_FINE_TUNE = "TuneFine";
/** The root note element of the layer element. */
- public static final String LAYER_ROOT_NOTE = "RootNote";
+ public static final String LAYER_ROOT_NOTE = "RootNote";
/** The key track element of the layer element. */
- public static final String LAYER_KEY_TRACK = "KeyTrack";
+ public static final String LAYER_KEY_TRACK = "KeyTrack";
/** The velocity start element of the instrument element. */
- public static final String LAYER_VEL_START = "VelStart";
+ public static final String LAYER_VEL_START = "VelStart";
/** The velocity end element of the instrument element. */
- public static final String LAYER_VEL_END = "VelEnd";
+ public static final String LAYER_VEL_END = "VelEnd";
/** The sample start element of the layer element. */
- public static final String LAYER_SAMPLE_START = "SampleStart";
+ public static final String LAYER_SAMPLE_START = "SampleStart";
/** The sample end element of the layer element. */
- public static final String LAYER_SAMPLE_END = "SampleEnd";
+ public static final String LAYER_SAMPLE_END = "SampleEnd";
/** The loop start element of the layer element. */
- public static final String LAYER_LOOP_START = "LoopStart";
+ public static final String LAYER_LOOP_START = "LoopStart";
/** The loop end element of the layer element. */
- public static final String LAYER_LOOP_END = "LoopEnd";
+ public static final String LAYER_LOOP_END = "LoopEnd";
/** The loop crossfade element of the layer element. */
- public static final String LAYER_LOOP_CROSSFADE = "LoopCrossfadeLength";
+ public static final String LAYER_LOOP_CROSSFADE = "LoopCrossfadeLength";
/** The loop tune element of the layer element. */
- public static final String LAYER_LOOP_TUNE = "LoopTune";
+ public static final String LAYER_LOOP_TUNE = "LoopTune";
/** The pitch randomization element of the layer element. */
- public static final String LAYER_PITCH_RANDOM = "PitchRandom";
+ public static final String LAYER_PITCH_RANDOM = "PitchRandom";
/** The volume randomization element of the layer element. */
- public static final String LAYER_VOLUME_RANDOM = "VolumeRandom";
+ public static final String LAYER_VOLUME_RANDOM = "VolumeRandom";
/** The panorama randomization element of the layer element. */
- public static final String LAYER_PAN_RANDOM = "PanRandom";
+ public static final String LAYER_PAN_RANDOM = "PanRandom";
/** The offset randomization element of the layer element. */
- public static final String LAYER_OFFSET_RANDOM = "OffsetRandom";
+ public static final String LAYER_OFFSET_RANDOM = "OffsetRandom";
/** The sample file element of the layer element. */
- public static final String LAYER_SAMPLE_FILE = "SampleFile";
+ public static final String LAYER_SAMPLE_FILE = "SampleFile";
/** The slice index element of the layer element. */
- public static final String LAYER_SLICE_INDEX = "SliceIndex";
+ public static final String LAYER_SLICE_INDEX = "SliceIndex";
/** The direction element of the layer element. */
- public static final String LAYER_DIRECTION = "Direction";
+ public static final String LAYER_DIRECTION = "Direction";
/** The offset element of the layer element. */
- public static final String LAYER_OFFSET = "Offset";
+ public static final String LAYER_OFFSET = "Offset";
/** The slice start element of the layer element. */
- public static final String LAYER_SLICE_START = "SliceStart";
+ public static final String LAYER_SLICE_START = "SliceStart";
/** The slice end element of the layer element. */
- public static final String LAYER_SLICE_END = "SliceEnd";
+ public static final String LAYER_SLICE_END = "SliceEnd";
/** The slice loop element of the layer element. */
- public static final String LAYER_SLICE_LOOP = "SliceLoop";
+ public static final String LAYER_SLICE_LOOP = "SliceLoop";
/** The slice loop start element of the layer element. */
- public static final String LAYER_SLICE_LOOP_START = "SliceLoopStart";
+ public static final String LAYER_SLICE_LOOP_START = "SliceLoopStart";
/** The slice loop crossfade element of the layer element. */
- public static final String LAYER_SLICE_LOOP_CROSSFADE = "SliceLoopCrossFadeLength";
+ public static final String LAYER_SLICE_LOOP_CROSSFADE = "SliceLoopCrossFadeLength";
/** The slice tail position element of the layer element. */
- public static final String LAYER_SLICE_TAIL_POSITION = "SliceTailPosition";
+ public static final String LAYER_SLICE_TAIL_POSITION = "SliceTailPosition";
/** The slice tail length element of the layer element. */
- public static final String LAYER_SLICE_TAIL_LENGTH = "SliceTailLength";
+ public static final String LAYER_SLICE_TAIL_LENGTH = "SliceTailLength";
/** The pad note element of the pad note map element. */
- public static final String PAD_NOTE_MAP_PAD_NOTE = "PadNote";
+ public static final String PAD_NOTE_MAP_PAD_NOTE = "PadNote";
/** The pad note element of the pad note map element. */
- public static final String PAD_NOTE_NOTE = "Note";
+ public static final String PAD_NOTE_NOTE = "Note";
///////////////////////////////////////////////////////
// Attributes
/** The type attribute of the program element. */
- public static final String PROGRAM_TYPE = "type";
+ public static final String PROGRAM_TYPE = "type";
/** The number attribute of the instrument element. */
- public static final String INSTRUMENT_NUMBER = "number";
+ public static final String INSTRUMENT_NUMBER = "number";
/** The number attribute of the pad note element. */
- public static final String PAD_NOTE_NUMBER = "number";
+ public static final String PAD_NOTE_NUMBER = "number";
/** The program type keygroup. */
- public static final String TYPE_KEYGROUP = "Keygroup";
+ public static final String TYPE_KEYGROUP = "Keygroup";
/** The program type drum. */
- public static final String TYPE_DRUM = "Drum";
+ public static final String TYPE_DRUM = "Drum";
/** The true value. */
- public static final String TRUE = "True";
+ public static final String TRUE = "True";
/**
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/disting/DistingExCreator.java b/src/main/java/de/mossgrabers/convertwithmoss/format/disting/DistingExCreator.java
index 4beb0ac..a7cc947 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/disting/DistingExCreator.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/disting/DistingExCreator.java
@@ -205,7 +205,7 @@ private static void storeMultisample (final IMultisampleSource multisampleSource
parameters[13] = (int) (tune * 100.0);
// Gain in the range of -40..24 dB
- parameters[14] = MathUtils.clamp ((int) Math.round (zone.getGain ()), -40, 24);
+ parameters[14] = Math.clamp ((int) Math.round (zone.getGain ()), -40, 24);
final Optional modulator = multisampleSource.getGlobalAmplitudeModulator ();
if (modulator.isPresent ())
@@ -214,9 +214,9 @@ private static void storeMultisample (final IMultisampleSource multisampleSource
if (envelopeModulator.hashCode () > 0)
{
final IEnvelope envelope = envelopeModulator.getSource ();
- parameters[7] = MathUtils.clamp ((int) Math.round (Math.log (envelope.getAttackTime () / 0.001) / 0.0757), 0, 127);
- parameters[8] = MathUtils.clamp ((int) Math.round (Math.log (envelope.getDecayTime () / 0.02) / 0.0521), 0, 127);
- parameters[10] = MathUtils.clamp ((int) Math.round (Math.log (envelope.getReleaseTime () / 0.01) / 0.0630), 0, 127);
+ parameters[7] = Math.clamp ((int) Math.round (Math.log (envelope.getAttackTime () / 0.001) / 0.0757), 0, 127);
+ parameters[8] = Math.clamp ((int) Math.round (Math.log (envelope.getDecayTime () / 0.02) / 0.0521), 0, 127);
+ parameters[10] = Math.clamp ((int) Math.round (Math.log (envelope.getReleaseTime () / 0.01) / 0.0630), 0, 127);
}
}
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/disting/DistingExDetectorTask.java b/src/main/java/de/mossgrabers/convertwithmoss/format/disting/DistingExDetectorTask.java
index 14aa462..adb4ca7 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/disting/DistingExDetectorTask.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/disting/DistingExDetectorTask.java
@@ -222,7 +222,7 @@ private static void calculateKeyAndVelocityRanges (final List groupsList
{
final IGroup group = groupsList.get (i);
final int velocityLow = 1 + i * velocitySteps;
- int velocityHigh = MathUtils.clamp ((i + 1) * velocitySteps, 1, 127);
+ int velocityHigh = Math.clamp ((i + 1) * velocitySteps, 1, 127);
// Ensure that the last step reaches till the highest velocity
if (i == size - 1)
velocityHigh = 127;
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/exs/EXS24Creator.java b/src/main/java/de/mossgrabers/convertwithmoss/format/exs/EXS24Creator.java
index ce7eb37..002cc88 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/exs/EXS24Creator.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/exs/EXS24Creator.java
@@ -206,7 +206,7 @@ private static void storeMultisample (final IMultisampleSource multisampleSource
exsParameters.put (EXS24Parameters.PITCH_BEND_DOWN, Math.abs (zone.getBendDown () / 100));
final double velocityDepth = zone.getAmplitudeVelocityModulator ().getDepth ();
- final int velocityModulation = (int) Math.round (MathUtils.clamp ((1 - velocityDepth) * -60.0, -60, 0));
+ final int velocityModulation = (int) Math.round (Math.clamp ((1 - velocityDepth) * -60.0, -60, 0));
exsParameters.put (EXS24Parameters.ENV1_VEL_SENS, velocityModulation);
createEnvelope (exsParameters, 1, zone.getAmplitudeEnvelopeModulator ());
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/exs/EXS24DetectorTask.java b/src/main/java/de/mossgrabers/convertwithmoss/format/exs/EXS24DetectorTask.java
index a053f4a..7ab250b 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/exs/EXS24DetectorTask.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/exs/EXS24DetectorTask.java
@@ -213,7 +213,7 @@ private Optional createMultisample (final File file, final M
if (exs24Zone.pitch && (exs24Zone.coarseTuning != 0 || exs24Zone.fineTuning != 0))
zone.setTune (exs24Zone.coarseTuning + exs24Zone.fineTuning / 100.0);
- zone.setPanorama (MathUtils.clamp (exs24Zone.pan, -50, 50) / 50.0);
+ zone.setPanorama (Math.clamp (exs24Zone.pan, -50, 50) / 50.0);
if (exs24Zone.loopOn)
{
@@ -286,7 +286,7 @@ private Pair createAndCheckSampleZone (final File parentFile,
}
final int height = this.levelsOfDirectorySearch.getSelectionModel ().getSelectedItem ().intValue ();
- final File sampleFile = findSampleFile (parentFile, previousFolder, exs24Sample.fileName, height);
+ final File sampleFile = this.findSampleFile (parentFile, previousFolder, exs24Sample.fileName, height);
if (!sampleFile.exists ())
{
this.notifier.logError ("IDS_NOTIFY_ERR_SAMPLE_DOES_NOT_EXIST", sampleFile.getAbsolutePath ());
@@ -316,7 +316,7 @@ private static void applyGlobalParameters (final IMultisampleSource multisampleS
final IEnvelope globalAmplitudeEnvelope = createEnvelope (parameters, 1);
final Integer env1Velocity = parameters.get (EXS24Parameters.ENV1_VEL_SENS);
- final double velocityModulation = env1Velocity == null ? 1 : 1 - MathUtils.clamp (env1Velocity.intValue () / -60.0, 0, 1);
+ final double velocityModulation = env1Velocity == null ? 1 : 1 - Math.clamp (env1Velocity.intValue () / -60.0, 0, 1);
for (final IGroup group: multisampleSource.getGroups ())
for (final ISampleZone zone: group.getSampleZones ())
@@ -408,7 +408,7 @@ private static void applyFilterParameters (final IMultisampleSource multisampleS
final int resonance = filterResonance == null ? 0 : filterResonance.intValue ();
final double cutoff = MathUtils.denormalize (frequency / 1000.0, 0, IFilter.MAX_FREQUENCY);
- final IFilter filter = new DefaultFilter (filterType, poles, cutoff, MathUtils.clamp (resonance / 1000.0, 0, 1));
+ final IFilter filter = new DefaultFilter (filterType, poles, cutoff, Math.clamp (resonance / 1000.0, 0, 1));
final IEnvelopeModulator cutoffModulator = filter.getCutoffEnvelopeModulator ();
cutoffModulator.setDepth (1.0);
cutoffModulator.setSource (globalFilterEnvelope);
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 e0105ee..e0a8989 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/kmp/KMPFile.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/kmp/KMPFile.java
@@ -15,7 +15,6 @@
import java.util.List;
import de.mossgrabers.convertwithmoss.core.INotifier;
-import de.mossgrabers.convertwithmoss.core.MathUtils;
import de.mossgrabers.convertwithmoss.core.creator.AbstractCreator;
import de.mossgrabers.convertwithmoss.core.model.IGroup;
import de.mossgrabers.convertwithmoss.core.model.ISampleZone;
@@ -349,7 +348,7 @@ private void writeParameterChunk1 (final DataOutputStream out) throws IOExceptio
out.write (keyHigh);
out.writeByte ((byte) Math.round (zone.getTune () * 100.0));
- out.writeByte ((byte) MathUtils.clamp (Math.round (MathUtils.clamp (zone.getGain (), -12, 12) / 12.0 * 100.0), -99, 99));
+ out.writeByte ((byte) Math.clamp (Math.round (Math.clamp (zone.getGain (), -12, 12) / 12.0 * 100.0), -99, 99));
// Panorama - unused in KMP itself, 64 is center
out.write (64);
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/music1010/Music1010DetectorTask.java b/src/main/java/de/mossgrabers/convertwithmoss/format/music1010/Music1010DetectorTask.java
index c36cf44..0ffbe73 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/music1010/Music1010DetectorTask.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/music1010/Music1010DetectorTask.java
@@ -442,7 +442,7 @@ private static void parseEffects (final Element paramsElement, final DefaultMult
// represents. Therefore, we assume 40dB maximum and a linear range (could also
// be logarithmic).
final int resonance = XMLUtils.getIntegerAttribute (paramsElement, Music1010Tag.ATTR_FILTER_RESONANCE, 0);
- multisampleSource.setGlobalFilter (new DefaultFilter (type, 4, cutoff, MathUtils.clamp (resonance / 1000.0, 0, 1.0)));
+ multisampleSource.setGlobalFilter (new DefaultFilter (type, 4, cutoff, Math.clamp (resonance / 1000.0, 0, 1.0)));
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/nki/AbstractNKIMetadataFileHandler.java b/src/main/java/de/mossgrabers/convertwithmoss/format/nki/AbstractNKIMetadataFileHandler.java
index 731251f..6dd851b 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/nki/AbstractNKIMetadataFileHandler.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/nki/AbstractNKIMetadataFileHandler.java
@@ -350,7 +350,7 @@ private static String addModulators (final ISampleZone sampleMetadata, final Str
final IFilter filter = filterOpt.isPresent () ? filterOpt.get () : new DefaultFilter (FilterType.LOW_PASS, 2, IFilter.MAX_FREQUENCY, 0);
groupContent = groupContent.replace ("%FILTER_TYPE%", createFilterType (filter));
- final double legacyCutoff = MathUtils.normalizeFrequency (MathUtils.clamp (filter.getCutoff (), 43.6, 21800), 21800.0);
+ final double legacyCutoff = MathUtils.normalizeFrequency (Math.clamp (filter.getCutoff (), 43.6, 21800), 21800.0);
groupContent = groupContent.replace ("%FILTER_CUTOFF%", formatDouble (legacyCutoff));
groupContent = groupContent.replace ("%FILTER_RESONANCE%", formatDouble (filter.getResonance ()));
@@ -363,6 +363,10 @@ private static String addModulators (final ISampleZone sampleMetadata, final Str
groupContent = groupContent.replace ("%FILTER_CUTOFF_ENVELOPE_HOLD%", formatDouble (limitToDefault (filterCutoffEnvelope.getHoldTime (), 0) * 1000.0d));
groupContent = groupContent.replace ("%FILTER_CUTOFF_ENVELOPE_RELEASE%", formatDouble (limitToDefault (filterCutoffEnvelope.getReleaseTime (), 1) * 1000.0d));
groupContent = groupContent.replace ("%FILTER_CUTOFF_ENVELOPE_SUSTAIN%", formatDouble (limitToDefault (filterCutoffEnvelope.getSustainLevel (), 1)));
+
+ final double cutoffVelocityDepth = filter.getCutoffVelocityModulator ().getDepth ();
+ groupContent = groupContent.replace ("%FILTER_CUTOFF_VELOCITY_MOD%", formatDouble (limitToDefault (cutoffVelocityDepth, 0)));
+
return groupContent;
}
@@ -693,7 +697,7 @@ private void readMetadata (final Map programParameters, final Ma
final double groupPan = AbstractNKIMetadataFileHandler.getDouble (groupParameters, this.tags.groupPanParam ());
final double progPan = AbstractNKIMetadataFileHandler.getDouble (programParameters, this.tags.progPanParam ());
final double totalPan = this.normalizePanning (zonePan) + this.normalizePanning (groupPan) + this.normalizePanning (progPan);
- zone.setPanorama (MathUtils.clamp (totalPan, -1.0d, 1.0d));
+ zone.setPanorama (Math.clamp (totalPan, -1.0d, 1.0d));
if (ampVelocityMod >= 0)
zone.getAmplitudeVelocityModulator ().setDepth (ampVelocityMod);
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/nki/type/kontakt5/Program.java b/src/main/java/de/mossgrabers/convertwithmoss/format/nki/type/kontakt5/Program.java
index 5bfbadd..6cae68b 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/nki/type/kontakt5/Program.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/nki/type/kontakt5/Program.java
@@ -296,7 +296,7 @@ public void fillInto (final DefaultMultisampleSource source) throws IOException
final float volume = this.instrumentVolume * kontaktGroup.getVolume () * kontaktZone.getZoneVolume ();
zone.setGain (MathUtils.valueToDb (volume));
- zone.setPanorama (MathUtils.clamp (this.instrumentPan + kontaktGroup.getPan () + kontaktZone.getZonePan (), -1, 1));
+ zone.setPanorama (Math.clamp (this.instrumentPan + kontaktGroup.getPan () + kontaktZone.getZonePan (), -1, 1));
zone.setTune (calculateTune (kontaktZone.getZoneTune (), kontaktGroup.getTune (), this.instrumentTune));
zone.setKeyTracking (kontaktGroup.isKeyTracking () ? 1 : 0);
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/sf2/Sf2Creator.java b/src/main/java/de/mossgrabers/convertwithmoss/format/sf2/Sf2Creator.java
index ba5e772..010bab3 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/sf2/Sf2Creator.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/sf2/Sf2Creator.java
@@ -14,7 +14,6 @@
import de.mossgrabers.convertwithmoss.core.IMultisampleSource;
import de.mossgrabers.convertwithmoss.core.INotifier;
-import de.mossgrabers.convertwithmoss.core.MathUtils;
import de.mossgrabers.convertwithmoss.core.creator.AbstractCreator;
import de.mossgrabers.convertwithmoss.core.creator.DestinationAudioFormat;
import de.mossgrabers.convertwithmoss.core.model.IEnvelope;
@@ -291,6 +290,10 @@ else if (sampleType == Sf2SampleDescriptor.RIGHT)
// Gain
instrumentZone.addGenerator (Generator.INITIAL_ATTENUATION, (int) (-sampleZone.getGain () * 10.0));
+ final double ampDepth = sampleZone.getAmplitudeVelocityModulator ().getDepth ();
+ if (ampDepth != 0)
+ instrumentZone.addModulator (Sf2Modulator.MODULATOR_VELOCITY.intValue (), Generator.INITIAL_ATTENUATION, (int) Math.round (ampDepth * 960), 0, 0);
+
// Volume envelope
final IEnvelope amplitudeEnvelope = sampleZone.getAmplitudeEnvelopeModulator ().getSource ();
@@ -331,8 +334,8 @@ else if (sampleType == Sf2SampleDescriptor.RIGHT)
// always a relation of two frequencies, 1200 cents are one octave:
// cents = 1200 * log2 (f1 / f2), f2 = 8.176 => f1 = f2 * 2^(cents / 1200)
final double initialCutoff = Math.log (frequency / 8.176) * 1200.0 / Math.log (2);
- instrumentZone.addGenerator (Generator.INITIAL_FILTER_CUTOFF, (int) MathUtils.clamp (initialCutoff, 1500, 13500));
- instrumentZone.addSignedGenerator (Generator.INITIAL_FILTER_RESONANCE, (int) (MathUtils.clamp (resonance, 0, 960) * 100.0));
+ instrumentZone.addGenerator (Generator.INITIAL_FILTER_CUTOFF, (int) Math.clamp (initialCutoff, 1500, 13500));
+ instrumentZone.addSignedGenerator (Generator.INITIAL_FILTER_RESONANCE, (int) (Math.clamp (resonance, 0, 960) * 100.0));
final IEnvelopeModulator cutoffModulator = filter.getCutoffEnvelopeModulator ();
final double cutoffModDepth = cutoffModulator.getDepth ();
@@ -347,6 +350,10 @@ else if (sampleType == Sf2SampleDescriptor.RIGHT)
setEnvelopeTime (instrumentZone, Generator.MOD_ENV_RELEASE, filterEnvelope.getReleaseTime ());
setEnvelopeLevel (instrumentZone, Generator.MOD_ENV_SUSTAIN, filterEnvelope.getSustainLevel ());
}
+
+ final double cutoffDepth = filter.getCutoffVelocityModulator ().getDepth ();
+ if (cutoffDepth != 0)
+ instrumentZone.addModulator (Sf2Modulator.MODULATOR_VELOCITY.intValue (), Generator.INITIAL_FILTER_CUTOFF, (int) Math.round (cutoffDepth * -2400), 0, 0);
}
}
@@ -417,7 +424,7 @@ private static Sf2SampleDescriptor createSf2SampleDescriptor (final int sampleTy
sampleDescriptor.setSampleType (sampleType);
sampleDescriptor.setSampleRate (formatChunk.getSampleRate ());
- sampleDescriptor.setOriginalPitch (MathUtils.clamp (sampleZone.getKeyRoot (), 0, 127));
+ sampleDescriptor.setOriginalPitch (Math.clamp (sampleZone.getKeyRoot (), 0, 127));
sampleDescriptor.setPitchCorrection ((int) (sampleZone.getTune () * 100));
String name = sampleZone.getName ();
@@ -460,7 +467,7 @@ private static int convertEnvelopeVolume (final double value)
// Attenuation is in centi-bel (dB / 10), so 0 is maximum volume, about 1000 is off
// This is likely not correct but since there is also no documentation what the percentage
// volume values mean in dB it is the best we can do...
- return (int) MathUtils.clamp ((1.0 - value) * 1000.0, 0, 1000);
+ return (int) Math.clamp ((1.0 - value) * 1000.0, 0, 1000);
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/sf2/Sf2DetectorTask.java b/src/main/java/de/mossgrabers/convertwithmoss/format/sf2/Sf2DetectorTask.java
index 80f3799..3818878 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/sf2/Sf2DetectorTask.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/sf2/Sf2DetectorTask.java
@@ -14,15 +14,14 @@
import de.mossgrabers.convertwithmoss.core.IMultisampleSource;
import de.mossgrabers.convertwithmoss.core.INotifier;
-import de.mossgrabers.convertwithmoss.core.MathUtils;
import de.mossgrabers.convertwithmoss.core.creator.AbstractCreator;
import de.mossgrabers.convertwithmoss.core.detector.AbstractDetectorTask;
import de.mossgrabers.convertwithmoss.core.detector.DefaultMultisampleSource;
import de.mossgrabers.convertwithmoss.core.model.IEnvelope;
+import de.mossgrabers.convertwithmoss.core.model.IEnvelopeModulator;
import de.mossgrabers.convertwithmoss.core.model.IFilter;
import de.mossgrabers.convertwithmoss.core.model.IGroup;
import de.mossgrabers.convertwithmoss.core.model.IMetadata;
-import de.mossgrabers.convertwithmoss.core.model.IEnvelopeModulator;
import de.mossgrabers.convertwithmoss.core.model.ISampleZone;
import de.mossgrabers.convertwithmoss.core.model.enumeration.FilterType;
import de.mossgrabers.convertwithmoss.core.model.implementation.DefaultFilter;
@@ -162,18 +161,37 @@ private List parseSF2File (final File sourceFile, final Sf2F
}
- private static void parseModulators (final ISampleZone sampleZone, final Sf2PresetZone zone, final Sf2InstrumentZone instrZone)
+ private static void parseModulators (final ISampleZone zone, final Sf2PresetZone sf2Zone, final Sf2InstrumentZone instrZone)
{
- List modulators = instrZone.getModulators (Sf2Modulator.MODULATOR_PITCH_BEND);
- if (modulators.isEmpty ())
- modulators = zone.getModulators (Sf2Modulator.MODULATOR_PITCH_BEND);
- for (final Sf2Modulator sf2Modulator: modulators)
+ for (final Sf2Modulator sf2Modulator: getModulators (sf2Zone, instrZone, Sf2Modulator.MODULATOR_PITCH_BEND))
if (sf2Modulator.getDestinationGenerator () == Generator.FINE_TUNE)
{
final int amount = sf2Modulator.getModulationAmount ();
- sampleZone.setBendUp (amount);
- sampleZone.setBendDown (-amount);
+ zone.setBendUp (amount);
+ zone.setBendDown (-amount);
+ }
+
+ for (final Sf2Modulator sf2Modulator: getModulators (sf2Zone, instrZone, Sf2Modulator.MODULATOR_VELOCITY))
+ {
+ final int destinationGenerator = sf2Modulator.getDestinationGenerator ();
+ if (destinationGenerator == Generator.INITIAL_ATTENUATION)
+ {
+ final int amount = sf2Modulator.getModulationAmount ();
+ zone.getAmplitudeVelocityModulator ().setDepth (Math.clamp (amount / 960, 0, 1));
}
+ else if (destinationGenerator == Generator.INITIAL_FILTER_CUTOFF)
+ {
+ final int amount = sf2Modulator.getModulationAmount ();
+ zone.getAmplitudeVelocityModulator ().setDepth (Math.clamp (amount / -2400, 0, 1));
+ }
+ }
+ }
+
+
+ private static List getModulators (final Sf2PresetZone zone, final Sf2InstrumentZone instrZone, final Integer modulatorID)
+ {
+ final List modulators = instrZone.getModulators (modulatorID);
+ return modulators.isEmpty () ? zone.getModulators (modulatorID) : modulators;
}
@@ -271,7 +289,7 @@ private List combineLinkedSamples (final List leftSamp
// Store the matching right side sample with the left side one
leftSampleData.setRightSample (sample);
updateFilename (leftSampleZone, rightSampleZone);
- leftSampleZone.setPanorama (MathUtils.clamp (leftSampleZone.getPanorama () + rightSampleZone.getPanorama (), -1.0, 1.0));
+ leftSampleZone.setPanorama (Math.clamp (leftSampleZone.getPanorama () + rightSampleZone.getPanorama (), -1.0, 1.0));
resultSamples.add (leftSampleZone);
rightSampleZones.remove (i);
found = true;
@@ -326,7 +344,7 @@ private List combinePanoramaSamples (final List panLef
final Sf2SampleData leftSampleData = (Sf2SampleData) panLeftSampleZone.getSampleData ();
updateFilename (panLeftSampleZone, panRightSampleZone);
leftSampleData.setRightSample (((Sf2SampleData) panRightSampleZone.getSampleData ()).getSample ());
- panLeftSampleZone.setPanorama (MathUtils.clamp (panLeftSampleZone.getPanorama () + panRightSampleZone.getPanorama (), -1.0, 1.0));
+ panLeftSampleZone.setPanorama (Math.clamp (panLeftSampleZone.getPanorama () + panRightSampleZone.getPanorama (), -1.0, 1.0));
resultSamples.add (panLeftSampleZone);
panRightSamples.remove (i);
found = true;
@@ -408,10 +426,10 @@ private static ISampleZone createSampleZone (final Sf2SampleDescriptor sample, f
zone.setKeyRoot (pitch);
final int fineTune = generators.getSignedValue (Generator.FINE_TUNE).intValue ();
final int pitchCorrection = sample.getPitchCorrection ();
- final double tune = Math.min (1, Math.max (-1, (pitchCorrection + (double) fineTune) / 100));
+ final double tune = Math.clamp ((pitchCorrection + (double) fineTune) / 100, -1, 1);
zone.setTune (tune);
final int scaleTuning = generators.getSignedValue (Generator.SCALE_TUNE).intValue ();
- zone.setKeyTracking (Math.min (100, Math.max (0, scaleTuning)) / 100.0);
+ zone.setKeyTracking (Math.clamp (scaleTuning / 100.0, 0, 100));
// Set the key range
final Pair keyRangeValue = generators.getRangeValue (Generator.KEY_RANGE);
@@ -538,7 +556,7 @@ private static double convertEnvelopeVolume (final Integer value)
// This is likely not correct but since there is also no documentation what the percentage
// volume values mean in dB it is the best we can do...
- return Math.max (0, Math.min (1.0, 1.0 - v / 1000.0));
+ return Math.clamp (1.0 - v / 1000.0, 0, 1);
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/sfz/SfzCreator.java b/src/main/java/de/mossgrabers/convertwithmoss/format/sfz/SfzCreator.java
index 4565c04..6be2d5e 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/sfz/SfzCreator.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/sfz/SfzCreator.java
@@ -19,7 +19,6 @@
import de.mossgrabers.convertwithmoss.core.IMultisampleSource;
import de.mossgrabers.convertwithmoss.core.INotifier;
-import de.mossgrabers.convertwithmoss.core.MathUtils;
import de.mossgrabers.convertwithmoss.core.creator.AbstractCreator;
import de.mossgrabers.convertwithmoss.core.model.IEnvelope;
import de.mossgrabers.convertwithmoss.core.model.IEnvelopeModulator;
@@ -481,7 +480,7 @@ private static void createFilter (final StringBuilder buffer, final ISampleZone
final IFilter filter = optFilter.get ();
final String type = FILTER_TYPE_MAP.get (filter.getType ());
- addAttribute (buffer, SfzOpcode.FILTER_TYPE, type + "_" + MathUtils.clamp (filter.getPoles (), 1, 4) + "p", false);
+ addAttribute (buffer, SfzOpcode.FILTER_TYPE, type + "_" + Math.clamp (filter.getPoles (), 1, 4) + "p", false);
addAttribute (buffer, SfzOpcode.CUTOFF, formatDouble (filter.getCutoff (), 2), false);
final double velFilterDepth = filter.getCutoffVelocityModulator ().getDepth ();
@@ -539,7 +538,7 @@ private static void addSlopeAttribute (final StringBuilder sb, final String opco
return;
if (sb.length () > 0)
sb.append (' ');
- sb.append (opcode).append ('=').append (MathUtils.clamp (value, -10.0, 10.0));
+ sb.append (opcode).append ('=').append (Math.clamp (value, -10.0, 10.0));
}
@@ -549,6 +548,6 @@ private static void addEnvelopeAttribute (final StringBuilder sb, final String o
return;
if (sb.length () > 0)
sb.append (' ');
- sb.append (opcode).append ('=').append (MathUtils.clamp (value, 0.0, 100.0));
+ sb.append (opcode).append ('=').append (Math.clamp (value, 0.0, 100.0));
}
}
\ No newline at end of file
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/sfz/SfzDetectorTask.java b/src/main/java/de/mossgrabers/convertwithmoss/format/sfz/SfzDetectorTask.java
index e13cf1e..c7f98fc 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/sfz/SfzDetectorTask.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/sfz/SfzDetectorTask.java
@@ -21,7 +21,6 @@
import de.mossgrabers.convertwithmoss.core.IMultisampleSource;
import de.mossgrabers.convertwithmoss.core.INotifier;
-import de.mossgrabers.convertwithmoss.core.MathUtils;
import de.mossgrabers.convertwithmoss.core.NoteParser;
import de.mossgrabers.convertwithmoss.core.detector.AbstractDetectorTask;
import de.mossgrabers.convertwithmoss.core.detector.DefaultMultisampleSource;
@@ -410,10 +409,10 @@ private void parseRegion (final ISampleZone sampleMetadata)
double tune = this.getDoubleValue (SfzOpcode.TUNE, 0);
if (tune == 0)
tune = this.getDoubleValue (SfzOpcode.PITCH, 0);
- sampleMetadata.setTune (MathUtils.clamp (tune, -3600, 3600) / 100.0);
+ sampleMetadata.setTune (Math.clamp (tune, -3600, 3600) / 100.0);
final double pitchKeytrack = this.getDoubleValue (SfzOpcode.PITCH_KEYTRACK, 100);
- sampleMetadata.setKeyTracking (MathUtils.clamp (pitchKeytrack, 0, 100) / 100.0);
+ sampleMetadata.setKeyTracking (Math.clamp (pitchKeytrack, 0, 100) / 100.0);
sampleMetadata.setBendUp ((int) (this.getIntegerValue (SfzOpcode.BEND_UP, 0) / 100.0));
sampleMetadata.setBendDown ((int) (this.getIntegerValue (SfzOpcode.BEND_DOWN, 0) / 100.0));
@@ -580,7 +579,7 @@ private void parseVolume (final ISampleZone sampleZone)
{
sampleZone.setGain (this.getDoubleValue (SfzOpcode.VOLUME, 0));
final double panorama = this.getDoubleValue (SfzOpcode.PANORAMA, 0);
- sampleZone.setPanorama (MathUtils.clamp (panorama, -100, 100) / 100.0);
+ sampleZone.setPanorama (Math.clamp (panorama, -100, 100) / 100.0);
// Amplitude envelope
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/sxt/SxtDetectorTask.java b/src/main/java/de/mossgrabers/convertwithmoss/format/sxt/SxtDetectorTask.java
index 776851c..ef99955 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/sxt/SxtDetectorTask.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/sxt/SxtDetectorTask.java
@@ -332,7 +332,7 @@ private File parseReference (final InputStream in, final File parentFile, final
// Find the sample file
final int height = this.levelsOfDirectorySearch.getSelectionModel ().getSelectedItem ().intValue ();
- return findSampleFile (parentFile, previousFolder, sampleFileName, height);
+ return this.findSampleFile (parentFile, previousFolder, sampleFileName, height);
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/sxt/SxtZone.java b/src/main/java/de/mossgrabers/convertwithmoss/format/sxt/SxtZone.java
index b48e4b8..d6efde9 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/sxt/SxtZone.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/sxt/SxtZone.java
@@ -10,7 +10,6 @@
import java.util.List;
import java.util.Optional;
-import de.mossgrabers.convertwithmoss.core.MathUtils;
import de.mossgrabers.convertwithmoss.core.creator.AbstractCreator;
import de.mossgrabers.convertwithmoss.core.model.IAudioMetadata;
import de.mossgrabers.convertwithmoss.core.model.IEnvelope;
@@ -505,7 +504,9 @@ public void fillInto (final ISampleZone zone)
if (this.alternateMode > 0)
zone.setPlayLogic (PlayLogic.ROUND_ROBIN);
- // Pitch Modulation envelope
+ //////////////////////////////////////////////////////////
+ // Pitch
+
if (this.modEnvToPitch > 0)
{
final IEnvelopeModulator pitchModulator = zone.getPitchModulator ();
@@ -522,7 +523,9 @@ public void fillInto (final ISampleZone zone)
modEnvelope.setReleaseTime (envelopeTimeCentsToSeconds (this.modEnvRelease));
}
- // Set filter
+ //////////////////////////////////////////////////////////
+ // Filter
+
if (this.filterIsOn > 0)
{
final double freqHz = 440 * Math.pow (2, (this.filterFreq - 6900) / 1200);
@@ -566,6 +569,7 @@ public void fillInto (final ISampleZone zone)
}
final IFilter filter = new DefaultFilter (type, poles, freqHz, this.filterResonance / 1000.0);
zone.setFilter (filter);
+
if (this.modEnvToFilterFreq != 0)
{
final IEnvelopeModulator cutoffModulator = filter.getCutoffEnvelopeModulator ();
@@ -581,9 +585,17 @@ public void fillInto (final ISampleZone zone)
modEnvelope.setSustainLevel (this.modEnvSustain / 1000.0);
modEnvelope.setReleaseTime (envelopeTimeCentsToSeconds (this.modEnvRelease));
}
+
+ if (this.velocityToFilterFreq != 0)
+ filter.getCutoffVelocityModulator ().setDepth (this.velocityToFilterFreq / 1000.0);
}
- // Set amplitude envelope
+ //////////////////////////////////////////////////////////
+ // Amplitude
+
+ if (this.velocityToAmpGain != 0)
+ zone.getAmplitudeVelocityModulator ().setDepth (this.velocityToAmpGain / 1000.0);
+
final IEnvelopeModulator amplitudeModulator = zone.getAmplitudeEnvelopeModulator ();
final IEnvelope ampEnvelope = amplitudeModulator.getSource ();
if (this.ampEnvDelayIsOff == 0)
@@ -612,11 +624,11 @@ public void fillFrom (final ISampleZone zone) throws IOException
{
this.keyRangeStart = AbstractCreator.limitToDefault (zone.getKeyLow (), 0);
this.keyRangeEnd = AbstractCreator.limitToDefault (zone.getKeyHigh (), 127);
- this.velocityRangeStart = MathUtils.clamp (zone.getVelocityLow (), 1, 127);
- this.velocityRangeEnd = MathUtils.clamp (AbstractCreator.limitToDefault (zone.getVelocityHigh (), 127), 1, 127);
+ this.velocityRangeStart = Math.clamp (zone.getVelocityLow (), 1, 127);
+ this.velocityRangeEnd = Math.clamp (AbstractCreator.limitToDefault (zone.getVelocityHigh (), 127), 1, 127);
this.velocityFadeIn = zone.getVelocityCrossfadeLow ();
final int velocityCrossfadeHigh = zone.getVelocityCrossfadeHigh ();
- this.velocityFadeOut = velocityCrossfadeHigh == 0 ? 0x80 : MathUtils.clamp (127 - velocityCrossfadeHigh, 1, 127);
+ this.velocityFadeOut = velocityCrossfadeHigh == 0 ? 0x80 : Math.clamp (127 - velocityCrossfadeHigh, 1, 127);
this.rootKey = AbstractCreator.limitToDefault (zone.getKeyRoot (), this.keyRangeStart);
this.sampleStart = zone.getStart ();
this.sampleEnd = zone.getStop ();
@@ -632,7 +644,9 @@ public void fillFrom (final ISampleZone zone) throws IOException
this.pitchWheelRange = zone.getBendUp ();
this.pitchWheelRange = zone.getBendDown ();
+ //////////////////////////////////////////////////////////
// Loop
+
final List loops = zone.getLoops ();
if (loops.isEmpty ())
this.playMode = 0;
@@ -649,7 +663,9 @@ else if (zone.isReversed ())
if (zone.getPlayLogic () == PlayLogic.ROUND_ROBIN)
this.alternateMode = 1;
- // Pitch Modulation envelope
+ //////////////////////////////////////////////////////////
+ // Pitch
+
final IEnvelopeModulator pitchModulator = zone.getPitchModulator ();
final double depth = pitchModulator.getDepth ();
if (depth > 0)
@@ -680,7 +696,9 @@ else if (zone.isReversed ())
this.modEnvRelease = envelopeTimeSecondsToCents (modEnvelope.getReleaseTime ());
}
- // Set filter
+ //////////////////////////////////////////////////////////
+ // Filter
+
final Optional optFilter = zone.getFilter ();
if (optFilter.isPresent ())
{
@@ -746,9 +764,15 @@ else if (poles == 2)
this.modEnvSustain = sustain < 0 ? 1000 : (int) (sustain * 1000);
this.modEnvRelease = envelopeTimeSecondsToCents (modEnvelope.getReleaseTime ());
}
+
+ final double cutoffVelocityAmount = filter.getCutoffVelocityModulator ().getDepth ();
+ if (cutoffVelocityAmount != 0)
+ this.velocityToFilterFreq = (int) Math.round (cutoffVelocityAmount * 1000.0);
}
- // Set amplitude envelope
+ //////////////////////////////////////////////////////////
+ // Amplitude
+
final IEnvelopeModulator amplitudeModulator = zone.getAmplitudeEnvelopeModulator ();
final IEnvelope ampEnvelope = amplitudeModulator.getSource ();
final double delay = ampEnvelope.getDelayTime ();
@@ -774,6 +798,10 @@ else if (poles == 2)
this.ampEnvSustain = sustain < 0 ? 1000 : (int) (sustain * 1000);
this.ampEnvRelease = envelopeTimeSecondsToCents (ampEnvelope.getReleaseTime ());
+ final double ampVelocityAmount = zone.getAmplitudeVelocityModulator ().getDepth ();
+ if (ampVelocityAmount != 0)
+ this.velocityToAmpGain = (int) Math.round (ampVelocityAmount * 1000.0);
+
// Set gain and panorama
final double dBValue = zone.getGain ();
final double gainRatio = Math.pow (10, dBValue / 20);
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/tal/TALSamplerConstants.java b/src/main/java/de/mossgrabers/convertwithmoss/format/tal/TALSamplerConstants.java
index 6159f97..8f2f1fa 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/tal/TALSamplerConstants.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/tal/TALSamplerConstants.java
@@ -8,7 +8,6 @@
import java.util.List;
import java.util.Optional;
-import de.mossgrabers.convertwithmoss.core.MathUtils;
import de.mossgrabers.convertwithmoss.core.model.IFilter;
import de.mossgrabers.convertwithmoss.core.model.IGroup;
import de.mossgrabers.convertwithmoss.core.model.ISampleZone;
@@ -119,7 +118,7 @@ public static double getFilterValue (final IFilter filter)
*/
public static Optional getFilterType (final double value)
{
- final int filterIndex = MathUtils.clamp ((int) Math.round (value / INDEX_OFFSET), 0, 12);
+ final int filterIndex = Math.clamp ((int) Math.round (value / INDEX_OFFSET), 0, 12);
switch (filterIndex)
{
case 0, 1, 2, 3, 4, 5, 6:
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/tal/TALSamplerCreator.java b/src/main/java/de/mossgrabers/convertwithmoss/format/tal/TALSamplerCreator.java
index 158a0c2..1e73237 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/tal/TALSamplerCreator.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/tal/TALSamplerCreator.java
@@ -159,11 +159,9 @@ private Optional createMetadata (final String folderName, final IMultisa
programElement.setAttribute (TALSamplerTag.PROGRAM_NAME, multisampleSource.getName ());
programElement.setAttribute (TALSamplerTag.PROGRAM_NUM_VOICES, "1.0");
- final List groups = this.optimizeGroups (multisampleSource.getNonEmptyGroups (true));
- addModulationAttributes (document, groups, programElement, multisampleSource.getGlobalFilter ());
-
// Add up to 4 groups
int groupCounter = 0;
+ final List groups = this.optimizeGroups (multisampleSource.getNonEmptyGroups (true));
for (final IGroup group: groups)
{
final Element groupElement = XMLUtils.addElement (document, programElement, TALSamplerTag.SAMPLE_LAYER + groupCounter);
@@ -181,6 +179,8 @@ private Optional createMetadata (final String folderName, final IMultisa
break;
}
+ addModulationAttributes (document, groups, programElement, multisampleSource.getGlobalFilter ());
+
return this.createXMLString (document);
}
@@ -215,18 +215,18 @@ private static void createSample (final Document document, final String folderNa
XMLUtils.setIntegerAttribute (sampleElement, TALSamplerTag.END_SAMPLE, stop);
XMLUtils.setIntegerAttribute (sampleElement, TALSamplerTag.REVERSE, zone.isReversed () ? 1 : 0);
- // transpose // tune in semitones = floor((48.0f * transpose + 0.5f) - 24.0f)
+ // transpose // tune in semi-tones = floor((48.0f * transpose + 0.5f) - 24.0f)
final double tune = zone.getTune ();
if (tune != 0)
{
- // transpose and de-tune are both +-24 semitones, fine tuning is set on the program with
- // +-100 cent
+ // transpose and de-tune are both +-24 semi-tones, fine tuning is set on the program
+ // with +-100 cent
final int transpose = (int) tune;
final double fine = tune - transpose;
int detune = 0;
if (transpose > 24 || transpose < -24)
- detune = MathUtils.clamp (transpose > 24 ? transpose - 24 : transpose + 24, -24, 24);
+ detune = Math.clamp (transpose > 24 ? transpose - 24 : transpose + 24, -24, 24);
XMLUtils.setDoubleAttribute (sampleElement, TALSamplerTag.TRANSPOSE, (transpose + 24.0) / 48.0, 4);
XMLUtils.setDoubleAttribute (sampleElement, TALSamplerTag.DETUNE, (detune + 24.0) / 48.0, 4);
XMLUtils.setDoubleAttribute (programElement, TALSamplerTag.SAMPLE_FINE_TUNE + TALSamplerConstants.LAYERS[groupCounter], (fine + 1.0) / 2.0, 4);
@@ -283,15 +283,18 @@ private static void addModulationAttributes (final Document document, final List
return;
final ISampleZone zone = groups.get (0).getSampleZones ().get (0);
+ final List modulators = new ArrayList<> (10);
// Pitch-bend
final int bendUp = Math.abs (zone.getBendUp ());
- final double bendUpValue = bendUp == 0 ? 0.16 : MathUtils.clamp (bendUp / 1200.0, 0, 1.0);
+ final double bendUpValue = bendUp == 0 ? 0.16 : Math.clamp (bendUp / 1200.0, 0, 1.0);
XMLUtils.setDoubleAttribute (programElement, TALSamplerTag.PITCHBEND_RANGE, bendUpValue, 3);
final double maxEnvelopeTime = TALSamplerConstants.getMediumSampleLength (groups);
- // Add amplitude envelope
+ //////////////////////////////////////////////////
+ // Amplitude
+
final IEnvelope amplitudeEnvelope = zone.getAmplitudeEnvelopeModulator ().getSource ();
setEnvelopeAttribute (programElement, TALSamplerTag.ADSR_AMP_ATTACK, amplitudeEnvelope.getAttackTime (), 0, maxEnvelopeTime);
setEnvelopeAttribute (programElement, TALSamplerTag.ADSR_AMP_HOLD, amplitudeEnvelope.getHoldTime (), 0, maxEnvelopeTime);
@@ -299,7 +302,13 @@ private static void addModulationAttributes (final Document document, final List
setEnvelopeAttribute (programElement, TALSamplerTag.ADSR_AMP_SUSTAIN, amplitudeEnvelope.getSustainLevel (), 0, 1);
setEnvelopeAttribute (programElement, TALSamplerTag.ADSR_AMP_RELEASE, amplitudeEnvelope.getReleaseTime (), 0, maxEnvelopeTime);
- // Add filter settings
+ final double ampModDepth = zone.getAmplitudeVelocityModulator ().getDepth ();
+ if (ampModDepth != 0)
+ modulators.add (new TALSamplerModulator (TALSamplerModulator.SOURCE_ID_VELOCITY, TALSamplerModulator.DEST_ID_VOLUME_A, ampModDepth));
+
+ //////////////////////////////////////////////////
+ // Filter
+
if (optFilter.isPresent ())
{
final IFilter filter = optFilter.get ();
@@ -329,9 +338,15 @@ private static void addModulationAttributes (final Document document, final List
// TALSamplerTag.FILTER_KEYBOARD not supported
}
+
+ final double cutoffModDepth = filter.getCutoffVelocityModulator ().getDepth ();
+ if (cutoffModDepth != 0)
+ modulators.add (new TALSamplerModulator (TALSamplerModulator.SOURCE_ID_VELOCITY, TALSamplerModulator.DEST_ID_CUTOFF, cutoffModDepth));
}
- // Add pitch envelope
+ //////////////////////////////////////////////////
+ // Pitch
+
final IEnvelopeModulator pitchModulator = zone.getPitchModulator ();
final double pitchModDepth = pitchModulator.getDepth ();
if (pitchModDepth > 0)
@@ -344,19 +359,15 @@ private static void addModulationAttributes (final Document document, final List
setEnvelopeAttribute (programElement, TALSamplerTag.ADSR_MOD_RELEASE, pitchEnvelope.getReleaseTime (), 0, maxEnvelopeTime);
// Envelope 3 needs to be set to modulate the global pitch
- final Element modMatrixElement = XMLUtils.addElement (document, programElement, "modmatrix");
- Element entryElement = XMLUtils.addElement (document, modMatrixElement, "entry");
- entryElement.setAttribute ("parameterid", "164");
- entryElement.setAttribute ("modmatrixsourceid", "2");
- XMLUtils.setDoubleAttribute (entryElement, "modmatrixamount", pitchModDepth, 16);
- for (int i = 0; i < 9; i++)
- {
- entryElement = XMLUtils.addElement (document, modMatrixElement, "entry");
- entryElement.setAttribute ("parameterid", "-1");
- entryElement.setAttribute ("modmatrixsourceid", "0");
- entryElement.setAttribute ("modmatrixamount", "0.5");
- }
+ modulators.add (new TALSamplerModulator (TALSamplerModulator.SOURCE_ID_ENV3, TALSamplerModulator.DEST_ID_MASTER_TUNE, pitchModDepth));
}
+
+ // Create modulator matrix
+ while (modulators.size () != 10)
+ modulators.add (new TALSamplerModulator ());
+ final Element modMatrixElement = XMLUtils.addElement (document, programElement, TALSamplerTag.MOD_MATRIX);
+ for (final TALSamplerModulator modulator: modulators)
+ modulator.createModElements (document, modMatrixElement);
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/tal/TALSamplerDetectorTask.java b/src/main/java/de/mossgrabers/convertwithmoss/format/tal/TALSamplerDetectorTask.java
index e267cef..3c904ce 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/tal/TALSamplerDetectorTask.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/tal/TALSamplerDetectorTask.java
@@ -24,9 +24,9 @@
import de.mossgrabers.convertwithmoss.core.detector.AbstractDetectorTask;
import de.mossgrabers.convertwithmoss.core.detector.DefaultMultisampleSource;
import de.mossgrabers.convertwithmoss.core.model.IEnvelope;
+import de.mossgrabers.convertwithmoss.core.model.IEnvelopeModulator;
import de.mossgrabers.convertwithmoss.core.model.IFilter;
import de.mossgrabers.convertwithmoss.core.model.IGroup;
-import de.mossgrabers.convertwithmoss.core.model.IEnvelopeModulator;
import de.mossgrabers.convertwithmoss.core.model.ISampleZone;
import de.mossgrabers.convertwithmoss.core.model.enumeration.LoopType;
import de.mossgrabers.convertwithmoss.core.model.implementation.DefaultFilter;
@@ -254,19 +254,30 @@ private ISampleZone parseSample (final File parentFolder, final Element programE
private static Optional parseModulationAttributes (final Element programElement, final DefaultMultisampleSource multisampleSource) throws IOException
{
- // Pitch-bend
- final int bend = (int) MathUtils.clamp (XMLUtils.getDoubleAttribute (programElement, TALSamplerTag.PITCHBEND_RANGE, 1.0) * 1200.0, 0.0, 1200.0);
+ final List modulators = parseModulators (programElement);
final double maxEnvelopeTime = TALSamplerConstants.getMediumSampleLength (multisampleSource.getGroups ());
- // Add amplitude envelope
- final double ampAttach = getEnvelopeAttribute (programElement, TALSamplerTag.ADSR_AMP_ATTACK, 0, maxEnvelopeTime, 0);
+ //////////////////////////////////////////////////
+ // Amplitude
+
+ final double ampAttack = getEnvelopeAttribute (programElement, TALSamplerTag.ADSR_AMP_ATTACK, 0, maxEnvelopeTime, 0);
final double ampHold = getEnvelopeAttribute (programElement, TALSamplerTag.ADSR_AMP_HOLD, 0, maxEnvelopeTime, 0);
final double ampDecay = getEnvelopeAttribute (programElement, TALSamplerTag.ADSR_AMP_DECAY, 0, maxEnvelopeTime, 0);
final double ampSustain = getEnvelopeAttribute (programElement, TALSamplerTag.ADSR_AMP_SUSTAIN, 0, 1, 1);
final double ampRelease = getEnvelopeAttribute (programElement, TALSamplerTag.ADSR_AMP_RELEASE, 0, maxEnvelopeTime, 0);
- // Get filter settings
+ double ampVelocityModAmount = 0.5;
+ for (final TALSamplerModulator modulator: modulators)
+ if (modulator.isDestination (TALSamplerModulator.DEST_ID_VOLUME_A) && modulator.isSource (TALSamplerModulator.SOURCE_ID_VELOCITY))
+ {
+ ampVelocityModAmount = modulator.getModAmount ();
+ break;
+ }
+
+ //////////////////////////////////////////////////
+ // Filter
+
// We only have a global filter, therefore take only values from the 1st layer
Optional optFilter = Optional.empty ();
if (XMLUtils.getDoubleAttribute (programElement, TALSamplerTag.FILTER_LAYER_ON + TALSamplerConstants.LAYERS[0], 0) > 0)
@@ -294,10 +305,23 @@ private static Optional parseModulationAttributes (final Element progra
filterEnvelope.setSustainLevel (getEnvelopeAttribute (programElement, TALSamplerTag.ADSR_VCF_SUSTAIN, 0, 1, 1));
filterEnvelope.setReleaseTime (getEnvelopeAttribute (programElement, TALSamplerTag.ADSR_VCF_RELEASE, 0, maxEnvelopeTime, 0));
}
+
+ for (final TALSamplerModulator modulator: modulators)
+ if (modulator.isDestination (TALSamplerModulator.DEST_ID_CUTOFF) && modulator.isSource (TALSamplerModulator.SOURCE_ID_VELOCITY))
+ {
+ filter.getCutoffVelocityModulator ().setDepth (modulator.getModAmount ());
+ break;
+ }
}
}
- // Get pitch (modulation) envelope
+ //////////////////////////////////////////////////
+ // Pitch
+
+ // Pitch-bend
+ final int bend = (int) Math.clamp (XMLUtils.getDoubleAttribute (programElement, TALSamplerTag.PITCHBEND_RANGE, 1.0) * 1200.0, 0.0, 1200.0);
+
+ // Envelope
final double pitchAttack = getEnvelopeAttribute (programElement, TALSamplerTag.ADSR_MOD_ATTACK, 0, maxEnvelopeTime, 0);
final double pitchHold = getEnvelopeAttribute (programElement, TALSamplerTag.ADSR_MOD_HOLD, 0, maxEnvelopeTime, 0);
final double pitchDecay = getEnvelopeAttribute (programElement, TALSamplerTag.ADSR_MOD_DECAY, 0, maxEnvelopeTime, 0);
@@ -305,16 +329,15 @@ private static Optional parseModulationAttributes (final Element progra
final double pitchRelease = getEnvelopeAttribute (programElement, TALSamplerTag.ADSR_MOD_RELEASE, 0, maxEnvelopeTime, 0);
// Envelope 3 needs to be set to modulate the global pitch
- final Element modMatrixElement = XMLUtils.getChildElementByName (programElement, "modmatrix");
double globalPitchEnvelopeDepth = -1;
- if (modMatrixElement != null)
- for (final Element entryElement: XMLUtils.getChildElementsByName (modMatrixElement, "entry", false))
- if (XMLUtils.getIntegerAttribute (entryElement, "parameterid", -1) == 164 && XMLUtils.getIntegerAttribute (entryElement, "modmatrixsourceid", -1) == 2)
- {
- globalPitchEnvelopeDepth = XMLUtils.getDoubleAttribute (entryElement, "modmatrixamount", 1.0);
- break;
- }
+ for (final TALSamplerModulator modulator: modulators)
+ if (modulator.isSource (TALSamplerModulator.SOURCE_ID_ENV3) || modulator.isDestination (TALSamplerModulator.DEST_ID_TUNE_A, TALSamplerModulator.DEST_ID_MASTER_TUNE))
+ {
+ globalPitchEnvelopeDepth = modulator.getModAmount ();
+ break;
+ }
+ // Set all zones of all groups to the same amplitude and pitch envelope
for (final IGroup group: multisampleSource.getGroups ())
for (final ISampleZone zone: group.getSampleZones ())
{
@@ -322,12 +345,14 @@ private static Optional parseModulationAttributes (final Element progra
zone.setBendDown (bend);
final IEnvelope amplitudeEnvelope = zone.getAmplitudeEnvelopeModulator ().getSource ();
- amplitudeEnvelope.setAttackTime (ampAttach);
+ amplitudeEnvelope.setAttackTime (ampAttack);
amplitudeEnvelope.setHoldTime (ampHold);
amplitudeEnvelope.setDecayTime (ampDecay);
amplitudeEnvelope.setSustainLevel (ampSustain);
amplitudeEnvelope.setReleaseTime (ampRelease);
+ zone.getAmplitudeVelocityModulator ().setDepth (ampVelocityModAmount);
+
if (globalPitchEnvelopeDepth > 0)
{
final IEnvelopeModulator pitchModulator = zone.getPitchModulator ();
@@ -346,6 +371,17 @@ private static Optional parseModulationAttributes (final Element progra
}
+ private static List parseModulators (final Element soundShapeElement)
+ {
+ final List modulators = new ArrayList<> ();
+ final Element modulationElement = XMLUtils.getChildElementByName (soundShapeElement, TALSamplerTag.MOD_MATRIX);
+ if (modulationElement != null)
+ for (final Element modulationEntryElement: XMLUtils.getChildElementsByName (modulationElement, TALSamplerTag.MOD_MATRIX_ENTRY, false))
+ modulators.add (new TALSamplerModulator (modulationEntryElement));
+ return modulators;
+ }
+
+
private static double getEnvelopeAttribute (final Element element, final String attribute, final double minimum, final double maximum, final double defaultValue)
{
final double value = XMLUtils.getDoubleAttribute (element, attribute, defaultValue);
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/tal/TALSamplerModulator.java b/src/main/java/de/mossgrabers/convertwithmoss/format/tal/TALSamplerModulator.java
new file mode 100644
index 0000000..ad91051
--- /dev/null
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/tal/TALSamplerModulator.java
@@ -0,0 +1,133 @@
+// 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.tal;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import de.mossgrabers.tools.XMLUtils;
+
+
+/**
+ * A TAL Sampler modulator entry.
+ *
+ * @author Jürgen Moßgraber
+ */
+public class TALSamplerModulator
+{
+ /** Parameter ID for Cutoff. */
+ public static int DEST_ID_CUTOFF = 7;
+ /** Parameter ID for Volume of Layer A. */
+ public static int DEST_ID_VOLUME_A = 62;
+ /** Parameter ID for Tune of Layer A. */
+ public static final int DEST_ID_TUNE_A = 71;
+ /** Parameter ID for Master Tune. */
+ public static final int DEST_ID_MASTER_TUNE = 164;
+
+ /** Source ID for Velocity. */
+ public static int SOURCE_ID_VELOCITY = 6;
+ /** Source ID for Envelope 3 (Modulation Envelope). */
+ public static final int SOURCE_ID_ENV3 = 2;
+
+ private final int source;
+ private final int destination;
+ private final double amount;
+
+
+ /**
+ * Default constructor.
+ */
+ public TALSamplerModulator ()
+ {
+ this (-1, 0, 0);
+ }
+
+
+ /**
+ * Constructor.
+ *
+ * @param source The source ID
+ * @param destination The destination ID
+ * @param amount The amount in the range of [-1..1]
+ */
+ public TALSamplerModulator (final int source, final int destination, final double amount)
+ {
+ this.source = source;
+ this.destination = destination;
+ this.amount = (amount + 1.0) / 2.0;
+ }
+
+
+ /**
+ * Constructor.
+ *
+ * @param modulationEntryElement The modulator entry element
+ */
+ public TALSamplerModulator (final Element modulationEntryElement)
+ {
+ this.source = XMLUtils.getIntegerAttribute (modulationEntryElement, TALSamplerTag.MOD_MATRIX_SOURCE_ID, -1);
+ this.destination = XMLUtils.getIntegerAttribute (modulationEntryElement, TALSamplerTag.MOD_MATRIX_PARAMETER_ID, 0);
+ this.amount = XMLUtils.getDoubleAttribute (modulationEntryElement, TALSamplerTag.MOD_MATRIX_AMOUNT, 0.5);
+ }
+
+
+ /**
+ * Creates a modulation entry from this modulator.
+ *
+ * @param document The document to which the element belongs
+ * @param modulationElement The modulation element to which to add the entry
+ */
+ public void createModElements (final Document document, final Element modulationElement)
+ {
+ final Element entryElement = XMLUtils.addElement (document, modulationElement, "entry");
+ XMLUtils.setIntegerAttribute (entryElement, TALSamplerTag.MOD_MATRIX_SOURCE_ID, this.source);
+ XMLUtils.setIntegerAttribute (entryElement, TALSamplerTag.MOD_MATRIX_PARAMETER_ID, this.destination);
+ XMLUtils.setDoubleAttribute (entryElement, TALSamplerTag.MOD_MATRIX_AMOUNT, this.amount, 6);
+ }
+
+
+ /**
+ * Test if one of the given IDs matches the source ID of this modulator.
+ *
+ * @param sourceIDs The source IDs to match
+ * @return True if one of the source IDs matches the parsed source ID
+ */
+ public boolean isSource (final int... sourceIDs)
+ {
+ return matchTags (this.source, sourceIDs);
+ }
+
+
+ /**
+ * Test if one of the given IDs matches the destination ID of this modulator.
+ *
+ * @param destinationIDs The destination IDs to match
+ * @return True if one of the destination IDs matches the parsed destination ID
+ */
+ public boolean isDestination (final int... destinationIDs)
+ {
+ return matchTags (this.destination, destinationIDs);
+ }
+
+
+ /**
+ * Get the modulation amount.
+ *
+ * @return The modulation amount in the range of [-1..1]
+ */
+ public double getModAmount ()
+ {
+ return this.amount * 2.0 - 1.0;
+ }
+
+
+ private static boolean matchTags (final int id, final int... ids)
+ {
+ for (final int t: ids)
+ if (t == id)
+ return true;
+ return false;
+ }
+}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/tal/TALSamplerTag.java b/src/main/java/de/mossgrabers/convertwithmoss/format/tal/TALSamplerTag.java
index 4f0f884..cea030a 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/tal/TALSamplerTag.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/tal/TALSamplerTag.java
@@ -12,148 +12,160 @@
public class TALSamplerTag
{
/** The root tag. */
- public static final String ROOT = "tal";
+ public static final String ROOT = "tal";
/** The programs tag. */
- public static final String PROGRAMS = "programs";
+ public static final String PROGRAMS = "programs";
/** The program tag. */
- public static final String PROGRAM = "program";
+ public static final String PROGRAM = "program";
/** The sample layer tag. */
- public static final String SAMPLE_LAYER = "samplelayer";
- /** The multi samples tag. */
- public static final String MULTISAMPLES = "multisamples";
+ public static final String SAMPLE_LAYER = "samplelayer";
+ /** The multi-samples tag. */
+ public static final String MULTISAMPLES = "multisamples";
/** The sample tag. */
- public static final String MULTISAMPLE = "multisample";
+ public static final String MULTISAMPLE = "multisample";
/** The current program attribute tag. */
- public static final String ROOT_CUR_PROGRAM = "curprogram";
+ public static final String ROOT_CUR_PROGRAM = "curprogram";
/** The version tag. */
- public static final String ROOT_VERSION = "version";
+ public static final String ROOT_VERSION = "version";
/** The program name attribute tag. */
- public static final String PROGRAM_NAME = "programname";
+ public static final String PROGRAM_NAME = "programname";
/** The layer enabled attribute tag. */
- public static final String PROGRAM_LAYER_ON = "sampleenabled";
+ public static final String PROGRAM_LAYER_ON = "sampleenabled";
/** The number of voices which can be played. */
- public static final String PROGRAM_NUM_VOICES = "numvoices";
+ public static final String PROGRAM_NUM_VOICES = "numvoices";
/** The pitchbend range. */
- public static final String PITCHBEND_RANGE = "globalpitchbendrange";
+ public static final String PITCHBEND_RANGE = "globalpitchbendrange";
- /** The multi sample URL tag. */
- public static final String MULTISAMPLE_URL = "url";
+ /** The multi-sample URL tag. */
+ public static final String MULTISAMPLE_URL = "url";
/** The start tag sample attribute. */
- public static final String START_SAMPLE = "startsample";
+ public static final String START_SAMPLE = "startsample";
/** The end tag sample attribute. */
- public static final String END_SAMPLE = "endsample";
+ public static final String END_SAMPLE = "endsample";
/** The root note tag sample attribute. */
- public static final String ROOT_NOTE = "rootkey";
+ public static final String ROOT_NOTE = "rootkey";
/** The low note tag sample attribute. */
- public static final String LO_NOTE = "lowkey";
+ public static final String LO_NOTE = "lowkey";
/** The high note tag sample attribute. */
- public static final String HI_NOTE = "highkey";
+ public static final String HI_NOTE = "highkey";
/** The low velocity tag sample attribute. */
- public static final String LO_VEL = "velocitystart";
+ public static final String LO_VEL = "velocitystart";
/** The high velocity tag sample attribute. */
- public static final String HI_VEL = "velocityend";
+ public static final String HI_VEL = "velocityend";
/** The volume tag on different levels. */
- public static final String VOLUME = "volume";
+ public static final String VOLUME = "volume";
/** The sample panorama attribute. */
- public static final String PANORAMA = "pan";
+ public static final String PANORAMA = "pan";
/** The layer transpose attribute. */
- public static final String LAYER_TRANSPOSE = "layertranspose";
+ public static final String LAYER_TRANSPOSE = "layertranspose";
/** The tune sample attribute. */
- public static final String SAMPLE_TUNE = "sampletune";
+ public static final String SAMPLE_TUNE = "sampletune";
/** The fine tune sample attribute. */
- public static final String SAMPLE_FINE_TUNE = "samplefinetune";
+ public static final String SAMPLE_FINE_TUNE = "samplefinetune";
/** The sample transpose attribute. */
- public static final String TRANSPOSE = "transpose";
+ public static final String TRANSPOSE = "transpose";
/** The transpose tag sample attribute. */
- public static final String DETUNE = "detune";
+ public static final String DETUNE = "detune";
/** The pitch key tracking tag sample attribute (0: no tracking, 1 tracking enabled). */
- public static final String PITCH_KEY_TRACK = "track";
+ public static final String PITCH_KEY_TRACK = "track";
/** The loop enabled tag sample attribute. */
- public static final String LOOP_ENABLED = "loopenabled";
+ public static final String LOOP_ENABLED = "loopenabled";
/** The loop start tag sample attribute. */
- public static final String LOOP_START = "loopstartsample";
+ public static final String LOOP_START = "loopstartsample";
/** The loop end tag sample attribute. */
- public static final String LOOP_END = "loopendsample";
+ public static final String LOOP_END = "loopendsample";
/** The loop alternate attribute tag. */
- public static final String LOOP_ALTERNATE = "pingpongloop";
+ public static final String LOOP_ALTERNATE = "pingpongloop";
/** The sample reverse attribute. */
- public static final String REVERSE = "reverse";
+ public static final String REVERSE = "reverse";
/** Fade in samples attribute. */
- public static final String FADE_IN_SAMPLES = "fadeinsamples";
+ public static final String FADE_IN_SAMPLES = "fadeinsamples";
/** TAL-Sampler has a few ROM Samples with base waveforms included, always zero. */
- public static final String IS_ROM_SAMPLE = "isromsample";
+ public static final String IS_ROM_SAMPLE = "isromsample";
/** The sample slice number attribute, always zero. */
- public static final String SLICE = "slice";
+ public static final String SLICE = "slice";
/** Should the phase be inverted? */
- public static final String PHASE_INVERSE = "phaseinverse";
+ public static final String PHASE_INVERSE = "phaseinverse";
/** Invert stereo: 1 -> normal; 0 -> inverted stereo output. */
- public static final String STEREO_INVERSE = "stereoinverse";
+ public static final String STEREO_INVERSE = "stereoinverse";
/** Is the group muted? */
- public static final String MUTE_GROUP = "mutegroup";
+ public static final String MUTE_GROUP = "mutegroup";
/** The global amplitude envelope attack attribute. */
- public static final String ADSR_AMP_ATTACK = "adsrampattack";
+ public static final String ADSR_AMP_ATTACK = "adsrampattack";
/** The global amplitude envelope hold attribute. */
- public static final String ADSR_AMP_HOLD = "adsramphold";
+ public static final String ADSR_AMP_HOLD = "adsramphold";
/** The global amplitude envelope decay attribute. */
- public static final String ADSR_AMP_DECAY = "adsrampdecay";
+ public static final String ADSR_AMP_DECAY = "adsrampdecay";
/** The global amplitude envelope sustain attribute. */
- public static final String ADSR_AMP_SUSTAIN = "adsrampsustain";
+ public static final String ADSR_AMP_SUSTAIN = "adsrampsustain";
/** The global amplitude envelope release attribute. */
- public static final String ADSR_AMP_RELEASE = "adsramprelease";
+ public static final String ADSR_AMP_RELEASE = "adsramprelease";
/** The global filter envelope attack attribute. */
- public static final String ADSR_VCF_ATTACK = "adsrvcfattack";
+ public static final String ADSR_VCF_ATTACK = "adsrvcfattack";
/** The global filter envelope hold attribute. */
- public static final String ADSR_VCF_HOLD = "adsrvcfhold";
+ public static final String ADSR_VCF_HOLD = "adsrvcfhold";
/** The global filter envelope decay attribute. */
- public static final String ADSR_VCF_DECAY = "adsrvcfdecay";
+ public static final String ADSR_VCF_DECAY = "adsrvcfdecay";
/** The global filter envelope sustain attribute. */
- public static final String ADSR_VCF_SUSTAIN = "adsrvcfsustain";
+ public static final String ADSR_VCF_SUSTAIN = "adsrvcfsustain";
/** The global filter envelope release attribute. */
- public static final String ADSR_VCF_RELEASE = "adsrvcfrelease";
+ public static final String ADSR_VCF_RELEASE = "adsrvcfrelease";
/** The global modulation (pitch) envelope attack attribute. */
- public static final String ADSR_MOD_ATTACK = "adsrmodattack";
+ public static final String ADSR_MOD_ATTACK = "adsrmodattack";
/** The global modulation (pitch) envelope hold attribute. */
- public static final String ADSR_MOD_HOLD = "adsrmodhold";
+ public static final String ADSR_MOD_HOLD = "adsrmodhold";
/** The global modulation (pitch) envelope decay attribute. */
- public static final String ADSR_MOD_DECAY = "adsrmoddecay";
+ public static final String ADSR_MOD_DECAY = "adsrmoddecay";
/** The global modulation (pitch) envelope sustain attribute. */
- public static final String ADSR_MOD_SUSTAIN = "adsrmodsustain";
+ public static final String ADSR_MOD_SUSTAIN = "adsrmodsustain";
/** The global modulation (pitch) envelope release attribute. */
- public static final String ADSR_MOD_RELEASE = "adsrmodrelease";
+ public static final String ADSR_MOD_RELEASE = "adsrmodrelease";
/** The amplitude envelope attack attribute. */
- public static final String AMP_ENV_ATTACK = "attack";
+ public static final String AMP_ENV_ATTACK = "attack";
/** The amplitude envelope decay attribute. */
- public static final String AMP_ENV_DECAY = "decay";
+ public static final String AMP_ENV_DECAY = "decay";
/** The amplitude envelope sustain attribute. */
- public static final String AMP_ENV_SUSTAIN = "sustain";
+ public static final String AMP_ENV_SUSTAIN = "sustain";
/** The amplitude envelope release attribute. */
- public static final String AMP_ENV_RELEASE = "release";
+ public static final String AMP_ENV_RELEASE = "release";
/** The filter cutoff attribute. */
- public static final String FILTER_CUTOFF = "filtercutoff";
+ public static final String FILTER_CUTOFF = "filtercutoff";
/** The filter resonance attribute. */
- public static final String FILTER_RESONANCE = "filterresonance";
+ public static final String FILTER_RESONANCE = "filterresonance";
/** The filter mode attribute. */
- public static final String FILTER_MODE = "filtermode";
+ public static final String FILTER_MODE = "filtermode";
/** The filter keyboard tracking attribute. [0..1] -> -100%..100% */
- public static final String FILTER_KEYBOARD = "filterkeyboardvalue";
+ public static final String FILTER_KEYBOARD = "filterkeyboardvalue";
/** The filter envelope intensity attribute. [0..1] -> -100%..100% */
- public static final String FILTER_ENVELOPE = "filterenvelope";
+ public static final String FILTER_ENVELOPE = "filterenvelope";
/** The filter layer on/off attribute. */
- public static final String FILTER_LAYER_ON = "filterlayer";
+ public static final String FILTER_LAYER_ON = "filterlayer";
+
+ /** The modulation matrix tag. */
+ public static final String MOD_MATRIX = "modmatrix";
+ /** The modulation matrix entry tag. */
+ public static final String MOD_MATRIX_ENTRY = "entry";
+
+ /** The modulation matrix source ID attribute. */
+ public static final String MOD_MATRIX_SOURCE_ID = "modmatrixsourceid";
+ /** The modulation matrix source ID attribute. */
+ public static final String MOD_MATRIX_AMOUNT = "modmatrixamount";
+ /** The modulation matrix parameter ID attribute. */
+ public static final String MOD_MATRIX_PARAMETER_ID = "parameterid";
/**
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/tx16wx/TX16WxCreator.java b/src/main/java/de/mossgrabers/convertwithmoss/format/tx16wx/TX16WxCreator.java
index 02a8be5..fde74f0 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/tx16wx/TX16WxCreator.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/tx16wx/TX16WxCreator.java
@@ -22,7 +22,6 @@
import de.mossgrabers.convertwithmoss.core.IMultisampleSource;
import de.mossgrabers.convertwithmoss.core.INotifier;
-import de.mossgrabers.convertwithmoss.core.MathUtils;
import de.mossgrabers.convertwithmoss.core.creator.AbstractCreator;
import de.mossgrabers.convertwithmoss.core.model.IEnvelope;
import de.mossgrabers.convertwithmoss.core.model.IEnvelopeModulator;
@@ -500,9 +499,11 @@ private static void addGroupModulationAttributes (final Document document, final
// The whole group can be delayed which is basically the same as the amplitude delay
soundshapeElement.setAttribute (TX16WxTag.GROUP_DELAY, formatTime (amplitudeEnvelope.getDelayTime ()));
- final double sustain = amplitudeEnvelope.getSustainLevel ();
- final String sustainLevel = sustain < 0 ? "1" : Double.toString (MathUtils.clamp (sustain, 0, 1));
+ //////////////////////////////////////////////
+ // Amplitude
+ final double sustain = amplitudeEnvelope.getSustainLevel ();
+ final String sustainLevel = sustain < 0 ? "1" : Double.toString (Math.clamp (sustain, 0, 1));
aegElement.setAttribute (TX16WxTag.AMP_ENV_ATTACK, formatTime (amplitudeEnvelope.getAttackTime ()));
aegElement.setAttribute (TX16WxTag.AMP_ENV_LEVEL1, "0 dB");
aegElement.setAttribute (TX16WxTag.AMP_ENV_DECAY1, formatTime (amplitudeEnvelope.getHoldTime ()));
@@ -514,12 +515,18 @@ private static void addGroupModulationAttributes (final Document document, final
XMLUtils.setDoubleAttribute (aegElement, TX16WxTag.AMP_ENV_DECAY2_SHAPE, amplitudeEnvelope.getDecaySlope (), 6);
XMLUtils.setDoubleAttribute (aegElement, TX16WxTag.AMP_ENV_RELEASE_SHAPE, amplitudeEnvelope.getReleaseSlope (), 6);
+ final double ampVelocityDepth = zone.getAmplitudeVelocityModulator ().getDepth ();
+ if (ampVelocityDepth != 0)
+ XMLUtils.setDoubleAttribute (soundshapeElement, TX16WxTag.AMP_VELOCITY, ampVelocityDepth, 6);
+
// Pitch-bend
int pitchbend = Math.abs (zone.getBendUp ());
pitchbend = pitchbend <= 0 ? 200 : pitchbend;
addModulationEntry (document, modulationElement, "Pitchbend", "Pitch", pitchbend + "Ct");
- // Add filter settings
+ //////////////////////////////////////////////
+ // Filter
+
final Optional optFilter = zone.getFilter ();
if (optFilter.isPresent ())
{
@@ -555,9 +562,15 @@ private static void addGroupModulationAttributes (final Document document, final
XMLUtils.setDoubleAttribute (envElement, TX16WxTag.ENV_SHAPE3, amplitudeEnvelope.getReleaseSlope (), 6);
addModulationEntry (document, modulationElement, "ENV1", "Filter 1 Freq", (int) (filterModDepth * IEnvelope.MAX_ENVELOPE_DEPTH) + "Ct");
}
+
+ final double filterVelocityDepth = filter.getCutoffVelocityModulator ().getDepth ();
+ if (filterVelocityDepth != 0)
+ addModulationEntry (document, modulationElement, "Vel", "Filter 1 Freq", (int) (filterVelocityDepth * IEnvelope.MAX_ENVELOPE_DEPTH) + "Ct");
}
- // Add pitch modulator
+ //////////////////////////////////////////////
+ // Pitch
+
final IEnvelopeModulator pitchModulator = zone.getPitchModulator ();
final double pitchModDepth = pitchModulator.getDepth ();
if (pitchModDepth != 0)
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/tx16wx/TX16WxDetectorTask.java b/src/main/java/de/mossgrabers/convertwithmoss/format/tx16wx/TX16WxDetectorTask.java
index 110d20f..0c32fe2 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/tx16wx/TX16WxDetectorTask.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/tx16wx/TX16WxDetectorTask.java
@@ -193,7 +193,7 @@ private Map parseSamples (final Element topElement, final S
sampleName = URLDecoder.decode (sampleName, StandardCharsets.UTF_8).replace ("\\", File.separator);
final int height = this.levelsOfDirectorySearch.getSelectionModel ().getSelectedItem ().intValue ();
- final File sampleFile = findSampleFile (parentFile, previousFolder, sampleName, height);
+ final File sampleFile = this.findSampleFile (parentFile, previousFolder, sampleName, height);
if (!sampleFile.exists ())
{
this.notifier.logError ("IDS_NOTIFY_ERR_SAMPLE_DOES_NOT_EXIST", sampleFile.getAbsolutePath ());
@@ -277,7 +277,7 @@ private Optional parseGroups (final Element programElement, final Map parseGroup (final DefaultGroup group, final Element groupElement, final Map sampleMap, final Map soundShapeElementMap)
{
final double groupVolumeOffset = this.parseVolume (groupElement, TX16WxTag.VOLUME);
- final double groupPanoramaOffset = parsePanorama (groupElement, TX16WxTag.PANORAMA);
+ final double groupPanoramaOffset = parsePercentage (groupElement, TX16WxTag.PANORAMA);
double groupTuningOffset = XMLUtils.getIntegerAttribute (groupElement, TX16WxTag.TUNING_COARSE, 0);
groupTuningOffset += XMLUtils.getIntegerAttribute (groupElement, TX16WxTag.TUNING_FINE, 0) / 100.0;
@@ -290,12 +290,16 @@ private Optional parseGroup (final DefaultGroup group, final Element gr
int pitchbend = -1;
IEnvelope ampEnvelope = null;
+ double ampVelocity = 0;
final String soundShape = groupElement.getAttribute (TX16WxTag.SOUND_SHAPE);
if (soundShape != null && !soundShape.isBlank ())
{
final Element soundShapeElement = soundShapeElementMap.get (soundShape);
if (soundShapeElement != null)
{
+ // Velocity modulation for amplitude
+ ampVelocity = parsePercentage (soundShapeElement, TX16WxTag.AMP_VELOCITY);
+
// Amplitude envelope
final Element aegElement = XMLUtils.getChildElementByName (soundShapeElement, TX16WxTag.AMP_ENVELOPE);
if (aegElement != null)
@@ -311,14 +315,16 @@ private Optional parseGroup (final DefaultGroup group, final Element gr
ampEnvelope.setSustainLevel (parseNormalizedVolume (aegElement, TX16WxTag.AMP_ENV_SUSTAIN));
- ampEnvelope.setAttackSlope (parsePanorama (aegElement, TX16WxTag.AMP_ENV_ATTACK_SHAPE));
- ampEnvelope.setDecaySlope (parsePanorama (aegElement, TX16WxTag.AMP_ENV_DECAY2_SHAPE));
- ampEnvelope.setReleaseSlope (parsePanorama (aegElement, TX16WxTag.AMP_ENV_RELEASE_SHAPE));
+ ampEnvelope.setAttackSlope (parsePercentage (aegElement, TX16WxTag.AMP_ENV_ATTACK_SHAPE));
+ ampEnvelope.setDecaySlope (parsePercentage (aegElement, TX16WxTag.AMP_ENV_DECAY2_SHAPE));
+ ampEnvelope.setReleaseSlope (parsePercentage (aegElement, TX16WxTag.AMP_ENV_RELEASE_SHAPE));
}
- filter = this.parseFilter (soundShapeElement);
- pitchModulator = parsePitchModulator (soundShapeElement);
- final int bend = parsePitchbend (soundShapeElement);
+ final List modulators = parseModulators (soundShapeElement);
+
+ filter = this.parseFilter (soundShapeElement, modulators);
+ pitchModulator = parsePitchModulator (soundShapeElement, modulators);
+ final int bend = parsePitchbend (modulators);
if (bend > 0)
pitchbend = bend;
}
@@ -346,6 +352,7 @@ private Optional parseGroup (final DefaultGroup group, final Element gr
zone.setBendDown (pitchbend);
}
+ zone.getAmplitudeVelocityModulator ().setDepth (ampVelocity);
zone.getAmplitudeEnvelopeModulator ().setSource (ampEnvelope);
}
}
@@ -366,7 +373,7 @@ private Optional parseGroup (final DefaultGroup group, final Element gr
private void parseZone (final ISampleZone zone, final Element regionElement, final double groupVolumeOffset, final double groupPanoramaOffset, final double groupTuningOffset)
{
zone.setGain (groupVolumeOffset + this.parseVolume (regionElement, TX16WxTag.ATTENUATION));
- zone.setPanorama (groupPanoramaOffset + parsePanorama (regionElement, TX16WxTag.PANORAMA));
+ zone.setPanorama (groupPanoramaOffset + parsePercentage (regionElement, TX16WxTag.PANORAMA));
double tuning = 0;
final Element soundOffsetsElement = XMLUtils.getChildElementByName (regionElement, TX16WxTag.SOUND_OFFSETS);
@@ -452,9 +459,10 @@ private static void readLoops (final Element sampleElement, final List parseFilter (final Element soundShapeElement)
+ private Optional parseFilter (final Element soundShapeElement, final List modulators)
{
Element filterElement = XMLUtils.getChildElementByName (soundShapeElement, TX16WxTag.FILTER);
if (filterElement == null)
@@ -499,55 +507,40 @@ else if (frequencyValue.endsWith ("hz") || frequencyValue.endsWith ("Hz"))
}
final DefaultFilter filter = new DefaultFilter (filterType, poles, frequency, resonance);
- parseFilterEnvelope (filter, soundShapeElement);
+ parseFilterModulation (filter, soundShapeElement, modulators);
return Optional.of (filter);
}
/**
- * Parse the filter envelope from the modulation section in the sound shape element.
+ * Parse the filter envelope and velocity modulation from the modulation section in the sound
+ * shape element.
*
* @param filter The filter for which to parse the envelope
* @param soundShapeElement The sound shape element
+ * @param modulators The already parsed modulators
*/
- private static void parseFilterEnvelope (final IFilter filter, final Element soundShapeElement)
+ private static void parseFilterModulation (final IFilter filter, final Element soundShapeElement, final List modulators)
{
- final Element modulationElement = XMLUtils.getChildElementByName (soundShapeElement, TX16WxTag.MODULATION);
- if (modulationElement == null)
- return;
- for (final Element modulationEntryElement: XMLUtils.getChildElementsByName (modulationElement, TX16WxTag.MODULATION_ENTRY, false))
+ for (final TX16WxModulator modulator: modulators)
{
- final String modeSource = modulationEntryElement.getAttribute (TX16WxTag.MODULATION_SOURCE);
- final String modDestination = modulationEntryElement.getAttribute (TX16WxTag.MODULATION_DESTINATION);
- final boolean isEnv1 = "ENV1".equals (modeSource);
- final boolean isEnv2 = "ENV2".equals (modeSource);
- if ((isEnv1 || isEnv2) && "Filter 1 Freq".equals (modDestination))
- {
- final String modAmount = modulationEntryElement.getAttribute (TX16WxTag.MODULATION_AMOUNT);
- if (modAmount.endsWith ("Ct"))
- {
- final IEnvelopeModulator cutoffModulator = filter.getCutoffEnvelopeModulator ();
- final double amount = Integer.parseInt (modAmount.substring (0, modAmount.length () - 2).trim ()) / (double) IEnvelope.MAX_ENVELOPE_DEPTH;
- cutoffModulator.setDepth (MathUtils.clamp (amount, -1, 1));
+ if (!modulator.isDestination ("Filter 1 Freq"))
+ continue;
- final Element envElement = XMLUtils.getChildElementByName (soundShapeElement, isEnv1 ? TX16WxTag.ENVELOPE_1 : TX16WxTag.ENVELOPE_2);
- if (envElement != null)
- {
- final IEnvelope envEnvelope = cutoffModulator.getSource ();
- envEnvelope.setAttackTime (parseTime (envElement, TX16WxTag.ENV_TIME1));
- envEnvelope.setDecayTime (parseTime (envElement, TX16WxTag.ENV_TIME2));
- envEnvelope.setReleaseTime (parseTime (envElement, TX16WxTag.ENV_TIME3));
-
- envEnvelope.setStartLevel (parseNormalizedVolume (envElement, TX16WxTag.ENV_LEVEL0));
- envEnvelope.setHoldLevel (parseNormalizedVolume (envElement, TX16WxTag.ENV_LEVEL1));
- envEnvelope.setSustainLevel (parseNormalizedVolume (envElement, TX16WxTag.ENV_LEVEL2));
- envEnvelope.setEndLevel (parseNormalizedVolume (envElement, TX16WxTag.ENV_LEVEL3));
-
- envEnvelope.setAttackSlope (parsePanorama (envElement, TX16WxTag.ENV_SHAPE1));
- envEnvelope.setDecaySlope (parsePanorama (envElement, TX16WxTag.ENV_SHAPE2));
- envEnvelope.setReleaseSlope (parsePanorama (envElement, TX16WxTag.ENV_SHAPE3));
- }
- }
+ final Optional modAmountAsCent = modulator.getModAmountAsCent ();
+ if (modAmountAsCent.isEmpty ())
+ continue;
+ final double amount = Math.clamp (modAmountAsCent.get ().intValue () / (double) IEnvelope.MAX_ENVELOPE_DEPTH, -1, 1);
+
+ if (modulator.isSource ("Vel"))
+ filter.getCutoffVelocityModulator ().setDepth (amount);
+ else if (modulator.isSource ("ENV1", "ENV2"))
+ {
+ final IEnvelopeModulator cutoffModulator = filter.getCutoffEnvelopeModulator ();
+ cutoffModulator.setDepth (amount);
+ final Optional envelope = parseEnvelope (soundShapeElement, modulator.isSource ("ENV1") ? TX16WxTag.ENVELOPE_1 : TX16WxTag.ENVELOPE_2);
+ if (envelope.isPresent ())
+ cutoffModulator.setSource (envelope.get ());
}
}
}
@@ -558,82 +551,88 @@ private static void parseFilterEnvelope (final IFilter filter, final Element sou
* element.
*
* @param soundShapeElement The sound shape element
+ * @param modulators The already parsed modulators
* @return The optional pitch modulator
*/
- private static Optional parsePitchModulator (final Element soundShapeElement)
+ private static Optional parsePitchModulator (final Element soundShapeElement, final List modulators)
{
- final Element modulationElement = XMLUtils.getChildElementByName (soundShapeElement, TX16WxTag.MODULATION);
- if (modulationElement == null)
- return null;
- for (final Element modulationEntryElement: XMLUtils.getChildElementsByName (modulationElement, TX16WxTag.MODULATION_ENTRY, false))
- {
- final String modeSource = modulationEntryElement.getAttribute (TX16WxTag.MODULATION_SOURCE);
- final String modDestination = modulationEntryElement.getAttribute (TX16WxTag.MODULATION_DESTINATION);
- final boolean isEnv1 = "ENV1".equals (modeSource);
- final boolean isEnv2 = "ENV2".equals (modeSource);
- if ((isEnv1 || isEnv2) && ("Pitch".equals (modDestination) || "Pitch (Raw)".equals (modDestination)))
+ for (final TX16WxModulator modulator: modulators)
+ if (modulator.isSource ("ENV1", "ENV2") && modulator.isDestination ("Pitch", "Pitch (Raw)"))
{
- final String modAmount = modulationEntryElement.getAttribute (TX16WxTag.MODULATION_AMOUNT);
- if (modAmount.endsWith ("Ct"))
+ final Optional modAmoundAsCent = modulator.getModAmountAsCent ();
+ if (modAmoundAsCent.isPresent ())
{
final IEnvelopeModulator pitchModulator = new DefaultEnvelopeModulator (0);
- final double amount = Integer.parseInt (modAmount.substring (0, modAmount.length () - 2).trim ()) / 4800.0;
- pitchModulator.setDepth (MathUtils.clamp (amount, -1, 1));
+ final double amount = modAmoundAsCent.get ().intValue () / 4800.0;
+ pitchModulator.setDepth (Math.clamp (amount, -1, 1));
- final Element envElement = XMLUtils.getChildElementByName (soundShapeElement, isEnv1 ? TX16WxTag.ENVELOPE_1 : TX16WxTag.ENVELOPE_2);
- if (envElement != null)
+ final Optional pitchEnvelope = parseEnvelope (soundShapeElement, modulator.isSource ("ENV1") ? TX16WxTag.ENVELOPE_1 : TX16WxTag.ENVELOPE_2);
+ if (pitchEnvelope.isPresent ())
{
- final IEnvelope envEnvelope = pitchModulator.getSource ();
- envEnvelope.setAttackTime (parseTime (envElement, TX16WxTag.ENV_TIME1));
- envEnvelope.setDecayTime (parseTime (envElement, TX16WxTag.ENV_TIME2));
- envEnvelope.setReleaseTime (parseTime (envElement, TX16WxTag.ENV_TIME3));
-
- envEnvelope.setStartLevel (parseNormalizedVolume (envElement, TX16WxTag.ENV_LEVEL0));
- envEnvelope.setHoldLevel (parseNormalizedVolume (envElement, TX16WxTag.ENV_LEVEL1));
- envEnvelope.setSustainLevel (parseNormalizedVolume (envElement, TX16WxTag.ENV_LEVEL2));
- envEnvelope.setEndLevel (parseNormalizedVolume (envElement, TX16WxTag.ENV_LEVEL3));
-
- envEnvelope.setAttackSlope (parsePanorama (envElement, TX16WxTag.ENV_SHAPE1));
- envEnvelope.setDecaySlope (parsePanorama (envElement, TX16WxTag.ENV_SHAPE2));
- envEnvelope.setReleaseSlope (parsePanorama (envElement, TX16WxTag.ENV_SHAPE3));
-
+ pitchModulator.setSource (pitchEnvelope.get ());
return Optional.of (pitchModulator);
}
}
}
- }
return Optional.empty ();
}
+ private static Optional parseEnvelope (final Element parentElement, final String envelopeTag)
+ {
+ final Element envElement = XMLUtils.getChildElementByName (parentElement, envelopeTag);
+ if (envElement == null)
+ return Optional.empty ();
+
+ final IEnvelope envelope = new DefaultEnvelope ();
+ envelope.setAttackTime (parseTime (envElement, TX16WxTag.ENV_TIME1));
+ envelope.setDecayTime (parseTime (envElement, TX16WxTag.ENV_TIME2));
+ envelope.setReleaseTime (parseTime (envElement, TX16WxTag.ENV_TIME3));
+
+ envelope.setStartLevel (parseNormalizedVolume (envElement, TX16WxTag.ENV_LEVEL0));
+ envelope.setHoldLevel (parseNormalizedVolume (envElement, TX16WxTag.ENV_LEVEL1));
+ envelope.setSustainLevel (parseNormalizedVolume (envElement, TX16WxTag.ENV_LEVEL2));
+ envelope.setEndLevel (parseNormalizedVolume (envElement, TX16WxTag.ENV_LEVEL3));
+
+ envelope.setAttackSlope (parsePercentage (envElement, TX16WxTag.ENV_SHAPE1));
+ envelope.setDecaySlope (parsePercentage (envElement, TX16WxTag.ENV_SHAPE2));
+ envelope.setReleaseSlope (parsePercentage (envElement, TX16WxTag.ENV_SHAPE3));
+
+ return Optional.of (envelope);
+ }
+
+
/**
* Parse the pitch-bend from the modulation section in the sound shape element.
*
- * @param soundShapeElement The sound shape element
+ * @param modulators The already parsed modulators
* @return The pitch-bend, negative if not found
*/
- private static int parsePitchbend (final Element soundShapeElement)
+ private static int parsePitchbend (final List modulators)
{
- final Element modulationElement = XMLUtils.getChildElementByName (soundShapeElement, TX16WxTag.MODULATION);
- if (modulationElement == null)
- return -1;
- for (final Element modulationEntryElement: XMLUtils.getChildElementsByName (modulationElement, TX16WxTag.MODULATION_ENTRY, false))
- {
- final String modeSource = modulationEntryElement.getAttribute (TX16WxTag.MODULATION_SOURCE);
- final String modDestination = modulationEntryElement.getAttribute (TX16WxTag.MODULATION_DESTINATION);
- if ("Pitchbend".equals (modeSource) && ("Pitch".equals (modDestination) || "Pitch (raw)".equals (modDestination)))
+ for (final TX16WxModulator modulator: modulators)
+ if (modulator.isSource ("Pitchbend") && modulator.isDestination ("Pitch", "Pitch (raw)"))
{
- final String modAmount = modulationEntryElement.getAttribute (TX16WxTag.MODULATION_AMOUNT);
- if (modAmount.endsWith ("Ct"))
- return Math.abs (Integer.parseInt (modAmount.substring (0, modAmount.length () - 2).trim ()));
+ final Optional modAmoundAsCent = modulator.getModAmountAsCent ();
+ if (modAmoundAsCent.isPresent ())
+ return Math.abs (modAmoundAsCent.get ().intValue ());
}
- }
-
return -1;
}
+ private static List parseModulators (final Element soundShapeElement)
+ {
+ final List modulators = new ArrayList<> ();
+ final Element modulationElement = XMLUtils.getChildElementByName (soundShapeElement, TX16WxTag.MODULATION);
+ if (modulationElement != null)
+ for (final Element modulationEntryElement: XMLUtils.getChildElementsByName (modulationElement, TX16WxTag.MODULATION_ENTRY, false))
+ modulators.add (new TX16WxModulator (modulationEntryElement));
+ return modulators;
+ }
+
+
/**
* Get the value of a note element. The value can be either an integer MIDI note or a text like
* C#5.
@@ -710,18 +709,18 @@ private static double parseNormalizedVolume (final Element element, final String
}
final double value = attribute.endsWith ("%") ? Double.parseDouble (attribute.substring (0, attribute.length () - 1).trim ()) / 100.0 : Double.parseDouble (attribute);
- return MathUtils.clamp (value, 0, 1);
+ return Math.clamp (value, 0, 1);
}
/**
- * Parses a panorama value from the given tag.
+ * Parses a percentage value from the given tag.
*
- * @param element The element which contains the volume attribute
- * @param tag The tag name of the attribute containing the volume
- * @return The panorama in the range of [-1..1]
+ * @param element The element which contains the percentage attribute
+ * @param tag The tag name of the attribute containing the percentage
+ * @return The percentage in the range of [-1..1]
*/
- private static double parsePanorama (final Element element, final String tag)
+ private static double parsePercentage (final Element element, final String tag)
{
String attribute = element.getAttribute (tag);
if (attribute == null)
@@ -738,7 +737,7 @@ private static double parsePanorama (final Element element, final String tag)
else
// The value is in the range of [-1..1]
value = Double.parseDouble (attribute);
- return MathUtils.clamp (value, -1.0, 1.0);
+ return Math.clamp (value, -1.0, 1.0);
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/tx16wx/TX16WxModulator.java b/src/main/java/de/mossgrabers/convertwithmoss/format/tx16wx/TX16WxModulator.java
new file mode 100644
index 0000000..b4fb864
--- /dev/null
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/tx16wx/TX16WxModulator.java
@@ -0,0 +1,81 @@
+// 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.tx16wx;
+
+import java.util.Optional;
+
+import org.w3c.dom.Element;
+
+
+/**
+ * A TX16Wx modulator entry.
+ *
+ * @author Jürgen Moßgraber
+ */
+public class TX16WxModulator
+{
+ private final String source;
+ private final String destination;
+ private final String amount;
+
+
+ /**
+ * Constructor.
+ *
+ * @param modulationEntryElement The modulator entry element
+ */
+ public TX16WxModulator (final Element modulationEntryElement)
+ {
+ this.source = modulationEntryElement.getAttribute (TX16WxTag.MODULATION_SOURCE);
+ this.destination = modulationEntryElement.getAttribute (TX16WxTag.MODULATION_DESTINATION);
+ this.amount = modulationEntryElement.getAttribute (TX16WxTag.MODULATION_AMOUNT);
+ }
+
+
+ /**
+ * Test if one of the given tags matches the source tag of this modulator.
+ *
+ * @param sourceTags The source tags to match
+ * @return True if one of the source tags matches the parsed source tag
+ */
+ public boolean isSource (final String... sourceTags)
+ {
+ return matchTags (this.source, sourceTags);
+ }
+
+
+ /**
+ * Test if one of the given tags matches the destination tag of this modulator.
+ *
+ * @param destinationTags The destination tags to match
+ * @return True if one of the destination tags matches the parsed destination tag
+ */
+ public boolean isDestination (final String... destinationTags)
+ {
+ return matchTags (this.destination, destinationTags);
+ }
+
+
+ /**
+ * Get the modulation amount if its unit is cents ('Ct').
+ *
+ * @return The cents if present and the unit is cents
+ */
+ public Optional getModAmountAsCent ()
+ {
+ if (this.amount != null && this.amount.endsWith ("Ct"))
+ return Optional.of (Integer.valueOf (this.amount.substring (0, this.amount.length () - 2).trim ()));
+ return Optional.empty ();
+ }
+
+
+ private static boolean matchTags (final String tag, final String... tags)
+ {
+ for (final String t: tags)
+ if (t.equals (tag))
+ return true;
+ return false;
+ }
+}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/tx16wx/TX16WxTag.java b/src/main/java/de/mossgrabers/convertwithmoss/format/tx16wx/TX16WxTag.java
index 6592d78..7a0131a 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/tx16wx/TX16WxTag.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/tx16wx/TX16WxTag.java
@@ -96,6 +96,8 @@ public class TX16WxTag
/** The high velocity tag sample attribute. */
public static final String HI_VEL = "tx:high-vel";
+ /** The amplitude velocity modulation tag. */
+ public static final String AMP_VELOCITY = "tx:velocity";
/** The amplitude envelope tag. */
public static final String AMP_ENVELOPE = "tx:aeg";
/** The amplitude envelope attack attribute. */
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/waldorf/qpat/WaldorfQpatCreator.java b/src/main/java/de/mossgrabers/convertwithmoss/format/waldorf/qpat/WaldorfQpatCreator.java
index d4a290b..045298f 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/waldorf/qpat/WaldorfQpatCreator.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/waldorf/qpat/WaldorfQpatCreator.java
@@ -18,7 +18,6 @@
import de.mossgrabers.convertwithmoss.core.IMultisampleSource;
import de.mossgrabers.convertwithmoss.core.INotifier;
-import de.mossgrabers.convertwithmoss.core.MathUtils;
import de.mossgrabers.convertwithmoss.core.creator.AbstractCreator;
import de.mossgrabers.convertwithmoss.core.creator.DestinationAudioFormat;
import de.mossgrabers.convertwithmoss.core.model.IEnvelope;
@@ -204,8 +203,8 @@ private static void storeMultisample (final IMultisampleSource multisampleSource
param.write (out);
// Write resource(s)
- for (int i = 0; i < sampleMaps.size (); i++)
- out.write (sampleMaps.get (i).getBytes ());
+ for (final String sampleMap: sampleMaps)
+ out.write (sampleMap.getBytes ());
}
}
@@ -289,7 +288,7 @@ private static List createSampleMaps (final List groups, final S
/**
* Reduces the groups of the multi-sample to a maximum of 3. The sample zones of all other
* groups are added to the 3rd group.
- *
+ *
* @param groups The groups
* @return The reduced groups
*/
@@ -300,10 +299,8 @@ private static List reduceGroups (final List groups)
// Add all sample zones of groups 4..last to group 3
final IGroup lastGroup = groups.get (2);
for (int i = 3; i < groups.size (); i++)
- {
for (final ISampleZone zone: groups.get (i).getSampleZones ())
lastGroup.addSampleZone (zone);
- }
// Remove groups 4..last
final int count = groups.size () - 3;
for (int i = 0; i < count; i++)
@@ -333,7 +330,7 @@ private static List createParameters (final List g
parameters.add (new WaldorfQpatParameter ("Osc" + groupIndex + "FinePitch", "+0.0 cents", 0.5f));
// Osc1PitchBendRange: [0..48] ~ [-24..24]
- final int pitchbend = MathUtils.clamp (firstZone.getBendUp (), -24, 24);
+ final int pitchbend = Math.clamp (firstZone.getBendUp (), -24, 24);
parameters.add (new WaldorfQpatParameter ("Osc" + groupIndex + "PitchBendRange", (pitchbend < 0 ? "-" : "+") + pitchbend, pitchbend + 24));
// Osc1Keytrack: [0..1] ~ [-200..200]
@@ -350,7 +347,7 @@ private static List createParameters (final List g
if (volumeDB == Double.NEGATIVE_INFINITY)
volumeStr = "-inf dB";
else
- volumeStr = ((volumeDB < 0 ? "-" : "+") + String.format (Locale.US, "%.3f dB", Double.valueOf (volumeDB)));
+ volumeStr = (volumeDB < 0 ? "-" : "+") + String.format (Locale.US, "%.3f dB", Double.valueOf (volumeDB));
parameters.add (new WaldorfQpatParameter ("Osc" + groupIndex + "Vol", volumeStr, (float) volume));
// Osc1Pan: [0..1] ~ [L..R]
@@ -377,7 +374,7 @@ private static List createParameters (final List g
{
createFilterParameters (parameters, firstZone.getFilter ());
- IEnvelopeModulator amplitudeEnvelopeModulator = firstZone.getAmplitudeEnvelopeModulator ();
+ final IEnvelopeModulator amplitudeEnvelopeModulator = firstZone.getAmplitudeEnvelopeModulator ();
final IEnvelope envelope = amplitudeEnvelopeModulator.getSource ();
createEnvelope (parameters, envelope, "AmpEnv", "AmpEnv");
@@ -409,7 +406,7 @@ private static void createPitchEnvelopeModulator (final List parameters,
// xxxEnvAttack
parameters.add (new WaldorfQpatParameter (prefix + "Attack", String.format (Locale.US, FORMAT_SECONDS, Double.valueOf (0)), 0));
// xxxEnvDecay
- final double decayTime = MathUtils.clamp (envelope.getAttackTime (), 0, 60);
- parameters.add (new WaldorfQpatParameter (prefix + "Decay", String.format (Locale.US, FORMAT_SECONDS, Double.valueOf (decayTime)), (float) (convertFromTime (decayTime))));
+ final double decayTime = Math.clamp (envelope.getAttackTime (), 0, 60);
+ parameters.add (new WaldorfQpatParameter (prefix + "Decay", String.format (Locale.US, FORMAT_SECONDS, Double.valueOf (decayTime)), (float) convertFromTime (decayTime)));
}
else
{
// xxxEnvDelay
- final double delayTime = MathUtils.clamp (envelope.getDelayTime (), 0, 2);
- parameters.add (new WaldorfQpatParameter (prefix + "Delay", String.format (Locale.US, FORMAT_SECONDS, Double.valueOf (delayTime)), (float) (convertFromDelayTime (delayTime))));
+ final double delayTime = Math.clamp (envelope.getDelayTime (), 0, 2);
+ parameters.add (new WaldorfQpatParameter (prefix + "Delay", String.format (Locale.US, FORMAT_SECONDS, Double.valueOf (delayTime)), (float) convertFromDelayTime (delayTime)));
// xxxEnvAttack
- final double attackTime = MathUtils.clamp (envelope.getAttackTime (), 0, 60);
- parameters.add (new WaldorfQpatParameter (prefix + "Attack", String.format (Locale.US, FORMAT_SECONDS, Double.valueOf (attackTime)), (float) (convertFromTime (attackTime))));
+ final double attackTime = Math.clamp (envelope.getAttackTime (), 0, 60);
+ parameters.add (new WaldorfQpatParameter (prefix + "Attack", String.format (Locale.US, FORMAT_SECONDS, Double.valueOf (attackTime)), (float) convertFromTime (attackTime)));
// xxxEnvDecay
- final double decayTime = MathUtils.clamp (envelope.getDecayTime (), 0, 60);
- parameters.add (new WaldorfQpatParameter (prefix + "Decay", String.format (Locale.US, FORMAT_SECONDS, Double.valueOf (decayTime)), (float) (convertFromTime (decayTime))));
+ final double decayTime = Math.clamp (envelope.getDecayTime (), 0, 60);
+ parameters.add (new WaldorfQpatParameter (prefix + "Decay", String.format (Locale.US, FORMAT_SECONDS, Double.valueOf (decayTime)), (float) convertFromTime (decayTime)));
}
// xxxEnvRelease
- final double releaseTime = MathUtils.clamp (envelope.getReleaseTime (), 0, 60);
- parameters.add (new WaldorfQpatParameter (prefix + "Release", String.format (Locale.US, FORMAT_SECONDS, Double.valueOf (releaseTime)), (float) (convertFromTime (releaseTime))));
+ final double releaseTime = Math.clamp (envelope.getReleaseTime (), 0, 60);
+ parameters.add (new WaldorfQpatParameter (prefix + "Release", String.format (Locale.US, FORMAT_SECONDS, Double.valueOf (releaseTime)), (float) convertFromTime (releaseTime)));
// xxxEnvSustain
double sustainLevel = envelope.getSustainLevel ();
if (sustainLevel == -1)
sustainLevel = isPitch ? 0 : 1;
- parameters.add (new WaldorfQpatParameter (prefix + "Sustain", String.format (Locale.US, "%.2f", Double.valueOf (sustainLevel * 100.0)) + " %", (float) (sustainLevel)));
+ parameters.add (new WaldorfQpatParameter (prefix + "Sustain", String.format (Locale.US, "%.2f", Double.valueOf (sustainLevel * 100.0)) + " %", (float) sustainLevel));
if (isPitch && envelope.getStartLevel () != 0)
{
@@ -625,7 +622,7 @@ private static double convertFromDecibels (final double db)
{
if (db == Double.NEGATIVE_INFINITY)
return 0;
- return MathUtils.clamp (Math.pow (10, db / 40), 0, 1);
+ return Math.clamp (Math.pow (10, db / 40), 0, 1);
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/waldorf/qpat/WaldorfQpatDetectorTask.java b/src/main/java/de/mossgrabers/convertwithmoss/format/waldorf/qpat/WaldorfQpatDetectorTask.java
index a003cbc..1a738f0 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/waldorf/qpat/WaldorfQpatDetectorTask.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/waldorf/qpat/WaldorfQpatDetectorTask.java
@@ -21,7 +21,6 @@
import de.mossgrabers.convertwithmoss.core.IMultisampleSource;
import de.mossgrabers.convertwithmoss.core.INotifier;
-import de.mossgrabers.convertwithmoss.core.MathUtils;
import de.mossgrabers.convertwithmoss.core.detector.AbstractDetectorTask;
import de.mossgrabers.convertwithmoss.core.detector.DefaultMultisampleSource;
import de.mossgrabers.convertwithmoss.core.model.IEnvelope;
@@ -123,7 +122,7 @@ protected List readFile (final File file)
*/
private List parseFile (final InputStream in, final File file) throws FormatException, IOException
{
- String name = FileUtils.getNameWithoutType (file);
+ final String name = FileUtils.getNameWithoutType (file);
final String [] parts = AudioFileUtils.createPathParts (file.getParentFile (), this.sourceFolder, name);
in.mark (in.available () + 1);
@@ -213,7 +212,7 @@ private List parseFile (final InputStream in, final File fil
/**
* Read all 3 resource headers.
- *
+ *
* @param in The input stream to read from
* @return The resource headers
* @throws IOException Could not read the headers
@@ -273,7 +272,7 @@ private void applyParameters (final IGroup [] groups, final Map findPitchEnvelopeModMatrixEntry (fin
{
// MatrixDstX: [2] "Osc1 Pitch" [3] "Osc2 Pitch" [4] "Osc3 Pitch"
final WaldorfQpatParameter destParam = parameters.get ("MatrixDst" + i);
- if (destParam != null && (destParam.value == oscIndex + 1.0))
+ if (destParam != null && destParam.value == oscIndex + 1.0)
{
// MatrixAmount1
final WaldorfQpatParameter amountParam = parameters.get ("MatrixAmount" + i);
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/waldorf/qpat/WaldorfQpatParameter.java b/src/main/java/de/mossgrabers/convertwithmoss/format/waldorf/qpat/WaldorfQpatParameter.java
index 285eb75..052694c 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/waldorf/qpat/WaldorfQpatParameter.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/waldorf/qpat/WaldorfQpatParameter.java
@@ -25,7 +25,7 @@ public class WaldorfQpatParameter
/**
* Constructor.
- *
+ *
* @param name The name of the parameter
* @param hint The descriptive text of the value
* @param value The value of the parameter
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/wav/WavFileSampleData.java b/src/main/java/de/mossgrabers/convertwithmoss/format/wav/WavFileSampleData.java
index 670d677..258ae77 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/wav/WavFileSampleData.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/wav/WavFileSampleData.java
@@ -13,7 +13,6 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
-import de.mossgrabers.convertwithmoss.core.MathUtils;
import de.mossgrabers.convertwithmoss.core.model.IMetadata;
import de.mossgrabers.convertwithmoss.core.model.ISampleLoop;
import de.mossgrabers.convertwithmoss.core.model.ISampleZone;
@@ -171,7 +170,7 @@ public void addZoneData (final ISampleZone zone, final boolean addRootKey, final
if (zone.getTune () == 0)
{
- final double tune = MathUtils.clamp (sampleChunk.getMIDIPitchFractionAsCents () / 100.0, -0.5, 0.5);
+ final double tune = Math.clamp (sampleChunk.getMIDIPitchFractionAsCents () / 100.0, -0.5, 0.5);
zone.setTune (tune);
// Root note needs to be updated as well!
if (tune < 0)
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/ui/ConvertWithMossApp.java b/src/main/java/de/mossgrabers/convertwithmoss/ui/ConvertWithMossApp.java
index bfb2d92..131ce15 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/ui/ConvertWithMossApp.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/ui/ConvertWithMossApp.java
@@ -49,8 +49,6 @@
import de.mossgrabers.convertwithmoss.format.tal.TALSamplerDetector;
import de.mossgrabers.convertwithmoss.format.tx16wx.TX16WxCreator;
import de.mossgrabers.convertwithmoss.format.tx16wx.TX16WxDetector;
-import de.mossgrabers.convertwithmoss.format.waldorf.qpat.WaldorfQpatCreator;
-import de.mossgrabers.convertwithmoss.format.waldorf.qpat.WaldorfQpatDetector;
import de.mossgrabers.convertwithmoss.format.wav.WavCreator;
import de.mossgrabers.convertwithmoss.format.wav.WavDetector;
import de.mossgrabers.tools.ui.AbstractFrame;
@@ -168,7 +166,7 @@ public ConvertWithMossApp () throws EndApplicationException
new SfzDetector (this),
new Sf2Detector (this),
new TALSamplerDetector (this),
- new WaldorfQpatDetector (this),
+ // new WaldorfQpatDetector (this),
new WavDetector (this)// ,
// new YamahaYsfcDetector (this)
};
@@ -190,7 +188,7 @@ public ConvertWithMossApp () throws EndApplicationException
new SfzCreator (this),
new Sf2Creator (this),
new TALSamplerCreator (this),
- new WaldorfQpatCreator (this),
+ // new WaldorfQpatCreator (this),
new WavCreator (this)
};
}
diff --git a/src/main/resources/Strings.properties b/src/main/resources/Strings.properties
index 1aac0bc..61b6435 100644
--- a/src/main/resources/Strings.properties
+++ b/src/main/resources/Strings.properties
@@ -1,4 +1,4 @@
-TITLE=ConvertWithMoss 10.5.0
+TITLE=ConvertWithMoss 10.2.0
##################################################################################
#
diff --git a/src/main/resources/de/mossgrabers/convertwithmoss/templates/nki/Kontakt1_02_Group.xml b/src/main/resources/de/mossgrabers/convertwithmoss/templates/nki/Kontakt1_02_Group.xml
index 74f17e9..7e210bf 100644
--- a/src/main/resources/de/mossgrabers/convertwithmoss/templates/nki/Kontakt1_02_Group.xml
+++ b/src/main/resources/de/mossgrabers/convertwithmoss/templates/nki/Kontakt1_02_Group.xml
@@ -99,5 +99,12 @@
+
+
+
+
+
+
+