1
1
Fork 0
mirror of https://github.com/QB64-Phoenix-Edition/QB64pe.git synced 2024-07-12 13:25:14 +00:00

Update to Libxmp-lite v4.6.0

This commit is contained in:
Samuel Gomes 2023-06-17 14:07:50 +05:30
parent 02cb3fbf87
commit 7c96db91be
47 changed files with 3862 additions and 1351 deletions

View file

@ -4,6 +4,7 @@ LIBXMP_SRCS := \
control.c \ control.c \
dataio.c \ dataio.c \
effects.c \ effects.c \
filetype.c \
filter.c \ filter.c \
format.c \ format.c \
hio.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 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 $(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) $(LIBXMP_LIB): $(LIBXMP_OBJS)
$(AR) rcs $@ $^ $(AR) rcs $@ $^

View file

@ -10,9 +10,7 @@ typedef struct {
int eof; int eof;
} CBFILE; } CBFILE;
#ifdef __cplusplus LIBXMP_BEGIN_DECLS
extern "C" {
#endif
static inline uint8 cbread8(CBFILE *f, int *err) 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) static inline uint16 cbread16l(CBFILE *f, int *err)
{ {
uint8 buf[2]; uint8 buf[2];
uint16 x = EOF; uint16 x = 0xffff;
size_t r = f->callbacks.read_func(buf, 2, 1, f->priv); size_t r = f->callbacks.read_func(buf, 2, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF; 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) static inline uint16 cbread16b(CBFILE *f, int *err)
{ {
uint8 buf[2]; uint8 buf[2];
uint16 x = EOF; uint16 x = 0xffff;
size_t r = f->callbacks.read_func(buf, 2, 1, f->priv); size_t r = f->callbacks.read_func(buf, 2, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF; 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) static inline uint32 cbread24l(CBFILE *f, int *err)
{ {
uint8 buf[3]; uint8 buf[3];
uint32 x = EOF; uint32 x = 0xffffffff;
size_t r = f->callbacks.read_func(buf, 3, 1, f->priv); size_t r = f->callbacks.read_func(buf, 3, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF; 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) static inline uint32 cbread24b(CBFILE *f, int *err)
{ {
uint8 buf[3]; uint8 buf[3];
uint32 x = EOF; uint32 x = 0xffffffff;
size_t r = f->callbacks.read_func(buf, 3, 1, f->priv); size_t r = f->callbacks.read_func(buf, 3, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF; 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) static inline uint32 cbread32l(CBFILE *f, int *err)
{ {
uint8 buf[4]; uint8 buf[4];
uint32 x = EOF; uint32 x = 0xffffffff;
size_t r = f->callbacks.read_func(buf, 4, 1, f->priv); size_t r = f->callbacks.read_func(buf, 4, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF; 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) static inline uint32 cbread32b(CBFILE *f, int *err)
{ {
uint8 buf[4]; uint8 buf[4];
uint32 x = EOF; uint32 x = 0xffffffff;
size_t r = f->callbacks.read_func(buf, 4, 1, f->priv); size_t r = f->callbacks.read_func(buf, 4, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF; 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) 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; f->eof = (r < nmemb) ? EOF : 0;
return r; return r;
} }
@ -183,8 +182,6 @@ static inline int cbclose(CBFILE *f)
return r; return r;
} }
#ifdef __cplusplus LIBXMP_END_DECLS
}
#endif
#endif /* LIBXMP_CALLBACKIO_H */ #endif /* LIBXMP_CALLBACKIO_H */

View file

@ -1,5 +1,5 @@
/* Extended Module Player /* 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 * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
@ -21,28 +21,14 @@
*/ */
#include <ctype.h> #include <ctype.h>
#if defined(HAVE_DIRENT)
#ifndef LIBXMP_CORE_PLAYER #include <sys/types.h>
#if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <limits.h>
#elif defined(__OS2__) || defined(__EMX__)
#define INCL_DOS
#define INCL_DOSERRORS
#include <os2.h>
#elif defined(__DJGPP__)
#include <dos.h>
#include <io.h>
#elif defined(HAVE_DIRENT_H)
#include <dirent.h> #include <dirent.h>
#endif #endif
#endif /* LIBXMP_CORE_PLAYER */
#include "common.h"
#include "xmp.h" #include "xmp.h"
#include "common.h"
#include "period.h" #include "period.h"
#include "loader.h" #include "loader.h"
@ -51,7 +37,7 @@ int libxmp_init_instrument(struct module_data *m)
struct xmp_module *mod = &m->mod; struct xmp_module *mod = &m->mod;
if (mod->ins > 0) { 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) if (mod->xxi == NULL)
return -1; return -1;
} }
@ -65,10 +51,10 @@ int libxmp_init_instrument(struct module_data *m)
return -1; 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) if (mod->xxs == NULL)
return -1; 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) if (m->xtra == NULL)
return -1; return -1;
@ -103,12 +89,12 @@ int libxmp_realloc_samples(struct module_data *m, int new_size)
return 0; 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) if (xxs == NULL)
return -1; return -1;
mod->xxs = xxs; 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) if (xtra == NULL)
return -1; return -1;
m->xtra = xtra; m->xtra = xtra;
@ -133,7 +119,7 @@ int libxmp_alloc_subinstrument(struct xmp_module *mod, int i, int num)
if (num == 0) if (num == 0)
return 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) if (mod->xxi[i].sub == NULL)
return -1; 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) 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) if (mod->xxt == NULL)
return -1; 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) if (mod->xxp == NULL)
return -1; 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) if (num < 0 || num >= mod->pat || mod->xxp[num] != NULL)
return -1; return -1;
mod->xxp[num] = calloc(1, sizeof (struct xmp_pattern) + mod->xxp[num] = (struct xmp_pattern *) calloc(1, sizeof(struct xmp_pattern) +
sizeof (int) * (mod->chn - 1)); sizeof(int) * (mod->chn - 1));
if (mod->xxp[num] == NULL) if (mod->xxp[num] == NULL)
return -1; 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) if (num < 0 || num >= mod->trk || mod->xxt[num] != NULL || rows <= 0)
return -1; return -1;
mod->xxt[num] = calloc(sizeof (struct xmp_track) + mod->xxt[num] = (struct xmp_track *) calloc(1, sizeof(struct xmp_track) +
sizeof (struct xmp_event) * (rows - 1), 1); sizeof(struct xmp_event) * (rows - 1));
if (mod->xxt[num] == NULL) if (mod->xxt[num] == NULL)
return -1; return -1;
@ -218,6 +204,7 @@ int libxmp_alloc_pattern_tracks(struct xmp_module *mod, int num, int rows)
return 0; return 0;
} }
#ifndef LIBXMP_CORE_PLAYER
/* Some formats explicitly allow more than 256 rows (e.g. OctaMED). This function /* 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. * 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; return 0;
} }
#endif
char *libxmp_instrument_name(struct xmp_module *mod, int i, uint8 *r, int n) 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); strncpy(s, (char *)r, n);
for (i = 0; s[i] && i < n; i++) { 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] = '.'; s[i] = '.';
} }
@ -267,7 +255,7 @@ void libxmp_read_title(HIO_HANDLE *f, char *t, int s)
{ {
uint8 buf[XMP_NAME_SIZE]; uint8 buf[XMP_NAME_SIZE];
if (t == NULL) if (t == NULL || s < 0)
return; return;
if (s >= XMP_NAME_SIZE) 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); memset(t, 0, s + 1);
hio_read(buf, 1, s, f); /* coverity[check_return] */ s = hio_read(buf, 1, s, f);
buf[s] = 0; buf[s] = 0;
libxmp_copy_adjust(t, buf, s); libxmp_copy_adjust(t, buf, s);
} }
#ifndef LIBXMP_CORE_PLAYER #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; int i;
for (i = 0; i < n; 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) if (s[i] > 0x7f)
return -1; return -1;
/* ACS_Team2.mod has a backspace in instrument name */ /* 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; 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] == ':') name[0] == '\\' || name[0] == '/' || name[0] == ':')
return -1; return -1;
for (i = 0; i < n - 1; i++) { for (i = 0; i < n - 1; i++) {
uint8 t = name[i]; uint8 t = name[i];
if (!t) 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". * module players erroneously interpret as "newer-version-trackers commands".
* Which they aren't. * 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; int fxt;
@ -388,7 +380,7 @@ void libxmp_decode_noisetracker_event(struct xmp_event *event, uint8 *mod_event)
} }
#endif #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]); int fxt = LSN(mod_event[2]);
@ -430,74 +422,45 @@ void libxmp_disable_continue_fx(struct xmp_event *event)
/* libxmp_check_filename_case(): */ /* libxmp_check_filename_case(): */
/* Given a directory, see if file exists there, ignoring 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) int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
{ {
char path[_MAX_PATH]; char path[XMP_MAXPATH];
DWORD rc;
/* win32 is case-insensitive: directly probe the file. */
snprintf(path, sizeof(path), "%s/%s", dir, name); snprintf(path, sizeof(path), "%s/%s", dir, name);
rc = GetFileAttributesA(path); if (! (libxmp_get_filetype(path) & XMP_FILETYPE_FILE))
if (rc == (DWORD)(-1)) return 0; return 0;
if (rc & FILE_ATTRIBUTE_DIRECTORY) return 0;
strncpy(new_name, name, size); strncpy(new_name, name, size);
return 1; return 1;
} }
#elif defined(__OS2__) || defined(__EMX__) #else /* target has dirent */
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)
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size) int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
{ {
int found = 0; int found = 0;
DIR *dirfd; DIR *dirp;
struct dirent *d; struct dirent *d;
dirfd = opendir(dir); dirp = opendir(dir);
if (dirfd == NULL) if (dirp == NULL)
return 0; return 0;
while ((d = readdir(dirfd)) != NULL) { while ((d = readdir(dirp)) != NULL) {
if (!strcasecmp(d->d_name, name)) { if (!strcasecmp(d->d_name, name)) {
found = 1; found = 1;
strncpy(new_name, d->d_name, size);
break; break;
} }
} }
if (found) closedir(dirp);
strncpy(new_name, d->d_name, size);
closedir(dirfd);
return found; return found;
} }
#else
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
{
return 0;
}
#endif #endif
void libxmp_get_instrument_path(struct module_data *m, char *path, int size) 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); va_end(ap);
} }
#ifndef LIBXMP_CORE_PLAYER
static int schism_tracker_date(int year, int month, int day) static int schism_tracker_date(int year, int month, int day)
{ {
int mm = (month + 9) % 12; 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) { if (s_ver >= 0x50) {
/* time_t epoch_sec = 1256947200; */ /* 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; int year, month, day, dayofyear;
if (s_ver == 0xfff) { 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. /* Date algorithm reimplemented from OpenMPT.
*/ */
year = (int)(((int64)t * 10000L + 14780) / 3652425); year = (int)((t * 10000L + 14780) / 3652425);
dayofyear = t - (365 * year + (year / 4) - (year / 100) + (year / 400)); dayofyear = t - (365L * year + (year / 4) - (year / 100) + (year / 400));
if (dayofyear < 0) { if (dayofyear < 0) {
year--; 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; month = (100 * dayofyear + 52) / 3060;
day = dayofyear - (month * 306 + 5) / 10 + 1; 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); 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;
}

View file

@ -1,7 +1,19 @@
#ifndef LIBXMP_COMMON_H #ifndef LIBXMP_COMMON_H
#define 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 <stdarg.h> #include <stdarg.h>
#include <limits.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@ -15,16 +27,63 @@
#define LIBXMP_EXPORT_VAR #define LIBXMP_EXPORT_VAR
#endif #endif
#if defined(__MORPHOS__) || defined(__AROS__) || defined(AMIGAOS) || \ #ifndef __cplusplus
defined(__amigaos__) || defined(__amigaos4__) ||defined(__amigados__) || \ #define LIBXMP_BEGIN_DECLS
defined(AMIGA) || defined(_AMIGA) || defined(__AMIGA__) #define LIBXMP_END_DECLS
#define LIBXMP_AMIGA 1 /* to identify amiga platforms. */ #else
#define LIBXMP_BEGIN_DECLS extern "C" {
#define LIBXMP_END_DECLS }
#endif #endif
#if (defined(__GNUC__) || defined(__clang__)) && defined(XMP_SYM_VISIBILITY) #if defined(_MSC_VER) && !defined(__cplusplus)
#if !defined(_WIN32) && !defined(__ANDROID__) && !defined(__APPLE__) && !defined(LIBXMP_AMIGA) && !defined(__MSDOS__) && !defined(B_BEOS_VERSION) && !defined(__ATHEOS__) && !defined(EMSCRIPTEN) && !defined(__MINT__) #define inline __inline
#define USE_VERSIONED_SYMBOLS
#endif #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(<winapifamily.h>)
#define HAVE_WINAPIFAMILY_H 1
#else
#define HAVE_WINAPIFAMILY_H 0
#endif
#endif
#if HAVE_WINAPIFAMILY_H
#include <winapifamily.h>
#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 #endif
/* AmigaOS fixes by Chris Young <cdyoung@ntlworld.com>, Nov 25, 2007 /* AmigaOS fixes by Chris Young <cdyoung@ntlworld.com>, Nov 25, 2007
@ -33,6 +92,8 @@
# include <SupportDefs.h> # include <SupportDefs.h>
#elif defined __amigaos4__ #elif defined __amigaos4__
# include <exec/types.h> # include <exec/types.h>
#elif defined _arch_dreamcast /* KallistiOS */
# include <arch/types.h>
#else #else
typedef signed char int8; typedef signed char int8;
typedef signed short int int16; typedef signed short int int16;
@ -42,14 +103,18 @@ typedef unsigned short int uint16;
typedef unsigned int uint32; typedef unsigned int uint32;
#endif #endif
#ifdef _MSC_VER /* MSVC++6.0 has no long long */ #ifdef _MSC_VER /* MSVC6 has no long long */
typedef signed __int64 int64; typedef signed __int64 int64;
typedef unsigned __int64 uint64; 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 unsigned long long uint64;
typedef signed long long int64; typedef signed long long int64;
#endif #endif
#ifdef _MSC_VER
#pragma warning(disable:4100) /* unreferenced formal parameter */
#endif
#ifndef LIBXMP_CORE_PLAYER #ifndef LIBXMP_CORE_PLAYER
#define LIBXMP_PAULA_SIMULATOR #define LIBXMP_PAULA_SIMULATOR
#endif #endif
@ -72,6 +137,11 @@ typedef signed long long int64;
#define RESET_FLAG(a,b) ((a)&=~(b)) #define RESET_FLAG(a,b) ((a)&=~(b))
#define TEST_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 { \ #define CLAMP(x,a,b) do { \
if ((x) < (a)) (x) = (a); \ if ((x) < (a)) (x) = (a); \
else if ((x) > (b)) (x) = (b); \ else if ((x) > (b)) (x) = (b); \
@ -87,20 +157,18 @@ typedef signed long long int64;
#define D_CRIT " Error: " #define D_CRIT " Error: "
#define D_WARN "Warning: " #define D_WARN "Warning: "
#define D_INFO " Info: " #define D_INFO " Info: "
#ifndef CLIB_DECL
#define CLIB_DECL
#endif
#ifdef DEBUG #ifdef DEBUG
#ifndef ATTR_PRINTF #define D_ libxmp_msvc_dbgprint /* in win32.c */
#define ATTR_PRINTF(x,y) void libxmp_msvc_dbgprint(const char *text, ...);
#endif
void CLIB_DECL D_(const char *text, ...) ATTR_PRINTF(1,2);
#else #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 #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 #else
#define D_(args, ...) do {} while (0) #define D_(...) do {} while (0)
#endif #endif
#endif #endif
@ -111,21 +179,8 @@ void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); }
#define D_CRIT " Error: " #define D_CRIT " Error: "
#define D_WARN "Warning: " #define D_WARN "Warning: "
#define D_INFO " Info: " #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 { \ #define D_(...) do { \
printf("\x1b[33m%s \x1b[37m[%s:%d] " D_INFO, __FUNCTION__, \ __android_log_print(ANDROID_LOG_DEBUG, "libxmp", __VA_ARGS__); \
__FILE__, __LINE__); printf (__VA_ARGS__); printf ("\x1b[0m\n"); \
} while (0) } while (0)
#else #else
#define D_(...) do {} while (0) #define D_(...) do {} while (0)
@ -134,15 +189,20 @@ void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); }
#else #else
#ifdef DEBUG #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_INFO "\x1b[33m"
#define D_CRIT "\x1b[31m" #define D_CRIT "\x1b[31m"
#define D_WARN "\x1b[36m" #define D_WARN "\x1b[36m"
#define D_(args...) do { \ #define D_(...) do { \
printf("\x1b[33m%s \x1b[37m[%s:%d] " D_INFO, __FUNCTION__, \ printf("\x1b[33m%s \x1b[37m[%s:%d] " D_INFO, LIBXMP_FUNC, \
__FILE__, __LINE__); printf (args); printf ("\x1b[0m\n"); \ __FILE__, __LINE__); printf (__VA_ARGS__); printf ("\x1b[0m\n"); \
} while (0) } while (0)
#else #else
#define D_(args...) do {} while (0) #define D_(...) do {} while (0)
#endif #endif
#endif /* !_MSC_VER */ #endif /* !_MSC_VER */
@ -151,7 +211,6 @@ void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); }
#define dup _dup #define dup _dup
#define fileno _fileno #define fileno _fileno
#define strnicmp _strnicmp #define strnicmp _strnicmp
#define strdup _strdup
#define fdopen _fdopen #define fdopen _fdopen
#define open _open #define open _open
#define close _close #define close _close
@ -171,13 +230,33 @@ void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); }
#undef USE_LIBXMP_SNPRINTF #undef USE_LIBXMP_SNPRINTF
#endif #endif
#ifdef USE_LIBXMP_SNPRINTF #ifdef USE_LIBXMP_SNPRINTF
int libxmp_vsnprintf(char *, size_t, const char *, va_list); #if defined(__GNUC__) || defined(__clang__)
int libxmp_snprintf (char *, size_t, const char *, ...); #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 snprintf libxmp_snprintf
#define vsnprintf libxmp_vsnprintf #define vsnprintf libxmp_vsnprintf
#endif #endif
#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 */ /* Quirks */
#define QUIRK_S3MLOOP (1 << 0) /* S3M loop mode */ #define QUIRK_S3MLOOP (1 << 0) /* S3M loop mode */
#define QUIRK_ENVFADE (1 << 1) /* Fade at end of envelope */ #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_UNISLD (1 << 10) /* Unified pitch slide/portamento */
#define QUIRK_ITVPOR (1 << 11) /* Disable fine bends in IT vol fx */ #define QUIRK_ITVPOR (1 << 11) /* Disable fine bends in IT vol fx */
#define QUIRK_FTMOD (1 << 12) /* Flag for multichannel mods */ #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_MODRNG (1 << 13)*/ /* Limit periods to MOD range */
#define QUIRK_INSVOL (1 << 14) /* Use instrument volume */ #define QUIRK_INSVOL (1 << 14) /* Use instrument volume */
#define QUIRK_VIRTUAL (1 << 15) /* Enable virtual channels */ #define QUIRK_VIRTUAL (1 << 15) /* Enable virtual channels */
@ -232,8 +312,9 @@ int libxmp_snprintf (char *, size_t, const char *, ...);
/* Time factor */ /* Time factor */
#define DEFAULT_TIME_FACTOR 10.0 #define DEFAULT_TIME_FACTOR 10.0
#define MED_TIME_FACTOR 2.64 #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_SAMPLE_SIZE 0x10000000
#define MAX_SAMPLES 1024 #define MAX_SAMPLES 1024
#define MAX_INSTRUMENTS 255 #define MAX_INSTRUMENTS 255
@ -262,7 +343,6 @@ struct ord_data {
}; };
/* Context */ /* Context */
struct smix_data { struct smix_data {
@ -276,6 +356,17 @@ struct smix_data {
/* This will be added to the sample structure in the next API revision */ /* This will be added to the sample structure in the next API revision */
struct extra_sample_data { struct extra_sample_data {
double c5spd; 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 { struct module_data {
@ -293,7 +384,9 @@ struct module_data {
int volbase; /* Volume base */ int volbase; /* Volume base */
int gvolbase; /* Global volume base */ int gvolbase; /* Global volume base */
int gvol; /* Global volume */ 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 */ int quirk; /* player quirks */
#define READ_EVENT_MOD 0 #define READ_EVENT_MOD 0
#define READ_EVENT_FT2 1 #define READ_EVENT_FT2 1
@ -315,9 +408,8 @@ struct module_data {
void *extra; /* format-specific extra fields */ void *extra; /* format-specific extra fields */
uint8 **scan_cnt; /* scan counters */ uint8 **scan_cnt; /* scan counters */
struct extra_sample_data *xtra; struct extra_sample_data *xtra;
#ifndef LIBXMP_CORE_DISABLE_IT struct midi_macro_data *midi;
struct xmp_sample *xsmp; /* sustain loop samples */ int compare_vblank;
#endif
}; };
@ -332,6 +424,9 @@ struct flow_control {
int delay; int delay;
int jumpline; int jumpline;
int loop_chn; int loop_chn;
#ifndef LIBXMP_CORE_PLAYER
int jump_in_pat;
#endif
struct pattern_loop *loop; struct pattern_loop *loop;
@ -348,6 +443,13 @@ struct virt_channel {
int map; int map;
}; };
struct scan_data {
int time; /* replay time in ms */
int row;
int ord;
int num;
};
struct player_data { struct player_data {
int ord; int ord;
int pos; int pos;
@ -372,12 +474,7 @@ struct player_data {
struct flow_control flow; struct flow_control flow;
struct { struct scan_data *scan;
int time; /* replay time in ms */
int ord;
int row;
int num;
} scan[MAX_SEQUENCES];
struct channel_data *xc_data; struct channel_data *xc_data;
@ -416,12 +513,13 @@ struct mixer_data {
int mix; /* percentage of channel separation */ int mix; /* percentage of channel separation */
int interp; /* interpolation type */ int interp; /* interpolation type */
int dsp; /* dsp effect flags */ int dsp; /* dsp effect flags */
char* buffer; /* output buffer */ char *buffer; /* output buffer */
int32* buf32; /* temporary buffer for 32 bit samples */ int32 *buf32; /* temporary buffer for 32 bit samples */
int numvoc; /* default softmixer voices number */ int numvoc; /* default softmixer voices number */
int ticksize; int ticksize;
int dtright; /* anticlick control, right channel */ int dtright; /* anticlick control, right channel */
int dtleft; /* anticlick control, left channel */ int dtleft; /* anticlick control, left channel */
int bidir_adjust; /* adjustment for IT bidirectional loops */
double pbase; /* period base */ double pbase; /* period base */
}; };
@ -442,6 +540,7 @@ void libxmp_free_scan (struct context_data *);
int libxmp_scan_sequences (struct context_data *); int libxmp_scan_sequences (struct context_data *);
int libxmp_get_sequence (struct context_data *, int); int libxmp_get_sequence (struct context_data *, int);
int libxmp_set_player_mode (struct context_data *); int libxmp_set_player_mode (struct context_data *);
void libxmp_reset_flow (struct context_data *);
int8 read8s (FILE *, int *err); int8 read8s (FILE *, int *err);
uint8 read8 (FILE *, int *err); uint8 read8 (FILE *, int *err);
@ -458,7 +557,6 @@ void write16l (FILE *, uint16);
void write16b (FILE *, uint16); void write16b (FILE *, uint16);
void write32l (FILE *, uint32); void write32l (FILE *, uint32);
void write32b (FILE *, uint32); void write32b (FILE *, uint32);
int move_data (FILE *, FILE *, int);
uint16 readmem16l (const uint8 *); uint16 readmem16l (const uint8 *);
uint16 readmem16b (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_instrument *libxmp_get_instrument(struct context_data *, int);
struct xmp_sample *libxmp_get_sample(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 */ #endif /* LIBXMP_COMMON_H */

View file

@ -1,5 +1,5 @@
/* Extended Module Player /* 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 * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
@ -31,7 +31,7 @@ xmp_context xmp_create_context(void)
{ {
struct context_data *ctx; struct context_data *ctx;
ctx = calloc(1, sizeof(struct context_data)); ctx = (struct context_data *) calloc(1, sizeof(struct context_data));
if (ctx == NULL) { if (ctx == NULL) {
return NULL; return NULL;
} }
@ -117,13 +117,9 @@ static void set_position(struct context_data *ctx, int pos, int dir)
} else { } else {
p->pos = pos; p->pos = pos;
} }
f->jumpline = 0; /* Clear flow vars to prevent old pattern jumps and
f->jump = -1; * other junk from executing in the new position. */
f->pbreak = 0; libxmp_reset_flow(ctx);
f->loop_chn = 0;
f->delay = 0;
f->rowdelay = 0;
f->rowdelay_set = 0;
} }
} }
} }
@ -185,16 +181,17 @@ int xmp_set_row(xmp_context opaque, int row)
struct xmp_module *mod = &m->mod; struct xmp_module *mod = &m->mod;
struct flow_control *f = &p->flow; struct flow_control *f = &p->flow;
int pos = p->pos; int pos = p->pos;
int pattern = mod->xxo[pos]; int pattern;
if (pos < 0 || pos >= mod->len) { if (pos < 0 || pos >= mod->len) {
pos = 0; pos = 0;
} }
pattern = mod->xxo[pos];
if (ctx->state < XMP_STATE_PLAYING) if (ctx->state < XMP_STATE_PLAYING)
return -XMP_ERROR_STATE; return -XMP_ERROR_STATE;
if (row >= mod->xxp[pattern]->rows) if (pattern >= mod->pat || row >= mod->xxp[pattern]->rows)
return -XMP_ERROR_INVALID; return -XMP_ERROR_INVALID;
/* See set_position. */ /* See set_position. */
@ -309,18 +306,22 @@ int xmp_channel_vol(xmp_context opaque, int chn, int vol)
} }
#ifdef USE_VERSIONED_SYMBOLS #ifdef USE_VERSIONED_SYMBOLS
LIBXMP_EXPORT extern int xmp_set_player_v40__(xmp_context, int, int); LIBXMP_BEGIN_DECLS /* no name-mangling */
LIBXMP_EXPORT extern int xmp_set_player_v41__(xmp_context, int, int) LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v40__(xmp_context, int, int) LIBXMP_ATTRIB_SYMVER("xmp_set_player@XMP_4.0");
__attribute__((alias("xmp_set_player_v40__"))); LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v41__(xmp_context, int, int)
LIBXMP_EXPORT extern int xmp_set_player_v43__(xmp_context, int, int) __attribute__((alias("xmp_set_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_set_player@XMP_4.1");
__attribute__((alias("xmp_set_player_v40__"))); LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v43__(xmp_context, int, int)
LIBXMP_EXPORT extern int xmp_set_player_v44__(xmp_context, int, int) __attribute__((alias("xmp_set_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_set_player@XMP_4.3");
__attribute__((alias("xmp_set_player_v40__"))); 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_v40__, xmp_set_player@XMP_4.0");
asm(".symver xmp_set_player_v41__, xmp_set_player@XMP_4.1"); 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_v43__, xmp_set_player@XMP_4.3");
asm(".symver xmp_set_player_v44__, xmp_set_player@@XMP_4.4"); asm(".symver xmp_set_player_v44__, xmp_set_player@@XMP_4.4");
#endif
LIBXMP_END_DECLS
#define xmp_set_player__ xmp_set_player_v40__ #define xmp_set_player__ xmp_set_player_v40__
#else #else
@ -427,21 +428,25 @@ int xmp_set_player__(xmp_context opaque, int parm, int val)
} }
#ifdef USE_VERSIONED_SYMBOLS #ifdef USE_VERSIONED_SYMBOLS
LIBXMP_EXPORT extern int xmp_get_player_v40__(xmp_context, int); LIBXMP_BEGIN_DECLS /* no name-mangling */
LIBXMP_EXPORT extern int xmp_get_player_v41__(xmp_context, int) LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v40__(xmp_context, int) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.0");
__attribute__((alias("xmp_get_player_v40__"))); LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v41__(xmp_context, int)
LIBXMP_EXPORT extern int xmp_get_player_v42__(xmp_context, int) __attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.1");
__attribute__((alias("xmp_get_player_v40__"))); LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v42__(xmp_context, int)
LIBXMP_EXPORT extern int xmp_get_player_v43__(xmp_context, int) __attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.2");
__attribute__((alias("xmp_get_player_v40__"))); LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v43__(xmp_context, int)
LIBXMP_EXPORT extern int xmp_get_player_v44__(xmp_context, int) __attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.3");
__attribute__((alias("xmp_get_player_v40__"))); 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_v40__, xmp_get_player@XMP_4.0");
asm(".symver xmp_get_player_v41__, xmp_get_player@XMP_4.1"); 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_v42__, xmp_get_player@XMP_4.2");
asm(".symver xmp_get_player_v43__, xmp_get_player@XMP_4.3"); asm(".symver xmp_get_player_v43__, xmp_get_player@XMP_4.3");
asm(".symver xmp_get_player_v44__, xmp_get_player@@XMP_4.4"); asm(".symver xmp_get_player_v44__, xmp_get_player@@XMP_4.4");
#endif
LIBXMP_END_DECLS
#define xmp_get_player__ xmp_get_player_v40__ #define xmp_get_player__ xmp_get_player_v40__
#else #else
@ -554,7 +559,7 @@ int xmp_set_instrument_path(xmp_context opaque, const char *path)
if (m->instrument_path != NULL) if (m->instrument_path != NULL)
free(m->instrument_path); free(m->instrument_path);
m->instrument_path = strdup(path); m->instrument_path = libxmp_strdup(path);
if (m->instrument_path == NULL) { if (m->instrument_path == NULL) {
return -XMP_ERROR_SYSTEM; return -XMP_ERROR_SYSTEM;
} }

View file

@ -1,5 +1,5 @@
/* Extended Module Player /* 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 * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
@ -33,7 +33,6 @@
if (err != NULL) *err = (x); \ if (err != NULL) *err = (x); \
} while (0) } while (0)
uint8 read8(FILE *f, int *err) uint8 read8(FILE *f, int *err)
{ {
int a; int a;
@ -97,13 +96,13 @@ uint32 read24l(FILE *f, int *err)
read_byte(a); read_byte(a);
read_byte(b); read_byte(b);
read_byte(c); read_byte(c);
set_error(0); set_error(0);
return (c << 16) | (b << 8) | a; return (c << 16) | (b << 8) | a;
error: error:
set_error(ferror(f) ? errno : EOF); set_error(ferror(f) ? errno : EOF);
return 0xffffff; return 0xffffffff;
} }
uint32 read24b(FILE *f, int *err) uint32 read24b(FILE *f, int *err)
@ -113,13 +112,13 @@ uint32 read24b(FILE *f, int *err)
read_byte(a); read_byte(a);
read_byte(b); read_byte(b);
read_byte(c); read_byte(c);
set_error(0); set_error(0);
return (a << 16) | (b << 8) | c; return (a << 16) | (b << 8) | c;
error: error:
set_error(ferror(f) ? errno : EOF); set_error(ferror(f) ? errno : EOF);
return 0xffffff; return 0xffffffff;
} }
uint32 read32l(FILE *f, int *err) uint32 read32l(FILE *f, int *err)
@ -252,18 +251,4 @@ void write32b(FILE *f, uint32 w)
write8(f, w & 0x000000ff); 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 #endif

View file

@ -1,5 +1,5 @@
/* Extended Module Player /* 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 * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * 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 module_data *m = &ctx->m;
struct xmp_instrument *instrument = &m->mod.xxi[xc->ins]; struct xmp_instrument *instrument = &m->mod.xxi[xc->ins];
int mapped = instrument->map[xc->key].ins;
struct xmp_subinstrument *sub; 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) { if (mapped >= instrument->nsm) {
mapped = 0; mapped = 0;
@ -81,11 +86,13 @@ static void do_toneporta(struct context_data *ctx,
sub = &instrument->sub[mapped]; 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--; 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 + xc->porta.target = libxmp_note_to_period(ctx, note + sub->xpo +
instrument->map[xc->key_porta].xpo, xc->finetune, mapped_xpo, xc->finetune, xc->per_adj);
xc->per_adj);
} }
xc->porta.dir = xc->period < xc->porta.target ? 1 : -1; 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); h = MSN(fxp);
l = LSN(fxp); l = LSN(fxp);
xc->vol.slide2 = h ? h : -l; xc->vol.slide2 = h ? h : -l;
} }
break; break;
case FX_JUMP: /* Order jump */ case FX_JUMP: /* Order jump */
p->flow.pbreak = 1; 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.val = fxp;
xc->retrig.count = LSN(xc->retrig.val) + 1; xc->retrig.count = LSN(xc->retrig.val) + 1;
xc->retrig.type = 0; xc->retrig.type = 0;
xc->retrig.limit = 0;
break; break;
case EX_F_VSLIDE_UP: /* Fine volume slide up */ case EX_F_VSLIDE_UP: /* Fine volume slide up */
EFFECT_MEMORY(fxp, xc->fine_vol.up_memory); 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) { if (fxp) {
SET(FINE_BEND); SET(FINE_BEND);
xc->freq.fslide = fxp; xc->freq.fslide = fxp;
} }
break; break;
case FX_PATT_DELAY: case FX_PATT_DELAY:
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: /* From the OpenMPT VolColMemory.it test case:
* "Volume column commands a, b, c and d (volume slide) share one * "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 * effect memory, but it should not be shared with Dxy in the effect
* column. * column.
*/ */
case FX_VSLIDE_UP_2: /* Fine volume slide up */ case FX_VSLIDE_UP_2: /* Fine volume slide up */
EFFECT_MEMORY(fxp, xc->vol.memory2); 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) { if (note) {
xc->retrig.count = LSN(xc->retrig.val) + 1; xc->retrig.count = LSN(xc->retrig.val) + 1;
} }
xc->retrig.limit = 0;
SET(RETRIG); SET(RETRIG);
break; break;
case FX_TREMOR: /* Tremor */ 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: case FX_SURROUND:
xc->pan.surround = fxp; xc->pan.surround = fxp;
break; break;
case FX_REVERSE: /* Play forward/backward */
libxmp_virt_reverse(ctx, chn, fxp);
break;
#ifndef LIBXMP_CORE_DISABLE_IT #ifndef LIBXMP_CORE_DISABLE_IT
case FX_TRK_VOL: /* Track volume setting */ 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; break;
case FX_FLT_CUTOFF: case FX_FLT_CUTOFF:
if (fxp < 0xfe || xc->filter.resonance > 0) { xc->filter.cutoff = fxp;
xc->filter.cutoff = fxp;
}
break; break;
case FX_FLT_RESN: case FX_FLT_RESN:
xc->filter.resonance = fxp; xc->filter.resonance = fxp;
break; 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 */ case FX_PANBRELLO: /* Panbrello */
SET(PANBRELLO); SET(PANBRELLO);
SET_LFO_NOTZERO(&xc->panbrello.lfo, LSN(fxp) << 4, MSN(fxp)); 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); h = MSN(fxp);
l = LSN(fxp); l = LSN(fxp);
xc->vol.fslide = h ? h : -l; xc->vol.fslide = h ? h : -l;
} }
break; break;
case FX_NSLIDE_DN: case FX_NSLIDE_DN:
case FX_NSLIDE_UP: 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.val = MSN(fxp);
xc->retrig.count = MSN(fxp) + 1; xc->retrig.count = MSN(fxp) + 1;
xc->retrig.type = 0; xc->retrig.type = 0;
xc->retrig.limit = 0;
} }
if (fxt == FX_NSLIDE_UP || fxt == FX_NSLIDE_R_UP) 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); SET_LFO_NOTZERO(&xc->vibrato.lfo, 669, 1);
break; 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 #endif
default: default:

View file

@ -59,7 +59,7 @@
#define FX_F_NSLIDE_DN 0x75 #define FX_F_NSLIDE_DN 0x75
#define FX_F_NSLIDE_UP 0x76 #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_DN 0x78
#define FX_PER_PORTA_UP 0x79 #define FX_PER_PORTA_UP 0x79
#define FX_PER_TPORTA 0x7a #define FX_PER_TPORTA 0x7a
@ -75,6 +75,21 @@
#define FX_669_TPORTA 0x62 #define FX_669_TPORTA 0x62
#define FX_669_FINETUNE 0x63 #define FX_669_FINETUNE 0x63
#define FX_669_VIBRATO 0x64 #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 #endif
#ifndef LIBXMP_CORE_DISABLE_IT #ifndef LIBXMP_CORE_DISABLE_IT
@ -92,6 +107,9 @@
#define FX_PANBRELLO_WF 0x8b #define FX_PANBRELLO_WF 0x8b
#define FX_HIOFFSET 0x8c #define FX_HIOFFSET 0x8c
#define FX_IT_BREAK 0x8e /* like FX_BREAK with hex parameter */ #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 #endif
#ifndef LIBXMP_CORE_PLAYER #ifndef LIBXMP_CORE_PLAYER
@ -119,9 +137,11 @@
#define FX_VOL_SUB 0xb7 /* SFX change volume down */ #define FX_VOL_SUB 0xb7 /* SFX change volume down */
#define FX_PITCH_ADD 0xb8 /* SFX add steps to current note */ #define FX_PITCH_ADD 0xb8 /* SFX add steps to current note */
#define FX_PITCH_SUB 0xb9 /* 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 #endif
#define FX_SURROUND 0x8d /* S3M/IT */ #define FX_SURROUND 0x8d /* S3M/IT */
#define FX_REVERSE 0x8f /* XM/IT/others: play forward/reverse */
#define FX_S3M_SPEED 0xa3 /* S3M */ #define FX_S3M_SPEED 0xa3 /* S3M */
#define FX_VOLSLIDE_2 0xa4 #define FX_VOLSLIDE_2 0xa4
#define FX_FINETUNE 0xa6 #define FX_FINETUNE 0xa6

View file

@ -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 <errno.h>
#if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
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 <os2.h>
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 <dos.h>
#include <io.h>
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 <dos.h>
#include <direct.h>
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 <proto/dos.h>
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 <proto/dos.h>
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 <sys/types.h>
#include <sys/stat.h>
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

View file

@ -68,7 +68,9 @@ static const float resonance_table[128] = {
#if !defined(HAVE_POWF) || defined(__DJGPP__) || defined(__WATCOMC__) #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 #endif

View file

@ -1,5 +1,5 @@
/* Extended Module Player /* 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 * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
@ -21,23 +21,85 @@
*/ */
#include "format.h" #include "format.h"
#ifndef LIBXMP_NO_PROWIZARD
#include "loaders/prowizard/prowiz.h"
#endif
extern const struct format_loader libxmp_loader_xm; const struct format_loader *const format_loaders[NUM_FORMATS + 2] = {
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] = {
&libxmp_loader_xm, &libxmp_loader_xm,
&libxmp_loader_mod, &libxmp_loader_mod,
#ifndef LIBXMP_CORE_DISABLE_IT #ifndef LIBXMP_CORE_DISABLE_IT
&libxmp_loader_it, &libxmp_loader_it,
#endif #endif
&libxmp_loader_s3m, &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) const char *const *format_list(void)
{ {
@ -45,6 +107,16 @@ const char *const *format_list(void)
if (_farray[0] == NULL) { if (_farray[0] == NULL) {
for (count = i = 0; format_loaders[i] != NULL; i++) { 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; _farray[count++] = format_loaders[i]->name;
} }

View file

@ -6,21 +6,98 @@
struct format_loader { struct format_loader {
const char *name; const char *name;
int (*const test)(HIO_HANDLE *, char *, const int); int (*test)(HIO_HANDLE *, char *, const int);
int (*const loader)(struct module_data *, HIO_HANDLE *, 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); 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 #ifndef LIBXMP_CORE_PLAYER
#define NUM_PW_FORMATS 43 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 #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 *); int pw_test_format(HIO_HANDLE *, char *, const int, struct xmp_test_info *);
#endif #else
#endif #define NUM_PW_FORMATS 0
#endif #endif
#endif /* LIBXMP_FORMAT_H */

View file

@ -1,5 +1,5 @@
/* Extended Module Player /* 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 * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * 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) { if (ret < 0) {
h->error = errno; h->error = errno;
} }
else if (h->error == EOF) {
h->error = 0;
}
break; break;
case HIO_HANDLE_TYPE_MEMORY: case HIO_HANDLE_TYPE_MEMORY:
ret = mseek(h->handle.mem, offset, whence); ret = mseek(h->handle.mem, offset, whence);
if (ret < 0) { if (ret < 0) {
h->error = EINVAL; h->error = EINVAL;
} }
else if (h->error == EOF) {
h->error = 0;
}
break; break;
case HIO_HANDLE_TYPE_CBFILE: case HIO_HANDLE_TYPE_CBFILE:
ret = cbseek(h->handle.cbfile, offset, whence); ret = cbseek(h->handle.cbfile, offset, whence);
if (ret < 0) { if (ret < 0) {
h->error = EINVAL; h->error = EINVAL;
} }
else if (h->error == EOF) {
h->error = 0;
}
break; break;
} }
@ -358,7 +367,7 @@ HIO_HANDLE *hio_open(const char *path, const char *mode)
{ {
HIO_HANDLE *h; HIO_HANDLE *h;
h = (HIO_HANDLE *)calloc(1, sizeof (HIO_HANDLE)); h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE));
if (h == NULL) if (h == NULL)
goto err; goto err;
@ -381,19 +390,24 @@ HIO_HANDLE *hio_open(const char *path, const char *mode)
return NULL; 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; HIO_HANDLE *h;
if (size <= 0) return NULL; if (size <= 0) return NULL;
h = (HIO_HANDLE *)calloc(1, sizeof (HIO_HANDLE)); h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE));
if (h == NULL) if (h == NULL)
return NULL; return NULL;
h->type = HIO_HANDLE_TYPE_MEMORY; h->type = HIO_HANDLE_TYPE_MEMORY;
h->handle.mem = mopen(ptr, size); h->handle.mem = mopen(ptr, size, free_after_use);
h->size = size; h->size = size;
if (!h->handle.mem) {
free(h);
h = NULL;
}
return h; return h;
} }
@ -401,7 +415,7 @@ HIO_HANDLE *hio_open_file(FILE *f)
{ {
HIO_HANDLE *h; HIO_HANDLE *h;
h = (HIO_HANDLE *)calloc(1, sizeof (HIO_HANDLE)); h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE));
if (h == NULL) if (h == NULL)
return NULL; return NULL;
@ -436,7 +450,7 @@ HIO_HANDLE *hio_open_callbacks(void *priv, struct xmp_callbacks callbacks)
if (!f) if (!f)
return NULL; return NULL;
h = (HIO_HANDLE *)calloc(1, sizeof(HIO_HANDLE)); h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE));
if (h == NULL) { if (h == NULL) {
cbclose(f); cbclose(f);
return NULL; return NULL;
@ -453,7 +467,7 @@ HIO_HANDLE *hio_open_callbacks(void *priv, struct xmp_callbacks callbacks)
return h; return h;
} }
int hio_close(HIO_HANDLE *h) static int hio_close_internal(HIO_HANDLE *h)
{ {
int ret = -1; int ret = -1;
@ -468,7 +482,58 @@ int hio_close(HIO_HANDLE *h)
ret = cbclose(h->handle.cbfile); ret = cbclose(h->handle.cbfile);
break; 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); free(h);
return ret; return ret;
} }

View file

@ -38,10 +38,12 @@ long hio_tell (HIO_HANDLE *);
int hio_eof (HIO_HANDLE *); int hio_eof (HIO_HANDLE *);
int hio_error (HIO_HANDLE *); int hio_error (HIO_HANDLE *);
HIO_HANDLE *hio_open (const char *, const char *); 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_file (FILE *);
HIO_HANDLE *hio_open_file2 (FILE *);/* allows fclose()ing the file by libxmp */ HIO_HANDLE *hio_open_file2 (FILE *);/* allows fclose()ing the file by libxmp */
HIO_HANDLE *hio_open_callbacks (void *, struct xmp_callbacks); 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 *); int hio_close (HIO_HANDLE *);
long hio_size (HIO_HANDLE *); long hio_size (HIO_HANDLE *);

View file

@ -20,6 +20,11 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#ifndef LIBXMP_LOADERS_IT_H
#define LIBXMP_LOADERS_IT_H
#include "loader.h"
/* IT flags */ /* IT flags */
#define IT_STEREO 0x01 #define IT_STEREO 0x01
#define IT_VOL_OPT 0x02 /* Not recognized */ #define IT_VOL_OPT 0x02 /* Not recognized */
@ -27,9 +32,14 @@
#define IT_LINEAR_FREQ 0x08 #define IT_LINEAR_FREQ 0x08
#define IT_OLD_FX 0x10 #define IT_OLD_FX 0x10
#define IT_LINK_GXX 0x20 #define IT_LINK_GXX 0x20
#define IT_MIDI_WHEEL 0x40
#define IT_MIDI_CONFIG 0x80
/* IT special */ /* IT special */
#define IT_HAS_MSG 0x01 #define IT_HAS_MSG 0x01
#define IT_EDIT_HISTORY 0x02
#define IT_HIGHLIGHTS 0x04
#define IT_SPEC_MIDICFG 0x08
/* IT instrument flags */ /* IT instrument flags */
#define IT_INST_SAMPLE 0x01 #define IT_INST_SAMPLE 0x01
@ -56,6 +66,7 @@
#define IT_CVT_DIFF 0x04 /* Compressed sample flag */ #define IT_CVT_DIFF 0x04 /* Compressed sample flag */
#define IT_CVT_BYTEDIFF 0x08 /* 'safe to ignore' according to ittech.txt */ #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_12BIT 0x10 /* 'safe to ignore' according to ittech.txt */
#define IT_CVT_ADPCM 0xff /* Special: always indicates Modplug ADPCM4 */
/* IT envelope flags */ /* IT envelope flags */
#define IT_ENV_ON 0x01 #define IT_ENV_ON 0x01
@ -179,3 +190,7 @@ struct it_sample_header {
uint8 vit; /* Vibrato waveform */ 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 */

View file

@ -1,5 +1,5 @@
/* Extended Module Player /* 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 * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
@ -30,7 +30,6 @@
#define MAGIC_IMPI MAGIC4('I','M','P','I') #define MAGIC_IMPI MAGIC4('I','M','P','I')
#define MAGIC_IMPS MAGIC4('I','M','P','S') #define MAGIC_IMPS MAGIC4('I','M','P','S')
static int it_test(HIO_HANDLE *, char *, const int); static int it_test(HIO_HANDLE *, char *, const int);
static int it_load(struct module_data *, HIO_HANDLE *, 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 FX_XTND 0xfe
#define L_CHANNELS 64 #define L_CHANNELS 64
static const uint8 fx[32] = { static const uint8 fx[32] = {
/* */ FX_NONE, /* */ FX_NONE,
/* A */ FX_S3M_SPEED, /* A */ FX_S3M_SPEED,
@ -83,19 +81,14 @@ static const uint8 fx[32] = {
/* W */ FX_GVOL_SLIDE, /* W */ FX_GVOL_SLIDE,
/* X */ FX_SETPAN, /* X */ FX_SETPAN,
/* Y */ FX_PANBRELLO, /* Y */ FX_PANBRELLO,
/* Z */ FX_FLT_CUTOFF, /* Z */ FX_MACRO,
/* ? */ FX_NONE,
/* ? */ FX_NONE, /* ? */ FX_NONE,
/* / */ FX_MACROSMOOTH,
/* ? */ FX_NONE, /* ? */ FX_NONE,
/* ? */ FX_NONE, /* ? */ 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) 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); 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->fxt = FX_SETPAN;
e->fxp = l << 4; e->fxp = l << 4;
break; break;
case 0x9: /* 0x91 = set surround */ case 0x9:
e->fxt = FX_SURROUND; if (l == 0 || l == 1) {
e->fxp = l; /* 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; break;
case 0xa: /* High offset */ case 0xa: /* High offset */
e->fxt = FX_HIOFFSET; 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->fxt = FX_IT_ROWDELAY;
e->fxp = l; e->fxp = l;
break; break;
case 0xf: /* Set parametered macro */
e->fxt = FX_MACRO_SET;
e->fxp = l;
break;
default: default:
e->fxt = e->fxp = 0; e->fxt = e->fxp = 0;
} }
break; 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: case FX_TREMOR:
if (!new_fx && e->fxp != 0) { if (!new_fx && e->fxp != 0) {
e->fxp = ((MSN(e->fxp) + 1) << 4) | (LSN(e->fxp) + 1); 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, static int read_envelope(struct xmp_envelope *ei, struct it_envelope *env,
HIO_HANDLE *f) HIO_HANDLE *f)
{ {
@ -313,7 +337,8 @@ static int read_envelope(struct xmp_envelope *ei, struct it_envelope *env,
return 0; 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 #ifndef LIBXMP_CORE_PLAYER
char tracker_name[40]; 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"); strcpy(tracker_name, "ModPlug Tracker 1.16");
/* ModPlug Tracker files aren't really IMPM 2.00 */ /* ModPlug Tracker files aren't really IMPM 2.00 */
ifh->cmwt = sample_mode ? 0x100 : 0x214; 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) { } else if (ifh->cwt == 0x0216) {
strcpy(tracker_name, "Impulse Tracker 2.14v3"); strcpy(tracker_name, "Impulse Tracker 2.14v3");
} else if (ifh->cwt == 0x0217) { } else if (ifh->cwt == 0x0217) {
@ -352,6 +387,7 @@ static void identify_tracker(struct module_data *m, struct it_file_header *ifh)
case 0x7f: case 0x7f:
if (ifh->cwt == 0x0888) { if (ifh->cwt == 0x0888) {
strcpy(tracker_name, "OpenMPT 1.17"); strcpy(tracker_name, "OpenMPT 1.17");
*is_mpt_116 = 1;
} else if (ifh->cwt == 0x7fff) { } else if (ifh->cwt == 0x7fff) {
strcpy(tracker_name, "munch.py"); strcpy(tracker_name, "munch.py");
} else { } else {
@ -486,7 +522,7 @@ static int load_old_it_instrument(struct xmp_instrument *xxi, HIO_HANDLE *f)
xxi->vol = 0x40; xxi->vol = 0x40;
if (k) { 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) { if (xxi->sub == NULL) {
return -1; return -1;
} }
@ -637,7 +673,7 @@ static int load_new_it_instrument(struct xmp_instrument *xxi, HIO_HANDLE *f)
xxi->vol = i2h.gbv >> 1; xxi->vol = i2h.gbv >> 1;
if (k) { 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) if (xxi->sub == NULL)
return -1; return -1;
@ -671,7 +707,7 @@ static int load_new_it_instrument(struct xmp_instrument *xxi, HIO_HANDLE *f)
return 0; 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; xxs->len = len;
@ -680,6 +716,14 @@ static void force_sample_length(struct xmp_sample *xxs, int len)
if (xxs->lps >= xxs->len) if (xxs->lps >= xxs->len)
xxs->flg &= ~XMP_SAMPLE_LOOP; 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, 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 it_sample_header ish;
struct xmp_module *mod = &m->mod; struct xmp_module *mod = &m->mod;
struct xmp_sample *xxs, *xsmp; struct extra_sample_data *xtra;
struct xmp_sample *xxs;
int j, k; int j, k;
uint8 buf[80]; uint8 buf[80];
if (sample_mode) { 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) { if (mod->xxi[i].sub == NULL) {
return -1; return -1;
} }
@ -711,7 +756,7 @@ static int load_it_sample(struct module_data *m, int i, int start,
} }
xxs = &mod->xxs[i]; xxs = &mod->xxs[i];
xsmp = &m->xsmp[i]; xtra = &m->xtra[i];
memcpy(ish.dosname, buf + 4, 12); memcpy(ish.dosname, buf + 4, 12);
ish.zero = buf[16]; 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; xxs->flg |= ish.flags & IT_SMP_BSLOOP ? XMP_SAMPLE_SLOOP_BIDIR : 0;
if (ish.flags & IT_SMP_SLOOP) { if (ish.flags & IT_SMP_SLOOP) {
memcpy(xsmp, xxs, sizeof (struct xmp_sample)); xtra->sus = ish.sloopbeg;
xsmp->lps = ish.sloopbeg; xtra->sue = ish.sloopend;
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;
}
} }
if (sample_mode) { 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) if (xxs->lpe > xxs->len || xxs->lps >= xxs->lpe)
xxs->flg &= ~XMP_SAMPLE_LOOP; xxs->flg &= ~XMP_SAMPLE_LOOP;
if (ish.convert == IT_CVT_ADPCM)
cvt |= SAMPLE_FLAG_ADPCM;
if (~ish.convert & IT_CVT_SIGNED) if (~ish.convert & IT_CVT_SIGNED)
cvt |= SAMPLE_FLAG_UNS; cvt |= SAMPLE_FLAG_UNS;
/* compressed samples */ /* compressed samples */
if (ish.flags & IT_SMP_COMP) { if (ish.flags & IT_SMP_COMP) {
long min_size, file_len, left; long min_size, file_len, left;
uint8 *buf; void *decbuf;
int ret; int ret;
/* Sanity check - the lower bound on IT compressed /* 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", "resizing to %ld",
i, xxs->len, min_size, left, left << 3); i, xxs->len, min_size, left, left << 3);
force_sample_length(xxs, left << 3); force_sample_length(xxs, xtra, left << 3);
if (ish.flags & IT_SMP_SLOOP)
force_sample_length(xsmp, left << 3);
} }
buf = calloc(1, xxs->len * 2); decbuf = (uint8 *) calloc(1, xxs->len * 2);
if (buf == NULL) if (decbuf == NULL)
return -1; return -1;
if (ish.flags & IT_SMP_16BIT) { if (ish.flags & IT_SMP_16BIT) {
itsex_decompress16(f, buf, xxs->len, itsex_decompress16(f, (int16 *)decbuf, xxs->len,
ish.convert & IT_CVT_DIFF); ish.convert & IT_CVT_DIFF);
#ifdef WORDS_BIGENDIAN #ifdef WORDS_BIGENDIAN
@ -873,44 +913,19 @@ static int load_it_sample(struct module_data *m, int i, int start,
cvt |= SAMPLE_FLAG_BIGEND; cvt |= SAMPLE_FLAG_BIGEND;
#endif #endif
} else { } else {
itsex_decompress8(f, buf, xxs->len, itsex_decompress8(f, (uint8 *)decbuf, xxs->len,
ish.convert & IT_CVT_DIFF); 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, ret = libxmp_load_sample(m, NULL, SAMPLE_FLAG_NOLOAD | cvt,
&mod->xxs[i], buf); &mod->xxs[i], decbuf);
if (ret < 0) { if (ret < 0) {
free(buf); free(decbuf);
return -1; return -1;
} }
free(buf); free(decbuf);
} else { } 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) if (libxmp_load_sample(m, f, cvt, &mod->xxs[i], NULL) < 0)
return -1; 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 mask[L_CHANNELS];
uint8 last_fxp[64]; uint8 last_fxp[64];
int r, c, pat_len; int r, c, pat_len, num_rows;
uint8 b; uint8 b;
r = 0; 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)); memset(&dummy, 0, sizeof(struct xmp_event));
pat_len = hio_read16l(f) /* - 4 */ ; 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) { if (libxmp_alloc_tracks_in_pattern(mod, i) < 0) {
return -1; 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);
hio_read16l(f); hio_read16l(f);
while (--pat_len >= 0) { while (r < num_rows && --pat_len >= 0) {
b = hio_read8(f); b = hio_read8(f);
if (hio_error(f)) { if (hio_error(f)) {
return -1; 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 * real number of channels before loading the patterns and
* we don't want to set it to 64 channels. * 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; event = &dummy;
} else { } else {
event = &EVENT(i, c, r); 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_smp; /* Pointers to samples */
uint32 *pp_pat; /* Pointers to patterns */ uint32 *pp_pat; /* Pointers to patterns */
int new_fx, sample_mode; int new_fx, sample_mode;
int pat_before_smp = 0;
int is_mpt_116 = 0;
LOAD_INIT(); 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); ifh.pwd = hio_read8(f);
/* Sanity check */ /* Sanity check */
if (ifh.gv > 0x80 || ifh.mv > 0x80) { if (ifh.gv > 0x80) {
D_(D_CRIT "invalid gv (%u) or mv (%u)", ifh.gv, ifh.mv); D_(D_CRIT "invalid gv (%u)", ifh.gv);
goto err; 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.chpan, 64, 1, f);
hio_read(ifh.chvol, 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)); memcpy(mod->name, ifh.name, sizeof(ifh.name));
/* sizeof(ifh.name) == 26, sizeof(mod->name) == 64. */ /* sizeof(ifh.name) == 26, sizeof(mod->name) == 64. */
mod->name[sizeof(ifh.name)] = '\0'; 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) { if (mod->ins) {
pp_ins = calloc(4, mod->ins); pp_ins = (uint32 *) calloc(4, mod->ins);
if (pp_ins == NULL) if (pp_ins == NULL)
goto err; goto err;
} else { } else {
pp_ins = NULL; pp_ins = NULL;
} }
pp_smp = calloc(4, mod->smp); pp_smp = (uint32 *) calloc(4, mod->smp);
if (pp_smp == NULL) if (pp_smp == NULL)
goto err2; goto err2;
pp_pat = calloc(4, mod->pat); pp_pat = (uint32 *) calloc(4, mod->pat);
if (pp_pat == NULL) if (pp_pat == NULL)
goto err3; 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++) for (i = 0; i < mod->pat; i++)
pp_pat[i] = hio_read32l(f); 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; m->c4rate = C4_NTSC_RATE;
identify_tracker(m, &ifh); identify_tracker(m, &ifh, pat_before_smp, &is_mpt_116);
MODULE_INFO(); 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) if (libxmp_init_instrument(m) < 0)
goto err4; 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); D_(D_INFO "Instruments: %d", mod->ins);
for (i = 0; i < mod->ins; i++) { 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; max_ch = 0;
for (i = 0; i < mod->pat; i++) { for (i = 0; i < mod->pat; i++) {
uint8 mask[L_CHANNELS]; 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 the offset to a pattern is 0, the pattern is empty */
if (pp_pat[i] == 0) 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); hio_seek(f, start + pp_pat[i], SEEK_SET);
pat_len = hio_read16l(f) /* - 4 */ ; pat_len = hio_read16l(f) /* - 4 */ ;
hio_read16l(f); num_rows = hio_read16l(f);
memset(mask, 0, L_CHANNELS); memset(mask, 0, L_CHANNELS);
hio_read16l(f); hio_read16l(f);
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); int b = hio_read8(f);
if (hio_error(f)) { if (hio_error(f)) {
D_(D_CRIT "error scanning pattern %d", i); D_(D_CRIT "error scanning pattern %d", i);
goto err4; goto err4;
} }
if (b == 0) if (b == 0) {
row++;
continue; continue;
}
c = (b - 1) & 63; 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); free(pp_ins);
/* Song message */ /* Song message */
if (ifh.special & IT_HAS_MSG) { 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); hio_seek(f, start + ifh.msgofs, SEEK_SET);
D_(D_INFO "Message length : %d", ifh.msglen); 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->gvolbase = 0x80;
m->gvol = ifh.gv; m->gvol = ifh.gv;
m->mvolbase = 48;
m->mvol = ifh.mv;
m->read_event_type = READ_EVENT_IT; m->read_event_type = READ_EVENT_IT;
#ifndef LIBXMP_CORE_PLAYER
if (is_mpt_116)
libxmp_apply_mpt_preamp(m);
#endif
return 0; return 0;
err4: err4:

View file

@ -3,16 +3,22 @@
/* Public domain IT sample decompressor by Olivier Lapicque */ /* Public domain IT sample decompressor by Olivier Lapicque */
#include "loader.h" #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; uint32 retval = 0;
int i = n; int i = n;
int bnum = *bitnum, bbuf = *bitbuf; int bnum = *bitnum;
uint32 bbuf = *bitbuf;
if (n > 0) { if (n > 0 && n <= 32) {
do { do {
if (bnum == 0) { if (bnum == 0) {
if (hio_eof(ibuf)) {
*err = EOF;
return 0;
}
bbuf = hio_read8(ibuf); bbuf = hio_read8(ibuf);
bnum = 8; bnum = 8;
} }
@ -27,6 +33,10 @@ static inline uint32 read_bits(HIO_HANDLE *ibuf, uint32 *bitbuf, int *bitnum, in
*bitnum = bnum; *bitnum = bnum;
*bitbuf = bbuf; *bitbuf = bbuf;
} else {
/* Invalid shift value. */
*err = -2;
return 0;
} }
return (retval >> (32 - i)); return (retval >> (32 - i));
@ -41,6 +51,7 @@ int itsex_decompress8(HIO_HANDLE *src, uint8 *dst, int len, int it215)
int bitnum = 0; int bitnum = 0;
uint8 left = 0, temp = 0, temp2 = 0; uint8 left = 0, temp = 0, temp2 = 0;
uint32 d, pos; uint32 d, pos;
int err = 0;
while (len) { while (len) {
if (!block_count) { if (!block_count) {
@ -58,8 +69,8 @@ int itsex_decompress8(HIO_HANDLE *src, uint8 *dst, int len, int it215)
/* Unpacking */ /* Unpacking */
pos = 0; pos = 0;
do { do {
uint16 bits = read_bits(src, &bitbuf, &bitnum, left); uint16 bits = read_bits(src, &bitbuf, &bitnum, left, &err);
if (hio_eof(src)) if (err != 0)
return -1; return -1;
if (left < 7) { if (left < 7) {
@ -67,9 +78,9 @@ int itsex_decompress8(HIO_HANDLE *src, uint8 *dst, int len, int it215)
uint32 j = bits & 0xffff; uint32 j = bits & 0xffff;
if (i != j) if (i != j)
goto unpack_byte; goto unpack_byte;
bits = (read_bits(src, &bitbuf, &bitnum, 3) bits = (read_bits(src, &bitbuf, &bitnum, 3, &err)
+ 1) & 0xff; + 1) & 0xff;
if (hio_eof(src)) if (err != 0)
return -1; return -1;
left = ((uint8)bits < left) ? (uint8)bits : 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; uint8 left = 0;
int16 temp = 0, temp2 = 0; int16 temp = 0, temp2 = 0;
uint32 d, pos; uint32 d, pos;
int err = 0;
while (len) { while (len) {
if (!block_count) { if (!block_count) {
@ -154,8 +166,8 @@ int itsex_decompress16(HIO_HANDLE *src, int16 *dst, int len, int it215)
/* Unpacking */ /* Unpacking */
pos = 0; pos = 0;
do { do {
uint32 bits = read_bits(src, &bitbuf, &bitnum, left); uint32 bits = read_bits(src, &bitbuf, &bitnum, left, &err);
if (hio_eof(src)) if (err != 0)
return -1; return -1;
if (left < 7) { if (left < 7) {
@ -165,9 +177,8 @@ int itsex_decompress16(HIO_HANDLE *src, int16 *dst, int len, int it215)
if (i != j) if (i != j)
goto unpack_byte; goto unpack_byte;
bits = read_bits(src, &bitbuf, &bitnum, 4) + 1; bits = read_bits(src, &bitbuf, &bitnum, 4, &err) + 1;
if (err != 0)
if (hio_eof(src))
return -1; return -1;
left = ((uint8)(bits & 0xff) < left) ? left = ((uint8)(bits & 0xff) < left) ?

View file

@ -1,12 +1,7 @@
#ifndef LIBXMP_LIST_H #ifndef LIBXMP_LIST_H
#define LIBXMP_LIST_H #define LIBXMP_LIST_H
#ifdef _MSC_VER #include <stddef.h> /* offsetof */
#define __inline__ __inline
#endif
#ifdef __WATCOMC__
#define __inline__ inline
#endif
/* /*
* Simple doubly linked list implementation. * Simple doubly linked list implementation.
@ -32,12 +27,12 @@ struct list_head {
} while (0) } 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 * This is only for internal list manipulation where we know
* the prev/next entries already! * 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 * prev,
struct list_head * next) 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. * Insert a new entry after the specified head.
* This is good for implementing stacks. * 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); __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. * Insert a new entry before the specified head.
* This is useful for implementing queues. * 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); __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 * This is only for internal list manipulation where we know
* the prev/next entries already! * 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) struct list_head * next)
{ {
next->prev = prev; next->prev = prev;
@ -91,7 +86,7 @@ static __inline__ void __list_del(struct list_head * prev,
* list_del - deletes entry from list. * list_del - deletes entry from list.
* @entry: the element to delete from the 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); __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 * list_empty - tests whether a list is empty
* @head: the list to test. * @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; return head->next == head;
} }
@ -110,7 +105,7 @@ static __inline__ int list_empty(struct list_head *head)
* @list: the new list to add. * @list: the new list to add.
* @head: the place to add it in the first list. * @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; 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. * @member: the name of the list_struct within the struct.
*/ */
#define list_entry(ptr, type, member) \ #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 * 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) \ #define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next) for (pos = (head)->next; pos != (head); pos = pos->next)
#endif #endif /* LIBXMP_LIST_H */

View file

@ -1,5 +1,5 @@
/* Extended Module Player /* 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 * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
@ -20,16 +20,15 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include <sys/stat.h>
#include <errno.h> #include <errno.h>
#include "format.h" #include "format.h"
#include "list.h" #include "list.h"
#include "hio.h" #include "hio.h"
#include "tempfile.h"
#include "loader.h" #include "loader.h"
#ifndef LIBXMP_NO_DEPACKERS #ifndef LIBXMP_NO_DEPACKERS
#include "tempfile.h"
#include "depackers/depacker.h" #include "depackers/depacker.h"
#endif #endif
@ -39,17 +38,13 @@
#endif #endif
extern struct format_loader *format_loaders[];
void libxmp_load_prologue(struct context_data *); void libxmp_load_prologue(struct context_data *);
void libxmp_load_epilogue(struct context_data *); void libxmp_load_epilogue(struct context_data *);
int libxmp_prepare_scan(struct context_data *); int libxmp_prepare_scan(struct context_data *);
#ifndef LIBXMP_CORE_PLAYER #ifndef LIBXMP_CORE_PLAYER
#define BUFLEN 16384 #define BUFLEN 16384
#endif
#ifndef LIBXMP_CORE_PLAYER
static void set_md5sum(HIO_HANDLE *f, unsigned char *digest) static void set_md5sum(HIO_HANDLE *f, unsigned char *digest)
{ {
unsigned char buf[BUFLEN]; 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) static char *get_dirname(const char *name)
{ {
char *dirname; char *dirname;
const char *div; const char *p;
ptrdiff_t len; ptrdiff_t len;
if ((div = strrchr(name, '/')) != NULL) { if ((p = strrchr(name, '/')) != NULL) {
len = div - name + 1; len = p - name + 1;
dirname = malloc(len + 1); dirname = (char *) malloc(len + 1);
if (dirname != NULL) { if (dirname != NULL) {
memcpy(dirname, name, len); memcpy(dirname, name, len);
dirname[len] = 0; dirname[len] = 0;
} }
} else { } else {
dirname = strdup(""); dirname = libxmp_strdup("");
} }
return dirname; return dirname;
@ -87,13 +82,13 @@ static char *get_dirname(const char *name)
static char *get_basename(const char *name) static char *get_basename(const char *name)
{ {
const char *div; const char *p;
char *basename; char *basename;
if ((div = strrchr(name, '/')) != NULL) { if ((p = strrchr(name, '/')) != NULL) {
basename = strdup(div + 1); basename = libxmp_strdup(p + 1);
} else { } else {
basename = strdup(name); basename = libxmp_strdup(name);
} }
return basename; 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) { if (format_loaders[i]->test(h, buf, 0) == 0) {
int is_prowizard = 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) { if (strcmp(format_loaders[i]->name, "prowizard") == 0) {
hio_seek(h, 0, SEEK_SET); hio_seek(h, 0, SEEK_SET);
pw_test_format(h, buf, 0, info); 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) int xmp_test_module(const char *path, struct xmp_test_info *info)
{ {
HIO_HANDLE *h; HIO_HANDLE *h;
struct stat st;
int ret;
#ifndef LIBXMP_NO_DEPACKERS #ifndef LIBXMP_NO_DEPACKERS
char *temp = NULL; char *temp = NULL;
#endif #endif
int ret;
if (stat(path, &st) < 0) ret = libxmp_get_filetype(path);
if (ret == XMP_FILETYPE_NONE) {
return -XMP_ERROR_SYSTEM; return -XMP_ERROR_SYSTEM;
}
if (S_ISDIR(st.st_mode)) { if (ret & XMP_FILETYPE_DIR) {
errno = EISDIR; errno = EISDIR;
return -XMP_ERROR_SYSTEM; return -XMP_ERROR_SYSTEM;
} }
@ -158,16 +154,10 @@ int xmp_test_module(const char *path, struct xmp_test_info *info)
return -XMP_ERROR_SYSTEM; return -XMP_ERROR_SYSTEM;
#ifndef LIBXMP_NO_DEPACKERS #ifndef LIBXMP_NO_DEPACKERS
if (libxmp_decrunch(&h, path, &temp) < 0) { if (libxmp_decrunch(h, path, &temp) < 0) {
ret = -XMP_ERROR_DEPACK; ret = -XMP_ERROR_DEPACK;
goto err; goto err;
} }
/* get size after decrunch */
if (hio_size(h) < 256) { /* set minimum valid module size */
ret = -XMP_ERROR_FORMAT;
goto err;
}
#endif #endif
ret = test_module(info, h); 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; 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; return -XMP_ERROR_SYSTEM;
ret = test_module(info, h); 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; return -XMP_ERROR_SYSTEM;
#ifndef LIBXMP_NO_DEPACKERS #ifndef LIBXMP_NO_DEPACKERS
if (libxmp_decrunch(&h, NULL, &temp) < 0) { if (libxmp_decrunch(h, NULL, &temp) < 0) {
ret = -XMP_ERROR_DEPACK; ret = -XMP_ERROR_DEPACK;
goto err; goto err;
} }
/* get size after decrunch */
if (hio_size(h) < 256) { /* set minimum valid module size */
ret = -XMP_ERROR_FORMAT;
goto err;
}
#endif #endif
ret = test_module(info, h); ret = test_module(info, h);
@ -265,7 +249,6 @@ static int load_module(xmp_context opaque, HIO_HANDLE *h)
test_result = load_result = -1; test_result = load_result = -1;
for (i = 0; format_loaders[i] != NULL; i++) { for (i = 0; format_loaders[i] != NULL; i++) {
hio_seek(h, 0, SEEK_SET); hio_seek(h, 0, SEEK_SET);
hio_error(h); /* reset error flag */
D_(D_WARN "test %s", format_loaders[i]->name); D_(D_WARN "test %s", format_loaders[i]->name);
test_result = format_loaders[i]->test(h, NULL, 0); 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) { if (test_result < 0) {
xmp_release_module(opaque); xmp_release_module(opaque);
return -XMP_ERROR_FORMAT; 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); 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); libxmp_load_epilogue(ctx);
ret = libxmp_prepare_scan(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; struct context_data *ctx = (struct context_data *)opaque;
#ifndef LIBXMP_CORE_PLAYER #ifndef LIBXMP_CORE_PLAYER
struct module_data *m = &ctx->m; struct module_data *m = &ctx->m;
long size;
#endif #endif
#ifndef LIBXMP_NO_DEPACKERS #ifndef LIBXMP_NO_DEPACKERS
char *temp_name; char *temp_name;
#endif #endif
HIO_HANDLE *h; HIO_HANDLE *h;
struct stat st;
int ret; int ret;
D_(D_WARN "path = %s", path); 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; return -XMP_ERROR_SYSTEM;
} }
if (ret & XMP_FILETYPE_DIR) {
if (S_ISDIR(st.st_mode)) {
errno = EISDIR; errno = EISDIR;
return -XMP_ERROR_SYSTEM; return -XMP_ERROR_SYSTEM;
} }
@ -384,20 +366,12 @@ int xmp_load_module(xmp_context opaque, const char *path)
#ifndef LIBXMP_NO_DEPACKERS #ifndef LIBXMP_NO_DEPACKERS
D_(D_INFO "decrunch"); D_(D_INFO "decrunch");
if (libxmp_decrunch(&h, path, &temp_name) < 0) { if (libxmp_decrunch(h, path, &temp_name) < 0) {
ret = -XMP_ERROR_DEPACK; ret = -XMP_ERROR_DEPACK;
goto err; goto err;
} }
#endif #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) if (ctx->state > XMP_STATE_UNLOADED)
xmp_release_module(opaque); 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->filename = path; /* For ALM, SSMT, etc */
m->size = size; m->size = hio_size(h);
#else #else
ctx->m.filename = NULL; ctx->m.filename = NULL;
ctx->m.dirname = 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; 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; return -XMP_ERROR_SYSTEM;
if (ctx->state > XMP_STATE_UNLOADED) if (ctx->state > XMP_STATE_UNLOADED)
@ -579,17 +553,9 @@ void xmp_release_module(xmp_context opaque)
} }
free(m->xtra); free(m->xtra);
free(m->midi);
m->xtra = NULL; m->xtra = NULL;
m->midi = 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
libxmp_free_scan(ctx); libxmp_free_scan(ctx);

View file

@ -1,5 +1,5 @@
/* Extended Module Player /* 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 * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * 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 XMP_FLAGS_FIXLOOP, XMP_MODE_AUTO
}, },
#if 0
/* "siedler ii" (added by Daniel Åkerud) */ /* "siedler ii" (added by Daniel Åkerud) */
/* Timing fixed by vblank scan compare. CIA: 32m10s VBlank: 12m32s */
{ {
{ 0x70, 0xaa, 0x03, 0x4d, 0xfb, 0x2f, 0x1f, 0x73, { 0x70, 0xaa, 0x03, 0x4d, 0xfb, 0x2f, 0x1f, 0x73,
0xd9, 0xfd, 0xba, 0xfe, 0x13, 0x1b, 0xb7, 0x01 }, 0xd9, 0xfd, 0xba, 0xfe, 0x13, 0x1b, 0xb7, 0x01 },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO XMP_FLAGS_VBLANK, XMP_MODE_AUTO
}, },
#endif
/* "Klisje paa klisje" (added by Kjetil Torgrim Homme) */ /* "Klisje paa klisje" (added by Kjetil Torgrim Homme) */
{ {
@ -99,6 +102,126 @@ const struct module_quirk mq[] = {
0, XMP_MODE_PROTRACKER 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,
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; int i;
for (i = 0; i < strlen(s); 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] = ' '; s[i] = ' ';
} }
@ -150,9 +273,21 @@ static void check_envelope(struct xmp_envelope *env)
env->flg &= ~XMP_ENVELOPE_LOOP; env->flg &= ~XMP_ENVELOPE_LOOP;
} }
/* Disable envelope loop if invalid sustain */ /* Disable envelope sustain if invalid sustain */
if (env->sus >= env->npt) { if (env->sus >= env->npt || env->sue >= env->npt) {
env->flg &= ~XMP_ENVELOPE_ON; 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->quirk = 0;
m->read_event_type = READ_EVENT_MOD; m->read_event_type = READ_EVENT_MOD;
m->period_type = PERIOD_AMIGA; m->period_type = PERIOD_AMIGA;
m->compare_vblank = 0;
m->comment = NULL; m->comment = NULL;
m->scan_cnt = NULL; m->scan_cnt = NULL;
m->midi = NULL;
/* Set defaults */ /* Set defaults */
m->mod.pat = 0; m->mod.pat = 0;
m->mod.trk = 0; m->mod.trk = 0;
m->mod.chn = 4; m->mod.chn = 4;
m->mod.ins = 0; m->mod.ins = 0;
m->mod.smp = 0; m->mod.smp = 0;
m->mod.spd = 6; m->mod.spd = 6;
m->mod.bpm = 125; m->mod.bpm = 125;
m->mod.len = 0; m->mod.len = 0;
m->mod.rst = 0; m->mod.rst = 0;
#ifndef LIBXMP_CORE_PLAYER #ifndef LIBXMP_CORE_PLAYER
m->extra = NULL; m->extra = NULL;
#endif #endif
#ifndef LIBXMP_CORE_DISABLE_IT
m->xsmp = NULL;
#endif
m->time_factor = DEFAULT_TIME_FACTOR; m->time_factor = DEFAULT_TIME_FACTOR;
@ -209,7 +343,7 @@ void libxmp_load_epilogue(struct context_data *ctx)
struct xmp_module *mod = &m->mod; struct xmp_module *mod = &m->mod;
int i, j; int i, j;
mod->gvl = m->gvol; mod->gvl = m->gvol;
/* Sanity check for module parameters */ /* Sanity check for module parameters */
CLAMP(mod->len, 0, XMP_MAX_MOD_LENGTH); 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) { if (mod->spd <= 0 || mod->spd > 255) {
mod->spd = 6; 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 /* Set appropriate values for instrument volumes and subinstrument
* global volumes when QUIRK_INSVOL is not set, to keep volume values * 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].aei);
check_envelope(&mod->xxi[i].fei); check_envelope(&mod->xxi[i].fei);
check_envelope(&mod->xxi[i].pei); 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->filter = 0;
p->mode = XMP_MODE_AUTO; p->mode = XMP_MODE_AUTO;
p->flags = p->player_flags; p->flags = p->player_flags;
@ -283,7 +437,7 @@ int libxmp_prepare_scan(struct context_data *ctx)
return 0; return 0;
} }
m->scan_cnt = calloc(sizeof (uint8 *), mod->len); m->scan_cnt = (uint8 **) calloc(mod->len, sizeof(uint8 *));
if (m->scan_cnt == NULL) if (m->scan_cnt == NULL)
return -XMP_ERROR_SYSTEM; 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]; 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) if (m->scan_cnt[i] == NULL)
return -XMP_ERROR_SYSTEM; return -XMP_ERROR_SYSTEM;
} }
@ -309,6 +463,7 @@ int libxmp_prepare_scan(struct context_data *ctx)
void libxmp_free_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 module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod; struct xmp_module *mod = &m->mod;
int i; int i;
@ -320,6 +475,9 @@ void libxmp_free_scan(struct context_data *ctx)
free(m->scan_cnt); free(m->scan_cnt);
m->scan_cnt = NULL; m->scan_cnt = NULL;
} }
free(p->scan);
p->scan = NULL;
} }
/* Process player personality flags */ /* Process player personality flags */
@ -394,6 +552,9 @@ int libxmp_set_player_mode(struct context_data *ctx)
return -1; return -1;
} }
if (p->mode != XMP_MODE_AUTO)
m->compare_vblank = 0;
return 0; return 0;
} }

View file

@ -20,6 +20,10 @@
#define SAMPLE_FLAG_HSC 0x2000 /* HSC Adlib synth instrument */ #define SAMPLE_FLAG_HSC 0x2000 /* HSC Adlib synth instrument */
#define SAMPLE_FLAG_ADPCM 0x4000 /* ADPCM4 encoded samples */ #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) #define DEFPAN(x) (0x80 + ((x) - 0x80) * m->defpan / 100)
int libxmp_init_instrument (struct module_data *); 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_track (struct xmp_module *, int, int);
int libxmp_alloc_tracks_in_pattern (struct xmp_module *, int); int libxmp_alloc_tracks_in_pattern (struct xmp_module *, int);
int libxmp_alloc_pattern_tracks (struct xmp_module *, int, 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); int libxmp_alloc_pattern_tracks_long(struct xmp_module *, int, int);
#endif
char *libxmp_instrument_name (struct xmp_module *, int, uint8 *, int); char *libxmp_instrument_name (struct xmp_module *, int, uint8 *, int);
char *libxmp_copy_adjust (char *, uint8 *, int); char *libxmp_copy_adjust (char *, uint8 *, int);
int libxmp_copy_name_for_fopen (char *, const char *, 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_read_title (HIO_HANDLE *, char *, int);
void libxmp_set_xxh_defaults (struct xmp_module *); void libxmp_set_xxh_defaults (struct xmp_module *);
void libxmp_decode_protracker_event (struct xmp_event *, uint8 *); void libxmp_decode_protracker_event (struct xmp_event *, const uint8 *);
void libxmp_decode_noisetracker_event(struct xmp_event *, uint8 *); void libxmp_decode_noisetracker_event(struct xmp_event *, const uint8 *);
void libxmp_disable_continue_fx (struct xmp_event *); void libxmp_disable_continue_fx (struct xmp_event *);
int libxmp_check_filename_case (const char *, const char *, char *, int); int libxmp_check_filename_case (const char *, const char *, char *, int);
void libxmp_get_instrument_path (struct module_data *, 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, int libxmp_load_sample (struct module_data *, HIO_HANDLE *, int,
struct xmp_sample *, const void *); struct xmp_sample *, const void *);
void libxmp_free_sample (struct xmp_sample *); void libxmp_free_sample (struct xmp_sample *);
#ifndef LIBXMP_CORE_PLAYER
void libxmp_schism_tracker_string (char *, size_t, int, int); 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 uint8 libxmp_ord_xlat[];
extern const int libxmp_arch_vol_table[]; extern const int libxmp_arch_vol_table[];

View file

@ -25,15 +25,13 @@ typedef struct MD5Context {
uint8 buffer[MD5_BLOCK_LENGTH]; /* input buffer */ uint8 buffer[MD5_BLOCK_LENGTH]; /* input buffer */
} MD5_CTX; } MD5_CTX;
#ifdef __cplusplus LIBXMP_BEGIN_DECLS
extern "C" {
#endif
void MD5Init(MD5_CTX *); void MD5Init(MD5_CTX *);
void MD5Update(MD5_CTX *, const unsigned char *, size_t); void MD5Update(MD5_CTX *, const unsigned char *, size_t);
void MD5Final(uint8[MD5_DIGEST_LENGTH], MD5_CTX *); void MD5Final(uint8[MD5_DIGEST_LENGTH], MD5_CTX *);
#ifdef __cplusplus
} LIBXMP_END_DECLS
#endif
#endif /* LIBXMP_MD5_H */ #endif /* LIBXMP_MD5_H */

View file

@ -2,7 +2,6 @@
#define LIBXMP_MDATAIO_H #define LIBXMP_MDATAIO_H
#include <stddef.h> #include <stddef.h>
#include <limits.h>
#include "common.h" #include "common.h"
static inline ptrdiff_t CAN_READ(MFILE *m) static inline ptrdiff_t CAN_READ(MFILE *m)
@ -43,7 +42,7 @@ static inline uint16 mread16l(MFILE *m, int *err)
} else { } else {
m->pos += can_read; m->pos += can_read;
if(err) *err = EOF; if(err) *err = EOF;
return EOF; return 0xffff;
} }
} }
@ -58,7 +57,7 @@ static inline uint16 mread16b(MFILE *m, int *err)
} else { } else {
m->pos += can_read; m->pos += can_read;
if(err) *err = EOF; if(err) *err = EOF;
return EOF; return 0xffff;
} }
} }
@ -73,7 +72,7 @@ static inline uint32 mread24l(MFILE *m, int *err)
} else { } else {
m->pos += can_read; m->pos += can_read;
if(err) *err = EOF; if(err) *err = EOF;
return EOF; return 0xffffffff;
} }
} }
@ -88,7 +87,7 @@ static inline uint32 mread24b(MFILE *m, int *err)
} else { } else {
m->pos += can_read; m->pos += can_read;
if(err) *err = EOF; if(err) *err = EOF;
return EOF; return 0xffffffff;
} }
} }
@ -103,7 +102,7 @@ static inline uint32 mread32l(MFILE *m, int *err)
} else { } else {
m->pos += can_read; m->pos += can_read;
if(err) *err = EOF; if(err) *err = EOF;
return EOF; return 0xffffffff;
} }
} }
@ -118,7 +117,7 @@ static inline uint32 mread32b(MFILE *m, int *err)
} else { } else {
m->pos += can_read; m->pos += can_read;
if(err) *err = EOF; if(err) *err = EOF;
return EOF; return 0xffffffff;
} }
} }

View file

@ -46,13 +46,16 @@ size_t mread(void *buf, size_t size, size_t num, MFILE *m)
} }
if (should_read > can_read) { 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; 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; MFILE *m;
m = (MFILE *)malloc(sizeof (MFILE)); m = (MFILE *) malloc(sizeof(MFILE));
if (m == NULL) if (m == NULL)
return NULL; return NULL;
m->start = ptr; m->start = (const unsigned char *)ptr;
m->pos = 0; m->pos = 0;
m->size = size; m->size = size;
m->free_after_use = free_after_use;
return m; return m;
} }
int mclose(MFILE *m) int mclose(MFILE *m)
{ {
if (m->free_after_use)
free((void *)m->start);
free(m); free(m);
return 0; return 0;
} }

View file

@ -2,18 +2,18 @@
#define LIBXMP_MEMIO_H #define LIBXMP_MEMIO_H
#include <stddef.h> #include <stddef.h>
#include "common.h"
typedef struct { typedef struct {
const unsigned char *start; const unsigned char *start;
ptrdiff_t pos; ptrdiff_t pos;
ptrdiff_t size; ptrdiff_t size;
int free_after_use;
} MFILE; } MFILE;
#ifdef __cplusplus LIBXMP_BEGIN_DECLS
extern "C" {
#endif
MFILE *mopen(const void *, long); MFILE *mopen(const void *, long, int);
int mgetc(MFILE *stream); int mgetc(MFILE *stream);
size_t mread(void *, size_t, size_t, MFILE *); size_t mread(void *, size_t, size_t, MFILE *);
int mseek(MFILE *, long, int); int mseek(MFILE *, long, int);
@ -21,8 +21,6 @@ long mtell(MFILE *);
int mclose(MFILE *); int mclose(MFILE *);
int meof(MFILE *); int meof(MFILE *);
#ifdef __cplusplus LIBXMP_END_DECLS
}
#endif
#endif #endif

View file

@ -1,5 +1,5 @@
/* Extended Module Player /* 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 * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
@ -99,10 +99,15 @@
*(buffer++) += smp_in * (old_vl >> 8); old_vl += delta_l; \ *(buffer++) += smp_in * (old_vl >> 8); old_vl += delta_l; \
} while (0) } 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 { \ #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; \ fl2 = fl1; fl1 = sl; \
*(buffer++) += sl; \ *(buffer++) += sl * vl; \
} while (0) } while (0)
#define MIX_MONO_FILTER_AC() do { \ #define MIX_MONO_FILTER_AC() do { \
@ -122,12 +127,14 @@
} while (0) } while (0)
#define MIX_STEREO_FILTER() do { \ #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; \ 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; \ fl2 = fl1; fl1 = sl; \
*(buffer++) += sr; \ *(buffer++) += sr * vr; \
*(buffer++) += sl; \ *(buffer++) += sl * vl; \
} while (0) } while (0)
#define MIX_STEREO_FILTER_AC() do { \ #define MIX_STEREO_FILTER_AC() do { \
@ -138,17 +145,19 @@
old_vl += delta_l; \ old_vl += delta_l; \
} while (0) } while (0)
#define MIX_STEREO_FILTER_AC() do { \ /* For "nearest" to be nearest neighbor (instead of floor), the position needs
int vr = old_vr >> 8; \ * to be rounded. This only needs to be done once at the start of mixing, and
int vl = old_vl >> 8; \ * is required for reverse samples to round the same as forward samples.
MIX_STEREO_FILTER(); \ */
old_vr += delta_r; \ #define NEAREST_ROUND() do { \
old_vl += delta_l; \ frac += (1 << (SMIX_SHIFT - 1)); \
pos += frac >> SMIX_SHIFT; \
frac &= SMIX_MASK; \
} while (0) } while (0)
#define VAR_NORM(x) \ #define VAR_NORM(x) \
register int smp_in; \ register int smp_in; \
x *sptr = vi->sptr; \ x *sptr = (x *)vi->sptr; \
unsigned int pos = vi->pos; \ unsigned int pos = vi->pos; \
int frac = (1 << SMIX_SHIFT) * (vi->pos - (int)vi->pos) int frac = (1 << SMIX_SHIFT) * (vi->pos - (int)vi->pos)
@ -194,6 +203,10 @@
#endif #endif
#ifdef _MSC_VER
#pragma warning(disable:4457) /* shadowing */
#endif
/* /*
* Nearest neighbor mixers * Nearest neighbor mixers
@ -204,6 +217,7 @@
MIXER(mono_8bit_nearest) MIXER(mono_8bit_nearest)
{ {
VAR_NORM(int8); VAR_NORM(int8);
NEAREST_ROUND();
LOOP { NEAREST_NEIGHBOR(); MIX_MONO(); UPDATE_POS(); } LOOP { NEAREST_NEIGHBOR(); MIX_MONO(); UPDATE_POS(); }
} }
@ -214,6 +228,7 @@ MIXER(mono_8bit_nearest)
MIXER(mono_16bit_nearest) MIXER(mono_16bit_nearest)
{ {
VAR_NORM(int16); VAR_NORM(int16);
NEAREST_ROUND();
LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_MONO(); UPDATE_POS(); } LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_MONO(); UPDATE_POS(); }
} }
@ -223,6 +238,7 @@ MIXER(mono_16bit_nearest)
MIXER(stereo_8bit_nearest) MIXER(stereo_8bit_nearest)
{ {
VAR_NORM(int8); VAR_NORM(int8);
NEAREST_ROUND();
LOOP { NEAREST_NEIGHBOR(); MIX_STEREO(); UPDATE_POS(); } LOOP { NEAREST_NEIGHBOR(); MIX_STEREO(); UPDATE_POS(); }
} }
@ -232,6 +248,7 @@ MIXER(stereo_8bit_nearest)
MIXER(stereo_16bit_nearest) MIXER(stereo_16bit_nearest)
{ {
VAR_NORM(int16); VAR_NORM(int16);
NEAREST_ROUND();
LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_STEREO(); UPDATE_POS(); } LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_STEREO(); UPDATE_POS(); }
} }

View file

@ -1,5 +1,5 @@
/* Extended Module Player /* 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 * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
@ -45,8 +45,24 @@
#define LIM16_HI 32767 #define LIM16_HI 32767
#define LIM16_LO -32768 #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 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_nearest);
MIX_FN(mono_8bit_linear); MIX_FN(mono_8bit_linear);
MIX_FN(mono_16bit_nearest); 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 player_data *p = &ctx->p;
struct mixer_data *s = &ctx->s; struct mixer_data *s = &ctx->s;
struct mixer_voice *vi = &p->virt.voice_array[voc]; 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 discharge = s->ticksize >> ANTICLICK_SHIFT;
int stepmul, stepval;
uint32 stepmul_sq;
smp_r = vi->sright; smp_r = vi->sright;
smp_l = vi->sleft; smp_l = vi->sleft;
@ -231,14 +249,23 @@ static void do_anticlick(struct context_data *ctx, int voc, int32 *buf, int coun
return; return;
} }
max_x2 = count * count; stepval = (1 << ANTICLICK_FPSHIFT) / count;
stepmul = stepval * count;
while (count--) { if (~s->format & XMP_FORMAT_MONO) {
if (~s->format & XMP_FORMAT_MONO) { while ((stepmul -= stepval) > 0) {
*buf++ += (count * (smp_r >> 10) / max_x2 * count) << 10; /* 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) { if (end) {
SET_NOTE(NOTE_SAMPLE_END); SET_NOTE(NOTE_SAMPLE_END);
vi->fidx &= ~FLAG_ACTIVE;
if (HAS_QUIRK(QUIRK_RSTCHN)) { if (HAS_QUIRK(QUIRK_RSTCHN)) {
libxmp_virt_resetvoice(ctx, voc, 0); 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)) { if ((xxs->flg & XMP_SAMPLE_LOOP_FULL) && (~vi->flags & SAMPLE_LOOP)) {
vi->end = xxs->len; vi->end = xxs->len;
} else { } else {
vi->end = xxs->lpe; vi->end = xxs->lpe;
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) vi->flags |= VOICE_BIDIR;
} }
} else { } else {
vi->start = 0;
vi->end = xxs->len; 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 int loop_changed = !(vi->flags & SAMPLE_LOOP);
struct module_data *m = &ctx->m;
#endif
int loop_size = xxs->lpe - xxs->lps;
/* Reposition for next loop */
vi->pos -= loop_size; /* forward loop */
vi->end = xxs->lpe;
vi->flags |= SAMPLE_LOOP; vi->flags |= SAMPLE_LOOP;
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) { if(loop_changed)
vi->end += loop_size; /* unrolled loop */ adjust_voice_end(ctx, vi, xxs, xtra);
vi->pos -= loop_size; /* forward loop */
#ifndef LIBXMP_CORE_DISABLE_IT if (~vi->flags & VOICE_BIDIR) {
/* OpenMPT Bidi-Loops.it: "In Impulse Trackers software mixer, /* Reposition for next loop */
* ping-pong loops are shortened by one sample. if (~vi->flags & VOICE_REVERSE)
*/ vi->pos -= vi->end - vi->start;
if (IS_PLAYER_MODE_IT()) { else
vi->end--; vi->pos += vi->end - vi->start;
vi->pos++; } 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; 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) { if (~s->format & XMP_FORMAT_MONO) {
bytelen *= 2; bytelen *= 2;
} }
@ -331,13 +482,14 @@ void libxmp_mixer_softmixer(struct context_data *ctx)
struct mixer_data *s = &ctx->s; struct mixer_data *s = &ctx->s;
struct module_data *m = &ctx->m; struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod; struct xmp_module *mod = &m->mod;
struct extra_sample_data *xtra;
struct xmp_sample *xxs; struct xmp_sample *xxs;
struct mixer_voice *vi; struct mixer_voice *vi;
double step; struct loop_data loop_data;
double step, step_dir;
int samples, size; 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 prev_l, prev_r = 0;
int lps, lpe;
int32 *buf_pos; int32 *buf_pos;
MIX_FP mix_fn; MIX_FP mix_fn;
MIX_FP *mixerset; MIX_FP *mixerset;
@ -368,6 +520,13 @@ void libxmp_mixer_softmixer(struct context_data *ctx)
} }
#endif #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); libxmp_mixer_prepare(ctx);
for (voc = 0; voc < p->virt.maxvoc; voc++) { for (voc = 0; voc < p->virt.maxvoc; voc++) {
@ -391,66 +550,56 @@ void libxmp_mixer_softmixer(struct context_data *ctx)
continue; 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; vi->pos0 = vi->pos;
buf_pos = s->buf32; 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) { if (vi->pan == PAN_SURROUND) {
vol_r = vi->vol * 0x80; vol_r = vol * 0x80;
vol_l = -vi->vol * 0x80; vol_l = -vol * 0x80;
} else { } else {
vol_r = vi->vol * (0x80 - vi->pan); vol_r = vol * (0x80 - vi->pan);
vol_l = vi->vol * (0x80 + vi->pan); vol_l = vol * (0x80 + vi->pan);
} }
if (vi->smp < mod->smp) { if (vi->smp < mod->smp) {
xxs = &mod->xxs[vi->smp]; xxs = &mod->xxs[vi->smp];
xtra = &m->xtra[vi->smp];
c5spd = m->xtra[vi->smp].c5spd; c5spd = m->xtra[vi->smp].c5spd;
} else { } else {
xxs = &ctx->smix.xxs[vi->smp - mod->smp]; xxs = &ctx->smix.xxs[vi->smp - mod->smp];
xtra = NULL;
c5spd = m->c4rate; c5spd = m->c4rate;
} }
step = C4_PERIOD * c5spd / s->freq / vi->period; 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; continue;
} }
#ifndef LIBXMP_CORE_DISABLE_IT adjust_voice_end(ctx, vi, xxs, xtra);
if (xxs->flg & XMP_SAMPLE_SLOOP && vi->smp < mod->smp) { init_sample_wraparound(s, &loop_data, vi, xxs);
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
}
rampsize = s->ticksize >> ANTICLICK_SHIFT; rampsize = s->ticksize >> ANTICLICK_SHIFT;
delta_l = (vol_l - vi->old_vl) / rampsize; delta_l = (vol_l - vi->old_vl) / rampsize;
delta_r = (vol_r - vi->old_vr) / rampsize; delta_r = (vol_r - vi->old_vr) / rampsize;
usmp = 0; for (size = usmp = s->ticksize; size > 0; ) {
for (size = s->ticksize; size > 0; ) {
int split_noloop = 0; int split_noloop = 0;
if (p->xc_data[vi->chn].split) { 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 /* How many samples we can write before the loop break
* or sample end... */ * or sample end... */
if (vi->pos >= vi->end) { if (~vi->flags & VOICE_REVERSE) {
samples = 0; if (vi->pos >= vi->end) {
usmp = 1; 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 { } else {
int s = ceil(((double)vi->end - vi->pos) / step); /* Reverse */
/* ...inside the tick boundaries */ if (vi->pos <= vi->start) {
if (s > size) { samples = 0;
s = size; if (--usmp <= 0)
} break;
} else {
samples = s; double c = ceil((vi->pos - (double)vi->start) / step);
if (samples > 0) { if (c > size) {
usmp = 0; c = size;
}
samples = c;
} }
step_dir = -step;
} }
if (vi->vol) { if (vi->vol) {
@ -496,7 +659,7 @@ void libxmp_mixer_softmixer(struct context_data *ctx)
#ifndef LIBXMP_CORE_DISABLE_IT #ifndef LIBXMP_CORE_DISABLE_IT
/* See OpenMPT env-flt-max.it */ /* See OpenMPT env-flt-max.it */
if (vi->filter.cutoff >= 0xfe && if (vi->filter.cutoff >= 0xfe &&
vi->filter.resonance == 0) { vi->filter.resonance == 0) {
mixer_id &= ~FLAG_FILTER; mixer_id &= ~FLAG_FILTER;
} }
#endif #endif
@ -521,7 +684,7 @@ void libxmp_mixer_softmixer(struct context_data *ctx)
if (mix_fn != NULL) { if (mix_fn != NULL) {
mix_fn(vi, buf_pos, samples, 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; 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 */ /* No more samples in this tick */
size -= samples + usmp; size -= samples;
if (size <= 0) { if (size <= 0) {
if (xxs->flg & XMP_SAMPLE_LOOP) { if (has_active_loop(ctx, vi, xxs)) {
if (vi->pos + step > vi->end) { /* This isn't particularly important for
vi->pos += step; * forward loops, but reverse loops need
loop_reposition(ctx, vi, xxs); * 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; continue;
} }
/* First sample loop run */ /* 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); do_anticlick(ctx, voc, buf_pos, size);
set_sample_end(ctx, voc, 1); set_sample_end(ctx, voc, 1);
size = 0; size = 0;
continue; 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_vl = vol_l;
vi->old_vr = vol_r; 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, downmix_int_8bit(s->buffer, s->buf32, size, s->amplify,
s->format & XMP_FORMAT_UNSIGNED ? 0x80 : 0); s->format & XMP_FORMAT_UNSIGNED ? 0x80 : 0);
} else { } 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); 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 module_data *m = &ctx->m;
struct mixer_voice *vi = &p->virt.voice_array[voc]; struct mixer_voice *vi = &p->virt.voice_array[voc];
struct xmp_sample *xxs; struct xmp_sample *xxs;
int lps; struct extra_sample_data *xtra;
if (vi->smp < m->mod.smp) { if (vi->smp < m->mod.smp) {
xxs = &m->mod.xxs[vi->smp]; xxs = &m->mod.xxs[vi->smp];
xtra = &m->xtra[vi->smp];
} else { } 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) { 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; vi->pos = pos;
adjust_voice_end(vi, xxs); adjust_voice_end(ctx, vi, xxs, xtra);
if (vi->pos >= vi->end) { if (vi->pos >= vi->end) {
if (xxs->flg & XMP_SAMPLE_LOOP) { vi->pos = vi->end;
vi->pos = xxs->lps; /* Restart forward sample loops. */
} else { if ((~vi->flags & VOICE_REVERSE) && has_active_loop(ctx, vi, xxs))
vi->pos = xxs->len; 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;
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
} }
if (ac) { if (ac) {
@ -650,12 +814,6 @@ double libxmp_mixer_getvoicepos(struct context_data *ctx, int voc)
return 0; 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; 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->smp = smp;
vi->vol = 0; vi->vol = 0;
vi->pan = 0; vi->pan = 0;
vi->flags &= ~SAMPLE_LOOP; vi->flags &= ~(SAMPLE_LOOP | VOICE_REVERSE | VOICE_BIDIR);
vi->fidx = 0; 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]; struct mixer_voice *vi = &p->virt.voice_array[voc];
if (rel) { 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; vi->flags |= VOICE_RELEASE;
} else { } else {
vi->flags &= ~VOICE_RELEASE; 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) void libxmp_mixer_seteffect(struct context_data *ctx, int voc, int type, int val)
{ {
#ifndef LIBXMP_CORE_DISABLE_IT #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->format = format;
s->amplify = DEFAULT_AMPLIFY; s->amplify = DEFAULT_AMPLIFY;
s->mix = DEFAULT_MIX; 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->interp = XMP_INTERP_LINEAR; /* default interpolation type */
s->dsp = XMP_DSP_LOWPASS; /* enable filters by default */ s->dsp = XMP_DSP_LOWPASS; /* enable filters by default */
/* s->numvoc = SMIX_NUMVOC; */ /* s->numvoc = SMIX_NUMVOC; */
s->dtright = s->dtleft = 0; s->dtright = s->dtleft = 0;
s->bidir_adjust = 0;
return 0; return 0;

View file

@ -30,15 +30,19 @@ struct mixer_voice {
int fidx; /* mixer function index */ int fidx; /* mixer function index */
int ins; /* instrument number */ int ins; /* instrument number */
int smp; /* sample number */ int smp; /* sample number */
int start; /* loop start */
int end; /* loop end */ int end; /* loop end */
int act; /* nna info & status of voice */ int act; /* nna info & status of voice */
int key; /* key for DCA note check */
int old_vl; /* previous volume, left channel */ int old_vl; /* previous volume, left channel */
int old_vr; /* previous volume, right channel */ int old_vr; /* previous volume, right channel */
int sleft; /* last left sample output, in 32bit */ int sleft; /* last left sample output, in 32bit */
int sright; /* last right sample output, in 32bit */ int sright; /* last right sample output, in 32bit */
#define VOICE_RELEASE (1 << 0) #define VOICE_RELEASE (1 << 0)
#define ANTICLICK (1 << 1) #define ANTICLICK (1 << 1)
#define SAMPLE_LOOP (1 << 2) #define SAMPLE_LOOP (1 << 2)
#define VOICE_REVERSE (1 << 3)
#define VOICE_BIDIR (1 << 4)
int flags; /* flags */ int flags; /* flags */
void *sptr; /* sample pointer */ void *sptr; /* sample pointer */
#ifdef LIBXMP_PAULA_SIMULATOR #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_setnote (struct context_data *, int, int);
void libxmp_mixer_setperiod (struct context_data *, int, double); void libxmp_mixer_setperiod (struct context_data *, int, double);
void libxmp_mixer_release (struct context_data *, int, int); void libxmp_mixer_release (struct context_data *, int, int);
void libxmp_mixer_reverse (struct context_data *, int, int);
#endif /* LIBXMP_MIXER_H */ #endif /* LIBXMP_MIXER_H */

View file

@ -20,6 +20,9 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#ifndef LIBXMP_LOADERS_MOD_H
#define LIBXMP_LOADERS_MOD_H
struct mod_instrument { struct mod_instrument {
uint8 name[22]; /* Instrument name */ uint8 name[22]; /* Instrument name */
uint16 size; /* Sample length in 16-bit words */ uint16 size; /* Sample length in 16-bit words */
@ -41,7 +44,6 @@ struct mod_header {
uint8 magic[4]; uint8 magic[4];
}; };
#ifndef LIBXMP_CORE_PLAYER #ifndef LIBXMP_CORE_PLAYER
/* Soundtracker 15-instrument module header */ /* Soundtracker 15-instrument module header */
@ -53,3 +55,5 @@ struct st_header {
uint8 order[128]; uint8 order[128];
}; };
#endif #endif
#endif /* LIBXMP_LOADERS_MOD_H */

File diff suppressed because it is too large Load diff

View file

@ -135,14 +135,11 @@ static uint16 pt_period_table[16][36] = {
#ifndef M_LN2 #ifndef M_LN2
#define M_LN2 0.69314718055994530942 #define M_LN2 0.69314718055994530942
#endif #endif
#if !defined(HAVE_ROUND) || defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DJGPP__)
static inline double libxmp_round(double val) static inline double libxmp_round(double val)
{ {
return (val >= 0.0)? floor(val + 0.5) : ceil(val - 0.5); return (val >= 0.0)? floor(val + 0.5) : ceil(val - 0.5);
} }
#else
#define libxmp_round round
#endif
#ifdef LIBXMP_PAULA_SIMULATOR #ifdef LIBXMP_PAULA_SIMULATOR
/* Get period from note using Protracker tuning */ /* 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) { switch (m->period_type) {
case PERIOD_LINEAR: case PERIOD_LINEAR:
per = (240.0 - d) * 16; /* Linear */ per = (240.0 - d) * 16; /* Linear */
break; break;
case PERIOD_CSPD: 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; break;
default: default:
per = PERIOD_BASE / pow(2, d / 12); /* Amiga */ per = PERIOD_BASE / pow(2, d / 12); /* Amiga */
} }
#ifndef LIBXMP_CORE_PLAYER #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; struct module_data *m = &ctx->m;
double d; double d;
if (n == 0) { if (n == 0 || p < 0.1) {
return 0; return 0;
} }
@ -255,7 +252,7 @@ void libxmp_c2spd_to_note(int c2spd, int *n, int *f)
{ {
int c; int c;
if (c2spd == 0) { if (c2spd <= 0) {
*n = *f = 0; *n = *f = 0;
return; return;
} }
@ -264,3 +261,25 @@ void libxmp_c2spd_to_note(int c2spd, int *n, int *f)
*n = c / 128; *n = c / 128;
*f = 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

View file

@ -20,5 +20,8 @@ double libxmp_note_to_period_mix (int, int);
int libxmp_period_to_note (int); int libxmp_period_to_note (int);
int libxmp_period_to_bend (struct context_data *, double, int, double); int libxmp_period_to_bend (struct context_data *, double, int, double);
void libxmp_c2spd_to_note (int, int *, int *); void libxmp_c2spd_to_note (int, int *, int *);
#ifndef LIBXMP_CORE_PLAYER
double libxmp_gus_frequency_steps (int, int);
#endif
#endif /* LIBXMP_PERIOD_H */ #endif /* LIBXMP_PERIOD_H */

View file

@ -1,5 +1,5 @@
/* Extended Module Player /* 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 * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * 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 }, { 0, 1, 1 }, { 1, 1, 1 }, { 2, 1, 1 }, { 4, 1, 1 },
{ 8, 1, 1 }, { 16, 1, 1 }, { 0, 3, 2 }, { 0, 2, 1 }, { 8, 1, 1 }, { 16, 1, 1 }, { 0, 3, 2 }, { 0, 2, 1 },
{ 0, 0, 1 } /* Note cut */ { 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) static int check_envelope_end(struct xmp_envelope *env, int x)
{ {
int16 *data = env->data; int16 *data = env->data;
int index; int idx;
if (~env->flg & XMP_ENVELOPE_ON || env->npt <= 0) if (~env->flg & XMP_ENVELOPE_ON || env->npt <= 0)
return 0; return 0;
index = (env->npt - 1) * 2; idx = (env->npt - 1) * 2;
/* last node */ /* last node */
if (x >= data[index] || index == 0) { if (x >= data[idx] || idx == 0) {
if (~env->flg & XMP_ENVELOPE_LOOP) { if (~env->flg & XMP_ENVELOPE_LOOP) {
return 1; return 1;
} }
@ -90,27 +89,30 @@ static int get_envelope(struct xmp_envelope *env, int x, int def)
{ {
int x1, x2, y1, y2; int x1, x2, y1, y2;
int16 *data = env->data; int16 *data = env->data;
int index; int idx;
if (x < 0 || ~env->flg & XMP_ENVELOPE_ON || env->npt <= 0) if (x < 0 || ~env->flg & XMP_ENVELOPE_ON || env->npt <= 0)
return def; return def;
index = (env->npt - 1) * 2; idx = (env->npt - 1) * 2;
x1 = data[index]; /* last node */ x1 = data[idx]; /* last node */
if (x >= x1 || index == 0) { if (x >= x1 || idx == 0) {
return data[index + 1]; return data[idx + 1];
} }
do { do {
index -= 2; idx -= 2;
x1 = data[index]; x1 = data[idx];
} while (index > 0 && x1 > x); } while (idx > 0 && x1 > x);
/* interpolate */ /* interpolate */
y1 = data[index + 1]; y1 = data[idx + 1];
x2 = data[index + 2]; x2 = data[idx + 2];
y2 = data[index + 3]; 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; 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) static int check_envelope_fade(struct xmp_envelope *env, int x)
{ {
int16 *data = env->data; int16 *data = env->data;
int index; int idx;
if (~env->flg & XMP_ENVELOPE_ON) if (~env->flg & XMP_ENVELOPE_ON)
return 0; return 0;
index = (env->npt - 1) * 2; /* last node */ idx = (env->npt - 1) * 2; /* last node */
if (x > data[index]) { if (x > data[idx]) {
if (data[index + 1] == 0) if (data[idx + 1] == 0)
return -1; return -1;
else else
return 1; 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 #ifndef LIBXMP_CORE_PLAYER
/* From http://www.un4seen.com/forum/?topic=7554.0 /* 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 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]; struct xmp_sample *xxs = libxmp_get_sample(ctx, xc->smp);
int len; struct module_data *m = &ctx->m;
int lps, len = -1;
xc->invloop.count += invloop_table[xc->invloop.speed]; 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; xc->invloop.count = 0;
len = xxs->lpe - xxs->lps;
if (++xc->invloop.pos > len) { if (++xc->invloop.pos > len) {
xc->invloop.pos = 0; xc->invloop.pos = 0;
} }
if (xxs->data == NULL) {
return;
}
if (~xxs->flg & XMP_SAMPLE_16BIT) { 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->mastervol = mod->xxc[i].vol;
xc->pan.val = mod->xxc[i].pan; xc->pan.val = mod->xxc[i].pan;
} }
#ifndef LIBXMP_CORE_DISABLE_IT #ifndef LIBXMP_CORE_DISABLE_IT
xc->filter.cutoff = 0xff; xc->filter.cutoff = 0xff;
@ -624,7 +854,7 @@ static int tremor_s3m(struct context_data *ctx, int chn, int finalvol)
* Update channel data * 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) 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 /* If IT, only apply fadeout on note release if we don't
* have envelope, or if we have envelope loop * 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) || if ((~instrument->aei.flg & XMP_ENVELOPE_ON) ||
(instrument->aei.flg & XMP_ENVELOPE_LOOP)) { (instrument->aei.flg & XMP_ENVELOPE_LOOP)) {
fade = 1; fade = 1;
@ -656,29 +886,30 @@ static void process_volume(struct context_data *ctx, int chn, int act)
} }
} else { } else {
if (~instrument->aei.flg & XMP_ENVELOPE_ON) { if (~instrument->aei.flg & XMP_ENVELOPE_ON) {
if (TEST_NOTE(NOTE_RELEASE)) { if (TEST_NOTE(NOTE_ENV_RELEASE)) {
xc->fadeout = 0; xc->fadeout = 0;
} }
} }
if (TEST_NOTE(NOTE_RELEASE) || act == VIRT_ACTION_OFF) { if (TEST_NOTE(NOTE_ENV_RELEASE) || act == VIRT_ACTION_OFF) {
fade = 1; fade = 1;
} }
} }
if (TEST_NOTE(NOTE_FADEOUT) || act == VIRT_ACTION_FADE) { if (!TEST_PER(VENV_PAUSE)) {
fade = 1; xc->v_idx = update_envelope(&instrument->aei, xc->v_idx,
DOENV_RELEASE, TEST(KEY_OFF), IS_PLAYER_MODE_IT());
} }
if (fade) { vol_envelope = get_envelope(&instrument->aei, xc->v_idx, 64);
if (xc->fadeout > xc->ins_fade) { if (check_envelope_end(&instrument->aei, xc->v_idx)) {
xc->fadeout -= xc->ins_fade; if (vol_envelope == 0) {
} else {
xc->fadeout = 0;
SET_NOTE(NOTE_END); 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)) { switch (check_envelope_fade(&instrument->aei, xc->v_idx)) {
case -1: case -1:
SET_NOTE(NOTE_END); 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)) { /* IT envelope fadeout starts immediately after the envelope tick,
xc->v_idx = update_envelope(&instrument->aei, xc->v_idx, * so process fadeout after the volume envelope. */
DOENV_RELEASE, TEST(KEY_OFF), IS_PLAYER_MODE_IT()); if (TEST_NOTE(NOTE_FADEOUT) || act == VIRT_ACTION_FADE) {
fade = 1;
} }
vol_envelope = get_envelope(&instrument->aei, xc->v_idx, 64); if (fade) {
if (check_envelope_end(&instrument->aei, xc->v_idx)) { if (xc->fadeout > xc->ins_fade) {
if (vol_envelope == 0) { xc->fadeout -= xc->ins_fade;
} else {
xc->fadeout = 0;
SET_NOTE(NOTE_END); SET_NOTE(NOTE_END);
} }
SET_NOTE(NOTE_ENV_END);
} }
/* If note ended in background channel, we can safely reset it */ /* 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 { } else {
finalvol = tremor_s3m(ctx, chn, finalvol); finalvol = tremor_s3m(ctx, chn, finalvol);
} }
#ifndef LIBXMP_CORE_DISABLE_IT
xc->macro.finalvol = finalvol;
#endif
if (chn < m->mod.chn) { if (chn < m->mod.chn) {
finalvol = finalvol * p->master_vol / 100; finalvol = finalvol * p->master_vol / 100;
@ -863,7 +1099,7 @@ static void process_frequency(struct context_data *ctx, int chn, int act)
/* Sanity check */ /* Sanity check */
if (period < 0.1) { if (period < 0.1) {
period = 0.1; period = 0.1;
} }
/* Arpeggio */ /* Arpeggio */
arp = arpeggio(ctx, xc); arp = arpeggio(ctx, xc);
@ -894,7 +1130,7 @@ static void process_frequency(struct context_data *ctx, int chn, int act)
} }
} }
} }
/* Envelope */ /* Envelope */
if (xc->f_idx >= 0 && (~instrument->fei.flg & XMP_ENVELOPE_FLT)) { 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() */ /* For xmp_get_frame_info() */
xc->info_pitchbend = linear_bend >> 7; 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()) { if (IS_PERIOD_MODRNG()) {
CLAMP(xc->info_period, const double min_period = libxmp_note_to_period(ctx, MAX_NOTE_MOD, xc->finetune, 0) * 4096;
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;
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)) { } else if (xc->info_period < (1 << 12)) {
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) { if (cutoff > 0xff) {
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; int a0, b0, b1;
libxmp_filter_setup(s->freq, cutoff, resonance, &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_A0, a0);
libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_B0, b0); 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_FILTER_B1, b1);
libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_RESONANCE, resonance); 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 #endif
} }
@ -1018,6 +1258,7 @@ static void process_pan(struct context_data *ctx, int chn, int act)
libxmp_lfo_update(&xc->panbrello.lfo); libxmp_lfo_update(&xc->panbrello.lfo);
} }
} }
xc->macro.notepan = xc->pan.val + panbrello + 0x80;
#endif #endif
channel_pan = xc->pan.val; channel_pan = xc->pan.val;
@ -1080,13 +1321,19 @@ static void update_volume(struct context_data *ctx, int chn)
#ifndef LIBXMP_CORE_PLAYER #ifndef LIBXMP_CORE_PLAYER
if (TEST_PER(VOL_SLIDE)) { if (TEST_PER(VOL_SLIDE)) {
if (xc->vol.slide > 0 && xc->volume > m->volbase) { if (xc->vol.slide > 0) {
xc->volume = m->volbase; int target = MAX(xc->vol.target - 1, m->volbase);
RESET_PER(VOL_SLIDE); if (xc->volume > target) {
xc->volume = target;
RESET_PER(VOL_SLIDE);
}
} }
if (xc->vol.slide < 0 && xc->volume < 0) { if (xc->vol.slide < 0) {
xc->volume = 0; int target = xc->vol.target > 0 ? MIN(0, xc->vol.target - 1) : 0;
RESET_PER(VOL_SLIDE); if (xc->volume < target) {
xc->volume = target;
RESET_PER(VOL_SLIDE);
}
} }
} }
#endif #endif
@ -1111,7 +1358,7 @@ static void update_volume(struct context_data *ctx, int chn)
* Unlike fine volume slides in the effect column, * Unlike fine volume slides in the effect column,
* fine volume slides in the volume column are only * fine volume slides in the volume column are only
* ever executed on the first tick -- not on multiples * 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) { if (!f->rowdelay_set || f->rowdelay_set & ROWDELAY_FIRST_FRAME) {
xc->volume += xc->vol.fslide2; xc->volume += xc->vol.fslide2;
@ -1178,7 +1425,7 @@ static void update_frequency(struct context_data *ctx, int chn)
} }
} }
} }
} }
} }
if (is_first_frame(ctx)) { if (is_first_frame(ctx)) {
@ -1199,10 +1446,11 @@ static void update_frequency(struct context_data *ctx, int chn)
case PERIOD_LINEAR: case PERIOD_LINEAR:
CLAMP(xc->period, MIN_PERIOD_L, MAX_PERIOD_L); CLAMP(xc->period, MIN_PERIOD_L, MAX_PERIOD_L);
break; break;
case PERIOD_MODRNG: case PERIOD_MODRNG: {
CLAMP(xc->period, const double min_period = libxmp_note_to_period(ctx, MAX_NOTE_MOD, xc->finetune, 0);
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);
libxmp_note_to_period(ctx, MIN_NOTE_MOD, xc->finetune, 0)); CLAMP(xc->period, min_period, max_period);
}
break; 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); act = libxmp_virt_cstat(ctx, chn);
if (act == VIRT_INVALID) { if (act == VIRT_INVALID) {
/* We need this to keep processing global volume slides */ /* 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].s;
xc->volume *= rval[xc->retrig.type].m; xc->volume *= rval[xc->retrig.type].m;
xc->volume /= rval[xc->retrig.type].d; 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 */ /* Do keyoff */
if (xc->keyoff) { if (xc->keyoff) {
@ -1308,7 +1568,7 @@ static void play_channel(struct context_data *ctx, int chn)
SET_NOTE(NOTE_RELEASE); 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_volume(ctx, chn);
update_frequency(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); process_pan(ctx, chn, act);
#ifndef LIBXMP_CORE_PLAYER #ifndef LIBXMP_CORE_PLAYER
if (HAS_QUIRK(QUIRK_PROTRACK) && xc->ins < mod->ins) { if (HAS_QUIRK(QUIRK_PROTRACK | QUIRK_INVLOOP) && xc->ins < mod->ins) {
update_invloop(m, xc); update_invloop(ctx, xc);
} }
#endif #endif
if (TEST_NOTE(NOTE_SUSEXIT)) { if (TEST_NOTE(NOTE_SUSEXIT)) {
SET_NOTE(NOTE_RELEASE); SET_NOTE(NOTE_ENV_RELEASE);
} }
xc->info_position = libxmp_virt_getvoicepos(ctx, chn); 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 xmp_module *mod = &m->mod;
struct smix_data *smix = &ctx->smix; struct smix_data *smix = &ctx->smix;
int chn; int chn;
for (chn = 0; chn < mod->chn + smix->chn; chn++) { for (chn = 0; chn < mod->chn + smix->chn; chn++) {
struct xmp_event *e = &p->inject_event[chn]; struct xmp_event *e = &p->inject_event[chn];
if (e->_flag > 0) { if (e->_flag > 0) {
@ -1362,13 +1622,14 @@ static void next_order(struct context_data *ctx)
struct flow_control *f = &p->flow; struct flow_control *f = &p->flow;
struct module_data *m = &ctx->m; struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod; struct xmp_module *mod = &m->mod;
int reset_gvol = 0;
int mark; int mark;
do { do {
p->ord++; p->ord++;
/* Restart module */ /* 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 (p->ord >= mod->len || mark) {
if (mod->rst > mod->len || if (mod->rst > mod->len ||
mod->xxo[mod->rst] >= mod->pat || 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->ord = m->seq_data[p->sequence].entry_point;
} }
} }
/* This might be a marker, so delay updating global
p->gvol = m->xxo_info[p->ord].gvl; * volume until an actual pattern is found */
reset_gvol = 1;
} }
} while (mod->xxo[p->ord] >= mod->pat); } 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; p->current_time = m->xxo_info[p->ord].time;
f->num_rows = mod->xxp[mod->xxo[p->ord]]->rows; 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; p->frame = 0;
#ifndef LIBXMP_CORE_PLAYER #ifndef LIBXMP_CORE_PLAYER
f->jump_in_pat = -1;
/* Reset persistent effects at new pattern */ /* Reset persistent effects at new pattern */
if (HAS_QUIRK(QUIRK_PERPAT)) { if (HAS_QUIRK(QUIRK_PERPAT)) {
int chn; int chn;
@ -1426,18 +1697,18 @@ static void next_row(struct context_data *ctx)
next_order(ctx); next_order(ctx);
} else { } else {
if (f->loop_chn) {
p->row = f->loop[f->loop_chn - 1].start - 1;
f->loop_chn = 0;
}
if (f->rowdelay == 0) { if (f->rowdelay == 0) {
p->row++; p->row++;
f->rowdelay_set = 0; f->rowdelay_set = 0;
} else { } else {
f->rowdelay--; f->rowdelay--;
} }
if (f->loop_chn) {
p->row = f->loop[f->loop_chn - 1].start;
f->loop_chn = 0;
}
/* check end of pattern */ /* check end of pattern */
if (p->row >= f->num_rows) { if (p->row >= f->num_rows) {
next_order(ctx); next_order(ctx);
@ -1486,6 +1757,21 @@ static void update_from_ord_info(struct context_data *ctx)
#endif #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) int xmp_start_player(xmp_context opaque, int rate, int format)
{ {
struct context_data *ctx = (struct context_data *)opaque; 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; mod->len = 0;
} }
if (mod->len == 0 || mod->chn == 0) { if (mod->len == 0) {
/* set variables to sane state */ /* 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->ord = p->scan[0].ord = 0;
p->row = p->scan[0].row = 0; p->row = p->scan[0].row = 0;
f->end_point = 0; f->end_point = 0;
@ -1557,19 +1846,15 @@ int xmp_start_player(xmp_context opaque, int rate, int format)
goto err; goto err;
} }
f->delay = 0; libxmp_reset_flow(ctx);
f->jumpline = 0;
f->jump = -1;
f->pbreak = 0;
f->rowdelay_set = 0;
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) { if (f->loop == NULL) {
ret = -XMP_ERROR_SYSTEM; ret = -XMP_ERROR_SYSTEM;
goto err; 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) { if (p->xc_data == NULL) {
ret = -XMP_ERROR_SYSTEM; ret = -XMP_ERROR_SYSTEM;
goto err1; goto err1;
@ -1578,11 +1863,14 @@ int xmp_start_player(xmp_context opaque, int rate, int format)
/* Reset our buffer pointers */ /* Reset our buffer pointers */
xmp_play_buffer(opaque, NULL, 0, 0); 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++) { for (i = 0; i < p->virt.virt_channels; i++) {
struct channel_data *xc = &p->xc_data[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) if (libxmp_new_channel_extras(ctx, xc) < 0)
goto err2; goto err2;
#endif
} }
#endif #endif
reset_channels(ctx); 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.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; 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; return ret;
} }
void xmp_end_player(xmp_context opaque) void xmp_end_player(xmp_context opaque)
{ {
struct context_data *ctx = (struct context_data *)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_track *track;
struct xmp_event *event; struct xmp_event *event;
int trk; int trk;
ci->note = c->key; ci->note = c->key;
ci->pitchbend = c->info_pitchbend; ci->pitchbend = c->info_pitchbend;
ci->period = c->info_period; 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->pan = c->info_finalpan;
ci->reserved = 0; ci->reserved = 0;
memset(&ci->event, 0, sizeof(*event)); memset(&ci->event, 0, sizeof(*event));
if (info->pattern < mod->pat && info->row < info->num_rows) { if (info->pattern < mod->pat && info->row < info->num_rows) {
trk = mod->xxp[info->pattern]->index[i]; trk = mod->xxp[info->pattern]->index[i];
track = mod->xxt[trk]; track = mod->xxt[trk];

View file

@ -56,20 +56,30 @@ struct retrig_control {
#define FINE_VOLS_2 (1 << 25) #define FINE_VOLS_2 (1 << 25)
#define KEY_OFF (1 << 26) /* for IT release on envloop end */ #define KEY_OFF (1 << 26) /* for IT release on envloop end */
#define TREMOR (1 << 27) /* for XM tremor */ #define TREMOR (1 << 27) /* for XM tremor */
#define MIDI_MACRO (1 << 28) /* IT midi macro */
#define NOTE_FADEOUT (1 << 0) #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_END (1 << 2)
#define NOTE_CUT (1 << 3) #define NOTE_CUT (1 << 3)
#define NOTE_ENV_END (1 << 4) #define NOTE_ENV_END (1 << 4)
#define NOTE_SAMPLE_END (1 << 5) #define NOTE_SAMPLE_END (1 << 5)
#define NOTE_SET (1 << 6) /* for IT portamento after keyoff */ #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_KEY_CUT (1 << 8) /* note cut with XMP_KEY_CUT event */
#define NOTE_GLISSANDO (1 << 9) #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(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_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 { struct instrument_vibrato {
int phase; int phase;
@ -147,6 +157,7 @@ struct channel_data {
int val; /* Retrig value */ int val; /* Retrig value */
int count; /* Retrig counter */ int count; /* Retrig counter */
int type; /* Retrig type */ int type; /* Retrig type */
int limit; /* Number of retrigs */
} retrig; } retrig;
struct { struct {
@ -163,6 +174,9 @@ struct channel_data {
#ifndef LIBXMP_CORE_DISABLE_IT #ifndef LIBXMP_CORE_DISABLE_IT
int fslide2; int fslide2;
int memory2; /* Volume slide effect memory */ int memory2; /* Volume slide effect memory */
#endif
#ifndef LIBXMP_CORE_PLAYER
int target; /* Target for persistent volslide */
#endif #endif
} vol; } vol;
@ -194,6 +208,7 @@ struct channel_data {
int dir; /* Tone portamento up/down directionh */ int dir; /* Tone portamento up/down directionh */
int slide; /* Delta for tone portamento */ int slide; /* Delta for tone portamento */
int memory; /* Tone portamento effect memory */ int memory; /* Tone portamento effect memory */
int note_memory;/* Tone portamento note memory (ULT) */
} porta; } porta;
struct { struct {
@ -207,7 +222,7 @@ struct channel_data {
int fslide; /* Pan fine slide value */ int fslide; /* Pan fine slide value */
int memory; /* Pan slide effect memory */ int memory; /* Pan slide effect memory */
int surround; /* Surround channel flag */ int surround; /* Surround channel flag */
} pan; } pan;
struct { struct {
int speed; int speed;
@ -224,8 +239,17 @@ struct channel_data {
int cutoff; /* IT filter cutoff frequency */ int cutoff; /* IT filter cutoff frequency */
int resonance; /* IT filter resonance */ int resonance; /* IT filter resonance */
int envelope; /* IT filter envelope */ int envelope; /* IT filter envelope */
int can_disable;/* IT hack: allow disabling for cutoff 127 */
} filter; } 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 #endif
#ifndef LIBXMP_CORE_PLAYER #ifndef LIBXMP_CORE_PLAYER

View file

@ -1,5 +1,5 @@
/* Extended Module Player /* 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 * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
@ -31,11 +31,6 @@
#endif #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, static struct xmp_subinstrument *get_subinstrument(struct context_data *ctx,
int ins, int key) int ins, int key)
{ {
@ -45,7 +40,7 @@ static struct xmp_subinstrument *get_subinstrument(struct context_data *ctx,
if (IS_VALID_INSTRUMENT(ins)) { if (IS_VALID_INSTRUMENT(ins)) {
instrument = &mod->xxi[ins]; instrument = &mod->xxi[ins];
if (is_valid_note(key)) { if (IS_VALID_NOTE(key)) {
int mapped = instrument->map[key].ins; int mapped = instrument->map[key].ins;
if (mapped != 0xff && mapped >= 0 && mapped < instrument->nsm) if (mapped != 0xff && mapped >= 0 && mapped < instrument->nsm)
return &instrument->sub[mapped]; 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)) if (!IS_VALID_INSTRUMENT(xc->ins))
return; return;
RESET_NOTE(NOTE_ENV_END); RESET_NOTE(NOTE_ENV_END);
xc->v_idx = -1; xc->v_idx = -1;
xc->p_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 #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, static void reset_envelopes_carry(struct context_data *ctx,
struct channel_data *xc) 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 channel_data *xc, int is_toneporta)
{ {
struct module_data *m = &ctx->m; struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
struct smix_data *smix = &ctx->smix;
if (sub != NULL && note >= 0) { 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)) { if (!HAS_QUIRK(QUIRK_PROTRACK)) {
xc->finetune = sub->fin; xc->finetune = sub->fin;
} }
@ -129,16 +128,16 @@ static void set_effect_defaults(struct context_data *ctx, int note,
#ifndef LIBXMP_CORE_DISABLE_IT #ifndef LIBXMP_CORE_DISABLE_IT
if (sub->ifc & 0x80) { if (sub->ifc & 0x80) {
xc->filter.cutoff = (sub->ifc - 0x80) * 2; xc->filter.cutoff = (sub->ifc - 0x80) * 2;
} else if (~xxi->fei.flg & XMP_ENVELOPE_FLT) {
xc->filter.cutoff = 0xff;
} }
xc->filter.envelope = 0x100; xc->filter.envelope = 0x100;
if (sub->ifr & 0x80) { if (sub->ifr & 0x80) {
xc->filter.resonance = (sub->ifr - 0x80) * 2; 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 #endif
/* TODO: should probably expand the LFO period size instead /* 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 #ifndef LIBXMP_CORE_PLAYER
#define IS_SFX_PITCH(x) ((x) == FX_PITCH_ADD || (x) == FX_PITCH_SUB) #define IS_SFX_PITCH(x) ((x) == FX_PITCH_ADD || (x) == FX_PITCH_SUB)
#define IS_TONEPORTA(x) ((x) == FX_TONEPORTA || (x) == FX_TONE_VSLIDE \ #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 #else
#define IS_TONEPORTA(x) ((x) == FX_TONEPORTA || (x) == FX_TONE_VSLIDE) #define IS_TONEPORTA(x) ((x) == FX_TONEPORTA || (x) == FX_TONE_VSLIDE)
#endif #endif
#define set_patch(ctx,chn,ins,smp,note) \ #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) 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) { if (e->note == XMP_KEY_OFF) {
SET_NOTE(NOTE_RELEASE); SET_NOTE(NOTE_RELEASE);
use_ins_vol = 0; 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; xc->key = e->note - 1;
RESET_NOTE(NOTE_END); 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; note = xc->key + sub->xpo + transp;
smp = sub->sid; smp = sub->sid;
if (mod->xxs[smp].len == 0) { if (!IS_VALID_SAMPLE(smp)) {
smp = -1; smp = -1;
} }
@ -332,6 +332,7 @@ static int read_event_mod(struct context_data *ctx, struct xmp_event *e, int chn
if (e->vol) { if (e->vol) {
xc->volume = e->vol - 1; xc->volume = e->vol - 1;
SET(NEW_VOL); SET(NEW_VOL);
RESET_PER(VOL_SLIDE); /* FIXME: should this be for FAR only? */
} }
/* Secondary effect handled first */ /* Secondary effect handled first */
@ -375,6 +376,7 @@ static int sustain_check(struct xmp_envelope *env, int idx)
{ {
return (env && return (env &&
(env->flg & XMP_ENVELOPE_ON) && (env->flg & XMP_ENVELOPE_ON) &&
(env->flg & XMP_ENVELOPE_SUS) &&
(~env->flg & XMP_ENVELOPE_LOOP) && (~env->flg & XMP_ENVELOPE_LOOP) &&
idx == env->data[env->sus << 1]); 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 */ /* FT2: Retrieve old instrument volume */
if (ins) { if (ins) {
if (key == 0 || key >= XMP_KEY_OFF) { if (key == 0 || key >= XMP_KEY_OFF) {
struct xmp_subinstrument *sub;
/* Previous instrument */ /* Previous instrument */
sub = get_subinstrument(ctx, xc->ins, xc->key); sub = get_subinstrument(ctx, xc->ins, xc->key);
/* No note */ /* No note */
if (sub != NULL) { if (sub != NULL) {
int p = mod->xxc[chn].pan - 128; int pan = mod->xxc[chn].pan - 128;
xc->volume = sub->vol; xc->volume = sub->vol;
if (!HAS_QUIRK(QUIRK_FTMOD)) { if (!HAS_QUIRK(QUIRK_FTMOD)) {
xc->pan.val = p + ((sub->pan - 128) * xc->pan.val = pan + ((sub->pan - 128) *
(128 - abs(p))) / 128 + 128; (128 - abs(pan))) / 128 + 128;
} }
xc->ins_fade = mod->xxi[xc->ins].rls; 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 */ /* Check note */
if (ins) { if (ins) {
if (key > 0 && key < XMP_KEY_OFF) { if (key > 0 && key < XMP_KEY_OFF) {
struct xmp_subinstrument *sub;
/* Retrieve volume when we have note */ /* Retrieve volume when we have note */
/* and only if we have instrument, otherwise we're in /* 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 */ /* Current instrument */
sub = get_subinstrument(ctx, xc->ins, key - 1); sub = get_subinstrument(ctx, xc->ins, key - 1);
if (sub != NULL) { if (sub != NULL) {
int p = mod->xxc[chn].pan - 128; int pan = mod->xxc[chn].pan - 128;
xc->volume = sub->vol; xc->volume = sub->vol;
if (!HAS_QUIRK(QUIRK_FTMOD)) { if (!HAS_QUIRK(QUIRK_FTMOD)) {
xc->pan.val = p + ((sub->pan - 128) * xc->pan.val = pan + ((sub->pan - 128) *
(128 - abs(p))) / 128 + 128; (128 - abs(pan))) / 128 + 128;
} }
xc->ins_fade = mod->xxi[xc->ins].rls; 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; env_on = 1;
} }
} }
if (env_on || (!vol_set && (!ev.ins || !delay_fx))) { if (env_on || (!vol_set && (!ev.ins || !delay_fx))) {
if (sustain_check(env, xc->v_idx)) { if (sustain_check(env, xc->v_idx)) {
/* See OpenMPT EnvOff.xm. In certain /* 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." * and remains in the memory."
*/ */
sub = NULL; sub = NULL;
if (is_valid_note(key - 1)) { if (IS_VALID_NOTE(key - 1)) {
int k = key - 1; int k = key - 1;
sub = get_subinstrument(ctx, xc->ins, k); sub = get_subinstrument(ctx, xc->ins, k);
if (!new_invalid_ins && sub != NULL) { 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->key = --key;
xc->fadeout = 0x10000; xc->fadeout = 0x10000;
RESET_NOTE(NOTE_END); 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; note = key + sub->xpo + transp;
smp = sub->sid; smp = sub->sid;
if (mod->xxs[smp].len == 0) { if (!IS_VALID_SAMPLE(smp)) {
smp = -1; 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) { if (not_same_ins) {
xc->offset.val = 0; 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; xc->key = e->note - 1;
RESET_NOTE(NOTE_END); 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; note = xc->key + sub->xpo + transp;
smp = sub->sid; smp = sub->sid;
if (mod->xxs[smp].len == 0) { if (!IS_VALID_SAMPLE(smp)) {
smp = -1; 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 is_toneporta, is_release;
int candidate_ins; int candidate_ins;
int reset_env; int reset_env;
int reset_susloop;
int use_ins_vol; int use_ins_vol;
int sample_mode; int sample_mode;
int toneporta_offset; int toneporta_offset;
int disabled_toneporta;
int retrig_ins; int retrig_ins;
struct xmp_event ev; 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_toneporta = 0;
is_release = 0; is_release = 0;
reset_env = 0; reset_env = 0;
reset_susloop = 0;
use_ins_vol = 0; use_ins_vol = 0;
candidate_ins = xc->ins; candidate_ins = xc->ins;
sample_mode = !HAS_QUIRK(QUIRK_VIRTUAL); sample_mode = !HAS_QUIRK(QUIRK_VIRTUAL);
toneporta_offset = 0; toneporta_offset = 0;
disabled_toneporta = 0;
retrig_ins = 0; retrig_ins = 0;
/* Keyoff + instrument retrigs current instrument in old fx mode */ /* 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; is_toneporta = 1;
} }
if (TEST_NOTE(NOTE_RELEASE | NOTE_FADEOUT)) { if (TEST_NOTE(NOTE_ENV_RELEASE | NOTE_FADEOUT)) {
is_release = 1; 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 */ /* Off-Porta.it */
if (is_toneporta && ev.fxt == FX_OFFSET) { if (is_toneporta && ev.fxt == FX_OFFSET) {
disabled_toneporta = 1; toneporta_offset = 1;
is_toneporta = 0;
if (!HAS_QUIRK(QUIRK_PRENV)) { if (!HAS_QUIRK(QUIRK_PRENV)) {
toneporta_offset = 1;
RESET_NOTE(NOTE_ENV_END); 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) { if (set_new_ins) {
SET(NEW_INS); SET(NEW_INS);
use_ins_vol = 1;
reset_env = 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; xc->per_flags = 0;
if (IS_VALID_INSTRUMENT(ins)) { 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 { } 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) { if (sample_mode) {
xc->volume = 0; xc->volume = 0;
} }
@ -1133,13 +1133,14 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
/* Check note */ /* Check note */
if (key && !new_invalid_ins) { if (key) {
SET(NEW_NOTE); SET(NEW_NOTE);
SET_NOTE(NOTE_SET); SET_NOTE(NOTE_SET);
if (key == XMP_KEY_FADE) { if (key == XMP_KEY_FADE) {
SET_NOTE(NOTE_FADEOUT); SET_NOTE(NOTE_FADEOUT);
reset_env = 0; reset_env = 0;
reset_susloop = 0;
use_ins_vol = 0; use_ins_vol = 0;
} else if (key == XMP_KEY_CUT) { } else if (key == XMP_KEY_CUT) {
SET_NOTE(NOTE_END | NOTE_CUT | NOTE_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). * However, never reset the envelope (see OpenMPT wnoteoff.it).
*/ */
reset_env = 0; reset_env = 0;
reset_susloop = 0;
if (!ev.ins) { if (!ev.ins) {
use_ins_vol = 0; 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 */ /* portamento_after_keyoff.it test case */
/* also see suburban_streets o13 c45 */ /* also see suburban_streets o13 c45 */
if (ev.ins || !is_toneporta) { if (!is_toneporta) {
if (!disabled_toneporta) { reset_env = 1;
reset_env = 1; reset_susloop = 1;
}
} }
if (is_toneporta) { if (is_toneporta) {
if (not_same_ins || TEST_NOTE(NOTE_END)) { if (not_same_ins || TEST_NOTE(NOTE_END)) {
SET(NEW_INS); SET(NEW_INS);
RESET_NOTE(NOTE_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT); RESET_NOTE(NOTE_ENV_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT);
} else { } else {
if (is_valid_note(key - 1)) { if (IS_VALID_NOTE(key - 1)) {
xc->key_porta = key - 1; xc->key_porta = key - 1;
} }
key = 0; 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)) { if (TEST_NOTE(NOTE_CUT)) {
use_ins_vol = 1; /* See OpenMPT NoteOffInstr.it */ 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) { if (sub != NULL) {
int transp = mod->xxi[candidate_ins].map[key].xpo; int transp = mod->xxi[candidate_ins].map[key].xpo;
int smp, to; int smp, to;
int dct;
int rvv; int rvv;
/* Clear note delay before duplicating channels:
* it_note_delay_nna.it */
xc->delay = 0;
note = key + sub->xpo + transp; note = key + sub->xpo + transp;
smp = sub->sid; smp = sub->sid;
if (smp >= mod->smp || mod->xxs[smp].len == 0) { if (!IS_VALID_SAMPLE(smp)) {
smp = -1; smp = -1;
} }
dct = sub->dct;
if (not_same_smp) { if (not_same_smp) {
fix_period(ctx, chn, sub); 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, 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 */ /* Random value for volume swing */
rvv = sub->rvv & 0xff; 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 * finished (OpenMPT test EnvReset.it). This must take place after
* channel copies in case of NNA (see test/test.it) * channel copies in case of NNA (see test/test.it)
* Also if we have envelope in carry mode, check fadeout * 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 (ev.ins && TEST_NOTE(NOTE_ENV_END)) {
if (check_fadeout(ctx, xc, candidate_ins)) { if (check_fadeout(ctx, xc, candidate_ins)) {
reset_envelopes(ctx, xc); reset_envelope_volume(ctx, xc);
} else { } else {
reset_env = 0; 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 (reset_env) {
if (ev.note) { 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) */ /* Set after copying to new virtual channel (see ambio.it) */
xc->fadeout = 0x10000; xc->fadeout = 0x10000;
} }
if (reset_susloop && ev.note) {
RESET_NOTE(NOTE_SAMPLE_RELEASE);
}
/* See OpenMPT wnoteoff.it vs noteoff3.it */ /* See OpenMPT wnoteoff.it vs noteoff3.it */
if (retrig_ins && not_same_ins) { 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)) { if (TEST_NOTE(NOTE_CUT)) {
reset_envelopes(ctx, xc); reset_envelopes(ctx, xc);
} else if (!toneporta_offset) { } else if (!toneporta_offset || HAS_QUIRK(QUIRK_PRENV)) {
reset_envelopes_carry(ctx, xc); reset_envelopes_carry(ctx, xc);
} }
RESET_NOTE(NOTE_CUT); 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) { if (note >= 0) {
xc->note = note; xc->note = note;
}
if (note >= 0 || toneporta_offset) {
libxmp_virt_voicepos(ctx, chn, xc->offset.val); 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); SET_NOTE(NOTE_END);
xc->period = 0; xc->period = 0;
libxmp_virt_resetchannel(ctx, chn); 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]; struct xmp_instrument *xxi = &mod->xxi[xc->ins];
xc->key = e->note - 1; 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; note = xc->key + sub->xpo + transp;
smp = sub->sid; smp = sub->sid;
if (mod->xxs[smp].len == 0) { if (!IS_VALID_SAMPLE(smp)) {
smp = -1; 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 xmp_module *mod = &m->mod;
struct channel_data *xc = &p->xc_data[chn]; struct channel_data *xc = &p->xc_data[chn];
struct xmp_subinstrument *sub; struct xmp_subinstrument *sub;
int is_smix_ins; struct xmp_instrument *xxi;
int ins, note, transp, smp; int ins, note, transp, smp;
xc->flags = 0; 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) if (!e->ins)
return 0; return 0;
is_smix_ins = 0;
ins = e->ins - 1; ins = e->ins - 1;
SET(NEW_INS); SET(NEW_INS);
xc->fadeout = 0x10000;
xc->per_flags = 0; xc->per_flags = 0;
xc->offset.val = 0; xc->offset.val = 0;
RESET_NOTE(NOTE_RELEASE); RESET_NOTE(NOTE_RELEASE|NOTE_FADEOUT);
xc->ins = ins; xxi = libxmp_get_instrument(ctx, ins);
if (xxi != NULL) {
if (ins >= mod->ins && ins < mod->ins + smix->ins) { xc->ins_fade = xxi->rls;
is_smix_ins = 1;
xc->ins_fade = smix->xxi[xc->ins - mod->ins].rls;
} }
xc->ins = ins;
SET(NEW_NOTE); SET(NEW_NOTE);
if (e->note == XMP_KEY_OFF) { if (e->note == XMP_KEY_OFF) {
SET_NOTE(NOTE_RELEASE); SET_NOTE(NOTE_RELEASE);
return 0; 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->key = e->note - 1;
xc->fadeout = 0x10000;
RESET_NOTE(NOTE_END); RESET_NOTE(NOTE_END);
if (is_smix_ins) { if (ins >= mod->ins && ins < mod->ins + smix->ins) {
sub = &smix->xxi[xc->ins - mod->ins].sub[0]; sub = &xxi->sub[0];
if (sub == NULL) { if (sub == NULL) {
return 0; return 0;
} }
@ -1559,15 +1587,15 @@ static int read_event_smix(struct context_data *ctx, struct xmp_event *e, int ch
xc->smp = smp; xc->smp = smp;
} }
} else { } else {
sub = is_valid_note(xc->key) ? sub = IS_VALID_NOTE(xc->key) ?
get_subinstrument(ctx, xc->ins, xc->key) : NULL; get_subinstrument(ctx, xc->ins, xc->key) : NULL;
if (sub == NULL) { if (sub == NULL) {
return 0; return 0;
} }
transp = mod->xxi[xc->ins].map[xc->key].xpo; transp = xxi->map[xc->key].xpo;
note = xc->key + sub->xpo + transp; note = xc->key + sub->xpo + transp;
smp = sub->sid; smp = sub->sid;
if (mod->xxs[smp].len == 0) if (!IS_VALID_SAMPLE(smp))
smp = -1; smp = -1;
if (smp >= 0 && smp < mod->smp) { if (smp >= 0 && smp < mod->smp) {
set_patch(ctx, chn, xc->ins, smp, note); set_patch(ctx, chn, xc->ins, smp, note);

View file

@ -20,6 +20,9 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#ifndef LIBXMP_LOADERS_S3M_H
#define LIBXMP_LOADERS_S3M_H
/* S3M packed pattern macros */ /* S3M packed pattern macros */
#define S3M_EOR 0 /* End of row */ #define S3M_EOR 0 /* End of row */
#define S3M_CH_MASK 0x1f /* Channel */ #define S3M_CH_MASK 0x1f /* Channel */
@ -27,10 +30,16 @@
#define S3M_VOL_FOLLOWS 0x40 /* Volume follows */ #define S3M_VOL_FOLLOWS 0x40 /* Volume follows */
#define S3M_FX_FOLLOWS 0x80 /* Effect and parameter follow */ #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 */ /* S3M channel info macros */
#define S3M_CH_ON 0x80 /* Psi says it's bit 8, I'll assume bit 7 */ #define S3M_CH_ON 0x80 /* Psi says it's bit 8, I'll assume bit 7 */
#define S3M_CH_OFF 0xff #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 */ /* S3M channel pan macros */
#define S3M_PAN_SET 0x20 #define S3M_PAN_SET 0x20
@ -79,7 +88,8 @@ struct s3m_file_header {
}; };
struct s3m_instrument_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 */ uint16 memseg; /* Pointer to sample data */
uint32 length; /* Length */ uint32 length; /* Length */
uint32 loopbeg; /* Loop begin */ uint32 loopbeg; /* Loop begin */
@ -113,4 +123,4 @@ struct s3m_adlib_header {
uint32 magic; /* 'SCRI' */ uint32 magic; /* 'SCRI' */
}; };
#endif #endif
#endif /* LIBXMP_LOADERS_S3M_H */

View file

@ -1,5 +1,5 @@
/* Extended Module Player /* 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 * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * 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; uint8 n, b;
uint16 *pp_ins; /* Parapointers to instruments */ uint16 *pp_ins; /* Parapointers to instruments */
uint16 *pp_pat; /* Parapointers to patterns */ uint16 *pp_pat; /* Parapointers to patterns */
int stereo;
int ret; int ret;
uint8 buf[96] 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); 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) { if (pp_ins == NULL) {
goto err; goto err;
} }
pp_pat = calloc(2, sfh.patnum); pp_pat = (uint16 *) calloc(sfh.patnum, sizeof(uint16));
if (pp_pat == NULL) { if (pp_pat == NULL) {
goto err2; 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->bpm = sfh.it;
mod->chn = 0; 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++) { for (i = 0; i < 32; i++) {
int x;
if (sfh.chset[i] == S3M_CH_OFF) if (sfh.chset[i] == S3M_CH_OFF)
continue; continue;
mod->chn = i + 1; mod->chn = i + 1;
if (sfh.mv & 0x80) { /* stereo */ x = sfh.chset[i] & S3M_CH_NUMBER;
int x = sfh.chset[i] & S3M_CH_PAN; if (stereo && x < S3M_CH_ADLIB) {
mod->xxc[i].pan = (x & 0x0f) < 8 ? 0x30 : 0xc0; mod->xxc[i].pan = x < S3M_CH_RIGHT ? 0x30 : 0xc0;
} else { } else {
mod->xxc[i].pan = 0x80; 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); uint8 x = hio_read8(f);
if (x & S3M_PAN_SET) { if (x & S3M_PAN_SET) {
mod->xxc[i].pan = (x << 4) & 0xff; 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; break;
case 5: case 5:
snprintf(tracker_name, 40, "OpenMPT %d.%02x", if (sfh.version == 0x5447) {
(sfh.version & 0x0f00) >> 8, sfh.version & 0xff); 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; m->quirk |= QUIRK_ST3BUGS;
break; break;
case 4: 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"); D_(D_INFO "Pan settings: %s", sfh.dp ? "no" : "yes");
if (libxmp_init_instrument(m) < 0) 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_sample *xxs = &mod->xxs[i];
struct xmp_subinstrument *sub; struct xmp_subinstrument *sub;
int load_sample_flags; 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) { if (xxi->sub == NULL) {
goto err3; goto err3;
} }
@ -527,9 +567,7 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
sub->vol = sah.vol; sub->vol = sah.vol;
libxmp_c2spd_to_note(sah.c2spd, &sub->xpo, &sub->fin); libxmp_c2spd_to_note(sah.c2spd, &sub->xpo, &sub->fin);
sub->xpo += 12; sub->xpo += 12;
ret = ret = libxmp_load_sample(m, f, SAMPLE_FLAG_ADLIB, xxs, (char *)sah.reg);
libxmp_load_sample(m, f, SAMPLE_FLAG_ADLIB, xxs,
(char *)sah.reg);
if (ret < 0) if (ret < 0)
goto err3; goto err3;
@ -541,7 +579,8 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
#endif #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.memseg = readmem16l(buf + 14); /* Pointer to sample data */
sih.length = readmem32l(buf + 16); /* Length */ 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); 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; goto err3;
} }

View file

@ -1,5 +1,5 @@
/* Extended Module Player /* 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 * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
@ -24,7 +24,6 @@
#include "loader.h" #include "loader.h"
#ifndef LIBXMP_CORE_PLAYER #ifndef LIBXMP_CORE_PLAYER
/* /*
* From the Audio File Formats (version 2.5) * From the Audio File Formats (version 2.5)
* Submitted-by: Guido van Rossum <guido@cwi.nl> * Submitted-by: Guido van Rossum <guido@cwi.nl>
@ -55,7 +54,6 @@ static const int8 vdic_table[128] = {
/* 120 */ 95, 98, 103, 109, 114, 120, 126, 127 /* 120 */ 95, 98, 103, 109, 114, 120, 126, 127
}; };
/* Convert 7 bit samples to 8 bit */ /* Convert 7 bit samples to 8 bit */
static void convert_7bit_to_8bit(uint8 *p, int l) 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; *outp++ = delta;
} }
} }
#endif #endif
/* Convert differential to absolute sample data */ /* Convert differential to absolute sample data */
static void convert_delta(uint8 *p, int l, int r) static void convert_delta(uint8 *p, int l, int r)
{ {
uint16 *w = (uint16 *)p; uint16 *w = (uint16 *)p;
uint16 abs = 0; uint16 absval = 0;
if (r) { if (r) {
for (; l--;) { for (; l--;) {
abs = *w + abs; absval = *w + absval;
*w++ = abs; *w++ = absval;
} }
} else { } else {
for (; l--;) { for (; l--;) {
abs = *p + abs; absval = *p + absval;
*p++ = (uint8) abs; *p++ = (uint8) absval;
} }
} }
} }
@ -163,41 +160,10 @@ static void convert_stereo_to_mono(uint8 *p, int l, int r)
} }
#endif #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 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 #ifndef LIBXMP_CORE_PLAYER
/* Adlib FM patches */ /* 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; bytelen = xxs->len;
extralen = 4; extralen = 4;
unroll_extralen = 0;
/* Disable birectional loop flag if sample is not looped /* 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) if (~xxs->flg & XMP_SAMPLE_LOOP)
xxs->flg &= ~XMP_SAMPLE_LOOP_BIDIR; xxs->flg &= ~XMP_SAMPLE_LOOP_BIDIR;
} }
/* Unroll bidirectional loops if (xxs->flg & XMP_SAMPLE_SLOOP_BIDIR) {
*/ if (~xxs->flg & XMP_SAMPLE_SLOOP)
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) { xxs->flg &= ~XMP_SAMPLE_SLOOP_BIDIR;
unroll_extralen = (xxs->lpe - xxs->lps) -
(xxs->len - xxs->lpe);
if (unroll_extralen < 0) {
unroll_extralen = 0;
}
} }
if (xxs->flg & XMP_SAMPLE_16BIT) { if (xxs->flg & XMP_SAMPLE_16BIT) {
bytelen *= 2; bytelen *= 2;
extralen *= 2; extralen *= 2;
unroll_extralen *= 2;
} }
/* add guard bytes before the buffer for higher order interpolation */ /* 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) { if (xxs->data == NULL) {
goto err; 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; 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 */ /* Add extra samples at end */
if (xxs->flg & XMP_SAMPLE_16BIT) { if (xxs->flg & XMP_SAMPLE_16BIT) {
for (i = 0; i < 8; i++) { 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]; 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; return 0;
#ifndef LIBXMP_CORE_PLAYER #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) void libxmp_free_sample(struct xmp_sample *s)
{ {
if (s->data) { if (s->data) {
free(s->data - 4); free(s->data - 4);
s->data = NULL; /* prevent double free in PCM load error */ s->data = NULL; /* prevent double free in PCM load error */
} }
} }

View file

@ -1,5 +1,5 @@
/* Extended Module Player /* 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 * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
@ -42,6 +42,12 @@
#include "effects.h" #include "effects.h"
#include "mixer.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_END 0xff
#define S3M_SKIP 0xfe #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 player_data *p = &ctx->p;
struct module_data *m = &ctx->m; 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 parm, gvol_memory, f1, f2, p1, p2, ord, ord2;
int row, last_row, break_row, row_count, row_count_total; int row, last_row, break_row, row_count, row_count_total;
int orders_since_last_valid, any_valid; int orders_since_last_valid, any_valid;
int gvl, bpm, speed, base_time, chn; int gvl, bpm, speed, base_time, chn;
int frame_count; int frame_count;
double time, start_time; double time, start_time;
int loop_chn, loop_num, inside_loop; int loop_chn, loop_num, inside_loop, line_jump;
int pdelay = 0; int pdelay = 0;
int loop_count[XMP_MAX_CHANNELS]; int loop_count[XMP_MAX_CHANNELS];
int loop_row[XMP_MAX_CHANNELS]; int loop_row[XMP_MAX_CHANNELS];
struct xmp_event* event;
int i, pat; int i, pat;
int has_marker; int has_marker;
struct ord_data *info; struct ord_data *info;
#ifndef LIBXMP_CORE_PLAYER #ifndef LIBXMP_CORE_PLAYER
int st26_speed; int st26_speed;
int far_tempo_coarse, far_tempo_fine, far_tempo_mode;
#endif #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) if (mod->len == 0)
return 0; return 0;
for (i = 0; i < mod->len; i++) { 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 : memset(m->scan_cnt[i], 0, pat >= mod->pat ? 1 :
mod->xxp[pat]->rows ? mod->xxp[pat]->rows : 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_num = 0;
loop_chn = -1; loop_chn = -1;
line_jump = 0;
gvl = mod->gvl; gvl = mod->gvl;
bpm = mod->bpm; bpm = mod->bpm;
@ -92,6 +104,15 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
base_time = m->rrate; base_time = m->rrate;
#ifndef LIBXMP_CORE_PLAYER #ifndef LIBXMP_CORE_PLAYER
st26_speed = 0; 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 #endif
has_marker = HAS_QUIRK(QUIRK_MARKER); 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; 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; last_row = mod->xxp[pat]->rows;
for (row = break_row, break_row = 0; row < last_row; row++, row_count++, row_count_total++) { for (row = break_row, break_row = 0; row < last_row; row++, row_count++, row_count_total++) {
/* Prevent crashes caused by large softmixer frames */ /* 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 * (...) 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); D_(D_CRIT "row_count_total = %d @ ord %d, pat %d, row %d; ending scan", row_count_total, ord, pat, row);
goto end_module; goto end_module;
} }
if (!loop_num && m->scan_cnt[ord][row]) { if (!loop_num && !line_jump && m->scan_cnt[ord][row]) {
row_count--; row_count--;
goto end_module; goto end_module;
} }
@ -237,12 +263,14 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
} }
pdelay = 0; pdelay = 0;
line_jump = 0;
for (chn = 0; chn < mod->chn; chn++) { for (chn = 0; chn < mod->chn; chn++) {
if (row >= mod->xxt[mod->xxp[pat]->index[chn]]->rows) if (row >= tracks[chn]->rows)
continue; continue;
event = &EVENT(mod->xxo[ord], chn, row); //event = &EVENT(mod->xxo[ord], chn, row);
event = &tracks[chn]->event[row];
f1 = event->fxt; f1 = event->fxt;
p1 = event->fxp; 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)) { /* Some formats can have two FX_SPEED effects, and both need
parm = (f1 == FX_SPEED) ? p1 : p2; * 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; frame_count += row_count * speed;
row_count = 0; row_count = 0;
if (parm) { if (HAS_QUIRK(QUIRK_NOBPM) || p->flags & XMP_FLAGS_VBLANK || parm < 0x20) {
if (HAS_QUIRK(QUIRK_NOBPM) || p->flags & XMP_FLAGS_VBLANK || parm < 0x20) { speed = parm;
if (parm > 0) {
speed = parm;
#ifndef LIBXMP_CORE_PLAYER #ifndef LIBXMP_CORE_PLAYER
st26_speed = 0; st26_speed = 0;
#endif #endif
} } else {
} else { time += m->time_factor * frame_count * base_time / bpm;
time += m->time_factor * frame_count * base_time / bpm; frame_count = 0;
frame_count = 0; bpm = parm;
bpm = parm;
}
} }
} }
@ -327,6 +355,39 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
st26_speed = MSN(p1); 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 #endif
if ((f1 == FX_S3M_SPEED && p1) || (f2 == FX_S3M_SPEED && p2)) { 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)) { if ((f1 == FX_S3M_BPM && p1) || (f2 == FX_S3M_BPM && p2)) {
parm = (f1 == FX_S3M_BPM) ? p1 : p2; parm = (f1 == FX_S3M_BPM) ? p1 : p2;
if (parm >= 0x20) { if (parm >= XMP_MIN_BPM) {
frame_count += row_count * speed; frame_count += row_count * speed;
row_count = 0; row_count = 0;
time += m->time_factor * frame_count * base_time / bpm; 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; 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) { if (f1 == FX_EXTENDED || f2 == FX_EXTENDED) {
parm = (f1 == FX_EXTENDED) ? p1 : p2; parm = (f1 == FX_EXTENDED) ? p1 : p2;
@ -514,6 +588,56 @@ end_module:
return (time + m->time_factor * frame_count * base_time / bpm); 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) int libxmp_get_sequence(struct context_data *ctx, int ord)
{ {
struct player_data *p = &ctx->p; 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 player_data *p = &ctx->p;
struct module_data *m = &ctx->m; struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod; struct xmp_module *mod = &m->mod;
struct scan_data *s;
int i, ep; int i, ep;
int seq; int seq;
unsigned char temp_ep[XMP_MAX_MOD_LENGTH]; 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 /* Initialize order data to prevent overwrite when a position is used
* multiple times at different starting points (see janosik.xm). * multiple times at different starting points (see janosik.xm).
*/ */
for (i = 0; i < XMP_MAX_MOD_LENGTH; i++) { reset_scan_data(ctx);
m->xxo_info[i].time = -1;
}
ep = 0; ep = 0;
memset(p->sequence_control, 0xff, XMP_MAX_MOD_LENGTH);
temp_ep[0] = 0; temp_ep[0] = 0;
p->scan[0].time = scan_module(ctx, ep, 0); p->scan[0].time = scan_module(ctx, ep, 0);
seq = 1; 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) { if (p->scan[0].time < 0) {
D_(D_CRIT "scan was not able to find any valid orders"); D_(D_CRIT "scan was not able to find any valid orders");
return -1; 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; m->num_sequences = seq;
/* Now place entry points in the public accessible array */ /* 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; m->seq_data[i].duration = p->scan[i].time;
} }
return 0; return 0;
} }

View file

@ -1,5 +1,5 @@
/* Extended Module Player /* 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 * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * 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_module *mod = &m->mod;
struct xmp_instrument *xxi; struct xmp_instrument *xxi;
if (ins < mod->ins) { if (ins < 0) {
xxi = NULL;
} else if (ins < mod->ins) {
xxi = &mod->xxi[ins]; xxi = &mod->xxi[ins];
} else if (ins < mod->ins + smix->ins) { } else if (ins < mod->ins + smix->ins) {
xxi = &smix->xxi[ins - mod->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_module *mod = &m->mod;
struct xmp_sample *xxs; struct xmp_sample *xxs;
if (smp < mod->smp) { if (smp < 0) {
xxs = NULL;
} else if (smp < mod->smp) {
xxs = &mod->xxs[smp]; xxs = &mod->xxs[smp];
} else if (smp < mod->smp + smix->smp) { } else if (smp < mod->smp + smix->smp) {
xxs = &smix->xxs[smp - mod->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; 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) { if (smix->xxi == NULL) {
goto err; 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) { if (smix->xxs == NULL) {
goto err1; 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; 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; 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]; event = &p->inject_event[mod->chn + chn];
memset(event, 0, sizeof (struct xmp_event)); 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->ins = ins + 1;
event->vol = vol + 1; event->vol = vol + 1;
event->_flag = 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; 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; 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]; event = &p->inject_event[mod->chn + chn];
memset(event, 0, sizeof (struct xmp_event)); 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->ins = mod->ins + ins + 1;
event->vol = vol + 1; event->vol = vol + 1;
event->_flag = 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; retval = -XMP_ERROR_SYSTEM;
goto err; goto err;
} }
/* Init instrument */ /* 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) { if (xxi->sub == NULL) {
retval = -XMP_ERROR_SYSTEM; retval = -XMP_ERROR_SYSTEM;
goto err1; goto err1;
@ -264,7 +268,7 @@ int xmp_smix_load_sample(xmp_context opaque, int num, const char *path)
xxs->lpe = 0; xxs->lpe = 0;
xxs->flg = bits == 16 ? XMP_SAMPLE_16BIT : 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) { if (xxs->data == NULL) {
retval = -XMP_ERROR_SYSTEM; retval = -XMP_ERROR_SYSTEM;
goto err2; goto err2;
@ -286,7 +290,7 @@ int xmp_smix_load_sample(xmp_context opaque, int num, const char *path)
hio_close(h); hio_close(h);
return 0; return 0;
err2: err2:
free(xxi->sub); free(xxi->sub);
xxi->sub = NULL; xxi->sub = NULL;

View file

@ -1,5 +1,5 @@
/* Extended Module Player /* 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 * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
@ -20,7 +20,6 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include <limits.h>
#include "common.h" #include "common.h"
#include "virtual.h" #include "virtual.h"
#include "mixer.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.maxvoc = libxmp_mixer_numvoices(ctx, num);
p->virt.voice_array = calloc(p->virt.maxvoc, p->virt.voice_array = (struct mixer_voice *) calloc(p->virt.maxvoc,
sizeof(struct mixer_voice)); sizeof(struct mixer_voice));
if (p->virt.voice_array == NULL) if (p->virt.voice_array == NULL)
goto err; goto err;
@ -116,7 +115,7 @@ int libxmp_virt_on(struct context_data *ctx, int num)
/* Initialize Paula simulator */ /* Initialize Paula simulator */
if (IS_AMIGA_MOD()) { if (IS_AMIGA_MOD()) {
for (i = 0; i < p->virt.maxvoc; i++) { 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) { if (p->virt.voice_array[i].paula == NULL) {
goto err2; goto err2;
} }
@ -125,8 +124,8 @@ int libxmp_virt_on(struct context_data *ctx, int num)
} }
#endif #endif
p->virt.virt_channel = malloc(p->virt.virt_channels * p->virt.virt_channel = (struct virt_channel *) malloc(p->virt.virt_channels *
sizeof(struct virt_channel)); sizeof(struct virt_channel));
if (p->virt.virt_channel == NULL) if (p->virt.virt_channel == NULL)
goto err2; goto err2;
@ -190,7 +189,7 @@ void libxmp_virt_reset(struct context_data *ctx)
} }
/* CID 129203 (#1 of 1): Useless call (USELESS_CALL) /* 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); * 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); 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) void libxmp_virt_setpan(struct context_data *ctx, int chn, int pan)
{ {
struct player_data *p = &ctx->p; 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) void libxmp_virt_setnna(struct context_data *ctx, int chn, int nna)
{ {
struct player_data *p = &ctx->p; struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
int voc; int voc;
if (!HAS_QUIRK(QUIRK_VIRTUAL)) {
return;
}
if ((voc = map_virt_channel(p, chn)) < 0) { if ((voc = map_virt_channel(p, chn)) < 0) {
return; 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, 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 player_data *p = &ctx->p;
struct mixer_voice *vi = &p->virt.voice_array[i]; 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 (vi->root == chn && vi->ins == ins) {
if (nna == XMP_INST_NNA_CUT) { if (nna == XMP_INST_NNA_CUT) {
libxmp_virt_resetvoice(ctx, i, 1); libxmp_virt_resetvoice(ctx, i, 1);
return; return;
} }
vi->act = nna; vi->act = nna;
if ((dct == XMP_INST_DCT_INST) || if ((dct == XMP_INST_DCT_INST) ||
(dct == XMP_INST_DCT_SMP && vi->smp == smp) || (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) { if (nna == XMP_INST_NNA_OFF && dca == XMP_INST_DCA_FADE) {
vi->act = VIRT_ACTION_OFF; 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 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; struct player_data *p = &ctx->p;
int voc, vfree; int voc, vfree;
@ -497,7 +513,7 @@ int libxmp_virt_setpatch(struct context_data *ctx, int chn, int ins, int smp,
int i; int i;
for (i = 0; i < p->virt.maxvoc; 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 #endif
@ -512,7 +528,7 @@ int libxmp_virt_setpatch(struct context_data *ctx, int chn, int ins, int smp,
return -1; 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.virt_channel[chn++].map > FREE;) ;
p->virt.voice_array[voc].chn = --chn; 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); libxmp_mixer_setnote(ctx, voc, note);
p->virt.voice_array[voc].ins = ins; p->virt.voice_array[voc].ins = ins;
p->virt.voice_array[voc].act = nna; p->virt.voice_array[voc].act = nna;
p->virt.voice_array[voc].key = key;
return chn; return chn;
} }

View file

@ -15,7 +15,7 @@ int libxmp_virt_on (struct context_data *, int);
void libxmp_virt_off (struct context_data *); void libxmp_virt_off (struct context_data *);
int libxmp_virt_mute (struct context_data *, int, int); int libxmp_virt_mute (struct context_data *, int, int);
int libxmp_virt_setpatch (struct context_data *, int, int, 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); int libxmp_virt_cvt8bit (void);
void libxmp_virt_setnote (struct context_data *, int, int); void libxmp_virt_setnote (struct context_data *, int, int);
void libxmp_virt_setsmp (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_resetvoice (struct context_data *, int, int);
void libxmp_virt_reset (struct context_data *); void libxmp_virt_reset (struct context_data *);
void libxmp_virt_release (struct context_data *, int, int); 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); int libxmp_virt_getroot (struct context_data *, int);
#endif /* LIBXMP_VIRTUAL_H */ #endif /* LIBXMP_VIRTUAL_H */

View file

@ -31,3 +31,17 @@ int libxmp_snprintf (char *str, size_t sz, const char *fmt, ...)
} }
#endif #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

View file

@ -14,12 +14,12 @@
#define XM_LOOP_FORWARD 1 #define XM_LOOP_FORWARD 1
#define XM_LOOP_PINGPONG 2 #define XM_LOOP_PINGPONG 2
#define XM_SAMPLE_16BIT 0x10 #define XM_SAMPLE_16BIT 0x10
#define XM_SAMPLE_STEREO 0x20
#define XM_ENVELOPE_ON 0x01 #define XM_ENVELOPE_ON 0x01
#define XM_ENVELOPE_SUSTAIN 0x02 #define XM_ENVELOPE_SUSTAIN 0x02
#define XM_ENVELOPE_LOOP 0x04 #define XM_ENVELOPE_LOOP 0x04
#define XM_LINEAR_PERIOD_MODE 0x01 #define XM_LINEAR_PERIOD_MODE 0x01
struct xm_file_header { struct xm_file_header {
uint8 id[17]; /* ID text: "Extended module: " */ uint8 id[17]; /* ID text: "Extended module: " */
uint8 name[20]; /* Module name, padded with zeroes */ uint8 name[20]; /* Module name, padded with zeroes */
@ -98,4 +98,4 @@ struct xm_event {
uint8 fx_parm; /* Effect parameter */ uint8 fx_parm; /* Effect parameter */
}; };
#endif #endif /* LIBXMP_LOADERS_XM_H */

View file

@ -1,5 +1,5 @@
/* Extended Module Player /* 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 * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
@ -35,6 +35,9 @@
#include "loader.h" #include "loader.h"
#include "xm.h" #include "xm.h"
#ifndef LIBXMP_CORE_PLAYER
#include "vorbis.h"
#endif
static int xm_test(HIO_HANDLE *, char *, const int); static int xm_test(HIO_HANDLE *, char *, const int);
static int xm_load(struct module_data *, HIO_HANDLE *, 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; 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; const int headsize = version > 0x0102 ? 9 : 8;
struct xmp_module *mod = &m->mod; struct xmp_module *mod = &m->mod;
struct xm_pattern_header xph; struct xm_pattern_header xph;
struct xmp_event *event; struct xmp_event *event;
uint8 *patbuf, *pat, b; uint8 *pat, b;
int j, r; int j, k, r;
int size; int size, size_read;
xph.length = hio_read32l(f); xph.length = hio_read32l(f);
xph.packing = hio_read8(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; size = xph.datasize;
pat = patbuf;
pat = patbuf = calloc(1, size); size_read = hio_read(patbuf, 1, size, f);
if (patbuf == NULL) { if (size_read < size) {
goto err; memset(patbuf + size_read, 0, size - size_read);
} }
hio_read(patbuf, 1, size, f); for (j = 0; j < r; j++) {
for (j = 0; j < (mod->chn * r); j++) { for (k = 0; k < mod->chn; k++) {
/*
if ((pat - patbuf) >= xph.datasize)
break;
*/
/*if ((pat - patbuf) >= xph.datasize) event = &EVENT(num, k, j);
break; */
event = &EVENT(num, j % mod->chn, j / mod->chn); if (--size < 0) {
goto err;
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 (b & XM_EVENT_INSTRUMENT_FOLLOWS) {
if (--size < 0) if ((b = *pat++) & XM_EVENT_PACKING) {
goto err2; 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++; event->ins = *pat++;
}
if (b & XM_EVENT_VOLUME_FOLLOWS) {
if (--size < 0)
goto err2;
event->vol = *pat++; event->vol = *pat++;
}
if (b & XM_EVENT_FXTYPE_FOLLOWS) {
if (--size < 0)
goto err2;
event->fxt = *pat++; event->fxt = *pat++;
}
if (b & XM_EVENT_FXPARM_FOLLOWS) {
if (--size < 0)
goto err2;
event->fxp = *pat++; 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 */ /* Sanity check */
switch (event->fxt) { switch (event->fxt) {
case 18: case 18:
case 19: case 19:
case 22: case 22:
case 23: case 23:
case 24: case 24:
case 26: case 26:
case 28: case 28:
case 30: case 30:
case 31: case 31:
case 32: case 32:
event->fxt = 0; 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;
} }
} else if (event->note > 0) { if (event->fxt > 34) {
event->note += 12; event->fxt = 0;
}
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: if (event->note == 0x61) {
case 0x73: /* See OpenMPT keyoff+instr.xm test case */
event->fxp--; 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; 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; return 0;
err2:
free(patbuf);
err: err:
return -1; return -1;
} }
@ -299,6 +317,7 @@ err:
static int load_patterns(struct module_data *m, int version, HIO_HANDLE *f) static int load_patterns(struct module_data *m, int version, HIO_HANDLE *f)
{ {
struct xmp_module *mod = &m->mod; struct xmp_module *mod = &m->mod;
uint8 *patbuf;
int i, j; int i, j;
mod->pat++; 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); 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++) { 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; goto err;
} }
} }
@ -333,21 +356,100 @@ static int load_patterns(struct module_data *m, int version, HIO_HANDLE *f)
} }
} }
free(patbuf);
return 0; return 0;
err: err:
free(patbuf);
return -1; return -1;
} }
/* Packed structures size */ /* Packed structures size */
#define XM_INST_HEADER_SIZE 33 #define XM_INST_HEADER_SIZE 29
#define XM_INST_SIZE 208 #define XM_INST_SIZE 212
/* grass.near.the.house.xm defines 23 samples in instrument 1. FT2 docs /* 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 * specify at most 16. See https://github.com/libxmp/libxmp/issues/168
* for more details. */ * for more details. */
#define XM_MAX_SAMPLES_PER_INST 32 #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) static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
{ {
struct xmp_module *mod = &m->mod; 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 * instruments, but file may end abruptly before that. Also covers
* XMLiTE stripped modules and truncated files. This test will not * XMLiTE stripped modules and truncated files. This test will not
* work if file has trailing garbage. * 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"); D_(D_WARN "short read in instrument header data");
break; 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 */ xih.sh_size = readmem32l(buf + 29); /* Sample header size */
/* Sanity check */ /* 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)) { 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); D_(D_CRIT "instrument %d: samples:%d sample header size:%d", i + 1, xih.samples, xih.sh_size);
return -1; 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. * 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; return -1;
} }
@ -427,17 +538,13 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
return -1; return -1;
} }
if (xih.size < XM_INST_HEADER_SIZE) {
return -1;
}
/* for BoobieSqueezer (see http://boobie.rotfl.at/) /* for BoobieSqueezer (see http://boobie.rotfl.at/)
* It works pretty much the same way as Impulse Tracker's sample * It works pretty much the same way as Impulse Tracker's sample
* only mode, where it will strip off the instrument data. * only mode, where it will strip off the instrument data.
*/ */
if (xih.size < XM_INST_HEADER_SIZE + XM_INST_SIZE) { if (xih.size < XM_INST_HEADER_SIZE + XM_INST_SIZE) {
memset(&xi, 0, sizeof(struct xm_instrument)); 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 { } else {
uint8 *b = buf; 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++) { for (j = 12; j < 108; j++) {
xxi->map[j].ins = xi.sample[j - 12]; xxi->map[j].ins = xi.sample[j - 12];
if (xxi->map[j].ins >= xxi->nsm) 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->lps >>= 1;
xxs->lpe >>= 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_FORWARD ? XMP_SAMPLE_LOOP : 0;
xxs->flg |= xsh[j].type & XM_LOOP_PINGPONG ? XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_BIDIR : 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; total_sample_size = 0;
for (j = 0; j < xxi->nsm; j++) { for (j = 0; j < xxi->nsm; j++) {
struct xmp_subinstrument *sub = &xxi->sub[j]; struct xmp_subinstrument *sub = &xxi->sub[j];
struct xmp_sample *xxs = &mod->xxs[sub->sid];
int flags; int flags;
flags = SAMPLE_FLAG_DIFF; flags = SAMPLE_FLAG_DIFF;
@ -606,7 +720,20 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
if (version > 0x0103) { if (version > 0x0103) {
D_(D_INFO " read sample: index:%d sample id:%d", j, sub->sid); 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; return -1;
} }
if (flags & SAMPLE_FLAG_ADPCM) { if (flags & SAMPLE_FLAG_ADPCM) {
@ -615,6 +742,12 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
} else { } else {
total_sample_size += xsh[j].length; 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; int i, j;
struct xm_file_header xfh; struct xm_file_header xfh;
char tracker_name[21]; char tracker_name[21];
#ifndef LIBXMP_CORE_PLAYER
int claims_ft2 = 0;
int is_mpt_116 = 0;
#endif
int len; int len;
uint8 buf[80]; 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 */ xfh.bpm = readmem16l(buf + 78); /* Default BPM */
/* Sanity checks */ /* Sanity checks */
if (xfh.songlen > 256 || xfh.patterns > 256 || xfh.instruments > 255) { if (xfh.songlen > 256) {
D_(D_CRIT "Sanity check: %d %d %d", xfh.songlen, xfh.patterns, xfh.instruments); 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; return -1;
} }
if (xfh.restart > 255 || xfh.channels > XMP_MAX_CHANNELS) { if (xfh.restart > 255) {
D_(D_CRIT "Sanity check: %d %d", xfh.restart, xfh.channels); 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; 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)) { 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; 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 */ /* Honor header size -- needed by BoobieSqueezer XMs */
len = xfh.headersz - 0x14; len = xfh.headersz - 0x14;
if (len < 0 || len > 256) { if (len < 0 || len > 256) {
D_(D_CRIT "Sanity check: %d", len); D_(D_CRIT "bad XM header length: %d", len);
return -1; return -1;
} }
memset(xfh.order, 0, sizeof(xfh.order));
if (hio_read(xfh.order, len, 1, f) != 1) { /* Pattern order table */ if (hio_read(xfh.order, len, 1, f) != 1) { /* Pattern order table */
D_(D_CRIT "error reading orders"); D_(D_CRIT "error reading orders");
return -1; 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 */ /* OpenMPT accurately emulates weird FT2 bugs */
if (!strncmp(tracker_name, "FastTracker v2.00", 17) || if (!strncmp(tracker_name, "FastTracker v2.00", 17)) {
!strncmp(tracker_name, "OpenMPT ", 8)) { m->quirk |= QUIRK_FT2BUGS;
#ifndef LIBXMP_CORE_PLAYER
claims_ft2 = 1;
#endif
} else if (!strncmp(tracker_name, "OpenMPT ", 8)) {
m->quirk |= QUIRK_FT2BUGS; m->quirk |= QUIRK_FT2BUGS;
} }
#ifndef LIBXMP_CORE_PLAYER #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)) { if (!strncmp(tracker_name, "FastTracker v 2.00", 18)) {
strcpy(tracker_name, "old ModPlug Tracker"); strcpy(tracker_name, "old ModPlug Tracker");
m->quirk &= ~QUIRK_FT2BUGS; m->quirk &= ~QUIRK_FT2BUGS;
is_mpt_116 = 1;
} }
libxmp_set_type(m, "%s XM %d.%02d", tracker_name, xfh.version >> 8, xfh.version & 0xff); 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++) { for (i = 0; i < mod->chn; i++) {
mod->xxc[i].pan = 0x80; mod->xxc[i].pan = 0x80;
} }

View file

@ -1,39 +1,56 @@
#ifndef XMP_H #ifndef XMP_H
#define XMP_H #define XMP_H
#if defined(EMSCRIPTEN)
# include <emscripten.h>
#endif
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#define XMP_VERSION "4.5.0" #define XMP_VERSION "4.6.0"
#define XMP_VERCODE 0x040500 #define XMP_VERCODE 0x040600
#define XMP_VER_MAJOR 4 #define XMP_VER_MAJOR 4
#define XMP_VER_MINOR 5 #define XMP_VER_MINOR 6
#define XMP_VER_RELEASE 0 #define XMP_VER_RELEASE 0
#if defined(_WIN32) && !defined(__CYGWIN__) #if defined(_WIN32) && !defined(__CYGWIN__)
# if defined(BUILDING_STATIC) # if defined(LIBXMP_STATIC)
# define LIBXMP_EXPORT # define LIBXMP_EXPORT
# elif defined(BUILDING_DLL) # elif defined(BUILDING_DLL)
# define LIBXMP_EXPORT __declspec(dllexport) # define LIBXMP_EXPORT __declspec(dllexport)
# else # else
# define LIBXMP_EXPORT __declspec(dllimport) # define LIBXMP_EXPORT __declspec(dllimport)
# endif # 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) # define LIBXMP_EXPORT __declspec(dllexport)
# else
# define LIBXMP_EXPORT
# endif
#elif (defined(__GNUC__) || defined(__clang__) || defined(__HP_cc)) && defined(XMP_SYM_VISIBILITY) #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) #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) #elif defined(EMSCRIPTEN)
# include <emscripten.h>
# define LIBXMP_EXPORT EMSCRIPTEN_KEEPALIVE # define LIBXMP_EXPORT EMSCRIPTEN_KEEPALIVE
# define LIBXMP_EXPORT_VAR # define LIBXMP_EXPORT_VAR
#else #else
# define LIBXMP_EXPORT # define LIBXMP_EXPORT
#endif #endif
#if !defined (LIBXMP_EXPORT_VAR) #if !defined(LIBXMP_EXPORT_VAR)
# define LIBXMP_EXPORT_VAR LIBXMP_EXPORT # define LIBXMP_EXPORT_VAR LIBXMP_EXPORT
#endif #endif
@ -238,6 +255,7 @@ struct xmp_sample {
#define XMP_SAMPLE_LOOP_FULL (1 << 4) /* Play full sample before looping */ #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 (1 << 5) /* Sample has sustain loop */
#define XMP_SAMPLE_SLOOP_BIDIR (1 << 6) /* Bidirectional 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 */ #define XMP_SAMPLE_SYNTH (1 << 15) /* Data contains synth patch */
int flg; /* Flags */ int flg; /* Flags */
unsigned char *data; /* Sample data */ unsigned char *data; /* Sample data */

View file

@ -19,7 +19,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#define BUILDING_STATIC 1 #define LIBXMP_STATIC 1
#include "libxmp-lite/xmp.h" #include "libxmp-lite/xmp.h"
#include "vtables.h" #include "vtables.h"