diff --git a/CHANGELOG.md b/CHANGELOG.md
index d834854..804ca54 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,14 @@
# Changes
-## 10.0.1 (unreleased)
+## 10.1.0
-* disting EX
+* All formats
+ * Fixed: Increased the heap memory to 64GB to support larger source files.
+ * Fixed: WAV files in 32-bit float can now be converted to 16-bit PCM (workaround for bug in Java AudioSystem).
+* 1010music format - Writing
+ * New: Added an option to trim samples with a delayed start.
+* disting EX - Writing
+ * New: Added an option to trim samples with a delayed start.
* Fixed: The MIDI note for the switch (SW) was off by 1 octave (disting assumes C3 as MIDI note 48 instead of 60). This caused playback issues.
* Fixed: Release trigger groups are now removed from the output since the distingEX does not support release triggers.
* SFZ
diff --git a/README-FORMATS.md b/README-FORMATS.md
index e42dc10..6994160 100644
--- a/README-FORMATS.md
+++ b/README-FORMATS.md
@@ -57,6 +57,7 @@ There are no metadata fields (category, creator, etc.) specified in the format.
### Destination Options
* Option to set the *Interpolation Quality*. Setting it to *High* requires a bit more processing power on the 1010music devices.
+* Option to trim sample to range of zone start to end. Since the format does not support a sample start attribute for multi-sample, this fixes the issue.
* Options to write/update [WAV Chunk Information](#wav-chunk-information)
## AIFF
@@ -155,6 +156,7 @@ The basic multi-sample setup is encoded in the file-names of the samples. Furthe
### Destination Options
* 'Limit sample resolution and rate to 16bit/44.1kHz': If enabled samples of a high resolution will be resampled to 16bit and 44.1kHz. While the device can play higher resolutions as well it decrease the number of voices it can play.
+* Option to trim sample to range of zone start to end. Since the format does not support a sample start attribute, this fixes the issue.
* Options to write/update [WAV Chunk Information](#wav-chunk-information). Writing the Sample chunk is important since the disting EX reads the loop information from it.
## Korg KMP/KSF
diff --git a/pom.xml b/pom.xml
index e2139da..dc3892e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
4.0.0
de.mossgrabers
convertwithmoss
- 10.0.0
+ 10.1.0
jar
ConvertWithMoss
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 1b4cada..280e277 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/core/creator/AbstractCreator.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/core/creator/AbstractCreator.java
@@ -42,6 +42,8 @@
import de.mossgrabers.convertwithmoss.file.AudioFileUtils;
import de.mossgrabers.convertwithmoss.file.riff.RiffID;
import de.mossgrabers.convertwithmoss.file.wav.BroadcastAudioExtensionChunk;
+import de.mossgrabers.convertwithmoss.file.wav.DataChunk;
+import de.mossgrabers.convertwithmoss.file.wav.FormatChunk;
import de.mossgrabers.convertwithmoss.file.wav.InstrumentChunk;
import de.mossgrabers.convertwithmoss.file.wav.SampleChunk;
import de.mossgrabers.convertwithmoss.file.wav.SampleChunk.SampleChunkLoop;
@@ -450,7 +452,23 @@ protected List writeSamples (final File sampleFolder, final IMultisampleSo
*/
protected List writeSamples (final File sampleFolder, final IMultisampleSource multisampleSource, final DestinationAudioFormat destinationFormat) throws IOException
{
- return this.writeSamples (sampleFolder, multisampleSource, ".wav", destinationFormat);
+ return this.writeSamples (sampleFolder, multisampleSource, ".wav", destinationFormat, false);
+ }
+
+
+ /**
+ * Writes all samples in WAV format from all groups into the given folder.
+ *
+ * @param sampleFolder The destination folder
+ * @param multisampleSource The multi-sample
+ * @param destinationFormat The destination audio format
+ * @param trim Trim the sample from zone start to end if enabled
+ * @return The written files
+ * @throws IOException Could not store the samples
+ */
+ protected List writeSamples (final File sampleFolder, final IMultisampleSource multisampleSource, final DestinationAudioFormat destinationFormat, final boolean trim) throws IOException
+ {
+ return this.writeSamples (sampleFolder, multisampleSource, ".wav", destinationFormat, trim);
}
@@ -465,7 +483,7 @@ protected List writeSamples (final File sampleFolder, final IMultisampleSo
*/
protected List writeSamples (final File sampleFolder, final IMultisampleSource multisampleSource, final String fileEnding) throws IOException
{
- return this.writeSamples (sampleFolder, multisampleSource, fileEnding, DESTINATION_FORMAT);
+ return this.writeSamples (sampleFolder, multisampleSource, fileEnding, DESTINATION_FORMAT, false);
}
@@ -476,10 +494,11 @@ protected List writeSamples (final File sampleFolder, final IMultisampleSo
* @param multisampleSource The multi-sample
* @param fileEnding The suffix to use for the file
* @param destinationFormat The destination audio format
+ * @param trim Trim the sample from zone start to end if enabled
* @return The written files
* @throws IOException Could not store the samples
*/
- protected List writeSamples (final File sampleFolder, final IMultisampleSource multisampleSource, final String fileEnding, final DestinationAudioFormat destinationFormat) throws IOException
+ protected List writeSamples (final File sampleFolder, final IMultisampleSource multisampleSource, final String fileEnding, final DestinationAudioFormat destinationFormat, final boolean trim) throws IOException
{
final List writtenFiles = new ArrayList<> ();
@@ -499,8 +518,8 @@ protected List writeSamples (final File sampleFolder, final IMultisampleSo
if (outputCount % 80 == 0)
this.notifyNewline ();
- if (this.requiresRewrite (destinationFormat))
- this.rewriteFile (multisampleSource.getMetadata (), zone, fos, destinationFormat);
+ if (this.requiresRewrite (destinationFormat) || trim)
+ this.rewriteFile (multisampleSource.getMetadata (), zone, fos, destinationFormat, trim);
else
{
final ISampleData sampleData = zone.getSampleData ();
@@ -612,25 +631,30 @@ protected static void recalculateSamplePositions (final IMultisampleSource multi
* @param zone The zone from which to take the data to store into the chunks
* @param outputStream Where to write the result
* @param destinationFormat The destination audio format
+ * @param trim Trim the sample from zone start to end if enabled
* @throws IOException Could not store the samples
*/
- private void rewriteFile (final IMetadata metadata, final ISampleZone zone, final OutputStream outputStream, final DestinationAudioFormat destinationFormat) throws IOException
+ private void rewriteFile (final IMetadata metadata, final ISampleZone zone, final OutputStream outputStream, final DestinationAudioFormat destinationFormat, final boolean trim) throws IOException
{
final ISampleData sampleData = zone.getSampleData ();
if (sampleData == null)
return;
+
+ // Convert resolution
final WaveFile wavFile = AudioFileUtils.convertToWav (sampleData, destinationFormat);
+ // Trim sample from zone start to end
+ if (trim)
+ trimStartToEnd (wavFile, zone);
+
+ // Update information chunks
if (this.isUpdateBroadcastAudioChunk ())
updateBroadcastAudioChunk (metadata, wavFile);
-
final int unityNote = MathUtils.clamp (zone.getKeyRoot (), 0, 127);
if (this.isUpdateInstrumentChunk ())
updateInstrumentChunk (zone, wavFile, unityNote);
-
if (this.isUpdateSampleChunk ())
updateSampleChunk (zone, wavFile, unityNote);
-
if (this.isRemoveJunkChunks ())
wavFile.removeChunks (RiffID.JUNK_ID, RiffID.JUNK2_ID, RiffID.FILLER_ID, RiffID.MD5_ID);
@@ -638,6 +662,47 @@ 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
+ */
+ private static void trimStartToEnd (final WaveFile wavFile, final ISampleZone zone)
+ {
+ final FormatChunk formatChunk = wavFile.getFormatChunk ();
+
+ // Create the truncated data array
+ final DataChunk dataChunk = wavFile.getDataChunk ();
+ final byte [] data = dataChunk.getData ();
+ final int start = zone.getStart ();
+ final int stop = zone.getStop ();
+ final int lengthInSamples = stop - start;
+ final int numBytesPerSample = formatChunk.calculateBytesPerSample ();
+ final int startByte = start * numBytesPerSample;
+ final int newLength = lengthInSamples * numBytesPerSample;
+ final byte [] truncatedData = new byte [newLength];
+ System.arraycopy (data, startByte, truncatedData, 0, Math.min (newLength, data.length - startByte));
+
+ // Replace the previous data chunk
+ final DataChunk truncatedDataChunk = new DataChunk (formatChunk, lengthInSamples);
+ truncatedDataChunk.setData (truncatedData);
+ wavFile.setDataChunk (truncatedDataChunk);
+
+ // Update the zone values - necessary for follow-up instrument/sample chunks!
+ zone.setStart (0);
+ zone.setStop (lengthInSamples);
+ final List loops = zone.getLoops ();
+ if (!loops.isEmpty ())
+ {
+ final ISampleLoop loop = loops.get (0);
+ loop.setStart (Math.max (loop.getStart () - start, 0));
+ loop.setEnd (Math.min (loop.getEnd () - start, lengthInSamples));
+ }
+ }
+
+
private static void updateSampleChunk (final ISampleZone zone, final WaveFile wavFile, final int unityNote)
{
final List loops = zone.getLoops ();
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/file/AudioFileUtils.java b/src/main/java/de/mossgrabers/convertwithmoss/file/AudioFileUtils.java
index c16fee3..8b99bd3 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/file/AudioFileUtils.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/file/AudioFileUtils.java
@@ -11,6 +11,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
@@ -252,7 +254,13 @@ public static WaveFile convertToWav (final ISampleData sampleData, final Destina
*/
private static byte [] convertToWav (final byte [] inputData, final DestinationAudioFormat destinationFormat) throws IOException
{
- try (final AudioInputStream audioInputStream = AudioSystem.getAudioInputStream (new ByteArrayInputStream (inputData)))
+ return convertToWav (new ByteArrayInputStream (inputData), destinationFormat);
+ }
+
+
+ private static byte [] convertToWav (final InputStream inputStream, final DestinationAudioFormat destinationFormat) throws IOException
+ {
+ try (final AudioInputStream audioInputStream = AudioSystem.getAudioInputStream (inputStream))
{
final AudioFormat audioFormat = audioInputStream.getFormat ();
final int bitResolution = getMatchingBitResolution (audioFormat.getSampleSizeInBits (), destinationFormat.getBitResolutions ());
@@ -262,21 +270,20 @@ public static WaveFile convertToWav (final ISampleData sampleData, final Destina
if (maxSampleRate != -1 && (sampleRate > maxSampleRate || destinationFormat.isUpSample ()))
sampleRate = maxSampleRate;
- final AudioFormat newAudioFormat = new AudioFormat (sampleRate, bitResolution, audioFormat.getChannels (), audioFormat.getEncoding () == Encoding.PCM_SIGNED, audioFormat.isBigEndian ());
- File tempFile = null;
- try (final AudioInputStream convertedAudioInputStream = AudioSystem.getAudioInputStream (newAudioFormat, audioInputStream))
- {
- // Cannot write to a stream since the length is not known and therefore the WAV
- // header cannot be written and write method crashes
- tempFile = File.createTempFile ("wav", "tmp");
- AudioSystem.write (convertedAudioInputStream, AudioFileFormat.Type.WAVE, tempFile);
- return Files.readAllBytes (tempFile.toPath ());
- }
- finally
+ final Encoding encoding = audioFormat.getEncoding ();
+ final boolean is32BitFloat = encoding == Encoding.PCM_FLOAT && audioFormat.getSampleSizeInBits () == 32;
+ final AudioFormat newAudioFormat = new AudioFormat (sampleRate, is32BitFloat ? 16 : bitResolution, audioFormat.getChannels (), encoding == Encoding.PCM_SIGNED || is32BitFloat, audioFormat.isBigEndian ());
+
+ // AudioSystem handles 32bit float values incorrect. We need our own implementation.
+ if (is32BitFloat)
{
- if (tempFile != null)
- tempFile.delete ();
+ try (AudioInputStream convertedAudioInputStream = convertAudioStreamFrom32BitFloatTo16BitPCM (audioInputStream, audioFormat, newAudioFormat))
+ {
+ return doConvertToWav (convertedAudioInputStream, newAudioFormat);
+ }
}
+
+ return doConvertToWav (audioInputStream, newAudioFormat);
}
catch (final UnsupportedAudioFileException ex)
{
@@ -285,6 +292,46 @@ public static WaveFile convertToWav (final ISampleData sampleData, final Destina
}
+ private static AudioInputStream convertAudioStreamFrom32BitFloatTo16BitPCM (final AudioInputStream inputStream, final AudioFormat sourceAudioFormat, final AudioFormat destinationAudioFormat) throws IOException
+ {
+ if (destinationAudioFormat.getSampleSizeInBits () != 16)
+ throw new IOException (Functions.getMessage ("IDS_WAV_ONLY_16_BIT_SUPPORTED", Integer.toString (destinationAudioFormat.getSampleSizeInBits ())));
+
+ final byte [] sourceData = inputStream.readAllBytes ();
+ final ByteBuffer inputBuffer = ByteBuffer.wrap (sourceData).order (sourceAudioFormat.isBigEndian () ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
+ final ByteBuffer outputBuffer = ByteBuffer.allocate (sourceData.length / 2).order (destinationAudioFormat.isBigEndian () ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
+
+ for (int i = 0; i < sourceData.length; i += 4)
+ {
+ float floatValue = inputBuffer.getFloat (i);
+
+ // Convert float to 16-bit PCM
+ outputBuffer.putShort ((short) (floatValue * Short.MAX_VALUE));
+ }
+
+ return new AudioInputStream (new ByteArrayInputStream (outputBuffer.array ()), destinationAudioFormat, inputStream.getFrameLength ());
+ }
+
+
+ private static byte [] doConvertToWav (final AudioInputStream audioInputStream, final AudioFormat newAudioFormat) throws IOException
+ {
+ File tempFile = null;
+ try (final AudioInputStream convertedAudioInputStream = AudioSystem.getAudioInputStream (newAudioFormat, audioInputStream))
+ {
+ // Cannot write to a stream since the length is not known and therefore the WAV
+ // header cannot be written and write method crashes
+ tempFile = File.createTempFile ("wav", "tmp");
+ AudioSystem.write (convertedAudioInputStream, AudioFileFormat.Type.WAVE, tempFile);
+ return Files.readAllBytes (tempFile.toPath ());
+ }
+ finally
+ {
+ if (tempFile != null)
+ tempFile.delete ();
+ }
+ }
+
+
/**
* De-compresses the input file and writes audio data in WAV format to the given output stream.
*
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/file/wav/DataChunk.java b/src/main/java/de/mossgrabers/convertwithmoss/file/wav/DataChunk.java
index 4a38c59..11843db 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/file/wav/DataChunk.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/file/wav/DataChunk.java
@@ -25,7 +25,7 @@ public class DataChunk extends RIFFChunk
*/
public DataChunk (final FormatChunk formatChunk, final int lengthInSamples)
{
- super (RiffID.DATA_ID, new byte [calculateDataSize (formatChunk, lengthInSamples)], calculateDataSize (formatChunk, lengthInSamples));
+ super (RiffID.DATA_ID, new byte [formatChunk.calculateDataSize (lengthInSamples)], formatChunk.calculateDataSize (lengthInSamples));
}
@@ -54,48 +54,20 @@ public int calculateLength (final FormatChunk formatChunk) throws CompressionNot
final int compressionCode = formatChunk.getCompressionCode ();
if (compressionCode == FormatChunk.WAVE_FORMAT_PCM || compressionCode == FormatChunk.WAVE_FORMAT_IEEE_FLOAT)
- return calculateLength (formatChunk, this.getData ());
+ return formatChunk.calculateLength (this.getData ());
if (compressionCode == FormatChunk.WAVE_FORMAT_EXTENSIBLE)
{
final int numberOfChannels = formatChunk.getNumberOfChannels ();
if (numberOfChannels > 2)
throw new CompressionNotSupportedException ("WAV files in Extensible format are only supported for stereo files.");
- return calculateLength (formatChunk, this.getData ());
+ return formatChunk.calculateLength (this.getData ());
}
throw new CompressionNotSupportedException ("Unsupported data compression: " + FormatChunk.getCompression (compressionCode));
}
- /**
- * Calculates the length of the data in samples.
- *
- * @param chunk The format chunk, necessary for the calculation (sample size and number of
- * channels
- * @param data The data
- * @return The length of the sample in samples (frames) of 1 channel
- */
- private static int calculateLength (final FormatChunk chunk, final byte [] data)
- {
- return data.length / (chunk.getNumberOfChannels () * chunk.getSignicantBitsPerSample () / 8);
- }
-
-
- /**
- * Calculates the data size.
- *
- * @param chunk The format chunk, necessary for the calculation (sample size and number of
- * channels
- * @param lengthInSamples The length of the sample (number of samples)
- * @return The size of the data block
- */
- private static int calculateDataSize (final FormatChunk chunk, final int lengthInSamples)
- {
- return lengthInSamples * (chunk.getNumberOfChannels () * chunk.getSignicantBitsPerSample () / 8);
- }
-
-
/** {@inheritDoc} */
@Override
public String infoText ()
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 05ef222..667f438 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/file/wav/FormatChunk.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/file/wav/FormatChunk.java
@@ -101,7 +101,7 @@ public FormatChunk (final int numberOfChannels, final int sampleRate, final int
this.setCompressionCode (WAVE_FORMAT_PCM);
this.setNumberOfChannels (numberOfChannels);
this.setSampleRate (sampleRate);
- this.setSignicantBitsPerSample (bitsPerSample);
+ this.setSignificantBitsPerSample (bitsPerSample);
}
@@ -251,7 +251,7 @@ public int getBlockAlign ()
*/
private void updateBlockAlign ()
{
- final int bitsPerSample = this.getSignicantBitsPerSample ();
+ final int bitsPerSample = this.getSignificantBitsPerSample ();
final int numberOfChannels = this.getNumberOfChannels ();
final int blockAlign = bitsPerSample / 8 * numberOfChannels;
this.setIntAsTwoBytes (0x0C, blockAlign);
@@ -269,7 +269,7 @@ private void updateBlockAlign ()
*
* @return The four bytes converted to an integer
*/
- public int getSignicantBitsPerSample ()
+ public int getSignificantBitsPerSample ()
{
if (0x0E < this.getData ().length)
return this.getTwoBytesAsInt (0x0E);
@@ -286,7 +286,7 @@ public int getSignicantBitsPerSample ()
*
* @param bitsPerSample The bits per sample
*/
- public void setSignicantBitsPerSample (final int bitsPerSample)
+ public void setSignificantBitsPerSample (final int bitsPerSample)
{
this.setIntAsTwoBytes (0x0E, bitsPerSample);
@@ -294,6 +294,42 @@ public void setSignicantBitsPerSample (final int bitsPerSample)
}
+ /**
+ * Calculates the length of the data in samples.
+ *
+ * @param data The data
+ * @return The length of the sample in samples (frames) of 1 channel
+ */
+ public int calculateLength (final byte [] data)
+ {
+ return data.length / calculateBytesPerSample ();
+ }
+
+
+ /**
+ * Calculates the data size.
+ *
+ * @param lengthInSamples The length of the sample (number of samples)
+ * @return The size of the data block
+ */
+ public int calculateDataSize (final int lengthInSamples)
+ {
+ return lengthInSamples * 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 ()
+ {
+ return this.getNumberOfChannels () * this.getSignificantBitsPerSample () / 8;
+ }
+
+
/** {@inheritDoc} */
@Override
public String infoText ()
@@ -304,7 +340,7 @@ public String infoText ()
sb.append ("Sample Rate: ").append (this.getSampleRate ()).append ('\n');
sb.append ("Average bytes per second: ").append (this.getAverageBytesPerSecond ()).append ('\n');
sb.append ("Block align: ").append (this.getBlockAlign ()).append ('\n');
- sb.append ("Significant bits per sample: ").append (this.getSignicantBitsPerSample ()).append ('\n');
+ sb.append ("Significant bits per sample: ").append (this.getSignificantBitsPerSample ()).append ('\n');
sb.append ("Extra bytes: ").append (this.getSize () - CHUNK_SIZE);
return sb.toString ();
}
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 51c4b4e..39b1118 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/file/wav/WaveFile.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/file/wav/WaveFile.java
@@ -130,11 +130,9 @@ public BroadcastAudioExtensionChunk getBroadcastAudioExtensionChunk ()
*/
public void setBroadcastAudioExtensionChunk (final BroadcastAudioExtensionChunk broadcastAudioExtensionChunk)
{
- if (this.broadcastAudioExtensionChunk != null)
- this.chunkStack.remove (this.broadcastAudioExtensionChunk);
this.broadcastAudioExtensionChunk = broadcastAudioExtensionChunk;
- if (this.broadcastAudioExtensionChunk != null)
- this.chunkStack.add (this.broadcastAudioExtensionChunk);
+ this.chunkStack.clear ();
+ fillChunkStack ();
}
@@ -156,11 +154,9 @@ public InstrumentChunk getInstrumentChunk ()
*/
public void setInstrumentChunk (final InstrumentChunk instrumentChunk)
{
- if (this.instrumentChunk != null)
- this.chunkStack.remove (this.instrumentChunk);
this.instrumentChunk = instrumentChunk;
- if (this.instrumentChunk != null)
- this.chunkStack.add (this.instrumentChunk);
+ this.chunkStack.clear ();
+ fillChunkStack ();
}
@@ -182,11 +178,9 @@ public SampleChunk getSampleChunk ()
*/
public void setSampleChunk (final SampleChunk sampleChunk)
{
- if (this.sampleChunk != null)
- this.chunkStack.remove (this.sampleChunk);
this.sampleChunk = sampleChunk;
- if (this.sampleChunk != null)
- this.chunkStack.add (this.sampleChunk);
+ this.chunkStack.clear ();
+ fillChunkStack ();
}
@@ -201,6 +195,19 @@ public FormatChunk getFormatChunk ()
}
+ /**
+ * Get the data chunk.
+ *
+ * @param dataChunk The data chunk
+ */
+ public void setDataChunk (final DataChunk dataChunk)
+ {
+ this.dataChunk = dataChunk;
+ this.chunkStack.clear ();
+ fillChunkStack ();
+ }
+
+
/**
* Get the data chunk if present in the WAV file.
*
@@ -240,7 +247,7 @@ public void combine (final WaveFile otherWave) throws CombinationNotPossibleExce
final int length = Math.max (leftData.length, rightData.length);
final byte [] combinedData = new byte [length * 2];
- final int blockSize = this.formatChunk.getSignicantBitsPerSample () / 8;
+ final int blockSize = this.formatChunk.getSignificantBitsPerSample () / 8;
for (int count = 0; count < length; count += blockSize)
{
if (count < leftData.length)
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 5c741ec..bff96d6 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/disting/DistingExCreator.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/disting/DistingExCreator.java
@@ -46,6 +46,7 @@
public class DistingExCreator extends WavCreator
{
private static final String DEX_LIMIT_TO_16_441 = "DistingLimitTo16441";
+ private static final String DEX_TRIM_START_TO_END = "DistingTrimStartToEnd";
private static final DestinationAudioFormat OPTIMIZED_AUDIO_FORMAT = new DestinationAudioFormat (new int []
{
16
@@ -55,6 +56,7 @@ public class DistingExCreator extends WavCreator
private final Map velocityLayerIndices = new HashMap<> ();
private String filenamePrefix;
private CheckBox limitTo16441;
+ private CheckBox trimStartToEnd;
/**
@@ -77,6 +79,7 @@ public Node getEditPane ()
panel.createSeparator ("@IDS_DEX_SEPARATOR");
this.limitTo16441 = panel.createCheckBox ("@IDS_DEX_LIMIT_TO_16_441");
+ this.trimStartToEnd = panel.createCheckBox ("@IDS_DEX_TRIM_START_TO_END");
final TitledSeparator separator = this.addWavChunkOptions (panel);
separator.getStyleClass ().add ("titled-separator-pane");
@@ -90,6 +93,7 @@ public Node getEditPane ()
public void loadSettings (final BasicConfig config)
{
this.limitTo16441.setSelected (config.getBoolean (DEX_LIMIT_TO_16_441, true));
+ this.trimStartToEnd.setSelected (config.getBoolean (DEX_TRIM_START_TO_END, true));
this.loadWavChunkSettings (config, "Disting");
}
@@ -100,6 +104,7 @@ public void loadSettings (final BasicConfig config)
public void saveSettings (final BasicConfig config)
{
config.setBoolean (DEX_LIMIT_TO_16_441, this.limitTo16441.isSelected ());
+ config.setBoolean (DEX_TRIM_START_TO_END, this.trimStartToEnd.isSelected ());
this.saveWavChunkSettings (config, "Disting");
}
@@ -109,6 +114,8 @@ public void saveSettings (final BasicConfig config)
@Override
public void create (final File destinationFolder, final IMultisampleSource multisampleSource) throws IOException
{
+ final boolean trim = this.trimStartToEnd.isSelected ();
+
this.prepareKeyAndVelocityRanges (multisampleSource);
final String sampleName = createSafeFilename (multisampleSource.getName ());
@@ -121,6 +128,8 @@ public void create (final File destinationFolder, final IMultisampleSource multi
this.notifier.logError ("IDS_NOTIFY_ALREADY_EXISTS", multiFile.getAbsolutePath ());
return;
}
+ // Note: trim doesn't need to be used in the preset since the loop information is stored in
+ // the sample chunk!
storeMultisample (multisampleSource, multiFile, safeSampleFolderName);
this.notifier.log ("IDS_NOTIFY_STORING", safeSampleFolderName);
@@ -132,7 +141,7 @@ public void create (final File destinationFolder, final IMultisampleSource multi
final boolean doLimit = this.limitTo16441.isSelected ();
if (doLimit)
recalculateSamplePositions (multisampleSource, 44100);
- this.writeSamples (sampleFolder, multisampleSource, doLimit ? OPTIMIZED_AUDIO_FORMAT : DEFEAULT_AUDIO_FORMAT);
+ this.writeSamples (sampleFolder, multisampleSource, doLimit ? OPTIMIZED_AUDIO_FORMAT : DEFEAULT_AUDIO_FORMAT, trim);
this.notifier.log ("IDS_NOTIFY_PROGRESS_DONE");
}
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/kmp/KSFFile.java b/src/main/java/de/mossgrabers/convertwithmoss/format/kmp/KSFFile.java
index 1a2b6f8..c0e2a1b 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/kmp/KSFFile.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/kmp/KSFFile.java
@@ -250,7 +250,7 @@ public static void write (final ISampleZone sampleZone, final int sampleIndex, f
out.write (formatChunk.getNumberOfChannels ());
// 8/16
- final int bits = formatChunk.getSignicantBitsPerSample ();
+ final int bits = formatChunk.getSignificantBitsPerSample ();
if (bits != 8 && bits != 16)
throw new IOException (Functions.getMessage ("IDS_KMP_BIT_SIZE_NOT_SUPPORTED", Integer.toString (bits)));
out.write (bits);
diff --git a/src/main/java/de/mossgrabers/convertwithmoss/format/music1010/Music1010Creator.java b/src/main/java/de/mossgrabers/convertwithmoss/format/music1010/Music1010Creator.java
index ccd59e6..7e57063 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/music1010/Music1010Creator.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/music1010/Music1010Creator.java
@@ -48,16 +48,13 @@ public class Music1010Creator extends AbstractCreator
{
private static final String MUSIC_1010_INTERPOLATION_QUALITY = "Music1010InterpolationQuality";
private static final String MUSIC_1010_RESAMPLE_TO_24_48 = "Music1010ResampleTo2448";
+ private static final String MUSIC_1010_TRIM_START_TO_END = "Music1010TrimStartToEnd";
private static final DestinationAudioFormat OPTIMIZED_AUDIO_FORMAT = new DestinationAudioFormat (new int []
{
24
}, 48000, true);
private static final DestinationAudioFormat DEFEAULT_AUDIO_FORMAT = new DestinationAudioFormat ();
- private ToggleGroup interpolationQualityGroup;
- private boolean isInterpolationQualityHigh;
- private CheckBox resampleTo2448;
-
private static final Map EMPTY_PARAM_ATTRIBUTES = new HashMap<> ();
private static final Map MULTISAMPLE_PARAM_ATTRIBUTES = new HashMap<> ();
static
@@ -155,6 +152,11 @@ public class Music1010Creator extends AbstractCreator
MULTISAMPLE_PARAM_ATTRIBUTES.put ("recmonoutbus", "0");
}
+ private ToggleGroup interpolationQualityGroup;
+ private boolean isInterpolationQualityHigh;
+ private CheckBox resampleTo2448;
+ private CheckBox trimStartToEnd;
+
/**
* Constructor.
@@ -184,6 +186,7 @@ public Node getEditPane ()
order2.setToggleGroup (this.interpolationQualityGroup);
this.resampleTo2448 = panel.createCheckBox ("@IDS_1010_MUSIC_CONVERT_TO_24_48");
+ this.trimStartToEnd = panel.createCheckBox ("@IDS_1010_MUSIC_TRIM_START_TO_END");
final TitledSeparator separator = this.addWavChunkOptions (panel);
separator.getStyleClass ().add ("titled-separator-pane");
@@ -198,6 +201,7 @@ public void loadSettings (final BasicConfig config)
{
this.interpolationQualityGroup.selectToggle (this.interpolationQualityGroup.getToggles ().get (config.getBoolean (MUSIC_1010_INTERPOLATION_QUALITY, false) ? 1 : 0));
this.resampleTo2448.setSelected (config.getBoolean (MUSIC_1010_RESAMPLE_TO_24_48, true));
+ this.trimStartToEnd.setSelected (config.getBoolean (MUSIC_1010_TRIM_START_TO_END, true));
this.loadWavChunkSettings (config, "Music1010");
}
@@ -209,6 +213,7 @@ public void saveSettings (final BasicConfig config)
{
config.setBoolean (MUSIC_1010_INTERPOLATION_QUALITY, this.isHighInterpolationQuality ());
config.setBoolean (MUSIC_1010_RESAMPLE_TO_24_48, this.resampleTo2448.isSelected ());
+ config.setBoolean (MUSIC_1010_TRIM_START_TO_END, this.trimStartToEnd.isSelected ());
this.saveWavChunkSettings (config, "Music1010");
}
@@ -218,6 +223,9 @@ public void saveSettings (final BasicConfig config)
@Override
public void create (final File destinationFolder, final IMultisampleSource multisampleSource) throws IOException
{
+ final boolean resample = this.resampleTo2448.isSelected ();
+ final boolean trim = this.trimStartToEnd.isSelected ();
+
this.setInterpolationQuality (this.isHighInterpolationQuality ());
final String sampleName = createSafeFilename (multisampleSource.getName ());
@@ -232,13 +240,18 @@ public void create (final File destinationFolder, final IMultisampleSource multi
return;
}
- final Optional metadata = this.createMetadata (sampleName, multisampleSource);
+ final Optional metadata = this.createMetadata (sampleName, multisampleSource, trim);
if (metadata.isEmpty ())
return;
this.notifier.log ("IDS_NOTIFY_STORING", multiFile.getAbsolutePath ());
- this.storePreset (presetFolder, multisampleSource, multiFile, metadata.get ());
+ storePreset (presetFolder, multisampleSource, multiFile, metadata.get ());
+
+ // Store all samples
+ if (resample)
+ recalculateSamplePositions (multisampleSource, 48000);
+ this.writeSamples (presetFolder, multisampleSource, resample ? OPTIMIZED_AUDIO_FORMAT : DEFEAULT_AUDIO_FORMAT, trim);
this.notifier.log ("IDS_NOTIFY_PROGRESS_DONE");
}
@@ -253,18 +266,12 @@ public void create (final File destinationFolder, final IMultisampleSource multi
* @param metadata The preset metadata description file
* @throws IOException Could not store the file
*/
- private void storePreset (final File destinationFolder, final IMultisampleSource multisampleSource, final File multiFile, final String metadata) throws IOException
+ private static void storePreset (final File destinationFolder, final IMultisampleSource multisampleSource, final File multiFile, final String metadata) throws IOException
{
try (final FileWriter writer = new FileWriter (multiFile, StandardCharsets.UTF_8))
{
writer.write (metadata);
}
-
- // Store all samples
- final boolean resample = this.resampleTo2448.isSelected ();
- if (resample)
- recalculateSamplePositions (multisampleSource, 48000);
- this.writeSamples (destinationFolder, multisampleSource, resample ? OPTIMIZED_AUDIO_FORMAT : DEFEAULT_AUDIO_FORMAT);
}
@@ -273,9 +280,10 @@ private void storePreset (final File destinationFolder, final IMultisampleSource
*
* @param folderName The name to use for the sample folder
* @param multisampleSource The multi-sample
+ * @param trim Trim to start/end if true
* @return The XML structure
*/
- private Optional createMetadata (final String folderName, final IMultisampleSource multisampleSource)
+ private Optional createMetadata (final String folderName, final IMultisampleSource multisampleSource, final boolean trim)
{
final Optional optionalDocument = this.createXMLDocument ();
if (optionalDocument.isEmpty ())
@@ -304,7 +312,7 @@ private Optional createMetadata (final String folderName, final IMultisa
for (final IGroup group: groups)
for (final ISampleZone zone: group.getSampleZones ())
{
- createSample (document, folderName, presetPath, sessionElement, zone, sampleIndex);
+ createSample (document, folderName, presetPath, sessionElement, zone, sampleIndex, trim);
sampleIndex++;
}
@@ -407,8 +415,9 @@ private Element createSlots (final Document document, final Element sessionEleme
* @param groupElement The element where to add the sample information
* @param zone Where to get the sample info from
* @param sampleIndex The index of the sample
+ * @param trim Trim to start/end if true
*/
- private static void createSample (final Document document, final String folderName, final String presetPath, final Element groupElement, final ISampleZone zone, final int sampleIndex)
+ private static void createSample (final Document document, final String folderName, final String presetPath, final Element groupElement, final ISampleZone zone, final int sampleIndex, final boolean trim)
{
/////////////////////////////////////////////////////
// Sample element and attributes
@@ -425,11 +434,15 @@ private static void createSample (final Document document, final String folderNa
// Stored in WAV file: zone.getGain (), zone.getTune ()
- final int start = limitToDefault (zone.getStart (), 0);
- XMLUtils.setIntegerAttribute (paramsElement, Music1010Tag.ATTR_SAMPLE_START, start);
- final int stop = zone.getStop ();
+ // Music1010Tag.ATTR_SAMPLE_START is not supported for multi-samples! Therefore, the sample
+ // needs to be truncated instead!
+ int stop = zone.getStop ();
if (stop > 0)
- XMLUtils.setIntegerAttribute (paramsElement, Music1010Tag.ATTR_SAMPLE_LENGTH, start + stop);
+ {
+ if (trim)
+ stop -= zone.getStart ();
+ XMLUtils.setIntegerAttribute (paramsElement, Music1010Tag.ATTR_SAMPLE_LENGTH, stop);
+ }
// No zone.getTrigger ();
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 eab34d8..ba5e772 100644
--- a/src/main/java/de/mossgrabers/convertwithmoss/format/sf2/Sf2Creator.java
+++ b/src/main/java/de/mossgrabers/convertwithmoss/format/sf2/Sf2Creator.java
@@ -211,7 +211,7 @@ private static Sf2Preset createSf2Preset (final IMultisampleSource multisampleSo
final byte [] data = dataChunk.getData ();
- final boolean is24Bit = formatChunk.getSignicantBitsPerSample () == 24;
+ final boolean is24Bit = formatChunk.getSignificantBitsPerSample () == 24;
final boolean isStereo = formatChunk.getNumberOfChannels () == 2;
final List sampleDataList = convertData (data, numSamples, is24Bit, isStereo);
diff --git a/src/main/resources/Strings.properties b/src/main/resources/Strings.properties
index e21843a..e0cda26 100644
--- a/src/main/resources/Strings.properties
+++ b/src/main/resources/Strings.properties
@@ -1,4 +1,4 @@
-TITLE=ConvertWithMoss 10.0.1
+TITLE=ConvertWithMoss 10.1.0
##################################################################################
#
@@ -99,6 +99,7 @@ IDS_DEX_NO_SD_PRESET=This preset is not for the SD-Algorithm.\n
IDS_DEX_NO_SAMPLE_FOLDER=The sample folder does not exist: %1\n
IDS_DEX_SEPARATOR=Disting EX
IDS_DEX_LIMIT_TO_16_441=Limit sample resolution and rate to 16bit/44.1kHz
+IDS_DEX_TRIM_START_TO_END=Trim sample to range of zone start to end.
IDS_MPC_MORE_THAN_4_LAYERS=Round-robin keygroup can only contain up to 4 layers (Range: %1 - %2, Velocity: %3 - %4).\n
IDS_MPC_MORE_THAN_128_KEYGROUPS=More than 128 keygroups present (%1). This might cause issues when loading the program.\n
@@ -210,6 +211,7 @@ IDS_WAV_ERR_IN_GROUP_PATTERN=Could not parse group pattern: %1\n
IDS_WAV_ONLY_ONE_NOTE=All files have the same MIDI note.\n
IDS_WAV_NO_MIDI_NOTE_DETECTED=Could not detect MIDI note in file name: %1\n
IDS_WAV_COMBINATION_NOT_POSSIBLE=Cannot combine the two files.\n
+IDS_WAV_ONLY_16_BIT_SUPPORTED=Can only convert 32-bit float to 16-bit PCM but destination bits are %1.\n
IDS_YSFC_NO_MULTISAMPLE_DATA=The file does not contain a multi-sample.\n
IDS_YSFC_NO_UKNOWN_CHUNK_MAGIC=Unknown chunk magic ID: %1\n
@@ -269,6 +271,7 @@ IDS_1010_MUSIC_INTERPOLATION_QUALITY_NORMAL=Normal
IDS_1010_MUSIC_INTERPOLATION_QUALITY_HIGH=High
IDS_1010_MUSIC_CONVERT_TO_24_48=Re-sample to 24bit/48kHz
IDS_1010_MUSIC_NO_MULTISAMPLE=No multi-sample found. Creating aggregated multi-sample.\n
+IDS_1010_MUSIC_TRIM_START_TO_END=Trim sample to range of zone start to end.
IDS_DS_USER_INTERFACE=User Interface
IDS_DS_OUTPUT_FORMAT=Output Format