Updated libopenmpt to version 0.3.8.
parent
df847cdd05
commit
baace3fea9
|
@ -473,7 +473,7 @@
|
|||
#endif // arch
|
||||
#endif // ENABLE_ASM
|
||||
|
||||
#if defined(MPT_WITH_MPG123) && defined(MPT_BUILD_MSVC) && defined(MPT_BUILD_MSVC_STATIC) && !MPT_OS_WINDOWS_WINRT
|
||||
#if defined(MPT_WITH_MPG123) && defined(MPT_BUILD_MSVC) && defined(MPT_BUILD_MSVC_STATIC) && !defined(LIBOPENMPT_NODELAYLOAD) && !MPT_OS_WINDOWS_WINRT
|
||||
#define MPT_ENABLE_MPG123_DELAYLOAD
|
||||
#endif
|
||||
|
||||
|
|
|
@ -349,8 +349,9 @@ public:
|
|||
}
|
||||
public:
|
||||
PinnedRawDataView()
|
||||
: size_(0)
|
||||
, pinnedData(nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
PinnedRawDataView(const FileReader &file)
|
||||
{
|
||||
|
|
|
@ -559,25 +559,33 @@ private:
|
|||
|
||||
static mpt::ustring From8bit(const std::string &str)
|
||||
{
|
||||
if(charset == mpt::CharsetUTF8)
|
||||
{
|
||||
return mpt::ToUnicode(mpt::CharsetUTF8, str);
|
||||
}
|
||||
// auto utf8 detection
|
||||
if(tryUTF8 && mpt::IsUTF8(str))
|
||||
MPT_CONSTANT_IF(charset == mpt::CharsetUTF8)
|
||||
{
|
||||
return mpt::ToUnicode(mpt::CharsetUTF8, str);
|
||||
} else
|
||||
{
|
||||
return mpt::ToUnicode(charset, str);
|
||||
// auto utf8 detection
|
||||
MPT_CONSTANT_IF(tryUTF8)
|
||||
{
|
||||
if(mpt::IsUTF8(str))
|
||||
{
|
||||
return mpt::ToUnicode(mpt::CharsetUTF8, str);
|
||||
} else
|
||||
{
|
||||
return mpt::ToUnicode(charset, str);
|
||||
}
|
||||
} else
|
||||
{
|
||||
return mpt::ToUnicode(charset, str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// 8 bit
|
||||
BasicAnyString(const char *str) : mpt::ustring(str ? mpt::ToUnicode(charset, str) : mpt::ustring()) { }
|
||||
BasicAnyString(const std::string str) : mpt::ustring(mpt::ToUnicode(charset, str)) { }
|
||||
BasicAnyString(const char *str) : mpt::ustring(From8bit(str ? str : std::string())) { }
|
||||
BasicAnyString(const std::string str) : mpt::ustring(From8bit(str)) { }
|
||||
|
||||
// unicode
|
||||
BasicAnyString(const mpt::ustring &str) : mpt::ustring(str) { }
|
||||
|
|
|
@ -18,8 +18,8 @@ OPENMPT_NAMESPACE_BEGIN
|
|||
//Version definitions. The only thing that needs to be changed when changing version number.
|
||||
#define VER_MAJORMAJOR 1
|
||||
#define VER_MAJOR 27
|
||||
#define VER_MINOR 04
|
||||
#define VER_MINORMINOR 02
|
||||
#define VER_MINOR 07
|
||||
#define VER_MINORMINOR 00
|
||||
|
||||
//Version string. For example "1.17.02.28"
|
||||
#define MPT_VERSION_STR VER_STRINGIZE(VER_MAJORMAJOR) "." VER_STRINGIZE(VER_MAJOR) "." VER_STRINGIZE(VER_MINOR) "." VER_STRINGIZE(VER_MINORMINOR)
|
||||
|
|
|
@ -4,6 +4,7 @@ The following changes have been made:
|
|||
- mp3_create() declaration has been fixed.
|
||||
- GET_DATA() has been rewritten to avoid unaligned access warnings.
|
||||
- Signed/unsigned comparison warnings have been fixed.
|
||||
- Detection of stdint types has been fixed to work on *BSD.
|
||||
- Modifications have been marked with // OpenMPT
|
||||
- Obviously, unnecessary folders and files have been removed.
|
||||
- For building, premake is used to generate Visual Studio project files.
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
#endif
|
||||
#include <math.h>
|
||||
|
||||
#ifndef __int8_t_defined
|
||||
#if !defined(__int8_t_defined) && !defined(_INT8_T_DECLARED) /* OpenMPT */
|
||||
#define __int8_t_defined
|
||||
typedef unsigned char uint8_t;
|
||||
typedef signed char int8_t;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
This folder contains the stb_vorbis library from
|
||||
https://github.com/nothings/stb/blob/master/stb_vorbis.c
|
||||
(commit 9d9f75eb682dd98b34de08bb5c489c6c561c9fa6)
|
||||
This folder contains the stb_vorbis library from
|
||||
https://github.com/nothings/stb/blob/master/stb_vorbis.c v1.14
|
||||
commit e6afb9cbae4064da8c3e69af3ff5c4629579c1d2 (2018-02-11)
|
||||
|
||||
Modifications:
|
||||
* Use of alloca has been replaced with malloc, as alloca is not in C99 and
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// Ogg Vorbis audio decoder - v1.11 - public domain
|
||||
// Ogg Vorbis audio decoder - v1.14 - public domain
|
||||
// http://nothings.org/stb_vorbis/
|
||||
//
|
||||
// Original version written by Sean Barrett in 2007.
|
||||
//
|
||||
// Originally sponsored by RAD Game Tools. Seeking sponsored
|
||||
// by Phillip Bennefall, Marc Andersen, Aaron Baker, Elias Software,
|
||||
// Aras Pranckevicius, and Sean Barrett.
|
||||
// Originally sponsored by RAD Game Tools. Seeking implementation
|
||||
// sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker,
|
||||
// Elias Software, Aras Pranckevicius, and Sean Barrett.
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
|
@ -30,22 +30,26 @@
|
|||
// Tom Beaumont Ingo Leitgeb Nicolas Guillemot
|
||||
// Phillip Bennefall Rohit Thiago Goulart
|
||||
// manxorist@github saga musix github:infatum
|
||||
// Timur Gagiev
|
||||
//
|
||||
// Partial history:
|
||||
// 1.11 - 2017/07/23 - fix MinGW compilation
|
||||
// 1.10 - 2017/03/03 - more robust seeking; fix negative ilog(); clear error in open_memory
|
||||
// 1.09 - 2016/04/04 - back out 'truncation of last frame' fix from previous version
|
||||
// 1.08 - 2016/04/02 - warnings; setup memory leaks; truncation of last frame
|
||||
// 1.07 - 2015/01/16 - fixes for crashes on invalid files; warning fixes; const
|
||||
// 1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson)
|
||||
// 1.14 - 2018-02-11 - delete bogus dealloca usage
|
||||
// 1.13 - 2018-01-29 - fix truncation of last frame (hopefully)
|
||||
// 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files
|
||||
// 1.11 - 2017-07-23 - fix MinGW compilation
|
||||
// 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory
|
||||
// 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version
|
||||
// 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame
|
||||
// 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const
|
||||
// 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson)
|
||||
// some crash fixes when out of memory or with corrupt files
|
||||
// fix some inappropriately signed shifts
|
||||
// 1.05 - 2015/04/19 - don't define __forceinline if it's redundant
|
||||
// 1.04 - 2014/08/27 - fix missing const-correct case in API
|
||||
// 1.03 - 2014/08/07 - warning fixes
|
||||
// 1.02 - 2014/07/09 - declare qsort comparison as explicitly _cdecl in Windows
|
||||
// 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float (interleaved was correct)
|
||||
// 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in >2-channel;
|
||||
// 1.05 - 2015-04-19 - don't define __forceinline if it's redundant
|
||||
// 1.04 - 2014-08-27 - fix missing const-correct case in API
|
||||
// 1.03 - 2014-08-07 - warning fixes
|
||||
// 1.02 - 2014-07-09 - declare qsort comparison as explicitly _cdecl in Windows
|
||||
// 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float (interleaved was correct)
|
||||
// 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in >2-channel;
|
||||
// (API change) report sample rate for decode-full-file funcs
|
||||
//
|
||||
// See end of file for full version history.
|
||||
|
@ -885,11 +889,7 @@ static int error(vorb *f, enum STBVorbisError e)
|
|||
|
||||
#if 0 // OpenMPT
|
||||
#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size))
|
||||
#ifdef dealloca
|
||||
#define temp_free(f,p) (f->alloc.alloc_buffer ? 0 : dealloca(size))
|
||||
#else
|
||||
#define temp_free(f,p) 0
|
||||
#endif
|
||||
#else // OpenMPT
|
||||
#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : malloc(size)) // OpenMPT
|
||||
#define temp_free(f,p) (f->alloc.alloc_buffer ? 0 : free(p)) // OpenMPT
|
||||
|
@ -2051,6 +2051,8 @@ static int residue_decode(vorb *f, Codebook *book, float *target, int offset, in
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
// n is 1/2 of the blocksize --
|
||||
// specification: "Correct per-vector decode length is [n]/2"
|
||||
static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode)
|
||||
{
|
||||
int i,j,pass;
|
||||
|
@ -2058,7 +2060,10 @@ static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int
|
|||
int rtype = f->residue_types[rn];
|
||||
int c = r->classbook;
|
||||
int classwords = f->codebooks[c].dimensions;
|
||||
int n_read = r->end - r->begin;
|
||||
unsigned int actual_size = rtype == 2 ? n*2 : n;
|
||||
unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size);
|
||||
unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size);
|
||||
int n_read = limit_r_end - limit_r_begin;
|
||||
int part_read = n_read / r->part_size;
|
||||
int temp_alloc_point = temp_alloc_save(f);
|
||||
#ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
|
||||
|
@ -3400,7 +3405,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
|
|||
if (f->last_seg_which == f->end_seg_with_known_loc) {
|
||||
// if we have a valid current loc, and this is final:
|
||||
if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) {
|
||||
uint32 current_end = f->known_loc_for_packet - (n-right_end);
|
||||
uint32 current_end = f->known_loc_for_packet;
|
||||
// then let's infer the size of the (probably) short final frame
|
||||
if (current_end < f->current_loc + (right_end-left_start)) {
|
||||
if (current_end < f->current_loc) {
|
||||
|
@ -3409,7 +3414,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
|
|||
} else {
|
||||
*len = current_end - f->current_loc;
|
||||
}
|
||||
*len += left_start;
|
||||
*len += left_start; // this doesn't seem right, but has no ill effect on my test files
|
||||
if (*len > right_end) *len = right_end; // this should never happen
|
||||
f->current_loc += *len;
|
||||
return TRUE;
|
||||
|
@ -4064,6 +4069,7 @@ static int start_decoder(vorb *f)
|
|||
f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2);
|
||||
f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist);
|
||||
if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem);
|
||||
memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1);
|
||||
#ifdef STB_VORBIS_NO_DEFER_FLOOR
|
||||
f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2);
|
||||
if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem);
|
||||
|
@ -4091,7 +4097,10 @@ static int start_decoder(vorb *f)
|
|||
int i,max_part_read=0;
|
||||
for (i=0; i < f->residue_count; ++i) {
|
||||
Residue *r = f->residue_config + i;
|
||||
int n_read = r->end - r->begin;
|
||||
unsigned int actual_size = f->blocksize_1 / 2;
|
||||
unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size;
|
||||
unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size;
|
||||
int n_read = limit_r_end - limit_r_begin;
|
||||
int part_read = n_read / r->part_size;
|
||||
if (part_read > max_part_read)
|
||||
max_part_read = part_read;
|
||||
|
@ -4102,6 +4111,8 @@ static int start_decoder(vorb *f)
|
|||
classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *));
|
||||
#endif
|
||||
|
||||
// maximum reasonable partition size is f->blocksize_1
|
||||
|
||||
f->temp_memory_required = classify_mem;
|
||||
if (imdct_mem > f->temp_memory_required)
|
||||
f->temp_memory_required = imdct_mem;
|
||||
|
@ -5365,20 +5376,22 @@ int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, in
|
|||
#endif // STB_VORBIS_NO_PULLDATA_API
|
||||
|
||||
/* Version history
|
||||
1.10 - 2017/03/03 - more robust seeking; fix negative ilog(); clear error in open_memory
|
||||
1.09 - 2016/04/04 - back out 'avoid discarding last frame' fix from previous version
|
||||
1.08 - 2016/04/02 - fixed multiple warnings; fix setup memory leaks;
|
||||
1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files
|
||||
1.11 - 2017-07-23 - fix MinGW compilation
|
||||
1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory
|
||||
1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version
|
||||
1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks;
|
||||
avoid discarding last frame of audio data
|
||||
1.07 - 2015/01/16 - fixed some warnings, fix mingw, const-correct API
|
||||
1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API
|
||||
some more crash fixes when out of memory or with corrupt files
|
||||
1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson)
|
||||
1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson)
|
||||
some crash fixes when out of memory or with corrupt files
|
||||
1.05 - 2015/04/19 - don't define __forceinline if it's redundant
|
||||
1.04 - 2014/08/27 - fix missing const-correct case in API
|
||||
1.03 - 2014/08/07 - Warning fixes
|
||||
1.02 - 2014/07/09 - Declare qsort compare function _cdecl on windows
|
||||
1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float
|
||||
1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in multichannel
|
||||
1.05 - 2015-04-19 - don't define __forceinline if it's redundant
|
||||
1.04 - 2014-08-27 - fix missing const-correct case in API
|
||||
1.03 - 2014-08-07 - Warning fixes
|
||||
1.02 - 2014-07-09 - Declare qsort compare function _cdecl on windows
|
||||
1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float
|
||||
1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in multichannel
|
||||
(API change) report sample rate for decode-full-file funcs
|
||||
0.99996 - bracket #include <malloc.h> for macintosh compilation by Laurent Gomila
|
||||
0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem
|
||||
|
|
|
@ -5,6 +5,39 @@ Changelog {#changelog}
|
|||
For fully detailed change log, please see the source repository directly. This
|
||||
is just a high-level summary.
|
||||
|
||||
### libopenmpt 0.3.8 (2018-04-08)
|
||||
|
||||
* [**Sec**] Possible out-of-bounds memory read with IT / ITP / MO3 files
|
||||
containing pattern loops (r10028).
|
||||
|
||||
* Keep track of active SFx macro during seeking.
|
||||
* The "note cut" duplicate note action did not volume-ramp the previously
|
||||
playing sample.
|
||||
* A song starting with non-existing patterns could not be played.
|
||||
* DSM: Support restart position and 16-bit samples.
|
||||
* DTM: Import global volume.
|
||||
|
||||
### libopenmpt 0.3.7 (2018-03-11)
|
||||
|
||||
* [**Bug**] libopenmpt did not build with NDK r13b on armeabi due to missing
|
||||
`-latomic`.
|
||||
* [**Bug**] xmp-openmpt: Sample rate and number of output channels were not
|
||||
applied correctly when using per-file settings.
|
||||
|
||||
* [**Change**] foo_openmpt: foo_openmpt is now packaged as a fb2k-component
|
||||
package for easier installation.
|
||||
|
||||
* IT: More accurate song length calculation for pattern loops that have no
|
||||
start command and are following another pattern loop.
|
||||
* IMF: Filter cutoff was upside down and the cutoff range was too small.
|
||||
* MED: Correctly import patterns with less channels than the maximum used
|
||||
amount. Import "STP" note stop command.
|
||||
* DBM: Key Off and Set Envelope Position were imported incorrectly.
|
||||
High sample offset (E7x) is now supported.
|
||||
* DIGI / DBM: Arpeggio should not return to base note at end of row.
|
||||
* Some filter changes through MIDI macros were not applied if the note volume
|
||||
was set to 0 on the same row.
|
||||
|
||||
### libopenmpt 0.3.6 (2018-02-03)
|
||||
|
||||
* [**Sec**] Possible out-of-bounds memory read with malformed STP files.
|
||||
|
|
|
@ -38,6 +38,7 @@ Dependencies
|
|||
particular:
|
||||
* `char` can be `signed` or `unsigned`
|
||||
* shifting signed values is implementation defined
|
||||
* `signed` integer overflow is undefined
|
||||
* `float` and `double` can be non-IEEE754
|
||||
|
||||
* Required compilers to use libopenmpt:
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
/*! \brief libopenmpt minor version number */
|
||||
#define OPENMPT_API_VERSION_MINOR 3
|
||||
/*! \brief libopenmpt patch version number */
|
||||
#define OPENMPT_API_VERSION_PATCH 6
|
||||
#define OPENMPT_API_VERSION_PATCH 8
|
||||
/*! \brief libopenmpt pre-release tag */
|
||||
#define OPENMPT_API_VERSION_PREREL ""
|
||||
/*! \brief libopenmpt pre-release flag */
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
LIBOPENMPT_VERSION_MAJOR=0
|
||||
LIBOPENMPT_VERSION_MINOR=3
|
||||
LIBOPENMPT_VERSION_PATCH=6
|
||||
LIBOPENMPT_VERSION_PATCH=8
|
||||
LIBOPENMPT_VERSION_PREREL=
|
||||
|
||||
LIBOPENMPT_LTVER_CURRENT=1
|
||||
LIBOPENMPT_LTVER_REVISION=6
|
||||
LIBOPENMPT_LTVER_REVISION=8
|
||||
LIBOPENMPT_LTVER_AGE=1
|
||||
|
|
|
@ -163,6 +163,7 @@ struct self_xmplay_t {
|
|||
std::size_t num_channels;
|
||||
xmp_openmpt_settings settings;
|
||||
openmpt::module_ext * mod;
|
||||
bool set_format_called;
|
||||
openmpt::ext::pattern_vis * pattern_vis;
|
||||
std::int32_t tempo_factor, pitch_factor;
|
||||
bool single_subsong_mode;
|
||||
|
@ -171,6 +172,7 @@ struct self_xmplay_t {
|
|||
, num_channels(2)
|
||||
, settings()
|
||||
, mod(0)
|
||||
, set_format_called(false)
|
||||
, pattern_vis(0)
|
||||
, tempo_factor(0)
|
||||
, pitch_factor(0)
|
||||
|
@ -179,11 +181,13 @@ struct self_xmplay_t {
|
|||
settings.changed = apply_and_save_options;
|
||||
}
|
||||
void on_new_mod() {
|
||||
set_format_called = false;
|
||||
self->pattern_vis = static_cast<openmpt::ext::pattern_vis *>( self->mod->get_interface( openmpt::ext::pattern_vis_id ) );
|
||||
}
|
||||
void delete_mod() {
|
||||
if ( mod ) {
|
||||
pattern_vis = 0;
|
||||
set_format_called = false;
|
||||
delete mod;
|
||||
mod = 0;
|
||||
}
|
||||
|
@ -310,6 +314,12 @@ static void save_settings_to_xml( std::string & xml, const libopenmpt::plugin::s
|
|||
|
||||
static void apply_options() {
|
||||
if ( self->mod ) {
|
||||
if ( !self->set_format_called ) {
|
||||
// SetFormat will only be called once after loading a file.
|
||||
// We cannot apply samplerate or numchannels changes afterwards during playback.
|
||||
self->samplerate = self->settings.samplerate;
|
||||
self->num_channels = self->settings.channels;
|
||||
}
|
||||
self->mod->set_repeat_count( self->settings.repeatcount );
|
||||
self->mod->set_render_param( openmpt::module::RENDER_MASTERGAIN_MILLIBEL, self->settings.mastergain_millibel );
|
||||
self->mod->set_render_param( openmpt::module::RENDER_STEREOSEPARATION_PERCENT, self->settings.stereoseparation );
|
||||
|
@ -893,8 +903,6 @@ static DWORD WINAPI openmpt_Open( const char * filename, XMPFILE file ) {
|
|||
clear_current_timeinfo();
|
||||
reset_timeinfos();
|
||||
apply_options();
|
||||
self->samplerate = self->settings.samplerate;
|
||||
self->num_channels = self->settings.channels;
|
||||
|
||||
std::int32_t num_subsongs = self->mod->get_num_subsongs();
|
||||
self->subsong_lengths.resize( num_subsongs );
|
||||
|
@ -926,6 +934,9 @@ static void WINAPI openmpt_SetFormat( XMPFORMAT * form ) {
|
|||
if ( !form ) {
|
||||
return;
|
||||
}
|
||||
// SetFormat will only be called once after loading a file.
|
||||
// We cannot apply samplerate or numchannels changes afterwards during playback.
|
||||
self->set_format_called = true;
|
||||
if ( !self->mod ) {
|
||||
form->rate = 0;
|
||||
form->chan = 0;
|
||||
|
|
|
@ -675,12 +675,7 @@ struct Convert<int64, float32>
|
|||
{
|
||||
Limit(val, -1.0f, 1.0f);
|
||||
val *= static_cast<float>(uint64(1)<<63);
|
||||
#if MPT_SC_AVOID_FLOOR
|
||||
// MSVC with x87 floating point math calls floor for the more intuitive version
|
||||
return mpt::saturate_cast<int64>(MPT_SC_RSHIFT_SIGNED(static_cast<int64>(val * 2.0f + 1.0f), 1));
|
||||
#else
|
||||
return mpt::saturate_cast<int32>(static_cast<int64>(std::floor(val + 0.5f)));
|
||||
#endif
|
||||
return mpt::saturate_cast<int64>(std::floor(val + 0.5f));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -693,12 +688,7 @@ struct Convert<int64, double>
|
|||
{
|
||||
Limit(val, -1.0, 1.0);
|
||||
val *= static_cast<double>(uint64(1)<<63);
|
||||
#if MPT_SC_AVOID_FLOOR
|
||||
// MSVC with x87 floating point math calls floor for the more intuitive version
|
||||
return mpt::saturate_cast<int64>(MPT_SC_RSHIFT_SIGNED(static_cast<int64>(val * 2.0 + 1.0), 1));
|
||||
#else
|
||||
return mpt::saturate_cast<int32>(static_cast<int64>(std::floor(val + 0.5)));
|
||||
#endif
|
||||
return mpt::saturate_cast<int64>(std::floor(val + 0.5));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -446,7 +446,7 @@ static uint8 DLSSustainLevelToLinear(int32 sustain)
|
|||
if(sustain >= 0)
|
||||
{
|
||||
int32 l = sustain / (1000 * 512);
|
||||
if(l >= 0 || l <= 128)
|
||||
if(l >= 0 && l <= 128)
|
||||
return static_cast<uint8>(l);
|
||||
}
|
||||
return 128;
|
||||
|
@ -1885,6 +1885,8 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui
|
|||
int32 lSusLevel = - DLS32BitRelativeLinearToGain(lStartFactor << 10) / 65536;
|
||||
int32 lDecayEndTime = (lReleaseTime * lSusLevel) / 960;
|
||||
lReleaseTime -= lDecayEndTime;
|
||||
if(pIns->VolEnv.nSustainEnd > 0)
|
||||
pIns->VolEnv.nReleaseNode = pIns->VolEnv.nSustainEnd;
|
||||
for (uint32 i=0; i<5; i++)
|
||||
{
|
||||
int32 lFactor = 1 + ((lStartFactor * 3) >> (i+2));
|
||||
|
@ -1930,6 +1932,7 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui
|
|||
pIns->VolEnv[3] = EnvelopeNode(pIns->VolEnv[2].tick * 2u, ENVELOPE_MIN); // 1 second max. for drums
|
||||
}
|
||||
}
|
||||
pIns->Convert(MOD_TYPE_MPT, sndFile.GetType());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -133,8 +133,8 @@ static const ModCommand::COMMAND dbmEffects[] =
|
|||
CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL, CMD_TREMOLO,
|
||||
CMD_PANNING8, CMD_OFFSET, CMD_VOLUMESLIDE, CMD_POSITIONJUMP,
|
||||
CMD_VOLUME, CMD_PATTERNBREAK, CMD_MODCMDEX, CMD_TEMPO,
|
||||
CMD_GLOBALVOLUME, CMD_GLOBALVOLSLIDE, CMD_KEYOFF, CMD_SETENVPOSITION,
|
||||
CMD_CHANNELVOLUME, CMD_CHANNELVOLSLIDE, CMD_NONE, CMD_NONE,
|
||||
CMD_GLOBALVOLUME, CMD_GLOBALVOLSLIDE, CMD_NONE, CMD_NONE,
|
||||
CMD_KEYOFF, CMD_SETENVPOSITION, CMD_NONE, CMD_NONE,
|
||||
CMD_NONE, CMD_PANNINGSLIDE, CMD_NONE, CMD_NONE,
|
||||
CMD_NONE, CMD_NONE, CMD_NONE,
|
||||
#ifndef NO_PLUGINS
|
||||
|
@ -215,11 +215,11 @@ static void ConvertDBMEffect(uint8 &command, uint8 ¶m)
|
|||
param = (param == 0x50) ? 0x00 : 0x40;
|
||||
}
|
||||
break;
|
||||
case 0x60: // set loop begin / loop
|
||||
// TODO
|
||||
case 0x60: // Pattern loop
|
||||
break;
|
||||
case 0x70: // set offset
|
||||
// TODO
|
||||
case 0x70: // Coarse offset
|
||||
command = CMD_S3MCMDEX;
|
||||
param = 0xA0 | (param & 0x0F);
|
||||
break;
|
||||
default:
|
||||
// Rest will be converted later from CMD_MODCMDEX to CMD_S3MCMDEX.
|
||||
|
@ -355,6 +355,7 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
m_madeWithTracker = mpt::format(MPT_USTRING("DigiBooster Pro %1.%2"))(mpt::ufmt::hex(fileHeader.trkVerHi), mpt::ufmt::hex(fileHeader.trkVerLo));
|
||||
m_playBehaviour.set(kSlidesAtSpeed1);
|
||||
m_playBehaviour.reset(kITVibratoTremoloPanbrello);
|
||||
m_playBehaviour.reset(kITArpeggio);
|
||||
|
||||
// Name chunk
|
||||
FileReader nameChunk = chunks.GetChunk(DBMChunk::idNAME);
|
||||
|
@ -493,7 +494,7 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
continue;
|
||||
}
|
||||
|
||||
ModCommand dummy;
|
||||
ModCommand dummy = ModCommand::Empty();
|
||||
ModCommand &m = ch <= GetNumChannels() ? patRow[ch - 1] : dummy;
|
||||
|
||||
const uint8 b = chunk.ReadUint8();
|
||||
|
@ -668,7 +669,8 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
{
|
||||
ModSample &srcSample = Samples[0];
|
||||
const int8 *smpData = srcSample.pSample8;
|
||||
uint32 predelay = Util::muldiv_unsigned(20116, srcSample.nC5Speed, 100000);
|
||||
SmpLength predelay = Util::muldiv_unsigned(20116, srcSample.nC5Speed, 100000);
|
||||
LimitMax(predelay, srcSample.nLength);
|
||||
smpData += predelay * srcSample.GetBytesPerSample();
|
||||
srcSample.nLength -= predelay;
|
||||
|
||||
|
@ -683,7 +685,8 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
memcpy(sample.pSample, smpData, sample.GetSampleSizeInBytes());
|
||||
smpData += sample.GetSampleSizeInBytes();
|
||||
srcSample.nLength -= sample.nLength;
|
||||
uint32 gap = Util::muldiv_unsigned(454, srcSample.nC5Speed, 10000);
|
||||
SmpLength gap = Util::muldiv_unsigned(454, srcSample.nC5Speed, 10000);
|
||||
LimitMax(gap, srcSample.nLength);
|
||||
smpData += gap * srcSample.GetBytesPerSample();
|
||||
srcSample.nLength -= gap;
|
||||
}
|
||||
|
|
|
@ -6,11 +6,13 @@
|
|||
* MilkyTracker can load it, but the only files of this format seen in the wild are also
|
||||
* available in their original format, so I did not bother implementing it so far.
|
||||
*
|
||||
* 2. Using PLAY.EXE v1.02, commands not supported in MOD do not seem to do anything at all.
|
||||
* 2. Using both PLAY.EXE v1.02 and v2.00, commands not supported in MOD do not seem to do
|
||||
* anything at all.
|
||||
* In particular commands 0x11-0x13 handled below are ignored, and no files have been spotted
|
||||
* in the wild using any commands > 0x0F at all.
|
||||
* S3M-style retrigger does not seem to exist - it is translated to volume slides by CONV.EXE,
|
||||
* and J00 in S3M files is not converted either.
|
||||
* and J00 in S3M files is not converted either. S3M pattern loops (SBx) are not converted
|
||||
* properly by CONV.EXE and completely ignored by PLAY.EXE.
|
||||
* Command 8 (set panning) uses 00-80 for regular panning and A4 for surround, probably
|
||||
* making DSIK one of the first applications to use this particular encoding scheme still
|
||||
* used in "extended" S3Ms today.
|
||||
|
@ -36,9 +38,10 @@ MPT_BINARY_STRUCT(DSMChunk, 8)
|
|||
struct DSMSongHeader
|
||||
{
|
||||
char songName[28];
|
||||
char reserved1[2];
|
||||
uint16le fileVersion;
|
||||
uint16le flags;
|
||||
char reserved2[4];
|
||||
uint16le orderPos;
|
||||
uint16le restartPos;
|
||||
uint16le numOrders;
|
||||
uint16le numSamples;
|
||||
uint16le numPatterns;
|
||||
|
@ -57,13 +60,12 @@ MPT_BINARY_STRUCT(DSMSongHeader, 192)
|
|||
struct DSMSampleHeader
|
||||
{
|
||||
char filename[13];
|
||||
uint8le flags;
|
||||
char reserved1;
|
||||
uint16le flags;
|
||||
uint8le volume;
|
||||
uint32le length;
|
||||
uint32le loopStart;
|
||||
uint32le loopEnd;
|
||||
char reserved2[4];
|
||||
uint32le dataPtr; // Interal sample pointer during playback in DSIK
|
||||
uint32le sampleRate;
|
||||
char sampleName[28];
|
||||
|
||||
|
@ -93,6 +95,8 @@ struct DSMSampleHeader
|
|||
sampleIO |= SampleIO::deltaPCM; // fairlight.dsm by Comrade J
|
||||
else if(flags & 0x02)
|
||||
sampleIO |= SampleIO::signedPCM;
|
||||
if(flags & 0x04)
|
||||
sampleIO |= SampleIO::_16bit;
|
||||
return sampleIO;
|
||||
}
|
||||
};
|
||||
|
@ -197,10 +201,14 @@ bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
|
||||
DSMSongHeader songHeader;
|
||||
file.ReadStructPartial(songHeader, chunkHeader.size);
|
||||
if(songHeader.numOrders > 128 || songHeader.numChannels > 16 || songHeader.numPatterns > 256 || songHeader.restartPos > 128)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
InitializeGlobals(MOD_TYPE_DSM);
|
||||
mpt::String::Read<mpt::String::maybeNullTerminated>(m_songName, songHeader.songName);
|
||||
m_nChannels = Clamp<uint16, uint16>(songHeader.numChannels, 1, 16);
|
||||
m_nChannels = std::max<uint16>(songHeader.numChannels, 1);
|
||||
m_nDefaultSpeed = songHeader.speed;
|
||||
m_nDefaultTempo.Set(songHeader.bpm);
|
||||
m_nDefaultGlobalVolume = std::min<uint8>(songHeader.globalVol, 64) * 4u;
|
||||
|
@ -224,6 +232,8 @@ bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
}
|
||||
|
||||
ReadOrderFromArray(Order(), songHeader.orders, songHeader.numOrders, 0xFF, 0xFE);
|
||||
if(songHeader.restartPos < songHeader.numOrders)
|
||||
Order().SetRestartPos(songHeader.restartPos);
|
||||
|
||||
// Read pattern and sample chunks
|
||||
PATTERNINDEX patNum = 0;
|
||||
|
@ -240,6 +250,7 @@ bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
}
|
||||
chunk.Skip(2);
|
||||
|
||||
ModCommand dummy = ModCommand::Empty();
|
||||
ROWINDEX row = 0;
|
||||
PatternRow rowBase = Patterns[patNum].GetRow(0);
|
||||
while(chunk.CanRead(1) && row < 64)
|
||||
|
@ -253,7 +264,6 @@ bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
}
|
||||
|
||||
CHANNELINDEX chn = (flag & 0x0F);
|
||||
ModCommand dummy = ModCommand();
|
||||
ModCommand &m = (chn < GetNumChannels() ? rowBase[chn] : dummy);
|
||||
|
||||
if(flag & 0x80)
|
||||
|
|
|
@ -102,6 +102,7 @@ struct DTMSample
|
|||
if(formatVersion == DTM_206_PATTERN_FORMAT && transpose > 0 && transpose != 48)
|
||||
{
|
||||
// Digital Home Studio applies this unconditionally, but some old songs sound wrong then (delirium.dtm).
|
||||
// Digital Tracker 2.03 ignores the setting.
|
||||
// Maybe this should not be applied for "real" Digital Tracker modules?
|
||||
transposeAmount += (48 - transpose) * 128;
|
||||
}
|
||||
|
@ -292,7 +293,13 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
ChnSettings[chn].nPan = static_cast<uint16>(128 + Util::muldivr(std::min<int>(panning[chn], 180) - 90, 128, 90));
|
||||
}
|
||||
|
||||
chunk.Skip(146);
|
||||
chunk.Skip(16);
|
||||
// Chunk ends here for old DTM modules
|
||||
if(chunk.CanRead(2))
|
||||
{
|
||||
m_nDefaultGlobalVolume = std::min<uint32>(chunk.ReadUint16BE(), MAX_GLOBAL_VOLUME);
|
||||
}
|
||||
chunk.Skip(128);
|
||||
uint16be volume[32];
|
||||
if(chunk.ReadArray(volume))
|
||||
{
|
||||
|
@ -356,9 +363,10 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
chunk.ReadStruct(instr);
|
||||
if(instr.insNum < GetNumInstruments())
|
||||
{
|
||||
Samples[instr.insNum + 1].nVibDepth = instr.vibDepth;
|
||||
Samples[instr.insNum + 1].nVibRate = instr.vibRate;
|
||||
Samples[instr.insNum + 1].nVibSweep = 255;
|
||||
ModSample &sample = Samples[instr.insNum + 1];
|
||||
sample.nVibDepth = instr.vibDepth;
|
||||
sample.nVibRate = instr.vibRate;
|
||||
sample.nVibSweep = 255;
|
||||
|
||||
ModInstrument *mptIns = AllocateInstrument(instr.insNum + 1, instr.insNum + 1);
|
||||
if(mptIns != nullptr)
|
||||
|
@ -463,6 +471,8 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
#ifdef MODPLUG_TRACKER
|
||||
m->Convert(MOD_TYPE_MOD, MOD_TYPE_IT, *this);
|
||||
#endif
|
||||
// G is 8-bit volume
|
||||
// P is tremor (need to disable oldfx)
|
||||
}
|
||||
if(data[5] & 0x80)
|
||||
tick += (data[5] & 0x7F) * 0x100 + rowChunk.ReadUint8();
|
||||
|
@ -562,12 +572,12 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
// Read sample data
|
||||
for(auto &chunk : chunks.GetAllChunks(DTMChunk::idDAIT))
|
||||
{
|
||||
PATTERNINDEX smp = chunk.ReadUint16BE() + 1;
|
||||
if(smp == 0 || smp > GetNumSamples() || !(loadFlags & loadSampleData))
|
||||
SAMPLEINDEX smp = chunk.ReadUint16BE();
|
||||
if(smp >= GetNumSamples() || !(loadFlags & loadSampleData))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ModSample &mptSmp = Samples[smp];
|
||||
ModSample &mptSmp = Samples[smp + 1];
|
||||
SampleIO(
|
||||
mptSmp.uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit,
|
||||
mptSmp.uFlags[CHN_STEREO] ? SampleIO::stereoInterleaved: SampleIO::mono,
|
||||
|
|
|
@ -138,9 +138,9 @@ struct IMFInstrument
|
|||
if(mptIns.PitchEnv.dwFlags[ENV_ENABLED])
|
||||
mptIns.PitchEnv.dwFlags.set(ENV_FILTER);
|
||||
|
||||
// hack to get === to stop notes (from modplug's xm loader)
|
||||
// hack to get === to stop notes
|
||||
if(!mptIns.VolEnv.dwFlags[ENV_ENABLED] && !mptIns.nFadeOut)
|
||||
mptIns.nFadeOut = 8192;
|
||||
mptIns.nFadeOut = 32767;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -284,7 +284,7 @@ static void ImportIMFEffect(ModCommand &m)
|
|||
m.param |= 0xE0;
|
||||
break;
|
||||
case 0x16: // cutoff
|
||||
m.param >>= 1;
|
||||
m.param = (0xFF - m.param) / 2u;
|
||||
break;
|
||||
case 0x1F: // set global volume
|
||||
m.param = MIN(m.param << 1, 0xFF);
|
||||
|
@ -361,9 +361,9 @@ static bool ValidateHeader(const IMFFileHeader &fileHeader)
|
|||
return false;
|
||||
}
|
||||
bool channelFound = false;
|
||||
for(uint8 chn = 0; chn < 32; chn++)
|
||||
for(const auto &chn : fileHeader.channels)
|
||||
{
|
||||
switch(fileHeader.channels[chn].status)
|
||||
switch(chn.status)
|
||||
{
|
||||
case 0: // enabled; don't worry about it
|
||||
channelFound = true;
|
||||
|
|
|
@ -1039,7 +1039,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
|
|||
}
|
||||
|
||||
// Now we grab the data for this particular row/channel.
|
||||
ModCommand dummy;
|
||||
ModCommand dummy = ModCommand::Empty();
|
||||
ModCommand &m = ch < m_nChannels ? patData[ch] : dummy;
|
||||
|
||||
if(chnMask[ch] & 0x10)
|
||||
|
@ -1226,7 +1226,10 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
|
|||
m_madeWithTracker = MPT_USTRING("BeRoTracker");
|
||||
break;
|
||||
case 7:
|
||||
m_madeWithTracker = mpt::format(MPT_USTRING("ITMCK %1.%2.%3"))((fileHeader.cwtv >> 8) & 0x0F, (fileHeader.cwtv >> 4) & 0x0F, fileHeader.cwtv & 0x0F);
|
||||
if(fileHeader.cwtv == 0x7FFF && fileHeader.cmwt == 0x0215)
|
||||
m_madeWithTracker = MPT_USTRING("munch.py");
|
||||
else
|
||||
m_madeWithTracker = mpt::format(MPT_USTRING("ITMCK %1.%2.%3"))((fileHeader.cwtv >> 8) & 0x0F, (fileHeader.cwtv >> 4) & 0x0F, fileHeader.cwtv & 0x0F);
|
||||
break;
|
||||
case 0xD:
|
||||
m_madeWithTracker = MPT_USTRING("spc2it");
|
||||
|
|
|
@ -833,12 +833,12 @@ bool CSoundFile::ReadMed(FileReader &file, ModLoadingFlags loadFlags)
|
|||
tracks = pmb->numtracks;
|
||||
if (!tracks) tracks = m_nChannels;
|
||||
if(!Patterns.Insert(iBlk, lines)) continue;
|
||||
auto p = Patterns[iBlk].begin();
|
||||
const uint8 * s = (const uint8 *)(lpStream + dwPos + 2);
|
||||
uint32 maxlen = tracks*lines*3;
|
||||
if (maxlen + dwPos > dwMemLength - 2) break;
|
||||
for (uint32 y=0; y<lines; y++)
|
||||
{
|
||||
ModCommand *p = Patterns[iBlk].GetpModCommand(y, 0);
|
||||
for (uint32 x=0; x<tracks; x++, s+=3) if (x < m_nChannels)
|
||||
{
|
||||
uint8 note = s[0] & 0x3F;
|
||||
|
@ -901,18 +901,21 @@ bool CSoundFile::ReadMed(FileReader &file, ModLoadingFlags loadFlags)
|
|||
const uint8 * s = (const uint8 *)(lpStream + dwPos + 8);
|
||||
uint32 maxlen = tracks*lines*4;
|
||||
if (maxlen + dwPos > dwMemLength - 8 || !Patterns.IsValidPat(iBlk)) break;
|
||||
auto p = Patterns[iBlk].begin();
|
||||
for (uint32 y=0; y<lines; y++)
|
||||
{
|
||||
ModCommand *p = Patterns[iBlk].GetpModCommand(y, 0);
|
||||
for (uint32 x=0; x<tracks; x++, s+=4) if (x < m_nChannels)
|
||||
{
|
||||
uint8 note = s[0];
|
||||
if ((note) && (note <= 132))
|
||||
if(note & 0x7F)
|
||||
{
|
||||
int rnote = note + playtransp;
|
||||
if (rnote < 1) rnote = 1;
|
||||
if (rnote > NOTE_MAX) rnote = NOTE_MAX;
|
||||
p->note = (uint8)rnote;
|
||||
} else if(note == 0x80)
|
||||
{
|
||||
p->note = NOTE_NOTECUT;
|
||||
}
|
||||
p->instr = s[1];
|
||||
p->command = s[2];
|
||||
|
|
|
@ -1083,7 +1083,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
|
|||
chn = 9;
|
||||
else if(chn < 10)
|
||||
chn--;
|
||||
drumChns.set(chn, xg[6] != 0);
|
||||
drumChns.set(chn, xg[7] != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2133,7 +2133,7 @@ bool CSoundFile::ReadPT36(FileReader &file, ModLoadingFlags loadFlags)
|
|||
&& IsInRange(info.dateMinute, 0, 59) && IsInRange(info.dateSecond, 0, 59))
|
||||
{
|
||||
FileHistory mptHistory;
|
||||
MemsetZero(mptHistory.loadDate);
|
||||
MemsetZero(mptHistory);
|
||||
mptHistory.loadDate.tm_year = info.dateYear;
|
||||
mptHistory.loadDate.tm_mon = info.dateMonth - 1;
|
||||
mptHistory.loadDate.tm_mday = info.dateDay;
|
||||
|
|
|
@ -492,7 +492,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
|
|||
}
|
||||
|
||||
CHANNELINDEX channel = (info & s3mChannelMask);
|
||||
ModCommand dummy;
|
||||
ModCommand dummy = ModCommand::Empty();
|
||||
ModCommand &m = (channel < GetNumChannels()) ? rowBase[channel] : dummy;
|
||||
|
||||
if(info & s3mNotePresent)
|
||||
|
|
|
@ -82,10 +82,11 @@ static bool ValidateHeader(const STMFileHeader &fileHeader)
|
|||
// After reviewing all STM files on ModLand and ModArchive, it was found that the
|
||||
// case-insensitive comparison is most likely not necessary for any files in the wild.
|
||||
if(fileHeader.filetype != 2
|
||||
|| (fileHeader.dosEof != 0x1A && fileHeader.dosEof != 2) // ST2 ignores this, ST3 doesn't. putup10.stm / putup11.stm have dosEof = 2.
|
||||
|| (fileHeader.dosEof != 0x1A && fileHeader.dosEof != 2) // ST2 ignores this, ST3 doesn't. Broken versions of putup10.stm / putup11.stm have dosEof = 2.
|
||||
|| fileHeader.verMajor != 2
|
||||
|| fileHeader.verMinor > 21 // ST3 only accepts 0, 10, 20 and 21
|
||||
|| fileHeader.globalVolume > 64
|
||||
|| (fileHeader.verMinor != 0 && fileHeader.verMinor != 10 && fileHeader.verMinor != 20 && fileHeader.verMinor != 21)
|
||||
|| fileHeader.numPatterns > 64
|
||||
|| (fileHeader.globalVolume > 64 && fileHeader.globalVolume != 0x58) // 0x58 may be a placeholder value in earlier ST2 versions.
|
||||
|| (std::memcmp(fileHeader.trackername, "!Scream!", 8)
|
||||
&& std::memcmp(fileHeader.trackername, "BMOD2STM", 8)
|
||||
&& std::memcmp(fileHeader.trackername, "WUZAMOD!", 8))
|
||||
|
@ -160,7 +161,7 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
m_nDefaultTempo = ConvertST2Tempo(initTempo);
|
||||
m_nDefaultSpeed = initTempo >> 4;
|
||||
if(fileHeader.verMinor > 10)
|
||||
m_nDefaultGlobalVolume = fileHeader.globalVolume * 4u;
|
||||
m_nDefaultGlobalVolume = std::min<uint8>(fileHeader.globalVolume, 64) * 4u;
|
||||
|
||||
// Setting up channels
|
||||
for(CHANNELINDEX chn = 0; chn < 4; chn++)
|
||||
|
|
|
@ -720,8 +720,13 @@ static void C_StereoFill(mixsample_t *pBuffer, uint32 nSamples, mixsample_t &rof
|
|||
{
|
||||
#ifdef MPT_INTMIXER
|
||||
// Equivalent to int x_r = (rofs + (rofs > 0 ? 255 : -255)) / 256;
|
||||
#if MPT_COMPILER_SHIFT_SIGNED
|
||||
const mixsample_t x_r = (rofs + (((-rofs) >> (sizeof(mixsample_t) * 8 - 1)) & OFSDECAYMASK)) >> OFSDECAYSHIFT;
|
||||
const mixsample_t x_l = (lofs + (((-lofs) >> (sizeof(mixsample_t) * 8 - 1)) & OFSDECAYMASK)) >> OFSDECAYSHIFT;
|
||||
#else
|
||||
const mixsample_t x_r = mpt::rshift_signed(rofs + (mpt::rshift_signed(-rofs, sizeof(int) * 8 - 1) & OFSDECAYMASK), OFSDECAYSHIFT);
|
||||
const mixsample_t x_l = mpt::rshift_signed(lofs + (mpt::rshift_signed(-lofs, sizeof(int) * 8 - 1) & OFSDECAYMASK), OFSDECAYSHIFT);
|
||||
#endif
|
||||
#else
|
||||
const mixsample_t x_r = rofs * (1.0f / (1 << OFSDECAYSHIFT));
|
||||
const mixsample_t x_l = lofs * (1.0f / (1 << OFSDECAYSHIFT));
|
||||
|
@ -803,8 +808,13 @@ static void C_EndChannelOfs(ModChannel &chn, mixsample_t *pBuffer, uint32 nSampl
|
|||
for (uint32 i=0; i<nSamples; i++)
|
||||
{
|
||||
#ifdef MPT_INTMIXER
|
||||
#if MPT_COMPILER_SHIFT_SIGNED
|
||||
const mixsample_t x_r = (rofs + (((-rofs) >> (sizeof(mixsample_t) * 8 - 1)) & OFSDECAYMASK)) >> OFSDECAYSHIFT;
|
||||
const mixsample_t x_l = (lofs + (((-lofs) >> (sizeof(mixsample_t) * 8 - 1)) & OFSDECAYMASK)) >> OFSDECAYSHIFT;
|
||||
#else
|
||||
const mixsample_t x_r = mpt::rshift_signed(rofs + (mpt::rshift_signed(-rofs, sizeof(int) * 8 - 1) & OFSDECAYMASK), OFSDECAYSHIFT);
|
||||
const mixsample_t x_l = mpt::rshift_signed(lofs + (mpt::rshift_signed(-lofs, sizeof(int) * 8 - 1) & OFSDECAYMASK), OFSDECAYSHIFT);
|
||||
#endif
|
||||
#else
|
||||
const mixsample_t x_r = rofs * (1.0f / (1 << OFSDECAYSHIFT));
|
||||
const mixsample_t x_l = lofs * (1.0f / (1 << OFSDECAYSHIFT));
|
||||
|
|
|
@ -58,6 +58,11 @@ void InstrumentEnvelope::Convert(MODTYPE fromType, MODTYPE toType)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(toType != MOD_TYPE_MPT)
|
||||
{
|
||||
nReleaseNode = ENV_RELEASE_NODE_UNSET;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1243,6 +1243,8 @@ struct SFZEnvelope
|
|||
env.nSustainStart = env.nSustainEnd = static_cast<uint8>(env.size() - 1);
|
||||
if(sustainLevel != 0)
|
||||
{
|
||||
if(envType == ENV_VOLUME && env.nSustainEnd > 0)
|
||||
env.nReleaseNode = env.nSustainEnd;
|
||||
env.push_back(env.back().tick + ToTicks(release, tickDuration), ToValue(0, envType));
|
||||
env.dwFlags.set(ENV_SUSTAIN);
|
||||
}
|
||||
|
@ -1269,7 +1271,7 @@ struct SFZRegion
|
|||
kAlternate,
|
||||
};
|
||||
|
||||
std::string filename;
|
||||
std::string filename, name;
|
||||
SFZEnvelope ampEnv, pitchEnv, filterEnv;
|
||||
SmpLength loopStart = 0, loopEnd = 0;
|
||||
SmpLength end = MAX_SAMPLE_LENGTH, offset = 0;
|
||||
|
@ -1355,6 +1357,8 @@ struct SFZRegion
|
|||
{
|
||||
if(key == "sample")
|
||||
filename = control.defaultPath + value;
|
||||
else if(key == "region_label")
|
||||
name = value;
|
||||
else if(key == "lokey")
|
||||
keyLo = ReadKey(value, control);
|
||||
else if(key == "hikey")
|
||||
|
@ -1446,7 +1450,7 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
|
|||
{
|
||||
file.Rewind();
|
||||
|
||||
enum { kNone, kGlobal, kMaster, kGroup, kRegion, kControl, kUnknown } section = kNone;
|
||||
enum { kNone, kGlobal, kMaster, kGroup, kRegion, kControl, kCurve, kUnknown } section = kNone;
|
||||
SFZControl control;
|
||||
SFZRegion group, master, globals;
|
||||
std::vector<SFZRegion> regions;
|
||||
|
@ -1524,6 +1528,9 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
|
|||
} else if(sec == "control")
|
||||
{
|
||||
section = kControl;
|
||||
} else if(sec == "curve")
|
||||
{
|
||||
section = kCurve;
|
||||
}
|
||||
charsRead++;
|
||||
} else if(s.substr(0, 8) == "#define " || s.substr(0, 8) == "#define\t")
|
||||
|
@ -1560,7 +1567,7 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
|
|||
auto keyEnd = s.find_first_of(" \t=");
|
||||
auto valueStart = s.find_first_not_of(" \t=", keyEnd);
|
||||
std::string key = mpt::ToLowerCaseAscii(s.substr(0, keyEnd));
|
||||
if(key == "sample" || key == "default_path" || key.substr(0, 8) == "label_cc")
|
||||
if(key == "sample" || key == "default_path" || key.substr(0, 8) == "label_cc" || key.substr(0, 12) == "region_label")
|
||||
{
|
||||
// Sample / CC name may contain spaces...
|
||||
charsRead = s.find_first_of("=\t<", valueStart);
|
||||
|
@ -1656,6 +1663,10 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
|
|||
prevSmp--;
|
||||
continue;
|
||||
}
|
||||
if(!region.name.empty())
|
||||
{
|
||||
mpt::String::Copy(m_szNames[smp], region.name);
|
||||
}
|
||||
if(!m_szNames[smp][0])
|
||||
{
|
||||
mpt::String::Copy(m_szNames[smp], filename.GetFileName().ToLocale());
|
||||
|
|
|
@ -40,10 +40,20 @@ uint8 CSoundFile::FrequencyToCutOff(double frequency) const
|
|||
uint32 CSoundFile::CutOffToFrequency(uint32 nCutOff, int flt_modifier) const
|
||||
{
|
||||
MPT_ASSERT(nCutOff < 128);
|
||||
float Fc = 110.0f * std::pow(2.0f, 0.25f + ((float)(nCutOff * (flt_modifier + 256))) / (m_SongFlags[SONG_EXFILTERRANGE] ? 20.0f * 512.0f : 24.0f * 512.0f));
|
||||
float computedCutoff = static_cast<float>(nCutOff * (flt_modifier + 256)); // 0...127*512
|
||||
float Fc;
|
||||
if(GetType() != MOD_TYPE_IMF)
|
||||
{
|
||||
Fc = 110.0f * std::pow(2.0f, 0.25f + computedCutoff / (m_SongFlags[SONG_EXFILTERRANGE] ? 20.0f * 512.0f : 24.0f * 512.0f));
|
||||
} else
|
||||
{
|
||||
// EMU8000: Documentation says the cutoff is in quarter semitones, with 0x00 being 125 Hz and 0xFF being 8 kHz
|
||||
// The first half of the sentence contradicts the second, though.
|
||||
Fc = 125.0f * std::pow(2.0f, computedCutoff * 6.0f / (127.0f * 512.0f));
|
||||
}
|
||||
int freq = Util::Round<int>(Fc);
|
||||
Limit(freq, 120, 20000);
|
||||
if (freq * 2 > (int)m_MixerSettings.gdwMixingFreq) freq = m_MixerSettings.gdwMixingFreq / 2;
|
||||
if(freq * 2 > (int)m_MixerSettings.gdwMixingFreq) freq = m_MixerSettings.gdwMixingFreq / 2;
|
||||
return static_cast<uint32>(freq);
|
||||
}
|
||||
|
||||
|
|
|
@ -57,21 +57,12 @@ public:
|
|||
std::unique_ptr<CSoundFile::PlayState> state;
|
||||
struct ChnSettings
|
||||
{
|
||||
double patLoop;
|
||||
CSoundFile::samplecount_t patLoopSmp;
|
||||
ROWINDEX patLoopStart;
|
||||
uint32 ticksToRender; // When using sample sync, we still need to render this many ticks
|
||||
bool incChanged; // When using sample sync, note frequency has changed
|
||||
uint8 vol;
|
||||
|
||||
ChnSettings()
|
||||
: patLoop(0.0)
|
||||
, patLoopSmp(0)
|
||||
, patLoopStart(0)
|
||||
, ticksToRender(0)
|
||||
, incChanged(false)
|
||||
, vol(0xFF)
|
||||
{ }
|
||||
double patLoop = 0.0;
|
||||
CSoundFile::samplecount_t patLoopSmp = 0;
|
||||
ROWINDEX patLoopStart = 0;
|
||||
uint32 ticksToRender = 0; // When using sample sync, we still need to render this many ticks
|
||||
bool incChanged = false; // When using sample sync, note frequency has changed
|
||||
uint8 vol = 0xFF;
|
||||
};
|
||||
|
||||
#ifndef NO_PLUGINS
|
||||
|
@ -329,9 +320,9 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
|||
bool patternBreakOnThisRow = false;
|
||||
bool patternLoopEndedOnThisRow = false, patternLoopStartedOnThisRow = false;
|
||||
|
||||
if(playState.m_nPattern == orderList.GetIgnoreIndex() && target.mode == GetLengthTarget::SeekPosition && playState.m_nCurrentOrder == target.pos.order)
|
||||
if(!Patterns.IsValidPat(playState.m_nPattern) && playState.m_nPattern != orderList.GetInvalidPatIndex() && target.mode == GetLengthTarget::SeekPosition && playState.m_nCurrentOrder == target.pos.order)
|
||||
{
|
||||
// Early test: Target is inside +++ pattern
|
||||
// Early test: Target is inside +++ or non-existing pattern
|
||||
retval.targetReached = true;
|
||||
break;
|
||||
}
|
||||
|
@ -494,7 +485,7 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
|||
{
|
||||
// Fine Pattern Delay
|
||||
tickDelay += (p->param & 0x0F);
|
||||
} if((p->param & 0xF0) == 0xE0 && !rowDelay)
|
||||
} else if((p->param & 0xF0) == 0xE0 && !rowDelay)
|
||||
{
|
||||
// Pattern Delay
|
||||
if(!(GetType() & MOD_TYPE_S3M) || (p->param & 0x0F) != 0)
|
||||
|
@ -674,12 +665,18 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
|||
patternLoopStartedOnThisRow = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xF0:
|
||||
// Active macro
|
||||
pChn->nActiveMacro = param & 0x0F;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_MODCMDEX:
|
||||
if ((param & 0xF0) == 0x60)
|
||||
switch(param & 0xF0)
|
||||
{
|
||||
case 0x60:
|
||||
// Pattern Loop
|
||||
if (param & 0x0F)
|
||||
{
|
||||
|
@ -692,6 +689,12 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
|||
memory.chnSettings[nChn].patLoopSmp = playState.m_lTotalSampleCount;
|
||||
memory.chnSettings[nChn].patLoopStart = playState.m_nRow;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xF0:
|
||||
// Active macro
|
||||
pChn->nActiveMacro = param & 0x0F;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1158,6 +1161,12 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
|||
if(memory.chnSettings[nChn].patLoop == i.first)
|
||||
{
|
||||
playState.m_lTotalSampleCount += (playState.m_lTotalSampleCount - memory.chnSettings[nChn].patLoopSmp) * (i.second - 1);
|
||||
if(m_playBehaviour[kITPatternLoopTargetReset] || (GetType() == MOD_TYPE_S3M))
|
||||
{
|
||||
memory.chnSettings[nChn].patLoop = memory.elapsedTime;
|
||||
memory.chnSettings[nChn].patLoopSmp = playState.m_lTotalSampleCount;
|
||||
memory.chnSettings[nChn].patLoopStart = playState.m_nRow + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1165,7 +1174,8 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
|||
if(GetType() == MOD_TYPE_IT)
|
||||
{
|
||||
// IT pattern loop start row update - at the end of a pattern loop, set pattern loop start to next row (for upcoming pattern loops with missing SB0)
|
||||
for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++)
|
||||
pChn = playState.Chn;
|
||||
for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++, pChn++)
|
||||
{
|
||||
if((pChn->rowCommand.command == CMD_S3MCMDEX && pChn->rowCommand.param >= 0xB1 && pChn->rowCommand.param <= 0xBF))
|
||||
{
|
||||
|
@ -1327,7 +1337,7 @@ void CSoundFile::InstrumentChange(ModChannel *pChn, uint32 instr, bool bPorta, b
|
|||
{
|
||||
// IT compatibility: No sample change (also within multi-sample instruments) during portamento when using Compatible Gxx.
|
||||
// Test case: PortaInsNumCompat.it, PortaSampleCompat.it, PortaCutCompat.it
|
||||
if(m_playBehaviour[kITPortamentoInstrument] && m_SongFlags[SONG_ITCOMPATGXX] && pChn->nLength != 0)
|
||||
if(m_playBehaviour[kITPortamentoInstrument] && m_SongFlags[SONG_ITCOMPATGXX] && !pChn->increment.IsZero())
|
||||
{
|
||||
pSmp = pChn->pModSample;
|
||||
}
|
||||
|
@ -2082,13 +2092,11 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo
|
|||
if (srcChn.dwFlags[CHN_MUTE])
|
||||
return CHANNELINDEX_INVALID;
|
||||
|
||||
bool applyDNAtoPlug; //rewbs.VSTiNNA
|
||||
|
||||
for(CHANNELINDEX i = nChn; i < MAX_CHANNELS; i++)
|
||||
if(i >= m_nChannels || i == nChn)
|
||||
{
|
||||
ModChannel &chn = m_PlayState.Chn[i];
|
||||
applyDNAtoPlug = false; //rewbs.VSTiNNA
|
||||
bool applyDNAtoPlug = false;
|
||||
if((chn.nMasterChn == nChn + 1 || i == nChn) && chn.pModInstrument != nullptr)
|
||||
{
|
||||
bool bOk = false;
|
||||
|
@ -2098,7 +2106,7 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo
|
|||
// Note
|
||||
case DCT_NOTE:
|
||||
if(note && chn.nNote == note && pIns == chn.pModInstrument) bOk = true;
|
||||
if(pIns && pIns->nMixPlug) applyDNAtoPlug = true; //rewbs.VSTiNNA
|
||||
if(pIns && pIns->nMixPlug) applyDNAtoPlug = true;
|
||||
break;
|
||||
// Sample
|
||||
case DCT_SAMPLE:
|
||||
|
@ -2107,7 +2115,6 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo
|
|||
// Instrument
|
||||
case DCT_INSTRUMENT:
|
||||
if(pIns == chn.pModInstrument) bOk = true;
|
||||
//rewbs.VSTiNNA
|
||||
if(pIns && pIns->nMixPlug) applyDNAtoPlug = true;
|
||||
break;
|
||||
// Plugin
|
||||
|
@ -2117,7 +2124,6 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo
|
|||
applyDNAtoPlug = true;
|
||||
bOk = true;
|
||||
}
|
||||
//end rewbs.VSTiNNA
|
||||
break;
|
||||
|
||||
}
|
||||
|
@ -2178,7 +2184,7 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo
|
|||
pPlugin = m_MixPlugins[nPlugin-1].pMixPlugin;
|
||||
if(pPlugin)
|
||||
{
|
||||
// apply NNA to this plugin iff it is currently playing a note on this tracking channel
|
||||
// apply NNA to this plugin iff it is currently playing a note on this tracker channel
|
||||
// (and if it is playing a note, we know that would be the last note played on this chan).
|
||||
applyNNAtoPlug = pPlugin->IsNotePlaying(srcChn.GetPluginNote(m_playBehaviour[kITRealNoteMapping]), GetBestMidiChannel(nChn), nChn);
|
||||
}
|
||||
|
@ -2187,8 +2193,7 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo
|
|||
#endif // NO_PLUGINS
|
||||
|
||||
// New Note Action
|
||||
//if ((pChn.nVolume) && (pChn.nLength))
|
||||
if((srcChn.nVolume != 0 && srcChn.nLength != 0) || applyNNAtoPlug) //rewbs.VSTiNNA
|
||||
if((srcChn.nRealVolume > 0 && srcChn.nLength > 0) || applyNNAtoPlug)
|
||||
{
|
||||
nnaChn = GetNNAChannel(nChn);
|
||||
if(nnaChn != 0)
|
||||
|
@ -4876,7 +4881,7 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned
|
|||
return 0;
|
||||
}
|
||||
|
||||
ModChannel *pChn = &m_PlayState.Chn[nChn];
|
||||
ModChannel &chn = m_PlayState.Chn[nChn];
|
||||
|
||||
if(macro[0] == 0xF0 && (macro[1] == 0xF0 || macro[1] == 0xF1))
|
||||
{
|
||||
|
@ -4889,60 +4894,44 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned
|
|||
const uint8 macroCode = macro[2];
|
||||
const uint8 param = macro[3];
|
||||
|
||||
if(macroCode == 0x00 && !isExtended)
|
||||
if(macroCode == 0x00 && !isExtended && param < 0x80)
|
||||
{
|
||||
// F0.F0.00.xx: Set CutOff
|
||||
int oldcutoff = pChn->nCutOff;
|
||||
if(param < 0x80)
|
||||
if(!isSmooth)
|
||||
{
|
||||
if(!isSmooth)
|
||||
{
|
||||
pChn->nCutOff = param;
|
||||
} else
|
||||
{
|
||||
pChn->nCutOff = (uint8)CalculateSmoothParamChange((float)pChn->nCutOff, (float)param);
|
||||
}
|
||||
pChn->nRestoreCutoffOnNewNote = 0;
|
||||
chn.nCutOff = param;
|
||||
} else
|
||||
{
|
||||
chn.nCutOff = Util::Round<uint8>(CalculateSmoothParamChange(chn.nCutOff, param));
|
||||
}
|
||||
|
||||
oldcutoff -= pChn->nCutOff;
|
||||
if(oldcutoff < 0) oldcutoff = -oldcutoff;
|
||||
if((pChn->nVolume > 0) || (oldcutoff < 0x10)
|
||||
|| !pChn->dwFlags[CHN_FILTER] || (!(pChn->rightVol | pChn->leftVol)))
|
||||
SetupChannelFilter(pChn, !pChn->dwFlags[CHN_FILTER]);
|
||||
chn.nRestoreCutoffOnNewNote = 0;
|
||||
SetupChannelFilter(&chn, !chn.dwFlags[CHN_FILTER]);
|
||||
|
||||
return 4;
|
||||
|
||||
} else if(macroCode == 0x01 && !isExtended)
|
||||
} else if(macroCode == 0x01 && !isExtended && param < 0x80)
|
||||
{
|
||||
// F0.F0.01.xx: Set Resonance
|
||||
if(param < 0x80)
|
||||
if(!isSmooth)
|
||||
{
|
||||
pChn->nRestoreResonanceOnNewNote = 0;
|
||||
if(!isSmooth)
|
||||
{
|
||||
pChn->nResonance = param;
|
||||
} else
|
||||
{
|
||||
pChn->nResonance = (uint8)CalculateSmoothParamChange((float)pChn->nResonance, (float)param);
|
||||
}
|
||||
chn.nResonance = param;
|
||||
} else
|
||||
{
|
||||
chn.nResonance = (uint8)CalculateSmoothParamChange((float)chn.nResonance, (float)param);
|
||||
}
|
||||
|
||||
SetupChannelFilter(pChn, !pChn->dwFlags[CHN_FILTER]);
|
||||
chn.nRestoreResonanceOnNewNote = 0;
|
||||
SetupChannelFilter(&chn, !chn.dwFlags[CHN_FILTER]);
|
||||
|
||||
return 4;
|
||||
|
||||
} else if(macroCode == 0x02 && !isExtended)
|
||||
{
|
||||
// F0.F0.02.xx: Set filter mode (high nibble determines filter mode)
|
||||
if(param < 0x20)
|
||||
{
|
||||
pChn->nFilterMode = (param >> 4);
|
||||
SetupChannelFilter(pChn, !pChn->dwFlags[CHN_FILTER]);
|
||||
chn.nFilterMode = (param >> 4);
|
||||
SetupChannelFilter(&chn, !chn.dwFlags[CHN_FILTER]);
|
||||
}
|
||||
|
||||
return 4;
|
||||
|
||||
#ifndef NO_PLUGINS
|
||||
} else if(macroCode == 0x03 && !isExtended)
|
||||
{
|
||||
|
@ -4961,7 +4950,6 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned
|
|||
}
|
||||
|
||||
return 4;
|
||||
|
||||
} else if((macroCode & 0x80) || isExtended)
|
||||
{
|
||||
// F0.F0.{80|n}.xx / F0.F1.n.xx: Set VST effect parameter n to xx
|
||||
|
@ -4985,7 +4973,6 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned
|
|||
|
||||
return 4;
|
||||
#endif // NO_PLUGINS
|
||||
|
||||
}
|
||||
|
||||
// If we reach this point, the internal macro was invalid.
|
||||
|
@ -4994,11 +4981,11 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned
|
|||
{
|
||||
#ifndef NO_PLUGINS
|
||||
// Not an internal device. Pass on to appropriate plugin.
|
||||
const CHANNELINDEX plugChannel = (nChn < GetNumChannels()) ? nChn + 1 : pChn->nMasterChn;
|
||||
const CHANNELINDEX plugChannel = (nChn < GetNumChannels()) ? nChn + 1 : chn.nMasterChn;
|
||||
if(plugChannel > 0 && plugChannel <= GetNumChannels()) // XXX do we need this? I guess it might be relevant for previewing notes in the pattern... Or when using this mechanism for volume/panning!
|
||||
{
|
||||
PLUGINDEX nPlug = 0;
|
||||
if(!pChn->dwFlags[CHN_NOFX])
|
||||
if(!chn.dwFlags[CHN_NOFX])
|
||||
{
|
||||
nPlug = (plugin != 0) ? plugin : GetBestPlugin(nChn, PrioritiseChannel, EvenIfMuted);
|
||||
}
|
||||
|
|
|
@ -320,7 +320,7 @@ CSoundFile::ProbeResult CSoundFile::Probe(ProbeFlags flags, mpt::span<const mpt:
|
|||
if((result == ProbeWantMoreData) && (data.size() >= ProbeRecommendedSize))
|
||||
{
|
||||
// If the prober wants more daat but we already provided the recommended required maximum,
|
||||
// just return success as this is th ebest we can do for the suggestesd probing size.
|
||||
// just return success as this is the best we can do for the suggestesd probing size.
|
||||
result = ProbeSuccess;
|
||||
}
|
||||
}
|
||||
|
@ -654,7 +654,7 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags)
|
|||
if (Reporting::Confirm(mpt::ToWide(mpt::CharsetUTF8, notFoundText.c_str()), L"OpenMPT - Plugins missing", false, true) == cnfYes)
|
||||
{
|
||||
std::string url = "https://resources.openmpt.org/plugins/search.php?p=";
|
||||
for(auto &id : notFoundIDs)
|
||||
for(const auto &id : notFoundIDs)
|
||||
{
|
||||
url += mpt::fmt::HEX0<8>(id->dwPluginId2.get());
|
||||
url += id->szLibraryName;
|
||||
|
@ -810,8 +810,11 @@ void CSoundFile::ResetPlayPos()
|
|||
|
||||
void CSoundFile::SetCurrentOrder(ORDERINDEX nOrder)
|
||||
{
|
||||
while ((nOrder < Order().size()) && (Order()[nOrder] == Order.GetIgnoreIndex())) nOrder++;
|
||||
if ((nOrder >= Order().size()) || (Order()[nOrder] >= Patterns.Size())) return;
|
||||
while(nOrder < Order().size() && !Order().IsValidPat(nOrder))
|
||||
nOrder++;
|
||||
if(nOrder >= Order().size())
|
||||
return;
|
||||
|
||||
for(auto &chn : m_PlayState.Chn)
|
||||
{
|
||||
chn.nPeriod = 0;
|
||||
|
|
|
@ -1452,12 +1452,14 @@ void CSoundFile::ProcessArpeggio(CHANNELINDEX nChn, int &period, Tuning::NOTEIND
|
|||
|
||||
// TODO other likely formats for MOD case: MED, OKT, etc
|
||||
uint8 note = (GetType() != MOD_TYPE_MOD) ? pChn->nNote : static_cast<uint8>(GetNoteFromPeriod(period, pChn->nFineTune, pChn->nC5Speed));
|
||||
if(GetType() & (MOD_TYPE_DBM | MOD_TYPE_DIGI))
|
||||
tick += 2;
|
||||
switch(tick % 3)
|
||||
{
|
||||
case 1: note += (pChn->nArpeggio >> 4); break;
|
||||
case 2: note += (pChn->nArpeggio & 0x0F); break;
|
||||
}
|
||||
if(note != pChn->nNote || GetType() == MOD_TYPE_STM || m_playBehaviour[KST3PortaAfterArpeggio])
|
||||
if(note != pChn->nNote || (GetType() & (MOD_TYPE_DBM | MOD_TYPE_DIGI | MOD_TYPE_STM)) || m_playBehaviour[KST3PortaAfterArpeggio])
|
||||
{
|
||||
if(m_SongFlags[SONG_PT_MODE])
|
||||
{
|
||||
|
@ -1474,7 +1476,7 @@ void CSoundFile::ProcessArpeggio(CHANNELINDEX nChn, int &period, Tuning::NOTEIND
|
|||
}
|
||||
period = GetPeriodFromNote(note, pChn->nFineTune, pChn->nC5Speed);
|
||||
|
||||
if(GetType() & (MOD_TYPE_STM | MOD_TYPE_PSM))
|
||||
if(GetType() & (MOD_TYPE_DBM | MOD_TYPE_DIGI | MOD_TYPE_PSM | MOD_TYPE_STM))
|
||||
{
|
||||
// The arpeggio note offset remains effective after the end of the current row in ScreamTracker 2.
|
||||
// This fixes the flute lead in MORPH.STM by Skaven, pattern 27.
|
||||
|
|
|
@ -651,7 +651,7 @@ bool IMixPlugin::SaveProgram()
|
|||
defaultDir = m_Factory.dllPath.GetPath();
|
||||
}
|
||||
|
||||
CString progName = GetCurrentProgramName();
|
||||
CString progName = m_Factory.libraryName.ToCString() + _T(" - ") + GetCurrentProgramName();
|
||||
SanitizeFilename(progName);
|
||||
|
||||
FileDialog dlg = SaveFileDialog()
|
||||
|
@ -928,7 +928,7 @@ void IMidiPlugin::MidiCommand(uint8 nMidiCh, uint8 nMidiProg, uint16 wMidiBank,
|
|||
// Problem: if a note dies out naturally and we never send a note off, this counter
|
||||
// will block at max until note off. Is this a problem?
|
||||
// Safe to assume we won't need more than 16 note offs max on a given note?
|
||||
if(channel.noteOnMap[note][trackChannel] < 17)
|
||||
if(channel.noteOnMap[note][trackChannel] < uint8_max)
|
||||
channel.noteOnMap[note][trackChannel]++;
|
||||
|
||||
MidiSend(MIDIEvents::NoteOn(nMidiCh, static_cast<uint8>(note), volume));
|
||||
|
|
Loading…
Reference in New Issue