Updated libopenmpt to version 0.5.12

CQTexperiment
Christopher Snowhill 2021-10-05 19:46:06 -07:00
parent ee6faec757
commit 3aa2e3149f
40 changed files with 461 additions and 298 deletions

View File

@ -1,4 +1,4 @@
MPT_SVNVERSION=15412
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.10
MPT_SVNDATE=2021-07-04T16:30:04.479627Z
MPT_SVNVERSION=15759
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.12
MPT_SVNDATE=2021-10-04T13:48:02.912129Z

View File

@ -1,10 +1,10 @@
#pragma once
#define OPENMPT_VERSION_SVNVERSION "15412"
#define OPENMPT_VERSION_REVISION 15412
#define OPENMPT_VERSION_SVNVERSION "15759"
#define OPENMPT_VERSION_REVISION 15759
#define OPENMPT_VERSION_DIRTY 0
#define OPENMPT_VERSION_MIXEDREVISIONS 0
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.10"
#define OPENMPT_VERSION_DATE "2021-07-04T16:30:04.479627Z"
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.12"
#define OPENMPT_VERSION_DATE "2021-10-04T13:48:02.912129Z"
#define OPENMPT_VERSION_IS_PACKAGE 1

View File

@ -17,7 +17,7 @@ OPENMPT_NAMESPACE_BEGIN
// Version definitions. The only thing that needs to be changed when changing version number.
#define VER_MAJORMAJOR 1
#define VER_MAJOR 29
#define VER_MINOR 11
#define VER_MINOR 13
#define VER_MINORMINOR 00
OPENMPT_NAMESPACE_END

View File

@ -10,4 +10,4 @@ mkdir $FUZZING_TEMPDIR/bin
cp -d ../../bin/* $FUZZING_TEMPDIR/bin/
#export AFL_PRELOAD=$AFL_DIR/libdislocator.so
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p exploit -f $FUZZING_TEMPDIR/infile01 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -M fuzzer01 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile01
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p exploit -f $FUZZING_TEMPDIR/infile01 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -D -M fuzzer01 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile01

View File

@ -1,6 +1,6 @@
This folder contains the stb_vorbis library from
https://github.com/nothings/stb/blob/master/stb_vorbis.c v1.21
commit 8e51be04dc7dcee462e1f09e410faceab52cc6d2 (2021-07-02)
https://github.com/nothings/stb/blob/master/stb_vorbis.c v1.22
commit 5a0bb8b1c1b1ca3f4e2485f4114c1c8ea021b781 (2021-07-12)
Modifications:
* Use of alloca has been replaced with malloc, as alloca is not in C99 and

View File

@ -1,4 +1,4 @@
// Ogg Vorbis audio decoder - v1.21 - public domain
// Ogg Vorbis audio decoder - v1.22 - public domain
// http://nothings.org/stb_vorbis/
//
// Original version written by Sean Barrett in 2007.
@ -29,13 +29,14 @@
// Bernhard Wodo Evan Balster github:alxprd
// Tom Beaumont Ingo Leitgeb Nicolas Guillemot
// Phillip Bennefall Rohit Thiago Goulart
// github:manxorist saga musix github:infatum
// github:manxorist Saga Musix github:infatum
// Timur Gagiev Maxwell Koo Peter Waller
// github:audinowho Dougall Johnson David Reid
// github:Clownacy Pedro J. Estebanez Remi Verschelde
// AnthoFoxo
// AnthoFoxo github:morlat Gabriel Ravier
//
// Partial history:
// 1.22 - 2021-07-11 - various small fixes
// 1.21 - 2021-07-02 - fix bug for files with no comments
// 1.20 - 2020-07-11 - several small fixes
// 1.19 - 2020-02-05 - warnings
@ -222,6 +223,12 @@ extern int stb_vorbis_decode_frame_pushdata(
// channel. In other words, (*output)[0][0] contains the first sample from
// the first channel, and (*output)[1][0] contains the first sample from
// the second channel.
//
// *output points into stb_vorbis's internal output buffer storage; these
// buffers are owned by stb_vorbis and application code should not free
// them or modify their contents. They are transient and will be overwritten
// once you ask for more data to get decoded, so be sure to grab any data
// you need before then.
extern void stb_vorbis_flush_pushdata(stb_vorbis *f);
// inform stb_vorbis that your next datablock will not be contiguous with
@ -583,7 +590,7 @@ enum STBVorbisError
#if defined(_MSC_VER) || defined(__MINGW32__)
#include <malloc.h>
#endif
#if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) || defined(__NEWLIB__)
#if defined(__linux__) || defined(__linux) || defined(__sun__) || defined(__EMSCRIPTEN__) || defined(__NEWLIB__)
#include <alloca.h>
#endif
#else // STB_VORBIS_NO_CRT
@ -652,6 +659,12 @@ typedef signed int int32;
typedef float codetype;
#ifdef _MSC_VER
#define STBV_NOTUSED(v) (void)(v)
#else
#define STBV_NOTUSED(v) (void)sizeof(v)
#endif
// @NOTE
//
// Some arrays below are tagged "//varies", which means it's actually
@ -1057,7 +1070,7 @@ static float float32_unpack(uint32 x)
uint32 sign = x & 0x80000000;
uint32 exp = (x & 0x7fe00000) >> 21;
double res = sign ? -(double)mantissa : (double)mantissa;
return (float) ldexp((float)res, exp-788);
return (float) ldexp((float)res, (int)exp-788);
}
@ -1088,6 +1101,7 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values)
// find the first entry
for (k=0; k < n; ++k) if (len[k] < NO_CODE) break;
if (k == n) { assert(c->sorted_entries == 0); return TRUE; }
assert(len[k] < 32); // no error return required, code reading lens checks this
// add to the list
add_entry(c, 0, k, m++, len[k], values);
// add all available leaves
@ -1101,6 +1115,7 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values)
uint32 res;
int z = len[i], y;
if (z == NO_CODE) continue;
assert(z < 32); // no error return required, code reading lens checks this
// find lowest available leaf (should always be earliest,
// which is what the specification calls for)
// note that this property, and the fact we can never have
@ -1110,12 +1125,10 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values)
while (z > 0 && !available[z]) --z;
if (z == 0) { return FALSE; }
res = available[z];
assert(z >= 0 && z < 32);
available[z] = 0;
add_entry(c, bit_reverse(res), i, m++, len[i], values);
// propagate availability up the tree
if (z != len[i]) {
assert(len[i] >= 0 && len[i] < 32);
for (y=len[i]; y > z; --y) {
assert(available[y] == 0);
available[y] = res + (1 << (32-y));
@ -2588,34 +2601,33 @@ static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A,
while (z > base) {
float k00,k11;
float l00,l11;
k00 = z[-0] - z[-8];
k11 = z[-1] - z[-9];
z[-0] = z[-0] + z[-8];
z[-1] = z[-1] + z[-9];
z[-8] = k00;
z[-9] = k11 ;
k00 = z[-0] - z[ -8];
k11 = z[-1] - z[ -9];
l00 = z[-2] - z[-10];
l11 = z[-3] - z[-11];
z[ -0] = z[-0] + z[ -8];
z[ -1] = z[-1] + z[ -9];
z[ -2] = z[-2] + z[-10];
z[ -3] = z[-3] + z[-11];
z[ -8] = k00;
z[ -9] = k11;
z[-10] = (l00+l11) * A2;
z[-11] = (l11-l00) * A2;
k00 = z[ -2] - z[-10];
k11 = z[ -3] - z[-11];
z[ -2] = z[ -2] + z[-10];
z[ -3] = z[ -3] + z[-11];
z[-10] = (k00+k11) * A2;
z[-11] = (k11-k00) * A2;
k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation
k00 = z[ -4] - z[-12];
k11 = z[ -5] - z[-13];
l00 = z[ -6] - z[-14];
l11 = z[ -7] - z[-15];
z[ -4] = z[ -4] + z[-12];
z[ -5] = z[ -5] + z[-13];
z[-12] = k11;
z[-13] = k00;
k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation
k11 = z[ -7] - z[-15];
z[ -6] = z[ -6] + z[-14];
z[ -7] = z[ -7] + z[-15];
z[-14] = (k00+k11) * A2;
z[-15] = (k00-k11) * A2;
z[-12] = k11;
z[-13] = -k00;
z[-14] = (l11-l00) * A2;
z[-15] = (l00+l11) * -A2;
iter_54(z);
iter_54(z-8);
@ -3080,6 +3092,7 @@ static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *f
for (q=1; q < g->values; ++q) {
j = g->sorted_order[q];
#ifndef STB_VORBIS_NO_DEFER_FLOOR
STBV_NOTUSED(step2_flag);
if (finalY[j] >= 0)
#else
if (step2_flag[j])
@ -3182,6 +3195,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
// WINDOWING
STBV_NOTUSED(left_end);
n = f->blocksize[m->blockflag];
map = &f->mapping[m->mapping];
@ -3379,7 +3393,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
// this isn't to spec, but spec would require us to read ahead
// and decode the size of all current frames--could be done,
// but presumably it's not a commonly used feature
f->current_loc = -n2; // start of first frame is positioned for discard
f->current_loc = 0u - n2; // start of first frame is positioned for discard (NB this is an intentional unsigned overflow/wrap-around)
// we might have to discard samples "from" the next frame too,
// if we're lapping a large block then a small at the start?
f->discard_samples_deferred = n - right_end;
@ -3880,8 +3894,7 @@ static int start_decoder(vorb *f)
unsigned int div=1;
for (k=0; k < c->dimensions; ++k) {
int off = (z / div) % c->lookup_values;
float val = mults[off];
val = mults[off]*c->delta_value + c->minimum_value + last;
float val = mults[off]*c->delta_value + c->minimum_value + last;
c->multiplicands[j*c->dimensions + k] = val;
if (c->sequence_p)
last = val;
@ -3964,7 +3977,7 @@ static int start_decoder(vorb *f)
if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup);
}
for (k=0; k < 1 << g->class_subclasses[j]; ++k) {
g->subclass_books[j][k] = get_bits(f,8)-1;
g->subclass_books[j][k] = (int16)get_bits(f,8)-1;
if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup);
}
}
@ -4522,6 +4535,7 @@ stb_vorbis *stb_vorbis_open_pushdata(
*error = VORBIS_need_more_data;
else
*error = p.error;
vorbis_deinit(&p);
return NULL;
}
f = vorbis_alloc(&p);
@ -4579,7 +4593,7 @@ static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last)
header[i] = get8(f);
if (f->eof) return 0;
if (header[4] != 0) goto invalid;
goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24);
goal = header[22] + (header[23] << 8) + (header[24]<<16) + ((uint32)header[25]<<24);
for (i=22; i < 26; ++i)
header[i] = 0;
crc = 0;
@ -4983,7 +4997,7 @@ unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f)
// set. whoops!
break;
}
previous_safe = last_page_loc+1;
//previous_safe = last_page_loc+1; // NOTE: not used after this point, but note for debugging
last_page_loc = stb_vorbis_get_file_offset(f);
}
@ -5094,7 +5108,10 @@ stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const st
stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc)
{
stb_vorbis *f, p;
if (data == NULL) return NULL;
if (!data) {
if (error) *error = VORBIS_unexpected_eof;
return NULL;
}
vorbis_init(&p, alloc);
p.stream = (uint8 *) data;
p.stream_end = (uint8 *) data + len;
@ -5169,11 +5186,11 @@ static void copy_samples(short *dest, float *src, int len)
static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len)
{
#define BUFFER_SIZE 32
float buffer[BUFFER_SIZE];
int i,j,o,n = BUFFER_SIZE;
#define STB_BUFFER_SIZE 32
float buffer[STB_BUFFER_SIZE];
int i,j,o,n = STB_BUFFER_SIZE;
check_endianness();
for (o = 0; o < len; o += BUFFER_SIZE) {
for (o = 0; o < len; o += STB_BUFFER_SIZE) {
memset(buffer, 0, sizeof(buffer));
if (o + n > len) n = len - o;
for (j=0; j < num_c; ++j) {
@ -5190,16 +5207,17 @@ static void compute_samples(int mask, short *output, int num_c, float **data, in
output[o+i] = v;
}
}
#undef STB_BUFFER_SIZE
}
static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len)
{
#define BUFFER_SIZE 32
float buffer[BUFFER_SIZE];
int i,j,o,n = BUFFER_SIZE >> 1;
#define STB_BUFFER_SIZE 32
float buffer[STB_BUFFER_SIZE];
int i,j,o,n = STB_BUFFER_SIZE >> 1;
// o is the offset in the source data
check_endianness();
for (o = 0; o < len; o += BUFFER_SIZE >> 1) {
for (o = 0; o < len; o += STB_BUFFER_SIZE >> 1) {
// o2 is the offset in the output data
int o2 = o << 1;
memset(buffer, 0, sizeof(buffer));
@ -5229,6 +5247,7 @@ static void compute_stereo_samples(short *output, int num_c, float **data, int d
output[o2+i] = v;
}
}
#undef STB_BUFFER_SIZE
}
static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples)
@ -5301,8 +5320,6 @@ int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short
float **outputs;
int len = num_shorts / channels;
int n=0;
int z = f->channels;
if (z > channels) z = channels;
while (n < len) {
int k = f->channel_buffer_end - f->channel_buffer_start;
if (n+k >= len) k = len - n;
@ -5321,8 +5338,6 @@ int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, in
{
float **outputs;
int n=0;
int z = f->channels;
if (z > channels) z = channels;
while (n < len) {
int k = f->channel_buffer_end - f->channel_buffer_start;
if (n+k >= len) k = len - n;

View File

@ -5,6 +5,46 @@ Changelog {#changelog}
For fully detailed change log, please see the source repository directly. This
is just a high-level summary.
### libopenmpt 0.5.12 (2021-10-04)
* [**Sec**] Possible crash when loading malformed MDL files. (r15603)
* [**Bug**] Fixed various undefined behaviour found with ubsan.
* Seeking with sample sync sometimes didn't compute the correct sample
position with pingpong-looped samples.
* IT: Tremor command I11 erroneously behaved like I00 (use previous parameter)
unless IT Old Effects were enabled.
* PTM: Panning was translated wrong in some edge cases.
* IMF / PTM: Note Slide commands were sometimes slightly off.
* OKT: Better support for fine note slides.
* DBM: Echo enable effect parameter range checks were incorrect.
* XM: Sample texts in XMs made with MadTracker are now also decoded using
Windows-1252 encoding.
* in_openmpt: Song metadata is no longer reverted when viewing file info.
### libopenmpt 0.5.11 (2021-08-22)
* [**Sec**] Possible crash with malformed modules when trying to access
non-existent plugin slots FX251-FX255. (r15479, r15518)
* [**Sec**] Possible read beyond sample start after swapping to a sample with
loop points set but not loop enabled. (r15499)
* [**Sec**] Fixed various possible crashes with malformed MMCMP files.
(r15504, 15528)
* [**Sec**] MED: Possible read past end of sequence name (stack-allocated, so
relatively unlikely to result in a crash). (r15477)
* Fixed excessive memory usage with files claiming to have an extremely high
rows per beat count while also using tempo swing. Maximum rows per beat are
now limited to 65536.
* STP: Avoid creating thousands of patterns when loading malformed files even
though no more pattern data can be read.
* mpg123: Update to v1.28.2 (2021-07-12).
* stb_vorbis: Update v1.22 commit 5a0bb8b1c1b1ca3f4e2485f4114c1c8ea021b781
(2021-07-12).
### libopenmpt 0.5.10 (2021-07-04)
* S3M: Honor the Stereo flag not being set. This improves the sound of some

View File

@ -342,7 +342,7 @@ static int infobox( const in_char * fn, HWND hWndParent ) {
} else {
libopenmpt::plugin::gui_show_file_info( hWndParent, TEXT(SHORT_TITLE), StringReplace( self->cached_infotext, L"\n", L"\r\n" ) );
}
return 0;
return INFOBOX_UNCHANGED;
}
static void getfileinfo( const in_char * filename, in_char * title, int * length_in_ms ) {

View File

@ -19,7 +19,7 @@
/*! \brief libopenmpt minor version number */
#define OPENMPT_API_VERSION_MINOR 5
/*! \brief libopenmpt patch version number */
#define OPENMPT_API_VERSION_PATCH 10
#define OPENMPT_API_VERSION_PATCH 12
/*! \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=5
LIBOPENMPT_VERSION_PATCH=10
LIBOPENMPT_VERSION_PATCH=12
LIBOPENMPT_VERSION_PREREL=
LIBOPENMPT_LTVER_CURRENT=2
LIBOPENMPT_LTVER_REVISION=10
LIBOPENMPT_LTVER_REVISION=12
LIBOPENMPT_LTVER_AGE=2

View File

@ -504,7 +504,7 @@ static void show_info( std::ostream & log, bool verbose ) {
SDL_version sdlver;
std::memset( &sdlver, 0, sizeof( SDL_version ) );
SDL_GetVersion( &sdlver );
log << static_cast<int>( sdlver.major ) << "." << static_cast<int>( sdlver.minor ) << "." << static_cast<int>( sdlver.patch ) << "." << SDL_GetRevisionNumber();
log << static_cast<int>( sdlver.major ) << "." << static_cast<int>( sdlver.minor ) << "." << static_cast<int>( sdlver.patch );
const char * revision = SDL_GetRevision();
if ( revision ) {
log << " (" << revision << ")";

View File

@ -102,6 +102,7 @@ static bool MMCMP_IsDstBlockValid(const std::vector<char> &unpackedData, uint32
if(pos >= unpackedData.size()) return false;
if(len > unpackedData.size()) return false;
if(len > unpackedData.size() - pos) return false;
if(!len) return false;
return true;
}
@ -252,7 +253,9 @@ bool UnpackMMCMP(std::vector<ContainerItem> &containerItems, FileReader &file, C
if(!psubblk) return false;
if(!MMCMP_IsDstBlockValid(unpackedData, psubblk[subblk])) return false;
char *pDest = &(unpackedData[psubblk[subblk].unpk_pos]);
uint32 dwSize = psubblk[subblk].unpk_size;
uint32 dwSize = psubblk[subblk].unpk_size & ~1u;
if(!dwSize)
return false;
uint32 dwPos = 0;
uint32 numbits = blk.num_bits;
uint32 oldval = 0;
@ -316,7 +319,9 @@ bool UnpackMMCMP(std::vector<ContainerItem> &containerItems, FileReader &file, C
dwPos = 0;
if(!(subblk < blk.sub_blk)) break;
if(!MMCMP_IsDstBlockValid(unpackedData, psubblk[subblk])) return false;
dwSize = psubblk[subblk].unpk_size;
dwSize = psubblk[subblk].unpk_size & ~1u;
if(!dwSize)
return false;
pDest = &(unpackedData[psubblk[subblk].unpk_pos]);
}
}

View File

@ -581,44 +581,46 @@ bool CDLSBank::IsDLSBank(const mpt::PathString &filename)
const DLSINSTRUMENT *CDLSBank::FindInstrument(bool isDrum, uint32 bank, uint32 program, uint32 key, uint32 *pInsNo) const
{
if (m_Instruments.empty()) return nullptr;
for (uint32 iIns=0; iIns<m_Instruments.size(); iIns++)
if(m_Instruments.empty())
return nullptr;
for (uint32 iIns = 0; iIns < m_Instruments.size(); iIns++)
{
const DLSINSTRUMENT &dlsIns = m_Instruments[iIns];
uint32 insbank = ((dlsIns.ulBank & 0x7F00) >> 1) | (dlsIns.ulBank & 0x7F);
if ((bank >= 0x4000) || (insbank == bank))
if((bank >= 0x4000) || (insbank == bank))
{
if (isDrum)
if(isDrum && (dlsIns.ulBank & F_INSTRUMENT_DRUMS))
{
if (dlsIns.ulBank & F_INSTRUMENT_DRUMS)
if((program >= 0x80) || (program == (dlsIns.ulInstrument & 0x7F)))
{
if ((program >= 0x80) || (program == (dlsIns.ulInstrument & 0x7F)))
for(const auto &region : dlsIns.Regions)
{
for (uint32 iRgn=0; iRgn<dlsIns.nRegions; iRgn++)
if(region.nWaveLink == Util::MaxValueOfType(region.nWaveLink))
continue;
if(region.fuOptions & DLSREGION_ISGLOBAL)
continue;
if((!key || key >= 0x80)
|| (key >= region.uKeyMin && key <= region.uKeyMax))
{
if ((!key) || (key >= 0x80)
|| ((key >= dlsIns.Regions[iRgn].uKeyMin)
&& (key <= dlsIns.Regions[iRgn].uKeyMax)))
{
if (pInsNo) *pInsNo = iIns;
return &dlsIns;
}
if(pInsNo)
*pInsNo = iIns;
return &dlsIns;
}
}
}
} else
} else if(!isDrum && !(dlsIns.ulBank & F_INSTRUMENT_DRUMS))
{
if (!(dlsIns.ulBank & F_INSTRUMENT_DRUMS))
if((program >= 0x80) || (program == (dlsIns.ulInstrument & 0x7F)))
{
if ((program >= 0x80) || (program == (dlsIns.ulInstrument & 0x7F)))
{
if (pInsNo) *pInsNo = iIns;
return &dlsIns;
}
if(pInsNo)
*pInsNo = iIns;
return &dlsIns;
}
}
}
}
return nullptr;
}
@ -678,7 +680,7 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chu
{
case IFFID_rgn: // Level 1 region
case IFFID_rgn2: // Level 2 region
if (pDlsIns->nRegions < DLSMAXREGIONS) pDlsIns->nRegions++;
pDlsIns->Regions.push_back({});
break;
}
} else
@ -696,11 +698,11 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chu
}
case IFFID_rgnh:
if (pDlsIns->nRegions < DLSMAXREGIONS)
if(!pDlsIns->Regions.empty())
{
RGNHChunk rgnh;
chunk.ReadStruct(rgnh);
DLSREGION &region = pDlsIns->Regions[pDlsIns->nRegions];
DLSREGION &region = pDlsIns->Regions.back();
region.uKeyMin = (uint8)rgnh.RangeKey.usLow;
region.uKeyMax = (uint8)rgnh.RangeKey.usHigh;
region.fuOptions = (uint8)(rgnh.usKeyGroup & DLSREGION_KEYGROUPMASK);
@ -712,11 +714,11 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chu
break;
case IFFID_wlnk:
if (pDlsIns->nRegions < DLSMAXREGIONS)
if (!pDlsIns->Regions.empty())
{
WLNKChunk wlnk;
chunk.ReadStruct(wlnk);
DLSREGION &region = pDlsIns->Regions[pDlsIns->nRegions];
DLSREGION &region = pDlsIns->Regions.back();
region.nWaveLink = (uint16)wlnk.ulTableIndex;
if((region.nWaveLink < Util::MaxValueOfType(region.nWaveLink)) && (region.nWaveLink >= m_nMaxWaveLink))
m_nMaxWaveLink = region.nWaveLink + 1;
@ -726,9 +728,9 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chu
break;
case IFFID_wsmp:
if (pDlsIns->nRegions < DLSMAXREGIONS)
if(!pDlsIns->Regions.empty())
{
DLSREGION &region = pDlsIns->Regions[pDlsIns->nRegions];
DLSREGION &region = pDlsIns->Regions.back();
WSMPCHUNK wsmp;
chunk.ReadStruct(wsmp);
region.fuOptions |= DLSREGION_OVERRIDEWSMP;
@ -762,10 +764,7 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chu
{
ART1Chunk art1;
chunk.ReadStruct(art1);
if (pDlsIns->ulBank & F_INSTRUMENT_DRUMS)
{
if (pDlsIns->nRegions >= DLSMAXREGIONS) break;
} else
if(!(pDlsIns->ulBank & F_INSTRUMENT_DRUMS))
{
pDlsIns->nMelodicEnv = static_cast<uint32>(m_Envelopes.size() + 1);
}
@ -791,9 +790,7 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chu
case ART_DEFAULTPAN:
{
int32 pan = 128 + blk.lScale / (65536000/128);
if (pan < 0) pan = 0;
if (pan > 255) pan = 255;
dlsEnv.nDefPan = (uint8)pan;
dlsEnv.nDefPan = mpt::saturate_cast<uint8>(pan);
}
break;
@ -1083,7 +1080,7 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info)
dlsIns.nMelodicEnv = static_cast<uint32>(m_Envelopes.size());
}
// Load Instrument Bags
dlsIns.nRegions = 0;
dlsIns.Regions.clear();
for(const auto nInstrNdx : instruments)
{
if(nInstrNdx >= numInsts)
@ -1091,13 +1088,13 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info)
sf2info.insts.Seek(nInstrNdx * sizeof(SFINST));
SFINST insts[2];
sf2info.insts.ReadArray(insts);
const auto startRegion = dlsIns.nRegions;
dlsIns.nRegions += insts[1].wInstBagNdx - insts[0].wInstBagNdx;
const auto startRegion = static_cast<uint32>(dlsIns.Regions.size());
const auto endRegion = startRegion + insts[1].wInstBagNdx - insts[0].wInstBagNdx;
dlsIns.Regions.resize(endRegion);
//Log("\nIns %3d, %2d regions:\n", nIns, pSmp->nRegions);
if (dlsIns.nRegions > DLSMAXREGIONS) dlsIns.nRegions = DLSMAXREGIONS;
DLSREGION *pRgn = &dlsIns.Regions[startRegion];
bool hasGlobalZone = false;
for(uint32 nRgn = startRegion; nRgn < dlsIns.nRegions; nRgn++, pRgn++)
for(uint32 nRgn = startRegion; nRgn < endRegion; nRgn++, pRgn++)
{
uint32 ibagcnt = insts[0].wInstBagNdx + nRgn - startRegion;
if(ibagcnt >= numInstBags)
@ -1164,7 +1161,8 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info)
case SF2_GEN_PAN:
{
int32 pan = static_cast<int16>(value);
pan = (((pan + 500) * 127) / 500) + 128;
pan = std::clamp(Util::muldivr(pan + 500, 256, 1000), 0, 256);
pRgn->panning = static_cast<int16>(pan);
pDlsEnv->nDefPan = mpt::saturate_cast<uint8>(pan);
}
break;
@ -1403,6 +1401,7 @@ bool CDLSBank::Open(FileReader file)
{
DLSINSTRUMENT *pDlsIns = &m_Instruments[nInsDef];
//Log("Instrument %d:\n", nInsDef);
pDlsIns->Regions.push_back({});
UpdateInstrumentDefinition(pDlsIns, subData);
nInsDef++;
}
@ -1488,14 +1487,16 @@ bool CDLSBank::Open(FileReader file)
uint32 CDLSBank::GetRegionFromKey(uint32 nIns, uint32 nKey) const
{
if (nIns >= m_Instruments.size()) return 0;
if(nIns >= m_Instruments.size())
return 0;
const DLSINSTRUMENT &dlsIns = m_Instruments[nIns];
for (uint32 rgn = 0; rgn < dlsIns.nRegions; rgn++)
for(uint32 rgn = 0; rgn < static_cast<uint32>(dlsIns.Regions.size()); rgn++)
{
if ((nKey >= dlsIns.Regions[rgn].uKeyMin) && (nKey <= dlsIns.Regions[rgn].uKeyMax))
{
return rgn;
}
if(nKey < dlsIns.Regions[rgn].uKeyMin || nKey > dlsIns.Regions[rgn].uKeyMax)
continue;
if(dlsIns.Regions[rgn].fuOptions & DLSREGION_ISGLOBAL)
continue;
return rgn;
}
return 0;
}
@ -1514,7 +1515,7 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector<uint8> &wav
return false;
}
const DLSINSTRUMENT &dlsIns = m_Instruments[nIns];
if (nRgn >= dlsIns.nRegions)
if(nRgn >= dlsIns.Regions.size())
{
#ifdef DLSBANK_LOG
MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_("invalid waveform region: nIns=%1 nRgn=%2 pSmp->nRegions=%3"))(nIns, nRgn, dlsIns.nRegions));
@ -1535,6 +1536,7 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector<uint8> &wav
{
return false;
}
mpt::IO::Offset sampleOffset = mpt::saturate_cast<mpt::IO::Offset>(m_WaveForms[nWaveLink] + m_dwWavePoolOffset);
if(mpt::IO::SeekAbsolute(f, sampleOffset))
{
@ -1586,11 +1588,15 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI
uint32 dwLen = 0;
bool ok, hasWaveform;
if (nIns >= m_Instruments.size()) return false;
if(nIns >= m_Instruments.size())
return false;
const DLSINSTRUMENT *pDlsIns = &m_Instruments[nIns];
if (nRgn >= pDlsIns->nRegions) return false;
if (!ExtractWaveForm(nIns, nRgn, pWaveForm, dwLen)) return false;
if (dwLen < 16) return false;
if(nRgn >= pDlsIns->Regions.size())
return false;
if(!ExtractWaveForm(nIns, nRgn, pWaveForm, dwLen))
return false;
if(dwLen < 16)
return false;
ok = false;
FileReader wsmpChunk;
@ -1719,22 +1725,24 @@ static uint16 ScaleEnvelope(uint32 time, float tempoScale)
bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, uint32 nIns, uint32 nDrumRgn) const
{
SAMPLEINDEX RgnToSmp[DLSMAXREGIONS];
uint32 nRgnMin, nRgnMax, nEnv;
if (nIns >= m_Instruments.size()) return false;
const DLSINSTRUMENT *pDlsIns = &m_Instruments[nIns];
std::vector<SAMPLEINDEX> RgnToSmp(pDlsIns->Regions.size());
if (pDlsIns->ulBank & F_INSTRUMENT_DRUMS)
{
if (nDrumRgn >= pDlsIns->nRegions) return false;
if(nDrumRgn >= pDlsIns->Regions.size())
return false;
nRgnMin = nDrumRgn;
nRgnMax = nDrumRgn+1;
nEnv = pDlsIns->Regions[nDrumRgn].uPercEnv;
} else
{
if (!pDlsIns->nRegions) return false;
if(pDlsIns->Regions.empty())
return false;
nRgnMin = 0;
nRgnMax = pDlsIns->nRegions;
nRgnMax = static_cast<uint32>(pDlsIns->Regions.size());
nEnv = pDlsIns->nMelodicEnv;
}
if(nRgnMin == 0 && (pDlsIns->Regions[0].fuOptions & DLSREGION_ISGLOBAL))
@ -1742,8 +1750,8 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui
#ifdef DLSINSTR_LOG
MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_("DLS Instrument #%1: %2"))(nIns, mpt::ToUnicode(mpt::Charset::ASCII, mpt::String::ReadAutoBuf(pDlsIns->szName))));
MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" Bank=0x%1 Instrument=0x%2"))(mpt::ufmt::HEX0<4>(pDlsIns->ulBank), mpt::ufmt::HEX0<4>(pDlsIns->ulInstrument)));
MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" %1 regions, nMelodicEnv=%2"))(pDlsIns->nRegions, pDlsIns->nMelodicEnv));
for (uint32 iDbg=0; iDbg<pDlsIns->nRegions; iDbg++)
MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" %1 regions, nMelodicEnv=%2"))(pDlsIns->Regions.size(), pDlsIns->nMelodicEnv));
for (uint32 iDbg=0; iDbg<pDlsIns->Regions.size(); iDbg++)
{
const DLSREGION *prgn = &pDlsIns->Regions[iDbg];
MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" Region %1:"))(iDbg));
@ -1845,7 +1853,7 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui
uint32 nmaxsmp = (m_nType & MOD_TYPE_XM) ? 16 : 32;
if (nLoadedSmp >= nmaxsmp)
{
nSmp = RgnToSmp[nRgn-1];
nSmp = RgnToSmp[nRgn - 1];
} else
{
nextSample = sndFile.GetNextFreeSample(nInstr, nextSample + 1);
@ -1874,7 +1882,7 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui
} else if(sndFile.GetSample(nSmp).GetNumChannels() == 1)
{
// Try to combine stereo samples
uint8 pan1 = GetPanning(nIns, nRgn), pan2 = GetPanning(nIns, iDup);
const uint16 pan1 = GetPanning(nIns, nRgn), pan2 = GetPanning(nIns, iDup);
if((pan1 < 16 && pan2 >= 240) || (pan2 < 16 && pan1 >= 240))
{
ModSample &sample = sndFile.GetSample(nSmp);
@ -2029,6 +2037,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->Sanitize(MOD_TYPE_MPT);
pIns->Convert(MOD_TYPE_MPT, sndFile.GetType());
return true;
}
@ -2036,9 +2045,11 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui
const char *CDLSBank::GetRegionName(uint32 nIns, uint32 nRgn) const
{
if (nIns >= m_Instruments.size()) return nullptr;
if(nIns >= m_Instruments.size())
return nullptr;
const DLSINSTRUMENT &dlsIns = m_Instruments[nIns];
if (nRgn >= dlsIns.nRegions) return nullptr;
if(nRgn >= dlsIns.Regions.size())
return nullptr;
if (m_nType & SOUNDBANK_TYPE_SF2)
{
@ -2052,12 +2063,15 @@ const char *CDLSBank::GetRegionName(uint32 nIns, uint32 nRgn) const
}
uint8 CDLSBank::GetPanning(uint32 ins, uint32 region) const
uint16 CDLSBank::GetPanning(uint32 ins, uint32 region) const
{
const DLSINSTRUMENT &dlsIns = m_Instruments[ins];
if(region >= CountOf(dlsIns.Regions))
if(region >= dlsIns.Regions.size())
return 128;
const DLSREGION &rgn = dlsIns.Regions[region];
if(rgn.panning >= 0)
return static_cast<uint16>(rgn.panning);
if(dlsIns.ulBank & F_INSTRUMENT_DRUMS)
{
if(rgn.uPercEnv > 0 && rgn.uPercEnv <= m_Envelopes.size())

View File

@ -23,17 +23,16 @@ OPENMPT_NAMESPACE_BEGIN
#ifdef MODPLUG_TRACKER
#define DLSMAXREGIONS 128
struct DLSREGION
{
uint32 ulLoopStart;
uint32 ulLoopEnd;
uint16 nWaveLink;
uint16 uPercEnv;
uint16 usVolume; // 0..256
uint16 fuOptions; // flags + key group
int16 sFineTune; // +128 = +1 semitone
uint16 usVolume; // 0..256
uint16 fuOptions; // flags + key group
int16 sFineTune; // +128 = +1 semitone
int16 panning = -1; // -1= unset (DLS), otherwise 0...256
uint8 uKeyMin;
uint8 uKeyMax;
uint8 uUnityNote;
@ -43,11 +42,11 @@ struct DLSREGION
struct DLSENVELOPE
{
// Volume Envelope
uint16 wVolAttack; // Attack Time: 0-1000, 1 = 20ms (1/50s) -> [0-20s]
uint16 wVolDecay; // Decay Time: 0-1000, 1 = 20ms (1/50s) -> [0-20s]
uint16 wVolRelease; // Release Time: 0-1000, 1 = 20ms (1/50s) -> [0-20s]
uint8 nVolSustainLevel; // Sustain Level: 0-128, 128=100%
uint8 nDefPan; // Default Pan
uint16 wVolAttack; // Attack Time: 0-1000, 1 = 20ms (1/50s) -> [0-20s]
uint16 wVolDecay; // Decay Time: 0-1000, 1 = 20ms (1/50s) -> [0-20s]
uint16 wVolRelease; // Release Time: 0-1000, 1 = 20ms (1/50s) -> [0-20s]
uint8 nVolSustainLevel; // Sustain Level: 0-128, 128=100%
uint8 nDefPan; // Default Pan
};
// Special Bank bits
@ -55,12 +54,12 @@ struct DLSENVELOPE
struct DLSINSTRUMENT
{
uint32 ulBank, ulInstrument;
uint32 nRegions, nMelodicEnv;
DLSREGION Regions[DLSMAXREGIONS];
uint32 ulBank = 0, ulInstrument = 0;
uint32 nMelodicEnv = 0;
std::vector<DLSREGION> Regions;
char szName[32];
// SF2 stuff (DO NOT USE! -> used internally by the SF2 loader)
uint16 wPresetBagNdx, wPresetBagNum;
uint16 wPresetBagNdx = 0, wPresetBagNum = 0;
};
struct DLSSAMPLEEX
@ -130,7 +129,7 @@ public:
bool ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nIns, uint32 nRgn, int transpose = 0) const;
bool ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, uint32 nIns, uint32 nDrumRgn) const;
const char *GetRegionName(uint32 nIns, uint32 nRgn) const;
uint8 GetPanning(uint32 ins, uint32 region) const;
uint16 GetPanning(uint32 ins, uint32 region) const;
// Internal Loader Functions
protected:

View File

@ -248,7 +248,7 @@ struct MixLoopState
{
if (nPosDest < nLoopStart)
{
nSmpCount = DistanceToBufferLength(SamplePosition(chn.nLoopStart, 0), nPos, nInv);
nSmpCount = DistanceToBufferLength(SamplePosition(nLoopStart, 0), nPos, nInv);
}
} else
{
@ -605,7 +605,7 @@ void CSoundFile::ProcessPlugins(uint32 nCount)
if (!plugin.IsOutputToMaster())
{
PLUGINDEX nOutput = plugin.GetOutputPlugin();
if(nOutput > plug && nOutput != PLUGINDEX_INVALID
if(nOutput > plug && nOutput < MAX_MIXPLUGINS
&& m_MixPlugins[nOutput].pMixPlugin != nullptr)
{
IMixPlugin *outPlugin = m_MixPlugins[nOutput].pMixPlugin;

View File

@ -596,7 +596,7 @@ bool ReadInstrumentHeaderField(ModInstrument *input, uint32 fcode, uint16 fsize,
case MagicBE("R..."):
{
// Resampling has been written as various sizes including uint16 and uint32 in the past
uint32 tmp = file.ReadTruncatedIntLE<uint32>(fsize);
uint32 tmp = file.ReadSizedIntLE<uint32>(fsize);
if(Resampling::IsKnownMode(tmp))
input->resampling = static_cast<ResamplingMode>(tmp);
result = true;
@ -604,27 +604,27 @@ bool ReadInstrumentHeaderField(ModInstrument *input, uint32 fcode, uint16 fsize,
case MagicBE("PTTL"):
{
// Integer part of pitch/tempo lock
uint16 tmp = file.ReadTruncatedIntLE<uint16>(fsize);
uint16 tmp = file.ReadSizedIntLE<uint16>(fsize);
input->pitchToTempoLock.Set(tmp, input->pitchToTempoLock.GetFract());
result = true;
} break;
case MagicLE("PTTF"):
{
// Fractional part of pitch/tempo lock
uint16 tmp = file.ReadTruncatedIntLE<uint16>(fsize);
uint16 tmp = file.ReadSizedIntLE<uint16>(fsize);
input->pitchToTempoLock.Set(input->pitchToTempoLock.GetInt(), tmp);
result = true;
} break;
case MagicBE("VE.."):
input->VolEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadTruncatedIntLE<uint32>(fsize)));
input->VolEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadSizedIntLE<uint32>(fsize)));
result = true;
break;
case MagicBE("PE.."):
input->PanEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadTruncatedIntLE<uint32>(fsize)));
input->PanEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadSizedIntLE<uint32>(fsize)));
result = true;
break;
case MagicBE("PiE."):
input->PitchEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadTruncatedIntLE<uint32>(fsize)));
input->PitchEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadSizedIntLE<uint32>(fsize)));
result = true;
break;
}

View File

@ -1126,6 +1126,8 @@ uintptr_t DMFUnpack(FileReader &file, uint8 *psample, uint32 maxlen)
try
{
tree.DMFNewNode();
if(tree.nodes[0].left < 0 || tree.nodes[0].right < 0)
return tree.file.GetPosition();
for(uint32 i = 0; i < maxlen; i++)
{
int actnode = 0;

View File

@ -2112,7 +2112,9 @@ void CSoundFile::ReadMixPluginChunk(FileReader &file, SNDMIXPLUGIN &plugin)
if(!memcmp(code, "DWRT", 4))
{
plugin.fDryRatio = dataChunk.ReadFloatLE();
plugin.fDryRatio = std::clamp(dataChunk.ReadFloatLE(), 0.0f, 1.0f);
if(!std::isnormal(plugin.fDryRatio))
plugin.fDryRatio = 0.0f;
} else if(!memcmp(code, "PROG", 4))
{
plugin.defaultProgram = dataChunk.ReadUint32LE();
@ -2448,8 +2450,10 @@ void CSoundFile::LoadExtendedSongProperties(FileReader &file, bool ignoreChannel
// Validate read values.
Limit(m_nDefaultTempo, GetModSpecifications().GetTempoMin(), GetModSpecifications().GetTempoMax());
if(m_nTempoMode >= tempoModeMax) m_nTempoMode = tempoModeClassic;
if(m_nMixLevels >= mixLevelsMax) m_nMixLevels = mixLevelsOriginal;
if(m_nTempoMode >= tempoModeMax)
m_nTempoMode = tempoModeClassic;
if(m_nMixLevels >= mixLevelsMax)
m_nMixLevels = mixLevelsOriginal;
//m_dwCreatedWithVersion
//m_dwLastSavedWithVersion
//m_nSamplePreAmp
@ -2458,7 +2462,8 @@ void CSoundFile::LoadExtendedSongProperties(FileReader &file, bool ignoreChannel
LimitMax(m_nDefaultGlobalVolume, MAX_GLOBAL_VOLUME);
//m_nRestartPos
//m_ModFlags
if(!m_tempoSwing.empty()) m_tempoSwing.resize(m_nDefaultRowsPerBeat);
LimitMax(m_nDefaultRowsPerBeat, MAX_ROWS_PER_BEAT);
LimitMax(m_nDefaultRowsPerMeasure, MAX_ROWS_PER_BEAT);
}

View File

@ -679,7 +679,7 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags)
}
for(CHANNELINDEX chn = 0; chn < numChans; chn++)
{
if(chunk.ReadUint16LE() > 0 && chn >= m_nChannels)
if(chunk.ReadUint16LE() > 0 && chn >= m_nChannels && chn < 32)
m_nChannels = chn + 1;
}
}

View File

@ -1139,7 +1139,7 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
}
}
if(playSeq.name[0])
order.SetName(mpt::ToUnicode(mpt::Charset::ISO8859_1, playSeq.name));
order.SetName(mpt::ToUnicode(mpt::Charset::ISO8859_1, mpt::String::ReadAutoBuf(playSeq.name)));
// Play commands (jump / stop)
if(playSeq.commandTableOffset > 0 && file.Seek(playSeq.commandTableOffset))

View File

@ -697,14 +697,26 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
uint16 finishedTracks = 0;
PATTERNINDEX emptyPattern = PATTERNINDEX_INVALID;
ORDERINDEX lastOrd = 0;
ROWINDEX lastRow = 0;
ORDERINDEX lastOrd = 0, loopEndOrd = ORDERINDEX_INVALID;
ROWINDEX lastRow = 0, loopEndRow = ROWINDEX_INVALID;
ROWINDEX restartRow = ROWINDEX_INVALID;
int8 masterTranspose = 0;
bool isXG = false;
bool isEMIDI = false;
bool isEMIDILoop = false;
const bool isType2 = (fileHeader.format == 2);
const auto ModPositionFromTick = [&](const tick_t tick, const tick_t offset = 0)
{
tick_t modTicks = Util::muldivr_unsigned(tick, quantize * ticksPerRow, ppqn * 4u) - offset;
ORDERINDEX ord = static_cast<ORDERINDEX>((modTicks / ticksPerRow) / patternLen);
ROWINDEX row = (modTicks / ticksPerRow) % patternLen;
uint8 delay = static_cast<uint8>(modTicks % ticksPerRow);
return std::make_tuple(ord, row, delay);
};
while(finishedTracks < numTracks)
{
uint16 t = 0;
@ -721,11 +733,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
}
FileReader &track = tracks[t].track;
tick_t modTicks = Util::muldivr_unsigned(tick, quantize * ticksPerRow, ppqn * 4u);
ORDERINDEX ord = static_cast<ORDERINDEX>((modTicks / ticksPerRow) / patternLen);
ROWINDEX row = (modTicks / ticksPerRow) % patternLen;
uint8 delay = static_cast<uint8>(modTicks % ticksPerRow);
const auto [ord, row, delay] = ModPositionFromTick(tick);
if(ord >= Order().GetLength())
{
@ -800,6 +808,9 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
{
Order().SetRestartPos(ord);
restartRow = row;
} else if(!mpt::CompareNoCaseAscii(s, "loopEnd"))
{
std::tie(loopEndOrd, loopEndRow, std::ignore) = ModPositionFromTick(tick, 1);
}
}
break;
@ -1003,13 +1014,31 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
case 111:
// Non-standard MIDI loop point. May conflict with Apogee EMIDI CCs (110/111), which is why we also check if CC 110 is ever used.
if(data2 == 0)
if(data2 == 0 && !isEMIDI)
{
Order().SetRestartPos(ord);
restartRow = row;
}
break;
case 118:
// EMIDI Global Loop Start
isEMIDI = true;
isEMIDILoop = false;
Order().SetRestartPos(ord);
restartRow = row;
break;
case 119:
// EMIDI Global Loop End
if(data2 == 0x7F)
{
isEMIDILoop = true;
isEMIDI = true;
std::tie(loopEndOrd, loopEndRow, std::ignore) = ModPositionFromTick(tick, 1);
}
break;
case MIDIEvents::MIDICC_AllControllersOff:
midiChnStatus[midiCh].ResetAllControllers();
break;
@ -1210,18 +1239,29 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
}
}
if(isEMIDILoop)
isEMIDI = false;
if(isEMIDI)
{
Order().SetRestartPos(0);
}
if(Order().IsValidPat(lastOrd))
if(loopEndOrd == ORDERINDEX_INVALID)
loopEndOrd = lastOrd;
if(loopEndRow == ROWINDEX_INVALID)
loopEndRow = lastRow;
if(Order().IsValidPat(loopEndOrd))
{
PATTERNINDEX lastPat = Order()[lastOrd];
Patterns[lastPat].Resize(lastRow + 1);
PATTERNINDEX lastPat = Order()[loopEndOrd];
if(loopEndOrd == lastOrd)
Patterns[lastPat].Resize(loopEndRow + 1);
if(restartRow != ROWINDEX_INVALID && !isEMIDI)
{
Patterns[lastPat].WriteEffect(EffectWriter(CMD_PATTERNBREAK, mpt::saturate_cast<ModCommand::PARAM>(restartRow)).Row(lastRow));
Patterns[lastPat].WriteEffect(EffectWriter(CMD_PATTERNBREAK, mpt::saturate_cast<ModCommand::PARAM>(restartRow)).Row(loopEndRow));
if(ORDERINDEX restartPos = Order().GetRestartPos(); loopEndOrd != lastOrd || restartPos <= std::numeric_limits<ModCommand::PARAM>::max())
Patterns[lastPat].WriteEffect(EffectWriter(CMD_POSITIONJUMP, mpt::saturate_cast<ModCommand::PARAM>(restartPos)).Row(loopEndRow));
}
}
Order.SetSequence(0);

View File

@ -590,7 +590,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags)
{
m_nTempoMode = tempoModeModern;
double d = chunk.ReadDoubleLE();
if(d != 0.0)
if(d > 0.00000001)
{
m_nDefaultTempo = TEMPO(44100.0 * 60.0 / (m_nDefaultSpeed * m_nDefaultRowsPerBeat * d));
}

View File

@ -389,6 +389,12 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags)
sampleChunks.push_back(chunk);
}
break;
default:
// Non-ASCII chunk ID?
if(iffHead.signature & 0x80808080)
return false;
break;
}
}

View File

@ -267,13 +267,9 @@ bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags)
switch(m.command)
{
case CMD_PANNING8:
// My observations of this weird command...
// 800...887 pan from hard left to hard right (whereas the low nibble seems to be ignored)
// 888...8FF do the same (so 888...88F is hard left, and 890 is identical to 810)
if(m.param >= 0x88) m.param &= 0x7F;
else if(m.param > 0x80) m.param = 0x80;
m.param &= 0x7F;
m.param = (m.param * 0xFF) / 0x7F;
// Don't be surprised about the strange formula, this is directly translated from original disassembly...
m.command = CMD_S3MCMDEX;
m.param = 0x80 | ((std::max<uint8>(m.param >> 3, 1u) - 1u) & 0x0F);
break;
case CMD_GLOBALVOLUME:
m.param = std::min(m.param, uint8(0x40)) * 2u;

View File

@ -374,13 +374,13 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
patternLength = file.ReadUint16BE();
channels = file.ReadUint16BE();
if(channels > MAX_BASECHANNELS)
return false;
m_nChannels = std::max(m_nChannels, channels);
file.Skip(channels * patternLength * 4u);
}
file.Seek(patOffset);
if(m_nChannels > MAX_BASECHANNELS)
return false;
}
struct ChannelMemory
@ -406,6 +406,9 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
channels = file.ReadUint16BE();
}
if(!file.CanRead(channels * patternLength * 4u))
break;
if(!(loadFlags & loadPatternData) || !Patterns.Insert(actualPat, patternLength))
{
file.Skip(channels * patternLength * 4u);

View File

@ -603,6 +603,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
FlagSet<TrackerVersions> madeWith(verUnknown);
mpt::ustring madeWithTracker;
bool isMadTracker = false;
if(!memcmp(fileHeader.trackerName, "FastTracker v2.00 ", 20) && fileHeader.size == 276)
{
@ -645,6 +646,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
m_playBehaviour.reset(kFT2PortaNoNote);
// Fix arpeggios in kragle_-_happy_day.xm
m_playBehaviour.reset(kFT2Arpeggio);
isMadTracker = true;
} else if(!memcmp(fileHeader.trackerName, "Skale Tracker\0", 14))
{
m_playBehaviour.reset(kFT2ST3OffsetOutOfRange);
@ -1021,20 +1023,18 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
Order().Replace(0xFF, Order.GetInvalidPatIndex());
}
m_modFormat.formatName = mpt::format(U_("FastTracker 2 v%1.%2"))(fileHeader.version >> 8, mpt::ufmt::hex0<2>(fileHeader.version & 0xFF));
m_modFormat.madeWithTracker = std::move(madeWithTracker);
m_modFormat.charset = (m_dwLastSavedWithVersion || isMadTracker) ? mpt::Charset::Windows1252 : mpt::Charset::CP437;
if(isOXM)
{
m_modFormat.originalFormatName = std::move(m_modFormat.formatName);
m_modFormat.formatName = U_("OggMod FastTracker 2");
m_modFormat.type = U_("oxm");
m_modFormat.originalFormatName = mpt::format(U_("FastTracker 2 v%1.%2"))(fileHeader.version >> 8, mpt::ufmt::hex0<2>(fileHeader.version & 0xFF));
m_modFormat.originalType = U_("xm");
m_modFormat.madeWithTracker = std::move(madeWithTracker);
m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::Charset::Windows1252 : mpt::Charset::CP437;
} else
{
m_modFormat.formatName = mpt::format(U_("FastTracker 2 v%1.%2"))(fileHeader.version >> 8, mpt::ufmt::hex0<2>(fileHeader.version & 0xFF));
m_modFormat.type = U_("xm");
m_modFormat.madeWithTracker = std::move(madeWithTracker);
m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::Charset::Windows1252 : mpt::Charset::CP437;
}
return true;

View File

@ -21,7 +21,7 @@ OPENMPT_NAMESPACE_BEGIN
// Read song message from a mapped file.
// [in] data: pointer to the data in memory that is going to be read
// [in] length: number of characters that should be read, not including a possible trailing null terminator (it is automatically appended).
// [in] length: number of characters that should be read. Trailing null characters are automatically removed.
// [in] lineEnding: line ending formatting of the text in memory.
// [out] returns true on success.
bool SongMessage::Read(const std::byte *data, size_t length, LineEnding lineEnding)
@ -36,18 +36,19 @@ bool SongMessage::Read(const std::byte *data, size_t length, LineEnding lineEndi
// Simple line-ending detection algorithm. VERY simple.
if(lineEnding == leAutodetect)
{
char cprev = 0;
size_t nCR = 0, nLF = 0, nCRLF = 0;
// find CRs, LFs and CRLFs
for(size_t i = 0; i < length; i++)
{
char c = str[i];
if(c == '\r') nCR++;
else if(c == '\n') nLF++;
if(c == '\r')
nCR++;
else if(c == '\n')
nLF++;
if(i && cprev == '\r' && c == '\n') nCRLF++;
cprev = c;
if(i && str[i - 1] == '\r' && c == '\n')
nCRLF++;
}
// evaluate findings
if(nCR == nLF && nCR == nCRLF)
@ -64,16 +65,15 @@ bool SongMessage::Read(const std::byte *data, size_t length, LineEnding lineEndi
// calculate the final amount of characters to be allocated.
for(size_t i = 0; i < length; i++)
{
char c = str[i];
if(c != '\n' || lineEnding != leCRLF)
finalLength++;
finalLength++;
if(str[i] == '\r' && lineEnding == leCRLF)
i++; // skip the LF
}
resize(finalLength);
clear();
reserve(finalLength);
size_t cpos = 0;
for(size_t i = 0; i < length; i++, cpos++)
for(size_t i = 0; i < length; i++)
{
char c = str[i];
@ -81,24 +81,25 @@ bool SongMessage::Read(const std::byte *data, size_t length, LineEnding lineEndi
{
case '\r':
if(lineEnding != leLF)
at(cpos) = InternalLineEnding;
c = InternalLineEnding;
else
at(cpos) = ' ';
if(lineEnding == leCRLF) i++; // skip the LF
c = ' ';
if(lineEnding == leCRLF)
i++; // skip the LF
break;
case '\n':
if(lineEnding != leCR && lineEnding != leCRLF)
at(cpos) = InternalLineEnding;
c = InternalLineEnding;
else
at(cpos) = ' ';
c = ' ';
break;
case '\0':
at(cpos) = ' ';
c = ' ';
break;
default:
at(cpos) = c;
break;
}
push_back(c);
}
return true;

View File

@ -106,7 +106,7 @@ int32 InstrumentEnvelope::GetValueFromPosition(int position, int32 rangeOut, int
{
// Linear approximation between the points;
// f(x + d) ~ f(x) + f'(x) * d, where f'(x) = (y2 - y1) / (x2 - x1)
value += ((position - x1) * (at(pt).value * ENV_PRECISION / rangeIn - value)) / (x2 - x1);
value += Util::muldiv(position - x1, (at(pt).value * ENV_PRECISION / rangeIn - value), x2 - x1);
}
}
@ -299,6 +299,9 @@ void ModInstrument::Sanitize(MODTYPE modType)
if(!Resampling::IsKnownMode(resampling))
resampling = SRCMODE_DEFAULT;
if(nMixPlug > MAX_MIXPLUGINS)
nMixPlug = 0;
}

View File

@ -30,6 +30,12 @@ static bool SFZStartsWith(const std::string_view &l, const char(&r)[N])
return l.substr(0, N - 1) == r;
}
template <size_t N>
static bool SFZEndsWith(const std::string_view &l, const char (&r)[N])
{
return l.size() >= (N - 1) && l.substr(l.size() - (N - 1), N - 1) == r;
}
static bool SFZIsNumeric(const std::string_view &str)
{
return std::find_if(str.begin(), str.end(), [](char c) { return c < '0' || c > '9'; }) == str.end();
@ -243,7 +249,7 @@ struct SFZEnvelope
auto &env = eg.points;
if(attack > 0 || delay > 0)
{
env.push_back({0, startLevel});
env.push_back({0.0, startLevel});
if(delay > 0)
env.push_back({delay, env.back().second});
env.push_back({attack, 100.0});
@ -255,7 +261,7 @@ struct SFZEnvelope
env.push_back({hold, env.back().second});
}
if(env.empty())
env.push_back({0, 100.0});
env.push_back({0.0, 100.0});
if(env.back().second != sustainLevel)
env.push_back({decay, sustainLevel});
if(sustainLevel != 0)
@ -685,7 +691,8 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
break;
}
const std::string key = mpt::ToLowerCaseAscii(s.substr(0, keyEnd));
if(key == "sample" || key == "default_path" || SFZStartsWith(key, "label_cc") || SFZStartsWith(key, "label_key") || SFZStartsWith(key, "region_label"))
// Currently defined *_label opcodes are global_label, group_label, master_label, region_label, sw_label
if(key == "sample" || key == "default_path" || SFZStartsWith(key, "label_cc") || SFZStartsWith(key, "label_key") || SFZEndsWith(key, "_label"))
{
// Sample / CC name may contain spaces...
charsRead = s.find_first_of("=\t<", valueStart);

View File

@ -56,6 +56,8 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const
restrictedSampleDataView = file.GetPinnedRawDataView(CalculateEncodedSize(sample.nLength));
sourceBuf = restrictedSampleDataView.data();
fileSize = restrictedSampleDataView.size();
if(sourceBuf == nullptr)
return 0;
} else
{
MPT_ASSERT_NOTREACHED();

View File

@ -43,6 +43,7 @@ using SmpLength = uint32;
inline constexpr SmpLength MAX_SAMPLE_LENGTH = 0x10000000; // Sample length in frames. Sample size in bytes can be more than this (= 256 MB).
inline constexpr ROWINDEX MAX_PATTERN_ROWS = 1024;
inline constexpr ROWINDEX MAX_ROWS_PER_BEAT = 65536;
inline constexpr ORDERINDEX MAX_ORDERS = ORDERINDEX_MAX + 1;
inline constexpr PATTERNINDEX MAX_PATTERNS = 4000;
inline constexpr SAMPLEINDEX MAX_SAMPLES = 4000;

View File

@ -113,7 +113,8 @@ public:
return;
}
const SmpLength sampleEnd = chn.dwFlags[CHN_LOOP] ? chn.nLoopEnd : chn.nLength;
const SamplePosition loopStart(chn.dwFlags[CHN_LOOP] ? chn.nLoopStart : 0u, 0);
const SamplePosition sampleEnd(chn.dwFlags[CHN_LOOP] ? chn.nLoopEnd : chn.nLength, 0);
const SmpLength loopLength = chn.nLoopEnd - chn.nLoopStart;
const bool itEnvMode = sndFile.m_playBehaviour[kITEnvelopePositionHandling];
const bool updatePitchEnv = (chn.PitchEnv.flags & (ENV_ENABLED | ENV_FILTER)) == ENV_ENABLED;
@ -176,53 +177,51 @@ public:
chn.position += inc;
if(chn.position.GetUInt() >= sampleEnd)
if(chn.position >= sampleEnd || (chn.position < loopStart && inc.IsNegative()))
{
if(chn.dwFlags[CHN_LOOP])
{
// We exceeded the sample loop, go back to loop start.
if(chn.dwFlags[CHN_PINGPONGLOOP])
{
if(chn.position < SamplePosition(chn.nLoopStart, 0))
{
chn.position = SamplePosition(chn.nLoopStart + chn.nLoopStart, 0) - chn.position;
chn.dwFlags.flip(CHN_PINGPONGFLAG);
inc.Negate();
}
SmpLength posInt = chn.position.GetUInt() - chn.nLoopStart;
SmpLength pingpongLength = loopLength * 2;
if(sndFile.m_playBehaviour[kITPingPongMode]) pingpongLength--;
posInt %= pingpongLength;
bool forward = (posInt < loopLength);
if(forward)
chn.position.SetInt(chn.nLoopStart + posInt);
else
chn.position.SetInt(chn.nLoopEnd - (posInt - loopLength));
if(forward == chn.dwFlags[CHN_PINGPONGFLAG])
{
chn.dwFlags.flip(CHN_PINGPONGFLAG);
inc.Negate();
}
} else
{
SmpLength posInt = chn.position.GetUInt();
if(posInt >= chn.nLoopEnd + loopLength)
{
const SmpLength overshoot = posInt - chn.nLoopEnd;
posInt -= (overshoot / loopLength) * loopLength;
}
while(posInt >= chn.nLoopEnd)
{
posInt -= loopLength;
}
chn.position.SetInt(posInt);
}
} else
if(!chn.dwFlags[CHN_LOOP])
{
// Past sample end.
stopNote = true;
break;
}
// We exceeded the sample loop, go back to loop start.
if(chn.dwFlags[CHN_PINGPONGLOOP])
{
if(chn.position < loopStart)
{
chn.position = SamplePosition(chn.nLoopStart + chn.nLoopStart, 0) - chn.position;
chn.dwFlags.flip(CHN_PINGPONGFLAG);
inc.Negate();
}
SmpLength posInt = chn.position.GetUInt() - chn.nLoopStart;
SmpLength pingpongLength = loopLength * 2;
if(sndFile.m_playBehaviour[kITPingPongMode]) pingpongLength--;
posInt %= pingpongLength;
bool forward = (posInt < loopLength);
if(forward)
chn.position.SetInt(chn.nLoopStart + posInt);
else
chn.position.SetInt(chn.nLoopEnd - (posInt - loopLength));
if(forward == chn.dwFlags[CHN_PINGPONGFLAG])
{
chn.dwFlags.flip(CHN_PINGPONGFLAG);
inc.Negate();
}
} else
{
SmpLength posInt = chn.position.GetUInt();
if(posInt >= chn.nLoopEnd + loopLength)
{
const SmpLength overshoot = posInt - chn.nLoopEnd;
posInt -= (overshoot / loopLength) * loopLength;
}
while(posInt >= chn.nLoopEnd)
{
posInt -= loopLength;
}
chn.position.SetInt(posInt);
}
}
}
@ -1794,7 +1793,7 @@ void CSoundFile::NoteChange(ModChannel &chn, int note, bool bPorta, bool bResetE
}
}
// IT compatibility tentative fix: Clear channel note memory.
// IT compatibility tentative fix: Clear channel note memory (TRANCE_N.IT by A3F).
if(m_playBehaviour[kITClearOldNoteAfterCut])
{
chn.nNote = chn.nNewNote = NOTE_NONE;
@ -2758,7 +2757,6 @@ bool CSoundFile::ProcessEffects()
// Now it's time for some FT2 crap...
if (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2))
{
// XM: Key-Off + Sample == Note Cut (BUT: Only if no instr number or volume effect is present!)
// Test case: NoteOffVolume.xm
if(note == NOTE_KEYOFF
@ -3347,8 +3345,11 @@ bool CSoundFile::ProcessEffects()
if(param && !m_SongFlags[SONG_ITOLDEFFECTS])
{
// Old effects have different length interpretation (+1 for both on and off)
if(param & 0xF0) param -= 0x10;
if(param & 0x0F) param -= 0x01;
if(param & 0xF0)
param -= 0x10;
if(param & 0x0F)
param -= 0x01;
chn.nTremorParam = static_cast<ModCommand::PARAM>(param);
}
chn.nTremorCount |= 0x80; // set on/off flag
} else if(m_playBehaviour[kFT2Tremor])
@ -3358,7 +3359,8 @@ bool CSoundFile::ProcessEffects()
}
chn.nCommand = CMD_TREMOR;
if (param) chn.nTremorParam = static_cast<ModCommand::PARAM>(param);
if(param)
chn.nTremorParam = static_cast<ModCommand::PARAM>(param);
break;
@ -3579,13 +3581,13 @@ bool CSoundFile::ProcessEffects()
case CMD_DBMECHO:
if(m_PlayState.m_nTickCount == 0)
{
uint32 chns = (param >> 4), enable = (param & 0x0F);
if(chns > 1 || enable > 2)
uint32 echoType = (param >> 4), enable = (param & 0x0F);
if(echoType > 2 || enable > 1)
{
break;
}
CHANNELINDEX firstChn = nChn, lastChn = nChn;
if(chns == 1)
if(echoType == 1)
{
firstChn = 0;
lastChn = m_nChannels - 1;
@ -4115,8 +4117,10 @@ void CSoundFile::ExtraFinePortamentoDown(ModChannel &chn, ModCommand::PARAM para
}
}
// Implemented for IMF compatibility, can't actually save this in any formats
// Implemented for IMF / PTM / OKT compatibility, can't actually save this in any formats
// Slide up / down every x ticks by y semitones
// Oktalyzer: Slide down on first tick only, or on every tick
void CSoundFile::NoteSlide(ModChannel &chn, uint32 param, bool slideUp, bool retrig) const
{
uint8 x, y;
@ -4129,23 +4133,26 @@ void CSoundFile::NoteSlide(ModChannel &chn, uint32 param, bool slideUp, bool ret
if (y)
chn.nNoteSlideStep = y;
chn.nNoteSlideCounter = chn.nNoteSlideSpeed;
} else
{
if (--chn.nNoteSlideCounter == 0)
{
chn.nNoteSlideCounter = chn.nNoteSlideSpeed;
// update it
const int32 delta = (slideUp ? 1 : -1) * chn.nNoteSlideStep;
if(chn.HasCustomTuning())
chn.m_PortamentoFineSteps += delta * chn.pModInstrument->pTuning->GetFineStepCount();
else
chn.nPeriod = GetPeriodFromNote(delta + GetNoteFromPeriod(chn.nPeriod), 8363, 0);
}
if(retrig)
{
chn.position.Set(0);
}
}
bool doTrigger = false;
if(GetType() == MOD_TYPE_OKT)
doTrigger = (chn.nNoteSlideSpeed == 0x10) || m_SongFlags[SONG_FIRSTTICK];
else
doTrigger = !m_SongFlags[SONG_FIRSTTICK] && (--chn.nNoteSlideCounter == 0);
if(doTrigger)
{
chn.nNoteSlideCounter = chn.nNoteSlideSpeed;
// update it
const int32 delta = (slideUp ? 1 : -1) * chn.nNoteSlideStep;
if(chn.HasCustomTuning())
chn.m_PortamentoFineSteps += delta * chn.pModInstrument->pTuning->GetFineStepCount();
else
chn.nPeriod = GetPeriodFromNote(delta + GetNoteFromPeriod(chn.nPeriod, 0, chn.nC5Speed), 0, chn.nC5Speed);
if(retrig)
chn.position.Set(0);
}
}
@ -5622,9 +5629,9 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset)
if(!m_playBehaviour[kFT2Retrigger] || !(chn.rowCommand.volcmd == VOLCMD_VOLUME))
{
if(retrigTable1[dv])
vol = (vol * retrigTable1[dv]) >> 4;
vol = (vol * retrigTable1[dv]) / 16;
else
vol += ((int)retrigTable2[dv]) << 2;
vol += ((int)retrigTable2[dv]) * 4;
}
Limit(vol, 0, 256);
@ -5675,7 +5682,7 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset)
if(m_playBehaviour[kITRetrigger]) chn.position.Set(0);
offset--;
if(offset >= 0 && offset <= static_cast<int>(CountOf(chn.pModSample->cues)) && chn.pModSample != nullptr)
if(chn.pModSample != nullptr && offset >= 0 && offset <= static_cast<int>(CountOf(chn.pModSample->cues)))
{
if(offset == 0)
offset = chn.oldOffset;
@ -6034,6 +6041,8 @@ uint32 CSoundFile::GetPeriodFromNote(uint32 note, int32 nFineTune, uint32 nC5Spe
// MDL uses non-linear slides, but their effectiveness does not depend on the middle-C frequency.
return (FreqS3MTable[note % 12u] << 4) >> (note / 12);
}
if(!nC5Speed)
nC5Speed = 8363;
if(m_SongFlags[SONG_LINEARSLIDES] || GetType() == MOD_TYPE_669)
{
// In IT linear slide mode, directly use frequency in Hertz rather than periods.
@ -6043,8 +6052,6 @@ uint32 CSoundFile::GetPeriodFromNote(uint32 note, int32 nFineTune, uint32 nC5Spe
return (FreqS3MTable[note % 12u] << 5) >> (note / 12);
} else
{
if (!nC5Speed)
nC5Speed = 8363;
LimitMax(nC5Speed, uint32_max >> (note / 12u));
//(a*b)/c
return Util::muldiv_unsigned(8363, (FreqS3MTable[note % 12u] << 5), nC5Speed << (note / 12u));

View File

@ -484,6 +484,8 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags)
LimitMax(ChnSettings[chn].nVolume, uint16(64));
if(ChnSettings[chn].nPan > 256)
ChnSettings[chn].nPan = 128;
if(ChnSettings[chn].nMixPlugin > MAX_MIXPLUGINS)
ChnSettings[chn].nMixPlugin = 0;
m_PlayState.Chn[chn].Reset(ModChannel::resetTotal, *this, chn, muteFlag);
}
@ -550,8 +552,15 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags)
LimitMax(m_nDefaultTempo, TEMPO(uint16_max, 0));
if(!m_nDefaultSpeed)
m_nDefaultSpeed = 6;
if(m_nDefaultRowsPerMeasure < m_nDefaultRowsPerBeat)
m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat;
LimitMax(m_nDefaultRowsPerBeat, MAX_ROWS_PER_BEAT);
LimitMax(m_nDefaultRowsPerMeasure, MAX_ROWS_PER_BEAT);
LimitMax(m_nDefaultGlobalVolume, MAX_GLOBAL_VOLUME);
if(!m_tempoSwing.empty())
m_tempoSwing.resize(m_nDefaultRowsPerBeat);
m_PlayState.m_nMusicSpeed = m_nDefaultSpeed;
m_PlayState.m_nMusicTempo = m_nDefaultTempo;
m_PlayState.m_nCurrentRowsPerBeat = m_nDefaultRowsPerBeat;

View File

@ -125,10 +125,10 @@ bool CSoundFile::FadeSong(uint32 msec)
{
ModChannel &pramp = m_PlayState.Chn[m_PlayState.ChnMix[noff]];
pramp.newRightVol = pramp.newLeftVol = 0;
pramp.leftRamp = (-pramp.leftVol << VOLUMERAMPPRECISION) / nRampLength;
pramp.rightRamp = (-pramp.rightVol << VOLUMERAMPPRECISION) / nRampLength;
pramp.rampLeftVol = pramp.leftVol << VOLUMERAMPPRECISION;
pramp.rampRightVol = pramp.rightVol << VOLUMERAMPPRECISION;
pramp.leftRamp = -pramp.leftVol * (1 << VOLUMERAMPPRECISION) / nRampLength;
pramp.rightRamp = -pramp.rightVol * (1 << VOLUMERAMPPRECISION) / nRampLength;
pramp.rampLeftVol = pramp.leftVol * (1 << VOLUMERAMPPRECISION);
pramp.rampRightVol = pramp.rightVol * (1 << VOLUMERAMPPRECISION);
pramp.nRampLength = nRampLength;
pramp.dwFlags.set(CHN_VOLUMERAMP);
}

View File

@ -112,6 +112,12 @@ struct UpgradePatternData
m.vol = 0;
}
}
// Command I11 accidentally behaved the same as command I00 with compatible IT tremor and old effects disabled
if(m.command == CMD_TREMOR && m.param == 0x11 && version < MPT_V("1.29.12.02") && sndFile.m_playBehaviour[kITTremor] && !sndFile.m_SongFlags[SONG_ITOLDEFFECTS])
{
m.param = 0;
}
}
else if(modType == MOD_TYPE_XM)
@ -250,7 +256,7 @@ void CSoundFile::UpgradeModule()
if(!compatModeIT || m_dwLastSavedWithVersion < MPT_V("1.18.00.00"))
{
// Previously, Pitch/Pan Separation was only half depth.
// Previously, Pitch/Pan Separation was only half depth (plot twist: it was actually only quarter depth).
// This was corrected in compatible mode in OpenMPT 1.18, and in OpenMPT 1.20 it is corrected in normal mode as well.
ins->nPPS = (ins->nPPS + (ins->nPPS >= 0 ? 1 : -1)) / 2;
}

View File

@ -645,6 +645,8 @@ Opal::Channel::Channel() {
ModulationType = 0;
ChannelPair = 0;
Enable = true;
LeftEnable = true;
RightEnable = true;
}
@ -672,12 +674,13 @@ void Opal::Channel::Output(int16_t &left, int16_t &right) {
else {
if (clk & 1)
vibrato >>= 1; // Odd positions are half the magnitude
vibrato <<= Octave;
if (clk & 4)
vibrato = -vibrato; // The second half positions are negative
}
vibrato <<= Octave;
// Combine individual operator outputs
int16_t out, acc;

View File

@ -500,13 +500,14 @@ void ReadModPattern(std::istream& iStrm, CPattern& pat, const size_t)
return;
ssb.ReadItem(pat, "data", &ReadData);
// pattern time signature
uint32 nRPB = 0, nRPM = 0;
ssb.ReadItem<uint32>(nRPB, "RPB.");
ssb.ReadItem<uint32>(nRPM, "RPM.");
pat.SetSignature(nRPB, nRPM);
uint32 rpb = 0, rpm = 0;
ssb.ReadItem<uint32>(rpb, "RPB.");
ssb.ReadItem<uint32>(rpm, "RPM.");
pat.SetSignature(rpb, rpm);
TempoSwing swing;
ssb.ReadItem<TempoSwing>(swing, "SWNG", TempoSwing::Deserialize);
if(!swing.empty()) swing.resize(nRPB);
if(!swing.empty())
swing.resize(pat.GetRowsPerBeat());
pat.SetTempoSwing(swing);
}

View File

@ -165,8 +165,6 @@ void LFOPlugin::SetParameter(PlugParamIndex index, PlugParamValue value)
break;
case kWaveform:
m_waveForm = ParamToWaveform(value);
if(m_waveForm >= kNumWaveforms)
m_waveForm = static_cast<LFOWaveform>(kNumWaveforms - 1);
break;
case kPolarity: m_polarity = (value >= 0.5f); break;
case kBypassed: m_bypassed = (value >= 0.5f); break;

View File

@ -144,7 +144,7 @@ protected:
IMixPlugin *GetOutputPlugin() const;
public:
static LFOWaveform ParamToWaveform(float param) { return static_cast<LFOWaveform>(mpt::saturate_round<int>(param * 32.0f)); }
static LFOWaveform ParamToWaveform(float param) { return static_cast<LFOWaveform>(std::clamp(mpt::saturate_round<int>(param * 32.0f), 0, kNumWaveforms - 1)); }
static float WaveformToParam(LFOWaveform waveform) { return static_cast<int>(waveform) / 32.0f; }
};

View File

@ -27,7 +27,7 @@ namespace DMO
// Computes (log2(x) + 1) * 2 ^ (shiftL - shiftR) (x = -2^31...2^31)
float logGain(float x, int32 shiftL, int32 shiftR)
{
uint32 intSample = static_cast<uint32>(static_cast<int32>(x));
uint32 intSample = static_cast<uint32>(static_cast<int64>(x));
const uint32 sign = intSample & 0x80000000;
if(sign)
intSample = (~intSample) + 1;