Skip to content

Commit 7d93f51

Browse files
committed
3.9.4
1 parent 4c14811 commit 7d93f51

22 files changed

+2832
-1388
lines changed

README.md

+61-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
# JavaMod V3.9.3
1+
V3.9.4
2+
3+
# JavaMod V3.9.4
24
JavaMod - a java based multimedia player for Protracker, Fast Tracker,
35
Impulse Tracker, Scream Tracker and other mod files plus
46
SID, MP3, WAV, OGG, APE, FLAC, MIDI, AdLib ROL-Files (OPL), ...
@@ -20,8 +22,8 @@ On Linux consider starting with OpenGL render pipeline activated:
2022
## Remarks to 3.9.x version updates
2123
With JavaMod versions 4.0 to 5.0 I want to integrate Midi and AdLib support.
2224
However, before starting that I want to have all test mods of Schism and
23-
Open ModPlug Tracker to work. We finished MOD and XM with this version
24-
and a whole lot of other stuff as well. So I decided to release new version
25+
Open ModPlug Tracker to work. We finished MOD, XM, STM and S3M with this version
26+
and a whole lot of other stuff as well. So I decided to release new versions
2527
with minor version number updates to have you participate in these changes!
2628

2729
## Download of compiled version and source code
@@ -78,12 +80,66 @@ JavaMod incorporates modified versions of the following libraries:
7880
* MO3 support
7981
* read from 7z archives
8082

83+
## New in Version 3.9.4
84+
* NEW: Supporting STX (ScreamTracker Music Interface Kit)
85+
* NEW: moved all STM, S3M, IT, ModPlug specific coding into ScreamTrackerMixer
86+
* NEW: PatternImagePanel now knows about unused (muted) channels with IT - with
87+
S3Ms I still throw them out (can be changed by a "compile switch").
88+
* NEW: also XMs, when saved with ModPlug (not when compatibility mode), get
89+
rainbow colored headers in pattern dialog
90+
* NEW: Same with S3Ms when OPL3 is needed
91+
* NEW: recognizing MPTM "sample on disc" to output an error. I do not support
92+
that, and I don't want to support all the sample sources MPTM can load.
93+
* NEW: AdLib Instruments loaded for MPTP and S3M plus display in sample dialog
94+
* FIX: Finished - (except those tests that require OPL):
95+
Enhanced ScreamTracker 2 compatibility by fixing everything to make these
96+
test MODs work: https://wiki.openmpt.org/Development:_Test_Cases/S3M
97+
(many, many changes...!)
98+
Remarks to the loop tests. They are now played, as ST3 (V3.21) does it.
99+
The loops run differently when you mute channels in ST3, because then the
100+
SB0 and SBx commands of the muted channels are not executed. JavaMod on
101+
the other hand does not simulated that. If you mute a channel, effects
102+
are still executed. Only channels that were saved as muted are ignored.
103+
* FIX: Bugs after finishing MODs and XMs. I broke XMs without instruments.
104+
And the overflow check at preparePortaToNoteEffect was checking a period
105+
against an index.
106+
* FIX: XM GlobalVolSlide only on Tick Zero.
107+
* FIX: XM porta2Note with new instrument - really ignore it. Was now dead code.
108+
* FIX: XM and low notes - removed security checks, FT2 does not do them.
109+
However, we simulate the uint8 note value to get negative wrap around.
110+
* FIX: XM show correct frequency and transposed note in sample dialog
111+
* FIX: XM only in modplug mode support extended XM effects >0xE2x
112+
* FIX: XM ignores sample offset, if no note is present
113+
* FIX: IT, STM, S3M jumpLoops
114+
* FIX: IT, STM, S3M vibrato depths fixed
115+
* FIX: IT Global Tempo Slide on ticks>0 and not (and not only) on tick 0
116+
* FIX: IT moving noteCut to a method: NNA/DNA must also call that. *ARGH!*
117+
* FIX: IT recognizing different SavedWithTracker names
118+
* FIX: S3M Loading: recognize different trackers now, ModPlug recognition
119+
optimized
120+
* FIX: S3M reading song flags now correctly
121+
* FIX: S3M if AmigaLimits is now active - set correct borders
122+
* FIX: S3M calculation of periods was too precise (cannot believe I say that)
123+
* FIX: S3M double clean up of pattern removed
124+
* FIX: Empty S3Ms are better loaded now
125+
* FIX: STM special speed set (also added ST2Tempo for S3M, if set in song flags,
126+
but did not check)
127+
* FIX: STM is mono (as is STX)
128+
* FIX: STM / S3M Arpeggios were one half note off
129+
* FIX: STM Zero Effects - no effect memo
130+
* FIX: STM unsupported effects: you can edit beyond arpeggio effect, but are not
131+
implemented in ScreamTracker 2.21
132+
* FIX: STM recognition optimized (false positive STX and some Schism)
133+
* FIX: Reworked proceedToNextRow - patternFineDelay plus PatternBreak failed
134+
* FIX: PatternImagePanel: no IndexArrayOutOfBounds with initial currentIndex=-1
135+
(went unnoticed - was just catched and "walked off")
136+
81137
## New in Version 3.9.3
82-
* FIX: finished:
138+
* FIX: Finished:
83139
Enhanced FastTracker 2 compatibility by fixing everything to make these
84140
test MODs work: https://wiki.openmpt.org/Development:_Test_Cases/XM
85141
(many, many changes...!)
86-
* FIX: When fixing (Fine) Volslides for XMs: MODs do not have effect memory with
142+
* FIX: When fixing (Fine) VolSlides for XMs: MODs do not have effect memory with
87143
fine volume slides
88144
* FIX: Note Delays with XMs did not work correctly anymore, due to other fixes.
89145
Refixed...

source/de/quippy/javamod/io/RandomAccessInputStreamImpl.java

+17-2
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,11 @@ public RandomAccessInputStreamImpl(File file) throws IOException, FileNotFoundEx
9191
* @param fileName
9292
* @throws FileNotFoundException
9393
*/
94-
public RandomAccessInputStreamImpl(String fileName) throws IOException, FileNotFoundException
94+
public RandomAccessInputStreamImpl(final String fileName) throws IOException, FileNotFoundException
9595
{
9696
this(new File(fileName));
9797
}
98-
public RandomAccessInputStreamImpl(URL fromUrl) throws IOException, FileNotFoundException
98+
public RandomAccessInputStreamImpl(final URL fromUrl) throws IOException, FileNotFoundException
9999
{
100100
super();
101101
if (Helpers.isFile(fromUrl))
@@ -143,6 +143,21 @@ public RandomAccessInputStreamImpl(URL fromUrl) throws IOException, FileNotFound
143143
}
144144
}
145145
}
146+
/**
147+
* Constructor for RandomAccessInputStreamImpl from a direct byte array
148+
* @param fromByteArray
149+
* @throws IOException
150+
* @throws FileNotFoundException
151+
*/
152+
public RandomAccessInputStreamImpl(final byte[] fromByteArray) throws IOException, FileNotFoundException
153+
{
154+
super();
155+
fullFileCache = fromByteArray;
156+
fullFileCache_length = fullFileCache.length;
157+
fullFileCache_readPointer = 0;
158+
raFile = null;
159+
localFile = null;
160+
}
146161
/**
147162
* @since 04.01.2011
148163
* @param file

source/de/quippy/javamod/main/gui/MainForm.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
import java.net.URISyntaxException;
5656
import java.net.URL;
5757
import java.time.LocalDate;
58-
import java.time.format.DateTimeFormatter;
5958
import java.util.ArrayList;
6059
import java.util.HashMap;
6160
import java.util.Iterator;
@@ -116,6 +115,7 @@
116115
import de.quippy.javamod.multimedia.MultimediaContainerEvent;
117116
import de.quippy.javamod.multimedia.MultimediaContainerEventListener;
118117
import de.quippy.javamod.multimedia.MultimediaContainerManager;
118+
import de.quippy.javamod.multimedia.mod.ModConstants;
119119
import de.quippy.javamod.system.Helpers;
120120
import de.quippy.javamod.system.Log;
121121
import de.quippy.javamod.system.LogMessageCallBack;
@@ -342,7 +342,6 @@ public class MainForm extends JFrame implements DspProcessorCallBack, PlayThread
342342
private float currentBalance; /* -1.0 - 1.0 */
343343

344344
private LocalDate lastUpdateCheck;
345-
private static final DateTimeFormatter DATE_FORMATER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
346345
private static final LocalDate today = LocalDate.now();
347346

348347
private ArrayList<URL> lastLoaded;
@@ -490,7 +489,7 @@ private void readPropertyFile()
490489
getSALMeterPanel().setDrawWhatTo(saMeterLeftDrawType);
491490
getSARMeterPanel().setDrawWhatTo(saMeterRightDrawType);
492491

493-
lastUpdateCheck = LocalDate.from(DATE_FORMATER.parse(props.getProperty(PROPERTY_LAST_UPDATECHECK, DATE_FORMATER.format(today))));
492+
lastUpdateCheck = LocalDate.from(ModConstants.DATE_FORMATER.parse(props.getProperty(PROPERTY_LAST_UPDATECHECK, ModConstants.DATE_FORMATER.format(today))));
494493

495494
if (currentEqualizer!=null)
496495
{
@@ -573,7 +572,7 @@ private void writePropertyFile()
573572
props.setProperty(PROPERTY_XMASCONFIG_VISABLE, Boolean.toString(getXmasConfigDialog().isVisible()));
574573
props.setProperty(PROPERTY_SAMETER_LEFT_DRAWTYPE, Integer.toString(getSALMeterPanel().getDrawWhat()));
575574
props.setProperty(PROPERTY_SAMETER_RIGHT_DRAWTYPE, Integer.toString(getSARMeterPanel().getDrawWhat()));
576-
props.setProperty(PROPERTY_LAST_UPDATECHECK, DATE_FORMATER.format(lastUpdateCheck));
575+
props.setProperty(PROPERTY_LAST_UPDATECHECK, ModConstants.DATE_FORMATER.format(lastUpdateCheck));
577576

578577
if (currentEqualizer!=null)
579578
{

source/de/quippy/javamod/multimedia/mod/ModConstants.java

+102-15
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
*/
2222
package de.quippy.javamod.multimedia.mod;
2323

24+
import java.time.LocalDate;
25+
import java.time.format.DateTimeFormatter;
26+
2427
/**
2528
* @author Daniel Becker
2629
* @since 02.08.2020
@@ -84,9 +87,12 @@ public ModConstants()
8487
0xB0, 0xB4, 0xB8, 0xBC, // 24-31
8588
};
8689

87-
// Panning values for old ProTracker and STMs
90+
// Panning values for old ProTracker and STMs / S3Ms
8891
public static final int OLD_PANNING_LEFT = 0; // 0: full left, 64: quarter left
8992
public static final int OLD_PANNING_RIGHT = 256 - OLD_PANNING_LEFT;
93+
// Special panning values for S3M and IT
94+
public static final int CHANNEL_IS_MUTED = 0x80<<2; // IT and S3M use this
95+
public static final int CHANNEL_IS_SURROUND = 100<<2; // IT uses this (YES, not hex!)
9096

9197
// Volume constants
9298
public static final int MAXGLOBALVOLUME = 128; // the maximum global volume at mod loading
@@ -133,19 +139,18 @@ public ModConstants()
133139
public static enum PanBits { Pan4Bit, Pan6Bit, Pan8Bit }
134140

135141
// Constants for different supported samples
136-
public static final int SM_PCMS = 0x00; // PCM 8 Bit Signed
137-
public static final int SM_PCMU = 0x01; // PCM 8 Bit unsigned
138-
public static final int SM_PCMD = 0x02; // PCM 8 Bit delta values
139-
public static final int SM_16BIT = 0x04; // 16 BIT
140-
public static final int SM_STEREO = 0x08; // STEREO
141-
public static final int SM_PCM16D = SM_PCMD | SM_16BIT; // PCM 16 Bit delta values
142+
public static final int SM_PCMS = 0x0001; // PCM 8 Bit Signed
143+
public static final int SM_PCMU = 0x0002; // PCM 8 Bit unsigned
144+
public static final int SM_PCMD = 0x0004; // PCM 8 Bit delta values
145+
public static final int SM_16BIT = 0x0008; // 16 BIT
146+
public static final int SM_BigEndian = 0x0010; // 16 Bit in BigEndian order
147+
public static final int SM_STEREO = 0x0020; // STEREO
142148
// IT Packed (>2.14)
143-
public static final int SM_IT2148 = 0x10; // IT 2.14 8Bit compressed - can have stereo set
144-
public static final int SM_IT21416 = SM_IT2148 | SM_16BIT; // IT 2.14 16Bit compressed - can have stereo set
145-
public static final int SM_IT2158 = 0x12; // IT 2.15 8Bit compressed - can have stereo set
146-
public static final int SM_IT21516 = SM_IT2158 | SM_16BIT; // IT 2.15 16Bit compressed - can have stereo set
149+
public static final int SM_IT214 = 0x0040; // IT 2.14 compressed
150+
public static final int SM_IT215 = 0x0080; // IT 2.15 compressed
151+
public static final int SM_PTM8Dto16 = 0x0100; // IT PTM8to16
147152
// XM ADPCM ModPlug
148-
public static final int SM_ADPCM = 0x20; // ModPlug ADPCM
153+
public static final int SM_ADPCM = 0x0200; // ModPlug ADPCM
149154

150155
// Loop Types
151156
public static final int LOOP_ON = 0x01;
@@ -201,6 +206,12 @@ public static enum PanBits { Pan4Bit, Pan6Bit, Pan8Bit }
201206
public static final int SONG_AMIGALIMITS = 0x00040;
202207
public static final int SONG_ISSTEREO = 0x00080;
203208
public static final int SONG_USEINSTRUMENTS = 0x00100;
209+
public static final int SONG_ST2VIBRATO = 0x00200;
210+
public static final int SONG_ST2TEMPO = 0x00400;
211+
public static final int SONG_AMIGASLIDES = 0x00800;
212+
public static final int SONG_VOL0MIXOPTI = 0x01000;
213+
public static final int SONG_USEMIDIPITCH = 0x02000;
214+
public static final int SONG_S3M_GUS = 0x80000;
204215

205216
// Player flags
206217
public static final int PLAYER_LOOP_DEACTIVATED = 0x00;
@@ -1018,12 +1029,30 @@ public static String getAsHex(final int value, final int digits)
10181029
for (int shift=(digits-1)<<2; shift>=0; shift-=4)
10191030
result.append(numbers[(value>>shift)&0xF]);
10201031
return result.toString();
1032+
}
1033+
/**
1034+
* Displays a value as a hex-value, using #digits. If the digits are not
1035+
* sufficient, the number is cut!
1036+
* @param value
1037+
* @param digits
1038+
* @return
1039+
*/
1040+
public static String getAsHex(final long value, final int digits)
1041+
{
1042+
StringBuilder result = new StringBuilder();
1043+
for (int shift=(digits-1)<<2; shift>=0; shift-=4)
1044+
result.append(numbers[(int)(value>>shift)&0xF]);
1045+
return result.toString();
10211046
// Old standard way, much slower, much more fail safe.
10221047
// final String hex = Integer.toHexString(value).toUpperCase();
10231048
// final int zeros = digits - hex.length();
10241049
// for (int i=0; i<zeros; i++) result.append('0');
10251050
// return (result.append(hex)).toString();
10261051
}
1052+
/**
1053+
* Our standard Date formatter
1054+
*/
1055+
public static final DateTimeFormatter DATE_FORMATER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
10271056
/**
10281057
* convert the ModPlug version information to a readable string
10291058
* @param version
@@ -1032,10 +1061,68 @@ public static String getAsHex(final int value, final int digits)
10321061
public static String getModPlugVersionString(final int version)
10331062
{
10341063
if (version==0) return "Unknown";
1035-
if ((version&0xFFFF)==0) return getAsHex((version>>24)&0xFF, 2) + '.' + getAsHex((version>>16)&0xFF, 2);
1036-
final String versionString = getAsHex((version>>24)&0xFF, 2) + '.' + getAsHex((version>>16)&0xFF, 2) + '.' + getAsHex((version>>8)&0xFF, 2) + '.' + getAsHex(version&0xFF, 2);
1037-
if (versionString.charAt(0)=='0') return versionString.substring(1); else return versionString;
1064+
if ((version&0xFFFF)==0) return String.format("%x.%02x", Integer.valueOf((version>>24)&0xFF), Integer.valueOf((version>>16)&0xFF));
1065+
return String.format("%x.%02x.%02x.%02x", Integer.valueOf((version>>24)&0xFF), Integer.valueOf((version>>16)&0xFF), Integer.valueOf((version>>8)&0xFF), Integer.valueOf(version&0xFF));
10381066
}
1067+
/**
1068+
* convert the Schism version information to a readable string
1069+
* @since 19.07.2024
1070+
* @param version
1071+
* @return
1072+
*/
1073+
public static String getSchismVersionString(final int version)
1074+
{
1075+
final int cwtv = (version>>16) & 0xFFF;
1076+
final int extVersion = version&0xFFFF;
1077+
if (cwtv>0x50)
1078+
{
1079+
// version is days from 2009-10-31
1080+
LocalDate d = LocalDate.of(2009, 10, 31);
1081+
if (cwtv<0xFFF)
1082+
d = d.plusDays(cwtv-0x50);
1083+
else
1084+
d = d.plusDays(extVersion);
1085+
return DATE_FORMATER.format(d);
1086+
}
1087+
return String.format("0.%x", Integer.valueOf(cwtv));
1088+
}
1089+
// private static final int ST2TempoFactor[] = { 140, 50, 25, 15, 10, 7, 6, 4, 3, 3, 2, 2, 2, 2, 1, 1 };
1090+
// private static final int st2MixingRate = 23863; // Highest possible setting in ST2
1091+
// public static int convertST2tempo(final int tempo)
1092+
// {
1093+
// // This underflows at tempo 06...0F, and the resulting tick lengths depend on the mixing rate.
1094+
// // Note: ST2.3 uses the constant 50 below, earlier versions use 49 but they also play samples at a different speed.
1095+
// int samplesPerTick = st2MixingRate / (50 - ((ST2TempoFactor[tempo >> 4] * (tempo & 0x0F)) >> 4));
1096+
// if(samplesPerTick <= 0)
1097+
// samplesPerTick += 65536;
1098+
// return (st2MixingRate<<5)/(samplesPerTick<<2);
1099+
// }
1100+
private static final int st2_tempo_table[][] =
1101+
{
1102+
{ 125, 117, 110, 102, 95, 87, 80, 72, 62, 55, 47, 40, 32, 25, 17, 10, },
1103+
{ 125, 122, 117, 115, 110, 107, 102, 100, 95, 90, 87, 82, 80, 75, 72, 67, },
1104+
{ 125, 125, 122, 120, 117, 115, 112, 110, 107, 105, 102, 100, 97, 95, 92, 90, },
1105+
{ 125, 125, 122, 122, 120, 117, 117, 115, 112, 112, 110, 110, 107, 105, 105, 102, },
1106+
{ 125, 125, 125, 122, 122, 120, 120, 117, 117, 117, 115, 115, 112, 112, 110, 110, },
1107+
{ 125, 125, 125, 122, 122, 122, 120, 120, 117, 117, 117, 115, 115, 115, 112, 112, },
1108+
{ 125, 125, 125, 125, 122, 122, 122, 122, 120, 120, 120, 120, 117, 117, 117, 117, },
1109+
{ 125, 125, 125, 125, 125, 125, 122, 122, 122, 122, 122, 120, 120, 120, 120, 120, },
1110+
{ 125, 125, 125, 125, 125, 125, 122, 122, 122, 122, 122, 120, 120, 120, 120, 120, },
1111+
{ 125, 125, 125, 125, 125, 125, 125, 125, 122, 122, 122, 122, 122, 122, 122, 122, },
1112+
{ 125, 125, 125, 125, 125, 125, 125, 125, 122, 122, 122, 122, 122, 122, 122, 122, },
1113+
{ 125, 125, 125, 125, 125, 125, 125, 125, 122, 122, 122, 122, 122, 122, 122, 122, },
1114+
{ 125, 125, 125, 125, 125, 125, 125, 125, 122, 122, 122, 122, 122, 122, 122, 122, },
1115+
{ 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, },
1116+
{ 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, },
1117+
};
1118+
public static int convertST2tempo(final int tempo)
1119+
{
1120+
final int tpr = ((tempo>>4)!=0)?(tempo>>4):1;
1121+
final int scale = tempo&0xF;
1122+
return st2_tempo_table[tpr-1][scale];
1123+
}
1124+
1125+
10391126
// Conversions for read bytes! *********************************************
10401127
// /**
10411128
// * Converts an Intel like stored word to an integer

source/de/quippy/javamod/multimedia/mod/gui/ModPatternDialog.java

+5
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,9 @@ private void updateMuteStatus()
989989
final int channels = patternContainer.getChannels();
990990
for (int i=0; i<channels; i++)
991991
{
992+
if (!patternContainer.getIsChannelActive(i))
993+
channelButtons[i].setForeground(Color.lightGray);
994+
else
992995
if (muteStatus!=null && i<muteStatus.length && i<internalMuteStatus.length)
993996
channelButtons[i].setForeground((!(internalMuteStatus[i] = muteStatus[i]))?Color.black:Color.lightGray);
994997
else
@@ -1320,6 +1323,7 @@ public void run()
13201323
fillButtonsForChannels(0, patternContainer.getChannels());
13211324
setCurrentPattern(0);
13221325
setPreferredSize(getSize());
1326+
updateMuteStatus();
13231327
pack();
13241328
}
13251329
catch (Throwable ex)
@@ -1389,6 +1393,7 @@ public void getStatusInformation(final StatusInformation infoObject)
13891393
else
13901394
if (!isPlaying)
13911395
{
1396+
if (currentIndex<0) return; // Obviously we do not display anything yet, so nothing to do
13921397
EventQueue.invokeLater(new Runnable()
13931398
{
13941399
public void run()

0 commit comments

Comments
 (0)