Updated libopenmpt to version 0.3.8.

CQTexperiment
Christopher Snowhill 2018-04-09 15:54:31 -07:00 committed by Chris Moeller
parent df847cdd05
commit baace3fea9
33 changed files with 313 additions and 194 deletions

View File

@ -473,7 +473,7 @@
#endif // arch
#endif // ENABLE_ASM
#if defined(MPT_WITH_MPG123) && defined(MPT_BUILD_MSVC) && defined(MPT_BUILD_MSVC_STATIC) && !MPT_OS_WINDOWS_WINRT
#if defined(MPT_WITH_MPG123) && defined(MPT_BUILD_MSVC) && defined(MPT_BUILD_MSVC_STATIC) && !defined(LIBOPENMPT_NODELAYLOAD) && !MPT_OS_WINDOWS_WINRT
#define MPT_ENABLE_MPG123_DELAYLOAD
#endif

View File

@ -349,8 +349,9 @@ public:
}
public:
PinnedRawDataView()
: size_(0)
, pinnedData(nullptr)
{
return;
}
PinnedRawDataView(const FileReader &file)
{

View File

@ -559,25 +559,33 @@ private:
static mpt::ustring From8bit(const std::string &str)
{
if(charset == mpt::CharsetUTF8)
{
return mpt::ToUnicode(mpt::CharsetUTF8, str);
}
// auto utf8 detection
if(tryUTF8 && mpt::IsUTF8(str))
MPT_CONSTANT_IF(charset == mpt::CharsetUTF8)
{
return mpt::ToUnicode(mpt::CharsetUTF8, str);
} else
{
return mpt::ToUnicode(charset, str);
// auto utf8 detection
MPT_CONSTANT_IF(tryUTF8)
{
if(mpt::IsUTF8(str))
{
return mpt::ToUnicode(mpt::CharsetUTF8, str);
} else
{
return mpt::ToUnicode(charset, str);
}
} else
{
return mpt::ToUnicode(charset, str);
}
}
}
public:
// 8 bit
BasicAnyString(const char *str) : mpt::ustring(str ? mpt::ToUnicode(charset, str) : mpt::ustring()) { }
BasicAnyString(const std::string str) : mpt::ustring(mpt::ToUnicode(charset, str)) { }
BasicAnyString(const char *str) : mpt::ustring(From8bit(str ? str : std::string())) { }
BasicAnyString(const std::string str) : mpt::ustring(From8bit(str)) { }
// unicode
BasicAnyString(const mpt::ustring &str) : mpt::ustring(str) { }

View File

@ -18,8 +18,8 @@ OPENMPT_NAMESPACE_BEGIN
//Version definitions. The only thing that needs to be changed when changing version number.
#define VER_MAJORMAJOR 1
#define VER_MAJOR 27
#define VER_MINOR 04
#define VER_MINORMINOR 02
#define VER_MINOR 07
#define VER_MINORMINOR 00
//Version string. For example "1.17.02.28"
#define MPT_VERSION_STR VER_STRINGIZE(VER_MAJORMAJOR) "." VER_STRINGIZE(VER_MAJOR) "." VER_STRINGIZE(VER_MINOR) "." VER_STRINGIZE(VER_MINORMINOR)

View File

@ -4,6 +4,7 @@ The following changes have been made:
- mp3_create() declaration has been fixed.
- GET_DATA() has been rewritten to avoid unaligned access warnings.
- Signed/unsigned comparison warnings have been fixed.
- Detection of stdint types has been fixed to work on *BSD.
- Modifications have been marked with // OpenMPT
- Obviously, unnecessary folders and files have been removed.
- For building, premake is used to generate Visual Studio project files.

View File

@ -42,7 +42,7 @@
#endif
#include <math.h>
#ifndef __int8_t_defined
#if !defined(__int8_t_defined) && !defined(_INT8_T_DECLARED) /* OpenMPT */
#define __int8_t_defined
typedef unsigned char uint8_t;
typedef signed char int8_t;

View File

@ -1,6 +1,6 @@
This folder contains the stb_vorbis library from
https://github.com/nothings/stb/blob/master/stb_vorbis.c
(commit 9d9f75eb682dd98b34de08bb5c489c6c561c9fa6)
This folder contains the stb_vorbis library from
https://github.com/nothings/stb/blob/master/stb_vorbis.c v1.14
commit e6afb9cbae4064da8c3e69af3ff5c4629579c1d2 (2018-02-11)
Modifications:
* Use of alloca has been replaced with malloc, as alloca is not in C99 and

View File

@ -1,11 +1,11 @@
// Ogg Vorbis audio decoder - v1.11 - public domain
// Ogg Vorbis audio decoder - v1.14 - public domain
// http://nothings.org/stb_vorbis/
//
// Original version written by Sean Barrett in 2007.
//
// Originally sponsored by RAD Game Tools. Seeking sponsored
// by Phillip Bennefall, Marc Andersen, Aaron Baker, Elias Software,
// Aras Pranckevicius, and Sean Barrett.
// Originally sponsored by RAD Game Tools. Seeking implementation
// sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker,
// Elias Software, Aras Pranckevicius, and Sean Barrett.
//
// LICENSE
//
@ -30,22 +30,26 @@
// Tom Beaumont Ingo Leitgeb Nicolas Guillemot
// Phillip Bennefall Rohit Thiago Goulart
// manxorist@github saga musix github:infatum
// Timur Gagiev
//
// Partial history:
// 1.11 - 2017/07/23 - fix MinGW compilation
// 1.10 - 2017/03/03 - more robust seeking; fix negative ilog(); clear error in open_memory
// 1.09 - 2016/04/04 - back out 'truncation of last frame' fix from previous version
// 1.08 - 2016/04/02 - warnings; setup memory leaks; truncation of last frame
// 1.07 - 2015/01/16 - fixes for crashes on invalid files; warning fixes; const
// 1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson)
// 1.14 - 2018-02-11 - delete bogus dealloca usage
// 1.13 - 2018-01-29 - fix truncation of last frame (hopefully)
// 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files
// 1.11 - 2017-07-23 - fix MinGW compilation
// 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory
// 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version
// 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame
// 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const
// 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson)
// some crash fixes when out of memory or with corrupt files
// fix some inappropriately signed shifts
// 1.05 - 2015/04/19 - don't define __forceinline if it's redundant
// 1.04 - 2014/08/27 - fix missing const-correct case in API
// 1.03 - 2014/08/07 - warning fixes
// 1.02 - 2014/07/09 - declare qsort comparison as explicitly _cdecl in Windows
// 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float (interleaved was correct)
// 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in >2-channel;
// 1.05 - 2015-04-19 - don't define __forceinline if it's redundant
// 1.04 - 2014-08-27 - fix missing const-correct case in API
// 1.03 - 2014-08-07 - warning fixes
// 1.02 - 2014-07-09 - declare qsort comparison as explicitly _cdecl in Windows
// 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float (interleaved was correct)
// 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in >2-channel;
// (API change) report sample rate for decode-full-file funcs
//
// See end of file for full version history.
@ -885,11 +889,7 @@ static int error(vorb *f, enum STBVorbisError e)
#if 0 // OpenMPT
#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size))
#ifdef dealloca
#define temp_free(f,p) (f->alloc.alloc_buffer ? 0 : dealloca(size))
#else
#define temp_free(f,p) 0
#endif
#else // OpenMPT
#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : malloc(size)) // OpenMPT
#define temp_free(f,p) (f->alloc.alloc_buffer ? 0 : free(p)) // OpenMPT
@ -2051,6 +2051,8 @@ static int residue_decode(vorb *f, Codebook *book, float *target, int offset, in
return TRUE;
}
// n is 1/2 of the blocksize --
// specification: "Correct per-vector decode length is [n]/2"
static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode)
{
int i,j,pass;
@ -2058,7 +2060,10 @@ static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int
int rtype = f->residue_types[rn];
int c = r->classbook;
int classwords = f->codebooks[c].dimensions;
int n_read = r->end - r->begin;
unsigned int actual_size = rtype == 2 ? n*2 : n;
unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size);
unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size);
int n_read = limit_r_end - limit_r_begin;
int part_read = n_read / r->part_size;
int temp_alloc_point = temp_alloc_save(f);
#ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
@ -3400,7 +3405,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
if (f->last_seg_which == f->end_seg_with_known_loc) {
// if we have a valid current loc, and this is final:
if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) {
uint32 current_end = f->known_loc_for_packet - (n-right_end);
uint32 current_end = f->known_loc_for_packet;
// then let's infer the size of the (probably) short final frame
if (current_end < f->current_loc + (right_end-left_start)) {
if (current_end < f->current_loc) {
@ -3409,7 +3414,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
} else {
*len = current_end - f->current_loc;
}
*len += left_start;
*len += left_start; // this doesn't seem right, but has no ill effect on my test files
if (*len > right_end) *len = right_end; // this should never happen
f->current_loc += *len;
return TRUE;
@ -4064,6 +4069,7 @@ static int start_decoder(vorb *f)
f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2);
f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist);
if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem);
memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1);
#ifdef STB_VORBIS_NO_DEFER_FLOOR
f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2);
if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem);
@ -4091,7 +4097,10 @@ static int start_decoder(vorb *f)
int i,max_part_read=0;
for (i=0; i < f->residue_count; ++i) {
Residue *r = f->residue_config + i;
int n_read = r->end - r->begin;
unsigned int actual_size = f->blocksize_1 / 2;
unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size;
unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size;
int n_read = limit_r_end - limit_r_begin;
int part_read = n_read / r->part_size;
if (part_read > max_part_read)
max_part_read = part_read;
@ -4102,6 +4111,8 @@ static int start_decoder(vorb *f)
classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *));
#endif
// maximum reasonable partition size is f->blocksize_1
f->temp_memory_required = classify_mem;
if (imdct_mem > f->temp_memory_required)
f->temp_memory_required = imdct_mem;
@ -5365,20 +5376,22 @@ int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, in
#endif // STB_VORBIS_NO_PULLDATA_API
/* Version history
1.10 - 2017/03/03 - more robust seeking; fix negative ilog(); clear error in open_memory
1.09 - 2016/04/04 - back out 'avoid discarding last frame' fix from previous version
1.08 - 2016/04/02 - fixed multiple warnings; fix setup memory leaks;
1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files
1.11 - 2017-07-23 - fix MinGW compilation
1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory
1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version
1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks;
avoid discarding last frame of audio data
1.07 - 2015/01/16 - fixed some warnings, fix mingw, const-correct API
1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API
some more crash fixes when out of memory or with corrupt files
1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson)
1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson)
some crash fixes when out of memory or with corrupt files
1.05 - 2015/04/19 - don't define __forceinline if it's redundant
1.04 - 2014/08/27 - fix missing const-correct case in API
1.03 - 2014/08/07 - Warning fixes
1.02 - 2014/07/09 - Declare qsort compare function _cdecl on windows
1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float
1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in multichannel
1.05 - 2015-04-19 - don't define __forceinline if it's redundant
1.04 - 2014-08-27 - fix missing const-correct case in API
1.03 - 2014-08-07 - Warning fixes
1.02 - 2014-07-09 - Declare qsort compare function _cdecl on windows
1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float
1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in multichannel
(API change) report sample rate for decode-full-file funcs
0.99996 - bracket #include <malloc.h> for macintosh compilation by Laurent Gomila
0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem

View File

@ -5,6 +5,39 @@ Changelog {#changelog}
For fully detailed change log, please see the source repository directly. This
is just a high-level summary.
### libopenmpt 0.3.8 (2018-04-08)
* [**Sec**] Possible out-of-bounds memory read with IT / ITP / MO3 files
containing pattern loops (r10028).
* Keep track of active SFx macro during seeking.
* The "note cut" duplicate note action did not volume-ramp the previously
playing sample.
* A song starting with non-existing patterns could not be played.
* DSM: Support restart position and 16-bit samples.
* DTM: Import global volume.
### libopenmpt 0.3.7 (2018-03-11)
* [**Bug**] libopenmpt did not build with NDK r13b on armeabi due to missing
`-latomic`.
* [**Bug**] xmp-openmpt: Sample rate and number of output channels were not
applied correctly when using per-file settings.
* [**Change**] foo_openmpt: foo_openmpt is now packaged as a fb2k-component
package for easier installation.
* IT: More accurate song length calculation for pattern loops that have no
start command and are following another pattern loop.
* IMF: Filter cutoff was upside down and the cutoff range was too small.
* MED: Correctly import patterns with less channels than the maximum used
amount. Import "STP" note stop command.
* DBM: Key Off and Set Envelope Position were imported incorrectly.
High sample offset (E7x) is now supported.
* DIGI / DBM: Arpeggio should not return to base note at end of row.
* Some filter changes through MIDI macros were not applied if the note volume
was set to 0 on the same row.
### libopenmpt 0.3.6 (2018-02-03)
* [**Sec**] Possible out-of-bounds memory read with malformed STP files.

View File

@ -38,6 +38,7 @@ Dependencies
particular:
* `char` can be `signed` or `unsigned`
* shifting signed values is implementation defined
* `signed` integer overflow is undefined
* `float` and `double` can be non-IEEE754
* Required compilers to use libopenmpt:

View File

@ -19,7 +19,7 @@
/*! \brief libopenmpt minor version number */
#define OPENMPT_API_VERSION_MINOR 3
/*! \brief libopenmpt patch version number */
#define OPENMPT_API_VERSION_PATCH 6
#define OPENMPT_API_VERSION_PATCH 8
/*! \brief libopenmpt pre-release tag */
#define OPENMPT_API_VERSION_PREREL ""
/*! \brief libopenmpt pre-release flag */

View File

@ -1,8 +1,8 @@
LIBOPENMPT_VERSION_MAJOR=0
LIBOPENMPT_VERSION_MINOR=3
LIBOPENMPT_VERSION_PATCH=6
LIBOPENMPT_VERSION_PATCH=8
LIBOPENMPT_VERSION_PREREL=
LIBOPENMPT_LTVER_CURRENT=1
LIBOPENMPT_LTVER_REVISION=6
LIBOPENMPT_LTVER_REVISION=8
LIBOPENMPT_LTVER_AGE=1

View File

@ -163,6 +163,7 @@ struct self_xmplay_t {
std::size_t num_channels;
xmp_openmpt_settings settings;
openmpt::module_ext * mod;
bool set_format_called;
openmpt::ext::pattern_vis * pattern_vis;
std::int32_t tempo_factor, pitch_factor;
bool single_subsong_mode;
@ -171,6 +172,7 @@ struct self_xmplay_t {
, num_channels(2)
, settings()
, mod(0)
, set_format_called(false)
, pattern_vis(0)
, tempo_factor(0)
, pitch_factor(0)
@ -179,11 +181,13 @@ struct self_xmplay_t {
settings.changed = apply_and_save_options;
}
void on_new_mod() {
set_format_called = false;
self->pattern_vis = static_cast<openmpt::ext::pattern_vis *>( self->mod->get_interface( openmpt::ext::pattern_vis_id ) );
}
void delete_mod() {
if ( mod ) {
pattern_vis = 0;
set_format_called = false;
delete mod;
mod = 0;
}
@ -310,6 +314,12 @@ static void save_settings_to_xml( std::string & xml, const libopenmpt::plugin::s
static void apply_options() {
if ( self->mod ) {
if ( !self->set_format_called ) {
// SetFormat will only be called once after loading a file.
// We cannot apply samplerate or numchannels changes afterwards during playback.
self->samplerate = self->settings.samplerate;
self->num_channels = self->settings.channels;
}
self->mod->set_repeat_count( self->settings.repeatcount );
self->mod->set_render_param( openmpt::module::RENDER_MASTERGAIN_MILLIBEL, self->settings.mastergain_millibel );
self->mod->set_render_param( openmpt::module::RENDER_STEREOSEPARATION_PERCENT, self->settings.stereoseparation );
@ -893,8 +903,6 @@ static DWORD WINAPI openmpt_Open( const char * filename, XMPFILE file ) {
clear_current_timeinfo();
reset_timeinfos();
apply_options();
self->samplerate = self->settings.samplerate;
self->num_channels = self->settings.channels;
std::int32_t num_subsongs = self->mod->get_num_subsongs();
self->subsong_lengths.resize( num_subsongs );
@ -926,6 +934,9 @@ static void WINAPI openmpt_SetFormat( XMPFORMAT * form ) {
if ( !form ) {
return;
}
// SetFormat will only be called once after loading a file.
// We cannot apply samplerate or numchannels changes afterwards during playback.
self->set_format_called = true;
if ( !self->mod ) {
form->rate = 0;
form->chan = 0;

View File

@ -675,12 +675,7 @@ struct Convert<int64, float32>
{
Limit(val, -1.0f, 1.0f);
val *= static_cast<float>(uint64(1)<<63);
#if MPT_SC_AVOID_FLOOR
// MSVC with x87 floating point math calls floor for the more intuitive version
return mpt::saturate_cast<int64>(MPT_SC_RSHIFT_SIGNED(static_cast<int64>(val * 2.0f + 1.0f), 1));
#else
return mpt::saturate_cast<int32>(static_cast<int64>(std::floor(val + 0.5f)));
#endif
return mpt::saturate_cast<int64>(std::floor(val + 0.5f));
}
};
@ -693,12 +688,7 @@ struct Convert<int64, double>
{
Limit(val, -1.0, 1.0);
val *= static_cast<double>(uint64(1)<<63);
#if MPT_SC_AVOID_FLOOR
// MSVC with x87 floating point math calls floor for the more intuitive version
return mpt::saturate_cast<int64>(MPT_SC_RSHIFT_SIGNED(static_cast<int64>(val * 2.0 + 1.0), 1));
#else
return mpt::saturate_cast<int32>(static_cast<int64>(std::floor(val + 0.5)));
#endif
return mpt::saturate_cast<int64>(std::floor(val + 0.5));
}
};

View File

@ -446,7 +446,7 @@ static uint8 DLSSustainLevelToLinear(int32 sustain)
if(sustain >= 0)
{
int32 l = sustain / (1000 * 512);
if(l >= 0 || l <= 128)
if(l >= 0 && l <= 128)
return static_cast<uint8>(l);
}
return 128;
@ -1885,6 +1885,8 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui
int32 lSusLevel = - DLS32BitRelativeLinearToGain(lStartFactor << 10) / 65536;
int32 lDecayEndTime = (lReleaseTime * lSusLevel) / 960;
lReleaseTime -= lDecayEndTime;
if(pIns->VolEnv.nSustainEnd > 0)
pIns->VolEnv.nReleaseNode = pIns->VolEnv.nSustainEnd;
for (uint32 i=0; i<5; i++)
{
int32 lFactor = 1 + ((lStartFactor * 3) >> (i+2));
@ -1930,6 +1932,7 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui
pIns->VolEnv[3] = EnvelopeNode(pIns->VolEnv[2].tick * 2u, ENVELOPE_MIN); // 1 second max. for drums
}
}
pIns->Convert(MOD_TYPE_MPT, sndFile.GetType());
return true;
}

View File

@ -133,8 +133,8 @@ static const ModCommand::COMMAND dbmEffects[] =
CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL, CMD_TREMOLO,
CMD_PANNING8, CMD_OFFSET, CMD_VOLUMESLIDE, CMD_POSITIONJUMP,
CMD_VOLUME, CMD_PATTERNBREAK, CMD_MODCMDEX, CMD_TEMPO,
CMD_GLOBALVOLUME, CMD_GLOBALVOLSLIDE, CMD_KEYOFF, CMD_SETENVPOSITION,
CMD_CHANNELVOLUME, CMD_CHANNELVOLSLIDE, CMD_NONE, CMD_NONE,
CMD_GLOBALVOLUME, CMD_GLOBALVOLSLIDE, CMD_NONE, CMD_NONE,
CMD_KEYOFF, CMD_SETENVPOSITION, CMD_NONE, CMD_NONE,
CMD_NONE, CMD_PANNINGSLIDE, CMD_NONE, CMD_NONE,
CMD_NONE, CMD_NONE, CMD_NONE,
#ifndef NO_PLUGINS
@ -215,11 +215,11 @@ static void ConvertDBMEffect(uint8 &command, uint8 &param)
param = (param == 0x50) ? 0x00 : 0x40;
}
break;
case 0x60: // set loop begin / loop
// TODO
case 0x60: // Pattern loop
break;
case 0x70: // set offset
// TODO
case 0x70: // Coarse offset
command = CMD_S3MCMDEX;
param = 0xA0 | (param & 0x0F);
break;
default:
// Rest will be converted later from CMD_MODCMDEX to CMD_S3MCMDEX.
@ -355,6 +355,7 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags)
m_madeWithTracker = mpt::format(MPT_USTRING("DigiBooster Pro %1.%2"))(mpt::ufmt::hex(fileHeader.trkVerHi), mpt::ufmt::hex(fileHeader.trkVerLo));
m_playBehaviour.set(kSlidesAtSpeed1);
m_playBehaviour.reset(kITVibratoTremoloPanbrello);
m_playBehaviour.reset(kITArpeggio);
// Name chunk
FileReader nameChunk = chunks.GetChunk(DBMChunk::idNAME);
@ -493,7 +494,7 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags)
continue;
}
ModCommand dummy;
ModCommand dummy = ModCommand::Empty();
ModCommand &m = ch <= GetNumChannels() ? patRow[ch - 1] : dummy;
const uint8 b = chunk.ReadUint8();
@ -668,7 +669,8 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags)
{
ModSample &srcSample = Samples[0];
const int8 *smpData = srcSample.pSample8;
uint32 predelay = Util::muldiv_unsigned(20116, srcSample.nC5Speed, 100000);
SmpLength predelay = Util::muldiv_unsigned(20116, srcSample.nC5Speed, 100000);
LimitMax(predelay, srcSample.nLength);
smpData += predelay * srcSample.GetBytesPerSample();
srcSample.nLength -= predelay;
@ -683,7 +685,8 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags)
memcpy(sample.pSample, smpData, sample.GetSampleSizeInBytes());
smpData += sample.GetSampleSizeInBytes();
srcSample.nLength -= sample.nLength;
uint32 gap = Util::muldiv_unsigned(454, srcSample.nC5Speed, 10000);
SmpLength gap = Util::muldiv_unsigned(454, srcSample.nC5Speed, 10000);
LimitMax(gap, srcSample.nLength);
smpData += gap * srcSample.GetBytesPerSample();
srcSample.nLength -= gap;
}

View File

@ -6,11 +6,13 @@
* MilkyTracker can load it, but the only files of this format seen in the wild are also
* available in their original format, so I did not bother implementing it so far.
*
* 2. Using PLAY.EXE v1.02, commands not supported in MOD do not seem to do anything at all.
* 2. Using both PLAY.EXE v1.02 and v2.00, commands not supported in MOD do not seem to do
* anything at all.
* In particular commands 0x11-0x13 handled below are ignored, and no files have been spotted
* in the wild using any commands > 0x0F at all.
* S3M-style retrigger does not seem to exist - it is translated to volume slides by CONV.EXE,
* and J00 in S3M files is not converted either.
* and J00 in S3M files is not converted either. S3M pattern loops (SBx) are not converted
* properly by CONV.EXE and completely ignored by PLAY.EXE.
* Command 8 (set panning) uses 00-80 for regular panning and A4 for surround, probably
* making DSIK one of the first applications to use this particular encoding scheme still
* used in "extended" S3Ms today.
@ -36,9 +38,10 @@ MPT_BINARY_STRUCT(DSMChunk, 8)
struct DSMSongHeader
{
char songName[28];
char reserved1[2];
uint16le fileVersion;
uint16le flags;
char reserved2[4];
uint16le orderPos;
uint16le restartPos;
uint16le numOrders;
uint16le numSamples;
uint16le numPatterns;
@ -57,13 +60,12 @@ MPT_BINARY_STRUCT(DSMSongHeader, 192)
struct DSMSampleHeader
{
char filename[13];
uint8le flags;
char reserved1;
uint16le flags;
uint8le volume;
uint32le length;
uint32le loopStart;
uint32le loopEnd;
char reserved2[4];
uint32le dataPtr; // Interal sample pointer during playback in DSIK
uint32le sampleRate;
char sampleName[28];
@ -93,6 +95,8 @@ struct DSMSampleHeader
sampleIO |= SampleIO::deltaPCM; // fairlight.dsm by Comrade J
else if(flags & 0x02)
sampleIO |= SampleIO::signedPCM;
if(flags & 0x04)
sampleIO |= SampleIO::_16bit;
return sampleIO;
}
};
@ -197,10 +201,14 @@ bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags)
DSMSongHeader songHeader;
file.ReadStructPartial(songHeader, chunkHeader.size);
if(songHeader.numOrders > 128 || songHeader.numChannels > 16 || songHeader.numPatterns > 256 || songHeader.restartPos > 128)
{
return false;
}
InitializeGlobals(MOD_TYPE_DSM);
mpt::String::Read<mpt::String::maybeNullTerminated>(m_songName, songHeader.songName);
m_nChannels = Clamp<uint16, uint16>(songHeader.numChannels, 1, 16);
m_nChannels = std::max<uint16>(songHeader.numChannels, 1);
m_nDefaultSpeed = songHeader.speed;
m_nDefaultTempo.Set(songHeader.bpm);
m_nDefaultGlobalVolume = std::min<uint8>(songHeader.globalVol, 64) * 4u;
@ -224,6 +232,8 @@ bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags)
}
ReadOrderFromArray(Order(), songHeader.orders, songHeader.numOrders, 0xFF, 0xFE);
if(songHeader.restartPos < songHeader.numOrders)
Order().SetRestartPos(songHeader.restartPos);
// Read pattern and sample chunks
PATTERNINDEX patNum = 0;
@ -240,6 +250,7 @@ bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags)
}
chunk.Skip(2);
ModCommand dummy = ModCommand::Empty();
ROWINDEX row = 0;
PatternRow rowBase = Patterns[patNum].GetRow(0);
while(chunk.CanRead(1) && row < 64)
@ -253,7 +264,6 @@ bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags)
}
CHANNELINDEX chn = (flag & 0x0F);
ModCommand dummy = ModCommand();
ModCommand &m = (chn < GetNumChannels() ? rowBase[chn] : dummy);
if(flag & 0x80)

View File

@ -102,6 +102,7 @@ struct DTMSample
if(formatVersion == DTM_206_PATTERN_FORMAT && transpose > 0 && transpose != 48)
{
// Digital Home Studio applies this unconditionally, but some old songs sound wrong then (delirium.dtm).
// Digital Tracker 2.03 ignores the setting.
// Maybe this should not be applied for "real" Digital Tracker modules?
transposeAmount += (48 - transpose) * 128;
}
@ -292,7 +293,13 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags)
ChnSettings[chn].nPan = static_cast<uint16>(128 + Util::muldivr(std::min<int>(panning[chn], 180) - 90, 128, 90));
}
chunk.Skip(146);
chunk.Skip(16);
// Chunk ends here for old DTM modules
if(chunk.CanRead(2))
{
m_nDefaultGlobalVolume = std::min<uint32>(chunk.ReadUint16BE(), MAX_GLOBAL_VOLUME);
}
chunk.Skip(128);
uint16be volume[32];
if(chunk.ReadArray(volume))
{
@ -356,9 +363,10 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags)
chunk.ReadStruct(instr);
if(instr.insNum < GetNumInstruments())
{
Samples[instr.insNum + 1].nVibDepth = instr.vibDepth;
Samples[instr.insNum + 1].nVibRate = instr.vibRate;
Samples[instr.insNum + 1].nVibSweep = 255;
ModSample &sample = Samples[instr.insNum + 1];
sample.nVibDepth = instr.vibDepth;
sample.nVibRate = instr.vibRate;
sample.nVibSweep = 255;
ModInstrument *mptIns = AllocateInstrument(instr.insNum + 1, instr.insNum + 1);
if(mptIns != nullptr)
@ -463,6 +471,8 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags)
#ifdef MODPLUG_TRACKER
m->Convert(MOD_TYPE_MOD, MOD_TYPE_IT, *this);
#endif
// G is 8-bit volume
// P is tremor (need to disable oldfx)
}
if(data[5] & 0x80)
tick += (data[5] & 0x7F) * 0x100 + rowChunk.ReadUint8();
@ -562,12 +572,12 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags)
// Read sample data
for(auto &chunk : chunks.GetAllChunks(DTMChunk::idDAIT))
{
PATTERNINDEX smp = chunk.ReadUint16BE() + 1;
if(smp == 0 || smp > GetNumSamples() || !(loadFlags & loadSampleData))
SAMPLEINDEX smp = chunk.ReadUint16BE();
if(smp >= GetNumSamples() || !(loadFlags & loadSampleData))
{
continue;
}
ModSample &mptSmp = Samples[smp];
ModSample &mptSmp = Samples[smp + 1];
SampleIO(
mptSmp.uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit,
mptSmp.uFlags[CHN_STEREO] ? SampleIO::stereoInterleaved: SampleIO::mono,

View File

@ -138,9 +138,9 @@ struct IMFInstrument
if(mptIns.PitchEnv.dwFlags[ENV_ENABLED])
mptIns.PitchEnv.dwFlags.set(ENV_FILTER);
// hack to get === to stop notes (from modplug's xm loader)
// hack to get === to stop notes
if(!mptIns.VolEnv.dwFlags[ENV_ENABLED] && !mptIns.nFadeOut)
mptIns.nFadeOut = 8192;
mptIns.nFadeOut = 32767;
}
};
@ -284,7 +284,7 @@ static void ImportIMFEffect(ModCommand &m)
m.param |= 0xE0;
break;
case 0x16: // cutoff
m.param >>= 1;
m.param = (0xFF - m.param) / 2u;
break;
case 0x1F: // set global volume
m.param = MIN(m.param << 1, 0xFF);
@ -361,9 +361,9 @@ static bool ValidateHeader(const IMFFileHeader &fileHeader)
return false;
}
bool channelFound = false;
for(uint8 chn = 0; chn < 32; chn++)
for(const auto &chn : fileHeader.channels)
{
switch(fileHeader.channels[chn].status)
switch(chn.status)
{
case 0: // enabled; don't worry about it
channelFound = true;

View File

@ -1039,7 +1039,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
}
// Now we grab the data for this particular row/channel.
ModCommand dummy;
ModCommand dummy = ModCommand::Empty();
ModCommand &m = ch < m_nChannels ? patData[ch] : dummy;
if(chnMask[ch] & 0x10)
@ -1226,7 +1226,10 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
m_madeWithTracker = MPT_USTRING("BeRoTracker");
break;
case 7:
m_madeWithTracker = mpt::format(MPT_USTRING("ITMCK %1.%2.%3"))((fileHeader.cwtv >> 8) & 0x0F, (fileHeader.cwtv >> 4) & 0x0F, fileHeader.cwtv & 0x0F);
if(fileHeader.cwtv == 0x7FFF && fileHeader.cmwt == 0x0215)
m_madeWithTracker = MPT_USTRING("munch.py");
else
m_madeWithTracker = mpt::format(MPT_USTRING("ITMCK %1.%2.%3"))((fileHeader.cwtv >> 8) & 0x0F, (fileHeader.cwtv >> 4) & 0x0F, fileHeader.cwtv & 0x0F);
break;
case 0xD:
m_madeWithTracker = MPT_USTRING("spc2it");

View File

@ -833,12 +833,12 @@ bool CSoundFile::ReadMed(FileReader &file, ModLoadingFlags loadFlags)
tracks = pmb->numtracks;
if (!tracks) tracks = m_nChannels;
if(!Patterns.Insert(iBlk, lines)) continue;
auto p = Patterns[iBlk].begin();
const uint8 * s = (const uint8 *)(lpStream + dwPos + 2);
uint32 maxlen = tracks*lines*3;
if (maxlen + dwPos > dwMemLength - 2) break;
for (uint32 y=0; y<lines; y++)
{
ModCommand *p = Patterns[iBlk].GetpModCommand(y, 0);
for (uint32 x=0; x<tracks; x++, s+=3) if (x < m_nChannels)
{
uint8 note = s[0] & 0x3F;
@ -901,18 +901,21 @@ bool CSoundFile::ReadMed(FileReader &file, ModLoadingFlags loadFlags)
const uint8 * s = (const uint8 *)(lpStream + dwPos + 8);
uint32 maxlen = tracks*lines*4;
if (maxlen + dwPos > dwMemLength - 8 || !Patterns.IsValidPat(iBlk)) break;
auto p = Patterns[iBlk].begin();
for (uint32 y=0; y<lines; y++)
{
ModCommand *p = Patterns[iBlk].GetpModCommand(y, 0);
for (uint32 x=0; x<tracks; x++, s+=4) if (x < m_nChannels)
{
uint8 note = s[0];
if ((note) && (note <= 132))
if(note & 0x7F)
{
int rnote = note + playtransp;
if (rnote < 1) rnote = 1;
if (rnote > NOTE_MAX) rnote = NOTE_MAX;
p->note = (uint8)rnote;
} else if(note == 0x80)
{
p->note = NOTE_NOTECUT;
}
p->instr = s[1];
p->command = s[2];

View File

@ -1083,7 +1083,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
chn = 9;
else if(chn < 10)
chn--;
drumChns.set(chn, xg[6] != 0);
drumChns.set(chn, xg[7] != 0);
}
}
}

View File

@ -2133,7 +2133,7 @@ bool CSoundFile::ReadPT36(FileReader &file, ModLoadingFlags loadFlags)
&& IsInRange(info.dateMinute, 0, 59) && IsInRange(info.dateSecond, 0, 59))
{
FileHistory mptHistory;
MemsetZero(mptHistory.loadDate);
MemsetZero(mptHistory);
mptHistory.loadDate.tm_year = info.dateYear;
mptHistory.loadDate.tm_mon = info.dateMonth - 1;
mptHistory.loadDate.tm_mday = info.dateDay;

View File

@ -492,7 +492,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
}
CHANNELINDEX channel = (info & s3mChannelMask);
ModCommand dummy;
ModCommand dummy = ModCommand::Empty();
ModCommand &m = (channel < GetNumChannels()) ? rowBase[channel] : dummy;
if(info & s3mNotePresent)

View File

@ -82,10 +82,11 @@ static bool ValidateHeader(const STMFileHeader &fileHeader)
// After reviewing all STM files on ModLand and ModArchive, it was found that the
// case-insensitive comparison is most likely not necessary for any files in the wild.
if(fileHeader.filetype != 2
|| (fileHeader.dosEof != 0x1A && fileHeader.dosEof != 2) // ST2 ignores this, ST3 doesn't. putup10.stm / putup11.stm have dosEof = 2.
|| (fileHeader.dosEof != 0x1A && fileHeader.dosEof != 2) // ST2 ignores this, ST3 doesn't. Broken versions of putup10.stm / putup11.stm have dosEof = 2.
|| fileHeader.verMajor != 2
|| fileHeader.verMinor > 21 // ST3 only accepts 0, 10, 20 and 21
|| fileHeader.globalVolume > 64
|| (fileHeader.verMinor != 0 && fileHeader.verMinor != 10 && fileHeader.verMinor != 20 && fileHeader.verMinor != 21)
|| fileHeader.numPatterns > 64
|| (fileHeader.globalVolume > 64 && fileHeader.globalVolume != 0x58) // 0x58 may be a placeholder value in earlier ST2 versions.
|| (std::memcmp(fileHeader.trackername, "!Scream!", 8)
&& std::memcmp(fileHeader.trackername, "BMOD2STM", 8)
&& std::memcmp(fileHeader.trackername, "WUZAMOD!", 8))
@ -160,7 +161,7 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags)
m_nDefaultTempo = ConvertST2Tempo(initTempo);
m_nDefaultSpeed = initTempo >> 4;
if(fileHeader.verMinor > 10)
m_nDefaultGlobalVolume = fileHeader.globalVolume * 4u;
m_nDefaultGlobalVolume = std::min<uint8>(fileHeader.globalVolume, 64) * 4u;
// Setting up channels
for(CHANNELINDEX chn = 0; chn < 4; chn++)

View File

@ -720,8 +720,13 @@ static void C_StereoFill(mixsample_t *pBuffer, uint32 nSamples, mixsample_t &rof
{
#ifdef MPT_INTMIXER
// Equivalent to int x_r = (rofs + (rofs > 0 ? 255 : -255)) / 256;
#if MPT_COMPILER_SHIFT_SIGNED
const mixsample_t x_r = (rofs + (((-rofs) >> (sizeof(mixsample_t) * 8 - 1)) & OFSDECAYMASK)) >> OFSDECAYSHIFT;
const mixsample_t x_l = (lofs + (((-lofs) >> (sizeof(mixsample_t) * 8 - 1)) & OFSDECAYMASK)) >> OFSDECAYSHIFT;
#else
const mixsample_t x_r = mpt::rshift_signed(rofs + (mpt::rshift_signed(-rofs, sizeof(int) * 8 - 1) & OFSDECAYMASK), OFSDECAYSHIFT);
const mixsample_t x_l = mpt::rshift_signed(lofs + (mpt::rshift_signed(-lofs, sizeof(int) * 8 - 1) & OFSDECAYMASK), OFSDECAYSHIFT);
#endif
#else
const mixsample_t x_r = rofs * (1.0f / (1 << OFSDECAYSHIFT));
const mixsample_t x_l = lofs * (1.0f / (1 << OFSDECAYSHIFT));
@ -803,8 +808,13 @@ static void C_EndChannelOfs(ModChannel &chn, mixsample_t *pBuffer, uint32 nSampl
for (uint32 i=0; i<nSamples; i++)
{
#ifdef MPT_INTMIXER
#if MPT_COMPILER_SHIFT_SIGNED
const mixsample_t x_r = (rofs + (((-rofs) >> (sizeof(mixsample_t) * 8 - 1)) & OFSDECAYMASK)) >> OFSDECAYSHIFT;
const mixsample_t x_l = (lofs + (((-lofs) >> (sizeof(mixsample_t) * 8 - 1)) & OFSDECAYMASK)) >> OFSDECAYSHIFT;
#else
const mixsample_t x_r = mpt::rshift_signed(rofs + (mpt::rshift_signed(-rofs, sizeof(int) * 8 - 1) & OFSDECAYMASK), OFSDECAYSHIFT);
const mixsample_t x_l = mpt::rshift_signed(lofs + (mpt::rshift_signed(-lofs, sizeof(int) * 8 - 1) & OFSDECAYMASK), OFSDECAYSHIFT);
#endif
#else
const mixsample_t x_r = rofs * (1.0f / (1 << OFSDECAYSHIFT));
const mixsample_t x_l = lofs * (1.0f / (1 << OFSDECAYSHIFT));

View File

@ -58,6 +58,11 @@ void InstrumentEnvelope::Convert(MODTYPE fromType, MODTYPE toType)
}
}
}
if(toType != MOD_TYPE_MPT)
{
nReleaseNode = ENV_RELEASE_NODE_UNSET;
}
}

View File

@ -1243,6 +1243,8 @@ struct SFZEnvelope
env.nSustainStart = env.nSustainEnd = static_cast<uint8>(env.size() - 1);
if(sustainLevel != 0)
{
if(envType == ENV_VOLUME && env.nSustainEnd > 0)
env.nReleaseNode = env.nSustainEnd;
env.push_back(env.back().tick + ToTicks(release, tickDuration), ToValue(0, envType));
env.dwFlags.set(ENV_SUSTAIN);
}
@ -1269,7 +1271,7 @@ struct SFZRegion
kAlternate,
};
std::string filename;
std::string filename, name;
SFZEnvelope ampEnv, pitchEnv, filterEnv;
SmpLength loopStart = 0, loopEnd = 0;
SmpLength end = MAX_SAMPLE_LENGTH, offset = 0;
@ -1355,6 +1357,8 @@ struct SFZRegion
{
if(key == "sample")
filename = control.defaultPath + value;
else if(key == "region_label")
name = value;
else if(key == "lokey")
keyLo = ReadKey(value, control);
else if(key == "hikey")
@ -1446,7 +1450,7 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
{
file.Rewind();
enum { kNone, kGlobal, kMaster, kGroup, kRegion, kControl, kUnknown } section = kNone;
enum { kNone, kGlobal, kMaster, kGroup, kRegion, kControl, kCurve, kUnknown } section = kNone;
SFZControl control;
SFZRegion group, master, globals;
std::vector<SFZRegion> regions;
@ -1524,6 +1528,9 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
} else if(sec == "control")
{
section = kControl;
} else if(sec == "curve")
{
section = kCurve;
}
charsRead++;
} else if(s.substr(0, 8) == "#define " || s.substr(0, 8) == "#define\t")
@ -1560,7 +1567,7 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
auto keyEnd = s.find_first_of(" \t=");
auto valueStart = s.find_first_not_of(" \t=", keyEnd);
std::string key = mpt::ToLowerCaseAscii(s.substr(0, keyEnd));
if(key == "sample" || key == "default_path" || key.substr(0, 8) == "label_cc")
if(key == "sample" || key == "default_path" || key.substr(0, 8) == "label_cc" || key.substr(0, 12) == "region_label")
{
// Sample / CC name may contain spaces...
charsRead = s.find_first_of("=\t<", valueStart);
@ -1656,6 +1663,10 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
prevSmp--;
continue;
}
if(!region.name.empty())
{
mpt::String::Copy(m_szNames[smp], region.name);
}
if(!m_szNames[smp][0])
{
mpt::String::Copy(m_szNames[smp], filename.GetFileName().ToLocale());

View File

@ -40,10 +40,20 @@ uint8 CSoundFile::FrequencyToCutOff(double frequency) const
uint32 CSoundFile::CutOffToFrequency(uint32 nCutOff, int flt_modifier) const
{
MPT_ASSERT(nCutOff < 128);
float Fc = 110.0f * std::pow(2.0f, 0.25f + ((float)(nCutOff * (flt_modifier + 256))) / (m_SongFlags[SONG_EXFILTERRANGE] ? 20.0f * 512.0f : 24.0f * 512.0f));
float computedCutoff = static_cast<float>(nCutOff * (flt_modifier + 256)); // 0...127*512
float Fc;
if(GetType() != MOD_TYPE_IMF)
{
Fc = 110.0f * std::pow(2.0f, 0.25f + computedCutoff / (m_SongFlags[SONG_EXFILTERRANGE] ? 20.0f * 512.0f : 24.0f * 512.0f));
} else
{
// EMU8000: Documentation says the cutoff is in quarter semitones, with 0x00 being 125 Hz and 0xFF being 8 kHz
// The first half of the sentence contradicts the second, though.
Fc = 125.0f * std::pow(2.0f, computedCutoff * 6.0f / (127.0f * 512.0f));
}
int freq = Util::Round<int>(Fc);
Limit(freq, 120, 20000);
if (freq * 2 > (int)m_MixerSettings.gdwMixingFreq) freq = m_MixerSettings.gdwMixingFreq / 2;
if(freq * 2 > (int)m_MixerSettings.gdwMixingFreq) freq = m_MixerSettings.gdwMixingFreq / 2;
return static_cast<uint32>(freq);
}

View File

@ -57,21 +57,12 @@ public:
std::unique_ptr<CSoundFile::PlayState> state;
struct ChnSettings
{
double patLoop;
CSoundFile::samplecount_t patLoopSmp;
ROWINDEX patLoopStart;
uint32 ticksToRender; // When using sample sync, we still need to render this many ticks
bool incChanged; // When using sample sync, note frequency has changed
uint8 vol;
ChnSettings()
: patLoop(0.0)
, patLoopSmp(0)
, patLoopStart(0)
, ticksToRender(0)
, incChanged(false)
, vol(0xFF)
{ }
double patLoop = 0.0;
CSoundFile::samplecount_t patLoopSmp = 0;
ROWINDEX patLoopStart = 0;
uint32 ticksToRender = 0; // When using sample sync, we still need to render this many ticks
bool incChanged = false; // When using sample sync, note frequency has changed
uint8 vol = 0xFF;
};
#ifndef NO_PLUGINS
@ -329,9 +320,9 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
bool patternBreakOnThisRow = false;
bool patternLoopEndedOnThisRow = false, patternLoopStartedOnThisRow = false;
if(playState.m_nPattern == orderList.GetIgnoreIndex() && target.mode == GetLengthTarget::SeekPosition && playState.m_nCurrentOrder == target.pos.order)
if(!Patterns.IsValidPat(playState.m_nPattern) && playState.m_nPattern != orderList.GetInvalidPatIndex() && target.mode == GetLengthTarget::SeekPosition && playState.m_nCurrentOrder == target.pos.order)
{
// Early test: Target is inside +++ pattern
// Early test: Target is inside +++ or non-existing pattern
retval.targetReached = true;
break;
}
@ -494,7 +485,7 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
{
// Fine Pattern Delay
tickDelay += (p->param & 0x0F);
} if((p->param & 0xF0) == 0xE0 && !rowDelay)
} else if((p->param & 0xF0) == 0xE0 && !rowDelay)
{
// Pattern Delay
if(!(GetType() & MOD_TYPE_S3M) || (p->param & 0x0F) != 0)
@ -674,12 +665,18 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
patternLoopStartedOnThisRow = true;
}
break;
case 0xF0:
// Active macro
pChn->nActiveMacro = param & 0x0F;
break;
}
break;
case CMD_MODCMDEX:
if ((param & 0xF0) == 0x60)
switch(param & 0xF0)
{
case 0x60:
// Pattern Loop
if (param & 0x0F)
{
@ -692,6 +689,12 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
memory.chnSettings[nChn].patLoopSmp = playState.m_lTotalSampleCount;
memory.chnSettings[nChn].patLoopStart = playState.m_nRow;
}
break;
case 0xF0:
// Active macro
pChn->nActiveMacro = param & 0x0F;
break;
}
break;
@ -1158,6 +1161,12 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
if(memory.chnSettings[nChn].patLoop == i.first)
{
playState.m_lTotalSampleCount += (playState.m_lTotalSampleCount - memory.chnSettings[nChn].patLoopSmp) * (i.second - 1);
if(m_playBehaviour[kITPatternLoopTargetReset] || (GetType() == MOD_TYPE_S3M))
{
memory.chnSettings[nChn].patLoop = memory.elapsedTime;
memory.chnSettings[nChn].patLoopSmp = playState.m_lTotalSampleCount;
memory.chnSettings[nChn].patLoopStart = playState.m_nRow + 1;
}
break;
}
}
@ -1165,7 +1174,8 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
if(GetType() == MOD_TYPE_IT)
{
// IT pattern loop start row update - at the end of a pattern loop, set pattern loop start to next row (for upcoming pattern loops with missing SB0)
for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++)
pChn = playState.Chn;
for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++, pChn++)
{
if((pChn->rowCommand.command == CMD_S3MCMDEX && pChn->rowCommand.param >= 0xB1 && pChn->rowCommand.param <= 0xBF))
{
@ -1327,7 +1337,7 @@ void CSoundFile::InstrumentChange(ModChannel *pChn, uint32 instr, bool bPorta, b
{
// IT compatibility: No sample change (also within multi-sample instruments) during portamento when using Compatible Gxx.
// Test case: PortaInsNumCompat.it, PortaSampleCompat.it, PortaCutCompat.it
if(m_playBehaviour[kITPortamentoInstrument] && m_SongFlags[SONG_ITCOMPATGXX] && pChn->nLength != 0)
if(m_playBehaviour[kITPortamentoInstrument] && m_SongFlags[SONG_ITCOMPATGXX] && !pChn->increment.IsZero())
{
pSmp = pChn->pModSample;
}
@ -2082,13 +2092,11 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo
if (srcChn.dwFlags[CHN_MUTE])
return CHANNELINDEX_INVALID;
bool applyDNAtoPlug; //rewbs.VSTiNNA
for(CHANNELINDEX i = nChn; i < MAX_CHANNELS; i++)
if(i >= m_nChannels || i == nChn)
{
ModChannel &chn = m_PlayState.Chn[i];
applyDNAtoPlug = false; //rewbs.VSTiNNA
bool applyDNAtoPlug = false;
if((chn.nMasterChn == nChn + 1 || i == nChn) && chn.pModInstrument != nullptr)
{
bool bOk = false;
@ -2098,7 +2106,7 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo
// Note
case DCT_NOTE:
if(note && chn.nNote == note && pIns == chn.pModInstrument) bOk = true;
if(pIns && pIns->nMixPlug) applyDNAtoPlug = true; //rewbs.VSTiNNA
if(pIns && pIns->nMixPlug) applyDNAtoPlug = true;
break;
// Sample
case DCT_SAMPLE:
@ -2107,7 +2115,6 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo
// Instrument
case DCT_INSTRUMENT:
if(pIns == chn.pModInstrument) bOk = true;
//rewbs.VSTiNNA
if(pIns && pIns->nMixPlug) applyDNAtoPlug = true;
break;
// Plugin
@ -2117,7 +2124,6 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo
applyDNAtoPlug = true;
bOk = true;
}
//end rewbs.VSTiNNA
break;
}
@ -2178,7 +2184,7 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo
pPlugin = m_MixPlugins[nPlugin-1].pMixPlugin;
if(pPlugin)
{
// apply NNA to this plugin iff it is currently playing a note on this tracking channel
// apply NNA to this plugin iff it is currently playing a note on this tracker channel
// (and if it is playing a note, we know that would be the last note played on this chan).
applyNNAtoPlug = pPlugin->IsNotePlaying(srcChn.GetPluginNote(m_playBehaviour[kITRealNoteMapping]), GetBestMidiChannel(nChn), nChn);
}
@ -2187,8 +2193,7 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo
#endif // NO_PLUGINS
// New Note Action
//if ((pChn.nVolume) && (pChn.nLength))
if((srcChn.nVolume != 0 && srcChn.nLength != 0) || applyNNAtoPlug) //rewbs.VSTiNNA
if((srcChn.nRealVolume > 0 && srcChn.nLength > 0) || applyNNAtoPlug)
{
nnaChn = GetNNAChannel(nChn);
if(nnaChn != 0)
@ -4876,7 +4881,7 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned
return 0;
}
ModChannel *pChn = &m_PlayState.Chn[nChn];
ModChannel &chn = m_PlayState.Chn[nChn];
if(macro[0] == 0xF0 && (macro[1] == 0xF0 || macro[1] == 0xF1))
{
@ -4889,60 +4894,44 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned
const uint8 macroCode = macro[2];
const uint8 param = macro[3];
if(macroCode == 0x00 && !isExtended)
if(macroCode == 0x00 && !isExtended && param < 0x80)
{
// F0.F0.00.xx: Set CutOff
int oldcutoff = pChn->nCutOff;
if(param < 0x80)
if(!isSmooth)
{
if(!isSmooth)
{
pChn->nCutOff = param;
} else
{
pChn->nCutOff = (uint8)CalculateSmoothParamChange((float)pChn->nCutOff, (float)param);
}
pChn->nRestoreCutoffOnNewNote = 0;
chn.nCutOff = param;
} else
{
chn.nCutOff = Util::Round<uint8>(CalculateSmoothParamChange(chn.nCutOff, param));
}
oldcutoff -= pChn->nCutOff;
if(oldcutoff < 0) oldcutoff = -oldcutoff;
if((pChn->nVolume > 0) || (oldcutoff < 0x10)
|| !pChn->dwFlags[CHN_FILTER] || (!(pChn->rightVol | pChn->leftVol)))
SetupChannelFilter(pChn, !pChn->dwFlags[CHN_FILTER]);
chn.nRestoreCutoffOnNewNote = 0;
SetupChannelFilter(&chn, !chn.dwFlags[CHN_FILTER]);
return 4;
} else if(macroCode == 0x01 && !isExtended)
} else if(macroCode == 0x01 && !isExtended && param < 0x80)
{
// F0.F0.01.xx: Set Resonance
if(param < 0x80)
if(!isSmooth)
{
pChn->nRestoreResonanceOnNewNote = 0;
if(!isSmooth)
{
pChn->nResonance = param;
} else
{
pChn->nResonance = (uint8)CalculateSmoothParamChange((float)pChn->nResonance, (float)param);
}
chn.nResonance = param;
} else
{
chn.nResonance = (uint8)CalculateSmoothParamChange((float)chn.nResonance, (float)param);
}
SetupChannelFilter(pChn, !pChn->dwFlags[CHN_FILTER]);
chn.nRestoreResonanceOnNewNote = 0;
SetupChannelFilter(&chn, !chn.dwFlags[CHN_FILTER]);
return 4;
} else if(macroCode == 0x02 && !isExtended)
{
// F0.F0.02.xx: Set filter mode (high nibble determines filter mode)
if(param < 0x20)
{
pChn->nFilterMode = (param >> 4);
SetupChannelFilter(pChn, !pChn->dwFlags[CHN_FILTER]);
chn.nFilterMode = (param >> 4);
SetupChannelFilter(&chn, !chn.dwFlags[CHN_FILTER]);
}
return 4;
#ifndef NO_PLUGINS
} else if(macroCode == 0x03 && !isExtended)
{
@ -4961,7 +4950,6 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned
}
return 4;
} else if((macroCode & 0x80) || isExtended)
{
// F0.F0.{80|n}.xx / F0.F1.n.xx: Set VST effect parameter n to xx
@ -4985,7 +4973,6 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned
return 4;
#endif // NO_PLUGINS
}
// If we reach this point, the internal macro was invalid.
@ -4994,11 +4981,11 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned
{
#ifndef NO_PLUGINS
// Not an internal device. Pass on to appropriate plugin.
const CHANNELINDEX plugChannel = (nChn < GetNumChannels()) ? nChn + 1 : pChn->nMasterChn;
const CHANNELINDEX plugChannel = (nChn < GetNumChannels()) ? nChn + 1 : chn.nMasterChn;
if(plugChannel > 0 && plugChannel <= GetNumChannels()) // XXX do we need this? I guess it might be relevant for previewing notes in the pattern... Or when using this mechanism for volume/panning!
{
PLUGINDEX nPlug = 0;
if(!pChn->dwFlags[CHN_NOFX])
if(!chn.dwFlags[CHN_NOFX])
{
nPlug = (plugin != 0) ? plugin : GetBestPlugin(nChn, PrioritiseChannel, EvenIfMuted);
}

View File

@ -320,7 +320,7 @@ CSoundFile::ProbeResult CSoundFile::Probe(ProbeFlags flags, mpt::span<const mpt:
if((result == ProbeWantMoreData) && (data.size() >= ProbeRecommendedSize))
{
// If the prober wants more daat but we already provided the recommended required maximum,
// just return success as this is th ebest we can do for the suggestesd probing size.
// just return success as this is the best we can do for the suggestesd probing size.
result = ProbeSuccess;
}
}
@ -654,7 +654,7 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags)
if (Reporting::Confirm(mpt::ToWide(mpt::CharsetUTF8, notFoundText.c_str()), L"OpenMPT - Plugins missing", false, true) == cnfYes)
{
std::string url = "https://resources.openmpt.org/plugins/search.php?p=";
for(auto &id : notFoundIDs)
for(const auto &id : notFoundIDs)
{
url += mpt::fmt::HEX0<8>(id->dwPluginId2.get());
url += id->szLibraryName;
@ -810,8 +810,11 @@ void CSoundFile::ResetPlayPos()
void CSoundFile::SetCurrentOrder(ORDERINDEX nOrder)
{
while ((nOrder < Order().size()) && (Order()[nOrder] == Order.GetIgnoreIndex())) nOrder++;
if ((nOrder >= Order().size()) || (Order()[nOrder] >= Patterns.Size())) return;
while(nOrder < Order().size() && !Order().IsValidPat(nOrder))
nOrder++;
if(nOrder >= Order().size())
return;
for(auto &chn : m_PlayState.Chn)
{
chn.nPeriod = 0;

View File

@ -1452,12 +1452,14 @@ void CSoundFile::ProcessArpeggio(CHANNELINDEX nChn, int &period, Tuning::NOTEIND
// TODO other likely formats for MOD case: MED, OKT, etc
uint8 note = (GetType() != MOD_TYPE_MOD) ? pChn->nNote : static_cast<uint8>(GetNoteFromPeriod(period, pChn->nFineTune, pChn->nC5Speed));
if(GetType() & (MOD_TYPE_DBM | MOD_TYPE_DIGI))
tick += 2;
switch(tick % 3)
{
case 1: note += (pChn->nArpeggio >> 4); break;
case 2: note += (pChn->nArpeggio & 0x0F); break;
}
if(note != pChn->nNote || GetType() == MOD_TYPE_STM || m_playBehaviour[KST3PortaAfterArpeggio])
if(note != pChn->nNote || (GetType() & (MOD_TYPE_DBM | MOD_TYPE_DIGI | MOD_TYPE_STM)) || m_playBehaviour[KST3PortaAfterArpeggio])
{
if(m_SongFlags[SONG_PT_MODE])
{
@ -1474,7 +1476,7 @@ void CSoundFile::ProcessArpeggio(CHANNELINDEX nChn, int &period, Tuning::NOTEIND
}
period = GetPeriodFromNote(note, pChn->nFineTune, pChn->nC5Speed);
if(GetType() & (MOD_TYPE_STM | MOD_TYPE_PSM))
if(GetType() & (MOD_TYPE_DBM | MOD_TYPE_DIGI | MOD_TYPE_PSM | MOD_TYPE_STM))
{
// The arpeggio note offset remains effective after the end of the current row in ScreamTracker 2.
// This fixes the flute lead in MORPH.STM by Skaven, pattern 27.

View File

@ -651,7 +651,7 @@ bool IMixPlugin::SaveProgram()
defaultDir = m_Factory.dllPath.GetPath();
}
CString progName = GetCurrentProgramName();
CString progName = m_Factory.libraryName.ToCString() + _T(" - ") + GetCurrentProgramName();
SanitizeFilename(progName);
FileDialog dlg = SaveFileDialog()
@ -928,7 +928,7 @@ void IMidiPlugin::MidiCommand(uint8 nMidiCh, uint8 nMidiProg, uint16 wMidiBank,
// Problem: if a note dies out naturally and we never send a note off, this counter
// will block at max until note off. Is this a problem?
// Safe to assume we won't need more than 16 note offs max on a given note?
if(channel.noteOnMap[note][trackChannel] < 17)
if(channel.noteOnMap[note][trackChannel] < uint8_max)
channel.noteOnMap[note][trackChannel]++;
MidiSend(MIDIEvents::NoteOn(nMidiCh, static_cast<uint8>(note), volume));