summaryrefslogtreecommitdiff
path: root/soundlib
diff options
context:
space:
mode:
authorJames Cowgill <jcowgill@debian.org>2019-07-15 23:43:22 +0100
committerJames Cowgill <jcowgill@debian.org>2019-07-15 23:43:22 +0100
commit1eb7116a256029ed6f5ce8d0ffac280adcbe8c35 (patch)
treeb8867e88955ad6c7195e6998c261c4e69664d054 /soundlib
parent2690ce9c090be73f3ba565e624bece14ced24878 (diff)
parentdc825bfd49ccba2594b05f2926234839182ed70b (diff)
Update upstream source from tag 'upstream/0.4.5'
Update to upstream version '0.4.5' with Debian dir dd5e0f3a68c5a79fc4c224020a9b8cedcbd36e32
Diffstat (limited to 'soundlib')
-rw-r--r--soundlib/AudioReadTarget.h4
-rw-r--r--soundlib/InstrumentExtensions.cpp35
-rw-r--r--soundlib/Load_mod.cpp66
-rw-r--r--soundlib/Load_mt2.cpp4
-rw-r--r--soundlib/Load_mtm.cpp22
-rw-r--r--soundlib/ModChannel.h6
-rw-r--r--soundlib/ModInstrument.cpp6
-rw-r--r--soundlib/ModInstrument.h80
-rw-r--r--soundlib/ModSample.cpp5
-rw-r--r--soundlib/ModSequence.cpp23
-rw-r--r--soundlib/SampleFormatFLAC.cpp4
-rw-r--r--soundlib/SampleIO.cpp5
-rw-r--r--soundlib/Snd_defs.h5
-rw-r--r--soundlib/Snd_fx.cpp30
-rw-r--r--soundlib/Sndfile.cpp3
-rw-r--r--soundlib/Sndmix.cpp33
-rw-r--r--soundlib/UpgradeModule.cpp12
17 files changed, 202 insertions, 141 deletions
diff --git a/soundlib/AudioReadTarget.h b/soundlib/AudioReadTarget.h
index e5da84a..3bb6f5d 100644
--- a/soundlib/AudioReadTarget.h
+++ b/soundlib/AudioReadTarget.h
@@ -40,7 +40,7 @@ public:
, outputBuffer(buffer)
, outputBuffers(buffers)
{
- MPT_ASSERT(SampleFormat(SampleFormatTraits<Tsample>::sampleFormat).IsValid());
+ MPT_ASSERT(SampleFormat(SampleFormatTraits<Tsample>::sampleFormat()).IsValid());
}
std::size_t GetRenderedCount() const { return countRendered; }
public:
@@ -48,7 +48,7 @@ public:
{
// Convert to output sample format and optionally perform dithering and clipping if needed
- const SampleFormat sampleFormat = SampleFormatTraits<Tsample>::sampleFormat;
+ const SampleFormat sampleFormat = SampleFormatTraits<Tsample>::sampleFormat();
if(sampleFormat.IsInt())
{
diff --git a/soundlib/InstrumentExtensions.cpp b/soundlib/InstrumentExtensions.cpp
index 10127bd..87bb171 100644
--- a/soundlib/InstrumentExtensions.cpp
+++ b/soundlib/InstrumentExtensions.cpp
@@ -120,9 +120,9 @@ PSB. PanEnv.nSustainStart;
PSE. PanEnv.nSustainEnd;
PTTL pitchToTempoLock;
PTTF pitchToTempoLock (fractional part);
-PVEH nPluginVelocityHandling;
-PVOH nPluginVolumeHandling;
-R... nResampling;
+PVEH pluginVelocityHandling;
+PVOH pluginVolumeHandling;
+R... resampling;
RP.. [EXT] nRestartPos;
RPB. [EXT] nRowsPerBeat;
RPM. [EXT] nRowsPerMeasure;
@@ -314,12 +314,12 @@ if(!writeAll)
WRITE_MPTHEADER_envelope_member( ENV_PITCH , value , uint8 , MagicBE("PiE[") )
WRITE_MPTHEADER_sized_member( nMixPlug , uint8 , MagicBE("MiP.") )
WRITE_MPTHEADER_sized_member( nVolRampUp , uint16 , MagicBE("VR..") )
- WRITE_MPTHEADER_trunc_member( nResampling , uint16 , MagicBE("R...") )
+ WRITE_MPTHEADER_sized_member( resampling , uint8 , MagicBE("R...") )
WRITE_MPTHEADER_sized_member( nCutSwing , uint8 , MagicBE("CS..") )
WRITE_MPTHEADER_sized_member( nResSwing , uint8 , MagicBE("RS..") )
WRITE_MPTHEADER_sized_member( nFilterMode , uint8 , MagicBE("FM..") )
- WRITE_MPTHEADER_sized_member( nPluginVelocityHandling , uint8 , MagicBE("PVEH") )
- WRITE_MPTHEADER_sized_member( nPluginVolumeHandling , uint8 , MagicBE("PVOH") )
+ WRITE_MPTHEADER_sized_member( pluginVelocityHandling , uint8 , MagicBE("PVEH") )
+ WRITE_MPTHEADER_sized_member( pluginVolumeHandling , uint8 , MagicBE("PVOH") )
WRITE_MPTHEADER_trunc_member( pitchToTempoLock.GetInt() , uint16 , MagicBE("PTTL") )
WRITE_MPTHEADER_trunc_member( pitchToTempoLock.GetFract() , uint16 , MagicLE("PTTF") )
WRITE_MPTHEADER_sized_member( PitchEnv.nReleaseNode , uint8 , MagicBE("PERN") )
@@ -373,9 +373,9 @@ void CSoundFile::SaveExtendedInstrumentProperties(INSTRUMENTINDEX numInstruments
WritePropertyIfNeeded(*this, &ModInstrument::nMidiChannel, MagicBE("MC.."), sizeof(ModInstrument::nMidiChannel), f, numInstruments);
WritePropertyIfNeeded(*this, &ModInstrument::nMidiProgram, MagicBE("MP.."), sizeof(ModInstrument::nMidiProgram), f, numInstruments);
WritePropertyIfNeeded(*this, &ModInstrument::wMidiBank, MagicBE("MB.."), sizeof(ModInstrument::wMidiBank), f, numInstruments);
- WritePropertyIfNeeded(*this, &ModInstrument::nResampling, MagicBE("R..."), sizeof(ModInstrument::nResampling), f, numInstruments);
- WritePropertyIfNeeded(*this, &ModInstrument::nPluginVelocityHandling, MagicBE("PVEH"), sizeof(ModInstrument::nPluginVelocityHandling), f, numInstruments);
- WritePropertyIfNeeded(*this, &ModInstrument::nPluginVolumeHandling, MagicBE("PVOH"), sizeof(ModInstrument::nPluginVolumeHandling), f, numInstruments);
+ WritePropertyIfNeeded(*this, &ModInstrument::resampling, MagicBE("R..."), sizeof(ModInstrument::resampling), f, numInstruments);
+ WritePropertyIfNeeded(*this, &ModInstrument::pluginVelocityHandling, MagicBE("PVEH"), sizeof(ModInstrument::pluginVelocityHandling), f, numInstruments);
+ WritePropertyIfNeeded(*this, &ModInstrument::pluginVolumeHandling, MagicBE("PVOH"), sizeof(ModInstrument::pluginVolumeHandling), f, numInstruments);
if(!(GetType() & MOD_TYPE_XM))
{
@@ -575,16 +575,15 @@ bool ReadInstrumentHeaderField(ModInstrument *input, uint32 fcode, uint16 fsize,
GET_MPTHEADER_envelope_member(ENV_PITCH , value , uint8 , MagicBE("PiE[") )
GET_MPTHEADER_array_member( NoteMap , uint8 , MagicBE("NM[.") )
GET_MPTHEADER_array_member( Keyboard , uint16 , MagicBE("K[..") )
- GET_MPTHEADER_chararray_member( name , char , MagicBE("n[..") )
- GET_MPTHEADER_chararray_member( filename , char , MagicBE("fn[.") )
+ GET_MPTHEADER_chararray_member( name , char , MagicBE("n[..") )
+ GET_MPTHEADER_chararray_member( filename , char , MagicBE("fn[.") )
GET_MPTHEADER_sized_member( nMixPlug , uint8 , MagicBE("MiP.") )
GET_MPTHEADER_sized_member( nVolRampUp , uint16 , MagicBE("VR..") )
- GET_MPTHEADER_sized_member( nResampling , uint32 , MagicBE("R...") )
GET_MPTHEADER_sized_member( nCutSwing , uint8 , MagicBE("CS..") )
GET_MPTHEADER_sized_member( nResSwing , uint8 , MagicBE("RS..") )
GET_MPTHEADER_sized_member( nFilterMode , uint8 , MagicBE("FM..") )
- GET_MPTHEADER_sized_member( nPluginVelocityHandling , uint8 , MagicBE("PVEH") )
- GET_MPTHEADER_sized_member( nPluginVolumeHandling , uint8 , MagicBE("PVOH") )
+ GET_MPTHEADER_sized_member( pluginVelocityHandling , uint8 , MagicBE("PVEH") )
+ GET_MPTHEADER_sized_member( pluginVolumeHandling , uint8 , MagicBE("PVOH") )
GET_MPTHEADER_sized_member( PitchEnv.nReleaseNode , uint8 , MagicBE("PERN") )
GET_MPTHEADER_sized_member( PanEnv.nReleaseNode , uint8 , MagicBE("AERN") )
GET_MPTHEADER_sized_member( VolEnv.nReleaseNode , uint8 , MagicBE("VERN") )
@@ -592,6 +591,14 @@ bool ReadInstrumentHeaderField(ModInstrument *input, uint32 fcode, uint16 fsize,
GET_MPTHEADER_sized_member( PanEnv.dwFlags , uint8 , MagicBE("AFLG") )
GET_MPTHEADER_sized_member( VolEnv.dwFlags , uint8 , MagicBE("VFLG") )
GET_MPTHEADER_sized_member( midiPWD , int8 , MagicBE("MPWD") )
+ case MagicBE("R..."):
+ {
+ // Resampling has been written as various sizes including uint16 and uint32 in the past
+ uint32 tmp = file.ReadTruncatedIntLE<uint32>(fsize);
+ if(Resampling::IsKnownMode(tmp))
+ input->resampling = static_cast<ResamplingMode>(tmp);
+ result = true;
+ } break;
case MagicBE("PTTL"):
{
// Integer part of pitch/tempo lock
diff --git a/soundlib/Load_mod.cpp b/soundlib/Load_mod.cpp
index 47567da..073fd7b 100644
--- a/soundlib/Load_mod.cpp
+++ b/soundlib/Load_mod.cpp
@@ -345,23 +345,23 @@ MPT_BINARY_STRUCT(MODSampleHeader, 30)
// Synthesized StarTrekker instruments
struct AMInstrument
{
- char am[2]; // "AM"
+ char am[2]; // "AM"
char zero[4];
- uint16be startLevel; // Start level
- uint16be attack1Level; // Attack 1 level
- uint16be attack1Speed; // Attack 1 speed
- uint16be attack2Level; // Attack 2 level
- uint16be attack2Speed; // Attack 2 speed
- uint16be sustainLevel; // Sustain level
- uint16be decaySpeed; // Decay speed
- uint16be sustainTime; // Sustain time
- uint16be nt; // ?
- uint16be releaseSpeed; // Release speed
- uint16be waveform; // Waveform
- int16be pitchFall; // Pitch fall
- uint16be vibAmp; // Vibrato amplitude
- uint16be vibSpeed; // Vibrato speed
- uint16be octave; // Base frequency
+ uint16be startLevel; // Start level
+ uint16be attack1Level; // Attack 1 level
+ uint16be attack1Speed; // Attack 1 speed
+ uint16be attack2Level; // Attack 2 level
+ uint16be attack2Speed; // Attack 2 speed
+ uint16be sustainLevel; // Sustain level
+ uint16be decaySpeed; // Decay speed
+ uint16be sustainTime; // Sustain time
+ uint16be nt; // ?
+ uint16be releaseSpeed; // Release speed
+ uint16be waveform; // Waveform
+ int16be pitchFall; // Pitch fall
+ uint16be vibAmp; // Vibrato amplitude
+ uint16be vibSpeed; // Vibrato speed
+ uint16be octave; // Base frequency
void ConvertToMPT(ModSample &sample, ModInstrument &ins, mpt::fast_prng &rng) const
{
@@ -376,15 +376,16 @@ struct AMInstrument
sample.RelativeTone = static_cast<int8>(-12 * octave);
if(sample.AllocateSample())
{
+ int8 *p = sample.sample8();
for(SmpLength i = 0; i < sample.nLength; i++)
{
switch(waveform)
{
default:
- case 0: sample.sample8()[i] = ModSinusTable[i * 2]; break; // Sine
- case 1: sample.sample8()[i] = static_cast<int8>(-128 + i * 8); break; // Saw
- case 2: sample.sample8()[i] = i < 16 ? -128 : 127; break; // Square
- case 3: sample.sample8()[i] = mpt::random<int8>(rng); break; // Noise
+ case 0: p[i] = ModSinusTable[i * 2]; break; // Sine
+ case 1: p[i] = static_cast<int8>(-128 + i * 8); break; // Saw
+ case 2: p[i] = i < 16 ? -128 : 127; break; // Square
+ case 3: p[i] = mpt::random<int8>(rng); break; // Noise
}
}
}
@@ -492,7 +493,7 @@ MPT_BINARY_STRUCT(PT36InfoChunk, 64)
// Check if header magic equals a given string.
-static bool IsMagic(const char *magic1, const char *magic2)
+static bool IsMagic(const char *magic1, const char (&magic2)[5])
{
return std::memcmp(magic1, magic2, 4) == 0;
}
@@ -1023,11 +1024,15 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
if(m.command || m.param)
{
- // No support for Startrekker assembly macros
if(isStartrekker && m.command == 0x0E)
{
+ // No support for Startrekker assembly macros
m.command = CMD_NONE;
m.param = 0;
+ } else if(isStartrekker && m.command == 0x0F && m.param > 0x1F)
+ {
+ // Startrekker caps speed at 31 ticks per row
+ m.param = 0x1F;
}
ConvertModCommand(m);
}
@@ -1281,13 +1286,13 @@ static uint32 CountInvalidChars(const char (&name)[N])
// Thanks for Fraggie for this information! (https://www.un4seen.com/forum/?topic=14471.msg100829#msg100829)
enum STVersions
{
- UST1_00, // Ultimate Soundtracker 1.0-1.21 (K. Obarski)
- UST1_80, // Ultimate Soundtracker 1.8-2.0 (K. Obarski)
- ST2_00_Exterminator, // SoundTracker 2.0 (The Exterminator), D.O.C. Sountracker II (Unknown/D.O.C.)
- ST_III, // Defjam Soundtracker III (Il Scuro/Defjam), Alpha Flight SoundTracker IV (Alpha Flight), D.O.C. SoundTracker IV (Unknown/D.O.C.), D.O.C. SoundTracker VI (Unknown/D.O.C.)
- ST_IX, // D.O.C. SoundTracker IX (Unknown/D.O.C.)
- MST1_00, // Master Soundtracker 1.0 (Tip/The New Masters)
- ST2_00, // SoundTracker 2.0, 2.1, 2.2 (Unknown/D.O.C.)
+ UST1_00, // Ultimate Soundtracker 1.0-1.21 (K. Obarski)
+ UST1_80, // Ultimate Soundtracker 1.8-2.0 (K. Obarski)
+ ST2_00_Exterminator, // SoundTracker 2.0 (The Exterminator), D.O.C. Sountracker II (Unknown/D.O.C.)
+ ST_III, // Defjam Soundtracker III (Il Scuro/Defjam), Alpha Flight SoundTracker IV (Alpha Flight), D.O.C. SoundTracker IV (Unknown/D.O.C.), D.O.C. SoundTracker VI (Unknown/D.O.C.)
+ ST_IX, // D.O.C. SoundTracker IX (Unknown/D.O.C.)
+ MST1_00, // Master Soundtracker 1.0 (Tip/The New Masters)
+ ST2_00, // SoundTracker 2.0, 2.1, 2.2 (Unknown/D.O.C.)
};
@@ -1521,8 +1526,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags)
if(fileHeader.restartPos != 0x78)
{
// Convert to CIA timing
- //m_nDefaultTempo = TEMPO(((709378.92 / 50.0) * 125.0) / ((240 - fileHeader.restartPos) * 122.0));
- m_nDefaultTempo.Set((709379 / ((240 - fileHeader.restartPos) * 122)) * 125 / 50);
+ m_nDefaultTempo = TEMPO((709379.0 * 125.0 / 50.0) / ((240 - fileHeader.restartPos) * 122.0));
if(minVersion > UST1_80)
{
// D.O.C. SoundTracker IX re-introduced the variable tempo after some other versions dropped it.
diff --git a/soundlib/Load_mt2.cpp b/soundlib/Load_mt2.cpp
index 2a98b24..e256cb1 100644
--- a/soundlib/Load_mt2.cpp
+++ b/soundlib/Load_mt2.cpp
@@ -1060,7 +1060,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags)
{
if(drumSample[drum] == i && Instruments[drumMap[drum]] != nullptr)
{
- Instruments[drumMap[drum]]->nResampling = SRCMODE_NEAREST;
+ Instruments[drumMap[drum]]->resampling = SRCMODE_NEAREST;
}
}
}
@@ -1105,7 +1105,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags)
mptSmp.nFineTune = group.pitch;
if(sampleNoInterpolation[sample - 1])
{
- mptIns->nResampling = SRCMODE_NEAREST;
+ mptIns->resampling = SRCMODE_NEAREST;
}
if(sampleSynchronized[sample - 1])
{
diff --git a/soundlib/Load_mtm.cpp b/soundlib/Load_mtm.cpp
index 10140ba..3819602 100644
--- a/soundlib/Load_mtm.cpp
+++ b/soundlib/Load_mtm.cpp
@@ -58,8 +58,8 @@ struct MTMSampleHeader
LimitMax(mptSmp.nLoopEnd, mptSmp.nLength);
if(mptSmp.nLoopStart + 4 >= mptSmp.nLoopEnd) mptSmp.nLoopStart = mptSmp.nLoopEnd = 0;
if(mptSmp.nLoopEnd) mptSmp.uFlags.set(CHN_LOOP);
- mptSmp.nFineTune = MOD2XMFineTune(finetune);
- mptSmp.nC5Speed = ModSample::TransposeToFrequency(0, mptSmp.nFineTune);
+ mptSmp.nFineTune = finetune; // Uses MOD units but allows the full int8 range rather than just -8...+7 so we keep the value as-is and convert it during playback
+ mptSmp.nC5Speed = ModSample::TransposeToFrequency(0, finetune * 16);
if(attribute & 0x01)
{
@@ -204,11 +204,25 @@ bool CSoundFile::ReadMTM(FileReader &file, ModLoadingFlags loadFlags)
{
// No 8xx panning in MultiTracker, only E8x
cmd = param = 0;
+ } else if(cmd == 0x0E)
+ {
+ // MultiTracker does not support these commands
+ switch(param & 0xF0)
+ {
+ case 0x00:
+ case 0x30:
+ case 0x40:
+ case 0x60:
+ case 0x70:
+ case 0xF0:
+ cmd = param = 0;
+ break;
+ }
}
- m->command = cmd;
- m->param = param;
if(cmd != 0 || param != 0)
{
+ m->command = cmd;
+ m->param = param;
ConvertModCommand(*m);
#ifdef MODPLUG_TRACKER
m->Convert(MOD_TYPE_MTM, MOD_TYPE_S3M, *this);
diff --git a/soundlib/ModChannel.h b/soundlib/ModChannel.h
index 88c6df5..4cc1f7f 100644
--- a/soundlib/ModChannel.h
+++ b/soundlib/ModChannel.h
@@ -28,8 +28,8 @@ struct ModChannel
struct EnvInfo
{
FlagSet<EnvelopeFlags> flags;
- uint32 nEnvPosition;
- int32 nEnvValueAtReleaseJump;
+ uint32 nEnvPosition = 0;
+ int32 nEnvValueAtReleaseJump = NOT_YET_RELEASED;
void Reset()
{
@@ -87,7 +87,7 @@ struct ModChannel
CHANNELINDEX nMasterChn;
ModCommand rowCommand;
// 8-bit members
- uint8 resamplingMode;
+ ResamplingMode resamplingMode;
uint8 nRestoreResonanceOnNewNote; // See nRestorePanOnNewNote
uint8 nRestoreCutoffOnNewNote; // ditto
uint8 nNote;
diff --git a/soundlib/ModInstrument.cpp b/soundlib/ModInstrument.cpp
index d5eb6e6..604e4ea 100644
--- a/soundlib/ModInstrument.cpp
+++ b/soundlib/ModInstrument.cpp
@@ -163,13 +163,13 @@ ModInstrument::ModInstrument(SAMPLEINDEX sample)
nMixPlug = 0;
nVolRampUp = 0;
- nResampling = SRCMODE_DEFAULT;
+ resampling = SRCMODE_DEFAULT;
nCutSwing = 0;
nResSwing = 0;
nFilterMode = FLTMODE_UNCHANGED;
pitchToTempoLock.Set(0);
- nPluginVelocityHandling = PLUGIN_VELOCITYHANDLING_CHANNEL;
- nPluginVolumeHandling = PLUGIN_VOLUMEHANDLING_IGNORE;
+ pluginVelocityHandling = PLUGIN_VELOCITYHANDLING_CHANNEL;
+ pluginVolumeHandling = PLUGIN_VOLUMEHANDLING_IGNORE;
pTuning = CSoundFile::GetDefaultTuning();
diff --git a/soundlib/ModInstrument.h b/soundlib/ModInstrument.h
index 8fc1bae..41c4b51 100644
--- a/soundlib/ModInstrument.h
+++ b/soundlib/ModInstrument.h
@@ -64,46 +64,46 @@ struct InstrumentEnvelope : public std::vector<EnvelopeNode>
// Instrument Struct
struct ModInstrument
{
- uint32 nFadeOut; // Instrument fadeout speed
- uint32 nGlobalVol; // Global volume (0...64, all sample volumes are multiplied with this - TODO: This is 0...128 in Impulse Tracker)
- uint32 nPan; // Default pan (0...256), if the appropriate flag is set. Sample panning overrides instrument panning.
-
- uint16 nVolRampUp; // Default sample ramping up, 0 = use global default
-
- uint16 wMidiBank; // MIDI Bank (1...16384). 0 = Don't send.
- uint8 nMidiProgram; // MIDI Program (1...128). 0 = Don't send.
- uint8 nMidiChannel; // MIDI Channel (1...16). 0 = Don't send. 17 = Mapped (Send to tracker channel modulo 16).
- uint8 nMidiDrumKey; // Drum set note mapping (currently only used by the .MID loader)
- int8 midiPWD; // MIDI Pitch Wheel Depth in semitones
-
- FlagSet<InstrumentFlags> dwFlags; // Instrument flags
- NewNoteAction nNNA; // New note action
- DuplicateCheckType nDCT; // Duplicate check type (i.e. which condition will trigger the duplicate note action)
- DuplicateNoteAction nDNA; // Duplicate note action
- uint8 nPanSwing; // Random panning factor (0...64)
- uint8 nVolSwing; // Random volume factor (0...100)
- uint8 nIFC; // Default filter cutoff (0...127). Used if the high bit is set
- uint8 nIFR; // Default filter resonance (0...127). Used if the high bit is set
-
- int8 nPPS; // Pitch/Pan separation (i.e. how wide the panning spreads, -32...32)
- uint8 nPPC; // Pitch/Pan centre (zero-based, default is NOTE_MIDDLE_C - 1)
-
- PLUGINDEX nMixPlug; // Plugin assigned to this instrument (0 = no plugin, 1 = first plugin)
- uint8 nCutSwing; // Random cutoff factor (0...64)
- uint8 nResSwing; // Random resonance factor (0...64)
- InstrFilterMode nFilterMode; // Default filter mode
- uint8 nPluginVelocityHandling; // How to deal with plugin velocity (PLUGIN_VELOCITYHANDLING_* constants)
- uint8 nPluginVolumeHandling; // How to deal with plugin volume (PLUGIN_VOLUMEHANDLING_* constants)
- TEMPO pitchToTempoLock; // BPM at which the samples assigned to this instrument loop correctly (0 = unset)
- uint32 nResampling; // Resampling mode (SRCMODE_* constants)
- CTuning *pTuning; // sample tuning assigned to this instrument
-
- InstrumentEnvelope VolEnv; // Volume envelope data
- InstrumentEnvelope PanEnv; // Panning envelope data
- InstrumentEnvelope PitchEnv; // Pitch / filter envelope data
-
- uint8 NoteMap[128]; // Note mapping, e.g. C-5 => D-5.
- SAMPLEINDEX Keyboard[128]; // Sample mapping, e.g. C-5 => Sample 1
+ uint32 nFadeOut; // Instrument fadeout speed
+ uint32 nGlobalVol; // Global volume (0...64, all sample volumes are multiplied with this - TODO: This is 0...128 in Impulse Tracker)
+ uint32 nPan; // Default pan (0...256), if the appropriate flag is set. Sample panning overrides instrument panning.
+
+ uint16 nVolRampUp; // Default sample ramping up, 0 = use global default
+
+ uint16 wMidiBank; // MIDI Bank (1...16384). 0 = Don't send.
+ uint8 nMidiProgram; // MIDI Program (1...128). 0 = Don't send.
+ uint8 nMidiChannel; // MIDI Channel (1...16). 0 = Don't send. 17 = Mapped (Send to tracker channel modulo 16).
+ uint8 nMidiDrumKey; // Drum set note mapping (currently only used by the .MID loader)
+ int8 midiPWD; // MIDI Pitch Wheel Depth in semitones
+
+ FlagSet<InstrumentFlags> dwFlags; // Instrument flags
+ NewNoteAction nNNA; // New note action
+ DuplicateCheckType nDCT; // Duplicate check type (i.e. which condition will trigger the duplicate note action)
+ DuplicateNoteAction nDNA; // Duplicate note action
+ uint8 nPanSwing; // Random panning factor (0...64)
+ uint8 nVolSwing; // Random volume factor (0...100)
+ uint8 nIFC; // Default filter cutoff (0...127). Used if the high bit is set
+ uint8 nIFR; // Default filter resonance (0...127). Used if the high bit is set
+
+ int8 nPPS; // Pitch/Pan separation (i.e. how wide the panning spreads, -32...32)
+ uint8 nPPC; // Pitch/Pan centre (zero-based, default is NOTE_MIDDLE_C - 1)
+
+ PLUGINDEX nMixPlug; // Plugin assigned to this instrument (0 = no plugin, 1 = first plugin)
+ uint8 nCutSwing; // Random cutoff factor (0...64)
+ uint8 nResSwing; // Random resonance factor (0...64)
+ InstrFilterMode nFilterMode; // Default filter mode
+ PlugVelocityHandling pluginVelocityHandling; // How to deal with plugin velocity
+ PlugVolumeHandling pluginVolumeHandling; // How to deal with plugin volume
+ ResamplingMode resampling; // Resampling mode
+ TEMPO pitchToTempoLock; // BPM at which the samples assigned to this instrument loop correctly (0 = unset)
+ CTuning *pTuning; // sample tuning assigned to this instrument
+
+ InstrumentEnvelope VolEnv; // Volume envelope data
+ InstrumentEnvelope PanEnv; // Panning envelope data
+ InstrumentEnvelope PitchEnv; // Pitch / filter envelope data
+
+ uint8 NoteMap[128]; // Note mapping, e.g. C-5 => D-5.
+ SAMPLEINDEX Keyboard[128]; // Sample mapping, e.g. C-5 => Sample 1
char name[MAX_INSTRUMENTNAME];
char filename[MAX_INSTRUMENTFILENAME];
diff --git a/soundlib/ModSample.cpp b/soundlib/ModSample.cpp
index 73a54ae..05a0117 100644
--- a/soundlib/ModSample.cpp
+++ b/soundlib/ModSample.cpp
@@ -113,6 +113,11 @@ void ModSample::Convert(MODTYPE fromType, MODTYPE toType)
if(!CSoundFile::SupportsOPL(toType) && uFlags[CHN_ADLIB])
{
SetAdlib(false);
+ } else if(toType == MOD_TYPE_S3M && uFlags[CHN_ADLIB])
+ {
+ // No support for OPL3 waveforms in S3M
+ adlib[8] &= 0x03;
+ adlib[9] &= 0x03;
}
}
diff --git a/soundlib/ModSequence.cpp b/soundlib/ModSequence.cpp
index 5036913..15c3945 100644
--- a/soundlib/ModSequence.cpp
+++ b/soundlib/ModSequence.cpp
@@ -342,27 +342,31 @@ bool ModSequenceSet::ConvertSubsongsToMultipleSequences()
Reporting::Confirm("The order list contains separator items.\nThe new format supports multiple sequences, do you want to convert those separate tracks into multiple song sequences?",
"Order list conversion", false, true) == cnfYes)
{
- ORDERINDEX length = m_Sequences[0].GetLengthTailTrimmed();
+ ORDERINDEX length = m_Sequences[0].GetLength();
for(ORDERINDEX ord = 0; ord < length; ord++)
{
// End of subsong?
if(!m_Sequences[0].IsValidPat(ord) && m_Sequences[0][ord] != GetIgnoreIndex())
{
- // remove all separator patterns between current and next subsong first
- while(ord < length && !m_sndFile.Patterns.IsValidIndex(m_Sequences[0][ord]))
+ // Remove all separator patterns between current and next subsong first
+ while(ord < length && !m_sndFile.Patterns.IsValidPat(m_Sequences[0][ord]))
{
m_Sequences[0][ord] = GetInvalidPatIndex();
ord++;
modified = true;
}
- if(ord >= length) break;
- ORDERINDEX startOrd = ord;
- modified = true;
+ if(ord >= length)
+ break;
+
+ const SEQUENCEINDEX newSeq = AddSequence(false);
+ if(newSeq == SEQUENCEINDEX_INVALID)
+ break;
- SEQUENCEINDEX newSeq = AddSequence(false);
+ const ORDERINDEX startOrd = ord;
m_Sequences[newSeq].reserve(length - startOrd);
+ modified = true;
- // now, move all following orders to the new sequence
+ // Now, move all following orders to the new sequence
while(ord < length && m_Sequences[0][ord] != GetInvalidPatIndex())
{
PATTERNINDEX copyPat = m_Sequences[0][ord];
@@ -370,7 +374,7 @@ bool ModSequenceSet::ConvertSubsongsToMultipleSequences()
m_Sequences[0][ord] = GetInvalidPatIndex();
ord++;
- // is this a valid pattern? adjust pattern jump commands, if necessary.
+ // Is this a valid pattern? adjust pattern jump commands, if necessary.
if(m_sndFile.Patterns.IsValidPat(copyPat))
{
for(auto &m : m_sndFile.Patterns[copyPat])
@@ -382,6 +386,7 @@ bool ModSequenceSet::ConvertSubsongsToMultipleSequences()
}
}
}
+ ord--;
}
}
SetSequence(0);
diff --git a/soundlib/SampleFormatFLAC.cpp b/soundlib/SampleFormatFLAC.cpp
index 09aed0a..63de71c 100644
--- a/soundlib/SampleFormatFLAC.cpp
+++ b/soundlib/SampleFormatFLAC.cpp
@@ -196,8 +196,8 @@ struct FLACDecoder
if(length > 6 && !mpt::CompareNoCaseAscii(tag, "TITLE=", 6))
{
mpt::ustring sampleName;
- mpt::String::Read<mpt::String::maybeNullTerminated>(sampleName, client.sndFile.GetCharsetInternal(), tag + 6, length - 6);
- mpt::String::Copy(client.sndFile.m_szNames[client.sample], mpt::ToCharset(mpt::CharsetUTF8, sampleName));
+ mpt::String::Read<mpt::String::maybeNullTerminated>(sampleName, mpt::CharsetUTF8, tag + 6, length - 6);
+ mpt::String::Copy(client.sndFile.m_szNames[client.sample], mpt::ToCharset(client.sndFile.GetCharsetInternal(), sampleName));
} else if(length > 11 && !mpt::CompareNoCaseAscii(tag, "SAMPLERATE=", 11))
{
uint32 sampleRate = ConvertStrTo<uint32>(tag + 11);
diff --git a/soundlib/SampleIO.cpp b/soundlib/SampleIO.cpp
index bb371b2..f7cf942 100644
--- a/soundlib/SampleIO.cpp
+++ b/soundlib/SampleIO.cpp
@@ -1018,8 +1018,7 @@ size_t SampleIO::WriteSample(std::ostream &f, const ModSample &sample, SmpLength
MPT_ASSERT(len == numSamples);
if(sample.uFlags[CHN_16BIT])
{
- const int16 *const pSample16 = sample.sample16();
- const int16 *p = pSample16;
+ const int16 *p = sample.sample16();
int s_old = 0;
const int s_ofs = (GetEncoding() == unsignedPCM) ? 0x80 : 0;
for(SmpLength j = 0; j < numSamples; j++)
@@ -1028,7 +1027,7 @@ size_t SampleIO::WriteSample(std::ostream &f, const ModSample &sample, SmpLength
p++;
if(sample.uFlags[CHN_STEREO])
{
- s_new = (s_new + (static_cast<int>(*p)) + 1) / 2;
+ s_new = (s_new + mpt::rshift_signed(*p, 8) + 1) / 2;
p++;
}
if(GetEncoding() == deltaPCM)
diff --git a/soundlib/Snd_defs.h b/soundlib/Snd_defs.h
index 7522582..02f21d0 100644
--- a/soundlib/Snd_defs.h
+++ b/soundlib/Snd_defs.h
@@ -366,14 +366,14 @@ enum PluginMutePriority
};
// Plugin velocity handling options
-enum PLUGVELOCITYHANDLING
+enum PlugVelocityHandling : uint8
{
PLUGIN_VELOCITYHANDLING_CHANNEL = 0,
PLUGIN_VELOCITYHANDLING_VOLUME
};
// Plugin volumecommand handling options
-enum PLUGVOLUMEHANDLING
+enum PlugVolumeHandling : uint8
{
PLUGIN_VOLUMEHANDLING_MIDI = 0,
PLUGIN_VOLUMEHANDLING_DRYWET,
@@ -514,6 +514,7 @@ enum PlayBehaviour
kFT2NoteDelayWithoutInstr, // Sometime between OpenMPT 1.18.03.00 and 1.19.01.00, delayed instrument-less notes in XM started recalling the default sample volume and panning
kOPLFlexibleNoteOff, // Full control after note-off over OPL voices, ^^^ sends note cut instead of just note-off
kITInstrWithNoteOffOldEffects, // Instrument number with note-off recalls default volume - special cases with Old Effects enabled
+ kMIDIVolumeOnNoteOffBug, // Update MIDI channel volume on note-off (legacy bug emulation)
// Add new play behaviours here.
diff --git a/soundlib/Snd_fx.cpp b/soundlib/Snd_fx.cpp
index a2b0b87..2da930a 100644
--- a/soundlib/Snd_fx.cpp
+++ b/soundlib/Snd_fx.cpp
@@ -1025,7 +1025,8 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
if(m.note == NOTE_KEYOFF || m.note == NOTE_NOTECUT || (m.note == NOTE_FADE && GetNumInstruments())
|| ((m.command == CMD_MODCMDEX || m.command == CMD_S3MCMDEX) && (m.param & 0xF0) == 0xC0 && paramLo < numTicks)
- || (m.command == CMD_DELAYCUT && paramLo != 0 && startTick + paramLo < numTicks))
+ || (m.command == CMD_DELAYCUT && paramLo != 0 && startTick + paramLo < numTicks)
+ || m.command == CMD_KEYOFF)
{
stopNote = true;
}
@@ -1452,7 +1453,6 @@ void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bo
if(returnAfterVolumeAdjust) return;
-
// Instrument adjust
chn.nNewIns = 0;
@@ -4487,6 +4487,15 @@ void CSoundFile::ExtendedMODCommands(CHANNELINDEX nChn, ModCommand::PARAM param)
{
chn.nFineTune = MOD2XMFineTune(param);
if(chn.nPeriod && chn.rowCommand.IsNote()) chn.nPeriod = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed);
+ } else if(GetType() == MOD_TYPE_MTM)
+ {
+ if(chn.rowCommand.IsNote() && chn.pModSample != nullptr)
+ {
+ // Effect is permanent in MultiTracker
+ const_cast<ModSample *>(chn.pModSample)->nFineTune = param;
+ chn.nFineTune = param;
+ if(chn.nPeriod) chn.nPeriod = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed);
+ }
} else if(chn.rowCommand.IsNote())
{
chn.nFineTune = MOD2XMFineTune(param - 8);
@@ -5578,7 +5587,7 @@ void CSoundFile::KeyOff(ModChannel &chn) const
if(chn.position.GetUInt() > chn.nLength)
{
// Test case: SusAfterLoop.it
- chn.position.Set(chn.position.GetInt() - chn.nLength + chn.nLoopStart);
+ chn.position.Set(chn.nLoopStart + ((chn.position.GetInt() - chn.nLoopStart) % (chn.nLoopEnd - chn.nLoopStart)));
}
} else
{
@@ -5657,7 +5666,7 @@ void CSoundFile::SetTempo(TEMPO param, bool setFromUI)
// ProTracker sets the tempo after the first tick.
// Note: The case of one tick per row is handled in ProcessRow() instead.
// Test case: TempoChange.mod
-#if MPT_MSVC_AT_LEAST(2017,8)
+#if MPT_MSVC_AT_LEAST(2017,8) && MPT_MSVC_BEFORE(2019,0)
// Work-around MSVC getting confused about deduced const input type in noexcept operator inside noexcept condition.
m_PlayState.m_nMusicTempo.SetRaw(std::min(param.GetRaw(), specs.GetTempoMax().GetRaw()));
#else
@@ -5837,15 +5846,18 @@ uint32 CSoundFile::GetPeriodFromNote(uint32 note, int32 nFineTune, uint32 nC5Spe
return Util::muldiv_unsigned(8363, (FreqS3MTable[note % 12u] << 5), nC5Speed << (note / 12u));
//8363 * freq[note%12] / nC5Speed * 2^(5-note/12)
}
- } else if (GetType() == MOD_TYPE_XM)
+ } else if (GetType() & (MOD_TYPE_XM | MOD_TYPE_MTM))
{
if (note < 12) note = 12;
note -= 12;
- // FT2 Compatibility: The lower three bits of the finetune are truncated.
- // Test case: Finetune-Precision.xm
- if(m_playBehaviour[kFT2FinetunePrecision])
+ if(GetType() == MOD_TYPE_MTM)
+ {
+ nFineTune *= 16;
+ } else if(m_playBehaviour[kFT2FinetunePrecision])
{
+ // FT2 Compatibility: The lower three bits of the finetune are truncated.
+ // Test case: Finetune-Precision.xm
nFineTune &= ~7;
}
@@ -5892,7 +5904,7 @@ uint32 CSoundFile::GetPeriodFromNote(uint32 note, int32 nFineTune, uint32 nC5Spe
uint32 CSoundFile::GetFreqFromPeriod(uint32 period, uint32 c5speed, int32 nPeriodFrac) const
{
if (!period) return 0;
- if (GetType() == MOD_TYPE_XM)
+ if (GetType() & (MOD_TYPE_XM | MOD_TYPE_MTM))
{
if(m_playBehaviour[kFT2Periods])
{
diff --git a/soundlib/Sndfile.cpp b/soundlib/Sndfile.cpp
index 2481b17..45e0ed6 100644
--- a/soundlib/Sndfile.cpp
+++ b/soundlib/Sndfile.cpp
@@ -711,7 +711,7 @@ void CSoundFile::SetDspEffects(uint32 DSPMask)
{
#ifdef ENABLE_ASM
#ifndef NO_REVERB
- if(!(GetProcSupport() & PROCSUPPORT_MMX)) DSPMask &= ~SNDDSP_REVERB;
+ if(!(GetRealProcSupport() & PROCSUPPORT_MMX)) DSPMask &= ~SNDDSP_REVERB;
#endif
#endif
m_MixerSettings.DSPMask = DSPMask;
@@ -1163,7 +1163,6 @@ PlayBehaviourSet CSoundFile::GetDefaultPlaybackBehaviour(MODTYPE type)
break;
case MOD_TYPE_MOD:
- playBehaviour.set(kMODSampleSwap);
playBehaviour.set(kRowDelayWithNoteDelay);
break;
diff --git a/soundlib/Sndmix.cpp b/soundlib/Sndmix.cpp
index 566441e..7f51ccf 100644
--- a/soundlib/Sndmix.cpp
+++ b/soundlib/Sndmix.cpp
@@ -1036,7 +1036,8 @@ void CSoundFile::ProcessVolumeEnvelope(ModChannel &chn, int &vol) const
// if we are in the release portion of the envelope,
// rescale envelope factor so that it is proportional to the release point
// and release envelope beginning.
- if(chn.VolEnv.nEnvValueAtReleaseJump != NOT_YET_RELEASED)
+ if(pIns->VolEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET
+ && chn.VolEnv.nEnvValueAtReleaseJump != NOT_YET_RELEASED)
{
int envValueAtReleaseJump = chn.VolEnv.nEnvValueAtReleaseJump;
int envValueAtReleaseNode = pIns->VolEnv[pIns->VolEnv.nReleaseNode].value * 4;
@@ -2315,12 +2316,12 @@ bool CSoundFile::ReadNote()
if (chn.pCurrentSample || (chn.HasMIDIOutput() && !chn.dwFlags[CHN_KEYOFF | CHN_NOTEFADE]))
{
// Update VU-Meter (nRealVolume is 14-bit)
- uint32 vul = (chn.nRealVolume * chn.nRealPan) / (1 << 14);
+ uint32 vul = (chn.nRealVolume * (256-chn.nRealPan)) / (1 << 14);
if (vul > 127) vul = 127;
if (chn.nLeftVU > 127) chn.nLeftVU = (uint8)vul;
vul /= 2;
if (chn.nLeftVU < vul) chn.nLeftVU = (uint8)vul;
- uint32 vur = (chn.nRealVolume * (256-chn.nRealPan)) / (1 << 14);
+ uint32 vur = (chn.nRealVolume * chn.nRealPan) / (1 << 14);
if (vur > 127) vur = 127;
if (chn.nRightVU > 127) chn.nRightVU = (uint8)vur;
vur /= 2;
@@ -2394,13 +2395,13 @@ bool CSoundFile::ReadNote()
//if (chn.nNewRightVol > 0xFFFF) chn.nNewRightVol = 0xFFFF;
//if (chn.nNewLeftVol > 0xFFFF) chn.nNewLeftVol = 0xFFFF;
- if(chn.pModInstrument && Resampling::IsKnownMode(chn.pModInstrument->nResampling))
+ if(chn.pModInstrument && Resampling::IsKnownMode(chn.pModInstrument->resampling))
{
// For defined resampling modes, use per-instrument resampling mode if set
- chn.resamplingMode = static_cast<uint8>(chn.pModInstrument->nResampling);
+ chn.resamplingMode = chn.pModInstrument->resampling;
} else if(Resampling::IsKnownMode(m_nResampling))
{
- chn.resamplingMode = static_cast<uint8>(m_nResampling);
+ chn.resamplingMode = m_nResampling;
} else if(m_SongFlags[SONG_ISAMIGA] && m_Resampler.m_Settings.emulateAmiga)
{
// Enforce Amiga resampler for Amiga modules
@@ -2408,7 +2409,7 @@ bool CSoundFile::ReadNote()
} else
{
// Default to global mixer settings
- chn.resamplingMode = static_cast<uint8>(m_Resampler.m_Settings.SrcMode);
+ chn.resamplingMode = m_Resampler.m_Settings.SrcMode;
}
if(chn.increment.IsUnity() && !(chn.dwFlags[CHN_VIBRATO] || chn.nAutoVibDepth || chn.resamplingMode == SRCMODE_AMIGA))
@@ -2540,11 +2541,13 @@ void CSoundFile::ProcessMidiOut(CHANNELINDEX nChn)
if(note != NOTE_NONE)
{
int32 velocity = static_cast<int32>(4 * defaultVolume);
- switch(pIns->nPluginVelocityHandling)
+ switch(pIns->pluginVelocityHandling)
{
case PLUGIN_VELOCITYHANDLING_CHANNEL:
velocity = chn.nVolume;
- break;
+ break;
+ default:
+ break;
}
int32 swing = chn.nVolSwing;
@@ -2560,23 +2563,23 @@ void CSoundFile::ProcessMidiOut(CHANNELINDEX nChn)
SendMIDINote(nChn, realNote, static_cast<uint16>(velocity));
}
+ const bool processVolumeAlsoOnNote = (pIns->pluginVelocityHandling == PLUGIN_VELOCITYHANDLING_VOLUME);
+ const bool hasNote = m_playBehaviour[kMIDIVolumeOnNoteOffBug] ? (note != NOTE_NONE) : ModCommand::IsNote(note);
- const bool processVolumeAlsoOnNote = (pIns->nPluginVelocityHandling == PLUGIN_VELOCITYHANDLING_VOLUME);
-
- if((hasVolCommand && !note) || (note && processVolumeAlsoOnNote))
+ if((hasVolCommand && !hasNote) || (hasNote && processVolumeAlsoOnNote))
{
- switch(pIns->nPluginVolumeHandling)
+ switch(pIns->pluginVolumeHandling)
{
case PLUGIN_VOLUMEHANDLING_DRYWET:
if(hasVolCommand) pPlugin->SetDryRatio(2 * vol);
else pPlugin->SetDryRatio(2 * defaultVolume);
break;
-
case PLUGIN_VOLUMEHANDLING_MIDI:
if(hasVolCommand) pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Coarse, std::min<uint8>(127u, 2u * vol), nChn);
else pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Coarse, static_cast<uint8>(std::min<uint32>(127u, 2u * defaultVolume)), nChn);
break;
-
+ default:
+ break;
}
}
}
diff --git a/soundlib/UpgradeModule.cpp b/soundlib/UpgradeModule.cpp
index 33e70af..c614d58 100644
--- a/soundlib/UpgradeModule.cpp
+++ b/soundlib/UpgradeModule.cpp
@@ -413,6 +413,18 @@ void CSoundFile::UpgradeModule()
}
}
+ if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 28, 03, 04))
+ {
+ for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if (Instruments[i] != nullptr)
+ {
+ if(Instruments[i]->pluginVolumeHandling == PLUGIN_VOLUMEHANDLING_MIDI || Instruments[i]->pluginVolumeHandling == PLUGIN_VOLUMEHANDLING_DRYWET)
+ {
+ m_playBehaviour.set(kMIDIVolumeOnNoteOffBug);
+ break;
+ }
+ }
+ }
+
Patterns.ForEachModCommand(UpgradePatternData(*this));
// Convert compatibility flags