mirror of
https://github.com/QB64-Phoenix-Edition/QB64pe.git
synced 2024-09-08 11:10:16 +00:00
commit
386b8d802e
47 changed files with 3862 additions and 1351 deletions
|
@ -4,6 +4,7 @@ LIBXMP_SRCS := \
|
||||||
control.c \
|
control.c \
|
||||||
dataio.c \
|
dataio.c \
|
||||||
effects.c \
|
effects.c \
|
||||||
|
filetype.c \
|
||||||
filter.c \
|
filter.c \
|
||||||
format.c \
|
format.c \
|
||||||
hio.c \
|
hio.c \
|
||||||
|
@ -34,7 +35,7 @@ LIBXMP_OBJS += $(patsubst %.c,$(PATH_INTERNAL_C)/parts/audio/extras/libxmp-lite/
|
||||||
LIBXMP_LIB := $(PATH_INTERNAL_C)/parts/audio/extras/libxmp-lite.a
|
LIBXMP_LIB := $(PATH_INTERNAL_C)/parts/audio/extras/libxmp-lite.a
|
||||||
|
|
||||||
$(PATH_INTERNAL_C)/parts/audio/extras/libxmp-lite/%.o: $(PATH_INTERNAL_C)/parts/audio/extras/libxmp-lite/%.c
|
$(PATH_INTERNAL_C)/parts/audio/extras/libxmp-lite/%.o: $(PATH_INTERNAL_C)/parts/audio/extras/libxmp-lite/%.c
|
||||||
$(CC) -O2 $(CFLAGS) -Wall -DLIBXMP_CORE_PLAYER -DLIBXMP_NO_PROWIZARD -DLIBXMP_NO_DEPACKERS -DBUILDING_STATIC $< -c -o $@
|
$(CC) -O2 $(CFLAGS) -Wall -DLIBXMP_CORE_PLAYER -DLIBXMP_STATIC $< -c -o $@
|
||||||
|
|
||||||
$(LIBXMP_LIB): $(LIBXMP_OBJS)
|
$(LIBXMP_LIB): $(LIBXMP_OBJS)
|
||||||
$(AR) rcs $@ $^
|
$(AR) rcs $@ $^
|
||||||
|
|
|
@ -10,9 +10,7 @@ typedef struct {
|
||||||
int eof;
|
int eof;
|
||||||
} CBFILE;
|
} CBFILE;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
LIBXMP_BEGIN_DECLS
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static inline uint8 cbread8(CBFILE *f, int *err)
|
static inline uint8 cbread8(CBFILE *f, int *err)
|
||||||
{
|
{
|
||||||
|
@ -33,7 +31,7 @@ static inline int8 cbread8s(CBFILE *f, int *err)
|
||||||
static inline uint16 cbread16l(CBFILE *f, int *err)
|
static inline uint16 cbread16l(CBFILE *f, int *err)
|
||||||
{
|
{
|
||||||
uint8 buf[2];
|
uint8 buf[2];
|
||||||
uint16 x = EOF;
|
uint16 x = 0xffff;
|
||||||
size_t r = f->callbacks.read_func(buf, 2, 1, f->priv);
|
size_t r = f->callbacks.read_func(buf, 2, 1, f->priv);
|
||||||
f->eof = (r == 1) ? 0 : EOF;
|
f->eof = (r == 1) ? 0 : EOF;
|
||||||
|
|
||||||
|
@ -46,7 +44,7 @@ static inline uint16 cbread16l(CBFILE *f, int *err)
|
||||||
static inline uint16 cbread16b(CBFILE *f, int *err)
|
static inline uint16 cbread16b(CBFILE *f, int *err)
|
||||||
{
|
{
|
||||||
uint8 buf[2];
|
uint8 buf[2];
|
||||||
uint16 x = EOF;
|
uint16 x = 0xffff;
|
||||||
size_t r = f->callbacks.read_func(buf, 2, 1, f->priv);
|
size_t r = f->callbacks.read_func(buf, 2, 1, f->priv);
|
||||||
f->eof = (r == 1) ? 0 : EOF;
|
f->eof = (r == 1) ? 0 : EOF;
|
||||||
|
|
||||||
|
@ -59,7 +57,7 @@ static inline uint16 cbread16b(CBFILE *f, int *err)
|
||||||
static inline uint32 cbread24l(CBFILE *f, int *err)
|
static inline uint32 cbread24l(CBFILE *f, int *err)
|
||||||
{
|
{
|
||||||
uint8 buf[3];
|
uint8 buf[3];
|
||||||
uint32 x = EOF;
|
uint32 x = 0xffffffff;
|
||||||
size_t r = f->callbacks.read_func(buf, 3, 1, f->priv);
|
size_t r = f->callbacks.read_func(buf, 3, 1, f->priv);
|
||||||
f->eof = (r == 1) ? 0 : EOF;
|
f->eof = (r == 1) ? 0 : EOF;
|
||||||
|
|
||||||
|
@ -72,7 +70,7 @@ static inline uint32 cbread24l(CBFILE *f, int *err)
|
||||||
static inline uint32 cbread24b(CBFILE *f, int *err)
|
static inline uint32 cbread24b(CBFILE *f, int *err)
|
||||||
{
|
{
|
||||||
uint8 buf[3];
|
uint8 buf[3];
|
||||||
uint32 x = EOF;
|
uint32 x = 0xffffffff;
|
||||||
size_t r = f->callbacks.read_func(buf, 3, 1, f->priv);
|
size_t r = f->callbacks.read_func(buf, 3, 1, f->priv);
|
||||||
f->eof = (r == 1) ? 0 : EOF;
|
f->eof = (r == 1) ? 0 : EOF;
|
||||||
|
|
||||||
|
@ -85,7 +83,7 @@ static inline uint32 cbread24b(CBFILE *f, int *err)
|
||||||
static inline uint32 cbread32l(CBFILE *f, int *err)
|
static inline uint32 cbread32l(CBFILE *f, int *err)
|
||||||
{
|
{
|
||||||
uint8 buf[4];
|
uint8 buf[4];
|
||||||
uint32 x = EOF;
|
uint32 x = 0xffffffff;
|
||||||
size_t r = f->callbacks.read_func(buf, 4, 1, f->priv);
|
size_t r = f->callbacks.read_func(buf, 4, 1, f->priv);
|
||||||
f->eof = (r == 1) ? 0 : EOF;
|
f->eof = (r == 1) ? 0 : EOF;
|
||||||
|
|
||||||
|
@ -98,7 +96,7 @@ static inline uint32 cbread32l(CBFILE *f, int *err)
|
||||||
static inline uint32 cbread32b(CBFILE *f, int *err)
|
static inline uint32 cbread32b(CBFILE *f, int *err)
|
||||||
{
|
{
|
||||||
uint8 buf[4];
|
uint8 buf[4];
|
||||||
uint32 x = EOF;
|
uint32 x = 0xffffffff;
|
||||||
size_t r = f->callbacks.read_func(buf, 4, 1, f->priv);
|
size_t r = f->callbacks.read_func(buf, 4, 1, f->priv);
|
||||||
f->eof = (r == 1) ? 0 : EOF;
|
f->eof = (r == 1) ? 0 : EOF;
|
||||||
|
|
||||||
|
@ -110,7 +108,8 @@ static inline uint32 cbread32b(CBFILE *f, int *err)
|
||||||
|
|
||||||
static inline size_t cbread(void *dest, size_t len, size_t nmemb, CBFILE *f)
|
static inline size_t cbread(void *dest, size_t len, size_t nmemb, CBFILE *f)
|
||||||
{
|
{
|
||||||
size_t r = f->callbacks.read_func(dest, len, nmemb, f->priv);
|
size_t r = f->callbacks.read_func(dest, (unsigned long)len,
|
||||||
|
(unsigned long)nmemb, f->priv);
|
||||||
f->eof = (r < nmemb) ? EOF : 0;
|
f->eof = (r < nmemb) ? EOF : 0;
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -183,8 +182,6 @@ static inline int cbclose(CBFILE *f)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
LIBXMP_END_DECLS
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* LIBXMP_CALLBACKIO_H */
|
#endif /* LIBXMP_CALLBACKIO_H */
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Extended Module Player
|
/* Extended Module Player
|
||||||
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
|
* Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -21,28 +21,14 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#if defined(HAVE_DIRENT)
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#include <sys/types.h>
|
||||||
#if defined(_WIN32)
|
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#endif
|
|
||||||
#include <windows.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#elif defined(__OS2__) || defined(__EMX__)
|
|
||||||
#define INCL_DOS
|
|
||||||
#define INCL_DOSERRORS
|
|
||||||
#include <os2.h>
|
|
||||||
#elif defined(__DJGPP__)
|
|
||||||
#include <dos.h>
|
|
||||||
#include <io.h>
|
|
||||||
#elif defined(HAVE_DIRENT_H)
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#endif
|
#endif
|
||||||
#endif /* LIBXMP_CORE_PLAYER */
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
#include "xmp.h"
|
#include "xmp.h"
|
||||||
#include "common.h"
|
|
||||||
#include "period.h"
|
#include "period.h"
|
||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
|
|
||||||
|
@ -51,7 +37,7 @@ int libxmp_init_instrument(struct module_data *m)
|
||||||
struct xmp_module *mod = &m->mod;
|
struct xmp_module *mod = &m->mod;
|
||||||
|
|
||||||
if (mod->ins > 0) {
|
if (mod->ins > 0) {
|
||||||
mod->xxi = calloc(sizeof (struct xmp_instrument), mod->ins);
|
mod->xxi = (struct xmp_instrument *) calloc(mod->ins, sizeof(struct xmp_instrument));
|
||||||
if (mod->xxi == NULL)
|
if (mod->xxi == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -65,10 +51,10 @@ int libxmp_init_instrument(struct module_data *m)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
mod->xxs = calloc(sizeof (struct xmp_sample), mod->smp);
|
mod->xxs = (struct xmp_sample *) calloc(mod->smp, sizeof(struct xmp_sample));
|
||||||
if (mod->xxs == NULL)
|
if (mod->xxs == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
m->xtra = calloc(sizeof (struct extra_sample_data), mod->smp);
|
m->xtra = (struct extra_sample_data *) calloc(mod->smp, sizeof(struct extra_sample_data));
|
||||||
if (m->xtra == NULL)
|
if (m->xtra == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -103,12 +89,12 @@ int libxmp_realloc_samples(struct module_data *m, int new_size)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
xxs = realloc(mod->xxs, sizeof(struct xmp_sample) * new_size);
|
xxs = (struct xmp_sample *) realloc(mod->xxs, sizeof(struct xmp_sample) * new_size);
|
||||||
if (xxs == NULL)
|
if (xxs == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
mod->xxs = xxs;
|
mod->xxs = xxs;
|
||||||
|
|
||||||
xtra = realloc(m->xtra, sizeof(struct extra_sample_data) * new_size);
|
xtra = (struct extra_sample_data *) realloc(m->xtra, sizeof(struct extra_sample_data) * new_size);
|
||||||
if (xtra == NULL)
|
if (xtra == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
m->xtra = xtra;
|
m->xtra = xtra;
|
||||||
|
@ -133,7 +119,7 @@ int libxmp_alloc_subinstrument(struct xmp_module *mod, int i, int num)
|
||||||
if (num == 0)
|
if (num == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
mod->xxi[i].sub = calloc(sizeof (struct xmp_subinstrument), num);
|
mod->xxi[i].sub = (struct xmp_subinstrument *) calloc(num, sizeof(struct xmp_subinstrument));
|
||||||
if (mod->xxi[i].sub == NULL)
|
if (mod->xxi[i].sub == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -142,11 +128,11 @@ int libxmp_alloc_subinstrument(struct xmp_module *mod, int i, int num)
|
||||||
|
|
||||||
int libxmp_init_pattern(struct xmp_module *mod)
|
int libxmp_init_pattern(struct xmp_module *mod)
|
||||||
{
|
{
|
||||||
mod->xxt = calloc(sizeof (struct xmp_track *), mod->trk);
|
mod->xxt = (struct xmp_track **) calloc(mod->trk, sizeof(struct xmp_track *));
|
||||||
if (mod->xxt == NULL)
|
if (mod->xxt == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
mod->xxp = calloc(sizeof (struct xmp_pattern *), mod->pat);
|
mod->xxp = (struct xmp_pattern **) calloc(mod->pat, sizeof(struct xmp_pattern *));
|
||||||
if (mod->xxp == NULL)
|
if (mod->xxp == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -159,8 +145,8 @@ int libxmp_alloc_pattern(struct xmp_module *mod, int num)
|
||||||
if (num < 0 || num >= mod->pat || mod->xxp[num] != NULL)
|
if (num < 0 || num >= mod->pat || mod->xxp[num] != NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
mod->xxp[num] = calloc(1, sizeof (struct xmp_pattern) +
|
mod->xxp[num] = (struct xmp_pattern *) calloc(1, sizeof(struct xmp_pattern) +
|
||||||
sizeof (int) * (mod->chn - 1));
|
sizeof(int) * (mod->chn - 1));
|
||||||
if (mod->xxp[num] == NULL)
|
if (mod->xxp[num] == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -173,8 +159,8 @@ int libxmp_alloc_track(struct xmp_module *mod, int num, int rows)
|
||||||
if (num < 0 || num >= mod->trk || mod->xxt[num] != NULL || rows <= 0)
|
if (num < 0 || num >= mod->trk || mod->xxt[num] != NULL || rows <= 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
mod->xxt[num] = calloc(sizeof (struct xmp_track) +
|
mod->xxt[num] = (struct xmp_track *) calloc(1, sizeof(struct xmp_track) +
|
||||||
sizeof (struct xmp_event) * (rows - 1), 1);
|
sizeof(struct xmp_event) * (rows - 1));
|
||||||
if (mod->xxt[num] == NULL)
|
if (mod->xxt[num] == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -218,6 +204,7 @@ int libxmp_alloc_pattern_tracks(struct xmp_module *mod, int num, int rows)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
/* Some formats explicitly allow more than 256 rows (e.g. OctaMED). This function
|
/* Some formats explicitly allow more than 256 rows (e.g. OctaMED). This function
|
||||||
* allows those formats to work without disrupting the sanity check for other formats.
|
* allows those formats to work without disrupting the sanity check for other formats.
|
||||||
*/
|
*/
|
||||||
|
@ -237,6 +224,7 @@ int libxmp_alloc_pattern_tracks_long(struct xmp_module *mod, int num, int rows)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
char *libxmp_instrument_name(struct xmp_module *mod, int i, uint8 *r, int n)
|
char *libxmp_instrument_name(struct xmp_module *mod, int i, uint8 *r, int n)
|
||||||
{
|
{
|
||||||
|
@ -253,7 +241,7 @@ char *libxmp_copy_adjust(char *s, uint8 *r, int n)
|
||||||
strncpy(s, (char *)r, n);
|
strncpy(s, (char *)r, n);
|
||||||
|
|
||||||
for (i = 0; s[i] && i < n; i++) {
|
for (i = 0; s[i] && i < n; i++) {
|
||||||
if (!isprint((int)s[i]) || ((uint8)s[i] > 127))
|
if (!isprint((unsigned char)s[i]) || ((uint8)s[i] > 127))
|
||||||
s[i] = '.';
|
s[i] = '.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +255,7 @@ void libxmp_read_title(HIO_HANDLE *f, char *t, int s)
|
||||||
{
|
{
|
||||||
uint8 buf[XMP_NAME_SIZE];
|
uint8 buf[XMP_NAME_SIZE];
|
||||||
|
|
||||||
if (t == NULL)
|
if (t == NULL || s < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (s >= XMP_NAME_SIZE)
|
if (s >= XMP_NAME_SIZE)
|
||||||
|
@ -275,22 +263,27 @@ void libxmp_read_title(HIO_HANDLE *f, char *t, int s)
|
||||||
|
|
||||||
memset(t, 0, s + 1);
|
memset(t, 0, s + 1);
|
||||||
|
|
||||||
hio_read(buf, 1, s, f); /* coverity[check_return] */
|
s = hio_read(buf, 1, s, f);
|
||||||
buf[s] = 0;
|
buf[s] = 0;
|
||||||
libxmp_copy_adjust(t, buf, s);
|
libxmp_copy_adjust(t, buf, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
|
||||||
int libxmp_test_name(uint8 *s, int n)
|
int libxmp_test_name(const uint8 *s, int n, int flags)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
|
if (s[i] == '\0' && (flags & TEST_NAME_IGNORE_AFTER_0))
|
||||||
|
break;
|
||||||
|
if (s[i] == '\r' && (flags & TEST_NAME_IGNORE_AFTER_CR))
|
||||||
|
break;
|
||||||
if (s[i] > 0x7f)
|
if (s[i] > 0x7f)
|
||||||
return -1;
|
return -1;
|
||||||
/* ACS_Team2.mod has a backspace in instrument name */
|
/* ACS_Team2.mod has a backspace in instrument name */
|
||||||
if (s[i] > 0 && s[i] < 32 && s[i] != 0x08)
|
/* Numerous ST modules from Music Channel BBS have char 14. */
|
||||||
|
if (s[i] > 0 && s[i] < 32 && s[i] != 0x08 && s[i] != 0x0e)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,7 +302,6 @@ int libxmp_copy_name_for_fopen(char *dest, const char *name, int n)
|
||||||
name[0] == '\\' || name[0] == '/' || name[0] == ':')
|
name[0] == '\\' || name[0] == '/' || name[0] == ':')
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
|
||||||
for (i = 0; i < n - 1; i++) {
|
for (i = 0; i < n - 1; i++) {
|
||||||
uint8 t = name[i];
|
uint8 t = name[i];
|
||||||
if (!t)
|
if (!t)
|
||||||
|
@ -370,7 +362,7 @@ int libxmp_copy_name_for_fopen(char *dest, const char *name, int n)
|
||||||
* module players erroneously interpret as "newer-version-trackers commands".
|
* module players erroneously interpret as "newer-version-trackers commands".
|
||||||
* Which they aren't.
|
* Which they aren't.
|
||||||
*/
|
*/
|
||||||
void libxmp_decode_noisetracker_event(struct xmp_event *event, uint8 *mod_event)
|
void libxmp_decode_noisetracker_event(struct xmp_event *event, const uint8 *mod_event)
|
||||||
{
|
{
|
||||||
int fxt;
|
int fxt;
|
||||||
|
|
||||||
|
@ -388,7 +380,7 @@ void libxmp_decode_noisetracker_event(struct xmp_event *event, uint8 *mod_event)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void libxmp_decode_protracker_event(struct xmp_event *event, uint8 *mod_event)
|
void libxmp_decode_protracker_event(struct xmp_event *event, const uint8 *mod_event)
|
||||||
{
|
{
|
||||||
int fxt = LSN(mod_event[2]);
|
int fxt = LSN(mod_event[2]);
|
||||||
|
|
||||||
|
@ -430,74 +422,45 @@ void libxmp_disable_continue_fx(struct xmp_event *event)
|
||||||
/* libxmp_check_filename_case(): */
|
/* libxmp_check_filename_case(): */
|
||||||
/* Given a directory, see if file exists there, ignoring case */
|
/* Given a directory, see if file exists there, ignoring case */
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32) || defined(__DJGPP__) || \
|
||||||
|
defined(__OS2__) || defined(__EMX__) || \
|
||||||
|
defined(_DOS) || defined(LIBXMP_AMIGA) || \
|
||||||
|
defined(__riscos__) || \
|
||||||
|
/* case-insensitive file system: directly probe the file */\
|
||||||
|
\
|
||||||
|
!defined(HAVE_DIRENT) /* or, target does not have dirent. */
|
||||||
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
|
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
|
||||||
{
|
{
|
||||||
char path[_MAX_PATH];
|
char path[XMP_MAXPATH];
|
||||||
DWORD rc;
|
|
||||||
/* win32 is case-insensitive: directly probe the file. */
|
|
||||||
snprintf(path, sizeof(path), "%s/%s", dir, name);
|
snprintf(path, sizeof(path), "%s/%s", dir, name);
|
||||||
rc = GetFileAttributesA(path);
|
if (! (libxmp_get_filetype(path) & XMP_FILETYPE_FILE))
|
||||||
if (rc == (DWORD)(-1)) return 0;
|
return 0;
|
||||||
if (rc & FILE_ATTRIBUTE_DIRECTORY) return 0;
|
|
||||||
strncpy(new_name, name, size);
|
strncpy(new_name, name, size);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
#elif defined(__OS2__) || defined(__EMX__)
|
#else /* target has dirent */
|
||||||
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
|
|
||||||
{
|
|
||||||
char path[CCHMAXPATH];
|
|
||||||
FILESTATUS3 fs;
|
|
||||||
/* os/2 is case-insensitive: directly probe the file. */
|
|
||||||
snprintf(path, sizeof(path), "%s/%s", dir, name);
|
|
||||||
if (DosQueryPathInfo(path, FIL_STANDARD, &fs, sizeof(fs)) != NO_ERROR) return 0;
|
|
||||||
if (fs.attrFile & FILE_DIRECTORY) return 0;
|
|
||||||
strncpy(new_name, name, size);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
#elif defined(__DJGPP__)
|
|
||||||
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
|
|
||||||
{
|
|
||||||
char path[256];
|
|
||||||
int attr;
|
|
||||||
/* dos is case-insensitive: directly probe the file. */
|
|
||||||
snprintf(path, sizeof(path), "%s/%s", dir, name);
|
|
||||||
attr = _chmod(path, 0);
|
|
||||||
if (attr == -1) return 0;
|
|
||||||
if (attr & (_A_SUBDIR|_A_VOLID)) return 0;
|
|
||||||
strncpy(new_name, name, size);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
#elif defined(HAVE_DIRENT_H)
|
|
||||||
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
|
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
|
||||||
{
|
{
|
||||||
int found = 0;
|
int found = 0;
|
||||||
DIR *dirfd;
|
DIR *dirp;
|
||||||
struct dirent *d;
|
struct dirent *d;
|
||||||
|
|
||||||
dirfd = opendir(dir);
|
dirp = opendir(dir);
|
||||||
if (dirfd == NULL)
|
if (dirp == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
while ((d = readdir(dirfd)) != NULL) {
|
while ((d = readdir(dirp)) != NULL) {
|
||||||
if (!strcasecmp(d->d_name, name)) {
|
if (!strcasecmp(d->d_name, name)) {
|
||||||
found = 1;
|
found = 1;
|
||||||
|
strncpy(new_name, d->d_name, size);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (found)
|
closedir(dirp);
|
||||||
strncpy(new_name, d->d_name, size);
|
|
||||||
|
|
||||||
closedir(dirfd);
|
|
||||||
|
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void libxmp_get_instrument_path(struct module_data *m, char *path, int size)
|
void libxmp_get_instrument_path(struct module_data *m, char *path, int size)
|
||||||
|
@ -521,6 +484,7 @@ void libxmp_set_type(struct module_data *m, const char *fmt, ...)
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
static int schism_tracker_date(int year, int month, int day)
|
static int schism_tracker_date(int year, int month, int day)
|
||||||
{
|
{
|
||||||
int mm = (month + 9) % 12;
|
int mm = (month + 9) % 12;
|
||||||
|
@ -543,7 +507,7 @@ void libxmp_schism_tracker_string(char *buf, size_t size, int s_ver, int l_ver)
|
||||||
{
|
{
|
||||||
if (s_ver >= 0x50) {
|
if (s_ver >= 0x50) {
|
||||||
/* time_t epoch_sec = 1256947200; */
|
/* time_t epoch_sec = 1256947200; */
|
||||||
int t = schism_tracker_date(2009, 10, 31);
|
int64 t = schism_tracker_date(2009, 10, 31);
|
||||||
int year, month, day, dayofyear;
|
int year, month, day, dayofyear;
|
||||||
|
|
||||||
if (s_ver == 0xfff) {
|
if (s_ver == 0xfff) {
|
||||||
|
@ -553,11 +517,11 @@ void libxmp_schism_tracker_string(char *buf, size_t size, int s_ver, int l_ver)
|
||||||
|
|
||||||
/* Date algorithm reimplemented from OpenMPT.
|
/* Date algorithm reimplemented from OpenMPT.
|
||||||
*/
|
*/
|
||||||
year = (int)(((int64)t * 10000L + 14780) / 3652425);
|
year = (int)((t * 10000L + 14780) / 3652425);
|
||||||
dayofyear = t - (365 * year + (year / 4) - (year / 100) + (year / 400));
|
dayofyear = t - (365L * year + (year / 4) - (year / 100) + (year / 400));
|
||||||
if (dayofyear < 0) {
|
if (dayofyear < 0) {
|
||||||
year--;
|
year--;
|
||||||
dayofyear = t - (365 * year + (year / 4) - (year / 100) + (year / 400));
|
dayofyear = t - (365L * year + (year / 4) - (year / 100) + (year / 400));
|
||||||
}
|
}
|
||||||
month = (100 * dayofyear + 52) / 3060;
|
month = (100 * dayofyear + 52) / 3060;
|
||||||
day = dayofyear - (month * 306 + 5) / 10 + 1;
|
day = dayofyear - (month * 306 + 5) / 10 + 1;
|
||||||
|
@ -571,3 +535,60 @@ void libxmp_schism_tracker_string(char *buf, size_t size, int s_ver, int l_ver)
|
||||||
snprintf(buf, size, "Schism Tracker 0.%x", s_ver);
|
snprintf(buf, size, "Schism Tracker 0.%x", s_ver);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Old MPT modules (from MPT <=1.16, older versions of OpenMPT) rely on a
|
||||||
|
* pre-amp routine that scales mix volume down. This is based on the module's
|
||||||
|
* channel count and a tracker pre-amp setting that isn't saved in the module.
|
||||||
|
* This setting defaults to 128. When fixed to 128, it can be optimized out.
|
||||||
|
*
|
||||||
|
* In OpenMPT, this pre-amp routine is only available in the MPT and OpenMPT
|
||||||
|
* 1.17 RC1 and RC2 mix modes. Changing a module to the compatible or 1.17 RC3
|
||||||
|
* mix modes will permanently disable it for that module. OpenMPT applies the
|
||||||
|
* old mix modes to MPT <=1.16 modules, "IT 8.88", and in old OpenMPT-made
|
||||||
|
* modules that specify one of these mix modes in their extended properties.
|
||||||
|
*
|
||||||
|
* Set mod->chn and m->mvol first!
|
||||||
|
*/
|
||||||
|
void libxmp_apply_mpt_preamp(struct module_data *m)
|
||||||
|
{
|
||||||
|
/* OpenMPT uses a slightly different table. */
|
||||||
|
static const uint8 preamp_table[16] =
|
||||||
|
{
|
||||||
|
0x60, 0x60, 0x60, 0x70, /* 0-7 */
|
||||||
|
0x80, 0x88, 0x90, 0x98, /* 8-15 */
|
||||||
|
0xA0, 0xA4, 0xA8, 0xB0, /* 16-23 */
|
||||||
|
0xB4, 0xB8, 0xBC, 0xC0, /* 24-31 */
|
||||||
|
};
|
||||||
|
|
||||||
|
int chn = m->mod.chn;
|
||||||
|
CLAMP(chn, 1, 31);
|
||||||
|
|
||||||
|
m->mvol = (m->mvol * 96) / preamp_table[chn >> 1];
|
||||||
|
|
||||||
|
/* Pre-amp is applied like this in the mixers of libmodplug/libopenmpt
|
||||||
|
* (still vastly simplified).
|
||||||
|
|
||||||
|
int preamp = 128;
|
||||||
|
|
||||||
|
if (preamp > 128) {
|
||||||
|
preamp = 128 + ((preamp - 128) * (chn + 4)) / 16;
|
||||||
|
}
|
||||||
|
preamp = preamp * m->mvol / 64;
|
||||||
|
preamp = (preamp << 7) / preamp_table[chn >> 1];
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
channel_volume_16bit = (channel_volume_16bit * preamp) >> 7;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char *libxmp_strdup(const char *src)
|
||||||
|
{
|
||||||
|
size_t len = strlen(src) + 1;
|
||||||
|
char *buf = (char *) malloc(len);
|
||||||
|
if (buf) {
|
||||||
|
memcpy(buf, src, len);
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,19 @@
|
||||||
#ifndef LIBXMP_COMMON_H
|
#ifndef LIBXMP_COMMON_H
|
||||||
#define LIBXMP_COMMON_H
|
#define LIBXMP_COMMON_H
|
||||||
|
|
||||||
|
#ifdef LIBXMP_CORE_PLAYER
|
||||||
|
#ifndef LIBXMP_NO_PROWIZARD
|
||||||
|
#define LIBXMP_NO_PROWIZARD
|
||||||
|
#endif
|
||||||
|
#ifndef LIBXMP_NO_DEPACKERS
|
||||||
|
#define LIBXMP_NO_DEPACKERS
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#undef LIBXMP_CORE_DISABLE_IT
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -15,16 +27,63 @@
|
||||||
#define LIBXMP_EXPORT_VAR
|
#define LIBXMP_EXPORT_VAR
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__MORPHOS__) || defined(__AROS__) || defined(AMIGAOS) || \
|
#ifndef __cplusplus
|
||||||
defined(__amigaos__) || defined(__amigaos4__) ||defined(__amigados__) || \
|
#define LIBXMP_BEGIN_DECLS
|
||||||
defined(AMIGA) || defined(_AMIGA) || defined(__AMIGA__)
|
#define LIBXMP_END_DECLS
|
||||||
#define LIBXMP_AMIGA 1 /* to identify amiga platforms. */
|
#else
|
||||||
|
#define LIBXMP_BEGIN_DECLS extern "C" {
|
||||||
|
#define LIBXMP_END_DECLS }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (defined(__GNUC__) || defined(__clang__)) && defined(XMP_SYM_VISIBILITY)
|
#if defined(_MSC_VER) && !defined(__cplusplus)
|
||||||
#if !defined(_WIN32) && !defined(__ANDROID__) && !defined(__APPLE__) && !defined(LIBXMP_AMIGA) && !defined(__MSDOS__) && !defined(B_BEOS_VERSION) && !defined(__ATHEOS__) && !defined(EMSCRIPTEN) && !defined(__MINT__)
|
#define inline __inline
|
||||||
#define USE_VERSIONED_SYMBOLS
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) ||\
|
||||||
|
(defined(_MSC_VER) && (_MSC_VER >= 1400)) || \
|
||||||
|
(defined(__WATCOMC__) && (__WATCOMC__ >= 1250) && !defined(__cplusplus))
|
||||||
|
#define LIBXMP_RESTRICT __restrict
|
||||||
|
#else
|
||||||
|
#define LIBXMP_RESTRICT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__EMX__)
|
||||||
|
#define XMP_MAXPATH _MAX_PATH
|
||||||
|
#elif defined(PATH_MAX)
|
||||||
|
#define XMP_MAXPATH PATH_MAX
|
||||||
|
#else
|
||||||
|
#define XMP_MAXPATH 1024
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__MORPHOS__) || defined(__AROS__) || defined(__AMIGA__) \
|
||||||
|
|| defined(__amigaos__) || defined(__amigaos4__) || defined(AMIGA)
|
||||||
|
#define LIBXMP_AMIGA 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && defined(__has_include)
|
||||||
|
#if __has_include(<winapifamily.h>)
|
||||||
|
#define HAVE_WINAPIFAMILY_H 1
|
||||||
|
#else
|
||||||
|
#define HAVE_WINAPIFAMILY_H 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if HAVE_WINAPIFAMILY_H
|
||||||
|
#include <winapifamily.h>
|
||||||
|
#define LIBXMP_UWP (!WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP))
|
||||||
|
#else
|
||||||
|
#define LIBXMP_UWP 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_EXTERNAL_VISIBILITY
|
||||||
|
#define LIBXMP_EXPORT_VERSIONED __attribute__((visibility("default"),externally_visible))
|
||||||
|
#else
|
||||||
|
#define LIBXMP_EXPORT_VERSIONED __attribute__((visibility("default")))
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_ATTRIBUTE_SYMVER
|
||||||
|
#define LIBXMP_ATTRIB_SYMVER(_sym) __attribute__((__symver__(_sym)))
|
||||||
|
#else
|
||||||
|
#define LIBXMP_ATTRIB_SYMVER(_sym)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* AmigaOS fixes by Chris Young <cdyoung@ntlworld.com>, Nov 25, 2007
|
/* AmigaOS fixes by Chris Young <cdyoung@ntlworld.com>, Nov 25, 2007
|
||||||
|
@ -33,6 +92,8 @@
|
||||||
# include <SupportDefs.h>
|
# include <SupportDefs.h>
|
||||||
#elif defined __amigaos4__
|
#elif defined __amigaos4__
|
||||||
# include <exec/types.h>
|
# include <exec/types.h>
|
||||||
|
#elif defined _arch_dreamcast /* KallistiOS */
|
||||||
|
# include <arch/types.h>
|
||||||
#else
|
#else
|
||||||
typedef signed char int8;
|
typedef signed char int8;
|
||||||
typedef signed short int int16;
|
typedef signed short int int16;
|
||||||
|
@ -42,14 +103,18 @@ typedef unsigned short int uint16;
|
||||||
typedef unsigned int uint32;
|
typedef unsigned int uint32;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _MSC_VER /* MSVC++6.0 has no long long */
|
#ifdef _MSC_VER /* MSVC6 has no long long */
|
||||||
typedef signed __int64 int64;
|
typedef signed __int64 int64;
|
||||||
typedef unsigned __int64 uint64;
|
typedef unsigned __int64 uint64;
|
||||||
#elif !defined B_BEOS_VERSION /* BeOS has its own int64 definition */
|
#elif !(defined(B_BEOS_VERSION) || defined(__amigaos4__))
|
||||||
typedef unsigned long long uint64;
|
typedef unsigned long long uint64;
|
||||||
typedef signed long long int64;
|
typedef signed long long int64;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(disable:4100) /* unreferenced formal parameter */
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
#define LIBXMP_PAULA_SIMULATOR
|
#define LIBXMP_PAULA_SIMULATOR
|
||||||
#endif
|
#endif
|
||||||
|
@ -72,6 +137,11 @@ typedef signed long long int64;
|
||||||
#define RESET_FLAG(a,b) ((a)&=~(b))
|
#define RESET_FLAG(a,b) ((a)&=~(b))
|
||||||
#define TEST_FLAG(a,b) !!((a)&(b))
|
#define TEST_FLAG(a,b) !!((a)&(b))
|
||||||
|
|
||||||
|
/* libxmp_get_filetype() return values */
|
||||||
|
#define XMP_FILETYPE_NONE 0
|
||||||
|
#define XMP_FILETYPE_DIR (1 << 0)
|
||||||
|
#define XMP_FILETYPE_FILE (1 << 1)
|
||||||
|
|
||||||
#define CLAMP(x,a,b) do { \
|
#define CLAMP(x,a,b) do { \
|
||||||
if ((x) < (a)) (x) = (a); \
|
if ((x) < (a)) (x) = (a); \
|
||||||
else if ((x) > (b)) (x) = (b); \
|
else if ((x) > (b)) (x) = (b); \
|
||||||
|
@ -87,20 +157,18 @@ typedef signed long long int64;
|
||||||
#define D_CRIT " Error: "
|
#define D_CRIT " Error: "
|
||||||
#define D_WARN "Warning: "
|
#define D_WARN "Warning: "
|
||||||
#define D_INFO " Info: "
|
#define D_INFO " Info: "
|
||||||
#ifndef CLIB_DECL
|
|
||||||
#define CLIB_DECL
|
|
||||||
#endif
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
#ifndef ATTR_PRINTF
|
#define D_ libxmp_msvc_dbgprint /* in win32.c */
|
||||||
#define ATTR_PRINTF(x,y)
|
void libxmp_msvc_dbgprint(const char *text, ...);
|
||||||
#endif
|
|
||||||
void CLIB_DECL D_(const char *text, ...) ATTR_PRINTF(1,2);
|
|
||||||
#else
|
#else
|
||||||
// VS prior to VC7.1 does not support variadic macros. VC8.0 does not optimize unused parameters passing
|
/* VS prior to VC7.1 does not support variadic macros.
|
||||||
|
* VC8.0 does not optimize unused parameters passing. */
|
||||||
#if _MSC_VER < 1400
|
#if _MSC_VER < 1400
|
||||||
void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); }
|
static void __inline D_(const char *text, ...) {
|
||||||
|
do { } while (0);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
#define D_(args, ...) do {} while (0)
|
#define D_(...) do {} while (0)
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -111,21 +179,8 @@ void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); }
|
||||||
#define D_CRIT " Error: "
|
#define D_CRIT " Error: "
|
||||||
#define D_WARN "Warning: "
|
#define D_WARN "Warning: "
|
||||||
#define D_INFO " Info: "
|
#define D_INFO " Info: "
|
||||||
#define D_(args...) do { \
|
|
||||||
__android_log_print(ANDROID_LOG_DEBUG, "libxmp", args); \
|
|
||||||
} while (0)
|
|
||||||
#else
|
|
||||||
#define D_(args...) do {} while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#elif defined(__WATCOMC__)
|
|
||||||
#ifdef DEBUG
|
|
||||||
#define D_INFO "\x1b[33m"
|
|
||||||
#define D_CRIT "\x1b[31m"
|
|
||||||
#define D_WARN "\x1b[36m"
|
|
||||||
#define D_(...) do { \
|
#define D_(...) do { \
|
||||||
printf("\x1b[33m%s \x1b[37m[%s:%d] " D_INFO, __FUNCTION__, \
|
__android_log_print(ANDROID_LOG_DEBUG, "libxmp", __VA_ARGS__); \
|
||||||
__FILE__, __LINE__); printf (__VA_ARGS__); printf ("\x1b[0m\n"); \
|
|
||||||
} while (0)
|
} while (0)
|
||||||
#else
|
#else
|
||||||
#define D_(...) do {} while (0)
|
#define D_(...) do {} while (0)
|
||||||
|
@ -134,15 +189,20 @@ void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); }
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
||||||
|
#define LIBXMP_FUNC __func__
|
||||||
|
#else
|
||||||
|
#define LIBXMP_FUNC __FUNCTION__
|
||||||
|
#endif
|
||||||
#define D_INFO "\x1b[33m"
|
#define D_INFO "\x1b[33m"
|
||||||
#define D_CRIT "\x1b[31m"
|
#define D_CRIT "\x1b[31m"
|
||||||
#define D_WARN "\x1b[36m"
|
#define D_WARN "\x1b[36m"
|
||||||
#define D_(args...) do { \
|
#define D_(...) do { \
|
||||||
printf("\x1b[33m%s \x1b[37m[%s:%d] " D_INFO, __FUNCTION__, \
|
printf("\x1b[33m%s \x1b[37m[%s:%d] " D_INFO, LIBXMP_FUNC, \
|
||||||
__FILE__, __LINE__); printf (args); printf ("\x1b[0m\n"); \
|
__FILE__, __LINE__); printf (__VA_ARGS__); printf ("\x1b[0m\n"); \
|
||||||
} while (0)
|
} while (0)
|
||||||
#else
|
#else
|
||||||
#define D_(args...) do {} while (0)
|
#define D_(...) do {} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* !_MSC_VER */
|
#endif /* !_MSC_VER */
|
||||||
|
@ -151,7 +211,6 @@ void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); }
|
||||||
#define dup _dup
|
#define dup _dup
|
||||||
#define fileno _fileno
|
#define fileno _fileno
|
||||||
#define strnicmp _strnicmp
|
#define strnicmp _strnicmp
|
||||||
#define strdup _strdup
|
|
||||||
#define fdopen _fdopen
|
#define fdopen _fdopen
|
||||||
#define open _open
|
#define open _open
|
||||||
#define close _close
|
#define close _close
|
||||||
|
@ -171,13 +230,33 @@ void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); }
|
||||||
#undef USE_LIBXMP_SNPRINTF
|
#undef USE_LIBXMP_SNPRINTF
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LIBXMP_SNPRINTF
|
#ifdef USE_LIBXMP_SNPRINTF
|
||||||
int libxmp_vsnprintf(char *, size_t, const char *, va_list);
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
int libxmp_snprintf (char *, size_t, const char *, ...);
|
#define LIBXMP_ATTRIB_PRINTF(x,y) __attribute__((__format__(__printf__,x,y)))
|
||||||
|
#else
|
||||||
|
#define LIBXMP_ATTRIB_PRINTF(x,y)
|
||||||
|
#endif
|
||||||
|
int libxmp_vsnprintf(char *, size_t, const char *, va_list) LIBXMP_ATTRIB_PRINTF(3,0);
|
||||||
|
int libxmp_snprintf (char *, size_t, const char *, ...) LIBXMP_ATTRIB_PRINTF(3,4);
|
||||||
#define snprintf libxmp_snprintf
|
#define snprintf libxmp_snprintf
|
||||||
#define vsnprintf libxmp_vsnprintf
|
#define vsnprintf libxmp_vsnprintf
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Output file size limit for files unpacked from unarchivers into RAM. Most
|
||||||
|
* general archive compression formats can't nicely bound the output size
|
||||||
|
* from their input filesize, and a cap is needed for a few reasons:
|
||||||
|
*
|
||||||
|
* - Linux is too dumb for its own good and its malloc/realloc will return
|
||||||
|
* pointers to RAM that doesn't exist instead of NULL. When these are used,
|
||||||
|
* it will kill the application instead of allowing it to fail gracefully.
|
||||||
|
* - libFuzzer and the clang sanitizers have malloc/realloc interceptors that
|
||||||
|
* terminate with an error instead of returning NULL.
|
||||||
|
*
|
||||||
|
* Depackers that have better ways of bounding the output size can ignore this.
|
||||||
|
* This value is fairly arbitrary and can be changed if needed.
|
||||||
|
*/
|
||||||
|
#define LIBXMP_DEPACK_LIMIT (512 << 20)
|
||||||
|
|
||||||
/* Quirks */
|
/* Quirks */
|
||||||
#define QUIRK_S3MLOOP (1 << 0) /* S3M loop mode */
|
#define QUIRK_S3MLOOP (1 << 0) /* S3M loop mode */
|
||||||
#define QUIRK_ENVFADE (1 << 1) /* Fade at end of envelope */
|
#define QUIRK_ENVFADE (1 << 1) /* Fade at end of envelope */
|
||||||
|
@ -191,6 +270,7 @@ int libxmp_snprintf (char *, size_t, const char *, ...);
|
||||||
#define QUIRK_UNISLD (1 << 10) /* Unified pitch slide/portamento */
|
#define QUIRK_UNISLD (1 << 10) /* Unified pitch slide/portamento */
|
||||||
#define QUIRK_ITVPOR (1 << 11) /* Disable fine bends in IT vol fx */
|
#define QUIRK_ITVPOR (1 << 11) /* Disable fine bends in IT vol fx */
|
||||||
#define QUIRK_FTMOD (1 << 12) /* Flag for multichannel mods */
|
#define QUIRK_FTMOD (1 << 12) /* Flag for multichannel mods */
|
||||||
|
#define QUIRK_INVLOOP (1 << 13) /* Enable invert loop */
|
||||||
/*#define QUIRK_MODRNG (1 << 13)*/ /* Limit periods to MOD range */
|
/*#define QUIRK_MODRNG (1 << 13)*/ /* Limit periods to MOD range */
|
||||||
#define QUIRK_INSVOL (1 << 14) /* Use instrument volume */
|
#define QUIRK_INSVOL (1 << 14) /* Use instrument volume */
|
||||||
#define QUIRK_VIRTUAL (1 << 15) /* Enable virtual channels */
|
#define QUIRK_VIRTUAL (1 << 15) /* Enable virtual channels */
|
||||||
|
@ -232,8 +312,9 @@ int libxmp_snprintf (char *, size_t, const char *, ...);
|
||||||
/* Time factor */
|
/* Time factor */
|
||||||
#define DEFAULT_TIME_FACTOR 10.0
|
#define DEFAULT_TIME_FACTOR 10.0
|
||||||
#define MED_TIME_FACTOR 2.64
|
#define MED_TIME_FACTOR 2.64
|
||||||
|
#define FAR_TIME_FACTOR 4.01373 /* See far_extras.c */
|
||||||
|
|
||||||
#define MAX_SEQUENCES 16
|
#define MAX_SEQUENCES 255
|
||||||
#define MAX_SAMPLE_SIZE 0x10000000
|
#define MAX_SAMPLE_SIZE 0x10000000
|
||||||
#define MAX_SAMPLES 1024
|
#define MAX_SAMPLES 1024
|
||||||
#define MAX_INSTRUMENTS 255
|
#define MAX_INSTRUMENTS 255
|
||||||
|
@ -262,7 +343,6 @@ struct ord_data {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Context */
|
/* Context */
|
||||||
|
|
||||||
struct smix_data {
|
struct smix_data {
|
||||||
|
@ -276,6 +356,17 @@ struct smix_data {
|
||||||
/* This will be added to the sample structure in the next API revision */
|
/* This will be added to the sample structure in the next API revision */
|
||||||
struct extra_sample_data {
|
struct extra_sample_data {
|
||||||
double c5spd;
|
double c5spd;
|
||||||
|
int sus;
|
||||||
|
int sue;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct midi_macro {
|
||||||
|
char data[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct midi_macro_data {
|
||||||
|
struct midi_macro param[16];
|
||||||
|
struct midi_macro fixed[128];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct module_data {
|
struct module_data {
|
||||||
|
@ -293,7 +384,9 @@ struct module_data {
|
||||||
int volbase; /* Volume base */
|
int volbase; /* Volume base */
|
||||||
int gvolbase; /* Global volume base */
|
int gvolbase; /* Global volume base */
|
||||||
int gvol; /* Global volume */
|
int gvol; /* Global volume */
|
||||||
int *vol_table; /* Volume translation table */
|
int mvolbase; /* Mix volume base (S3M/IT) */
|
||||||
|
int mvol; /* Mix volume (S3M/IT) */
|
||||||
|
const int *vol_table; /* Volume translation table */
|
||||||
int quirk; /* player quirks */
|
int quirk; /* player quirks */
|
||||||
#define READ_EVENT_MOD 0
|
#define READ_EVENT_MOD 0
|
||||||
#define READ_EVENT_FT2 1
|
#define READ_EVENT_FT2 1
|
||||||
|
@ -315,9 +408,8 @@ struct module_data {
|
||||||
void *extra; /* format-specific extra fields */
|
void *extra; /* format-specific extra fields */
|
||||||
uint8 **scan_cnt; /* scan counters */
|
uint8 **scan_cnt; /* scan counters */
|
||||||
struct extra_sample_data *xtra;
|
struct extra_sample_data *xtra;
|
||||||
#ifndef LIBXMP_CORE_DISABLE_IT
|
struct midi_macro_data *midi;
|
||||||
struct xmp_sample *xsmp; /* sustain loop samples */
|
int compare_vblank;
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -332,6 +424,9 @@ struct flow_control {
|
||||||
int delay;
|
int delay;
|
||||||
int jumpline;
|
int jumpline;
|
||||||
int loop_chn;
|
int loop_chn;
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
int jump_in_pat;
|
||||||
|
#endif
|
||||||
|
|
||||||
struct pattern_loop *loop;
|
struct pattern_loop *loop;
|
||||||
|
|
||||||
|
@ -348,6 +443,13 @@ struct virt_channel {
|
||||||
int map;
|
int map;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct scan_data {
|
||||||
|
int time; /* replay time in ms */
|
||||||
|
int row;
|
||||||
|
int ord;
|
||||||
|
int num;
|
||||||
|
};
|
||||||
|
|
||||||
struct player_data {
|
struct player_data {
|
||||||
int ord;
|
int ord;
|
||||||
int pos;
|
int pos;
|
||||||
|
@ -372,12 +474,7 @@ struct player_data {
|
||||||
|
|
||||||
struct flow_control flow;
|
struct flow_control flow;
|
||||||
|
|
||||||
struct {
|
struct scan_data *scan;
|
||||||
int time; /* replay time in ms */
|
|
||||||
int ord;
|
|
||||||
int row;
|
|
||||||
int num;
|
|
||||||
} scan[MAX_SEQUENCES];
|
|
||||||
|
|
||||||
struct channel_data *xc_data;
|
struct channel_data *xc_data;
|
||||||
|
|
||||||
|
@ -416,12 +513,13 @@ struct mixer_data {
|
||||||
int mix; /* percentage of channel separation */
|
int mix; /* percentage of channel separation */
|
||||||
int interp; /* interpolation type */
|
int interp; /* interpolation type */
|
||||||
int dsp; /* dsp effect flags */
|
int dsp; /* dsp effect flags */
|
||||||
char* buffer; /* output buffer */
|
char *buffer; /* output buffer */
|
||||||
int32* buf32; /* temporary buffer for 32 bit samples */
|
int32 *buf32; /* temporary buffer for 32 bit samples */
|
||||||
int numvoc; /* default softmixer voices number */
|
int numvoc; /* default softmixer voices number */
|
||||||
int ticksize;
|
int ticksize;
|
||||||
int dtright; /* anticlick control, right channel */
|
int dtright; /* anticlick control, right channel */
|
||||||
int dtleft; /* anticlick control, left channel */
|
int dtleft; /* anticlick control, left channel */
|
||||||
|
int bidir_adjust; /* adjustment for IT bidirectional loops */
|
||||||
double pbase; /* period base */
|
double pbase; /* period base */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -442,6 +540,7 @@ void libxmp_free_scan (struct context_data *);
|
||||||
int libxmp_scan_sequences (struct context_data *);
|
int libxmp_scan_sequences (struct context_data *);
|
||||||
int libxmp_get_sequence (struct context_data *, int);
|
int libxmp_get_sequence (struct context_data *, int);
|
||||||
int libxmp_set_player_mode (struct context_data *);
|
int libxmp_set_player_mode (struct context_data *);
|
||||||
|
void libxmp_reset_flow (struct context_data *);
|
||||||
|
|
||||||
int8 read8s (FILE *, int *err);
|
int8 read8s (FILE *, int *err);
|
||||||
uint8 read8 (FILE *, int *err);
|
uint8 read8 (FILE *, int *err);
|
||||||
|
@ -458,7 +557,6 @@ void write16l (FILE *, uint16);
|
||||||
void write16b (FILE *, uint16);
|
void write16b (FILE *, uint16);
|
||||||
void write32l (FILE *, uint32);
|
void write32l (FILE *, uint32);
|
||||||
void write32b (FILE *, uint32);
|
void write32b (FILE *, uint32);
|
||||||
int move_data (FILE *, FILE *, int);
|
|
||||||
|
|
||||||
uint16 readmem16l (const uint8 *);
|
uint16 readmem16l (const uint8 *);
|
||||||
uint16 readmem16b (const uint8 *);
|
uint16 readmem16b (const uint8 *);
|
||||||
|
@ -470,4 +568,7 @@ uint32 readmem32b (const uint8 *);
|
||||||
struct xmp_instrument *libxmp_get_instrument(struct context_data *, int);
|
struct xmp_instrument *libxmp_get_instrument(struct context_data *, int);
|
||||||
struct xmp_sample *libxmp_get_sample(struct context_data *, int);
|
struct xmp_sample *libxmp_get_sample(struct context_data *, int);
|
||||||
|
|
||||||
|
char *libxmp_strdup(const char *);
|
||||||
|
int libxmp_get_filetype (const char *);
|
||||||
|
|
||||||
#endif /* LIBXMP_COMMON_H */
|
#endif /* LIBXMP_COMMON_H */
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Extended Module Player
|
/* Extended Module Player
|
||||||
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
|
* Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -31,7 +31,7 @@ xmp_context xmp_create_context(void)
|
||||||
{
|
{
|
||||||
struct context_data *ctx;
|
struct context_data *ctx;
|
||||||
|
|
||||||
ctx = calloc(1, sizeof(struct context_data));
|
ctx = (struct context_data *) calloc(1, sizeof(struct context_data));
|
||||||
if (ctx == NULL) {
|
if (ctx == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -117,13 +117,9 @@ static void set_position(struct context_data *ctx, int pos, int dir)
|
||||||
} else {
|
} else {
|
||||||
p->pos = pos;
|
p->pos = pos;
|
||||||
}
|
}
|
||||||
f->jumpline = 0;
|
/* Clear flow vars to prevent old pattern jumps and
|
||||||
f->jump = -1;
|
* other junk from executing in the new position. */
|
||||||
f->pbreak = 0;
|
libxmp_reset_flow(ctx);
|
||||||
f->loop_chn = 0;
|
|
||||||
f->delay = 0;
|
|
||||||
f->rowdelay = 0;
|
|
||||||
f->rowdelay_set = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,16 +181,17 @@ int xmp_set_row(xmp_context opaque, int row)
|
||||||
struct xmp_module *mod = &m->mod;
|
struct xmp_module *mod = &m->mod;
|
||||||
struct flow_control *f = &p->flow;
|
struct flow_control *f = &p->flow;
|
||||||
int pos = p->pos;
|
int pos = p->pos;
|
||||||
int pattern = mod->xxo[pos];
|
int pattern;
|
||||||
|
|
||||||
if (pos < 0 || pos >= mod->len) {
|
if (pos < 0 || pos >= mod->len) {
|
||||||
pos = 0;
|
pos = 0;
|
||||||
}
|
}
|
||||||
|
pattern = mod->xxo[pos];
|
||||||
|
|
||||||
if (ctx->state < XMP_STATE_PLAYING)
|
if (ctx->state < XMP_STATE_PLAYING)
|
||||||
return -XMP_ERROR_STATE;
|
return -XMP_ERROR_STATE;
|
||||||
|
|
||||||
if (row >= mod->xxp[pattern]->rows)
|
if (pattern >= mod->pat || row >= mod->xxp[pattern]->rows)
|
||||||
return -XMP_ERROR_INVALID;
|
return -XMP_ERROR_INVALID;
|
||||||
|
|
||||||
/* See set_position. */
|
/* See set_position. */
|
||||||
|
@ -309,18 +306,22 @@ int xmp_channel_vol(xmp_context opaque, int chn, int vol)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_VERSIONED_SYMBOLS
|
#ifdef USE_VERSIONED_SYMBOLS
|
||||||
LIBXMP_EXPORT extern int xmp_set_player_v40__(xmp_context, int, int);
|
LIBXMP_BEGIN_DECLS /* no name-mangling */
|
||||||
LIBXMP_EXPORT extern int xmp_set_player_v41__(xmp_context, int, int)
|
LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v40__(xmp_context, int, int) LIBXMP_ATTRIB_SYMVER("xmp_set_player@XMP_4.0");
|
||||||
__attribute__((alias("xmp_set_player_v40__")));
|
LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v41__(xmp_context, int, int)
|
||||||
LIBXMP_EXPORT extern int xmp_set_player_v43__(xmp_context, int, int)
|
__attribute__((alias("xmp_set_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_set_player@XMP_4.1");
|
||||||
__attribute__((alias("xmp_set_player_v40__")));
|
LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v43__(xmp_context, int, int)
|
||||||
LIBXMP_EXPORT extern int xmp_set_player_v44__(xmp_context, int, int)
|
__attribute__((alias("xmp_set_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_set_player@XMP_4.3");
|
||||||
__attribute__((alias("xmp_set_player_v40__")));
|
LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v44__(xmp_context, int, int)
|
||||||
|
__attribute__((alias("xmp_set_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_set_player@@XMP_4.4");
|
||||||
|
|
||||||
|
#ifndef HAVE_ATTRIBUTE_SYMVER
|
||||||
asm(".symver xmp_set_player_v40__, xmp_set_player@XMP_4.0");
|
asm(".symver xmp_set_player_v40__, xmp_set_player@XMP_4.0");
|
||||||
asm(".symver xmp_set_player_v41__, xmp_set_player@XMP_4.1");
|
asm(".symver xmp_set_player_v41__, xmp_set_player@XMP_4.1");
|
||||||
asm(".symver xmp_set_player_v43__, xmp_set_player@XMP_4.3");
|
asm(".symver xmp_set_player_v43__, xmp_set_player@XMP_4.3");
|
||||||
asm(".symver xmp_set_player_v44__, xmp_set_player@@XMP_4.4");
|
asm(".symver xmp_set_player_v44__, xmp_set_player@@XMP_4.4");
|
||||||
|
#endif
|
||||||
|
LIBXMP_END_DECLS
|
||||||
|
|
||||||
#define xmp_set_player__ xmp_set_player_v40__
|
#define xmp_set_player__ xmp_set_player_v40__
|
||||||
#else
|
#else
|
||||||
|
@ -427,21 +428,25 @@ int xmp_set_player__(xmp_context opaque, int parm, int val)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_VERSIONED_SYMBOLS
|
#ifdef USE_VERSIONED_SYMBOLS
|
||||||
LIBXMP_EXPORT extern int xmp_get_player_v40__(xmp_context, int);
|
LIBXMP_BEGIN_DECLS /* no name-mangling */
|
||||||
LIBXMP_EXPORT extern int xmp_get_player_v41__(xmp_context, int)
|
LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v40__(xmp_context, int) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.0");
|
||||||
__attribute__((alias("xmp_get_player_v40__")));
|
LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v41__(xmp_context, int)
|
||||||
LIBXMP_EXPORT extern int xmp_get_player_v42__(xmp_context, int)
|
__attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.1");
|
||||||
__attribute__((alias("xmp_get_player_v40__")));
|
LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v42__(xmp_context, int)
|
||||||
LIBXMP_EXPORT extern int xmp_get_player_v43__(xmp_context, int)
|
__attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.2");
|
||||||
__attribute__((alias("xmp_get_player_v40__")));
|
LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v43__(xmp_context, int)
|
||||||
LIBXMP_EXPORT extern int xmp_get_player_v44__(xmp_context, int)
|
__attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.3");
|
||||||
__attribute__((alias("xmp_get_player_v40__")));
|
LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v44__(xmp_context, int)
|
||||||
|
__attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@@XMP_4.4");
|
||||||
|
|
||||||
|
#ifndef HAVE_ATTRIBUTE_SYMVER
|
||||||
asm(".symver xmp_get_player_v40__, xmp_get_player@XMP_4.0");
|
asm(".symver xmp_get_player_v40__, xmp_get_player@XMP_4.0");
|
||||||
asm(".symver xmp_get_player_v41__, xmp_get_player@XMP_4.1");
|
asm(".symver xmp_get_player_v41__, xmp_get_player@XMP_4.1");
|
||||||
asm(".symver xmp_get_player_v42__, xmp_get_player@XMP_4.2");
|
asm(".symver xmp_get_player_v42__, xmp_get_player@XMP_4.2");
|
||||||
asm(".symver xmp_get_player_v43__, xmp_get_player@XMP_4.3");
|
asm(".symver xmp_get_player_v43__, xmp_get_player@XMP_4.3");
|
||||||
asm(".symver xmp_get_player_v44__, xmp_get_player@@XMP_4.4");
|
asm(".symver xmp_get_player_v44__, xmp_get_player@@XMP_4.4");
|
||||||
|
#endif
|
||||||
|
LIBXMP_END_DECLS
|
||||||
|
|
||||||
#define xmp_get_player__ xmp_get_player_v40__
|
#define xmp_get_player__ xmp_get_player_v40__
|
||||||
#else
|
#else
|
||||||
|
@ -554,7 +559,7 @@ int xmp_set_instrument_path(xmp_context opaque, const char *path)
|
||||||
if (m->instrument_path != NULL)
|
if (m->instrument_path != NULL)
|
||||||
free(m->instrument_path);
|
free(m->instrument_path);
|
||||||
|
|
||||||
m->instrument_path = strdup(path);
|
m->instrument_path = libxmp_strdup(path);
|
||||||
if (m->instrument_path == NULL) {
|
if (m->instrument_path == NULL) {
|
||||||
return -XMP_ERROR_SYSTEM;
|
return -XMP_ERROR_SYSTEM;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Extended Module Player
|
/* Extended Module Player
|
||||||
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
|
* Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -33,7 +33,6 @@
|
||||||
if (err != NULL) *err = (x); \
|
if (err != NULL) *err = (x); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
uint8 read8(FILE *f, int *err)
|
uint8 read8(FILE *f, int *err)
|
||||||
{
|
{
|
||||||
int a;
|
int a;
|
||||||
|
@ -103,7 +102,7 @@ uint32 read24l(FILE *f, int *err)
|
||||||
|
|
||||||
error:
|
error:
|
||||||
set_error(ferror(f) ? errno : EOF);
|
set_error(ferror(f) ? errno : EOF);
|
||||||
return 0xffffff;
|
return 0xffffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 read24b(FILE *f, int *err)
|
uint32 read24b(FILE *f, int *err)
|
||||||
|
@ -119,7 +118,7 @@ uint32 read24b(FILE *f, int *err)
|
||||||
|
|
||||||
error:
|
error:
|
||||||
set_error(ferror(f) ? errno : EOF);
|
set_error(ferror(f) ? errno : EOF);
|
||||||
return 0xffffff;
|
return 0xffffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 read32l(FILE *f, int *err)
|
uint32 read32l(FILE *f, int *err)
|
||||||
|
@ -252,18 +251,4 @@ void write32b(FILE *f, uint32 w)
|
||||||
write8(f, w & 0x000000ff);
|
write8(f, w & 0x000000ff);
|
||||||
}
|
}
|
||||||
|
|
||||||
int move_data(FILE *out, FILE *in, int len)
|
|
||||||
{
|
|
||||||
uint8 buf[1024];
|
|
||||||
int l;
|
|
||||||
|
|
||||||
do {
|
|
||||||
l = fread(buf, 1, len > 1024 ? 1024 : len, in);
|
|
||||||
fwrite(buf, 1, l, out);
|
|
||||||
len -= l;
|
|
||||||
} while (l > 0 && len > 0);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Extended Module Player
|
/* Extended Module Player
|
||||||
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
|
* Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -72,8 +72,13 @@ static void do_toneporta(struct context_data *ctx,
|
||||||
{
|
{
|
||||||
struct module_data *m = &ctx->m;
|
struct module_data *m = &ctx->m;
|
||||||
struct xmp_instrument *instrument = &m->mod.xxi[xc->ins];
|
struct xmp_instrument *instrument = &m->mod.xxi[xc->ins];
|
||||||
int mapped = instrument->map[xc->key].ins;
|
|
||||||
struct xmp_subinstrument *sub;
|
struct xmp_subinstrument *sub;
|
||||||
|
int mapped_xpo = 0;
|
||||||
|
int mapped = 0;
|
||||||
|
|
||||||
|
if (IS_VALID_NOTE(xc->key)) {
|
||||||
|
mapped = instrument->map[xc->key].ins;
|
||||||
|
}
|
||||||
|
|
||||||
if (mapped >= instrument->nsm) {
|
if (mapped >= instrument->nsm) {
|
||||||
mapped = 0;
|
mapped = 0;
|
||||||
|
@ -81,11 +86,13 @@ static void do_toneporta(struct context_data *ctx,
|
||||||
|
|
||||||
sub = &instrument->sub[mapped];
|
sub = &instrument->sub[mapped];
|
||||||
|
|
||||||
if (note >= 1 && note <= 0x80 && (uint32)xc->ins < m->mod.ins) {
|
if (IS_VALID_NOTE(note - 1) && (uint32)xc->ins < m->mod.ins) {
|
||||||
note--;
|
note--;
|
||||||
|
if (IS_VALID_NOTE(xc->key_porta)) {
|
||||||
|
mapped_xpo = instrument->map[xc->key_porta].xpo;
|
||||||
|
}
|
||||||
xc->porta.target = libxmp_note_to_period(ctx, note + sub->xpo +
|
xc->porta.target = libxmp_note_to_period(ctx, note + sub->xpo +
|
||||||
instrument->map[xc->key_porta].xpo, xc->finetune,
|
mapped_xpo, xc->finetune, xc->per_adj);
|
||||||
xc->per_adj);
|
|
||||||
}
|
}
|
||||||
xc->porta.dir = xc->period < xc->porta.target ? 1 : -1;
|
xc->porta.dir = xc->period < xc->porta.target ? 1 : -1;
|
||||||
}
|
}
|
||||||
|
@ -442,6 +449,7 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch
|
||||||
xc->retrig.val = fxp;
|
xc->retrig.val = fxp;
|
||||||
xc->retrig.count = LSN(xc->retrig.val) + 1;
|
xc->retrig.count = LSN(xc->retrig.val) + 1;
|
||||||
xc->retrig.type = 0;
|
xc->retrig.type = 0;
|
||||||
|
xc->retrig.limit = 0;
|
||||||
break;
|
break;
|
||||||
case EX_F_VSLIDE_UP: /* Fine volume slide up */
|
case EX_F_VSLIDE_UP: /* Fine volume slide up */
|
||||||
EFFECT_MEMORY(fxp, xc->fine_vol.up_memory);
|
EFFECT_MEMORY(fxp, xc->fine_vol.up_memory);
|
||||||
|
@ -692,6 +700,7 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch
|
||||||
if (note) {
|
if (note) {
|
||||||
xc->retrig.count = LSN(xc->retrig.val) + 1;
|
xc->retrig.count = LSN(xc->retrig.val) + 1;
|
||||||
}
|
}
|
||||||
|
xc->retrig.limit = 0;
|
||||||
SET(RETRIG);
|
SET(RETRIG);
|
||||||
break;
|
break;
|
||||||
case FX_TREMOR: /* Tremor */
|
case FX_TREMOR: /* Tremor */
|
||||||
|
@ -725,6 +734,9 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch
|
||||||
case FX_SURROUND:
|
case FX_SURROUND:
|
||||||
xc->pan.surround = fxp;
|
xc->pan.surround = fxp;
|
||||||
break;
|
break;
|
||||||
|
case FX_REVERSE: /* Play forward/backward */
|
||||||
|
libxmp_virt_reverse(ctx, chn, fxp);
|
||||||
|
break;
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_DISABLE_IT
|
#ifndef LIBXMP_CORE_DISABLE_IT
|
||||||
case FX_TRK_VOL: /* Track volume setting */
|
case FX_TRK_VOL: /* Track volume setting */
|
||||||
|
@ -818,13 +830,26 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case FX_FLT_CUTOFF:
|
case FX_FLT_CUTOFF:
|
||||||
if (fxp < 0xfe || xc->filter.resonance > 0) {
|
xc->filter.cutoff = fxp;
|
||||||
xc->filter.cutoff = fxp;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case FX_FLT_RESN:
|
case FX_FLT_RESN:
|
||||||
xc->filter.resonance = fxp;
|
xc->filter.resonance = fxp;
|
||||||
break;
|
break;
|
||||||
|
case FX_MACRO_SET:
|
||||||
|
xc->macro.active = LSN(fxp);
|
||||||
|
break;
|
||||||
|
case FX_MACRO:
|
||||||
|
SET(MIDI_MACRO);
|
||||||
|
xc->macro.val = fxp;
|
||||||
|
xc->macro.slide = 0;
|
||||||
|
break;
|
||||||
|
case FX_MACROSMOOTH:
|
||||||
|
if (ctx->p.speed && xc->macro.val < 0x80) {
|
||||||
|
SET(MIDI_MACRO);
|
||||||
|
xc->macro.target = fxp;
|
||||||
|
xc->macro.slide = ((float)fxp - xc->macro.val) / ctx->p.speed;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case FX_PANBRELLO: /* Panbrello */
|
case FX_PANBRELLO: /* Panbrello */
|
||||||
SET(PANBRELLO);
|
SET(PANBRELLO);
|
||||||
SET_LFO_NOTZERO(&xc->panbrello.lfo, LSN(fxp) << 4, MSN(fxp));
|
SET_LFO_NOTZERO(&xc->panbrello.lfo, LSN(fxp) << 4, MSN(fxp));
|
||||||
|
@ -939,6 +964,7 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch
|
||||||
xc->retrig.val = MSN(fxp);
|
xc->retrig.val = MSN(fxp);
|
||||||
xc->retrig.count = MSN(fxp) + 1;
|
xc->retrig.count = MSN(fxp) + 1;
|
||||||
xc->retrig.type = 0;
|
xc->retrig.type = 0;
|
||||||
|
xc->retrig.limit = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fxt == FX_NSLIDE_UP || fxt == FX_NSLIDE_R_UP)
|
if (fxt == FX_NSLIDE_UP || fxt == FX_NSLIDE_R_UP)
|
||||||
|
@ -1061,6 +1087,41 @@ void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int ch
|
||||||
}
|
}
|
||||||
SET_LFO_NOTZERO(&xc->vibrato.lfo, 669, 1);
|
SET_LFO_NOTZERO(&xc->vibrato.lfo, 669, 1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/* ULT effects */
|
||||||
|
|
||||||
|
case FX_ULT_TPORTA: /* ULT tone portamento */
|
||||||
|
/* Like normal persistent tone portamento, except:
|
||||||
|
*
|
||||||
|
* 1) Despite the documentation claiming 300 cancels tone
|
||||||
|
* portamento, it actually reuses the last parameter.
|
||||||
|
*
|
||||||
|
* 2) A 3xx without a note will reuse the last target note.
|
||||||
|
*/
|
||||||
|
if (!IS_VALID_INSTRUMENT(xc->ins))
|
||||||
|
break;
|
||||||
|
SET_PER(TONEPORTA);
|
||||||
|
EFFECT_MEMORY(fxp, xc->porta.memory);
|
||||||
|
EFFECT_MEMORY(note, xc->porta.note_memory);
|
||||||
|
do_toneporta(ctx, xc, note);
|
||||||
|
xc->porta.slide = fxp;
|
||||||
|
if (fxp == 0)
|
||||||
|
RESET_PER(TONEPORTA);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Archimedes (!Tracker, Digital Symphony, et al.) effects */
|
||||||
|
|
||||||
|
case FX_LINE_JUMP: /* !Tracker and Digital Symphony "Line Jump" */
|
||||||
|
/* Jump to a line within the current order. In Digital Symphony
|
||||||
|
* this can be combined with position jump (like pattern break)
|
||||||
|
* and overrides the pattern break line in lower channels. */
|
||||||
|
if (p->flow.pbreak == 0) {
|
||||||
|
p->flow.pbreak = 1;
|
||||||
|
p->flow.jump = p->ord;
|
||||||
|
}
|
||||||
|
p->flow.jumpline = fxp;
|
||||||
|
p->flow.jump_in_pat = p->ord;
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
#define FX_F_NSLIDE_DN 0x75
|
#define FX_F_NSLIDE_DN 0x75
|
||||||
#define FX_F_NSLIDE_UP 0x76
|
#define FX_F_NSLIDE_UP 0x76
|
||||||
|
|
||||||
/* Persistent effects -- for FNK and FAR */
|
/* Persistent effects -- for FNK */
|
||||||
#define FX_PER_PORTA_DN 0x78
|
#define FX_PER_PORTA_DN 0x78
|
||||||
#define FX_PER_PORTA_UP 0x79
|
#define FX_PER_PORTA_UP 0x79
|
||||||
#define FX_PER_TPORTA 0x7a
|
#define FX_PER_TPORTA 0x7a
|
||||||
|
@ -75,6 +75,21 @@
|
||||||
#define FX_669_TPORTA 0x62
|
#define FX_669_TPORTA 0x62
|
||||||
#define FX_669_FINETUNE 0x63
|
#define FX_669_FINETUNE 0x63
|
||||||
#define FX_669_VIBRATO 0x64
|
#define FX_669_VIBRATO 0x64
|
||||||
|
|
||||||
|
/* FAR effects */
|
||||||
|
#define FX_FAR_PORTA_UP 0x65 /* FAR pitch offset up */
|
||||||
|
#define FX_FAR_PORTA_DN 0x66 /* FAR pitch offset down */
|
||||||
|
#define FX_FAR_TPORTA 0x67 /* FAR persistent tone portamento */
|
||||||
|
#define FX_FAR_TEMPO 0x68 /* FAR coarse tempo and tempo mode */
|
||||||
|
#define FX_FAR_F_TEMPO 0x69 /* FAR fine tempo slide up/down */
|
||||||
|
#define FX_FAR_VIBDEPTH 0x6a /* FAR set vibrato depth */
|
||||||
|
#define FX_FAR_VIBRATO 0x6b /* FAR persistent vibrato */
|
||||||
|
#define FX_FAR_SLIDEVOL 0x6c /* FAR persistent slide-to-volume */
|
||||||
|
#define FX_FAR_RETRIG 0x6d /* FAR retrigger */
|
||||||
|
#define FX_FAR_DELAY 0x6e /* FAR note offset */
|
||||||
|
|
||||||
|
/* Other frequency based effects (ULT, etc) */
|
||||||
|
#define FX_ULT_TPORTA 0x6f
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_DISABLE_IT
|
#ifndef LIBXMP_CORE_DISABLE_IT
|
||||||
|
@ -92,6 +107,9 @@
|
||||||
#define FX_PANBRELLO_WF 0x8b
|
#define FX_PANBRELLO_WF 0x8b
|
||||||
#define FX_HIOFFSET 0x8c
|
#define FX_HIOFFSET 0x8c
|
||||||
#define FX_IT_BREAK 0x8e /* like FX_BREAK with hex parameter */
|
#define FX_IT_BREAK 0x8e /* like FX_BREAK with hex parameter */
|
||||||
|
#define FX_MACRO_SET 0xbd /* Set active IT parametered MIDI macro */
|
||||||
|
#define FX_MACRO 0xbe /* Execute IT MIDI macro */
|
||||||
|
#define FX_MACROSMOOTH 0xbf /* Execute IT MIDI macro slide */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
@ -119,9 +137,11 @@
|
||||||
#define FX_VOL_SUB 0xb7 /* SFX change volume down */
|
#define FX_VOL_SUB 0xb7 /* SFX change volume down */
|
||||||
#define FX_PITCH_ADD 0xb8 /* SFX add steps to current note */
|
#define FX_PITCH_ADD 0xb8 /* SFX add steps to current note */
|
||||||
#define FX_PITCH_SUB 0xb9 /* SFX add steps to current note */
|
#define FX_PITCH_SUB 0xb9 /* SFX add steps to current note */
|
||||||
|
#define FX_LINE_JUMP 0xba /* Archimedes jump to line in current order */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define FX_SURROUND 0x8d /* S3M/IT */
|
#define FX_SURROUND 0x8d /* S3M/IT */
|
||||||
|
#define FX_REVERSE 0x8f /* XM/IT/others: play forward/reverse */
|
||||||
#define FX_S3M_SPEED 0xa3 /* S3M */
|
#define FX_S3M_SPEED 0xa3 /* S3M */
|
||||||
#define FX_VOLSLIDE_2 0xa4
|
#define FX_VOLSLIDE_2 0xa4
|
||||||
#define FX_FINETUNE 0xa6
|
#define FX_FINETUNE 0xa6
|
||||||
|
|
145
internal/c/parts/audio/extras/libxmp-lite/filetype.c
Normal file
145
internal/c/parts/audio/extras/libxmp-lite/filetype.c
Normal 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
|
||||||
|
|
|
@ -68,7 +68,9 @@ static const float resonance_table[128] = {
|
||||||
|
|
||||||
|
|
||||||
#if !defined(HAVE_POWF) || defined(__DJGPP__) || defined(__WATCOMC__)
|
#if !defined(HAVE_POWF) || defined(__DJGPP__) || defined(__WATCOMC__)
|
||||||
#define powf pow /* Watcom doesn't have powf. DJGPP have a C-only implementation in libm. */
|
/* Watcom doesn't have powf. DJGPP have a C-only implementation in libm. */
|
||||||
|
#undef powf
|
||||||
|
#define powf(f1_,f2_) (float)pow((f1_),(f2_))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Extended Module Player
|
/* Extended Module Player
|
||||||
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
|
* Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -21,23 +21,85 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
#ifndef LIBXMP_NO_PROWIZARD
|
||||||
|
#include "loaders/prowizard/prowiz.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
extern const struct format_loader libxmp_loader_xm;
|
const struct format_loader *const format_loaders[NUM_FORMATS + 2] = {
|
||||||
extern const struct format_loader libxmp_loader_mod;
|
|
||||||
extern const struct format_loader libxmp_loader_it;
|
|
||||||
extern const struct format_loader libxmp_loader_s3m;
|
|
||||||
|
|
||||||
const struct format_loader *const format_loaders[5] = {
|
|
||||||
&libxmp_loader_xm,
|
&libxmp_loader_xm,
|
||||||
&libxmp_loader_mod,
|
&libxmp_loader_mod,
|
||||||
#ifndef LIBXMP_CORE_DISABLE_IT
|
#ifndef LIBXMP_CORE_DISABLE_IT
|
||||||
&libxmp_loader_it,
|
&libxmp_loader_it,
|
||||||
#endif
|
#endif
|
||||||
&libxmp_loader_s3m,
|
&libxmp_loader_s3m,
|
||||||
NULL
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
&libxmp_loader_flt,
|
||||||
|
&libxmp_loader_st,
|
||||||
|
&libxmp_loader_stm,
|
||||||
|
&libxmp_loader_stx,
|
||||||
|
&libxmp_loader_mtm,
|
||||||
|
&libxmp_loader_ice,
|
||||||
|
&libxmp_loader_imf,
|
||||||
|
&libxmp_loader_ptm,
|
||||||
|
&libxmp_loader_mdl,
|
||||||
|
&libxmp_loader_ult,
|
||||||
|
&libxmp_loader_liq,
|
||||||
|
&libxmp_loader_no,
|
||||||
|
&libxmp_loader_masi,
|
||||||
|
&libxmp_loader_masi16,
|
||||||
|
&libxmp_loader_muse,
|
||||||
|
&libxmp_loader_gal5,
|
||||||
|
&libxmp_loader_gal4,
|
||||||
|
&libxmp_loader_amf,
|
||||||
|
&libxmp_loader_asylum,
|
||||||
|
&libxmp_loader_gdm,
|
||||||
|
&libxmp_loader_mmd1,
|
||||||
|
&libxmp_loader_mmd3,
|
||||||
|
&libxmp_loader_med2,
|
||||||
|
&libxmp_loader_med3,
|
||||||
|
&libxmp_loader_med4,
|
||||||
|
/* &libxmp_loader_dmf, */
|
||||||
|
&libxmp_loader_chip,
|
||||||
|
&libxmp_loader_rtm,
|
||||||
|
&libxmp_loader_pt3,
|
||||||
|
/* &libxmp_loader_tcb, */
|
||||||
|
&libxmp_loader_dt,
|
||||||
|
/* &libxmp_loader_gtk, */
|
||||||
|
/* &libxmp_loader_dtt, */
|
||||||
|
&libxmp_loader_mgt,
|
||||||
|
&libxmp_loader_arch,
|
||||||
|
&libxmp_loader_sym,
|
||||||
|
&libxmp_loader_digi,
|
||||||
|
&libxmp_loader_dbm,
|
||||||
|
&libxmp_loader_emod,
|
||||||
|
&libxmp_loader_okt,
|
||||||
|
&libxmp_loader_sfx,
|
||||||
|
&libxmp_loader_far,
|
||||||
|
&libxmp_loader_umx,
|
||||||
|
&libxmp_loader_hmn,
|
||||||
|
&libxmp_loader_stim,
|
||||||
|
&libxmp_loader_coco,
|
||||||
|
/* &libxmp_loader_mtp, */
|
||||||
|
&libxmp_loader_ims,
|
||||||
|
&libxmp_loader_669,
|
||||||
|
&libxmp_loader_fnk,
|
||||||
|
/* &libxmp_loader_amd, */
|
||||||
|
/* &libxmp_loader_rad, */
|
||||||
|
/* &libxmp_loader_hsc, */
|
||||||
|
&libxmp_loader_mfp,
|
||||||
|
&libxmp_loader_abk,
|
||||||
|
/* &libxmp_loader_alm, */
|
||||||
|
/* &libxmp_loader_polly, */
|
||||||
|
/* &libxmp_loader_stc, */
|
||||||
|
&libxmp_loader_xmf,
|
||||||
|
#ifndef LIBXMP_NO_PROWIZARD
|
||||||
|
&libxmp_loader_pw,
|
||||||
|
#endif
|
||||||
|
#endif /* LIBXMP_CORE_PLAYER */
|
||||||
|
NULL /* list teminator */
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *_farray[5] = { NULL };
|
static const char *_farray[NUM_FORMATS + NUM_PW_FORMATS + 1] = { NULL };
|
||||||
|
|
||||||
const char *const *format_list(void)
|
const char *const *format_list(void)
|
||||||
{
|
{
|
||||||
|
@ -45,6 +107,16 @@ const char *const *format_list(void)
|
||||||
|
|
||||||
if (_farray[0] == NULL) {
|
if (_farray[0] == NULL) {
|
||||||
for (count = i = 0; format_loaders[i] != NULL; i++) {
|
for (count = i = 0; format_loaders[i] != NULL; i++) {
|
||||||
|
#ifndef LIBXMP_NO_PROWIZARD
|
||||||
|
if (strcmp(format_loaders[i]->name, "prowizard") == 0) {
|
||||||
|
int j;
|
||||||
|
|
||||||
|
for (j = 0; pw_formats[j] != NULL; j++) {
|
||||||
|
_farray[count++] = pw_formats[j]->name;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
_farray[count++] = format_loaders[i]->name;
|
_farray[count++] = format_loaders[i]->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,21 +6,98 @@
|
||||||
|
|
||||||
struct format_loader {
|
struct format_loader {
|
||||||
const char *name;
|
const char *name;
|
||||||
int (*const test)(HIO_HANDLE *, char *, const int);
|
int (*test)(HIO_HANDLE *, char *, const int);
|
||||||
int (*const loader)(struct module_data *, HIO_HANDLE *, const int);
|
int (*loader)(struct module_data *, HIO_HANDLE *, const int);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern const struct format_loader *const format_loaders[];
|
||||||
|
|
||||||
const char *const *format_list(void);
|
const char *const *format_list(void);
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
extern const struct format_loader libxmp_loader_xm;
|
||||||
|
extern const struct format_loader libxmp_loader_mod;
|
||||||
|
extern const struct format_loader libxmp_loader_it;
|
||||||
|
extern const struct format_loader libxmp_loader_s3m;
|
||||||
|
|
||||||
#define NUM_FORMATS 53
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
#define NUM_PW_FORMATS 43
|
extern const struct format_loader libxmp_loader_flt;
|
||||||
|
extern const struct format_loader libxmp_loader_st;
|
||||||
|
extern const struct format_loader libxmp_loader_stm;
|
||||||
|
extern const struct format_loader libxmp_loader_stx;
|
||||||
|
extern const struct format_loader libxmp_loader_mtm;
|
||||||
|
extern const struct format_loader libxmp_loader_ice;
|
||||||
|
extern const struct format_loader libxmp_loader_imf;
|
||||||
|
extern const struct format_loader libxmp_loader_ptm;
|
||||||
|
extern const struct format_loader libxmp_loader_mdl;
|
||||||
|
extern const struct format_loader libxmp_loader_ult;
|
||||||
|
extern const struct format_loader libxmp_loader_liq;
|
||||||
|
extern const struct format_loader libxmp_loader_no;
|
||||||
|
extern const struct format_loader libxmp_loader_masi;
|
||||||
|
extern const struct format_loader libxmp_loader_masi16;
|
||||||
|
extern const struct format_loader libxmp_loader_muse;
|
||||||
|
extern const struct format_loader libxmp_loader_gal5;
|
||||||
|
extern const struct format_loader libxmp_loader_gal4;
|
||||||
|
extern const struct format_loader libxmp_loader_amf;
|
||||||
|
extern const struct format_loader libxmp_loader_asylum;
|
||||||
|
extern const struct format_loader libxmp_loader_gdm;
|
||||||
|
extern const struct format_loader libxmp_loader_mmd1;
|
||||||
|
extern const struct format_loader libxmp_loader_mmd3;
|
||||||
|
extern const struct format_loader libxmp_loader_med2;
|
||||||
|
extern const struct format_loader libxmp_loader_med3;
|
||||||
|
extern const struct format_loader libxmp_loader_med4;
|
||||||
|
extern const struct format_loader libxmp_loader_rtm;
|
||||||
|
extern const struct format_loader libxmp_loader_pt3;
|
||||||
|
extern const struct format_loader libxmp_loader_dt;
|
||||||
|
extern const struct format_loader libxmp_loader_mgt;
|
||||||
|
extern const struct format_loader libxmp_loader_arch;
|
||||||
|
extern const struct format_loader libxmp_loader_sym;
|
||||||
|
extern const struct format_loader libxmp_loader_digi;
|
||||||
|
extern const struct format_loader libxmp_loader_dbm;
|
||||||
|
extern const struct format_loader libxmp_loader_emod;
|
||||||
|
extern const struct format_loader libxmp_loader_okt;
|
||||||
|
extern const struct format_loader libxmp_loader_sfx;
|
||||||
|
extern const struct format_loader libxmp_loader_far;
|
||||||
|
extern const struct format_loader libxmp_loader_umx;
|
||||||
|
extern const struct format_loader libxmp_loader_stim;
|
||||||
|
extern const struct format_loader libxmp_loader_coco;
|
||||||
|
extern const struct format_loader libxmp_loader_ims;
|
||||||
|
extern const struct format_loader libxmp_loader_669;
|
||||||
|
extern const struct format_loader libxmp_loader_fnk;
|
||||||
|
extern const struct format_loader libxmp_loader_mfp;
|
||||||
|
extern const struct format_loader libxmp_loader_pw;
|
||||||
|
extern const struct format_loader libxmp_loader_hmn;
|
||||||
|
extern const struct format_loader libxmp_loader_chip;
|
||||||
|
extern const struct format_loader libxmp_loader_abk;
|
||||||
|
extern const struct format_loader libxmp_loader_xmf;
|
||||||
|
#if 0 /* broken / unused, yet. */
|
||||||
|
extern const struct format_loader libxmp_loader_dmf;
|
||||||
|
extern const struct format_loader libxmp_loader_tcb;
|
||||||
|
extern const struct format_loader libxmp_loader_gtk;
|
||||||
|
extern const struct format_loader libxmp_loader_dtt;
|
||||||
|
extern const struct format_loader libxmp_loader_mtp;
|
||||||
|
extern const struct format_loader libxmp_loader_amd;
|
||||||
|
extern const struct format_loader libxmp_loader_rad;
|
||||||
|
extern const struct format_loader libxmp_loader_hsc;
|
||||||
|
extern const struct format_loader libxmp_loader_alm;
|
||||||
|
extern const struct format_loader libxmp_loader_polly;
|
||||||
|
extern const struct format_loader libxmp_loader_stc;
|
||||||
|
#endif
|
||||||
|
#endif /* LIBXMP_CORE_PLAYER */
|
||||||
|
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
#define NUM_FORMATS 52
|
||||||
|
#elif !defined(LIBXMP_CORE_DISABLE_IT)
|
||||||
|
#define NUM_FORMATS 4
|
||||||
|
#else
|
||||||
|
#define NUM_FORMATS 3
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef LIBXMP_NO_PROWIZARD
|
#ifndef LIBXMP_NO_PROWIZARD
|
||||||
|
#define NUM_PW_FORMATS 43
|
||||||
|
extern const struct pw_format *const pw_formats[];
|
||||||
int pw_test_format(HIO_HANDLE *, char *, const int, struct xmp_test_info *);
|
int pw_test_format(HIO_HANDLE *, char *, const int, struct xmp_test_info *);
|
||||||
#endif
|
#else
|
||||||
#endif
|
#define NUM_PW_FORMATS 0
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif /* LIBXMP_FORMAT_H */
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Extended Module Player
|
/* Extended Module Player
|
||||||
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
|
* Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -287,18 +287,27 @@ int hio_seek(HIO_HANDLE *h, long offset, int whence)
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
h->error = errno;
|
h->error = errno;
|
||||||
}
|
}
|
||||||
|
else if (h->error == EOF) {
|
||||||
|
h->error = 0;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case HIO_HANDLE_TYPE_MEMORY:
|
case HIO_HANDLE_TYPE_MEMORY:
|
||||||
ret = mseek(h->handle.mem, offset, whence);
|
ret = mseek(h->handle.mem, offset, whence);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
h->error = EINVAL;
|
h->error = EINVAL;
|
||||||
}
|
}
|
||||||
|
else if (h->error == EOF) {
|
||||||
|
h->error = 0;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case HIO_HANDLE_TYPE_CBFILE:
|
case HIO_HANDLE_TYPE_CBFILE:
|
||||||
ret = cbseek(h->handle.cbfile, offset, whence);
|
ret = cbseek(h->handle.cbfile, offset, whence);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
h->error = EINVAL;
|
h->error = EINVAL;
|
||||||
}
|
}
|
||||||
|
else if (h->error == EOF) {
|
||||||
|
h->error = 0;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,7 +367,7 @@ HIO_HANDLE *hio_open(const char *path, const char *mode)
|
||||||
{
|
{
|
||||||
HIO_HANDLE *h;
|
HIO_HANDLE *h;
|
||||||
|
|
||||||
h = (HIO_HANDLE *)calloc(1, sizeof (HIO_HANDLE));
|
h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE));
|
||||||
if (h == NULL)
|
if (h == NULL)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
@ -381,19 +390,24 @@ HIO_HANDLE *hio_open(const char *path, const char *mode)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
HIO_HANDLE *hio_open_mem(const void *ptr, long size)
|
HIO_HANDLE *hio_open_mem(const void *ptr, long size, int free_after_use)
|
||||||
{
|
{
|
||||||
HIO_HANDLE *h;
|
HIO_HANDLE *h;
|
||||||
|
|
||||||
if (size <= 0) return NULL;
|
if (size <= 0) return NULL;
|
||||||
h = (HIO_HANDLE *)calloc(1, sizeof (HIO_HANDLE));
|
h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE));
|
||||||
if (h == NULL)
|
if (h == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
h->type = HIO_HANDLE_TYPE_MEMORY;
|
h->type = HIO_HANDLE_TYPE_MEMORY;
|
||||||
h->handle.mem = mopen(ptr, size);
|
h->handle.mem = mopen(ptr, size, free_after_use);
|
||||||
h->size = size;
|
h->size = size;
|
||||||
|
|
||||||
|
if (!h->handle.mem) {
|
||||||
|
free(h);
|
||||||
|
h = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,7 +415,7 @@ HIO_HANDLE *hio_open_file(FILE *f)
|
||||||
{
|
{
|
||||||
HIO_HANDLE *h;
|
HIO_HANDLE *h;
|
||||||
|
|
||||||
h = (HIO_HANDLE *)calloc(1, sizeof (HIO_HANDLE));
|
h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE));
|
||||||
if (h == NULL)
|
if (h == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -436,7 +450,7 @@ HIO_HANDLE *hio_open_callbacks(void *priv, struct xmp_callbacks callbacks)
|
||||||
if (!f)
|
if (!f)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
h = (HIO_HANDLE *)calloc(1, sizeof(HIO_HANDLE));
|
h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE));
|
||||||
if (h == NULL) {
|
if (h == NULL) {
|
||||||
cbclose(f);
|
cbclose(f);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -453,7 +467,7 @@ HIO_HANDLE *hio_open_callbacks(void *priv, struct xmp_callbacks callbacks)
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
int hio_close(HIO_HANDLE *h)
|
static int hio_close_internal(HIO_HANDLE *h)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
|
||||||
|
@ -468,7 +482,58 @@ int hio_close(HIO_HANDLE *h)
|
||||||
ret = cbclose(h->handle.cbfile);
|
ret = cbclose(h->handle.cbfile);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hio_close + hio_open_mem. Reuses the same HIO_HANDLE. */
|
||||||
|
int hio_reopen_mem(const void *ptr, long size, int free_after_use, HIO_HANDLE *h)
|
||||||
|
{
|
||||||
|
MFILE *m;
|
||||||
|
int ret;
|
||||||
|
if (size <= 0) return -1;
|
||||||
|
|
||||||
|
m = mopen(ptr, size, free_after_use);
|
||||||
|
if (m == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = hio_close_internal(h);
|
||||||
|
if (ret < 0) {
|
||||||
|
m->free_after_use = 0;
|
||||||
|
mclose(m);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
h->type = HIO_HANDLE_TYPE_MEMORY;
|
||||||
|
h->handle.mem = m;
|
||||||
|
h->size = size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hio_close + hio_open_file. Reuses the same HIO_HANDLE. */
|
||||||
|
int hio_reopen_file(FILE *f, int close_after_use, HIO_HANDLE *h)
|
||||||
|
{
|
||||||
|
long size = get_size(f);
|
||||||
|
int ret;
|
||||||
|
if (size < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = hio_close_internal(h);
|
||||||
|
if (ret < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
h->noclose = !close_after_use;
|
||||||
|
h->type = HIO_HANDLE_TYPE_FILE;
|
||||||
|
h->handle.file = f;
|
||||||
|
h->size = size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hio_close(HIO_HANDLE *h)
|
||||||
|
{
|
||||||
|
int ret = hio_close_internal(h);
|
||||||
free(h);
|
free(h);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,10 +38,12 @@ long hio_tell (HIO_HANDLE *);
|
||||||
int hio_eof (HIO_HANDLE *);
|
int hio_eof (HIO_HANDLE *);
|
||||||
int hio_error (HIO_HANDLE *);
|
int hio_error (HIO_HANDLE *);
|
||||||
HIO_HANDLE *hio_open (const char *, const char *);
|
HIO_HANDLE *hio_open (const char *, const char *);
|
||||||
HIO_HANDLE *hio_open_mem (const void *, long);
|
HIO_HANDLE *hio_open_mem (const void *, long, int);
|
||||||
HIO_HANDLE *hio_open_file (FILE *);
|
HIO_HANDLE *hio_open_file (FILE *);
|
||||||
HIO_HANDLE *hio_open_file2 (FILE *);/* allows fclose()ing the file by libxmp */
|
HIO_HANDLE *hio_open_file2 (FILE *);/* allows fclose()ing the file by libxmp */
|
||||||
HIO_HANDLE *hio_open_callbacks (void *, struct xmp_callbacks);
|
HIO_HANDLE *hio_open_callbacks (void *, struct xmp_callbacks);
|
||||||
|
int hio_reopen_mem (const void *, long, int, HIO_HANDLE *);
|
||||||
|
int hio_reopen_file (FILE *, int, HIO_HANDLE *);
|
||||||
int hio_close (HIO_HANDLE *);
|
int hio_close (HIO_HANDLE *);
|
||||||
long hio_size (HIO_HANDLE *);
|
long hio_size (HIO_HANDLE *);
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,11 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef LIBXMP_LOADERS_IT_H
|
||||||
|
#define LIBXMP_LOADERS_IT_H
|
||||||
|
|
||||||
|
#include "loader.h"
|
||||||
|
|
||||||
/* IT flags */
|
/* IT flags */
|
||||||
#define IT_STEREO 0x01
|
#define IT_STEREO 0x01
|
||||||
#define IT_VOL_OPT 0x02 /* Not recognized */
|
#define IT_VOL_OPT 0x02 /* Not recognized */
|
||||||
|
@ -27,9 +32,14 @@
|
||||||
#define IT_LINEAR_FREQ 0x08
|
#define IT_LINEAR_FREQ 0x08
|
||||||
#define IT_OLD_FX 0x10
|
#define IT_OLD_FX 0x10
|
||||||
#define IT_LINK_GXX 0x20
|
#define IT_LINK_GXX 0x20
|
||||||
|
#define IT_MIDI_WHEEL 0x40
|
||||||
|
#define IT_MIDI_CONFIG 0x80
|
||||||
|
|
||||||
/* IT special */
|
/* IT special */
|
||||||
#define IT_HAS_MSG 0x01
|
#define IT_HAS_MSG 0x01
|
||||||
|
#define IT_EDIT_HISTORY 0x02
|
||||||
|
#define IT_HIGHLIGHTS 0x04
|
||||||
|
#define IT_SPEC_MIDICFG 0x08
|
||||||
|
|
||||||
/* IT instrument flags */
|
/* IT instrument flags */
|
||||||
#define IT_INST_SAMPLE 0x01
|
#define IT_INST_SAMPLE 0x01
|
||||||
|
@ -56,6 +66,7 @@
|
||||||
#define IT_CVT_DIFF 0x04 /* Compressed sample flag */
|
#define IT_CVT_DIFF 0x04 /* Compressed sample flag */
|
||||||
#define IT_CVT_BYTEDIFF 0x08 /* 'safe to ignore' according to ittech.txt */
|
#define IT_CVT_BYTEDIFF 0x08 /* 'safe to ignore' according to ittech.txt */
|
||||||
#define IT_CVT_12BIT 0x10 /* 'safe to ignore' according to ittech.txt */
|
#define IT_CVT_12BIT 0x10 /* 'safe to ignore' according to ittech.txt */
|
||||||
|
#define IT_CVT_ADPCM 0xff /* Special: always indicates Modplug ADPCM4 */
|
||||||
|
|
||||||
/* IT envelope flags */
|
/* IT envelope flags */
|
||||||
#define IT_ENV_ON 0x01
|
#define IT_ENV_ON 0x01
|
||||||
|
@ -179,3 +190,7 @@ struct it_sample_header {
|
||||||
uint8 vit; /* Vibrato waveform */
|
uint8 vit; /* Vibrato waveform */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int itsex_decompress8(HIO_HANDLE *src, uint8 *dst, int len, int it215);
|
||||||
|
int itsex_decompress16(HIO_HANDLE *src, int16 *dst, int len, int it215);
|
||||||
|
|
||||||
|
#endif /* LIBXMP_LOADERS_IT_H */
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Extended Module Player
|
/* Extended Module Player
|
||||||
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
|
* Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -30,7 +30,6 @@
|
||||||
#define MAGIC_IMPI MAGIC4('I','M','P','I')
|
#define MAGIC_IMPI MAGIC4('I','M','P','I')
|
||||||
#define MAGIC_IMPS MAGIC4('I','M','P','S')
|
#define MAGIC_IMPS MAGIC4('I','M','P','S')
|
||||||
|
|
||||||
|
|
||||||
static int it_test(HIO_HANDLE *, char *, const int);
|
static int it_test(HIO_HANDLE *, char *, const int);
|
||||||
static int it_load(struct module_data *, HIO_HANDLE *, const int);
|
static int it_load(struct module_data *, HIO_HANDLE *, const int);
|
||||||
|
|
||||||
|
@ -55,7 +54,6 @@ static int it_test(HIO_HANDLE *f, char *t, const int start)
|
||||||
#define FX_XTND 0xfe
|
#define FX_XTND 0xfe
|
||||||
#define L_CHANNELS 64
|
#define L_CHANNELS 64
|
||||||
|
|
||||||
|
|
||||||
static const uint8 fx[32] = {
|
static const uint8 fx[32] = {
|
||||||
/* */ FX_NONE,
|
/* */ FX_NONE,
|
||||||
/* A */ FX_S3M_SPEED,
|
/* A */ FX_S3M_SPEED,
|
||||||
|
@ -83,19 +81,14 @@ static const uint8 fx[32] = {
|
||||||
/* W */ FX_GVOL_SLIDE,
|
/* W */ FX_GVOL_SLIDE,
|
||||||
/* X */ FX_SETPAN,
|
/* X */ FX_SETPAN,
|
||||||
/* Y */ FX_PANBRELLO,
|
/* Y */ FX_PANBRELLO,
|
||||||
/* Z */ FX_FLT_CUTOFF,
|
/* Z */ FX_MACRO,
|
||||||
/* ? */ FX_NONE,
|
|
||||||
/* ? */ FX_NONE,
|
/* ? */ FX_NONE,
|
||||||
|
/* / */ FX_MACROSMOOTH,
|
||||||
/* ? */ FX_NONE,
|
/* ? */ FX_NONE,
|
||||||
/* ? */ FX_NONE,
|
/* ? */ FX_NONE,
|
||||||
/* ? */ FX_NONE
|
/* ? */ FX_NONE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
int itsex_decompress8 (HIO_HANDLE *, void *, int, int);
|
|
||||||
int itsex_decompress16 (HIO_HANDLE *, void *, int, int);
|
|
||||||
|
|
||||||
|
|
||||||
static void xlat_fx(int c, struct xmp_event *e, uint8 *last_fxp, int new_fx)
|
static void xlat_fx(int c, struct xmp_event *e, uint8 *last_fxp, int new_fx)
|
||||||
{
|
{
|
||||||
uint8 h = MSN(e->fxp), l = LSN(e->fxp);
|
uint8 h = MSN(e->fxp), l = LSN(e->fxp);
|
||||||
|
@ -144,9 +137,16 @@ static void xlat_fx(int c, struct xmp_event *e, uint8 *last_fxp, int new_fx)
|
||||||
e->fxt = FX_SETPAN;
|
e->fxt = FX_SETPAN;
|
||||||
e->fxp = l << 4;
|
e->fxp = l << 4;
|
||||||
break;
|
break;
|
||||||
case 0x9: /* 0x91 = set surround */
|
case 0x9:
|
||||||
e->fxt = FX_SURROUND;
|
if (l == 0 || l == 1) {
|
||||||
e->fxp = l;
|
/* 0x91 = set surround */
|
||||||
|
e->fxt = FX_SURROUND;
|
||||||
|
e->fxp = l;
|
||||||
|
} else if (l == 0xe || l == 0xf) {
|
||||||
|
/* 0x9f Play reverse (MPT) */
|
||||||
|
e->fxt = FX_REVERSE;
|
||||||
|
e->fxp = l - 0xe;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 0xa: /* High offset */
|
case 0xa: /* High offset */
|
||||||
e->fxt = FX_HIOFFSET;
|
e->fxt = FX_HIOFFSET;
|
||||||
|
@ -165,18 +165,14 @@ static void xlat_fx(int c, struct xmp_event *e, uint8 *last_fxp, int new_fx)
|
||||||
e->fxt = FX_IT_ROWDELAY;
|
e->fxt = FX_IT_ROWDELAY;
|
||||||
e->fxp = l;
|
e->fxp = l;
|
||||||
break;
|
break;
|
||||||
|
case 0xf: /* Set parametered macro */
|
||||||
|
e->fxt = FX_MACRO_SET;
|
||||||
|
e->fxp = l;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
e->fxt = e->fxp = 0;
|
e->fxt = e->fxp = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case FX_FLT_CUTOFF:
|
|
||||||
if (e->fxp > 0x7f && e->fxp < 0x90) { /* Resonance */
|
|
||||||
e->fxt = FX_FLT_RESN;
|
|
||||||
e->fxp = (e->fxp - 0x80) * 16;
|
|
||||||
} else { /* Cutoff */
|
|
||||||
e->fxp *= 2;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case FX_TREMOR:
|
case FX_TREMOR:
|
||||||
if (!new_fx && e->fxp != 0) {
|
if (!new_fx && e->fxp != 0) {
|
||||||
e->fxp = ((MSN(e->fxp) + 1) << 4) | (LSN(e->fxp) + 1);
|
e->fxp = ((MSN(e->fxp) + 1) << 4) | (LSN(e->fxp) + 1);
|
||||||
|
@ -258,6 +254,34 @@ static void fix_name(uint8 *s, int l)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int load_it_midi_config(struct module_data *m, HIO_HANDLE *f)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
m->midi = (struct midi_macro_data *) calloc(1, sizeof(struct midi_macro_data));
|
||||||
|
if (m->midi == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Skip global MIDI macros */
|
||||||
|
if (hio_seek(f, 9 * 32, SEEK_CUR) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* SFx macros */
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
if (hio_read(m->midi->param[i].data, 1, 32, f) < 32)
|
||||||
|
return -1;
|
||||||
|
m->midi->param[i].data[31] = '\0';
|
||||||
|
}
|
||||||
|
/* Zxx macros */
|
||||||
|
for (i = 0; i < 128; i++) {
|
||||||
|
if (hio_read(m->midi->fixed[i].data, 1, 32, f) < 32)
|
||||||
|
return -1;
|
||||||
|
m->midi->fixed[i].data[31] = '\0';
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int read_envelope(struct xmp_envelope *ei, struct it_envelope *env,
|
static int read_envelope(struct xmp_envelope *ei, struct it_envelope *env,
|
||||||
HIO_HANDLE *f)
|
HIO_HANDLE *f)
|
||||||
{
|
{
|
||||||
|
@ -313,7 +337,8 @@ static int read_envelope(struct xmp_envelope *ei, struct it_envelope *env,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void identify_tracker(struct module_data *m, struct it_file_header *ifh)
|
static void identify_tracker(struct module_data *m, struct it_file_header *ifh,
|
||||||
|
int pat_before_smp, int *is_mpt_116)
|
||||||
{
|
{
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
char tracker_name[40];
|
char tracker_name[40];
|
||||||
|
@ -337,6 +362,16 @@ static void identify_tracker(struct module_data *m, struct it_file_header *ifh)
|
||||||
strcpy(tracker_name, "ModPlug Tracker 1.16");
|
strcpy(tracker_name, "ModPlug Tracker 1.16");
|
||||||
/* ModPlug Tracker files aren't really IMPM 2.00 */
|
/* ModPlug Tracker files aren't really IMPM 2.00 */
|
||||||
ifh->cmwt = sample_mode ? 0x100 : 0x214;
|
ifh->cmwt = sample_mode ? 0x100 : 0x214;
|
||||||
|
*is_mpt_116 = 1;
|
||||||
|
} else if (ifh->cmwt == 0x0200 && ifh->cwt == 0x0202 && pat_before_smp) {
|
||||||
|
/* ModPlug Tracker ITs from pre-alpha 4 use tracker
|
||||||
|
* 0x0202 and format 0x0200. Unfortunately, ITs from
|
||||||
|
* Impulse Tracker may *also* use this. These MPT ITs
|
||||||
|
* can be detected because they write patterns before
|
||||||
|
* samples/instruments. */
|
||||||
|
strcpy(tracker_name, "ModPlug Tracker 1.0 pre-alpha");
|
||||||
|
ifh->cmwt = sample_mode ? 0x100 : 0x200;
|
||||||
|
*is_mpt_116 = 1;
|
||||||
} else if (ifh->cwt == 0x0216) {
|
} else if (ifh->cwt == 0x0216) {
|
||||||
strcpy(tracker_name, "Impulse Tracker 2.14v3");
|
strcpy(tracker_name, "Impulse Tracker 2.14v3");
|
||||||
} else if (ifh->cwt == 0x0217) {
|
} else if (ifh->cwt == 0x0217) {
|
||||||
|
@ -352,6 +387,7 @@ static void identify_tracker(struct module_data *m, struct it_file_header *ifh)
|
||||||
case 0x7f:
|
case 0x7f:
|
||||||
if (ifh->cwt == 0x0888) {
|
if (ifh->cwt == 0x0888) {
|
||||||
strcpy(tracker_name, "OpenMPT 1.17");
|
strcpy(tracker_name, "OpenMPT 1.17");
|
||||||
|
*is_mpt_116 = 1;
|
||||||
} else if (ifh->cwt == 0x7fff) {
|
} else if (ifh->cwt == 0x7fff) {
|
||||||
strcpy(tracker_name, "munch.py");
|
strcpy(tracker_name, "munch.py");
|
||||||
} else {
|
} else {
|
||||||
|
@ -486,7 +522,7 @@ static int load_old_it_instrument(struct xmp_instrument *xxi, HIO_HANDLE *f)
|
||||||
xxi->vol = 0x40;
|
xxi->vol = 0x40;
|
||||||
|
|
||||||
if (k) {
|
if (k) {
|
||||||
xxi->sub = calloc(sizeof(struct xmp_subinstrument), k);
|
xxi->sub = (struct xmp_subinstrument *) calloc(k, sizeof(struct xmp_subinstrument));
|
||||||
if (xxi->sub == NULL) {
|
if (xxi->sub == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -637,7 +673,7 @@ static int load_new_it_instrument(struct xmp_instrument *xxi, HIO_HANDLE *f)
|
||||||
xxi->vol = i2h.gbv >> 1;
|
xxi->vol = i2h.gbv >> 1;
|
||||||
|
|
||||||
if (k) {
|
if (k) {
|
||||||
xxi->sub = calloc(sizeof(struct xmp_subinstrument), k);
|
xxi->sub = (struct xmp_subinstrument *) calloc(k, sizeof(struct xmp_subinstrument));
|
||||||
if (xxi->sub == NULL)
|
if (xxi->sub == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -671,7 +707,7 @@ static int load_new_it_instrument(struct xmp_instrument *xxi, HIO_HANDLE *f)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void force_sample_length(struct xmp_sample *xxs, int len)
|
static void force_sample_length(struct xmp_sample *xxs, struct extra_sample_data *xtra, int len)
|
||||||
{
|
{
|
||||||
xxs->len = len;
|
xxs->len = len;
|
||||||
|
|
||||||
|
@ -680,6 +716,14 @@ static void force_sample_length(struct xmp_sample *xxs, int len)
|
||||||
|
|
||||||
if (xxs->lps >= xxs->len)
|
if (xxs->lps >= xxs->len)
|
||||||
xxs->flg &= ~XMP_SAMPLE_LOOP;
|
xxs->flg &= ~XMP_SAMPLE_LOOP;
|
||||||
|
|
||||||
|
if (xtra) {
|
||||||
|
if (xtra->sue > xxs->len)
|
||||||
|
xtra->sue = xxs->len;
|
||||||
|
|
||||||
|
if(xtra->sus >= xxs->len)
|
||||||
|
xxs->flg &= ~(XMP_SAMPLE_SLOOP | XMP_SAMPLE_SLOOP_BIDIR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int load_it_sample(struct module_data *m, int i, int start,
|
static int load_it_sample(struct module_data *m, int i, int start,
|
||||||
|
@ -687,12 +731,13 @@ static int load_it_sample(struct module_data *m, int i, int start,
|
||||||
{
|
{
|
||||||
struct it_sample_header ish;
|
struct it_sample_header ish;
|
||||||
struct xmp_module *mod = &m->mod;
|
struct xmp_module *mod = &m->mod;
|
||||||
struct xmp_sample *xxs, *xsmp;
|
struct extra_sample_data *xtra;
|
||||||
|
struct xmp_sample *xxs;
|
||||||
int j, k;
|
int j, k;
|
||||||
uint8 buf[80];
|
uint8 buf[80];
|
||||||
|
|
||||||
if (sample_mode) {
|
if (sample_mode) {
|
||||||
mod->xxi[i].sub = calloc(sizeof(struct xmp_subinstrument), 1);
|
mod->xxi[i].sub = (struct xmp_subinstrument *) calloc(1, sizeof(struct xmp_subinstrument));
|
||||||
if (mod->xxi[i].sub == NULL) {
|
if (mod->xxi[i].sub == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -711,7 +756,7 @@ static int load_it_sample(struct module_data *m, int i, int start,
|
||||||
}
|
}
|
||||||
|
|
||||||
xxs = &mod->xxs[i];
|
xxs = &mod->xxs[i];
|
||||||
xsmp = &m->xsmp[i];
|
xtra = &m->xtra[i];
|
||||||
|
|
||||||
memcpy(ish.dosname, buf + 4, 12);
|
memcpy(ish.dosname, buf + 4, 12);
|
||||||
ish.zero = buf[16];
|
ish.zero = buf[16];
|
||||||
|
@ -749,14 +794,8 @@ static int load_it_sample(struct module_data *m, int i, int start,
|
||||||
xxs->flg |= ish.flags & IT_SMP_BSLOOP ? XMP_SAMPLE_SLOOP_BIDIR : 0;
|
xxs->flg |= ish.flags & IT_SMP_BSLOOP ? XMP_SAMPLE_SLOOP_BIDIR : 0;
|
||||||
|
|
||||||
if (ish.flags & IT_SMP_SLOOP) {
|
if (ish.flags & IT_SMP_SLOOP) {
|
||||||
memcpy(xsmp, xxs, sizeof (struct xmp_sample));
|
xtra->sus = ish.sloopbeg;
|
||||||
xsmp->lps = ish.sloopbeg;
|
xtra->sue = ish.sloopend;
|
||||||
xsmp->lpe = ish.sloopend;
|
|
||||||
xsmp->flg |= XMP_SAMPLE_LOOP;
|
|
||||||
xsmp->flg &= ~XMP_SAMPLE_LOOP_BIDIR;
|
|
||||||
if (ish.flags & IT_SMP_BSLOOP) {
|
|
||||||
xsmp->flg |= XMP_SAMPLE_LOOP_BIDIR;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sample_mode) {
|
if (sample_mode) {
|
||||||
|
@ -827,13 +866,16 @@ static int load_it_sample(struct module_data *m, int i, int start,
|
||||||
if (xxs->lpe > xxs->len || xxs->lps >= xxs->lpe)
|
if (xxs->lpe > xxs->len || xxs->lps >= xxs->lpe)
|
||||||
xxs->flg &= ~XMP_SAMPLE_LOOP;
|
xxs->flg &= ~XMP_SAMPLE_LOOP;
|
||||||
|
|
||||||
|
if (ish.convert == IT_CVT_ADPCM)
|
||||||
|
cvt |= SAMPLE_FLAG_ADPCM;
|
||||||
|
|
||||||
if (~ish.convert & IT_CVT_SIGNED)
|
if (~ish.convert & IT_CVT_SIGNED)
|
||||||
cvt |= SAMPLE_FLAG_UNS;
|
cvt |= SAMPLE_FLAG_UNS;
|
||||||
|
|
||||||
/* compressed samples */
|
/* compressed samples */
|
||||||
if (ish.flags & IT_SMP_COMP) {
|
if (ish.flags & IT_SMP_COMP) {
|
||||||
long min_size, file_len, left;
|
long min_size, file_len, left;
|
||||||
uint8 *buf;
|
void *decbuf;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Sanity check - the lower bound on IT compressed
|
/* Sanity check - the lower bound on IT compressed
|
||||||
|
@ -853,17 +895,15 @@ static int load_it_sample(struct module_data *m, int i, int start,
|
||||||
"resizing to %ld",
|
"resizing to %ld",
|
||||||
i, xxs->len, min_size, left, left << 3);
|
i, xxs->len, min_size, left, left << 3);
|
||||||
|
|
||||||
force_sample_length(xxs, left << 3);
|
force_sample_length(xxs, xtra, left << 3);
|
||||||
if (ish.flags & IT_SMP_SLOOP)
|
|
||||||
force_sample_length(xsmp, left << 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buf = calloc(1, xxs->len * 2);
|
decbuf = (uint8 *) calloc(1, xxs->len * 2);
|
||||||
if (buf == NULL)
|
if (decbuf == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (ish.flags & IT_SMP_16BIT) {
|
if (ish.flags & IT_SMP_16BIT) {
|
||||||
itsex_decompress16(f, buf, xxs->len,
|
itsex_decompress16(f, (int16 *)decbuf, xxs->len,
|
||||||
ish.convert & IT_CVT_DIFF);
|
ish.convert & IT_CVT_DIFF);
|
||||||
|
|
||||||
#ifdef WORDS_BIGENDIAN
|
#ifdef WORDS_BIGENDIAN
|
||||||
|
@ -873,44 +913,19 @@ static int load_it_sample(struct module_data *m, int i, int start,
|
||||||
cvt |= SAMPLE_FLAG_BIGEND;
|
cvt |= SAMPLE_FLAG_BIGEND;
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
itsex_decompress8(f, buf, xxs->len,
|
itsex_decompress8(f, (uint8 *)decbuf, xxs->len,
|
||||||
ish.convert & IT_CVT_DIFF);
|
ish.convert & IT_CVT_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ish.flags & IT_SMP_SLOOP) {
|
|
||||||
long pos = hio_tell(f);
|
|
||||||
if (pos < 0) {
|
|
||||||
free(buf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
ret = libxmp_load_sample(m, NULL, SAMPLE_FLAG_NOLOAD |
|
|
||||||
cvt, &m->xsmp[i], buf);
|
|
||||||
if (ret < 0) {
|
|
||||||
free(buf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
hio_seek(f, pos, SEEK_SET);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = libxmp_load_sample(m, NULL, SAMPLE_FLAG_NOLOAD | cvt,
|
ret = libxmp_load_sample(m, NULL, SAMPLE_FLAG_NOLOAD | cvt,
|
||||||
&mod->xxs[i], buf);
|
&mod->xxs[i], decbuf);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
free(buf);
|
free(decbuf);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(buf);
|
free(decbuf);
|
||||||
} else {
|
} else {
|
||||||
if (ish.flags & IT_SMP_SLOOP) {
|
|
||||||
long pos = hio_tell(f);
|
|
||||||
if (pos < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (libxmp_load_sample(m, f, cvt, &m->xsmp[i], NULL) < 0)
|
|
||||||
return -1;
|
|
||||||
hio_seek(f, pos, SEEK_SET);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (libxmp_load_sample(m, f, cvt, &mod->xxs[i], NULL) < 0)
|
if (libxmp_load_sample(m, f, cvt, &mod->xxs[i], NULL) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -927,7 +942,7 @@ static int load_it_pattern(struct module_data *m, int i, int new_fx,
|
||||||
uint8 mask[L_CHANNELS];
|
uint8 mask[L_CHANNELS];
|
||||||
uint8 last_fxp[64];
|
uint8 last_fxp[64];
|
||||||
|
|
||||||
int r, c, pat_len;
|
int r, c, pat_len, num_rows;
|
||||||
uint8 b;
|
uint8 b;
|
||||||
|
|
||||||
r = 0;
|
r = 0;
|
||||||
|
@ -937,7 +952,7 @@ static int load_it_pattern(struct module_data *m, int i, int new_fx,
|
||||||
memset(&dummy, 0, sizeof(struct xmp_event));
|
memset(&dummy, 0, sizeof(struct xmp_event));
|
||||||
|
|
||||||
pat_len = hio_read16l(f) /* - 4 */ ;
|
pat_len = hio_read16l(f) /* - 4 */ ;
|
||||||
mod->xxp[i]->rows = hio_read16l(f);
|
mod->xxp[i]->rows = num_rows = hio_read16l(f);
|
||||||
|
|
||||||
if (libxmp_alloc_tracks_in_pattern(mod, i) < 0) {
|
if (libxmp_alloc_tracks_in_pattern(mod, i) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -947,7 +962,7 @@ static int load_it_pattern(struct module_data *m, int i, int new_fx,
|
||||||
hio_read16l(f);
|
hio_read16l(f);
|
||||||
hio_read16l(f);
|
hio_read16l(f);
|
||||||
|
|
||||||
while (--pat_len >= 0) {
|
while (r < num_rows && --pat_len >= 0) {
|
||||||
b = hio_read8(f);
|
b = hio_read8(f);
|
||||||
if (hio_error(f)) {
|
if (hio_error(f)) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -968,7 +983,7 @@ static int load_it_pattern(struct module_data *m, int i, int new_fx,
|
||||||
* real number of channels before loading the patterns and
|
* real number of channels before loading the patterns and
|
||||||
* we don't want to set it to 64 channels.
|
* we don't want to set it to 64 channels.
|
||||||
*/
|
*/
|
||||||
if (c >= mod->chn || r >= mod->xxp[i]->rows) {
|
if (c >= mod->chn) {
|
||||||
event = &dummy;
|
event = &dummy;
|
||||||
} else {
|
} else {
|
||||||
event = &EVENT(i, c, r);
|
event = &EVENT(i, c, r);
|
||||||
|
@ -1056,6 +1071,8 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start)
|
||||||
uint32 *pp_smp; /* Pointers to samples */
|
uint32 *pp_smp; /* Pointers to samples */
|
||||||
uint32 *pp_pat; /* Pointers to patterns */
|
uint32 *pp_pat; /* Pointers to patterns */
|
||||||
int new_fx, sample_mode;
|
int new_fx, sample_mode;
|
||||||
|
int pat_before_smp = 0;
|
||||||
|
int is_mpt_116 = 0;
|
||||||
|
|
||||||
LOAD_INIT();
|
LOAD_INIT();
|
||||||
|
|
||||||
|
@ -1087,8 +1104,8 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start)
|
||||||
ifh.pwd = hio_read8(f);
|
ifh.pwd = hio_read8(f);
|
||||||
|
|
||||||
/* Sanity check */
|
/* Sanity check */
|
||||||
if (ifh.gv > 0x80 || ifh.mv > 0x80) {
|
if (ifh.gv > 0x80) {
|
||||||
D_(D_CRIT "invalid gv (%u) or mv (%u)", ifh.gv, ifh.mv);
|
D_(D_CRIT "invalid gv (%u)", ifh.gv);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1099,6 +1116,11 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start)
|
||||||
hio_read(ifh.chpan, 64, 1, f);
|
hio_read(ifh.chpan, 64, 1, f);
|
||||||
hio_read(ifh.chvol, 64, 1, f);
|
hio_read(ifh.chvol, 64, 1, f);
|
||||||
|
|
||||||
|
if (hio_error(f)) {
|
||||||
|
D_(D_CRIT "error reading IT header");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
memcpy(mod->name, ifh.name, sizeof(ifh.name));
|
memcpy(mod->name, ifh.name, sizeof(ifh.name));
|
||||||
/* sizeof(ifh.name) == 26, sizeof(mod->name) == 64. */
|
/* sizeof(ifh.name) == 26, sizeof(mod->name) == 64. */
|
||||||
mod->name[sizeof(ifh.name)] = '\0';
|
mod->name[sizeof(ifh.name)] = '\0';
|
||||||
|
@ -1115,18 +1137,18 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mod->ins) {
|
if (mod->ins) {
|
||||||
pp_ins = calloc(4, mod->ins);
|
pp_ins = (uint32 *) calloc(4, mod->ins);
|
||||||
if (pp_ins == NULL)
|
if (pp_ins == NULL)
|
||||||
goto err;
|
goto err;
|
||||||
} else {
|
} else {
|
||||||
pp_ins = NULL;
|
pp_ins = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
pp_smp = calloc(4, mod->smp);
|
pp_smp = (uint32 *) calloc(4, mod->smp);
|
||||||
if (pp_smp == NULL)
|
if (pp_smp == NULL)
|
||||||
goto err2;
|
goto err2;
|
||||||
|
|
||||||
pp_pat = calloc(4, mod->pat);
|
pp_pat = (uint32 *) calloc(4, mod->pat);
|
||||||
if (pp_pat == NULL)
|
if (pp_pat == NULL)
|
||||||
goto err3;
|
goto err3;
|
||||||
|
|
||||||
|
@ -1178,9 +1200,22 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start)
|
||||||
for (i = 0; i < mod->pat; i++)
|
for (i = 0; i < mod->pat; i++)
|
||||||
pp_pat[i] = hio_read32l(f);
|
pp_pat[i] = hio_read32l(f);
|
||||||
|
|
||||||
|
if ((ifh.flags & IT_MIDI_CONFIG) || (ifh.special & IT_SPEC_MIDICFG)) {
|
||||||
|
/* Skip edit history if it exists. */
|
||||||
|
if (ifh.special & IT_EDIT_HISTORY) {
|
||||||
|
int skip = hio_read16l(f) * 8;
|
||||||
|
if (hio_error(f) || (skip && hio_seek(f, skip, SEEK_CUR) < 0))
|
||||||
|
goto err4;
|
||||||
|
}
|
||||||
|
if (load_it_midi_config(m, f) < 0)
|
||||||
|
goto err4;
|
||||||
|
}
|
||||||
|
if (mod->smp && mod->pat && pp_pat[0] != 0 && pp_pat[0] < pp_smp[0])
|
||||||
|
pat_before_smp = 1;
|
||||||
|
|
||||||
m->c4rate = C4_NTSC_RATE;
|
m->c4rate = C4_NTSC_RATE;
|
||||||
|
|
||||||
identify_tracker(m, &ifh);
|
identify_tracker(m, &ifh, pat_before_smp, &is_mpt_116);
|
||||||
|
|
||||||
MODULE_INFO();
|
MODULE_INFO();
|
||||||
|
|
||||||
|
@ -1194,14 +1229,6 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start)
|
||||||
if (libxmp_init_instrument(m) < 0)
|
if (libxmp_init_instrument(m) < 0)
|
||||||
goto err4;
|
goto err4;
|
||||||
|
|
||||||
/* Alloc extra samples for sustain loop */
|
|
||||||
if (mod->smp > 0) {
|
|
||||||
m->xsmp = calloc(sizeof (struct xmp_sample), mod->smp);
|
|
||||||
if (m->xsmp == NULL) {
|
|
||||||
goto err4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
D_(D_INFO "Instruments: %d", mod->ins);
|
D_(D_INFO "Instruments: %d", mod->ins);
|
||||||
|
|
||||||
for (i = 0; i < mod->ins; i++) {
|
for (i = 0; i < mod->ins; i++) {
|
||||||
|
@ -1258,7 +1285,7 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start)
|
||||||
max_ch = 0;
|
max_ch = 0;
|
||||||
for (i = 0; i < mod->pat; i++) {
|
for (i = 0; i < mod->pat; i++) {
|
||||||
uint8 mask[L_CHANNELS];
|
uint8 mask[L_CHANNELS];
|
||||||
int pat_len;
|
int pat_len, num_rows, row;
|
||||||
|
|
||||||
/* If the offset to a pattern is 0, the pattern is empty */
|
/* If the offset to a pattern is 0, the pattern is empty */
|
||||||
if (pp_pat[i] == 0)
|
if (pp_pat[i] == 0)
|
||||||
|
@ -1266,19 +1293,33 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start)
|
||||||
|
|
||||||
hio_seek(f, start + pp_pat[i], SEEK_SET);
|
hio_seek(f, start + pp_pat[i], SEEK_SET);
|
||||||
pat_len = hio_read16l(f) /* - 4 */ ;
|
pat_len = hio_read16l(f) /* - 4 */ ;
|
||||||
hio_read16l(f);
|
num_rows = hio_read16l(f);
|
||||||
memset(mask, 0, L_CHANNELS);
|
memset(mask, 0, L_CHANNELS);
|
||||||
hio_read16l(f);
|
hio_read16l(f);
|
||||||
hio_read16l(f);
|
hio_read16l(f);
|
||||||
|
|
||||||
while (--pat_len >= 0) {
|
/* Sanity check:
|
||||||
|
* - Impulse Tracker and Schism Tracker allow up to 200 rows.
|
||||||
|
* - ModPlug Tracker 1.16 allows 256 rows.
|
||||||
|
* - OpenMPT allows 1024 rows.
|
||||||
|
*/
|
||||||
|
if (num_rows > 1024) {
|
||||||
|
D_(D_WARN "skipping pattern %d (%d rows)", i, num_rows);
|
||||||
|
pp_pat[i] = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
row = 0;
|
||||||
|
while (row < num_rows && --pat_len >= 0) {
|
||||||
int b = hio_read8(f);
|
int b = hio_read8(f);
|
||||||
if (hio_error(f)) {
|
if (hio_error(f)) {
|
||||||
D_(D_CRIT "error scanning pattern %d", i);
|
D_(D_CRIT "error scanning pattern %d", i);
|
||||||
goto err4;
|
goto err4;
|
||||||
}
|
}
|
||||||
if (b == 0)
|
if (b == 0) {
|
||||||
|
row++;
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
c = (b - 1) & 63;
|
c = (b - 1) & 63;
|
||||||
|
|
||||||
|
@ -1354,9 +1395,8 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start)
|
||||||
free(pp_ins);
|
free(pp_ins);
|
||||||
|
|
||||||
/* Song message */
|
/* Song message */
|
||||||
|
|
||||||
if (ifh.special & IT_HAS_MSG) {
|
if (ifh.special & IT_HAS_MSG) {
|
||||||
if ((m->comment = malloc(ifh.msglen)) != NULL) {
|
if ((m->comment = (char *)malloc(ifh.msglen)) != NULL) {
|
||||||
hio_seek(f, start + ifh.msgofs, SEEK_SET);
|
hio_seek(f, start + ifh.msgofs, SEEK_SET);
|
||||||
|
|
||||||
D_(D_INFO "Message length : %d", ifh.msglen);
|
D_(D_INFO "Message length : %d", ifh.msglen);
|
||||||
|
@ -1401,8 +1441,15 @@ static int it_load(struct module_data *m, HIO_HANDLE *f, const int start)
|
||||||
|
|
||||||
m->gvolbase = 0x80;
|
m->gvolbase = 0x80;
|
||||||
m->gvol = ifh.gv;
|
m->gvol = ifh.gv;
|
||||||
|
m->mvolbase = 48;
|
||||||
|
m->mvol = ifh.mv;
|
||||||
m->read_event_type = READ_EVENT_IT;
|
m->read_event_type = READ_EVENT_IT;
|
||||||
|
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
if (is_mpt_116)
|
||||||
|
libxmp_apply_mpt_preamp(m);
|
||||||
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err4:
|
err4:
|
||||||
|
|
|
@ -3,16 +3,22 @@
|
||||||
/* Public domain IT sample decompressor by Olivier Lapicque */
|
/* Public domain IT sample decompressor by Olivier Lapicque */
|
||||||
|
|
||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
|
#include "it.h"
|
||||||
|
|
||||||
static inline uint32 read_bits(HIO_HANDLE *ibuf, uint32 *bitbuf, int *bitnum, int n)
|
static inline uint32 read_bits(HIO_HANDLE *ibuf, uint32 *bitbuf, int *bitnum, int n, int *err)
|
||||||
{
|
{
|
||||||
uint32 retval = 0;
|
uint32 retval = 0;
|
||||||
int i = n;
|
int i = n;
|
||||||
int bnum = *bitnum, bbuf = *bitbuf;
|
int bnum = *bitnum;
|
||||||
|
uint32 bbuf = *bitbuf;
|
||||||
|
|
||||||
if (n > 0) {
|
if (n > 0 && n <= 32) {
|
||||||
do {
|
do {
|
||||||
if (bnum == 0) {
|
if (bnum == 0) {
|
||||||
|
if (hio_eof(ibuf)) {
|
||||||
|
*err = EOF;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
bbuf = hio_read8(ibuf);
|
bbuf = hio_read8(ibuf);
|
||||||
bnum = 8;
|
bnum = 8;
|
||||||
}
|
}
|
||||||
|
@ -27,6 +33,10 @@ static inline uint32 read_bits(HIO_HANDLE *ibuf, uint32 *bitbuf, int *bitnum, in
|
||||||
|
|
||||||
*bitnum = bnum;
|
*bitnum = bnum;
|
||||||
*bitbuf = bbuf;
|
*bitbuf = bbuf;
|
||||||
|
} else {
|
||||||
|
/* Invalid shift value. */
|
||||||
|
*err = -2;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (retval >> (32 - i));
|
return (retval >> (32 - i));
|
||||||
|
@ -41,6 +51,7 @@ int itsex_decompress8(HIO_HANDLE *src, uint8 *dst, int len, int it215)
|
||||||
int bitnum = 0;
|
int bitnum = 0;
|
||||||
uint8 left = 0, temp = 0, temp2 = 0;
|
uint8 left = 0, temp = 0, temp2 = 0;
|
||||||
uint32 d, pos;
|
uint32 d, pos;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
while (len) {
|
while (len) {
|
||||||
if (!block_count) {
|
if (!block_count) {
|
||||||
|
@ -58,8 +69,8 @@ int itsex_decompress8(HIO_HANDLE *src, uint8 *dst, int len, int it215)
|
||||||
/* Unpacking */
|
/* Unpacking */
|
||||||
pos = 0;
|
pos = 0;
|
||||||
do {
|
do {
|
||||||
uint16 bits = read_bits(src, &bitbuf, &bitnum, left);
|
uint16 bits = read_bits(src, &bitbuf, &bitnum, left, &err);
|
||||||
if (hio_eof(src))
|
if (err != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (left < 7) {
|
if (left < 7) {
|
||||||
|
@ -67,9 +78,9 @@ int itsex_decompress8(HIO_HANDLE *src, uint8 *dst, int len, int it215)
|
||||||
uint32 j = bits & 0xffff;
|
uint32 j = bits & 0xffff;
|
||||||
if (i != j)
|
if (i != j)
|
||||||
goto unpack_byte;
|
goto unpack_byte;
|
||||||
bits = (read_bits(src, &bitbuf, &bitnum, 3)
|
bits = (read_bits(src, &bitbuf, &bitnum, 3, &err)
|
||||||
+ 1) & 0xff;
|
+ 1) & 0xff;
|
||||||
if (hio_eof(src))
|
if (err != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
left = ((uint8)bits < left) ? (uint8)bits :
|
left = ((uint8)bits < left) ? (uint8)bits :
|
||||||
|
@ -137,6 +148,7 @@ int itsex_decompress16(HIO_HANDLE *src, int16 *dst, int len, int it215)
|
||||||
uint8 left = 0;
|
uint8 left = 0;
|
||||||
int16 temp = 0, temp2 = 0;
|
int16 temp = 0, temp2 = 0;
|
||||||
uint32 d, pos;
|
uint32 d, pos;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
while (len) {
|
while (len) {
|
||||||
if (!block_count) {
|
if (!block_count) {
|
||||||
|
@ -154,8 +166,8 @@ int itsex_decompress16(HIO_HANDLE *src, int16 *dst, int len, int it215)
|
||||||
/* Unpacking */
|
/* Unpacking */
|
||||||
pos = 0;
|
pos = 0;
|
||||||
do {
|
do {
|
||||||
uint32 bits = read_bits(src, &bitbuf, &bitnum, left);
|
uint32 bits = read_bits(src, &bitbuf, &bitnum, left, &err);
|
||||||
if (hio_eof(src))
|
if (err != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (left < 7) {
|
if (left < 7) {
|
||||||
|
@ -165,9 +177,8 @@ int itsex_decompress16(HIO_HANDLE *src, int16 *dst, int len, int it215)
|
||||||
if (i != j)
|
if (i != j)
|
||||||
goto unpack_byte;
|
goto unpack_byte;
|
||||||
|
|
||||||
bits = read_bits(src, &bitbuf, &bitnum, 4) + 1;
|
bits = read_bits(src, &bitbuf, &bitnum, 4, &err) + 1;
|
||||||
|
if (err != 0)
|
||||||
if (hio_eof(src))
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
left = ((uint8)(bits & 0xff) < left) ?
|
left = ((uint8)(bits & 0xff) < left) ?
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
#ifndef LIBXMP_LIST_H
|
#ifndef LIBXMP_LIST_H
|
||||||
#define LIBXMP_LIST_H
|
#define LIBXMP_LIST_H
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#include <stddef.h> /* offsetof */
|
||||||
#define __inline__ __inline
|
|
||||||
#endif
|
|
||||||
#ifdef __WATCOMC__
|
|
||||||
#define __inline__ inline
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Simple doubly linked list implementation.
|
* Simple doubly linked list implementation.
|
||||||
|
@ -37,7 +32,7 @@ struct list_head {
|
||||||
* This is only for internal list manipulation where we know
|
* This is only for internal list manipulation where we know
|
||||||
* the prev/next entries already!
|
* the prev/next entries already!
|
||||||
*/
|
*/
|
||||||
static __inline__ void __list_add(struct list_head *_new,
|
static inline void __list_add(struct list_head *_new,
|
||||||
struct list_head * prev,
|
struct list_head * prev,
|
||||||
struct list_head * next)
|
struct list_head * next)
|
||||||
{
|
{
|
||||||
|
@ -55,7 +50,7 @@ static __inline__ void __list_add(struct list_head *_new,
|
||||||
* Insert a new entry after the specified head.
|
* Insert a new entry after the specified head.
|
||||||
* This is good for implementing stacks.
|
* This is good for implementing stacks.
|
||||||
*/
|
*/
|
||||||
static __inline__ void list_add(struct list_head *_new, struct list_head *head)
|
static inline void list_add(struct list_head *_new, struct list_head *head)
|
||||||
{
|
{
|
||||||
__list_add(_new, head, head->next);
|
__list_add(_new, head, head->next);
|
||||||
}
|
}
|
||||||
|
@ -68,7 +63,7 @@ static __inline__ void list_add(struct list_head *_new, struct list_head *head)
|
||||||
* Insert a new entry before the specified head.
|
* Insert a new entry before the specified head.
|
||||||
* This is useful for implementing queues.
|
* This is useful for implementing queues.
|
||||||
*/
|
*/
|
||||||
static __inline__ void list_add_tail(struct list_head *_new, struct list_head *head)
|
static inline void list_add_tail(struct list_head *_new, struct list_head *head)
|
||||||
{
|
{
|
||||||
__list_add(_new, head->prev, head);
|
__list_add(_new, head->prev, head);
|
||||||
}
|
}
|
||||||
|
@ -80,7 +75,7 @@ static __inline__ void list_add_tail(struct list_head *_new, struct list_head *h
|
||||||
* This is only for internal list manipulation where we know
|
* This is only for internal list manipulation where we know
|
||||||
* the prev/next entries already!
|
* the prev/next entries already!
|
||||||
*/
|
*/
|
||||||
static __inline__ void __list_del(struct list_head * prev,
|
static inline void __list_del(struct list_head * prev,
|
||||||
struct list_head * next)
|
struct list_head * next)
|
||||||
{
|
{
|
||||||
next->prev = prev;
|
next->prev = prev;
|
||||||
|
@ -91,7 +86,7 @@ static __inline__ void __list_del(struct list_head * prev,
|
||||||
* list_del - deletes entry from list.
|
* list_del - deletes entry from list.
|
||||||
* @entry: the element to delete from the list.
|
* @entry: the element to delete from the list.
|
||||||
*/
|
*/
|
||||||
static __inline__ void list_del(struct list_head *entry)
|
static inline void list_del(struct list_head *entry)
|
||||||
{
|
{
|
||||||
__list_del(entry->prev, entry->next);
|
__list_del(entry->prev, entry->next);
|
||||||
}
|
}
|
||||||
|
@ -100,7 +95,7 @@ static __inline__ void list_del(struct list_head *entry)
|
||||||
* list_empty - tests whether a list is empty
|
* list_empty - tests whether a list is empty
|
||||||
* @head: the list to test.
|
* @head: the list to test.
|
||||||
*/
|
*/
|
||||||
static __inline__ int list_empty(struct list_head *head)
|
static inline int list_empty(struct list_head *head)
|
||||||
{
|
{
|
||||||
return head->next == head;
|
return head->next == head;
|
||||||
}
|
}
|
||||||
|
@ -110,7 +105,7 @@ static __inline__ int list_empty(struct list_head *head)
|
||||||
* @list: the new list to add.
|
* @list: the new list to add.
|
||||||
* @head: the place to add it in the first list.
|
* @head: the place to add it in the first list.
|
||||||
*/
|
*/
|
||||||
static __inline__ void list_splice(struct list_head *list, struct list_head *head)
|
static inline void list_splice(struct list_head *list, struct list_head *head)
|
||||||
{
|
{
|
||||||
struct list_head *first = list->next;
|
struct list_head *first = list->next;
|
||||||
|
|
||||||
|
@ -133,7 +128,7 @@ static __inline__ void list_splice(struct list_head *list, struct list_head *hea
|
||||||
* @member: the name of the list_struct within the struct.
|
* @member: the name of the list_struct within the struct.
|
||||||
*/
|
*/
|
||||||
#define list_entry(ptr, type, member) \
|
#define list_entry(ptr, type, member) \
|
||||||
((type *)((char *)(ptr)-(size_t)(&((type *)0)->member)))
|
((type *)((char *)(ptr) - offsetof(type, member)))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* list_for_each - iterate over a list
|
* list_for_each - iterate over a list
|
||||||
|
@ -143,4 +138,4 @@ static __inline__ void list_splice(struct list_head *list, struct list_head *hea
|
||||||
#define list_for_each(pos, head) \
|
#define list_for_each(pos, head) \
|
||||||
for (pos = (head)->next; pos != (head); pos = pos->next)
|
for (pos = (head)->next; pos != (head); pos = pos->next)
|
||||||
|
|
||||||
#endif
|
#endif /* LIBXMP_LIST_H */
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Extended Module Player
|
/* Extended Module Player
|
||||||
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
|
* Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -20,16 +20,15 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
#include "hio.h"
|
#include "hio.h"
|
||||||
#include "tempfile.h"
|
|
||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
|
|
||||||
#ifndef LIBXMP_NO_DEPACKERS
|
#ifndef LIBXMP_NO_DEPACKERS
|
||||||
|
#include "tempfile.h"
|
||||||
#include "depackers/depacker.h"
|
#include "depackers/depacker.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -39,17 +38,13 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
extern struct format_loader *format_loaders[];
|
|
||||||
|
|
||||||
void libxmp_load_prologue(struct context_data *);
|
void libxmp_load_prologue(struct context_data *);
|
||||||
void libxmp_load_epilogue(struct context_data *);
|
void libxmp_load_epilogue(struct context_data *);
|
||||||
int libxmp_prepare_scan(struct context_data *);
|
int libxmp_prepare_scan(struct context_data *);
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
#define BUFLEN 16384
|
#define BUFLEN 16384
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
|
||||||
static void set_md5sum(HIO_HANDLE *f, unsigned char *digest)
|
static void set_md5sum(HIO_HANDLE *f, unsigned char *digest)
|
||||||
{
|
{
|
||||||
unsigned char buf[BUFLEN];
|
unsigned char buf[BUFLEN];
|
||||||
|
@ -68,18 +63,18 @@ static void set_md5sum(HIO_HANDLE *f, unsigned char *digest)
|
||||||
static char *get_dirname(const char *name)
|
static char *get_dirname(const char *name)
|
||||||
{
|
{
|
||||||
char *dirname;
|
char *dirname;
|
||||||
const char *div;
|
const char *p;
|
||||||
ptrdiff_t len;
|
ptrdiff_t len;
|
||||||
|
|
||||||
if ((div = strrchr(name, '/')) != NULL) {
|
if ((p = strrchr(name, '/')) != NULL) {
|
||||||
len = div - name + 1;
|
len = p - name + 1;
|
||||||
dirname = malloc(len + 1);
|
dirname = (char *) malloc(len + 1);
|
||||||
if (dirname != NULL) {
|
if (dirname != NULL) {
|
||||||
memcpy(dirname, name, len);
|
memcpy(dirname, name, len);
|
||||||
dirname[len] = 0;
|
dirname[len] = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dirname = strdup("");
|
dirname = libxmp_strdup("");
|
||||||
}
|
}
|
||||||
|
|
||||||
return dirname;
|
return dirname;
|
||||||
|
@ -87,13 +82,13 @@ static char *get_dirname(const char *name)
|
||||||
|
|
||||||
static char *get_basename(const char *name)
|
static char *get_basename(const char *name)
|
||||||
{
|
{
|
||||||
const char *div;
|
const char *p;
|
||||||
char *basename;
|
char *basename;
|
||||||
|
|
||||||
if ((div = strrchr(name, '/')) != NULL) {
|
if ((p = strrchr(name, '/')) != NULL) {
|
||||||
basename = strdup(div + 1);
|
basename = libxmp_strdup(p + 1);
|
||||||
} else {
|
} else {
|
||||||
basename = strdup(name);
|
basename = libxmp_strdup(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return basename;
|
return basename;
|
||||||
|
@ -115,7 +110,7 @@ static int test_module(struct xmp_test_info *info, HIO_HANDLE *h)
|
||||||
if (format_loaders[i]->test(h, buf, 0) == 0) {
|
if (format_loaders[i]->test(h, buf, 0) == 0) {
|
||||||
int is_prowizard = 0;
|
int is_prowizard = 0;
|
||||||
|
|
||||||
#if !defined(LIBXMP_CORE_PLAYER) && !defined(LIBXMP_NO_PROWIZARD)
|
#ifndef LIBXMP_NO_PROWIZARD
|
||||||
if (strcmp(format_loaders[i]->name, "prowizard") == 0) {
|
if (strcmp(format_loaders[i]->name, "prowizard") == 0) {
|
||||||
hio_seek(h, 0, SEEK_SET);
|
hio_seek(h, 0, SEEK_SET);
|
||||||
pw_test_format(h, buf, 0, info);
|
pw_test_format(h, buf, 0, info);
|
||||||
|
@ -140,16 +135,17 @@ static int test_module(struct xmp_test_info *info, HIO_HANDLE *h)
|
||||||
int xmp_test_module(const char *path, struct xmp_test_info *info)
|
int xmp_test_module(const char *path, struct xmp_test_info *info)
|
||||||
{
|
{
|
||||||
HIO_HANDLE *h;
|
HIO_HANDLE *h;
|
||||||
struct stat st;
|
|
||||||
int ret;
|
|
||||||
#ifndef LIBXMP_NO_DEPACKERS
|
#ifndef LIBXMP_NO_DEPACKERS
|
||||||
char *temp = NULL;
|
char *temp = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (stat(path, &st) < 0)
|
ret = libxmp_get_filetype(path);
|
||||||
|
|
||||||
|
if (ret == XMP_FILETYPE_NONE) {
|
||||||
return -XMP_ERROR_SYSTEM;
|
return -XMP_ERROR_SYSTEM;
|
||||||
|
}
|
||||||
if (S_ISDIR(st.st_mode)) {
|
if (ret & XMP_FILETYPE_DIR) {
|
||||||
errno = EISDIR;
|
errno = EISDIR;
|
||||||
return -XMP_ERROR_SYSTEM;
|
return -XMP_ERROR_SYSTEM;
|
||||||
}
|
}
|
||||||
|
@ -158,16 +154,10 @@ int xmp_test_module(const char *path, struct xmp_test_info *info)
|
||||||
return -XMP_ERROR_SYSTEM;
|
return -XMP_ERROR_SYSTEM;
|
||||||
|
|
||||||
#ifndef LIBXMP_NO_DEPACKERS
|
#ifndef LIBXMP_NO_DEPACKERS
|
||||||
if (libxmp_decrunch(&h, path, &temp) < 0) {
|
if (libxmp_decrunch(h, path, &temp) < 0) {
|
||||||
ret = -XMP_ERROR_DEPACK;
|
ret = -XMP_ERROR_DEPACK;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get size after decrunch */
|
|
||||||
if (hio_size(h) < 256) { /* set minimum valid module size */
|
|
||||||
ret = -XMP_ERROR_FORMAT;
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ret = test_module(info, h);
|
ret = test_module(info, h);
|
||||||
|
@ -191,7 +181,7 @@ int xmp_test_module_from_memory(const void *mem, long size, struct xmp_test_info
|
||||||
return -XMP_ERROR_INVALID;
|
return -XMP_ERROR_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((h = hio_open_mem(mem, size)) == NULL)
|
if ((h = hio_open_mem(mem, size, 0)) == NULL)
|
||||||
return -XMP_ERROR_SYSTEM;
|
return -XMP_ERROR_SYSTEM;
|
||||||
|
|
||||||
ret = test_module(info, h);
|
ret = test_module(info, h);
|
||||||
|
@ -212,16 +202,10 @@ int xmp_test_module_from_file(void *file, struct xmp_test_info *info)
|
||||||
return -XMP_ERROR_SYSTEM;
|
return -XMP_ERROR_SYSTEM;
|
||||||
|
|
||||||
#ifndef LIBXMP_NO_DEPACKERS
|
#ifndef LIBXMP_NO_DEPACKERS
|
||||||
if (libxmp_decrunch(&h, NULL, &temp) < 0) {
|
if (libxmp_decrunch(h, NULL, &temp) < 0) {
|
||||||
ret = -XMP_ERROR_DEPACK;
|
ret = -XMP_ERROR_DEPACK;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get size after decrunch */
|
|
||||||
if (hio_size(h) < 256) { /* set minimum valid module size */
|
|
||||||
ret = -XMP_ERROR_FORMAT;
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ret = test_module(info, h);
|
ret = test_module(info, h);
|
||||||
|
@ -265,7 +249,6 @@ static int load_module(xmp_context opaque, HIO_HANDLE *h)
|
||||||
test_result = load_result = -1;
|
test_result = load_result = -1;
|
||||||
for (i = 0; format_loaders[i] != NULL; i++) {
|
for (i = 0; format_loaders[i] != NULL; i++) {
|
||||||
hio_seek(h, 0, SEEK_SET);
|
hio_seek(h, 0, SEEK_SET);
|
||||||
hio_error(h); /* reset error flag */
|
|
||||||
|
|
||||||
D_(D_WARN "test %s", format_loaders[i]->name);
|
D_(D_WARN "test %s", format_loaders[i]->name);
|
||||||
test_result = format_loaders[i]->test(h, NULL, 0);
|
test_result = format_loaders[i]->test(h, NULL, 0);
|
||||||
|
@ -277,11 +260,6 @@ static int load_module(xmp_context opaque, HIO_HANDLE *h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
|
||||||
if (test_result == 0 && load_result == 0)
|
|
||||||
set_md5sum(h, m->md5);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (test_result < 0) {
|
if (test_result < 0) {
|
||||||
xmp_release_module(opaque);
|
xmp_release_module(opaque);
|
||||||
return -XMP_ERROR_FORMAT;
|
return -XMP_ERROR_FORMAT;
|
||||||
|
@ -330,6 +308,11 @@ static int load_module(xmp_context opaque, HIO_HANDLE *h)
|
||||||
libxmp_adjust_string(mod->xxs[i].name);
|
libxmp_adjust_string(mod->xxs[i].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
if (test_result == 0 && load_result == 0)
|
||||||
|
set_md5sum(h, m->md5);
|
||||||
|
#endif
|
||||||
|
|
||||||
libxmp_load_epilogue(ctx);
|
libxmp_load_epilogue(ctx);
|
||||||
|
|
||||||
ret = libxmp_prepare_scan(ctx);
|
ret = libxmp_prepare_scan(ctx);
|
||||||
|
@ -358,22 +341,21 @@ int xmp_load_module(xmp_context opaque, const char *path)
|
||||||
struct context_data *ctx = (struct context_data *)opaque;
|
struct context_data *ctx = (struct context_data *)opaque;
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
struct module_data *m = &ctx->m;
|
struct module_data *m = &ctx->m;
|
||||||
long size;
|
|
||||||
#endif
|
#endif
|
||||||
#ifndef LIBXMP_NO_DEPACKERS
|
#ifndef LIBXMP_NO_DEPACKERS
|
||||||
char *temp_name;
|
char *temp_name;
|
||||||
#endif
|
#endif
|
||||||
HIO_HANDLE *h;
|
HIO_HANDLE *h;
|
||||||
struct stat st;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
D_(D_WARN "path = %s", path);
|
D_(D_WARN "path = %s", path);
|
||||||
|
|
||||||
if (stat(path, &st) < 0) {
|
ret = libxmp_get_filetype(path);
|
||||||
|
|
||||||
|
if (ret == XMP_FILETYPE_NONE) {
|
||||||
return -XMP_ERROR_SYSTEM;
|
return -XMP_ERROR_SYSTEM;
|
||||||
}
|
}
|
||||||
|
if (ret & XMP_FILETYPE_DIR) {
|
||||||
if (S_ISDIR(st.st_mode)) {
|
|
||||||
errno = EISDIR;
|
errno = EISDIR;
|
||||||
return -XMP_ERROR_SYSTEM;
|
return -XMP_ERROR_SYSTEM;
|
||||||
}
|
}
|
||||||
|
@ -384,20 +366,12 @@ int xmp_load_module(xmp_context opaque, const char *path)
|
||||||
|
|
||||||
#ifndef LIBXMP_NO_DEPACKERS
|
#ifndef LIBXMP_NO_DEPACKERS
|
||||||
D_(D_INFO "decrunch");
|
D_(D_INFO "decrunch");
|
||||||
if (libxmp_decrunch(&h, path, &temp_name) < 0) {
|
if (libxmp_decrunch(h, path, &temp_name) < 0) {
|
||||||
ret = -XMP_ERROR_DEPACK;
|
ret = -XMP_ERROR_DEPACK;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
|
||||||
size = hio_size(h);
|
|
||||||
if (size < 256) { /* get size after decrunch */
|
|
||||||
ret = -XMP_ERROR_FORMAT;
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ctx->state > XMP_STATE_UNLOADED)
|
if (ctx->state > XMP_STATE_UNLOADED)
|
||||||
xmp_release_module(opaque);
|
xmp_release_module(opaque);
|
||||||
|
|
||||||
|
@ -415,7 +389,7 @@ int xmp_load_module(xmp_context opaque, const char *path)
|
||||||
}
|
}
|
||||||
|
|
||||||
m->filename = path; /* For ALM, SSMT, etc */
|
m->filename = path; /* For ALM, SSMT, etc */
|
||||||
m->size = size;
|
m->size = hio_size(h);
|
||||||
#else
|
#else
|
||||||
ctx->m.filename = NULL;
|
ctx->m.filename = NULL;
|
||||||
ctx->m.dirname = NULL;
|
ctx->m.dirname = NULL;
|
||||||
|
@ -452,7 +426,7 @@ int xmp_load_module_from_memory(xmp_context opaque, const void *mem, long size)
|
||||||
return -XMP_ERROR_INVALID;
|
return -XMP_ERROR_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((h = hio_open_mem(mem, size)) == NULL)
|
if ((h = hio_open_mem(mem, size, 0)) == NULL)
|
||||||
return -XMP_ERROR_SYSTEM;
|
return -XMP_ERROR_SYSTEM;
|
||||||
|
|
||||||
if (ctx->state > XMP_STATE_UNLOADED)
|
if (ctx->state > XMP_STATE_UNLOADED)
|
||||||
|
@ -579,17 +553,9 @@ void xmp_release_module(xmp_context opaque)
|
||||||
}
|
}
|
||||||
|
|
||||||
free(m->xtra);
|
free(m->xtra);
|
||||||
|
free(m->midi);
|
||||||
m->xtra = NULL;
|
m->xtra = NULL;
|
||||||
|
m->midi = NULL;
|
||||||
#ifndef LIBXMP_CORE_DISABLE_IT
|
|
||||||
if (m->xsmp != NULL) {
|
|
||||||
for (i = 0; i < mod->smp; i++) {
|
|
||||||
libxmp_free_sample(&m->xsmp[i]);
|
|
||||||
}
|
|
||||||
free(m->xsmp);
|
|
||||||
m->xsmp = NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
libxmp_free_scan(ctx);
|
libxmp_free_scan(ctx);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Extended Module Player
|
/* Extended Module Player
|
||||||
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
|
* Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -53,12 +53,15 @@ const struct module_quirk mq[] = {
|
||||||
XMP_FLAGS_FIXLOOP, XMP_MODE_AUTO
|
XMP_FLAGS_FIXLOOP, XMP_MODE_AUTO
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#if 0
|
||||||
/* "siedler ii" (added by Daniel Åkerud) */
|
/* "siedler ii" (added by Daniel Åkerud) */
|
||||||
|
/* Timing fixed by vblank scan compare. CIA: 32m10s VBlank: 12m32s */
|
||||||
{
|
{
|
||||||
{ 0x70, 0xaa, 0x03, 0x4d, 0xfb, 0x2f, 0x1f, 0x73,
|
{ 0x70, 0xaa, 0x03, 0x4d, 0xfb, 0x2f, 0x1f, 0x73,
|
||||||
0xd9, 0xfd, 0xba, 0xfe, 0x13, 0x1b, 0xb7, 0x01 },
|
0xd9, 0xfd, 0xba, 0xfe, 0x13, 0x1b, 0xb7, 0x01 },
|
||||||
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
|
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
|
||||||
},
|
},
|
||||||
|
#endif
|
||||||
|
|
||||||
/* "Klisje paa klisje" (added by Kjetil Torgrim Homme) */
|
/* "Klisje paa klisje" (added by Kjetil Torgrim Homme) */
|
||||||
{
|
{
|
||||||
|
@ -99,6 +102,126 @@ const struct module_quirk mq[] = {
|
||||||
0, XMP_MODE_PROTRACKER
|
0, XMP_MODE_PROTRACKER
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/* grooving3.mod */
|
||||||
|
/* length 150778 crc32c 0xfdcf9aadU */
|
||||||
|
{
|
||||||
|
{ 0xdb, 0x61, 0x22, 0x44, 0x39, 0x85, 0x74, 0xe9,
|
||||||
|
0xfa, 0x11, 0xb8, 0xfb, 0x87, 0xe8, 0xde, 0xc5, },
|
||||||
|
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
|
||||||
|
},
|
||||||
|
/* mod.Rundgren */
|
||||||
|
/* length 195078 crc32c 0x8fa827a4U */
|
||||||
|
{
|
||||||
|
{ 0x9a, 0xdb, 0xb2, 0x09, 0x07, 0x1c, 0x44, 0x82,
|
||||||
|
0xc5, 0xdf, 0x83, 0x52, 0xcc, 0x73, 0x9f, 0x20, },
|
||||||
|
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
|
||||||
|
},
|
||||||
|
/* dance feeling by Audiomonster */
|
||||||
|
/* length 169734 crc32c 0x79fa2c9bU */
|
||||||
|
{
|
||||||
|
{ 0x31, 0x2c, 0x3d, 0xaa, 0x5f, 0x1a, 0x54, 0x44,
|
||||||
|
0x9d, 0xf7, 0xc4, 0x41, 0x8a, 0xc5, 0x01, 0x02, },
|
||||||
|
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
|
||||||
|
},
|
||||||
|
/* knights melody by Audiomonster */
|
||||||
|
/* length 77798 crc32c 0x7bf19c5bU */
|
||||||
|
{
|
||||||
|
{ 0x31, 0xc3, 0x0e, 0x32, 0xfc, 0x99, 0x95, 0xd2,
|
||||||
|
0x97, 0x20, 0xb3, 0x77, 0x50, 0x05, 0xfe, 0xa5, },
|
||||||
|
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
|
||||||
|
},
|
||||||
|
/* hcomme by Bouffon */
|
||||||
|
/* length 71346 crc32c 0x4ad49cb3U */
|
||||||
|
{
|
||||||
|
{ 0x6e, 0xf9, 0x78, 0xc1, 0x80, 0xae, 0x51, 0x06,
|
||||||
|
0x05, 0x7c, 0x6e, 0xd0, 0x26, 0x7e, 0xfe, 0x3d, },
|
||||||
|
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
|
||||||
|
},
|
||||||
|
/* ((((aquapool)))) by Dolphin */
|
||||||
|
/* length 62932 crc32c 0x05b103fcU */
|
||||||
|
{
|
||||||
|
{ 0xff, 0x0b, 0xe0, 0x26, 0xc6, 0x31, 0xb5, 0x9b,
|
||||||
|
0x94, 0x83, 0x94, 0x99, 0x7e, 0x24, 0x7c, 0xdd, },
|
||||||
|
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
|
||||||
|
},
|
||||||
|
/* 100yarddash by Dr. Awesome */
|
||||||
|
/* length 104666 crc32c 0xd2b0e4a6U */
|
||||||
|
{
|
||||||
|
{ 0x5b, 0xff, 0x2f, 0xb8, 0xef, 0x3c, 0xbe, 0x55,
|
||||||
|
0xa8, 0xe2, 0xa7, 0xcf, 0x5c, 0xbd, 0xdd, 0xb2, },
|
||||||
|
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
|
||||||
|
},
|
||||||
|
/* jazz-reggae-funk by Droid */
|
||||||
|
/* length 115564 crc32c 0x41ff635fU */
|
||||||
|
{
|
||||||
|
{ 0xe5, 0x6e, 0x31, 0x2f, 0x62, 0x80, 0xc1, 0x9d,
|
||||||
|
0x2f, 0x24, 0x54, 0xf3, 0x89, 0x3f, 0x94, 0x6c, },
|
||||||
|
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
|
||||||
|
},
|
||||||
|
/* hard and heavy by Fish */
|
||||||
|
/* length 69814 crc32c 0x1f09d3d5U */
|
||||||
|
{
|
||||||
|
{ 0x6b, 0xce, 0x39, 0x94, 0x75, 0x42, 0x06, 0x74,
|
||||||
|
0xd2, 0x83, 0xbc, 0x5e, 0x7b, 0x42, 0x1f, 0xa0, },
|
||||||
|
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
|
||||||
|
},
|
||||||
|
/* crazy valley by Julius and Droid */
|
||||||
|
/* length 97496 crc32c 0xb8eec40eU */
|
||||||
|
{
|
||||||
|
{ 0x23, 0x77, 0x18, 0x1d, 0x21, 0x9b, 0x41, 0x8f,
|
||||||
|
0xc1, 0xb4, 0xf4, 0xf8, 0x22, 0xdd, 0xd8, 0xb6, },
|
||||||
|
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
|
||||||
|
},
|
||||||
|
/* THE ILLOGICAL ONE by Rhino */
|
||||||
|
/* length 173432 crc32c 0xcb4e2987U */
|
||||||
|
{
|
||||||
|
{ 0xd8, 0xc2, 0xbb, 0xe6, 0x11, 0xd0, 0x5c, 0x02,
|
||||||
|
0x8e, 0x3b, 0xcb, 0x7c, 0x4a, 0x7d, 0x43, 0xa0, },
|
||||||
|
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
|
||||||
|
},
|
||||||
|
/* sounds of holiday by Spacebrain */
|
||||||
|
/* length 309520 crc32c 0x28804a57U */
|
||||||
|
{
|
||||||
|
{ 0x36, 0x18, 0x19, 0xa4, 0x9d, 0xa2, 0xa2, 0x6f,
|
||||||
|
0x58, 0x60, 0xc4, 0xd9, 0x0d, 0xa2, 0x9f, 0x49, },
|
||||||
|
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
|
||||||
|
},
|
||||||
|
/* sunisinus by Speed-Head */
|
||||||
|
/* length 175706 crc32c 0x2e56451bU */
|
||||||
|
{
|
||||||
|
{ 0x7e, 0x69, 0x44, 0xb6, 0x38, 0x0d, 0x27, 0x14,
|
||||||
|
0x70, 0x5d, 0x44, 0xce, 0xce, 0xdd, 0x37, 0x31, },
|
||||||
|
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
|
||||||
|
},
|
||||||
|
/* eat the fulcrum bop by The Assassin */
|
||||||
|
/* length 160286 crc32c 0x583a4683U */
|
||||||
|
{
|
||||||
|
{ 0x11, 0xe9, 0x6f, 0x62, 0xe1, 0xc3, 0xc5, 0xcc,
|
||||||
|
0x3b, 0xaf, 0xea, 0x69, 0x4b, 0xce, 0x5f, 0xec, },
|
||||||
|
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
|
||||||
|
},
|
||||||
|
/* obvious disaster by Tip */
|
||||||
|
/* length 221086 crc32c 0x51c6d489U */
|
||||||
|
{
|
||||||
|
{ 0x06, 0x8e, 0x69, 0x01, 0x49, 0x8f, 0xbd, 0x0f,
|
||||||
|
0xfc, 0xb7, 0x8f, 0x2a, 0x91, 0xe1, 0x8b, 0xe8, },
|
||||||
|
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
|
||||||
|
},
|
||||||
|
/* alien nation by Turtle */
|
||||||
|
/* length 167548 crc32c 0xc9ec1674U */
|
||||||
|
{
|
||||||
|
{ 0x71, 0xdf, 0x11, 0xac, 0x5d, 0xec, 0x07, 0xf8,
|
||||||
|
0x10, 0x6f, 0x28, 0x8d, 0x47, 0x59, 0x54, 0x9b, },
|
||||||
|
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
|
||||||
|
},
|
||||||
|
/* illusions!2 by Zuhl */
|
||||||
|
/* length 289770 crc32c 0x6bf5fbcfU */
|
||||||
|
{
|
||||||
|
{ 0xca, 0x37, 0x8c, 0x0e, 0x87, 0x4f, 0x1e, 0xcd,
|
||||||
|
0xa3, 0xe9, 0x8b, 0xdd, 0x11, 0x46, 0x8d, 0x69, },
|
||||||
|
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||||
|
@ -127,7 +250,7 @@ char *libxmp_adjust_string(char *s)
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < strlen(s); i++) {
|
for (i = 0; i < strlen(s); i++) {
|
||||||
if (!isprint((int)s[i]) || ((uint8) s[i] > 127))
|
if (!isprint((unsigned char)s[i]) || ((uint8) s[i] > 127))
|
||||||
s[i] = ' ';
|
s[i] = ' ';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,9 +273,21 @@ static void check_envelope(struct xmp_envelope *env)
|
||||||
env->flg &= ~XMP_ENVELOPE_LOOP;
|
env->flg &= ~XMP_ENVELOPE_LOOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Disable envelope loop if invalid sustain */
|
/* Disable envelope sustain if invalid sustain */
|
||||||
if (env->sus >= env->npt) {
|
if (env->sus >= env->npt || env->sue >= env->npt) {
|
||||||
env->flg &= ~XMP_ENVELOPE_ON;
|
env->flg &= ~XMP_ENVELOPE_SUS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clamp_volume_envelope(struct module_data *m, struct xmp_envelope *env)
|
||||||
|
{
|
||||||
|
/* Clamp broken values in the volume envelope to the expected range. */
|
||||||
|
if (env->flg & XMP_ENVELOPE_ON) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < env->npt; i++) {
|
||||||
|
int16 *data = &env->data[i * 2 + 1];
|
||||||
|
CLAMP(*data, 0, m->volbase);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,26 +306,25 @@ void libxmp_load_prologue(struct context_data *ctx)
|
||||||
m->quirk = 0;
|
m->quirk = 0;
|
||||||
m->read_event_type = READ_EVENT_MOD;
|
m->read_event_type = READ_EVENT_MOD;
|
||||||
m->period_type = PERIOD_AMIGA;
|
m->period_type = PERIOD_AMIGA;
|
||||||
|
m->compare_vblank = 0;
|
||||||
m->comment = NULL;
|
m->comment = NULL;
|
||||||
m->scan_cnt = NULL;
|
m->scan_cnt = NULL;
|
||||||
|
m->midi = NULL;
|
||||||
|
|
||||||
/* Set defaults */
|
/* Set defaults */
|
||||||
m->mod.pat = 0;
|
m->mod.pat = 0;
|
||||||
m->mod.trk = 0;
|
m->mod.trk = 0;
|
||||||
m->mod.chn = 4;
|
m->mod.chn = 4;
|
||||||
m->mod.ins = 0;
|
m->mod.ins = 0;
|
||||||
m->mod.smp = 0;
|
m->mod.smp = 0;
|
||||||
m->mod.spd = 6;
|
m->mod.spd = 6;
|
||||||
m->mod.bpm = 125;
|
m->mod.bpm = 125;
|
||||||
m->mod.len = 0;
|
m->mod.len = 0;
|
||||||
m->mod.rst = 0;
|
m->mod.rst = 0;
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
m->extra = NULL;
|
m->extra = NULL;
|
||||||
#endif
|
#endif
|
||||||
#ifndef LIBXMP_CORE_DISABLE_IT
|
|
||||||
m->xsmp = NULL;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m->time_factor = DEFAULT_TIME_FACTOR;
|
m->time_factor = DEFAULT_TIME_FACTOR;
|
||||||
|
|
||||||
|
@ -209,7 +343,7 @@ void libxmp_load_epilogue(struct context_data *ctx)
|
||||||
struct xmp_module *mod = &m->mod;
|
struct xmp_module *mod = &m->mod;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
mod->gvl = m->gvol;
|
mod->gvl = m->gvol;
|
||||||
|
|
||||||
/* Sanity check for module parameters */
|
/* Sanity check for module parameters */
|
||||||
CLAMP(mod->len, 0, XMP_MAX_MOD_LENGTH);
|
CLAMP(mod->len, 0, XMP_MAX_MOD_LENGTH);
|
||||||
|
@ -230,7 +364,7 @@ void libxmp_load_epilogue(struct context_data *ctx)
|
||||||
if (mod->spd <= 0 || mod->spd > 255) {
|
if (mod->spd <= 0 || mod->spd > 255) {
|
||||||
mod->spd = 6;
|
mod->spd = 6;
|
||||||
}
|
}
|
||||||
CLAMP(mod->bpm, XMP_MIN_BPM, 255);
|
CLAMP(mod->bpm, XMP_MIN_BPM, 1000);
|
||||||
|
|
||||||
/* Set appropriate values for instrument volumes and subinstrument
|
/* Set appropriate values for instrument volumes and subinstrument
|
||||||
* global volumes when QUIRK_INSVOL is not set, to keep volume values
|
* global volumes when QUIRK_INSVOL is not set, to keep volume values
|
||||||
|
@ -254,8 +388,28 @@ void libxmp_load_epilogue(struct context_data *ctx)
|
||||||
check_envelope(&mod->xxi[i].aei);
|
check_envelope(&mod->xxi[i].aei);
|
||||||
check_envelope(&mod->xxi[i].fei);
|
check_envelope(&mod->xxi[i].fei);
|
||||||
check_envelope(&mod->xxi[i].pei);
|
check_envelope(&mod->xxi[i].pei);
|
||||||
|
clamp_volume_envelope(m, &mod->xxi[i].aei);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef LIBXMP_CORE_DISABLE_IT
|
||||||
|
/* TODO: there's no unintrusive and clean way to get this struct into
|
||||||
|
* libxmp_load_sample currently, so bound these fields here for now. */
|
||||||
|
for (i = 0; i < mod->smp; i++) {
|
||||||
|
struct xmp_sample *xxs = &mod->xxs[i];
|
||||||
|
struct extra_sample_data *xtra = &m->xtra[i];
|
||||||
|
if (xtra->sus < 0) {
|
||||||
|
xtra->sus = 0;
|
||||||
|
}
|
||||||
|
if (xtra->sue > xxs->len) {
|
||||||
|
xtra->sue = xxs->len;
|
||||||
|
}
|
||||||
|
if (xtra->sus >= xxs->len || xtra->sus >= xtra->sue) {
|
||||||
|
xtra->sus = xtra->sue = 0;
|
||||||
|
xxs->flg &= ~(XMP_SAMPLE_SLOOP | XMP_SAMPLE_SLOOP_BIDIR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
p->filter = 0;
|
p->filter = 0;
|
||||||
p->mode = XMP_MODE_AUTO;
|
p->mode = XMP_MODE_AUTO;
|
||||||
p->flags = p->player_flags;
|
p->flags = p->player_flags;
|
||||||
|
@ -283,7 +437,7 @@ int libxmp_prepare_scan(struct context_data *ctx)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
m->scan_cnt = calloc(sizeof (uint8 *), mod->len);
|
m->scan_cnt = (uint8 **) calloc(mod->len, sizeof(uint8 *));
|
||||||
if (m->scan_cnt == NULL)
|
if (m->scan_cnt == NULL)
|
||||||
return -XMP_ERROR_SYSTEM;
|
return -XMP_ERROR_SYSTEM;
|
||||||
|
|
||||||
|
@ -299,7 +453,7 @@ int libxmp_prepare_scan(struct context_data *ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pat = pat_idx >= mod->pat ? NULL : mod->xxp[pat_idx];
|
pat = pat_idx >= mod->pat ? NULL : mod->xxp[pat_idx];
|
||||||
m->scan_cnt[i] = calloc(1, pat && pat->rows ? pat->rows : 1);
|
m->scan_cnt[i] = (uint8 *) calloc(1, (pat && pat->rows)? pat->rows : 1);
|
||||||
if (m->scan_cnt[i] == NULL)
|
if (m->scan_cnt[i] == NULL)
|
||||||
return -XMP_ERROR_SYSTEM;
|
return -XMP_ERROR_SYSTEM;
|
||||||
}
|
}
|
||||||
|
@ -309,6 +463,7 @@ int libxmp_prepare_scan(struct context_data *ctx)
|
||||||
|
|
||||||
void libxmp_free_scan(struct context_data *ctx)
|
void libxmp_free_scan(struct context_data *ctx)
|
||||||
{
|
{
|
||||||
|
struct player_data *p = &ctx->p;
|
||||||
struct module_data *m = &ctx->m;
|
struct module_data *m = &ctx->m;
|
||||||
struct xmp_module *mod = &m->mod;
|
struct xmp_module *mod = &m->mod;
|
||||||
int i;
|
int i;
|
||||||
|
@ -320,6 +475,9 @@ void libxmp_free_scan(struct context_data *ctx)
|
||||||
free(m->scan_cnt);
|
free(m->scan_cnt);
|
||||||
m->scan_cnt = NULL;
|
m->scan_cnt = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(p->scan);
|
||||||
|
p->scan = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Process player personality flags */
|
/* Process player personality flags */
|
||||||
|
@ -394,6 +552,9 @@ int libxmp_set_player_mode(struct context_data *ctx)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (p->mode != XMP_MODE_AUTO)
|
||||||
|
m->compare_vblank = 0;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,10 @@
|
||||||
#define SAMPLE_FLAG_HSC 0x2000 /* HSC Adlib synth instrument */
|
#define SAMPLE_FLAG_HSC 0x2000 /* HSC Adlib synth instrument */
|
||||||
#define SAMPLE_FLAG_ADPCM 0x4000 /* ADPCM4 encoded samples */
|
#define SAMPLE_FLAG_ADPCM 0x4000 /* ADPCM4 encoded samples */
|
||||||
|
|
||||||
|
/* libxmp_test_name flags */
|
||||||
|
#define TEST_NAME_IGNORE_AFTER_0 0x0001
|
||||||
|
#define TEST_NAME_IGNORE_AFTER_CR 0x0002
|
||||||
|
|
||||||
#define DEFPAN(x) (0x80 + ((x) - 0x80) * m->defpan / 100)
|
#define DEFPAN(x) (0x80 + ((x) - 0x80) * m->defpan / 100)
|
||||||
|
|
||||||
int libxmp_init_instrument (struct module_data *);
|
int libxmp_init_instrument (struct module_data *);
|
||||||
|
@ -30,16 +34,18 @@ int libxmp_alloc_pattern (struct xmp_module *, int);
|
||||||
int libxmp_alloc_track (struct xmp_module *, int, int);
|
int libxmp_alloc_track (struct xmp_module *, int, int);
|
||||||
int libxmp_alloc_tracks_in_pattern (struct xmp_module *, int);
|
int libxmp_alloc_tracks_in_pattern (struct xmp_module *, int);
|
||||||
int libxmp_alloc_pattern_tracks (struct xmp_module *, int, int);
|
int libxmp_alloc_pattern_tracks (struct xmp_module *, int, int);
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
int libxmp_alloc_pattern_tracks_long(struct xmp_module *, int, int);
|
int libxmp_alloc_pattern_tracks_long(struct xmp_module *, int, int);
|
||||||
|
#endif
|
||||||
char *libxmp_instrument_name (struct xmp_module *, int, uint8 *, int);
|
char *libxmp_instrument_name (struct xmp_module *, int, uint8 *, int);
|
||||||
|
|
||||||
char *libxmp_copy_adjust (char *, uint8 *, int);
|
char *libxmp_copy_adjust (char *, uint8 *, int);
|
||||||
int libxmp_copy_name_for_fopen (char *, const char *, int);
|
int libxmp_copy_name_for_fopen (char *, const char *, int);
|
||||||
int libxmp_test_name (uint8 *, int);
|
int libxmp_test_name (const uint8 *, int, int);
|
||||||
void libxmp_read_title (HIO_HANDLE *, char *, int);
|
void libxmp_read_title (HIO_HANDLE *, char *, int);
|
||||||
void libxmp_set_xxh_defaults (struct xmp_module *);
|
void libxmp_set_xxh_defaults (struct xmp_module *);
|
||||||
void libxmp_decode_protracker_event (struct xmp_event *, uint8 *);
|
void libxmp_decode_protracker_event (struct xmp_event *, const uint8 *);
|
||||||
void libxmp_decode_noisetracker_event(struct xmp_event *, uint8 *);
|
void libxmp_decode_noisetracker_event(struct xmp_event *, const uint8 *);
|
||||||
void libxmp_disable_continue_fx (struct xmp_event *);
|
void libxmp_disable_continue_fx (struct xmp_event *);
|
||||||
int libxmp_check_filename_case (const char *, const char *, char *, int);
|
int libxmp_check_filename_case (const char *, const char *, char *, int);
|
||||||
void libxmp_get_instrument_path (struct module_data *, char *, int);
|
void libxmp_get_instrument_path (struct module_data *, char *, int);
|
||||||
|
@ -47,7 +53,10 @@ void libxmp_set_type (struct module_data *, const char *, ...);
|
||||||
int libxmp_load_sample (struct module_data *, HIO_HANDLE *, int,
|
int libxmp_load_sample (struct module_data *, HIO_HANDLE *, int,
|
||||||
struct xmp_sample *, const void *);
|
struct xmp_sample *, const void *);
|
||||||
void libxmp_free_sample (struct xmp_sample *);
|
void libxmp_free_sample (struct xmp_sample *);
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
void libxmp_schism_tracker_string (char *, size_t, int, int);
|
void libxmp_schism_tracker_string (char *, size_t, int, int);
|
||||||
|
void libxmp_apply_mpt_preamp (struct module_data *m);
|
||||||
|
#endif
|
||||||
|
|
||||||
extern uint8 libxmp_ord_xlat[];
|
extern uint8 libxmp_ord_xlat[];
|
||||||
extern const int libxmp_arch_vol_table[];
|
extern const int libxmp_arch_vol_table[];
|
||||||
|
|
|
@ -25,15 +25,13 @@ typedef struct MD5Context {
|
||||||
uint8 buffer[MD5_BLOCK_LENGTH]; /* input buffer */
|
uint8 buffer[MD5_BLOCK_LENGTH]; /* input buffer */
|
||||||
} MD5_CTX;
|
} MD5_CTX;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
LIBXMP_BEGIN_DECLS
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
void MD5Init(MD5_CTX *);
|
void MD5Init(MD5_CTX *);
|
||||||
void MD5Update(MD5_CTX *, const unsigned char *, size_t);
|
void MD5Update(MD5_CTX *, const unsigned char *, size_t);
|
||||||
void MD5Final(uint8[MD5_DIGEST_LENGTH], MD5_CTX *);
|
void MD5Final(uint8[MD5_DIGEST_LENGTH], MD5_CTX *);
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
LIBXMP_END_DECLS
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* LIBXMP_MD5_H */
|
#endif /* LIBXMP_MD5_H */
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
#define LIBXMP_MDATAIO_H
|
#define LIBXMP_MDATAIO_H
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <limits.h>
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
static inline ptrdiff_t CAN_READ(MFILE *m)
|
static inline ptrdiff_t CAN_READ(MFILE *m)
|
||||||
|
@ -43,7 +42,7 @@ static inline uint16 mread16l(MFILE *m, int *err)
|
||||||
} else {
|
} else {
|
||||||
m->pos += can_read;
|
m->pos += can_read;
|
||||||
if(err) *err = EOF;
|
if(err) *err = EOF;
|
||||||
return EOF;
|
return 0xffff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +57,7 @@ static inline uint16 mread16b(MFILE *m, int *err)
|
||||||
} else {
|
} else {
|
||||||
m->pos += can_read;
|
m->pos += can_read;
|
||||||
if(err) *err = EOF;
|
if(err) *err = EOF;
|
||||||
return EOF;
|
return 0xffff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +72,7 @@ static inline uint32 mread24l(MFILE *m, int *err)
|
||||||
} else {
|
} else {
|
||||||
m->pos += can_read;
|
m->pos += can_read;
|
||||||
if(err) *err = EOF;
|
if(err) *err = EOF;
|
||||||
return EOF;
|
return 0xffffffff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +87,7 @@ static inline uint32 mread24b(MFILE *m, int *err)
|
||||||
} else {
|
} else {
|
||||||
m->pos += can_read;
|
m->pos += can_read;
|
||||||
if(err) *err = EOF;
|
if(err) *err = EOF;
|
||||||
return EOF;
|
return 0xffffffff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +102,7 @@ static inline uint32 mread32l(MFILE *m, int *err)
|
||||||
} else {
|
} else {
|
||||||
m->pos += can_read;
|
m->pos += can_read;
|
||||||
if(err) *err = EOF;
|
if(err) *err = EOF;
|
||||||
return EOF;
|
return 0xffffffff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +117,7 @@ static inline uint32 mread32b(MFILE *m, int *err)
|
||||||
} else {
|
} else {
|
||||||
m->pos += can_read;
|
m->pos += can_read;
|
||||||
if(err) *err = EOF;
|
if(err) *err = EOF;
|
||||||
return EOF;
|
return 0xffffffff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,13 +46,16 @@ size_t mread(void *buf, size_t size, size_t num, MFILE *m)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (should_read > can_read) {
|
if (should_read > can_read) {
|
||||||
should_read = can_read;
|
memcpy(buf, m->start + m->pos, can_read);
|
||||||
|
m->pos += can_read;
|
||||||
|
|
||||||
|
return can_read / size;
|
||||||
|
} else {
|
||||||
|
memcpy(buf, m->start + m->pos, should_read);
|
||||||
|
m->pos += should_read;
|
||||||
|
|
||||||
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(buf, m->start + m->pos, should_read);
|
|
||||||
m->pos += should_read;
|
|
||||||
|
|
||||||
return should_read / size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -89,23 +92,26 @@ int meof(MFILE *m)
|
||||||
return CAN_READ(m) <= 0;
|
return CAN_READ(m) <= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
MFILE *mopen(const void *ptr, long size)
|
MFILE *mopen(const void *ptr, long size, int free_after_use)
|
||||||
{
|
{
|
||||||
MFILE *m;
|
MFILE *m;
|
||||||
|
|
||||||
m = (MFILE *)malloc(sizeof (MFILE));
|
m = (MFILE *) malloc(sizeof(MFILE));
|
||||||
if (m == NULL)
|
if (m == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
m->start = ptr;
|
m->start = (const unsigned char *)ptr;
|
||||||
m->pos = 0;
|
m->pos = 0;
|
||||||
m->size = size;
|
m->size = size;
|
||||||
|
m->free_after_use = free_after_use;
|
||||||
|
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mclose(MFILE *m)
|
int mclose(MFILE *m)
|
||||||
{
|
{
|
||||||
|
if (m->free_after_use)
|
||||||
|
free((void *)m->start);
|
||||||
free(m);
|
free(m);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,18 +2,18 @@
|
||||||
#define LIBXMP_MEMIO_H
|
#define LIBXMP_MEMIO_H
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const unsigned char *start;
|
const unsigned char *start;
|
||||||
ptrdiff_t pos;
|
ptrdiff_t pos;
|
||||||
ptrdiff_t size;
|
ptrdiff_t size;
|
||||||
|
int free_after_use;
|
||||||
} MFILE;
|
} MFILE;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
LIBXMP_BEGIN_DECLS
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
MFILE *mopen(const void *, long);
|
MFILE *mopen(const void *, long, int);
|
||||||
int mgetc(MFILE *stream);
|
int mgetc(MFILE *stream);
|
||||||
size_t mread(void *, size_t, size_t, MFILE *);
|
size_t mread(void *, size_t, size_t, MFILE *);
|
||||||
int mseek(MFILE *, long, int);
|
int mseek(MFILE *, long, int);
|
||||||
|
@ -21,8 +21,6 @@ long mtell(MFILE *);
|
||||||
int mclose(MFILE *);
|
int mclose(MFILE *);
|
||||||
int meof(MFILE *);
|
int meof(MFILE *);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
LIBXMP_END_DECLS
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Extended Module Player
|
/* Extended Module Player
|
||||||
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
|
* Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -99,10 +99,15 @@
|
||||||
*(buffer++) += smp_in * (old_vl >> 8); old_vl += delta_l; \
|
*(buffer++) += smp_in * (old_vl >> 8); old_vl += delta_l; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
/* IT's WAV output driver uses a clamp that seems to roughly match this:
|
||||||
|
* compare the WAV output of OpenMPT env-flt-max.it and filter-reset.it */
|
||||||
|
#define MIX_FILTER_CLAMP(a) CLAMP((a), -65536, 65535)
|
||||||
|
|
||||||
#define MIX_MONO_FILTER() do { \
|
#define MIX_MONO_FILTER() do { \
|
||||||
sl = (a0 * smp_in * vl + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \
|
sl = (a0 * smp_in + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \
|
||||||
|
MIX_FILTER_CLAMP(sl); \
|
||||||
fl2 = fl1; fl1 = sl; \
|
fl2 = fl1; fl1 = sl; \
|
||||||
*(buffer++) += sl; \
|
*(buffer++) += sl * vl; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define MIX_MONO_FILTER_AC() do { \
|
#define MIX_MONO_FILTER_AC() do { \
|
||||||
|
@ -122,12 +127,14 @@
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define MIX_STEREO_FILTER() do { \
|
#define MIX_STEREO_FILTER() do { \
|
||||||
sr = (a0 * smp_in * vr + b0 * fr1 + b1 * fr2) >> FILTER_SHIFT; \
|
sr = (a0 * smp_in + b0 * fr1 + b1 * fr2) >> FILTER_SHIFT; \
|
||||||
|
MIX_FILTER_CLAMP(sr); \
|
||||||
fr2 = fr1; fr1 = sr; \
|
fr2 = fr1; fr1 = sr; \
|
||||||
sl = (a0 * smp_in * vl + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \
|
sl = (a0 * smp_in + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \
|
||||||
|
MIX_FILTER_CLAMP(sl); \
|
||||||
fl2 = fl1; fl1 = sl; \
|
fl2 = fl1; fl1 = sl; \
|
||||||
*(buffer++) += sr; \
|
*(buffer++) += sr * vr; \
|
||||||
*(buffer++) += sl; \
|
*(buffer++) += sl * vl; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define MIX_STEREO_FILTER_AC() do { \
|
#define MIX_STEREO_FILTER_AC() do { \
|
||||||
|
@ -138,17 +145,19 @@
|
||||||
old_vl += delta_l; \
|
old_vl += delta_l; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define MIX_STEREO_FILTER_AC() do { \
|
/* For "nearest" to be nearest neighbor (instead of floor), the position needs
|
||||||
int vr = old_vr >> 8; \
|
* to be rounded. This only needs to be done once at the start of mixing, and
|
||||||
int vl = old_vl >> 8; \
|
* is required for reverse samples to round the same as forward samples.
|
||||||
MIX_STEREO_FILTER(); \
|
*/
|
||||||
old_vr += delta_r; \
|
#define NEAREST_ROUND() do { \
|
||||||
old_vl += delta_l; \
|
frac += (1 << (SMIX_SHIFT - 1)); \
|
||||||
|
pos += frac >> SMIX_SHIFT; \
|
||||||
|
frac &= SMIX_MASK; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define VAR_NORM(x) \
|
#define VAR_NORM(x) \
|
||||||
register int smp_in; \
|
register int smp_in; \
|
||||||
x *sptr = vi->sptr; \
|
x *sptr = (x *)vi->sptr; \
|
||||||
unsigned int pos = vi->pos; \
|
unsigned int pos = vi->pos; \
|
||||||
int frac = (1 << SMIX_SHIFT) * (vi->pos - (int)vi->pos)
|
int frac = (1 << SMIX_SHIFT) * (vi->pos - (int)vi->pos)
|
||||||
|
|
||||||
|
@ -194,6 +203,10 @@
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(disable:4457) /* shadowing */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Nearest neighbor mixers
|
* Nearest neighbor mixers
|
||||||
|
@ -204,6 +217,7 @@
|
||||||
MIXER(mono_8bit_nearest)
|
MIXER(mono_8bit_nearest)
|
||||||
{
|
{
|
||||||
VAR_NORM(int8);
|
VAR_NORM(int8);
|
||||||
|
NEAREST_ROUND();
|
||||||
|
|
||||||
LOOP { NEAREST_NEIGHBOR(); MIX_MONO(); UPDATE_POS(); }
|
LOOP { NEAREST_NEIGHBOR(); MIX_MONO(); UPDATE_POS(); }
|
||||||
}
|
}
|
||||||
|
@ -214,6 +228,7 @@ MIXER(mono_8bit_nearest)
|
||||||
MIXER(mono_16bit_nearest)
|
MIXER(mono_16bit_nearest)
|
||||||
{
|
{
|
||||||
VAR_NORM(int16);
|
VAR_NORM(int16);
|
||||||
|
NEAREST_ROUND();
|
||||||
|
|
||||||
LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_MONO(); UPDATE_POS(); }
|
LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_MONO(); UPDATE_POS(); }
|
||||||
}
|
}
|
||||||
|
@ -223,6 +238,7 @@ MIXER(mono_16bit_nearest)
|
||||||
MIXER(stereo_8bit_nearest)
|
MIXER(stereo_8bit_nearest)
|
||||||
{
|
{
|
||||||
VAR_NORM(int8);
|
VAR_NORM(int8);
|
||||||
|
NEAREST_ROUND();
|
||||||
|
|
||||||
LOOP { NEAREST_NEIGHBOR(); MIX_STEREO(); UPDATE_POS(); }
|
LOOP { NEAREST_NEIGHBOR(); MIX_STEREO(); UPDATE_POS(); }
|
||||||
}
|
}
|
||||||
|
@ -232,6 +248,7 @@ MIXER(stereo_8bit_nearest)
|
||||||
MIXER(stereo_16bit_nearest)
|
MIXER(stereo_16bit_nearest)
|
||||||
{
|
{
|
||||||
VAR_NORM(int16);
|
VAR_NORM(int16);
|
||||||
|
NEAREST_ROUND();
|
||||||
|
|
||||||
LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_STEREO(); UPDATE_POS(); }
|
LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_STEREO(); UPDATE_POS(); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Extended Module Player
|
/* Extended Module Player
|
||||||
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
|
* Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -45,8 +45,24 @@
|
||||||
#define LIM16_HI 32767
|
#define LIM16_HI 32767
|
||||||
#define LIM16_LO -32768
|
#define LIM16_LO -32768
|
||||||
|
|
||||||
|
struct loop_data
|
||||||
|
{
|
||||||
|
#define LOOP_PROLOGUE 1
|
||||||
|
#define LOOP_EPILOGUE 2
|
||||||
|
void *sptr;
|
||||||
|
int start;
|
||||||
|
int end;
|
||||||
|
int first_loop;
|
||||||
|
int _16bit;
|
||||||
|
int active;
|
||||||
|
uint32 prologue[LOOP_PROLOGUE];
|
||||||
|
uint32 epilogue[LOOP_EPILOGUE];
|
||||||
|
};
|
||||||
|
|
||||||
#define MIX_FN(x) void libxmp_mix_##x(struct mixer_voice *, int32 *, int, int, int, int, int, int, int)
|
#define MIX_FN(x) void libxmp_mix_##x(struct mixer_voice *, int32 *, int, int, int, int, int, int, int)
|
||||||
|
|
||||||
|
#define ANTICLICK_FPSHIFT 24
|
||||||
|
|
||||||
MIX_FN(mono_8bit_nearest);
|
MIX_FN(mono_8bit_nearest);
|
||||||
MIX_FN(mono_8bit_linear);
|
MIX_FN(mono_8bit_linear);
|
||||||
MIX_FN(mono_16bit_nearest);
|
MIX_FN(mono_16bit_nearest);
|
||||||
|
@ -209,8 +225,10 @@ static void do_anticlick(struct context_data *ctx, int voc, int32 *buf, int coun
|
||||||
struct player_data *p = &ctx->p;
|
struct player_data *p = &ctx->p;
|
||||||
struct mixer_data *s = &ctx->s;
|
struct mixer_data *s = &ctx->s;
|
||||||
struct mixer_voice *vi = &p->virt.voice_array[voc];
|
struct mixer_voice *vi = &p->virt.voice_array[voc];
|
||||||
int smp_l, smp_r, max_x2;
|
int smp_l, smp_r;
|
||||||
int discharge = s->ticksize >> ANTICLICK_SHIFT;
|
int discharge = s->ticksize >> ANTICLICK_SHIFT;
|
||||||
|
int stepmul, stepval;
|
||||||
|
uint32 stepmul_sq;
|
||||||
|
|
||||||
smp_r = vi->sright;
|
smp_r = vi->sright;
|
||||||
smp_l = vi->sleft;
|
smp_l = vi->sleft;
|
||||||
|
@ -231,14 +249,23 @@ static void do_anticlick(struct context_data *ctx, int voc, int32 *buf, int coun
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
max_x2 = count * count;
|
stepval = (1 << ANTICLICK_FPSHIFT) / count;
|
||||||
|
stepmul = stepval * count;
|
||||||
|
|
||||||
while (count--) {
|
if (~s->format & XMP_FORMAT_MONO) {
|
||||||
if (~s->format & XMP_FORMAT_MONO) {
|
while ((stepmul -= stepval) > 0) {
|
||||||
*buf++ += (count * (smp_r >> 10) / max_x2 * count) << 10;
|
/* Truncate to 16-bits of precision so the product is 32-bits. */
|
||||||
|
stepmul_sq = stepmul >> (ANTICLICK_FPSHIFT - 16);
|
||||||
|
stepmul_sq *= stepmul_sq;
|
||||||
|
*buf++ += (stepmul_sq * (int64)smp_r) >> 32;
|
||||||
|
*buf++ += (stepmul_sq * (int64)smp_l) >> 32;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while ((stepmul -= stepval) > 0) {
|
||||||
|
stepmul_sq = stepmul >> (ANTICLICK_FPSHIFT - 16);
|
||||||
|
stepmul_sq *= stepmul_sq;
|
||||||
|
*buf++ += (stepmul_sq * (int64)smp_l) >> 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
*buf++ += (count * (smp_l >> 10) / max_x2 * count) << 10;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,6 +283,7 @@ static void set_sample_end(struct context_data *ctx, int voc, int end)
|
||||||
|
|
||||||
if (end) {
|
if (end) {
|
||||||
SET_NOTE(NOTE_SAMPLE_END);
|
SET_NOTE(NOTE_SAMPLE_END);
|
||||||
|
vi->fidx &= ~FLAG_ACTIVE;
|
||||||
if (HAS_QUIRK(QUIRK_RSTCHN)) {
|
if (HAS_QUIRK(QUIRK_RSTCHN)) {
|
||||||
libxmp_virt_resetvoice(ctx, voc, 0);
|
libxmp_virt_resetvoice(ctx, voc, 0);
|
||||||
}
|
}
|
||||||
|
@ -264,45 +292,165 @@ static void set_sample_end(struct context_data *ctx, int voc, int end)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void adjust_voice_end(struct mixer_voice *vi, struct xmp_sample *xxs)
|
/* Back up sample data before and after loop and replace it for interpolation.
|
||||||
|
* TODO: use an overlap buffer like OpenMPT? This is easier, but a little dirty. */
|
||||||
|
static void init_sample_wraparound(struct mixer_data *s, struct loop_data *ld,
|
||||||
|
struct mixer_voice *vi, struct xmp_sample *xxs)
|
||||||
{
|
{
|
||||||
if (xxs->flg & XMP_SAMPLE_LOOP) {
|
int bidir;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!vi->sptr || s->interp == XMP_INTERP_NEAREST || (~xxs->flg & XMP_SAMPLE_LOOP)) {
|
||||||
|
ld->active = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ld->sptr = vi->sptr;
|
||||||
|
ld->start = vi->start;
|
||||||
|
ld->end = vi->end;
|
||||||
|
ld->first_loop = !(vi->flags & SAMPLE_LOOP);
|
||||||
|
ld->_16bit = (xxs->flg & XMP_SAMPLE_16BIT);
|
||||||
|
ld->active = 1;
|
||||||
|
|
||||||
|
bidir = vi->flags & VOICE_BIDIR;
|
||||||
|
|
||||||
|
if (ld->_16bit) {
|
||||||
|
uint16 *start = (uint16 *)vi->sptr + vi->start;
|
||||||
|
uint16 *end = (uint16 *)vi->sptr + vi->end;
|
||||||
|
|
||||||
|
if (!ld->first_loop) {
|
||||||
|
for (i = 0; i < LOOP_PROLOGUE; i++) {
|
||||||
|
int j = i - LOOP_PROLOGUE;
|
||||||
|
ld->prologue[i] = start[j];
|
||||||
|
start[j] = bidir ? start[-1 - j] : end[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < LOOP_EPILOGUE; i++) {
|
||||||
|
ld->epilogue[i] = end[i];
|
||||||
|
end[i] = bidir ? end[-1 - i] : start[i];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint8 *start = (uint8 *)vi->sptr + vi->start;
|
||||||
|
uint8 *end = (uint8 *)vi->sptr + vi->end;
|
||||||
|
|
||||||
|
if (!ld->first_loop) {
|
||||||
|
for (i = 0; i < LOOP_PROLOGUE; i++) {
|
||||||
|
int j = i - LOOP_PROLOGUE;
|
||||||
|
ld->prologue[i] = start[j];
|
||||||
|
start[j] = bidir ? start[-1 - j] : end[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < LOOP_EPILOGUE; i++) {
|
||||||
|
ld->epilogue[i] = end[i];
|
||||||
|
end[i] = bidir ? end[-1 - i] : start[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restore old sample data from before and after loop. */
|
||||||
|
static void reset_sample_wraparound(struct loop_data *ld)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!ld->active)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ld->_16bit) {
|
||||||
|
uint16 *start = (uint16 *)ld->sptr + ld->start;
|
||||||
|
uint16 *end = (uint16 *)ld->sptr + ld->end;
|
||||||
|
|
||||||
|
if (!ld->first_loop) {
|
||||||
|
for (i = 0; i < LOOP_PROLOGUE; i++)
|
||||||
|
start[i - LOOP_PROLOGUE] = ld->prologue[i];
|
||||||
|
}
|
||||||
|
for (i = 0; i < LOOP_EPILOGUE; i++)
|
||||||
|
end[i] = ld->epilogue[i];
|
||||||
|
} else {
|
||||||
|
uint8 *start = (uint8 *)ld->sptr + ld->start;
|
||||||
|
uint8 *end = (uint8 *)ld->sptr + ld->end;
|
||||||
|
|
||||||
|
if (!ld->first_loop) {
|
||||||
|
for (i = 0; i < LOOP_PROLOGUE; i++)
|
||||||
|
start[i - LOOP_PROLOGUE] = ld->prologue[i];
|
||||||
|
}
|
||||||
|
for (i = 0; i < LOOP_EPILOGUE; i++)
|
||||||
|
end[i] = ld->epilogue[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int has_active_sustain_loop(struct context_data *ctx, struct mixer_voice *vi,
|
||||||
|
struct xmp_sample *xxs)
|
||||||
|
{
|
||||||
|
#ifndef LIBXMP_CORE_DISABLE_IT
|
||||||
|
struct module_data *m = &ctx->m;
|
||||||
|
return vi->smp < m->mod.smp && (xxs->flg & XMP_SAMPLE_SLOOP) && (~vi->flags & VOICE_RELEASE);
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static int has_active_loop(struct context_data *ctx, struct mixer_voice *vi,
|
||||||
|
struct xmp_sample *xxs)
|
||||||
|
{
|
||||||
|
return (xxs->flg & XMP_SAMPLE_LOOP) || has_active_sustain_loop(ctx, vi, xxs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the voice endpoints based on current sample loop state. */
|
||||||
|
static void adjust_voice_end(struct context_data *ctx, struct mixer_voice *vi,
|
||||||
|
struct xmp_sample *xxs, struct extra_sample_data *xtra)
|
||||||
|
{
|
||||||
|
vi->flags &= ~VOICE_BIDIR;
|
||||||
|
|
||||||
|
if (xtra && has_active_sustain_loop(ctx, vi, xxs)) {
|
||||||
|
vi->start = xtra->sus;
|
||||||
|
vi->end = xtra->sue;
|
||||||
|
if (xxs->flg & XMP_SAMPLE_SLOOP_BIDIR) vi->flags |= VOICE_BIDIR;
|
||||||
|
|
||||||
|
} else if (xxs->flg & XMP_SAMPLE_LOOP) {
|
||||||
|
vi->start = xxs->lps;
|
||||||
if ((xxs->flg & XMP_SAMPLE_LOOP_FULL) && (~vi->flags & SAMPLE_LOOP)) {
|
if ((xxs->flg & XMP_SAMPLE_LOOP_FULL) && (~vi->flags & SAMPLE_LOOP)) {
|
||||||
vi->end = xxs->len;
|
vi->end = xxs->len;
|
||||||
} else {
|
} else {
|
||||||
vi->end = xxs->lpe;
|
vi->end = xxs->lpe;
|
||||||
|
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) vi->flags |= VOICE_BIDIR;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
vi->start = 0;
|
||||||
vi->end = xxs->len;
|
vi->end = xxs->len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void loop_reposition(struct context_data *ctx, struct mixer_voice *vi, struct xmp_sample *xxs)
|
static int loop_reposition(struct context_data *ctx, struct mixer_voice *vi,
|
||||||
|
struct xmp_sample *xxs, struct extra_sample_data *xtra)
|
||||||
{
|
{
|
||||||
#ifndef LIBXMP_CORE_DISABLE_IT
|
int loop_changed = !(vi->flags & SAMPLE_LOOP);
|
||||||
struct module_data *m = &ctx->m;
|
|
||||||
#endif
|
|
||||||
int loop_size = xxs->lpe - xxs->lps;
|
|
||||||
|
|
||||||
/* Reposition for next loop */
|
|
||||||
vi->pos -= loop_size; /* forward loop */
|
|
||||||
vi->end = xxs->lpe;
|
|
||||||
vi->flags |= SAMPLE_LOOP;
|
vi->flags |= SAMPLE_LOOP;
|
||||||
|
|
||||||
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
|
if(loop_changed)
|
||||||
vi->end += loop_size; /* unrolled loop */
|
adjust_voice_end(ctx, vi, xxs, xtra);
|
||||||
vi->pos -= loop_size; /* forward loop */
|
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_DISABLE_IT
|
if (~vi->flags & VOICE_BIDIR) {
|
||||||
/* OpenMPT Bidi-Loops.it: "In Impulse Tracker’s software mixer,
|
/* Reposition for next loop */
|
||||||
* ping-pong loops are shortened by one sample.
|
if (~vi->flags & VOICE_REVERSE)
|
||||||
*/
|
vi->pos -= vi->end - vi->start;
|
||||||
if (IS_PLAYER_MODE_IT()) {
|
else
|
||||||
vi->end--;
|
vi->pos += vi->end - vi->start;
|
||||||
vi->pos++;
|
} else {
|
||||||
|
/* Bidirectional loop: switch directions */
|
||||||
|
vi->flags ^= VOICE_REVERSE;
|
||||||
|
|
||||||
|
/* Wrap voice position around endpoint */
|
||||||
|
if (vi->flags & VOICE_REVERSE) {
|
||||||
|
/* OpenMPT Bidi-Loops.it: "In Impulse Tracker's software
|
||||||
|
* mixer, ping-pong loops are shortened by one sample."
|
||||||
|
*/
|
||||||
|
vi->pos = vi->end * 2 - ctx->s.bidir_adjust - vi->pos;
|
||||||
|
} else {
|
||||||
|
vi->pos = vi->start * 2 - vi->pos;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
return loop_changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -316,7 +464,10 @@ void libxmp_mixer_prepare(struct context_data *ctx)
|
||||||
|
|
||||||
s->ticksize = s->freq * m->time_factor * m->rrate / p->bpm / 1000;
|
s->ticksize = s->freq * m->time_factor * m->rrate / p->bpm / 1000;
|
||||||
|
|
||||||
bytelen = s->ticksize * sizeof(int);
|
if (s->ticksize < (1 << ANTICLICK_SHIFT))
|
||||||
|
s->ticksize = 1 << ANTICLICK_SHIFT;
|
||||||
|
|
||||||
|
bytelen = s->ticksize * sizeof(int32);
|
||||||
if (~s->format & XMP_FORMAT_MONO) {
|
if (~s->format & XMP_FORMAT_MONO) {
|
||||||
bytelen *= 2;
|
bytelen *= 2;
|
||||||
}
|
}
|
||||||
|
@ -331,13 +482,14 @@ void libxmp_mixer_softmixer(struct context_data *ctx)
|
||||||
struct mixer_data *s = &ctx->s;
|
struct mixer_data *s = &ctx->s;
|
||||||
struct module_data *m = &ctx->m;
|
struct module_data *m = &ctx->m;
|
||||||
struct xmp_module *mod = &m->mod;
|
struct xmp_module *mod = &m->mod;
|
||||||
|
struct extra_sample_data *xtra;
|
||||||
struct xmp_sample *xxs;
|
struct xmp_sample *xxs;
|
||||||
struct mixer_voice *vi;
|
struct mixer_voice *vi;
|
||||||
double step;
|
struct loop_data loop_data;
|
||||||
|
double step, step_dir;
|
||||||
int samples, size;
|
int samples, size;
|
||||||
int vol_l, vol_r, voc, usmp;
|
int vol, vol_l, vol_r, voc, usmp;
|
||||||
int prev_l, prev_r = 0;
|
int prev_l, prev_r = 0;
|
||||||
int lps, lpe;
|
|
||||||
int32 *buf_pos;
|
int32 *buf_pos;
|
||||||
MIX_FP mix_fn;
|
MIX_FP mix_fn;
|
||||||
MIX_FP *mixerset;
|
MIX_FP *mixerset;
|
||||||
|
@ -368,6 +520,13 @@ void libxmp_mixer_softmixer(struct context_data *ctx)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef LIBXMP_CORE_DISABLE_IT
|
||||||
|
/* OpenMPT Bidi-Loops.it: "In Impulse Tracker's software
|
||||||
|
* mixer, ping-pong loops are shortened by one sample."
|
||||||
|
*/
|
||||||
|
s->bidir_adjust = IS_PLAYER_MODE_IT() ? 1 : 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
libxmp_mixer_prepare(ctx);
|
libxmp_mixer_prepare(ctx);
|
||||||
|
|
||||||
for (voc = 0; voc < p->virt.maxvoc; voc++) {
|
for (voc = 0; voc < p->virt.maxvoc; voc++) {
|
||||||
|
@ -391,66 +550,56 @@ void libxmp_mixer_softmixer(struct context_data *ctx)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Negative positions can be left over from some
|
||||||
|
* loop edge cases. These can be safely clamped. */
|
||||||
|
if (vi->pos < 0.0)
|
||||||
|
vi->pos = 0.0;
|
||||||
|
|
||||||
vi->pos0 = vi->pos;
|
vi->pos0 = vi->pos;
|
||||||
|
|
||||||
buf_pos = s->buf32;
|
buf_pos = s->buf32;
|
||||||
|
vol = vi->vol;
|
||||||
|
|
||||||
|
/* Mix volume (S3M and IT) */
|
||||||
|
if (m->mvolbase > 0 && m->mvol != m->mvolbase) {
|
||||||
|
vol = vol * m->mvol / m->mvolbase;
|
||||||
|
}
|
||||||
|
|
||||||
if (vi->pan == PAN_SURROUND) {
|
if (vi->pan == PAN_SURROUND) {
|
||||||
vol_r = vi->vol * 0x80;
|
vol_r = vol * 0x80;
|
||||||
vol_l = -vi->vol * 0x80;
|
vol_l = -vol * 0x80;
|
||||||
} else {
|
} else {
|
||||||
vol_r = vi->vol * (0x80 - vi->pan);
|
vol_r = vol * (0x80 - vi->pan);
|
||||||
vol_l = vi->vol * (0x80 + vi->pan);
|
vol_l = vol * (0x80 + vi->pan);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vi->smp < mod->smp) {
|
if (vi->smp < mod->smp) {
|
||||||
xxs = &mod->xxs[vi->smp];
|
xxs = &mod->xxs[vi->smp];
|
||||||
|
xtra = &m->xtra[vi->smp];
|
||||||
c5spd = m->xtra[vi->smp].c5spd;
|
c5spd = m->xtra[vi->smp].c5spd;
|
||||||
} else {
|
} else {
|
||||||
xxs = &ctx->smix.xxs[vi->smp - mod->smp];
|
xxs = &ctx->smix.xxs[vi->smp - mod->smp];
|
||||||
|
xtra = NULL;
|
||||||
c5spd = m->c4rate;
|
c5spd = m->c4rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
step = C4_PERIOD * c5spd / s->freq / vi->period;
|
step = C4_PERIOD * c5spd / s->freq / vi->period;
|
||||||
|
|
||||||
if (step < 0.001) { /* otherwise m5v-nwlf.it crashes */
|
/* Don't allow <=0, otherwise m5v-nwlf.it crashes
|
||||||
|
* Extremely high values that can cause undefined float/int
|
||||||
|
* conversion are also possible for c5spd modules. */
|
||||||
|
if (step < 0.001 || step > (double)SHRT_MAX) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_DISABLE_IT
|
adjust_voice_end(ctx, vi, xxs, xtra);
|
||||||
if (xxs->flg & XMP_SAMPLE_SLOOP && vi->smp < mod->smp) {
|
init_sample_wraparound(s, &loop_data, vi, xxs);
|
||||||
if (~vi->flags & VOICE_RELEASE) {
|
|
||||||
if (vi->pos < m->xsmp[vi->smp].lpe) {
|
|
||||||
xxs = &m->xsmp[vi->smp];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
adjust_voice_end(vi, xxs);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
lps = xxs->lps;
|
|
||||||
lpe = xxs->lpe;
|
|
||||||
|
|
||||||
if (p->flags & XMP_FLAGS_FIXLOOP) {
|
|
||||||
lps >>= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
|
|
||||||
vi->end += lpe - lps;
|
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_DISABLE_IT
|
|
||||||
if (IS_PLAYER_MODE_IT()) {
|
|
||||||
vi->end--;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
rampsize = s->ticksize >> ANTICLICK_SHIFT;
|
rampsize = s->ticksize >> ANTICLICK_SHIFT;
|
||||||
delta_l = (vol_l - vi->old_vl) / rampsize;
|
delta_l = (vol_l - vi->old_vl) / rampsize;
|
||||||
delta_r = (vol_r - vi->old_vr) / rampsize;
|
delta_r = (vol_r - vi->old_vr) / rampsize;
|
||||||
|
|
||||||
usmp = 0;
|
for (size = usmp = s->ticksize; size > 0; ) {
|
||||||
for (size = s->ticksize; size > 0; ) {
|
|
||||||
int split_noloop = 0;
|
int split_noloop = 0;
|
||||||
|
|
||||||
if (p->xc_data[vi->chn].split) {
|
if (p->xc_data[vi->chn].split) {
|
||||||
|
@ -459,20 +608,34 @@ void libxmp_mixer_softmixer(struct context_data *ctx)
|
||||||
|
|
||||||
/* How many samples we can write before the loop break
|
/* How many samples we can write before the loop break
|
||||||
* or sample end... */
|
* or sample end... */
|
||||||
if (vi->pos >= vi->end) {
|
if (~vi->flags & VOICE_REVERSE) {
|
||||||
samples = 0;
|
if (vi->pos >= vi->end) {
|
||||||
usmp = 1;
|
samples = 0;
|
||||||
|
if (--usmp <= 0)
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
double c = ceil(((double)vi->end - vi->pos) / step);
|
||||||
|
/* ...inside the tick boundaries */
|
||||||
|
if (c > size) {
|
||||||
|
c = size;
|
||||||
|
}
|
||||||
|
samples = c;
|
||||||
|
}
|
||||||
|
step_dir = step;
|
||||||
} else {
|
} else {
|
||||||
int s = ceil(((double)vi->end - vi->pos) / step);
|
/* Reverse */
|
||||||
/* ...inside the tick boundaries */
|
if (vi->pos <= vi->start) {
|
||||||
if (s > size) {
|
samples = 0;
|
||||||
s = size;
|
if (--usmp <= 0)
|
||||||
}
|
break;
|
||||||
|
} else {
|
||||||
samples = s;
|
double c = ceil((vi->pos - (double)vi->start) / step);
|
||||||
if (samples > 0) {
|
if (c > size) {
|
||||||
usmp = 0;
|
c = size;
|
||||||
|
}
|
||||||
|
samples = c;
|
||||||
}
|
}
|
||||||
|
step_dir = -step;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vi->vol) {
|
if (vi->vol) {
|
||||||
|
@ -496,7 +659,7 @@ void libxmp_mixer_softmixer(struct context_data *ctx)
|
||||||
#ifndef LIBXMP_CORE_DISABLE_IT
|
#ifndef LIBXMP_CORE_DISABLE_IT
|
||||||
/* See OpenMPT env-flt-max.it */
|
/* See OpenMPT env-flt-max.it */
|
||||||
if (vi->filter.cutoff >= 0xfe &&
|
if (vi->filter.cutoff >= 0xfe &&
|
||||||
vi->filter.resonance == 0) {
|
vi->filter.resonance == 0) {
|
||||||
mixer_id &= ~FLAG_FILTER;
|
mixer_id &= ~FLAG_FILTER;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -521,7 +684,7 @@ void libxmp_mixer_softmixer(struct context_data *ctx)
|
||||||
|
|
||||||
if (mix_fn != NULL) {
|
if (mix_fn != NULL) {
|
||||||
mix_fn(vi, buf_pos, samples,
|
mix_fn(vi, buf_pos, samples,
|
||||||
vol_l >> 8, vol_r >> 8, step * (1 << SMIX_SHIFT), rsize, delta_l, delta_r);
|
vol_l >> 8, vol_r >> 8, step_dir * (1 << SMIX_SHIFT), rsize, delta_l, delta_r);
|
||||||
}
|
}
|
||||||
|
|
||||||
buf_pos += mix_size;
|
buf_pos += mix_size;
|
||||||
|
@ -537,31 +700,43 @@ void libxmp_mixer_softmixer(struct context_data *ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vi->pos += step * samples;
|
vi->pos += step_dir * samples;
|
||||||
|
|
||||||
/* No more samples in this tick */
|
/* No more samples in this tick */
|
||||||
size -= samples + usmp;
|
size -= samples;
|
||||||
if (size <= 0) {
|
if (size <= 0) {
|
||||||
if (xxs->flg & XMP_SAMPLE_LOOP) {
|
if (has_active_loop(ctx, vi, xxs)) {
|
||||||
if (vi->pos + step > vi->end) {
|
/* This isn't particularly important for
|
||||||
vi->pos += step;
|
* forward loops, but reverse loops need
|
||||||
loop_reposition(ctx, vi, xxs);
|
* to be corrected here to avoid their
|
||||||
|
* negative positions getting clamped
|
||||||
|
* in later ticks. */
|
||||||
|
if (((~vi->flags & VOICE_REVERSE) && vi->pos >= vi->end) ||
|
||||||
|
((vi->flags & VOICE_REVERSE) && vi->pos <= vi->start)) {
|
||||||
|
if (loop_reposition(ctx, vi, xxs, xtra)) {
|
||||||
|
reset_sample_wraparound(&loop_data);
|
||||||
|
init_sample_wraparound(s, &loop_data, vi, xxs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* First sample loop run */
|
/* First sample loop run */
|
||||||
if ((~xxs->flg & XMP_SAMPLE_LOOP) || split_noloop) {
|
if (!has_active_loop(ctx, vi, xxs) || split_noloop) {
|
||||||
do_anticlick(ctx, voc, buf_pos, size);
|
do_anticlick(ctx, voc, buf_pos, size);
|
||||||
set_sample_end(ctx, voc, 1);
|
set_sample_end(ctx, voc, 1);
|
||||||
size = 0;
|
size = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
loop_reposition(ctx, vi, xxs);
|
if (loop_reposition(ctx, vi, xxs, xtra)) {
|
||||||
|
reset_sample_wraparound(&loop_data);
|
||||||
|
init_sample_wraparound(s, &loop_data, vi, xxs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reset_sample_wraparound(&loop_data);
|
||||||
vi->old_vl = vol_l;
|
vi->old_vl = vol_l;
|
||||||
vi->old_vr = vol_r;
|
vi->old_vr = vol_r;
|
||||||
}
|
}
|
||||||
|
@ -581,7 +756,7 @@ void libxmp_mixer_softmixer(struct context_data *ctx)
|
||||||
downmix_int_8bit(s->buffer, s->buf32, size, s->amplify,
|
downmix_int_8bit(s->buffer, s->buf32, size, s->amplify,
|
||||||
s->format & XMP_FORMAT_UNSIGNED ? 0x80 : 0);
|
s->format & XMP_FORMAT_UNSIGNED ? 0x80 : 0);
|
||||||
} else {
|
} else {
|
||||||
downmix_int_16bit((int16 *)s->buffer, s->buf32, size,s->amplify,
|
downmix_int_16bit((int16 *)s->buffer, s->buf32, size, s->amplify,
|
||||||
s->format & XMP_FORMAT_UNSIGNED ? 0x8000 : 0);
|
s->format & XMP_FORMAT_UNSIGNED ? 0x8000 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -594,12 +769,14 @@ void libxmp_mixer_voicepos(struct context_data *ctx, int voc, double pos, int ac
|
||||||
struct module_data *m = &ctx->m;
|
struct module_data *m = &ctx->m;
|
||||||
struct mixer_voice *vi = &p->virt.voice_array[voc];
|
struct mixer_voice *vi = &p->virt.voice_array[voc];
|
||||||
struct xmp_sample *xxs;
|
struct xmp_sample *xxs;
|
||||||
int lps;
|
struct extra_sample_data *xtra;
|
||||||
|
|
||||||
if (vi->smp < m->mod.smp) {
|
if (vi->smp < m->mod.smp) {
|
||||||
xxs = &m->mod.xxs[vi->smp];
|
xxs = &m->mod.xxs[vi->smp];
|
||||||
|
xtra = &m->xtra[vi->smp];
|
||||||
} else {
|
} else {
|
||||||
xxs = &ctx->smix.xxs[vi->smp - m->mod.smp];
|
xxs = &ctx->smix.xxs[vi->smp - m->mod.smp];
|
||||||
|
xtra = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xxs->flg & XMP_SAMPLE_SYNTH) {
|
if (xxs->flg & XMP_SAMPLE_SYNTH) {
|
||||||
|
@ -608,29 +785,16 @@ void libxmp_mixer_voicepos(struct context_data *ctx, int voc, double pos, int ac
|
||||||
|
|
||||||
vi->pos = pos;
|
vi->pos = pos;
|
||||||
|
|
||||||
adjust_voice_end(vi, xxs);
|
adjust_voice_end(ctx, vi, xxs, xtra);
|
||||||
|
|
||||||
if (vi->pos >= vi->end) {
|
if (vi->pos >= vi->end) {
|
||||||
if (xxs->flg & XMP_SAMPLE_LOOP) {
|
vi->pos = vi->end;
|
||||||
vi->pos = xxs->lps;
|
/* Restart forward sample loops. */
|
||||||
} else {
|
if ((~vi->flags & VOICE_REVERSE) && has_active_loop(ctx, vi, xxs))
|
||||||
vi->pos = xxs->len;
|
loop_reposition(ctx, vi, xxs, xtra);
|
||||||
}
|
} else if ((vi->flags & VOICE_REVERSE) && vi->pos <= 0.1) {
|
||||||
}
|
/* Hack: 0 maps to the end for reversed samples. */
|
||||||
|
vi->pos = vi->end;
|
||||||
lps = xxs->lps;
|
|
||||||
if (p->flags & XMP_FLAGS_FIXLOOP) {
|
|
||||||
lps >>= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
|
|
||||||
vi->end += (xxs->lpe - lps);
|
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_DISABLE_IT
|
|
||||||
if (IS_PLAYER_MODE_IT()) {
|
|
||||||
vi->end--;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ac) {
|
if (ac) {
|
||||||
|
@ -650,12 +814,6 @@ double libxmp_mixer_getvoicepos(struct context_data *ctx, int voc)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
|
|
||||||
if (vi->pos >= xxs->lpe) {
|
|
||||||
return xxs->lpe - (vi->pos - xxs->lpe) - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return vi->pos;
|
return vi->pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -674,7 +832,7 @@ void libxmp_mixer_setpatch(struct context_data *ctx, int voc, int smp, int ac)
|
||||||
vi->smp = smp;
|
vi->smp = smp;
|
||||||
vi->vol = 0;
|
vi->vol = 0;
|
||||||
vi->pan = 0;
|
vi->pan = 0;
|
||||||
vi->flags &= ~SAMPLE_LOOP;
|
vi->flags &= ~(SAMPLE_LOOP | VOICE_REVERSE | VOICE_BIDIR);
|
||||||
|
|
||||||
vi->fidx = 0;
|
vi->fidx = 0;
|
||||||
|
|
||||||
|
@ -746,12 +904,42 @@ void libxmp_mixer_release(struct context_data *ctx, int voc, int rel)
|
||||||
struct mixer_voice *vi = &p->virt.voice_array[voc];
|
struct mixer_voice *vi = &p->virt.voice_array[voc];
|
||||||
|
|
||||||
if (rel) {
|
if (rel) {
|
||||||
|
#ifndef LIBXMP_CORE_DISABLE_IT
|
||||||
|
/* Cancel voice reverse when releasing an active sustain loop,
|
||||||
|
* unless the main loop is bidirectional. This is done both for
|
||||||
|
* bidirectional sustain loops and for forward sustain loops
|
||||||
|
* that have been reversed with MPT S9F Play Backward. */
|
||||||
|
if (~vi->flags & VOICE_RELEASE) {
|
||||||
|
struct xmp_sample *xxs = libxmp_get_sample(ctx, vi->smp);
|
||||||
|
|
||||||
|
if (has_active_sustain_loop(ctx, vi, xxs) &&
|
||||||
|
(~xxs->flg & XMP_SAMPLE_LOOP_BIDIR))
|
||||||
|
vi->flags &= ~VOICE_REVERSE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
vi->flags |= VOICE_RELEASE;
|
vi->flags |= VOICE_RELEASE;
|
||||||
} else {
|
} else {
|
||||||
vi->flags &= ~VOICE_RELEASE;
|
vi->flags &= ~VOICE_RELEASE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void libxmp_mixer_reverse(struct context_data *ctx, int voc, int rev)
|
||||||
|
{
|
||||||
|
struct player_data *p = &ctx->p;
|
||||||
|
struct mixer_voice *vi = &p->virt.voice_array[voc];
|
||||||
|
|
||||||
|
/* Don't reverse samples that have already ended */
|
||||||
|
if (~vi->fidx & FLAG_ACTIVE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rev) {
|
||||||
|
vi->flags |= VOICE_REVERSE;
|
||||||
|
} else {
|
||||||
|
vi->flags &= ~VOICE_REVERSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void libxmp_mixer_seteffect(struct context_data *ctx, int voc, int type, int val)
|
void libxmp_mixer_seteffect(struct context_data *ctx, int voc, int type, int val)
|
||||||
{
|
{
|
||||||
#ifndef LIBXMP_CORE_DISABLE_IT
|
#ifndef LIBXMP_CORE_DISABLE_IT
|
||||||
|
@ -813,11 +1001,12 @@ int libxmp_mixer_on(struct context_data *ctx, int rate, int format, int c4rate)
|
||||||
s->format = format;
|
s->format = format;
|
||||||
s->amplify = DEFAULT_AMPLIFY;
|
s->amplify = DEFAULT_AMPLIFY;
|
||||||
s->mix = DEFAULT_MIX;
|
s->mix = DEFAULT_MIX;
|
||||||
/* s->pbase = C4_PERIOD * c4rate / s->freq; */(void) c4rate;
|
/* s->pbase = C4_PERIOD * c4rate / s->freq; */
|
||||||
s->interp = XMP_INTERP_LINEAR; /* default interpolation type */
|
s->interp = XMP_INTERP_LINEAR; /* default interpolation type */
|
||||||
s->dsp = XMP_DSP_LOWPASS; /* enable filters by default */
|
s->dsp = XMP_DSP_LOWPASS; /* enable filters by default */
|
||||||
/* s->numvoc = SMIX_NUMVOC; */
|
/* s->numvoc = SMIX_NUMVOC; */
|
||||||
s->dtright = s->dtleft = 0;
|
s->dtright = s->dtleft = 0;
|
||||||
|
s->bidir_adjust = 0;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
|
@ -30,8 +30,10 @@ struct mixer_voice {
|
||||||
int fidx; /* mixer function index */
|
int fidx; /* mixer function index */
|
||||||
int ins; /* instrument number */
|
int ins; /* instrument number */
|
||||||
int smp; /* sample number */
|
int smp; /* sample number */
|
||||||
|
int start; /* loop start */
|
||||||
int end; /* loop end */
|
int end; /* loop end */
|
||||||
int act; /* nna info & status of voice */
|
int act; /* nna info & status of voice */
|
||||||
|
int key; /* key for DCA note check */
|
||||||
int old_vl; /* previous volume, left channel */
|
int old_vl; /* previous volume, left channel */
|
||||||
int old_vr; /* previous volume, right channel */
|
int old_vr; /* previous volume, right channel */
|
||||||
int sleft; /* last left sample output, in 32bit */
|
int sleft; /* last left sample output, in 32bit */
|
||||||
|
@ -39,6 +41,8 @@ struct mixer_voice {
|
||||||
#define VOICE_RELEASE (1 << 0)
|
#define VOICE_RELEASE (1 << 0)
|
||||||
#define ANTICLICK (1 << 1)
|
#define ANTICLICK (1 << 1)
|
||||||
#define SAMPLE_LOOP (1 << 2)
|
#define SAMPLE_LOOP (1 << 2)
|
||||||
|
#define VOICE_REVERSE (1 << 3)
|
||||||
|
#define VOICE_BIDIR (1 << 4)
|
||||||
int flags; /* flags */
|
int flags; /* flags */
|
||||||
void *sptr; /* sample pointer */
|
void *sptr; /* sample pointer */
|
||||||
#ifdef LIBXMP_PAULA_SIMULATOR
|
#ifdef LIBXMP_PAULA_SIMULATOR
|
||||||
|
@ -74,5 +78,6 @@ double libxmp_mixer_getvoicepos(struct context_data *, int);
|
||||||
void libxmp_mixer_setnote (struct context_data *, int, int);
|
void libxmp_mixer_setnote (struct context_data *, int, int);
|
||||||
void libxmp_mixer_setperiod (struct context_data *, int, double);
|
void libxmp_mixer_setperiod (struct context_data *, int, double);
|
||||||
void libxmp_mixer_release (struct context_data *, int, int);
|
void libxmp_mixer_release (struct context_data *, int, int);
|
||||||
|
void libxmp_mixer_reverse (struct context_data *, int, int);
|
||||||
|
|
||||||
#endif /* LIBXMP_MIXER_H */
|
#endif /* LIBXMP_MIXER_H */
|
||||||
|
|
|
@ -20,6 +20,9 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef LIBXMP_LOADERS_MOD_H
|
||||||
|
#define LIBXMP_LOADERS_MOD_H
|
||||||
|
|
||||||
struct mod_instrument {
|
struct mod_instrument {
|
||||||
uint8 name[22]; /* Instrument name */
|
uint8 name[22]; /* Instrument name */
|
||||||
uint16 size; /* Sample length in 16-bit words */
|
uint16 size; /* Sample length in 16-bit words */
|
||||||
|
@ -41,7 +44,6 @@ struct mod_header {
|
||||||
uint8 magic[4];
|
uint8 magic[4];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
/* Soundtracker 15-instrument module header */
|
/* Soundtracker 15-instrument module header */
|
||||||
|
|
||||||
|
@ -53,3 +55,5 @@ struct st_header {
|
||||||
uint8 order[128];
|
uint8 order[128];
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif /* LIBXMP_LOADERS_MOD_H */
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -135,14 +135,11 @@ static uint16 pt_period_table[16][36] = {
|
||||||
#ifndef M_LN2
|
#ifndef M_LN2
|
||||||
#define M_LN2 0.69314718055994530942
|
#define M_LN2 0.69314718055994530942
|
||||||
#endif
|
#endif
|
||||||
#if !defined(HAVE_ROUND) || defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DJGPP__)
|
|
||||||
static inline double libxmp_round(double val)
|
static inline double libxmp_round(double val)
|
||||||
{
|
{
|
||||||
return (val >= 0.0)? floor(val + 0.5) : ceil(val - 0.5);
|
return (val >= 0.0)? floor(val + 0.5) : ceil(val - 0.5);
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
#define libxmp_round round
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef LIBXMP_PAULA_SIMULATOR
|
#ifdef LIBXMP_PAULA_SIMULATOR
|
||||||
/* Get period from note using Protracker tuning */
|
/* Get period from note using Protracker tuning */
|
||||||
|
@ -186,13 +183,13 @@ double libxmp_note_to_period(struct context_data *ctx, int n, int f, double adj)
|
||||||
|
|
||||||
switch (m->period_type) {
|
switch (m->period_type) {
|
||||||
case PERIOD_LINEAR:
|
case PERIOD_LINEAR:
|
||||||
per = (240.0 - d) * 16; /* Linear */
|
per = (240.0 - d) * 16; /* Linear */
|
||||||
break;
|
break;
|
||||||
case PERIOD_CSPD:
|
case PERIOD_CSPD:
|
||||||
per = 8363.0 * pow(2, n / 12) / 32 + f; /* Hz */
|
per = 8363.0 * pow(2, n / 12.0) / 32 + f; /* Hz */
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
per = PERIOD_BASE / pow(2, d / 12); /* Amiga */
|
per = PERIOD_BASE / pow(2, d / 12); /* Amiga */
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
@ -228,7 +225,7 @@ int libxmp_period_to_bend(struct context_data *ctx, double p, int n, double adj)
|
||||||
struct module_data *m = &ctx->m;
|
struct module_data *m = &ctx->m;
|
||||||
double d;
|
double d;
|
||||||
|
|
||||||
if (n == 0) {
|
if (n == 0 || p < 0.1) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,7 +252,7 @@ void libxmp_c2spd_to_note(int c2spd, int *n, int *f)
|
||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
if (c2spd == 0) {
|
if (c2spd <= 0) {
|
||||||
*n = *f = 0;
|
*n = *f = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -264,3 +261,25 @@ void libxmp_c2spd_to_note(int c2spd, int *n, int *f)
|
||||||
*n = c / 128;
|
*n = c / 128;
|
||||||
*f = c % 128;
|
*f = c % 128;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
/* Gravis Ultrasound frequency increments in steps of Hz/1024, where Hz is the
|
||||||
|
* current rate of the card and is dependent on the active channel count.
|
||||||
|
* For <=14 channels, the rate is 44100. For 15 to 32 channels, the rate is
|
||||||
|
* round(14 * 44100 / active_channels).
|
||||||
|
*/
|
||||||
|
static const double GUS_rates[19] = {
|
||||||
|
/* <= 14 */ 44100.0,
|
||||||
|
/* 15-20 */ 41160.0, 38587.5, 36317.65, 34300.0, 32494.74, 30870.0,
|
||||||
|
/* 21-26 */ 29400.0, 28063.64, 26843.48, 25725.0, 24696.0, 23746.15,
|
||||||
|
/* 27-32 */ 22866.67, 22050.0, 21289.66, 20580.0, 19916.13, 19294.75
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Get a Gravis Ultrasound frequency offset in Hz for a given number of steps.
|
||||||
|
*/
|
||||||
|
double libxmp_gus_frequency_steps(int num_steps, int num_channels_active)
|
||||||
|
{
|
||||||
|
CLAMP(num_channels_active, 14, 32);
|
||||||
|
return (num_steps * GUS_rates[num_channels_active - 14]) / 1024.0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -20,5 +20,8 @@ double libxmp_note_to_period_mix (int, int);
|
||||||
int libxmp_period_to_note (int);
|
int libxmp_period_to_note (int);
|
||||||
int libxmp_period_to_bend (struct context_data *, double, int, double);
|
int libxmp_period_to_bend (struct context_data *, double, int, double);
|
||||||
void libxmp_c2spd_to_note (int, int *, int *);
|
void libxmp_c2spd_to_note (int, int *, int *);
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
double libxmp_gus_frequency_steps (int, int);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* LIBXMP_PERIOD_H */
|
#endif /* LIBXMP_PERIOD_H */
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Extended Module Player
|
/* Extended Module Player
|
||||||
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
|
* Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -53,7 +53,6 @@ static const struct retrig_control rval[] = {
|
||||||
{ 0, 1, 1 }, { 1, 1, 1 }, { 2, 1, 1 }, { 4, 1, 1 },
|
{ 0, 1, 1 }, { 1, 1, 1 }, { 2, 1, 1 }, { 4, 1, 1 },
|
||||||
{ 8, 1, 1 }, { 16, 1, 1 }, { 0, 3, 2 }, { 0, 2, 1 },
|
{ 8, 1, 1 }, { 16, 1, 1 }, { 0, 3, 2 }, { 0, 2, 1 },
|
||||||
{ 0, 0, 1 } /* Note cut */
|
{ 0, 0, 1 } /* Note cut */
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,15 +68,15 @@ static const struct retrig_control rval[] = {
|
||||||
static int check_envelope_end(struct xmp_envelope *env, int x)
|
static int check_envelope_end(struct xmp_envelope *env, int x)
|
||||||
{
|
{
|
||||||
int16 *data = env->data;
|
int16 *data = env->data;
|
||||||
int index;
|
int idx;
|
||||||
|
|
||||||
if (~env->flg & XMP_ENVELOPE_ON || env->npt <= 0)
|
if (~env->flg & XMP_ENVELOPE_ON || env->npt <= 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
index = (env->npt - 1) * 2;
|
idx = (env->npt - 1) * 2;
|
||||||
|
|
||||||
/* last node */
|
/* last node */
|
||||||
if (x >= data[index] || index == 0) {
|
if (x >= data[idx] || idx == 0) {
|
||||||
if (~env->flg & XMP_ENVELOPE_LOOP) {
|
if (~env->flg & XMP_ENVELOPE_LOOP) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -90,27 +89,30 @@ static int get_envelope(struct xmp_envelope *env, int x, int def)
|
||||||
{
|
{
|
||||||
int x1, x2, y1, y2;
|
int x1, x2, y1, y2;
|
||||||
int16 *data = env->data;
|
int16 *data = env->data;
|
||||||
int index;
|
int idx;
|
||||||
|
|
||||||
if (x < 0 || ~env->flg & XMP_ENVELOPE_ON || env->npt <= 0)
|
if (x < 0 || ~env->flg & XMP_ENVELOPE_ON || env->npt <= 0)
|
||||||
return def;
|
return def;
|
||||||
|
|
||||||
index = (env->npt - 1) * 2;
|
idx = (env->npt - 1) * 2;
|
||||||
|
|
||||||
x1 = data[index]; /* last node */
|
x1 = data[idx]; /* last node */
|
||||||
if (x >= x1 || index == 0) {
|
if (x >= x1 || idx == 0) {
|
||||||
return data[index + 1];
|
return data[idx + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
index -= 2;
|
idx -= 2;
|
||||||
x1 = data[index];
|
x1 = data[idx];
|
||||||
} while (index > 0 && x1 > x);
|
} while (idx > 0 && x1 > x);
|
||||||
|
|
||||||
/* interpolate */
|
/* interpolate */
|
||||||
y1 = data[index + 1];
|
y1 = data[idx + 1];
|
||||||
x2 = data[index + 2];
|
x2 = data[idx + 2];
|
||||||
y2 = data[index + 3];
|
y2 = data[idx + 3];
|
||||||
|
|
||||||
|
/* Interpolation requires x1 <= x <= x2 */
|
||||||
|
if (x < x1 || x2 < x1) return y1;
|
||||||
|
|
||||||
return x2 == x1 ? y2 : ((y2 - y1) * (x - x1) / (x2 - x1)) + y1;
|
return x2 == x1 ? y2 : ((y2 - y1) * (x - x1) / (x2 - x1)) + y1;
|
||||||
}
|
}
|
||||||
|
@ -232,14 +234,14 @@ static int update_envelope(struct xmp_envelope *env, int x, int release, int key
|
||||||
static int check_envelope_fade(struct xmp_envelope *env, int x)
|
static int check_envelope_fade(struct xmp_envelope *env, int x)
|
||||||
{
|
{
|
||||||
int16 *data = env->data;
|
int16 *data = env->data;
|
||||||
int index;
|
int idx;
|
||||||
|
|
||||||
if (~env->flg & XMP_ENVELOPE_ON)
|
if (~env->flg & XMP_ENVELOPE_ON)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
index = (env->npt - 1) * 2; /* last node */
|
idx = (env->npt - 1) * 2; /* last node */
|
||||||
if (x > data[index]) {
|
if (x > data[idx]) {
|
||||||
if (data[index + 1] == 0)
|
if (data[idx + 1] == 0)
|
||||||
return -1;
|
return -1;
|
||||||
else
|
else
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -249,6 +251,218 @@ static int check_envelope_fade(struct xmp_envelope *env, int x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef LIBXMP_CORE_DISABLE_IT
|
||||||
|
|
||||||
|
/* Impulse Tracker's filter effects are implemented using its MIDI macros.
|
||||||
|
* Any module can customize these and they are parameterized using various
|
||||||
|
* player and mixer values, which requires parsing them here instead of in
|
||||||
|
* the loader. Since they're MIDI macros, they can contain actual MIDI junk
|
||||||
|
* that needs to be skipped, and one macro may have multiple IT commands. */
|
||||||
|
|
||||||
|
struct midi_stream
|
||||||
|
{
|
||||||
|
const char *pos;
|
||||||
|
int buffer;
|
||||||
|
int param;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int midi_nibble(struct context_data *ctx, struct channel_data *xc,
|
||||||
|
int chn, struct midi_stream *in)
|
||||||
|
{
|
||||||
|
struct xmp_instrument *xxi;
|
||||||
|
struct mixer_voice *vi;
|
||||||
|
int voc, val, byte = -1;
|
||||||
|
if (in->buffer >= 0) {
|
||||||
|
val = in->buffer;
|
||||||
|
in->buffer = -1;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*in->pos) {
|
||||||
|
val = *(in->pos)++;
|
||||||
|
if (val >= '0' && val <= '9') return val - '0';
|
||||||
|
if (val >= 'A' && val <= 'F') return val - 'A' + 10;
|
||||||
|
switch (val) {
|
||||||
|
case 'z': /* Macro parameter */
|
||||||
|
byte = in->param;
|
||||||
|
break;
|
||||||
|
case 'n': /* Host key */
|
||||||
|
byte = xc->key & 0x7f;
|
||||||
|
break;
|
||||||
|
case 'h': /* Host channel */
|
||||||
|
byte = chn;
|
||||||
|
break;
|
||||||
|
case 'o': /* Offset effect memory */
|
||||||
|
/* Intentionally not clamped, see ZxxSecrets.it */
|
||||||
|
byte = xc->offset.memory;
|
||||||
|
break;
|
||||||
|
case 'm': /* Voice reverse flag */
|
||||||
|
voc = libxmp_virt_mapchannel(ctx, chn);
|
||||||
|
vi = (voc >= 0) ? &ctx->p.virt.voice_array[voc] : NULL;
|
||||||
|
byte = vi ? !!(vi->flags & VOICE_REVERSE) : 0;
|
||||||
|
break;
|
||||||
|
case 'v': /* Note velocity */
|
||||||
|
xxi = libxmp_get_instrument(ctx, xc->ins);
|
||||||
|
byte = ((uint32)ctx->p.gvol *
|
||||||
|
(uint32)xc->volume *
|
||||||
|
(uint32)xc->mastervol *
|
||||||
|
(uint32)xc->gvl *
|
||||||
|
(uint32)(xxi ? xxi->vol : 0x40)) >> 24UL;
|
||||||
|
CLAMP(byte, 1, 127);
|
||||||
|
break;
|
||||||
|
case 'u': /* Computed velocity */
|
||||||
|
byte = xc->macro.finalvol >> 3;
|
||||||
|
CLAMP(byte, 1, 127);
|
||||||
|
break;
|
||||||
|
case 'x': /* Note panning */
|
||||||
|
byte = xc->macro.notepan >> 1;
|
||||||
|
CLAMP(byte, 0, 127);
|
||||||
|
break;
|
||||||
|
case 'y': /* Computed panning */
|
||||||
|
byte = xc->info_finalpan >> 1;
|
||||||
|
CLAMP(byte, 0, 127);
|
||||||
|
break;
|
||||||
|
case 'a': /* Ins MIDI Bank hi */
|
||||||
|
case 'b': /* Ins MIDI Bank lo */
|
||||||
|
case 'p': /* Ins MIDI Program */
|
||||||
|
case 's': /* MPT: SysEx checksum */
|
||||||
|
byte = 0;
|
||||||
|
break;
|
||||||
|
case 'c': /* Ins MIDI Channel */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Byte output */
|
||||||
|
if (byte >= 0) {
|
||||||
|
in->buffer = byte & 0xf;
|
||||||
|
return (byte >> 4) & 0xf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int midi_byte(struct context_data *ctx, struct channel_data *xc,
|
||||||
|
int chn, struct midi_stream *in)
|
||||||
|
{
|
||||||
|
int a = midi_nibble(ctx, xc, chn, in);
|
||||||
|
int b = midi_nibble(ctx, xc, chn, in);
|
||||||
|
return (a >= 0 && b >= 0) ? (a << 4) | b : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void apply_midi_macro_effect(struct channel_data *xc, int type, int val)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case 0: /* Filter cutoff */
|
||||||
|
xc->filter.cutoff = val << 1;
|
||||||
|
break;
|
||||||
|
case 1: /* Filter resonance */
|
||||||
|
xc->filter.resonance = val << 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void execute_midi_macro(struct context_data *ctx, struct channel_data *xc,
|
||||||
|
int chn, struct midi_macro *midi, int param)
|
||||||
|
{
|
||||||
|
struct midi_stream in;
|
||||||
|
int byte, cmd, val;
|
||||||
|
|
||||||
|
in.pos = midi->data;
|
||||||
|
in.buffer = -1;
|
||||||
|
in.param = param;
|
||||||
|
|
||||||
|
while (*in.pos) {
|
||||||
|
/* Very simple MIDI 1.0 parser--most bytes can just be ignored
|
||||||
|
* (or passed through, if libxmp gets MIDI output). All bytes
|
||||||
|
* with bit 7 are statuses which interrupt unfinished messages
|
||||||
|
* ("Data Types: Status Bytes") or are real time messages.
|
||||||
|
* This holds even for SysEx messages, which end at ANY non-
|
||||||
|
* real time status ("System Common Messages: EOX").
|
||||||
|
*
|
||||||
|
* IT intercepts internal "messages" that begin with F0 F0,
|
||||||
|
* which in MIDI is a useless zero-length SysEx followed by
|
||||||
|
* a second SysEx. They are four bytes long including F0 F0,
|
||||||
|
* and shouldn't be passed through. OpenMPT also uses F0 F1.
|
||||||
|
*/
|
||||||
|
cmd = -1;
|
||||||
|
byte = midi_byte(ctx, xc, chn, &in);
|
||||||
|
if (byte == 0xf0) {
|
||||||
|
byte = midi_byte(ctx, xc, chn, &in);
|
||||||
|
if (byte == 0xf0 || byte == 0xf1)
|
||||||
|
cmd = byte & 0xf;
|
||||||
|
}
|
||||||
|
if (cmd < 0) {
|
||||||
|
if (byte == 0xfa || byte == 0xfc || byte == 0xff) {
|
||||||
|
/* These real time statuses can appear anywhere
|
||||||
|
* (even in SysEx) and reset the channel filter
|
||||||
|
* params. See: OpenMPT ZxxSecrets.it */
|
||||||
|
apply_midi_macro_effect(xc, 0, 127);
|
||||||
|
apply_midi_macro_effect(xc, 1, 0);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cmd = midi_byte(ctx, xc, chn, &in) | (cmd << 8);
|
||||||
|
val = midi_byte(ctx, xc, chn, &in);
|
||||||
|
if (cmd < 0 || cmd >= 0x80 || val < 0 || val >= 0x80) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
apply_midi_macro_effect(xc, cmd, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This needs to occur before all process_* functions:
|
||||||
|
* - It modifies the filter parameters, used by process_frequency.
|
||||||
|
* - process_volume and process_pan apply slide effects, which the
|
||||||
|
* filter parameters expect to occur after macro effect parsing. */
|
||||||
|
static void update_midi_macro(struct context_data *ctx, int chn)
|
||||||
|
{
|
||||||
|
struct player_data *p = &ctx->p;
|
||||||
|
struct module_data *m = &ctx->m;
|
||||||
|
struct channel_data *xc = &p->xc_data[chn];
|
||||||
|
struct midi_macro_data *midicfg = m->midi;
|
||||||
|
struct midi_macro *macro;
|
||||||
|
int val;
|
||||||
|
|
||||||
|
if (TEST(MIDI_MACRO) && HAS_QUIRK(QUIRK_FILTER)) {
|
||||||
|
if (xc->macro.slide > 0) {
|
||||||
|
xc->macro.val += xc->macro.slide;
|
||||||
|
if (xc->macro.val > xc->macro.target) {
|
||||||
|
xc->macro.val = xc->macro.target;
|
||||||
|
xc->macro.slide = 0;
|
||||||
|
}
|
||||||
|
} else if (xc->macro.slide < 0) {
|
||||||
|
xc->macro.val += xc->macro.slide;
|
||||||
|
if (xc->macro.val < xc->macro.target) {
|
||||||
|
xc->macro.val = xc->macro.target;
|
||||||
|
xc->macro.slide = 0;
|
||||||
|
}
|
||||||
|
} else if (p->frame) {
|
||||||
|
/* Execute non-smooth macros on frame 0 only */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = (int)xc->macro.val;
|
||||||
|
if (val >= 0x80) {
|
||||||
|
if (midicfg) {
|
||||||
|
macro = &midicfg->fixed[val - 0x80];
|
||||||
|
execute_midi_macro(ctx, xc, chn, macro, val);
|
||||||
|
} else if (val < 0x90) {
|
||||||
|
/* Default fixed macro: set resonance */
|
||||||
|
apply_midi_macro_effect(xc, 1, (val - 0x80) << 3);
|
||||||
|
}
|
||||||
|
} else if (midicfg) {
|
||||||
|
macro = &midicfg->param[xc->macro.active];
|
||||||
|
execute_midi_macro(ctx, xc, chn, macro, val);
|
||||||
|
} else if (xc->macro.active == 0) {
|
||||||
|
/* Default parameterized macro 0: set filter cutoff */
|
||||||
|
apply_midi_macro_effect(xc, 0, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* LIBXMP_CORE_DISABLE_IT */
|
||||||
|
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
|
||||||
/* From http://www.un4seen.com/forum/?topic=7554.0
|
/* From http://www.un4seen.com/forum/?topic=7554.0
|
||||||
|
@ -268,23 +482,39 @@ static const int invloop_table[] = {
|
||||||
0, 5, 6, 7, 8, 10, 11, 13, 16, 19, 22, 26, 32, 43, 64, 128
|
0, 5, 6, 7, 8, 10, 11, 13, 16, 19, 22, 26, 32, 43, 64, 128
|
||||||
};
|
};
|
||||||
|
|
||||||
static void update_invloop(struct module_data *m, struct channel_data *xc)
|
static void update_invloop(struct context_data *ctx, struct channel_data *xc)
|
||||||
{
|
{
|
||||||
struct xmp_sample *xxs = &m->mod.xxs[xc->smp];
|
struct xmp_sample *xxs = libxmp_get_sample(ctx, xc->smp);
|
||||||
int len;
|
struct module_data *m = &ctx->m;
|
||||||
|
int lps, len = -1;
|
||||||
|
|
||||||
xc->invloop.count += invloop_table[xc->invloop.speed];
|
xc->invloop.count += invloop_table[xc->invloop.speed];
|
||||||
|
|
||||||
if ((xxs->flg & XMP_SAMPLE_LOOP) && xc->invloop.count >= 128) {
|
if (xxs != NULL) {
|
||||||
|
if (xxs->flg & XMP_SAMPLE_LOOP) {
|
||||||
|
lps = xxs->lps;
|
||||||
|
len = xxs->lpe - lps;
|
||||||
|
} else if (xxs->flg & XMP_SAMPLE_SLOOP) {
|
||||||
|
/* Some formats that support invert loop use sustain
|
||||||
|
* loops instead (Digital Symphony). */
|
||||||
|
lps = m->xtra[xc->smp].sus;
|
||||||
|
len = m->xtra[xc->smp].sue - lps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len >= 0 && xc->invloop.count >= 128) {
|
||||||
xc->invloop.count = 0;
|
xc->invloop.count = 0;
|
||||||
len = xxs->lpe - xxs->lps;
|
|
||||||
|
|
||||||
if (++xc->invloop.pos > len) {
|
if (++xc->invloop.pos > len) {
|
||||||
xc->invloop.pos = 0;
|
xc->invloop.pos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (xxs->data == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (~xxs->flg & XMP_SAMPLE_16BIT) {
|
if (~xxs->flg & XMP_SAMPLE_16BIT) {
|
||||||
xxs->data[xxs->lps + xc->invloop.pos] ^= 0xff;
|
xxs->data[lps + xc->invloop.pos] ^= 0xff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -624,7 +854,7 @@ static int tremor_s3m(struct context_data *ctx, int chn, int finalvol)
|
||||||
* Update channel data
|
* Update channel data
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define DOENV_RELEASE ((TEST_NOTE(NOTE_RELEASE) || act == VIRT_ACTION_OFF))
|
#define DOENV_RELEASE ((TEST_NOTE(NOTE_ENV_RELEASE) || act == VIRT_ACTION_OFF))
|
||||||
|
|
||||||
static void process_volume(struct context_data *ctx, int chn, int act)
|
static void process_volume(struct context_data *ctx, int chn, int act)
|
||||||
{
|
{
|
||||||
|
@ -648,7 +878,7 @@ static void process_volume(struct context_data *ctx, int chn, int act)
|
||||||
/* If IT, only apply fadeout on note release if we don't
|
/* If IT, only apply fadeout on note release if we don't
|
||||||
* have envelope, or if we have envelope loop
|
* have envelope, or if we have envelope loop
|
||||||
*/
|
*/
|
||||||
if (TEST_NOTE(NOTE_RELEASE) || act == VIRT_ACTION_OFF) {
|
if (TEST_NOTE(NOTE_ENV_RELEASE) || act == VIRT_ACTION_OFF) {
|
||||||
if ((~instrument->aei.flg & XMP_ENVELOPE_ON) ||
|
if ((~instrument->aei.flg & XMP_ENVELOPE_ON) ||
|
||||||
(instrument->aei.flg & XMP_ENVELOPE_LOOP)) {
|
(instrument->aei.flg & XMP_ENVELOPE_LOOP)) {
|
||||||
fade = 1;
|
fade = 1;
|
||||||
|
@ -656,29 +886,30 @@ static void process_volume(struct context_data *ctx, int chn, int act)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (~instrument->aei.flg & XMP_ENVELOPE_ON) {
|
if (~instrument->aei.flg & XMP_ENVELOPE_ON) {
|
||||||
if (TEST_NOTE(NOTE_RELEASE)) {
|
if (TEST_NOTE(NOTE_ENV_RELEASE)) {
|
||||||
xc->fadeout = 0;
|
xc->fadeout = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TEST_NOTE(NOTE_RELEASE) || act == VIRT_ACTION_OFF) {
|
if (TEST_NOTE(NOTE_ENV_RELEASE) || act == VIRT_ACTION_OFF) {
|
||||||
fade = 1;
|
fade = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TEST_NOTE(NOTE_FADEOUT) || act == VIRT_ACTION_FADE) {
|
if (!TEST_PER(VENV_PAUSE)) {
|
||||||
fade = 1;
|
xc->v_idx = update_envelope(&instrument->aei, xc->v_idx,
|
||||||
|
DOENV_RELEASE, TEST(KEY_OFF), IS_PLAYER_MODE_IT());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fade) {
|
vol_envelope = get_envelope(&instrument->aei, xc->v_idx, 64);
|
||||||
if (xc->fadeout > xc->ins_fade) {
|
if (check_envelope_end(&instrument->aei, xc->v_idx)) {
|
||||||
xc->fadeout -= xc->ins_fade;
|
if (vol_envelope == 0) {
|
||||||
} else {
|
|
||||||
xc->fadeout = 0;
|
|
||||||
SET_NOTE(NOTE_END);
|
SET_NOTE(NOTE_END);
|
||||||
}
|
}
|
||||||
|
SET_NOTE(NOTE_ENV_END);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* IT starts fadeout automatically at the end of the volume envelope. */
|
||||||
switch (check_envelope_fade(&instrument->aei, xc->v_idx)) {
|
switch (check_envelope_fade(&instrument->aei, xc->v_idx)) {
|
||||||
case -1:
|
case -1:
|
||||||
SET_NOTE(NOTE_END);
|
SET_NOTE(NOTE_END);
|
||||||
|
@ -694,17 +925,19 @@ static void process_volume(struct context_data *ctx, int chn, int act)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TEST_PER(VENV_PAUSE)) {
|
/* IT envelope fadeout starts immediately after the envelope tick,
|
||||||
xc->v_idx = update_envelope(&instrument->aei, xc->v_idx,
|
* so process fadeout after the volume envelope. */
|
||||||
DOENV_RELEASE, TEST(KEY_OFF), IS_PLAYER_MODE_IT());
|
if (TEST_NOTE(NOTE_FADEOUT) || act == VIRT_ACTION_FADE) {
|
||||||
|
fade = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
vol_envelope = get_envelope(&instrument->aei, xc->v_idx, 64);
|
if (fade) {
|
||||||
if (check_envelope_end(&instrument->aei, xc->v_idx)) {
|
if (xc->fadeout > xc->ins_fade) {
|
||||||
if (vol_envelope == 0) {
|
xc->fadeout -= xc->ins_fade;
|
||||||
|
} else {
|
||||||
|
xc->fadeout = 0;
|
||||||
SET_NOTE(NOTE_END);
|
SET_NOTE(NOTE_END);
|
||||||
}
|
}
|
||||||
SET_NOTE(NOTE_ENV_END);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If note ended in background channel, we can safely reset it */
|
/* If note ended in background channel, we can safely reset it */
|
||||||
|
@ -762,6 +995,9 @@ static void process_volume(struct context_data *ctx, int chn, int act)
|
||||||
} else {
|
} else {
|
||||||
finalvol = tremor_s3m(ctx, chn, finalvol);
|
finalvol = tremor_s3m(ctx, chn, finalvol);
|
||||||
}
|
}
|
||||||
|
#ifndef LIBXMP_CORE_DISABLE_IT
|
||||||
|
xc->macro.finalvol = finalvol;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (chn < m->mod.chn) {
|
if (chn < m->mod.chn) {
|
||||||
finalvol = finalvol * p->master_vol / 100;
|
finalvol = finalvol * p->master_vol / 100;
|
||||||
|
@ -946,12 +1182,12 @@ static void process_frequency(struct context_data *ctx, int chn, int act)
|
||||||
|
|
||||||
/* For xmp_get_frame_info() */
|
/* For xmp_get_frame_info() */
|
||||||
xc->info_pitchbend = linear_bend >> 7;
|
xc->info_pitchbend = linear_bend >> 7;
|
||||||
xc->info_period = final_period * 4096;
|
xc->info_period = MIN(final_period * 4096, INT_MAX);
|
||||||
|
|
||||||
if (IS_PERIOD_MODRNG()) {
|
if (IS_PERIOD_MODRNG()) {
|
||||||
CLAMP(xc->info_period,
|
const double min_period = libxmp_note_to_period(ctx, MAX_NOTE_MOD, xc->finetune, 0) * 4096;
|
||||||
libxmp_note_to_period(ctx, MAX_NOTE_MOD, xc->finetune, 0) * 4096,
|
const double max_period = libxmp_note_to_period(ctx, MIN_NOTE_MOD, xc->finetune, 0) * 4096;
|
||||||
libxmp_note_to_period(ctx, MIN_NOTE_MOD, xc->finetune, 0) * 4096);
|
CLAMP(xc->info_period, min_period, max_period);
|
||||||
} else if (xc->info_period < (1 << 12)) {
|
} else if (xc->info_period < (1 << 12)) {
|
||||||
xc->info_period = (1 << 12);
|
xc->info_period = (1 << 12);
|
||||||
}
|
}
|
||||||
|
@ -977,18 +1213,22 @@ static void process_frequency(struct context_data *ctx, int chn, int act)
|
||||||
|
|
||||||
if (cutoff > 0xff) {
|
if (cutoff > 0xff) {
|
||||||
cutoff = 0xff;
|
cutoff = 0xff;
|
||||||
} else if (cutoff < 0xff) {
|
}
|
||||||
|
/* IT: cutoff 127 + resonance 0 turns off the filter, but this
|
||||||
|
* is only applied when playing a new note without toneporta.
|
||||||
|
* All other combinations take effect immediately.
|
||||||
|
* See OpenMPT filter-reset.it, filter-reset-carry.it */
|
||||||
|
if (cutoff < 0xfe || resonance > 0 || xc->filter.can_disable) {
|
||||||
int a0, b0, b1;
|
int a0, b0, b1;
|
||||||
libxmp_filter_setup(s->freq, cutoff, resonance, &a0, &b0, &b1);
|
libxmp_filter_setup(s->freq, cutoff, resonance, &a0, &b0, &b1);
|
||||||
libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_A0, a0);
|
libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_A0, a0);
|
||||||
libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_B0, b0);
|
libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_B0, b0);
|
||||||
libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_B1, b1);
|
libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_B1, b1);
|
||||||
libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_RESONANCE, resonance);
|
libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_RESONANCE, resonance);
|
||||||
|
libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_CUTOFF, cutoff);
|
||||||
|
xc->filter.can_disable = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Always set cutoff */
|
|
||||||
libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_CUTOFF, cutoff);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1018,6 +1258,7 @@ static void process_pan(struct context_data *ctx, int chn, int act)
|
||||||
libxmp_lfo_update(&xc->panbrello.lfo);
|
libxmp_lfo_update(&xc->panbrello.lfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
xc->macro.notepan = xc->pan.val + panbrello + 0x80;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
channel_pan = xc->pan.val;
|
channel_pan = xc->pan.val;
|
||||||
|
@ -1080,13 +1321,19 @@ static void update_volume(struct context_data *ctx, int chn)
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
if (TEST_PER(VOL_SLIDE)) {
|
if (TEST_PER(VOL_SLIDE)) {
|
||||||
if (xc->vol.slide > 0 && xc->volume > m->volbase) {
|
if (xc->vol.slide > 0) {
|
||||||
xc->volume = m->volbase;
|
int target = MAX(xc->vol.target - 1, m->volbase);
|
||||||
RESET_PER(VOL_SLIDE);
|
if (xc->volume > target) {
|
||||||
|
xc->volume = target;
|
||||||
|
RESET_PER(VOL_SLIDE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (xc->vol.slide < 0 && xc->volume < 0) {
|
if (xc->vol.slide < 0) {
|
||||||
xc->volume = 0;
|
int target = xc->vol.target > 0 ? MIN(0, xc->vol.target - 1) : 0;
|
||||||
RESET_PER(VOL_SLIDE);
|
if (xc->volume < target) {
|
||||||
|
xc->volume = target;
|
||||||
|
RESET_PER(VOL_SLIDE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -1199,10 +1446,11 @@ static void update_frequency(struct context_data *ctx, int chn)
|
||||||
case PERIOD_LINEAR:
|
case PERIOD_LINEAR:
|
||||||
CLAMP(xc->period, MIN_PERIOD_L, MAX_PERIOD_L);
|
CLAMP(xc->period, MIN_PERIOD_L, MAX_PERIOD_L);
|
||||||
break;
|
break;
|
||||||
case PERIOD_MODRNG:
|
case PERIOD_MODRNG: {
|
||||||
CLAMP(xc->period,
|
const double min_period = libxmp_note_to_period(ctx, MAX_NOTE_MOD, xc->finetune, 0);
|
||||||
libxmp_note_to_period(ctx, MAX_NOTE_MOD, xc->finetune, 0),
|
const double max_period = libxmp_note_to_period(ctx, MIN_NOTE_MOD, xc->finetune, 0);
|
||||||
libxmp_note_to_period(ctx, MIN_NOTE_MOD, xc->finetune, 0));
|
CLAMP(xc->period, min_period, max_period);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1261,6 +1509,11 @@ static void play_channel(struct context_data *ctx, int chn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef LIBXMP_CORE_DISABLE_IT
|
||||||
|
/* IT MIDI macros need to update regardless of the current voice state. */
|
||||||
|
update_midi_macro(ctx, chn);
|
||||||
|
#endif
|
||||||
|
|
||||||
act = libxmp_virt_cstat(ctx, chn);
|
act = libxmp_virt_cstat(ctx, chn);
|
||||||
if (act == VIRT_INVALID) {
|
if (act == VIRT_INVALID) {
|
||||||
/* We need this to keep processing global volume slides */
|
/* We need this to keep processing global volume slides */
|
||||||
|
@ -1298,9 +1551,16 @@ static void play_channel(struct context_data *ctx, int chn)
|
||||||
xc->volume += rval[xc->retrig.type].s;
|
xc->volume += rval[xc->retrig.type].s;
|
||||||
xc->volume *= rval[xc->retrig.type].m;
|
xc->volume *= rval[xc->retrig.type].m;
|
||||||
xc->volume /= rval[xc->retrig.type].d;
|
xc->volume /= rval[xc->retrig.type].d;
|
||||||
xc->retrig.count = LSN(xc->retrig.val);
|
xc->retrig.count = LSN(xc->retrig.val);
|
||||||
|
|
||||||
|
if (xc->retrig.limit > 0) {
|
||||||
|
/* Limit the number of retriggers. */
|
||||||
|
--xc->retrig.limit;
|
||||||
|
if (xc->retrig.limit == 0)
|
||||||
|
RESET(RETRIG);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do keyoff */
|
/* Do keyoff */
|
||||||
if (xc->keyoff) {
|
if (xc->keyoff) {
|
||||||
|
@ -1308,7 +1568,7 @@ static void play_channel(struct context_data *ctx, int chn)
|
||||||
SET_NOTE(NOTE_RELEASE);
|
SET_NOTE(NOTE_RELEASE);
|
||||||
}
|
}
|
||||||
|
|
||||||
libxmp_virt_release(ctx, chn, TEST_NOTE(NOTE_RELEASE));
|
libxmp_virt_release(ctx, chn, TEST_NOTE(NOTE_SAMPLE_RELEASE));
|
||||||
|
|
||||||
update_volume(ctx, chn);
|
update_volume(ctx, chn);
|
||||||
update_frequency(ctx, chn);
|
update_frequency(ctx, chn);
|
||||||
|
@ -1319,13 +1579,13 @@ static void play_channel(struct context_data *ctx, int chn)
|
||||||
process_pan(ctx, chn, act);
|
process_pan(ctx, chn, act);
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
if (HAS_QUIRK(QUIRK_PROTRACK) && xc->ins < mod->ins) {
|
if (HAS_QUIRK(QUIRK_PROTRACK | QUIRK_INVLOOP) && xc->ins < mod->ins) {
|
||||||
update_invloop(m, xc);
|
update_invloop(ctx, xc);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (TEST_NOTE(NOTE_SUSEXIT)) {
|
if (TEST_NOTE(NOTE_SUSEXIT)) {
|
||||||
SET_NOTE(NOTE_RELEASE);
|
SET_NOTE(NOTE_ENV_RELEASE);
|
||||||
}
|
}
|
||||||
|
|
||||||
xc->info_position = libxmp_virt_getvoicepos(ctx, chn);
|
xc->info_position = libxmp_virt_getvoicepos(ctx, chn);
|
||||||
|
@ -1362,13 +1622,14 @@ static void next_order(struct context_data *ctx)
|
||||||
struct flow_control *f = &p->flow;
|
struct flow_control *f = &p->flow;
|
||||||
struct module_data *m = &ctx->m;
|
struct module_data *m = &ctx->m;
|
||||||
struct xmp_module *mod = &m->mod;
|
struct xmp_module *mod = &m->mod;
|
||||||
|
int reset_gvol = 0;
|
||||||
int mark;
|
int mark;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
p->ord++;
|
p->ord++;
|
||||||
|
|
||||||
/* Restart module */
|
/* Restart module */
|
||||||
mark = HAS_QUIRK(QUIRK_MARKER) && mod->xxo[p->ord] == 0xff;
|
mark = HAS_QUIRK(QUIRK_MARKER) && p->ord < mod->len && mod->xxo[p->ord] == 0xff;
|
||||||
if (p->ord >= mod->len || mark) {
|
if (p->ord >= mod->len || mark) {
|
||||||
if (mod->rst > mod->len ||
|
if (mod->rst > mod->len ||
|
||||||
mod->xxo[mod->rst] >= mod->pat ||
|
mod->xxo[mod->rst] >= mod->pat ||
|
||||||
|
@ -1381,11 +1642,19 @@ static void next_order(struct context_data *ctx)
|
||||||
p->ord = m->seq_data[p->sequence].entry_point;
|
p->ord = m->seq_data[p->sequence].entry_point;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* This might be a marker, so delay updating global
|
||||||
p->gvol = m->xxo_info[p->ord].gvl;
|
* volume until an actual pattern is found */
|
||||||
|
reset_gvol = 1;
|
||||||
}
|
}
|
||||||
} while (mod->xxo[p->ord] >= mod->pat);
|
} while (mod->xxo[p->ord] >= mod->pat);
|
||||||
|
|
||||||
|
if (reset_gvol)
|
||||||
|
p->gvol = m->xxo_info[p->ord].gvl;
|
||||||
|
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
/* Archimedes line jump -- don't reset time tracking. */
|
||||||
|
if (f->jump_in_pat != p->ord)
|
||||||
|
#endif
|
||||||
p->current_time = m->xxo_info[p->ord].time;
|
p->current_time = m->xxo_info[p->ord].time;
|
||||||
|
|
||||||
f->num_rows = mod->xxp[mod->xxo[p->ord]]->rows;
|
f->num_rows = mod->xxp[mod->xxo[p->ord]]->rows;
|
||||||
|
@ -1398,6 +1667,8 @@ static void next_order(struct context_data *ctx)
|
||||||
p->frame = 0;
|
p->frame = 0;
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
f->jump_in_pat = -1;
|
||||||
|
|
||||||
/* Reset persistent effects at new pattern */
|
/* Reset persistent effects at new pattern */
|
||||||
if (HAS_QUIRK(QUIRK_PERPAT)) {
|
if (HAS_QUIRK(QUIRK_PERPAT)) {
|
||||||
int chn;
|
int chn;
|
||||||
|
@ -1426,11 +1697,6 @@ static void next_row(struct context_data *ctx)
|
||||||
|
|
||||||
next_order(ctx);
|
next_order(ctx);
|
||||||
} else {
|
} else {
|
||||||
if (f->loop_chn) {
|
|
||||||
p->row = f->loop[f->loop_chn - 1].start - 1;
|
|
||||||
f->loop_chn = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (f->rowdelay == 0) {
|
if (f->rowdelay == 0) {
|
||||||
p->row++;
|
p->row++;
|
||||||
f->rowdelay_set = 0;
|
f->rowdelay_set = 0;
|
||||||
|
@ -1438,6 +1704,11 @@ static void next_row(struct context_data *ctx)
|
||||||
f->rowdelay--;
|
f->rowdelay--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (f->loop_chn) {
|
||||||
|
p->row = f->loop[f->loop_chn - 1].start;
|
||||||
|
f->loop_chn = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* check end of pattern */
|
/* check end of pattern */
|
||||||
if (p->row >= f->num_rows) {
|
if (p->row >= f->num_rows) {
|
||||||
next_order(ctx);
|
next_order(ctx);
|
||||||
|
@ -1486,6 +1757,21 @@ static void update_from_ord_info(struct context_data *ctx)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void libxmp_reset_flow(struct context_data *ctx)
|
||||||
|
{
|
||||||
|
struct flow_control *f = &ctx->p.flow;
|
||||||
|
f->jumpline = 0;
|
||||||
|
f->jump = -1;
|
||||||
|
f->pbreak = 0;
|
||||||
|
f->loop_chn = 0;
|
||||||
|
f->delay = 0;
|
||||||
|
f->rowdelay = 0;
|
||||||
|
f->rowdelay_set = 0;
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
f->jump_in_pat = -1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
int xmp_start_player(xmp_context opaque, int rate, int format)
|
int xmp_start_player(xmp_context opaque, int rate, int format)
|
||||||
{
|
{
|
||||||
struct context_data *ctx = (struct context_data *)opaque;
|
struct context_data *ctx = (struct context_data *)opaque;
|
||||||
|
@ -1539,8 +1825,11 @@ int xmp_start_player(xmp_context opaque, int rate, int format)
|
||||||
mod->len = 0;
|
mod->len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mod->len == 0 || mod->chn == 0) {
|
if (mod->len == 0) {
|
||||||
/* set variables to sane state */
|
/* set variables to sane state */
|
||||||
|
/* Note: previously did this for mod->chn == 0, which caused
|
||||||
|
* crashes on invalid order 0s. 0 channel modules are technically
|
||||||
|
* valid (if useless) so just let them play normally. */
|
||||||
p->ord = p->scan[0].ord = 0;
|
p->ord = p->scan[0].ord = 0;
|
||||||
p->row = p->scan[0].row = 0;
|
p->row = p->scan[0].row = 0;
|
||||||
f->end_point = 0;
|
f->end_point = 0;
|
||||||
|
@ -1557,19 +1846,15 @@ int xmp_start_player(xmp_context opaque, int rate, int format)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
f->delay = 0;
|
libxmp_reset_flow(ctx);
|
||||||
f->jumpline = 0;
|
|
||||||
f->jump = -1;
|
|
||||||
f->pbreak = 0;
|
|
||||||
f->rowdelay_set = 0;
|
|
||||||
|
|
||||||
f->loop = calloc(p->virt.virt_channels, sizeof(struct pattern_loop));
|
f->loop = (struct pattern_loop *) calloc(p->virt.virt_channels, sizeof(struct pattern_loop));
|
||||||
if (f->loop == NULL) {
|
if (f->loop == NULL) {
|
||||||
ret = -XMP_ERROR_SYSTEM;
|
ret = -XMP_ERROR_SYSTEM;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
p->xc_data = calloc(p->virt.virt_channels, sizeof(struct channel_data));
|
p->xc_data = (struct channel_data *) calloc(p->virt.virt_channels, sizeof(struct channel_data));
|
||||||
if (p->xc_data == NULL) {
|
if (p->xc_data == NULL) {
|
||||||
ret = -XMP_ERROR_SYSTEM;
|
ret = -XMP_ERROR_SYSTEM;
|
||||||
goto err1;
|
goto err1;
|
||||||
|
@ -1578,11 +1863,14 @@ int xmp_start_player(xmp_context opaque, int rate, int format)
|
||||||
/* Reset our buffer pointers */
|
/* Reset our buffer pointers */
|
||||||
xmp_play_buffer(opaque, NULL, 0, 0);
|
xmp_play_buffer(opaque, NULL, 0, 0);
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_DISABLE_IT
|
||||||
for (i = 0; i < p->virt.virt_channels; i++) {
|
for (i = 0; i < p->virt.virt_channels; i++) {
|
||||||
struct channel_data *xc = &p->xc_data[i];
|
struct channel_data *xc = &p->xc_data[i];
|
||||||
|
xc->filter.cutoff = 0xff;
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
if (libxmp_new_channel_extras(ctx, xc) < 0)
|
if (libxmp_new_channel_extras(ctx, xc) < 0)
|
||||||
goto err2;
|
goto err2;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
reset_channels(ctx);
|
reset_channels(ctx);
|
||||||
|
@ -1776,7 +2064,7 @@ int xmp_play_buffer(xmp_context opaque, void *out_buffer, int size, int loop)
|
||||||
}
|
}
|
||||||
|
|
||||||
p->buffer_data.consumed = 0;
|
p->buffer_data.consumed = 0;
|
||||||
p->buffer_data.in_buffer = fi.buffer;
|
p->buffer_data.in_buffer = (char *)fi.buffer;
|
||||||
p->buffer_data.in_size = fi.buffer_size;
|
p->buffer_data.in_size = fi.buffer_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,20 +56,30 @@ struct retrig_control {
|
||||||
#define FINE_VOLS_2 (1 << 25)
|
#define FINE_VOLS_2 (1 << 25)
|
||||||
#define KEY_OFF (1 << 26) /* for IT release on envloop end */
|
#define KEY_OFF (1 << 26) /* for IT release on envloop end */
|
||||||
#define TREMOR (1 << 27) /* for XM tremor */
|
#define TREMOR (1 << 27) /* for XM tremor */
|
||||||
|
#define MIDI_MACRO (1 << 28) /* IT midi macro */
|
||||||
|
|
||||||
#define NOTE_FADEOUT (1 << 0)
|
#define NOTE_FADEOUT (1 << 0)
|
||||||
#define NOTE_RELEASE (1 << 1)
|
#define NOTE_ENV_RELEASE (1 << 1) /* envelope sustain loop release */
|
||||||
#define NOTE_END (1 << 2)
|
#define NOTE_END (1 << 2)
|
||||||
#define NOTE_CUT (1 << 3)
|
#define NOTE_CUT (1 << 3)
|
||||||
#define NOTE_ENV_END (1 << 4)
|
#define NOTE_ENV_END (1 << 4)
|
||||||
#define NOTE_SAMPLE_END (1 << 5)
|
#define NOTE_SAMPLE_END (1 << 5)
|
||||||
#define NOTE_SET (1 << 6) /* for IT portamento after keyoff */
|
#define NOTE_SET (1 << 6) /* for IT portamento after keyoff */
|
||||||
#define NOTE_SUSEXIT (1 << 7) /* for delayed note release */
|
#define NOTE_SUSEXIT (1 << 7) /* for delayed envelope release */
|
||||||
#define NOTE_KEY_CUT (1 << 8) /* note cut with XMP_KEY_CUT event */
|
#define NOTE_KEY_CUT (1 << 8) /* note cut with XMP_KEY_CUT event */
|
||||||
#define NOTE_GLISSANDO (1 << 9)
|
#define NOTE_GLISSANDO (1 << 9)
|
||||||
|
#define NOTE_SAMPLE_RELEASE (1 << 10) /* sample sustain loop release */
|
||||||
|
|
||||||
|
/* Most of the time, these should be set/reset together. */
|
||||||
|
#define NOTE_RELEASE (NOTE_ENV_RELEASE | NOTE_SAMPLE_RELEASE)
|
||||||
|
|
||||||
|
/* Note: checking the data pointer for samples should be good enough to filter
|
||||||
|
* broken samples, since libxmp_load_sample will always allocate it for valid
|
||||||
|
* samples of >0 length and bound the loop values for these samples. */
|
||||||
#define IS_VALID_INSTRUMENT(x) ((uint32)(x) < mod->ins && mod->xxi[(x)].nsm > 0)
|
#define IS_VALID_INSTRUMENT(x) ((uint32)(x) < mod->ins && mod->xxi[(x)].nsm > 0)
|
||||||
#define IS_VALID_INSTRUMENT_OR_SFX(x) (((uint32)(x) < mod->ins && mod->xxi[(x)].nsm > 0) || (smix->ins > 0 && (uint32)(x) < mod->ins + smix->ins))
|
#define IS_VALID_INSTRUMENT_OR_SFX(x) (((uint32)(x) < mod->ins && mod->xxi[(x)].nsm > 0) || (smix->ins > 0 && (uint32)(x) < mod->ins + smix->ins))
|
||||||
|
#define IS_VALID_SAMPLE(x) ((uint32)(x) < mod->smp && mod->xxs[(x)].data != NULL)
|
||||||
|
#define IS_VALID_NOTE(x) ((uint32)(x) < XMP_MAX_KEYS)
|
||||||
|
|
||||||
struct instrument_vibrato {
|
struct instrument_vibrato {
|
||||||
int phase;
|
int phase;
|
||||||
|
@ -147,6 +157,7 @@ struct channel_data {
|
||||||
int val; /* Retrig value */
|
int val; /* Retrig value */
|
||||||
int count; /* Retrig counter */
|
int count; /* Retrig counter */
|
||||||
int type; /* Retrig type */
|
int type; /* Retrig type */
|
||||||
|
int limit; /* Number of retrigs */
|
||||||
} retrig;
|
} retrig;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
@ -163,6 +174,9 @@ struct channel_data {
|
||||||
#ifndef LIBXMP_CORE_DISABLE_IT
|
#ifndef LIBXMP_CORE_DISABLE_IT
|
||||||
int fslide2;
|
int fslide2;
|
||||||
int memory2; /* Volume slide effect memory */
|
int memory2; /* Volume slide effect memory */
|
||||||
|
#endif
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
int target; /* Target for persistent volslide */
|
||||||
#endif
|
#endif
|
||||||
} vol;
|
} vol;
|
||||||
|
|
||||||
|
@ -194,6 +208,7 @@ struct channel_data {
|
||||||
int dir; /* Tone portamento up/down directionh */
|
int dir; /* Tone portamento up/down directionh */
|
||||||
int slide; /* Delta for tone portamento */
|
int slide; /* Delta for tone portamento */
|
||||||
int memory; /* Tone portamento effect memory */
|
int memory; /* Tone portamento effect memory */
|
||||||
|
int note_memory;/* Tone portamento note memory (ULT) */
|
||||||
} porta;
|
} porta;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
@ -224,8 +239,17 @@ struct channel_data {
|
||||||
int cutoff; /* IT filter cutoff frequency */
|
int cutoff; /* IT filter cutoff frequency */
|
||||||
int resonance; /* IT filter resonance */
|
int resonance; /* IT filter resonance */
|
||||||
int envelope; /* IT filter envelope */
|
int envelope; /* IT filter envelope */
|
||||||
|
int can_disable;/* IT hack: allow disabling for cutoff 127 */
|
||||||
} filter;
|
} filter;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
float val; /* Current macro effect (use float for slides) */
|
||||||
|
float target; /* Current macro target (smooth macro) */
|
||||||
|
float slide; /* Current macro slide (smooth macro) */
|
||||||
|
int active; /* Current active parameterized macro */
|
||||||
|
int finalvol; /* Previous tick calculated volume (0-0x400) */
|
||||||
|
int notepan; /* Previous tick note panning (0x80 center) */
|
||||||
|
} macro;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Extended Module Player
|
/* Extended Module Player
|
||||||
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
|
* Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -31,11 +31,6 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static inline int is_valid_note(int note)
|
|
||||||
{
|
|
||||||
return (note >= 0 && note < XMP_MAX_KEYS);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct xmp_subinstrument *get_subinstrument(struct context_data *ctx,
|
static struct xmp_subinstrument *get_subinstrument(struct context_data *ctx,
|
||||||
int ins, int key)
|
int ins, int key)
|
||||||
{
|
{
|
||||||
|
@ -45,7 +40,7 @@ static struct xmp_subinstrument *get_subinstrument(struct context_data *ctx,
|
||||||
|
|
||||||
if (IS_VALID_INSTRUMENT(ins)) {
|
if (IS_VALID_INSTRUMENT(ins)) {
|
||||||
instrument = &mod->xxi[ins];
|
instrument = &mod->xxi[ins];
|
||||||
if (is_valid_note(key)) {
|
if (IS_VALID_NOTE(key)) {
|
||||||
int mapped = instrument->map[key].ins;
|
int mapped = instrument->map[key].ins;
|
||||||
if (mapped != 0xff && mapped >= 0 && mapped < instrument->nsm)
|
if (mapped != 0xff && mapped >= 0 && mapped < instrument->nsm)
|
||||||
return &instrument->sub[mapped];
|
return &instrument->sub[mapped];
|
||||||
|
@ -67,7 +62,7 @@ static void reset_envelopes(struct context_data *ctx, struct channel_data *xc)
|
||||||
if (!IS_VALID_INSTRUMENT(xc->ins))
|
if (!IS_VALID_INSTRUMENT(xc->ins))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
RESET_NOTE(NOTE_ENV_END);
|
RESET_NOTE(NOTE_ENV_END);
|
||||||
|
|
||||||
xc->v_idx = -1;
|
xc->v_idx = -1;
|
||||||
xc->p_idx = -1;
|
xc->p_idx = -1;
|
||||||
|
@ -76,6 +71,20 @@ static void reset_envelopes(struct context_data *ctx, struct channel_data *xc)
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_DISABLE_IT
|
#ifndef LIBXMP_CORE_DISABLE_IT
|
||||||
|
|
||||||
|
static void reset_envelope_volume(struct context_data *ctx,
|
||||||
|
struct channel_data *xc)
|
||||||
|
{
|
||||||
|
struct module_data *m = &ctx->m;
|
||||||
|
struct xmp_module *mod = &m->mod;
|
||||||
|
|
||||||
|
if (!IS_VALID_INSTRUMENT(xc->ins))
|
||||||
|
return;
|
||||||
|
|
||||||
|
RESET_NOTE(NOTE_ENV_END);
|
||||||
|
|
||||||
|
xc->v_idx = -1;
|
||||||
|
}
|
||||||
|
|
||||||
static void reset_envelopes_carry(struct context_data *ctx,
|
static void reset_envelopes_carry(struct context_data *ctx,
|
||||||
struct channel_data *xc)
|
struct channel_data *xc)
|
||||||
{
|
{
|
||||||
|
@ -109,18 +118,8 @@ static void set_effect_defaults(struct context_data *ctx, int note,
|
||||||
struct channel_data *xc, int is_toneporta)
|
struct channel_data *xc, int is_toneporta)
|
||||||
{
|
{
|
||||||
struct module_data *m = &ctx->m;
|
struct module_data *m = &ctx->m;
|
||||||
struct xmp_module *mod = &m->mod;
|
|
||||||
struct smix_data *smix = &ctx->smix;
|
|
||||||
|
|
||||||
if (sub != NULL && note >= 0) {
|
if (sub != NULL && note >= 0) {
|
||||||
struct xmp_instrument *xxi;
|
|
||||||
|
|
||||||
if (xc->ins >= mod->ins) {
|
|
||||||
xxi = &smix->xxi[xc->ins - mod->ins];
|
|
||||||
} else {
|
|
||||||
xxi = &mod->xxi[xc->ins];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!HAS_QUIRK(QUIRK_PROTRACK)) {
|
if (!HAS_QUIRK(QUIRK_PROTRACK)) {
|
||||||
xc->finetune = sub->fin;
|
xc->finetune = sub->fin;
|
||||||
}
|
}
|
||||||
|
@ -129,16 +128,16 @@ static void set_effect_defaults(struct context_data *ctx, int note,
|
||||||
#ifndef LIBXMP_CORE_DISABLE_IT
|
#ifndef LIBXMP_CORE_DISABLE_IT
|
||||||
if (sub->ifc & 0x80) {
|
if (sub->ifc & 0x80) {
|
||||||
xc->filter.cutoff = (sub->ifc - 0x80) * 2;
|
xc->filter.cutoff = (sub->ifc - 0x80) * 2;
|
||||||
} else if (~xxi->fei.flg & XMP_ENVELOPE_FLT) {
|
|
||||||
xc->filter.cutoff = 0xff;
|
|
||||||
}
|
}
|
||||||
xc->filter.envelope = 0x100;
|
xc->filter.envelope = 0x100;
|
||||||
|
|
||||||
if (sub->ifr & 0x80) {
|
if (sub->ifr & 0x80) {
|
||||||
xc->filter.resonance = (sub->ifr - 0x80) * 2;
|
xc->filter.resonance = (sub->ifr - 0x80) * 2;
|
||||||
} /* else {
|
}
|
||||||
xc->filter.resonance = 0;
|
|
||||||
} */
|
/* IT: on a new note without toneporta, allow a computed cutoff
|
||||||
|
* of 127 with resonance 0 to disable the filter. */
|
||||||
|
xc->filter.can_disable = !is_toneporta;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* TODO: should probably expand the LFO period size instead
|
/* TODO: should probably expand the LFO period size instead
|
||||||
|
@ -215,13 +214,14 @@ static void set_period_ft2(struct context_data *ctx, int note,
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
#define IS_SFX_PITCH(x) ((x) == FX_PITCH_ADD || (x) == FX_PITCH_SUB)
|
#define IS_SFX_PITCH(x) ((x) == FX_PITCH_ADD || (x) == FX_PITCH_SUB)
|
||||||
#define IS_TONEPORTA(x) ((x) == FX_TONEPORTA || (x) == FX_TONE_VSLIDE \
|
#define IS_TONEPORTA(x) ((x) == FX_TONEPORTA || (x) == FX_TONE_VSLIDE \
|
||||||
|| (x) == FX_PER_TPORTA)
|
|| (x) == FX_PER_TPORTA || (x) == FX_ULT_TPORTA \
|
||||||
|
|| (x) == FX_FAR_TPORTA)
|
||||||
#else
|
#else
|
||||||
#define IS_TONEPORTA(x) ((x) == FX_TONEPORTA || (x) == FX_TONE_VSLIDE)
|
#define IS_TONEPORTA(x) ((x) == FX_TONEPORTA || (x) == FX_TONE_VSLIDE)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define set_patch(ctx,chn,ins,smp,note) \
|
#define set_patch(ctx,chn,ins,smp,note) \
|
||||||
libxmp_virt_setpatch(ctx, chn, ins, smp, note, 0, 0, 0)
|
libxmp_virt_setpatch(ctx, chn, ins, smp, note, 0, 0, 0, 0)
|
||||||
|
|
||||||
static int read_event_mod(struct context_data *ctx, struct xmp_event *e, int chn)
|
static int read_event_mod(struct context_data *ctx, struct xmp_event *e, int chn)
|
||||||
{
|
{
|
||||||
|
@ -293,7 +293,7 @@ static int read_event_mod(struct context_data *ctx, struct xmp_event *e, int chn
|
||||||
if (e->note == XMP_KEY_OFF) {
|
if (e->note == XMP_KEY_OFF) {
|
||||||
SET_NOTE(NOTE_RELEASE);
|
SET_NOTE(NOTE_RELEASE);
|
||||||
use_ins_vol = 0;
|
use_ins_vol = 0;
|
||||||
} else if (!is_toneporta && is_valid_note(e->note - 1)) {
|
} else if (!is_toneporta && IS_VALID_NOTE(e->note - 1)) {
|
||||||
xc->key = e->note - 1;
|
xc->key = e->note - 1;
|
||||||
RESET_NOTE(NOTE_END);
|
RESET_NOTE(NOTE_END);
|
||||||
|
|
||||||
|
@ -306,7 +306,7 @@ static int read_event_mod(struct context_data *ctx, struct xmp_event *e, int chn
|
||||||
note = xc->key + sub->xpo + transp;
|
note = xc->key + sub->xpo + transp;
|
||||||
smp = sub->sid;
|
smp = sub->sid;
|
||||||
|
|
||||||
if (mod->xxs[smp].len == 0) {
|
if (!IS_VALID_SAMPLE(smp)) {
|
||||||
smp = -1;
|
smp = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,6 +332,7 @@ static int read_event_mod(struct context_data *ctx, struct xmp_event *e, int chn
|
||||||
if (e->vol) {
|
if (e->vol) {
|
||||||
xc->volume = e->vol - 1;
|
xc->volume = e->vol - 1;
|
||||||
SET(NEW_VOL);
|
SET(NEW_VOL);
|
||||||
|
RESET_PER(VOL_SLIDE); /* FIXME: should this be for FAR only? */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Secondary effect handled first */
|
/* Secondary effect handled first */
|
||||||
|
@ -375,6 +376,7 @@ static int sustain_check(struct xmp_envelope *env, int idx)
|
||||||
{
|
{
|
||||||
return (env &&
|
return (env &&
|
||||||
(env->flg & XMP_ENVELOPE_ON) &&
|
(env->flg & XMP_ENVELOPE_ON) &&
|
||||||
|
(env->flg & XMP_ENVELOPE_SUS) &&
|
||||||
(~env->flg & XMP_ENVELOPE_LOOP) &&
|
(~env->flg & XMP_ENVELOPE_LOOP) &&
|
||||||
idx == env->data[env->sus << 1]);
|
idx == env->data[env->sus << 1]);
|
||||||
}
|
}
|
||||||
|
@ -457,19 +459,17 @@ static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn
|
||||||
/* FT2: Retrieve old instrument volume */
|
/* FT2: Retrieve old instrument volume */
|
||||||
if (ins) {
|
if (ins) {
|
||||||
if (key == 0 || key >= XMP_KEY_OFF) {
|
if (key == 0 || key >= XMP_KEY_OFF) {
|
||||||
struct xmp_subinstrument *sub;
|
|
||||||
|
|
||||||
/* Previous instrument */
|
/* Previous instrument */
|
||||||
sub = get_subinstrument(ctx, xc->ins, xc->key);
|
sub = get_subinstrument(ctx, xc->ins, xc->key);
|
||||||
|
|
||||||
/* No note */
|
/* No note */
|
||||||
if (sub != NULL) {
|
if (sub != NULL) {
|
||||||
int p = mod->xxc[chn].pan - 128;
|
int pan = mod->xxc[chn].pan - 128;
|
||||||
xc->volume = sub->vol;
|
xc->volume = sub->vol;
|
||||||
|
|
||||||
if (!HAS_QUIRK(QUIRK_FTMOD)) {
|
if (!HAS_QUIRK(QUIRK_FTMOD)) {
|
||||||
xc->pan.val = p + ((sub->pan - 128) *
|
xc->pan.val = pan + ((sub->pan - 128) *
|
||||||
(128 - abs(p))) / 128 + 128;
|
(128 - abs(pan))) / 128 + 128;
|
||||||
}
|
}
|
||||||
|
|
||||||
xc->ins_fade = mod->xxi[xc->ins].rls;
|
xc->ins_fade = mod->xxi[xc->ins].rls;
|
||||||
|
@ -516,11 +516,8 @@ static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check note */
|
/* Check note */
|
||||||
|
|
||||||
if (ins) {
|
if (ins) {
|
||||||
if (key > 0 && key < XMP_KEY_OFF) {
|
if (key > 0 && key < XMP_KEY_OFF) {
|
||||||
struct xmp_subinstrument *sub;
|
|
||||||
|
|
||||||
/* Retrieve volume when we have note */
|
/* Retrieve volume when we have note */
|
||||||
|
|
||||||
/* and only if we have instrument, otherwise we're in
|
/* and only if we have instrument, otherwise we're in
|
||||||
|
@ -530,12 +527,12 @@ static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn
|
||||||
/* Current instrument */
|
/* Current instrument */
|
||||||
sub = get_subinstrument(ctx, xc->ins, key - 1);
|
sub = get_subinstrument(ctx, xc->ins, key - 1);
|
||||||
if (sub != NULL) {
|
if (sub != NULL) {
|
||||||
int p = mod->xxc[chn].pan - 128;
|
int pan = mod->xxc[chn].pan - 128;
|
||||||
xc->volume = sub->vol;
|
xc->volume = sub->vol;
|
||||||
|
|
||||||
if (!HAS_QUIRK(QUIRK_FTMOD)) {
|
if (!HAS_QUIRK(QUIRK_FTMOD)) {
|
||||||
xc->pan.val = p + ((sub->pan - 128) *
|
xc->pan.val = pan + ((sub->pan - 128) *
|
||||||
(128 - abs(p))) / 128 + 128;
|
(128 - abs(pan))) / 128 + 128;
|
||||||
}
|
}
|
||||||
|
|
||||||
xc->ins_fade = mod->xxi[xc->ins].rls;
|
xc->ins_fade = mod->xxi[xc->ins].rls;
|
||||||
|
@ -627,7 +624,7 @@ static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn
|
||||||
* and remains in the memory."
|
* and remains in the memory."
|
||||||
*/
|
*/
|
||||||
sub = NULL;
|
sub = NULL;
|
||||||
if (is_valid_note(key - 1)) {
|
if (IS_VALID_NOTE(key - 1)) {
|
||||||
int k = key - 1;
|
int k = key - 1;
|
||||||
sub = get_subinstrument(ctx, xc->ins, k);
|
sub = get_subinstrument(ctx, xc->ins, k);
|
||||||
if (!new_invalid_ins && sub != NULL) {
|
if (!new_invalid_ins && sub != NULL) {
|
||||||
|
@ -640,7 +637,7 @@ static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_valid_note(key - 1)) {
|
if (IS_VALID_NOTE(key - 1)) {
|
||||||
xc->key = --key;
|
xc->key = --key;
|
||||||
xc->fadeout = 0x10000;
|
xc->fadeout = 0x10000;
|
||||||
RESET_NOTE(NOTE_END);
|
RESET_NOTE(NOTE_END);
|
||||||
|
@ -658,7 +655,7 @@ static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn
|
||||||
note = key + sub->xpo + transp;
|
note = key + sub->xpo + transp;
|
||||||
smp = sub->sid;
|
smp = sub->sid;
|
||||||
|
|
||||||
if (mod->xxs[smp].len == 0) {
|
if (!IS_VALID_SAMPLE(smp)) {
|
||||||
smp = -1;
|
smp = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -814,7 +811,7 @@ static int read_event_st3(struct context_data *ctx, struct xmp_event *e, int chn
|
||||||
if (not_same_ins) {
|
if (not_same_ins) {
|
||||||
xc->offset.val = 0;
|
xc->offset.val = 0;
|
||||||
}
|
}
|
||||||
} else if (is_valid_note(e->note - 1)) {
|
} else if (IS_VALID_NOTE(e->note - 1)) {
|
||||||
xc->key = e->note - 1;
|
xc->key = e->note - 1;
|
||||||
RESET_NOTE(NOTE_END);
|
RESET_NOTE(NOTE_END);
|
||||||
|
|
||||||
|
@ -827,7 +824,7 @@ static int read_event_st3(struct context_data *ctx, struct xmp_event *e, int chn
|
||||||
note = xc->key + sub->xpo + transp;
|
note = xc->key + sub->xpo + transp;
|
||||||
smp = sub->sid;
|
smp = sub->sid;
|
||||||
|
|
||||||
if (mod->xxs[smp].len == 0) {
|
if (!IS_VALID_SAMPLE(smp)) {
|
||||||
smp = -1;
|
smp = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -961,10 +958,10 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
|
||||||
int is_toneporta, is_release;
|
int is_toneporta, is_release;
|
||||||
int candidate_ins;
|
int candidate_ins;
|
||||||
int reset_env;
|
int reset_env;
|
||||||
|
int reset_susloop;
|
||||||
int use_ins_vol;
|
int use_ins_vol;
|
||||||
int sample_mode;
|
int sample_mode;
|
||||||
int toneporta_offset;
|
int toneporta_offset;
|
||||||
int disabled_toneporta;
|
|
||||||
int retrig_ins;
|
int retrig_ins;
|
||||||
struct xmp_event ev;
|
struct xmp_event ev;
|
||||||
|
|
||||||
|
@ -987,11 +984,11 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
|
||||||
is_toneporta = 0;
|
is_toneporta = 0;
|
||||||
is_release = 0;
|
is_release = 0;
|
||||||
reset_env = 0;
|
reset_env = 0;
|
||||||
|
reset_susloop = 0;
|
||||||
use_ins_vol = 0;
|
use_ins_vol = 0;
|
||||||
candidate_ins = xc->ins;
|
candidate_ins = xc->ins;
|
||||||
sample_mode = !HAS_QUIRK(QUIRK_VIRTUAL);
|
sample_mode = !HAS_QUIRK(QUIRK_VIRTUAL);
|
||||||
toneporta_offset = 0;
|
toneporta_offset = 0;
|
||||||
disabled_toneporta = 0;
|
|
||||||
retrig_ins = 0;
|
retrig_ins = 0;
|
||||||
|
|
||||||
/* Keyoff + instrument retrigs current instrument in old fx mode */
|
/* Keyoff + instrument retrigs current instrument in old fx mode */
|
||||||
|
@ -1025,7 +1022,7 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
|
||||||
is_toneporta = 1;
|
is_toneporta = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TEST_NOTE(NOTE_RELEASE | NOTE_FADEOUT)) {
|
if (TEST_NOTE(NOTE_ENV_RELEASE | NOTE_FADEOUT)) {
|
||||||
is_release = 1;
|
is_release = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1035,10 +1032,8 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
|
||||||
|
|
||||||
/* Off-Porta.it */
|
/* Off-Porta.it */
|
||||||
if (is_toneporta && ev.fxt == FX_OFFSET) {
|
if (is_toneporta && ev.fxt == FX_OFFSET) {
|
||||||
disabled_toneporta = 1;
|
toneporta_offset = 1;
|
||||||
is_toneporta = 0;
|
|
||||||
if (!HAS_QUIRK(QUIRK_PRENV)) {
|
if (!HAS_QUIRK(QUIRK_PRENV)) {
|
||||||
toneporta_offset = 1;
|
|
||||||
RESET_NOTE(NOTE_ENV_END);
|
RESET_NOTE(NOTE_ENV_END);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1077,9 +1072,11 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
|
||||||
|
|
||||||
if (set_new_ins) {
|
if (set_new_ins) {
|
||||||
SET(NEW_INS);
|
SET(NEW_INS);
|
||||||
use_ins_vol = 1;
|
|
||||||
reset_env = 1;
|
reset_env = 1;
|
||||||
}
|
}
|
||||||
|
/* Sample default volume is always enabled if a valid sample
|
||||||
|
* is provided (Atomic Playboy, default_volume.it). */
|
||||||
|
use_ins_vol = 1;
|
||||||
xc->per_flags = 0;
|
xc->per_flags = 0;
|
||||||
|
|
||||||
if (IS_VALID_INSTRUMENT(ins)) {
|
if (IS_VALID_INSTRUMENT(ins)) {
|
||||||
|
@ -1119,7 +1116,10 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* In sample mode invalid ins cut previous ins */
|
/* In sample mode invalid instruments cut the current
|
||||||
|
* note (OpenMPT SampleNumberChange.it).
|
||||||
|
* TODO: portamento_sustain.it order 3 row 19: when
|
||||||
|
* sample release is set, this isn't always done? */
|
||||||
if (sample_mode) {
|
if (sample_mode) {
|
||||||
xc->volume = 0;
|
xc->volume = 0;
|
||||||
}
|
}
|
||||||
|
@ -1133,13 +1133,14 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
|
||||||
|
|
||||||
/* Check note */
|
/* Check note */
|
||||||
|
|
||||||
if (key && !new_invalid_ins) {
|
if (key) {
|
||||||
SET(NEW_NOTE);
|
SET(NEW_NOTE);
|
||||||
SET_NOTE(NOTE_SET);
|
SET_NOTE(NOTE_SET);
|
||||||
|
|
||||||
if (key == XMP_KEY_FADE) {
|
if (key == XMP_KEY_FADE) {
|
||||||
SET_NOTE(NOTE_FADEOUT);
|
SET_NOTE(NOTE_FADEOUT);
|
||||||
reset_env = 0;
|
reset_env = 0;
|
||||||
|
reset_susloop = 0;
|
||||||
use_ins_vol = 0;
|
use_ins_vol = 0;
|
||||||
} else if (key == XMP_KEY_CUT) {
|
} else if (key == XMP_KEY_CUT) {
|
||||||
SET_NOTE(NOTE_END | NOTE_CUT | NOTE_KEY_CUT);
|
SET_NOTE(NOTE_END | NOTE_CUT | NOTE_KEY_CUT);
|
||||||
|
@ -1161,24 +1162,27 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
|
||||||
* However, never reset the envelope (see OpenMPT wnoteoff.it).
|
* However, never reset the envelope (see OpenMPT wnoteoff.it).
|
||||||
*/
|
*/
|
||||||
reset_env = 0;
|
reset_env = 0;
|
||||||
|
reset_susloop = 0;
|
||||||
if (!ev.ins) {
|
if (!ev.ins) {
|
||||||
use_ins_vol = 0;
|
use_ins_vol = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (!new_invalid_ins) {
|
||||||
|
/* Sample sustain release should always carry for tone
|
||||||
|
* portamento, and is not reset unless a note is
|
||||||
|
* present (Atomic Playboy, portamento_sustain.it). */
|
||||||
/* portamento_after_keyoff.it test case */
|
/* portamento_after_keyoff.it test case */
|
||||||
/* also see suburban_streets o13 c45 */
|
/* also see suburban_streets o13 c45 */
|
||||||
if (ev.ins || !is_toneporta) {
|
if (!is_toneporta) {
|
||||||
if (!disabled_toneporta) {
|
reset_env = 1;
|
||||||
reset_env = 1;
|
reset_susloop = 1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_toneporta) {
|
if (is_toneporta) {
|
||||||
if (not_same_ins || TEST_NOTE(NOTE_END)) {
|
if (not_same_ins || TEST_NOTE(NOTE_END)) {
|
||||||
SET(NEW_INS);
|
SET(NEW_INS);
|
||||||
RESET_NOTE(NOTE_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT);
|
RESET_NOTE(NOTE_ENV_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT);
|
||||||
} else {
|
} else {
|
||||||
if (is_valid_note(key - 1)) {
|
if (IS_VALID_NOTE(key - 1)) {
|
||||||
xc->key_porta = key - 1;
|
xc->key_porta = key - 1;
|
||||||
}
|
}
|
||||||
key = 0;
|
key = 0;
|
||||||
|
@ -1187,7 +1191,9 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_valid_note(key - 1) && !new_invalid_ins) {
|
/* TODO: instrument change+porta(+release?) doesn't require a key.
|
||||||
|
* Order 3/row 11 of portamento_sustain.it should change the sample. */
|
||||||
|
if (IS_VALID_NOTE(key - 1) && !new_invalid_ins) {
|
||||||
if (TEST_NOTE(NOTE_CUT)) {
|
if (TEST_NOTE(NOTE_CUT)) {
|
||||||
use_ins_vol = 1; /* See OpenMPT NoteOffInstr.it */
|
use_ins_vol = 1; /* See OpenMPT NoteOffInstr.it */
|
||||||
}
|
}
|
||||||
|
@ -1199,20 +1205,30 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
|
||||||
if (sub != NULL) {
|
if (sub != NULL) {
|
||||||
int transp = mod->xxi[candidate_ins].map[key].xpo;
|
int transp = mod->xxi[candidate_ins].map[key].xpo;
|
||||||
int smp, to;
|
int smp, to;
|
||||||
|
int dct;
|
||||||
int rvv;
|
int rvv;
|
||||||
|
|
||||||
|
/* Clear note delay before duplicating channels:
|
||||||
|
* it_note_delay_nna.it */
|
||||||
|
xc->delay = 0;
|
||||||
|
|
||||||
note = key + sub->xpo + transp;
|
note = key + sub->xpo + transp;
|
||||||
smp = sub->sid;
|
smp = sub->sid;
|
||||||
if (smp >= mod->smp || mod->xxs[smp].len == 0) {
|
if (!IS_VALID_SAMPLE(smp)) {
|
||||||
smp = -1;
|
smp = -1;
|
||||||
}
|
}
|
||||||
|
dct = sub->dct;
|
||||||
|
|
||||||
if (not_same_smp) {
|
if (not_same_smp) {
|
||||||
fix_period(ctx, chn, sub);
|
fix_period(ctx, chn, sub);
|
||||||
libxmp_virt_resetchannel(ctx, chn);
|
/* Toneporta, even when not executed, disables
|
||||||
|
* NNA and DCAs for the current note:
|
||||||
|
* portamento_nna_sample.it, gxsmp2.it */
|
||||||
|
libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_CUT);
|
||||||
|
dct = XMP_INST_DCT_OFF;
|
||||||
}
|
}
|
||||||
to = libxmp_virt_setpatch(ctx, chn, candidate_ins, smp,
|
to = libxmp_virt_setpatch(ctx, chn, candidate_ins, smp,
|
||||||
note, sub->nna, sub->dct, sub->dca);
|
note, key, sub->nna, dct, sub->dca);
|
||||||
|
|
||||||
/* Random value for volume swing */
|
/* Random value for volume swing */
|
||||||
rvv = sub->rvv & 0xff;
|
rvv = sub->rvv & 0xff;
|
||||||
|
@ -1268,10 +1284,11 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
|
||||||
* finished (OpenMPT test EnvReset.it). This must take place after
|
* finished (OpenMPT test EnvReset.it). This must take place after
|
||||||
* channel copies in case of NNA (see test/test.it)
|
* channel copies in case of NNA (see test/test.it)
|
||||||
* Also if we have envelope in carry mode, check fadeout
|
* Also if we have envelope in carry mode, check fadeout
|
||||||
|
* Also, only reset the volume envelope. (it_fade_env_reset_carry.it)
|
||||||
*/
|
*/
|
||||||
if (ev.ins && TEST_NOTE(NOTE_ENV_END)) {
|
if (ev.ins && TEST_NOTE(NOTE_ENV_END)) {
|
||||||
if (check_fadeout(ctx, xc, candidate_ins)) {
|
if (check_fadeout(ctx, xc, candidate_ins)) {
|
||||||
reset_envelopes(ctx, xc);
|
reset_envelope_volume(ctx, xc);
|
||||||
} else {
|
} else {
|
||||||
reset_env = 0;
|
reset_env = 0;
|
||||||
}
|
}
|
||||||
|
@ -1279,11 +1296,14 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
|
||||||
|
|
||||||
if (reset_env) {
|
if (reset_env) {
|
||||||
if (ev.note) {
|
if (ev.note) {
|
||||||
RESET_NOTE(NOTE_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT);
|
RESET_NOTE(NOTE_ENV_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT);
|
||||||
}
|
}
|
||||||
/* Set after copying to new virtual channel (see ambio.it) */
|
/* Set after copying to new virtual channel (see ambio.it) */
|
||||||
xc->fadeout = 0x10000;
|
xc->fadeout = 0x10000;
|
||||||
}
|
}
|
||||||
|
if (reset_susloop && ev.note) {
|
||||||
|
RESET_NOTE(NOTE_SAMPLE_RELEASE);
|
||||||
|
}
|
||||||
|
|
||||||
/* See OpenMPT wnoteoff.it vs noteoff3.it */
|
/* See OpenMPT wnoteoff.it vs noteoff3.it */
|
||||||
if (retrig_ins && not_same_ins) {
|
if (retrig_ins && not_same_ins) {
|
||||||
|
@ -1306,7 +1326,7 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
|
||||||
|
|
||||||
if (TEST_NOTE(NOTE_CUT)) {
|
if (TEST_NOTE(NOTE_CUT)) {
|
||||||
reset_envelopes(ctx, xc);
|
reset_envelopes(ctx, xc);
|
||||||
} else if (!toneporta_offset) {
|
} else if (!toneporta_offset || HAS_QUIRK(QUIRK_PRENV)) {
|
||||||
reset_envelopes_carry(ctx, xc);
|
reset_envelopes_carry(ctx, xc);
|
||||||
}
|
}
|
||||||
RESET_NOTE(NOTE_CUT);
|
RESET_NOTE(NOTE_CUT);
|
||||||
|
@ -1336,6 +1356,8 @@ static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn)
|
||||||
|
|
||||||
if (note >= 0) {
|
if (note >= 0) {
|
||||||
xc->note = note;
|
xc->note = note;
|
||||||
|
}
|
||||||
|
if (note >= 0 || toneporta_offset) {
|
||||||
libxmp_virt_voicepos(ctx, chn, xc->offset.val);
|
libxmp_virt_voicepos(ctx, chn, xc->offset.val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1420,7 +1442,7 @@ static int read_event_med(struct context_data *ctx, struct xmp_event *e, int chn
|
||||||
SET_NOTE(NOTE_END);
|
SET_NOTE(NOTE_END);
|
||||||
xc->period = 0;
|
xc->period = 0;
|
||||||
libxmp_virt_resetchannel(ctx, chn);
|
libxmp_virt_resetchannel(ctx, chn);
|
||||||
} else if (!is_toneporta && IS_VALID_INSTRUMENT(xc->ins) && is_valid_note(e->note - 1)) {
|
} else if (!is_toneporta && IS_VALID_INSTRUMENT(xc->ins) && IS_VALID_NOTE(e->note - 1)) {
|
||||||
struct xmp_instrument *xxi = &mod->xxi[xc->ins];
|
struct xmp_instrument *xxi = &mod->xxi[xc->ins];
|
||||||
|
|
||||||
xc->key = e->note - 1;
|
xc->key = e->note - 1;
|
||||||
|
@ -1445,7 +1467,7 @@ static int read_event_med(struct context_data *ctx, struct xmp_event *e, int chn
|
||||||
note = xc->key + sub->xpo + transp;
|
note = xc->key + sub->xpo + transp;
|
||||||
smp = sub->sid;
|
smp = sub->sid;
|
||||||
|
|
||||||
if (mod->xxs[smp].len == 0) {
|
if (!IS_VALID_SAMPLE(smp)) {
|
||||||
smp = -1;
|
smp = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1510,7 +1532,7 @@ static int read_event_smix(struct context_data *ctx, struct xmp_event *e, int ch
|
||||||
struct xmp_module *mod = &m->mod;
|
struct xmp_module *mod = &m->mod;
|
||||||
struct channel_data *xc = &p->xc_data[chn];
|
struct channel_data *xc = &p->xc_data[chn];
|
||||||
struct xmp_subinstrument *sub;
|
struct xmp_subinstrument *sub;
|
||||||
int is_smix_ins;
|
struct xmp_instrument *xxi;
|
||||||
int ins, note, transp, smp;
|
int ins, note, transp, smp;
|
||||||
|
|
||||||
xc->flags = 0;
|
xc->flags = 0;
|
||||||
|
@ -1518,33 +1540,39 @@ static int read_event_smix(struct context_data *ctx, struct xmp_event *e, int ch
|
||||||
if (!e->ins)
|
if (!e->ins)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
is_smix_ins = 0;
|
|
||||||
ins = e->ins - 1;
|
ins = e->ins - 1;
|
||||||
SET(NEW_INS);
|
SET(NEW_INS);
|
||||||
xc->fadeout = 0x10000;
|
|
||||||
xc->per_flags = 0;
|
xc->per_flags = 0;
|
||||||
xc->offset.val = 0;
|
xc->offset.val = 0;
|
||||||
RESET_NOTE(NOTE_RELEASE);
|
RESET_NOTE(NOTE_RELEASE|NOTE_FADEOUT);
|
||||||
|
|
||||||
xc->ins = ins;
|
xxi = libxmp_get_instrument(ctx, ins);
|
||||||
|
if (xxi != NULL) {
|
||||||
if (ins >= mod->ins && ins < mod->ins + smix->ins) {
|
xc->ins_fade = xxi->rls;
|
||||||
is_smix_ins = 1;
|
|
||||||
xc->ins_fade = smix->xxi[xc->ins - mod->ins].rls;
|
|
||||||
}
|
}
|
||||||
|
xc->ins = ins;
|
||||||
|
|
||||||
SET(NEW_NOTE);
|
SET(NEW_NOTE);
|
||||||
|
|
||||||
if (e->note == XMP_KEY_OFF) {
|
if (e->note == XMP_KEY_OFF) {
|
||||||
SET_NOTE(NOTE_RELEASE);
|
SET_NOTE(NOTE_RELEASE);
|
||||||
return 0;
|
return 0;
|
||||||
|
} else if (e->note == XMP_KEY_FADE) {
|
||||||
|
SET_NOTE(NOTE_FADEOUT);
|
||||||
|
return 0;
|
||||||
|
} else if (e->note == XMP_KEY_CUT) {
|
||||||
|
SET_NOTE(NOTE_END);
|
||||||
|
xc->period = 0;
|
||||||
|
libxmp_virt_resetchannel(ctx, chn);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
xc->key = e->note - 1;
|
xc->key = e->note - 1;
|
||||||
|
xc->fadeout = 0x10000;
|
||||||
RESET_NOTE(NOTE_END);
|
RESET_NOTE(NOTE_END);
|
||||||
|
|
||||||
if (is_smix_ins) {
|
if (ins >= mod->ins && ins < mod->ins + smix->ins) {
|
||||||
sub = &smix->xxi[xc->ins - mod->ins].sub[0];
|
sub = &xxi->sub[0];
|
||||||
if (sub == NULL) {
|
if (sub == NULL) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1559,15 +1587,15 @@ static int read_event_smix(struct context_data *ctx, struct xmp_event *e, int ch
|
||||||
xc->smp = smp;
|
xc->smp = smp;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sub = is_valid_note(xc->key) ?
|
sub = IS_VALID_NOTE(xc->key) ?
|
||||||
get_subinstrument(ctx, xc->ins, xc->key) : NULL;
|
get_subinstrument(ctx, xc->ins, xc->key) : NULL;
|
||||||
if (sub == NULL) {
|
if (sub == NULL) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
transp = mod->xxi[xc->ins].map[xc->key].xpo;
|
transp = xxi->map[xc->key].xpo;
|
||||||
note = xc->key + sub->xpo + transp;
|
note = xc->key + sub->xpo + transp;
|
||||||
smp = sub->sid;
|
smp = sub->sid;
|
||||||
if (mod->xxs[smp].len == 0)
|
if (!IS_VALID_SAMPLE(smp))
|
||||||
smp = -1;
|
smp = -1;
|
||||||
if (smp >= 0 && smp < mod->smp) {
|
if (smp >= 0 && smp < mod->smp) {
|
||||||
set_patch(ctx, chn, xc->ins, smp, note);
|
set_patch(ctx, chn, xc->ins, smp, note);
|
||||||
|
|
|
@ -20,6 +20,9 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef LIBXMP_LOADERS_S3M_H
|
||||||
|
#define LIBXMP_LOADERS_S3M_H
|
||||||
|
|
||||||
/* S3M packed pattern macros */
|
/* S3M packed pattern macros */
|
||||||
#define S3M_EOR 0 /* End of row */
|
#define S3M_EOR 0 /* End of row */
|
||||||
#define S3M_CH_MASK 0x1f /* Channel */
|
#define S3M_CH_MASK 0x1f /* Channel */
|
||||||
|
@ -27,10 +30,16 @@
|
||||||
#define S3M_VOL_FOLLOWS 0x40 /* Volume follows */
|
#define S3M_VOL_FOLLOWS 0x40 /* Volume follows */
|
||||||
#define S3M_FX_FOLLOWS 0x80 /* Effect and parameter follow */
|
#define S3M_FX_FOLLOWS 0x80 /* Effect and parameter follow */
|
||||||
|
|
||||||
|
/* S3M mix volume macros */
|
||||||
|
#define S3M_MV_VOLUME 0x7f /* Module mix volume, typically 16 to 127 */
|
||||||
|
#define S3M_MV_STEREO 0x80 /* Module is stereo if set, otherwise mono */
|
||||||
|
|
||||||
/* S3M channel info macros */
|
/* S3M channel info macros */
|
||||||
#define S3M_CH_ON 0x80 /* Psi says it's bit 8, I'll assume bit 7 */
|
#define S3M_CH_ON 0x80 /* Psi says it's bit 8, I'll assume bit 7 */
|
||||||
#define S3M_CH_OFF 0xff
|
#define S3M_CH_OFF 0xff
|
||||||
#define S3M_CH_PAN 0x7f /* Left/Right */
|
#define S3M_CH_NUMBER 0x1f
|
||||||
|
#define S3M_CH_RIGHT 0x08
|
||||||
|
#define S3M_CH_ADLIB 0x10
|
||||||
|
|
||||||
/* S3M channel pan macros */
|
/* S3M channel pan macros */
|
||||||
#define S3M_PAN_SET 0x20
|
#define S3M_PAN_SET 0x20
|
||||||
|
@ -79,7 +88,8 @@ struct s3m_file_header {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct s3m_instrument_header {
|
struct s3m_instrument_header {
|
||||||
uint8 dosname[13]; /* DOS file name */
|
uint8 dosname[12]; /* DOS file name */
|
||||||
|
uint8 memseg_hi; /* High byte of sample pointer */
|
||||||
uint16 memseg; /* Pointer to sample data */
|
uint16 memseg; /* Pointer to sample data */
|
||||||
uint32 length; /* Length */
|
uint32 length; /* Length */
|
||||||
uint32 loopbeg; /* Loop begin */
|
uint32 loopbeg; /* Loop begin */
|
||||||
|
@ -113,4 +123,4 @@ struct s3m_adlib_header {
|
||||||
uint32 magic; /* 'SCRI' */
|
uint32 magic; /* 'SCRI' */
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
#endif /* LIBXMP_LOADERS_S3M_H */
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Extended Module Player
|
/* Extended Module Player
|
||||||
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
|
* Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -219,6 +219,7 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
|
||||||
uint8 n, b;
|
uint8 n, b;
|
||||||
uint16 *pp_ins; /* Parapointers to instruments */
|
uint16 *pp_ins; /* Parapointers to instruments */
|
||||||
uint16 *pp_pat; /* Parapointers to patterns */
|
uint16 *pp_pat; /* Parapointers to patterns */
|
||||||
|
int stereo;
|
||||||
int ret;
|
int ret;
|
||||||
uint8 buf[96]
|
uint8 buf[96]
|
||||||
|
|
||||||
|
@ -262,12 +263,12 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
|
||||||
|
|
||||||
libxmp_copy_adjust(mod->name, sfh.name, 28);
|
libxmp_copy_adjust(mod->name, sfh.name, 28);
|
||||||
|
|
||||||
pp_ins = calloc(2, sfh.insnum);
|
pp_ins = (uint16 *) calloc(sfh.insnum, sizeof(uint16));
|
||||||
if (pp_ins == NULL) {
|
if (pp_ins == NULL) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
pp_pat = calloc(2, sfh.patnum);
|
pp_pat = (uint16 *) calloc(sfh.patnum, sizeof(uint16));
|
||||||
if (pp_pat == NULL) {
|
if (pp_pat == NULL) {
|
||||||
goto err2;
|
goto err2;
|
||||||
}
|
}
|
||||||
|
@ -283,15 +284,49 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
|
||||||
mod->bpm = sfh.it;
|
mod->bpm = sfh.it;
|
||||||
mod->chn = 0;
|
mod->chn = 0;
|
||||||
|
|
||||||
|
/* Mix volume and stereo flag conversion (reported by Saga Musix).
|
||||||
|
* 1) Old format uses mix volume 0-7, and the stereo flag is 0x10.
|
||||||
|
* 2) Newer ST3s unconditionally convert MV 0x02 and 0x12 to 0x20.
|
||||||
|
*/
|
||||||
|
m->mvolbase = 48;
|
||||||
|
|
||||||
|
if (sfh.ffi == 1) {
|
||||||
|
m->mvol = ((sfh.mv & 0xf) + 1) * 0x10;
|
||||||
|
stereo = sfh.mv & 0x10;
|
||||||
|
CLAMP(m->mvol, 0x10, 0x7f);
|
||||||
|
|
||||||
|
} else if (sfh.mv == 0x02 || sfh.mv == 0x12) {
|
||||||
|
m->mvol = 0x20;
|
||||||
|
stereo = sfh.mv & 0x10;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
m->mvol = sfh.mv & S3M_MV_VOLUME;
|
||||||
|
stereo = sfh.mv & S3M_MV_STEREO;
|
||||||
|
|
||||||
|
if (m->mvol == 0) {
|
||||||
|
m->mvol = 48; /* Default is 48 */
|
||||||
|
} else if (m->mvol < 16) {
|
||||||
|
m->mvol = 16; /* Minimum is 16 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* "Note that in stereo, the mastermul is internally multiplied by
|
||||||
|
* 11/8 inside the player since there is generally more room in the
|
||||||
|
* output stream." Do the inverse to affect fewer modules. */
|
||||||
|
if (!stereo) {
|
||||||
|
m->mvol = m->mvol * 8 / 11;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < 32; i++) {
|
for (i = 0; i < 32; i++) {
|
||||||
|
int x;
|
||||||
if (sfh.chset[i] == S3M_CH_OFF)
|
if (sfh.chset[i] == S3M_CH_OFF)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
mod->chn = i + 1;
|
mod->chn = i + 1;
|
||||||
|
|
||||||
if (sfh.mv & 0x80) { /* stereo */
|
x = sfh.chset[i] & S3M_CH_NUMBER;
|
||||||
int x = sfh.chset[i] & S3M_CH_PAN;
|
if (stereo && x < S3M_CH_ADLIB) {
|
||||||
mod->xxc[i].pan = (x & 0x0f) < 8 ? 0x30 : 0xc0;
|
mod->xxc[i].pan = x < S3M_CH_RIGHT ? 0x30 : 0xc0;
|
||||||
} else {
|
} else {
|
||||||
mod->xxc[i].pan = 0x80;
|
mod->xxc[i].pan = 0x80;
|
||||||
}
|
}
|
||||||
|
@ -346,9 +381,6 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
|
||||||
uint8 x = hio_read8(f);
|
uint8 x = hio_read8(f);
|
||||||
if (x & S3M_PAN_SET) {
|
if (x & S3M_PAN_SET) {
|
||||||
mod->xxc[i].pan = (x << 4) & 0xff;
|
mod->xxc[i].pan = (x << 4) & 0xff;
|
||||||
} else {
|
|
||||||
mod->xxc[i].pan =
|
|
||||||
sfh.mv % 0x80 ? 0x30 + 0xa0 * (i & 1) : 0x80;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,8 +413,15 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
snprintf(tracker_name, 40, "OpenMPT %d.%02x",
|
if (sfh.version == 0x5447) {
|
||||||
(sfh.version & 0x0f00) >> 8, sfh.version & 0xff);
|
strcpy(tracker_name, "Graoumf Tracker");
|
||||||
|
} else if (sfh.rsvd2[0] || sfh.rsvd2[1]) {
|
||||||
|
snprintf(tracker_name, 40, "OpenMPT %d.%02x.%02x.%02x",
|
||||||
|
(sfh.version & 0x0f00) >> 8, sfh.version & 0xff, sfh.rsvd2[1], sfh.rsvd2[0]);
|
||||||
|
} else {
|
||||||
|
snprintf(tracker_name, 40, "OpenMPT %d.%02x",
|
||||||
|
(sfh.version & 0x0f00) >> 8, sfh.version & 0xff);
|
||||||
|
}
|
||||||
m->quirk |= QUIRK_ST3BUGS;
|
m->quirk |= QUIRK_ST3BUGS;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
|
@ -472,7 +511,7 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
D_(D_INFO "Stereo enabled: %s", sfh.mv & 0x80 ? "yes" : "no");
|
D_(D_INFO "Stereo enabled: %s", stereo ? "yes" : "no");
|
||||||
D_(D_INFO "Pan settings: %s", sfh.dp ? "no" : "yes");
|
D_(D_INFO "Pan settings: %s", sfh.dp ? "no" : "yes");
|
||||||
|
|
||||||
if (libxmp_init_instrument(m) < 0)
|
if (libxmp_init_instrument(m) < 0)
|
||||||
|
@ -487,8 +526,9 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
|
||||||
struct xmp_sample *xxs = &mod->xxs[i];
|
struct xmp_sample *xxs = &mod->xxs[i];
|
||||||
struct xmp_subinstrument *sub;
|
struct xmp_subinstrument *sub;
|
||||||
int load_sample_flags;
|
int load_sample_flags;
|
||||||
|
uint32 sample_segment;
|
||||||
|
|
||||||
xxi->sub = calloc(sizeof(struct xmp_subinstrument), 1);
|
xxi->sub = (struct xmp_subinstrument *) calloc(1, sizeof(struct xmp_subinstrument));
|
||||||
if (xxi->sub == NULL) {
|
if (xxi->sub == NULL) {
|
||||||
goto err3;
|
goto err3;
|
||||||
}
|
}
|
||||||
|
@ -527,9 +567,7 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
|
||||||
sub->vol = sah.vol;
|
sub->vol = sah.vol;
|
||||||
libxmp_c2spd_to_note(sah.c2spd, &sub->xpo, &sub->fin);
|
libxmp_c2spd_to_note(sah.c2spd, &sub->xpo, &sub->fin);
|
||||||
sub->xpo += 12;
|
sub->xpo += 12;
|
||||||
ret =
|
ret = libxmp_load_sample(m, f, SAMPLE_FLAG_ADLIB, xxs, (char *)sah.reg);
|
||||||
libxmp_load_sample(m, f, SAMPLE_FLAG_ADLIB, xxs,
|
|
||||||
(char *)sah.reg);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err3;
|
goto err3;
|
||||||
|
|
||||||
|
@ -541,7 +579,8 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(sih.dosname, buf + 1, 13); /* DOS file name */
|
memcpy(sih.dosname, buf + 1, 12); /* DOS file name */
|
||||||
|
sih.memseg_hi = buf[13]; /* High byte of sample pointer */
|
||||||
sih.memseg = readmem16l(buf + 14); /* Pointer to sample data */
|
sih.memseg = readmem16l(buf + 14); /* Pointer to sample data */
|
||||||
sih.length = readmem32l(buf + 16); /* Length */
|
sih.length = readmem32l(buf + 16); /* Length */
|
||||||
|
|
||||||
|
@ -598,7 +637,9 @@ static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
|
||||||
|
|
||||||
libxmp_c2spd_to_note(sih.c2spd, &sub->xpo, &sub->fin);
|
libxmp_c2spd_to_note(sih.c2spd, &sub->xpo, &sub->fin);
|
||||||
|
|
||||||
if (hio_seek(f, start + 16L * sih.memseg, SEEK_SET) < 0) {
|
sample_segment = sih.memseg + ((uint32)sih.memseg_hi << 16);
|
||||||
|
|
||||||
|
if (hio_seek(f, start + 16L * sample_segment, SEEK_SET) < 0) {
|
||||||
goto err3;
|
goto err3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Extended Module Player
|
/* Extended Module Player
|
||||||
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
|
* Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -24,7 +24,6 @@
|
||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* From the Audio File Formats (version 2.5)
|
* From the Audio File Formats (version 2.5)
|
||||||
* Submitted-by: Guido van Rossum <guido@cwi.nl>
|
* Submitted-by: Guido van Rossum <guido@cwi.nl>
|
||||||
|
@ -55,7 +54,6 @@ static const int8 vdic_table[128] = {
|
||||||
/* 120 */ 95, 98, 103, 109, 114, 120, 126, 127
|
/* 120 */ 95, 98, 103, 109, 114, 120, 126, 127
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Convert 7 bit samples to 8 bit */
|
/* Convert 7 bit samples to 8 bit */
|
||||||
static void convert_7bit_to_8bit(uint8 *p, int l)
|
static void convert_7bit_to_8bit(uint8 *p, int l)
|
||||||
{
|
{
|
||||||
|
@ -95,24 +93,23 @@ static void adpcm4_decoder(uint8 *inp, uint8 *outp, char *tab, int len)
|
||||||
*outp++ = delta;
|
*outp++ = delta;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Convert differential to absolute sample data */
|
/* Convert differential to absolute sample data */
|
||||||
static void convert_delta(uint8 *p, int l, int r)
|
static void convert_delta(uint8 *p, int l, int r)
|
||||||
{
|
{
|
||||||
uint16 *w = (uint16 *)p;
|
uint16 *w = (uint16 *)p;
|
||||||
uint16 abs = 0;
|
uint16 absval = 0;
|
||||||
|
|
||||||
if (r) {
|
if (r) {
|
||||||
for (; l--;) {
|
for (; l--;) {
|
||||||
abs = *w + abs;
|
absval = *w + absval;
|
||||||
*w++ = abs;
|
*w++ = absval;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (; l--;) {
|
for (; l--;) {
|
||||||
abs = *p + abs;
|
absval = *p + absval;
|
||||||
*p++ = (uint8) abs;
|
*p++ = (uint8) absval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,41 +160,10 @@ static void convert_stereo_to_mono(uint8 *p, int l, int r)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void unroll_loop(struct xmp_sample *xxs)
|
|
||||||
{
|
|
||||||
int8 *s8;
|
|
||||||
int16 *s16;
|
|
||||||
int start, loop_size;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
s16 = (int16 *)xxs->data;
|
|
||||||
s8 = (int8 *)xxs->data;
|
|
||||||
|
|
||||||
if (xxs->len > xxs->lpe) {
|
|
||||||
start = xxs->lpe;
|
|
||||||
} else {
|
|
||||||
start = xxs->len;
|
|
||||||
}
|
|
||||||
|
|
||||||
loop_size = xxs->lpe - xxs->lps;
|
|
||||||
|
|
||||||
if (xxs->flg & XMP_SAMPLE_16BIT) {
|
|
||||||
s16 += start;
|
|
||||||
for (i = 0; i < loop_size; i++) {
|
|
||||||
*(s16 + i) = *(s16 - i - 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
s8 += start;
|
|
||||||
for (i = 0; i < loop_size; i++) {
|
|
||||||
*(s8 + i) = *(s8 - i - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct xmp_sample *xxs, const void *buffer)
|
int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct xmp_sample *xxs, const void *buffer)
|
||||||
{
|
{
|
||||||
int bytelen, extralen, unroll_extralen, i;
|
int bytelen, extralen, i;
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
/* Adlib FM patches */
|
/* Adlib FM patches */
|
||||||
|
@ -264,7 +230,6 @@ int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct x
|
||||||
*/
|
*/
|
||||||
bytelen = xxs->len;
|
bytelen = xxs->len;
|
||||||
extralen = 4;
|
extralen = 4;
|
||||||
unroll_extralen = 0;
|
|
||||||
|
|
||||||
/* Disable birectional loop flag if sample is not looped
|
/* Disable birectional loop flag if sample is not looped
|
||||||
*/
|
*/
|
||||||
|
@ -272,25 +237,18 @@ int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct x
|
||||||
if (~xxs->flg & XMP_SAMPLE_LOOP)
|
if (~xxs->flg & XMP_SAMPLE_LOOP)
|
||||||
xxs->flg &= ~XMP_SAMPLE_LOOP_BIDIR;
|
xxs->flg &= ~XMP_SAMPLE_LOOP_BIDIR;
|
||||||
}
|
}
|
||||||
/* Unroll bidirectional loops
|
if (xxs->flg & XMP_SAMPLE_SLOOP_BIDIR) {
|
||||||
*/
|
if (~xxs->flg & XMP_SAMPLE_SLOOP)
|
||||||
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
|
xxs->flg &= ~XMP_SAMPLE_SLOOP_BIDIR;
|
||||||
unroll_extralen = (xxs->lpe - xxs->lps) -
|
|
||||||
(xxs->len - xxs->lpe);
|
|
||||||
|
|
||||||
if (unroll_extralen < 0) {
|
|
||||||
unroll_extralen = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xxs->flg & XMP_SAMPLE_16BIT) {
|
if (xxs->flg & XMP_SAMPLE_16BIT) {
|
||||||
bytelen *= 2;
|
bytelen *= 2;
|
||||||
extralen *= 2;
|
extralen *= 2;
|
||||||
unroll_extralen *= 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* add guard bytes before the buffer for higher order interpolation */
|
/* add guard bytes before the buffer for higher order interpolation */
|
||||||
xxs->data = malloc(bytelen + extralen + unroll_extralen + 4);
|
xxs->data = (unsigned char *) malloc(bytelen + extralen + 4);
|
||||||
if (xxs->data == NULL) {
|
if (xxs->data == NULL) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
@ -379,12 +337,6 @@ int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct x
|
||||||
xxs->flg |= XMP_SAMPLE_LOOP_FULL;
|
xxs->flg |= XMP_SAMPLE_LOOP_FULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unroll bidirectional loops */
|
|
||||||
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
|
|
||||||
unroll_loop(xxs);
|
|
||||||
bytelen += unroll_extralen;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add extra samples at end */
|
/* Add extra samples at end */
|
||||||
if (xxs->flg & XMP_SAMPLE_16BIT) {
|
if (xxs->flg & XMP_SAMPLE_16BIT) {
|
||||||
for (i = 0; i < 8; i++) {
|
for (i = 0; i < 8; i++) {
|
||||||
|
@ -404,28 +356,6 @@ int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct x
|
||||||
xxs->data[-1] = xxs->data[0];
|
xxs->data[-1] = xxs->data[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fix sample at loop */
|
|
||||||
if (xxs->flg & XMP_SAMPLE_LOOP) {
|
|
||||||
int lpe = xxs->lpe;
|
|
||||||
int lps = xxs->lps;
|
|
||||||
|
|
||||||
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
|
|
||||||
lpe += lpe - lps;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xxs->flg & XMP_SAMPLE_16BIT) {
|
|
||||||
lpe <<= 1;
|
|
||||||
lps <<= 1;
|
|
||||||
for (i = 0; i < 8; i++) {
|
|
||||||
xxs->data[lpe + i] = xxs->data[lps + i];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < 4; i++) {
|
|
||||||
xxs->data[lpe + i] = xxs->data[lps + i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
@ -438,8 +368,8 @@ int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct x
|
||||||
|
|
||||||
void libxmp_free_sample(struct xmp_sample *s)
|
void libxmp_free_sample(struct xmp_sample *s)
|
||||||
{
|
{
|
||||||
if (s->data) {
|
if (s->data) {
|
||||||
free(s->data - 4);
|
free(s->data - 4);
|
||||||
s->data = NULL; /* prevent double free in PCM load error */
|
s->data = NULL; /* prevent double free in PCM load error */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Extended Module Player
|
/* Extended Module Player
|
||||||
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
|
* Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -42,6 +42,12 @@
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
#include "mixer.h"
|
#include "mixer.h"
|
||||||
|
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
#include "far_extras.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define VBLANK_TIME_THRESHOLD 480000 /* 8 minutes */
|
||||||
|
|
||||||
#define S3M_END 0xff
|
#define S3M_END 0xff
|
||||||
#define S3M_SKIP 0xfe
|
#define S3M_SKIP 0xfe
|
||||||
|
|
||||||
|
@ -50,30 +56,35 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
|
||||||
{
|
{
|
||||||
struct player_data *p = &ctx->p;
|
struct player_data *p = &ctx->p;
|
||||||
struct module_data *m = &ctx->m;
|
struct module_data *m = &ctx->m;
|
||||||
struct xmp_module *mod = &m->mod;
|
const struct xmp_module *mod = &m->mod;
|
||||||
|
const struct xmp_track *tracks[XMP_MAX_CHANNELS];
|
||||||
|
const struct xmp_event *event;
|
||||||
int parm, gvol_memory, f1, f2, p1, p2, ord, ord2;
|
int parm, gvol_memory, f1, f2, p1, p2, ord, ord2;
|
||||||
int row, last_row, break_row, row_count, row_count_total;
|
int row, last_row, break_row, row_count, row_count_total;
|
||||||
int orders_since_last_valid, any_valid;
|
int orders_since_last_valid, any_valid;
|
||||||
int gvl, bpm, speed, base_time, chn;
|
int gvl, bpm, speed, base_time, chn;
|
||||||
int frame_count;
|
int frame_count;
|
||||||
double time, start_time;
|
double time, start_time;
|
||||||
int loop_chn, loop_num, inside_loop;
|
int loop_chn, loop_num, inside_loop, line_jump;
|
||||||
int pdelay = 0;
|
int pdelay = 0;
|
||||||
int loop_count[XMP_MAX_CHANNELS];
|
int loop_count[XMP_MAX_CHANNELS];
|
||||||
int loop_row[XMP_MAX_CHANNELS];
|
int loop_row[XMP_MAX_CHANNELS];
|
||||||
struct xmp_event* event;
|
|
||||||
int i, pat;
|
int i, pat;
|
||||||
int has_marker;
|
int has_marker;
|
||||||
struct ord_data *info;
|
struct ord_data *info;
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
int st26_speed;
|
int st26_speed;
|
||||||
|
int far_tempo_coarse, far_tempo_fine, far_tempo_mode;
|
||||||
#endif
|
#endif
|
||||||
|
/* was 255, but Global trash goes to 318.
|
||||||
|
* Higher limit for MEDs, defiance.crybaby.5 has blocks with 2048+ rows. */
|
||||||
|
const int row_limit = IS_PLAYER_MODE_MED() ? 3200 : 512;
|
||||||
|
|
||||||
if (mod->len == 0)
|
if (mod->len == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
for (i = 0; i < mod->len; i++) {
|
for (i = 0; i < mod->len; i++) {
|
||||||
int pat = mod->xxo[i];
|
pat = mod->xxo[i];
|
||||||
memset(m->scan_cnt[i], 0, pat >= mod->pat ? 1 :
|
memset(m->scan_cnt[i], 0, pat >= mod->pat ? 1 :
|
||||||
mod->xxp[pat]->rows ? mod->xxp[pat]->rows : 1);
|
mod->xxp[pat]->rows ? mod->xxp[pat]->rows : 1);
|
||||||
}
|
}
|
||||||
|
@ -84,6 +95,7 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
|
||||||
}
|
}
|
||||||
loop_num = 0;
|
loop_num = 0;
|
||||||
loop_chn = -1;
|
loop_chn = -1;
|
||||||
|
line_jump = 0;
|
||||||
|
|
||||||
gvl = mod->gvl;
|
gvl = mod->gvl;
|
||||||
bpm = mod->bpm;
|
bpm = mod->bpm;
|
||||||
|
@ -92,6 +104,15 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
|
||||||
base_time = m->rrate;
|
base_time = m->rrate;
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
st26_speed = 0;
|
st26_speed = 0;
|
||||||
|
far_tempo_coarse = 4;
|
||||||
|
far_tempo_fine = 0;
|
||||||
|
far_tempo_mode = 1;
|
||||||
|
|
||||||
|
if (HAS_FAR_MODULE_EXTRAS(ctx->m)) {
|
||||||
|
far_tempo_coarse = FAR_MODULE_EXTRAS(ctx->m)->coarse_tempo;
|
||||||
|
libxmp_far_translate_tempo(far_tempo_mode, 0, far_tempo_coarse,
|
||||||
|
&far_tempo_fine, &speed, &bpm);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
has_marker = HAS_QUIRK(QUIRK_MARKER);
|
has_marker = HAS_QUIRK(QUIRK_MARKER);
|
||||||
|
@ -196,6 +217,11 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
|
||||||
info->start_row = break_row;
|
info->start_row = break_row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get tracks in advance to speed up the event parsing loop. */
|
||||||
|
for (chn = 0; chn < mod->chn; chn++) {
|
||||||
|
tracks[chn] = mod->xxt[TRACK_NUM(pat, chn)];
|
||||||
|
}
|
||||||
|
|
||||||
last_row = mod->xxp[pat]->rows;
|
last_row = mod->xxp[pat]->rows;
|
||||||
for (row = break_row, break_row = 0; row < last_row; row++, row_count++, row_count_total++) {
|
for (row = break_row, break_row = 0; row < last_row; row++, row_count++, row_count_total++) {
|
||||||
/* Prevent crashes caused by large softmixer frames */
|
/* Prevent crashes caused by large softmixer frames */
|
||||||
|
@ -216,12 +242,12 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
|
||||||
* (...) it dies at the end of position 2F
|
* (...) it dies at the end of position 2F
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (row_count_total > 512) { /* was 255, but Global trash goes to 318. */
|
if (row_count_total > row_limit) {
|
||||||
D_(D_CRIT "row_count_total = %d @ ord %d, pat %d, row %d; ending scan", row_count_total, ord, pat, row);
|
D_(D_CRIT "row_count_total = %d @ ord %d, pat %d, row %d; ending scan", row_count_total, ord, pat, row);
|
||||||
goto end_module;
|
goto end_module;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!loop_num && m->scan_cnt[ord][row]) {
|
if (!loop_num && !line_jump && m->scan_cnt[ord][row]) {
|
||||||
row_count--;
|
row_count--;
|
||||||
goto end_module;
|
goto end_module;
|
||||||
}
|
}
|
||||||
|
@ -237,12 +263,14 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
|
||||||
}
|
}
|
||||||
|
|
||||||
pdelay = 0;
|
pdelay = 0;
|
||||||
|
line_jump = 0;
|
||||||
|
|
||||||
for (chn = 0; chn < mod->chn; chn++) {
|
for (chn = 0; chn < mod->chn; chn++) {
|
||||||
if (row >= mod->xxt[mod->xxp[pat]->index[chn]]->rows)
|
if (row >= tracks[chn]->rows)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
event = &EVENT(mod->xxo[ord], chn, row);
|
//event = &EVENT(mod->xxo[ord], chn, row);
|
||||||
|
event = &tracks[chn]->event[row];
|
||||||
|
|
||||||
f1 = event->fxt;
|
f1 = event->fxt;
|
||||||
p1 = event->fxp;
|
p1 = event->fxp;
|
||||||
|
@ -290,23 +318,23 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((f1 == FX_SPEED && p1) || (f2 == FX_SPEED && p2)) {
|
/* Some formats can have two FX_SPEED effects, and both need
|
||||||
parm = (f1 == FX_SPEED) ? p1 : p2;
|
* to be checked. Slot 2 is currently handled first. */
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
parm = i ? p1 : p2;
|
||||||
|
if ((i ? f1 : f2) != FX_SPEED || parm == 0)
|
||||||
|
continue;
|
||||||
frame_count += row_count * speed;
|
frame_count += row_count * speed;
|
||||||
row_count = 0;
|
row_count = 0;
|
||||||
if (parm) {
|
if (HAS_QUIRK(QUIRK_NOBPM) || p->flags & XMP_FLAGS_VBLANK || parm < 0x20) {
|
||||||
if (HAS_QUIRK(QUIRK_NOBPM) || p->flags & XMP_FLAGS_VBLANK || parm < 0x20) {
|
speed = parm;
|
||||||
if (parm > 0) {
|
|
||||||
speed = parm;
|
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
st26_speed = 0;
|
st26_speed = 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
} else {
|
||||||
} else {
|
time += m->time_factor * frame_count * base_time / bpm;
|
||||||
time += m->time_factor * frame_count * base_time / bpm;
|
frame_count = 0;
|
||||||
frame_count = 0;
|
bpm = parm;
|
||||||
bpm = parm;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,6 +355,39 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
|
||||||
st26_speed = MSN(p1);
|
st26_speed = MSN(p1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* FAR tempo processing */
|
||||||
|
|
||||||
|
if (f1 == FX_FAR_TEMPO || f1 == FX_FAR_F_TEMPO) {
|
||||||
|
int far_speed, far_bpm, fine_change = 0;
|
||||||
|
if (f1 == FX_FAR_TEMPO) {
|
||||||
|
if (MSN(p1)) {
|
||||||
|
far_tempo_mode = MSN(p1) - 1;
|
||||||
|
} else {
|
||||||
|
far_tempo_coarse = LSN(p1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (f1 == FX_FAR_F_TEMPO) {
|
||||||
|
if (MSN(p1)) {
|
||||||
|
far_tempo_fine += MSN(p1);
|
||||||
|
fine_change = MSN(p1);
|
||||||
|
} else if (LSN(p1)) {
|
||||||
|
far_tempo_fine -= LSN(p1);
|
||||||
|
fine_change = -LSN(p1);
|
||||||
|
} else {
|
||||||
|
far_tempo_fine = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (libxmp_far_translate_tempo(far_tempo_mode, fine_change,
|
||||||
|
far_tempo_coarse, &far_tempo_fine, &far_speed, &far_bpm) == 0) {
|
||||||
|
frame_count += row_count * speed;
|
||||||
|
row_count = 0;
|
||||||
|
time += m->time_factor * frame_count * base_time / bpm;
|
||||||
|
frame_count = 0;
|
||||||
|
speed = far_speed;
|
||||||
|
bpm = far_bpm;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ((f1 == FX_S3M_SPEED && p1) || (f2 == FX_S3M_SPEED && p2)) {
|
if ((f1 == FX_S3M_SPEED && p1) || (f2 == FX_S3M_SPEED && p2)) {
|
||||||
|
@ -343,7 +404,7 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
|
||||||
|
|
||||||
if ((f1 == FX_S3M_BPM && p1) || (f2 == FX_S3M_BPM && p2)) {
|
if ((f1 == FX_S3M_BPM && p1) || (f2 == FX_S3M_BPM && p2)) {
|
||||||
parm = (f1 == FX_S3M_BPM) ? p1 : p2;
|
parm = (f1 == FX_S3M_BPM) ? p1 : p2;
|
||||||
if (parm >= 0x20) {
|
if (parm >= XMP_MIN_BPM) {
|
||||||
frame_count += row_count * speed;
|
frame_count += row_count * speed;
|
||||||
row_count = 0;
|
row_count = 0;
|
||||||
time += m->time_factor * frame_count * base_time / bpm;
|
time += m->time_factor * frame_count * base_time / bpm;
|
||||||
|
@ -417,6 +478,19 @@ static int scan_module(struct context_data *ctx, int ep, int chain)
|
||||||
last_row = 0;
|
last_row = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
/* Archimedes line jump */
|
||||||
|
if (f1 == FX_LINE_JUMP || f2 == FX_LINE_JUMP) {
|
||||||
|
/* Don't set order if preceded by jump or break. */
|
||||||
|
if (last_row > 0)
|
||||||
|
ord2 = ord;
|
||||||
|
parm = (f1 == FX_LINE_JUMP) ? p1 : p2;
|
||||||
|
break_row = parm;
|
||||||
|
last_row = 0;
|
||||||
|
line_jump = 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (f1 == FX_EXTENDED || f2 == FX_EXTENDED) {
|
if (f1 == FX_EXTENDED || f2 == FX_EXTENDED) {
|
||||||
parm = (f1 == FX_EXTENDED) ? p1 : p2;
|
parm = (f1 == FX_EXTENDED) ? p1 : p2;
|
||||||
|
|
||||||
|
@ -514,6 +588,56 @@ end_module:
|
||||||
return (time + m->time_factor * frame_count * base_time / bpm);
|
return (time + m->time_factor * frame_count * base_time / bpm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void reset_scan_data(struct context_data *ctx)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < XMP_MAX_MOD_LENGTH; i++) {
|
||||||
|
ctx->m.xxo_info[i].time = -1;
|
||||||
|
}
|
||||||
|
memset(ctx->p.sequence_control, 0xff, XMP_MAX_MOD_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
static void compare_vblank_scan(struct context_data *ctx)
|
||||||
|
{
|
||||||
|
/* Calculate both CIA and VBlank time for certain long MODs
|
||||||
|
* and pick the more likely (i.e. shorter) one. The same logic
|
||||||
|
* works regardless of the initial mode selected--either way,
|
||||||
|
* the wrong timing mode usually makes modules MUCH longer. */
|
||||||
|
struct player_data *p = &ctx->p;
|
||||||
|
struct module_data *m = &ctx->m;
|
||||||
|
struct ord_data *info_backup;
|
||||||
|
struct scan_data scan_backup;
|
||||||
|
unsigned char ctrl_backup[256];
|
||||||
|
|
||||||
|
if ((info_backup = (struct ord_data *)malloc(sizeof(m->xxo_info))) != NULL) {
|
||||||
|
/* Back up the current info to avoid a third scan. */
|
||||||
|
scan_backup = p->scan[0];
|
||||||
|
memcpy(info_backup, m->xxo_info, sizeof(m->xxo_info));
|
||||||
|
memcpy(ctrl_backup, p->sequence_control,
|
||||||
|
sizeof(p->sequence_control));
|
||||||
|
|
||||||
|
reset_scan_data(ctx);
|
||||||
|
|
||||||
|
m->quirk ^= QUIRK_NOBPM;
|
||||||
|
p->scan[0].time = scan_module(ctx, 0, 0);
|
||||||
|
|
||||||
|
D_(D_INFO "%-6s %dms", !HAS_QUIRK(QUIRK_NOBPM)?"VBlank":"CIA", scan_backup.time);
|
||||||
|
D_(D_INFO "%-6s %dms", HAS_QUIRK(QUIRK_NOBPM)?"VBlank":"CIA", p->scan[0].time);
|
||||||
|
|
||||||
|
if (p->scan[0].time >= scan_backup.time) {
|
||||||
|
m->quirk ^= QUIRK_NOBPM;
|
||||||
|
p->scan[0] = scan_backup;
|
||||||
|
memcpy(m->xxo_info, info_backup, sizeof(m->xxo_info));
|
||||||
|
memcpy(p->sequence_control, ctrl_backup,
|
||||||
|
sizeof(p->sequence_control));
|
||||||
|
}
|
||||||
|
|
||||||
|
free(info_backup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int libxmp_get_sequence(struct context_data *ctx, int ord)
|
int libxmp_get_sequence(struct context_data *ctx, int ord)
|
||||||
{
|
{
|
||||||
struct player_data *p = &ctx->p;
|
struct player_data *p = &ctx->p;
|
||||||
|
@ -525,23 +649,35 @@ int libxmp_scan_sequences(struct context_data *ctx)
|
||||||
struct player_data *p = &ctx->p;
|
struct player_data *p = &ctx->p;
|
||||||
struct module_data *m = &ctx->m;
|
struct module_data *m = &ctx->m;
|
||||||
struct xmp_module *mod = &m->mod;
|
struct xmp_module *mod = &m->mod;
|
||||||
|
struct scan_data *s;
|
||||||
int i, ep;
|
int i, ep;
|
||||||
int seq;
|
int seq;
|
||||||
unsigned char temp_ep[XMP_MAX_MOD_LENGTH];
|
unsigned char temp_ep[XMP_MAX_MOD_LENGTH];
|
||||||
|
|
||||||
|
s = (struct scan_data *) realloc(p->scan, MAX(1, mod->len) * sizeof(struct scan_data));
|
||||||
|
if (!s) {
|
||||||
|
D_(D_CRIT "failed to allocate scan data");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
p->scan = s;
|
||||||
|
|
||||||
/* Initialize order data to prevent overwrite when a position is used
|
/* Initialize order data to prevent overwrite when a position is used
|
||||||
* multiple times at different starting points (see janosik.xm).
|
* multiple times at different starting points (see janosik.xm).
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < XMP_MAX_MOD_LENGTH; i++) {
|
reset_scan_data(ctx);
|
||||||
m->xxo_info[i].time = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ep = 0;
|
ep = 0;
|
||||||
memset(p->sequence_control, 0xff, XMP_MAX_MOD_LENGTH);
|
|
||||||
temp_ep[0] = 0;
|
temp_ep[0] = 0;
|
||||||
p->scan[0].time = scan_module(ctx, ep, 0);
|
p->scan[0].time = scan_module(ctx, ep, 0);
|
||||||
seq = 1;
|
seq = 1;
|
||||||
|
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
if (m->compare_vblank && !(p->flags & XMP_FLAGS_VBLANK) &&
|
||||||
|
p->scan[0].time >= VBLANK_TIME_THRESHOLD) {
|
||||||
|
compare_vblank_scan(ctx);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (p->scan[0].time < 0) {
|
if (p->scan[0].time < 0) {
|
||||||
D_(D_CRIT "scan was not able to find any valid orders");
|
D_(D_CRIT "scan was not able to find any valid orders");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -567,6 +703,12 @@ int libxmp_scan_sequences(struct context_data *ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (seq < mod->len) {
|
||||||
|
s = (struct scan_data *) realloc(p->scan, seq * sizeof(struct scan_data));
|
||||||
|
if (s != NULL) {
|
||||||
|
p->scan = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
m->num_sequences = seq;
|
m->num_sequences = seq;
|
||||||
|
|
||||||
/* Now place entry points in the public accessible array */
|
/* Now place entry points in the public accessible array */
|
||||||
|
@ -575,6 +717,5 @@ int libxmp_scan_sequences(struct context_data *ctx)
|
||||||
m->seq_data[i].duration = p->scan[i].time;
|
m->seq_data[i].duration = p->scan[i].time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Extended Module Player
|
/* Extended Module Player
|
||||||
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
|
* Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -34,7 +34,9 @@ struct xmp_instrument *libxmp_get_instrument(struct context_data *ctx, int ins)
|
||||||
struct xmp_module *mod = &m->mod;
|
struct xmp_module *mod = &m->mod;
|
||||||
struct xmp_instrument *xxi;
|
struct xmp_instrument *xxi;
|
||||||
|
|
||||||
if (ins < mod->ins) {
|
if (ins < 0) {
|
||||||
|
xxi = NULL;
|
||||||
|
} else if (ins < mod->ins) {
|
||||||
xxi = &mod->xxi[ins];
|
xxi = &mod->xxi[ins];
|
||||||
} else if (ins < mod->ins + smix->ins) {
|
} else if (ins < mod->ins + smix->ins) {
|
||||||
xxi = &smix->xxi[ins - mod->ins];
|
xxi = &smix->xxi[ins - mod->ins];
|
||||||
|
@ -52,7 +54,9 @@ struct xmp_sample *libxmp_get_sample(struct context_data *ctx, int smp)
|
||||||
struct xmp_module *mod = &m->mod;
|
struct xmp_module *mod = &m->mod;
|
||||||
struct xmp_sample *xxs;
|
struct xmp_sample *xxs;
|
||||||
|
|
||||||
if (smp < mod->smp) {
|
if (smp < 0) {
|
||||||
|
xxs = NULL;
|
||||||
|
} else if (smp < mod->smp) {
|
||||||
xxs = &mod->xxs[smp];
|
xxs = &mod->xxs[smp];
|
||||||
} else if (smp < mod->smp + smix->smp) {
|
} else if (smp < mod->smp + smix->smp) {
|
||||||
xxs = &smix->xxs[smp - mod->smp];
|
xxs = &smix->xxs[smp - mod->smp];
|
||||||
|
@ -72,11 +76,11 @@ int xmp_start_smix(xmp_context opaque, int chn, int smp)
|
||||||
return -XMP_ERROR_STATE;
|
return -XMP_ERROR_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
smix->xxi = calloc(sizeof (struct xmp_instrument), smp);
|
smix->xxi = (struct xmp_instrument *) calloc(smp, sizeof(struct xmp_instrument));
|
||||||
if (smix->xxi == NULL) {
|
if (smix->xxi == NULL) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
smix->xxs = calloc(sizeof (struct xmp_sample), smp);
|
smix->xxs = (struct xmp_sample *) calloc(smp, sizeof(struct xmp_sample));
|
||||||
if (smix->xxs == NULL) {
|
if (smix->xxs == NULL) {
|
||||||
goto err1;
|
goto err1;
|
||||||
}
|
}
|
||||||
|
@ -106,7 +110,7 @@ int xmp_smix_play_instrument(xmp_context opaque, int ins, int note, int vol, int
|
||||||
return -XMP_ERROR_STATE;
|
return -XMP_ERROR_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chn >= smix->chn || ins >= mod->ins) {
|
if (chn >= smix->chn || chn < 0 || ins >= mod->ins || ins < 0) {
|
||||||
return -XMP_ERROR_INVALID;
|
return -XMP_ERROR_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +120,7 @@ int xmp_smix_play_instrument(xmp_context opaque, int ins, int note, int vol, int
|
||||||
|
|
||||||
event = &p->inject_event[mod->chn + chn];
|
event = &p->inject_event[mod->chn + chn];
|
||||||
memset(event, 0, sizeof (struct xmp_event));
|
memset(event, 0, sizeof (struct xmp_event));
|
||||||
event->note = note + 1;
|
event->note = (note < XMP_MAX_KEYS) ? note + 1 : note;
|
||||||
event->ins = ins + 1;
|
event->ins = ins + 1;
|
||||||
event->vol = vol + 1;
|
event->vol = vol + 1;
|
||||||
event->_flag = 1;
|
event->_flag = 1;
|
||||||
|
@ -137,7 +141,7 @@ int xmp_smix_play_sample(xmp_context opaque, int ins, int note, int vol, int chn
|
||||||
return -XMP_ERROR_STATE;
|
return -XMP_ERROR_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chn >= smix->chn || ins >= smix->ins) {
|
if (chn >= smix->chn || chn < 0 || ins >= smix->ins || ins < 0) {
|
||||||
return -XMP_ERROR_INVALID;
|
return -XMP_ERROR_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +151,7 @@ int xmp_smix_play_sample(xmp_context opaque, int ins, int note, int vol, int chn
|
||||||
|
|
||||||
event = &p->inject_event[mod->chn + chn];
|
event = &p->inject_event[mod->chn + chn];
|
||||||
memset(event, 0, sizeof (struct xmp_event));
|
memset(event, 0, sizeof (struct xmp_event));
|
||||||
event->note = note + 1;
|
event->note = (note < XMP_MAX_KEYS) ? note + 1 : note;
|
||||||
event->ins = mod->ins + ins + 1;
|
event->ins = mod->ins + ins + 1;
|
||||||
event->vol = vol + 1;
|
event->vol = vol + 1;
|
||||||
event->_flag = 1;
|
event->_flag = 1;
|
||||||
|
@ -201,7 +205,7 @@ int xmp_smix_load_sample(xmp_context opaque, int num, const char *path)
|
||||||
|
|
||||||
/* Init instrument */
|
/* Init instrument */
|
||||||
|
|
||||||
xxi->sub = calloc(sizeof(struct xmp_subinstrument), 1);
|
xxi->sub = (struct xmp_subinstrument *) calloc(1, sizeof(struct xmp_subinstrument));
|
||||||
if (xxi->sub == NULL) {
|
if (xxi->sub == NULL) {
|
||||||
retval = -XMP_ERROR_SYSTEM;
|
retval = -XMP_ERROR_SYSTEM;
|
||||||
goto err1;
|
goto err1;
|
||||||
|
@ -264,7 +268,7 @@ int xmp_smix_load_sample(xmp_context opaque, int num, const char *path)
|
||||||
xxs->lpe = 0;
|
xxs->lpe = 0;
|
||||||
xxs->flg = bits == 16 ? XMP_SAMPLE_16BIT : 0;
|
xxs->flg = bits == 16 ? XMP_SAMPLE_16BIT : 0;
|
||||||
|
|
||||||
xxs->data = malloc(size + 8);
|
xxs->data = (unsigned char *) malloc(size + 8);
|
||||||
if (xxs->data == NULL) {
|
if (xxs->data == NULL) {
|
||||||
retval = -XMP_ERROR_SYSTEM;
|
retval = -XMP_ERROR_SYSTEM;
|
||||||
goto err2;
|
goto err2;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Extended Module Player
|
/* Extended Module Player
|
||||||
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
|
* Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -20,7 +20,6 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <limits.h>
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "virtual.h"
|
#include "virtual.h"
|
||||||
#include "mixer.h"
|
#include "mixer.h"
|
||||||
|
@ -102,8 +101,8 @@ int libxmp_virt_on(struct context_data *ctx, int num)
|
||||||
|
|
||||||
p->virt.maxvoc = libxmp_mixer_numvoices(ctx, num);
|
p->virt.maxvoc = libxmp_mixer_numvoices(ctx, num);
|
||||||
|
|
||||||
p->virt.voice_array = calloc(p->virt.maxvoc,
|
p->virt.voice_array = (struct mixer_voice *) calloc(p->virt.maxvoc,
|
||||||
sizeof(struct mixer_voice));
|
sizeof(struct mixer_voice));
|
||||||
if (p->virt.voice_array == NULL)
|
if (p->virt.voice_array == NULL)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
@ -116,7 +115,7 @@ int libxmp_virt_on(struct context_data *ctx, int num)
|
||||||
/* Initialize Paula simulator */
|
/* Initialize Paula simulator */
|
||||||
if (IS_AMIGA_MOD()) {
|
if (IS_AMIGA_MOD()) {
|
||||||
for (i = 0; i < p->virt.maxvoc; i++) {
|
for (i = 0; i < p->virt.maxvoc; i++) {
|
||||||
p->virt.voice_array[i].paula = calloc(1, sizeof (struct paula_state));
|
p->virt.voice_array[i].paula = (struct paula_state *) calloc(1, sizeof(struct paula_state));
|
||||||
if (p->virt.voice_array[i].paula == NULL) {
|
if (p->virt.voice_array[i].paula == NULL) {
|
||||||
goto err2;
|
goto err2;
|
||||||
}
|
}
|
||||||
|
@ -125,8 +124,8 @@ int libxmp_virt_on(struct context_data *ctx, int num)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
p->virt.virt_channel = malloc(p->virt.virt_channels *
|
p->virt.virt_channel = (struct virt_channel *) malloc(p->virt.virt_channels *
|
||||||
sizeof(struct virt_channel));
|
sizeof(struct virt_channel));
|
||||||
if (p->virt.virt_channel == NULL)
|
if (p->virt.virt_channel == NULL)
|
||||||
goto err2;
|
goto err2;
|
||||||
|
|
||||||
|
@ -353,6 +352,18 @@ void libxmp_virt_release(struct context_data *ctx, int chn, int rel)
|
||||||
libxmp_mixer_release(ctx, voc, rel);
|
libxmp_mixer_release(ctx, voc, rel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void libxmp_virt_reverse(struct context_data *ctx, int chn, int rev)
|
||||||
|
{
|
||||||
|
struct player_data *p = &ctx->p;
|
||||||
|
int voc;
|
||||||
|
|
||||||
|
if ((voc = map_virt_channel(p, chn)) < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
libxmp_mixer_reverse(ctx, voc, rev);
|
||||||
|
}
|
||||||
|
|
||||||
void libxmp_virt_setpan(struct context_data *ctx, int chn, int pan)
|
void libxmp_virt_setpan(struct context_data *ctx, int chn, int pan)
|
||||||
{
|
{
|
||||||
struct player_data *p = &ctx->p;
|
struct player_data *p = &ctx->p;
|
||||||
|
@ -419,8 +430,13 @@ void libxmp_virt_setsmp(struct context_data *ctx, int chn, int smp)
|
||||||
void libxmp_virt_setnna(struct context_data *ctx, int chn, int nna)
|
void libxmp_virt_setnna(struct context_data *ctx, int chn, int nna)
|
||||||
{
|
{
|
||||||
struct player_data *p = &ctx->p;
|
struct player_data *p = &ctx->p;
|
||||||
|
struct module_data *m = &ctx->m;
|
||||||
int voc;
|
int voc;
|
||||||
|
|
||||||
|
if (!HAS_QUIRK(QUIRK_VIRTUAL)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ((voc = map_virt_channel(p, chn)) < 0) {
|
if ((voc = map_virt_channel(p, chn)) < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -429,7 +445,7 @@ void libxmp_virt_setnna(struct context_data *ctx, int chn, int nna)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void check_dct(struct context_data *ctx, int i, int chn, int ins,
|
static void check_dct(struct context_data *ctx, int i, int chn, int ins,
|
||||||
int smp, int note, int nna, int dct, int dca)
|
int smp, int key, int nna, int dct, int dca)
|
||||||
{
|
{
|
||||||
struct player_data *p = &ctx->p;
|
struct player_data *p = &ctx->p;
|
||||||
struct mixer_voice *vi = &p->virt.voice_array[i];
|
struct mixer_voice *vi = &p->virt.voice_array[i];
|
||||||
|
@ -440,15 +456,15 @@ static void check_dct(struct context_data *ctx, int i, int chn, int ins,
|
||||||
if (vi->root == chn && vi->ins == ins) {
|
if (vi->root == chn && vi->ins == ins) {
|
||||||
|
|
||||||
if (nna == XMP_INST_NNA_CUT) {
|
if (nna == XMP_INST_NNA_CUT) {
|
||||||
libxmp_virt_resetvoice(ctx, i, 1);
|
libxmp_virt_resetvoice(ctx, i, 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
vi->act = nna;
|
vi->act = nna;
|
||||||
|
|
||||||
if ((dct == XMP_INST_DCT_INST) ||
|
if ((dct == XMP_INST_DCT_INST) ||
|
||||||
(dct == XMP_INST_DCT_SMP && vi->smp == smp) ||
|
(dct == XMP_INST_DCT_SMP && vi->smp == smp) ||
|
||||||
(dct == XMP_INST_DCT_NOTE && vi->note == note)) {
|
(dct == XMP_INST_DCT_NOTE && vi->key == key)) {
|
||||||
|
|
||||||
if (nna == XMP_INST_NNA_OFF && dca == XMP_INST_DCA_FADE) {
|
if (nna == XMP_INST_NNA_OFF && dca == XMP_INST_DCA_FADE) {
|
||||||
vi->act = VIRT_ACTION_OFF;
|
vi->act = VIRT_ACTION_OFF;
|
||||||
|
@ -479,7 +495,7 @@ void libxmp_virt_setnote(struct context_data *ctx, int chn, int note)
|
||||||
}
|
}
|
||||||
|
|
||||||
int libxmp_virt_setpatch(struct context_data *ctx, int chn, int ins, int smp,
|
int libxmp_virt_setpatch(struct context_data *ctx, int chn, int ins, int smp,
|
||||||
int note, int nna, int dct, int dca)
|
int note, int key, int nna, int dct, int dca)
|
||||||
{
|
{
|
||||||
struct player_data *p = &ctx->p;
|
struct player_data *p = &ctx->p;
|
||||||
int voc, vfree;
|
int voc, vfree;
|
||||||
|
@ -497,7 +513,7 @@ int libxmp_virt_setpatch(struct context_data *ctx, int chn, int ins, int smp,
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < p->virt.maxvoc; i++) {
|
for (i = 0; i < p->virt.maxvoc; i++) {
|
||||||
check_dct(ctx, i, chn, ins, smp, note, nna, dct, dca);
|
check_dct(ctx, i, chn, ins, smp, key, nna, dct, dca);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -512,7 +528,7 @@ int libxmp_virt_setpatch(struct context_data *ctx, int chn, int ins, int smp,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (chn = p->virt.num_tracks;
|
for (chn = p->virt.num_tracks; chn < p->virt.virt_channels &&
|
||||||
p->virt.virt_channel[chn++].map > FREE;) ;
|
p->virt.virt_channel[chn++].map > FREE;) ;
|
||||||
|
|
||||||
p->virt.voice_array[voc].chn = --chn;
|
p->virt.voice_array[voc].chn = --chn;
|
||||||
|
@ -535,6 +551,7 @@ int libxmp_virt_setpatch(struct context_data *ctx, int chn, int ins, int smp,
|
||||||
libxmp_mixer_setnote(ctx, voc, note);
|
libxmp_mixer_setnote(ctx, voc, note);
|
||||||
p->virt.voice_array[voc].ins = ins;
|
p->virt.voice_array[voc].ins = ins;
|
||||||
p->virt.voice_array[voc].act = nna;
|
p->virt.voice_array[voc].act = nna;
|
||||||
|
p->virt.voice_array[voc].key = key;
|
||||||
|
|
||||||
return chn;
|
return chn;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ int libxmp_virt_on (struct context_data *, int);
|
||||||
void libxmp_virt_off (struct context_data *);
|
void libxmp_virt_off (struct context_data *);
|
||||||
int libxmp_virt_mute (struct context_data *, int, int);
|
int libxmp_virt_mute (struct context_data *, int, int);
|
||||||
int libxmp_virt_setpatch (struct context_data *, int, int, int, int,
|
int libxmp_virt_setpatch (struct context_data *, int, int, int, int,
|
||||||
int, int, int);
|
int, int, int, int);
|
||||||
int libxmp_virt_cvt8bit (void);
|
int libxmp_virt_cvt8bit (void);
|
||||||
void libxmp_virt_setnote (struct context_data *, int, int);
|
void libxmp_virt_setnote (struct context_data *, int, int);
|
||||||
void libxmp_virt_setsmp (struct context_data *, int, int);
|
void libxmp_virt_setsmp (struct context_data *, int, int);
|
||||||
|
@ -33,6 +33,7 @@ void libxmp_virt_resetchannel(struct context_data *, int);
|
||||||
void libxmp_virt_resetvoice (struct context_data *, int, int);
|
void libxmp_virt_resetvoice (struct context_data *, int, int);
|
||||||
void libxmp_virt_reset (struct context_data *);
|
void libxmp_virt_reset (struct context_data *);
|
||||||
void libxmp_virt_release (struct context_data *, int, int);
|
void libxmp_virt_release (struct context_data *, int, int);
|
||||||
|
void libxmp_virt_reverse (struct context_data *, int, int);
|
||||||
int libxmp_virt_getroot (struct context_data *, int);
|
int libxmp_virt_getroot (struct context_data *, int);
|
||||||
|
|
||||||
#endif /* LIBXMP_VIRTUAL_H */
|
#endif /* LIBXMP_VIRTUAL_H */
|
||||||
|
|
|
@ -31,3 +31,17 @@ int libxmp_snprintf (char *str, size_t sz, const char *fmt, ...)
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Win32 debug message helper by Mirko Buffoni */
|
||||||
|
#if defined(_MSC_VER) && defined(DEBUG)
|
||||||
|
void libxmp_msvc_dbgprint(const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list argptr;
|
||||||
|
|
||||||
|
/* do the output */
|
||||||
|
va_start(argptr, format);
|
||||||
|
vprintf(format, argptr);
|
||||||
|
printf("\n");
|
||||||
|
va_end(argptr);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -14,12 +14,12 @@
|
||||||
#define XM_LOOP_FORWARD 1
|
#define XM_LOOP_FORWARD 1
|
||||||
#define XM_LOOP_PINGPONG 2
|
#define XM_LOOP_PINGPONG 2
|
||||||
#define XM_SAMPLE_16BIT 0x10
|
#define XM_SAMPLE_16BIT 0x10
|
||||||
|
#define XM_SAMPLE_STEREO 0x20
|
||||||
#define XM_ENVELOPE_ON 0x01
|
#define XM_ENVELOPE_ON 0x01
|
||||||
#define XM_ENVELOPE_SUSTAIN 0x02
|
#define XM_ENVELOPE_SUSTAIN 0x02
|
||||||
#define XM_ENVELOPE_LOOP 0x04
|
#define XM_ENVELOPE_LOOP 0x04
|
||||||
#define XM_LINEAR_PERIOD_MODE 0x01
|
#define XM_LINEAR_PERIOD_MODE 0x01
|
||||||
|
|
||||||
|
|
||||||
struct xm_file_header {
|
struct xm_file_header {
|
||||||
uint8 id[17]; /* ID text: "Extended module: " */
|
uint8 id[17]; /* ID text: "Extended module: " */
|
||||||
uint8 name[20]; /* Module name, padded with zeroes */
|
uint8 name[20]; /* Module name, padded with zeroes */
|
||||||
|
@ -98,4 +98,4 @@ struct xm_event {
|
||||||
uint8 fx_parm; /* Effect parameter */
|
uint8 fx_parm; /* Effect parameter */
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif /* LIBXMP_LOADERS_XM_H */
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Extended Module Player
|
/* Extended Module Player
|
||||||
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
|
* Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -35,6 +35,9 @@
|
||||||
|
|
||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
#include "xm.h"
|
#include "xm.h"
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
#include "vorbis.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
static int xm_test(HIO_HANDLE *, char *, const int);
|
static int xm_test(HIO_HANDLE *, char *, const int);
|
||||||
static int xm_load(struct module_data *, HIO_HANDLE *, const int);
|
static int xm_load(struct module_data *, HIO_HANDLE *, const int);
|
||||||
|
@ -60,15 +63,16 @@ static int xm_test(HIO_HANDLE *f, char *t, const int start)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int load_xm_pattern(struct module_data *m, int num, int version, HIO_HANDLE *f)
|
static int load_xm_pattern(struct module_data *m, int num, int version,
|
||||||
|
uint8 *patbuf, HIO_HANDLE *f)
|
||||||
{
|
{
|
||||||
const int headsize = version > 0x0102 ? 9 : 8;
|
const int headsize = version > 0x0102 ? 9 : 8;
|
||||||
struct xmp_module *mod = &m->mod;
|
struct xmp_module *mod = &m->mod;
|
||||||
struct xm_pattern_header xph;
|
struct xm_pattern_header xph;
|
||||||
struct xmp_event *event;
|
struct xmp_event *event;
|
||||||
uint8 *patbuf, *pat, b;
|
uint8 *pat, b;
|
||||||
int j, r;
|
int j, k, r;
|
||||||
int size;
|
int size, size_read;
|
||||||
|
|
||||||
xph.length = hio_read32l(f);
|
xph.length = hio_read32l(f);
|
||||||
xph.packing = hio_read8(f);
|
xph.packing = hio_read8(f);
|
||||||
|
@ -99,199 +103,213 @@ static int load_xm_pattern(struct module_data *m, int num, int version, HIO_HAND
|
||||||
}
|
}
|
||||||
|
|
||||||
size = xph.datasize;
|
size = xph.datasize;
|
||||||
|
pat = patbuf;
|
||||||
|
|
||||||
pat = patbuf = calloc(1, size);
|
size_read = hio_read(patbuf, 1, size, f);
|
||||||
if (patbuf == NULL) {
|
if (size_read < size) {
|
||||||
goto err;
|
memset(patbuf + size_read, 0, size - size_read);
|
||||||
}
|
}
|
||||||
|
|
||||||
hio_read(patbuf, 1, size, f);
|
for (j = 0; j < r; j++) {
|
||||||
for (j = 0; j < (mod->chn * r); j++) {
|
for (k = 0; k < mod->chn; k++) {
|
||||||
|
/*
|
||||||
|
if ((pat - patbuf) >= xph.datasize)
|
||||||
|
break;
|
||||||
|
*/
|
||||||
|
|
||||||
/*if ((pat - patbuf) >= xph.datasize)
|
event = &EVENT(num, k, j);
|
||||||
break; */
|
|
||||||
|
|
||||||
event = &EVENT(num, j % mod->chn, j / mod->chn);
|
if (--size < 0) {
|
||||||
|
goto err;
|
||||||
if (--size < 0) {
|
|
||||||
goto err2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((b = *pat++) & XM_EVENT_PACKING) {
|
|
||||||
if (b & XM_EVENT_NOTE_FOLLOWS) {
|
|
||||||
if (--size < 0)
|
|
||||||
goto err2;
|
|
||||||
event->note = *pat++;
|
|
||||||
}
|
}
|
||||||
if (b & XM_EVENT_INSTRUMENT_FOLLOWS) {
|
|
||||||
if (--size < 0)
|
if ((b = *pat++) & XM_EVENT_PACKING) {
|
||||||
goto err2;
|
if (b & XM_EVENT_NOTE_FOLLOWS) {
|
||||||
|
if (--size < 0)
|
||||||
|
goto err;
|
||||||
|
event->note = *pat++;
|
||||||
|
}
|
||||||
|
if (b & XM_EVENT_INSTRUMENT_FOLLOWS) {
|
||||||
|
if (--size < 0)
|
||||||
|
goto err;
|
||||||
|
event->ins = *pat++;
|
||||||
|
}
|
||||||
|
if (b & XM_EVENT_VOLUME_FOLLOWS) {
|
||||||
|
if (--size < 0)
|
||||||
|
goto err;
|
||||||
|
event->vol = *pat++;
|
||||||
|
}
|
||||||
|
if (b & XM_EVENT_FXTYPE_FOLLOWS) {
|
||||||
|
if (--size < 0)
|
||||||
|
goto err;
|
||||||
|
event->fxt = *pat++;
|
||||||
|
}
|
||||||
|
if (b & XM_EVENT_FXPARM_FOLLOWS) {
|
||||||
|
if (--size < 0)
|
||||||
|
goto err;
|
||||||
|
event->fxp = *pat++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
size -= 4;
|
||||||
|
if (size < 0)
|
||||||
|
goto err;
|
||||||
|
event->note = b;
|
||||||
event->ins = *pat++;
|
event->ins = *pat++;
|
||||||
}
|
|
||||||
if (b & XM_EVENT_VOLUME_FOLLOWS) {
|
|
||||||
if (--size < 0)
|
|
||||||
goto err2;
|
|
||||||
event->vol = *pat++;
|
event->vol = *pat++;
|
||||||
}
|
|
||||||
if (b & XM_EVENT_FXTYPE_FOLLOWS) {
|
|
||||||
if (--size < 0)
|
|
||||||
goto err2;
|
|
||||||
event->fxt = *pat++;
|
event->fxt = *pat++;
|
||||||
}
|
|
||||||
if (b & XM_EVENT_FXPARM_FOLLOWS) {
|
|
||||||
if (--size < 0)
|
|
||||||
goto err2;
|
|
||||||
event->fxp = *pat++;
|
event->fxp = *pat++;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
size -= 4;
|
|
||||||
if (size < 0)
|
|
||||||
goto err2;
|
|
||||||
event->note = b;
|
|
||||||
event->ins = *pat++;
|
|
||||||
event->vol = *pat++;
|
|
||||||
event->fxt = *pat++;
|
|
||||||
event->fxp = *pat++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sanity check */
|
/* Sanity check */
|
||||||
switch (event->fxt) {
|
switch (event->fxt) {
|
||||||
case 18:
|
case 18:
|
||||||
case 19:
|
case 19:
|
||||||
case 22:
|
case 22:
|
||||||
case 23:
|
case 23:
|
||||||
case 24:
|
case 24:
|
||||||
case 26:
|
case 26:
|
||||||
case 28:
|
case 28:
|
||||||
case 30:
|
case 30:
|
||||||
case 31:
|
case 31:
|
||||||
case 32:
|
case 32:
|
||||||
event->fxt = 0;
|
event->fxt = 0;
|
||||||
}
|
|
||||||
if (event->fxt > 34) {
|
|
||||||
event->fxt = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event->note == 0x61) {
|
|
||||||
/* See OpenMPT keyoff+instr.xm test case */
|
|
||||||
if (event->fxt == 0x0e && MSN(event->fxp) == 0x0d) {
|
|
||||||
event->note = XMP_KEY_OFF;
|
|
||||||
} else {
|
|
||||||
event->note =
|
|
||||||
event->ins ? XMP_KEY_FADE : XMP_KEY_OFF;
|
|
||||||
}
|
}
|
||||||
} else if (event->note > 0) {
|
if (event->fxt > 34) {
|
||||||
event->note += 12;
|
event->fxt = 0;
|
||||||
}
|
|
||||||
|
|
||||||
if (event->fxt == 0x0e) {
|
|
||||||
if (MSN(event->fxp) == EX_FINETUNE) {
|
|
||||||
unsigned char val = (LSN(event->fxp) - 8) & 0xf;
|
|
||||||
event->fxp = (EX_FINETUNE << 4) | val;
|
|
||||||
}
|
}
|
||||||
switch (event->fxp) {
|
|
||||||
case 0x43:
|
if (event->note == 0x61) {
|
||||||
case 0x73:
|
/* See OpenMPT keyoff+instr.xm test case */
|
||||||
event->fxp--;
|
if (event->fxt == 0x0e && MSN(event->fxp) == 0x0d) {
|
||||||
|
event->note = XMP_KEY_OFF;
|
||||||
|
} else {
|
||||||
|
event->note =
|
||||||
|
event->ins ? XMP_KEY_FADE : XMP_KEY_OFF;
|
||||||
|
}
|
||||||
|
} else if (event->note > 0) {
|
||||||
|
event->note += 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event->fxt == 0x0e) {
|
||||||
|
if (MSN(event->fxp) == EX_FINETUNE) {
|
||||||
|
unsigned char val = (LSN(event->fxp) - 8) & 0xf;
|
||||||
|
event->fxp = (EX_FINETUNE << 4) | val;
|
||||||
|
}
|
||||||
|
switch (event->fxp) {
|
||||||
|
case 0x43:
|
||||||
|
case 0x73:
|
||||||
|
event->fxp--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (event->fxt == FX_XF_PORTA && MSN(event->fxp) == 0x09) {
|
||||||
|
/* Translate MPT hacks */
|
||||||
|
switch (LSN(event->fxp)) {
|
||||||
|
case 0x0: /* Surround off */
|
||||||
|
case 0x1: /* Surround on */
|
||||||
|
event->fxt = FX_SURROUND;
|
||||||
|
event->fxp = LSN(event->fxp);
|
||||||
|
break;
|
||||||
|
case 0xe: /* Play forward */
|
||||||
|
case 0xf: /* Play reverse */
|
||||||
|
event->fxt = FX_REVERSE;
|
||||||
|
event->fxp = LSN(event->fxp) - 0xe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!event->vol) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Volume set */
|
||||||
|
if ((event->vol >= 0x10) && (event->vol <= 0x50)) {
|
||||||
|
event->vol -= 0x0f;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Volume column effects */
|
||||||
|
switch (event->vol >> 4) {
|
||||||
|
case 0x06: /* Volume slide down */
|
||||||
|
event->f2t = FX_VOLSLIDE_2;
|
||||||
|
event->f2p = event->vol - 0x60;
|
||||||
|
break;
|
||||||
|
case 0x07: /* Volume slide up */
|
||||||
|
event->f2t = FX_VOLSLIDE_2;
|
||||||
|
event->f2p = (event->vol - 0x70) << 4;
|
||||||
|
break;
|
||||||
|
case 0x08: /* Fine volume slide down */
|
||||||
|
event->f2t = FX_EXTENDED;
|
||||||
|
event->f2p =
|
||||||
|
(EX_F_VSLIDE_DN << 4) | (event->vol - 0x80);
|
||||||
|
break;
|
||||||
|
case 0x09: /* Fine volume slide up */
|
||||||
|
event->f2t = FX_EXTENDED;
|
||||||
|
event->f2p =
|
||||||
|
(EX_F_VSLIDE_UP << 4) | (event->vol - 0x90);
|
||||||
|
break;
|
||||||
|
case 0x0a: /* Set vibrato speed */
|
||||||
|
event->f2t = FX_VIBRATO;
|
||||||
|
event->f2p = (event->vol - 0xa0) << 4;
|
||||||
|
break;
|
||||||
|
case 0x0b: /* Vibrato */
|
||||||
|
event->f2t = FX_VIBRATO;
|
||||||
|
event->f2p = event->vol - 0xb0;
|
||||||
|
break;
|
||||||
|
case 0x0c: /* Set panning */
|
||||||
|
event->f2t = FX_SETPAN;
|
||||||
|
event->f2p = (event->vol - 0xc0) << 4;
|
||||||
|
break;
|
||||||
|
case 0x0d: /* Pan slide left */
|
||||||
|
event->f2t = FX_PANSL_NOMEM;
|
||||||
|
event->f2p = (event->vol - 0xd0) << 4;
|
||||||
|
break;
|
||||||
|
case 0x0e: /* Pan slide right */
|
||||||
|
event->f2t = FX_PANSL_NOMEM;
|
||||||
|
event->f2p = event->vol - 0xe0;
|
||||||
|
break;
|
||||||
|
case 0x0f: /* Tone portamento */
|
||||||
|
event->f2t = FX_TONEPORTA;
|
||||||
|
event->f2p = (event->vol - 0xf0) << 4;
|
||||||
|
|
||||||
|
/* From OpenMPT TonePortamentoMemory.xm:
|
||||||
|
* "Another nice bug (...) is the combination of both
|
||||||
|
* portamento commands (Mx and 3xx) in the same cell:
|
||||||
|
* The 3xx parameter is ignored completely, and the Mx
|
||||||
|
* parameter is doubled. (M2 3FF is the same as M4 000)
|
||||||
|
*/
|
||||||
|
if (event->fxt == FX_TONEPORTA
|
||||||
|
|| event->fxt == FX_TONE_VSLIDE) {
|
||||||
|
if (event->fxt == FX_TONEPORTA) {
|
||||||
|
event->fxt = 0;
|
||||||
|
} else {
|
||||||
|
event->fxt = FX_VOLSLIDE;
|
||||||
|
}
|
||||||
|
event->fxp = 0;
|
||||||
|
|
||||||
|
if (event->f2p < 0x80) {
|
||||||
|
event->f2p <<= 1;
|
||||||
|
} else {
|
||||||
|
event->f2p = 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* From OpenMPT porta-offset.xm:
|
||||||
|
* "If there is a portamento command next to an offset
|
||||||
|
* command, the offset command is ignored completely. In
|
||||||
|
* particular, the offset parameter is not memorized."
|
||||||
|
*/
|
||||||
|
if (event->fxt == FX_OFFSET
|
||||||
|
&& event->f2t == FX_TONEPORTA) {
|
||||||
|
event->fxt = event->fxp = 0;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
event->vol = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!event->vol) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Volume set */
|
|
||||||
if ((event->vol >= 0x10) && (event->vol <= 0x50)) {
|
|
||||||
event->vol -= 0x0f;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Volume column effects */
|
|
||||||
switch (event->vol >> 4) {
|
|
||||||
case 0x06: /* Volume slide down */
|
|
||||||
event->f2t = FX_VOLSLIDE_2;
|
|
||||||
event->f2p = event->vol - 0x60;
|
|
||||||
break;
|
|
||||||
case 0x07: /* Volume slide up */
|
|
||||||
event->f2t = FX_VOLSLIDE_2;
|
|
||||||
event->f2p = (event->vol - 0x70) << 4;
|
|
||||||
break;
|
|
||||||
case 0x08: /* Fine volume slide down */
|
|
||||||
event->f2t = FX_EXTENDED;
|
|
||||||
event->f2p =
|
|
||||||
(EX_F_VSLIDE_DN << 4) | (event->vol - 0x80);
|
|
||||||
break;
|
|
||||||
case 0x09: /* Fine volume slide up */
|
|
||||||
event->f2t = FX_EXTENDED;
|
|
||||||
event->f2p =
|
|
||||||
(EX_F_VSLIDE_UP << 4) | (event->vol - 0x90);
|
|
||||||
break;
|
|
||||||
case 0x0a: /* Set vibrato speed */
|
|
||||||
event->f2t = FX_VIBRATO;
|
|
||||||
event->f2p = (event->vol - 0xa0) << 4;
|
|
||||||
break;
|
|
||||||
case 0x0b: /* Vibrato */
|
|
||||||
event->f2t = FX_VIBRATO;
|
|
||||||
event->f2p = event->vol - 0xb0;
|
|
||||||
break;
|
|
||||||
case 0x0c: /* Set panning */
|
|
||||||
event->f2t = FX_SETPAN;
|
|
||||||
event->f2p = (event->vol - 0xc0) << 4;
|
|
||||||
break;
|
|
||||||
case 0x0d: /* Pan slide left */
|
|
||||||
event->f2t = FX_PANSL_NOMEM;
|
|
||||||
event->f2p = (event->vol - 0xd0) << 4;
|
|
||||||
break;
|
|
||||||
case 0x0e: /* Pan slide right */
|
|
||||||
event->f2t = FX_PANSL_NOMEM;
|
|
||||||
event->f2p = event->vol - 0xe0;
|
|
||||||
break;
|
|
||||||
case 0x0f: /* Tone portamento */
|
|
||||||
event->f2t = FX_TONEPORTA;
|
|
||||||
event->f2p = (event->vol - 0xf0) << 4;
|
|
||||||
|
|
||||||
/* From OpenMPT TonePortamentoMemory.xm:
|
|
||||||
* "Another nice bug (...) is the combination of both
|
|
||||||
* portamento commands (Mx and 3xx) in the same cell:
|
|
||||||
* The 3xx parameter is ignored completely, and the Mx
|
|
||||||
* parameter is doubled. (M2 3FF is the same as M4 000)
|
|
||||||
*/
|
|
||||||
if (event->fxt == FX_TONEPORTA
|
|
||||||
|| event->fxt == FX_TONE_VSLIDE) {
|
|
||||||
if (event->fxt == FX_TONEPORTA) {
|
|
||||||
event->fxt = 0;
|
|
||||||
} else {
|
|
||||||
event->fxt = FX_VOLSLIDE;
|
|
||||||
}
|
|
||||||
event->fxp = 0;
|
|
||||||
|
|
||||||
if (event->f2p < 0x80) {
|
|
||||||
event->f2p <<= 1;
|
|
||||||
} else {
|
|
||||||
event->f2p = 0xff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* From OpenMPT porta-offset.xm:
|
|
||||||
* "If there is a portamento command next to an offset
|
|
||||||
* command, the offset command is ignored completely. In
|
|
||||||
* particular, the offset parameter is not memorized."
|
|
||||||
*/
|
|
||||||
if (event->fxt == FX_OFFSET
|
|
||||||
&& event->f2t == FX_TONEPORTA) {
|
|
||||||
event->fxt = event->fxp = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
event->vol = 0;
|
|
||||||
}
|
}
|
||||||
free(patbuf);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err2:
|
|
||||||
free(patbuf);
|
|
||||||
err:
|
err:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -299,6 +317,7 @@ err:
|
||||||
static int load_patterns(struct module_data *m, int version, HIO_HANDLE *f)
|
static int load_patterns(struct module_data *m, int version, HIO_HANDLE *f)
|
||||||
{
|
{
|
||||||
struct xmp_module *mod = &m->mod;
|
struct xmp_module *mod = &m->mod;
|
||||||
|
uint8 *patbuf;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
mod->pat++;
|
mod->pat++;
|
||||||
|
@ -308,8 +327,12 @@ static int load_patterns(struct module_data *m, int version, HIO_HANDLE *f)
|
||||||
|
|
||||||
D_(D_INFO "Stored patterns: %d", mod->pat - 1);
|
D_(D_INFO "Stored patterns: %d", mod->pat - 1);
|
||||||
|
|
||||||
|
if ((patbuf = (uint8 *) calloc(1, 65536)) == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < mod->pat - 1; i++) {
|
for (i = 0; i < mod->pat - 1; i++) {
|
||||||
if (load_xm_pattern(m, i, version, f) < 0) {
|
if (load_xm_pattern(m, i, version, patbuf, f) < 0) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -333,21 +356,100 @@ static int load_patterns(struct module_data *m, int version, HIO_HANDLE *f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(patbuf);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
free(patbuf);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Packed structures size */
|
/* Packed structures size */
|
||||||
#define XM_INST_HEADER_SIZE 33
|
#define XM_INST_HEADER_SIZE 29
|
||||||
#define XM_INST_SIZE 208
|
#define XM_INST_SIZE 212
|
||||||
|
|
||||||
/* grass.near.the.house.xm defines 23 samples in instrument 1. FT2 docs
|
/* grass.near.the.house.xm defines 23 samples in instrument 1. FT2 docs
|
||||||
* specify at most 16. See https://github.com/libxmp/libxmp/issues/168
|
* specify at most 16. See https://github.com/libxmp/libxmp/issues/168
|
||||||
* for more details. */
|
* for more details. */
|
||||||
#define XM_MAX_SAMPLES_PER_INST 32
|
#define XM_MAX_SAMPLES_PER_INST 32
|
||||||
|
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
#define MAGIC_OGGS 0x4f676753
|
||||||
|
|
||||||
|
static int is_ogg_sample(HIO_HANDLE *f, struct xmp_sample *xxs)
|
||||||
|
{
|
||||||
|
/* uint32 size; */
|
||||||
|
uint32 id;
|
||||||
|
|
||||||
|
/* Sample must be at least 4 bytes long to be an OGG sample.
|
||||||
|
* Bonnie's Bookstore music.oxm contains zero length samples
|
||||||
|
* followed immediately by OGG samples. */
|
||||||
|
if (xxs->len < 4)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* size = */ hio_read32l(f);
|
||||||
|
id = hio_read32b(f);
|
||||||
|
if (hio_error(f) != 0 || hio_seek(f, -8, SEEK_CUR) < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (id != MAGIC_OGGS) { /* copy input data if not Ogg file */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int oggdec(struct module_data *m, HIO_HANDLE *f, struct xmp_sample *xxs, int len)
|
||||||
|
{
|
||||||
|
int i, n, ch, rate, ret, flags = 0;
|
||||||
|
uint8 *data;
|
||||||
|
int16 *pcm16 = NULL;
|
||||||
|
|
||||||
|
if ((data = (uint8 *)calloc(1, len)) == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
hio_read32b(f);
|
||||||
|
if (hio_error(f) != 0 || hio_read(data, 1, len - 4, f) != len - 4) {
|
||||||
|
free(data);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = stb_vorbis_decode_memory(data, len, &ch, &rate, &pcm16);
|
||||||
|
free(data);
|
||||||
|
|
||||||
|
if (n <= 0) {
|
||||||
|
free(pcm16);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
xxs->len = n;
|
||||||
|
|
||||||
|
if ((xxs->flg & XMP_SAMPLE_16BIT) == 0) {
|
||||||
|
uint8 *pcm = (uint8 *)pcm16;
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
pcm[i] = pcm16[i] >> 8;
|
||||||
|
}
|
||||||
|
pcm = (uint8 *)realloc(pcm16, n);
|
||||||
|
if (pcm == NULL) {
|
||||||
|
free(pcm16);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
pcm16 = (int16 *)pcm;
|
||||||
|
}
|
||||||
|
|
||||||
|
flags |= SAMPLE_FLAG_NOLOAD;
|
||||||
|
#ifdef WORDS_BIGENDIAN
|
||||||
|
flags |= SAMPLE_FLAG_BIGEND;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ret = libxmp_load_sample(m, NULL, flags, xxs, pcm16);
|
||||||
|
free(pcm16);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
|
static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
|
||||||
{
|
{
|
||||||
struct xmp_module *mod = &m->mod;
|
struct xmp_module *mod = &m->mod;
|
||||||
|
@ -376,8 +478,12 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
|
||||||
* instruments, but file may end abruptly before that. Also covers
|
* instruments, but file may end abruptly before that. Also covers
|
||||||
* XMLiTE stripped modules and truncated files. This test will not
|
* XMLiTE stripped modules and truncated files. This test will not
|
||||||
* work if file has trailing garbage.
|
* work if file has trailing garbage.
|
||||||
|
*
|
||||||
|
* Note: loading 4 bytes past the instrument header to get the
|
||||||
|
* sample header size (if it exists). This is NOT considered to
|
||||||
|
* be part of the instrument header.
|
||||||
*/
|
*/
|
||||||
if (hio_read(buf, 33, 1, f) != 1) {
|
if (hio_read(buf, XM_INST_HEADER_SIZE + 4, 1, f) != 1) {
|
||||||
D_(D_WARN "short read in instrument header data");
|
D_(D_WARN "short read in instrument header data");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -389,6 +495,11 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
|
||||||
xih.sh_size = readmem32l(buf + 29); /* Sample header size */
|
xih.sh_size = readmem32l(buf + 29); /* Sample header size */
|
||||||
|
|
||||||
/* Sanity check */
|
/* Sanity check */
|
||||||
|
if ((int)xih.size < XM_INST_HEADER_SIZE) {
|
||||||
|
D_(D_CRIT "instrument %d: instrument header size:%d", i + 1, xih.size);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (xih.samples > XM_MAX_SAMPLES_PER_INST || (xih.samples > 0 && xih.sh_size > 0x100)) {
|
if (xih.samples > XM_MAX_SAMPLES_PER_INST || (xih.samples > 0 && xih.sh_size > 0x100)) {
|
||||||
D_(D_CRIT "instrument %d: samples:%d sample header size:%d", i + 1, xih.samples, xih.sh_size);
|
D_(D_CRIT "instrument %d: samples:%d sample header size:%d", i + 1, xih.samples, xih.sh_size);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -416,7 +527,7 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
|
||||||
* generalization should take care of both cases.
|
* generalization should take care of both cases.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (hio_seek(f, (int)xih.size - XM_INST_HEADER_SIZE, SEEK_CUR) < 0) {
|
if (hio_seek(f, (int)xih.size - (XM_INST_HEADER_SIZE + 4), SEEK_CUR) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,17 +538,13 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xih.size < XM_INST_HEADER_SIZE) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* for BoobieSqueezer (see http://boobie.rotfl.at/)
|
/* for BoobieSqueezer (see http://boobie.rotfl.at/)
|
||||||
* It works pretty much the same way as Impulse Tracker's sample
|
* It works pretty much the same way as Impulse Tracker's sample
|
||||||
* only mode, where it will strip off the instrument data.
|
* only mode, where it will strip off the instrument data.
|
||||||
*/
|
*/
|
||||||
if (xih.size < XM_INST_HEADER_SIZE + XM_INST_SIZE) {
|
if (xih.size < XM_INST_HEADER_SIZE + XM_INST_SIZE) {
|
||||||
memset(&xi, 0, sizeof(struct xm_instrument));
|
memset(&xi, 0, sizeof(struct xm_instrument));
|
||||||
hio_seek(f, xih.size - XM_INST_HEADER_SIZE, SEEK_CUR);
|
hio_seek(f, xih.size - (XM_INST_HEADER_SIZE + 4), SEEK_CUR);
|
||||||
} else {
|
} else {
|
||||||
uint8 *b = buf;
|
uint8 *b = buf;
|
||||||
|
|
||||||
|
@ -508,7 +615,7 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
|
||||||
for (j = 12; j < 108; j++) {
|
for (j = 12; j < 108; j++) {
|
||||||
xxi->map[j].ins = xi.sample[j - 12];
|
xxi->map[j].ins = xi.sample[j - 12];
|
||||||
if (xxi->map[j].ins >= xxi->nsm)
|
if (xxi->map[j].ins >= xxi->nsm)
|
||||||
xxi->map[j].ins = -1;
|
xxi->map[j].ins = 0xff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -576,6 +683,12 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
|
||||||
xxs->lps >>= 1;
|
xxs->lps >>= 1;
|
||||||
xxs->lpe >>= 1;
|
xxs->lpe >>= 1;
|
||||||
}
|
}
|
||||||
|
if (xsh[j].type & XM_SAMPLE_STEREO) {
|
||||||
|
/* xxs->flg |= XMP_SAMPLE_STEREO; */
|
||||||
|
xxs->len >>= 1;
|
||||||
|
xxs->lps >>= 1;
|
||||||
|
xxs->lpe >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
xxs->flg |= xsh[j].type & XM_LOOP_FORWARD ? XMP_SAMPLE_LOOP : 0;
|
xxs->flg |= xsh[j].type & XM_LOOP_FORWARD ? XMP_SAMPLE_LOOP : 0;
|
||||||
xxs->flg |= xsh[j].type & XM_LOOP_PINGPONG ? XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_BIDIR : 0;
|
xxs->flg |= xsh[j].type & XM_LOOP_PINGPONG ? XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_BIDIR : 0;
|
||||||
|
@ -595,6 +708,7 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
|
||||||
total_sample_size = 0;
|
total_sample_size = 0;
|
||||||
for (j = 0; j < xxi->nsm; j++) {
|
for (j = 0; j < xxi->nsm; j++) {
|
||||||
struct xmp_subinstrument *sub = &xxi->sub[j];
|
struct xmp_subinstrument *sub = &xxi->sub[j];
|
||||||
|
struct xmp_sample *xxs = &mod->xxs[sub->sid];
|
||||||
int flags;
|
int flags;
|
||||||
|
|
||||||
flags = SAMPLE_FLAG_DIFF;
|
flags = SAMPLE_FLAG_DIFF;
|
||||||
|
@ -606,7 +720,20 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
|
||||||
|
|
||||||
if (version > 0x0103) {
|
if (version > 0x0103) {
|
||||||
D_(D_INFO " read sample: index:%d sample id:%d", j, sub->sid);
|
D_(D_INFO " read sample: index:%d sample id:%d", j, sub->sid);
|
||||||
if (libxmp_load_sample(m, f, flags, &mod->xxs[sub->sid], NULL) < 0) {
|
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
if (is_ogg_sample(f, xxs)) {
|
||||||
|
if (oggdec(m, f, xxs, xsh[j].length) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
D_(D_INFO " sample is vorbis");
|
||||||
|
total_sample_size += xsh[j].length;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (libxmp_load_sample(m, f, flags, xxs, NULL) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (flags & SAMPLE_FLAG_ADPCM) {
|
if (flags & SAMPLE_FLAG_ADPCM) {
|
||||||
|
@ -615,6 +742,12 @@ static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
|
||||||
} else {
|
} else {
|
||||||
total_sample_size += xsh[j].length;
|
total_sample_size += xsh[j].length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: implement stereo samples.
|
||||||
|
* For now, just skip the right channel. */
|
||||||
|
if (xsh[j].type & XM_SAMPLE_STEREO) {
|
||||||
|
hio_seek(f, xsh[j].length >> 1, SEEK_CUR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -640,6 +773,10 @@ static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start)
|
||||||
int i, j;
|
int i, j;
|
||||||
struct xm_file_header xfh;
|
struct xm_file_header xfh;
|
||||||
char tracker_name[21];
|
char tracker_name[21];
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
int claims_ft2 = 0;
|
||||||
|
int is_mpt_116 = 0;
|
||||||
|
#endif
|
||||||
int len;
|
int len;
|
||||||
uint8 buf[80];
|
uint8 buf[80];
|
||||||
|
|
||||||
|
@ -666,19 +803,32 @@ static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start)
|
||||||
xfh.bpm = readmem16l(buf + 78); /* Default BPM */
|
xfh.bpm = readmem16l(buf + 78); /* Default BPM */
|
||||||
|
|
||||||
/* Sanity checks */
|
/* Sanity checks */
|
||||||
if (xfh.songlen > 256 || xfh.patterns > 256 || xfh.instruments > 255) {
|
if (xfh.songlen > 256) {
|
||||||
D_(D_CRIT "Sanity check: %d %d %d", xfh.songlen, xfh.patterns, xfh.instruments);
|
D_(D_CRIT "bad song length: %d", xfh.songlen);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (xfh.patterns > 256) {
|
||||||
|
D_(D_CRIT "bad pattern count: %d", xfh.patterns);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (xfh.instruments > 255) {
|
||||||
|
D_(D_CRIT "bad instrument count: %d", xfh.instruments);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xfh.restart > 255 || xfh.channels > XMP_MAX_CHANNELS) {
|
if (xfh.restart > 255) {
|
||||||
D_(D_CRIT "Sanity check: %d %d", xfh.restart, xfh.channels);
|
D_(D_CRIT "bad restart position: %d", xfh.restart);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (xfh.channels > XMP_MAX_CHANNELS) {
|
||||||
|
D_(D_CRIT "bad channel count: %d", xfh.channels);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xfh.tempo >= 32 || xfh.bpm < 32 || xfh.bpm > 255) {
|
/* FT2 and MPT allow up to 255 BPM. OpenMPT allows up to 1000 BPM. */
|
||||||
|
if (xfh.tempo >= 32 || xfh.bpm < 32 || xfh.bpm > 1000) {
|
||||||
if (memcmp("MED2XM", xfh.tracker, 6)) {
|
if (memcmp("MED2XM", xfh.tracker, 6)) {
|
||||||
D_(D_CRIT "Sanity check: %d %d", xfh.tempo, xfh.bpm);
|
D_(D_CRIT "bad tempo or BPM: %d %d", xfh.tempo, xfh.bpm);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -686,10 +836,11 @@ static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start)
|
||||||
/* Honor header size -- needed by BoobieSqueezer XMs */
|
/* Honor header size -- needed by BoobieSqueezer XMs */
|
||||||
len = xfh.headersz - 0x14;
|
len = xfh.headersz - 0x14;
|
||||||
if (len < 0 || len > 256) {
|
if (len < 0 || len > 256) {
|
||||||
D_(D_CRIT "Sanity check: %d", len);
|
D_(D_CRIT "bad XM header length: %d", len);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memset(xfh.order, 0, sizeof(xfh.order));
|
||||||
if (hio_read(xfh.order, len, 1, f) != 1) { /* Pattern order table */
|
if (hio_read(xfh.order, len, 1, f) != 1) { /* Pattern order table */
|
||||||
D_(D_CRIT "error reading orders");
|
D_(D_CRIT "error reading orders");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -720,8 +871,12 @@ static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* OpenMPT accurately emulates weird FT2 bugs */
|
/* OpenMPT accurately emulates weird FT2 bugs */
|
||||||
if (!strncmp(tracker_name, "FastTracker v2.00", 17) ||
|
if (!strncmp(tracker_name, "FastTracker v2.00", 17)) {
|
||||||
!strncmp(tracker_name, "OpenMPT ", 8)) {
|
m->quirk |= QUIRK_FT2BUGS;
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
claims_ft2 = 1;
|
||||||
|
#endif
|
||||||
|
} else if (!strncmp(tracker_name, "OpenMPT ", 8)) {
|
||||||
m->quirk |= QUIRK_FT2BUGS;
|
m->quirk |= QUIRK_FT2BUGS;
|
||||||
}
|
}
|
||||||
#ifndef LIBXMP_CORE_PLAYER
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
@ -744,6 +899,7 @@ static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start)
|
||||||
if (!strncmp(tracker_name, "FastTracker v 2.00", 18)) {
|
if (!strncmp(tracker_name, "FastTracker v 2.00", 18)) {
|
||||||
strcpy(tracker_name, "old ModPlug Tracker");
|
strcpy(tracker_name, "old ModPlug Tracker");
|
||||||
m->quirk &= ~QUIRK_FT2BUGS;
|
m->quirk &= ~QUIRK_FT2BUGS;
|
||||||
|
is_mpt_116 = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
libxmp_set_type(m, "%s XM %d.%02d", tracker_name, xfh.version >> 8, xfh.version & 0xff);
|
libxmp_set_type(m, "%s XM %d.%02d", tracker_name, xfh.version >> 8, xfh.version & 0xff);
|
||||||
|
@ -789,6 +945,73 @@ static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef LIBXMP_CORE_PLAYER
|
||||||
|
/* Load MPT properties from the end of the file. */
|
||||||
|
while (1) {
|
||||||
|
uint32 ext = hio_read32b(f);
|
||||||
|
uint32 sz = hio_read32l(f);
|
||||||
|
int known = 0;
|
||||||
|
|
||||||
|
if (hio_error(f) || sz > 0x7fffffff /* INT32_MAX */)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (ext) {
|
||||||
|
case MAGIC4('t','e','x','t'): /* Song comment */
|
||||||
|
known = 1;
|
||||||
|
if (m->comment != NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if ((m->comment = (char *)malloc(sz + 1)) == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
sz = hio_read(m->comment, 1, sz, f);
|
||||||
|
m->comment[sz] = '\0';
|
||||||
|
|
||||||
|
for (i = 0; i < (int)sz; i++) {
|
||||||
|
int b = m->comment[i];
|
||||||
|
if (b == '\r') {
|
||||||
|
m->comment[i] = '\n';
|
||||||
|
} else if ((b < 32 || b > 127) && b != '\n'
|
||||||
|
&& b != '\t') {
|
||||||
|
m->comment[i] = '.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MAGIC4('M','I','D','I'): /* MIDI config */
|
||||||
|
case MAGIC4('P','N','A','M'): /* Pattern names */
|
||||||
|
case MAGIC4('C','N','A','M'): /* Channel names */
|
||||||
|
case MAGIC4('C','H','F','X'): /* Channel plugins */
|
||||||
|
case MAGIC4('X','T','P','M'): /* Inst. extensions */
|
||||||
|
known = 1;
|
||||||
|
/* fall-through */
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* Plugin definition */
|
||||||
|
if ((ext & MAGIC4('F','X', 0, 0)) == MAGIC4('F','X', 0, 0))
|
||||||
|
known = 1;
|
||||||
|
|
||||||
|
if (sz) hio_seek(f, sz, SEEK_CUR);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(known && claims_ft2)
|
||||||
|
is_mpt_116 = 1;
|
||||||
|
|
||||||
|
if (ext == MAGIC4('X','T','P','M'))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_mpt_116) {
|
||||||
|
libxmp_set_type(m, "ModPlug Tracker 1.16 XM %d.%02d",
|
||||||
|
xfh.version >> 8, xfh.version & 0xff);
|
||||||
|
|
||||||
|
m->mvolbase = 48;
|
||||||
|
m->mvol = 48;
|
||||||
|
libxmp_apply_mpt_preamp(m);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
for (i = 0; i < mod->chn; i++) {
|
for (i = 0; i < mod->chn; i++) {
|
||||||
mod->xxc[i].pan = 0x80;
|
mod->xxc[i].pan = 0x80;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +1,56 @@
|
||||||
#ifndef XMP_H
|
#ifndef XMP_H
|
||||||
#define XMP_H
|
#define XMP_H
|
||||||
|
|
||||||
|
#if defined(EMSCRIPTEN)
|
||||||
|
# include <emscripten.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define XMP_VERSION "4.5.0"
|
#define XMP_VERSION "4.6.0"
|
||||||
#define XMP_VERCODE 0x040500
|
#define XMP_VERCODE 0x040600
|
||||||
#define XMP_VER_MAJOR 4
|
#define XMP_VER_MAJOR 4
|
||||||
#define XMP_VER_MINOR 5
|
#define XMP_VER_MINOR 6
|
||||||
#define XMP_VER_RELEASE 0
|
#define XMP_VER_RELEASE 0
|
||||||
|
|
||||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||||
# if defined(BUILDING_STATIC)
|
# if defined(LIBXMP_STATIC)
|
||||||
# define LIBXMP_EXPORT
|
# define LIBXMP_EXPORT
|
||||||
# elif defined(BUILDING_DLL)
|
# elif defined(BUILDING_DLL)
|
||||||
# define LIBXMP_EXPORT __declspec(dllexport)
|
# define LIBXMP_EXPORT __declspec(dllexport)
|
||||||
# else
|
# else
|
||||||
# define LIBXMP_EXPORT __declspec(dllimport)
|
# define LIBXMP_EXPORT __declspec(dllimport)
|
||||||
# endif
|
# endif
|
||||||
#elif defined(__OS2__) && defined(__WATCOMC__) && defined(__SW_BD)
|
#elif defined(__OS2__) && defined(__WATCOMC__)
|
||||||
|
# if defined(LIBXMP_STATIC)
|
||||||
|
# define LIBXMP_EXPORT
|
||||||
|
# elif defined(BUILDING_DLL)
|
||||||
# define LIBXMP_EXPORT __declspec(dllexport)
|
# define LIBXMP_EXPORT __declspec(dllexport)
|
||||||
|
# else
|
||||||
|
# define LIBXMP_EXPORT
|
||||||
|
# endif
|
||||||
#elif (defined(__GNUC__) || defined(__clang__) || defined(__HP_cc)) && defined(XMP_SYM_VISIBILITY)
|
#elif (defined(__GNUC__) || defined(__clang__) || defined(__HP_cc)) && defined(XMP_SYM_VISIBILITY)
|
||||||
# define LIBXMP_EXPORT __attribute__((visibility ("default")))
|
# if defined(LIBXMP_STATIC)
|
||||||
|
# define LIBXMP_EXPORT
|
||||||
|
# else
|
||||||
|
# define LIBXMP_EXPORT __attribute__((visibility("default")))
|
||||||
|
# endif
|
||||||
#elif defined(__SUNPRO_C) && defined(XMP_LDSCOPE_GLOBAL)
|
#elif defined(__SUNPRO_C) && defined(XMP_LDSCOPE_GLOBAL)
|
||||||
# define LIBXMP_EXPORT __global
|
# if defined(LIBXMP_STATIC)
|
||||||
|
# define LIBXMP_EXPORT
|
||||||
|
# else
|
||||||
|
# define LIBXMP_EXPORT __global
|
||||||
|
# endif
|
||||||
#elif defined(EMSCRIPTEN)
|
#elif defined(EMSCRIPTEN)
|
||||||
# include <emscripten.h>
|
|
||||||
# define LIBXMP_EXPORT EMSCRIPTEN_KEEPALIVE
|
# define LIBXMP_EXPORT EMSCRIPTEN_KEEPALIVE
|
||||||
# define LIBXMP_EXPORT_VAR
|
# define LIBXMP_EXPORT_VAR
|
||||||
#else
|
#else
|
||||||
# define LIBXMP_EXPORT
|
# define LIBXMP_EXPORT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined (LIBXMP_EXPORT_VAR)
|
#if !defined(LIBXMP_EXPORT_VAR)
|
||||||
# define LIBXMP_EXPORT_VAR LIBXMP_EXPORT
|
# define LIBXMP_EXPORT_VAR LIBXMP_EXPORT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -238,6 +255,7 @@ struct xmp_sample {
|
||||||
#define XMP_SAMPLE_LOOP_FULL (1 << 4) /* Play full sample before looping */
|
#define XMP_SAMPLE_LOOP_FULL (1 << 4) /* Play full sample before looping */
|
||||||
#define XMP_SAMPLE_SLOOP (1 << 5) /* Sample has sustain loop */
|
#define XMP_SAMPLE_SLOOP (1 << 5) /* Sample has sustain loop */
|
||||||
#define XMP_SAMPLE_SLOOP_BIDIR (1 << 6) /* Bidirectional sustain loop */
|
#define XMP_SAMPLE_SLOOP_BIDIR (1 << 6) /* Bidirectional sustain loop */
|
||||||
|
#define XMP_SAMPLE_STEREO (1 << 7) /* Interlaced stereo sample */
|
||||||
#define XMP_SAMPLE_SYNTH (1 << 15) /* Data contains synth patch */
|
#define XMP_SAMPLE_SYNTH (1 << 15) /* Data contains synth patch */
|
||||||
int flg; /* Flags */
|
int flg; /* Flags */
|
||||||
unsigned char *data; /* Sample data */
|
unsigned char *data; /* Sample data */
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#define BUILDING_STATIC 1
|
#define LIBXMP_STATIC 1
|
||||||
#include "libxmp-lite/xmp.h"
|
#include "libxmp-lite/xmp.h"
|
||||||
|
|
||||||
#include "vtables.h"
|
#include "vtables.h"
|
||||||
|
|
Loading…
Reference in a new issue