1
1
Fork 0
mirror of https://github.com/QB64-Phoenix-Edition/QB64pe.git synced 2024-07-06 18:40:23 +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 \
dataio.c \
effects.c \
filetype.c \
filter.c \
format.c \
hio.c \
@ -34,7 +35,7 @@ LIBXMP_OBJS += $(patsubst %.c,$(PATH_INTERNAL_C)/parts/audio/extras/libxmp-lite/
LIBXMP_LIB := $(PATH_INTERNAL_C)/parts/audio/extras/libxmp-lite.a
$(PATH_INTERNAL_C)/parts/audio/extras/libxmp-lite/%.o: $(PATH_INTERNAL_C)/parts/audio/extras/libxmp-lite/%.c
$(CC) -O2 $(CFLAGS) -Wall -DLIBXMP_CORE_PLAYER -DLIBXMP_NO_PROWIZARD -DLIBXMP_NO_DEPACKERS -DBUILDING_STATIC $< -c -o $@
$(CC) -O2 $(CFLAGS) -Wall -DLIBXMP_CORE_PLAYER -DLIBXMP_STATIC $< -c -o $@
$(LIBXMP_LIB): $(LIBXMP_OBJS)
$(AR) rcs $@ $^

View file

@ -10,9 +10,7 @@ typedef struct {
int eof;
} CBFILE;
#ifdef __cplusplus
extern "C" {
#endif
LIBXMP_BEGIN_DECLS
static inline uint8 cbread8(CBFILE *f, int *err)
{
@ -33,7 +31,7 @@ static inline int8 cbread8s(CBFILE *f, int *err)
static inline uint16 cbread16l(CBFILE *f, int *err)
{
uint8 buf[2];
uint16 x = EOF;
uint16 x = 0xffff;
size_t r = f->callbacks.read_func(buf, 2, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF;
@ -46,7 +44,7 @@ static inline uint16 cbread16l(CBFILE *f, int *err)
static inline uint16 cbread16b(CBFILE *f, int *err)
{
uint8 buf[2];
uint16 x = EOF;
uint16 x = 0xffff;
size_t r = f->callbacks.read_func(buf, 2, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF;
@ -59,7 +57,7 @@ static inline uint16 cbread16b(CBFILE *f, int *err)
static inline uint32 cbread24l(CBFILE *f, int *err)
{
uint8 buf[3];
uint32 x = EOF;
uint32 x = 0xffffffff;
size_t r = f->callbacks.read_func(buf, 3, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF;
@ -72,7 +70,7 @@ static inline uint32 cbread24l(CBFILE *f, int *err)
static inline uint32 cbread24b(CBFILE *f, int *err)
{
uint8 buf[3];
uint32 x = EOF;
uint32 x = 0xffffffff;
size_t r = f->callbacks.read_func(buf, 3, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF;
@ -85,7 +83,7 @@ static inline uint32 cbread24b(CBFILE *f, int *err)
static inline uint32 cbread32l(CBFILE *f, int *err)
{
uint8 buf[4];
uint32 x = EOF;
uint32 x = 0xffffffff;
size_t r = f->callbacks.read_func(buf, 4, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF;
@ -98,7 +96,7 @@ static inline uint32 cbread32l(CBFILE *f, int *err)
static inline uint32 cbread32b(CBFILE *f, int *err)
{
uint8 buf[4];
uint32 x = EOF;
uint32 x = 0xffffffff;
size_t r = f->callbacks.read_func(buf, 4, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF;
@ -110,7 +108,8 @@ static inline uint32 cbread32b(CBFILE *f, int *err)
static inline size_t cbread(void *dest, size_t len, size_t nmemb, CBFILE *f)
{
size_t r = f->callbacks.read_func(dest, len, nmemb, f->priv);
size_t r = f->callbacks.read_func(dest, (unsigned long)len,
(unsigned long)nmemb, f->priv);
f->eof = (r < nmemb) ? EOF : 0;
return r;
}
@ -183,8 +182,6 @@ static inline int cbclose(CBFILE *f)
return r;
}
#ifdef __cplusplus
}
#endif
LIBXMP_END_DECLS
#endif /* LIBXMP_CALLBACKIO_H */

View file

@ -1,5 +1,5 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
* Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -21,28 +21,14 @@
*/
#include <ctype.h>
#ifndef LIBXMP_CORE_PLAYER
#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)
#if defined(HAVE_DIRENT)
#include <sys/types.h>
#include <dirent.h>
#endif
#endif /* LIBXMP_CORE_PLAYER */
#include "common.h"
#include "xmp.h"
#include "common.h"
#include "period.h"
#include "loader.h"
@ -51,7 +37,7 @@ int libxmp_init_instrument(struct module_data *m)
struct xmp_module *mod = &m->mod;
if (mod->ins > 0) {
mod->xxi = calloc(sizeof (struct xmp_instrument), mod->ins);
mod->xxi = (struct xmp_instrument *) calloc(mod->ins, sizeof(struct xmp_instrument));
if (mod->xxi == NULL)
return -1;
}
@ -65,10 +51,10 @@ int libxmp_init_instrument(struct module_data *m)
return -1;
}
mod->xxs = calloc(sizeof (struct xmp_sample), mod->smp);
mod->xxs = (struct xmp_sample *) calloc(mod->smp, sizeof(struct xmp_sample));
if (mod->xxs == NULL)
return -1;
m->xtra = calloc(sizeof (struct extra_sample_data), mod->smp);
m->xtra = (struct extra_sample_data *) calloc(mod->smp, sizeof(struct extra_sample_data));
if (m->xtra == NULL)
return -1;
@ -103,12 +89,12 @@ int libxmp_realloc_samples(struct module_data *m, int new_size)
return 0;
}
xxs = realloc(mod->xxs, sizeof(struct xmp_sample) * new_size);
xxs = (struct xmp_sample *) realloc(mod->xxs, sizeof(struct xmp_sample) * new_size);
if (xxs == NULL)
return -1;
mod->xxs = xxs;
xtra = realloc(m->xtra, sizeof(struct extra_sample_data) * new_size);
xtra = (struct extra_sample_data *) realloc(m->xtra, sizeof(struct extra_sample_data) * new_size);
if (xtra == NULL)
return -1;
m->xtra = xtra;
@ -133,7 +119,7 @@ int libxmp_alloc_subinstrument(struct xmp_module *mod, int i, int num)
if (num == 0)
return 0;
mod->xxi[i].sub = calloc(sizeof (struct xmp_subinstrument), num);
mod->xxi[i].sub = (struct xmp_subinstrument *) calloc(num, sizeof(struct xmp_subinstrument));
if (mod->xxi[i].sub == NULL)
return -1;
@ -142,11 +128,11 @@ int libxmp_alloc_subinstrument(struct xmp_module *mod, int i, int num)
int libxmp_init_pattern(struct xmp_module *mod)
{
mod->xxt = calloc(sizeof (struct xmp_track *), mod->trk);
mod->xxt = (struct xmp_track **) calloc(mod->trk, sizeof(struct xmp_track *));
if (mod->xxt == NULL)
return -1;
mod->xxp = calloc(sizeof (struct xmp_pattern *), mod->pat);
mod->xxp = (struct xmp_pattern **) calloc(mod->pat, sizeof(struct xmp_pattern *));
if (mod->xxp == NULL)
return -1;
@ -159,8 +145,8 @@ int libxmp_alloc_pattern(struct xmp_module *mod, int num)
if (num < 0 || num >= mod->pat || mod->xxp[num] != NULL)
return -1;
mod->xxp[num] = calloc(1, sizeof (struct xmp_pattern) +
sizeof (int) * (mod->chn - 1));
mod->xxp[num] = (struct xmp_pattern *) calloc(1, sizeof(struct xmp_pattern) +
sizeof(int) * (mod->chn - 1));
if (mod->xxp[num] == NULL)
return -1;
@ -173,8 +159,8 @@ int libxmp_alloc_track(struct xmp_module *mod, int num, int rows)
if (num < 0 || num >= mod->trk || mod->xxt[num] != NULL || rows <= 0)
return -1;
mod->xxt[num] = calloc(sizeof (struct xmp_track) +
sizeof (struct xmp_event) * (rows - 1), 1);
mod->xxt[num] = (struct xmp_track *) calloc(1, sizeof(struct xmp_track) +
sizeof(struct xmp_event) * (rows - 1));
if (mod->xxt[num] == NULL)
return -1;
@ -218,6 +204,7 @@ int libxmp_alloc_pattern_tracks(struct xmp_module *mod, int num, int rows)
return 0;
}
#ifndef LIBXMP_CORE_PLAYER
/* Some formats explicitly allow more than 256 rows (e.g. OctaMED). This function
* allows those formats to work without disrupting the sanity check for other formats.
*/
@ -237,6 +224,7 @@ int libxmp_alloc_pattern_tracks_long(struct xmp_module *mod, int num, int rows)
return 0;
}
#endif
char *libxmp_instrument_name(struct xmp_module *mod, int i, uint8 *r, int n)
{
@ -253,7 +241,7 @@ char *libxmp_copy_adjust(char *s, uint8 *r, int n)
strncpy(s, (char *)r, n);
for (i = 0; s[i] && i < n; i++) {
if (!isprint((int)s[i]) || ((uint8)s[i] > 127))
if (!isprint((unsigned char)s[i]) || ((uint8)s[i] > 127))
s[i] = '.';
}
@ -267,7 +255,7 @@ void libxmp_read_title(HIO_HANDLE *f, char *t, int s)
{
uint8 buf[XMP_NAME_SIZE];
if (t == NULL)
if (t == NULL || s < 0)
return;
if (s >= XMP_NAME_SIZE)
@ -275,22 +263,27 @@ void libxmp_read_title(HIO_HANDLE *f, char *t, int s)
memset(t, 0, s + 1);
hio_read(buf, 1, s, f); /* coverity[check_return] */
s = hio_read(buf, 1, s, f);
buf[s] = 0;
libxmp_copy_adjust(t, buf, s);
}
#ifndef LIBXMP_CORE_PLAYER
int libxmp_test_name(uint8 *s, int n)
int libxmp_test_name(const uint8 *s, int n, int flags)
{
int i;
for (i = 0; i < n; i++) {
if (s[i] == '\0' && (flags & TEST_NAME_IGNORE_AFTER_0))
break;
if (s[i] == '\r' && (flags & TEST_NAME_IGNORE_AFTER_CR))
break;
if (s[i] > 0x7f)
return -1;
/* ACS_Team2.mod has a backspace in instrument name */
if (s[i] > 0 && s[i] < 32 && s[i] != 0x08)
/* Numerous ST modules from Music Channel BBS have char 14. */
if (s[i] > 0 && s[i] < 32 && s[i] != 0x08 && s[i] != 0x0e)
return -1;
}
@ -309,7 +302,6 @@ int libxmp_copy_name_for_fopen(char *dest, const char *name, int n)
name[0] == '\\' || name[0] == '/' || name[0] == ':')
return -1;
for (i = 0; i < n - 1; i++) {
uint8 t = name[i];
if (!t)
@ -370,7 +362,7 @@ int libxmp_copy_name_for_fopen(char *dest, const char *name, int n)
* module players erroneously interpret as "newer-version-trackers commands".
* Which they aren't.
*/
void libxmp_decode_noisetracker_event(struct xmp_event *event, uint8 *mod_event)
void libxmp_decode_noisetracker_event(struct xmp_event *event, const uint8 *mod_event)
{
int fxt;
@ -388,7 +380,7 @@ void libxmp_decode_noisetracker_event(struct xmp_event *event, uint8 *mod_event)
}
#endif
void libxmp_decode_protracker_event(struct xmp_event *event, uint8 *mod_event)
void libxmp_decode_protracker_event(struct xmp_event *event, const uint8 *mod_event)
{
int fxt = LSN(mod_event[2]);
@ -430,74 +422,45 @@ void libxmp_disable_continue_fx(struct xmp_event *event)
/* libxmp_check_filename_case(): */
/* Given a directory, see if file exists there, ignoring case */
#if defined(_WIN32)
#if defined(_WIN32) || defined(__DJGPP__) || \
defined(__OS2__) || defined(__EMX__) || \
defined(_DOS) || defined(LIBXMP_AMIGA) || \
defined(__riscos__) || \
/* case-insensitive file system: directly probe the file */\
\
!defined(HAVE_DIRENT) /* or, target does not have dirent. */
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
{
char path[_MAX_PATH];
DWORD rc;
/* win32 is case-insensitive: directly probe the file. */
char path[XMP_MAXPATH];
snprintf(path, sizeof(path), "%s/%s", dir, name);
rc = GetFileAttributesA(path);
if (rc == (DWORD)(-1)) return 0;
if (rc & FILE_ATTRIBUTE_DIRECTORY) return 0;
if (! (libxmp_get_filetype(path) & XMP_FILETYPE_FILE))
return 0;
strncpy(new_name, name, size);
return 1;
}
#elif defined(__OS2__) || defined(__EMX__)
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
{
char path[CCHMAXPATH];
FILESTATUS3 fs;
/* os/2 is case-insensitive: directly probe the file. */
snprintf(path, sizeof(path), "%s/%s", dir, name);
if (DosQueryPathInfo(path, FIL_STANDARD, &fs, sizeof(fs)) != NO_ERROR) return 0;
if (fs.attrFile & FILE_DIRECTORY) return 0;
strncpy(new_name, name, size);
return 1;
}
#elif defined(__DJGPP__)
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
{
char path[256];
int attr;
/* dos is case-insensitive: directly probe the file. */
snprintf(path, sizeof(path), "%s/%s", dir, name);
attr = _chmod(path, 0);
if (attr == -1) return 0;
if (attr & (_A_SUBDIR|_A_VOLID)) return 0;
strncpy(new_name, name, size);
return 1;
}
#elif defined(HAVE_DIRENT_H)
#else /* target has dirent */
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
{
int found = 0;
DIR *dirfd;
DIR *dirp;
struct dirent *d;
dirfd = opendir(dir);
if (dirfd == NULL)
dirp = opendir(dir);
if (dirp == NULL)
return 0;
while ((d = readdir(dirfd)) != NULL) {
while ((d = readdir(dirp)) != NULL) {
if (!strcasecmp(d->d_name, name)) {
found = 1;
strncpy(new_name, d->d_name, size);
break;
}
}
if (found)
strncpy(new_name, d->d_name, size);
closedir(dirfd);
closedir(dirp);
return found;
}
#else
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
{
return 0;
}
#endif
void libxmp_get_instrument_path(struct module_data *m, char *path, int size)
@ -521,6 +484,7 @@ void libxmp_set_type(struct module_data *m, const char *fmt, ...)
va_end(ap);
}
#ifndef LIBXMP_CORE_PLAYER
static int schism_tracker_date(int year, int month, int day)
{
int mm = (month + 9) % 12;
@ -543,7 +507,7 @@ void libxmp_schism_tracker_string(char *buf, size_t size, int s_ver, int l_ver)
{
if (s_ver >= 0x50) {
/* time_t epoch_sec = 1256947200; */
int t = schism_tracker_date(2009, 10, 31);
int64 t = schism_tracker_date(2009, 10, 31);
int year, month, day, dayofyear;
if (s_ver == 0xfff) {
@ -553,11 +517,11 @@ void libxmp_schism_tracker_string(char *buf, size_t size, int s_ver, int l_ver)
/* Date algorithm reimplemented from OpenMPT.
*/
year = (int)(((int64)t * 10000L + 14780) / 3652425);
dayofyear = t - (365 * year + (year / 4) - (year / 100) + (year / 400));
year = (int)((t * 10000L + 14780) / 3652425);
dayofyear = t - (365L * year + (year / 4) - (year / 100) + (year / 400));
if (dayofyear < 0) {
year--;
dayofyear = t - (365 * year + (year / 4) - (year / 100) + (year / 400));
dayofyear = t - (365L * year + (year / 4) - (year / 100) + (year / 400));
}
month = (100 * dayofyear + 52) / 3060;
day = dayofyear - (month * 306 + 5) / 10 + 1;
@ -571,3 +535,60 @@ void libxmp_schism_tracker_string(char *buf, size_t size, int s_ver, int l_ver)
snprintf(buf, size, "Schism Tracker 0.%x", s_ver);
}
}
/* Old MPT modules (from MPT <=1.16, older versions of OpenMPT) rely on a
* pre-amp routine that scales mix volume down. This is based on the module's
* channel count and a tracker pre-amp setting that isn't saved in the module.
* This setting defaults to 128. When fixed to 128, it can be optimized out.
*
* In OpenMPT, this pre-amp routine is only available in the MPT and OpenMPT
* 1.17 RC1 and RC2 mix modes. Changing a module to the compatible or 1.17 RC3
* mix modes will permanently disable it for that module. OpenMPT applies the
* old mix modes to MPT <=1.16 modules, "IT 8.88", and in old OpenMPT-made
* modules that specify one of these mix modes in their extended properties.
*
* Set mod->chn and m->mvol first!
*/
void libxmp_apply_mpt_preamp(struct module_data *m)
{
/* OpenMPT uses a slightly different table. */
static const uint8 preamp_table[16] =
{
0x60, 0x60, 0x60, 0x70, /* 0-7 */
0x80, 0x88, 0x90, 0x98, /* 8-15 */
0xA0, 0xA4, 0xA8, 0xB0, /* 16-23 */
0xB4, 0xB8, 0xBC, 0xC0, /* 24-31 */
};
int chn = m->mod.chn;
CLAMP(chn, 1, 31);
m->mvol = (m->mvol * 96) / preamp_table[chn >> 1];
/* Pre-amp is applied like this in the mixers of libmodplug/libopenmpt
* (still vastly simplified).
int preamp = 128;
if (preamp > 128) {
preamp = 128 + ((preamp - 128) * (chn + 4)) / 16;
}
preamp = preamp * m->mvol / 64;
preamp = (preamp << 7) / preamp_table[chn >> 1];
...
channel_volume_16bit = (channel_volume_16bit * preamp) >> 7;
*/
}
#endif
char *libxmp_strdup(const char *src)
{
size_t len = strlen(src) + 1;
char *buf = (char *) malloc(len);
if (buf) {
memcpy(buf, src, len);
}
return buf;
}

View file

@ -1,7 +1,19 @@
#ifndef LIBXMP_COMMON_H
#define LIBXMP_COMMON_H
#ifdef LIBXMP_CORE_PLAYER
#ifndef LIBXMP_NO_PROWIZARD
#define LIBXMP_NO_PROWIZARD
#endif
#ifndef LIBXMP_NO_DEPACKERS
#define LIBXMP_NO_DEPACKERS
#endif
#else
#undef LIBXMP_CORE_DISABLE_IT
#endif
#include <stdarg.h>
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@ -15,16 +27,63 @@
#define LIBXMP_EXPORT_VAR
#endif
#if defined(__MORPHOS__) || defined(__AROS__) || defined(AMIGAOS) || \
defined(__amigaos__) || defined(__amigaos4__) ||defined(__amigados__) || \
defined(AMIGA) || defined(_AMIGA) || defined(__AMIGA__)
#define LIBXMP_AMIGA 1 /* to identify amiga platforms. */
#ifndef __cplusplus
#define LIBXMP_BEGIN_DECLS
#define LIBXMP_END_DECLS
#else
#define LIBXMP_BEGIN_DECLS extern "C" {
#define LIBXMP_END_DECLS }
#endif
#if (defined(__GNUC__) || defined(__clang__)) && defined(XMP_SYM_VISIBILITY)
#if !defined(_WIN32) && !defined(__ANDROID__) && !defined(__APPLE__) && !defined(LIBXMP_AMIGA) && !defined(__MSDOS__) && !defined(B_BEOS_VERSION) && !defined(__ATHEOS__) && !defined(EMSCRIPTEN) && !defined(__MINT__)
#define USE_VERSIONED_SYMBOLS
#if defined(_MSC_VER) && !defined(__cplusplus)
#define inline __inline
#endif
#if (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) ||\
(defined(_MSC_VER) && (_MSC_VER >= 1400)) || \
(defined(__WATCOMC__) && (__WATCOMC__ >= 1250) && !defined(__cplusplus))
#define LIBXMP_RESTRICT __restrict
#else
#define LIBXMP_RESTRICT
#endif
#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__EMX__)
#define XMP_MAXPATH _MAX_PATH
#elif defined(PATH_MAX)
#define XMP_MAXPATH PATH_MAX
#else
#define XMP_MAXPATH 1024
#endif
#if defined(__MORPHOS__) || defined(__AROS__) || defined(__AMIGA__) \
|| defined(__amigaos__) || defined(__amigaos4__) || defined(AMIGA)
#define LIBXMP_AMIGA 1
#endif
#if defined(_MSC_VER) && defined(__has_include)
#if __has_include(<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
/* AmigaOS fixes by Chris Young <cdyoung@ntlworld.com>, Nov 25, 2007
@ -33,6 +92,8 @@
# include <SupportDefs.h>
#elif defined __amigaos4__
# include <exec/types.h>
#elif defined _arch_dreamcast /* KallistiOS */
# include <arch/types.h>
#else
typedef signed char int8;
typedef signed short int int16;
@ -42,14 +103,18 @@ typedef unsigned short int uint16;
typedef unsigned int uint32;
#endif
#ifdef _MSC_VER /* MSVC++6.0 has no long long */
#ifdef _MSC_VER /* MSVC6 has no long long */
typedef signed __int64 int64;
typedef unsigned __int64 uint64;
#elif !defined B_BEOS_VERSION /* BeOS has its own int64 definition */
#elif !(defined(B_BEOS_VERSION) || defined(__amigaos4__))
typedef unsigned long long uint64;
typedef signed long long int64;
#endif
#ifdef _MSC_VER
#pragma warning(disable:4100) /* unreferenced formal parameter */
#endif
#ifndef LIBXMP_CORE_PLAYER
#define LIBXMP_PAULA_SIMULATOR
#endif
@ -72,6 +137,11 @@ typedef signed long long int64;
#define RESET_FLAG(a,b) ((a)&=~(b))
#define TEST_FLAG(a,b) !!((a)&(b))
/* libxmp_get_filetype() return values */
#define XMP_FILETYPE_NONE 0
#define XMP_FILETYPE_DIR (1 << 0)
#define XMP_FILETYPE_FILE (1 << 1)
#define CLAMP(x,a,b) do { \
if ((x) < (a)) (x) = (a); \
else if ((x) > (b)) (x) = (b); \
@ -87,20 +157,18 @@ typedef signed long long int64;
#define D_CRIT " Error: "
#define D_WARN "Warning: "
#define D_INFO " Info: "
#ifndef CLIB_DECL
#define CLIB_DECL
#endif
#ifdef DEBUG
#ifndef ATTR_PRINTF
#define ATTR_PRINTF(x,y)
#endif
void CLIB_DECL D_(const char *text, ...) ATTR_PRINTF(1,2);
#define D_ libxmp_msvc_dbgprint /* in win32.c */
void libxmp_msvc_dbgprint(const char *text, ...);
#else
// VS prior to VC7.1 does not support variadic macros. VC8.0 does not optimize unused parameters passing
/* VS prior to VC7.1 does not support variadic macros.
* VC8.0 does not optimize unused parameters passing. */
#if _MSC_VER < 1400
void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); }
static void __inline D_(const char *text, ...) {
do { } while (0);
}
#else
#define D_(args, ...) do {} while (0)
#define D_(...) do {} while (0)
#endif
#endif
@ -111,21 +179,8 @@ void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); }
#define D_CRIT " Error: "
#define D_WARN "Warning: "
#define D_INFO " Info: "
#define D_(args...) do { \
__android_log_print(ANDROID_LOG_DEBUG, "libxmp", args); \
} while (0)
#else
#define D_(args...) do {} while (0)
#endif
#elif defined(__WATCOMC__)
#ifdef DEBUG
#define D_INFO "\x1b[33m"
#define D_CRIT "\x1b[31m"
#define D_WARN "\x1b[36m"
#define D_(...) do { \
printf("\x1b[33m%s \x1b[37m[%s:%d] " D_INFO, __FUNCTION__, \
__FILE__, __LINE__); printf (__VA_ARGS__); printf ("\x1b[0m\n"); \
__android_log_print(ANDROID_LOG_DEBUG, "libxmp", __VA_ARGS__); \
} while (0)
#else
#define D_(...) do {} while (0)
@ -134,15 +189,20 @@ void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); }
#else
#ifdef DEBUG
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define LIBXMP_FUNC __func__
#else
#define LIBXMP_FUNC __FUNCTION__
#endif
#define D_INFO "\x1b[33m"
#define D_CRIT "\x1b[31m"
#define D_WARN "\x1b[36m"
#define D_(args...) do { \
printf("\x1b[33m%s \x1b[37m[%s:%d] " D_INFO, __FUNCTION__, \
__FILE__, __LINE__); printf (args); printf ("\x1b[0m\n"); \
#define D_(...) do { \
printf("\x1b[33m%s \x1b[37m[%s:%d] " D_INFO, LIBXMP_FUNC, \
__FILE__, __LINE__); printf (__VA_ARGS__); printf ("\x1b[0m\n"); \
} while (0)
#else
#define D_(args...) do {} while (0)
#define D_(...) do {} while (0)
#endif
#endif /* !_MSC_VER */
@ -151,7 +211,6 @@ void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); }
#define dup _dup
#define fileno _fileno
#define strnicmp _strnicmp
#define strdup _strdup
#define fdopen _fdopen
#define open _open
#define close _close
@ -171,13 +230,33 @@ void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); }
#undef USE_LIBXMP_SNPRINTF
#endif
#ifdef USE_LIBXMP_SNPRINTF
int libxmp_vsnprintf(char *, size_t, const char *, va_list);
int libxmp_snprintf (char *, size_t, const char *, ...);
#if defined(__GNUC__) || defined(__clang__)
#define LIBXMP_ATTRIB_PRINTF(x,y) __attribute__((__format__(__printf__,x,y)))
#else
#define LIBXMP_ATTRIB_PRINTF(x,y)
#endif
int libxmp_vsnprintf(char *, size_t, const char *, va_list) LIBXMP_ATTRIB_PRINTF(3,0);
int libxmp_snprintf (char *, size_t, const char *, ...) LIBXMP_ATTRIB_PRINTF(3,4);
#define snprintf libxmp_snprintf
#define vsnprintf libxmp_vsnprintf
#endif
#endif
/* Output file size limit for files unpacked from unarchivers into RAM. Most
* general archive compression formats can't nicely bound the output size
* from their input filesize, and a cap is needed for a few reasons:
*
* - Linux is too dumb for its own good and its malloc/realloc will return
* pointers to RAM that doesn't exist instead of NULL. When these are used,
* it will kill the application instead of allowing it to fail gracefully.
* - libFuzzer and the clang sanitizers have malloc/realloc interceptors that
* terminate with an error instead of returning NULL.
*
* Depackers that have better ways of bounding the output size can ignore this.
* This value is fairly arbitrary and can be changed if needed.
*/
#define LIBXMP_DEPACK_LIMIT (512 << 20)
/* Quirks */
#define QUIRK_S3MLOOP (1 << 0) /* S3M loop mode */
#define QUIRK_ENVFADE (1 << 1) /* Fade at end of envelope */
@ -191,6 +270,7 @@ int libxmp_snprintf (char *, size_t, const char *, ...);
#define QUIRK_UNISLD (1 << 10) /* Unified pitch slide/portamento */
#define QUIRK_ITVPOR (1 << 11) /* Disable fine bends in IT vol fx */
#define QUIRK_FTMOD (1 << 12) /* Flag for multichannel mods */
#define QUIRK_INVLOOP (1 << 13) /* Enable invert loop */
/*#define QUIRK_MODRNG (1 << 13)*/ /* Limit periods to MOD range */
#define QUIRK_INSVOL (1 << 14) /* Use instrument volume */
#define QUIRK_VIRTUAL (1 << 15) /* Enable virtual channels */
@ -232,8 +312,9 @@ int libxmp_snprintf (char *, size_t, const char *, ...);
/* Time factor */
#define DEFAULT_TIME_FACTOR 10.0
#define MED_TIME_FACTOR 2.64
#define FAR_TIME_FACTOR 4.01373 /* See far_extras.c */
#define MAX_SEQUENCES 16
#define MAX_SEQUENCES 255
#define MAX_SAMPLE_SIZE 0x10000000
#define MAX_SAMPLES 1024
#define MAX_INSTRUMENTS 255
@ -262,7 +343,6 @@ struct ord_data {
};
/* Context */
struct smix_data {
@ -276,6 +356,17 @@ struct smix_data {
/* This will be added to the sample structure in the next API revision */
struct extra_sample_data {
double c5spd;
int sus;
int sue;
};
struct midi_macro {
char data[32];
};
struct midi_macro_data {
struct midi_macro param[16];
struct midi_macro fixed[128];
};
struct module_data {
@ -293,7 +384,9 @@ struct module_data {
int volbase; /* Volume base */
int gvolbase; /* Global volume base */
int gvol; /* Global volume */
int *vol_table; /* Volume translation table */
int mvolbase; /* Mix volume base (S3M/IT) */
int mvol; /* Mix volume (S3M/IT) */
const int *vol_table; /* Volume translation table */
int quirk; /* player quirks */
#define READ_EVENT_MOD 0
#define READ_EVENT_FT2 1
@ -315,9 +408,8 @@ struct module_data {
void *extra; /* format-specific extra fields */
uint8 **scan_cnt; /* scan counters */
struct extra_sample_data *xtra;
#ifndef LIBXMP_CORE_DISABLE_IT
struct xmp_sample *xsmp; /* sustain loop samples */
#endif
struct midi_macro_data *midi;
int compare_vblank;
};
@ -332,6 +424,9 @@ struct flow_control {
int delay;
int jumpline;
int loop_chn;
#ifndef LIBXMP_CORE_PLAYER
int jump_in_pat;
#endif
struct pattern_loop *loop;
@ -348,6 +443,13 @@ struct virt_channel {
int map;
};
struct scan_data {
int time; /* replay time in ms */
int row;
int ord;
int num;
};
struct player_data {
int ord;
int pos;
@ -372,12 +474,7 @@ struct player_data {
struct flow_control flow;
struct {
int time; /* replay time in ms */
int ord;
int row;
int num;
} scan[MAX_SEQUENCES];
struct scan_data *scan;
struct channel_data *xc_data;
@ -416,12 +513,13 @@ struct mixer_data {
int mix; /* percentage of channel separation */
int interp; /* interpolation type */
int dsp; /* dsp effect flags */
char* buffer; /* output buffer */
int32* buf32; /* temporary buffer for 32 bit samples */
char *buffer; /* output buffer */
int32 *buf32; /* temporary buffer for 32 bit samples */
int numvoc; /* default softmixer voices number */
int ticksize;
int dtright; /* anticlick control, right channel */
int dtleft; /* anticlick control, left channel */
int bidir_adjust; /* adjustment for IT bidirectional loops */
double pbase; /* period base */
};
@ -442,6 +540,7 @@ void libxmp_free_scan (struct context_data *);
int libxmp_scan_sequences (struct context_data *);
int libxmp_get_sequence (struct context_data *, int);
int libxmp_set_player_mode (struct context_data *);
void libxmp_reset_flow (struct context_data *);
int8 read8s (FILE *, int *err);
uint8 read8 (FILE *, int *err);
@ -458,7 +557,6 @@ void write16l (FILE *, uint16);
void write16b (FILE *, uint16);
void write32l (FILE *, uint32);
void write32b (FILE *, uint32);
int move_data (FILE *, FILE *, int);
uint16 readmem16l (const uint8 *);
uint16 readmem16b (const uint8 *);
@ -470,4 +568,7 @@ uint32 readmem32b (const uint8 *);
struct xmp_instrument *libxmp_get_instrument(struct context_data *, int);
struct xmp_sample *libxmp_get_sample(struct context_data *, int);
char *libxmp_strdup(const char *);
int libxmp_get_filetype (const char *);
#endif /* LIBXMP_COMMON_H */

View file

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

View file

@ -1,5 +1,5 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
* Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -33,7 +33,6 @@
if (err != NULL) *err = (x); \
} while (0)
uint8 read8(FILE *f, int *err)
{
int a;
@ -97,13 +96,13 @@ uint32 read24l(FILE *f, int *err)
read_byte(a);
read_byte(b);
read_byte(c);
set_error(0);
return (c << 16) | (b << 8) | a;
error:
set_error(ferror(f) ? errno : EOF);
return 0xffffff;
return 0xffffffff;
}
uint32 read24b(FILE *f, int *err)
@ -113,13 +112,13 @@ uint32 read24b(FILE *f, int *err)
read_byte(a);
read_byte(b);
read_byte(c);
set_error(0);
return (a << 16) | (b << 8) | c;
error:
set_error(ferror(f) ? errno : EOF);
return 0xffffff;
return 0xffffffff;
}
uint32 read32l(FILE *f, int *err)
@ -252,18 +251,4 @@ void write32b(FILE *f, uint32 w)
write8(f, w & 0x000000ff);
}
int move_data(FILE *out, FILE *in, int len)
{
uint8 buf[1024];
int l;
do {
l = fread(buf, 1, len > 1024 ? 1024 : len, in);
fwrite(buf, 1, l, out);
len -= l;
} while (l > 0 && len > 0);
return 0;
}
#endif

View file

@ -1,5 +1,5 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
* Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -72,8 +72,13 @@ static void do_toneporta(struct context_data *ctx,
{
struct module_data *m = &ctx->m;
struct xmp_instrument *instrument = &m->mod.xxi[xc->ins];
int mapped = instrument->map[xc->key].ins;
struct xmp_subinstrument *sub;
int mapped_xpo = 0;
int mapped = 0;
if (IS_VALID_NOTE(xc->key)) {
mapped = instrument->map[xc->key].ins;
}
if (mapped >= instrument->nsm) {
mapped = 0;
@ -81,11 +86,13 @@ static void do_toneporta(struct context_data *ctx,
sub = &instrument->sub[mapped];
if (note >= 1 && note <= 0x80 && (uint32)xc->ins < m->mod.ins) {
if (IS_VALID_NOTE(note - 1) && (uint32)xc->ins < m->mod.ins) {
note--;
if (IS_VALID_NOTE(xc->key_porta)) {
mapped_xpo = instrument->map[xc->key_porta].xpo;
}
xc->porta.target = libxmp_note_to_period(ctx, note + sub->xpo +
instrument->map[xc->key_porta].xpo, xc->finetune,
xc->per_adj);
mapped_xpo, xc->finetune, xc->per_adj);
}
xc->porta.dir = xc->period < xc->porta.target ? 1 : -1;
}
@ -357,7 +364,7 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch
h = MSN(fxp);
l = LSN(fxp);
xc->vol.slide2 = h ? h : -l;
}
}
break;
case FX_JUMP: /* Order jump */
p->flow.pbreak = 1;
@ -442,6 +449,7 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch
xc->retrig.val = fxp;
xc->retrig.count = LSN(xc->retrig.val) + 1;
xc->retrig.type = 0;
xc->retrig.limit = 0;
break;
case EX_F_VSLIDE_UP: /* Fine volume slide up */
EFFECT_MEMORY(fxp, xc->fine_vol.up_memory);
@ -506,7 +514,7 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch
if (fxp) {
SET(FINE_BEND);
xc->freq.fslide = fxp;
}
}
break;
case FX_PATT_DELAY:
fx_patt_delay:
@ -563,7 +571,7 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch
/* From the OpenMPT VolColMemory.it test case:
* "Volume column commands a, b, c and d (volume slide) share one
* effect memory, but it should not be shared with Dxy in the effect
* column.
* column.
*/
case FX_VSLIDE_UP_2: /* Fine volume slide up */
EFFECT_MEMORY(fxp, xc->vol.memory2);
@ -692,6 +700,7 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch
if (note) {
xc->retrig.count = LSN(xc->retrig.val) + 1;
}
xc->retrig.limit = 0;
SET(RETRIG);
break;
case FX_TREMOR: /* Tremor */
@ -725,6 +734,9 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch
case FX_SURROUND:
xc->pan.surround = fxp;
break;
case FX_REVERSE: /* Play forward/backward */
libxmp_virt_reverse(ctx, chn, fxp);
break;
#ifndef LIBXMP_CORE_DISABLE_IT
case FX_TRK_VOL: /* Track volume setting */
@ -818,13 +830,26 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch
}
break;
case FX_FLT_CUTOFF:
if (fxp < 0xfe || xc->filter.resonance > 0) {
xc->filter.cutoff = fxp;
}
xc->filter.cutoff = fxp;
break;
case FX_FLT_RESN:
xc->filter.resonance = fxp;
break;
case FX_MACRO_SET:
xc->macro.active = LSN(fxp);
break;
case FX_MACRO:
SET(MIDI_MACRO);
xc->macro.val = fxp;
xc->macro.slide = 0;
break;
case FX_MACROSMOOTH:
if (ctx->p.speed && xc->macro.val < 0x80) {
SET(MIDI_MACRO);
xc->macro.target = fxp;
xc->macro.slide = ((float)fxp - xc->macro.val) / ctx->p.speed;
}
break;
case FX_PANBRELLO: /* Panbrello */
SET(PANBRELLO);
SET_LFO_NOTZERO(&xc->panbrello.lfo, LSN(fxp) << 4, MSN(fxp));
@ -928,7 +953,7 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch
h = MSN(fxp);
l = LSN(fxp);
xc->vol.fslide = h ? h : -l;
}
}
break;
case FX_NSLIDE_DN:
case FX_NSLIDE_UP:
@ -939,6 +964,7 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch
xc->retrig.val = MSN(fxp);
xc->retrig.count = MSN(fxp) + 1;
xc->retrig.type = 0;
xc->retrig.limit = 0;
}
if (fxt == FX_NSLIDE_UP || fxt == FX_NSLIDE_R_UP)
@ -1061,6 +1087,41 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch
}
SET_LFO_NOTZERO(&xc->vibrato.lfo, 669, 1);
break;
/* ULT effects */
case FX_ULT_TPORTA: /* ULT tone portamento */
/* Like normal persistent tone portamento, except:
*
* 1) Despite the documentation claiming 300 cancels tone
* portamento, it actually reuses the last parameter.
*
* 2) A 3xx without a note will reuse the last target note.
*/
if (!IS_VALID_INSTRUMENT(xc->ins))
break;
SET_PER(TONEPORTA);
EFFECT_MEMORY(fxp, xc->porta.memory);
EFFECT_MEMORY(note, xc->porta.note_memory);
do_toneporta(ctx, xc, note);
xc->porta.slide = fxp;
if (fxp == 0)
RESET_PER(TONEPORTA);
break;
/* Archimedes (!Tracker, Digital Symphony, et al.) effects */
case FX_LINE_JUMP: /* !Tracker and Digital Symphony "Line Jump" */
/* Jump to a line within the current order. In Digital Symphony
* this can be combined with position jump (like pattern break)
* and overrides the pattern break line in lower channels. */
if (p->flow.pbreak == 0) {
p->flow.pbreak = 1;
p->flow.jump = p->ord;
}
p->flow.jumpline = fxp;
p->flow.jump_in_pat = p->ord;
break;
#endif
default:

View file

@ -59,7 +59,7 @@
#define FX_F_NSLIDE_DN 0x75
#define FX_F_NSLIDE_UP 0x76
/* Persistent effects -- for FNK and FAR */
/* Persistent effects -- for FNK */
#define FX_PER_PORTA_DN 0x78
#define FX_PER_PORTA_UP 0x79
#define FX_PER_TPORTA 0x7a
@ -75,6 +75,21 @@
#define FX_669_TPORTA 0x62
#define FX_669_FINETUNE 0x63
#define FX_669_VIBRATO 0x64
/* FAR effects */
#define FX_FAR_PORTA_UP 0x65 /* FAR pitch offset up */
#define FX_FAR_PORTA_DN 0x66 /* FAR pitch offset down */
#define FX_FAR_TPORTA 0x67 /* FAR persistent tone portamento */
#define FX_FAR_TEMPO 0x68 /* FAR coarse tempo and tempo mode */
#define FX_FAR_F_TEMPO 0x69 /* FAR fine tempo slide up/down */
#define FX_FAR_VIBDEPTH 0x6a /* FAR set vibrato depth */
#define FX_FAR_VIBRATO 0x6b /* FAR persistent vibrato */
#define FX_FAR_SLIDEVOL 0x6c /* FAR persistent slide-to-volume */
#define FX_FAR_RETRIG 0x6d /* FAR retrigger */
#define FX_FAR_DELAY 0x6e /* FAR note offset */
/* Other frequency based effects (ULT, etc) */
#define FX_ULT_TPORTA 0x6f
#endif
#ifndef LIBXMP_CORE_DISABLE_IT
@ -92,6 +107,9 @@
#define FX_PANBRELLO_WF 0x8b
#define FX_HIOFFSET 0x8c
#define FX_IT_BREAK 0x8e /* like FX_BREAK with hex parameter */
#define FX_MACRO_SET 0xbd /* Set active IT parametered MIDI macro */
#define FX_MACRO 0xbe /* Execute IT MIDI macro */
#define FX_MACROSMOOTH 0xbf /* Execute IT MIDI macro slide */
#endif
#ifndef LIBXMP_CORE_PLAYER
@ -119,9 +137,11 @@
#define FX_VOL_SUB 0xb7 /* SFX change volume down */
#define FX_PITCH_ADD 0xb8 /* SFX add steps to current note */
#define FX_PITCH_SUB 0xb9 /* SFX add steps to current note */
#define FX_LINE_JUMP 0xba /* Archimedes jump to line in current order */
#endif
#define FX_SURROUND 0x8d /* S3M/IT */
#define FX_REVERSE 0x8f /* XM/IT/others: play forward/reverse */
#define FX_S3M_SPEED 0xa3 /* S3M */
#define FX_VOLSLIDE_2 0xa4
#define FX_FINETUNE 0xa6

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__)
#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

View file

@ -1,5 +1,5 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
* Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -21,23 +21,85 @@
*/
#include "format.h"
#ifndef LIBXMP_NO_PROWIZARD
#include "loaders/prowizard/prowiz.h"
#endif
extern const struct format_loader libxmp_loader_xm;
extern const struct format_loader libxmp_loader_mod;
extern const struct format_loader libxmp_loader_it;
extern const struct format_loader libxmp_loader_s3m;
const struct format_loader *const format_loaders[5] = {
const struct format_loader *const format_loaders[NUM_FORMATS + 2] = {
&libxmp_loader_xm,
&libxmp_loader_mod,
#ifndef LIBXMP_CORE_DISABLE_IT
&libxmp_loader_it,
#endif
&libxmp_loader_s3m,
NULL
#ifndef LIBXMP_CORE_PLAYER
&libxmp_loader_flt,
&libxmp_loader_st,
&libxmp_loader_stm,
&libxmp_loader_stx,
&libxmp_loader_mtm,
&libxmp_loader_ice,
&libxmp_loader_imf,
&libxmp_loader_ptm,
&libxmp_loader_mdl,
&libxmp_loader_ult,
&libxmp_loader_liq,
&libxmp_loader_no,
&libxmp_loader_masi,
&libxmp_loader_masi16,
&libxmp_loader_muse,
&libxmp_loader_gal5,
&libxmp_loader_gal4,
&libxmp_loader_amf,
&libxmp_loader_asylum,
&libxmp_loader_gdm,
&libxmp_loader_mmd1,
&libxmp_loader_mmd3,
&libxmp_loader_med2,
&libxmp_loader_med3,
&libxmp_loader_med4,
/* &libxmp_loader_dmf, */
&libxmp_loader_chip,
&libxmp_loader_rtm,
&libxmp_loader_pt3,
/* &libxmp_loader_tcb, */
&libxmp_loader_dt,
/* &libxmp_loader_gtk, */
/* &libxmp_loader_dtt, */
&libxmp_loader_mgt,
&libxmp_loader_arch,
&libxmp_loader_sym,
&libxmp_loader_digi,
&libxmp_loader_dbm,
&libxmp_loader_emod,
&libxmp_loader_okt,
&libxmp_loader_sfx,
&libxmp_loader_far,
&libxmp_loader_umx,
&libxmp_loader_hmn,
&libxmp_loader_stim,
&libxmp_loader_coco,
/* &libxmp_loader_mtp, */
&libxmp_loader_ims,
&libxmp_loader_669,
&libxmp_loader_fnk,
/* &libxmp_loader_amd, */
/* &libxmp_loader_rad, */
/* &libxmp_loader_hsc, */
&libxmp_loader_mfp,
&libxmp_loader_abk,
/* &libxmp_loader_alm, */
/* &libxmp_loader_polly, */
/* &libxmp_loader_stc, */
&libxmp_loader_xmf,
#ifndef LIBXMP_NO_PROWIZARD
&libxmp_loader_pw,
#endif
#endif /* LIBXMP_CORE_PLAYER */
NULL /* list teminator */
};
static const char *_farray[5] = { NULL };
static const char *_farray[NUM_FORMATS + NUM_PW_FORMATS + 1] = { NULL };
const char *const *format_list(void)
{
@ -45,6 +107,16 @@ const char *const *format_list(void)
if (_farray[0] == NULL) {
for (count = i = 0; format_loaders[i] != NULL; i++) {
#ifndef LIBXMP_NO_PROWIZARD
if (strcmp(format_loaders[i]->name, "prowizard") == 0) {
int j;
for (j = 0; pw_formats[j] != NULL; j++) {
_farray[count++] = pw_formats[j]->name;
}
continue;
}
#endif
_farray[count++] = format_loaders[i]->name;
}

View file

@ -6,21 +6,98 @@
struct format_loader {
const char *name;
int (*const test)(HIO_HANDLE *, char *, const int);
int (*const loader)(struct module_data *, HIO_HANDLE *, const int);
int (*test)(HIO_HANDLE *, char *, const int);
int (*loader)(struct module_data *, HIO_HANDLE *, const int);
};
extern const struct format_loader *const format_loaders[];
const char *const *format_list(void);
#ifndef LIBXMP_CORE_PLAYER
extern const struct format_loader libxmp_loader_xm;
extern const struct format_loader libxmp_loader_mod;
extern const struct format_loader libxmp_loader_it;
extern const struct format_loader libxmp_loader_s3m;
#define NUM_FORMATS 53
#define NUM_PW_FORMATS 43
#ifndef LIBXMP_CORE_PLAYER
extern const struct format_loader libxmp_loader_flt;
extern const struct format_loader libxmp_loader_st;
extern const struct format_loader libxmp_loader_stm;
extern const struct format_loader libxmp_loader_stx;
extern const struct format_loader libxmp_loader_mtm;
extern const struct format_loader libxmp_loader_ice;
extern const struct format_loader libxmp_loader_imf;
extern const struct format_loader libxmp_loader_ptm;
extern const struct format_loader libxmp_loader_mdl;
extern const struct format_loader libxmp_loader_ult;
extern const struct format_loader libxmp_loader_liq;
extern const struct format_loader libxmp_loader_no;
extern const struct format_loader libxmp_loader_masi;
extern const struct format_loader libxmp_loader_masi16;
extern const struct format_loader libxmp_loader_muse;
extern const struct format_loader libxmp_loader_gal5;
extern const struct format_loader libxmp_loader_gal4;
extern const struct format_loader libxmp_loader_amf;
extern const struct format_loader libxmp_loader_asylum;
extern const struct format_loader libxmp_loader_gdm;
extern const struct format_loader libxmp_loader_mmd1;
extern const struct format_loader libxmp_loader_mmd3;
extern const struct format_loader libxmp_loader_med2;
extern const struct format_loader libxmp_loader_med3;
extern const struct format_loader libxmp_loader_med4;
extern const struct format_loader libxmp_loader_rtm;
extern const struct format_loader libxmp_loader_pt3;
extern const struct format_loader libxmp_loader_dt;
extern const struct format_loader libxmp_loader_mgt;
extern const struct format_loader libxmp_loader_arch;
extern const struct format_loader libxmp_loader_sym;
extern const struct format_loader libxmp_loader_digi;
extern const struct format_loader libxmp_loader_dbm;
extern const struct format_loader libxmp_loader_emod;
extern const struct format_loader libxmp_loader_okt;
extern const struct format_loader libxmp_loader_sfx;
extern const struct format_loader libxmp_loader_far;
extern const struct format_loader libxmp_loader_umx;
extern const struct format_loader libxmp_loader_stim;
extern const struct format_loader libxmp_loader_coco;
extern const struct format_loader libxmp_loader_ims;
extern const struct format_loader libxmp_loader_669;
extern const struct format_loader libxmp_loader_fnk;
extern const struct format_loader libxmp_loader_mfp;
extern const struct format_loader libxmp_loader_pw;
extern const struct format_loader libxmp_loader_hmn;
extern const struct format_loader libxmp_loader_chip;
extern const struct format_loader libxmp_loader_abk;
extern const struct format_loader libxmp_loader_xmf;
#if 0 /* broken / unused, yet. */
extern const struct format_loader libxmp_loader_dmf;
extern const struct format_loader libxmp_loader_tcb;
extern const struct format_loader libxmp_loader_gtk;
extern const struct format_loader libxmp_loader_dtt;
extern const struct format_loader libxmp_loader_mtp;
extern const struct format_loader libxmp_loader_amd;
extern const struct format_loader libxmp_loader_rad;
extern const struct format_loader libxmp_loader_hsc;
extern const struct format_loader libxmp_loader_alm;
extern const struct format_loader libxmp_loader_polly;
extern const struct format_loader libxmp_loader_stc;
#endif
#endif /* LIBXMP_CORE_PLAYER */
#ifndef LIBXMP_CORE_PLAYER
#define NUM_FORMATS 52
#elif !defined(LIBXMP_CORE_DISABLE_IT)
#define NUM_FORMATS 4
#else
#define NUM_FORMATS 3
#endif
#ifndef LIBXMP_NO_PROWIZARD
#define NUM_PW_FORMATS 43
extern const struct pw_format *const pw_formats[];
int pw_test_format(HIO_HANDLE *, char *, const int, struct xmp_test_info *);
#endif
#endif
#else
#define NUM_PW_FORMATS 0
#endif
#endif /* LIBXMP_FORMAT_H */

View file

@ -1,5 +1,5 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
* Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -287,18 +287,27 @@ int hio_seek(HIO_HANDLE *h, long offset, int whence)
if (ret < 0) {
h->error = errno;
}
else if (h->error == EOF) {
h->error = 0;
}
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mseek(h->handle.mem, offset, whence);
if (ret < 0) {
h->error = EINVAL;
}
else if (h->error == EOF) {
h->error = 0;
}
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbseek(h->handle.cbfile, offset, whence);
if (ret < 0) {
h->error = EINVAL;
}
else if (h->error == EOF) {
h->error = 0;
}
break;
}
@ -358,7 +367,7 @@ HIO_HANDLE *hio_open(const char *path, const char *mode)
{
HIO_HANDLE *h;
h = (HIO_HANDLE *)calloc(1, sizeof (HIO_HANDLE));
h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE));
if (h == NULL)
goto err;
@ -381,19 +390,24 @@ HIO_HANDLE *hio_open(const char *path, const char *mode)
return NULL;
}
HIO_HANDLE *hio_open_mem(const void *ptr, long size)
HIO_HANDLE *hio_open_mem(const void *ptr, long size, int free_after_use)
{
HIO_HANDLE *h;
if (size <= 0) return NULL;
h = (HIO_HANDLE *)calloc(1, sizeof (HIO_HANDLE));
h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE));
if (h == NULL)
return NULL;
h->type = HIO_HANDLE_TYPE_MEMORY;
h->handle.mem = mopen(ptr, size);
h->handle.mem = mopen(ptr, size, free_after_use);
h->size = size;
if (!h->handle.mem) {
free(h);
h = NULL;
}
return h;
}
@ -401,7 +415,7 @@ HIO_HANDLE *hio_open_file(FILE *f)
{
HIO_HANDLE *h;
h = (HIO_HANDLE *)calloc(1, sizeof (HIO_HANDLE));
h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE));
if (h == NULL)
return NULL;
@ -436,7 +450,7 @@ HIO_HANDLE *hio_open_callbacks(void *priv, struct xmp_callbacks callbacks)
if (!f)
return NULL;
h = (HIO_HANDLE *)calloc(1, sizeof(HIO_HANDLE));
h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE));
if (h == NULL) {
cbclose(f);
return NULL;
@ -453,7 +467,7 @@ HIO_HANDLE *hio_open_callbacks(void *priv, struct xmp_callbacks callbacks)
return h;
}
int hio_close(HIO_HANDLE *h)
static int hio_close_internal(HIO_HANDLE *h)
{
int ret = -1;
@ -468,7 +482,58 @@ int hio_close(HIO_HANDLE *h)
ret = cbclose(h->handle.cbfile);
break;
}
return ret;
}
/* hio_close + hio_open_mem. Reuses the same HIO_HANDLE. */
int hio_reopen_mem(const void *ptr, long size, int free_after_use, HIO_HANDLE *h)
{
MFILE *m;
int ret;
if (size <= 0) return -1;
m = mopen(ptr, size, free_after_use);
if (m == NULL) {
return -1;
}
ret = hio_close_internal(h);
if (ret < 0) {
m->free_after_use = 0;
mclose(m);
return ret;
}
h->type = HIO_HANDLE_TYPE_MEMORY;
h->handle.mem = m;
h->size = size;
return 0;
}
/* hio_close + hio_open_file. Reuses the same HIO_HANDLE. */
int hio_reopen_file(FILE *f, int close_after_use, HIO_HANDLE *h)
{
long size = get_size(f);
int ret;
if (size < 0) {
return -1;
}
ret = hio_close_internal(h);
if (ret < 0) {
return -1;
}
h->noclose = !close_after_use;
h->type = HIO_HANDLE_TYPE_FILE;
h->handle.file = f;
h->size = size;
return 0;
}
int hio_close(HIO_HANDLE *h)
{
int ret = hio_close_internal(h);
free(h);
return ret;
}

View file

@ -38,10 +38,12 @@ long hio_tell (HIO_HANDLE *);
int hio_eof (HIO_HANDLE *);
int hio_error (HIO_HANDLE *);
HIO_HANDLE *hio_open (const char *, const char *);
HIO_HANDLE *hio_open_mem (const void *, long);
HIO_HANDLE *hio_open_mem (const void *, long, int);
HIO_HANDLE *hio_open_file (FILE *);
HIO_HANDLE *hio_open_file2 (FILE *);/* allows fclose()ing the file by libxmp */
HIO_HANDLE *hio_open_callbacks (void *, struct xmp_callbacks);
int hio_reopen_mem (const void *, long, int, HIO_HANDLE *);
int hio_reopen_file (FILE *, int, HIO_HANDLE *);
int hio_close (HIO_HANDLE *);
long hio_size (HIO_HANDLE *);

View file

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

View file

@ -1,5 +1,5 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
* Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -30,7 +30,6 @@
#define MAGIC_IMPI MAGIC4('I','M','P','I')
#define MAGIC_IMPS MAGIC4('I','M','P','S')
static int it_test(HIO_HANDLE *, char *, const int);
static int it_load(struct module_data *, HIO_HANDLE *, const int);
@ -55,7 +54,6 @@ static int it_test(HIO_HANDLE *f, char *t, const int start)
#define FX_XTND 0xfe
#define L_CHANNELS 64
static const uint8 fx[32] = {
/* */ FX_NONE,
/* A */ FX_S3M_SPEED,
@ -83,19 +81,14 @@ static const uint8 fx[32] = {
/* W */ FX_GVOL_SLIDE,
/* X */ FX_SETPAN,
/* Y */ FX_PANBRELLO,
/* Z */ FX_FLT_CUTOFF,
/* ? */ FX_NONE,
/* Z */ FX_MACRO,
/* ? */ FX_NONE,
/* / */ FX_MACROSMOOTH,
/* ? */ FX_NONE,
/* ? */ FX_NONE,
/* ? */ FX_NONE
};
int itsex_decompress8 (HIO_HANDLE *, void *, int, int);
int itsex_decompress16 (HIO_HANDLE *, void *, int, int);
static void xlat_fx(int c, struct xmp_event *e, uint8 *last_fxp, int new_fx)
{
uint8 h = MSN(e->fxp), l = LSN(e->fxp);
@ -144,9 +137,16 @@ static void xlat_fx(int c, struct xmp_event *e, uint8 *last_fxp, int new_fx)
e->fxt = FX_SETPAN;
e->fxp = l << 4;
break;
case 0x9: /* 0x91 = set surround */
e->fxt = FX_SURROUND;
e->fxp = l;
case 0x9:
if (l == 0 || l == 1) {
/* 0x91 = set surround */
e->fxt = FX_SURROUND;
e->fxp = l;
} else if (l == 0xe || l == 0xf) {
/* 0x9f Play reverse (MPT) */
e->fxt = FX_REVERSE;
e->fxp = l - 0xe;
}
break;
case 0xa: /* High offset */
e->fxt = FX_HIOFFSET;
@ -165,18 +165,14 @@ static void xlat_fx(int c, struct xmp_event *e, uint8 *last_fxp, int new_fx)
e->fxt = FX_IT_ROWDELAY;
e->fxp = l;
break;
case 0xf: /* Set parametered macro */
e->fxt = FX_MACRO_SET;
e->fxp = l;
break;
default:
e->fxt = e->fxp = 0;
}
break;
case FX_FLT_CUTOFF:
if (e->fxp > 0x7f && e->fxp < 0x90) { /* Resonance */
e->fxt = FX_FLT_RESN;
e->fxp = (e->fxp - 0x80) * 16;
} else { /* Cutoff */
e->fxp *= 2;
}
break;
case FX_TREMOR:
if (!new_fx && e->fxp != 0) {
e->fxp = ((MSN(e->fxp) + 1) << 4) | (LSN(e->fxp) + 1);
@ -258,6 +254,34 @@ static void fix_name(uint8 *s, int l)
}
static int load_it_midi_config(struct module_data *m, HIO_HANDLE *f)
{
int i;
m->midi = (struct midi_macro_data *) calloc(1, sizeof(struct midi_macro_data));
if (m->midi == NULL)
return -1;
/* Skip global MIDI macros */
if (hio_seek(f, 9 * 32, SEEK_CUR) < 0)
return -1;
/* SFx macros */
for (i = 0; i < 16; i++) {
if (hio_read(m->midi->param[i].data, 1, 32, f) < 32)
return -1;
m->midi->param[i].data[31] = '\0';
}
/* Zxx macros */
for (i = 0; i < 128; i++) {
if (hio_read(m->midi->fixed[i].data, 1, 32, f) < 32)
return -1;
m->midi->fixed[i].data[31] = '\0';
}
return 0;
}
static int read_envelope(struct xmp_envelope *ei, struct it_envelope *env,
HIO_HANDLE *f)
{
@ -313,7 +337,8 @@ static int read_envelope(struct xmp_envelope *ei, struct it_envelope *env,
return 0;
}
static void identify_tracker(struct module_data *m, struct it_file_header *ifh)
static void identify_tracker(struct module_data *m, struct it_file_header *ifh,
int pat_before_smp, int *is_mpt_116)
{
#ifndef LIBXMP_CORE_PLAYER
char tracker_name[40];
@ -337,6 +362,16 @@ static void identify_tracker(struct module_data *m, struct it_file_header *ifh)
strcpy(tracker_name, "ModPlug Tracker 1.16");
/* ModPlug Tracker files aren't really IMPM 2.00 */
ifh->cmwt = sample_mode ? 0x100 : 0x214;
*is_mpt_116 = 1;
} else if (ifh->cmwt == 0x0200 && ifh->cwt == 0x0202 && pat_before_smp) {
/* ModPlug Tracker ITs from pre-alpha 4 use tracker
* 0x0202 and format 0x0200. Unfortunately, ITs from
* Impulse Tracker may *also* use this. These MPT ITs
* can be detected because they write patterns before
* samples/instruments. */
strcpy(tracker_name, "ModPlug Tracker 1.0 pre-alpha");
ifh->cmwt = sample_mode ? 0x100 : 0x200;
*is_mpt_116 = 1;
} else if (ifh->cwt == 0x0216) {
strcpy(tracker_name, "Impulse Tracker 2.14v3");
} else if (ifh->cwt == 0x0217) {
@ -352,6 +387,7 @@ static void identify_tracker(struct module_data *m, struct it_file_header *ifh)
case 0x7f:
if (ifh->cwt == 0x0888) {
strcpy(tracker_name, "OpenMPT 1.17");
*is_mpt_116 = 1;
} else if (ifh->cwt == 0x7fff) {
strcpy(tracker_name, "munch.py");
} else {
@ -486,7 +522,7 @@ static int load_old_it_instrument(struct xmp_instrument *xxi, HIO_HANDLE *f)
xxi->vol = 0x40;
if (k) {
xxi->sub = calloc(sizeof(struct xmp_subinstrument), k);
xxi->sub = (struct xmp_subinstrument *) calloc(k, sizeof(struct xmp_subinstrument));
if (xxi->sub == NULL) {
return -1;
}
@ -637,7 +673,7 @@ static int load_new_it_instrument(struct xmp_instrument *xxi, HIO_HANDLE *f)
xxi->vol = i2h.gbv >> 1;
if (k) {
xxi->sub = calloc(sizeof(struct xmp_subinstrument), k);
xxi->sub = (struct xmp_subinstrument *) calloc(k, sizeof(struct xmp_subinstrument));
if (xxi->sub == NULL)
return -1;
@ -671,7 +707,7 @@ static int load_new_it_instrument(struct xmp_instrument *xxi, HIO_HANDLE *f)
return 0;
}
static void force_sample_length(struct xmp_sample *xxs, int len)
static void force_sample_length(struct xmp_sample *xxs, struct extra_sample_data *xtra, int len)
{
xxs->len = len;
@ -680,6 +716,14 @@ static void force_sample_length(struct xmp_sample *xxs, int len)
if (xxs->lps >= xxs->len)
xxs->flg &= ~XMP_SAMPLE_LOOP;
if (xtra) {
if (xtra->sue > xxs->len)
xtra->sue = xxs->len;
if(xtra->sus >= xxs->len)
xxs->flg &= ~(XMP_SAMPLE_SLOOP | XMP_SAMPLE_SLOOP_BIDIR);
}
}
static int load_it_sample(struct module_data *m, int i, int start,
@ -687,12 +731,13 @@ static int load_it_sample(struct module_data *m, int i, int start,
{
struct it_sample_header ish;
struct xmp_module *mod = &m->mod;
struct xmp_sample *xxs, *xsmp;
struct extra_sample_data *xtra;
struct xmp_sample *xxs;
int j, k;
uint8 buf[80];
if (sample_mode) {
mod->xxi[i].sub = calloc(sizeof(struct xmp_subinstrument), 1);
mod->xxi[i].sub = (struct xmp_subinstrument *) calloc(1, sizeof(struct xmp_subinstrument));
if (mod->xxi[i].sub == NULL) {
return -1;
}
@ -711,7 +756,7 @@ static int load_it_sample(struct module_data *m, int i, int start,
}
xxs = &mod->xxs[i];
xsmp = &m->xsmp[i];
xtra = &m->xtra[i];
memcpy(ish.dosname, buf + 4, 12);
ish.zero = buf[16];
@ -749,14 +794,8 @@ static int load_it_sample(struct module_data *m, int i, int start,
xxs->flg |= ish.flags & IT_SMP_BSLOOP ? XMP_SAMPLE_SLOOP_BIDIR : 0;
if (ish.flags & IT_SMP_SLOOP) {
memcpy(xsmp, xxs, sizeof (struct xmp_sample));
xsmp->lps = ish.sloopbeg;
xsmp->lpe = ish.sloopend;
xsmp->flg |= XMP_SAMPLE_LOOP;
xsmp->flg &= ~XMP_SAMPLE_LOOP_BIDIR;
if (ish.flags & IT_SMP_BSLOOP) {
xsmp->flg |= XMP_SAMPLE_LOOP_BIDIR;
}
xtra->sus = ish.sloopbeg;
xtra->sue = ish.sloopend;
}
if (sample_mode) {
@ -827,13 +866,16 @@ static int load_it_sample(struct module_data *m, int i, int start,
if (xxs->lpe > xxs->len || xxs->lps >= xxs->lpe)
xxs->flg &= ~XMP_SAMPLE_LOOP;
if (ish.convert == IT_CVT_ADPCM)
cvt |= SAMPLE_FLAG_ADPCM;
if (~ish.convert & IT_CVT_SIGNED)
cvt |= SAMPLE_FLAG_UNS;
/* compressed samples */
if (ish.flags & IT_SMP_COMP) {
long min_size, file_len, left;
uint8 *buf;
void *decbuf;
int ret;
/* Sanity check - the lower bound on IT compressed
@ -853,17 +895,15 @@ static int load_it_sample(struct module_data *m, int i, int start,
"resizing to %ld",
i, xxs->len, min_size, left, left << 3);
force_sample_length(xxs, left << 3);
if (ish.flags & IT_SMP_SLOOP)
force_sample_length(xsmp, left << 3);
force_sample_length(xxs, xtra, left << 3);
}
buf = calloc(1, xxs->len * 2);
if (buf == NULL)
decbuf = (uint8 *) calloc(1, xxs->len * 2);
if (decbuf == NULL)
return -1;
if (ish.flags & IT_SMP_16BIT) {
itsex_decompress16(f, buf, xxs->len,
itsex_decompress16(f, (int16 *)decbuf, xxs->len,
ish.convert & IT_CVT_DIFF);
#ifdef WORDS_BIGENDIAN
@ -873,44 +913,19 @@ static int load_it_sample(struct module_data *m, int i, int start,
cvt |= SAMPLE_FLAG_BIGEND;
#endif
} else {
itsex_decompress8(f, buf, xxs->len,
itsex_decompress8(f, (uint8 *)decbuf, xxs->len,
ish.convert & IT_CVT_DIFF);
}
if (ish.flags & IT_SMP_SLOOP) {
long pos = hio_tell(f);
if (pos < 0) {
free(buf);
return -1;
}
ret = libxmp_load_sample(m, NULL, SAMPLE_FLAG_NOLOAD |
cvt, &m->xsmp[i], buf);
if (ret < 0) {
free(buf);
return -1;
}
hio_seek(f, pos, SEEK_SET);
}
ret = libxmp_load_sample(m, NULL, SAMPLE_FLAG_NOLOAD | cvt,
&mod->xxs[i], buf);
&mod->xxs[i], decbuf);
if (ret < 0) {
free(buf);
free(decbuf);
return -1;
}
free(buf);
free(decbuf);
} else {
if (ish.flags & IT_SMP_SLOOP) {
long pos = hio_tell(f);
if (pos < 0) {
return -1;
}
if (libxmp_load_sample(m, f, cvt, &m->xsmp[i], NULL) < 0)
return -1;
hio_seek(f, pos, SEEK_SET);
}
if (libxmp_load_sample(m, f, cvt, &mod->xxs[i], NULL) < 0)
return -1;
}
@ -927,7 +942,7 @@ static int load_it_pattern(struct module_data *m, int i, int new_fx,
uint8 mask[L_CHANNELS];
uint8 last_fxp[64];
int r, c, pat_len;
int r, c, pat_len, num_rows;
uint8 b;
r = 0;
@ -937,7 +952,7 @@ static int load_it_pattern(struct module_data *m, int i, int new_fx,
memset(&dummy, 0, sizeof(struct xmp_event));
pat_len = hio_read16l(f) /* - 4 */ ;
mod->xxp[i]->rows = hio_read16l(f);
mod->xxp[i]->rows = num_rows = hio_read16l(f);
if (libxmp_alloc_tracks_in_pattern(mod, i) < 0) {
return -1;
@ -947,7 +962,7 @@ static int load_it_pattern(struct module_data *m, int i, int new_fx,
hio_read16l(f);
hio_read16l(f);
while (--pat_len >= 0) {
while (r < num_rows && --pat_len >= 0) {
b = hio_read8(f);
if (hio_error(f)) {
return -1;
@ -968,7 +983,7 @@ static int load_it_pattern(struct module_data *m, int i, int new_fx,
* real number of channels before loading the patterns and
* we don't want to set it to 64 channels.
*/
if (c >= mod->chn || r >= mod->xxp[i]->rows) {
if (c >= mod->chn) {
event = &dummy;
} else {
event = &EVENT(i, c, r);
@ -1056,6 +1071,8 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start)
uint32 *pp_smp; /* Pointers to samples */
uint32 *pp_pat; /* Pointers to patterns */
int new_fx, sample_mode;
int pat_before_smp = 0;
int is_mpt_116 = 0;
LOAD_INIT();
@ -1087,8 +1104,8 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start)
ifh.pwd = hio_read8(f);
/* Sanity check */
if (ifh.gv > 0x80 || ifh.mv > 0x80) {
D_(D_CRIT "invalid gv (%u) or mv (%u)", ifh.gv, ifh.mv);
if (ifh.gv > 0x80) {
D_(D_CRIT "invalid gv (%u)", ifh.gv);
goto err;
}
@ -1099,6 +1116,11 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start)
hio_read(ifh.chpan, 64, 1, f);
hio_read(ifh.chvol, 64, 1, f);
if (hio_error(f)) {
D_(D_CRIT "error reading IT header");
goto err;
}
memcpy(mod->name, ifh.name, sizeof(ifh.name));
/* sizeof(ifh.name) == 26, sizeof(mod->name) == 64. */
mod->name[sizeof(ifh.name)] = '\0';
@ -1115,18 +1137,18 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start)
}
if (mod->ins) {
pp_ins = calloc(4, mod->ins);
pp_ins = (uint32 *) calloc(4, mod->ins);
if (pp_ins == NULL)
goto err;
} else {
pp_ins = NULL;
}
pp_smp = calloc(4, mod->smp);
pp_smp = (uint32 *) calloc(4, mod->smp);
if (pp_smp == NULL)
goto err2;
pp_pat = calloc(4, mod->pat);
pp_pat = (uint32 *) calloc(4, mod->pat);
if (pp_pat == NULL)
goto err3;
@ -1178,9 +1200,22 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start)
for (i = 0; i < mod->pat; i++)
pp_pat[i] = hio_read32l(f);
if ((ifh.flags & IT_MIDI_CONFIG) || (ifh.special & IT_SPEC_MIDICFG)) {
/* Skip edit history if it exists. */
if (ifh.special & IT_EDIT_HISTORY) {
int skip = hio_read16l(f) * 8;
if (hio_error(f) || (skip && hio_seek(f, skip, SEEK_CUR) < 0))
goto err4;
}
if (load_it_midi_config(m, f) < 0)
goto err4;
}
if (mod->smp && mod->pat && pp_pat[0] != 0 && pp_pat[0] < pp_smp[0])
pat_before_smp = 1;
m->c4rate = C4_NTSC_RATE;
identify_tracker(m, &ifh);
identify_tracker(m, &ifh, pat_before_smp, &is_mpt_116);
MODULE_INFO();
@ -1194,14 +1229,6 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start)
if (libxmp_init_instrument(m) < 0)
goto err4;
/* Alloc extra samples for sustain loop */
if (mod->smp > 0) {
m->xsmp = calloc(sizeof (struct xmp_sample), mod->smp);
if (m->xsmp == NULL) {
goto err4;
}
}
D_(D_INFO "Instruments: %d", mod->ins);
for (i = 0; i < mod->ins; i++) {
@ -1258,7 +1285,7 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start)
max_ch = 0;
for (i = 0; i < mod->pat; i++) {
uint8 mask[L_CHANNELS];
int pat_len;
int pat_len, num_rows, row;
/* If the offset to a pattern is 0, the pattern is empty */
if (pp_pat[i] == 0)
@ -1266,19 +1293,33 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start)
hio_seek(f, start + pp_pat[i], SEEK_SET);
pat_len = hio_read16l(f) /* - 4 */ ;
hio_read16l(f);
num_rows = hio_read16l(f);
memset(mask, 0, L_CHANNELS);
hio_read16l(f);
hio_read16l(f);
while (--pat_len >= 0) {
/* Sanity check:
* - Impulse Tracker and Schism Tracker allow up to 200 rows.
* - ModPlug Tracker 1.16 allows 256 rows.
* - OpenMPT allows 1024 rows.
*/
if (num_rows > 1024) {
D_(D_WARN "skipping pattern %d (%d rows)", i, num_rows);
pp_pat[i] = 0;
continue;
}
row = 0;
while (row < num_rows && --pat_len >= 0) {
int b = hio_read8(f);
if (hio_error(f)) {
D_(D_CRIT "error scanning pattern %d", i);
goto err4;
}
if (b == 0)
if (b == 0) {
row++;
continue;
}
c = (b - 1) & 63;
@ -1354,9 +1395,8 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start)
free(pp_ins);
/* Song message */
if (ifh.special & IT_HAS_MSG) {
if ((m->comment = malloc(ifh.msglen)) != NULL) {
if ((m->comment = (char *)malloc(ifh.msglen)) != NULL) {
hio_seek(f, start + ifh.msgofs, SEEK_SET);
D_(D_INFO "Message length : %d", ifh.msglen);
@ -1401,8 +1441,15 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start)
m->gvolbase = 0x80;
m->gvol = ifh.gv;
m->mvolbase = 48;
m->mvol = ifh.mv;
m->read_event_type = READ_EVENT_IT;
#ifndef LIBXMP_CORE_PLAYER
if (is_mpt_116)
libxmp_apply_mpt_preamp(m);
#endif
return 0;
err4:

View file

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

View file

@ -1,12 +1,7 @@
#ifndef LIBXMP_LIST_H
#define LIBXMP_LIST_H
#ifdef _MSC_VER
#define __inline__ __inline
#endif
#ifdef __WATCOMC__
#define __inline__ inline
#endif
#include <stddef.h> /* offsetof */
/*
* Simple doubly linked list implementation.
@ -32,12 +27,12 @@ struct list_head {
} while (0)
/*
* Insert a new entry between two known consecutive entries.
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static __inline__ void __list_add(struct list_head *_new,
static inline void __list_add(struct list_head *_new,
struct list_head * prev,
struct list_head * next)
{
@ -55,7 +50,7 @@ static __inline__ void __list_add(struct list_head *_new,
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static __inline__ void list_add(struct list_head *_new, struct list_head *head)
static inline void list_add(struct list_head *_new, struct list_head *head)
{
__list_add(_new, head, head->next);
}
@ -68,7 +63,7 @@ static __inline__ void list_add(struct list_head *_new, struct list_head *head)
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static __inline__ void list_add_tail(struct list_head *_new, struct list_head *head)
static inline void list_add_tail(struct list_head *_new, struct list_head *head)
{
__list_add(_new, head->prev, head);
}
@ -80,7 +75,7 @@ static __inline__ void list_add_tail(struct list_head *_new, struct list_head *h
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static __inline__ void __list_del(struct list_head * prev,
static inline void __list_del(struct list_head * prev,
struct list_head * next)
{
next->prev = prev;
@ -91,7 +86,7 @@ static __inline__ void __list_del(struct list_head * prev,
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
*/
static __inline__ void list_del(struct list_head *entry)
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
@ -100,7 +95,7 @@ static __inline__ void list_del(struct list_head *entry)
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static __inline__ int list_empty(struct list_head *head)
static inline int list_empty(struct list_head *head)
{
return head->next == head;
}
@ -110,7 +105,7 @@ static __inline__ int list_empty(struct list_head *head)
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static __inline__ void list_splice(struct list_head *list, struct list_head *head)
static inline void list_splice(struct list_head *list, struct list_head *head)
{
struct list_head *first = list->next;
@ -133,7 +128,7 @@ static __inline__ void list_splice(struct list_head *list, struct list_head *hea
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(size_t)(&((type *)0)->member)))
((type *)((char *)(ptr) - offsetof(type, member)))
/**
* list_for_each - iterate over a list
@ -143,4 +138,4 @@ static __inline__ void list_splice(struct list_head *list, struct list_head *hea
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
#endif
#endif /* LIBXMP_LIST_H */

View file

@ -1,5 +1,5 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
* Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -20,16 +20,15 @@
* THE SOFTWARE.
*/
#include <sys/stat.h>
#include <errno.h>
#include "format.h"
#include "list.h"
#include "hio.h"
#include "tempfile.h"
#include "loader.h"
#ifndef LIBXMP_NO_DEPACKERS
#include "tempfile.h"
#include "depackers/depacker.h"
#endif
@ -39,17 +38,13 @@
#endif
extern struct format_loader *format_loaders[];
void libxmp_load_prologue(struct context_data *);
void libxmp_load_epilogue(struct context_data *);
int libxmp_prepare_scan(struct context_data *);
#ifndef LIBXMP_CORE_PLAYER
#define BUFLEN 16384
#endif
#ifndef LIBXMP_CORE_PLAYER
static void set_md5sum(HIO_HANDLE *f, unsigned char *digest)
{
unsigned char buf[BUFLEN];
@ -68,18 +63,18 @@ static void set_md5sum(HIO_HANDLE *f, unsigned char *digest)
static char *get_dirname(const char *name)
{
char *dirname;
const char *div;
const char *p;
ptrdiff_t len;
if ((div = strrchr(name, '/')) != NULL) {
len = div - name + 1;
dirname = malloc(len + 1);
if ((p = strrchr(name, '/')) != NULL) {
len = p - name + 1;
dirname = (char *) malloc(len + 1);
if (dirname != NULL) {
memcpy(dirname, name, len);
dirname[len] = 0;
}
} else {
dirname = strdup("");
dirname = libxmp_strdup("");
}
return dirname;
@ -87,13 +82,13 @@ static char *get_dirname(const char *name)
static char *get_basename(const char *name)
{
const char *div;
const char *p;
char *basename;
if ((div = strrchr(name, '/')) != NULL) {
basename = strdup(div + 1);
if ((p = strrchr(name, '/')) != NULL) {
basename = libxmp_strdup(p + 1);
} else {
basename = strdup(name);
basename = libxmp_strdup(name);
}
return basename;
@ -115,7 +110,7 @@ static int test_module(struct xmp_test_info *info, HIO_HANDLE *h)
if (format_loaders[i]->test(h, buf, 0) == 0) {
int is_prowizard = 0;
#if !defined(LIBXMP_CORE_PLAYER) && !defined(LIBXMP_NO_PROWIZARD)
#ifndef LIBXMP_NO_PROWIZARD
if (strcmp(format_loaders[i]->name, "prowizard") == 0) {
hio_seek(h, 0, SEEK_SET);
pw_test_format(h, buf, 0, info);
@ -140,16 +135,17 @@ static int test_module(struct xmp_test_info *info, HIO_HANDLE *h)
int xmp_test_module(const char *path, struct xmp_test_info *info)
{
HIO_HANDLE *h;
struct stat st;
int ret;
#ifndef LIBXMP_NO_DEPACKERS
char *temp = NULL;
#endif
int ret;
if (stat(path, &st) < 0)
ret = libxmp_get_filetype(path);
if (ret == XMP_FILETYPE_NONE) {
return -XMP_ERROR_SYSTEM;
if (S_ISDIR(st.st_mode)) {
}
if (ret & XMP_FILETYPE_DIR) {
errno = EISDIR;
return -XMP_ERROR_SYSTEM;
}
@ -158,16 +154,10 @@ int xmp_test_module(const char *path, struct xmp_test_info *info)
return -XMP_ERROR_SYSTEM;
#ifndef LIBXMP_NO_DEPACKERS
if (libxmp_decrunch(&h, path, &temp) < 0) {
if (libxmp_decrunch(h, path, &temp) < 0) {
ret = -XMP_ERROR_DEPACK;
goto err;
}
/* get size after decrunch */
if (hio_size(h) < 256) { /* set minimum valid module size */
ret = -XMP_ERROR_FORMAT;
goto err;
}
#endif
ret = test_module(info, h);
@ -191,7 +181,7 @@ int xmp_test_module_from_memory(const void *mem, long size, struct xmp_test_info
return -XMP_ERROR_INVALID;
}
if ((h = hio_open_mem(mem, size)) == NULL)
if ((h = hio_open_mem(mem, size, 0)) == NULL)
return -XMP_ERROR_SYSTEM;
ret = test_module(info, h);
@ -212,16 +202,10 @@ int xmp_test_module_from_file(void *file, struct xmp_test_info *info)
return -XMP_ERROR_SYSTEM;
#ifndef LIBXMP_NO_DEPACKERS
if (libxmp_decrunch(&h, NULL, &temp) < 0) {
if (libxmp_decrunch(h, NULL, &temp) < 0) {
ret = -XMP_ERROR_DEPACK;
goto err;
}
/* get size after decrunch */
if (hio_size(h) < 256) { /* set minimum valid module size */
ret = -XMP_ERROR_FORMAT;
goto err;
}
#endif
ret = test_module(info, h);
@ -265,7 +249,6 @@ static int load_module(xmp_context opaque, HIO_HANDLE *h)
test_result = load_result = -1;
for (i = 0; format_loaders[i] != NULL; i++) {
hio_seek(h, 0, SEEK_SET);
hio_error(h); /* reset error flag */
D_(D_WARN "test %s", format_loaders[i]->name);
test_result = format_loaders[i]->test(h, NULL, 0);
@ -277,11 +260,6 @@ static int load_module(xmp_context opaque, HIO_HANDLE *h)
}
}
#ifndef LIBXMP_CORE_PLAYER
if (test_result == 0 && load_result == 0)
set_md5sum(h, m->md5);
#endif
if (test_result < 0) {
xmp_release_module(opaque);
return -XMP_ERROR_FORMAT;
@ -330,6 +308,11 @@ static int load_module(xmp_context opaque, HIO_HANDLE *h)
libxmp_adjust_string(mod->xxs[i].name);
}
#ifndef LIBXMP_CORE_PLAYER
if (test_result == 0 && load_result == 0)
set_md5sum(h, m->md5);
#endif
libxmp_load_epilogue(ctx);
ret = libxmp_prepare_scan(ctx);
@ -358,22 +341,21 @@ int xmp_load_module(xmp_context opaque, const char *path)
struct context_data *ctx = (struct context_data *)opaque;
#ifndef LIBXMP_CORE_PLAYER
struct module_data *m = &ctx->m;
long size;
#endif
#ifndef LIBXMP_NO_DEPACKERS
char *temp_name;
#endif
HIO_HANDLE *h;
struct stat st;
int ret;
D_(D_WARN "path = %s", path);
if (stat(path, &st) < 0) {
ret = libxmp_get_filetype(path);
if (ret == XMP_FILETYPE_NONE) {
return -XMP_ERROR_SYSTEM;
}
if (S_ISDIR(st.st_mode)) {
if (ret & XMP_FILETYPE_DIR) {
errno = EISDIR;
return -XMP_ERROR_SYSTEM;
}
@ -384,20 +366,12 @@ int xmp_load_module(xmp_context opaque, const char *path)
#ifndef LIBXMP_NO_DEPACKERS
D_(D_INFO "decrunch");
if (libxmp_decrunch(&h, path, &temp_name) < 0) {
if (libxmp_decrunch(h, path, &temp_name) < 0) {
ret = -XMP_ERROR_DEPACK;
goto err;
}
#endif
#ifndef LIBXMP_CORE_PLAYER
size = hio_size(h);
if (size < 256) { /* get size after decrunch */
ret = -XMP_ERROR_FORMAT;
goto err;
}
#endif
if (ctx->state > XMP_STATE_UNLOADED)
xmp_release_module(opaque);
@ -415,7 +389,7 @@ int xmp_load_module(xmp_context opaque, const char *path)
}
m->filename = path; /* For ALM, SSMT, etc */
m->size = size;
m->size = hio_size(h);
#else
ctx->m.filename = NULL;
ctx->m.dirname = NULL;
@ -452,7 +426,7 @@ int xmp_load_module_from_memory(xmp_context opaque, const void *mem, long size)
return -XMP_ERROR_INVALID;
}
if ((h = hio_open_mem(mem, size)) == NULL)
if ((h = hio_open_mem(mem, size, 0)) == NULL)
return -XMP_ERROR_SYSTEM;
if (ctx->state > XMP_STATE_UNLOADED)
@ -579,17 +553,9 @@ void xmp_release_module(xmp_context opaque)
}
free(m->xtra);
free(m->midi);
m->xtra = NULL;
#ifndef LIBXMP_CORE_DISABLE_IT
if (m->xsmp != NULL) {
for (i = 0; i < mod->smp; i++) {
libxmp_free_sample(&m->xsmp[i]);
}
free(m->xsmp);
m->xsmp = NULL;
}
#endif
m->midi = NULL;
libxmp_free_scan(ctx);

View file

@ -1,5 +1,5 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
* Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -53,12 +53,15 @@ const struct module_quirk mq[] = {
XMP_FLAGS_FIXLOOP, XMP_MODE_AUTO
},
#if 0
/* "siedler ii" (added by Daniel Åkerud) */
/* Timing fixed by vblank scan compare. CIA: 32m10s VBlank: 12m32s */
{
{ 0x70, 0xaa, 0x03, 0x4d, 0xfb, 0x2f, 0x1f, 0x73,
0xd9, 0xfd, 0xba, 0xfe, 0x13, 0x1b, 0xb7, 0x01 },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
},
#endif
/* "Klisje paa klisje" (added by Kjetil Torgrim Homme) */
{
@ -99,6 +102,126 @@ const struct module_quirk mq[] = {
0, XMP_MODE_PROTRACKER
},
/* grooving3.mod */
/* length 150778 crc32c 0xfdcf9aadU */
{
{ 0xdb, 0x61, 0x22, 0x44, 0x39, 0x85, 0x74, 0xe9,
0xfa, 0x11, 0xb8, 0xfb, 0x87, 0xe8, 0xde, 0xc5, },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
},
/* mod.Rundgren */
/* length 195078 crc32c 0x8fa827a4U */
{
{ 0x9a, 0xdb, 0xb2, 0x09, 0x07, 0x1c, 0x44, 0x82,
0xc5, 0xdf, 0x83, 0x52, 0xcc, 0x73, 0x9f, 0x20, },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
},
/* dance feeling by Audiomonster */
/* length 169734 crc32c 0x79fa2c9bU */
{
{ 0x31, 0x2c, 0x3d, 0xaa, 0x5f, 0x1a, 0x54, 0x44,
0x9d, 0xf7, 0xc4, 0x41, 0x8a, 0xc5, 0x01, 0x02, },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
},
/* knights melody by Audiomonster */
/* length 77798 crc32c 0x7bf19c5bU */
{
{ 0x31, 0xc3, 0x0e, 0x32, 0xfc, 0x99, 0x95, 0xd2,
0x97, 0x20, 0xb3, 0x77, 0x50, 0x05, 0xfe, 0xa5, },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
},
/* hcomme by Bouffon */
/* length 71346 crc32c 0x4ad49cb3U */
{
{ 0x6e, 0xf9, 0x78, 0xc1, 0x80, 0xae, 0x51, 0x06,
0x05, 0x7c, 0x6e, 0xd0, 0x26, 0x7e, 0xfe, 0x3d, },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
},
/* ((((aquapool)))) by Dolphin */
/* length 62932 crc32c 0x05b103fcU */
{
{ 0xff, 0x0b, 0xe0, 0x26, 0xc6, 0x31, 0xb5, 0x9b,
0x94, 0x83, 0x94, 0x99, 0x7e, 0x24, 0x7c, 0xdd, },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
},
/* 100yarddash by Dr. Awesome */
/* length 104666 crc32c 0xd2b0e4a6U */
{
{ 0x5b, 0xff, 0x2f, 0xb8, 0xef, 0x3c, 0xbe, 0x55,
0xa8, 0xe2, 0xa7, 0xcf, 0x5c, 0xbd, 0xdd, 0xb2, },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
},
/* jazz-reggae-funk by Droid */
/* length 115564 crc32c 0x41ff635fU */
{
{ 0xe5, 0x6e, 0x31, 0x2f, 0x62, 0x80, 0xc1, 0x9d,
0x2f, 0x24, 0x54, 0xf3, 0x89, 0x3f, 0x94, 0x6c, },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
},
/* hard and heavy by Fish */
/* length 69814 crc32c 0x1f09d3d5U */
{
{ 0x6b, 0xce, 0x39, 0x94, 0x75, 0x42, 0x06, 0x74,
0xd2, 0x83, 0xbc, 0x5e, 0x7b, 0x42, 0x1f, 0xa0, },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
},
/* crazy valley by Julius and Droid */
/* length 97496 crc32c 0xb8eec40eU */
{
{ 0x23, 0x77, 0x18, 0x1d, 0x21, 0x9b, 0x41, 0x8f,
0xc1, 0xb4, 0xf4, 0xf8, 0x22, 0xdd, 0xd8, 0xb6, },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
},
/* THE ILLOGICAL ONE by Rhino */
/* length 173432 crc32c 0xcb4e2987U */
{
{ 0xd8, 0xc2, 0xbb, 0xe6, 0x11, 0xd0, 0x5c, 0x02,
0x8e, 0x3b, 0xcb, 0x7c, 0x4a, 0x7d, 0x43, 0xa0, },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
},
/* sounds of holiday by Spacebrain */
/* length 309520 crc32c 0x28804a57U */
{
{ 0x36, 0x18, 0x19, 0xa4, 0x9d, 0xa2, 0xa2, 0x6f,
0x58, 0x60, 0xc4, 0xd9, 0x0d, 0xa2, 0x9f, 0x49, },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
},
/* sunisinus by Speed-Head */
/* length 175706 crc32c 0x2e56451bU */
{
{ 0x7e, 0x69, 0x44, 0xb6, 0x38, 0x0d, 0x27, 0x14,
0x70, 0x5d, 0x44, 0xce, 0xce, 0xdd, 0x37, 0x31, },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
},
/* eat the fulcrum bop by The Assassin */
/* length 160286 crc32c 0x583a4683U */
{
{ 0x11, 0xe9, 0x6f, 0x62, 0xe1, 0xc3, 0xc5, 0xcc,
0x3b, 0xaf, 0xea, 0x69, 0x4b, 0xce, 0x5f, 0xec, },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
},
/* obvious disaster by Tip */
/* length 221086 crc32c 0x51c6d489U */
{
{ 0x06, 0x8e, 0x69, 0x01, 0x49, 0x8f, 0xbd, 0x0f,
0xfc, 0xb7, 0x8f, 0x2a, 0x91, 0xe1, 0x8b, 0xe8, },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
},
/* alien nation by Turtle */
/* length 167548 crc32c 0xc9ec1674U */
{
{ 0x71, 0xdf, 0x11, 0xac, 0x5d, 0xec, 0x07, 0xf8,
0x10, 0x6f, 0x28, 0x8d, 0x47, 0x59, 0x54, 0x9b, },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
},
/* illusions!2 by Zuhl */
/* length 289770 crc32c 0x6bf5fbcfU */
{
{ 0xca, 0x37, 0x8c, 0x0e, 0x87, 0x4f, 0x1e, 0xcd,
0xa3, 0xe9, 0x8b, 0xdd, 0x11, 0x46, 0x8d, 0x69, },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
},
{
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
@ -127,7 +250,7 @@ char *libxmp_adjust_string(char *s)
int i;
for (i = 0; i < strlen(s); i++) {
if (!isprint((int)s[i]) || ((uint8) s[i] > 127))
if (!isprint((unsigned char)s[i]) || ((uint8) s[i] > 127))
s[i] = ' ';
}
@ -150,9 +273,21 @@ static void check_envelope(struct xmp_envelope *env)
env->flg &= ~XMP_ENVELOPE_LOOP;
}
/* Disable envelope loop if invalid sustain */
if (env->sus >= env->npt) {
env->flg &= ~XMP_ENVELOPE_ON;
/* Disable envelope sustain if invalid sustain */
if (env->sus >= env->npt || env->sue >= env->npt) {
env->flg &= ~XMP_ENVELOPE_SUS;
}
}
static void clamp_volume_envelope(struct module_data *m, struct xmp_envelope *env)
{
/* Clamp broken values in the volume envelope to the expected range. */
if (env->flg & XMP_ENVELOPE_ON) {
int i;
for (i = 0; i < env->npt; i++) {
int16 *data = &env->data[i * 2 + 1];
CLAMP(*data, 0, m->volbase);
}
}
}
@ -171,26 +306,25 @@ void libxmp_load_prologue(struct context_data *ctx)
m->quirk = 0;
m->read_event_type = READ_EVENT_MOD;
m->period_type = PERIOD_AMIGA;
m->compare_vblank = 0;
m->comment = NULL;
m->scan_cnt = NULL;
m->midi = NULL;
/* Set defaults */
m->mod.pat = 0;
m->mod.trk = 0;
m->mod.chn = 4;
m->mod.ins = 0;
m->mod.smp = 0;
m->mod.spd = 6;
m->mod.bpm = 125;
m->mod.len = 0;
m->mod.rst = 0;
m->mod.pat = 0;
m->mod.trk = 0;
m->mod.chn = 4;
m->mod.ins = 0;
m->mod.smp = 0;
m->mod.spd = 6;
m->mod.bpm = 125;
m->mod.len = 0;
m->mod.rst = 0;
#ifndef LIBXMP_CORE_PLAYER
m->extra = NULL;
#endif
#ifndef LIBXMP_CORE_DISABLE_IT
m->xsmp = NULL;
#endif
m->time_factor = DEFAULT_TIME_FACTOR;
@ -209,7 +343,7 @@ void libxmp_load_epilogue(struct context_data *ctx)
struct xmp_module *mod = &m->mod;
int i, j;
mod->gvl = m->gvol;
mod->gvl = m->gvol;
/* Sanity check for module parameters */
CLAMP(mod->len, 0, XMP_MAX_MOD_LENGTH);
@ -230,7 +364,7 @@ void libxmp_load_epilogue(struct context_data *ctx)
if (mod->spd <= 0 || mod->spd > 255) {
mod->spd = 6;
}
CLAMP(mod->bpm, XMP_MIN_BPM, 255);
CLAMP(mod->bpm, XMP_MIN_BPM, 1000);
/* Set appropriate values for instrument volumes and subinstrument
* global volumes when QUIRK_INSVOL is not set, to keep volume values
@ -254,8 +388,28 @@ void libxmp_load_epilogue(struct context_data *ctx)
check_envelope(&mod->xxi[i].aei);
check_envelope(&mod->xxi[i].fei);
check_envelope(&mod->xxi[i].pei);
clamp_volume_envelope(m, &mod->xxi[i].aei);
}
#ifndef LIBXMP_CORE_DISABLE_IT
/* TODO: there's no unintrusive and clean way to get this struct into
* libxmp_load_sample currently, so bound these fields here for now. */
for (i = 0; i < mod->smp; i++) {
struct xmp_sample *xxs = &mod->xxs[i];
struct extra_sample_data *xtra = &m->xtra[i];
if (xtra->sus < 0) {
xtra->sus = 0;
}
if (xtra->sue > xxs->len) {
xtra->sue = xxs->len;
}
if (xtra->sus >= xxs->len || xtra->sus >= xtra->sue) {
xtra->sus = xtra->sue = 0;
xxs->flg &= ~(XMP_SAMPLE_SLOOP | XMP_SAMPLE_SLOOP_BIDIR);
}
}
#endif
p->filter = 0;
p->mode = XMP_MODE_AUTO;
p->flags = p->player_flags;
@ -283,7 +437,7 @@ int libxmp_prepare_scan(struct context_data *ctx)
return 0;
}
m->scan_cnt = calloc(sizeof (uint8 *), mod->len);
m->scan_cnt = (uint8 **) calloc(mod->len, sizeof(uint8 *));
if (m->scan_cnt == NULL)
return -XMP_ERROR_SYSTEM;
@ -299,7 +453,7 @@ int libxmp_prepare_scan(struct context_data *ctx)
}
pat = pat_idx >= mod->pat ? NULL : mod->xxp[pat_idx];
m->scan_cnt[i] = calloc(1, pat && pat->rows ? pat->rows : 1);
m->scan_cnt[i] = (uint8 *) calloc(1, (pat && pat->rows)? pat->rows : 1);
if (m->scan_cnt[i] == NULL)
return -XMP_ERROR_SYSTEM;
}
@ -309,6 +463,7 @@ int libxmp_prepare_scan(struct context_data *ctx)
void libxmp_free_scan(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
int i;
@ -320,6 +475,9 @@ void libxmp_free_scan(struct context_data *ctx)
free(m->scan_cnt);
m->scan_cnt = NULL;
}
free(p->scan);
p->scan = NULL;
}
/* Process player personality flags */
@ -394,6 +552,9 @@ int libxmp_set_player_mode(struct context_data *ctx)
return -1;
}
if (p->mode != XMP_MODE_AUTO)
m->compare_vblank = 0;
return 0;
}

View file

@ -20,6 +20,10 @@
#define SAMPLE_FLAG_HSC 0x2000 /* HSC Adlib synth instrument */
#define SAMPLE_FLAG_ADPCM 0x4000 /* ADPCM4 encoded samples */
/* libxmp_test_name flags */
#define TEST_NAME_IGNORE_AFTER_0 0x0001
#define TEST_NAME_IGNORE_AFTER_CR 0x0002
#define DEFPAN(x) (0x80 + ((x) - 0x80) * m->defpan / 100)
int libxmp_init_instrument (struct module_data *);
@ -30,16 +34,18 @@ int libxmp_alloc_pattern (struct xmp_module *, int);
int libxmp_alloc_track (struct xmp_module *, int, int);
int libxmp_alloc_tracks_in_pattern (struct xmp_module *, int);
int libxmp_alloc_pattern_tracks (struct xmp_module *, int, int);
#ifndef LIBXMP_CORE_PLAYER
int libxmp_alloc_pattern_tracks_long(struct xmp_module *, int, int);
#endif
char *libxmp_instrument_name (struct xmp_module *, int, uint8 *, int);
char *libxmp_copy_adjust (char *, uint8 *, int);
int libxmp_copy_name_for_fopen (char *, const char *, int);
int libxmp_test_name (uint8 *, int);
int libxmp_test_name (const uint8 *, int, int);
void libxmp_read_title (HIO_HANDLE *, char *, int);
void libxmp_set_xxh_defaults (struct xmp_module *);
void libxmp_decode_protracker_event (struct xmp_event *, uint8 *);
void libxmp_decode_noisetracker_event(struct xmp_event *, uint8 *);
void libxmp_decode_protracker_event (struct xmp_event *, const uint8 *);
void libxmp_decode_noisetracker_event(struct xmp_event *, const uint8 *);
void libxmp_disable_continue_fx (struct xmp_event *);
int libxmp_check_filename_case (const char *, const char *, char *, int);
void libxmp_get_instrument_path (struct module_data *, char *, int);
@ -47,7 +53,10 @@ void libxmp_set_type (struct module_data *, const char *, ...);
int libxmp_load_sample (struct module_data *, HIO_HANDLE *, int,
struct xmp_sample *, const void *);
void libxmp_free_sample (struct xmp_sample *);
#ifndef LIBXMP_CORE_PLAYER
void libxmp_schism_tracker_string (char *, size_t, int, int);
void libxmp_apply_mpt_preamp (struct module_data *m);
#endif
extern uint8 libxmp_ord_xlat[];
extern const int libxmp_arch_vol_table[];

View file

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

View file

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

View file

@ -46,13 +46,16 @@ size_t mread(void *buf, size_t size, size_t num, MFILE *m)
}
if (should_read > can_read) {
should_read = can_read;
memcpy(buf, m->start + m->pos, can_read);
m->pos += can_read;
return can_read / size;
} else {
memcpy(buf, m->start + m->pos, should_read);
m->pos += should_read;
return num;
}
memcpy(buf, m->start + m->pos, should_read);
m->pos += should_read;
return should_read / size;
}
@ -89,23 +92,26 @@ int meof(MFILE *m)
return CAN_READ(m) <= 0;
}
MFILE *mopen(const void *ptr, long size)
MFILE *mopen(const void *ptr, long size, int free_after_use)
{
MFILE *m;
m = (MFILE *)malloc(sizeof (MFILE));
m = (MFILE *) malloc(sizeof(MFILE));
if (m == NULL)
return NULL;
m->start = ptr;
m->start = (const unsigned char *)ptr;
m->pos = 0;
m->size = size;
m->free_after_use = free_after_use;
return m;
}
int mclose(MFILE *m)
{
if (m->free_after_use)
free((void *)m->start);
free(m);
return 0;
}

View file

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

View file

@ -1,5 +1,5 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
* Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -99,10 +99,15 @@
*(buffer++) += smp_in * (old_vl >> 8); old_vl += delta_l; \
} while (0)
/* IT's WAV output driver uses a clamp that seems to roughly match this:
* compare the WAV output of OpenMPT env-flt-max.it and filter-reset.it */
#define MIX_FILTER_CLAMP(a) CLAMP((a), -65536, 65535)
#define MIX_MONO_FILTER() do { \
sl = (a0 * smp_in * vl + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \
sl = (a0 * smp_in + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \
MIX_FILTER_CLAMP(sl); \
fl2 = fl1; fl1 = sl; \
*(buffer++) += sl; \
*(buffer++) += sl * vl; \
} while (0)
#define MIX_MONO_FILTER_AC() do { \
@ -122,12 +127,14 @@
} while (0)
#define MIX_STEREO_FILTER() do { \
sr = (a0 * smp_in * vr + b0 * fr1 + b1 * fr2) >> FILTER_SHIFT; \
sr = (a0 * smp_in + b0 * fr1 + b1 * fr2) >> FILTER_SHIFT; \
MIX_FILTER_CLAMP(sr); \
fr2 = fr1; fr1 = sr; \
sl = (a0 * smp_in * vl + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \
sl = (a0 * smp_in + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \
MIX_FILTER_CLAMP(sl); \
fl2 = fl1; fl1 = sl; \
*(buffer++) += sr; \
*(buffer++) += sl; \
*(buffer++) += sr * vr; \
*(buffer++) += sl * vl; \
} while (0)
#define MIX_STEREO_FILTER_AC() do { \
@ -138,17 +145,19 @@
old_vl += delta_l; \
} while (0)
#define MIX_STEREO_FILTER_AC() do { \
int vr = old_vr >> 8; \
int vl = old_vl >> 8; \
MIX_STEREO_FILTER(); \
old_vr += delta_r; \
old_vl += delta_l; \
/* For "nearest" to be nearest neighbor (instead of floor), the position needs
* to be rounded. This only needs to be done once at the start of mixing, and
* is required for reverse samples to round the same as forward samples.
*/
#define NEAREST_ROUND() do { \
frac += (1 << (SMIX_SHIFT - 1)); \
pos += frac >> SMIX_SHIFT; \
frac &= SMIX_MASK; \
} while (0)
#define VAR_NORM(x) \
register int smp_in; \
x *sptr = vi->sptr; \
x *sptr = (x *)vi->sptr; \
unsigned int pos = vi->pos; \
int frac = (1 << SMIX_SHIFT) * (vi->pos - (int)vi->pos)
@ -194,6 +203,10 @@
#endif
#ifdef _MSC_VER
#pragma warning(disable:4457) /* shadowing */
#endif
/*
* Nearest neighbor mixers
@ -204,6 +217,7 @@
MIXER(mono_8bit_nearest)
{
VAR_NORM(int8);
NEAREST_ROUND();
LOOP { NEAREST_NEIGHBOR(); MIX_MONO(); UPDATE_POS(); }
}
@ -214,6 +228,7 @@ MIXER(mono_8bit_nearest)
MIXER(mono_16bit_nearest)
{
VAR_NORM(int16);
NEAREST_ROUND();
LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_MONO(); UPDATE_POS(); }
}
@ -223,6 +238,7 @@ MIXER(mono_16bit_nearest)
MIXER(stereo_8bit_nearest)
{
VAR_NORM(int8);
NEAREST_ROUND();
LOOP { NEAREST_NEIGHBOR(); MIX_STEREO(); UPDATE_POS(); }
}
@ -232,6 +248,7 @@ MIXER(stereo_8bit_nearest)
MIXER(stereo_16bit_nearest)
{
VAR_NORM(int16);
NEAREST_ROUND();
LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_STEREO(); UPDATE_POS(); }
}

View file

@ -1,5 +1,5 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
* Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -45,8 +45,24 @@
#define LIM16_HI 32767
#define LIM16_LO -32768
struct loop_data
{
#define LOOP_PROLOGUE 1
#define LOOP_EPILOGUE 2
void *sptr;
int start;
int end;
int first_loop;
int _16bit;
int active;
uint32 prologue[LOOP_PROLOGUE];
uint32 epilogue[LOOP_EPILOGUE];
};
#define MIX_FN(x) void libxmp_mix_##x(struct mixer_voice *, int32 *, int, int, int, int, int, int, int)
#define ANTICLICK_FPSHIFT 24
MIX_FN(mono_8bit_nearest);
MIX_FN(mono_8bit_linear);
MIX_FN(mono_16bit_nearest);
@ -209,8 +225,10 @@ static void do_anticlick(struct context_data *ctx, int voc, int32 *buf, int coun
struct player_data *p = &ctx->p;
struct mixer_data *s = &ctx->s;
struct mixer_voice *vi = &p->virt.voice_array[voc];
int smp_l, smp_r, max_x2;
int smp_l, smp_r;
int discharge = s->ticksize >> ANTICLICK_SHIFT;
int stepmul, stepval;
uint32 stepmul_sq;
smp_r = vi->sright;
smp_l = vi->sleft;
@ -231,14 +249,23 @@ static void do_anticlick(struct context_data *ctx, int voc, int32 *buf, int coun
return;
}
max_x2 = count * count;
stepval = (1 << ANTICLICK_FPSHIFT) / count;
stepmul = stepval * count;
while (count--) {
if (~s->format & XMP_FORMAT_MONO) {
*buf++ += (count * (smp_r >> 10) / max_x2 * count) << 10;
if (~s->format & XMP_FORMAT_MONO) {
while ((stepmul -= stepval) > 0) {
/* Truncate to 16-bits of precision so the product is 32-bits. */
stepmul_sq = stepmul >> (ANTICLICK_FPSHIFT - 16);
stepmul_sq *= stepmul_sq;
*buf++ += (stepmul_sq * (int64)smp_r) >> 32;
*buf++ += (stepmul_sq * (int64)smp_l) >> 32;
}
} else {
while ((stepmul -= stepval) > 0) {
stepmul_sq = stepmul >> (ANTICLICK_FPSHIFT - 16);
stepmul_sq *= stepmul_sq;
*buf++ += (stepmul_sq * (int64)smp_l) >> 32;
}
*buf++ += (count * (smp_l >> 10) / max_x2 * count) << 10;
}
}
@ -256,6 +283,7 @@ static void set_sample_end(struct context_data *ctx, int voc, int end)
if (end) {
SET_NOTE(NOTE_SAMPLE_END);
vi->fidx &= ~FLAG_ACTIVE;
if (HAS_QUIRK(QUIRK_RSTCHN)) {
libxmp_virt_resetvoice(ctx, voc, 0);
}
@ -264,45 +292,165 @@ static void set_sample_end(struct context_data *ctx, int voc, int end)
}
}
static void adjust_voice_end(struct mixer_voice *vi, struct xmp_sample *xxs)
/* Back up sample data before and after loop and replace it for interpolation.
* TODO: use an overlap buffer like OpenMPT? This is easier, but a little dirty. */
static void init_sample_wraparound(struct mixer_data *s, struct loop_data *ld,
struct mixer_voice *vi, struct xmp_sample *xxs)
{
if (xxs->flg & XMP_SAMPLE_LOOP) {
int bidir;
int i;
if (!vi->sptr || s->interp == XMP_INTERP_NEAREST || (~xxs->flg & XMP_SAMPLE_LOOP)) {
ld->active = 0;
return;
}
ld->sptr = vi->sptr;
ld->start = vi->start;
ld->end = vi->end;
ld->first_loop = !(vi->flags & SAMPLE_LOOP);
ld->_16bit = (xxs->flg & XMP_SAMPLE_16BIT);
ld->active = 1;
bidir = vi->flags & VOICE_BIDIR;
if (ld->_16bit) {
uint16 *start = (uint16 *)vi->sptr + vi->start;
uint16 *end = (uint16 *)vi->sptr + vi->end;
if (!ld->first_loop) {
for (i = 0; i < LOOP_PROLOGUE; i++) {
int j = i - LOOP_PROLOGUE;
ld->prologue[i] = start[j];
start[j] = bidir ? start[-1 - j] : end[j];
}
}
for (i = 0; i < LOOP_EPILOGUE; i++) {
ld->epilogue[i] = end[i];
end[i] = bidir ? end[-1 - i] : start[i];
}
} else {
uint8 *start = (uint8 *)vi->sptr + vi->start;
uint8 *end = (uint8 *)vi->sptr + vi->end;
if (!ld->first_loop) {
for (i = 0; i < LOOP_PROLOGUE; i++) {
int j = i - LOOP_PROLOGUE;
ld->prologue[i] = start[j];
start[j] = bidir ? start[-1 - j] : end[j];
}
}
for (i = 0; i < LOOP_EPILOGUE; i++) {
ld->epilogue[i] = end[i];
end[i] = bidir ? end[-1 - i] : start[i];
}
}
}
/* Restore old sample data from before and after loop. */
static void reset_sample_wraparound(struct loop_data *ld)
{
int i;
if (!ld->active)
return;
if (ld->_16bit) {
uint16 *start = (uint16 *)ld->sptr + ld->start;
uint16 *end = (uint16 *)ld->sptr + ld->end;
if (!ld->first_loop) {
for (i = 0; i < LOOP_PROLOGUE; i++)
start[i - LOOP_PROLOGUE] = ld->prologue[i];
}
for (i = 0; i < LOOP_EPILOGUE; i++)
end[i] = ld->epilogue[i];
} else {
uint8 *start = (uint8 *)ld->sptr + ld->start;
uint8 *end = (uint8 *)ld->sptr + ld->end;
if (!ld->first_loop) {
for (i = 0; i < LOOP_PROLOGUE; i++)
start[i - LOOP_PROLOGUE] = ld->prologue[i];
}
for (i = 0; i < LOOP_EPILOGUE; i++)
end[i] = ld->epilogue[i];
}
}
static int has_active_sustain_loop(struct context_data *ctx, struct mixer_voice *vi,
struct xmp_sample *xxs)
{
#ifndef LIBXMP_CORE_DISABLE_IT
struct module_data *m = &ctx->m;
return vi->smp < m->mod.smp && (xxs->flg & XMP_SAMPLE_SLOOP) && (~vi->flags & VOICE_RELEASE);
#else
return 0;
#endif
}
static int has_active_loop(struct context_data *ctx, struct mixer_voice *vi,
struct xmp_sample *xxs)
{
return (xxs->flg & XMP_SAMPLE_LOOP) || has_active_sustain_loop(ctx, vi, xxs);
}
/* Update the voice endpoints based on current sample loop state. */
static void adjust_voice_end(struct context_data *ctx, struct mixer_voice *vi,
struct xmp_sample *xxs, struct extra_sample_data *xtra)
{
vi->flags &= ~VOICE_BIDIR;
if (xtra && has_active_sustain_loop(ctx, vi, xxs)) {
vi->start = xtra->sus;
vi->end = xtra->sue;
if (xxs->flg & XMP_SAMPLE_SLOOP_BIDIR) vi->flags |= VOICE_BIDIR;
} else if (xxs->flg & XMP_SAMPLE_LOOP) {
vi->start = xxs->lps;
if ((xxs->flg & XMP_SAMPLE_LOOP_FULL) && (~vi->flags & SAMPLE_LOOP)) {
vi->end = xxs->len;
} else {
vi->end = xxs->lpe;
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) vi->flags |= VOICE_BIDIR;
}
} else {
vi->start = 0;
vi->end = xxs->len;
}
}
static void loop_reposition(struct context_data *ctx, struct mixer_voice *vi, struct xmp_sample *xxs)
static int loop_reposition(struct context_data *ctx, struct mixer_voice *vi,
struct xmp_sample *xxs, struct extra_sample_data *xtra)
{
#ifndef LIBXMP_CORE_DISABLE_IT
struct module_data *m = &ctx->m;
#endif
int loop_size = xxs->lpe - xxs->lps;
int loop_changed = !(vi->flags & SAMPLE_LOOP);
/* Reposition for next loop */
vi->pos -= loop_size; /* forward loop */
vi->end = xxs->lpe;
vi->flags |= SAMPLE_LOOP;
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
vi->end += loop_size; /* unrolled loop */
vi->pos -= loop_size; /* forward loop */
if(loop_changed)
adjust_voice_end(ctx, vi, xxs, xtra);
#ifndef LIBXMP_CORE_DISABLE_IT
/* OpenMPT Bidi-Loops.it: "In Impulse Trackers software mixer,
* ping-pong loops are shortened by one sample.
*/
if (IS_PLAYER_MODE_IT()) {
vi->end--;
vi->pos++;
if (~vi->flags & VOICE_BIDIR) {
/* Reposition for next loop */
if (~vi->flags & VOICE_REVERSE)
vi->pos -= vi->end - vi->start;
else
vi->pos += vi->end - vi->start;
} else {
/* Bidirectional loop: switch directions */
vi->flags ^= VOICE_REVERSE;
/* Wrap voice position around endpoint */
if (vi->flags & VOICE_REVERSE) {
/* OpenMPT Bidi-Loops.it: "In Impulse Tracker's software
* mixer, ping-pong loops are shortened by one sample."
*/
vi->pos = vi->end * 2 - ctx->s.bidir_adjust - vi->pos;
} else {
vi->pos = vi->start * 2 - vi->pos;
}
#endif
}
return loop_changed;
}
@ -316,7 +464,10 @@ void libxmp_mixer_prepare(struct context_data *ctx)
s->ticksize = s->freq * m->time_factor * m->rrate / p->bpm / 1000;
bytelen = s->ticksize * sizeof(int);
if (s->ticksize < (1 << ANTICLICK_SHIFT))
s->ticksize = 1 << ANTICLICK_SHIFT;
bytelen = s->ticksize * sizeof(int32);
if (~s->format & XMP_FORMAT_MONO) {
bytelen *= 2;
}
@ -331,13 +482,14 @@ void libxmp_mixer_softmixer(struct context_data *ctx)
struct mixer_data *s = &ctx->s;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
struct extra_sample_data *xtra;
struct xmp_sample *xxs;
struct mixer_voice *vi;
double step;
struct loop_data loop_data;
double step, step_dir;
int samples, size;
int vol_l, vol_r, voc, usmp;
int vol, vol_l, vol_r, voc, usmp;
int prev_l, prev_r = 0;
int lps, lpe;
int32 *buf_pos;
MIX_FP mix_fn;
MIX_FP *mixerset;
@ -368,6 +520,13 @@ void libxmp_mixer_softmixer(struct context_data *ctx)
}
#endif
#ifndef LIBXMP_CORE_DISABLE_IT
/* OpenMPT Bidi-Loops.it: "In Impulse Tracker's software
* mixer, ping-pong loops are shortened by one sample."
*/
s->bidir_adjust = IS_PLAYER_MODE_IT() ? 1 : 0;
#endif
libxmp_mixer_prepare(ctx);
for (voc = 0; voc < p->virt.maxvoc; voc++) {
@ -391,66 +550,56 @@ void libxmp_mixer_softmixer(struct context_data *ctx)
continue;
}
/* Negative positions can be left over from some
* loop edge cases. These can be safely clamped. */
if (vi->pos < 0.0)
vi->pos = 0.0;
vi->pos0 = vi->pos;
buf_pos = s->buf32;
vol = vi->vol;
/* Mix volume (S3M and IT) */
if (m->mvolbase > 0 && m->mvol != m->mvolbase) {
vol = vol * m->mvol / m->mvolbase;
}
if (vi->pan == PAN_SURROUND) {
vol_r = vi->vol * 0x80;
vol_l = -vi->vol * 0x80;
vol_r = vol * 0x80;
vol_l = -vol * 0x80;
} else {
vol_r = vi->vol * (0x80 - vi->pan);
vol_l = vi->vol * (0x80 + vi->pan);
vol_r = vol * (0x80 - vi->pan);
vol_l = vol * (0x80 + vi->pan);
}
if (vi->smp < mod->smp) {
xxs = &mod->xxs[vi->smp];
xtra = &m->xtra[vi->smp];
c5spd = m->xtra[vi->smp].c5spd;
} else {
xxs = &ctx->smix.xxs[vi->smp - mod->smp];
xtra = NULL;
c5spd = m->c4rate;
}
step = C4_PERIOD * c5spd / s->freq / vi->period;
if (step < 0.001) { /* otherwise m5v-nwlf.it crashes */
/* Don't allow <=0, otherwise m5v-nwlf.it crashes
* Extremely high values that can cause undefined float/int
* conversion are also possible for c5spd modules. */
if (step < 0.001 || step > (double)SHRT_MAX) {
continue;
}
#ifndef LIBXMP_CORE_DISABLE_IT
if (xxs->flg & XMP_SAMPLE_SLOOP && vi->smp < mod->smp) {
if (~vi->flags & VOICE_RELEASE) {
if (vi->pos < m->xsmp[vi->smp].lpe) {
xxs = &m->xsmp[vi->smp];
}
}
}
adjust_voice_end(vi, xxs);
#endif
lps = xxs->lps;
lpe = xxs->lpe;
if (p->flags & XMP_FLAGS_FIXLOOP) {
lps >>= 1;
}
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
vi->end += lpe - lps;
#ifndef LIBXMP_CORE_DISABLE_IT
if (IS_PLAYER_MODE_IT()) {
vi->end--;
}
#endif
}
adjust_voice_end(ctx, vi, xxs, xtra);
init_sample_wraparound(s, &loop_data, vi, xxs);
rampsize = s->ticksize >> ANTICLICK_SHIFT;
delta_l = (vol_l - vi->old_vl) / rampsize;
delta_r = (vol_r - vi->old_vr) / rampsize;
usmp = 0;
for (size = s->ticksize; size > 0; ) {
for (size = usmp = s->ticksize; size > 0; ) {
int split_noloop = 0;
if (p->xc_data[vi->chn].split) {
@ -459,20 +608,34 @@ void libxmp_mixer_softmixer(struct context_data *ctx)
/* How many samples we can write before the loop break
* or sample end... */
if (vi->pos >= vi->end) {
samples = 0;
usmp = 1;
if (~vi->flags & VOICE_REVERSE) {
if (vi->pos >= vi->end) {
samples = 0;
if (--usmp <= 0)
break;
} else {
double c = ceil(((double)vi->end - vi->pos) / step);
/* ...inside the tick boundaries */
if (c > size) {
c = size;
}
samples = c;
}
step_dir = step;
} else {
int s = ceil(((double)vi->end - vi->pos) / step);
/* ...inside the tick boundaries */
if (s > size) {
s = size;
}
samples = s;
if (samples > 0) {
usmp = 0;
/* Reverse */
if (vi->pos <= vi->start) {
samples = 0;
if (--usmp <= 0)
break;
} else {
double c = ceil((vi->pos - (double)vi->start) / step);
if (c > size) {
c = size;
}
samples = c;
}
step_dir = -step;
}
if (vi->vol) {
@ -496,7 +659,7 @@ void libxmp_mixer_softmixer(struct context_data *ctx)
#ifndef LIBXMP_CORE_DISABLE_IT
/* See OpenMPT env-flt-max.it */
if (vi->filter.cutoff >= 0xfe &&
vi->filter.resonance == 0) {
vi->filter.resonance == 0) {
mixer_id &= ~FLAG_FILTER;
}
#endif
@ -521,7 +684,7 @@ void libxmp_mixer_softmixer(struct context_data *ctx)
if (mix_fn != NULL) {
mix_fn(vi, buf_pos, samples,
vol_l >> 8, vol_r >> 8, step * (1 << SMIX_SHIFT), rsize, delta_l, delta_r);
vol_l >> 8, vol_r >> 8, step_dir * (1 << SMIX_SHIFT), rsize, delta_l, delta_r);
}
buf_pos += mix_size;
@ -537,31 +700,43 @@ void libxmp_mixer_softmixer(struct context_data *ctx)
}
}
vi->pos += step * samples;
vi->pos += step_dir * samples;
/* No more samples in this tick */
size -= samples + usmp;
size -= samples;
if (size <= 0) {
if (xxs->flg & XMP_SAMPLE_LOOP) {
if (vi->pos + step > vi->end) {
vi->pos += step;
loop_reposition(ctx, vi, xxs);
if (has_active_loop(ctx, vi, xxs)) {
/* This isn't particularly important for
* forward loops, but reverse loops need
* to be corrected here to avoid their
* negative positions getting clamped
* in later ticks. */
if (((~vi->flags & VOICE_REVERSE) && vi->pos >= vi->end) ||
((vi->flags & VOICE_REVERSE) && vi->pos <= vi->start)) {
if (loop_reposition(ctx, vi, xxs, xtra)) {
reset_sample_wraparound(&loop_data);
init_sample_wraparound(s, &loop_data, vi, xxs);
}
}
}
continue;
}
/* First sample loop run */
if ((~xxs->flg & XMP_SAMPLE_LOOP) || split_noloop) {
if (!has_active_loop(ctx, vi, xxs) || split_noloop) {
do_anticlick(ctx, voc, buf_pos, size);
set_sample_end(ctx, voc, 1);
size = 0;
continue;
}
loop_reposition(ctx, vi, xxs);
if (loop_reposition(ctx, vi, xxs, xtra)) {
reset_sample_wraparound(&loop_data);
init_sample_wraparound(s, &loop_data, vi, xxs);
}
}
reset_sample_wraparound(&loop_data);
vi->old_vl = vol_l;
vi->old_vr = vol_r;
}
@ -581,7 +756,7 @@ void libxmp_mixer_softmixer(struct context_data *ctx)
downmix_int_8bit(s->buffer, s->buf32, size, s->amplify,
s->format & XMP_FORMAT_UNSIGNED ? 0x80 : 0);
} else {
downmix_int_16bit((int16 *)s->buffer, s->buf32, size,s->amplify,
downmix_int_16bit((int16 *)s->buffer, s->buf32, size, s->amplify,
s->format & XMP_FORMAT_UNSIGNED ? 0x8000 : 0);
}
@ -594,12 +769,14 @@ void libxmp_mixer_voicepos(struct context_data *ctx, int voc, double pos, int ac
struct module_data *m = &ctx->m;
struct mixer_voice *vi = &p->virt.voice_array[voc];
struct xmp_sample *xxs;
int lps;
struct extra_sample_data *xtra;
if (vi->smp < m->mod.smp) {
xxs = &m->mod.xxs[vi->smp];
xxs = &m->mod.xxs[vi->smp];
xtra = &m->xtra[vi->smp];
} else {
xxs = &ctx->smix.xxs[vi->smp - m->mod.smp];
xxs = &ctx->smix.xxs[vi->smp - m->mod.smp];
xtra = NULL;
}
if (xxs->flg & XMP_SAMPLE_SYNTH) {
@ -608,29 +785,16 @@ void libxmp_mixer_voicepos(struct context_data *ctx, int voc, double pos, int ac
vi->pos = pos;
adjust_voice_end(vi, xxs);
adjust_voice_end(ctx, vi, xxs, xtra);
if (vi->pos >= vi->end) {
if (xxs->flg & XMP_SAMPLE_LOOP) {
vi->pos = xxs->lps;
} else {
vi->pos = xxs->len;
}
}
lps = xxs->lps;
if (p->flags & XMP_FLAGS_FIXLOOP) {
lps >>= 1;
}
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
vi->end += (xxs->lpe - lps);
#ifndef LIBXMP_CORE_DISABLE_IT
if (IS_PLAYER_MODE_IT()) {
vi->end--;
}
#endif
vi->pos = vi->end;
/* Restart forward sample loops. */
if ((~vi->flags & VOICE_REVERSE) && has_active_loop(ctx, vi, xxs))
loop_reposition(ctx, vi, xxs, xtra);
} else if ((vi->flags & VOICE_REVERSE) && vi->pos <= 0.1) {
/* Hack: 0 maps to the end for reversed samples. */
vi->pos = vi->end;
}
if (ac) {
@ -650,12 +814,6 @@ double libxmp_mixer_getvoicepos(struct context_data *ctx, int voc)
return 0;
}
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
if (vi->pos >= xxs->lpe) {
return xxs->lpe - (vi->pos - xxs->lpe) - 1;
}
}
return vi->pos;
}
@ -674,7 +832,7 @@ void libxmp_mixer_setpatch(struct context_data *ctx, int voc, int smp, int ac)
vi->smp = smp;
vi->vol = 0;
vi->pan = 0;
vi->flags &= ~SAMPLE_LOOP;
vi->flags &= ~(SAMPLE_LOOP | VOICE_REVERSE | VOICE_BIDIR);
vi->fidx = 0;
@ -746,12 +904,42 @@ void libxmp_mixer_release(struct context_data *ctx, int voc, int rel)
struct mixer_voice *vi = &p->virt.voice_array[voc];
if (rel) {
#ifndef LIBXMP_CORE_DISABLE_IT
/* Cancel voice reverse when releasing an active sustain loop,
* unless the main loop is bidirectional. This is done both for
* bidirectional sustain loops and for forward sustain loops
* that have been reversed with MPT S9F Play Backward. */
if (~vi->flags & VOICE_RELEASE) {
struct xmp_sample *xxs = libxmp_get_sample(ctx, vi->smp);
if (has_active_sustain_loop(ctx, vi, xxs) &&
(~xxs->flg & XMP_SAMPLE_LOOP_BIDIR))
vi->flags &= ~VOICE_REVERSE;
}
#endif
vi->flags |= VOICE_RELEASE;
} else {
vi->flags &= ~VOICE_RELEASE;
}
}
void libxmp_mixer_reverse(struct context_data *ctx, int voc, int rev)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi = &p->virt.voice_array[voc];
/* Don't reverse samples that have already ended */
if (~vi->fidx & FLAG_ACTIVE) {
return;
}
if (rev) {
vi->flags |= VOICE_REVERSE;
} else {
vi->flags &= ~VOICE_REVERSE;
}
}
void libxmp_mixer_seteffect(struct context_data *ctx, int voc, int type, int val)
{
#ifndef LIBXMP_CORE_DISABLE_IT
@ -813,11 +1001,12 @@ int libxmp_mixer_on(struct context_data *ctx, int rate, int format, int c4rate)
s->format = format;
s->amplify = DEFAULT_AMPLIFY;
s->mix = DEFAULT_MIX;
/* s->pbase = C4_PERIOD * c4rate / s->freq; */(void) c4rate;
/* s->pbase = C4_PERIOD * c4rate / s->freq; */
s->interp = XMP_INTERP_LINEAR; /* default interpolation type */
s->dsp = XMP_DSP_LOWPASS; /* enable filters by default */
/* s->numvoc = SMIX_NUMVOC; */
s->dtright = s->dtleft = 0;
s->bidir_adjust = 0;
return 0;

View file

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

View file

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

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
#define M_LN2 0.69314718055994530942
#endif
#if !defined(HAVE_ROUND) || defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DJGPP__)
static inline double libxmp_round(double val)
{
return (val >= 0.0)? floor(val + 0.5) : ceil(val - 0.5);
}
#else
#define libxmp_round round
#endif
#ifdef LIBXMP_PAULA_SIMULATOR
/* Get period from note using Protracker tuning */
@ -186,13 +183,13 @@ double libxmp_note_to_period(struct context_data *ctx, int n, int f, double adj)
switch (m->period_type) {
case PERIOD_LINEAR:
per = (240.0 - d) * 16; /* Linear */
per = (240.0 - d) * 16; /* Linear */
break;
case PERIOD_CSPD:
per = 8363.0 * pow(2, n / 12) / 32 + f; /* Hz */
per = 8363.0 * pow(2, n / 12.0) / 32 + f; /* Hz */
break;
default:
per = PERIOD_BASE / pow(2, d / 12); /* Amiga */
per = PERIOD_BASE / pow(2, d / 12); /* Amiga */
}
#ifndef LIBXMP_CORE_PLAYER
@ -228,7 +225,7 @@ int libxmp_period_to_bend(struct context_data *ctx, double p, int n, double adj)
struct module_data *m = &ctx->m;
double d;
if (n == 0) {
if (n == 0 || p < 0.1) {
return 0;
}
@ -255,7 +252,7 @@ void libxmp_c2spd_to_note(int c2spd, int *n, int *f)
{
int c;
if (c2spd == 0) {
if (c2spd <= 0) {
*n = *f = 0;
return;
}
@ -264,3 +261,25 @@ void libxmp_c2spd_to_note(int c2spd, int *n, int *f)
*n = c / 128;
*f = c % 128;
}
#ifndef LIBXMP_CORE_PLAYER
/* Gravis Ultrasound frequency increments in steps of Hz/1024, where Hz is the
* current rate of the card and is dependent on the active channel count.
* For <=14 channels, the rate is 44100. For 15 to 32 channels, the rate is
* round(14 * 44100 / active_channels).
*/
static const double GUS_rates[19] = {
/* <= 14 */ 44100.0,
/* 15-20 */ 41160.0, 38587.5, 36317.65, 34300.0, 32494.74, 30870.0,
/* 21-26 */ 29400.0, 28063.64, 26843.48, 25725.0, 24696.0, 23746.15,
/* 27-32 */ 22866.67, 22050.0, 21289.66, 20580.0, 19916.13, 19294.75
};
/* Get a Gravis Ultrasound frequency offset in Hz for a given number of steps.
*/
double libxmp_gus_frequency_steps(int num_steps, int num_channels_active)
{
CLAMP(num_channels_active, 14, 32);
return (num_steps * GUS_rates[num_channels_active - 14]) / 1024.0;
}
#endif

View file

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

View file

@ -1,5 +1,5 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
* Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -53,7 +53,6 @@ static const struct retrig_control rval[] = {
{ 0, 1, 1 }, { 1, 1, 1 }, { 2, 1, 1 }, { 4, 1, 1 },
{ 8, 1, 1 }, { 16, 1, 1 }, { 0, 3, 2 }, { 0, 2, 1 },
{ 0, 0, 1 } /* Note cut */
};
@ -69,15 +68,15 @@ static const struct retrig_control rval[] = {
static int check_envelope_end(struct xmp_envelope *env, int x)
{
int16 *data = env->data;
int index;
int idx;
if (~env->flg & XMP_ENVELOPE_ON || env->npt <= 0)
return 0;
index = (env->npt - 1) * 2;
idx = (env->npt - 1) * 2;
/* last node */
if (x >= data[index] || index == 0) {
if (x >= data[idx] || idx == 0) {
if (~env->flg & XMP_ENVELOPE_LOOP) {
return 1;
}
@ -90,27 +89,30 @@ static int get_envelope(struct xmp_envelope *env, int x, int def)
{
int x1, x2, y1, y2;
int16 *data = env->data;
int index;
int idx;
if (x < 0 || ~env->flg & XMP_ENVELOPE_ON || env->npt <= 0)
return def;
index = (env->npt - 1) * 2;
idx = (env->npt - 1) * 2;
x1 = data[index]; /* last node */
if (x >= x1 || index == 0) {
return data[index + 1];
x1 = data[idx]; /* last node */
if (x >= x1 || idx == 0) {
return data[idx + 1];
}
do {
index -= 2;
x1 = data[index];
} while (index > 0 && x1 > x);
idx -= 2;
x1 = data[idx];
} while (idx > 0 && x1 > x);
/* interpolate */
y1 = data[index + 1];
x2 = data[index + 2];
y2 = data[index + 3];
y1 = data[idx + 1];
x2 = data[idx + 2];
y2 = data[idx + 3];
/* Interpolation requires x1 <= x <= x2 */
if (x < x1 || x2 < x1) return y1;
return x2 == x1 ? y2 : ((y2 - y1) * (x - x1) / (x2 - x1)) + y1;
}
@ -232,14 +234,14 @@ static int update_envelope(struct xmp_envelope *env, int x, int release, int key
static int check_envelope_fade(struct xmp_envelope *env, int x)
{
int16 *data = env->data;
int index;
int idx;
if (~env->flg & XMP_ENVELOPE_ON)
return 0;
index = (env->npt - 1) * 2; /* last node */
if (x > data[index]) {
if (data[index + 1] == 0)
idx = (env->npt - 1) * 2; /* last node */
if (x > data[idx]) {
if (data[idx + 1] == 0)
return -1;
else
return 1;
@ -249,6 +251,218 @@ static int check_envelope_fade(struct xmp_envelope *env, int x)
}
#ifndef LIBXMP_CORE_DISABLE_IT
/* Impulse Tracker's filter effects are implemented using its MIDI macros.
* Any module can customize these and they are parameterized using various
* player and mixer values, which requires parsing them here instead of in
* the loader. Since they're MIDI macros, they can contain actual MIDI junk
* that needs to be skipped, and one macro may have multiple IT commands. */
struct midi_stream
{
const char *pos;
int buffer;
int param;
};
static int midi_nibble(struct context_data *ctx, struct channel_data *xc,
int chn, struct midi_stream *in)
{
struct xmp_instrument *xxi;
struct mixer_voice *vi;
int voc, val, byte = -1;
if (in->buffer >= 0) {
val = in->buffer;
in->buffer = -1;
return val;
}
while (*in->pos) {
val = *(in->pos)++;
if (val >= '0' && val <= '9') return val - '0';
if (val >= 'A' && val <= 'F') return val - 'A' + 10;
switch (val) {
case 'z': /* Macro parameter */
byte = in->param;
break;
case 'n': /* Host key */
byte = xc->key & 0x7f;
break;
case 'h': /* Host channel */
byte = chn;
break;
case 'o': /* Offset effect memory */
/* Intentionally not clamped, see ZxxSecrets.it */
byte = xc->offset.memory;
break;
case 'm': /* Voice reverse flag */
voc = libxmp_virt_mapchannel(ctx, chn);
vi = (voc >= 0) ? &ctx->p.virt.voice_array[voc] : NULL;
byte = vi ? !!(vi->flags & VOICE_REVERSE) : 0;
break;
case 'v': /* Note velocity */
xxi = libxmp_get_instrument(ctx, xc->ins);
byte = ((uint32)ctx->p.gvol *
(uint32)xc->volume *
(uint32)xc->mastervol *
(uint32)xc->gvl *
(uint32)(xxi ? xxi->vol : 0x40)) >> 24UL;
CLAMP(byte, 1, 127);
break;
case 'u': /* Computed velocity */
byte = xc->macro.finalvol >> 3;
CLAMP(byte, 1, 127);
break;
case 'x': /* Note panning */
byte = xc->macro.notepan >> 1;
CLAMP(byte, 0, 127);
break;
case 'y': /* Computed panning */
byte = xc->info_finalpan >> 1;
CLAMP(byte, 0, 127);
break;
case 'a': /* Ins MIDI Bank hi */
case 'b': /* Ins MIDI Bank lo */
case 'p': /* Ins MIDI Program */
case 's': /* MPT: SysEx checksum */
byte = 0;
break;
case 'c': /* Ins MIDI Channel */
return 0;
}
/* Byte output */
if (byte >= 0) {
in->buffer = byte & 0xf;
return (byte >> 4) & 0xf;
}
}
return -1;
}
static int midi_byte(struct context_data *ctx, struct channel_data *xc,
int chn, struct midi_stream *in)
{
int a = midi_nibble(ctx, xc, chn, in);
int b = midi_nibble(ctx, xc, chn, in);
return (a >= 0 && b >= 0) ? (a << 4) | b : -1;
}
static void apply_midi_macro_effect(struct channel_data *xc, int type, int val)
{
switch (type) {
case 0: /* Filter cutoff */
xc->filter.cutoff = val << 1;
break;
case 1: /* Filter resonance */
xc->filter.resonance = val << 1;
break;
}
}
static void execute_midi_macro(struct context_data *ctx, struct channel_data *xc,
int chn, struct midi_macro *midi, int param)
{
struct midi_stream in;
int byte, cmd, val;
in.pos = midi->data;
in.buffer = -1;
in.param = param;
while (*in.pos) {
/* Very simple MIDI 1.0 parser--most bytes can just be ignored
* (or passed through, if libxmp gets MIDI output). All bytes
* with bit 7 are statuses which interrupt unfinished messages
* ("Data Types: Status Bytes") or are real time messages.
* This holds even for SysEx messages, which end at ANY non-
* real time status ("System Common Messages: EOX").
*
* IT intercepts internal "messages" that begin with F0 F0,
* which in MIDI is a useless zero-length SysEx followed by
* a second SysEx. They are four bytes long including F0 F0,
* and shouldn't be passed through. OpenMPT also uses F0 F1.
*/
cmd = -1;
byte = midi_byte(ctx, xc, chn, &in);
if (byte == 0xf0) {
byte = midi_byte(ctx, xc, chn, &in);
if (byte == 0xf0 || byte == 0xf1)
cmd = byte & 0xf;
}
if (cmd < 0) {
if (byte == 0xfa || byte == 0xfc || byte == 0xff) {
/* These real time statuses can appear anywhere
* (even in SysEx) and reset the channel filter
* params. See: OpenMPT ZxxSecrets.it */
apply_midi_macro_effect(xc, 0, 127);
apply_midi_macro_effect(xc, 1, 0);
}
continue;
}
cmd = midi_byte(ctx, xc, chn, &in) | (cmd << 8);
val = midi_byte(ctx, xc, chn, &in);
if (cmd < 0 || cmd >= 0x80 || val < 0 || val >= 0x80) {
continue;
}
apply_midi_macro_effect(xc, cmd, val);
}
}
/* This needs to occur before all process_* functions:
* - It modifies the filter parameters, used by process_frequency.
* - process_volume and process_pan apply slide effects, which the
* filter parameters expect to occur after macro effect parsing. */
static void update_midi_macro(struct context_data *ctx, int chn)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct channel_data *xc = &p->xc_data[chn];
struct midi_macro_data *midicfg = m->midi;
struct midi_macro *macro;
int val;
if (TEST(MIDI_MACRO) && HAS_QUIRK(QUIRK_FILTER)) {
if (xc->macro.slide > 0) {
xc->macro.val += xc->macro.slide;
if (xc->macro.val > xc->macro.target) {
xc->macro.val = xc->macro.target;
xc->macro.slide = 0;
}
} else if (xc->macro.slide < 0) {
xc->macro.val += xc->macro.slide;
if (xc->macro.val < xc->macro.target) {
xc->macro.val = xc->macro.target;
xc->macro.slide = 0;
}
} else if (p->frame) {
/* Execute non-smooth macros on frame 0 only */
return;
}
val = (int)xc->macro.val;
if (val >= 0x80) {
if (midicfg) {
macro = &midicfg->fixed[val - 0x80];
execute_midi_macro(ctx, xc, chn, macro, val);
} else if (val < 0x90) {
/* Default fixed macro: set resonance */
apply_midi_macro_effect(xc, 1, (val - 0x80) << 3);
}
} else if (midicfg) {
macro = &midicfg->param[xc->macro.active];
execute_midi_macro(ctx, xc, chn, macro, val);
} else if (xc->macro.active == 0) {
/* Default parameterized macro 0: set filter cutoff */
apply_midi_macro_effect(xc, 0, val);
}
}
}
#endif /* LIBXMP_CORE_DISABLE_IT */
#ifndef LIBXMP_CORE_PLAYER
/* From http://www.un4seen.com/forum/?topic=7554.0
@ -268,23 +482,39 @@ static const int invloop_table[] = {
0, 5, 6, 7, 8, 10, 11, 13, 16, 19, 22, 26, 32, 43, 64, 128
};
static void update_invloop(struct module_data *m, struct channel_data *xc)
static void update_invloop(struct context_data *ctx, struct channel_data *xc)
{
struct xmp_sample *xxs = &m->mod.xxs[xc->smp];
int len;
struct xmp_sample *xxs = libxmp_get_sample(ctx, xc->smp);
struct module_data *m = &ctx->m;
int lps, len = -1;
xc->invloop.count += invloop_table[xc->invloop.speed];
if ((xxs->flg & XMP_SAMPLE_LOOP) && xc->invloop.count >= 128) {
if (xxs != NULL) {
if (xxs->flg & XMP_SAMPLE_LOOP) {
lps = xxs->lps;
len = xxs->lpe - lps;
} else if (xxs->flg & XMP_SAMPLE_SLOOP) {
/* Some formats that support invert loop use sustain
* loops instead (Digital Symphony). */
lps = m->xtra[xc->smp].sus;
len = m->xtra[xc->smp].sue - lps;
}
}
if (len >= 0 && xc->invloop.count >= 128) {
xc->invloop.count = 0;
len = xxs->lpe - xxs->lps;
if (++xc->invloop.pos > len) {
xc->invloop.pos = 0;
}
if (xxs->data == NULL) {
return;
}
if (~xxs->flg & XMP_SAMPLE_16BIT) {
xxs->data[xxs->lps + xc->invloop.pos] ^= 0xff;
xxs->data[lps + xc->invloop.pos] ^= 0xff;
}
}
}
@ -412,7 +642,7 @@ static void reset_channels(struct context_data *ctx)
xc->mastervol = mod->xxc[i].vol;
xc->pan.val = mod->xxc[i].pan;
}
#ifndef LIBXMP_CORE_DISABLE_IT
xc->filter.cutoff = 0xff;
@ -624,7 +854,7 @@ static int tremor_s3m(struct context_data *ctx, int chn, int finalvol)
* Update channel data
*/
#define DOENV_RELEASE ((TEST_NOTE(NOTE_RELEASE) || act == VIRT_ACTION_OFF))
#define DOENV_RELEASE ((TEST_NOTE(NOTE_ENV_RELEASE) || act == VIRT_ACTION_OFF))
static void process_volume(struct context_data *ctx, int chn, int act)
{
@ -648,7 +878,7 @@ static void process_volume(struct context_data *ctx, int chn, int act)
/* If IT, only apply fadeout on note release if we don't
* have envelope, or if we have envelope loop
*/
if (TEST_NOTE(NOTE_RELEASE) || act == VIRT_ACTION_OFF) {
if (TEST_NOTE(NOTE_ENV_RELEASE) || act == VIRT_ACTION_OFF) {
if ((~instrument->aei.flg & XMP_ENVELOPE_ON) ||
(instrument->aei.flg & XMP_ENVELOPE_LOOP)) {
fade = 1;
@ -656,29 +886,30 @@ static void process_volume(struct context_data *ctx, int chn, int act)
}
} else {
if (~instrument->aei.flg & XMP_ENVELOPE_ON) {
if (TEST_NOTE(NOTE_RELEASE)) {
if (TEST_NOTE(NOTE_ENV_RELEASE)) {
xc->fadeout = 0;
}
}
if (TEST_NOTE(NOTE_RELEASE) || act == VIRT_ACTION_OFF) {
if (TEST_NOTE(NOTE_ENV_RELEASE) || act == VIRT_ACTION_OFF) {
fade = 1;
}
}
if (TEST_NOTE(NOTE_FADEOUT) || act == VIRT_ACTION_FADE) {
fade = 1;
if (!TEST_PER(VENV_PAUSE)) {
xc->v_idx = update_envelope(&instrument->aei, xc->v_idx,
DOENV_RELEASE, TEST(KEY_OFF), IS_PLAYER_MODE_IT());
}
if (fade) {
if (xc->fadeout > xc->ins_fade) {
xc->fadeout -= xc->ins_fade;
} else {
xc->fadeout = 0;
vol_envelope = get_envelope(&instrument->aei, xc->v_idx, 64);
if (check_envelope_end(&instrument->aei, xc->v_idx)) {
if (vol_envelope == 0) {
SET_NOTE(NOTE_END);
}
SET_NOTE(NOTE_ENV_END);
}
/* IT starts fadeout automatically at the end of the volume envelope. */
switch (check_envelope_fade(&instrument->aei, xc->v_idx)) {
case -1:
SET_NOTE(NOTE_END);
@ -694,17 +925,19 @@ static void process_volume(struct context_data *ctx, int chn, int act)
}
}
if (!TEST_PER(VENV_PAUSE)) {
xc->v_idx = update_envelope(&instrument->aei, xc->v_idx,
DOENV_RELEASE, TEST(KEY_OFF), IS_PLAYER_MODE_IT());
/* IT envelope fadeout starts immediately after the envelope tick,
* so process fadeout after the volume envelope. */
if (TEST_NOTE(NOTE_FADEOUT) || act == VIRT_ACTION_FADE) {
fade = 1;
}
vol_envelope = get_envelope(&instrument->aei, xc->v_idx, 64);
if (check_envelope_end(&instrument->aei, xc->v_idx)) {
if (vol_envelope == 0) {
if (fade) {
if (xc->fadeout > xc->ins_fade) {
xc->fadeout -= xc->ins_fade;
} else {
xc->fadeout = 0;
SET_NOTE(NOTE_END);
}
SET_NOTE(NOTE_ENV_END);
}
/* If note ended in background channel, we can safely reset it */
@ -762,6 +995,9 @@ static void process_volume(struct context_data *ctx, int chn, int act)
} else {
finalvol = tremor_s3m(ctx, chn, finalvol);
}
#ifndef LIBXMP_CORE_DISABLE_IT
xc->macro.finalvol = finalvol;
#endif
if (chn < m->mod.chn) {
finalvol = finalvol * p->master_vol / 100;
@ -863,7 +1099,7 @@ static void process_frequency(struct context_data *ctx, int chn, int act)
/* Sanity check */
if (period < 0.1) {
period = 0.1;
}
}
/* Arpeggio */
arp = arpeggio(ctx, xc);
@ -894,7 +1130,7 @@ static void process_frequency(struct context_data *ctx, int chn, int act)
}
}
}
/* Envelope */
if (xc->f_idx >= 0 && (~instrument->fei.flg & XMP_ENVELOPE_FLT)) {
@ -946,12 +1182,12 @@ static void process_frequency(struct context_data *ctx, int chn, int act)
/* For xmp_get_frame_info() */
xc->info_pitchbend = linear_bend >> 7;
xc->info_period = final_period * 4096;
xc->info_period = MIN(final_period * 4096, INT_MAX);
if (IS_PERIOD_MODRNG()) {
CLAMP(xc->info_period,
libxmp_note_to_period(ctx, MAX_NOTE_MOD, xc->finetune, 0) * 4096,
libxmp_note_to_period(ctx, MIN_NOTE_MOD, xc->finetune, 0) * 4096);
const double min_period = libxmp_note_to_period(ctx, MAX_NOTE_MOD, xc->finetune, 0) * 4096;
const double max_period = libxmp_note_to_period(ctx, MIN_NOTE_MOD, xc->finetune, 0) * 4096;
CLAMP(xc->info_period, min_period, max_period);
} else if (xc->info_period < (1 << 12)) {
xc->info_period = (1 << 12);
}
@ -977,18 +1213,22 @@ static void process_frequency(struct context_data *ctx, int chn, int act)
if (cutoff > 0xff) {
cutoff = 0xff;
} else if (cutoff < 0xff) {
}
/* IT: cutoff 127 + resonance 0 turns off the filter, but this
* is only applied when playing a new note without toneporta.
* All other combinations take effect immediately.
* See OpenMPT filter-reset.it, filter-reset-carry.it */
if (cutoff < 0xfe || resonance > 0 || xc->filter.can_disable) {
int a0, b0, b1;
libxmp_filter_setup(s->freq, cutoff, resonance, &a0, &b0, &b1);
libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_A0, a0);
libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_B0, b0);
libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_B1, b1);
libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_RESONANCE, resonance);
libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_CUTOFF, cutoff);
xc->filter.can_disable = 0;
}
/* Always set cutoff */
libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_CUTOFF, cutoff);
#endif
}
@ -1018,6 +1258,7 @@ static void process_pan(struct context_data *ctx, int chn, int act)
libxmp_lfo_update(&xc->panbrello.lfo);
}
}
xc->macro.notepan = xc->pan.val + panbrello + 0x80;
#endif
channel_pan = xc->pan.val;
@ -1080,13 +1321,19 @@ static void update_volume(struct context_data *ctx, int chn)
#ifndef LIBXMP_CORE_PLAYER
if (TEST_PER(VOL_SLIDE)) {
if (xc->vol.slide > 0 && xc->volume > m->volbase) {
xc->volume = m->volbase;
RESET_PER(VOL_SLIDE);
if (xc->vol.slide > 0) {
int target = MAX(xc->vol.target - 1, m->volbase);
if (xc->volume > target) {
xc->volume = target;
RESET_PER(VOL_SLIDE);
}
}
if (xc->vol.slide < 0 && xc->volume < 0) {
xc->volume = 0;
RESET_PER(VOL_SLIDE);
if (xc->vol.slide < 0) {
int target = xc->vol.target > 0 ? MIN(0, xc->vol.target - 1) : 0;
if (xc->volume < target) {
xc->volume = target;
RESET_PER(VOL_SLIDE);
}
}
}
#endif
@ -1111,7 +1358,7 @@ static void update_volume(struct context_data *ctx, int chn)
* Unlike fine volume slides in the effect column,
* fine volume slides in the volume column are only
* ever executed on the first tick -- not on multiples
* of the first tick if there is a pattern delay.
* of the first tick if there is a pattern delay.
*/
if (!f->rowdelay_set || f->rowdelay_set & ROWDELAY_FIRST_FRAME) {
xc->volume += xc->vol.fslide2;
@ -1178,7 +1425,7 @@ static void update_frequency(struct context_data *ctx, int chn)
}
}
}
}
}
}
if (is_first_frame(ctx)) {
@ -1199,10 +1446,11 @@ static void update_frequency(struct context_data *ctx, int chn)
case PERIOD_LINEAR:
CLAMP(xc->period, MIN_PERIOD_L, MAX_PERIOD_L);
break;
case PERIOD_MODRNG:
CLAMP(xc->period,
libxmp_note_to_period(ctx, MAX_NOTE_MOD, xc->finetune, 0),
libxmp_note_to_period(ctx, MIN_NOTE_MOD, xc->finetune, 0));
case PERIOD_MODRNG: {
const double min_period = libxmp_note_to_period(ctx, MAX_NOTE_MOD, xc->finetune, 0);
const double max_period = libxmp_note_to_period(ctx, MIN_NOTE_MOD, xc->finetune, 0);
CLAMP(xc->period, min_period, max_period);
}
break;
}
@ -1261,6 +1509,11 @@ static void play_channel(struct context_data *ctx, int chn)
}
}
#ifndef LIBXMP_CORE_DISABLE_IT
/* IT MIDI macros need to update regardless of the current voice state. */
update_midi_macro(ctx, chn);
#endif
act = libxmp_virt_cstat(ctx, chn);
if (act == VIRT_INVALID) {
/* We need this to keep processing global volume slides */
@ -1298,9 +1551,16 @@ static void play_channel(struct context_data *ctx, int chn)
xc->volume += rval[xc->retrig.type].s;
xc->volume *= rval[xc->retrig.type].m;
xc->volume /= rval[xc->retrig.type].d;
xc->retrig.count = LSN(xc->retrig.val);
xc->retrig.count = LSN(xc->retrig.val);
if (xc->retrig.limit > 0) {
/* Limit the number of retriggers. */
--xc->retrig.limit;
if (xc->retrig.limit == 0)
RESET(RETRIG);
}
}
}
}
/* Do keyoff */
if (xc->keyoff) {
@ -1308,7 +1568,7 @@ static void play_channel(struct context_data *ctx, int chn)
SET_NOTE(NOTE_RELEASE);
}
libxmp_virt_release(ctx, chn, TEST_NOTE(NOTE_RELEASE));
libxmp_virt_release(ctx, chn, TEST_NOTE(NOTE_SAMPLE_RELEASE));
update_volume(ctx, chn);
update_frequency(ctx, chn);
@ -1319,13 +1579,13 @@ static void play_channel(struct context_data *ctx, int chn)
process_pan(ctx, chn, act);
#ifndef LIBXMP_CORE_PLAYER
if (HAS_QUIRK(QUIRK_PROTRACK) && xc->ins < mod->ins) {
update_invloop(m, xc);
if (HAS_QUIRK(QUIRK_PROTRACK | QUIRK_INVLOOP) && xc->ins < mod->ins) {
update_invloop(ctx, xc);
}
#endif
if (TEST_NOTE(NOTE_SUSEXIT)) {
SET_NOTE(NOTE_RELEASE);
SET_NOTE(NOTE_ENV_RELEASE);
}
xc->info_position = libxmp_virt_getvoicepos(ctx, chn);
@ -1342,7 +1602,7 @@ static void inject_event(struct context_data *ctx)
struct xmp_module *mod = &m->mod;
struct smix_data *smix = &ctx->smix;
int chn;
for (chn = 0; chn < mod->chn + smix->chn; chn++) {
struct xmp_event *e = &p->inject_event[chn];
if (e->_flag > 0) {
@ -1362,13 +1622,14 @@ static void next_order(struct context_data *ctx)
struct flow_control *f = &p->flow;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
int reset_gvol = 0;
int mark;
do {
p->ord++;
p->ord++;
/* Restart module */
mark = HAS_QUIRK(QUIRK_MARKER) && mod->xxo[p->ord] == 0xff;
mark = HAS_QUIRK(QUIRK_MARKER) && p->ord < mod->len && mod->xxo[p->ord] == 0xff;
if (p->ord >= mod->len || mark) {
if (mod->rst > mod->len ||
mod->xxo[mod->rst] >= mod->pat ||
@ -1381,11 +1642,19 @@ static void next_order(struct context_data *ctx)
p->ord = m->seq_data[p->sequence].entry_point;
}
}
p->gvol = m->xxo_info[p->ord].gvl;
/* This might be a marker, so delay updating global
* volume until an actual pattern is found */
reset_gvol = 1;
}
} while (mod->xxo[p->ord] >= mod->pat);
if (reset_gvol)
p->gvol = m->xxo_info[p->ord].gvl;
#ifndef LIBXMP_CORE_PLAYER
/* Archimedes line jump -- don't reset time tracking. */
if (f->jump_in_pat != p->ord)
#endif
p->current_time = m->xxo_info[p->ord].time;
f->num_rows = mod->xxp[mod->xxo[p->ord]]->rows;
@ -1398,6 +1667,8 @@ static void next_order(struct context_data *ctx)
p->frame = 0;
#ifndef LIBXMP_CORE_PLAYER
f->jump_in_pat = -1;
/* Reset persistent effects at new pattern */
if (HAS_QUIRK(QUIRK_PERPAT)) {
int chn;
@ -1426,18 +1697,18 @@ static void next_row(struct context_data *ctx)
next_order(ctx);
} else {
if (f->loop_chn) {
p->row = f->loop[f->loop_chn - 1].start - 1;
f->loop_chn = 0;
}
if (f->rowdelay == 0) {
p->row++;
f->rowdelay_set = 0;
} else {
f->rowdelay--;
}
if (f->loop_chn) {
p->row = f->loop[f->loop_chn - 1].start;
f->loop_chn = 0;
}
/* check end of pattern */
if (p->row >= f->num_rows) {
next_order(ctx);
@ -1486,6 +1757,21 @@ static void update_from_ord_info(struct context_data *ctx)
#endif
}
void libxmp_reset_flow(struct context_data *ctx)
{
struct flow_control *f = &ctx->p.flow;
f->jumpline = 0;
f->jump = -1;
f->pbreak = 0;
f->loop_chn = 0;
f->delay = 0;
f->rowdelay = 0;
f->rowdelay_set = 0;
#ifndef LIBXMP_CORE_PLAYER
f->jump_in_pat = -1;
#endif
}
int xmp_start_player(xmp_context opaque, int rate, int format)
{
struct context_data *ctx = (struct context_data *)opaque;
@ -1539,8 +1825,11 @@ int xmp_start_player(xmp_context opaque, int rate, int format)
mod->len = 0;
}
if (mod->len == 0 || mod->chn == 0) {
if (mod->len == 0) {
/* set variables to sane state */
/* Note: previously did this for mod->chn == 0, which caused
* crashes on invalid order 0s. 0 channel modules are technically
* valid (if useless) so just let them play normally. */
p->ord = p->scan[0].ord = 0;
p->row = p->scan[0].row = 0;
f->end_point = 0;
@ -1557,19 +1846,15 @@ int xmp_start_player(xmp_context opaque, int rate, int format)
goto err;
}
f->delay = 0;
f->jumpline = 0;
f->jump = -1;
f->pbreak = 0;
f->rowdelay_set = 0;
libxmp_reset_flow(ctx);
f->loop = calloc(p->virt.virt_channels, sizeof(struct pattern_loop));
f->loop = (struct pattern_loop *) calloc(p->virt.virt_channels, sizeof(struct pattern_loop));
if (f->loop == NULL) {
ret = -XMP_ERROR_SYSTEM;
goto err;
}
p->xc_data = calloc(p->virt.virt_channels, sizeof(struct channel_data));
p->xc_data = (struct channel_data *) calloc(p->virt.virt_channels, sizeof(struct channel_data));
if (p->xc_data == NULL) {
ret = -XMP_ERROR_SYSTEM;
goto err1;
@ -1578,11 +1863,14 @@ int xmp_start_player(xmp_context opaque, int rate, int format)
/* Reset our buffer pointers */
xmp_play_buffer(opaque, NULL, 0, 0);
#ifndef LIBXMP_CORE_PLAYER
#ifndef LIBXMP_CORE_DISABLE_IT
for (i = 0; i < p->virt.virt_channels; i++) {
struct channel_data *xc = &p->xc_data[i];
xc->filter.cutoff = 0xff;
#ifndef LIBXMP_CORE_PLAYER
if (libxmp_new_channel_extras(ctx, xc) < 0)
goto err2;
#endif
}
#endif
reset_channels(ctx);
@ -1776,7 +2064,7 @@ int xmp_play_buffer(xmp_context opaque, void *out_buffer, int size, int loop)
}
p->buffer_data.consumed = 0;
p->buffer_data.in_buffer = fi.buffer;
p->buffer_data.in_buffer = (char *)fi.buffer;
p->buffer_data.in_size = fi.buffer_size;
}
@ -1791,7 +2079,7 @@ int xmp_play_buffer(xmp_context opaque, void *out_buffer, int size, int loop)
return ret;
}
void xmp_end_player(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
@ -1903,7 +2191,7 @@ void xmp_get_frame_info(xmp_context opaque, struct xmp_frame_info *info)
struct xmp_track *track;
struct xmp_event *event;
int trk;
ci->note = c->key;
ci->pitchbend = c->info_pitchbend;
ci->period = c->info_period;
@ -1914,7 +2202,7 @@ void xmp_get_frame_info(xmp_context opaque, struct xmp_frame_info *info)
ci->pan = c->info_finalpan;
ci->reserved = 0;
memset(&ci->event, 0, sizeof(*event));
if (info->pattern < mod->pat && info->row < info->num_rows) {
trk = mod->xxp[info->pattern]->index[i];
track = mod->xxt[trk];

View file

@ -56,20 +56,30 @@ struct retrig_control {
#define FINE_VOLS_2 (1 << 25)
#define KEY_OFF (1 << 26) /* for IT release on envloop end */
#define TREMOR (1 << 27) /* for XM tremor */
#define MIDI_MACRO (1 << 28) /* IT midi macro */
#define NOTE_FADEOUT (1 << 0)
#define NOTE_RELEASE (1 << 1)
#define NOTE_ENV_RELEASE (1 << 1) /* envelope sustain loop release */
#define NOTE_END (1 << 2)
#define NOTE_CUT (1 << 3)
#define NOTE_ENV_END (1 << 4)
#define NOTE_SAMPLE_END (1 << 5)
#define NOTE_SET (1 << 6) /* for IT portamento after keyoff */
#define NOTE_SUSEXIT (1 << 7) /* for delayed note release */
#define NOTE_SUSEXIT (1 << 7) /* for delayed envelope release */
#define NOTE_KEY_CUT (1 << 8) /* note cut with XMP_KEY_CUT event */
#define NOTE_GLISSANDO (1 << 9)
#define NOTE_SAMPLE_RELEASE (1 << 10) /* sample sustain loop release */
/* Most of the time, these should be set/reset together. */
#define NOTE_RELEASE (NOTE_ENV_RELEASE | NOTE_SAMPLE_RELEASE)
/* Note: checking the data pointer for samples should be good enough to filter
* broken samples, since libxmp_load_sample will always allocate it for valid
* samples of >0 length and bound the loop values for these samples. */
#define IS_VALID_INSTRUMENT(x) ((uint32)(x) < mod->ins && mod->xxi[(x)].nsm > 0)
#define IS_VALID_INSTRUMENT_OR_SFX(x) (((uint32)(x) < mod->ins && mod->xxi[(x)].nsm > 0) || (smix->ins > 0 && (uint32)(x) < mod->ins + smix->ins))
#define IS_VALID_SAMPLE(x) ((uint32)(x) < mod->smp && mod->xxs[(x)].data != NULL)
#define IS_VALID_NOTE(x) ((uint32)(x) < XMP_MAX_KEYS)
struct instrument_vibrato {
int phase;
@ -147,6 +157,7 @@ struct channel_data {
int val; /* Retrig value */
int count; /* Retrig counter */
int type; /* Retrig type */
int limit; /* Number of retrigs */
} retrig;
struct {
@ -163,6 +174,9 @@ struct channel_data {
#ifndef LIBXMP_CORE_DISABLE_IT
int fslide2;
int memory2; /* Volume slide effect memory */
#endif
#ifndef LIBXMP_CORE_PLAYER
int target; /* Target for persistent volslide */
#endif
} vol;
@ -194,6 +208,7 @@ struct channel_data {
int dir; /* Tone portamento up/down directionh */
int slide; /* Delta for tone portamento */
int memory; /* Tone portamento effect memory */
int note_memory;/* Tone portamento note memory (ULT) */
} porta;
struct {
@ -207,7 +222,7 @@ struct channel_data {
int fslide; /* Pan fine slide value */
int memory; /* Pan slide effect memory */
int surround; /* Surround channel flag */
} pan;
} pan;
struct {
int speed;
@ -224,8 +239,17 @@ struct channel_data {
int cutoff; /* IT filter cutoff frequency */
int resonance; /* IT filter resonance */
int envelope; /* IT filter envelope */
int can_disable;/* IT hack: allow disabling for cutoff 127 */
} filter;
struct {
float val; /* Current macro effect (use float for slides) */
float target; /* Current macro target (smooth macro) */
float slide; /* Current macro slide (smooth macro) */
int active; /* Current active parameterized macro */
int finalvol; /* Previous tick calculated volume (0-0x400) */
int notepan; /* Previous tick note panning (0x80 center) */
} macro;
#endif
#ifndef LIBXMP_CORE_PLAYER

View file

@ -1,5 +1,5 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
* Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -31,11 +31,6 @@
#endif
static inline int is_valid_note(int note)
{
return (note >= 0 && note < XMP_MAX_KEYS);
}
static struct xmp_subinstrument *get_subinstrument(struct context_data *ctx,
int ins, int key)
{
@ -45,7 +40,7 @@ static struct xmp_subinstrument *get_subinstrument(struct context_data *ctx,
if (IS_VALID_INSTRUMENT(ins)) {
instrument = &mod->xxi[ins];
if (is_valid_note(key)) {
if (IS_VALID_NOTE(key)) {
int mapped = instrument->map[key].ins;
if (mapped != 0xff && mapped >= 0 && mapped < instrument->nsm)
return &instrument->sub[mapped];
@ -67,7 +62,7 @@ static void reset_envelopes(struct context_data *ctx, struct channel_data *xc)
if (!IS_VALID_INSTRUMENT(xc->ins))
return;
RESET_NOTE(NOTE_ENV_END);
RESET_NOTE(NOTE_ENV_END);
xc->v_idx = -1;
xc->p_idx = -1;
@ -76,6 +71,20 @@ static void reset_envelopes(struct context_data *ctx, struct channel_data *xc)
#ifndef LIBXMP_CORE_DISABLE_IT
static void reset_envelope_volume(struct context_data *ctx,
struct channel_data *xc)
{
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
if (!IS_VALID_INSTRUMENT(xc->ins))
return;
RESET_NOTE(NOTE_ENV_END);
xc->v_idx = -1;
}
static void reset_envelopes_carry(struct context_data *ctx,
struct channel_data *xc)
{
@ -109,18 +118,8 @@ static void set_effect_defaults(struct context_data *ctx, int note,
struct channel_data *xc, int is_toneporta)
{
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
struct smix_data *smix = &ctx->smix;
if (sub != NULL && note >= 0) {
struct xmp_instrument *xxi;
if (xc->ins >= mod->ins) {
xxi = &smix->xxi[xc->ins - mod->ins];
} else {
xxi = &mod->xxi[xc->ins];
}
if (!HAS_QUIRK(QUIRK_PROTRACK)) {
xc->finetune = sub->fin;
}
@ -129,16 +128,16 @@ static void set_effect_defaults(struct context_data *ctx, int note,
#ifndef LIBXMP_CORE_DISABLE_IT
if (sub->ifc & 0x80) {
xc->filter.cutoff = (sub->ifc - 0x80) * 2;
} else if (~xxi->fei.flg & XMP_ENVELOPE_FLT) {
xc->filter.cutoff = 0xff;
}
xc->filter.envelope = 0x100;
if (sub->ifr & 0x80) {
xc->filter.resonance = (sub->ifr - 0x80) * 2;
} /* else {
xc->filter.resonance = 0;
} */
}
/* IT: on a new note without toneporta, allow a computed cutoff
* of 127 with resonance 0 to disable the filter. */
xc->filter.can_disable = !is_toneporta;
#endif
/* TODO: should probably expand the LFO period size instead
@ -215,13 +214,14 @@ static void set_period_ft2(struct context_data *ctx, int note,
#ifndef LIBXMP_CORE_PLAYER
#define IS_SFX_PITCH(x) ((x) == FX_PITCH_ADD || (x) == FX_PITCH_SUB)
#define IS_TONEPORTA(x) ((x) == FX_TONEPORTA || (x) == FX_TONE_VSLIDE \
|| (x) == FX_PER_TPORTA)
|| (x) == FX_PER_TPORTA || (x) == FX_ULT_TPORTA \
|| (x) == FX_FAR_TPORTA)
#else
#define IS_TONEPORTA(x) ((x) == FX_TONEPORTA || (x) == FX_TONE_VSLIDE)
#endif
#define set_patch(ctx,chn,ins,smp,note) \
libxmp_virt_setpatch(ctx, chn, ins, smp, note, 0, 0, 0)
libxmp_virt_setpatch(ctx, chn, ins, smp, note, 0, 0, 0, 0)
static int read_event_mod(struct context_data *ctx, struct xmp_event *e, int chn)
{
@ -293,7 +293,7 @@ static int read_event_mod(struct context_data *ctx, struct xmp_event *e, int chn
if (e->note == XMP_KEY_OFF) {
SET_NOTE(NOTE_RELEASE);
use_ins_vol = 0;
} else if (!is_toneporta && is_valid_note(e->note - 1)) {
} else if (!is_toneporta && IS_VALID_NOTE(e->note - 1)) {
xc->key = e->note - 1;
RESET_NOTE(NOTE_END);
@ -306,7 +306,7 @@ static int read_event_mod(struct context_data *ctx, struct xmp_event *e, int chn
note = xc->key + sub->xpo + transp;
smp = sub->sid;
if (mod->xxs[smp].len == 0) {
if (!IS_VALID_SAMPLE(smp)) {
smp = -1;
}
@ -332,6 +332,7 @@ static int read_event_mod(struct context_data *ctx, struct xmp_event *e, int chn
if (e->vol) {
xc->volume = e->vol - 1;
SET(NEW_VOL);
RESET_PER(VOL_SLIDE); /* FIXME: should this be for FAR only? */
}
/* Secondary effect handled first */
@ -375,6 +376,7 @@ static int sustain_check(struct xmp_envelope *env, int idx)
{
return (env &&
(env->flg & XMP_ENVELOPE_ON) &&
(env->flg & XMP_ENVELOPE_SUS) &&
(~env->flg & XMP_ENVELOPE_LOOP) &&
idx == env->data[env->sus << 1]);
}
@ -457,19 +459,17 @@ static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn
/* FT2: Retrieve old instrument volume */
if (ins) {
if (key == 0 || key >= XMP_KEY_OFF) {
struct xmp_subinstrument *sub;
/* Previous instrument */
sub = get_subinstrument(ctx, xc->ins, xc->key);
/* No note */
if (sub != NULL) {
int p = mod->xxc[chn].pan - 128;
int pan = mod->xxc[chn].pan - 128;
xc->volume = sub->vol;
if (!HAS_QUIRK(QUIRK_FTMOD)) {
xc->pan.val = p + ((sub->pan - 128) *
(128 - abs(p))) / 128 + 128;
xc->pan.val = pan + ((sub->pan - 128) *
(128 - abs(pan))) / 128 + 128;
}
xc->ins_fade = mod->xxi[xc->ins].rls;
@ -516,11 +516,8 @@ static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn
}
/* Check note */
if (ins) {
if (key > 0 && key < XMP_KEY_OFF) {
struct xmp_subinstrument *sub;
/* Retrieve volume when we have note */
/* and only if we have instrument, otherwise we're in
@ -530,12 +527,12 @@ static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn
/* Current instrument */
sub = get_subinstrument(ctx, xc->ins, key - 1);
if (sub != NULL) {
int p = mod->xxc[chn].pan - 128;
int pan = mod->xxc[chn].pan - 128;
xc->volume = sub->vol;
if (!HAS_QUIRK(QUIRK_FTMOD)) {
xc->pan.val = p + ((sub->pan - 128) *
(128 - abs(p))) / 128 + 128;
xc->pan.val = pan + ((sub->pan - 128) *
(128 - abs(pan))) / 128 + 128;
}
xc->ins_fade = mod->xxi[xc->ins].rls;
@ -570,7 +567,7 @@ static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn
env_on = 1;
}
}
if (env_on || (!vol_set && (!ev.ins || !delay_fx))) {
if (sustain_check(env, xc->v_idx)) {
/* See OpenMPT EnvOff.xm. In certain
@ -627,7 +624,7 @@ static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn
* and remains in the memory."
*/
sub = NULL;
if (is_valid_note(key - 1)) {
if (IS_VALID_NOTE(key - 1)) {
int k = key - 1;
sub = get_subinstrument(ctx, xc->ins, k);
if (!new_invalid_ins && sub != NULL) {
@ -640,7 +637,7 @@ static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn
}
}
if (is_valid_note(key - 1)) {
if (IS_VALID_NOTE(key - 1)) {
xc->key = --key;
xc->fadeout = 0x10000;
RESET_NOTE(NOTE_END);
@ -658,7 +655,7 @@ static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn
note = key + sub->xpo + transp;
smp = sub->sid;
if (mod->xxs[smp].len == 0) {
if (!IS_VALID_SAMPLE(smp)) {
smp = -1;
}
@ -814,7 +811,7 @@ static int read_event_st3(struct context_data *ctx, struct xmp_event *e, int chn
if (not_same_ins) {
xc->offset.val = 0;
}
} else if (is_valid_note(e->note - 1)) {
} else if (IS_VALID_NOTE(e->note - 1)) {
xc->key = e->note - 1;
RESET_NOTE(NOTE_END);
@ -827,7 +824,7 @@ static int read_event_st3(struct context_data *ctx, struct xmp_event *e, int chn
note = xc->key + sub->xpo + transp;
smp = sub->sid;
if (mod->xxs[smp].len == 0) {
if (!IS_VALID_SAMPLE(smp)) {
smp = -1;
}
@ -961,10 +958,10 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
int is_toneporta, is_release;
int candidate_ins;
int reset_env;
int reset_susloop;
int use_ins_vol;
int sample_mode;
int toneporta_offset;
int disabled_toneporta;
int retrig_ins;
struct xmp_event ev;
@ -987,11 +984,11 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
is_toneporta = 0;
is_release = 0;
reset_env = 0;
reset_susloop = 0;
use_ins_vol = 0;
candidate_ins = xc->ins;
sample_mode = !HAS_QUIRK(QUIRK_VIRTUAL);
toneporta_offset = 0;
disabled_toneporta = 0;
retrig_ins = 0;
/* Keyoff + instrument retrigs current instrument in old fx mode */
@ -1025,7 +1022,7 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
is_toneporta = 1;
}
if (TEST_NOTE(NOTE_RELEASE | NOTE_FADEOUT)) {
if (TEST_NOTE(NOTE_ENV_RELEASE | NOTE_FADEOUT)) {
is_release = 1;
}
@ -1035,10 +1032,8 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
/* Off-Porta.it */
if (is_toneporta && ev.fxt == FX_OFFSET) {
disabled_toneporta = 1;
is_toneporta = 0;
toneporta_offset = 1;
if (!HAS_QUIRK(QUIRK_PRENV)) {
toneporta_offset = 1;
RESET_NOTE(NOTE_ENV_END);
}
}
@ -1077,9 +1072,11 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
if (set_new_ins) {
SET(NEW_INS);
use_ins_vol = 1;
reset_env = 1;
}
/* Sample default volume is always enabled if a valid sample
* is provided (Atomic Playboy, default_volume.it). */
use_ins_vol = 1;
xc->per_flags = 0;
if (IS_VALID_INSTRUMENT(ins)) {
@ -1119,7 +1116,10 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
}
}
} else {
/* In sample mode invalid ins cut previous ins */
/* In sample mode invalid instruments cut the current
* note (OpenMPT SampleNumberChange.it).
* TODO: portamento_sustain.it order 3 row 19: when
* sample release is set, this isn't always done? */
if (sample_mode) {
xc->volume = 0;
}
@ -1133,13 +1133,14 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
/* Check note */
if (key && !new_invalid_ins) {
if (key) {
SET(NEW_NOTE);
SET_NOTE(NOTE_SET);
if (key == XMP_KEY_FADE) {
SET_NOTE(NOTE_FADEOUT);
reset_env = 0;
reset_susloop = 0;
use_ins_vol = 0;
} else if (key == XMP_KEY_CUT) {
SET_NOTE(NOTE_END | NOTE_CUT | NOTE_KEY_CUT);
@ -1161,24 +1162,27 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
* However, never reset the envelope (see OpenMPT wnoteoff.it).
*/
reset_env = 0;
reset_susloop = 0;
if (!ev.ins) {
use_ins_vol = 0;
}
} else {
} else if (!new_invalid_ins) {
/* Sample sustain release should always carry for tone
* portamento, and is not reset unless a note is
* present (Atomic Playboy, portamento_sustain.it). */
/* portamento_after_keyoff.it test case */
/* also see suburban_streets o13 c45 */
if (ev.ins || !is_toneporta) {
if (!disabled_toneporta) {
reset_env = 1;
}
if (!is_toneporta) {
reset_env = 1;
reset_susloop = 1;
}
if (is_toneporta) {
if (not_same_ins || TEST_NOTE(NOTE_END)) {
SET(NEW_INS);
RESET_NOTE(NOTE_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT);
RESET_NOTE(NOTE_ENV_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT);
} else {
if (is_valid_note(key - 1)) {
if (IS_VALID_NOTE(key - 1)) {
xc->key_porta = key - 1;
}
key = 0;
@ -1187,7 +1191,9 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
}
}
if (is_valid_note(key - 1) && !new_invalid_ins) {
/* TODO: instrument change+porta(+release?) doesn't require a key.
* Order 3/row 11 of portamento_sustain.it should change the sample. */
if (IS_VALID_NOTE(key - 1) && !new_invalid_ins) {
if (TEST_NOTE(NOTE_CUT)) {
use_ins_vol = 1; /* See OpenMPT NoteOffInstr.it */
}
@ -1199,20 +1205,30 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
if (sub != NULL) {
int transp = mod->xxi[candidate_ins].map[key].xpo;
int smp, to;
int dct;
int rvv;
/* Clear note delay before duplicating channels:
* it_note_delay_nna.it */
xc->delay = 0;
note = key + sub->xpo + transp;
smp = sub->sid;
if (smp >= mod->smp || mod->xxs[smp].len == 0) {
if (!IS_VALID_SAMPLE(smp)) {
smp = -1;
}
dct = sub->dct;
if (not_same_smp) {
fix_period(ctx, chn, sub);
libxmp_virt_resetchannel(ctx, chn);
/* Toneporta, even when not executed, disables
* NNA and DCAs for the current note:
* portamento_nna_sample.it, gxsmp2.it */
libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_CUT);
dct = XMP_INST_DCT_OFF;
}
to = libxmp_virt_setpatch(ctx, chn, candidate_ins, smp,
note, sub->nna, sub->dct, sub->dca);
note, key, sub->nna, dct, sub->dca);
/* Random value for volume swing */
rvv = sub->rvv & 0xff;
@ -1268,10 +1284,11 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
* finished (OpenMPT test EnvReset.it). This must take place after
* channel copies in case of NNA (see test/test.it)
* Also if we have envelope in carry mode, check fadeout
* Also, only reset the volume envelope. (it_fade_env_reset_carry.it)
*/
if (ev.ins && TEST_NOTE(NOTE_ENV_END)) {
if (check_fadeout(ctx, xc, candidate_ins)) {
reset_envelopes(ctx, xc);
reset_envelope_volume(ctx, xc);
} else {
reset_env = 0;
}
@ -1279,11 +1296,14 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
if (reset_env) {
if (ev.note) {
RESET_NOTE(NOTE_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT);
RESET_NOTE(NOTE_ENV_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT);
}
/* Set after copying to new virtual channel (see ambio.it) */
xc->fadeout = 0x10000;
}
if (reset_susloop && ev.note) {
RESET_NOTE(NOTE_SAMPLE_RELEASE);
}
/* See OpenMPT wnoteoff.it vs noteoff3.it */
if (retrig_ins && not_same_ins) {
@ -1306,7 +1326,7 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
if (TEST_NOTE(NOTE_CUT)) {
reset_envelopes(ctx, xc);
} else if (!toneporta_offset) {
} else if (!toneporta_offset || HAS_QUIRK(QUIRK_PRENV)) {
reset_envelopes_carry(ctx, xc);
}
RESET_NOTE(NOTE_CUT);
@ -1336,6 +1356,8 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
if (note >= 0) {
xc->note = note;
}
if (note >= 0 || toneporta_offset) {
libxmp_virt_voicepos(ctx, chn, xc->offset.val);
}
@ -1420,7 +1442,7 @@ static int read_event_med(struct context_data *ctx, struct xmp_event *e, int chn
SET_NOTE(NOTE_END);
xc->period = 0;
libxmp_virt_resetchannel(ctx, chn);
} else if (!is_toneporta && IS_VALID_INSTRUMENT(xc->ins) && is_valid_note(e->note - 1)) {
} else if (!is_toneporta && IS_VALID_INSTRUMENT(xc->ins) && IS_VALID_NOTE(e->note - 1)) {
struct xmp_instrument *xxi = &mod->xxi[xc->ins];
xc->key = e->note - 1;
@ -1445,7 +1467,7 @@ static int read_event_med(struct context_data *ctx, struct xmp_event *e, int chn
note = xc->key + sub->xpo + transp;
smp = sub->sid;
if (mod->xxs[smp].len == 0) {
if (!IS_VALID_SAMPLE(smp)) {
smp = -1;
}
@ -1510,7 +1532,7 @@ static int read_event_smix(struct context_data *ctx, struct xmp_event *e, int ch
struct xmp_module *mod = &m->mod;
struct channel_data *xc = &p->xc_data[chn];
struct xmp_subinstrument *sub;
int is_smix_ins;
struct xmp_instrument *xxi;
int ins, note, transp, smp;
xc->flags = 0;
@ -1518,33 +1540,39 @@ static int read_event_smix(struct context_data *ctx, struct xmp_event *e, int ch
if (!e->ins)
return 0;
is_smix_ins = 0;
ins = e->ins - 1;
SET(NEW_INS);
xc->fadeout = 0x10000;
xc->per_flags = 0;
xc->offset.val = 0;
RESET_NOTE(NOTE_RELEASE);
RESET_NOTE(NOTE_RELEASE|NOTE_FADEOUT);
xc->ins = ins;
if (ins >= mod->ins && ins < mod->ins + smix->ins) {
is_smix_ins = 1;
xc->ins_fade = smix->xxi[xc->ins - mod->ins].rls;
xxi = libxmp_get_instrument(ctx, ins);
if (xxi != NULL) {
xc->ins_fade = xxi->rls;
}
xc->ins = ins;
SET(NEW_NOTE);
if (e->note == XMP_KEY_OFF) {
SET_NOTE(NOTE_RELEASE);
return 0;
} else if (e->note == XMP_KEY_FADE) {
SET_NOTE(NOTE_FADEOUT);
return 0;
} else if (e->note == XMP_KEY_CUT) {
SET_NOTE(NOTE_END);
xc->period = 0;
libxmp_virt_resetchannel(ctx, chn);
return 0;
}
xc->key = e->note - 1;
xc->fadeout = 0x10000;
RESET_NOTE(NOTE_END);
if (is_smix_ins) {
sub = &smix->xxi[xc->ins - mod->ins].sub[0];
if (ins >= mod->ins && ins < mod->ins + smix->ins) {
sub = &xxi->sub[0];
if (sub == NULL) {
return 0;
}
@ -1559,15 +1587,15 @@ static int read_event_smix(struct context_data *ctx, struct xmp_event *e, int ch
xc->smp = smp;
}
} else {
sub = is_valid_note(xc->key) ?
sub = IS_VALID_NOTE(xc->key) ?
get_subinstrument(ctx, xc->ins, xc->key) : NULL;
if (sub == NULL) {
return 0;
}
transp = mod->xxi[xc->ins].map[xc->key].xpo;
transp = xxi->map[xc->key].xpo;
note = xc->key + sub->xpo + transp;
smp = sub->sid;
if (mod->xxs[smp].len == 0)
if (!IS_VALID_SAMPLE(smp))
smp = -1;
if (smp >= 0 && smp < mod->smp) {
set_patch(ctx, chn, xc->ins, smp, note);

View file

@ -20,6 +20,9 @@
* THE SOFTWARE.
*/
#ifndef LIBXMP_LOADERS_S3M_H
#define LIBXMP_LOADERS_S3M_H
/* S3M packed pattern macros */
#define S3M_EOR 0 /* End of row */
#define S3M_CH_MASK 0x1f /* Channel */
@ -27,10 +30,16 @@
#define S3M_VOL_FOLLOWS 0x40 /* Volume follows */
#define S3M_FX_FOLLOWS 0x80 /* Effect and parameter follow */
/* S3M mix volume macros */
#define S3M_MV_VOLUME 0x7f /* Module mix volume, typically 16 to 127 */
#define S3M_MV_STEREO 0x80 /* Module is stereo if set, otherwise mono */
/* S3M channel info macros */
#define S3M_CH_ON 0x80 /* Psi says it's bit 8, I'll assume bit 7 */
#define S3M_CH_OFF 0xff
#define S3M_CH_PAN 0x7f /* Left/Right */
#define S3M_CH_NUMBER 0x1f
#define S3M_CH_RIGHT 0x08
#define S3M_CH_ADLIB 0x10
/* S3M channel pan macros */
#define S3M_PAN_SET 0x20
@ -79,7 +88,8 @@ struct s3m_file_header {
};
struct s3m_instrument_header {
uint8 dosname[13]; /* DOS file name */
uint8 dosname[12]; /* DOS file name */
uint8 memseg_hi; /* High byte of sample pointer */
uint16 memseg; /* Pointer to sample data */
uint32 length; /* Length */
uint32 loopbeg; /* Loop begin */
@ -113,4 +123,4 @@ struct s3m_adlib_header {
uint32 magic; /* 'SCRI' */
};
#endif
#endif /* LIBXMP_LOADERS_S3M_H */

View file

@ -1,5 +1,5 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
* Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -219,6 +219,7 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
uint8 n, b;
uint16 *pp_ins; /* Parapointers to instruments */
uint16 *pp_pat; /* Parapointers to patterns */
int stereo;
int ret;
uint8 buf[96]
@ -262,12 +263,12 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
libxmp_copy_adjust(mod->name, sfh.name, 28);
pp_ins = calloc(2, sfh.insnum);
pp_ins = (uint16 *) calloc(sfh.insnum, sizeof(uint16));
if (pp_ins == NULL) {
goto err;
}
pp_pat = calloc(2, sfh.patnum);
pp_pat = (uint16 *) calloc(sfh.patnum, sizeof(uint16));
if (pp_pat == NULL) {
goto err2;
}
@ -283,15 +284,49 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
mod->bpm = sfh.it;
mod->chn = 0;
/* Mix volume and stereo flag conversion (reported by Saga Musix).
* 1) Old format uses mix volume 0-7, and the stereo flag is 0x10.
* 2) Newer ST3s unconditionally convert MV 0x02 and 0x12 to 0x20.
*/
m->mvolbase = 48;
if (sfh.ffi == 1) {
m->mvol = ((sfh.mv & 0xf) + 1) * 0x10;
stereo = sfh.mv & 0x10;
CLAMP(m->mvol, 0x10, 0x7f);
} else if (sfh.mv == 0x02 || sfh.mv == 0x12) {
m->mvol = 0x20;
stereo = sfh.mv & 0x10;
} else {
m->mvol = sfh.mv & S3M_MV_VOLUME;
stereo = sfh.mv & S3M_MV_STEREO;
if (m->mvol == 0) {
m->mvol = 48; /* Default is 48 */
} else if (m->mvol < 16) {
m->mvol = 16; /* Minimum is 16 */
}
}
/* "Note that in stereo, the mastermul is internally multiplied by
* 11/8 inside the player since there is generally more room in the
* output stream." Do the inverse to affect fewer modules. */
if (!stereo) {
m->mvol = m->mvol * 8 / 11;
}
for (i = 0; i < 32; i++) {
int x;
if (sfh.chset[i] == S3M_CH_OFF)
continue;
mod->chn = i + 1;
if (sfh.mv & 0x80) { /* stereo */
int x = sfh.chset[i] & S3M_CH_PAN;
mod->xxc[i].pan = (x & 0x0f) < 8 ? 0x30 : 0xc0;
x = sfh.chset[i] & S3M_CH_NUMBER;
if (stereo && x < S3M_CH_ADLIB) {
mod->xxc[i].pan = x < S3M_CH_RIGHT ? 0x30 : 0xc0;
} else {
mod->xxc[i].pan = 0x80;
}
@ -346,9 +381,6 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
uint8 x = hio_read8(f);
if (x & S3M_PAN_SET) {
mod->xxc[i].pan = (x << 4) & 0xff;
} else {
mod->xxc[i].pan =
sfh.mv % 0x80 ? 0x30 + 0xa0 * (i & 1) : 0x80;
}
}
@ -381,8 +413,15 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
}
break;
case 5:
snprintf(tracker_name, 40, "OpenMPT %d.%02x",
(sfh.version & 0x0f00) >> 8, sfh.version & 0xff);
if (sfh.version == 0x5447) {
strcpy(tracker_name, "Graoumf Tracker");
} else if (sfh.rsvd2[0] || sfh.rsvd2[1]) {
snprintf(tracker_name, 40, "OpenMPT %d.%02x.%02x.%02x",
(sfh.version & 0x0f00) >> 8, sfh.version & 0xff, sfh.rsvd2[1], sfh.rsvd2[0]);
} else {
snprintf(tracker_name, 40, "OpenMPT %d.%02x",
(sfh.version & 0x0f00) >> 8, sfh.version & 0xff);
}
m->quirk |= QUIRK_ST3BUGS;
break;
case 4:
@ -472,7 +511,7 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
}
}
D_(D_INFO "Stereo enabled: %s", sfh.mv & 0x80 ? "yes" : "no");
D_(D_INFO "Stereo enabled: %s", stereo ? "yes" : "no");
D_(D_INFO "Pan settings: %s", sfh.dp ? "no" : "yes");
if (libxmp_init_instrument(m) < 0)
@ -487,8 +526,9 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
struct xmp_sample *xxs = &mod->xxs[i];
struct xmp_subinstrument *sub;
int load_sample_flags;
uint32 sample_segment;
xxi->sub = calloc(sizeof(struct xmp_subinstrument), 1);
xxi->sub = (struct xmp_subinstrument *) calloc(1, sizeof(struct xmp_subinstrument));
if (xxi->sub == NULL) {
goto err3;
}
@ -527,9 +567,7 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
sub->vol = sah.vol;
libxmp_c2spd_to_note(sah.c2spd, &sub->xpo, &sub->fin);
sub->xpo += 12;
ret =
libxmp_load_sample(m, f, SAMPLE_FLAG_ADLIB, xxs,
(char *)sah.reg);
ret = libxmp_load_sample(m, f, SAMPLE_FLAG_ADLIB, xxs, (char *)sah.reg);
if (ret < 0)
goto err3;
@ -541,7 +579,8 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
#endif
}
memcpy(sih.dosname, buf + 1, 13); /* DOS file name */
memcpy(sih.dosname, buf + 1, 12); /* DOS file name */
sih.memseg_hi = buf[13]; /* High byte of sample pointer */
sih.memseg = readmem16l(buf + 14); /* Pointer to sample data */
sih.length = readmem32l(buf + 16); /* Length */
@ -598,7 +637,9 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
libxmp_c2spd_to_note(sih.c2spd, &sub->xpo, &sub->fin);
if (hio_seek(f, start + 16L * sih.memseg, SEEK_SET) < 0) {
sample_segment = sih.memseg + ((uint32)sih.memseg_hi << 16);
if (hio_seek(f, start + 16L * sample_segment, SEEK_SET) < 0) {
goto err3;
}

View file

@ -1,5 +1,5 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
* Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -24,7 +24,6 @@
#include "loader.h"
#ifndef LIBXMP_CORE_PLAYER
/*
* From the Audio File Formats (version 2.5)
* Submitted-by: Guido van Rossum <guido@cwi.nl>
@ -55,7 +54,6 @@ static const int8 vdic_table[128] = {
/* 120 */ 95, 98, 103, 109, 114, 120, 126, 127
};
/* Convert 7 bit samples to 8 bit */
static void convert_7bit_to_8bit(uint8 *p, int l)
{
@ -95,24 +93,23 @@ static void adpcm4_decoder(uint8 *inp, uint8 *outp, char *tab, int len)
*outp++ = delta;
}
}
#endif
/* Convert differential to absolute sample data */
static void convert_delta(uint8 *p, int l, int r)
{
uint16 *w = (uint16 *)p;
uint16 abs = 0;
uint16 absval = 0;
if (r) {
for (; l--;) {
abs = *w + abs;
*w++ = abs;
absval = *w + absval;
*w++ = absval;
}
} else {
for (; l--;) {
abs = *p + abs;
*p++ = (uint8) abs;
absval = *p + absval;
*p++ = (uint8) absval;
}
}
}
@ -163,41 +160,10 @@ static void convert_stereo_to_mono(uint8 *p, int l, int r)
}
#endif
static void unroll_loop(struct xmp_sample *xxs)
{
int8 *s8;
int16 *s16;
int start, loop_size;
int i;
s16 = (int16 *)xxs->data;
s8 = (int8 *)xxs->data;
if (xxs->len > xxs->lpe) {
start = xxs->lpe;
} else {
start = xxs->len;
}
loop_size = xxs->lpe - xxs->lps;
if (xxs->flg & XMP_SAMPLE_16BIT) {
s16 += start;
for (i = 0; i < loop_size; i++) {
*(s16 + i) = *(s16 - i - 1);
}
} else {
s8 += start;
for (i = 0; i < loop_size; i++) {
*(s8 + i) = *(s8 - i - 1);
}
}
}
int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct xmp_sample *xxs, const void *buffer)
{
int bytelen, extralen, unroll_extralen, i;
int bytelen, extralen, i;
#ifndef LIBXMP_CORE_PLAYER
/* Adlib FM patches */
@ -264,7 +230,6 @@ int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct x
*/
bytelen = xxs->len;
extralen = 4;
unroll_extralen = 0;
/* Disable birectional loop flag if sample is not looped
*/
@ -272,25 +237,18 @@ int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct x
if (~xxs->flg & XMP_SAMPLE_LOOP)
xxs->flg &= ~XMP_SAMPLE_LOOP_BIDIR;
}
/* Unroll bidirectional loops
*/
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
unroll_extralen = (xxs->lpe - xxs->lps) -
(xxs->len - xxs->lpe);
if (unroll_extralen < 0) {
unroll_extralen = 0;
}
if (xxs->flg & XMP_SAMPLE_SLOOP_BIDIR) {
if (~xxs->flg & XMP_SAMPLE_SLOOP)
xxs->flg &= ~XMP_SAMPLE_SLOOP_BIDIR;
}
if (xxs->flg & XMP_SAMPLE_16BIT) {
bytelen *= 2;
extralen *= 2;
unroll_extralen *= 2;
}
/* add guard bytes before the buffer for higher order interpolation */
xxs->data = malloc(bytelen + extralen + unroll_extralen + 4);
xxs->data = (unsigned char *) malloc(bytelen + extralen + 4);
if (xxs->data == NULL) {
goto err;
}
@ -379,12 +337,6 @@ int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct x
xxs->flg |= XMP_SAMPLE_LOOP_FULL;
}
/* Unroll bidirectional loops */
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
unroll_loop(xxs);
bytelen += unroll_extralen;
}
/* Add extra samples at end */
if (xxs->flg & XMP_SAMPLE_16BIT) {
for (i = 0; i < 8; i++) {
@ -404,28 +356,6 @@ int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct x
xxs->data[-1] = xxs->data[0];
}
/* Fix sample at loop */
if (xxs->flg & XMP_SAMPLE_LOOP) {
int lpe = xxs->lpe;
int lps = xxs->lps;
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
lpe += lpe - lps;
}
if (xxs->flg & XMP_SAMPLE_16BIT) {
lpe <<= 1;
lps <<= 1;
for (i = 0; i < 8; i++) {
xxs->data[lpe + i] = xxs->data[lps + i];
}
} else {
for (i = 0; i < 4; i++) {
xxs->data[lpe + i] = xxs->data[lps + i];
}
}
}
return 0;
#ifndef LIBXMP_CORE_PLAYER
@ -438,8 +368,8 @@ int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct x
void libxmp_free_sample(struct xmp_sample *s)
{
if (s->data) {
free(s->data - 4);
s->data = NULL; /* prevent double free in PCM load error */
}
if (s->data) {
free(s->data - 4);
s->data = NULL; /* prevent double free in PCM load error */
}
}

View file

@ -1,5 +1,5 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
* Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -42,6 +42,12 @@
#include "effects.h"
#include "mixer.h"
#ifndef LIBXMP_CORE_PLAYER
#include "far_extras.h"
#endif
#define VBLANK_TIME_THRESHOLD 480000 /* 8 minutes */
#define S3M_END 0xff
#define S3M_SKIP 0xfe
@ -50,30 +56,35 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
const struct xmp_module *mod = &m->mod;
const struct xmp_track *tracks[XMP_MAX_CHANNELS];
const struct xmp_event *event;
int parm, gvol_memory, f1, f2, p1, p2, ord, ord2;
int row, last_row, break_row, row_count, row_count_total;
int orders_since_last_valid, any_valid;
int gvl, bpm, speed, base_time, chn;
int frame_count;
double time, start_time;
int loop_chn, loop_num, inside_loop;
int loop_chn, loop_num, inside_loop, line_jump;
int pdelay = 0;
int loop_count[XMP_MAX_CHANNELS];
int loop_row[XMP_MAX_CHANNELS];
struct xmp_event* event;
int i, pat;
int has_marker;
struct ord_data *info;
#ifndef LIBXMP_CORE_PLAYER
int st26_speed;
int far_tempo_coarse, far_tempo_fine, far_tempo_mode;
#endif
/* was 255, but Global trash goes to 318.
* Higher limit for MEDs, defiance.crybaby.5 has blocks with 2048+ rows. */
const int row_limit = IS_PLAYER_MODE_MED() ? 3200 : 512;
if (mod->len == 0)
return 0;
for (i = 0; i < mod->len; i++) {
int pat = mod->xxo[i];
pat = mod->xxo[i];
memset(m->scan_cnt[i], 0, pat >= mod->pat ? 1 :
mod->xxp[pat]->rows ? mod->xxp[pat]->rows : 1);
}
@ -84,6 +95,7 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
}
loop_num = 0;
loop_chn = -1;
line_jump = 0;
gvl = mod->gvl;
bpm = mod->bpm;
@ -92,6 +104,15 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
base_time = m->rrate;
#ifndef LIBXMP_CORE_PLAYER
st26_speed = 0;
far_tempo_coarse = 4;
far_tempo_fine = 0;
far_tempo_mode = 1;
if (HAS_FAR_MODULE_EXTRAS(ctx->m)) {
far_tempo_coarse = FAR_MODULE_EXTRAS(ctx->m)->coarse_tempo;
libxmp_far_translate_tempo(far_tempo_mode, 0, far_tempo_coarse,
&far_tempo_fine, &speed, &bpm);
}
#endif
has_marker = HAS_QUIRK(QUIRK_MARKER);
@ -196,6 +217,11 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
info->start_row = break_row;
}
/* Get tracks in advance to speed up the event parsing loop. */
for (chn = 0; chn < mod->chn; chn++) {
tracks[chn] = mod->xxt[TRACK_NUM(pat, chn)];
}
last_row = mod->xxp[pat]->rows;
for (row = break_row, break_row = 0; row < last_row; row++, row_count++, row_count_total++) {
/* Prevent crashes caused by large softmixer frames */
@ -216,12 +242,12 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
* (...) it dies at the end of position 2F
*/
if (row_count_total > 512) { /* was 255, but Global trash goes to 318. */
if (row_count_total > row_limit) {
D_(D_CRIT "row_count_total = %d @ ord %d, pat %d, row %d; ending scan", row_count_total, ord, pat, row);
goto end_module;
}
if (!loop_num && m->scan_cnt[ord][row]) {
if (!loop_num && !line_jump && m->scan_cnt[ord][row]) {
row_count--;
goto end_module;
}
@ -237,12 +263,14 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
}
pdelay = 0;
line_jump = 0;
for (chn = 0; chn < mod->chn; chn++) {
if (row >= mod->xxt[mod->xxp[pat]->index[chn]]->rows)
if (row >= tracks[chn]->rows)
continue;
event = &EVENT(mod->xxo[ord], chn, row);
//event = &EVENT(mod->xxo[ord], chn, row);
event = &tracks[chn]->event[row];
f1 = event->fxt;
p1 = event->fxp;
@ -290,23 +318,23 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
}
}
if ((f1 == FX_SPEED && p1) || (f2 == FX_SPEED && p2)) {
parm = (f1 == FX_SPEED) ? p1 : p2;
/* Some formats can have two FX_SPEED effects, and both need
* to be checked. Slot 2 is currently handled first. */
for (i = 0; i < 2; i++) {
parm = i ? p1 : p2;
if ((i ? f1 : f2) != FX_SPEED || parm == 0)
continue;
frame_count += row_count * speed;
row_count = 0;
if (parm) {
if (HAS_QUIRK(QUIRK_NOBPM) || p->flags & XMP_FLAGS_VBLANK || parm < 0x20) {
if (parm > 0) {
speed = parm;
if (HAS_QUIRK(QUIRK_NOBPM) || p->flags & XMP_FLAGS_VBLANK || parm < 0x20) {
speed = parm;
#ifndef LIBXMP_CORE_PLAYER
st26_speed = 0;
st26_speed = 0;
#endif
}
} else {
time += m->time_factor * frame_count * base_time / bpm;
frame_count = 0;
bpm = parm;
}
} else {
time += m->time_factor * frame_count * base_time / bpm;
frame_count = 0;
bpm = parm;
}
}
@ -327,6 +355,39 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
st26_speed = MSN(p1);
}
}
/* FAR tempo processing */
if (f1 == FX_FAR_TEMPO || f1 == FX_FAR_F_TEMPO) {
int far_speed, far_bpm, fine_change = 0;
if (f1 == FX_FAR_TEMPO) {
if (MSN(p1)) {
far_tempo_mode = MSN(p1) - 1;
} else {
far_tempo_coarse = LSN(p1);
}
}
if (f1 == FX_FAR_F_TEMPO) {
if (MSN(p1)) {
far_tempo_fine += MSN(p1);
fine_change = MSN(p1);
} else if (LSN(p1)) {
far_tempo_fine -= LSN(p1);
fine_change = -LSN(p1);
} else {
far_tempo_fine = 0;
}
}
if (libxmp_far_translate_tempo(far_tempo_mode, fine_change,
far_tempo_coarse, &far_tempo_fine, &far_speed, &far_bpm) == 0) {
frame_count += row_count * speed;
row_count = 0;
time += m->time_factor * frame_count * base_time / bpm;
frame_count = 0;
speed = far_speed;
bpm = far_bpm;
}
}
#endif
if ((f1 == FX_S3M_SPEED && p1) || (f2 == FX_S3M_SPEED && p2)) {
@ -343,7 +404,7 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
if ((f1 == FX_S3M_BPM && p1) || (f2 == FX_S3M_BPM && p2)) {
parm = (f1 == FX_S3M_BPM) ? p1 : p2;
if (parm >= 0x20) {
if (parm >= XMP_MIN_BPM) {
frame_count += row_count * speed;
row_count = 0;
time += m->time_factor * frame_count * base_time / bpm;
@ -417,6 +478,19 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
last_row = 0;
}
#ifndef LIBXMP_CORE_PLAYER
/* Archimedes line jump */
if (f1 == FX_LINE_JUMP || f2 == FX_LINE_JUMP) {
/* Don't set order if preceded by jump or break. */
if (last_row > 0)
ord2 = ord;
parm = (f1 == FX_LINE_JUMP) ? p1 : p2;
break_row = parm;
last_row = 0;
line_jump = 1;
}
#endif
if (f1 == FX_EXTENDED || f2 == FX_EXTENDED) {
parm = (f1 == FX_EXTENDED) ? p1 : p2;
@ -514,6 +588,56 @@ end_module:
return (time + m->time_factor * frame_count * base_time / bpm);
}
static void reset_scan_data(struct context_data *ctx)
{
int i;
for (i = 0; i < XMP_MAX_MOD_LENGTH; i++) {
ctx->m.xxo_info[i].time = -1;
}
memset(ctx->p.sequence_control, 0xff, XMP_MAX_MOD_LENGTH);
}
#ifndef LIBXMP_CORE_PLAYER
static void compare_vblank_scan(struct context_data *ctx)
{
/* Calculate both CIA and VBlank time for certain long MODs
* and pick the more likely (i.e. shorter) one. The same logic
* works regardless of the initial mode selected--either way,
* the wrong timing mode usually makes modules MUCH longer. */
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct ord_data *info_backup;
struct scan_data scan_backup;
unsigned char ctrl_backup[256];
if ((info_backup = (struct ord_data *)malloc(sizeof(m->xxo_info))) != NULL) {
/* Back up the current info to avoid a third scan. */
scan_backup = p->scan[0];
memcpy(info_backup, m->xxo_info, sizeof(m->xxo_info));
memcpy(ctrl_backup, p->sequence_control,
sizeof(p->sequence_control));
reset_scan_data(ctx);
m->quirk ^= QUIRK_NOBPM;
p->scan[0].time = scan_module(ctx, 0, 0);
D_(D_INFO "%-6s %dms", !HAS_QUIRK(QUIRK_NOBPM)?"VBlank":"CIA", scan_backup.time);
D_(D_INFO "%-6s %dms", HAS_QUIRK(QUIRK_NOBPM)?"VBlank":"CIA", p->scan[0].time);
if (p->scan[0].time >= scan_backup.time) {
m->quirk ^= QUIRK_NOBPM;
p->scan[0] = scan_backup;
memcpy(m->xxo_info, info_backup, sizeof(m->xxo_info));
memcpy(p->sequence_control, ctrl_backup,
sizeof(p->sequence_control));
}
free(info_backup);
}
}
#endif
int libxmp_get_sequence(struct context_data *ctx, int ord)
{
struct player_data *p = &ctx->p;
@ -525,23 +649,35 @@ int libxmp_scan_sequences(struct context_data *ctx)
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
struct scan_data *s;
int i, ep;
int seq;
unsigned char temp_ep[XMP_MAX_MOD_LENGTH];
s = (struct scan_data *) realloc(p->scan, MAX(1, mod->len) * sizeof(struct scan_data));
if (!s) {
D_(D_CRIT "failed to allocate scan data");
return -1;
}
p->scan = s;
/* Initialize order data to prevent overwrite when a position is used
* multiple times at different starting points (see janosik.xm).
*/
for (i = 0; i < XMP_MAX_MOD_LENGTH; i++) {
m->xxo_info[i].time = -1;
}
reset_scan_data(ctx);
ep = 0;
memset(p->sequence_control, 0xff, XMP_MAX_MOD_LENGTH);
temp_ep[0] = 0;
p->scan[0].time = scan_module(ctx, ep, 0);
seq = 1;
#ifndef LIBXMP_CORE_PLAYER
if (m->compare_vblank && !(p->flags & XMP_FLAGS_VBLANK) &&
p->scan[0].time >= VBLANK_TIME_THRESHOLD) {
compare_vblank_scan(ctx);
}
#endif
if (p->scan[0].time < 0) {
D_(D_CRIT "scan was not able to find any valid orders");
return -1;
@ -567,6 +703,12 @@ int libxmp_scan_sequences(struct context_data *ctx)
}
}
if (seq < mod->len) {
s = (struct scan_data *) realloc(p->scan, seq * sizeof(struct scan_data));
if (s != NULL) {
p->scan = s;
}
}
m->num_sequences = seq;
/* Now place entry points in the public accessible array */
@ -575,6 +717,5 @@ int libxmp_scan_sequences(struct context_data *ctx)
m->seq_data[i].duration = p->scan[i].time;
}
return 0;
}

View file

@ -1,5 +1,5 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
* Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -34,7 +34,9 @@ struct xmp_instrument *libxmp_get_instrument(struct context_data *ctx, int ins)
struct xmp_module *mod = &m->mod;
struct xmp_instrument *xxi;
if (ins < mod->ins) {
if (ins < 0) {
xxi = NULL;
} else if (ins < mod->ins) {
xxi = &mod->xxi[ins];
} else if (ins < mod->ins + smix->ins) {
xxi = &smix->xxi[ins - mod->ins];
@ -52,7 +54,9 @@ struct xmp_sample *libxmp_get_sample(struct context_data *ctx, int smp)
struct xmp_module *mod = &m->mod;
struct xmp_sample *xxs;
if (smp < mod->smp) {
if (smp < 0) {
xxs = NULL;
} else if (smp < mod->smp) {
xxs = &mod->xxs[smp];
} else if (smp < mod->smp + smix->smp) {
xxs = &smix->xxs[smp - mod->smp];
@ -72,11 +76,11 @@ int xmp_start_smix(xmp_context opaque, int chn, int smp)
return -XMP_ERROR_STATE;
}
smix->xxi = calloc(sizeof (struct xmp_instrument), smp);
smix->xxi = (struct xmp_instrument *) calloc(smp, sizeof(struct xmp_instrument));
if (smix->xxi == NULL) {
goto err;
}
smix->xxs = calloc(sizeof (struct xmp_sample), smp);
smix->xxs = (struct xmp_sample *) calloc(smp, sizeof(struct xmp_sample));
if (smix->xxs == NULL) {
goto err1;
}
@ -106,7 +110,7 @@ int xmp_smix_play_instrument(xmp_context opaque, int ins, int note, int vol, int
return -XMP_ERROR_STATE;
}
if (chn >= smix->chn || ins >= mod->ins) {
if (chn >= smix->chn || chn < 0 || ins >= mod->ins || ins < 0) {
return -XMP_ERROR_INVALID;
}
@ -116,7 +120,7 @@ int xmp_smix_play_instrument(xmp_context opaque, int ins, int note, int vol, int
event = &p->inject_event[mod->chn + chn];
memset(event, 0, sizeof (struct xmp_event));
event->note = note + 1;
event->note = (note < XMP_MAX_KEYS) ? note + 1 : note;
event->ins = ins + 1;
event->vol = vol + 1;
event->_flag = 1;
@ -137,7 +141,7 @@ int xmp_smix_play_sample(xmp_context opaque, int ins, int note, int vol, int chn
return -XMP_ERROR_STATE;
}
if (chn >= smix->chn || ins >= smix->ins) {
if (chn >= smix->chn || chn < 0 || ins >= smix->ins || ins < 0) {
return -XMP_ERROR_INVALID;
}
@ -147,7 +151,7 @@ int xmp_smix_play_sample(xmp_context opaque, int ins, int note, int vol, int chn
event = &p->inject_event[mod->chn + chn];
memset(event, 0, sizeof (struct xmp_event));
event->note = note + 1;
event->note = (note < XMP_MAX_KEYS) ? note + 1 : note;
event->ins = mod->ins + ins + 1;
event->vol = vol + 1;
event->_flag = 1;
@ -198,10 +202,10 @@ int xmp_smix_load_sample(xmp_context opaque, int num, const char *path)
retval = -XMP_ERROR_SYSTEM;
goto err;
}
/* Init instrument */
xxi->sub = calloc(sizeof(struct xmp_subinstrument), 1);
xxi->sub = (struct xmp_subinstrument *) calloc(1, sizeof(struct xmp_subinstrument));
if (xxi->sub == NULL) {
retval = -XMP_ERROR_SYSTEM;
goto err1;
@ -264,7 +268,7 @@ int xmp_smix_load_sample(xmp_context opaque, int num, const char *path)
xxs->lpe = 0;
xxs->flg = bits == 16 ? XMP_SAMPLE_16BIT : 0;
xxs->data = malloc(size + 8);
xxs->data = (unsigned char *) malloc(size + 8);
if (xxs->data == NULL) {
retval = -XMP_ERROR_SYSTEM;
goto err2;
@ -286,7 +290,7 @@ int xmp_smix_load_sample(xmp_context opaque, int num, const char *path)
hio_close(h);
return 0;
err2:
free(xxi->sub);
xxi->sub = NULL;

View file

@ -1,5 +1,5 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
* Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -20,7 +20,6 @@
* THE SOFTWARE.
*/
#include <limits.h>
#include "common.h"
#include "virtual.h"
#include "mixer.h"
@ -102,8 +101,8 @@ int libxmp_virt_on(struct context_data *ctx, int num)
p->virt.maxvoc = libxmp_mixer_numvoices(ctx, num);
p->virt.voice_array = calloc(p->virt.maxvoc,
sizeof(struct mixer_voice));
p->virt.voice_array = (struct mixer_voice *) calloc(p->virt.maxvoc,
sizeof(struct mixer_voice));
if (p->virt.voice_array == NULL)
goto err;
@ -116,7 +115,7 @@ int libxmp_virt_on(struct context_data *ctx, int num)
/* Initialize Paula simulator */
if (IS_AMIGA_MOD()) {
for (i = 0; i < p->virt.maxvoc; i++) {
p->virt.voice_array[i].paula = calloc(1, sizeof (struct paula_state));
p->virt.voice_array[i].paula = (struct paula_state *) calloc(1, sizeof(struct paula_state));
if (p->virt.voice_array[i].paula == NULL) {
goto err2;
}
@ -125,8 +124,8 @@ int libxmp_virt_on(struct context_data *ctx, int num)
}
#endif
p->virt.virt_channel = malloc(p->virt.virt_channels *
sizeof(struct virt_channel));
p->virt.virt_channel = (struct virt_channel *) malloc(p->virt.virt_channels *
sizeof(struct virt_channel));
if (p->virt.virt_channel == NULL)
goto err2;
@ -190,7 +189,7 @@ void libxmp_virt_reset(struct context_data *ctx)
}
/* CID 129203 (#1 of 1): Useless call (USELESS_CALL)
* Call is only useful for its return value, which is ignored.
* Call is only useful for its return value, which is ignored.
*
* libxmp_mixer_numvoices(ctx, p->virt.maxvoc);
*/
@ -353,6 +352,18 @@ void libxmp_virt_release(struct context_data *ctx, int chn, int rel)
libxmp_mixer_release(ctx, voc, rel);
}
void libxmp_virt_reverse(struct context_data *ctx, int chn, int rev)
{
struct player_data *p = &ctx->p;
int voc;
if ((voc = map_virt_channel(p, chn)) < 0) {
return;
}
libxmp_mixer_reverse(ctx, voc, rev);
}
void libxmp_virt_setpan(struct context_data *ctx, int chn, int pan)
{
struct player_data *p = &ctx->p;
@ -419,8 +430,13 @@ void libxmp_virt_setsmp(struct context_data *ctx, int chn, int smp)
void libxmp_virt_setnna(struct context_data *ctx, int chn, int nna)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
int voc;
if (!HAS_QUIRK(QUIRK_VIRTUAL)) {
return;
}
if ((voc = map_virt_channel(p, chn)) < 0) {
return;
}
@ -429,7 +445,7 @@ void libxmp_virt_setnna(struct context_data *ctx, int chn, int nna)
}
static void check_dct(struct context_data *ctx, int i, int chn, int ins,
int smp, int note, int nna, int dct, int dca)
int smp, int key, int nna, int dct, int dca)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi = &p->virt.voice_array[i];
@ -440,15 +456,15 @@ static void check_dct(struct context_data *ctx, int i, int chn, int ins,
if (vi->root == chn && vi->ins == ins) {
if (nna == XMP_INST_NNA_CUT) {
libxmp_virt_resetvoice(ctx, i, 1);
return;
libxmp_virt_resetvoice(ctx, i, 1);
return;
}
vi->act = nna;
if ((dct == XMP_INST_DCT_INST) ||
(dct == XMP_INST_DCT_SMP && vi->smp == smp) ||
(dct == XMP_INST_DCT_NOTE && vi->note == note)) {
(dct == XMP_INST_DCT_NOTE && vi->key == key)) {
if (nna == XMP_INST_NNA_OFF && dca == XMP_INST_DCA_FADE) {
vi->act = VIRT_ACTION_OFF;
@ -479,7 +495,7 @@ void libxmp_virt_setnote(struct context_data *ctx, int chn, int note)
}
int libxmp_virt_setpatch(struct context_data *ctx, int chn, int ins, int smp,
int note, int nna, int dct, int dca)
int note, int key, int nna, int dct, int dca)
{
struct player_data *p = &ctx->p;
int voc, vfree;
@ -497,7 +513,7 @@ int libxmp_virt_setpatch(struct context_data *ctx, int chn, int ins, int smp,
int i;
for (i = 0; i < p->virt.maxvoc; i++) {
check_dct(ctx, i, chn, ins, smp, note, nna, dct, dca);
check_dct(ctx, i, chn, ins, smp, key, nna, dct, dca);
}
}
#endif
@ -512,7 +528,7 @@ int libxmp_virt_setpatch(struct context_data *ctx, int chn, int ins, int smp,
return -1;
}
for (chn = p->virt.num_tracks;
for (chn = p->virt.num_tracks; chn < p->virt.virt_channels &&
p->virt.virt_channel[chn++].map > FREE;) ;
p->virt.voice_array[voc].chn = --chn;
@ -535,6 +551,7 @@ int libxmp_virt_setpatch(struct context_data *ctx, int chn, int ins, int smp,
libxmp_mixer_setnote(ctx, voc, note);
p->virt.voice_array[voc].ins = ins;
p->virt.voice_array[voc].act = nna;
p->virt.voice_array[voc].key = key;
return chn;
}

View file

@ -15,7 +15,7 @@ int libxmp_virt_on (struct context_data *, int);
void libxmp_virt_off (struct context_data *);
int libxmp_virt_mute (struct context_data *, int, int);
int libxmp_virt_setpatch (struct context_data *, int, int, int, int,
int, int, int);
int, int, int, int);
int libxmp_virt_cvt8bit (void);
void libxmp_virt_setnote (struct context_data *, int, int);
void libxmp_virt_setsmp (struct context_data *, int, int);
@ -33,6 +33,7 @@ void libxmp_virt_resetchannel(struct context_data *, int);
void libxmp_virt_resetvoice (struct context_data *, int, int);
void libxmp_virt_reset (struct context_data *);
void libxmp_virt_release (struct context_data *, int, int);
void libxmp_virt_reverse (struct context_data *, int, int);
int libxmp_virt_getroot (struct context_data *, int);
#endif /* LIBXMP_VIRTUAL_H */

View file

@ -31,3 +31,17 @@ int libxmp_snprintf (char *str, size_t sz, const char *fmt, ...)
}
#endif
/* Win32 debug message helper by Mirko Buffoni */
#if defined(_MSC_VER) && defined(DEBUG)
void libxmp_msvc_dbgprint(const char *format, ...)
{
va_list argptr;
/* do the output */
va_start(argptr, format);
vprintf(format, argptr);
printf("\n");
va_end(argptr);
}
#endif

View file

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

View file

@ -1,5 +1,5 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
* Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -35,6 +35,9 @@
#include "loader.h"
#include "xm.h"
#ifndef LIBXMP_CORE_PLAYER
#include "vorbis.h"
#endif
static int xm_test(HIO_HANDLE *, char *, const int);
static int xm_load(struct module_data *, HIO_HANDLE *, const int);
@ -60,15 +63,16 @@ static int xm_test(HIO_HANDLE *f, char *t, const int start)
return 0;
}
static int load_xm_pattern(struct module_data *m, int num, int version, HIO_HANDLE *f)
static int load_xm_pattern(struct module_data *m, int num, int version,
uint8 *patbuf, HIO_HANDLE *f)
{
const int headsize = version > 0x0102 ? 9 : 8;
struct xmp_module *mod = &m->mod;
struct xm_pattern_header xph;
struct xmp_event *event;
uint8 *patbuf, *pat, b;
int j, r;
int size;
uint8 *pat, b;
int j, k, r;
int size, size_read;
xph.length = hio_read32l(f);
xph.packing = hio_read8(f);
@ -99,199 +103,213 @@ static int load_xm_pattern(struct module_data *m, int num, int version, HIO_HAND
}
size = xph.datasize;
pat = patbuf;
pat = patbuf = calloc(1, size);
if (patbuf == NULL) {
goto err;
size_read = hio_read(patbuf, 1, size, f);
if (size_read < size) {
memset(patbuf + size_read, 0, size - size_read);
}
hio_read(patbuf, 1, size, f);
for (j = 0; j < (mod->chn * r); j++) {
for (j = 0; j < r; j++) {
for (k = 0; k < mod->chn; k++) {
/*
if ((pat - patbuf) >= xph.datasize)
break;
*/
/*if ((pat - patbuf) >= xph.datasize)
break; */
event = &EVENT(num, k, j);
event = &EVENT(num, j % mod->chn, j / mod->chn);
if (--size < 0) {
goto err2;
}
if ((b = *pat++) & XM_EVENT_PACKING) {
if (b & XM_EVENT_NOTE_FOLLOWS) {
if (--size < 0)
goto err2;
event->note = *pat++;
if (--size < 0) {
goto err;
}
if (b & XM_EVENT_INSTRUMENT_FOLLOWS) {
if (--size < 0)
goto err2;
if ((b = *pat++) & XM_EVENT_PACKING) {
if (b & XM_EVENT_NOTE_FOLLOWS) {
if (--size < 0)
goto err;
event->note = *pat++;
}
if (b & XM_EVENT_INSTRUMENT_FOLLOWS) {
if (--size < 0)
goto err;
event->ins = *pat++;
}
if (b & XM_EVENT_VOLUME_FOLLOWS) {
if (--size < 0)
goto err;
event->vol = *pat++;
}
if (b & XM_EVENT_FXTYPE_FOLLOWS) {
if (--size < 0)
goto err;
event->fxt = *pat++;
}
if (b & XM_EVENT_FXPARM_FOLLOWS) {
if (--size < 0)
goto err;
event->fxp = *pat++;
}
} else {
size -= 4;
if (size < 0)
goto err;
event->note = b;
event->ins = *pat++;
}
if (b & XM_EVENT_VOLUME_FOLLOWS) {
if (--size < 0)
goto err2;
event->vol = *pat++;
}
if (b & XM_EVENT_FXTYPE_FOLLOWS) {
if (--size < 0)
goto err2;
event->fxt = *pat++;
}
if (b & XM_EVENT_FXPARM_FOLLOWS) {
if (--size < 0)
goto err2;
event->fxp = *pat++;
}
} else {
size -= 4;
if (size < 0)
goto err2;
event->note = b;
event->ins = *pat++;
event->vol = *pat++;
event->fxt = *pat++;
event->fxp = *pat++;
}
/* Sanity check */
switch (event->fxt) {
case 18:
case 19:
case 22:
case 23:
case 24:
case 26:
case 28:
case 30:
case 31:
case 32:
event->fxt = 0;
}
if (event->fxt > 34) {
event->fxt = 0;
}
if (event->note == 0x61) {
/* See OpenMPT keyoff+instr.xm test case */
if (event->fxt == 0x0e && MSN(event->fxp) == 0x0d) {
event->note = XMP_KEY_OFF;
} else {
event->note =
event->ins ? XMP_KEY_FADE : XMP_KEY_OFF;
/* Sanity check */
switch (event->fxt) {
case 18:
case 19:
case 22:
case 23:
case 24:
case 26:
case 28:
case 30:
case 31:
case 32:
event->fxt = 0;
}
} else if (event->note > 0) {
event->note += 12;
}
if (event->fxt == 0x0e) {
if (MSN(event->fxp) == EX_FINETUNE) {
unsigned char val = (LSN(event->fxp) - 8) & 0xf;
event->fxp = (EX_FINETUNE << 4) | val;
if (event->fxt > 34) {
event->fxt = 0;
}
switch (event->fxp) {
case 0x43:
case 0x73:
event->fxp--;
if (event->note == 0x61) {
/* See OpenMPT keyoff+instr.xm test case */
if (event->fxt == 0x0e && MSN(event->fxp) == 0x0d) {
event->note = XMP_KEY_OFF;
} else {
event->note =
event->ins ? XMP_KEY_FADE : XMP_KEY_OFF;
}
} else if (event->note > 0) {
event->note += 12;
}
if (event->fxt == 0x0e) {
if (MSN(event->fxp) == EX_FINETUNE) {
unsigned char val = (LSN(event->fxp) - 8) & 0xf;
event->fxp = (EX_FINETUNE << 4) | val;
}
switch (event->fxp) {
case 0x43:
case 0x73:
event->fxp--;
break;
}
}
if (event->fxt == FX_XF_PORTA && MSN(event->fxp) == 0x09) {
/* Translate MPT hacks */
switch (LSN(event->fxp)) {
case 0x0: /* Surround off */
case 0x1: /* Surround on */
event->fxt = FX_SURROUND;
event->fxp = LSN(event->fxp);
break;
case 0xe: /* Play forward */
case 0xf: /* Play reverse */
event->fxt = FX_REVERSE;
event->fxp = LSN(event->fxp) - 0xe;
}
}
if (!event->vol) {
continue;
}
/* Volume set */
if ((event->vol >= 0x10) && (event->vol <= 0x50)) {
event->vol -= 0x0f;
continue;
}
/* Volume column effects */
switch (event->vol >> 4) {
case 0x06: /* Volume slide down */
event->f2t = FX_VOLSLIDE_2;
event->f2p = event->vol - 0x60;
break;
case 0x07: /* Volume slide up */
event->f2t = FX_VOLSLIDE_2;
event->f2p = (event->vol - 0x70) << 4;
break;
case 0x08: /* Fine volume slide down */
event->f2t = FX_EXTENDED;
event->f2p =
(EX_F_VSLIDE_DN << 4) | (event->vol - 0x80);
break;
case 0x09: /* Fine volume slide up */
event->f2t = FX_EXTENDED;
event->f2p =
(EX_F_VSLIDE_UP << 4) | (event->vol - 0x90);
break;
case 0x0a: /* Set vibrato speed */
event->f2t = FX_VIBRATO;
event->f2p = (event->vol - 0xa0) << 4;
break;
case 0x0b: /* Vibrato */
event->f2t = FX_VIBRATO;
event->f2p = event->vol - 0xb0;
break;
case 0x0c: /* Set panning */
event->f2t = FX_SETPAN;
event->f2p = (event->vol - 0xc0) << 4;
break;
case 0x0d: /* Pan slide left */
event->f2t = FX_PANSL_NOMEM;
event->f2p = (event->vol - 0xd0) << 4;
break;
case 0x0e: /* Pan slide right */
event->f2t = FX_PANSL_NOMEM;
event->f2p = event->vol - 0xe0;
break;
case 0x0f: /* Tone portamento */
event->f2t = FX_TONEPORTA;
event->f2p = (event->vol - 0xf0) << 4;
/* From OpenMPT TonePortamentoMemory.xm:
* "Another nice bug (...) is the combination of both
* portamento commands (Mx and 3xx) in the same cell:
* The 3xx parameter is ignored completely, and the Mx
* parameter is doubled. (M2 3FF is the same as M4 000)
*/
if (event->fxt == FX_TONEPORTA
|| event->fxt == FX_TONE_VSLIDE) {
if (event->fxt == FX_TONEPORTA) {
event->fxt = 0;
} else {
event->fxt = FX_VOLSLIDE;
}
event->fxp = 0;
if (event->f2p < 0x80) {
event->f2p <<= 1;
} else {
event->f2p = 0xff;
}
}
/* From OpenMPT porta-offset.xm:
* "If there is a portamento command next to an offset
* command, the offset command is ignored completely. In
* particular, the offset parameter is not memorized."
*/
if (event->fxt == FX_OFFSET
&& event->f2t == FX_TONEPORTA) {
event->fxt = event->fxp = 0;
}
break;
}
event->vol = 0;
}
if (!event->vol) {
continue;
}
/* Volume set */
if ((event->vol >= 0x10) && (event->vol <= 0x50)) {
event->vol -= 0x0f;
continue;
}
/* Volume column effects */
switch (event->vol >> 4) {
case 0x06: /* Volume slide down */
event->f2t = FX_VOLSLIDE_2;
event->f2p = event->vol - 0x60;
break;
case 0x07: /* Volume slide up */
event->f2t = FX_VOLSLIDE_2;
event->f2p = (event->vol - 0x70) << 4;
break;
case 0x08: /* Fine volume slide down */
event->f2t = FX_EXTENDED;
event->f2p =
(EX_F_VSLIDE_DN << 4) | (event->vol - 0x80);
break;
case 0x09: /* Fine volume slide up */
event->f2t = FX_EXTENDED;
event->f2p =
(EX_F_VSLIDE_UP << 4) | (event->vol - 0x90);
break;
case 0x0a: /* Set vibrato speed */
event->f2t = FX_VIBRATO;
event->f2p = (event->vol - 0xa0) << 4;
break;
case 0x0b: /* Vibrato */
event->f2t = FX_VIBRATO;
event->f2p = event->vol - 0xb0;
break;
case 0x0c: /* Set panning */
event->f2t = FX_SETPAN;
event->f2p = (event->vol - 0xc0) << 4;
break;
case 0x0d: /* Pan slide left */
event->f2t = FX_PANSL_NOMEM;
event->f2p = (event->vol - 0xd0) << 4;
break;
case 0x0e: /* Pan slide right */
event->f2t = FX_PANSL_NOMEM;
event->f2p = event->vol - 0xe0;
break;
case 0x0f: /* Tone portamento */
event->f2t = FX_TONEPORTA;
event->f2p = (event->vol - 0xf0) << 4;
/* From OpenMPT TonePortamentoMemory.xm:
* "Another nice bug (...) is the combination of both
* portamento commands (Mx and 3xx) in the same cell:
* The 3xx parameter is ignored completely, and the Mx
* parameter is doubled. (M2 3FF is the same as M4 000)
*/
if (event->fxt == FX_TONEPORTA
|| event->fxt == FX_TONE_VSLIDE) {
if (event->fxt == FX_TONEPORTA) {
event->fxt = 0;
} else {
event->fxt = FX_VOLSLIDE;
}
event->fxp = 0;
if (event->f2p < 0x80) {
event->f2p <<= 1;
} else {
event->f2p = 0xff;
}
}
/* From OpenMPT porta-offset.xm:
* "If there is a portamento command next to an offset
* command, the offset command is ignored completely. In
* particular, the offset parameter is not memorized."
*/
if (event->fxt == FX_OFFSET
&& event->f2t == FX_TONEPORTA) {
event->fxt = event->fxp = 0;
}
break;
}
event->vol = 0;
}
free(patbuf);
return 0;
err2:
free(patbuf);
err:
return -1;
}
@ -299,6 +317,7 @@ err:
static int load_patterns(struct module_data *m, int version, HIO_HANDLE *f)
{
struct xmp_module *mod = &m->mod;
uint8 *patbuf;
int i, j;
mod->pat++;
@ -308,8 +327,12 @@ static int load_patterns(struct module_data *m, int version, HIO_HANDLE *f)
D_(D_INFO "Stored patterns: %d", mod->pat - 1);
if ((patbuf = (uint8 *) calloc(1, 65536)) == NULL) {
return -1;
}
for (i = 0; i < mod->pat - 1; i++) {
if (load_xm_pattern(m, i, version, f) < 0) {
if (load_xm_pattern(m, i, version, patbuf, f) < 0) {
goto err;
}
}
@ -333,21 +356,100 @@ static int load_patterns(struct module_data *m, int version, HIO_HANDLE *f)
}
}
free(patbuf);
return 0;
err:
free(patbuf);
return -1;
}
/* Packed structures size */
#define XM_INST_HEADER_SIZE 33
#define XM_INST_SIZE 208
#define XM_INST_HEADER_SIZE 29
#define XM_INST_SIZE 212
/* grass.near.the.house.xm defines 23 samples in instrument 1. FT2 docs
* specify at most 16. See https://github.com/libxmp/libxmp/issues/168
* for more details. */
#define XM_MAX_SAMPLES_PER_INST 32
#ifndef LIBXMP_CORE_PLAYER
#define MAGIC_OGGS 0x4f676753
static int is_ogg_sample(HIO_HANDLE *f, struct xmp_sample *xxs)
{
/* uint32 size; */
uint32 id;
/* Sample must be at least 4 bytes long to be an OGG sample.
* Bonnie's Bookstore music.oxm contains zero length samples
* followed immediately by OGG samples. */
if (xxs->len < 4)
return 0;
/* size = */ hio_read32l(f);
id = hio_read32b(f);
if (hio_error(f) != 0 || hio_seek(f, -8, SEEK_CUR) < 0)
return 0;
if (id != MAGIC_OGGS) { /* copy input data if not Ogg file */
return 0;
}
return 1;
}
static int oggdec(struct module_data *m, HIO_HANDLE *f, struct xmp_sample *xxs, int len)
{
int i, n, ch, rate, ret, flags = 0;
uint8 *data;
int16 *pcm16 = NULL;
if ((data = (uint8 *)calloc(1, len)) == NULL)
return -1;
hio_read32b(f);
if (hio_error(f) != 0 || hio_read(data, 1, len - 4, f) != len - 4) {
free(data);
return -1;
}
n = stb_vorbis_decode_memory(data, len, &ch, &rate, &pcm16);
free(data);
if (n <= 0) {
free(pcm16);
return -1;
}
xxs->len = n;
if ((xxs->flg & XMP_SAMPLE_16BIT) == 0) {
uint8 *pcm = (uint8 *)pcm16;
for (i = 0; i < n; i++) {
pcm[i] = pcm16[i] >> 8;
}
pcm = (uint8 *)realloc(pcm16, n);
if (pcm == NULL) {
free(pcm16);
return -1;
}
pcm16 = (int16 *)pcm;
}
flags |= SAMPLE_FLAG_NOLOAD;
#ifdef WORDS_BIGENDIAN
flags |= SAMPLE_FLAG_BIGEND;
#endif
ret = libxmp_load_sample(m, NULL, flags, xxs, pcm16);
free(pcm16);
return ret;
}
#endif
static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
{
struct xmp_module *mod = &m->mod;
@ -376,8 +478,12 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
* instruments, but file may end abruptly before that. Also covers
* XMLiTE stripped modules and truncated files. This test will not
* work if file has trailing garbage.
*
* Note: loading 4 bytes past the instrument header to get the
* sample header size (if it exists). This is NOT considered to
* be part of the instrument header.
*/
if (hio_read(buf, 33, 1, f) != 1) {
if (hio_read(buf, XM_INST_HEADER_SIZE + 4, 1, f) != 1) {
D_(D_WARN "short read in instrument header data");
break;
}
@ -389,6 +495,11 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
xih.sh_size = readmem32l(buf + 29); /* Sample header size */
/* Sanity check */
if ((int)xih.size < XM_INST_HEADER_SIZE) {
D_(D_CRIT "instrument %d: instrument header size:%d", i + 1, xih.size);
return -1;
}
if (xih.samples > XM_MAX_SAMPLES_PER_INST || (xih.samples > 0 && xih.sh_size > 0x100)) {
D_(D_CRIT "instrument %d: samples:%d sample header size:%d", i + 1, xih.samples, xih.sh_size);
return -1;
@ -416,7 +527,7 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
* generalization should take care of both cases.
*/
if (hio_seek(f, (int)xih.size - XM_INST_HEADER_SIZE, SEEK_CUR) < 0) {
if (hio_seek(f, (int)xih.size - (XM_INST_HEADER_SIZE + 4), SEEK_CUR) < 0) {
return -1;
}
@ -427,17 +538,13 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
return -1;
}
if (xih.size < XM_INST_HEADER_SIZE) {
return -1;
}
/* for BoobieSqueezer (see http://boobie.rotfl.at/)
* It works pretty much the same way as Impulse Tracker's sample
* only mode, where it will strip off the instrument data.
*/
if (xih.size < XM_INST_HEADER_SIZE + XM_INST_SIZE) {
memset(&xi, 0, sizeof(struct xm_instrument));
hio_seek(f, xih.size - XM_INST_HEADER_SIZE, SEEK_CUR);
hio_seek(f, xih.size - (XM_INST_HEADER_SIZE + 4), SEEK_CUR);
} else {
uint8 *b = buf;
@ -508,7 +615,7 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
for (j = 12; j < 108; j++) {
xxi->map[j].ins = xi.sample[j - 12];
if (xxi->map[j].ins >= xxi->nsm)
xxi->map[j].ins = -1;
xxi->map[j].ins = 0xff;
}
}
@ -576,6 +683,12 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
xxs->lps >>= 1;
xxs->lpe >>= 1;
}
if (xsh[j].type & XM_SAMPLE_STEREO) {
/* xxs->flg |= XMP_SAMPLE_STEREO; */
xxs->len >>= 1;
xxs->lps >>= 1;
xxs->lpe >>= 1;
}
xxs->flg |= xsh[j].type & XM_LOOP_FORWARD ? XMP_SAMPLE_LOOP : 0;
xxs->flg |= xsh[j].type & XM_LOOP_PINGPONG ? XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_BIDIR : 0;
@ -595,6 +708,7 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
total_sample_size = 0;
for (j = 0; j < xxi->nsm; j++) {
struct xmp_subinstrument *sub = &xxi->sub[j];
struct xmp_sample *xxs = &mod->xxs[sub->sid];
int flags;
flags = SAMPLE_FLAG_DIFF;
@ -606,7 +720,20 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
if (version > 0x0103) {
D_(D_INFO " read sample: index:%d sample id:%d", j, sub->sid);
if (libxmp_load_sample(m, f, flags, &mod->xxs[sub->sid], NULL) < 0) {
#ifndef LIBXMP_CORE_PLAYER
if (is_ogg_sample(f, xxs)) {
if (oggdec(m, f, xxs, xsh[j].length) < 0) {
return -1;
}
D_(D_INFO " sample is vorbis");
total_sample_size += xsh[j].length;
continue;
}
#endif
if (libxmp_load_sample(m, f, flags, xxs, NULL) < 0) {
return -1;
}
if (flags & SAMPLE_FLAG_ADPCM) {
@ -615,6 +742,12 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
} else {
total_sample_size += xsh[j].length;
}
/* TODO: implement stereo samples.
* For now, just skip the right channel. */
if (xsh[j].type & XM_SAMPLE_STEREO) {
hio_seek(f, xsh[j].length >> 1, SEEK_CUR);
}
}
}
@ -640,6 +773,10 @@ static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start)
int i, j;
struct xm_file_header xfh;
char tracker_name[21];
#ifndef LIBXMP_CORE_PLAYER
int claims_ft2 = 0;
int is_mpt_116 = 0;
#endif
int len;
uint8 buf[80];
@ -666,19 +803,32 @@ static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start)
xfh.bpm = readmem16l(buf + 78); /* Default BPM */
/* Sanity checks */
if (xfh.songlen > 256 || xfh.patterns > 256 || xfh.instruments > 255) {
D_(D_CRIT "Sanity check: %d %d %d", xfh.songlen, xfh.patterns, xfh.instruments);
if (xfh.songlen > 256) {
D_(D_CRIT "bad song length: %d", xfh.songlen);
return -1;
}
if (xfh.patterns > 256) {
D_(D_CRIT "bad pattern count: %d", xfh.patterns);
return -1;
}
if (xfh.instruments > 255) {
D_(D_CRIT "bad instrument count: %d", xfh.instruments);
return -1;
}
if (xfh.restart > 255 || xfh.channels > XMP_MAX_CHANNELS) {
D_(D_CRIT "Sanity check: %d %d", xfh.restart, xfh.channels);
if (xfh.restart > 255) {
D_(D_CRIT "bad restart position: %d", xfh.restart);
return -1;
}
if (xfh.channels > XMP_MAX_CHANNELS) {
D_(D_CRIT "bad channel count: %d", xfh.channels);
return -1;
}
if (xfh.tempo >= 32 || xfh.bpm < 32 || xfh.bpm > 255) {
/* FT2 and MPT allow up to 255 BPM. OpenMPT allows up to 1000 BPM. */
if (xfh.tempo >= 32 || xfh.bpm < 32 || xfh.bpm > 1000) {
if (memcmp("MED2XM", xfh.tracker, 6)) {
D_(D_CRIT "Sanity check: %d %d", xfh.tempo, xfh.bpm);
D_(D_CRIT "bad tempo or BPM: %d %d", xfh.tempo, xfh.bpm);
return -1;
}
}
@ -686,10 +836,11 @@ static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start)
/* Honor header size -- needed by BoobieSqueezer XMs */
len = xfh.headersz - 0x14;
if (len < 0 || len > 256) {
D_(D_CRIT "Sanity check: %d", len);
D_(D_CRIT "bad XM header length: %d", len);
return -1;
}
memset(xfh.order, 0, sizeof(xfh.order));
if (hio_read(xfh.order, len, 1, f) != 1) { /* Pattern order table */
D_(D_CRIT "error reading orders");
return -1;
@ -720,8 +871,12 @@ static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start)
}
/* OpenMPT accurately emulates weird FT2 bugs */
if (!strncmp(tracker_name, "FastTracker v2.00", 17) ||
!strncmp(tracker_name, "OpenMPT ", 8)) {
if (!strncmp(tracker_name, "FastTracker v2.00", 17)) {
m->quirk |= QUIRK_FT2BUGS;
#ifndef LIBXMP_CORE_PLAYER
claims_ft2 = 1;
#endif
} else if (!strncmp(tracker_name, "OpenMPT ", 8)) {
m->quirk |= QUIRK_FT2BUGS;
}
#ifndef LIBXMP_CORE_PLAYER
@ -744,6 +899,7 @@ static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start)
if (!strncmp(tracker_name, "FastTracker v 2.00", 18)) {
strcpy(tracker_name, "old ModPlug Tracker");
m->quirk &= ~QUIRK_FT2BUGS;
is_mpt_116 = 1;
}
libxmp_set_type(m, "%s XM %d.%02d", tracker_name, xfh.version >> 8, xfh.version & 0xff);
@ -789,6 +945,73 @@ static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start)
}
}
#ifndef LIBXMP_CORE_PLAYER
/* Load MPT properties from the end of the file. */
while (1) {
uint32 ext = hio_read32b(f);
uint32 sz = hio_read32l(f);
int known = 0;
if (hio_error(f) || sz > 0x7fffffff /* INT32_MAX */)
break;
switch (ext) {
case MAGIC4('t','e','x','t'): /* Song comment */
known = 1;
if (m->comment != NULL)
break;
if ((m->comment = (char *)malloc(sz + 1)) == NULL)
break;
sz = hio_read(m->comment, 1, sz, f);
m->comment[sz] = '\0';
for (i = 0; i < (int)sz; i++) {
int b = m->comment[i];
if (b == '\r') {
m->comment[i] = '\n';
} else if ((b < 32 || b > 127) && b != '\n'
&& b != '\t') {
m->comment[i] = '.';
}
}
break;
case MAGIC4('M','I','D','I'): /* MIDI config */
case MAGIC4('P','N','A','M'): /* Pattern names */
case MAGIC4('C','N','A','M'): /* Channel names */
case MAGIC4('C','H','F','X'): /* Channel plugins */
case MAGIC4('X','T','P','M'): /* Inst. extensions */
known = 1;
/* fall-through */
default:
/* Plugin definition */
if ((ext & MAGIC4('F','X', 0, 0)) == MAGIC4('F','X', 0, 0))
known = 1;
if (sz) hio_seek(f, sz, SEEK_CUR);
break;
}
if(known && claims_ft2)
is_mpt_116 = 1;
if (ext == MAGIC4('X','T','P','M'))
break;
}
if (is_mpt_116) {
libxmp_set_type(m, "ModPlug Tracker 1.16 XM %d.%02d",
xfh.version >> 8, xfh.version & 0xff);
m->mvolbase = 48;
m->mvol = 48;
libxmp_apply_mpt_preamp(m);
}
#endif
for (i = 0; i < mod->chn; i++) {
mod->xxc[i].pan = 0x80;
}

View file

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

View file

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