Updated libopenmpt to version 0.5.12
This commit is contained in:
parent
ee6faec757
commit
3aa2e3149f
40 changed files with 461 additions and 298 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 << ")";
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 ®ion : 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 ®ion = pDlsIns->Regions[pDlsIns->nRegions];
|
||||
DLSREGION ®ion = 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 ®ion = pDlsIns->Regions[pDlsIns->nRegions];
|
||||
DLSREGION ®ion = 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 ®ion = pDlsIns->Regions[pDlsIns->nRegions];
|
||||
DLSREGION ®ion = 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())
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue