From 7c96db91becbdb4e5cc56c478d91be19002c1aa5 Mon Sep 17 00:00:00 2001 From: Samuel Gomes Date: Sat, 17 Jun 2023 14:07:50 +0530 Subject: [PATCH] Update to Libxmp-lite v4.6.0 --- internal/c/parts/audio/extras/build.mk | 3 +- .../audio/extras/libxmp-lite/callbackio.h | 23 +- .../c/parts/audio/extras/libxmp-lite/common.c | 199 +-- .../c/parts/audio/extras/libxmp-lite/common.h | 211 +++- .../parts/audio/extras/libxmp-lite/control.c | 61 +- .../c/parts/audio/extras/libxmp-lite/dataio.c | 25 +- .../parts/audio/extras/libxmp-lite/effects.c | 85 +- .../parts/audio/extras/libxmp-lite/effects.h | 22 +- .../parts/audio/extras/libxmp-lite/filetype.c | 145 +++ .../c/parts/audio/extras/libxmp-lite/filter.c | 4 +- .../c/parts/audio/extras/libxmp-lite/format.c | 90 +- .../c/parts/audio/extras/libxmp-lite/format.h | 93 +- .../c/parts/audio/extras/libxmp-lite/hio.c | 81 +- .../c/parts/audio/extras/libxmp-lite/hio.h | 4 +- .../c/parts/audio/extras/libxmp-lite/it.h | 15 + .../parts/audio/extras/libxmp-lite/it_load.c | 239 ++-- .../c/parts/audio/extras/libxmp-lite/itsex.c | 35 +- .../c/parts/audio/extras/libxmp-lite/list.h | 27 +- .../c/parts/audio/extras/libxmp-lite/load.c | 104 +- .../audio/extras/libxmp-lite/load_helpers.c | 203 ++- .../c/parts/audio/extras/libxmp-lite/loader.h | 15 +- .../c/parts/audio/extras/libxmp-lite/md5.h | 10 +- .../parts/audio/extras/libxmp-lite/mdataio.h | 13 +- .../c/parts/audio/extras/libxmp-lite/memio.c | 24 +- .../c/parts/audio/extras/libxmp-lite/memio.h | 12 +- .../parts/audio/extras/libxmp-lite/mix_all.c | 45 +- .../c/parts/audio/extras/libxmp-lite/mixer.c | 435 +++++-- .../c/parts/audio/extras/libxmp-lite/mixer.h | 7 +- .../c/parts/audio/extras/libxmp-lite/mod.h | 6 +- .../parts/audio/extras/libxmp-lite/mod_load.c | 1084 ++++++++++++++--- .../c/parts/audio/extras/libxmp-lite/period.c | 37 +- .../c/parts/audio/extras/libxmp-lite/period.h | 3 + .../c/parts/audio/extras/libxmp-lite/player.c | 482 ++++++-- .../c/parts/audio/extras/libxmp-lite/player.h | 30 +- .../audio/extras/libxmp-lite/read_event.c | 196 +-- .../c/parts/audio/extras/libxmp-lite/s3m.h | 16 +- .../parts/audio/extras/libxmp-lite/s3m_load.c | 77 +- .../c/parts/audio/extras/libxmp-lite/sample.c | 100 +- .../c/parts/audio/extras/libxmp-lite/scan.c | 197 ++- .../c/parts/audio/extras/libxmp-lite/smix.c | 30 +- .../parts/audio/extras/libxmp-lite/virtual.c | 47 +- .../parts/audio/extras/libxmp-lite/virtual.h | 3 +- .../c/parts/audio/extras/libxmp-lite/win32.c | 14 + .../c/parts/audio/extras/libxmp-lite/xm.h | 4 +- .../parts/audio/extras/libxmp-lite/xm_load.c | 619 +++++++--- .../c/parts/audio/extras/libxmp-lite/xmp.h | 36 +- .../c/parts/audio/extras/mod_ma_vtable.cpp | 2 +- 47 files changed, 3862 insertions(+), 1351 deletions(-) create mode 100644 internal/c/parts/audio/extras/libxmp-lite/filetype.c diff --git a/internal/c/parts/audio/extras/build.mk b/internal/c/parts/audio/extras/build.mk index 3b860e358..e4527b1d9 100644 --- a/internal/c/parts/audio/extras/build.mk +++ b/internal/c/parts/audio/extras/build.mk @@ -4,6 +4,7 @@ LIBXMP_SRCS := \ control.c \ dataio.c \ effects.c \ + filetype.c \ filter.c \ format.c \ hio.c \ @@ -34,7 +35,7 @@ LIBXMP_OBJS += $(patsubst %.c,$(PATH_INTERNAL_C)/parts/audio/extras/libxmp-lite/ LIBXMP_LIB := $(PATH_INTERNAL_C)/parts/audio/extras/libxmp-lite.a $(PATH_INTERNAL_C)/parts/audio/extras/libxmp-lite/%.o: $(PATH_INTERNAL_C)/parts/audio/extras/libxmp-lite/%.c - $(CC) -O2 $(CFLAGS) -Wall -DLIBXMP_CORE_PLAYER -DLIBXMP_NO_PROWIZARD -DLIBXMP_NO_DEPACKERS -DBUILDING_STATIC $< -c -o $@ + $(CC) -O2 $(CFLAGS) -Wall -DLIBXMP_CORE_PLAYER -DLIBXMP_STATIC $< -c -o $@ $(LIBXMP_LIB): $(LIBXMP_OBJS) $(AR) rcs $@ $^ diff --git a/internal/c/parts/audio/extras/libxmp-lite/callbackio.h b/internal/c/parts/audio/extras/libxmp-lite/callbackio.h index f93e70296..de46eb497 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/callbackio.h +++ b/internal/c/parts/audio/extras/libxmp-lite/callbackio.h @@ -10,9 +10,7 @@ typedef struct { int eof; } CBFILE; -#ifdef __cplusplus -extern "C" { -#endif +LIBXMP_BEGIN_DECLS static inline uint8 cbread8(CBFILE *f, int *err) { @@ -33,7 +31,7 @@ static inline int8 cbread8s(CBFILE *f, int *err) static inline uint16 cbread16l(CBFILE *f, int *err) { uint8 buf[2]; - uint16 x = EOF; + uint16 x = 0xffff; size_t r = f->callbacks.read_func(buf, 2, 1, f->priv); f->eof = (r == 1) ? 0 : EOF; @@ -46,7 +44,7 @@ static inline uint16 cbread16l(CBFILE *f, int *err) static inline uint16 cbread16b(CBFILE *f, int *err) { uint8 buf[2]; - uint16 x = EOF; + uint16 x = 0xffff; size_t r = f->callbacks.read_func(buf, 2, 1, f->priv); f->eof = (r == 1) ? 0 : EOF; @@ -59,7 +57,7 @@ static inline uint16 cbread16b(CBFILE *f, int *err) static inline uint32 cbread24l(CBFILE *f, int *err) { uint8 buf[3]; - uint32 x = EOF; + uint32 x = 0xffffffff; size_t r = f->callbacks.read_func(buf, 3, 1, f->priv); f->eof = (r == 1) ? 0 : EOF; @@ -72,7 +70,7 @@ static inline uint32 cbread24l(CBFILE *f, int *err) static inline uint32 cbread24b(CBFILE *f, int *err) { uint8 buf[3]; - uint32 x = EOF; + uint32 x = 0xffffffff; size_t r = f->callbacks.read_func(buf, 3, 1, f->priv); f->eof = (r == 1) ? 0 : EOF; @@ -85,7 +83,7 @@ static inline uint32 cbread24b(CBFILE *f, int *err) static inline uint32 cbread32l(CBFILE *f, int *err) { uint8 buf[4]; - uint32 x = EOF; + uint32 x = 0xffffffff; size_t r = f->callbacks.read_func(buf, 4, 1, f->priv); f->eof = (r == 1) ? 0 : EOF; @@ -98,7 +96,7 @@ static inline uint32 cbread32l(CBFILE *f, int *err) static inline uint32 cbread32b(CBFILE *f, int *err) { uint8 buf[4]; - uint32 x = EOF; + uint32 x = 0xffffffff; size_t r = f->callbacks.read_func(buf, 4, 1, f->priv); f->eof = (r == 1) ? 0 : EOF; @@ -110,7 +108,8 @@ static inline uint32 cbread32b(CBFILE *f, int *err) static inline size_t cbread(void *dest, size_t len, size_t nmemb, CBFILE *f) { - size_t r = f->callbacks.read_func(dest, len, nmemb, f->priv); + size_t r = f->callbacks.read_func(dest, (unsigned long)len, + (unsigned long)nmemb, f->priv); f->eof = (r < nmemb) ? EOF : 0; return r; } @@ -183,8 +182,6 @@ static inline int cbclose(CBFILE *f) return r; } -#ifdef __cplusplus -} -#endif +LIBXMP_END_DECLS #endif /* LIBXMP_CALLBACKIO_H */ diff --git a/internal/c/parts/audio/extras/libxmp-lite/common.c b/internal/c/parts/audio/extras/libxmp-lite/common.c index e9c230d4d..57c710325 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/common.c +++ b/internal/c/parts/audio/extras/libxmp-lite/common.c @@ -1,5 +1,5 @@ /* Extended Module Player - * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -21,28 +21,14 @@ */ #include - -#ifndef LIBXMP_CORE_PLAYER -#if defined(_WIN32) -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -#include -#elif defined(__OS2__) || defined(__EMX__) -#define INCL_DOS -#define INCL_DOSERRORS -#include -#elif defined(__DJGPP__) -#include -#include -#elif defined(HAVE_DIRENT_H) +#if defined(HAVE_DIRENT) +#include #include #endif -#endif /* LIBXMP_CORE_PLAYER */ + +#include "common.h" #include "xmp.h" -#include "common.h" #include "period.h" #include "loader.h" @@ -51,7 +37,7 @@ int libxmp_init_instrument(struct module_data *m) struct xmp_module *mod = &m->mod; if (mod->ins > 0) { - mod->xxi = calloc(sizeof (struct xmp_instrument), mod->ins); + mod->xxi = (struct xmp_instrument *) calloc(mod->ins, sizeof(struct xmp_instrument)); if (mod->xxi == NULL) return -1; } @@ -65,10 +51,10 @@ int libxmp_init_instrument(struct module_data *m) return -1; } - mod->xxs = calloc(sizeof (struct xmp_sample), mod->smp); + mod->xxs = (struct xmp_sample *) calloc(mod->smp, sizeof(struct xmp_sample)); if (mod->xxs == NULL) return -1; - m->xtra = calloc(sizeof (struct extra_sample_data), mod->smp); + m->xtra = (struct extra_sample_data *) calloc(mod->smp, sizeof(struct extra_sample_data)); if (m->xtra == NULL) return -1; @@ -103,12 +89,12 @@ int libxmp_realloc_samples(struct module_data *m, int new_size) return 0; } - xxs = realloc(mod->xxs, sizeof(struct xmp_sample) * new_size); + xxs = (struct xmp_sample *) realloc(mod->xxs, sizeof(struct xmp_sample) * new_size); if (xxs == NULL) return -1; mod->xxs = xxs; - xtra = realloc(m->xtra, sizeof(struct extra_sample_data) * new_size); + xtra = (struct extra_sample_data *) realloc(m->xtra, sizeof(struct extra_sample_data) * new_size); if (xtra == NULL) return -1; m->xtra = xtra; @@ -133,7 +119,7 @@ int libxmp_alloc_subinstrument(struct xmp_module *mod, int i, int num) if (num == 0) return 0; - mod->xxi[i].sub = calloc(sizeof (struct xmp_subinstrument), num); + mod->xxi[i].sub = (struct xmp_subinstrument *) calloc(num, sizeof(struct xmp_subinstrument)); if (mod->xxi[i].sub == NULL) return -1; @@ -142,11 +128,11 @@ int libxmp_alloc_subinstrument(struct xmp_module *mod, int i, int num) int libxmp_init_pattern(struct xmp_module *mod) { - mod->xxt = calloc(sizeof (struct xmp_track *), mod->trk); + mod->xxt = (struct xmp_track **) calloc(mod->trk, sizeof(struct xmp_track *)); if (mod->xxt == NULL) return -1; - mod->xxp = calloc(sizeof (struct xmp_pattern *), mod->pat); + mod->xxp = (struct xmp_pattern **) calloc(mod->pat, sizeof(struct xmp_pattern *)); if (mod->xxp == NULL) return -1; @@ -159,8 +145,8 @@ int libxmp_alloc_pattern(struct xmp_module *mod, int num) if (num < 0 || num >= mod->pat || mod->xxp[num] != NULL) return -1; - mod->xxp[num] = calloc(1, sizeof (struct xmp_pattern) + - sizeof (int) * (mod->chn - 1)); + mod->xxp[num] = (struct xmp_pattern *) calloc(1, sizeof(struct xmp_pattern) + + sizeof(int) * (mod->chn - 1)); if (mod->xxp[num] == NULL) return -1; @@ -173,8 +159,8 @@ int libxmp_alloc_track(struct xmp_module *mod, int num, int rows) if (num < 0 || num >= mod->trk || mod->xxt[num] != NULL || rows <= 0) return -1; - mod->xxt[num] = calloc(sizeof (struct xmp_track) + - sizeof (struct xmp_event) * (rows - 1), 1); + mod->xxt[num] = (struct xmp_track *) calloc(1, sizeof(struct xmp_track) + + sizeof(struct xmp_event) * (rows - 1)); if (mod->xxt[num] == NULL) return -1; @@ -218,6 +204,7 @@ int libxmp_alloc_pattern_tracks(struct xmp_module *mod, int num, int rows) return 0; } +#ifndef LIBXMP_CORE_PLAYER /* Some formats explicitly allow more than 256 rows (e.g. OctaMED). This function * allows those formats to work without disrupting the sanity check for other formats. */ @@ -237,6 +224,7 @@ int libxmp_alloc_pattern_tracks_long(struct xmp_module *mod, int num, int rows) return 0; } +#endif char *libxmp_instrument_name(struct xmp_module *mod, int i, uint8 *r, int n) { @@ -253,7 +241,7 @@ char *libxmp_copy_adjust(char *s, uint8 *r, int n) strncpy(s, (char *)r, n); for (i = 0; s[i] && i < n; i++) { - if (!isprint((int)s[i]) || ((uint8)s[i] > 127)) + if (!isprint((unsigned char)s[i]) || ((uint8)s[i] > 127)) s[i] = '.'; } @@ -267,7 +255,7 @@ void libxmp_read_title(HIO_HANDLE *f, char *t, int s) { uint8 buf[XMP_NAME_SIZE]; - if (t == NULL) + if (t == NULL || s < 0) return; if (s >= XMP_NAME_SIZE) @@ -275,22 +263,27 @@ void libxmp_read_title(HIO_HANDLE *f, char *t, int s) memset(t, 0, s + 1); - hio_read(buf, 1, s, f); /* coverity[check_return] */ + s = hio_read(buf, 1, s, f); buf[s] = 0; libxmp_copy_adjust(t, buf, s); } #ifndef LIBXMP_CORE_PLAYER -int libxmp_test_name(uint8 *s, int n) +int libxmp_test_name(const uint8 *s, int n, int flags) { int i; for (i = 0; i < n; i++) { + if (s[i] == '\0' && (flags & TEST_NAME_IGNORE_AFTER_0)) + break; + if (s[i] == '\r' && (flags & TEST_NAME_IGNORE_AFTER_CR)) + break; if (s[i] > 0x7f) return -1; /* ACS_Team2.mod has a backspace in instrument name */ - if (s[i] > 0 && s[i] < 32 && s[i] != 0x08) + /* Numerous ST modules from Music Channel BBS have char 14. */ + if (s[i] > 0 && s[i] < 32 && s[i] != 0x08 && s[i] != 0x0e) return -1; } @@ -309,7 +302,6 @@ int libxmp_copy_name_for_fopen(char *dest, const char *name, int n) name[0] == '\\' || name[0] == '/' || name[0] == ':') return -1; - for (i = 0; i < n - 1; i++) { uint8 t = name[i]; if (!t) @@ -370,7 +362,7 @@ int libxmp_copy_name_for_fopen(char *dest, const char *name, int n) * module players erroneously interpret as "newer-version-trackers commands". * Which they aren't. */ -void libxmp_decode_noisetracker_event(struct xmp_event *event, uint8 *mod_event) +void libxmp_decode_noisetracker_event(struct xmp_event *event, const uint8 *mod_event) { int fxt; @@ -388,7 +380,7 @@ void libxmp_decode_noisetracker_event(struct xmp_event *event, uint8 *mod_event) } #endif -void libxmp_decode_protracker_event(struct xmp_event *event, uint8 *mod_event) +void libxmp_decode_protracker_event(struct xmp_event *event, const uint8 *mod_event) { int fxt = LSN(mod_event[2]); @@ -430,74 +422,45 @@ void libxmp_disable_continue_fx(struct xmp_event *event) /* libxmp_check_filename_case(): */ /* Given a directory, see if file exists there, ignoring case */ -#if defined(_WIN32) +#if defined(_WIN32) || defined(__DJGPP__) || \ + defined(__OS2__) || defined(__EMX__) || \ + defined(_DOS) || defined(LIBXMP_AMIGA) || \ + defined(__riscos__) || \ + /* case-insensitive file system: directly probe the file */\ + \ + !defined(HAVE_DIRENT) /* or, target does not have dirent. */ int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size) { - char path[_MAX_PATH]; - DWORD rc; - /* win32 is case-insensitive: directly probe the file. */ + char path[XMP_MAXPATH]; snprintf(path, sizeof(path), "%s/%s", dir, name); - rc = GetFileAttributesA(path); - if (rc == (DWORD)(-1)) return 0; - if (rc & FILE_ATTRIBUTE_DIRECTORY) return 0; + if (! (libxmp_get_filetype(path) & XMP_FILETYPE_FILE)) + return 0; strncpy(new_name, name, size); return 1; } -#elif defined(__OS2__) || defined(__EMX__) -int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size) -{ - char path[CCHMAXPATH]; - FILESTATUS3 fs; - /* os/2 is case-insensitive: directly probe the file. */ - snprintf(path, sizeof(path), "%s/%s", dir, name); - if (DosQueryPathInfo(path, FIL_STANDARD, &fs, sizeof(fs)) != NO_ERROR) return 0; - if (fs.attrFile & FILE_DIRECTORY) return 0; - strncpy(new_name, name, size); - return 1; -} -#elif defined(__DJGPP__) -int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size) -{ - char path[256]; - int attr; - /* dos is case-insensitive: directly probe the file. */ - snprintf(path, sizeof(path), "%s/%s", dir, name); - attr = _chmod(path, 0); - if (attr == -1) return 0; - if (attr & (_A_SUBDIR|_A_VOLID)) return 0; - strncpy(new_name, name, size); - return 1; -} -#elif defined(HAVE_DIRENT_H) +#else /* target has dirent */ int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size) { int found = 0; - DIR *dirfd; + DIR *dirp; struct dirent *d; - dirfd = opendir(dir); - if (dirfd == NULL) + dirp = opendir(dir); + if (dirp == NULL) return 0; - while ((d = readdir(dirfd)) != NULL) { + while ((d = readdir(dirp)) != NULL) { if (!strcasecmp(d->d_name, name)) { found = 1; + strncpy(new_name, d->d_name, size); break; } } - if (found) - strncpy(new_name, d->d_name, size); - - closedir(dirfd); + closedir(dirp); return found; } -#else -int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size) -{ - return 0; -} #endif void libxmp_get_instrument_path(struct module_data *m, char *path, int size) @@ -521,6 +484,7 @@ void libxmp_set_type(struct module_data *m, const char *fmt, ...) va_end(ap); } +#ifndef LIBXMP_CORE_PLAYER static int schism_tracker_date(int year, int month, int day) { int mm = (month + 9) % 12; @@ -543,7 +507,7 @@ void libxmp_schism_tracker_string(char *buf, size_t size, int s_ver, int l_ver) { if (s_ver >= 0x50) { /* time_t epoch_sec = 1256947200; */ - int t = schism_tracker_date(2009, 10, 31); + int64 t = schism_tracker_date(2009, 10, 31); int year, month, day, dayofyear; if (s_ver == 0xfff) { @@ -553,11 +517,11 @@ void libxmp_schism_tracker_string(char *buf, size_t size, int s_ver, int l_ver) /* Date algorithm reimplemented from OpenMPT. */ - year = (int)(((int64)t * 10000L + 14780) / 3652425); - dayofyear = t - (365 * year + (year / 4) - (year / 100) + (year / 400)); + year = (int)((t * 10000L + 14780) / 3652425); + dayofyear = t - (365L * year + (year / 4) - (year / 100) + (year / 400)); if (dayofyear < 0) { year--; - dayofyear = t - (365 * year + (year / 4) - (year / 100) + (year / 400)); + dayofyear = t - (365L * year + (year / 4) - (year / 100) + (year / 400)); } month = (100 * dayofyear + 52) / 3060; day = dayofyear - (month * 306 + 5) / 10 + 1; @@ -571,3 +535,60 @@ void libxmp_schism_tracker_string(char *buf, size_t size, int s_ver, int l_ver) snprintf(buf, size, "Schism Tracker 0.%x", s_ver); } } + +/* Old MPT modules (from MPT <=1.16, older versions of OpenMPT) rely on a + * pre-amp routine that scales mix volume down. This is based on the module's + * channel count and a tracker pre-amp setting that isn't saved in the module. + * This setting defaults to 128. When fixed to 128, it can be optimized out. + * + * In OpenMPT, this pre-amp routine is only available in the MPT and OpenMPT + * 1.17 RC1 and RC2 mix modes. Changing a module to the compatible or 1.17 RC3 + * mix modes will permanently disable it for that module. OpenMPT applies the + * old mix modes to MPT <=1.16 modules, "IT 8.88", and in old OpenMPT-made + * modules that specify one of these mix modes in their extended properties. + * + * Set mod->chn and m->mvol first! + */ +void libxmp_apply_mpt_preamp(struct module_data *m) +{ + /* OpenMPT uses a slightly different table. */ + static const uint8 preamp_table[16] = + { + 0x60, 0x60, 0x60, 0x70, /* 0-7 */ + 0x80, 0x88, 0x90, 0x98, /* 8-15 */ + 0xA0, 0xA4, 0xA8, 0xB0, /* 16-23 */ + 0xB4, 0xB8, 0xBC, 0xC0, /* 24-31 */ + }; + + int chn = m->mod.chn; + CLAMP(chn, 1, 31); + + m->mvol = (m->mvol * 96) / preamp_table[chn >> 1]; + + /* Pre-amp is applied like this in the mixers of libmodplug/libopenmpt + * (still vastly simplified). + + int preamp = 128; + + if (preamp > 128) { + preamp = 128 + ((preamp - 128) * (chn + 4)) / 16; + } + preamp = preamp * m->mvol / 64; + preamp = (preamp << 7) / preamp_table[chn >> 1]; + + ... + + channel_volume_16bit = (channel_volume_16bit * preamp) >> 7; + */ +} +#endif + +char *libxmp_strdup(const char *src) +{ + size_t len = strlen(src) + 1; + char *buf = (char *) malloc(len); + if (buf) { + memcpy(buf, src, len); + } + return buf; +} diff --git a/internal/c/parts/audio/extras/libxmp-lite/common.h b/internal/c/parts/audio/extras/libxmp-lite/common.h index 2f5cbaeb1..0f62151ae 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/common.h +++ b/internal/c/parts/audio/extras/libxmp-lite/common.h @@ -1,7 +1,19 @@ #ifndef LIBXMP_COMMON_H #define LIBXMP_COMMON_H +#ifdef LIBXMP_CORE_PLAYER +#ifndef LIBXMP_NO_PROWIZARD +#define LIBXMP_NO_PROWIZARD +#endif +#ifndef LIBXMP_NO_DEPACKERS +#define LIBXMP_NO_DEPACKERS +#endif +#else +#undef LIBXMP_CORE_DISABLE_IT +#endif + #include +#include #include #include #include @@ -15,16 +27,63 @@ #define LIBXMP_EXPORT_VAR #endif -#if defined(__MORPHOS__) || defined(__AROS__) || defined(AMIGAOS) || \ - defined(__amigaos__) || defined(__amigaos4__) ||defined(__amigados__) || \ - defined(AMIGA) || defined(_AMIGA) || defined(__AMIGA__) -#define LIBXMP_AMIGA 1 /* to identify amiga platforms. */ +#ifndef __cplusplus +#define LIBXMP_BEGIN_DECLS +#define LIBXMP_END_DECLS +#else +#define LIBXMP_BEGIN_DECLS extern "C" { +#define LIBXMP_END_DECLS } #endif -#if (defined(__GNUC__) || defined(__clang__)) && defined(XMP_SYM_VISIBILITY) -#if !defined(_WIN32) && !defined(__ANDROID__) && !defined(__APPLE__) && !defined(LIBXMP_AMIGA) && !defined(__MSDOS__) && !defined(B_BEOS_VERSION) && !defined(__ATHEOS__) && !defined(EMSCRIPTEN) && !defined(__MINT__) -#define USE_VERSIONED_SYMBOLS +#if defined(_MSC_VER) && !defined(__cplusplus) +#define inline __inline #endif + +#if (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) ||\ + (defined(_MSC_VER) && (_MSC_VER >= 1400)) || \ + (defined(__WATCOMC__) && (__WATCOMC__ >= 1250) && !defined(__cplusplus)) +#define LIBXMP_RESTRICT __restrict +#else +#define LIBXMP_RESTRICT +#endif + +#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__EMX__) +#define XMP_MAXPATH _MAX_PATH +#elif defined(PATH_MAX) +#define XMP_MAXPATH PATH_MAX +#else +#define XMP_MAXPATH 1024 +#endif + +#if defined(__MORPHOS__) || defined(__AROS__) || defined(__AMIGA__) \ + || defined(__amigaos__) || defined(__amigaos4__) || defined(AMIGA) +#define LIBXMP_AMIGA 1 +#endif + +#if defined(_MSC_VER) && defined(__has_include) +#if __has_include() +#define HAVE_WINAPIFAMILY_H 1 +#else +#define HAVE_WINAPIFAMILY_H 0 +#endif +#endif + +#if HAVE_WINAPIFAMILY_H +#include +#define LIBXMP_UWP (!WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)) +#else +#define LIBXMP_UWP 0 +#endif + +#ifdef HAVE_EXTERNAL_VISIBILITY +#define LIBXMP_EXPORT_VERSIONED __attribute__((visibility("default"),externally_visible)) +#else +#define LIBXMP_EXPORT_VERSIONED __attribute__((visibility("default"))) +#endif +#ifdef HAVE_ATTRIBUTE_SYMVER +#define LIBXMP_ATTRIB_SYMVER(_sym) __attribute__((__symver__(_sym))) +#else +#define LIBXMP_ATTRIB_SYMVER(_sym) #endif /* AmigaOS fixes by Chris Young , Nov 25, 2007 @@ -33,6 +92,8 @@ # include #elif defined __amigaos4__ # include +#elif defined _arch_dreamcast /* KallistiOS */ +# include #else typedef signed char int8; typedef signed short int int16; @@ -42,14 +103,18 @@ typedef unsigned short int uint16; typedef unsigned int uint32; #endif -#ifdef _MSC_VER /* MSVC++6.0 has no long long */ +#ifdef _MSC_VER /* MSVC6 has no long long */ typedef signed __int64 int64; typedef unsigned __int64 uint64; -#elif !defined B_BEOS_VERSION /* BeOS has its own int64 definition */ +#elif !(defined(B_BEOS_VERSION) || defined(__amigaos4__)) typedef unsigned long long uint64; typedef signed long long int64; #endif +#ifdef _MSC_VER +#pragma warning(disable:4100) /* unreferenced formal parameter */ +#endif + #ifndef LIBXMP_CORE_PLAYER #define LIBXMP_PAULA_SIMULATOR #endif @@ -72,6 +137,11 @@ typedef signed long long int64; #define RESET_FLAG(a,b) ((a)&=~(b)) #define TEST_FLAG(a,b) !!((a)&(b)) +/* libxmp_get_filetype() return values */ +#define XMP_FILETYPE_NONE 0 +#define XMP_FILETYPE_DIR (1 << 0) +#define XMP_FILETYPE_FILE (1 << 1) + #define CLAMP(x,a,b) do { \ if ((x) < (a)) (x) = (a); \ else if ((x) > (b)) (x) = (b); \ @@ -87,20 +157,18 @@ typedef signed long long int64; #define D_CRIT " Error: " #define D_WARN "Warning: " #define D_INFO " Info: " -#ifndef CLIB_DECL -#define CLIB_DECL -#endif #ifdef DEBUG -#ifndef ATTR_PRINTF -#define ATTR_PRINTF(x,y) -#endif -void CLIB_DECL D_(const char *text, ...) ATTR_PRINTF(1,2); +#define D_ libxmp_msvc_dbgprint /* in win32.c */ +void libxmp_msvc_dbgprint(const char *text, ...); #else -// VS prior to VC7.1 does not support variadic macros. VC8.0 does not optimize unused parameters passing +/* VS prior to VC7.1 does not support variadic macros. + * VC8.0 does not optimize unused parameters passing. */ #if _MSC_VER < 1400 -void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); } +static void __inline D_(const char *text, ...) { + do { } while (0); +} #else -#define D_(args, ...) do {} while (0) +#define D_(...) do {} while (0) #endif #endif @@ -111,21 +179,8 @@ void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); } #define D_CRIT " Error: " #define D_WARN "Warning: " #define D_INFO " Info: " -#define D_(args...) do { \ - __android_log_print(ANDROID_LOG_DEBUG, "libxmp", args); \ - } while (0) -#else -#define D_(args...) do {} while (0) -#endif - -#elif defined(__WATCOMC__) -#ifdef DEBUG -#define D_INFO "\x1b[33m" -#define D_CRIT "\x1b[31m" -#define D_WARN "\x1b[36m" #define D_(...) do { \ - printf("\x1b[33m%s \x1b[37m[%s:%d] " D_INFO, __FUNCTION__, \ - __FILE__, __LINE__); printf (__VA_ARGS__); printf ("\x1b[0m\n"); \ + __android_log_print(ANDROID_LOG_DEBUG, "libxmp", __VA_ARGS__); \ } while (0) #else #define D_(...) do {} while (0) @@ -134,15 +189,20 @@ void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); } #else #ifdef DEBUG +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define LIBXMP_FUNC __func__ +#else +#define LIBXMP_FUNC __FUNCTION__ +#endif #define D_INFO "\x1b[33m" #define D_CRIT "\x1b[31m" #define D_WARN "\x1b[36m" -#define D_(args...) do { \ - printf("\x1b[33m%s \x1b[37m[%s:%d] " D_INFO, __FUNCTION__, \ - __FILE__, __LINE__); printf (args); printf ("\x1b[0m\n"); \ +#define D_(...) do { \ + printf("\x1b[33m%s \x1b[37m[%s:%d] " D_INFO, LIBXMP_FUNC, \ + __FILE__, __LINE__); printf (__VA_ARGS__); printf ("\x1b[0m\n"); \ } while (0) #else -#define D_(args...) do {} while (0) +#define D_(...) do {} while (0) #endif #endif /* !_MSC_VER */ @@ -151,7 +211,6 @@ void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); } #define dup _dup #define fileno _fileno #define strnicmp _strnicmp -#define strdup _strdup #define fdopen _fdopen #define open _open #define close _close @@ -171,13 +230,33 @@ void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); } #undef USE_LIBXMP_SNPRINTF #endif #ifdef USE_LIBXMP_SNPRINTF -int libxmp_vsnprintf(char *, size_t, const char *, va_list); -int libxmp_snprintf (char *, size_t, const char *, ...); +#if defined(__GNUC__) || defined(__clang__) +#define LIBXMP_ATTRIB_PRINTF(x,y) __attribute__((__format__(__printf__,x,y))) +#else +#define LIBXMP_ATTRIB_PRINTF(x,y) +#endif +int libxmp_vsnprintf(char *, size_t, const char *, va_list) LIBXMP_ATTRIB_PRINTF(3,0); +int libxmp_snprintf (char *, size_t, const char *, ...) LIBXMP_ATTRIB_PRINTF(3,4); #define snprintf libxmp_snprintf #define vsnprintf libxmp_vsnprintf #endif #endif +/* Output file size limit for files unpacked from unarchivers into RAM. Most + * general archive compression formats can't nicely bound the output size + * from their input filesize, and a cap is needed for a few reasons: + * + * - Linux is too dumb for its own good and its malloc/realloc will return + * pointers to RAM that doesn't exist instead of NULL. When these are used, + * it will kill the application instead of allowing it to fail gracefully. + * - libFuzzer and the clang sanitizers have malloc/realloc interceptors that + * terminate with an error instead of returning NULL. + * + * Depackers that have better ways of bounding the output size can ignore this. + * This value is fairly arbitrary and can be changed if needed. + */ +#define LIBXMP_DEPACK_LIMIT (512 << 20) + /* Quirks */ #define QUIRK_S3MLOOP (1 << 0) /* S3M loop mode */ #define QUIRK_ENVFADE (1 << 1) /* Fade at end of envelope */ @@ -191,6 +270,7 @@ int libxmp_snprintf (char *, size_t, const char *, ...); #define QUIRK_UNISLD (1 << 10) /* Unified pitch slide/portamento */ #define QUIRK_ITVPOR (1 << 11) /* Disable fine bends in IT vol fx */ #define QUIRK_FTMOD (1 << 12) /* Flag for multichannel mods */ +#define QUIRK_INVLOOP (1 << 13) /* Enable invert loop */ /*#define QUIRK_MODRNG (1 << 13)*/ /* Limit periods to MOD range */ #define QUIRK_INSVOL (1 << 14) /* Use instrument volume */ #define QUIRK_VIRTUAL (1 << 15) /* Enable virtual channels */ @@ -232,8 +312,9 @@ int libxmp_snprintf (char *, size_t, const char *, ...); /* Time factor */ #define DEFAULT_TIME_FACTOR 10.0 #define MED_TIME_FACTOR 2.64 +#define FAR_TIME_FACTOR 4.01373 /* See far_extras.c */ -#define MAX_SEQUENCES 16 +#define MAX_SEQUENCES 255 #define MAX_SAMPLE_SIZE 0x10000000 #define MAX_SAMPLES 1024 #define MAX_INSTRUMENTS 255 @@ -262,7 +343,6 @@ struct ord_data { }; - /* Context */ struct smix_data { @@ -276,6 +356,17 @@ struct smix_data { /* This will be added to the sample structure in the next API revision */ struct extra_sample_data { double c5spd; + int sus; + int sue; +}; + +struct midi_macro { + char data[32]; +}; + +struct midi_macro_data { + struct midi_macro param[16]; + struct midi_macro fixed[128]; }; struct module_data { @@ -293,7 +384,9 @@ struct module_data { int volbase; /* Volume base */ int gvolbase; /* Global volume base */ int gvol; /* Global volume */ - int *vol_table; /* Volume translation table */ + int mvolbase; /* Mix volume base (S3M/IT) */ + int mvol; /* Mix volume (S3M/IT) */ + const int *vol_table; /* Volume translation table */ int quirk; /* player quirks */ #define READ_EVENT_MOD 0 #define READ_EVENT_FT2 1 @@ -315,9 +408,8 @@ struct module_data { void *extra; /* format-specific extra fields */ uint8 **scan_cnt; /* scan counters */ struct extra_sample_data *xtra; -#ifndef LIBXMP_CORE_DISABLE_IT - struct xmp_sample *xsmp; /* sustain loop samples */ -#endif + struct midi_macro_data *midi; + int compare_vblank; }; @@ -332,6 +424,9 @@ struct flow_control { int delay; int jumpline; int loop_chn; +#ifndef LIBXMP_CORE_PLAYER + int jump_in_pat; +#endif struct pattern_loop *loop; @@ -348,6 +443,13 @@ struct virt_channel { int map; }; +struct scan_data { + int time; /* replay time in ms */ + int row; + int ord; + int num; +}; + struct player_data { int ord; int pos; @@ -372,12 +474,7 @@ struct player_data { struct flow_control flow; - struct { - int time; /* replay time in ms */ - int ord; - int row; - int num; - } scan[MAX_SEQUENCES]; + struct scan_data *scan; struct channel_data *xc_data; @@ -416,12 +513,13 @@ struct mixer_data { int mix; /* percentage of channel separation */ int interp; /* interpolation type */ int dsp; /* dsp effect flags */ - char* buffer; /* output buffer */ - int32* buf32; /* temporary buffer for 32 bit samples */ + char *buffer; /* output buffer */ + int32 *buf32; /* temporary buffer for 32 bit samples */ int numvoc; /* default softmixer voices number */ int ticksize; int dtright; /* anticlick control, right channel */ int dtleft; /* anticlick control, left channel */ + int bidir_adjust; /* adjustment for IT bidirectional loops */ double pbase; /* period base */ }; @@ -442,6 +540,7 @@ void libxmp_free_scan (struct context_data *); int libxmp_scan_sequences (struct context_data *); int libxmp_get_sequence (struct context_data *, int); int libxmp_set_player_mode (struct context_data *); +void libxmp_reset_flow (struct context_data *); int8 read8s (FILE *, int *err); uint8 read8 (FILE *, int *err); @@ -458,7 +557,6 @@ void write16l (FILE *, uint16); void write16b (FILE *, uint16); void write32l (FILE *, uint32); void write32b (FILE *, uint32); -int move_data (FILE *, FILE *, int); uint16 readmem16l (const uint8 *); uint16 readmem16b (const uint8 *); @@ -470,4 +568,7 @@ uint32 readmem32b (const uint8 *); struct xmp_instrument *libxmp_get_instrument(struct context_data *, int); struct xmp_sample *libxmp_get_sample(struct context_data *, int); +char *libxmp_strdup(const char *); +int libxmp_get_filetype (const char *); + #endif /* LIBXMP_COMMON_H */ diff --git a/internal/c/parts/audio/extras/libxmp-lite/control.c b/internal/c/parts/audio/extras/libxmp-lite/control.c index c0c3abe49..1e56f4833 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/control.c +++ b/internal/c/parts/audio/extras/libxmp-lite/control.c @@ -1,5 +1,5 @@ /* Extended Module Player - * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -31,7 +31,7 @@ xmp_context xmp_create_context(void) { struct context_data *ctx; - ctx = calloc(1, sizeof(struct context_data)); + ctx = (struct context_data *) calloc(1, sizeof(struct context_data)); if (ctx == NULL) { return NULL; } @@ -117,13 +117,9 @@ static void set_position(struct context_data *ctx, int pos, int dir) } else { p->pos = pos; } - f->jumpline = 0; - f->jump = -1; - f->pbreak = 0; - f->loop_chn = 0; - f->delay = 0; - f->rowdelay = 0; - f->rowdelay_set = 0; + /* Clear flow vars to prevent old pattern jumps and + * other junk from executing in the new position. */ + libxmp_reset_flow(ctx); } } } @@ -185,16 +181,17 @@ int xmp_set_row(xmp_context opaque, int row) struct xmp_module *mod = &m->mod; struct flow_control *f = &p->flow; int pos = p->pos; - int pattern = mod->xxo[pos]; + int pattern; if (pos < 0 || pos >= mod->len) { pos = 0; } + pattern = mod->xxo[pos]; if (ctx->state < XMP_STATE_PLAYING) return -XMP_ERROR_STATE; - if (row >= mod->xxp[pattern]->rows) + if (pattern >= mod->pat || row >= mod->xxp[pattern]->rows) return -XMP_ERROR_INVALID; /* See set_position. */ @@ -309,18 +306,22 @@ int xmp_channel_vol(xmp_context opaque, int chn, int vol) } #ifdef USE_VERSIONED_SYMBOLS -LIBXMP_EXPORT extern int xmp_set_player_v40__(xmp_context, int, int); -LIBXMP_EXPORT extern int xmp_set_player_v41__(xmp_context, int, int) - __attribute__((alias("xmp_set_player_v40__"))); -LIBXMP_EXPORT extern int xmp_set_player_v43__(xmp_context, int, int) - __attribute__((alias("xmp_set_player_v40__"))); -LIBXMP_EXPORT extern int xmp_set_player_v44__(xmp_context, int, int) - __attribute__((alias("xmp_set_player_v40__"))); +LIBXMP_BEGIN_DECLS /* no name-mangling */ +LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v40__(xmp_context, int, int) LIBXMP_ATTRIB_SYMVER("xmp_set_player@XMP_4.0"); +LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v41__(xmp_context, int, int) + __attribute__((alias("xmp_set_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_set_player@XMP_4.1"); +LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v43__(xmp_context, int, int) + __attribute__((alias("xmp_set_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_set_player@XMP_4.3"); +LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v44__(xmp_context, int, int) + __attribute__((alias("xmp_set_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_set_player@@XMP_4.4"); +#ifndef HAVE_ATTRIBUTE_SYMVER asm(".symver xmp_set_player_v40__, xmp_set_player@XMP_4.0"); asm(".symver xmp_set_player_v41__, xmp_set_player@XMP_4.1"); asm(".symver xmp_set_player_v43__, xmp_set_player@XMP_4.3"); asm(".symver xmp_set_player_v44__, xmp_set_player@@XMP_4.4"); +#endif +LIBXMP_END_DECLS #define xmp_set_player__ xmp_set_player_v40__ #else @@ -427,21 +428,25 @@ int xmp_set_player__(xmp_context opaque, int parm, int val) } #ifdef USE_VERSIONED_SYMBOLS -LIBXMP_EXPORT extern int xmp_get_player_v40__(xmp_context, int); -LIBXMP_EXPORT extern int xmp_get_player_v41__(xmp_context, int) - __attribute__((alias("xmp_get_player_v40__"))); -LIBXMP_EXPORT extern int xmp_get_player_v42__(xmp_context, int) - __attribute__((alias("xmp_get_player_v40__"))); -LIBXMP_EXPORT extern int xmp_get_player_v43__(xmp_context, int) - __attribute__((alias("xmp_get_player_v40__"))); -LIBXMP_EXPORT extern int xmp_get_player_v44__(xmp_context, int) - __attribute__((alias("xmp_get_player_v40__"))); +LIBXMP_BEGIN_DECLS /* no name-mangling */ +LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v40__(xmp_context, int) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.0"); +LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v41__(xmp_context, int) + __attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.1"); +LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v42__(xmp_context, int) + __attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.2"); +LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v43__(xmp_context, int) + __attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.3"); +LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v44__(xmp_context, int) + __attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@@XMP_4.4"); +#ifndef HAVE_ATTRIBUTE_SYMVER asm(".symver xmp_get_player_v40__, xmp_get_player@XMP_4.0"); asm(".symver xmp_get_player_v41__, xmp_get_player@XMP_4.1"); asm(".symver xmp_get_player_v42__, xmp_get_player@XMP_4.2"); asm(".symver xmp_get_player_v43__, xmp_get_player@XMP_4.3"); asm(".symver xmp_get_player_v44__, xmp_get_player@@XMP_4.4"); +#endif +LIBXMP_END_DECLS #define xmp_get_player__ xmp_get_player_v40__ #else @@ -554,7 +559,7 @@ int xmp_set_instrument_path(xmp_context opaque, const char *path) if (m->instrument_path != NULL) free(m->instrument_path); - m->instrument_path = strdup(path); + m->instrument_path = libxmp_strdup(path); if (m->instrument_path == NULL) { return -XMP_ERROR_SYSTEM; } diff --git a/internal/c/parts/audio/extras/libxmp-lite/dataio.c b/internal/c/parts/audio/extras/libxmp-lite/dataio.c index d284f56df..3612a3631 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/dataio.c +++ b/internal/c/parts/audio/extras/libxmp-lite/dataio.c @@ -1,5 +1,5 @@ /* Extended Module Player - * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -33,7 +33,6 @@ if (err != NULL) *err = (x); \ } while (0) - uint8 read8(FILE *f, int *err) { int a; @@ -97,13 +96,13 @@ uint32 read24l(FILE *f, int *err) read_byte(a); read_byte(b); read_byte(c); - + set_error(0); return (c << 16) | (b << 8) | a; error: set_error(ferror(f) ? errno : EOF); - return 0xffffff; + return 0xffffffff; } uint32 read24b(FILE *f, int *err) @@ -113,13 +112,13 @@ uint32 read24b(FILE *f, int *err) read_byte(a); read_byte(b); read_byte(c); - + set_error(0); return (a << 16) | (b << 8) | c; error: set_error(ferror(f) ? errno : EOF); - return 0xffffff; + return 0xffffffff; } uint32 read32l(FILE *f, int *err) @@ -252,18 +251,4 @@ void write32b(FILE *f, uint32 w) write8(f, w & 0x000000ff); } -int move_data(FILE *out, FILE *in, int len) -{ - uint8 buf[1024]; - int l; - - do { - l = fread(buf, 1, len > 1024 ? 1024 : len, in); - fwrite(buf, 1, l, out); - len -= l; - } while (l > 0 && len > 0); - - return 0; -} - #endif diff --git a/internal/c/parts/audio/extras/libxmp-lite/effects.c b/internal/c/parts/audio/extras/libxmp-lite/effects.c index 0b91a8186..5c5049cf7 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/effects.c +++ b/internal/c/parts/audio/extras/libxmp-lite/effects.c @@ -1,5 +1,5 @@ /* Extended Module Player - * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -72,8 +72,13 @@ static void do_toneporta(struct context_data *ctx, { struct module_data *m = &ctx->m; struct xmp_instrument *instrument = &m->mod.xxi[xc->ins]; - int mapped = instrument->map[xc->key].ins; struct xmp_subinstrument *sub; + int mapped_xpo = 0; + int mapped = 0; + + if (IS_VALID_NOTE(xc->key)) { + mapped = instrument->map[xc->key].ins; + } if (mapped >= instrument->nsm) { mapped = 0; @@ -81,11 +86,13 @@ static void do_toneporta(struct context_data *ctx, sub = &instrument->sub[mapped]; - if (note >= 1 && note <= 0x80 && (uint32)xc->ins < m->mod.ins) { + if (IS_VALID_NOTE(note - 1) && (uint32)xc->ins < m->mod.ins) { note--; + if (IS_VALID_NOTE(xc->key_porta)) { + mapped_xpo = instrument->map[xc->key_porta].xpo; + } xc->porta.target = libxmp_note_to_period(ctx, note + sub->xpo + - instrument->map[xc->key_porta].xpo, xc->finetune, - xc->per_adj); + mapped_xpo, xc->finetune, xc->per_adj); } xc->porta.dir = xc->period < xc->porta.target ? 1 : -1; } @@ -357,7 +364,7 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch h = MSN(fxp); l = LSN(fxp); xc->vol.slide2 = h ? h : -l; - } + } break; case FX_JUMP: /* Order jump */ p->flow.pbreak = 1; @@ -442,6 +449,7 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch xc->retrig.val = fxp; xc->retrig.count = LSN(xc->retrig.val) + 1; xc->retrig.type = 0; + xc->retrig.limit = 0; break; case EX_F_VSLIDE_UP: /* Fine volume slide up */ EFFECT_MEMORY(fxp, xc->fine_vol.up_memory); @@ -506,7 +514,7 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch if (fxp) { SET(FINE_BEND); xc->freq.fslide = fxp; - } + } break; case FX_PATT_DELAY: fx_patt_delay: @@ -563,7 +571,7 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch /* From the OpenMPT VolColMemory.it test case: * "Volume column commands a, b, c and d (volume slide) share one * effect memory, but it should not be shared with Dxy in the effect - * column. + * column. */ case FX_VSLIDE_UP_2: /* Fine volume slide up */ EFFECT_MEMORY(fxp, xc->vol.memory2); @@ -692,6 +700,7 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch if (note) { xc->retrig.count = LSN(xc->retrig.val) + 1; } + xc->retrig.limit = 0; SET(RETRIG); break; case FX_TREMOR: /* Tremor */ @@ -725,6 +734,9 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch case FX_SURROUND: xc->pan.surround = fxp; break; + case FX_REVERSE: /* Play forward/backward */ + libxmp_virt_reverse(ctx, chn, fxp); + break; #ifndef LIBXMP_CORE_DISABLE_IT case FX_TRK_VOL: /* Track volume setting */ @@ -818,13 +830,26 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch } break; case FX_FLT_CUTOFF: - if (fxp < 0xfe || xc->filter.resonance > 0) { - xc->filter.cutoff = fxp; - } + xc->filter.cutoff = fxp; break; case FX_FLT_RESN: xc->filter.resonance = fxp; break; + case FX_MACRO_SET: + xc->macro.active = LSN(fxp); + break; + case FX_MACRO: + SET(MIDI_MACRO); + xc->macro.val = fxp; + xc->macro.slide = 0; + break; + case FX_MACROSMOOTH: + if (ctx->p.speed && xc->macro.val < 0x80) { + SET(MIDI_MACRO); + xc->macro.target = fxp; + xc->macro.slide = ((float)fxp - xc->macro.val) / ctx->p.speed; + } + break; case FX_PANBRELLO: /* Panbrello */ SET(PANBRELLO); SET_LFO_NOTZERO(&xc->panbrello.lfo, LSN(fxp) << 4, MSN(fxp)); @@ -928,7 +953,7 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch h = MSN(fxp); l = LSN(fxp); xc->vol.fslide = h ? h : -l; - } + } break; case FX_NSLIDE_DN: case FX_NSLIDE_UP: @@ -939,6 +964,7 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch xc->retrig.val = MSN(fxp); xc->retrig.count = MSN(fxp) + 1; xc->retrig.type = 0; + xc->retrig.limit = 0; } if (fxt == FX_NSLIDE_UP || fxt == FX_NSLIDE_R_UP) @@ -1061,6 +1087,41 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch } SET_LFO_NOTZERO(&xc->vibrato.lfo, 669, 1); break; + + /* ULT effects */ + + case FX_ULT_TPORTA: /* ULT tone portamento */ + /* Like normal persistent tone portamento, except: + * + * 1) Despite the documentation claiming 300 cancels tone + * portamento, it actually reuses the last parameter. + * + * 2) A 3xx without a note will reuse the last target note. + */ + if (!IS_VALID_INSTRUMENT(xc->ins)) + break; + SET_PER(TONEPORTA); + EFFECT_MEMORY(fxp, xc->porta.memory); + EFFECT_MEMORY(note, xc->porta.note_memory); + do_toneporta(ctx, xc, note); + xc->porta.slide = fxp; + if (fxp == 0) + RESET_PER(TONEPORTA); + break; + + /* Archimedes (!Tracker, Digital Symphony, et al.) effects */ + + case FX_LINE_JUMP: /* !Tracker and Digital Symphony "Line Jump" */ + /* Jump to a line within the current order. In Digital Symphony + * this can be combined with position jump (like pattern break) + * and overrides the pattern break line in lower channels. */ + if (p->flow.pbreak == 0) { + p->flow.pbreak = 1; + p->flow.jump = p->ord; + } + p->flow.jumpline = fxp; + p->flow.jump_in_pat = p->ord; + break; #endif default: diff --git a/internal/c/parts/audio/extras/libxmp-lite/effects.h b/internal/c/parts/audio/extras/libxmp-lite/effects.h index b269bac43..fb48b495d 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/effects.h +++ b/internal/c/parts/audio/extras/libxmp-lite/effects.h @@ -59,7 +59,7 @@ #define FX_F_NSLIDE_DN 0x75 #define FX_F_NSLIDE_UP 0x76 -/* Persistent effects -- for FNK and FAR */ +/* Persistent effects -- for FNK */ #define FX_PER_PORTA_DN 0x78 #define FX_PER_PORTA_UP 0x79 #define FX_PER_TPORTA 0x7a @@ -75,6 +75,21 @@ #define FX_669_TPORTA 0x62 #define FX_669_FINETUNE 0x63 #define FX_669_VIBRATO 0x64 + +/* FAR effects */ +#define FX_FAR_PORTA_UP 0x65 /* FAR pitch offset up */ +#define FX_FAR_PORTA_DN 0x66 /* FAR pitch offset down */ +#define FX_FAR_TPORTA 0x67 /* FAR persistent tone portamento */ +#define FX_FAR_TEMPO 0x68 /* FAR coarse tempo and tempo mode */ +#define FX_FAR_F_TEMPO 0x69 /* FAR fine tempo slide up/down */ +#define FX_FAR_VIBDEPTH 0x6a /* FAR set vibrato depth */ +#define FX_FAR_VIBRATO 0x6b /* FAR persistent vibrato */ +#define FX_FAR_SLIDEVOL 0x6c /* FAR persistent slide-to-volume */ +#define FX_FAR_RETRIG 0x6d /* FAR retrigger */ +#define FX_FAR_DELAY 0x6e /* FAR note offset */ + +/* Other frequency based effects (ULT, etc) */ +#define FX_ULT_TPORTA 0x6f #endif #ifndef LIBXMP_CORE_DISABLE_IT @@ -92,6 +107,9 @@ #define FX_PANBRELLO_WF 0x8b #define FX_HIOFFSET 0x8c #define FX_IT_BREAK 0x8e /* like FX_BREAK with hex parameter */ +#define FX_MACRO_SET 0xbd /* Set active IT parametered MIDI macro */ +#define FX_MACRO 0xbe /* Execute IT MIDI macro */ +#define FX_MACROSMOOTH 0xbf /* Execute IT MIDI macro slide */ #endif #ifndef LIBXMP_CORE_PLAYER @@ -119,9 +137,11 @@ #define FX_VOL_SUB 0xb7 /* SFX change volume down */ #define FX_PITCH_ADD 0xb8 /* SFX add steps to current note */ #define FX_PITCH_SUB 0xb9 /* SFX add steps to current note */ +#define FX_LINE_JUMP 0xba /* Archimedes jump to line in current order */ #endif #define FX_SURROUND 0x8d /* S3M/IT */ +#define FX_REVERSE 0x8f /* XM/IT/others: play forward/reverse */ #define FX_S3M_SPEED 0xa3 /* S3M */ #define FX_VOLSLIDE_2 0xa4 #define FX_FINETUNE 0xa6 diff --git a/internal/c/parts/audio/extras/libxmp-lite/filetype.c b/internal/c/parts/audio/extras/libxmp-lite/filetype.c new file mode 100644 index 000000000..363150c10 --- /dev/null +++ b/internal/c/parts/audio/extras/libxmp-lite/filetype.c @@ -0,0 +1,145 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include + +#if defined(_WIN32) + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include + +int libxmp_get_filetype (const char *path) +{ + DWORD result = GetFileAttributesA(path); + if (result == (DWORD)(-1)) { + errno = ENOENT; + return XMP_FILETYPE_NONE; + } + return (result & FILE_ATTRIBUTE_DIRECTORY) ? XMP_FILETYPE_DIR : XMP_FILETYPE_FILE; +} + +#elif defined(__OS2__) || defined(__EMX__) + +#define INCL_DOSFILEMGR +#include + +int libxmp_get_filetype (const char *path) +{ + FILESTATUS3 fs; + if (DosQueryPathInfo(path, FIL_STANDARD, &fs, sizeof(fs)) != 0) { + errno = ENOENT; + return XMP_FILETYPE_NONE; + } + return (fs.attrFile & FILE_DIRECTORY) ? XMP_FILETYPE_DIR : XMP_FILETYPE_FILE; +} + +#elif defined(__DJGPP__) + +#include +#include + +int libxmp_get_filetype (const char *path) +{ + int attr = _chmod(path, 0); + /* Root directories on some non-local drives (e.g. CD-ROM), as well as + * devices may fail _chmod, but we are not interested in such cases. */ + if (attr < 0) return XMP_FILETYPE_NONE; + /* we shouldn't hit _A_VOLID ! */ + return (attr & (_A_SUBDIR|_A_VOLID)) ? XMP_FILETYPE_DIR : XMP_FILETYPE_FILE; +} + +#elif defined(__WATCOMC__) && defined(_DOS) + +#include +#include + +int libxmp_get_filetype (const char *path) +{ + unsigned int attr; + if (_dos_getfileattr(path, &attr)) return XMP_FILETYPE_NONE; + return (attr & (_A_SUBDIR|_A_VOLID)) ? XMP_FILETYPE_DIR : XMP_FILETYPE_FILE; +} + +#elif defined(__amigaos4__) + +#define __USE_INLINE__ +#include + +int libxmp_get_filetype (const char *path) +{ + int typ = XMP_FILETYPE_NONE; + struct ExamineData *data = ExamineObjectTags(EX_StringNameInput, path, TAG_END); + if (data) { + if (EXD_IS_FILE(data)) { + typ = XMP_FILETYPE_FILE; + } else + if (EXD_IS_DIRECTORY(data)) { + typ = XMP_FILETYPE_DIR; + } + FreeDosObject(DOS_EXAMINEDATA, data); + } + if (typ == XMP_FILETYPE_NONE) errno = ENOENT; + return typ; +} + +#elif defined(LIBXMP_AMIGA) + +#include + +int libxmp_get_filetype (const char *path) +{ + int typ = XMP_FILETYPE_NONE; + BPTR lock = Lock((const STRPTR)path, ACCESS_READ); + if (lock) { + struct FileInfoBlock *fib = (struct FileInfoBlock *) AllocDosObject(DOS_FIB,NULL); + if (fib) { + if (Examine(lock, fib)) { + typ = (fib->fib_DirEntryType < 0) ? XMP_FILETYPE_FILE : XMP_FILETYPE_DIR; + } + FreeDosObject(DOS_FIB, fib); + } + UnLock(lock); + } + if (typ == XMP_FILETYPE_NONE) errno = ENOENT; + return typ; +} + +#else /* unix (ish): */ + +#include +#include + +int libxmp_get_filetype (const char *path) +{ + struct stat st; + memset(&st, 0, sizeof(st)); /* silence sanitizers.. */ + if (stat(path, &st) < 0) return XMP_FILETYPE_NONE; + if (S_ISDIR(st.st_mode)) return XMP_FILETYPE_DIR; + if (S_ISREG(st.st_mode)) return XMP_FILETYPE_FILE; + return XMP_FILETYPE_NONE; +} + +#endif + diff --git a/internal/c/parts/audio/extras/libxmp-lite/filter.c b/internal/c/parts/audio/extras/libxmp-lite/filter.c index d15d89c9e..a8a34b672 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/filter.c +++ b/internal/c/parts/audio/extras/libxmp-lite/filter.c @@ -68,7 +68,9 @@ static const float resonance_table[128] = { #if !defined(HAVE_POWF) || defined(__DJGPP__) || defined(__WATCOMC__) -#define powf pow /* Watcom doesn't have powf. DJGPP have a C-only implementation in libm. */ +/* Watcom doesn't have powf. DJGPP have a C-only implementation in libm. */ +#undef powf +#define powf(f1_,f2_) (float)pow((f1_),(f2_)) #endif diff --git a/internal/c/parts/audio/extras/libxmp-lite/format.c b/internal/c/parts/audio/extras/libxmp-lite/format.c index 5922c084c..593ace13e 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/format.c +++ b/internal/c/parts/audio/extras/libxmp-lite/format.c @@ -1,5 +1,5 @@ /* Extended Module Player - * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -21,23 +21,85 @@ */ #include "format.h" +#ifndef LIBXMP_NO_PROWIZARD +#include "loaders/prowizard/prowiz.h" +#endif -extern const struct format_loader libxmp_loader_xm; -extern const struct format_loader libxmp_loader_mod; -extern const struct format_loader libxmp_loader_it; -extern const struct format_loader libxmp_loader_s3m; - -const struct format_loader *const format_loaders[5] = { +const struct format_loader *const format_loaders[NUM_FORMATS + 2] = { &libxmp_loader_xm, &libxmp_loader_mod, #ifndef LIBXMP_CORE_DISABLE_IT &libxmp_loader_it, #endif &libxmp_loader_s3m, - NULL +#ifndef LIBXMP_CORE_PLAYER + &libxmp_loader_flt, + &libxmp_loader_st, + &libxmp_loader_stm, + &libxmp_loader_stx, + &libxmp_loader_mtm, + &libxmp_loader_ice, + &libxmp_loader_imf, + &libxmp_loader_ptm, + &libxmp_loader_mdl, + &libxmp_loader_ult, + &libxmp_loader_liq, + &libxmp_loader_no, + &libxmp_loader_masi, + &libxmp_loader_masi16, + &libxmp_loader_muse, + &libxmp_loader_gal5, + &libxmp_loader_gal4, + &libxmp_loader_amf, + &libxmp_loader_asylum, + &libxmp_loader_gdm, + &libxmp_loader_mmd1, + &libxmp_loader_mmd3, + &libxmp_loader_med2, + &libxmp_loader_med3, + &libxmp_loader_med4, + /* &libxmp_loader_dmf, */ + &libxmp_loader_chip, + &libxmp_loader_rtm, + &libxmp_loader_pt3, + /* &libxmp_loader_tcb, */ + &libxmp_loader_dt, + /* &libxmp_loader_gtk, */ + /* &libxmp_loader_dtt, */ + &libxmp_loader_mgt, + &libxmp_loader_arch, + &libxmp_loader_sym, + &libxmp_loader_digi, + &libxmp_loader_dbm, + &libxmp_loader_emod, + &libxmp_loader_okt, + &libxmp_loader_sfx, + &libxmp_loader_far, + &libxmp_loader_umx, + &libxmp_loader_hmn, + &libxmp_loader_stim, + &libxmp_loader_coco, + /* &libxmp_loader_mtp, */ + &libxmp_loader_ims, + &libxmp_loader_669, + &libxmp_loader_fnk, + /* &libxmp_loader_amd, */ + /* &libxmp_loader_rad, */ + /* &libxmp_loader_hsc, */ + &libxmp_loader_mfp, + &libxmp_loader_abk, + /* &libxmp_loader_alm, */ + /* &libxmp_loader_polly, */ + /* &libxmp_loader_stc, */ + &libxmp_loader_xmf, +#ifndef LIBXMP_NO_PROWIZARD + &libxmp_loader_pw, +#endif +#endif /* LIBXMP_CORE_PLAYER */ + NULL /* list teminator */ }; -static const char *_farray[5] = { NULL }; +static const char *_farray[NUM_FORMATS + NUM_PW_FORMATS + 1] = { NULL }; const char *const *format_list(void) { @@ -45,6 +107,16 @@ const char *const *format_list(void) if (_farray[0] == NULL) { for (count = i = 0; format_loaders[i] != NULL; i++) { +#ifndef LIBXMP_NO_PROWIZARD + if (strcmp(format_loaders[i]->name, "prowizard") == 0) { + int j; + + for (j = 0; pw_formats[j] != NULL; j++) { + _farray[count++] = pw_formats[j]->name; + } + continue; + } +#endif _farray[count++] = format_loaders[i]->name; } diff --git a/internal/c/parts/audio/extras/libxmp-lite/format.h b/internal/c/parts/audio/extras/libxmp-lite/format.h index d44e840bc..a4cfd5bfc 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/format.h +++ b/internal/c/parts/audio/extras/libxmp-lite/format.h @@ -6,21 +6,98 @@ struct format_loader { const char *name; - int (*const test)(HIO_HANDLE *, char *, const int); - int (*const loader)(struct module_data *, HIO_HANDLE *, const int); + int (*test)(HIO_HANDLE *, char *, const int); + int (*loader)(struct module_data *, HIO_HANDLE *, const int); }; +extern const struct format_loader *const format_loaders[]; + const char *const *format_list(void); -#ifndef LIBXMP_CORE_PLAYER +extern const struct format_loader libxmp_loader_xm; +extern const struct format_loader libxmp_loader_mod; +extern const struct format_loader libxmp_loader_it; +extern const struct format_loader libxmp_loader_s3m; -#define NUM_FORMATS 53 -#define NUM_PW_FORMATS 43 +#ifndef LIBXMP_CORE_PLAYER +extern const struct format_loader libxmp_loader_flt; +extern const struct format_loader libxmp_loader_st; +extern const struct format_loader libxmp_loader_stm; +extern const struct format_loader libxmp_loader_stx; +extern const struct format_loader libxmp_loader_mtm; +extern const struct format_loader libxmp_loader_ice; +extern const struct format_loader libxmp_loader_imf; +extern const struct format_loader libxmp_loader_ptm; +extern const struct format_loader libxmp_loader_mdl; +extern const struct format_loader libxmp_loader_ult; +extern const struct format_loader libxmp_loader_liq; +extern const struct format_loader libxmp_loader_no; +extern const struct format_loader libxmp_loader_masi; +extern const struct format_loader libxmp_loader_masi16; +extern const struct format_loader libxmp_loader_muse; +extern const struct format_loader libxmp_loader_gal5; +extern const struct format_loader libxmp_loader_gal4; +extern const struct format_loader libxmp_loader_amf; +extern const struct format_loader libxmp_loader_asylum; +extern const struct format_loader libxmp_loader_gdm; +extern const struct format_loader libxmp_loader_mmd1; +extern const struct format_loader libxmp_loader_mmd3; +extern const struct format_loader libxmp_loader_med2; +extern const struct format_loader libxmp_loader_med3; +extern const struct format_loader libxmp_loader_med4; +extern const struct format_loader libxmp_loader_rtm; +extern const struct format_loader libxmp_loader_pt3; +extern const struct format_loader libxmp_loader_dt; +extern const struct format_loader libxmp_loader_mgt; +extern const struct format_loader libxmp_loader_arch; +extern const struct format_loader libxmp_loader_sym; +extern const struct format_loader libxmp_loader_digi; +extern const struct format_loader libxmp_loader_dbm; +extern const struct format_loader libxmp_loader_emod; +extern const struct format_loader libxmp_loader_okt; +extern const struct format_loader libxmp_loader_sfx; +extern const struct format_loader libxmp_loader_far; +extern const struct format_loader libxmp_loader_umx; +extern const struct format_loader libxmp_loader_stim; +extern const struct format_loader libxmp_loader_coco; +extern const struct format_loader libxmp_loader_ims; +extern const struct format_loader libxmp_loader_669; +extern const struct format_loader libxmp_loader_fnk; +extern const struct format_loader libxmp_loader_mfp; +extern const struct format_loader libxmp_loader_pw; +extern const struct format_loader libxmp_loader_hmn; +extern const struct format_loader libxmp_loader_chip; +extern const struct format_loader libxmp_loader_abk; +extern const struct format_loader libxmp_loader_xmf; +#if 0 /* broken / unused, yet. */ +extern const struct format_loader libxmp_loader_dmf; +extern const struct format_loader libxmp_loader_tcb; +extern const struct format_loader libxmp_loader_gtk; +extern const struct format_loader libxmp_loader_dtt; +extern const struct format_loader libxmp_loader_mtp; +extern const struct format_loader libxmp_loader_amd; +extern const struct format_loader libxmp_loader_rad; +extern const struct format_loader libxmp_loader_hsc; +extern const struct format_loader libxmp_loader_alm; +extern const struct format_loader libxmp_loader_polly; +extern const struct format_loader libxmp_loader_stc; +#endif +#endif /* LIBXMP_CORE_PLAYER */ + +#ifndef LIBXMP_CORE_PLAYER +#define NUM_FORMATS 52 +#elif !defined(LIBXMP_CORE_DISABLE_IT) +#define NUM_FORMATS 4 +#else +#define NUM_FORMATS 3 +#endif #ifndef LIBXMP_NO_PROWIZARD +#define NUM_PW_FORMATS 43 +extern const struct pw_format *const pw_formats[]; int pw_test_format(HIO_HANDLE *, char *, const int, struct xmp_test_info *); -#endif -#endif - +#else +#define NUM_PW_FORMATS 0 #endif +#endif /* LIBXMP_FORMAT_H */ diff --git a/internal/c/parts/audio/extras/libxmp-lite/hio.c b/internal/c/parts/audio/extras/libxmp-lite/hio.c index a3190387b..bee385172 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/hio.c +++ b/internal/c/parts/audio/extras/libxmp-lite/hio.c @@ -1,5 +1,5 @@ /* Extended Module Player - * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -287,18 +287,27 @@ int hio_seek(HIO_HANDLE *h, long offset, int whence) if (ret < 0) { h->error = errno; } + else if (h->error == EOF) { + h->error = 0; + } break; case HIO_HANDLE_TYPE_MEMORY: ret = mseek(h->handle.mem, offset, whence); if (ret < 0) { h->error = EINVAL; } + else if (h->error == EOF) { + h->error = 0; + } break; case HIO_HANDLE_TYPE_CBFILE: ret = cbseek(h->handle.cbfile, offset, whence); if (ret < 0) { h->error = EINVAL; } + else if (h->error == EOF) { + h->error = 0; + } break; } @@ -358,7 +367,7 @@ HIO_HANDLE *hio_open(const char *path, const char *mode) { HIO_HANDLE *h; - h = (HIO_HANDLE *)calloc(1, sizeof (HIO_HANDLE)); + h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE)); if (h == NULL) goto err; @@ -381,19 +390,24 @@ HIO_HANDLE *hio_open(const char *path, const char *mode) return NULL; } -HIO_HANDLE *hio_open_mem(const void *ptr, long size) +HIO_HANDLE *hio_open_mem(const void *ptr, long size, int free_after_use) { HIO_HANDLE *h; if (size <= 0) return NULL; - h = (HIO_HANDLE *)calloc(1, sizeof (HIO_HANDLE)); + h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE)); if (h == NULL) return NULL; h->type = HIO_HANDLE_TYPE_MEMORY; - h->handle.mem = mopen(ptr, size); + h->handle.mem = mopen(ptr, size, free_after_use); h->size = size; + if (!h->handle.mem) { + free(h); + h = NULL; + } + return h; } @@ -401,7 +415,7 @@ HIO_HANDLE *hio_open_file(FILE *f) { HIO_HANDLE *h; - h = (HIO_HANDLE *)calloc(1, sizeof (HIO_HANDLE)); + h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE)); if (h == NULL) return NULL; @@ -436,7 +450,7 @@ HIO_HANDLE *hio_open_callbacks(void *priv, struct xmp_callbacks callbacks) if (!f) return NULL; - h = (HIO_HANDLE *)calloc(1, sizeof(HIO_HANDLE)); + h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE)); if (h == NULL) { cbclose(f); return NULL; @@ -453,7 +467,7 @@ HIO_HANDLE *hio_open_callbacks(void *priv, struct xmp_callbacks callbacks) return h; } -int hio_close(HIO_HANDLE *h) +static int hio_close_internal(HIO_HANDLE *h) { int ret = -1; @@ -468,7 +482,58 @@ int hio_close(HIO_HANDLE *h) ret = cbclose(h->handle.cbfile); break; } + return ret; +} +/* hio_close + hio_open_mem. Reuses the same HIO_HANDLE. */ +int hio_reopen_mem(const void *ptr, long size, int free_after_use, HIO_HANDLE *h) +{ + MFILE *m; + int ret; + if (size <= 0) return -1; + + m = mopen(ptr, size, free_after_use); + if (m == NULL) { + return -1; + } + + ret = hio_close_internal(h); + if (ret < 0) { + m->free_after_use = 0; + mclose(m); + return ret; + } + + h->type = HIO_HANDLE_TYPE_MEMORY; + h->handle.mem = m; + h->size = size; + return 0; +} + +/* hio_close + hio_open_file. Reuses the same HIO_HANDLE. */ +int hio_reopen_file(FILE *f, int close_after_use, HIO_HANDLE *h) +{ + long size = get_size(f); + int ret; + if (size < 0) { + return -1; + } + + ret = hio_close_internal(h); + if (ret < 0) { + return -1; + } + + h->noclose = !close_after_use; + h->type = HIO_HANDLE_TYPE_FILE; + h->handle.file = f; + h->size = size; + return 0; +} + +int hio_close(HIO_HANDLE *h) +{ + int ret = hio_close_internal(h); free(h); return ret; } diff --git a/internal/c/parts/audio/extras/libxmp-lite/hio.h b/internal/c/parts/audio/extras/libxmp-lite/hio.h index cf164a295..3925a82d5 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/hio.h +++ b/internal/c/parts/audio/extras/libxmp-lite/hio.h @@ -38,10 +38,12 @@ long hio_tell (HIO_HANDLE *); int hio_eof (HIO_HANDLE *); int hio_error (HIO_HANDLE *); HIO_HANDLE *hio_open (const char *, const char *); -HIO_HANDLE *hio_open_mem (const void *, long); +HIO_HANDLE *hio_open_mem (const void *, long, int); HIO_HANDLE *hio_open_file (FILE *); HIO_HANDLE *hio_open_file2 (FILE *);/* allows fclose()ing the file by libxmp */ HIO_HANDLE *hio_open_callbacks (void *, struct xmp_callbacks); +int hio_reopen_mem (const void *, long, int, HIO_HANDLE *); +int hio_reopen_file (FILE *, int, HIO_HANDLE *); int hio_close (HIO_HANDLE *); long hio_size (HIO_HANDLE *); diff --git a/internal/c/parts/audio/extras/libxmp-lite/it.h b/internal/c/parts/audio/extras/libxmp-lite/it.h index 0dc3baa2f..6df35b414 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/it.h +++ b/internal/c/parts/audio/extras/libxmp-lite/it.h @@ -20,6 +20,11 @@ * THE SOFTWARE. */ +#ifndef LIBXMP_LOADERS_IT_H +#define LIBXMP_LOADERS_IT_H + +#include "loader.h" + /* IT flags */ #define IT_STEREO 0x01 #define IT_VOL_OPT 0x02 /* Not recognized */ @@ -27,9 +32,14 @@ #define IT_LINEAR_FREQ 0x08 #define IT_OLD_FX 0x10 #define IT_LINK_GXX 0x20 +#define IT_MIDI_WHEEL 0x40 +#define IT_MIDI_CONFIG 0x80 /* IT special */ #define IT_HAS_MSG 0x01 +#define IT_EDIT_HISTORY 0x02 +#define IT_HIGHLIGHTS 0x04 +#define IT_SPEC_MIDICFG 0x08 /* IT instrument flags */ #define IT_INST_SAMPLE 0x01 @@ -56,6 +66,7 @@ #define IT_CVT_DIFF 0x04 /* Compressed sample flag */ #define IT_CVT_BYTEDIFF 0x08 /* 'safe to ignore' according to ittech.txt */ #define IT_CVT_12BIT 0x10 /* 'safe to ignore' according to ittech.txt */ +#define IT_CVT_ADPCM 0xff /* Special: always indicates Modplug ADPCM4 */ /* IT envelope flags */ #define IT_ENV_ON 0x01 @@ -179,3 +190,7 @@ struct it_sample_header { uint8 vit; /* Vibrato waveform */ }; +int itsex_decompress8(HIO_HANDLE *src, uint8 *dst, int len, int it215); +int itsex_decompress16(HIO_HANDLE *src, int16 *dst, int len, int it215); + +#endif /* LIBXMP_LOADERS_IT_H */ diff --git a/internal/c/parts/audio/extras/libxmp-lite/it_load.c b/internal/c/parts/audio/extras/libxmp-lite/it_load.c index 139282f23..234b6f221 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/it_load.c +++ b/internal/c/parts/audio/extras/libxmp-lite/it_load.c @@ -1,5 +1,5 @@ /* Extended Module Player - * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -30,7 +30,6 @@ #define MAGIC_IMPI MAGIC4('I','M','P','I') #define MAGIC_IMPS MAGIC4('I','M','P','S') - static int it_test(HIO_HANDLE *, char *, const int); static int it_load(struct module_data *, HIO_HANDLE *, const int); @@ -55,7 +54,6 @@ static int it_test(HIO_HANDLE *f, char *t, const int start) #define FX_XTND 0xfe #define L_CHANNELS 64 - static const uint8 fx[32] = { /* */ FX_NONE, /* A */ FX_S3M_SPEED, @@ -83,19 +81,14 @@ static const uint8 fx[32] = { /* W */ FX_GVOL_SLIDE, /* X */ FX_SETPAN, /* Y */ FX_PANBRELLO, - /* Z */ FX_FLT_CUTOFF, - /* ? */ FX_NONE, + /* Z */ FX_MACRO, /* ? */ FX_NONE, + /* / */ FX_MACROSMOOTH, /* ? */ FX_NONE, /* ? */ FX_NONE, /* ? */ FX_NONE }; - -int itsex_decompress8 (HIO_HANDLE *, void *, int, int); -int itsex_decompress16 (HIO_HANDLE *, void *, int, int); - - static void xlat_fx(int c, struct xmp_event *e, uint8 *last_fxp, int new_fx) { uint8 h = MSN(e->fxp), l = LSN(e->fxp); @@ -144,9 +137,16 @@ static void xlat_fx(int c, struct xmp_event *e, uint8 *last_fxp, int new_fx) e->fxt = FX_SETPAN; e->fxp = l << 4; break; - case 0x9: /* 0x91 = set surround */ - e->fxt = FX_SURROUND; - e->fxp = l; + case 0x9: + if (l == 0 || l == 1) { + /* 0x91 = set surround */ + e->fxt = FX_SURROUND; + e->fxp = l; + } else if (l == 0xe || l == 0xf) { + /* 0x9f Play reverse (MPT) */ + e->fxt = FX_REVERSE; + e->fxp = l - 0xe; + } break; case 0xa: /* High offset */ e->fxt = FX_HIOFFSET; @@ -165,18 +165,14 @@ static void xlat_fx(int c, struct xmp_event *e, uint8 *last_fxp, int new_fx) e->fxt = FX_IT_ROWDELAY; e->fxp = l; break; + case 0xf: /* Set parametered macro */ + e->fxt = FX_MACRO_SET; + e->fxp = l; + break; default: e->fxt = e->fxp = 0; } break; - case FX_FLT_CUTOFF: - if (e->fxp > 0x7f && e->fxp < 0x90) { /* Resonance */ - e->fxt = FX_FLT_RESN; - e->fxp = (e->fxp - 0x80) * 16; - } else { /* Cutoff */ - e->fxp *= 2; - } - break; case FX_TREMOR: if (!new_fx && e->fxp != 0) { e->fxp = ((MSN(e->fxp) + 1) << 4) | (LSN(e->fxp) + 1); @@ -258,6 +254,34 @@ static void fix_name(uint8 *s, int l) } +static int load_it_midi_config(struct module_data *m, HIO_HANDLE *f) +{ + int i; + + m->midi = (struct midi_macro_data *) calloc(1, sizeof(struct midi_macro_data)); + if (m->midi == NULL) + return -1; + + /* Skip global MIDI macros */ + if (hio_seek(f, 9 * 32, SEEK_CUR) < 0) + return -1; + + /* SFx macros */ + for (i = 0; i < 16; i++) { + if (hio_read(m->midi->param[i].data, 1, 32, f) < 32) + return -1; + m->midi->param[i].data[31] = '\0'; + } + /* Zxx macros */ + for (i = 0; i < 128; i++) { + if (hio_read(m->midi->fixed[i].data, 1, 32, f) < 32) + return -1; + m->midi->fixed[i].data[31] = '\0'; + } + return 0; +} + + static int read_envelope(struct xmp_envelope *ei, struct it_envelope *env, HIO_HANDLE *f) { @@ -313,7 +337,8 @@ static int read_envelope(struct xmp_envelope *ei, struct it_envelope *env, return 0; } -static void identify_tracker(struct module_data *m, struct it_file_header *ifh) +static void identify_tracker(struct module_data *m, struct it_file_header *ifh, + int pat_before_smp, int *is_mpt_116) { #ifndef LIBXMP_CORE_PLAYER char tracker_name[40]; @@ -337,6 +362,16 @@ static void identify_tracker(struct module_data *m, struct it_file_header *ifh) strcpy(tracker_name, "ModPlug Tracker 1.16"); /* ModPlug Tracker files aren't really IMPM 2.00 */ ifh->cmwt = sample_mode ? 0x100 : 0x214; + *is_mpt_116 = 1; + } else if (ifh->cmwt == 0x0200 && ifh->cwt == 0x0202 && pat_before_smp) { + /* ModPlug Tracker ITs from pre-alpha 4 use tracker + * 0x0202 and format 0x0200. Unfortunately, ITs from + * Impulse Tracker may *also* use this. These MPT ITs + * can be detected because they write patterns before + * samples/instruments. */ + strcpy(tracker_name, "ModPlug Tracker 1.0 pre-alpha"); + ifh->cmwt = sample_mode ? 0x100 : 0x200; + *is_mpt_116 = 1; } else if (ifh->cwt == 0x0216) { strcpy(tracker_name, "Impulse Tracker 2.14v3"); } else if (ifh->cwt == 0x0217) { @@ -352,6 +387,7 @@ static void identify_tracker(struct module_data *m, struct it_file_header *ifh) case 0x7f: if (ifh->cwt == 0x0888) { strcpy(tracker_name, "OpenMPT 1.17"); + *is_mpt_116 = 1; } else if (ifh->cwt == 0x7fff) { strcpy(tracker_name, "munch.py"); } else { @@ -486,7 +522,7 @@ static int load_old_it_instrument(struct xmp_instrument *xxi, HIO_HANDLE *f) xxi->vol = 0x40; if (k) { - xxi->sub = calloc(sizeof(struct xmp_subinstrument), k); + xxi->sub = (struct xmp_subinstrument *) calloc(k, sizeof(struct xmp_subinstrument)); if (xxi->sub == NULL) { return -1; } @@ -637,7 +673,7 @@ static int load_new_it_instrument(struct xmp_instrument *xxi, HIO_HANDLE *f) xxi->vol = i2h.gbv >> 1; if (k) { - xxi->sub = calloc(sizeof(struct xmp_subinstrument), k); + xxi->sub = (struct xmp_subinstrument *) calloc(k, sizeof(struct xmp_subinstrument)); if (xxi->sub == NULL) return -1; @@ -671,7 +707,7 @@ static int load_new_it_instrument(struct xmp_instrument *xxi, HIO_HANDLE *f) return 0; } -static void force_sample_length(struct xmp_sample *xxs, int len) +static void force_sample_length(struct xmp_sample *xxs, struct extra_sample_data *xtra, int len) { xxs->len = len; @@ -680,6 +716,14 @@ static void force_sample_length(struct xmp_sample *xxs, int len) if (xxs->lps >= xxs->len) xxs->flg &= ~XMP_SAMPLE_LOOP; + + if (xtra) { + if (xtra->sue > xxs->len) + xtra->sue = xxs->len; + + if(xtra->sus >= xxs->len) + xxs->flg &= ~(XMP_SAMPLE_SLOOP | XMP_SAMPLE_SLOOP_BIDIR); + } } static int load_it_sample(struct module_data *m, int i, int start, @@ -687,12 +731,13 @@ static int load_it_sample(struct module_data *m, int i, int start, { struct it_sample_header ish; struct xmp_module *mod = &m->mod; - struct xmp_sample *xxs, *xsmp; + struct extra_sample_data *xtra; + struct xmp_sample *xxs; int j, k; uint8 buf[80]; if (sample_mode) { - mod->xxi[i].sub = calloc(sizeof(struct xmp_subinstrument), 1); + mod->xxi[i].sub = (struct xmp_subinstrument *) calloc(1, sizeof(struct xmp_subinstrument)); if (mod->xxi[i].sub == NULL) { return -1; } @@ -711,7 +756,7 @@ static int load_it_sample(struct module_data *m, int i, int start, } xxs = &mod->xxs[i]; - xsmp = &m->xsmp[i]; + xtra = &m->xtra[i]; memcpy(ish.dosname, buf + 4, 12); ish.zero = buf[16]; @@ -749,14 +794,8 @@ static int load_it_sample(struct module_data *m, int i, int start, xxs->flg |= ish.flags & IT_SMP_BSLOOP ? XMP_SAMPLE_SLOOP_BIDIR : 0; if (ish.flags & IT_SMP_SLOOP) { - memcpy(xsmp, xxs, sizeof (struct xmp_sample)); - xsmp->lps = ish.sloopbeg; - xsmp->lpe = ish.sloopend; - xsmp->flg |= XMP_SAMPLE_LOOP; - xsmp->flg &= ~XMP_SAMPLE_LOOP_BIDIR; - if (ish.flags & IT_SMP_BSLOOP) { - xsmp->flg |= XMP_SAMPLE_LOOP_BIDIR; - } + xtra->sus = ish.sloopbeg; + xtra->sue = ish.sloopend; } if (sample_mode) { @@ -827,13 +866,16 @@ static int load_it_sample(struct module_data *m, int i, int start, if (xxs->lpe > xxs->len || xxs->lps >= xxs->lpe) xxs->flg &= ~XMP_SAMPLE_LOOP; + if (ish.convert == IT_CVT_ADPCM) + cvt |= SAMPLE_FLAG_ADPCM; + if (~ish.convert & IT_CVT_SIGNED) cvt |= SAMPLE_FLAG_UNS; /* compressed samples */ if (ish.flags & IT_SMP_COMP) { long min_size, file_len, left; - uint8 *buf; + void *decbuf; int ret; /* Sanity check - the lower bound on IT compressed @@ -853,17 +895,15 @@ static int load_it_sample(struct module_data *m, int i, int start, "resizing to %ld", i, xxs->len, min_size, left, left << 3); - force_sample_length(xxs, left << 3); - if (ish.flags & IT_SMP_SLOOP) - force_sample_length(xsmp, left << 3); + force_sample_length(xxs, xtra, left << 3); } - buf = calloc(1, xxs->len * 2); - if (buf == NULL) + decbuf = (uint8 *) calloc(1, xxs->len * 2); + if (decbuf == NULL) return -1; if (ish.flags & IT_SMP_16BIT) { - itsex_decompress16(f, buf, xxs->len, + itsex_decompress16(f, (int16 *)decbuf, xxs->len, ish.convert & IT_CVT_DIFF); #ifdef WORDS_BIGENDIAN @@ -873,44 +913,19 @@ static int load_it_sample(struct module_data *m, int i, int start, cvt |= SAMPLE_FLAG_BIGEND; #endif } else { - itsex_decompress8(f, buf, xxs->len, + itsex_decompress8(f, (uint8 *)decbuf, xxs->len, ish.convert & IT_CVT_DIFF); } - if (ish.flags & IT_SMP_SLOOP) { - long pos = hio_tell(f); - if (pos < 0) { - free(buf); - return -1; - } - ret = libxmp_load_sample(m, NULL, SAMPLE_FLAG_NOLOAD | - cvt, &m->xsmp[i], buf); - if (ret < 0) { - free(buf); - return -1; - } - hio_seek(f, pos, SEEK_SET); - } - ret = libxmp_load_sample(m, NULL, SAMPLE_FLAG_NOLOAD | cvt, - &mod->xxs[i], buf); + &mod->xxs[i], decbuf); if (ret < 0) { - free(buf); + free(decbuf); return -1; } - free(buf); + free(decbuf); } else { - if (ish.flags & IT_SMP_SLOOP) { - long pos = hio_tell(f); - if (pos < 0) { - return -1; - } - if (libxmp_load_sample(m, f, cvt, &m->xsmp[i], NULL) < 0) - return -1; - hio_seek(f, pos, SEEK_SET); - } - if (libxmp_load_sample(m, f, cvt, &mod->xxs[i], NULL) < 0) return -1; } @@ -927,7 +942,7 @@ static int load_it_pattern(struct module_data *m, int i, int new_fx, uint8 mask[L_CHANNELS]; uint8 last_fxp[64]; - int r, c, pat_len; + int r, c, pat_len, num_rows; uint8 b; r = 0; @@ -937,7 +952,7 @@ static int load_it_pattern(struct module_data *m, int i, int new_fx, memset(&dummy, 0, sizeof(struct xmp_event)); pat_len = hio_read16l(f) /* - 4 */ ; - mod->xxp[i]->rows = hio_read16l(f); + mod->xxp[i]->rows = num_rows = hio_read16l(f); if (libxmp_alloc_tracks_in_pattern(mod, i) < 0) { return -1; @@ -947,7 +962,7 @@ static int load_it_pattern(struct module_data *m, int i, int new_fx, hio_read16l(f); hio_read16l(f); - while (--pat_len >= 0) { + while (r < num_rows && --pat_len >= 0) { b = hio_read8(f); if (hio_error(f)) { return -1; @@ -968,7 +983,7 @@ static int load_it_pattern(struct module_data *m, int i, int new_fx, * real number of channels before loading the patterns and * we don't want to set it to 64 channels. */ - if (c >= mod->chn || r >= mod->xxp[i]->rows) { + if (c >= mod->chn) { event = &dummy; } else { event = &EVENT(i, c, r); @@ -1056,6 +1071,8 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start) uint32 *pp_smp; /* Pointers to samples */ uint32 *pp_pat; /* Pointers to patterns */ int new_fx, sample_mode; + int pat_before_smp = 0; + int is_mpt_116 = 0; LOAD_INIT(); @@ -1087,8 +1104,8 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start) ifh.pwd = hio_read8(f); /* Sanity check */ - if (ifh.gv > 0x80 || ifh.mv > 0x80) { - D_(D_CRIT "invalid gv (%u) or mv (%u)", ifh.gv, ifh.mv); + if (ifh.gv > 0x80) { + D_(D_CRIT "invalid gv (%u)", ifh.gv); goto err; } @@ -1099,6 +1116,11 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start) hio_read(ifh.chpan, 64, 1, f); hio_read(ifh.chvol, 64, 1, f); + if (hio_error(f)) { + D_(D_CRIT "error reading IT header"); + goto err; + } + memcpy(mod->name, ifh.name, sizeof(ifh.name)); /* sizeof(ifh.name) == 26, sizeof(mod->name) == 64. */ mod->name[sizeof(ifh.name)] = '\0'; @@ -1115,18 +1137,18 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start) } if (mod->ins) { - pp_ins = calloc(4, mod->ins); + pp_ins = (uint32 *) calloc(4, mod->ins); if (pp_ins == NULL) goto err; } else { pp_ins = NULL; } - pp_smp = calloc(4, mod->smp); + pp_smp = (uint32 *) calloc(4, mod->smp); if (pp_smp == NULL) goto err2; - pp_pat = calloc(4, mod->pat); + pp_pat = (uint32 *) calloc(4, mod->pat); if (pp_pat == NULL) goto err3; @@ -1178,9 +1200,22 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start) for (i = 0; i < mod->pat; i++) pp_pat[i] = hio_read32l(f); + if ((ifh.flags & IT_MIDI_CONFIG) || (ifh.special & IT_SPEC_MIDICFG)) { + /* Skip edit history if it exists. */ + if (ifh.special & IT_EDIT_HISTORY) { + int skip = hio_read16l(f) * 8; + if (hio_error(f) || (skip && hio_seek(f, skip, SEEK_CUR) < 0)) + goto err4; + } + if (load_it_midi_config(m, f) < 0) + goto err4; + } + if (mod->smp && mod->pat && pp_pat[0] != 0 && pp_pat[0] < pp_smp[0]) + pat_before_smp = 1; + m->c4rate = C4_NTSC_RATE; - identify_tracker(m, &ifh); + identify_tracker(m, &ifh, pat_before_smp, &is_mpt_116); MODULE_INFO(); @@ -1194,14 +1229,6 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start) if (libxmp_init_instrument(m) < 0) goto err4; - /* Alloc extra samples for sustain loop */ - if (mod->smp > 0) { - m->xsmp = calloc(sizeof (struct xmp_sample), mod->smp); - if (m->xsmp == NULL) { - goto err4; - } - } - D_(D_INFO "Instruments: %d", mod->ins); for (i = 0; i < mod->ins; i++) { @@ -1258,7 +1285,7 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start) max_ch = 0; for (i = 0; i < mod->pat; i++) { uint8 mask[L_CHANNELS]; - int pat_len; + int pat_len, num_rows, row; /* If the offset to a pattern is 0, the pattern is empty */ if (pp_pat[i] == 0) @@ -1266,19 +1293,33 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start) hio_seek(f, start + pp_pat[i], SEEK_SET); pat_len = hio_read16l(f) /* - 4 */ ; - hio_read16l(f); + num_rows = hio_read16l(f); memset(mask, 0, L_CHANNELS); hio_read16l(f); hio_read16l(f); - while (--pat_len >= 0) { + /* Sanity check: + * - Impulse Tracker and Schism Tracker allow up to 200 rows. + * - ModPlug Tracker 1.16 allows 256 rows. + * - OpenMPT allows 1024 rows. + */ + if (num_rows > 1024) { + D_(D_WARN "skipping pattern %d (%d rows)", i, num_rows); + pp_pat[i] = 0; + continue; + } + + row = 0; + while (row < num_rows && --pat_len >= 0) { int b = hio_read8(f); if (hio_error(f)) { D_(D_CRIT "error scanning pattern %d", i); goto err4; } - if (b == 0) + if (b == 0) { + row++; continue; + } c = (b - 1) & 63; @@ -1354,9 +1395,8 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start) free(pp_ins); /* Song message */ - if (ifh.special & IT_HAS_MSG) { - if ((m->comment = malloc(ifh.msglen)) != NULL) { + if ((m->comment = (char *)malloc(ifh.msglen)) != NULL) { hio_seek(f, start + ifh.msgofs, SEEK_SET); D_(D_INFO "Message length : %d", ifh.msglen); @@ -1401,8 +1441,15 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start) m->gvolbase = 0x80; m->gvol = ifh.gv; + m->mvolbase = 48; + m->mvol = ifh.mv; m->read_event_type = READ_EVENT_IT; +#ifndef LIBXMP_CORE_PLAYER + if (is_mpt_116) + libxmp_apply_mpt_preamp(m); +#endif + return 0; err4: diff --git a/internal/c/parts/audio/extras/libxmp-lite/itsex.c b/internal/c/parts/audio/extras/libxmp-lite/itsex.c index 55b93fe30..a56dd4fa0 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/itsex.c +++ b/internal/c/parts/audio/extras/libxmp-lite/itsex.c @@ -3,16 +3,22 @@ /* Public domain IT sample decompressor by Olivier Lapicque */ #include "loader.h" +#include "it.h" -static inline uint32 read_bits(HIO_HANDLE *ibuf, uint32 *bitbuf, int *bitnum, int n) +static inline uint32 read_bits(HIO_HANDLE *ibuf, uint32 *bitbuf, int *bitnum, int n, int *err) { uint32 retval = 0; int i = n; - int bnum = *bitnum, bbuf = *bitbuf; + int bnum = *bitnum; + uint32 bbuf = *bitbuf; - if (n > 0) { + if (n > 0 && n <= 32) { do { if (bnum == 0) { + if (hio_eof(ibuf)) { + *err = EOF; + return 0; + } bbuf = hio_read8(ibuf); bnum = 8; } @@ -27,6 +33,10 @@ static inline uint32 read_bits(HIO_HANDLE *ibuf, uint32 *bitbuf, int *bitnum, in *bitnum = bnum; *bitbuf = bbuf; + } else { + /* Invalid shift value. */ + *err = -2; + return 0; } return (retval >> (32 - i)); @@ -41,6 +51,7 @@ int itsex_decompress8(HIO_HANDLE *src, uint8 *dst, int len, int it215) int bitnum = 0; uint8 left = 0, temp = 0, temp2 = 0; uint32 d, pos; + int err = 0; while (len) { if (!block_count) { @@ -58,8 +69,8 @@ int itsex_decompress8(HIO_HANDLE *src, uint8 *dst, int len, int it215) /* Unpacking */ pos = 0; do { - uint16 bits = read_bits(src, &bitbuf, &bitnum, left); - if (hio_eof(src)) + uint16 bits = read_bits(src, &bitbuf, &bitnum, left, &err); + if (err != 0) return -1; if (left < 7) { @@ -67,9 +78,9 @@ int itsex_decompress8(HIO_HANDLE *src, uint8 *dst, int len, int it215) uint32 j = bits & 0xffff; if (i != j) goto unpack_byte; - bits = (read_bits(src, &bitbuf, &bitnum, 3) + bits = (read_bits(src, &bitbuf, &bitnum, 3, &err) + 1) & 0xff; - if (hio_eof(src)) + if (err != 0) return -1; left = ((uint8)bits < left) ? (uint8)bits : @@ -137,6 +148,7 @@ int itsex_decompress16(HIO_HANDLE *src, int16 *dst, int len, int it215) uint8 left = 0; int16 temp = 0, temp2 = 0; uint32 d, pos; + int err = 0; while (len) { if (!block_count) { @@ -154,8 +166,8 @@ int itsex_decompress16(HIO_HANDLE *src, int16 *dst, int len, int it215) /* Unpacking */ pos = 0; do { - uint32 bits = read_bits(src, &bitbuf, &bitnum, left); - if (hio_eof(src)) + uint32 bits = read_bits(src, &bitbuf, &bitnum, left, &err); + if (err != 0) return -1; if (left < 7) { @@ -165,9 +177,8 @@ int itsex_decompress16(HIO_HANDLE *src, int16 *dst, int len, int it215) if (i != j) goto unpack_byte; - bits = read_bits(src, &bitbuf, &bitnum, 4) + 1; - - if (hio_eof(src)) + bits = read_bits(src, &bitbuf, &bitnum, 4, &err) + 1; + if (err != 0) return -1; left = ((uint8)(bits & 0xff) < left) ? diff --git a/internal/c/parts/audio/extras/libxmp-lite/list.h b/internal/c/parts/audio/extras/libxmp-lite/list.h index e284a5eef..2d7926655 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/list.h +++ b/internal/c/parts/audio/extras/libxmp-lite/list.h @@ -1,12 +1,7 @@ #ifndef LIBXMP_LIST_H #define LIBXMP_LIST_H -#ifdef _MSC_VER -#define __inline__ __inline -#endif -#ifdef __WATCOMC__ -#define __inline__ inline -#endif +#include /* offsetof */ /* * Simple doubly linked list implementation. @@ -32,12 +27,12 @@ struct list_head { } while (0) /* - * Insert a new entry between two known consecutive entries. + * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ -static __inline__ void __list_add(struct list_head *_new, +static inline void __list_add(struct list_head *_new, struct list_head * prev, struct list_head * next) { @@ -55,7 +50,7 @@ static __inline__ void __list_add(struct list_head *_new, * Insert a new entry after the specified head. * This is good for implementing stacks. */ -static __inline__ void list_add(struct list_head *_new, struct list_head *head) +static inline void list_add(struct list_head *_new, struct list_head *head) { __list_add(_new, head, head->next); } @@ -68,7 +63,7 @@ static __inline__ void list_add(struct list_head *_new, struct list_head *head) * Insert a new entry before the specified head. * This is useful for implementing queues. */ -static __inline__ void list_add_tail(struct list_head *_new, struct list_head *head) +static inline void list_add_tail(struct list_head *_new, struct list_head *head) { __list_add(_new, head->prev, head); } @@ -80,7 +75,7 @@ static __inline__ void list_add_tail(struct list_head *_new, struct list_head *h * This is only for internal list manipulation where we know * the prev/next entries already! */ -static __inline__ void __list_del(struct list_head * prev, +static inline void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; @@ -91,7 +86,7 @@ static __inline__ void __list_del(struct list_head * prev, * list_del - deletes entry from list. * @entry: the element to delete from the list. */ -static __inline__ void list_del(struct list_head *entry) +static inline void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); } @@ -100,7 +95,7 @@ static __inline__ void list_del(struct list_head *entry) * list_empty - tests whether a list is empty * @head: the list to test. */ -static __inline__ int list_empty(struct list_head *head) +static inline int list_empty(struct list_head *head) { return head->next == head; } @@ -110,7 +105,7 @@ static __inline__ int list_empty(struct list_head *head) * @list: the new list to add. * @head: the place to add it in the first list. */ -static __inline__ void list_splice(struct list_head *list, struct list_head *head) +static inline void list_splice(struct list_head *list, struct list_head *head) { struct list_head *first = list->next; @@ -133,7 +128,7 @@ static __inline__ void list_splice(struct list_head *list, struct list_head *hea * @member: the name of the list_struct within the struct. */ #define list_entry(ptr, type, member) \ - ((type *)((char *)(ptr)-(size_t)(&((type *)0)->member))) + ((type *)((char *)(ptr) - offsetof(type, member))) /** * list_for_each - iterate over a list @@ -143,4 +138,4 @@ static __inline__ void list_splice(struct list_head *list, struct list_head *hea #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) -#endif +#endif /* LIBXMP_LIST_H */ diff --git a/internal/c/parts/audio/extras/libxmp-lite/load.c b/internal/c/parts/audio/extras/libxmp-lite/load.c index 91e926265..9f5f29ffa 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/load.c +++ b/internal/c/parts/audio/extras/libxmp-lite/load.c @@ -1,5 +1,5 @@ /* Extended Module Player - * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -20,16 +20,15 @@ * THE SOFTWARE. */ -#include #include #include "format.h" #include "list.h" #include "hio.h" -#include "tempfile.h" #include "loader.h" #ifndef LIBXMP_NO_DEPACKERS +#include "tempfile.h" #include "depackers/depacker.h" #endif @@ -39,17 +38,13 @@ #endif -extern struct format_loader *format_loaders[]; - void libxmp_load_prologue(struct context_data *); void libxmp_load_epilogue(struct context_data *); int libxmp_prepare_scan(struct context_data *); #ifndef LIBXMP_CORE_PLAYER #define BUFLEN 16384 -#endif -#ifndef LIBXMP_CORE_PLAYER static void set_md5sum(HIO_HANDLE *f, unsigned char *digest) { unsigned char buf[BUFLEN]; @@ -68,18 +63,18 @@ static void set_md5sum(HIO_HANDLE *f, unsigned char *digest) static char *get_dirname(const char *name) { char *dirname; - const char *div; + const char *p; ptrdiff_t len; - if ((div = strrchr(name, '/')) != NULL) { - len = div - name + 1; - dirname = malloc(len + 1); + if ((p = strrchr(name, '/')) != NULL) { + len = p - name + 1; + dirname = (char *) malloc(len + 1); if (dirname != NULL) { memcpy(dirname, name, len); dirname[len] = 0; } } else { - dirname = strdup(""); + dirname = libxmp_strdup(""); } return dirname; @@ -87,13 +82,13 @@ static char *get_dirname(const char *name) static char *get_basename(const char *name) { - const char *div; + const char *p; char *basename; - if ((div = strrchr(name, '/')) != NULL) { - basename = strdup(div + 1); + if ((p = strrchr(name, '/')) != NULL) { + basename = libxmp_strdup(p + 1); } else { - basename = strdup(name); + basename = libxmp_strdup(name); } return basename; @@ -115,7 +110,7 @@ static int test_module(struct xmp_test_info *info, HIO_HANDLE *h) if (format_loaders[i]->test(h, buf, 0) == 0) { int is_prowizard = 0; -#if !defined(LIBXMP_CORE_PLAYER) && !defined(LIBXMP_NO_PROWIZARD) +#ifndef LIBXMP_NO_PROWIZARD if (strcmp(format_loaders[i]->name, "prowizard") == 0) { hio_seek(h, 0, SEEK_SET); pw_test_format(h, buf, 0, info); @@ -140,16 +135,17 @@ static int test_module(struct xmp_test_info *info, HIO_HANDLE *h) int xmp_test_module(const char *path, struct xmp_test_info *info) { HIO_HANDLE *h; - struct stat st; - int ret; #ifndef LIBXMP_NO_DEPACKERS char *temp = NULL; #endif + int ret; - if (stat(path, &st) < 0) + ret = libxmp_get_filetype(path); + + if (ret == XMP_FILETYPE_NONE) { return -XMP_ERROR_SYSTEM; - - if (S_ISDIR(st.st_mode)) { + } + if (ret & XMP_FILETYPE_DIR) { errno = EISDIR; return -XMP_ERROR_SYSTEM; } @@ -158,16 +154,10 @@ int xmp_test_module(const char *path, struct xmp_test_info *info) return -XMP_ERROR_SYSTEM; #ifndef LIBXMP_NO_DEPACKERS - if (libxmp_decrunch(&h, path, &temp) < 0) { + if (libxmp_decrunch(h, path, &temp) < 0) { ret = -XMP_ERROR_DEPACK; goto err; } - - /* get size after decrunch */ - if (hio_size(h) < 256) { /* set minimum valid module size */ - ret = -XMP_ERROR_FORMAT; - goto err; - } #endif ret = test_module(info, h); @@ -191,7 +181,7 @@ int xmp_test_module_from_memory(const void *mem, long size, struct xmp_test_info return -XMP_ERROR_INVALID; } - if ((h = hio_open_mem(mem, size)) == NULL) + if ((h = hio_open_mem(mem, size, 0)) == NULL) return -XMP_ERROR_SYSTEM; ret = test_module(info, h); @@ -212,16 +202,10 @@ int xmp_test_module_from_file(void *file, struct xmp_test_info *info) return -XMP_ERROR_SYSTEM; #ifndef LIBXMP_NO_DEPACKERS - if (libxmp_decrunch(&h, NULL, &temp) < 0) { + if (libxmp_decrunch(h, NULL, &temp) < 0) { ret = -XMP_ERROR_DEPACK; goto err; } - - /* get size after decrunch */ - if (hio_size(h) < 256) { /* set minimum valid module size */ - ret = -XMP_ERROR_FORMAT; - goto err; - } #endif ret = test_module(info, h); @@ -265,7 +249,6 @@ static int load_module(xmp_context opaque, HIO_HANDLE *h) test_result = load_result = -1; for (i = 0; format_loaders[i] != NULL; i++) { hio_seek(h, 0, SEEK_SET); - hio_error(h); /* reset error flag */ D_(D_WARN "test %s", format_loaders[i]->name); test_result = format_loaders[i]->test(h, NULL, 0); @@ -277,11 +260,6 @@ static int load_module(xmp_context opaque, HIO_HANDLE *h) } } -#ifndef LIBXMP_CORE_PLAYER - if (test_result == 0 && load_result == 0) - set_md5sum(h, m->md5); -#endif - if (test_result < 0) { xmp_release_module(opaque); return -XMP_ERROR_FORMAT; @@ -330,6 +308,11 @@ static int load_module(xmp_context opaque, HIO_HANDLE *h) libxmp_adjust_string(mod->xxs[i].name); } +#ifndef LIBXMP_CORE_PLAYER + if (test_result == 0 && load_result == 0) + set_md5sum(h, m->md5); +#endif + libxmp_load_epilogue(ctx); ret = libxmp_prepare_scan(ctx); @@ -358,22 +341,21 @@ int xmp_load_module(xmp_context opaque, const char *path) struct context_data *ctx = (struct context_data *)opaque; #ifndef LIBXMP_CORE_PLAYER struct module_data *m = &ctx->m; - long size; #endif #ifndef LIBXMP_NO_DEPACKERS char *temp_name; #endif HIO_HANDLE *h; - struct stat st; int ret; D_(D_WARN "path = %s", path); - if (stat(path, &st) < 0) { + ret = libxmp_get_filetype(path); + + if (ret == XMP_FILETYPE_NONE) { return -XMP_ERROR_SYSTEM; } - - if (S_ISDIR(st.st_mode)) { + if (ret & XMP_FILETYPE_DIR) { errno = EISDIR; return -XMP_ERROR_SYSTEM; } @@ -384,20 +366,12 @@ int xmp_load_module(xmp_context opaque, const char *path) #ifndef LIBXMP_NO_DEPACKERS D_(D_INFO "decrunch"); - if (libxmp_decrunch(&h, path, &temp_name) < 0) { + if (libxmp_decrunch(h, path, &temp_name) < 0) { ret = -XMP_ERROR_DEPACK; goto err; } #endif -#ifndef LIBXMP_CORE_PLAYER - size = hio_size(h); - if (size < 256) { /* get size after decrunch */ - ret = -XMP_ERROR_FORMAT; - goto err; - } -#endif - if (ctx->state > XMP_STATE_UNLOADED) xmp_release_module(opaque); @@ -415,7 +389,7 @@ int xmp_load_module(xmp_context opaque, const char *path) } m->filename = path; /* For ALM, SSMT, etc */ - m->size = size; + m->size = hio_size(h); #else ctx->m.filename = NULL; ctx->m.dirname = NULL; @@ -452,7 +426,7 @@ int xmp_load_module_from_memory(xmp_context opaque, const void *mem, long size) return -XMP_ERROR_INVALID; } - if ((h = hio_open_mem(mem, size)) == NULL) + if ((h = hio_open_mem(mem, size, 0)) == NULL) return -XMP_ERROR_SYSTEM; if (ctx->state > XMP_STATE_UNLOADED) @@ -579,17 +553,9 @@ void xmp_release_module(xmp_context opaque) } free(m->xtra); + free(m->midi); m->xtra = NULL; - -#ifndef LIBXMP_CORE_DISABLE_IT - if (m->xsmp != NULL) { - for (i = 0; i < mod->smp; i++) { - libxmp_free_sample(&m->xsmp[i]); - } - free(m->xsmp); - m->xsmp = NULL; - } -#endif + m->midi = NULL; libxmp_free_scan(ctx); diff --git a/internal/c/parts/audio/extras/libxmp-lite/load_helpers.c b/internal/c/parts/audio/extras/libxmp-lite/load_helpers.c index 5efb55694..d159a7a47 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/load_helpers.c +++ b/internal/c/parts/audio/extras/libxmp-lite/load_helpers.c @@ -1,5 +1,5 @@ /* Extended Module Player - * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -53,12 +53,15 @@ const struct module_quirk mq[] = { XMP_FLAGS_FIXLOOP, XMP_MODE_AUTO }, +#if 0 /* "siedler ii" (added by Daniel Åkerud) */ + /* Timing fixed by vblank scan compare. CIA: 32m10s VBlank: 12m32s */ { { 0x70, 0xaa, 0x03, 0x4d, 0xfb, 0x2f, 0x1f, 0x73, 0xd9, 0xfd, 0xba, 0xfe, 0x13, 0x1b, 0xb7, 0x01 }, XMP_FLAGS_VBLANK, XMP_MODE_AUTO }, +#endif /* "Klisje paa klisje" (added by Kjetil Torgrim Homme) */ { @@ -99,6 +102,126 @@ const struct module_quirk mq[] = { 0, XMP_MODE_PROTRACKER }, + /* grooving3.mod */ + /* length 150778 crc32c 0xfdcf9aadU */ + { + { 0xdb, 0x61, 0x22, 0x44, 0x39, 0x85, 0x74, 0xe9, + 0xfa, 0x11, 0xb8, 0xfb, 0x87, 0xe8, 0xde, 0xc5, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* mod.Rundgren */ + /* length 195078 crc32c 0x8fa827a4U */ + { + { 0x9a, 0xdb, 0xb2, 0x09, 0x07, 0x1c, 0x44, 0x82, + 0xc5, 0xdf, 0x83, 0x52, 0xcc, 0x73, 0x9f, 0x20, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* dance feeling by Audiomonster */ + /* length 169734 crc32c 0x79fa2c9bU */ + { + { 0x31, 0x2c, 0x3d, 0xaa, 0x5f, 0x1a, 0x54, 0x44, + 0x9d, 0xf7, 0xc4, 0x41, 0x8a, 0xc5, 0x01, 0x02, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* knights melody by Audiomonster */ + /* length 77798 crc32c 0x7bf19c5bU */ + { + { 0x31, 0xc3, 0x0e, 0x32, 0xfc, 0x99, 0x95, 0xd2, + 0x97, 0x20, 0xb3, 0x77, 0x50, 0x05, 0xfe, 0xa5, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* hcomme by Bouffon */ + /* length 71346 crc32c 0x4ad49cb3U */ + { + { 0x6e, 0xf9, 0x78, 0xc1, 0x80, 0xae, 0x51, 0x06, + 0x05, 0x7c, 0x6e, 0xd0, 0x26, 0x7e, 0xfe, 0x3d, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* ((((aquapool)))) by Dolphin */ + /* length 62932 crc32c 0x05b103fcU */ + { + { 0xff, 0x0b, 0xe0, 0x26, 0xc6, 0x31, 0xb5, 0x9b, + 0x94, 0x83, 0x94, 0x99, 0x7e, 0x24, 0x7c, 0xdd, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* 100yarddash by Dr. Awesome */ + /* length 104666 crc32c 0xd2b0e4a6U */ + { + { 0x5b, 0xff, 0x2f, 0xb8, 0xef, 0x3c, 0xbe, 0x55, + 0xa8, 0xe2, 0xa7, 0xcf, 0x5c, 0xbd, 0xdd, 0xb2, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* jazz-reggae-funk by Droid */ + /* length 115564 crc32c 0x41ff635fU */ + { + { 0xe5, 0x6e, 0x31, 0x2f, 0x62, 0x80, 0xc1, 0x9d, + 0x2f, 0x24, 0x54, 0xf3, 0x89, 0x3f, 0x94, 0x6c, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* hard and heavy by Fish */ + /* length 69814 crc32c 0x1f09d3d5U */ + { + { 0x6b, 0xce, 0x39, 0x94, 0x75, 0x42, 0x06, 0x74, + 0xd2, 0x83, 0xbc, 0x5e, 0x7b, 0x42, 0x1f, 0xa0, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* crazy valley by Julius and Droid */ + /* length 97496 crc32c 0xb8eec40eU */ + { + { 0x23, 0x77, 0x18, 0x1d, 0x21, 0x9b, 0x41, 0x8f, + 0xc1, 0xb4, 0xf4, 0xf8, 0x22, 0xdd, 0xd8, 0xb6, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* THE ILLOGICAL ONE by Rhino */ + /* length 173432 crc32c 0xcb4e2987U */ + { + { 0xd8, 0xc2, 0xbb, 0xe6, 0x11, 0xd0, 0x5c, 0x02, + 0x8e, 0x3b, 0xcb, 0x7c, 0x4a, 0x7d, 0x43, 0xa0, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* sounds of holiday by Spacebrain */ + /* length 309520 crc32c 0x28804a57U */ + { + { 0x36, 0x18, 0x19, 0xa4, 0x9d, 0xa2, 0xa2, 0x6f, + 0x58, 0x60, 0xc4, 0xd9, 0x0d, 0xa2, 0x9f, 0x49, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* sunisinus by Speed-Head */ + /* length 175706 crc32c 0x2e56451bU */ + { + { 0x7e, 0x69, 0x44, 0xb6, 0x38, 0x0d, 0x27, 0x14, + 0x70, 0x5d, 0x44, 0xce, 0xce, 0xdd, 0x37, 0x31, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* eat the fulcrum bop by The Assassin */ + /* length 160286 crc32c 0x583a4683U */ + { + { 0x11, 0xe9, 0x6f, 0x62, 0xe1, 0xc3, 0xc5, 0xcc, + 0x3b, 0xaf, 0xea, 0x69, 0x4b, 0xce, 0x5f, 0xec, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* obvious disaster by Tip */ + /* length 221086 crc32c 0x51c6d489U */ + { + { 0x06, 0x8e, 0x69, 0x01, 0x49, 0x8f, 0xbd, 0x0f, + 0xfc, 0xb7, 0x8f, 0x2a, 0x91, 0xe1, 0x8b, 0xe8, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* alien nation by Turtle */ + /* length 167548 crc32c 0xc9ec1674U */ + { + { 0x71, 0xdf, 0x11, 0xac, 0x5d, 0xec, 0x07, 0xf8, + 0x10, 0x6f, 0x28, 0x8d, 0x47, 0x59, 0x54, 0x9b, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* illusions!2 by Zuhl */ + /* length 289770 crc32c 0x6bf5fbcfU */ + { + { 0xca, 0x37, 0x8c, 0x0e, 0x87, 0x4f, 0x1e, 0xcd, + 0xa3, 0xe9, 0x8b, 0xdd, 0x11, 0x46, 0x8d, 0x69, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, @@ -127,7 +250,7 @@ char *libxmp_adjust_string(char *s) int i; for (i = 0; i < strlen(s); i++) { - if (!isprint((int)s[i]) || ((uint8) s[i] > 127)) + if (!isprint((unsigned char)s[i]) || ((uint8) s[i] > 127)) s[i] = ' '; } @@ -150,9 +273,21 @@ static void check_envelope(struct xmp_envelope *env) env->flg &= ~XMP_ENVELOPE_LOOP; } - /* Disable envelope loop if invalid sustain */ - if (env->sus >= env->npt) { - env->flg &= ~XMP_ENVELOPE_ON; + /* Disable envelope sustain if invalid sustain */ + if (env->sus >= env->npt || env->sue >= env->npt) { + env->flg &= ~XMP_ENVELOPE_SUS; + } +} + +static void clamp_volume_envelope(struct module_data *m, struct xmp_envelope *env) +{ + /* Clamp broken values in the volume envelope to the expected range. */ + if (env->flg & XMP_ENVELOPE_ON) { + int i; + for (i = 0; i < env->npt; i++) { + int16 *data = &env->data[i * 2 + 1]; + CLAMP(*data, 0, m->volbase); + } } } @@ -171,26 +306,25 @@ void libxmp_load_prologue(struct context_data *ctx) m->quirk = 0; m->read_event_type = READ_EVENT_MOD; m->period_type = PERIOD_AMIGA; + m->compare_vblank = 0; m->comment = NULL; m->scan_cnt = NULL; + m->midi = NULL; /* Set defaults */ - m->mod.pat = 0; - m->mod.trk = 0; - m->mod.chn = 4; - m->mod.ins = 0; - m->mod.smp = 0; - m->mod.spd = 6; - m->mod.bpm = 125; - m->mod.len = 0; - m->mod.rst = 0; + m->mod.pat = 0; + m->mod.trk = 0; + m->mod.chn = 4; + m->mod.ins = 0; + m->mod.smp = 0; + m->mod.spd = 6; + m->mod.bpm = 125; + m->mod.len = 0; + m->mod.rst = 0; #ifndef LIBXMP_CORE_PLAYER m->extra = NULL; #endif -#ifndef LIBXMP_CORE_DISABLE_IT - m->xsmp = NULL; -#endif m->time_factor = DEFAULT_TIME_FACTOR; @@ -209,7 +343,7 @@ void libxmp_load_epilogue(struct context_data *ctx) struct xmp_module *mod = &m->mod; int i, j; - mod->gvl = m->gvol; + mod->gvl = m->gvol; /* Sanity check for module parameters */ CLAMP(mod->len, 0, XMP_MAX_MOD_LENGTH); @@ -230,7 +364,7 @@ void libxmp_load_epilogue(struct context_data *ctx) if (mod->spd <= 0 || mod->spd > 255) { mod->spd = 6; } - CLAMP(mod->bpm, XMP_MIN_BPM, 255); + CLAMP(mod->bpm, XMP_MIN_BPM, 1000); /* Set appropriate values for instrument volumes and subinstrument * global volumes when QUIRK_INSVOL is not set, to keep volume values @@ -254,8 +388,28 @@ void libxmp_load_epilogue(struct context_data *ctx) check_envelope(&mod->xxi[i].aei); check_envelope(&mod->xxi[i].fei); check_envelope(&mod->xxi[i].pei); + clamp_volume_envelope(m, &mod->xxi[i].aei); } +#ifndef LIBXMP_CORE_DISABLE_IT + /* TODO: there's no unintrusive and clean way to get this struct into + * libxmp_load_sample currently, so bound these fields here for now. */ + for (i = 0; i < mod->smp; i++) { + struct xmp_sample *xxs = &mod->xxs[i]; + struct extra_sample_data *xtra = &m->xtra[i]; + if (xtra->sus < 0) { + xtra->sus = 0; + } + if (xtra->sue > xxs->len) { + xtra->sue = xxs->len; + } + if (xtra->sus >= xxs->len || xtra->sus >= xtra->sue) { + xtra->sus = xtra->sue = 0; + xxs->flg &= ~(XMP_SAMPLE_SLOOP | XMP_SAMPLE_SLOOP_BIDIR); + } + } +#endif + p->filter = 0; p->mode = XMP_MODE_AUTO; p->flags = p->player_flags; @@ -283,7 +437,7 @@ int libxmp_prepare_scan(struct context_data *ctx) return 0; } - m->scan_cnt = calloc(sizeof (uint8 *), mod->len); + m->scan_cnt = (uint8 **) calloc(mod->len, sizeof(uint8 *)); if (m->scan_cnt == NULL) return -XMP_ERROR_SYSTEM; @@ -299,7 +453,7 @@ int libxmp_prepare_scan(struct context_data *ctx) } pat = pat_idx >= mod->pat ? NULL : mod->xxp[pat_idx]; - m->scan_cnt[i] = calloc(1, pat && pat->rows ? pat->rows : 1); + m->scan_cnt[i] = (uint8 *) calloc(1, (pat && pat->rows)? pat->rows : 1); if (m->scan_cnt[i] == NULL) return -XMP_ERROR_SYSTEM; } @@ -309,6 +463,7 @@ int libxmp_prepare_scan(struct context_data *ctx) void libxmp_free_scan(struct context_data *ctx) { + struct player_data *p = &ctx->p; struct module_data *m = &ctx->m; struct xmp_module *mod = &m->mod; int i; @@ -320,6 +475,9 @@ void libxmp_free_scan(struct context_data *ctx) free(m->scan_cnt); m->scan_cnt = NULL; } + + free(p->scan); + p->scan = NULL; } /* Process player personality flags */ @@ -394,6 +552,9 @@ int libxmp_set_player_mode(struct context_data *ctx) return -1; } + if (p->mode != XMP_MODE_AUTO) + m->compare_vblank = 0; + return 0; } diff --git a/internal/c/parts/audio/extras/libxmp-lite/loader.h b/internal/c/parts/audio/extras/libxmp-lite/loader.h index 65a59d905..a84ac035b 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/loader.h +++ b/internal/c/parts/audio/extras/libxmp-lite/loader.h @@ -20,6 +20,10 @@ #define SAMPLE_FLAG_HSC 0x2000 /* HSC Adlib synth instrument */ #define SAMPLE_FLAG_ADPCM 0x4000 /* ADPCM4 encoded samples */ +/* libxmp_test_name flags */ +#define TEST_NAME_IGNORE_AFTER_0 0x0001 +#define TEST_NAME_IGNORE_AFTER_CR 0x0002 + #define DEFPAN(x) (0x80 + ((x) - 0x80) * m->defpan / 100) int libxmp_init_instrument (struct module_data *); @@ -30,16 +34,18 @@ int libxmp_alloc_pattern (struct xmp_module *, int); int libxmp_alloc_track (struct xmp_module *, int, int); int libxmp_alloc_tracks_in_pattern (struct xmp_module *, int); int libxmp_alloc_pattern_tracks (struct xmp_module *, int, int); +#ifndef LIBXMP_CORE_PLAYER int libxmp_alloc_pattern_tracks_long(struct xmp_module *, int, int); +#endif char *libxmp_instrument_name (struct xmp_module *, int, uint8 *, int); char *libxmp_copy_adjust (char *, uint8 *, int); int libxmp_copy_name_for_fopen (char *, const char *, int); -int libxmp_test_name (uint8 *, int); +int libxmp_test_name (const uint8 *, int, int); void libxmp_read_title (HIO_HANDLE *, char *, int); void libxmp_set_xxh_defaults (struct xmp_module *); -void libxmp_decode_protracker_event (struct xmp_event *, uint8 *); -void libxmp_decode_noisetracker_event(struct xmp_event *, uint8 *); +void libxmp_decode_protracker_event (struct xmp_event *, const uint8 *); +void libxmp_decode_noisetracker_event(struct xmp_event *, const uint8 *); void libxmp_disable_continue_fx (struct xmp_event *); int libxmp_check_filename_case (const char *, const char *, char *, int); void libxmp_get_instrument_path (struct module_data *, char *, int); @@ -47,7 +53,10 @@ void libxmp_set_type (struct module_data *, const char *, ...); int libxmp_load_sample (struct module_data *, HIO_HANDLE *, int, struct xmp_sample *, const void *); void libxmp_free_sample (struct xmp_sample *); +#ifndef LIBXMP_CORE_PLAYER void libxmp_schism_tracker_string (char *, size_t, int, int); +void libxmp_apply_mpt_preamp (struct module_data *m); +#endif extern uint8 libxmp_ord_xlat[]; extern const int libxmp_arch_vol_table[]; diff --git a/internal/c/parts/audio/extras/libxmp-lite/md5.h b/internal/c/parts/audio/extras/libxmp-lite/md5.h index 7cac7170f..b5c44bac9 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/md5.h +++ b/internal/c/parts/audio/extras/libxmp-lite/md5.h @@ -25,15 +25,13 @@ typedef struct MD5Context { uint8 buffer[MD5_BLOCK_LENGTH]; /* input buffer */ } MD5_CTX; -#ifdef __cplusplus -extern "C" { -#endif +LIBXMP_BEGIN_DECLS + void MD5Init(MD5_CTX *); void MD5Update(MD5_CTX *, const unsigned char *, size_t); void MD5Final(uint8[MD5_DIGEST_LENGTH], MD5_CTX *); -#ifdef __cplusplus -} -#endif + +LIBXMP_END_DECLS #endif /* LIBXMP_MD5_H */ diff --git a/internal/c/parts/audio/extras/libxmp-lite/mdataio.h b/internal/c/parts/audio/extras/libxmp-lite/mdataio.h index 49504d634..c076c0894 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/mdataio.h +++ b/internal/c/parts/audio/extras/libxmp-lite/mdataio.h @@ -2,7 +2,6 @@ #define LIBXMP_MDATAIO_H #include -#include #include "common.h" static inline ptrdiff_t CAN_READ(MFILE *m) @@ -43,7 +42,7 @@ static inline uint16 mread16l(MFILE *m, int *err) } else { m->pos += can_read; if(err) *err = EOF; - return EOF; + return 0xffff; } } @@ -58,7 +57,7 @@ static inline uint16 mread16b(MFILE *m, int *err) } else { m->pos += can_read; if(err) *err = EOF; - return EOF; + return 0xffff; } } @@ -73,7 +72,7 @@ static inline uint32 mread24l(MFILE *m, int *err) } else { m->pos += can_read; if(err) *err = EOF; - return EOF; + return 0xffffffff; } } @@ -88,7 +87,7 @@ static inline uint32 mread24b(MFILE *m, int *err) } else { m->pos += can_read; if(err) *err = EOF; - return EOF; + return 0xffffffff; } } @@ -103,7 +102,7 @@ static inline uint32 mread32l(MFILE *m, int *err) } else { m->pos += can_read; if(err) *err = EOF; - return EOF; + return 0xffffffff; } } @@ -118,7 +117,7 @@ static inline uint32 mread32b(MFILE *m, int *err) } else { m->pos += can_read; if(err) *err = EOF; - return EOF; + return 0xffffffff; } } diff --git a/internal/c/parts/audio/extras/libxmp-lite/memio.c b/internal/c/parts/audio/extras/libxmp-lite/memio.c index 23bce7a7f..b3b61cd54 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/memio.c +++ b/internal/c/parts/audio/extras/libxmp-lite/memio.c @@ -46,13 +46,16 @@ size_t mread(void *buf, size_t size, size_t num, MFILE *m) } if (should_read > can_read) { - should_read = can_read; + memcpy(buf, m->start + m->pos, can_read); + m->pos += can_read; + + return can_read / size; + } else { + memcpy(buf, m->start + m->pos, should_read); + m->pos += should_read; + + return num; } - - memcpy(buf, m->start + m->pos, should_read); - m->pos += should_read; - - return should_read / size; } @@ -89,23 +92,26 @@ int meof(MFILE *m) return CAN_READ(m) <= 0; } -MFILE *mopen(const void *ptr, long size) +MFILE *mopen(const void *ptr, long size, int free_after_use) { MFILE *m; - m = (MFILE *)malloc(sizeof (MFILE)); + m = (MFILE *) malloc(sizeof(MFILE)); if (m == NULL) return NULL; - m->start = ptr; + m->start = (const unsigned char *)ptr; m->pos = 0; m->size = size; + m->free_after_use = free_after_use; return m; } int mclose(MFILE *m) { + if (m->free_after_use) + free((void *)m->start); free(m); return 0; } diff --git a/internal/c/parts/audio/extras/libxmp-lite/memio.h b/internal/c/parts/audio/extras/libxmp-lite/memio.h index 3fa4e003e..66bdc0921 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/memio.h +++ b/internal/c/parts/audio/extras/libxmp-lite/memio.h @@ -2,18 +2,18 @@ #define LIBXMP_MEMIO_H #include +#include "common.h" typedef struct { const unsigned char *start; ptrdiff_t pos; ptrdiff_t size; + int free_after_use; } MFILE; -#ifdef __cplusplus -extern "C" { -#endif +LIBXMP_BEGIN_DECLS -MFILE *mopen(const void *, long); +MFILE *mopen(const void *, long, int); int mgetc(MFILE *stream); size_t mread(void *, size_t, size_t, MFILE *); int mseek(MFILE *, long, int); @@ -21,8 +21,6 @@ long mtell(MFILE *); int mclose(MFILE *); int meof(MFILE *); -#ifdef __cplusplus -} -#endif +LIBXMP_END_DECLS #endif diff --git a/internal/c/parts/audio/extras/libxmp-lite/mix_all.c b/internal/c/parts/audio/extras/libxmp-lite/mix_all.c index a09188ff4..b39826c67 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/mix_all.c +++ b/internal/c/parts/audio/extras/libxmp-lite/mix_all.c @@ -1,5 +1,5 @@ /* Extended Module Player - * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -99,10 +99,15 @@ *(buffer++) += smp_in * (old_vl >> 8); old_vl += delta_l; \ } while (0) +/* IT's WAV output driver uses a clamp that seems to roughly match this: + * compare the WAV output of OpenMPT env-flt-max.it and filter-reset.it */ +#define MIX_FILTER_CLAMP(a) CLAMP((a), -65536, 65535) + #define MIX_MONO_FILTER() do { \ - sl = (a0 * smp_in * vl + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \ + sl = (a0 * smp_in + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \ + MIX_FILTER_CLAMP(sl); \ fl2 = fl1; fl1 = sl; \ - *(buffer++) += sl; \ + *(buffer++) += sl * vl; \ } while (0) #define MIX_MONO_FILTER_AC() do { \ @@ -122,12 +127,14 @@ } while (0) #define MIX_STEREO_FILTER() do { \ - sr = (a0 * smp_in * vr + b0 * fr1 + b1 * fr2) >> FILTER_SHIFT; \ + sr = (a0 * smp_in + b0 * fr1 + b1 * fr2) >> FILTER_SHIFT; \ + MIX_FILTER_CLAMP(sr); \ fr2 = fr1; fr1 = sr; \ - sl = (a0 * smp_in * vl + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \ + sl = (a0 * smp_in + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \ + MIX_FILTER_CLAMP(sl); \ fl2 = fl1; fl1 = sl; \ - *(buffer++) += sr; \ - *(buffer++) += sl; \ + *(buffer++) += sr * vr; \ + *(buffer++) += sl * vl; \ } while (0) #define MIX_STEREO_FILTER_AC() do { \ @@ -138,17 +145,19 @@ old_vl += delta_l; \ } while (0) -#define MIX_STEREO_FILTER_AC() do { \ - int vr = old_vr >> 8; \ - int vl = old_vl >> 8; \ - MIX_STEREO_FILTER(); \ - old_vr += delta_r; \ - old_vl += delta_l; \ +/* For "nearest" to be nearest neighbor (instead of floor), the position needs + * to be rounded. This only needs to be done once at the start of mixing, and + * is required for reverse samples to round the same as forward samples. + */ +#define NEAREST_ROUND() do { \ + frac += (1 << (SMIX_SHIFT - 1)); \ + pos += frac >> SMIX_SHIFT; \ + frac &= SMIX_MASK; \ } while (0) #define VAR_NORM(x) \ register int smp_in; \ - x *sptr = vi->sptr; \ + x *sptr = (x *)vi->sptr; \ unsigned int pos = vi->pos; \ int frac = (1 << SMIX_SHIFT) * (vi->pos - (int)vi->pos) @@ -194,6 +203,10 @@ #endif +#ifdef _MSC_VER +#pragma warning(disable:4457) /* shadowing */ +#endif + /* * Nearest neighbor mixers @@ -204,6 +217,7 @@ MIXER(mono_8bit_nearest) { VAR_NORM(int8); + NEAREST_ROUND(); LOOP { NEAREST_NEIGHBOR(); MIX_MONO(); UPDATE_POS(); } } @@ -214,6 +228,7 @@ MIXER(mono_8bit_nearest) MIXER(mono_16bit_nearest) { VAR_NORM(int16); + NEAREST_ROUND(); LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_MONO(); UPDATE_POS(); } } @@ -223,6 +238,7 @@ MIXER(mono_16bit_nearest) MIXER(stereo_8bit_nearest) { VAR_NORM(int8); + NEAREST_ROUND(); LOOP { NEAREST_NEIGHBOR(); MIX_STEREO(); UPDATE_POS(); } } @@ -232,6 +248,7 @@ MIXER(stereo_8bit_nearest) MIXER(stereo_16bit_nearest) { VAR_NORM(int16); + NEAREST_ROUND(); LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_STEREO(); UPDATE_POS(); } } diff --git a/internal/c/parts/audio/extras/libxmp-lite/mixer.c b/internal/c/parts/audio/extras/libxmp-lite/mixer.c index 70f41f562..168f5d515 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/mixer.c +++ b/internal/c/parts/audio/extras/libxmp-lite/mixer.c @@ -1,5 +1,5 @@ /* Extended Module Player - * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -45,8 +45,24 @@ #define LIM16_HI 32767 #define LIM16_LO -32768 +struct loop_data +{ +#define LOOP_PROLOGUE 1 +#define LOOP_EPILOGUE 2 + void *sptr; + int start; + int end; + int first_loop; + int _16bit; + int active; + uint32 prologue[LOOP_PROLOGUE]; + uint32 epilogue[LOOP_EPILOGUE]; +}; + #define MIX_FN(x) void libxmp_mix_##x(struct mixer_voice *, int32 *, int, int, int, int, int, int, int) +#define ANTICLICK_FPSHIFT 24 + MIX_FN(mono_8bit_nearest); MIX_FN(mono_8bit_linear); MIX_FN(mono_16bit_nearest); @@ -209,8 +225,10 @@ static void do_anticlick(struct context_data *ctx, int voc, int32 *buf, int coun struct player_data *p = &ctx->p; struct mixer_data *s = &ctx->s; struct mixer_voice *vi = &p->virt.voice_array[voc]; - int smp_l, smp_r, max_x2; + int smp_l, smp_r; int discharge = s->ticksize >> ANTICLICK_SHIFT; + int stepmul, stepval; + uint32 stepmul_sq; smp_r = vi->sright; smp_l = vi->sleft; @@ -231,14 +249,23 @@ static void do_anticlick(struct context_data *ctx, int voc, int32 *buf, int coun return; } - max_x2 = count * count; + stepval = (1 << ANTICLICK_FPSHIFT) / count; + stepmul = stepval * count; - while (count--) { - if (~s->format & XMP_FORMAT_MONO) { - *buf++ += (count * (smp_r >> 10) / max_x2 * count) << 10; + if (~s->format & XMP_FORMAT_MONO) { + while ((stepmul -= stepval) > 0) { + /* Truncate to 16-bits of precision so the product is 32-bits. */ + stepmul_sq = stepmul >> (ANTICLICK_FPSHIFT - 16); + stepmul_sq *= stepmul_sq; + *buf++ += (stepmul_sq * (int64)smp_r) >> 32; + *buf++ += (stepmul_sq * (int64)smp_l) >> 32; + } + } else { + while ((stepmul -= stepval) > 0) { + stepmul_sq = stepmul >> (ANTICLICK_FPSHIFT - 16); + stepmul_sq *= stepmul_sq; + *buf++ += (stepmul_sq * (int64)smp_l) >> 32; } - - *buf++ += (count * (smp_l >> 10) / max_x2 * count) << 10; } } @@ -256,6 +283,7 @@ static void set_sample_end(struct context_data *ctx, int voc, int end) if (end) { SET_NOTE(NOTE_SAMPLE_END); + vi->fidx &= ~FLAG_ACTIVE; if (HAS_QUIRK(QUIRK_RSTCHN)) { libxmp_virt_resetvoice(ctx, voc, 0); } @@ -264,45 +292,165 @@ static void set_sample_end(struct context_data *ctx, int voc, int end) } } -static void adjust_voice_end(struct mixer_voice *vi, struct xmp_sample *xxs) +/* Back up sample data before and after loop and replace it for interpolation. + * TODO: use an overlap buffer like OpenMPT? This is easier, but a little dirty. */ +static void init_sample_wraparound(struct mixer_data *s, struct loop_data *ld, + struct mixer_voice *vi, struct xmp_sample *xxs) { - if (xxs->flg & XMP_SAMPLE_LOOP) { + int bidir; + int i; + + if (!vi->sptr || s->interp == XMP_INTERP_NEAREST || (~xxs->flg & XMP_SAMPLE_LOOP)) { + ld->active = 0; + return; + } + + ld->sptr = vi->sptr; + ld->start = vi->start; + ld->end = vi->end; + ld->first_loop = !(vi->flags & SAMPLE_LOOP); + ld->_16bit = (xxs->flg & XMP_SAMPLE_16BIT); + ld->active = 1; + + bidir = vi->flags & VOICE_BIDIR; + + if (ld->_16bit) { + uint16 *start = (uint16 *)vi->sptr + vi->start; + uint16 *end = (uint16 *)vi->sptr + vi->end; + + if (!ld->first_loop) { + for (i = 0; i < LOOP_PROLOGUE; i++) { + int j = i - LOOP_PROLOGUE; + ld->prologue[i] = start[j]; + start[j] = bidir ? start[-1 - j] : end[j]; + } + } + for (i = 0; i < LOOP_EPILOGUE; i++) { + ld->epilogue[i] = end[i]; + end[i] = bidir ? end[-1 - i] : start[i]; + } + } else { + uint8 *start = (uint8 *)vi->sptr + vi->start; + uint8 *end = (uint8 *)vi->sptr + vi->end; + + if (!ld->first_loop) { + for (i = 0; i < LOOP_PROLOGUE; i++) { + int j = i - LOOP_PROLOGUE; + ld->prologue[i] = start[j]; + start[j] = bidir ? start[-1 - j] : end[j]; + } + } + for (i = 0; i < LOOP_EPILOGUE; i++) { + ld->epilogue[i] = end[i]; + end[i] = bidir ? end[-1 - i] : start[i]; + } + } +} + +/* Restore old sample data from before and after loop. */ +static void reset_sample_wraparound(struct loop_data *ld) +{ + int i; + + if (!ld->active) + return; + + if (ld->_16bit) { + uint16 *start = (uint16 *)ld->sptr + ld->start; + uint16 *end = (uint16 *)ld->sptr + ld->end; + + if (!ld->first_loop) { + for (i = 0; i < LOOP_PROLOGUE; i++) + start[i - LOOP_PROLOGUE] = ld->prologue[i]; + } + for (i = 0; i < LOOP_EPILOGUE; i++) + end[i] = ld->epilogue[i]; + } else { + uint8 *start = (uint8 *)ld->sptr + ld->start; + uint8 *end = (uint8 *)ld->sptr + ld->end; + + if (!ld->first_loop) { + for (i = 0; i < LOOP_PROLOGUE; i++) + start[i - LOOP_PROLOGUE] = ld->prologue[i]; + } + for (i = 0; i < LOOP_EPILOGUE; i++) + end[i] = ld->epilogue[i]; + } +} + +static int has_active_sustain_loop(struct context_data *ctx, struct mixer_voice *vi, + struct xmp_sample *xxs) +{ +#ifndef LIBXMP_CORE_DISABLE_IT + struct module_data *m = &ctx->m; + return vi->smp < m->mod.smp && (xxs->flg & XMP_SAMPLE_SLOOP) && (~vi->flags & VOICE_RELEASE); +#else + return 0; +#endif +} + +static int has_active_loop(struct context_data *ctx, struct mixer_voice *vi, + struct xmp_sample *xxs) +{ + return (xxs->flg & XMP_SAMPLE_LOOP) || has_active_sustain_loop(ctx, vi, xxs); +} + +/* Update the voice endpoints based on current sample loop state. */ +static void adjust_voice_end(struct context_data *ctx, struct mixer_voice *vi, + struct xmp_sample *xxs, struct extra_sample_data *xtra) +{ + vi->flags &= ~VOICE_BIDIR; + + if (xtra && has_active_sustain_loop(ctx, vi, xxs)) { + vi->start = xtra->sus; + vi->end = xtra->sue; + if (xxs->flg & XMP_SAMPLE_SLOOP_BIDIR) vi->flags |= VOICE_BIDIR; + + } else if (xxs->flg & XMP_SAMPLE_LOOP) { + vi->start = xxs->lps; if ((xxs->flg & XMP_SAMPLE_LOOP_FULL) && (~vi->flags & SAMPLE_LOOP)) { vi->end = xxs->len; } else { vi->end = xxs->lpe; + if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) vi->flags |= VOICE_BIDIR; } } else { + vi->start = 0; vi->end = xxs->len; } } -static void loop_reposition(struct context_data *ctx, struct mixer_voice *vi, struct xmp_sample *xxs) +static int loop_reposition(struct context_data *ctx, struct mixer_voice *vi, + struct xmp_sample *xxs, struct extra_sample_data *xtra) { -#ifndef LIBXMP_CORE_DISABLE_IT - struct module_data *m = &ctx->m; -#endif - int loop_size = xxs->lpe - xxs->lps; + int loop_changed = !(vi->flags & SAMPLE_LOOP); - /* Reposition for next loop */ - vi->pos -= loop_size; /* forward loop */ - vi->end = xxs->lpe; vi->flags |= SAMPLE_LOOP; - if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) { - vi->end += loop_size; /* unrolled loop */ - vi->pos -= loop_size; /* forward loop */ + if(loop_changed) + adjust_voice_end(ctx, vi, xxs, xtra); -#ifndef LIBXMP_CORE_DISABLE_IT - /* OpenMPT Bidi-Loops.it: "In Impulse Tracker’s software mixer, - * ping-pong loops are shortened by one sample. - */ - if (IS_PLAYER_MODE_IT()) { - vi->end--; - vi->pos++; + if (~vi->flags & VOICE_BIDIR) { + /* Reposition for next loop */ + if (~vi->flags & VOICE_REVERSE) + vi->pos -= vi->end - vi->start; + else + vi->pos += vi->end - vi->start; + } else { + /* Bidirectional loop: switch directions */ + vi->flags ^= VOICE_REVERSE; + + /* Wrap voice position around endpoint */ + if (vi->flags & VOICE_REVERSE) { + /* OpenMPT Bidi-Loops.it: "In Impulse Tracker's software + * mixer, ping-pong loops are shortened by one sample." + */ + vi->pos = vi->end * 2 - ctx->s.bidir_adjust - vi->pos; + } else { + vi->pos = vi->start * 2 - vi->pos; } -#endif } + return loop_changed; } @@ -316,7 +464,10 @@ void libxmp_mixer_prepare(struct context_data *ctx) s->ticksize = s->freq * m->time_factor * m->rrate / p->bpm / 1000; - bytelen = s->ticksize * sizeof(int); + if (s->ticksize < (1 << ANTICLICK_SHIFT)) + s->ticksize = 1 << ANTICLICK_SHIFT; + + bytelen = s->ticksize * sizeof(int32); if (~s->format & XMP_FORMAT_MONO) { bytelen *= 2; } @@ -331,13 +482,14 @@ void libxmp_mixer_softmixer(struct context_data *ctx) struct mixer_data *s = &ctx->s; struct module_data *m = &ctx->m; struct xmp_module *mod = &m->mod; + struct extra_sample_data *xtra; struct xmp_sample *xxs; struct mixer_voice *vi; - double step; + struct loop_data loop_data; + double step, step_dir; int samples, size; - int vol_l, vol_r, voc, usmp; + int vol, vol_l, vol_r, voc, usmp; int prev_l, prev_r = 0; - int lps, lpe; int32 *buf_pos; MIX_FP mix_fn; MIX_FP *mixerset; @@ -368,6 +520,13 @@ void libxmp_mixer_softmixer(struct context_data *ctx) } #endif +#ifndef LIBXMP_CORE_DISABLE_IT + /* OpenMPT Bidi-Loops.it: "In Impulse Tracker's software + * mixer, ping-pong loops are shortened by one sample." + */ + s->bidir_adjust = IS_PLAYER_MODE_IT() ? 1 : 0; +#endif + libxmp_mixer_prepare(ctx); for (voc = 0; voc < p->virt.maxvoc; voc++) { @@ -391,66 +550,56 @@ void libxmp_mixer_softmixer(struct context_data *ctx) continue; } + /* Negative positions can be left over from some + * loop edge cases. These can be safely clamped. */ + if (vi->pos < 0.0) + vi->pos = 0.0; + vi->pos0 = vi->pos; buf_pos = s->buf32; + vol = vi->vol; + + /* Mix volume (S3M and IT) */ + if (m->mvolbase > 0 && m->mvol != m->mvolbase) { + vol = vol * m->mvol / m->mvolbase; + } + if (vi->pan == PAN_SURROUND) { - vol_r = vi->vol * 0x80; - vol_l = -vi->vol * 0x80; + vol_r = vol * 0x80; + vol_l = -vol * 0x80; } else { - vol_r = vi->vol * (0x80 - vi->pan); - vol_l = vi->vol * (0x80 + vi->pan); + vol_r = vol * (0x80 - vi->pan); + vol_l = vol * (0x80 + vi->pan); } if (vi->smp < mod->smp) { xxs = &mod->xxs[vi->smp]; + xtra = &m->xtra[vi->smp]; c5spd = m->xtra[vi->smp].c5spd; } else { xxs = &ctx->smix.xxs[vi->smp - mod->smp]; + xtra = NULL; c5spd = m->c4rate; } step = C4_PERIOD * c5spd / s->freq / vi->period; - if (step < 0.001) { /* otherwise m5v-nwlf.it crashes */ + /* Don't allow <=0, otherwise m5v-nwlf.it crashes + * Extremely high values that can cause undefined float/int + * conversion are also possible for c5spd modules. */ + if (step < 0.001 || step > (double)SHRT_MAX) { continue; } -#ifndef LIBXMP_CORE_DISABLE_IT - if (xxs->flg & XMP_SAMPLE_SLOOP && vi->smp < mod->smp) { - if (~vi->flags & VOICE_RELEASE) { - if (vi->pos < m->xsmp[vi->smp].lpe) { - xxs = &m->xsmp[vi->smp]; - } - } - } - - adjust_voice_end(vi, xxs); -#endif - - lps = xxs->lps; - lpe = xxs->lpe; - - if (p->flags & XMP_FLAGS_FIXLOOP) { - lps >>= 1; - } - - if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) { - vi->end += lpe - lps; - -#ifndef LIBXMP_CORE_DISABLE_IT - if (IS_PLAYER_MODE_IT()) { - vi->end--; - } -#endif - } + adjust_voice_end(ctx, vi, xxs, xtra); + init_sample_wraparound(s, &loop_data, vi, xxs); rampsize = s->ticksize >> ANTICLICK_SHIFT; delta_l = (vol_l - vi->old_vl) / rampsize; delta_r = (vol_r - vi->old_vr) / rampsize; - usmp = 0; - for (size = s->ticksize; size > 0; ) { + for (size = usmp = s->ticksize; size > 0; ) { int split_noloop = 0; if (p->xc_data[vi->chn].split) { @@ -459,20 +608,34 @@ void libxmp_mixer_softmixer(struct context_data *ctx) /* How many samples we can write before the loop break * or sample end... */ - if (vi->pos >= vi->end) { - samples = 0; - usmp = 1; + if (~vi->flags & VOICE_REVERSE) { + if (vi->pos >= vi->end) { + samples = 0; + if (--usmp <= 0) + break; + } else { + double c = ceil(((double)vi->end - vi->pos) / step); + /* ...inside the tick boundaries */ + if (c > size) { + c = size; + } + samples = c; + } + step_dir = step; } else { - int s = ceil(((double)vi->end - vi->pos) / step); - /* ...inside the tick boundaries */ - if (s > size) { - s = size; - } - - samples = s; - if (samples > 0) { - usmp = 0; + /* Reverse */ + if (vi->pos <= vi->start) { + samples = 0; + if (--usmp <= 0) + break; + } else { + double c = ceil((vi->pos - (double)vi->start) / step); + if (c > size) { + c = size; + } + samples = c; } + step_dir = -step; } if (vi->vol) { @@ -496,7 +659,7 @@ void libxmp_mixer_softmixer(struct context_data *ctx) #ifndef LIBXMP_CORE_DISABLE_IT /* See OpenMPT env-flt-max.it */ if (vi->filter.cutoff >= 0xfe && - vi->filter.resonance == 0) { + vi->filter.resonance == 0) { mixer_id &= ~FLAG_FILTER; } #endif @@ -521,7 +684,7 @@ void libxmp_mixer_softmixer(struct context_data *ctx) if (mix_fn != NULL) { mix_fn(vi, buf_pos, samples, - vol_l >> 8, vol_r >> 8, step * (1 << SMIX_SHIFT), rsize, delta_l, delta_r); + vol_l >> 8, vol_r >> 8, step_dir * (1 << SMIX_SHIFT), rsize, delta_l, delta_r); } buf_pos += mix_size; @@ -537,31 +700,43 @@ void libxmp_mixer_softmixer(struct context_data *ctx) } } - vi->pos += step * samples; + vi->pos += step_dir * samples; /* No more samples in this tick */ - size -= samples + usmp; + size -= samples; if (size <= 0) { - if (xxs->flg & XMP_SAMPLE_LOOP) { - if (vi->pos + step > vi->end) { - vi->pos += step; - loop_reposition(ctx, vi, xxs); + if (has_active_loop(ctx, vi, xxs)) { + /* This isn't particularly important for + * forward loops, but reverse loops need + * to be corrected here to avoid their + * negative positions getting clamped + * in later ticks. */ + if (((~vi->flags & VOICE_REVERSE) && vi->pos >= vi->end) || + ((vi->flags & VOICE_REVERSE) && vi->pos <= vi->start)) { + if (loop_reposition(ctx, vi, xxs, xtra)) { + reset_sample_wraparound(&loop_data); + init_sample_wraparound(s, &loop_data, vi, xxs); + } } } continue; } /* First sample loop run */ - if ((~xxs->flg & XMP_SAMPLE_LOOP) || split_noloop) { + if (!has_active_loop(ctx, vi, xxs) || split_noloop) { do_anticlick(ctx, voc, buf_pos, size); set_sample_end(ctx, voc, 1); size = 0; continue; } - loop_reposition(ctx, vi, xxs); + if (loop_reposition(ctx, vi, xxs, xtra)) { + reset_sample_wraparound(&loop_data); + init_sample_wraparound(s, &loop_data, vi, xxs); + } } + reset_sample_wraparound(&loop_data); vi->old_vl = vol_l; vi->old_vr = vol_r; } @@ -581,7 +756,7 @@ void libxmp_mixer_softmixer(struct context_data *ctx) downmix_int_8bit(s->buffer, s->buf32, size, s->amplify, s->format & XMP_FORMAT_UNSIGNED ? 0x80 : 0); } else { - downmix_int_16bit((int16 *)s->buffer, s->buf32, size,s->amplify, + downmix_int_16bit((int16 *)s->buffer, s->buf32, size, s->amplify, s->format & XMP_FORMAT_UNSIGNED ? 0x8000 : 0); } @@ -594,12 +769,14 @@ void libxmp_mixer_voicepos(struct context_data *ctx, int voc, double pos, int ac struct module_data *m = &ctx->m; struct mixer_voice *vi = &p->virt.voice_array[voc]; struct xmp_sample *xxs; - int lps; + struct extra_sample_data *xtra; if (vi->smp < m->mod.smp) { - xxs = &m->mod.xxs[vi->smp]; + xxs = &m->mod.xxs[vi->smp]; + xtra = &m->xtra[vi->smp]; } else { - xxs = &ctx->smix.xxs[vi->smp - m->mod.smp]; + xxs = &ctx->smix.xxs[vi->smp - m->mod.smp]; + xtra = NULL; } if (xxs->flg & XMP_SAMPLE_SYNTH) { @@ -608,29 +785,16 @@ void libxmp_mixer_voicepos(struct context_data *ctx, int voc, double pos, int ac vi->pos = pos; - adjust_voice_end(vi, xxs); + adjust_voice_end(ctx, vi, xxs, xtra); if (vi->pos >= vi->end) { - if (xxs->flg & XMP_SAMPLE_LOOP) { - vi->pos = xxs->lps; - } else { - vi->pos = xxs->len; - } - } - - lps = xxs->lps; - if (p->flags & XMP_FLAGS_FIXLOOP) { - lps >>= 1; - } - - if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) { - vi->end += (xxs->lpe - lps); - -#ifndef LIBXMP_CORE_DISABLE_IT - if (IS_PLAYER_MODE_IT()) { - vi->end--; - } -#endif + vi->pos = vi->end; + /* Restart forward sample loops. */ + if ((~vi->flags & VOICE_REVERSE) && has_active_loop(ctx, vi, xxs)) + loop_reposition(ctx, vi, xxs, xtra); + } else if ((vi->flags & VOICE_REVERSE) && vi->pos <= 0.1) { + /* Hack: 0 maps to the end for reversed samples. */ + vi->pos = vi->end; } if (ac) { @@ -650,12 +814,6 @@ double libxmp_mixer_getvoicepos(struct context_data *ctx, int voc) return 0; } - if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) { - if (vi->pos >= xxs->lpe) { - return xxs->lpe - (vi->pos - xxs->lpe) - 1; - } - } - return vi->pos; } @@ -674,7 +832,7 @@ void libxmp_mixer_setpatch(struct context_data *ctx, int voc, int smp, int ac) vi->smp = smp; vi->vol = 0; vi->pan = 0; - vi->flags &= ~SAMPLE_LOOP; + vi->flags &= ~(SAMPLE_LOOP | VOICE_REVERSE | VOICE_BIDIR); vi->fidx = 0; @@ -746,12 +904,42 @@ void libxmp_mixer_release(struct context_data *ctx, int voc, int rel) struct mixer_voice *vi = &p->virt.voice_array[voc]; if (rel) { +#ifndef LIBXMP_CORE_DISABLE_IT + /* Cancel voice reverse when releasing an active sustain loop, + * unless the main loop is bidirectional. This is done both for + * bidirectional sustain loops and for forward sustain loops + * that have been reversed with MPT S9F Play Backward. */ + if (~vi->flags & VOICE_RELEASE) { + struct xmp_sample *xxs = libxmp_get_sample(ctx, vi->smp); + + if (has_active_sustain_loop(ctx, vi, xxs) && + (~xxs->flg & XMP_SAMPLE_LOOP_BIDIR)) + vi->flags &= ~VOICE_REVERSE; + } +#endif vi->flags |= VOICE_RELEASE; } else { vi->flags &= ~VOICE_RELEASE; } } +void libxmp_mixer_reverse(struct context_data *ctx, int voc, int rev) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + + /* Don't reverse samples that have already ended */ + if (~vi->fidx & FLAG_ACTIVE) { + return; + } + + if (rev) { + vi->flags |= VOICE_REVERSE; + } else { + vi->flags &= ~VOICE_REVERSE; + } +} + void libxmp_mixer_seteffect(struct context_data *ctx, int voc, int type, int val) { #ifndef LIBXMP_CORE_DISABLE_IT @@ -813,11 +1001,12 @@ int libxmp_mixer_on(struct context_data *ctx, int rate, int format, int c4rate) s->format = format; s->amplify = DEFAULT_AMPLIFY; s->mix = DEFAULT_MIX; - /* s->pbase = C4_PERIOD * c4rate / s->freq; */(void) c4rate; + /* s->pbase = C4_PERIOD * c4rate / s->freq; */ s->interp = XMP_INTERP_LINEAR; /* default interpolation type */ s->dsp = XMP_DSP_LOWPASS; /* enable filters by default */ /* s->numvoc = SMIX_NUMVOC; */ s->dtright = s->dtleft = 0; + s->bidir_adjust = 0; return 0; diff --git a/internal/c/parts/audio/extras/libxmp-lite/mixer.h b/internal/c/parts/audio/extras/libxmp-lite/mixer.h index 5ed3b9763..c80b1e897 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/mixer.h +++ b/internal/c/parts/audio/extras/libxmp-lite/mixer.h @@ -30,15 +30,19 @@ struct mixer_voice { int fidx; /* mixer function index */ int ins; /* instrument number */ int smp; /* sample number */ + int start; /* loop start */ int end; /* loop end */ int act; /* nna info & status of voice */ + int key; /* key for DCA note check */ int old_vl; /* previous volume, left channel */ int old_vr; /* previous volume, right channel */ int sleft; /* last left sample output, in 32bit */ int sright; /* last right sample output, in 32bit */ #define VOICE_RELEASE (1 << 0) -#define ANTICLICK (1 << 1) +#define ANTICLICK (1 << 1) #define SAMPLE_LOOP (1 << 2) +#define VOICE_REVERSE (1 << 3) +#define VOICE_BIDIR (1 << 4) int flags; /* flags */ void *sptr; /* sample pointer */ #ifdef LIBXMP_PAULA_SIMULATOR @@ -74,5 +78,6 @@ double libxmp_mixer_getvoicepos(struct context_data *, int); void libxmp_mixer_setnote (struct context_data *, int, int); void libxmp_mixer_setperiod (struct context_data *, int, double); void libxmp_mixer_release (struct context_data *, int, int); +void libxmp_mixer_reverse (struct context_data *, int, int); #endif /* LIBXMP_MIXER_H */ diff --git a/internal/c/parts/audio/extras/libxmp-lite/mod.h b/internal/c/parts/audio/extras/libxmp-lite/mod.h index 1971a1b1a..682aef022 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/mod.h +++ b/internal/c/parts/audio/extras/libxmp-lite/mod.h @@ -20,6 +20,9 @@ * THE SOFTWARE. */ +#ifndef LIBXMP_LOADERS_MOD_H +#define LIBXMP_LOADERS_MOD_H + struct mod_instrument { uint8 name[22]; /* Instrument name */ uint16 size; /* Sample length in 16-bit words */ @@ -41,7 +44,6 @@ struct mod_header { uint8 magic[4]; }; - #ifndef LIBXMP_CORE_PLAYER /* Soundtracker 15-instrument module header */ @@ -53,3 +55,5 @@ struct st_header { uint8 order[128]; }; #endif + +#endif /* LIBXMP_LOADERS_MOD_H */ diff --git a/internal/c/parts/audio/extras/libxmp-lite/mod_load.c b/internal/c/parts/audio/extras/libxmp-lite/mod_load.c index 80a3cfdad..425604c15 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/mod_load.c +++ b/internal/c/parts/audio/extras/libxmp-lite/mod_load.c @@ -1,5 +1,5 @@ /* Extended Module Player - * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -23,49 +23,249 @@ /* This loader recognizes the following variants of the Protracker * module format: * - * - Protracker M.K. - * - Fasttracker ?CHN and ??CH + * - Protracker M.K. and M!K! + * - Protracker songs + * - Noisetracker N.T. and M&K! (not tested) + * - Fast Tracker 6CHN and 8CHN + * - Fasttracker II/Take Tracker ?CHN and ??CH + * - Mod's Grave M.K. w/ 8 channels (WOW) + * - Atari Octalyser CD61 and CD81 + * - Digital Tracker FA04, FA06 and FA08 + * - TakeTracker TDZ1, TDZ2, TDZ3, and TDZ4 + * - (unknown) NSMS, LARD + * + * The 'lite' version only recognizes Protracker M.K. and + * Fasttracker ?CHN and ??CH formats. */ #include -#include #include "loader.h" #include "mod.h" +#ifndef LIBXMP_CORE_PLAYER +struct mod_magic { + const char *magic; + int flag; + int id; + int ch; +}; + +#define TRACKER_PROTRACKER 0 +#define TRACKER_NOISETRACKER 1 +#define TRACKER_SOUNDTRACKER 2 +#define TRACKER_FASTTRACKER 3 +#define TRACKER_FASTTRACKER2 4 +#define TRACKER_OCTALYSER 5 +#define TRACKER_TAKETRACKER 6 +#define TRACKER_DIGITALTRACKER 7 +#define TRACKER_FLEXTRAX 8 +#define TRACKER_MODSGRAVE 9 +#define TRACKER_SCREAMTRACKER3 10 +#define TRACKER_OPENMPT 11 +#define TRACKER_UNKNOWN_CONV 95 +#define TRACKER_CONVERTEDST 96 +#define TRACKER_CONVERTED 97 +#define TRACKER_CLONE 98 +#define TRACKER_UNKNOWN 99 + +#define TRACKER_PROBABLY_NOISETRACKER 20 + +const struct mod_magic mod_magic[] = { + {"M.K.", 0, TRACKER_PROTRACKER, 4}, + {"M!K!", 1, TRACKER_PROTRACKER, 4}, + {"M&K!", 1, TRACKER_NOISETRACKER, 4}, + {"N.T.", 1, TRACKER_NOISETRACKER, 4}, + {"6CHN", 0, TRACKER_FASTTRACKER, 6}, + {"8CHN", 0, TRACKER_FASTTRACKER, 8}, + {"CD61", 1, TRACKER_OCTALYSER, 6}, /* Atari STe/Falcon */ + {"CD81", 1, TRACKER_OCTALYSER, 8}, /* Atari STe/Falcon */ + {"TDZ1", 1, TRACKER_TAKETRACKER, 1}, /* TakeTracker 1ch */ + {"TDZ2", 1, TRACKER_TAKETRACKER, 2}, /* TakeTracker 2ch */ + {"TDZ3", 1, TRACKER_TAKETRACKER, 3}, /* TakeTracker 3ch */ + {"TDZ4", 1, TRACKER_TAKETRACKER, 4}, /* see XModule SaveTracker.c */ + {"FA04", 1, TRACKER_DIGITALTRACKER, 4}, /* Atari Falcon */ + {"FA06", 1, TRACKER_DIGITALTRACKER, 6}, /* Atari Falcon */ + {"FA08", 1, TRACKER_DIGITALTRACKER, 8}, /* Atari Falcon */ + {"LARD", 1, TRACKER_UNKNOWN, 4}, /* in judgement_day_gvine.mod */ + {"NSMS", 1, TRACKER_UNKNOWN, 4}, /* in Kingdom.mod */ +}; + +/* Returns non-zero if the given tracker ONLY supports VBlank timing. This + * should be used only when the tracker is known for sure, e.g. magic match. */ +static int tracker_is_vblank(int id) +{ + switch (id) { + case TRACKER_NOISETRACKER: + case TRACKER_SOUNDTRACKER: + return 1; + default: + return 0; + } +} +#endif /* LIBXMP_CORE_PLAYER */ + static int mod_test(HIO_HANDLE *, char *, const int); static int mod_load(struct module_data *, HIO_HANDLE *, const int); const struct format_loader libxmp_loader_mod = { + #ifdef LIBXMP_CORE_PLAYER "Protracker", + #else + "Amiga Protracker/Compatible", + #endif mod_test, mod_load }; -static int mod_test(HIO_HANDLE *f, char *t, const int start) +#ifndef LIBXMP_CORE_PLAYER +static int validate_pattern(uint8 *buf) +{ + int i, j; + + for (i = 0; i < 64; i++) { + for (j = 0; j < 4; j++) { + uint8 *d = buf + (i * 4 + j) * 4; + if ((d[0] >> 4) > 1) { + D_(D_CRIT "invalid pattern data: row %d ch %d: %02x", i, j, d[0]); + return -1; + } + } + } + + return 0; +} +#endif + +static int mod_test(HIO_HANDLE * f, char *t, const int start) { int i; char buf[4]; + #ifndef LIBXMP_CORE_PLAYER + uint8 pat_buf[1024]; + int smp_size, num_pat; + long size; + int count; + int detected; + #endif hio_seek(f, start + 1080, SEEK_SET); - if (hio_read(buf, 1, 4, f) < 4) + if (hio_read(buf, 1, 4, f) < 4) { return -1; + } - if (!strncmp(buf + 2, "CH", 2) && isdigit((int)buf[0]) - && isdigit((int)buf[1])) { + if (!strncmp(buf + 2, "CH", 2) && + isdigit((unsigned char)buf[0]) && isdigit((unsigned char)buf[1])) { i = (buf[0] - '0') * 10 + buf[1] - '0'; if (i > 0 && i <= 32) { goto found; } } - if (!strncmp(buf + 1, "CHN", 3) && isdigit((int)*buf)) { - if (*buf >= '0' && *buf <= '9') { + if (!strncmp(buf + 1, "CHN", 3) && isdigit((unsigned char)*buf)) { + if (*buf - '0') { goto found; } } +#ifdef LIBXMP_CORE_PLAYER if (memcmp(buf, "M.K.", 4)) return -1; +#else + for (i = 0; i < ARRAY_SIZE(mod_magic); i++) { + if (!memcmp(buf, mod_magic[i].magic, 4)) + break; + } + if (i >= ARRAY_SIZE(mod_magic)) { + return -1; + } + + detected = mod_magic[i].flag; + + /* + * Sanity check to prevent loading NoiseRunner and other module + * formats with valid magic at offset 1080 (e.g. His Master's Noise) + */ + + hio_seek(f, start + 20, SEEK_SET); + for (i = 0; i < 31; i++) { + uint8 x; + + hio_seek(f, 22, SEEK_CUR); /* Instrument name */ + + /* OpenMPT can create mods with large samples */ + hio_read16b(f); /* sample size */ + + /* Chris Spiegel tells me that sandman.mod has 0x20 in finetune */ + x = hio_read8(f); + if (x & 0xf0 && x != 0x20) /* test finetune */ + return -1; + if (hio_read8(f) > 0x40) /* test volume */ + return -1; + hio_read16b(f); /* loop start */ + hio_read16b(f); /* loop size */ + } + + /* The following checks are only relevant for filtering out atypical + * M.K. variants. If the magic is from a recognizable source, skip them. */ + if (detected) + goto found; + + /* Test for UNIC tracker modules + * + * From Gryzor's Pro-Wizard PW_FORMATS-Engl.guide: + * ``The UNIC format is very similar to Protracker... At least in the + * heading... same length : 1084 bytes. Even the "M.K." is present, + * sometimes !! Maybe to disturb the rippers.. hehe but Pro-Wizard + * doesn't test this only!'' + */ + + /* get file size */ + size = hio_size(f); + smp_size = 0; + hio_seek(f, start + 20, SEEK_SET); + + /* get samples size */ + for (i = 0; i < 31; i++) { + hio_seek(f, 22, SEEK_CUR); + smp_size += 2 * hio_read16b(f); /* Length in 16-bit words */ + hio_seek(f, 6, SEEK_CUR); + } + + /* get number of patterns */ + num_pat = 0; + hio_seek(f, start + 952, SEEK_SET); + for (i = 0; i < 128; i++) { + uint8 x = hio_read8(f); + if (x > 0x7f) + break; + if (x > num_pat) + num_pat = x; + } + num_pat++; + + /* see if module size matches UNIC */ + if (start + 1084 + num_pat * 0x300 + smp_size == size) { + D_(D_CRIT "module size matches UNIC"); + return -1; + } + + /* validate pattern data in an attempt to catch UNICs with MOD size */ + for (count = i = 0; i < num_pat; i++) { + hio_seek(f, start + 1084 + 1024 * i, SEEK_SET); + if (!hio_read(pat_buf, 1024, 1, f)) { + D_(D_WARN "pattern %d: failed to read pattern data", i); + return -1; + } + if (validate_pattern(pat_buf) < 0) { + D_(D_WARN "pattern %d: error in pattern data", i); + /* Allow a few errors, "lexstacy" has 0x52 */ + count++; + } + } + if (count > 2) { + return -1; + } +#endif /* LIBXMP_CORE_PLAYER */ found: hio_seek(f, start + 0, SEEK_SET); @@ -74,161 +274,737 @@ found: return 0; } -static int mod_load(struct module_data *m, HIO_HANDLE *f, const int start) + +#ifndef LIBXMP_CORE_PLAYER +static int is_st_ins(const char *s) +{ + if (s[0] != 's' && s[0] != 'S') + return 0; + if (s[1] != 't' && s[1] != 'T') + return 0; + if (s[2] != '-' || s[5] != ':') + return 0; + if (!isdigit((unsigned char)s[3]) || !isdigit((unsigned char)s[4])) + return 0; + + return 1; +} + +static int get_tracker_id(struct module_data *m, struct mod_header *mh, int id) { struct xmp_module *mod = &m->mod; - int i, j; - struct xmp_event *event; - struct mod_header mh; - uint8 mod_event[4]; - char magic[8]; - int ptkloop = 0; /* Protracker loop */ + int has_loop_0 = 0; + int has_vol_in_empty_ins = 0; + int i; - LOAD_INIT(); - - mod->ins = 31; - mod->smp = mod->ins; - mod->chn = 0; - - m->quirk |= QUIRK_PROTRACK; - m->period_type = PERIOD_MODRNG; - - hio_read(mh.name, 20, 1, f); + /* Check if has instruments with loop size 0 */ for (i = 0; i < 31; i++) { - hio_read(mh.ins[i].name, 22, 1, f); /* Instrument name */ - mh.ins[i].size = hio_read16b(f); /* Length in 16-bit words */ - mh.ins[i].finetune = hio_read8(f); /* Finetune (signed nibble) */ - mh.ins[i].volume = hio_read8(f); /* Linear playback volume */ - mh.ins[i].loop_start = hio_read16b(f); /* Loop start in 16-bit words */ - mh.ins[i].loop_size = hio_read16b(f); /* Loop size in 16-bit words */ + if (mh->ins[i].loop_size == 0) { + has_loop_0 = 1; + break; + } } - mh.len = hio_read8(f); - mh.restart = hio_read8(f); - hio_read(mh.order, 128, 1, f); - memset(magic, 0, 8); - hio_read(magic, 4, 1, f); + /* Check if has instruments with size 0 and volume > 0 */ + for (i = 0; i < 31; i++) { + if (mh->ins[i].size == 0 && mh->ins[i].volume > 0) { + has_vol_in_empty_ins = 1; + break; + } + } + + /* + * Test Protracker-like files + */ + if (mh->restart == mod->pat) { + if (mod->chn == 4) { + id = TRACKER_SOUNDTRACKER; + } else { + id = TRACKER_UNKNOWN; + } + } else if (mh->restart == 0x78) { + if (mod->chn == 4) { + /* Can't trust this for Noisetracker, MOD.Data City Remix + * has Protracker effects and Noisetracker restart byte + */ + id = TRACKER_PROBABLY_NOISETRACKER; + } else { + id = TRACKER_UNKNOWN; + } + return id; + } else if (mh->restart < 0x7f) { + if (mod->chn == 4 && !has_vol_in_empty_ins) { + id = TRACKER_NOISETRACKER; + } else { + id = TRACKER_UNKNOWN; /* ? */ + } + mod->rst = mh->restart; + } else if (mh->restart == 0x7f) { + if (mod->chn == 4) { + if (has_loop_0) { + id = TRACKER_CLONE; + } + } else { + id = TRACKER_SCREAMTRACKER3; + } + return id; + } else if (mh->restart > 0x7f) { + id = TRACKER_UNKNOWN; /* ? */ + return id; + } + + if (!has_loop_0) { /* All loops are size 2 or greater */ + + for (i = 0; i < 31; i++) { + if (mh->ins[i].size == 1 && mh->ins[i].volume == 0) { + return TRACKER_CONVERTED; + } + } + + for (i = 0; i < 31; i++) { + if (is_st_ins((char *)mh->ins[i].name)) + break; + } + if (i == 31) { /* No st- instruments */ + for (i = 0; i < 31; i++) { + if (mh->ins[i].size != 0 + || mh->ins[i].loop_size != 1) { + continue; + } + + switch (mod->chn) { + case 4: + if (has_vol_in_empty_ins) { + id = TRACKER_OPENMPT; + } else { + id = TRACKER_NOISETRACKER; + /* or Octalyser */ + } + break; + case 6: + case 8: + id = TRACKER_OCTALYSER; + break; + default: + id = TRACKER_UNKNOWN; + } + return id; + } + + if (mod->chn == 4) { + id = TRACKER_PROTRACKER; + } else if (mod->chn == 6 || mod->chn == 8) { + /* FastTracker 1.01? */ + id = TRACKER_FASTTRACKER; + } else { + id = TRACKER_UNKNOWN; + } + } + } else { /* Has loops with size 0 */ + for (i = 15; i < 31; i++) { + /* Is the name or size set? */ + if (mh->ins[i].name[0] || mh->ins[i].size > 0) + break; + } + if (i == 31 && is_st_ins((char *)mh->ins[14].name)) { + return TRACKER_CONVERTEDST; + } + + /* Assume that Fast Tracker modules won't have ST- instruments */ + for (i = 0; i < 31; i++) { + if (is_st_ins((char *)mh->ins[i].name)) + break; + } + if (i < 31) { + return TRACKER_UNKNOWN_CONV; + } + + if (mod->chn == 4 || mod->chn == 6 || mod->chn == 8) { + return TRACKER_FASTTRACKER; + } + + id = TRACKER_UNKNOWN; /* ?! */ + } + + return id; +} +#endif /* LIBXMP_CORE_PLAYER */ + +static int mod_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, k; + struct xmp_event *event; + struct mod_header mh; + char magic[8]; + uint8 *patbuf; +#ifndef LIBXMP_CORE_PLAYER + uint8 pat_high_fxx[256]; + const char *tracker = ""; + int detected = 0; + int tracker_id = TRACKER_PROTRACKER; + int out_of_range = 0; + int maybe_wow = 1; + int smp_size, ptsong = 0; + int needs_timing_detection = 0; + int samerow_fxx = 0; /* speed + BPM set on same row */ + int high_fxx = 0; /* high Fxx is used anywhere */ +#endif + int ptkloop = 0; /* Protracker loop */ + + LOAD_INIT(); + + mod->ins = 31; + mod->smp = mod->ins; + mod->chn = 0; + #ifndef LIBXMP_CORE_PLAYER + smp_size = 0; + #else + m->quirk |= QUIRK_PROTRACK; + #endif + m->period_type = PERIOD_MODRNG; + + hio_read(mh.name, 20, 1, f); + for (i = 0; i < 31; i++) { + hio_read(mh.ins[i].name, 22, 1, f); /* Instrument name */ + mh.ins[i].size = hio_read16b(f); /* Length in 16-bit words */ + mh.ins[i].finetune = hio_read8(f); /* Finetune (signed nibble) */ + mh.ins[i].volume = hio_read8(f); /* Linear playback volume */ + mh.ins[i].loop_start = hio_read16b(f); /* Loop start in 16-bit words */ + mh.ins[i].loop_size = hio_read16b(f); /* Loop size in 16-bit words */ + + #ifndef LIBXMP_CORE_PLAYER + /* Mod's Grave WOW files are converted from 669s and have default + * finetune and volume. + */ + if (mh.ins[i].size && (mh.ins[i].finetune != 0 || mh.ins[i].volume != 64)) + maybe_wow = 0; + + smp_size += 2 * mh.ins[i].size; + #endif + } + mh.len = hio_read8(f); + mh.restart = hio_read8(f); + hio_read(mh.order, 128, 1, f); + memset(magic, 0, sizeof(magic)); + hio_read(magic, 1, 4, f); + if (hio_error(f)) { + return -1; + } + +#ifndef LIBXMP_CORE_PLAYER + /* Mod's Grave WOW files always have a 0 restart byte; 6692WOW implements + * 669 repeating by inserting a pattern jump and ignores this byte. + */ + if (mh.restart != 0) + maybe_wow = 0; + + for (i = 0; i < ARRAY_SIZE(mod_magic); i++) { + if (!(strncmp (magic, mod_magic[i].magic, 4))) { + mod->chn = mod_magic[i].ch; + tracker_id = mod_magic[i].id; + detected = mod_magic[i].flag; + break; + } + } + + /* Enable timing detection for M.K. and M!K! modules. */ + if (tracker_id == TRACKER_PROTRACKER) + needs_timing_detection = 1; + + /* Digital Tracker MODs have an extra four bytes after the magic. + * These are always 00h 40h 00h 00h and can probably be ignored. */ + if (tracker_id == TRACKER_DIGITALTRACKER) { + hio_read32b(f); + } +#endif + + if (mod->chn == 0) { + #ifdef LIBXMP_CORE_PLAYER if (!memcmp(magic, "M.K.", 4)) { mod->chn = 4; - } else if (!strncmp(magic + 2, "CH", 2) && - isdigit((int)magic[0]) && isdigit((int)magic[1])) { - mod->chn = (*magic - '0') * 10 + magic[1] - '0'; - } else if (!strncmp(magic + 1, "CHN", 3) && isdigit((int)*magic)) { - mod->chn = *magic - '0'; + } else + #endif + if (!strncmp(magic + 2, "CH", 2) && + isdigit((unsigned char)magic[0]) && isdigit((unsigned char)magic[1])) { + mod->chn = (*magic - '0') * 10 + magic[1] - '0'; + } else if (!strncmp(magic + 1, "CHN", 3) && isdigit((unsigned char)*magic)) { + mod->chn = *magic - '0'; } else { + return -1; + } + #ifndef LIBXMP_CORE_PLAYER + tracker_id = mod->chn & 1 ? TRACKER_TAKETRACKER : TRACKER_FASTTRACKER2; + detected = 1; + #endif + } + + strncpy(mod->name, (char *) mh.name, 20); + + mod->len = mh.len; + /* mod->rst = mh.restart; */ + + if (mod->rst >= mod->len) + mod->rst = 0; + memcpy(mod->xxo, mh.order, 128); + + for (i = 0; i < 128; i++) { + /* This fixes dragnet.mod (garbage in the order list) */ + if (mod->xxo[i] > 0x7f) + break; + if (mod->xxo[i] > mod->pat) + mod->pat = mod->xxo[i]; + } + mod->pat++; + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi; + struct xmp_subinstrument *sub; + struct xmp_sample *xxs; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + +#ifndef LIBXMP_CORE_PLAYER + if (mh.ins[i].size >= 0x8000) { + tracker_id = TRACKER_OPENMPT; + needs_timing_detection = 0; + detected = 1; + } +#endif + + xxi = &mod->xxi[i]; + sub = &xxi->sub[0]; + xxs = &mod->xxs[i]; + + xxs->len = 2 * mh.ins[i].size; + xxs->lps = 2 * mh.ins[i].loop_start; + xxs->lpe = xxs->lps + 2 * mh.ins[i].loop_size; + if (xxs->lpe > xxs->len) { + xxs->lpe = xxs->len; + } + xxs->flg = (mh.ins[i].loop_size > 1 && xxs->lpe >= 4) ? + XMP_SAMPLE_LOOP : 0; + sub->fin = (int8)(mh.ins[i].finetune << 4); + sub->vol = mh.ins[i].volume; + sub->pan = 0x80; + sub->sid = i; + libxmp_instrument_name(mod, i, mh.ins[i].name, 22); + + if (xxs->len > 0) { + xxi->nsm = 1; + } + } + +#ifndef LIBXMP_CORE_PLAYER + /* Experimental tracker-detection routine */ + + if (detected) + goto skip_test; + + /* Test for Flextrax modules + * + * FlexTrax is a soundtracker for Atari Falcon030 compatible computers. + * FlexTrax supports the standard MOD file format (up to eight channels) + * for compatibility reasons but also features a new enhanced module + * format FLX. The FLX format is an extended version of the standard + * MOD file format with support for real-time sound effects like reverb + * and delay. + */ + + if (0x43c + mod->pat * 4 * mod->chn * 0x40 + smp_size < m->size) { + char idbuffer[4]; + int pos = hio_tell(f); + int num_read; + if (pos < 0) { + return -1; + } + hio_seek(f, start + 0x43c + mod->pat * 4 * mod->chn * 0x40 + smp_size, SEEK_SET); + num_read = hio_read(idbuffer, 1, 4, f); + hio_seek(f, start + pos, SEEK_SET); + + if (num_read == 4 && !memcmp(idbuffer, "FLEX", 4)) { + tracker_id = TRACKER_FLEXTRAX; + needs_timing_detection = 0; + goto skip_test; + } + } + + /* Test for Mod's Grave WOW modules + * + * Stefan Danes said: + * This weird format is identical to '8CHN' but still uses the 'M.K.' ID. + * You can only test for WOW by calculating the size of the module for 8 + * channels and comparing this to the actual module length. If it's equal, + * the module is an 8 channel WOW. + * + * Addendum: very rarely, WOWs will have an odd length due to an extra byte, + * so round the filesize down in this check. False positive WOWs can be ruled + * out by checking the restart byte and sample volume (see above). + * + * Worst case if there are still issues with this, OpenMPT validates later + * patterns in potential WOW files (where sample data would be located in a + * regular M.K. MOD) to rule out false positives. + */ + + if (!strncmp(magic, "M.K.", 4) && maybe_wow && + (0x43c + mod->pat * 32 * 0x40 + smp_size) == (m->size & ~1)) { + mod->chn = 8; + tracker_id = TRACKER_MODSGRAVE; + needs_timing_detection = 0; + } else { + /* Test for Protracker song files */ + ptsong = !strncmp((char *)magic, "M.K.", 4) && + (0x43c + mod->pat * 0x400 == m->size); + if (ptsong) { + tracker_id = TRACKER_PROTRACKER; + goto skip_test; + } else { + /* something else */ + tracker_id = get_tracker_id(m, &mh, tracker_id); + } + } + +skip_test: +#endif + + if (mod->chn >= XMP_MAX_CHANNELS) { + return -1; + } + + mod->trk = mod->chn * mod->pat; + + for (i = 0; i < mod->ins; i++) { + D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c V%02x %+d %c", + i, mod->xxi[i].name, + mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe, + (mh.ins[i].loop_size > 1 && mod->xxs[i].lpe > 8) ? + 'L' : ' ', mod->xxi[i].sub[0].vol, + mod->xxi[i].sub[0].fin >> 4, + ptkloop && mod->xxs[i].lps == 0 && mh.ins[i].loop_size > 1 && + mod->xxs[i].len > mod->xxs[i].lpe ? '!' : ' '); + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Load and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + if ((patbuf = (uint8 *) malloc(64 * 4 * mod->chn)) == NULL) { + return -1; + } + +#ifndef LIBXMP_CORE_PLAYER + memset(pat_high_fxx, 0, sizeof(pat_high_fxx)); +#endif + + for (i = 0; i < mod->pat; i++) { + uint8 *mod_event; + + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) { + free(patbuf); + return -1; + } + + if (hio_read(patbuf, 64 * 4 * mod->chn, 1, f) < 1) { + free(patbuf); + return -1; + } + +#ifndef LIBXMP_CORE_PLAYER + mod_event = patbuf; + for (j = 0; j < 64; j++) { + int speed_row = 0; + int bpm_row = 0; + for (k = 0; k < mod->chn; k++) { + int period; + + period = ((int)(LSN(mod_event[0])) << 8) | mod_event[1]; + if (period != 0 && (period < 108 || period > 907)) { + out_of_range = 1; + } + + /* Filter noisetracker events */ + if (tracker_id == TRACKER_PROBABLY_NOISETRACKER) { + unsigned char fxt = LSN(mod_event[2]); + unsigned char fxp = LSN(mod_event[3]); + + if ((fxt > 0x06 && fxt < 0x0a) || (fxt == 0x0e && fxp > 1)) { + tracker_id = TRACKER_UNKNOWN; + } + } + /* Needs CIA/VBlank detection? */ + if (LSN(mod_event[2]) == 0x0f) { + if (mod_event[3] >= 0x20) { + pat_high_fxx[i] = mod_event[3]; + m->compare_vblank = 1; + high_fxx = 1; + bpm_row = 1; + } else { + speed_row = 1; + } + } + mod_event += 4; + } + if (bpm_row && speed_row) { + samerow_fxx = 1; + } + } + + if (out_of_range) { + if (tracker_id == TRACKER_UNKNOWN && mh.restart == 0x7f) { + tracker_id = TRACKER_SCREAMTRACKER3; + } + + /* Check out-of-range notes in Amiga trackers */ + if (tracker_id == TRACKER_PROTRACKER || + tracker_id == TRACKER_NOISETRACKER || + tracker_id == TRACKER_PROBABLY_NOISETRACKER || + tracker_id == TRACKER_SOUNDTRACKER) { /* note > B-3 */ + + tracker_id = TRACKER_UNKNOWN; + } + } +#endif + + mod_event = patbuf; + for (j = 0; j < 64; j++) { + for (k = 0; k < mod->chn; k++) { + event = &EVENT(i, k, j); +#ifdef LIBXMP_CORE_PLAYER + libxmp_decode_protracker_event(event, mod_event); +#else + switch (tracker_id) { + case TRACKER_PROBABLY_NOISETRACKER: + case TRACKER_NOISETRACKER: + libxmp_decode_noisetracker_event(event, mod_event); + break; + default: + libxmp_decode_protracker_event(event, mod_event); + } +#endif + mod_event += 4; + } + } + } + free(patbuf); + +#ifndef LIBXMP_CORE_PLAYER + /* VBlank detection routine. + * Despite VBlank being dependent on the tracker used, VBlank detection + * is complex and uses heuristics mostly independent from tracker ID. + * See also: the scan.c comparison code enabled by m->compare_vblank + */ + if (!needs_timing_detection) { + /* Noisetracker and some other trackers do not support CIA timing. The + * only known MOD in the wild that relies on this is muppenkorva.mod + * by Glue Master (loaded by the His Master's Noise loader). + */ + if (tracker_is_vblank(tracker_id)) { + m->quirk |= QUIRK_NOBPM; + } + m->compare_vblank = 0; + + } else if (samerow_fxx) { + /* If low Fxx and high Fxx are on the same row, there's a high chance + * this is from a CIA-based tracker. There are some exceptions. + */ + if (tracker_id == TRACKER_NOISETRACKER || + tracker_id == TRACKER_PROBABLY_NOISETRACKER || + tracker_id == TRACKER_SOUNDTRACKER) { + + tracker_id = TRACKER_UNKNOWN; + } + m->compare_vblank = 0; + + } else if (high_fxx && mod->len >= 8) { + /* Test for high Fxx at the end only--this is typically VBlank, + * and is used to add silence to the end of modules. + * + * Exception: if the final high Fxx is F7D, this module is either CIA + * or is VBlank that was modified to play as CIA, so do nothing. + * + * TODO: MPT resets modules on the end loop, so some of the very long + * silent sections in modules affected by this probably expect CIA. It + * should eventually be possible to detect those. + */ + const int threshold = mod->len - 2; + + for (i = 0; i < threshold; i++) { + if (pat_high_fxx[mod->xxo[i]]) + break; + } + if (i == threshold) { + for (i = mod->len - 1; i >= threshold; i--) { + uint8 fxx = pat_high_fxx[mod->xxo[i]]; + if (fxx == 0x00) + continue; + if (fxx == 0x7d) + break; + + m->compare_vblank = 0; + m->quirk |= QUIRK_NOBPM; + break; + } + } + } + + switch (tracker_id) { + case TRACKER_PROTRACKER: + tracker = "Protracker"; + ptkloop = 1; + break; + case TRACKER_PROBABLY_NOISETRACKER: + case TRACKER_NOISETRACKER: + tracker = "Noisetracker"; + break; + case TRACKER_SOUNDTRACKER: + tracker = "Soundtracker"; + break; + case TRACKER_FASTTRACKER: + case TRACKER_FASTTRACKER2: + tracker = "Fast Tracker"; + m->period_type = PERIOD_AMIGA; + break; + case TRACKER_TAKETRACKER: + tracker = "Take Tracker"; + m->period_type = PERIOD_AMIGA; + break; + case TRACKER_OCTALYSER: + tracker = "Octalyser"; + break; + case TRACKER_DIGITALTRACKER: + tracker = "Digital Tracker"; + break; + case TRACKER_FLEXTRAX: + tracker = "Flextrax"; + break; + case TRACKER_MODSGRAVE: + tracker = "Mod's Grave"; + break; + case TRACKER_SCREAMTRACKER3: + tracker = "Scream Tracker"; + m->period_type = PERIOD_AMIGA; + break; + case TRACKER_CONVERTEDST: + case TRACKER_CONVERTED: + tracker = "Converted"; + break; + case TRACKER_CLONE: + tracker = "Protracker clone"; + m->period_type = PERIOD_AMIGA; + break; + case TRACKER_OPENMPT: + tracker = "OpenMPT"; + ptkloop = 1; + break; + default: + case TRACKER_UNKNOWN_CONV: + case TRACKER_UNKNOWN: + tracker = "Unknown tracker"; + m->period_type = PERIOD_AMIGA; + break; + } + + if (out_of_range) { + m->period_type = PERIOD_AMIGA; + } + + if (tracker_id == TRACKER_MODSGRAVE) { + snprintf(mod->type, XMP_NAME_SIZE, "%s", tracker); + } else { + snprintf(mod->type, XMP_NAME_SIZE, "%s %s", tracker, magic); + } +#else + libxmp_set_type(m, (mod->chn == 4) ? "Protracker" : "Fasttracker"); +#endif + + MODULE_INFO(); + + /* Load samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + int flags; + + if (!mod->xxs[i].len) + continue; + + flags = (ptkloop && mod->xxs[i].lps == 0) ? SAMPLE_FLAG_FULLREP : 0; + + #ifdef LIBXMP_CORE_PLAYER + if (libxmp_load_sample(m, f, flags, &mod->xxs[i], NULL) < 0) + return -1; + #else + if (ptsong) { + HIO_HANDLE *s; + char sn[XMP_MAXPATH]; + char tmpname[32]; + const char *instname = mod->xxi[i].name; + + if (!instname[0] || !m->dirname) + continue; + + if (libxmp_copy_name_for_fopen(tmpname, instname, 32)) + continue; + + snprintf(sn, XMP_MAXPATH, "%s%s", m->dirname, tmpname); + + if ((s = hio_open(sn, "rb")) != NULL) { + if (libxmp_load_sample(m, s, flags, &mod->xxs[i], NULL) < 0) { + hio_close(s); + return -1; + } + hio_close(s); + } + } else { + uint8 buf[5]; + long pos; + int num; + + if ((pos = hio_tell(f)) < 0) { + return -1; + } + num = hio_read(buf, 1, 5, f); + + if (num == 5 && !memcmp(buf, "ADPCM", 5)) { + flags |= SAMPLE_FLAG_ADPCM; + } else { + hio_seek(f, pos, SEEK_SET); + } + + if (libxmp_load_sample(m, f, flags, &mod->xxs[i], NULL) < 0) return -1; } + #endif + } - strncpy(mod->name, (char *) mh.name, 20); + #ifdef LIBXMP_CORE_PLAYER + if (mod->chn > 4) { + m->quirk &= ~QUIRK_PROTRACK; + m->quirk |= QUIRKS_FT2 | QUIRK_FTMOD; + m->read_event_type = READ_EVENT_FT2; + m->period_type = PERIOD_AMIGA; + } + #else + if (tracker_id == TRACKER_PROTRACKER || tracker_id == TRACKER_OPENMPT) { + m->quirk |= QUIRK_PROTRACK; + } else if (tracker_id == TRACKER_SCREAMTRACKER3) { + m->c4rate = C4_NTSC_RATE; + m->quirk |= QUIRKS_ST3; + m->read_event_type = READ_EVENT_ST3; + } else if (tracker_id == TRACKER_FASTTRACKER || tracker_id == TRACKER_FASTTRACKER2 || tracker_id == TRACKER_TAKETRACKER || tracker_id == TRACKER_MODSGRAVE || mod->chn > 4) { + m->c4rate = C4_NTSC_RATE; + m->quirk |= QUIRKS_FT2 | QUIRK_FTMOD; + m->read_event_type = READ_EVENT_FT2; + m->period_type = PERIOD_AMIGA; + } + #endif - mod->len = mh.len; - /* mod->rst = mh.restart; */ - - if (mod->rst >= mod->len) - mod->rst = 0; - memcpy(mod->xxo, mh.order, 128); - - for (i = 0; i < 128; i++) { - /* This fixes dragnet.mod (garbage in the order list) */ - if (mod->xxo[i] > 0x7f) - break; - if (mod->xxo[i] > mod->pat) - mod->pat = mod->xxo[i]; - } - mod->pat++; - - if (libxmp_init_instrument(m) < 0) - return -1; - - for (i = 0; i < mod->ins; i++) { - struct xmp_instrument *xxi; - struct xmp_subinstrument *sub; - struct xmp_sample *xxs; - - if (libxmp_alloc_subinstrument(mod, i, 1) < 0) - return -1; - - xxi = &mod->xxi[i]; - sub = &xxi->sub[0]; - xxs = &mod->xxs[i]; - - xxs->len = 2 * mh.ins[i].size; - xxs->lps = 2 * mh.ins[i].loop_start; - xxs->lpe = xxs->lps + 2 * mh.ins[i].loop_size; - if (xxs->lpe > xxs->len) { - xxs->lpe = xxs->len; - } - xxs->flg = (mh.ins[i].loop_size > 1 && xxs->lpe >= 4) ? - XMP_SAMPLE_LOOP : 0; - sub->fin = (int8) (mh.ins[i].finetune << 4); - sub->vol = mh.ins[i].volume; - sub->pan = 0x80; - sub->sid = i; - libxmp_instrument_name(mod, i, mh.ins[i].name, 22); - - if (xxs->len > 0) { - xxi->nsm = 1; - } - } - - mod->trk = mod->chn * mod->pat; - - libxmp_set_type(m, (mod->chn == 4) ? "Protracker" : "Fasttracker"); - - MODULE_INFO(); - - for (i = 0; i < mod->ins; i++) { - D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c V%02x %+d %c\n", - i, mod->xxi[i].name, - mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe, - (mh.ins[i].loop_size > 1 && mod->xxs[i].lpe > 8) ? - 'L' : ' ', mod->xxi[i].sub[0].vol, - mod->xxi[i].sub[0].fin >> 4, - ptkloop && mod->xxs[i].lps == 0 && mh.ins[i].loop_size > 1 && - mod->xxs[i].len > mod->xxs[i].lpe ? '!' : ' '); - } - - if (libxmp_init_pattern(mod) < 0) - return -1; - - /* Load and convert patterns */ - D_(D_INFO "Stored patterns: %d", mod->pat); - - for (i = 0; i < mod->pat; i++) { - if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) - return -1; - - for (j = 0; j < (64 * mod->chn); j++) { - event = &EVENT(i, j % mod->chn, j / mod->chn); - if (hio_read(mod_event, 1, 4, f) < 4) { - return -1; - } - libxmp_decode_protracker_event(event, mod_event); - } - } - - /* Load samples */ - - D_(D_INFO "Stored samples: %d", mod->smp); - - for (i = 0; i < mod->smp; i++) { - int flags; - - if (!mod->xxs[i].len) - continue; - - flags = ptkloop ? SAMPLE_FLAG_FULLREP : 0; - - if (libxmp_load_sample(m, f, flags, &mod->xxs[i], NULL) < 0) - return -1; - } - - if (mod->chn > 4) { - m->quirk &= ~QUIRK_PROTRACK; - m->quirk |= QUIRKS_FT2 | QUIRK_FTMOD; - m->read_event_type = READ_EVENT_FT2; - m->period_type = PERIOD_AMIGA; - } - - return 0; + return 0; } diff --git a/internal/c/parts/audio/extras/libxmp-lite/period.c b/internal/c/parts/audio/extras/libxmp-lite/period.c index 854f864e2..2cbf95577 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/period.c +++ b/internal/c/parts/audio/extras/libxmp-lite/period.c @@ -135,14 +135,11 @@ static uint16 pt_period_table[16][36] = { #ifndef M_LN2 #define M_LN2 0.69314718055994530942 #endif -#if !defined(HAVE_ROUND) || defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DJGPP__) + static inline double libxmp_round(double val) { return (val >= 0.0)? floor(val + 0.5) : ceil(val - 0.5); } -#else -#define libxmp_round round -#endif #ifdef LIBXMP_PAULA_SIMULATOR /* Get period from note using Protracker tuning */ @@ -186,13 +183,13 @@ double libxmp_note_to_period(struct context_data *ctx, int n, int f, double adj) switch (m->period_type) { case PERIOD_LINEAR: - per = (240.0 - d) * 16; /* Linear */ + per = (240.0 - d) * 16; /* Linear */ break; case PERIOD_CSPD: - per = 8363.0 * pow(2, n / 12) / 32 + f; /* Hz */ + per = 8363.0 * pow(2, n / 12.0) / 32 + f; /* Hz */ break; default: - per = PERIOD_BASE / pow(2, d / 12); /* Amiga */ + per = PERIOD_BASE / pow(2, d / 12); /* Amiga */ } #ifndef LIBXMP_CORE_PLAYER @@ -228,7 +225,7 @@ int libxmp_period_to_bend(struct context_data *ctx, double p, int n, double adj) struct module_data *m = &ctx->m; double d; - if (n == 0) { + if (n == 0 || p < 0.1) { return 0; } @@ -255,7 +252,7 @@ void libxmp_c2spd_to_note(int c2spd, int *n, int *f) { int c; - if (c2spd == 0) { + if (c2spd <= 0) { *n = *f = 0; return; } @@ -264,3 +261,25 @@ void libxmp_c2spd_to_note(int c2spd, int *n, int *f) *n = c / 128; *f = c % 128; } + +#ifndef LIBXMP_CORE_PLAYER +/* Gravis Ultrasound frequency increments in steps of Hz/1024, where Hz is the + * current rate of the card and is dependent on the active channel count. + * For <=14 channels, the rate is 44100. For 15 to 32 channels, the rate is + * round(14 * 44100 / active_channels). + */ +static const double GUS_rates[19] = { + /* <= 14 */ 44100.0, + /* 15-20 */ 41160.0, 38587.5, 36317.65, 34300.0, 32494.74, 30870.0, + /* 21-26 */ 29400.0, 28063.64, 26843.48, 25725.0, 24696.0, 23746.15, + /* 27-32 */ 22866.67, 22050.0, 21289.66, 20580.0, 19916.13, 19294.75 +}; + +/* Get a Gravis Ultrasound frequency offset in Hz for a given number of steps. + */ +double libxmp_gus_frequency_steps(int num_steps, int num_channels_active) +{ + CLAMP(num_channels_active, 14, 32); + return (num_steps * GUS_rates[num_channels_active - 14]) / 1024.0; +} +#endif diff --git a/internal/c/parts/audio/extras/libxmp-lite/period.h b/internal/c/parts/audio/extras/libxmp-lite/period.h index 10a8c67dc..bf4bd175a 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/period.h +++ b/internal/c/parts/audio/extras/libxmp-lite/period.h @@ -20,5 +20,8 @@ double libxmp_note_to_period_mix (int, int); int libxmp_period_to_note (int); int libxmp_period_to_bend (struct context_data *, double, int, double); void libxmp_c2spd_to_note (int, int *, int *); +#ifndef LIBXMP_CORE_PLAYER +double libxmp_gus_frequency_steps (int, int); +#endif #endif /* LIBXMP_PERIOD_H */ diff --git a/internal/c/parts/audio/extras/libxmp-lite/player.c b/internal/c/parts/audio/extras/libxmp-lite/player.c index 1d6bfdd15..4eacab676 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/player.c +++ b/internal/c/parts/audio/extras/libxmp-lite/player.c @@ -1,5 +1,5 @@ /* Extended Module Player - * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -53,7 +53,6 @@ static const struct retrig_control rval[] = { { 0, 1, 1 }, { 1, 1, 1 }, { 2, 1, 1 }, { 4, 1, 1 }, { 8, 1, 1 }, { 16, 1, 1 }, { 0, 3, 2 }, { 0, 2, 1 }, { 0, 0, 1 } /* Note cut */ - }; @@ -69,15 +68,15 @@ static const struct retrig_control rval[] = { static int check_envelope_end(struct xmp_envelope *env, int x) { int16 *data = env->data; - int index; + int idx; if (~env->flg & XMP_ENVELOPE_ON || env->npt <= 0) return 0; - index = (env->npt - 1) * 2; + idx = (env->npt - 1) * 2; /* last node */ - if (x >= data[index] || index == 0) { + if (x >= data[idx] || idx == 0) { if (~env->flg & XMP_ENVELOPE_LOOP) { return 1; } @@ -90,27 +89,30 @@ static int get_envelope(struct xmp_envelope *env, int x, int def) { int x1, x2, y1, y2; int16 *data = env->data; - int index; + int idx; if (x < 0 || ~env->flg & XMP_ENVELOPE_ON || env->npt <= 0) return def; - index = (env->npt - 1) * 2; + idx = (env->npt - 1) * 2; - x1 = data[index]; /* last node */ - if (x >= x1 || index == 0) { - return data[index + 1]; + x1 = data[idx]; /* last node */ + if (x >= x1 || idx == 0) { + return data[idx + 1]; } do { - index -= 2; - x1 = data[index]; - } while (index > 0 && x1 > x); + idx -= 2; + x1 = data[idx]; + } while (idx > 0 && x1 > x); /* interpolate */ - y1 = data[index + 1]; - x2 = data[index + 2]; - y2 = data[index + 3]; + y1 = data[idx + 1]; + x2 = data[idx + 2]; + y2 = data[idx + 3]; + + /* Interpolation requires x1 <= x <= x2 */ + if (x < x1 || x2 < x1) return y1; return x2 == x1 ? y2 : ((y2 - y1) * (x - x1) / (x2 - x1)) + y1; } @@ -232,14 +234,14 @@ static int update_envelope(struct xmp_envelope *env, int x, int release, int key static int check_envelope_fade(struct xmp_envelope *env, int x) { int16 *data = env->data; - int index; + int idx; if (~env->flg & XMP_ENVELOPE_ON) return 0; - index = (env->npt - 1) * 2; /* last node */ - if (x > data[index]) { - if (data[index + 1] == 0) + idx = (env->npt - 1) * 2; /* last node */ + if (x > data[idx]) { + if (data[idx + 1] == 0) return -1; else return 1; @@ -249,6 +251,218 @@ static int check_envelope_fade(struct xmp_envelope *env, int x) } +#ifndef LIBXMP_CORE_DISABLE_IT + +/* Impulse Tracker's filter effects are implemented using its MIDI macros. + * Any module can customize these and they are parameterized using various + * player and mixer values, which requires parsing them here instead of in + * the loader. Since they're MIDI macros, they can contain actual MIDI junk + * that needs to be skipped, and one macro may have multiple IT commands. */ + +struct midi_stream +{ + const char *pos; + int buffer; + int param; +}; + +static int midi_nibble(struct context_data *ctx, struct channel_data *xc, + int chn, struct midi_stream *in) +{ + struct xmp_instrument *xxi; + struct mixer_voice *vi; + int voc, val, byte = -1; + if (in->buffer >= 0) { + val = in->buffer; + in->buffer = -1; + return val; + } + + while (*in->pos) { + val = *(in->pos)++; + if (val >= '0' && val <= '9') return val - '0'; + if (val >= 'A' && val <= 'F') return val - 'A' + 10; + switch (val) { + case 'z': /* Macro parameter */ + byte = in->param; + break; + case 'n': /* Host key */ + byte = xc->key & 0x7f; + break; + case 'h': /* Host channel */ + byte = chn; + break; + case 'o': /* Offset effect memory */ + /* Intentionally not clamped, see ZxxSecrets.it */ + byte = xc->offset.memory; + break; + case 'm': /* Voice reverse flag */ + voc = libxmp_virt_mapchannel(ctx, chn); + vi = (voc >= 0) ? &ctx->p.virt.voice_array[voc] : NULL; + byte = vi ? !!(vi->flags & VOICE_REVERSE) : 0; + break; + case 'v': /* Note velocity */ + xxi = libxmp_get_instrument(ctx, xc->ins); + byte = ((uint32)ctx->p.gvol * + (uint32)xc->volume * + (uint32)xc->mastervol * + (uint32)xc->gvl * + (uint32)(xxi ? xxi->vol : 0x40)) >> 24UL; + CLAMP(byte, 1, 127); + break; + case 'u': /* Computed velocity */ + byte = xc->macro.finalvol >> 3; + CLAMP(byte, 1, 127); + break; + case 'x': /* Note panning */ + byte = xc->macro.notepan >> 1; + CLAMP(byte, 0, 127); + break; + case 'y': /* Computed panning */ + byte = xc->info_finalpan >> 1; + CLAMP(byte, 0, 127); + break; + case 'a': /* Ins MIDI Bank hi */ + case 'b': /* Ins MIDI Bank lo */ + case 'p': /* Ins MIDI Program */ + case 's': /* MPT: SysEx checksum */ + byte = 0; + break; + case 'c': /* Ins MIDI Channel */ + return 0; + } + + /* Byte output */ + if (byte >= 0) { + in->buffer = byte & 0xf; + return (byte >> 4) & 0xf; + } + } + return -1; +} + +static int midi_byte(struct context_data *ctx, struct channel_data *xc, + int chn, struct midi_stream *in) +{ + int a = midi_nibble(ctx, xc, chn, in); + int b = midi_nibble(ctx, xc, chn, in); + return (a >= 0 && b >= 0) ? (a << 4) | b : -1; +} + +static void apply_midi_macro_effect(struct channel_data *xc, int type, int val) +{ + switch (type) { + case 0: /* Filter cutoff */ + xc->filter.cutoff = val << 1; + break; + case 1: /* Filter resonance */ + xc->filter.resonance = val << 1; + break; + } +} + +static void execute_midi_macro(struct context_data *ctx, struct channel_data *xc, + int chn, struct midi_macro *midi, int param) +{ + struct midi_stream in; + int byte, cmd, val; + + in.pos = midi->data; + in.buffer = -1; + in.param = param; + + while (*in.pos) { + /* Very simple MIDI 1.0 parser--most bytes can just be ignored + * (or passed through, if libxmp gets MIDI output). All bytes + * with bit 7 are statuses which interrupt unfinished messages + * ("Data Types: Status Bytes") or are real time messages. + * This holds even for SysEx messages, which end at ANY non- + * real time status ("System Common Messages: EOX"). + * + * IT intercepts internal "messages" that begin with F0 F0, + * which in MIDI is a useless zero-length SysEx followed by + * a second SysEx. They are four bytes long including F0 F0, + * and shouldn't be passed through. OpenMPT also uses F0 F1. + */ + cmd = -1; + byte = midi_byte(ctx, xc, chn, &in); + if (byte == 0xf0) { + byte = midi_byte(ctx, xc, chn, &in); + if (byte == 0xf0 || byte == 0xf1) + cmd = byte & 0xf; + } + if (cmd < 0) { + if (byte == 0xfa || byte == 0xfc || byte == 0xff) { + /* These real time statuses can appear anywhere + * (even in SysEx) and reset the channel filter + * params. See: OpenMPT ZxxSecrets.it */ + apply_midi_macro_effect(xc, 0, 127); + apply_midi_macro_effect(xc, 1, 0); + } + continue; + } + cmd = midi_byte(ctx, xc, chn, &in) | (cmd << 8); + val = midi_byte(ctx, xc, chn, &in); + if (cmd < 0 || cmd >= 0x80 || val < 0 || val >= 0x80) { + continue; + } + apply_midi_macro_effect(xc, cmd, val); + } +} + +/* This needs to occur before all process_* functions: + * - It modifies the filter parameters, used by process_frequency. + * - process_volume and process_pan apply slide effects, which the + * filter parameters expect to occur after macro effect parsing. */ +static void update_midi_macro(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct channel_data *xc = &p->xc_data[chn]; + struct midi_macro_data *midicfg = m->midi; + struct midi_macro *macro; + int val; + + if (TEST(MIDI_MACRO) && HAS_QUIRK(QUIRK_FILTER)) { + if (xc->macro.slide > 0) { + xc->macro.val += xc->macro.slide; + if (xc->macro.val > xc->macro.target) { + xc->macro.val = xc->macro.target; + xc->macro.slide = 0; + } + } else if (xc->macro.slide < 0) { + xc->macro.val += xc->macro.slide; + if (xc->macro.val < xc->macro.target) { + xc->macro.val = xc->macro.target; + xc->macro.slide = 0; + } + } else if (p->frame) { + /* Execute non-smooth macros on frame 0 only */ + return; + } + + val = (int)xc->macro.val; + if (val >= 0x80) { + if (midicfg) { + macro = &midicfg->fixed[val - 0x80]; + execute_midi_macro(ctx, xc, chn, macro, val); + } else if (val < 0x90) { + /* Default fixed macro: set resonance */ + apply_midi_macro_effect(xc, 1, (val - 0x80) << 3); + } + } else if (midicfg) { + macro = &midicfg->param[xc->macro.active]; + execute_midi_macro(ctx, xc, chn, macro, val); + } else if (xc->macro.active == 0) { + /* Default parameterized macro 0: set filter cutoff */ + apply_midi_macro_effect(xc, 0, val); + } + } +} + +#endif /* LIBXMP_CORE_DISABLE_IT */ + + #ifndef LIBXMP_CORE_PLAYER /* From http://www.un4seen.com/forum/?topic=7554.0 @@ -268,23 +482,39 @@ static const int invloop_table[] = { 0, 5, 6, 7, 8, 10, 11, 13, 16, 19, 22, 26, 32, 43, 64, 128 }; -static void update_invloop(struct module_data *m, struct channel_data *xc) +static void update_invloop(struct context_data *ctx, struct channel_data *xc) { - struct xmp_sample *xxs = &m->mod.xxs[xc->smp]; - int len; + struct xmp_sample *xxs = libxmp_get_sample(ctx, xc->smp); + struct module_data *m = &ctx->m; + int lps, len = -1; xc->invloop.count += invloop_table[xc->invloop.speed]; - if ((xxs->flg & XMP_SAMPLE_LOOP) && xc->invloop.count >= 128) { + if (xxs != NULL) { + if (xxs->flg & XMP_SAMPLE_LOOP) { + lps = xxs->lps; + len = xxs->lpe - lps; + } else if (xxs->flg & XMP_SAMPLE_SLOOP) { + /* Some formats that support invert loop use sustain + * loops instead (Digital Symphony). */ + lps = m->xtra[xc->smp].sus; + len = m->xtra[xc->smp].sue - lps; + } + } + + if (len >= 0 && xc->invloop.count >= 128) { xc->invloop.count = 0; - len = xxs->lpe - xxs->lps; if (++xc->invloop.pos > len) { xc->invloop.pos = 0; } + if (xxs->data == NULL) { + return; + } + if (~xxs->flg & XMP_SAMPLE_16BIT) { - xxs->data[xxs->lps + xc->invloop.pos] ^= 0xff; + xxs->data[lps + xc->invloop.pos] ^= 0xff; } } } @@ -412,7 +642,7 @@ static void reset_channels(struct context_data *ctx) xc->mastervol = mod->xxc[i].vol; xc->pan.val = mod->xxc[i].pan; } - + #ifndef LIBXMP_CORE_DISABLE_IT xc->filter.cutoff = 0xff; @@ -624,7 +854,7 @@ static int tremor_s3m(struct context_data *ctx, int chn, int finalvol) * Update channel data */ -#define DOENV_RELEASE ((TEST_NOTE(NOTE_RELEASE) || act == VIRT_ACTION_OFF)) +#define DOENV_RELEASE ((TEST_NOTE(NOTE_ENV_RELEASE) || act == VIRT_ACTION_OFF)) static void process_volume(struct context_data *ctx, int chn, int act) { @@ -648,7 +878,7 @@ static void process_volume(struct context_data *ctx, int chn, int act) /* If IT, only apply fadeout on note release if we don't * have envelope, or if we have envelope loop */ - if (TEST_NOTE(NOTE_RELEASE) || act == VIRT_ACTION_OFF) { + if (TEST_NOTE(NOTE_ENV_RELEASE) || act == VIRT_ACTION_OFF) { if ((~instrument->aei.flg & XMP_ENVELOPE_ON) || (instrument->aei.flg & XMP_ENVELOPE_LOOP)) { fade = 1; @@ -656,29 +886,30 @@ static void process_volume(struct context_data *ctx, int chn, int act) } } else { if (~instrument->aei.flg & XMP_ENVELOPE_ON) { - if (TEST_NOTE(NOTE_RELEASE)) { + if (TEST_NOTE(NOTE_ENV_RELEASE)) { xc->fadeout = 0; } } - if (TEST_NOTE(NOTE_RELEASE) || act == VIRT_ACTION_OFF) { + if (TEST_NOTE(NOTE_ENV_RELEASE) || act == VIRT_ACTION_OFF) { fade = 1; } } - if (TEST_NOTE(NOTE_FADEOUT) || act == VIRT_ACTION_FADE) { - fade = 1; + if (!TEST_PER(VENV_PAUSE)) { + xc->v_idx = update_envelope(&instrument->aei, xc->v_idx, + DOENV_RELEASE, TEST(KEY_OFF), IS_PLAYER_MODE_IT()); } - if (fade) { - if (xc->fadeout > xc->ins_fade) { - xc->fadeout -= xc->ins_fade; - } else { - xc->fadeout = 0; + vol_envelope = get_envelope(&instrument->aei, xc->v_idx, 64); + if (check_envelope_end(&instrument->aei, xc->v_idx)) { + if (vol_envelope == 0) { SET_NOTE(NOTE_END); } + SET_NOTE(NOTE_ENV_END); } + /* IT starts fadeout automatically at the end of the volume envelope. */ switch (check_envelope_fade(&instrument->aei, xc->v_idx)) { case -1: SET_NOTE(NOTE_END); @@ -694,17 +925,19 @@ static void process_volume(struct context_data *ctx, int chn, int act) } } - if (!TEST_PER(VENV_PAUSE)) { - xc->v_idx = update_envelope(&instrument->aei, xc->v_idx, - DOENV_RELEASE, TEST(KEY_OFF), IS_PLAYER_MODE_IT()); + /* IT envelope fadeout starts immediately after the envelope tick, + * so process fadeout after the volume envelope. */ + if (TEST_NOTE(NOTE_FADEOUT) || act == VIRT_ACTION_FADE) { + fade = 1; } - vol_envelope = get_envelope(&instrument->aei, xc->v_idx, 64); - if (check_envelope_end(&instrument->aei, xc->v_idx)) { - if (vol_envelope == 0) { + if (fade) { + if (xc->fadeout > xc->ins_fade) { + xc->fadeout -= xc->ins_fade; + } else { + xc->fadeout = 0; SET_NOTE(NOTE_END); } - SET_NOTE(NOTE_ENV_END); } /* If note ended in background channel, we can safely reset it */ @@ -762,6 +995,9 @@ static void process_volume(struct context_data *ctx, int chn, int act) } else { finalvol = tremor_s3m(ctx, chn, finalvol); } +#ifndef LIBXMP_CORE_DISABLE_IT + xc->macro.finalvol = finalvol; +#endif if (chn < m->mod.chn) { finalvol = finalvol * p->master_vol / 100; @@ -863,7 +1099,7 @@ static void process_frequency(struct context_data *ctx, int chn, int act) /* Sanity check */ if (period < 0.1) { period = 0.1; - } + } /* Arpeggio */ arp = arpeggio(ctx, xc); @@ -894,7 +1130,7 @@ static void process_frequency(struct context_data *ctx, int chn, int act) } } } - + /* Envelope */ if (xc->f_idx >= 0 && (~instrument->fei.flg & XMP_ENVELOPE_FLT)) { @@ -946,12 +1182,12 @@ static void process_frequency(struct context_data *ctx, int chn, int act) /* For xmp_get_frame_info() */ xc->info_pitchbend = linear_bend >> 7; - xc->info_period = final_period * 4096; + xc->info_period = MIN(final_period * 4096, INT_MAX); if (IS_PERIOD_MODRNG()) { - CLAMP(xc->info_period, - libxmp_note_to_period(ctx, MAX_NOTE_MOD, xc->finetune, 0) * 4096, - libxmp_note_to_period(ctx, MIN_NOTE_MOD, xc->finetune, 0) * 4096); + const double min_period = libxmp_note_to_period(ctx, MAX_NOTE_MOD, xc->finetune, 0) * 4096; + const double max_period = libxmp_note_to_period(ctx, MIN_NOTE_MOD, xc->finetune, 0) * 4096; + CLAMP(xc->info_period, min_period, max_period); } else if (xc->info_period < (1 << 12)) { xc->info_period = (1 << 12); } @@ -977,18 +1213,22 @@ static void process_frequency(struct context_data *ctx, int chn, int act) if (cutoff > 0xff) { cutoff = 0xff; - } else if (cutoff < 0xff) { + } + /* IT: cutoff 127 + resonance 0 turns off the filter, but this + * is only applied when playing a new note without toneporta. + * All other combinations take effect immediately. + * See OpenMPT filter-reset.it, filter-reset-carry.it */ + if (cutoff < 0xfe || resonance > 0 || xc->filter.can_disable) { int a0, b0, b1; libxmp_filter_setup(s->freq, cutoff, resonance, &a0, &b0, &b1); libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_A0, a0); libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_B0, b0); libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_B1, b1); libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_RESONANCE, resonance); + libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_CUTOFF, cutoff); + xc->filter.can_disable = 0; } - /* Always set cutoff */ - libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_CUTOFF, cutoff); - #endif } @@ -1018,6 +1258,7 @@ static void process_pan(struct context_data *ctx, int chn, int act) libxmp_lfo_update(&xc->panbrello.lfo); } } + xc->macro.notepan = xc->pan.val + panbrello + 0x80; #endif channel_pan = xc->pan.val; @@ -1080,13 +1321,19 @@ static void update_volume(struct context_data *ctx, int chn) #ifndef LIBXMP_CORE_PLAYER if (TEST_PER(VOL_SLIDE)) { - if (xc->vol.slide > 0 && xc->volume > m->volbase) { - xc->volume = m->volbase; - RESET_PER(VOL_SLIDE); + if (xc->vol.slide > 0) { + int target = MAX(xc->vol.target - 1, m->volbase); + if (xc->volume > target) { + xc->volume = target; + RESET_PER(VOL_SLIDE); + } } - if (xc->vol.slide < 0 && xc->volume < 0) { - xc->volume = 0; - RESET_PER(VOL_SLIDE); + if (xc->vol.slide < 0) { + int target = xc->vol.target > 0 ? MIN(0, xc->vol.target - 1) : 0; + if (xc->volume < target) { + xc->volume = target; + RESET_PER(VOL_SLIDE); + } } } #endif @@ -1111,7 +1358,7 @@ static void update_volume(struct context_data *ctx, int chn) * Unlike fine volume slides in the effect column, * fine volume slides in the volume column are only * ever executed on the first tick -- not on multiples - * of the first tick if there is a pattern delay. + * of the first tick if there is a pattern delay. */ if (!f->rowdelay_set || f->rowdelay_set & ROWDELAY_FIRST_FRAME) { xc->volume += xc->vol.fslide2; @@ -1178,7 +1425,7 @@ static void update_frequency(struct context_data *ctx, int chn) } } } - } + } } if (is_first_frame(ctx)) { @@ -1199,10 +1446,11 @@ static void update_frequency(struct context_data *ctx, int chn) case PERIOD_LINEAR: CLAMP(xc->period, MIN_PERIOD_L, MAX_PERIOD_L); break; - case PERIOD_MODRNG: - CLAMP(xc->period, - libxmp_note_to_period(ctx, MAX_NOTE_MOD, xc->finetune, 0), - libxmp_note_to_period(ctx, MIN_NOTE_MOD, xc->finetune, 0)); + case PERIOD_MODRNG: { + const double min_period = libxmp_note_to_period(ctx, MAX_NOTE_MOD, xc->finetune, 0); + const double max_period = libxmp_note_to_period(ctx, MIN_NOTE_MOD, xc->finetune, 0); + CLAMP(xc->period, min_period, max_period); + } break; } @@ -1261,6 +1509,11 @@ static void play_channel(struct context_data *ctx, int chn) } } +#ifndef LIBXMP_CORE_DISABLE_IT + /* IT MIDI macros need to update regardless of the current voice state. */ + update_midi_macro(ctx, chn); +#endif + act = libxmp_virt_cstat(ctx, chn); if (act == VIRT_INVALID) { /* We need this to keep processing global volume slides */ @@ -1298,9 +1551,16 @@ static void play_channel(struct context_data *ctx, int chn) xc->volume += rval[xc->retrig.type].s; xc->volume *= rval[xc->retrig.type].m; xc->volume /= rval[xc->retrig.type].d; - xc->retrig.count = LSN(xc->retrig.val); + xc->retrig.count = LSN(xc->retrig.val); + + if (xc->retrig.limit > 0) { + /* Limit the number of retriggers. */ + --xc->retrig.limit; + if (xc->retrig.limit == 0) + RESET(RETRIG); + } } - } + } /* Do keyoff */ if (xc->keyoff) { @@ -1308,7 +1568,7 @@ static void play_channel(struct context_data *ctx, int chn) SET_NOTE(NOTE_RELEASE); } - libxmp_virt_release(ctx, chn, TEST_NOTE(NOTE_RELEASE)); + libxmp_virt_release(ctx, chn, TEST_NOTE(NOTE_SAMPLE_RELEASE)); update_volume(ctx, chn); update_frequency(ctx, chn); @@ -1319,13 +1579,13 @@ static void play_channel(struct context_data *ctx, int chn) process_pan(ctx, chn, act); #ifndef LIBXMP_CORE_PLAYER - if (HAS_QUIRK(QUIRK_PROTRACK) && xc->ins < mod->ins) { - update_invloop(m, xc); + if (HAS_QUIRK(QUIRK_PROTRACK | QUIRK_INVLOOP) && xc->ins < mod->ins) { + update_invloop(ctx, xc); } #endif if (TEST_NOTE(NOTE_SUSEXIT)) { - SET_NOTE(NOTE_RELEASE); + SET_NOTE(NOTE_ENV_RELEASE); } xc->info_position = libxmp_virt_getvoicepos(ctx, chn); @@ -1342,7 +1602,7 @@ static void inject_event(struct context_data *ctx) struct xmp_module *mod = &m->mod; struct smix_data *smix = &ctx->smix; int chn; - + for (chn = 0; chn < mod->chn + smix->chn; chn++) { struct xmp_event *e = &p->inject_event[chn]; if (e->_flag > 0) { @@ -1362,13 +1622,14 @@ static void next_order(struct context_data *ctx) struct flow_control *f = &p->flow; struct module_data *m = &ctx->m; struct xmp_module *mod = &m->mod; + int reset_gvol = 0; int mark; do { - p->ord++; + p->ord++; /* Restart module */ - mark = HAS_QUIRK(QUIRK_MARKER) && mod->xxo[p->ord] == 0xff; + mark = HAS_QUIRK(QUIRK_MARKER) && p->ord < mod->len && mod->xxo[p->ord] == 0xff; if (p->ord >= mod->len || mark) { if (mod->rst > mod->len || mod->xxo[mod->rst] >= mod->pat || @@ -1381,11 +1642,19 @@ static void next_order(struct context_data *ctx) p->ord = m->seq_data[p->sequence].entry_point; } } - - p->gvol = m->xxo_info[p->ord].gvl; + /* This might be a marker, so delay updating global + * volume until an actual pattern is found */ + reset_gvol = 1; } } while (mod->xxo[p->ord] >= mod->pat); + if (reset_gvol) + p->gvol = m->xxo_info[p->ord].gvl; + +#ifndef LIBXMP_CORE_PLAYER + /* Archimedes line jump -- don't reset time tracking. */ + if (f->jump_in_pat != p->ord) +#endif p->current_time = m->xxo_info[p->ord].time; f->num_rows = mod->xxp[mod->xxo[p->ord]]->rows; @@ -1398,6 +1667,8 @@ static void next_order(struct context_data *ctx) p->frame = 0; #ifndef LIBXMP_CORE_PLAYER + f->jump_in_pat = -1; + /* Reset persistent effects at new pattern */ if (HAS_QUIRK(QUIRK_PERPAT)) { int chn; @@ -1426,18 +1697,18 @@ static void next_row(struct context_data *ctx) next_order(ctx); } else { - if (f->loop_chn) { - p->row = f->loop[f->loop_chn - 1].start - 1; - f->loop_chn = 0; - } - if (f->rowdelay == 0) { p->row++; f->rowdelay_set = 0; } else { f->rowdelay--; } - + + if (f->loop_chn) { + p->row = f->loop[f->loop_chn - 1].start; + f->loop_chn = 0; + } + /* check end of pattern */ if (p->row >= f->num_rows) { next_order(ctx); @@ -1486,6 +1757,21 @@ static void update_from_ord_info(struct context_data *ctx) #endif } +void libxmp_reset_flow(struct context_data *ctx) +{ + struct flow_control *f = &ctx->p.flow; + f->jumpline = 0; + f->jump = -1; + f->pbreak = 0; + f->loop_chn = 0; + f->delay = 0; + f->rowdelay = 0; + f->rowdelay_set = 0; +#ifndef LIBXMP_CORE_PLAYER + f->jump_in_pat = -1; +#endif +} + int xmp_start_player(xmp_context opaque, int rate, int format) { struct context_data *ctx = (struct context_data *)opaque; @@ -1539,8 +1825,11 @@ int xmp_start_player(xmp_context opaque, int rate, int format) mod->len = 0; } - if (mod->len == 0 || mod->chn == 0) { + if (mod->len == 0) { /* set variables to sane state */ + /* Note: previously did this for mod->chn == 0, which caused + * crashes on invalid order 0s. 0 channel modules are technically + * valid (if useless) so just let them play normally. */ p->ord = p->scan[0].ord = 0; p->row = p->scan[0].row = 0; f->end_point = 0; @@ -1557,19 +1846,15 @@ int xmp_start_player(xmp_context opaque, int rate, int format) goto err; } - f->delay = 0; - f->jumpline = 0; - f->jump = -1; - f->pbreak = 0; - f->rowdelay_set = 0; + libxmp_reset_flow(ctx); - f->loop = calloc(p->virt.virt_channels, sizeof(struct pattern_loop)); + f->loop = (struct pattern_loop *) calloc(p->virt.virt_channels, sizeof(struct pattern_loop)); if (f->loop == NULL) { ret = -XMP_ERROR_SYSTEM; goto err; } - p->xc_data = calloc(p->virt.virt_channels, sizeof(struct channel_data)); + p->xc_data = (struct channel_data *) calloc(p->virt.virt_channels, sizeof(struct channel_data)); if (p->xc_data == NULL) { ret = -XMP_ERROR_SYSTEM; goto err1; @@ -1578,11 +1863,14 @@ int xmp_start_player(xmp_context opaque, int rate, int format) /* Reset our buffer pointers */ xmp_play_buffer(opaque, NULL, 0, 0); -#ifndef LIBXMP_CORE_PLAYER +#ifndef LIBXMP_CORE_DISABLE_IT for (i = 0; i < p->virt.virt_channels; i++) { struct channel_data *xc = &p->xc_data[i]; + xc->filter.cutoff = 0xff; +#ifndef LIBXMP_CORE_PLAYER if (libxmp_new_channel_extras(ctx, xc) < 0) goto err2; +#endif } #endif reset_channels(ctx); @@ -1776,7 +2064,7 @@ int xmp_play_buffer(xmp_context opaque, void *out_buffer, int size, int loop) } p->buffer_data.consumed = 0; - p->buffer_data.in_buffer = fi.buffer; + p->buffer_data.in_buffer = (char *)fi.buffer; p->buffer_data.in_size = fi.buffer_size; } @@ -1791,7 +2079,7 @@ int xmp_play_buffer(xmp_context opaque, void *out_buffer, int size, int loop) return ret; } - + void xmp_end_player(xmp_context opaque) { struct context_data *ctx = (struct context_data *)opaque; @@ -1903,7 +2191,7 @@ void xmp_get_frame_info(xmp_context opaque, struct xmp_frame_info *info) struct xmp_track *track; struct xmp_event *event; int trk; - + ci->note = c->key; ci->pitchbend = c->info_pitchbend; ci->period = c->info_period; @@ -1914,7 +2202,7 @@ void xmp_get_frame_info(xmp_context opaque, struct xmp_frame_info *info) ci->pan = c->info_finalpan; ci->reserved = 0; memset(&ci->event, 0, sizeof(*event)); - + if (info->pattern < mod->pat && info->row < info->num_rows) { trk = mod->xxp[info->pattern]->index[i]; track = mod->xxt[trk]; diff --git a/internal/c/parts/audio/extras/libxmp-lite/player.h b/internal/c/parts/audio/extras/libxmp-lite/player.h index 4b46e608e..e1fc9ab79 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/player.h +++ b/internal/c/parts/audio/extras/libxmp-lite/player.h @@ -56,20 +56,30 @@ struct retrig_control { #define FINE_VOLS_2 (1 << 25) #define KEY_OFF (1 << 26) /* for IT release on envloop end */ #define TREMOR (1 << 27) /* for XM tremor */ +#define MIDI_MACRO (1 << 28) /* IT midi macro */ #define NOTE_FADEOUT (1 << 0) -#define NOTE_RELEASE (1 << 1) +#define NOTE_ENV_RELEASE (1 << 1) /* envelope sustain loop release */ #define NOTE_END (1 << 2) #define NOTE_CUT (1 << 3) #define NOTE_ENV_END (1 << 4) #define NOTE_SAMPLE_END (1 << 5) #define NOTE_SET (1 << 6) /* for IT portamento after keyoff */ -#define NOTE_SUSEXIT (1 << 7) /* for delayed note release */ +#define NOTE_SUSEXIT (1 << 7) /* for delayed envelope release */ #define NOTE_KEY_CUT (1 << 8) /* note cut with XMP_KEY_CUT event */ #define NOTE_GLISSANDO (1 << 9) +#define NOTE_SAMPLE_RELEASE (1 << 10) /* sample sustain loop release */ +/* Most of the time, these should be set/reset together. */ +#define NOTE_RELEASE (NOTE_ENV_RELEASE | NOTE_SAMPLE_RELEASE) + +/* Note: checking the data pointer for samples should be good enough to filter + * broken samples, since libxmp_load_sample will always allocate it for valid + * samples of >0 length and bound the loop values for these samples. */ #define IS_VALID_INSTRUMENT(x) ((uint32)(x) < mod->ins && mod->xxi[(x)].nsm > 0) #define IS_VALID_INSTRUMENT_OR_SFX(x) (((uint32)(x) < mod->ins && mod->xxi[(x)].nsm > 0) || (smix->ins > 0 && (uint32)(x) < mod->ins + smix->ins)) +#define IS_VALID_SAMPLE(x) ((uint32)(x) < mod->smp && mod->xxs[(x)].data != NULL) +#define IS_VALID_NOTE(x) ((uint32)(x) < XMP_MAX_KEYS) struct instrument_vibrato { int phase; @@ -147,6 +157,7 @@ struct channel_data { int val; /* Retrig value */ int count; /* Retrig counter */ int type; /* Retrig type */ + int limit; /* Number of retrigs */ } retrig; struct { @@ -163,6 +174,9 @@ struct channel_data { #ifndef LIBXMP_CORE_DISABLE_IT int fslide2; int memory2; /* Volume slide effect memory */ +#endif +#ifndef LIBXMP_CORE_PLAYER + int target; /* Target for persistent volslide */ #endif } vol; @@ -194,6 +208,7 @@ struct channel_data { int dir; /* Tone portamento up/down directionh */ int slide; /* Delta for tone portamento */ int memory; /* Tone portamento effect memory */ + int note_memory;/* Tone portamento note memory (ULT) */ } porta; struct { @@ -207,7 +222,7 @@ struct channel_data { int fslide; /* Pan fine slide value */ int memory; /* Pan slide effect memory */ int surround; /* Surround channel flag */ - } pan; + } pan; struct { int speed; @@ -224,8 +239,17 @@ struct channel_data { int cutoff; /* IT filter cutoff frequency */ int resonance; /* IT filter resonance */ int envelope; /* IT filter envelope */ + int can_disable;/* IT hack: allow disabling for cutoff 127 */ } filter; + struct { + float val; /* Current macro effect (use float for slides) */ + float target; /* Current macro target (smooth macro) */ + float slide; /* Current macro slide (smooth macro) */ + int active; /* Current active parameterized macro */ + int finalvol; /* Previous tick calculated volume (0-0x400) */ + int notepan; /* Previous tick note panning (0x80 center) */ + } macro; #endif #ifndef LIBXMP_CORE_PLAYER diff --git a/internal/c/parts/audio/extras/libxmp-lite/read_event.c b/internal/c/parts/audio/extras/libxmp-lite/read_event.c index 784731f61..6d1e40a7b 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/read_event.c +++ b/internal/c/parts/audio/extras/libxmp-lite/read_event.c @@ -1,5 +1,5 @@ /* Extended Module Player - * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -31,11 +31,6 @@ #endif -static inline int is_valid_note(int note) -{ - return (note >= 0 && note < XMP_MAX_KEYS); -} - static struct xmp_subinstrument *get_subinstrument(struct context_data *ctx, int ins, int key) { @@ -45,7 +40,7 @@ static struct xmp_subinstrument *get_subinstrument(struct context_data *ctx, if (IS_VALID_INSTRUMENT(ins)) { instrument = &mod->xxi[ins]; - if (is_valid_note(key)) { + if (IS_VALID_NOTE(key)) { int mapped = instrument->map[key].ins; if (mapped != 0xff && mapped >= 0 && mapped < instrument->nsm) return &instrument->sub[mapped]; @@ -67,7 +62,7 @@ static void reset_envelopes(struct context_data *ctx, struct channel_data *xc) if (!IS_VALID_INSTRUMENT(xc->ins)) return; - RESET_NOTE(NOTE_ENV_END); + RESET_NOTE(NOTE_ENV_END); xc->v_idx = -1; xc->p_idx = -1; @@ -76,6 +71,20 @@ static void reset_envelopes(struct context_data *ctx, struct channel_data *xc) #ifndef LIBXMP_CORE_DISABLE_IT +static void reset_envelope_volume(struct context_data *ctx, + struct channel_data *xc) +{ + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + + if (!IS_VALID_INSTRUMENT(xc->ins)) + return; + + RESET_NOTE(NOTE_ENV_END); + + xc->v_idx = -1; +} + static void reset_envelopes_carry(struct context_data *ctx, struct channel_data *xc) { @@ -109,18 +118,8 @@ static void set_effect_defaults(struct context_data *ctx, int note, struct channel_data *xc, int is_toneporta) { struct module_data *m = &ctx->m; - struct xmp_module *mod = &m->mod; - struct smix_data *smix = &ctx->smix; - + if (sub != NULL && note >= 0) { - struct xmp_instrument *xxi; - - if (xc->ins >= mod->ins) { - xxi = &smix->xxi[xc->ins - mod->ins]; - } else { - xxi = &mod->xxi[xc->ins]; - } - if (!HAS_QUIRK(QUIRK_PROTRACK)) { xc->finetune = sub->fin; } @@ -129,16 +128,16 @@ static void set_effect_defaults(struct context_data *ctx, int note, #ifndef LIBXMP_CORE_DISABLE_IT if (sub->ifc & 0x80) { xc->filter.cutoff = (sub->ifc - 0x80) * 2; - } else if (~xxi->fei.flg & XMP_ENVELOPE_FLT) { - xc->filter.cutoff = 0xff; } xc->filter.envelope = 0x100; if (sub->ifr & 0x80) { xc->filter.resonance = (sub->ifr - 0x80) * 2; - } /* else { - xc->filter.resonance = 0; - } */ + } + + /* IT: on a new note without toneporta, allow a computed cutoff + * of 127 with resonance 0 to disable the filter. */ + xc->filter.can_disable = !is_toneporta; #endif /* TODO: should probably expand the LFO period size instead @@ -215,13 +214,14 @@ static void set_period_ft2(struct context_data *ctx, int note, #ifndef LIBXMP_CORE_PLAYER #define IS_SFX_PITCH(x) ((x) == FX_PITCH_ADD || (x) == FX_PITCH_SUB) #define IS_TONEPORTA(x) ((x) == FX_TONEPORTA || (x) == FX_TONE_VSLIDE \ - || (x) == FX_PER_TPORTA) + || (x) == FX_PER_TPORTA || (x) == FX_ULT_TPORTA \ + || (x) == FX_FAR_TPORTA) #else #define IS_TONEPORTA(x) ((x) == FX_TONEPORTA || (x) == FX_TONE_VSLIDE) #endif #define set_patch(ctx,chn,ins,smp,note) \ - libxmp_virt_setpatch(ctx, chn, ins, smp, note, 0, 0, 0) + libxmp_virt_setpatch(ctx, chn, ins, smp, note, 0, 0, 0, 0) static int read_event_mod(struct context_data *ctx, struct xmp_event *e, int chn) { @@ -293,7 +293,7 @@ static int read_event_mod(struct context_data *ctx, struct xmp_event *e, int chn if (e->note == XMP_KEY_OFF) { SET_NOTE(NOTE_RELEASE); use_ins_vol = 0; - } else if (!is_toneporta && is_valid_note(e->note - 1)) { + } else if (!is_toneporta && IS_VALID_NOTE(e->note - 1)) { xc->key = e->note - 1; RESET_NOTE(NOTE_END); @@ -306,7 +306,7 @@ static int read_event_mod(struct context_data *ctx, struct xmp_event *e, int chn note = xc->key + sub->xpo + transp; smp = sub->sid; - if (mod->xxs[smp].len == 0) { + if (!IS_VALID_SAMPLE(smp)) { smp = -1; } @@ -332,6 +332,7 @@ static int read_event_mod(struct context_data *ctx, struct xmp_event *e, int chn if (e->vol) { xc->volume = e->vol - 1; SET(NEW_VOL); + RESET_PER(VOL_SLIDE); /* FIXME: should this be for FAR only? */ } /* Secondary effect handled first */ @@ -375,6 +376,7 @@ static int sustain_check(struct xmp_envelope *env, int idx) { return (env && (env->flg & XMP_ENVELOPE_ON) && + (env->flg & XMP_ENVELOPE_SUS) && (~env->flg & XMP_ENVELOPE_LOOP) && idx == env->data[env->sus << 1]); } @@ -457,19 +459,17 @@ static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn /* FT2: Retrieve old instrument volume */ if (ins) { if (key == 0 || key >= XMP_KEY_OFF) { - struct xmp_subinstrument *sub; - /* Previous instrument */ sub = get_subinstrument(ctx, xc->ins, xc->key); /* No note */ if (sub != NULL) { - int p = mod->xxc[chn].pan - 128; + int pan = mod->xxc[chn].pan - 128; xc->volume = sub->vol; if (!HAS_QUIRK(QUIRK_FTMOD)) { - xc->pan.val = p + ((sub->pan - 128) * - (128 - abs(p))) / 128 + 128; + xc->pan.val = pan + ((sub->pan - 128) * + (128 - abs(pan))) / 128 + 128; } xc->ins_fade = mod->xxi[xc->ins].rls; @@ -516,11 +516,8 @@ static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn } /* Check note */ - if (ins) { if (key > 0 && key < XMP_KEY_OFF) { - struct xmp_subinstrument *sub; - /* Retrieve volume when we have note */ /* and only if we have instrument, otherwise we're in @@ -530,12 +527,12 @@ static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn /* Current instrument */ sub = get_subinstrument(ctx, xc->ins, key - 1); if (sub != NULL) { - int p = mod->xxc[chn].pan - 128; + int pan = mod->xxc[chn].pan - 128; xc->volume = sub->vol; if (!HAS_QUIRK(QUIRK_FTMOD)) { - xc->pan.val = p + ((sub->pan - 128) * - (128 - abs(p))) / 128 + 128; + xc->pan.val = pan + ((sub->pan - 128) * + (128 - abs(pan))) / 128 + 128; } xc->ins_fade = mod->xxi[xc->ins].rls; @@ -570,7 +567,7 @@ static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn env_on = 1; } } - + if (env_on || (!vol_set && (!ev.ins || !delay_fx))) { if (sustain_check(env, xc->v_idx)) { /* See OpenMPT EnvOff.xm. In certain @@ -627,7 +624,7 @@ static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn * and remains in the memory." */ sub = NULL; - if (is_valid_note(key - 1)) { + if (IS_VALID_NOTE(key - 1)) { int k = key - 1; sub = get_subinstrument(ctx, xc->ins, k); if (!new_invalid_ins && sub != NULL) { @@ -640,7 +637,7 @@ static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn } } - if (is_valid_note(key - 1)) { + if (IS_VALID_NOTE(key - 1)) { xc->key = --key; xc->fadeout = 0x10000; RESET_NOTE(NOTE_END); @@ -658,7 +655,7 @@ static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn note = key + sub->xpo + transp; smp = sub->sid; - if (mod->xxs[smp].len == 0) { + if (!IS_VALID_SAMPLE(smp)) { smp = -1; } @@ -814,7 +811,7 @@ static int read_event_st3(struct context_data *ctx, struct xmp_event *e, int chn if (not_same_ins) { xc->offset.val = 0; } - } else if (is_valid_note(e->note - 1)) { + } else if (IS_VALID_NOTE(e->note - 1)) { xc->key = e->note - 1; RESET_NOTE(NOTE_END); @@ -827,7 +824,7 @@ static int read_event_st3(struct context_data *ctx, struct xmp_event *e, int chn note = xc->key + sub->xpo + transp; smp = sub->sid; - if (mod->xxs[smp].len == 0) { + if (!IS_VALID_SAMPLE(smp)) { smp = -1; } @@ -961,10 +958,10 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn) int is_toneporta, is_release; int candidate_ins; int reset_env; + int reset_susloop; int use_ins_vol; int sample_mode; int toneporta_offset; - int disabled_toneporta; int retrig_ins; struct xmp_event ev; @@ -987,11 +984,11 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn) is_toneporta = 0; is_release = 0; reset_env = 0; + reset_susloop = 0; use_ins_vol = 0; candidate_ins = xc->ins; sample_mode = !HAS_QUIRK(QUIRK_VIRTUAL); toneporta_offset = 0; - disabled_toneporta = 0; retrig_ins = 0; /* Keyoff + instrument retrigs current instrument in old fx mode */ @@ -1025,7 +1022,7 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn) is_toneporta = 1; } - if (TEST_NOTE(NOTE_RELEASE | NOTE_FADEOUT)) { + if (TEST_NOTE(NOTE_ENV_RELEASE | NOTE_FADEOUT)) { is_release = 1; } @@ -1035,10 +1032,8 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn) /* Off-Porta.it */ if (is_toneporta && ev.fxt == FX_OFFSET) { - disabled_toneporta = 1; - is_toneporta = 0; + toneporta_offset = 1; if (!HAS_QUIRK(QUIRK_PRENV)) { - toneporta_offset = 1; RESET_NOTE(NOTE_ENV_END); } } @@ -1077,9 +1072,11 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn) if (set_new_ins) { SET(NEW_INS); - use_ins_vol = 1; reset_env = 1; } + /* Sample default volume is always enabled if a valid sample + * is provided (Atomic Playboy, default_volume.it). */ + use_ins_vol = 1; xc->per_flags = 0; if (IS_VALID_INSTRUMENT(ins)) { @@ -1119,7 +1116,10 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn) } } } else { - /* In sample mode invalid ins cut previous ins */ + /* In sample mode invalid instruments cut the current + * note (OpenMPT SampleNumberChange.it). + * TODO: portamento_sustain.it order 3 row 19: when + * sample release is set, this isn't always done? */ if (sample_mode) { xc->volume = 0; } @@ -1133,13 +1133,14 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn) /* Check note */ - if (key && !new_invalid_ins) { + if (key) { SET(NEW_NOTE); SET_NOTE(NOTE_SET); if (key == XMP_KEY_FADE) { SET_NOTE(NOTE_FADEOUT); reset_env = 0; + reset_susloop = 0; use_ins_vol = 0; } else if (key == XMP_KEY_CUT) { SET_NOTE(NOTE_END | NOTE_CUT | NOTE_KEY_CUT); @@ -1161,24 +1162,27 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn) * However, never reset the envelope (see OpenMPT wnoteoff.it). */ reset_env = 0; + reset_susloop = 0; if (!ev.ins) { use_ins_vol = 0; } - } else { + } else if (!new_invalid_ins) { + /* Sample sustain release should always carry for tone + * portamento, and is not reset unless a note is + * present (Atomic Playboy, portamento_sustain.it). */ /* portamento_after_keyoff.it test case */ /* also see suburban_streets o13 c45 */ - if (ev.ins || !is_toneporta) { - if (!disabled_toneporta) { - reset_env = 1; - } + if (!is_toneporta) { + reset_env = 1; + reset_susloop = 1; } if (is_toneporta) { if (not_same_ins || TEST_NOTE(NOTE_END)) { SET(NEW_INS); - RESET_NOTE(NOTE_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT); + RESET_NOTE(NOTE_ENV_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT); } else { - if (is_valid_note(key - 1)) { + if (IS_VALID_NOTE(key - 1)) { xc->key_porta = key - 1; } key = 0; @@ -1187,7 +1191,9 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn) } } - if (is_valid_note(key - 1) && !new_invalid_ins) { + /* TODO: instrument change+porta(+release?) doesn't require a key. + * Order 3/row 11 of portamento_sustain.it should change the sample. */ + if (IS_VALID_NOTE(key - 1) && !new_invalid_ins) { if (TEST_NOTE(NOTE_CUT)) { use_ins_vol = 1; /* See OpenMPT NoteOffInstr.it */ } @@ -1199,20 +1205,30 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn) if (sub != NULL) { int transp = mod->xxi[candidate_ins].map[key].xpo; int smp, to; + int dct; int rvv; + /* Clear note delay before duplicating channels: + * it_note_delay_nna.it */ + xc->delay = 0; + note = key + sub->xpo + transp; smp = sub->sid; - if (smp >= mod->smp || mod->xxs[smp].len == 0) { + if (!IS_VALID_SAMPLE(smp)) { smp = -1; } + dct = sub->dct; if (not_same_smp) { fix_period(ctx, chn, sub); - libxmp_virt_resetchannel(ctx, chn); + /* Toneporta, even when not executed, disables + * NNA and DCAs for the current note: + * portamento_nna_sample.it, gxsmp2.it */ + libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_CUT); + dct = XMP_INST_DCT_OFF; } to = libxmp_virt_setpatch(ctx, chn, candidate_ins, smp, - note, sub->nna, sub->dct, sub->dca); + note, key, sub->nna, dct, sub->dca); /* Random value for volume swing */ rvv = sub->rvv & 0xff; @@ -1268,10 +1284,11 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn) * finished (OpenMPT test EnvReset.it). This must take place after * channel copies in case of NNA (see test/test.it) * Also if we have envelope in carry mode, check fadeout + * Also, only reset the volume envelope. (it_fade_env_reset_carry.it) */ if (ev.ins && TEST_NOTE(NOTE_ENV_END)) { if (check_fadeout(ctx, xc, candidate_ins)) { - reset_envelopes(ctx, xc); + reset_envelope_volume(ctx, xc); } else { reset_env = 0; } @@ -1279,11 +1296,14 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn) if (reset_env) { if (ev.note) { - RESET_NOTE(NOTE_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT); + RESET_NOTE(NOTE_ENV_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT); } /* Set after copying to new virtual channel (see ambio.it) */ xc->fadeout = 0x10000; } + if (reset_susloop && ev.note) { + RESET_NOTE(NOTE_SAMPLE_RELEASE); + } /* See OpenMPT wnoteoff.it vs noteoff3.it */ if (retrig_ins && not_same_ins) { @@ -1306,7 +1326,7 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn) if (TEST_NOTE(NOTE_CUT)) { reset_envelopes(ctx, xc); - } else if (!toneporta_offset) { + } else if (!toneporta_offset || HAS_QUIRK(QUIRK_PRENV)) { reset_envelopes_carry(ctx, xc); } RESET_NOTE(NOTE_CUT); @@ -1336,6 +1356,8 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn) if (note >= 0) { xc->note = note; + } + if (note >= 0 || toneporta_offset) { libxmp_virt_voicepos(ctx, chn, xc->offset.val); } @@ -1420,7 +1442,7 @@ static int read_event_med(struct context_data *ctx, struct xmp_event *e, int chn SET_NOTE(NOTE_END); xc->period = 0; libxmp_virt_resetchannel(ctx, chn); - } else if (!is_toneporta && IS_VALID_INSTRUMENT(xc->ins) && is_valid_note(e->note - 1)) { + } else if (!is_toneporta && IS_VALID_INSTRUMENT(xc->ins) && IS_VALID_NOTE(e->note - 1)) { struct xmp_instrument *xxi = &mod->xxi[xc->ins]; xc->key = e->note - 1; @@ -1445,7 +1467,7 @@ static int read_event_med(struct context_data *ctx, struct xmp_event *e, int chn note = xc->key + sub->xpo + transp; smp = sub->sid; - if (mod->xxs[smp].len == 0) { + if (!IS_VALID_SAMPLE(smp)) { smp = -1; } @@ -1510,7 +1532,7 @@ static int read_event_smix(struct context_data *ctx, struct xmp_event *e, int ch struct xmp_module *mod = &m->mod; struct channel_data *xc = &p->xc_data[chn]; struct xmp_subinstrument *sub; - int is_smix_ins; + struct xmp_instrument *xxi; int ins, note, transp, smp; xc->flags = 0; @@ -1518,33 +1540,39 @@ static int read_event_smix(struct context_data *ctx, struct xmp_event *e, int ch if (!e->ins) return 0; - is_smix_ins = 0; ins = e->ins - 1; SET(NEW_INS); - xc->fadeout = 0x10000; xc->per_flags = 0; xc->offset.val = 0; - RESET_NOTE(NOTE_RELEASE); + RESET_NOTE(NOTE_RELEASE|NOTE_FADEOUT); - xc->ins = ins; - - if (ins >= mod->ins && ins < mod->ins + smix->ins) { - is_smix_ins = 1; - xc->ins_fade = smix->xxi[xc->ins - mod->ins].rls; + xxi = libxmp_get_instrument(ctx, ins); + if (xxi != NULL) { + xc->ins_fade = xxi->rls; } + xc->ins = ins; SET(NEW_NOTE); if (e->note == XMP_KEY_OFF) { SET_NOTE(NOTE_RELEASE); return 0; + } else if (e->note == XMP_KEY_FADE) { + SET_NOTE(NOTE_FADEOUT); + return 0; + } else if (e->note == XMP_KEY_CUT) { + SET_NOTE(NOTE_END); + xc->period = 0; + libxmp_virt_resetchannel(ctx, chn); + return 0; } xc->key = e->note - 1; + xc->fadeout = 0x10000; RESET_NOTE(NOTE_END); - if (is_smix_ins) { - sub = &smix->xxi[xc->ins - mod->ins].sub[0]; + if (ins >= mod->ins && ins < mod->ins + smix->ins) { + sub = &xxi->sub[0]; if (sub == NULL) { return 0; } @@ -1559,15 +1587,15 @@ static int read_event_smix(struct context_data *ctx, struct xmp_event *e, int ch xc->smp = smp; } } else { - sub = is_valid_note(xc->key) ? + sub = IS_VALID_NOTE(xc->key) ? get_subinstrument(ctx, xc->ins, xc->key) : NULL; if (sub == NULL) { return 0; } - transp = mod->xxi[xc->ins].map[xc->key].xpo; + transp = xxi->map[xc->key].xpo; note = xc->key + sub->xpo + transp; smp = sub->sid; - if (mod->xxs[smp].len == 0) + if (!IS_VALID_SAMPLE(smp)) smp = -1; if (smp >= 0 && smp < mod->smp) { set_patch(ctx, chn, xc->ins, smp, note); diff --git a/internal/c/parts/audio/extras/libxmp-lite/s3m.h b/internal/c/parts/audio/extras/libxmp-lite/s3m.h index 816f8dd57..9b88f1b92 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/s3m.h +++ b/internal/c/parts/audio/extras/libxmp-lite/s3m.h @@ -20,6 +20,9 @@ * THE SOFTWARE. */ +#ifndef LIBXMP_LOADERS_S3M_H +#define LIBXMP_LOADERS_S3M_H + /* S3M packed pattern macros */ #define S3M_EOR 0 /* End of row */ #define S3M_CH_MASK 0x1f /* Channel */ @@ -27,10 +30,16 @@ #define S3M_VOL_FOLLOWS 0x40 /* Volume follows */ #define S3M_FX_FOLLOWS 0x80 /* Effect and parameter follow */ +/* S3M mix volume macros */ +#define S3M_MV_VOLUME 0x7f /* Module mix volume, typically 16 to 127 */ +#define S3M_MV_STEREO 0x80 /* Module is stereo if set, otherwise mono */ + /* S3M channel info macros */ #define S3M_CH_ON 0x80 /* Psi says it's bit 8, I'll assume bit 7 */ #define S3M_CH_OFF 0xff -#define S3M_CH_PAN 0x7f /* Left/Right */ +#define S3M_CH_NUMBER 0x1f +#define S3M_CH_RIGHT 0x08 +#define S3M_CH_ADLIB 0x10 /* S3M channel pan macros */ #define S3M_PAN_SET 0x20 @@ -79,7 +88,8 @@ struct s3m_file_header { }; struct s3m_instrument_header { - uint8 dosname[13]; /* DOS file name */ + uint8 dosname[12]; /* DOS file name */ + uint8 memseg_hi; /* High byte of sample pointer */ uint16 memseg; /* Pointer to sample data */ uint32 length; /* Length */ uint32 loopbeg; /* Loop begin */ @@ -113,4 +123,4 @@ struct s3m_adlib_header { uint32 magic; /* 'SCRI' */ }; #endif - +#endif /* LIBXMP_LOADERS_S3M_H */ diff --git a/internal/c/parts/audio/extras/libxmp-lite/s3m_load.c b/internal/c/parts/audio/extras/libxmp-lite/s3m_load.c index 716cbe7a7..ebc887034 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/s3m_load.c +++ b/internal/c/parts/audio/extras/libxmp-lite/s3m_load.c @@ -1,5 +1,5 @@ /* Extended Module Player - * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -219,6 +219,7 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start) uint8 n, b; uint16 *pp_ins; /* Parapointers to instruments */ uint16 *pp_pat; /* Parapointers to patterns */ + int stereo; int ret; uint8 buf[96] @@ -262,12 +263,12 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start) libxmp_copy_adjust(mod->name, sfh.name, 28); - pp_ins = calloc(2, sfh.insnum); + pp_ins = (uint16 *) calloc(sfh.insnum, sizeof(uint16)); if (pp_ins == NULL) { goto err; } - pp_pat = calloc(2, sfh.patnum); + pp_pat = (uint16 *) calloc(sfh.patnum, sizeof(uint16)); if (pp_pat == NULL) { goto err2; } @@ -283,15 +284,49 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start) mod->bpm = sfh.it; mod->chn = 0; + /* Mix volume and stereo flag conversion (reported by Saga Musix). + * 1) Old format uses mix volume 0-7, and the stereo flag is 0x10. + * 2) Newer ST3s unconditionally convert MV 0x02 and 0x12 to 0x20. + */ + m->mvolbase = 48; + + if (sfh.ffi == 1) { + m->mvol = ((sfh.mv & 0xf) + 1) * 0x10; + stereo = sfh.mv & 0x10; + CLAMP(m->mvol, 0x10, 0x7f); + + } else if (sfh.mv == 0x02 || sfh.mv == 0x12) { + m->mvol = 0x20; + stereo = sfh.mv & 0x10; + + } else { + m->mvol = sfh.mv & S3M_MV_VOLUME; + stereo = sfh.mv & S3M_MV_STEREO; + + if (m->mvol == 0) { + m->mvol = 48; /* Default is 48 */ + } else if (m->mvol < 16) { + m->mvol = 16; /* Minimum is 16 */ + } + } + + /* "Note that in stereo, the mastermul is internally multiplied by + * 11/8 inside the player since there is generally more room in the + * output stream." Do the inverse to affect fewer modules. */ + if (!stereo) { + m->mvol = m->mvol * 8 / 11; + } + for (i = 0; i < 32; i++) { + int x; if (sfh.chset[i] == S3M_CH_OFF) continue; mod->chn = i + 1; - if (sfh.mv & 0x80) { /* stereo */ - int x = sfh.chset[i] & S3M_CH_PAN; - mod->xxc[i].pan = (x & 0x0f) < 8 ? 0x30 : 0xc0; + x = sfh.chset[i] & S3M_CH_NUMBER; + if (stereo && x < S3M_CH_ADLIB) { + mod->xxc[i].pan = x < S3M_CH_RIGHT ? 0x30 : 0xc0; } else { mod->xxc[i].pan = 0x80; } @@ -346,9 +381,6 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start) uint8 x = hio_read8(f); if (x & S3M_PAN_SET) { mod->xxc[i].pan = (x << 4) & 0xff; - } else { - mod->xxc[i].pan = - sfh.mv % 0x80 ? 0x30 + 0xa0 * (i & 1) : 0x80; } } @@ -381,8 +413,15 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start) } break; case 5: - snprintf(tracker_name, 40, "OpenMPT %d.%02x", - (sfh.version & 0x0f00) >> 8, sfh.version & 0xff); + if (sfh.version == 0x5447) { + strcpy(tracker_name, "Graoumf Tracker"); + } else if (sfh.rsvd2[0] || sfh.rsvd2[1]) { + snprintf(tracker_name, 40, "OpenMPT %d.%02x.%02x.%02x", + (sfh.version & 0x0f00) >> 8, sfh.version & 0xff, sfh.rsvd2[1], sfh.rsvd2[0]); + } else { + snprintf(tracker_name, 40, "OpenMPT %d.%02x", + (sfh.version & 0x0f00) >> 8, sfh.version & 0xff); + } m->quirk |= QUIRK_ST3BUGS; break; case 4: @@ -472,7 +511,7 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start) } } - D_(D_INFO "Stereo enabled: %s", sfh.mv & 0x80 ? "yes" : "no"); + D_(D_INFO "Stereo enabled: %s", stereo ? "yes" : "no"); D_(D_INFO "Pan settings: %s", sfh.dp ? "no" : "yes"); if (libxmp_init_instrument(m) < 0) @@ -487,8 +526,9 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start) struct xmp_sample *xxs = &mod->xxs[i]; struct xmp_subinstrument *sub; int load_sample_flags; + uint32 sample_segment; - xxi->sub = calloc(sizeof(struct xmp_subinstrument), 1); + xxi->sub = (struct xmp_subinstrument *) calloc(1, sizeof(struct xmp_subinstrument)); if (xxi->sub == NULL) { goto err3; } @@ -527,9 +567,7 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start) sub->vol = sah.vol; libxmp_c2spd_to_note(sah.c2spd, &sub->xpo, &sub->fin); sub->xpo += 12; - ret = - libxmp_load_sample(m, f, SAMPLE_FLAG_ADLIB, xxs, - (char *)sah.reg); + ret = libxmp_load_sample(m, f, SAMPLE_FLAG_ADLIB, xxs, (char *)sah.reg); if (ret < 0) goto err3; @@ -541,7 +579,8 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start) #endif } - memcpy(sih.dosname, buf + 1, 13); /* DOS file name */ + memcpy(sih.dosname, buf + 1, 12); /* DOS file name */ + sih.memseg_hi = buf[13]; /* High byte of sample pointer */ sih.memseg = readmem16l(buf + 14); /* Pointer to sample data */ sih.length = readmem32l(buf + 16); /* Length */ @@ -598,7 +637,9 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start) libxmp_c2spd_to_note(sih.c2spd, &sub->xpo, &sub->fin); - if (hio_seek(f, start + 16L * sih.memseg, SEEK_SET) < 0) { + sample_segment = sih.memseg + ((uint32)sih.memseg_hi << 16); + + if (hio_seek(f, start + 16L * sample_segment, SEEK_SET) < 0) { goto err3; } diff --git a/internal/c/parts/audio/extras/libxmp-lite/sample.c b/internal/c/parts/audio/extras/libxmp-lite/sample.c index 97ee6d3d9..e563897cb 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/sample.c +++ b/internal/c/parts/audio/extras/libxmp-lite/sample.c @@ -1,5 +1,5 @@ /* Extended Module Player - * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -24,7 +24,6 @@ #include "loader.h" #ifndef LIBXMP_CORE_PLAYER - /* * From the Audio File Formats (version 2.5) * Submitted-by: Guido van Rossum @@ -55,7 +54,6 @@ static const int8 vdic_table[128] = { /* 120 */ 95, 98, 103, 109, 114, 120, 126, 127 }; - /* Convert 7 bit samples to 8 bit */ static void convert_7bit_to_8bit(uint8 *p, int l) { @@ -95,24 +93,23 @@ static void adpcm4_decoder(uint8 *inp, uint8 *outp, char *tab, int len) *outp++ = delta; } } - #endif /* Convert differential to absolute sample data */ static void convert_delta(uint8 *p, int l, int r) { uint16 *w = (uint16 *)p; - uint16 abs = 0; + uint16 absval = 0; if (r) { for (; l--;) { - abs = *w + abs; - *w++ = abs; + absval = *w + absval; + *w++ = absval; } } else { for (; l--;) { - abs = *p + abs; - *p++ = (uint8) abs; + absval = *p + absval; + *p++ = (uint8) absval; } } } @@ -163,41 +160,10 @@ static void convert_stereo_to_mono(uint8 *p, int l, int r) } #endif -static void unroll_loop(struct xmp_sample *xxs) -{ - int8 *s8; - int16 *s16; - int start, loop_size; - int i; - - s16 = (int16 *)xxs->data; - s8 = (int8 *)xxs->data; - - if (xxs->len > xxs->lpe) { - start = xxs->lpe; - } else { - start = xxs->len; - } - - loop_size = xxs->lpe - xxs->lps; - - if (xxs->flg & XMP_SAMPLE_16BIT) { - s16 += start; - for (i = 0; i < loop_size; i++) { - *(s16 + i) = *(s16 - i - 1); - } - } else { - s8 += start; - for (i = 0; i < loop_size; i++) { - *(s8 + i) = *(s8 - i - 1); - } - } -} - int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct xmp_sample *xxs, const void *buffer) { - int bytelen, extralen, unroll_extralen, i; + int bytelen, extralen, i; #ifndef LIBXMP_CORE_PLAYER /* Adlib FM patches */ @@ -264,7 +230,6 @@ int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct x */ bytelen = xxs->len; extralen = 4; - unroll_extralen = 0; /* Disable birectional loop flag if sample is not looped */ @@ -272,25 +237,18 @@ int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct x if (~xxs->flg & XMP_SAMPLE_LOOP) xxs->flg &= ~XMP_SAMPLE_LOOP_BIDIR; } - /* Unroll bidirectional loops - */ - if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) { - unroll_extralen = (xxs->lpe - xxs->lps) - - (xxs->len - xxs->lpe); - - if (unroll_extralen < 0) { - unroll_extralen = 0; - } + if (xxs->flg & XMP_SAMPLE_SLOOP_BIDIR) { + if (~xxs->flg & XMP_SAMPLE_SLOOP) + xxs->flg &= ~XMP_SAMPLE_SLOOP_BIDIR; } if (xxs->flg & XMP_SAMPLE_16BIT) { bytelen *= 2; extralen *= 2; - unroll_extralen *= 2; } /* add guard bytes before the buffer for higher order interpolation */ - xxs->data = malloc(bytelen + extralen + unroll_extralen + 4); + xxs->data = (unsigned char *) malloc(bytelen + extralen + 4); if (xxs->data == NULL) { goto err; } @@ -379,12 +337,6 @@ int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct x xxs->flg |= XMP_SAMPLE_LOOP_FULL; } - /* Unroll bidirectional loops */ - if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) { - unroll_loop(xxs); - bytelen += unroll_extralen; - } - /* Add extra samples at end */ if (xxs->flg & XMP_SAMPLE_16BIT) { for (i = 0; i < 8; i++) { @@ -404,28 +356,6 @@ int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct x xxs->data[-1] = xxs->data[0]; } - /* Fix sample at loop */ - if (xxs->flg & XMP_SAMPLE_LOOP) { - int lpe = xxs->lpe; - int lps = xxs->lps; - - if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) { - lpe += lpe - lps; - } - - if (xxs->flg & XMP_SAMPLE_16BIT) { - lpe <<= 1; - lps <<= 1; - for (i = 0; i < 8; i++) { - xxs->data[lpe + i] = xxs->data[lps + i]; - } - } else { - for (i = 0; i < 4; i++) { - xxs->data[lpe + i] = xxs->data[lps + i]; - } - } - } - return 0; #ifndef LIBXMP_CORE_PLAYER @@ -438,8 +368,8 @@ int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct x void libxmp_free_sample(struct xmp_sample *s) { - if (s->data) { - free(s->data - 4); - s->data = NULL; /* prevent double free in PCM load error */ - } + if (s->data) { + free(s->data - 4); + s->data = NULL; /* prevent double free in PCM load error */ + } } diff --git a/internal/c/parts/audio/extras/libxmp-lite/scan.c b/internal/c/parts/audio/extras/libxmp-lite/scan.c index 79f3c3bda..a1a2daa28 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/scan.c +++ b/internal/c/parts/audio/extras/libxmp-lite/scan.c @@ -1,5 +1,5 @@ /* Extended Module Player - * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -42,6 +42,12 @@ #include "effects.h" #include "mixer.h" +#ifndef LIBXMP_CORE_PLAYER +#include "far_extras.h" +#endif + +#define VBLANK_TIME_THRESHOLD 480000 /* 8 minutes */ + #define S3M_END 0xff #define S3M_SKIP 0xfe @@ -50,30 +56,35 @@ static int scan_module(struct context_data *ctx, int ep, int chain) { struct player_data *p = &ctx->p; struct module_data *m = &ctx->m; - struct xmp_module *mod = &m->mod; + const struct xmp_module *mod = &m->mod; + const struct xmp_track *tracks[XMP_MAX_CHANNELS]; + const struct xmp_event *event; int parm, gvol_memory, f1, f2, p1, p2, ord, ord2; int row, last_row, break_row, row_count, row_count_total; int orders_since_last_valid, any_valid; int gvl, bpm, speed, base_time, chn; int frame_count; double time, start_time; - int loop_chn, loop_num, inside_loop; + int loop_chn, loop_num, inside_loop, line_jump; int pdelay = 0; int loop_count[XMP_MAX_CHANNELS]; int loop_row[XMP_MAX_CHANNELS]; - struct xmp_event* event; int i, pat; int has_marker; struct ord_data *info; #ifndef LIBXMP_CORE_PLAYER int st26_speed; + int far_tempo_coarse, far_tempo_fine, far_tempo_mode; #endif + /* was 255, but Global trash goes to 318. + * Higher limit for MEDs, defiance.crybaby.5 has blocks with 2048+ rows. */ + const int row_limit = IS_PLAYER_MODE_MED() ? 3200 : 512; if (mod->len == 0) return 0; for (i = 0; i < mod->len; i++) { - int pat = mod->xxo[i]; + pat = mod->xxo[i]; memset(m->scan_cnt[i], 0, pat >= mod->pat ? 1 : mod->xxp[pat]->rows ? mod->xxp[pat]->rows : 1); } @@ -84,6 +95,7 @@ static int scan_module(struct context_data *ctx, int ep, int chain) } loop_num = 0; loop_chn = -1; + line_jump = 0; gvl = mod->gvl; bpm = mod->bpm; @@ -92,6 +104,15 @@ static int scan_module(struct context_data *ctx, int ep, int chain) base_time = m->rrate; #ifndef LIBXMP_CORE_PLAYER st26_speed = 0; + far_tempo_coarse = 4; + far_tempo_fine = 0; + far_tempo_mode = 1; + + if (HAS_FAR_MODULE_EXTRAS(ctx->m)) { + far_tempo_coarse = FAR_MODULE_EXTRAS(ctx->m)->coarse_tempo; + libxmp_far_translate_tempo(far_tempo_mode, 0, far_tempo_coarse, + &far_tempo_fine, &speed, &bpm); + } #endif has_marker = HAS_QUIRK(QUIRK_MARKER); @@ -196,6 +217,11 @@ static int scan_module(struct context_data *ctx, int ep, int chain) info->start_row = break_row; } + /* Get tracks in advance to speed up the event parsing loop. */ + for (chn = 0; chn < mod->chn; chn++) { + tracks[chn] = mod->xxt[TRACK_NUM(pat, chn)]; + } + last_row = mod->xxp[pat]->rows; for (row = break_row, break_row = 0; row < last_row; row++, row_count++, row_count_total++) { /* Prevent crashes caused by large softmixer frames */ @@ -216,12 +242,12 @@ static int scan_module(struct context_data *ctx, int ep, int chain) * (...) it dies at the end of position 2F */ - if (row_count_total > 512) { /* was 255, but Global trash goes to 318. */ + if (row_count_total > row_limit) { D_(D_CRIT "row_count_total = %d @ ord %d, pat %d, row %d; ending scan", row_count_total, ord, pat, row); goto end_module; } - if (!loop_num && m->scan_cnt[ord][row]) { + if (!loop_num && !line_jump && m->scan_cnt[ord][row]) { row_count--; goto end_module; } @@ -237,12 +263,14 @@ static int scan_module(struct context_data *ctx, int ep, int chain) } pdelay = 0; + line_jump = 0; for (chn = 0; chn < mod->chn; chn++) { - if (row >= mod->xxt[mod->xxp[pat]->index[chn]]->rows) + if (row >= tracks[chn]->rows) continue; - event = &EVENT(mod->xxo[ord], chn, row); + //event = &EVENT(mod->xxo[ord], chn, row); + event = &tracks[chn]->event[row]; f1 = event->fxt; p1 = event->fxp; @@ -290,23 +318,23 @@ static int scan_module(struct context_data *ctx, int ep, int chain) } } - if ((f1 == FX_SPEED && p1) || (f2 == FX_SPEED && p2)) { - parm = (f1 == FX_SPEED) ? p1 : p2; + /* Some formats can have two FX_SPEED effects, and both need + * to be checked. Slot 2 is currently handled first. */ + for (i = 0; i < 2; i++) { + parm = i ? p1 : p2; + if ((i ? f1 : f2) != FX_SPEED || parm == 0) + continue; frame_count += row_count * speed; row_count = 0; - if (parm) { - if (HAS_QUIRK(QUIRK_NOBPM) || p->flags & XMP_FLAGS_VBLANK || parm < 0x20) { - if (parm > 0) { - speed = parm; + if (HAS_QUIRK(QUIRK_NOBPM) || p->flags & XMP_FLAGS_VBLANK || parm < 0x20) { + speed = parm; #ifndef LIBXMP_CORE_PLAYER - st26_speed = 0; + st26_speed = 0; #endif - } - } else { - time += m->time_factor * frame_count * base_time / bpm; - frame_count = 0; - bpm = parm; - } + } else { + time += m->time_factor * frame_count * base_time / bpm; + frame_count = 0; + bpm = parm; } } @@ -327,6 +355,39 @@ static int scan_module(struct context_data *ctx, int ep, int chain) st26_speed = MSN(p1); } } + + /* FAR tempo processing */ + + if (f1 == FX_FAR_TEMPO || f1 == FX_FAR_F_TEMPO) { + int far_speed, far_bpm, fine_change = 0; + if (f1 == FX_FAR_TEMPO) { + if (MSN(p1)) { + far_tempo_mode = MSN(p1) - 1; + } else { + far_tempo_coarse = LSN(p1); + } + } + if (f1 == FX_FAR_F_TEMPO) { + if (MSN(p1)) { + far_tempo_fine += MSN(p1); + fine_change = MSN(p1); + } else if (LSN(p1)) { + far_tempo_fine -= LSN(p1); + fine_change = -LSN(p1); + } else { + far_tempo_fine = 0; + } + } + if (libxmp_far_translate_tempo(far_tempo_mode, fine_change, + far_tempo_coarse, &far_tempo_fine, &far_speed, &far_bpm) == 0) { + frame_count += row_count * speed; + row_count = 0; + time += m->time_factor * frame_count * base_time / bpm; + frame_count = 0; + speed = far_speed; + bpm = far_bpm; + } + } #endif if ((f1 == FX_S3M_SPEED && p1) || (f2 == FX_S3M_SPEED && p2)) { @@ -343,7 +404,7 @@ static int scan_module(struct context_data *ctx, int ep, int chain) if ((f1 == FX_S3M_BPM && p1) || (f2 == FX_S3M_BPM && p2)) { parm = (f1 == FX_S3M_BPM) ? p1 : p2; - if (parm >= 0x20) { + if (parm >= XMP_MIN_BPM) { frame_count += row_count * speed; row_count = 0; time += m->time_factor * frame_count * base_time / bpm; @@ -417,6 +478,19 @@ static int scan_module(struct context_data *ctx, int ep, int chain) last_row = 0; } +#ifndef LIBXMP_CORE_PLAYER + /* Archimedes line jump */ + if (f1 == FX_LINE_JUMP || f2 == FX_LINE_JUMP) { + /* Don't set order if preceded by jump or break. */ + if (last_row > 0) + ord2 = ord; + parm = (f1 == FX_LINE_JUMP) ? p1 : p2; + break_row = parm; + last_row = 0; + line_jump = 1; + } +#endif + if (f1 == FX_EXTENDED || f2 == FX_EXTENDED) { parm = (f1 == FX_EXTENDED) ? p1 : p2; @@ -514,6 +588,56 @@ end_module: return (time + m->time_factor * frame_count * base_time / bpm); } +static void reset_scan_data(struct context_data *ctx) +{ + int i; + for (i = 0; i < XMP_MAX_MOD_LENGTH; i++) { + ctx->m.xxo_info[i].time = -1; + } + memset(ctx->p.sequence_control, 0xff, XMP_MAX_MOD_LENGTH); +} + +#ifndef LIBXMP_CORE_PLAYER +static void compare_vblank_scan(struct context_data *ctx) +{ + /* Calculate both CIA and VBlank time for certain long MODs + * and pick the more likely (i.e. shorter) one. The same logic + * works regardless of the initial mode selected--either way, + * the wrong timing mode usually makes modules MUCH longer. */ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct ord_data *info_backup; + struct scan_data scan_backup; + unsigned char ctrl_backup[256]; + + if ((info_backup = (struct ord_data *)malloc(sizeof(m->xxo_info))) != NULL) { + /* Back up the current info to avoid a third scan. */ + scan_backup = p->scan[0]; + memcpy(info_backup, m->xxo_info, sizeof(m->xxo_info)); + memcpy(ctrl_backup, p->sequence_control, + sizeof(p->sequence_control)); + + reset_scan_data(ctx); + + m->quirk ^= QUIRK_NOBPM; + p->scan[0].time = scan_module(ctx, 0, 0); + + D_(D_INFO "%-6s %dms", !HAS_QUIRK(QUIRK_NOBPM)?"VBlank":"CIA", scan_backup.time); + D_(D_INFO "%-6s %dms", HAS_QUIRK(QUIRK_NOBPM)?"VBlank":"CIA", p->scan[0].time); + + if (p->scan[0].time >= scan_backup.time) { + m->quirk ^= QUIRK_NOBPM; + p->scan[0] = scan_backup; + memcpy(m->xxo_info, info_backup, sizeof(m->xxo_info)); + memcpy(p->sequence_control, ctrl_backup, + sizeof(p->sequence_control)); + } + + free(info_backup); + } +} +#endif + int libxmp_get_sequence(struct context_data *ctx, int ord) { struct player_data *p = &ctx->p; @@ -525,23 +649,35 @@ int libxmp_scan_sequences(struct context_data *ctx) struct player_data *p = &ctx->p; struct module_data *m = &ctx->m; struct xmp_module *mod = &m->mod; + struct scan_data *s; int i, ep; int seq; unsigned char temp_ep[XMP_MAX_MOD_LENGTH]; + s = (struct scan_data *) realloc(p->scan, MAX(1, mod->len) * sizeof(struct scan_data)); + if (!s) { + D_(D_CRIT "failed to allocate scan data"); + return -1; + } + p->scan = s; + /* Initialize order data to prevent overwrite when a position is used * multiple times at different starting points (see janosik.xm). */ - for (i = 0; i < XMP_MAX_MOD_LENGTH; i++) { - m->xxo_info[i].time = -1; - } + reset_scan_data(ctx); ep = 0; - memset(p->sequence_control, 0xff, XMP_MAX_MOD_LENGTH); temp_ep[0] = 0; p->scan[0].time = scan_module(ctx, ep, 0); seq = 1; +#ifndef LIBXMP_CORE_PLAYER + if (m->compare_vblank && !(p->flags & XMP_FLAGS_VBLANK) && + p->scan[0].time >= VBLANK_TIME_THRESHOLD) { + compare_vblank_scan(ctx); + } +#endif + if (p->scan[0].time < 0) { D_(D_CRIT "scan was not able to find any valid orders"); return -1; @@ -567,6 +703,12 @@ int libxmp_scan_sequences(struct context_data *ctx) } } + if (seq < mod->len) { + s = (struct scan_data *) realloc(p->scan, seq * sizeof(struct scan_data)); + if (s != NULL) { + p->scan = s; + } + } m->num_sequences = seq; /* Now place entry points in the public accessible array */ @@ -575,6 +717,5 @@ int libxmp_scan_sequences(struct context_data *ctx) m->seq_data[i].duration = p->scan[i].time; } - return 0; } diff --git a/internal/c/parts/audio/extras/libxmp-lite/smix.c b/internal/c/parts/audio/extras/libxmp-lite/smix.c index 0e4c61e5d..aac110652 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/smix.c +++ b/internal/c/parts/audio/extras/libxmp-lite/smix.c @@ -1,5 +1,5 @@ /* Extended Module Player - * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -34,7 +34,9 @@ struct xmp_instrument *libxmp_get_instrument(struct context_data *ctx, int ins) struct xmp_module *mod = &m->mod; struct xmp_instrument *xxi; - if (ins < mod->ins) { + if (ins < 0) { + xxi = NULL; + } else if (ins < mod->ins) { xxi = &mod->xxi[ins]; } else if (ins < mod->ins + smix->ins) { xxi = &smix->xxi[ins - mod->ins]; @@ -52,7 +54,9 @@ struct xmp_sample *libxmp_get_sample(struct context_data *ctx, int smp) struct xmp_module *mod = &m->mod; struct xmp_sample *xxs; - if (smp < mod->smp) { + if (smp < 0) { + xxs = NULL; + } else if (smp < mod->smp) { xxs = &mod->xxs[smp]; } else if (smp < mod->smp + smix->smp) { xxs = &smix->xxs[smp - mod->smp]; @@ -72,11 +76,11 @@ int xmp_start_smix(xmp_context opaque, int chn, int smp) return -XMP_ERROR_STATE; } - smix->xxi = calloc(sizeof (struct xmp_instrument), smp); + smix->xxi = (struct xmp_instrument *) calloc(smp, sizeof(struct xmp_instrument)); if (smix->xxi == NULL) { goto err; } - smix->xxs = calloc(sizeof (struct xmp_sample), smp); + smix->xxs = (struct xmp_sample *) calloc(smp, sizeof(struct xmp_sample)); if (smix->xxs == NULL) { goto err1; } @@ -106,7 +110,7 @@ int xmp_smix_play_instrument(xmp_context opaque, int ins, int note, int vol, int return -XMP_ERROR_STATE; } - if (chn >= smix->chn || ins >= mod->ins) { + if (chn >= smix->chn || chn < 0 || ins >= mod->ins || ins < 0) { return -XMP_ERROR_INVALID; } @@ -116,7 +120,7 @@ int xmp_smix_play_instrument(xmp_context opaque, int ins, int note, int vol, int event = &p->inject_event[mod->chn + chn]; memset(event, 0, sizeof (struct xmp_event)); - event->note = note + 1; + event->note = (note < XMP_MAX_KEYS) ? note + 1 : note; event->ins = ins + 1; event->vol = vol + 1; event->_flag = 1; @@ -137,7 +141,7 @@ int xmp_smix_play_sample(xmp_context opaque, int ins, int note, int vol, int chn return -XMP_ERROR_STATE; } - if (chn >= smix->chn || ins >= smix->ins) { + if (chn >= smix->chn || chn < 0 || ins >= smix->ins || ins < 0) { return -XMP_ERROR_INVALID; } @@ -147,7 +151,7 @@ int xmp_smix_play_sample(xmp_context opaque, int ins, int note, int vol, int chn event = &p->inject_event[mod->chn + chn]; memset(event, 0, sizeof (struct xmp_event)); - event->note = note + 1; + event->note = (note < XMP_MAX_KEYS) ? note + 1 : note; event->ins = mod->ins + ins + 1; event->vol = vol + 1; event->_flag = 1; @@ -198,10 +202,10 @@ int xmp_smix_load_sample(xmp_context opaque, int num, const char *path) retval = -XMP_ERROR_SYSTEM; goto err; } - + /* Init instrument */ - xxi->sub = calloc(sizeof(struct xmp_subinstrument), 1); + xxi->sub = (struct xmp_subinstrument *) calloc(1, sizeof(struct xmp_subinstrument)); if (xxi->sub == NULL) { retval = -XMP_ERROR_SYSTEM; goto err1; @@ -264,7 +268,7 @@ int xmp_smix_load_sample(xmp_context opaque, int num, const char *path) xxs->lpe = 0; xxs->flg = bits == 16 ? XMP_SAMPLE_16BIT : 0; - xxs->data = malloc(size + 8); + xxs->data = (unsigned char *) malloc(size + 8); if (xxs->data == NULL) { retval = -XMP_ERROR_SYSTEM; goto err2; @@ -286,7 +290,7 @@ int xmp_smix_load_sample(xmp_context opaque, int num, const char *path) hio_close(h); return 0; - + err2: free(xxi->sub); xxi->sub = NULL; diff --git a/internal/c/parts/audio/extras/libxmp-lite/virtual.c b/internal/c/parts/audio/extras/libxmp-lite/virtual.c index f73f93e9c..ed9a69f8e 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/virtual.c +++ b/internal/c/parts/audio/extras/libxmp-lite/virtual.c @@ -1,5 +1,5 @@ /* Extended Module Player - * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -20,7 +20,6 @@ * THE SOFTWARE. */ -#include #include "common.h" #include "virtual.h" #include "mixer.h" @@ -102,8 +101,8 @@ int libxmp_virt_on(struct context_data *ctx, int num) p->virt.maxvoc = libxmp_mixer_numvoices(ctx, num); - p->virt.voice_array = calloc(p->virt.maxvoc, - sizeof(struct mixer_voice)); + p->virt.voice_array = (struct mixer_voice *) calloc(p->virt.maxvoc, + sizeof(struct mixer_voice)); if (p->virt.voice_array == NULL) goto err; @@ -116,7 +115,7 @@ int libxmp_virt_on(struct context_data *ctx, int num) /* Initialize Paula simulator */ if (IS_AMIGA_MOD()) { for (i = 0; i < p->virt.maxvoc; i++) { - p->virt.voice_array[i].paula = calloc(1, sizeof (struct paula_state)); + p->virt.voice_array[i].paula = (struct paula_state *) calloc(1, sizeof(struct paula_state)); if (p->virt.voice_array[i].paula == NULL) { goto err2; } @@ -125,8 +124,8 @@ int libxmp_virt_on(struct context_data *ctx, int num) } #endif - p->virt.virt_channel = malloc(p->virt.virt_channels * - sizeof(struct virt_channel)); + p->virt.virt_channel = (struct virt_channel *) malloc(p->virt.virt_channels * + sizeof(struct virt_channel)); if (p->virt.virt_channel == NULL) goto err2; @@ -190,7 +189,7 @@ void libxmp_virt_reset(struct context_data *ctx) } /* CID 129203 (#1 of 1): Useless call (USELESS_CALL) - * Call is only useful for its return value, which is ignored. + * Call is only useful for its return value, which is ignored. * * libxmp_mixer_numvoices(ctx, p->virt.maxvoc); */ @@ -353,6 +352,18 @@ void libxmp_virt_release(struct context_data *ctx, int chn, int rel) libxmp_mixer_release(ctx, voc, rel); } +void libxmp_virt_reverse(struct context_data *ctx, int chn, int rev) +{ + struct player_data *p = &ctx->p; + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return; + } + + libxmp_mixer_reverse(ctx, voc, rev); +} + void libxmp_virt_setpan(struct context_data *ctx, int chn, int pan) { struct player_data *p = &ctx->p; @@ -419,8 +430,13 @@ void libxmp_virt_setsmp(struct context_data *ctx, int chn, int smp) void libxmp_virt_setnna(struct context_data *ctx, int chn, int nna) { struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; int voc; + if (!HAS_QUIRK(QUIRK_VIRTUAL)) { + return; + } + if ((voc = map_virt_channel(p, chn)) < 0) { return; } @@ -429,7 +445,7 @@ void libxmp_virt_setnna(struct context_data *ctx, int chn, int nna) } static void check_dct(struct context_data *ctx, int i, int chn, int ins, - int smp, int note, int nna, int dct, int dca) + int smp, int key, int nna, int dct, int dca) { struct player_data *p = &ctx->p; struct mixer_voice *vi = &p->virt.voice_array[i]; @@ -440,15 +456,15 @@ static void check_dct(struct context_data *ctx, int i, int chn, int ins, if (vi->root == chn && vi->ins == ins) { if (nna == XMP_INST_NNA_CUT) { - libxmp_virt_resetvoice(ctx, i, 1); - return; + libxmp_virt_resetvoice(ctx, i, 1); + return; } vi->act = nna; if ((dct == XMP_INST_DCT_INST) || (dct == XMP_INST_DCT_SMP && vi->smp == smp) || - (dct == XMP_INST_DCT_NOTE && vi->note == note)) { + (dct == XMP_INST_DCT_NOTE && vi->key == key)) { if (nna == XMP_INST_NNA_OFF && dca == XMP_INST_DCA_FADE) { vi->act = VIRT_ACTION_OFF; @@ -479,7 +495,7 @@ void libxmp_virt_setnote(struct context_data *ctx, int chn, int note) } int libxmp_virt_setpatch(struct context_data *ctx, int chn, int ins, int smp, - int note, int nna, int dct, int dca) + int note, int key, int nna, int dct, int dca) { struct player_data *p = &ctx->p; int voc, vfree; @@ -497,7 +513,7 @@ int libxmp_virt_setpatch(struct context_data *ctx, int chn, int ins, int smp, int i; for (i = 0; i < p->virt.maxvoc; i++) { - check_dct(ctx, i, chn, ins, smp, note, nna, dct, dca); + check_dct(ctx, i, chn, ins, smp, key, nna, dct, dca); } } #endif @@ -512,7 +528,7 @@ int libxmp_virt_setpatch(struct context_data *ctx, int chn, int ins, int smp, return -1; } - for (chn = p->virt.num_tracks; + for (chn = p->virt.num_tracks; chn < p->virt.virt_channels && p->virt.virt_channel[chn++].map > FREE;) ; p->virt.voice_array[voc].chn = --chn; @@ -535,6 +551,7 @@ int libxmp_virt_setpatch(struct context_data *ctx, int chn, int ins, int smp, libxmp_mixer_setnote(ctx, voc, note); p->virt.voice_array[voc].ins = ins; p->virt.voice_array[voc].act = nna; + p->virt.voice_array[voc].key = key; return chn; } diff --git a/internal/c/parts/audio/extras/libxmp-lite/virtual.h b/internal/c/parts/audio/extras/libxmp-lite/virtual.h index a7668c562..88a8cdcfb 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/virtual.h +++ b/internal/c/parts/audio/extras/libxmp-lite/virtual.h @@ -15,7 +15,7 @@ int libxmp_virt_on (struct context_data *, int); void libxmp_virt_off (struct context_data *); int libxmp_virt_mute (struct context_data *, int, int); int libxmp_virt_setpatch (struct context_data *, int, int, int, int, - int, int, int); + int, int, int, int); int libxmp_virt_cvt8bit (void); void libxmp_virt_setnote (struct context_data *, int, int); void libxmp_virt_setsmp (struct context_data *, int, int); @@ -33,6 +33,7 @@ void libxmp_virt_resetchannel(struct context_data *, int); void libxmp_virt_resetvoice (struct context_data *, int, int); void libxmp_virt_reset (struct context_data *); void libxmp_virt_release (struct context_data *, int, int); +void libxmp_virt_reverse (struct context_data *, int, int); int libxmp_virt_getroot (struct context_data *, int); #endif /* LIBXMP_VIRTUAL_H */ diff --git a/internal/c/parts/audio/extras/libxmp-lite/win32.c b/internal/c/parts/audio/extras/libxmp-lite/win32.c index cd4398504..88ac97d19 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/win32.c +++ b/internal/c/parts/audio/extras/libxmp-lite/win32.c @@ -31,3 +31,17 @@ int libxmp_snprintf (char *str, size_t sz, const char *fmt, ...) } #endif + +/* Win32 debug message helper by Mirko Buffoni */ +#if defined(_MSC_VER) && defined(DEBUG) +void libxmp_msvc_dbgprint(const char *format, ...) +{ + va_list argptr; + + /* do the output */ + va_start(argptr, format); + vprintf(format, argptr); + printf("\n"); + va_end(argptr); +} +#endif diff --git a/internal/c/parts/audio/extras/libxmp-lite/xm.h b/internal/c/parts/audio/extras/libxmp-lite/xm.h index 443a369ce..4d41486cc 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/xm.h +++ b/internal/c/parts/audio/extras/libxmp-lite/xm.h @@ -14,12 +14,12 @@ #define XM_LOOP_FORWARD 1 #define XM_LOOP_PINGPONG 2 #define XM_SAMPLE_16BIT 0x10 +#define XM_SAMPLE_STEREO 0x20 #define XM_ENVELOPE_ON 0x01 #define XM_ENVELOPE_SUSTAIN 0x02 #define XM_ENVELOPE_LOOP 0x04 #define XM_LINEAR_PERIOD_MODE 0x01 - struct xm_file_header { uint8 id[17]; /* ID text: "Extended module: " */ uint8 name[20]; /* Module name, padded with zeroes */ @@ -98,4 +98,4 @@ struct xm_event { uint8 fx_parm; /* Effect parameter */ }; -#endif +#endif /* LIBXMP_LOADERS_XM_H */ diff --git a/internal/c/parts/audio/extras/libxmp-lite/xm_load.c b/internal/c/parts/audio/extras/libxmp-lite/xm_load.c index 296d9c99b..72189600b 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/xm_load.c +++ b/internal/c/parts/audio/extras/libxmp-lite/xm_load.c @@ -1,5 +1,5 @@ /* Extended Module Player - * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -35,6 +35,9 @@ #include "loader.h" #include "xm.h" +#ifndef LIBXMP_CORE_PLAYER +#include "vorbis.h" +#endif static int xm_test(HIO_HANDLE *, char *, const int); static int xm_load(struct module_data *, HIO_HANDLE *, const int); @@ -60,15 +63,16 @@ static int xm_test(HIO_HANDLE *f, char *t, const int start) return 0; } -static int load_xm_pattern(struct module_data *m, int num, int version, HIO_HANDLE *f) +static int load_xm_pattern(struct module_data *m, int num, int version, + uint8 *patbuf, HIO_HANDLE *f) { const int headsize = version > 0x0102 ? 9 : 8; struct xmp_module *mod = &m->mod; struct xm_pattern_header xph; struct xmp_event *event; - uint8 *patbuf, *pat, b; - int j, r; - int size; + uint8 *pat, b; + int j, k, r; + int size, size_read; xph.length = hio_read32l(f); xph.packing = hio_read8(f); @@ -99,199 +103,213 @@ static int load_xm_pattern(struct module_data *m, int num, int version, HIO_HAND } size = xph.datasize; + pat = patbuf; - pat = patbuf = calloc(1, size); - if (patbuf == NULL) { - goto err; + size_read = hio_read(patbuf, 1, size, f); + if (size_read < size) { + memset(patbuf + size_read, 0, size - size_read); } - hio_read(patbuf, 1, size, f); - for (j = 0; j < (mod->chn * r); j++) { + for (j = 0; j < r; j++) { + for (k = 0; k < mod->chn; k++) { + /* + if ((pat - patbuf) >= xph.datasize) + break; + */ - /*if ((pat - patbuf) >= xph.datasize) - break; */ + event = &EVENT(num, k, j); - event = &EVENT(num, j % mod->chn, j / mod->chn); - - if (--size < 0) { - goto err2; - } - - if ((b = *pat++) & XM_EVENT_PACKING) { - if (b & XM_EVENT_NOTE_FOLLOWS) { - if (--size < 0) - goto err2; - event->note = *pat++; + if (--size < 0) { + goto err; } - if (b & XM_EVENT_INSTRUMENT_FOLLOWS) { - if (--size < 0) - goto err2; + + if ((b = *pat++) & XM_EVENT_PACKING) { + if (b & XM_EVENT_NOTE_FOLLOWS) { + if (--size < 0) + goto err; + event->note = *pat++; + } + if (b & XM_EVENT_INSTRUMENT_FOLLOWS) { + if (--size < 0) + goto err; + event->ins = *pat++; + } + if (b & XM_EVENT_VOLUME_FOLLOWS) { + if (--size < 0) + goto err; + event->vol = *pat++; + } + if (b & XM_EVENT_FXTYPE_FOLLOWS) { + if (--size < 0) + goto err; + event->fxt = *pat++; + } + if (b & XM_EVENT_FXPARM_FOLLOWS) { + if (--size < 0) + goto err; + event->fxp = *pat++; + } + } else { + size -= 4; + if (size < 0) + goto err; + event->note = b; event->ins = *pat++; - } - if (b & XM_EVENT_VOLUME_FOLLOWS) { - if (--size < 0) - goto err2; event->vol = *pat++; - } - if (b & XM_EVENT_FXTYPE_FOLLOWS) { - if (--size < 0) - goto err2; event->fxt = *pat++; - } - if (b & XM_EVENT_FXPARM_FOLLOWS) { - if (--size < 0) - goto err2; event->fxp = *pat++; } - } else { - size -= 4; - if (size < 0) - goto err2; - event->note = b; - event->ins = *pat++; - event->vol = *pat++; - event->fxt = *pat++; - event->fxp = *pat++; - } - /* Sanity check */ - switch (event->fxt) { - case 18: - case 19: - case 22: - case 23: - case 24: - case 26: - case 28: - case 30: - case 31: - case 32: - event->fxt = 0; - } - if (event->fxt > 34) { - event->fxt = 0; - } - - if (event->note == 0x61) { - /* See OpenMPT keyoff+instr.xm test case */ - if (event->fxt == 0x0e && MSN(event->fxp) == 0x0d) { - event->note = XMP_KEY_OFF; - } else { - event->note = - event->ins ? XMP_KEY_FADE : XMP_KEY_OFF; + /* Sanity check */ + switch (event->fxt) { + case 18: + case 19: + case 22: + case 23: + case 24: + case 26: + case 28: + case 30: + case 31: + case 32: + event->fxt = 0; } - } else if (event->note > 0) { - event->note += 12; - } - - if (event->fxt == 0x0e) { - if (MSN(event->fxp) == EX_FINETUNE) { - unsigned char val = (LSN(event->fxp) - 8) & 0xf; - event->fxp = (EX_FINETUNE << 4) | val; + if (event->fxt > 34) { + event->fxt = 0; } - switch (event->fxp) { - case 0x43: - case 0x73: - event->fxp--; + + if (event->note == 0x61) { + /* See OpenMPT keyoff+instr.xm test case */ + if (event->fxt == 0x0e && MSN(event->fxp) == 0x0d) { + event->note = XMP_KEY_OFF; + } else { + event->note = + event->ins ? XMP_KEY_FADE : XMP_KEY_OFF; + } + } else if (event->note > 0) { + event->note += 12; + } + + if (event->fxt == 0x0e) { + if (MSN(event->fxp) == EX_FINETUNE) { + unsigned char val = (LSN(event->fxp) - 8) & 0xf; + event->fxp = (EX_FINETUNE << 4) | val; + } + switch (event->fxp) { + case 0x43: + case 0x73: + event->fxp--; + break; + } + } + if (event->fxt == FX_XF_PORTA && MSN(event->fxp) == 0x09) { + /* Translate MPT hacks */ + switch (LSN(event->fxp)) { + case 0x0: /* Surround off */ + case 0x1: /* Surround on */ + event->fxt = FX_SURROUND; + event->fxp = LSN(event->fxp); + break; + case 0xe: /* Play forward */ + case 0xf: /* Play reverse */ + event->fxt = FX_REVERSE; + event->fxp = LSN(event->fxp) - 0xe; + } + } + + if (!event->vol) { + continue; + } + + /* Volume set */ + if ((event->vol >= 0x10) && (event->vol <= 0x50)) { + event->vol -= 0x0f; + continue; + } + + /* Volume column effects */ + switch (event->vol >> 4) { + case 0x06: /* Volume slide down */ + event->f2t = FX_VOLSLIDE_2; + event->f2p = event->vol - 0x60; + break; + case 0x07: /* Volume slide up */ + event->f2t = FX_VOLSLIDE_2; + event->f2p = (event->vol - 0x70) << 4; + break; + case 0x08: /* Fine volume slide down */ + event->f2t = FX_EXTENDED; + event->f2p = + (EX_F_VSLIDE_DN << 4) | (event->vol - 0x80); + break; + case 0x09: /* Fine volume slide up */ + event->f2t = FX_EXTENDED; + event->f2p = + (EX_F_VSLIDE_UP << 4) | (event->vol - 0x90); + break; + case 0x0a: /* Set vibrato speed */ + event->f2t = FX_VIBRATO; + event->f2p = (event->vol - 0xa0) << 4; + break; + case 0x0b: /* Vibrato */ + event->f2t = FX_VIBRATO; + event->f2p = event->vol - 0xb0; + break; + case 0x0c: /* Set panning */ + event->f2t = FX_SETPAN; + event->f2p = (event->vol - 0xc0) << 4; + break; + case 0x0d: /* Pan slide left */ + event->f2t = FX_PANSL_NOMEM; + event->f2p = (event->vol - 0xd0) << 4; + break; + case 0x0e: /* Pan slide right */ + event->f2t = FX_PANSL_NOMEM; + event->f2p = event->vol - 0xe0; + break; + case 0x0f: /* Tone portamento */ + event->f2t = FX_TONEPORTA; + event->f2p = (event->vol - 0xf0) << 4; + + /* From OpenMPT TonePortamentoMemory.xm: + * "Another nice bug (...) is the combination of both + * portamento commands (Mx and 3xx) in the same cell: + * The 3xx parameter is ignored completely, and the Mx + * parameter is doubled. (M2 3FF is the same as M4 000) + */ + if (event->fxt == FX_TONEPORTA + || event->fxt == FX_TONE_VSLIDE) { + if (event->fxt == FX_TONEPORTA) { + event->fxt = 0; + } else { + event->fxt = FX_VOLSLIDE; + } + event->fxp = 0; + + if (event->f2p < 0x80) { + event->f2p <<= 1; + } else { + event->f2p = 0xff; + } + } + + /* From OpenMPT porta-offset.xm: + * "If there is a portamento command next to an offset + * command, the offset command is ignored completely. In + * particular, the offset parameter is not memorized." + */ + if (event->fxt == FX_OFFSET + && event->f2t == FX_TONEPORTA) { + event->fxt = event->fxp = 0; + } break; } + event->vol = 0; } - - if (!event->vol) { - continue; - } - - /* Volume set */ - if ((event->vol >= 0x10) && (event->vol <= 0x50)) { - event->vol -= 0x0f; - continue; - } - - /* Volume column effects */ - switch (event->vol >> 4) { - case 0x06: /* Volume slide down */ - event->f2t = FX_VOLSLIDE_2; - event->f2p = event->vol - 0x60; - break; - case 0x07: /* Volume slide up */ - event->f2t = FX_VOLSLIDE_2; - event->f2p = (event->vol - 0x70) << 4; - break; - case 0x08: /* Fine volume slide down */ - event->f2t = FX_EXTENDED; - event->f2p = - (EX_F_VSLIDE_DN << 4) | (event->vol - 0x80); - break; - case 0x09: /* Fine volume slide up */ - event->f2t = FX_EXTENDED; - event->f2p = - (EX_F_VSLIDE_UP << 4) | (event->vol - 0x90); - break; - case 0x0a: /* Set vibrato speed */ - event->f2t = FX_VIBRATO; - event->f2p = (event->vol - 0xa0) << 4; - break; - case 0x0b: /* Vibrato */ - event->f2t = FX_VIBRATO; - event->f2p = event->vol - 0xb0; - break; - case 0x0c: /* Set panning */ - event->f2t = FX_SETPAN; - event->f2p = (event->vol - 0xc0) << 4; - break; - case 0x0d: /* Pan slide left */ - event->f2t = FX_PANSL_NOMEM; - event->f2p = (event->vol - 0xd0) << 4; - break; - case 0x0e: /* Pan slide right */ - event->f2t = FX_PANSL_NOMEM; - event->f2p = event->vol - 0xe0; - break; - case 0x0f: /* Tone portamento */ - event->f2t = FX_TONEPORTA; - event->f2p = (event->vol - 0xf0) << 4; - - /* From OpenMPT TonePortamentoMemory.xm: - * "Another nice bug (...) is the combination of both - * portamento commands (Mx and 3xx) in the same cell: - * The 3xx parameter is ignored completely, and the Mx - * parameter is doubled. (M2 3FF is the same as M4 000) - */ - if (event->fxt == FX_TONEPORTA - || event->fxt == FX_TONE_VSLIDE) { - if (event->fxt == FX_TONEPORTA) { - event->fxt = 0; - } else { - event->fxt = FX_VOLSLIDE; - } - event->fxp = 0; - - if (event->f2p < 0x80) { - event->f2p <<= 1; - } else { - event->f2p = 0xff; - } - } - - /* From OpenMPT porta-offset.xm: - * "If there is a portamento command next to an offset - * command, the offset command is ignored completely. In - * particular, the offset parameter is not memorized." - */ - if (event->fxt == FX_OFFSET - && event->f2t == FX_TONEPORTA) { - event->fxt = event->fxp = 0; - } - break; - } - event->vol = 0; } - free(patbuf); return 0; -err2: - free(patbuf); err: return -1; } @@ -299,6 +317,7 @@ err: static int load_patterns(struct module_data *m, int version, HIO_HANDLE *f) { struct xmp_module *mod = &m->mod; + uint8 *patbuf; int i, j; mod->pat++; @@ -308,8 +327,12 @@ static int load_patterns(struct module_data *m, int version, HIO_HANDLE *f) D_(D_INFO "Stored patterns: %d", mod->pat - 1); + if ((patbuf = (uint8 *) calloc(1, 65536)) == NULL) { + return -1; + } + for (i = 0; i < mod->pat - 1; i++) { - if (load_xm_pattern(m, i, version, f) < 0) { + if (load_xm_pattern(m, i, version, patbuf, f) < 0) { goto err; } } @@ -333,21 +356,100 @@ static int load_patterns(struct module_data *m, int version, HIO_HANDLE *f) } } + free(patbuf); return 0; err: + free(patbuf); return -1; } /* Packed structures size */ -#define XM_INST_HEADER_SIZE 33 -#define XM_INST_SIZE 208 +#define XM_INST_HEADER_SIZE 29 +#define XM_INST_SIZE 212 /* grass.near.the.house.xm defines 23 samples in instrument 1. FT2 docs * specify at most 16. See https://github.com/libxmp/libxmp/issues/168 * for more details. */ #define XM_MAX_SAMPLES_PER_INST 32 +#ifndef LIBXMP_CORE_PLAYER +#define MAGIC_OGGS 0x4f676753 + +static int is_ogg_sample(HIO_HANDLE *f, struct xmp_sample *xxs) +{ + /* uint32 size; */ + uint32 id; + + /* Sample must be at least 4 bytes long to be an OGG sample. + * Bonnie's Bookstore music.oxm contains zero length samples + * followed immediately by OGG samples. */ + if (xxs->len < 4) + return 0; + + /* size = */ hio_read32l(f); + id = hio_read32b(f); + if (hio_error(f) != 0 || hio_seek(f, -8, SEEK_CUR) < 0) + return 0; + + if (id != MAGIC_OGGS) { /* copy input data if not Ogg file */ + return 0; + } + + return 1; +} + +static int oggdec(struct module_data *m, HIO_HANDLE *f, struct xmp_sample *xxs, int len) +{ + int i, n, ch, rate, ret, flags = 0; + uint8 *data; + int16 *pcm16 = NULL; + + if ((data = (uint8 *)calloc(1, len)) == NULL) + return -1; + + hio_read32b(f); + if (hio_error(f) != 0 || hio_read(data, 1, len - 4, f) != len - 4) { + free(data); + return -1; + } + + n = stb_vorbis_decode_memory(data, len, &ch, &rate, &pcm16); + free(data); + + if (n <= 0) { + free(pcm16); + return -1; + } + + xxs->len = n; + + if ((xxs->flg & XMP_SAMPLE_16BIT) == 0) { + uint8 *pcm = (uint8 *)pcm16; + + for (i = 0; i < n; i++) { + pcm[i] = pcm16[i] >> 8; + } + pcm = (uint8 *)realloc(pcm16, n); + if (pcm == NULL) { + free(pcm16); + return -1; + } + pcm16 = (int16 *)pcm; + } + + flags |= SAMPLE_FLAG_NOLOAD; +#ifdef WORDS_BIGENDIAN + flags |= SAMPLE_FLAG_BIGEND; +#endif + + ret = libxmp_load_sample(m, NULL, flags, xxs, pcm16); + free(pcm16); + + return ret; +} +#endif + static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f) { struct xmp_module *mod = &m->mod; @@ -376,8 +478,12 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f) * instruments, but file may end abruptly before that. Also covers * XMLiTE stripped modules and truncated files. This test will not * work if file has trailing garbage. + * + * Note: loading 4 bytes past the instrument header to get the + * sample header size (if it exists). This is NOT considered to + * be part of the instrument header. */ - if (hio_read(buf, 33, 1, f) != 1) { + if (hio_read(buf, XM_INST_HEADER_SIZE + 4, 1, f) != 1) { D_(D_WARN "short read in instrument header data"); break; } @@ -389,6 +495,11 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f) xih.sh_size = readmem32l(buf + 29); /* Sample header size */ /* Sanity check */ + if ((int)xih.size < XM_INST_HEADER_SIZE) { + D_(D_CRIT "instrument %d: instrument header size:%d", i + 1, xih.size); + return -1; + } + if (xih.samples > XM_MAX_SAMPLES_PER_INST || (xih.samples > 0 && xih.sh_size > 0x100)) { D_(D_CRIT "instrument %d: samples:%d sample header size:%d", i + 1, xih.samples, xih.sh_size); return -1; @@ -416,7 +527,7 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f) * generalization should take care of both cases. */ - if (hio_seek(f, (int)xih.size - XM_INST_HEADER_SIZE, SEEK_CUR) < 0) { + if (hio_seek(f, (int)xih.size - (XM_INST_HEADER_SIZE + 4), SEEK_CUR) < 0) { return -1; } @@ -427,17 +538,13 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f) return -1; } - if (xih.size < XM_INST_HEADER_SIZE) { - return -1; - } - /* for BoobieSqueezer (see http://boobie.rotfl.at/) * It works pretty much the same way as Impulse Tracker's sample * only mode, where it will strip off the instrument data. */ if (xih.size < XM_INST_HEADER_SIZE + XM_INST_SIZE) { memset(&xi, 0, sizeof(struct xm_instrument)); - hio_seek(f, xih.size - XM_INST_HEADER_SIZE, SEEK_CUR); + hio_seek(f, xih.size - (XM_INST_HEADER_SIZE + 4), SEEK_CUR); } else { uint8 *b = buf; @@ -508,7 +615,7 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f) for (j = 12; j < 108; j++) { xxi->map[j].ins = xi.sample[j - 12]; if (xxi->map[j].ins >= xxi->nsm) - xxi->map[j].ins = -1; + xxi->map[j].ins = 0xff; } } @@ -576,6 +683,12 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f) xxs->lps >>= 1; xxs->lpe >>= 1; } + if (xsh[j].type & XM_SAMPLE_STEREO) { + /* xxs->flg |= XMP_SAMPLE_STEREO; */ + xxs->len >>= 1; + xxs->lps >>= 1; + xxs->lpe >>= 1; + } xxs->flg |= xsh[j].type & XM_LOOP_FORWARD ? XMP_SAMPLE_LOOP : 0; xxs->flg |= xsh[j].type & XM_LOOP_PINGPONG ? XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_BIDIR : 0; @@ -595,6 +708,7 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f) total_sample_size = 0; for (j = 0; j < xxi->nsm; j++) { struct xmp_subinstrument *sub = &xxi->sub[j]; + struct xmp_sample *xxs = &mod->xxs[sub->sid]; int flags; flags = SAMPLE_FLAG_DIFF; @@ -606,7 +720,20 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f) if (version > 0x0103) { D_(D_INFO " read sample: index:%d sample id:%d", j, sub->sid); - if (libxmp_load_sample(m, f, flags, &mod->xxs[sub->sid], NULL) < 0) { + +#ifndef LIBXMP_CORE_PLAYER + if (is_ogg_sample(f, xxs)) { + if (oggdec(m, f, xxs, xsh[j].length) < 0) { + return -1; + } + + D_(D_INFO " sample is vorbis"); + total_sample_size += xsh[j].length; + continue; + } +#endif + + if (libxmp_load_sample(m, f, flags, xxs, NULL) < 0) { return -1; } if (flags & SAMPLE_FLAG_ADPCM) { @@ -615,6 +742,12 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f) } else { total_sample_size += xsh[j].length; } + + /* TODO: implement stereo samples. + * For now, just skip the right channel. */ + if (xsh[j].type & XM_SAMPLE_STEREO) { + hio_seek(f, xsh[j].length >> 1, SEEK_CUR); + } } } @@ -640,6 +773,10 @@ static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start) int i, j; struct xm_file_header xfh; char tracker_name[21]; +#ifndef LIBXMP_CORE_PLAYER + int claims_ft2 = 0; + int is_mpt_116 = 0; +#endif int len; uint8 buf[80]; @@ -666,19 +803,32 @@ static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start) xfh.bpm = readmem16l(buf + 78); /* Default BPM */ /* Sanity checks */ - if (xfh.songlen > 256 || xfh.patterns > 256 || xfh.instruments > 255) { - D_(D_CRIT "Sanity check: %d %d %d", xfh.songlen, xfh.patterns, xfh.instruments); + if (xfh.songlen > 256) { + D_(D_CRIT "bad song length: %d", xfh.songlen); + return -1; + } + if (xfh.patterns > 256) { + D_(D_CRIT "bad pattern count: %d", xfh.patterns); + return -1; + } + if (xfh.instruments > 255) { + D_(D_CRIT "bad instrument count: %d", xfh.instruments); return -1; } - if (xfh.restart > 255 || xfh.channels > XMP_MAX_CHANNELS) { - D_(D_CRIT "Sanity check: %d %d", xfh.restart, xfh.channels); + if (xfh.restart > 255) { + D_(D_CRIT "bad restart position: %d", xfh.restart); + return -1; + } + if (xfh.channels > XMP_MAX_CHANNELS) { + D_(D_CRIT "bad channel count: %d", xfh.channels); return -1; } - if (xfh.tempo >= 32 || xfh.bpm < 32 || xfh.bpm > 255) { + /* FT2 and MPT allow up to 255 BPM. OpenMPT allows up to 1000 BPM. */ + if (xfh.tempo >= 32 || xfh.bpm < 32 || xfh.bpm > 1000) { if (memcmp("MED2XM", xfh.tracker, 6)) { - D_(D_CRIT "Sanity check: %d %d", xfh.tempo, xfh.bpm); + D_(D_CRIT "bad tempo or BPM: %d %d", xfh.tempo, xfh.bpm); return -1; } } @@ -686,10 +836,11 @@ static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start) /* Honor header size -- needed by BoobieSqueezer XMs */ len = xfh.headersz - 0x14; if (len < 0 || len > 256) { - D_(D_CRIT "Sanity check: %d", len); + D_(D_CRIT "bad XM header length: %d", len); return -1; } + memset(xfh.order, 0, sizeof(xfh.order)); if (hio_read(xfh.order, len, 1, f) != 1) { /* Pattern order table */ D_(D_CRIT "error reading orders"); return -1; @@ -720,8 +871,12 @@ static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start) } /* OpenMPT accurately emulates weird FT2 bugs */ - if (!strncmp(tracker_name, "FastTracker v2.00", 17) || - !strncmp(tracker_name, "OpenMPT ", 8)) { + if (!strncmp(tracker_name, "FastTracker v2.00", 17)) { + m->quirk |= QUIRK_FT2BUGS; +#ifndef LIBXMP_CORE_PLAYER + claims_ft2 = 1; +#endif + } else if (!strncmp(tracker_name, "OpenMPT ", 8)) { m->quirk |= QUIRK_FT2BUGS; } #ifndef LIBXMP_CORE_PLAYER @@ -744,6 +899,7 @@ static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start) if (!strncmp(tracker_name, "FastTracker v 2.00", 18)) { strcpy(tracker_name, "old ModPlug Tracker"); m->quirk &= ~QUIRK_FT2BUGS; + is_mpt_116 = 1; } libxmp_set_type(m, "%s XM %d.%02d", tracker_name, xfh.version >> 8, xfh.version & 0xff); @@ -789,6 +945,73 @@ static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start) } } +#ifndef LIBXMP_CORE_PLAYER + /* Load MPT properties from the end of the file. */ + while (1) { + uint32 ext = hio_read32b(f); + uint32 sz = hio_read32l(f); + int known = 0; + + if (hio_error(f) || sz > 0x7fffffff /* INT32_MAX */) + break; + + switch (ext) { + case MAGIC4('t','e','x','t'): /* Song comment */ + known = 1; + if (m->comment != NULL) + break; + + if ((m->comment = (char *)malloc(sz + 1)) == NULL) + break; + + sz = hio_read(m->comment, 1, sz, f); + m->comment[sz] = '\0'; + + for (i = 0; i < (int)sz; i++) { + int b = m->comment[i]; + if (b == '\r') { + m->comment[i] = '\n'; + } else if ((b < 32 || b > 127) && b != '\n' + && b != '\t') { + m->comment[i] = '.'; + } + } + break; + + case MAGIC4('M','I','D','I'): /* MIDI config */ + case MAGIC4('P','N','A','M'): /* Pattern names */ + case MAGIC4('C','N','A','M'): /* Channel names */ + case MAGIC4('C','H','F','X'): /* Channel plugins */ + case MAGIC4('X','T','P','M'): /* Inst. extensions */ + known = 1; + /* fall-through */ + + default: + /* Plugin definition */ + if ((ext & MAGIC4('F','X', 0, 0)) == MAGIC4('F','X', 0, 0)) + known = 1; + + if (sz) hio_seek(f, sz, SEEK_CUR); + break; + } + + if(known && claims_ft2) + is_mpt_116 = 1; + + if (ext == MAGIC4('X','T','P','M')) + break; + } + + if (is_mpt_116) { + libxmp_set_type(m, "ModPlug Tracker 1.16 XM %d.%02d", + xfh.version >> 8, xfh.version & 0xff); + + m->mvolbase = 48; + m->mvol = 48; + libxmp_apply_mpt_preamp(m); + } +#endif + for (i = 0; i < mod->chn; i++) { mod->xxc[i].pan = 0x80; } diff --git a/internal/c/parts/audio/extras/libxmp-lite/xmp.h b/internal/c/parts/audio/extras/libxmp-lite/xmp.h index 2d725411b..22db2a3da 100644 --- a/internal/c/parts/audio/extras/libxmp-lite/xmp.h +++ b/internal/c/parts/audio/extras/libxmp-lite/xmp.h @@ -1,39 +1,56 @@ #ifndef XMP_H #define XMP_H +#if defined(EMSCRIPTEN) +# include +#endif + #ifdef __cplusplus extern "C" { #endif -#define XMP_VERSION "4.5.0" -#define XMP_VERCODE 0x040500 +#define XMP_VERSION "4.6.0" +#define XMP_VERCODE 0x040600 #define XMP_VER_MAJOR 4 -#define XMP_VER_MINOR 5 +#define XMP_VER_MINOR 6 #define XMP_VER_RELEASE 0 #if defined(_WIN32) && !defined(__CYGWIN__) -# if defined(BUILDING_STATIC) +# if defined(LIBXMP_STATIC) # define LIBXMP_EXPORT # elif defined(BUILDING_DLL) # define LIBXMP_EXPORT __declspec(dllexport) # else # define LIBXMP_EXPORT __declspec(dllimport) # endif -#elif defined(__OS2__) && defined(__WATCOMC__) && defined(__SW_BD) +#elif defined(__OS2__) && defined(__WATCOMC__) +# if defined(LIBXMP_STATIC) +# define LIBXMP_EXPORT +# elif defined(BUILDING_DLL) # define LIBXMP_EXPORT __declspec(dllexport) +# else +# define LIBXMP_EXPORT +# endif #elif (defined(__GNUC__) || defined(__clang__) || defined(__HP_cc)) && defined(XMP_SYM_VISIBILITY) -# define LIBXMP_EXPORT __attribute__((visibility ("default"))) +# if defined(LIBXMP_STATIC) +# define LIBXMP_EXPORT +# else +# define LIBXMP_EXPORT __attribute__((visibility("default"))) +# endif #elif defined(__SUNPRO_C) && defined(XMP_LDSCOPE_GLOBAL) -# define LIBXMP_EXPORT __global +# if defined(LIBXMP_STATIC) +# define LIBXMP_EXPORT +# else +# define LIBXMP_EXPORT __global +# endif #elif defined(EMSCRIPTEN) -# include # define LIBXMP_EXPORT EMSCRIPTEN_KEEPALIVE # define LIBXMP_EXPORT_VAR #else # define LIBXMP_EXPORT #endif -#if !defined (LIBXMP_EXPORT_VAR) +#if !defined(LIBXMP_EXPORT_VAR) # define LIBXMP_EXPORT_VAR LIBXMP_EXPORT #endif @@ -238,6 +255,7 @@ struct xmp_sample { #define XMP_SAMPLE_LOOP_FULL (1 << 4) /* Play full sample before looping */ #define XMP_SAMPLE_SLOOP (1 << 5) /* Sample has sustain loop */ #define XMP_SAMPLE_SLOOP_BIDIR (1 << 6) /* Bidirectional sustain loop */ +#define XMP_SAMPLE_STEREO (1 << 7) /* Interlaced stereo sample */ #define XMP_SAMPLE_SYNTH (1 << 15) /* Data contains synth patch */ int flg; /* Flags */ unsigned char *data; /* Sample data */ diff --git a/internal/c/parts/audio/extras/mod_ma_vtable.cpp b/internal/c/parts/audio/extras/mod_ma_vtable.cpp index 71d125171..986df563e 100644 --- a/internal/c/parts/audio/extras/mod_ma_vtable.cpp +++ b/internal/c/parts/audio/extras/mod_ma_vtable.cpp @@ -19,7 +19,7 @@ #include #include -#define BUILDING_STATIC 1 +#define LIBXMP_STATIC 1 #include "libxmp-lite/xmp.h" #include "vtables.h"