1
1
Fork 0
mirror of https://github.com/QB64-Phoenix-Edition/QB64pe.git synced 2024-07-03 12:21:20 +00:00

Merge pull request #143 from mkilgore/a740g-miniaudio-backend

a740g's miniaudio audio backend
This commit is contained in:
Matt Kilgore 2022-08-27 16:35:28 -04:00 committed by GitHub
commit 4241d41e4f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
88 changed files with 127738 additions and 1716 deletions

1
.gitignore vendored
View file

@ -15,3 +15,4 @@ internal/c/qbx[2-9].cpp
/qb64pe
mingw32.exe
mingw64.exe
.vscode

View file

@ -181,6 +181,7 @@ include $(PATH_INTERNAL_C)/parts/audio/conversion/build.mk
include $(PATH_INTERNAL_C)/parts/audio/decode/mp3_mini/build.mk
include $(PATH_INTERNAL_C)/parts/audio/decode/ogg/build.mk
include $(PATH_INTERNAL_C)/parts/audio/out/build.mk
include $(PATH_INTERNAL_C)/parts/audio/build.mk
include $(PATH_INTERNAL_C)/parts/core/build.mk
include $(PATH_INTERNAL_C)/parts/input/game_controller/build.mk
include $(PATH_INTERNAL_C)/parts/video/font/ttf/build.mk
@ -266,6 +267,15 @@ else
QBLIB_NAME := $(addsuffix 0,$(QBLIB_NAME))
endif
ifneq ($(filter y,$(DEP_AUDIO_MINIAUDIO)),)
EXE_LIBS += $(MINIAUDIO_OBJS)
CXXFLAGS += -DDEPENDENCY_AUDIO_MINIAUDIO
QBLIB_NAME := $(addsuffix 1,$(QBLIB_NAME))
else
QBLIB_NAME := $(addsuffix 0,$(QBLIB_NAME))
endif
ifneq ($(filter y,$(DEP_AUDIO_CONVERSION) $(DEP_AUDIO_DECODE)),)
EXE_LIBS += $(QB_AUDIO_CONVERSION_LIB)
CXXFLAGS += -DDEPENDENCY_AUDIO_CONVERSION
@ -329,7 +339,7 @@ ifeq ($(OS),win)
CXXLIBS += -lwinspool
endif
ifneq ($(filter y,$(DEP_AUDIO_OUT) $(DEP_AUDIO_CONVERSION) $(DEP_AUDIO_DECODE)),)
ifneq ($(filter y,$(DEP_AUDIO_OUT) $(DEP_AUDIO_CONVERSION) $(DEP_AUDIO_DECODE) $(DEP_AUDIO_MINIAUDIO)),)
CXXLIBS += -lwinmm -lksguid -ldxguid -lole32
endif

View file

@ -128,6 +128,7 @@ These flags controls whether certain dependencies are compiled in or not. All of
| `DEP_DATA` | Compiles in data produced via `DATA` statements. |
| `DEP_CONSOLE` | On Windows, this gives the program console support (graphical support is still allowed) |
| `DEP_CONSOLE_ONLY` | Same as `DEP_CONSOLE`, but also removes GLUT and graphics support. |
| `DEP_AUDIO_MINIAUDIO` | Enables the miniaudio backend. Should not be used with the other `DEP_AUDIO` switches which enable the old backend. |
Versioning
----------

View file

@ -23,6 +23,7 @@
#endif
#include "mutex.h"
#include "audio.h"
int32 disableEvents = 0;
@ -22101,26 +22102,15 @@ double func_sqr(double value) {
return sqrt(value);
}
#ifdef QB64_BACKSLASH_FILESYSTEM
# include "parts\\audio\\out\\src.c"
#else
#ifndef DEPENDENCY_AUDIO_MINIAUDIO
# include "parts/audio/out/src.c"
#endif
#ifdef QB64_BACKSLASH_FILESYSTEM
# include "parts\\audio\\conversion\\src.c"
# include "parts\\audio\\decode\\src.c"
#else
# include "parts/audio/conversion/src.c"
# include "parts/audio/decode/src.c"
#endif
#ifdef DEPENDENCY_ZLIB
# ifdef QB64_BACKSLASH_FILESYSTEM
# include "parts\\zlib\\src.c"
# else
# include "parts/zlib/src.c"
# endif
# include "parts/zlib/src.c"
#endif
qbs *func_command_str = NULL;

View file

@ -0,0 +1,91 @@
//----------------------------------------------------------------------------------------------------
// ___ ___ __ _ _ ___ ___ _ _ _ ___ _
// / _ \| _ ) / /| | || _ \ __| /_\ _ _ __| (_)___ | __|_ _ __ _(_)_ _ ___
// | (_) | _ \/ _ \_ _| _/ _| / _ \ || / _` | / _ \ | _|| ' \/ _` | | ' \/ -_)
// \__\_\___/\___/ |_||_| |___| /_/ \_\_,_\__,_|_\___/ |___|_||_\__, |_|_||_\___|
// |___/
//
// QB64-PE Audio Engine powered by miniaudio (https://miniaud.io/)
//
// Copyright (c) 2022 Samuel Gomes
// https://github.com/a740g
//
//-----------------------------------------------------------------------------------------------------
#pragma once
//-----------------------------------------------------------------------------------------------------
// HEADER FILES
//-----------------------------------------------------------------------------------------------------
#include <stdint.h>
#include <stdio.h>
//-----------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------
// MACROS
//-----------------------------------------------------------------------------------------------------
#if defined(AUDIO_DEBUG) && AUDIO_DEBUG > 0
# ifdef _MSC_VER
# define AUDIO_DEBUG_PRINT(_fmt_, ...) fprintf(stderr, "DEBUG: %s:%d:%s(): " _fmt_ "\n", __FILE__, __LINE__, __func__, __VA_ARGS__)
# else
# define AUDIO_DEBUG_PRINT(_fmt_, _args_...) fprintf(stderr, "DEBUG: %s:%d:%s(): " _fmt_ "\n", __FILE__, __LINE__, __func__, ##_args_)
# endif
# define AUDIO_DEBUG_CHECK(_exp_) \
if (!(_exp_)) \
AUDIO_DEBUG_PRINT("Condition (%s) failed", #_exp_)
#else
# ifdef _MSC_VER
# define AUDIO_DEBUG_PRINT(_fmt_, ...) // Don't do anything in release builds
# else
# define AUDIO_DEBUG_PRINT(_fmt_, _args_...) // Don't do anything in release builds
# endif
# define AUDIO_DEBUG_CHECK(_exp_) // Don't do anything in release builds
#endif
//-----------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------
// FORWARD DECLARATIONS
//-----------------------------------------------------------------------------------------------------
struct qbs;
struct mem_block;
//-----------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------
// FUNCTIONS
//-----------------------------------------------------------------------------------------------------
void sub_sound(double frequency, double lengthInClockTicks);
void sub_beep();
void sub_play(qbs *str);
int32_t func_play(int32_t ignore);
int32_t func__sndrate();
int32_t func__sndopen(qbs *fileName, qbs *requirements, int32_t passed);
void sub__sndclose(int32_t handle);
int32_t func__sndcopy(int32_t src_handle);
void sub__sndplay(int32_t handle);
void sub__sndplaycopy(int32_t src_handle, double volume, int32_t passed);
void sub__sndplayfile(qbs *fileName, int32_t sync, double volume, int32_t passed);
void sub__sndpause(int32_t handle);
int32_t func__sndplaying(int32_t handle);
int32_t func__sndpaused(int32_t handle);
void sub__sndvol(int32_t handle, float volume);
void sub__sndloop(int32_t handle);
void sub__sndbal(int32_t handle, double x, double y, double z, int32_t channel, int32_t passed);
double func__sndlen(int32_t handle);
double func__sndgetpos(int32_t handle);
void sub__sndsetpos(int32_t handle, double seconds);
void sub__sndlimit(int32_t handle, double limit);
void sub__sndstop(int32_t handle);
int32_t func__sndopenraw();
void sub__sndraw(float left, float right, int32_t handle, int32_t passed);
void sub__sndrawdone(int32_t handle, int32_t passed);
double func__sndrawlen(int32_t handle, int32_t passed);
mem_block func__memsound(int32_t handle, int32_t targetChannel);
void snd_init();
void snd_un_init();
void snd_mainloop();
//-----------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,58 @@
MINIAUDIO_REAL_SRCS := \
audio.cpp \
miniaudio_impl.cpp
MINIAUDIO_REAL_EXTRAS_SRCS := \
extras/libxmp-lite/common.c \
extras/libxmp-lite/control.c \
extras/libxmp-lite/dataio.c \
extras/libxmp-lite/effects.c \
extras/libxmp-lite/filter.c \
extras/libxmp-lite/format.c \
extras/libxmp-lite/hio.c \
extras/libxmp-lite/it_load.c \
extras/libxmp-lite/itsex.c \
extras/libxmp-lite/lfo.c \
extras/libxmp-lite/load.c \
extras/libxmp-lite/load_helpers.c \
extras/libxmp-lite/md5.c \
extras/libxmp-lite/memio.c \
extras/libxmp-lite/misc.c \
extras/libxmp-lite/mix_all.c \
extras/libxmp-lite/mixer.c \
extras/libxmp-lite/mod_load.c \
extras/libxmp-lite/period.c \
extras/libxmp-lite/player.c \
extras/libxmp-lite/read_event.c \
extras/libxmp-lite/s3m_load.c \
extras/libxmp-lite/sample.c \
extras/libxmp-lite/scan.c \
extras/libxmp-lite/smix.c \
extras/libxmp-lite/virtual.c \
extras/libxmp-lite/win32.c \
extras/libxmp-lite/xm_load.c
MINIAUDIO_STUB_SRCS := \
stub_audio.cpp
# We always produce both lists so that `make clean` will clean them up even
# when not passed a paticular DEP_* flag
MINIAUDIO_REAL_OBJS := $(patsubst %.cpp,$(PATH_INTERNAL_C)/parts/audio/%.o,$(MINIAUDIO_REAL_SRCS))
MINIAUDIO_REAL_OBJS += $(patsubst %.c,$(PATH_INTERNAL_C)/parts/audio/%.o,$(MINIAUDIO_REAL_EXTRAS_SRCS))
MINIAUDIO_STUB_OBJS := $(patsubst %.cpp,$(PATH_INTERNAL_C)/parts/audio/%.o,$(MINIAUDIO_STUB_SRCS))
ifdef DEP_AUDIO_MINIAUDIO
MINIAUDIO_OBJS := $(MINIAUDIO_REAL_OBJS)
else
MINIAUDIO_OBJS := $(MINIAUDIO_STUB_OBJS)
endif
$(PATH_INTERNAL_C)/parts/audio/%.o: $(PATH_INTERNAL_C)/parts/audio/%.cpp
$(CXX) $(CXXFLAGS) -Wall $< -c -o $@
$(PATH_INTERNAL_C)/parts/audio/extras/libxmp-lite/%.o: $(PATH_INTERNAL_C)/parts/audio/extras/libxmp-lite/%.c
$(CC) $(CFLAGS) -Wall -DLIBXMP_CORE_PLAYER -DLIBXMP_NO_PROWIZARD -DLIBXMP_NO_DEPACKERS -DBUILDING_STATIC $< -c -o $@
CLEAN_LIST += $(MINIAUDIO_REAL_OBJS) $(MINIAUDIO_STUB_OBJS)

View file

@ -0,0 +1,190 @@
#ifndef LIBXMP_CALLBACKIO_H
#define LIBXMP_CALLBACKIO_H
#include <stddef.h>
#include "common.h"
typedef struct {
void *priv;
struct xmp_callbacks callbacks;
int eof;
} CBFILE;
#ifdef __cplusplus
extern "C" {
#endif
static inline uint8 cbread8(CBFILE *f, int *err)
{
uint8 x = 0xff;
size_t r = f->callbacks.read_func(&x, 1, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF;
if (err) *err = f->eof;
return x;
}
static inline int8 cbread8s(CBFILE *f, int *err)
{
return (int8)cbread8(f, err);
}
static inline uint16 cbread16l(CBFILE *f, int *err)
{
uint8 buf[2];
uint16 x = EOF;
size_t r = f->callbacks.read_func(buf, 2, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF;
if (r) x = readmem16l(buf);
if (err) *err = f->eof;
return x;
}
static inline uint16 cbread16b(CBFILE *f, int *err)
{
uint8 buf[2];
uint16 x = EOF;
size_t r = f->callbacks.read_func(buf, 2, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF;
if (r) x = readmem16b(buf);
if (err) *err = f->eof;
return x;
}
static inline uint32 cbread24l(CBFILE *f, int *err)
{
uint8 buf[3];
uint32 x = EOF;
size_t r = f->callbacks.read_func(buf, 3, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF;
if (r) x = readmem24l(buf);
if (err) *err = f->eof;
return x;
}
static inline uint32 cbread24b(CBFILE *f, int *err)
{
uint8 buf[3];
uint32 x = EOF;
size_t r = f->callbacks.read_func(buf, 3, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF;
if (r) x = readmem24b(buf);
if (err) *err = f->eof;
return x;
}
static inline uint32 cbread32l(CBFILE *f, int *err)
{
uint8 buf[4];
uint32 x = EOF;
size_t r = f->callbacks.read_func(buf, 4, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF;
if (r) x = readmem32l(buf);
if (err) *err = f->eof;
return x;
}
static inline uint32 cbread32b(CBFILE *f, int *err)
{
uint8 buf[4];
uint32 x = EOF;
size_t r = f->callbacks.read_func(buf, 4, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF;
if (r) x = readmem32b(buf);
if (err) *err = f->eof;
return x;
}
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);
f->eof = (r < nmemb) ? EOF : 0;
return r;
}
static inline int cbseek(CBFILE *f, long offset, int whence)
{
f->eof = 0;
return f->callbacks.seek_func(f->priv, offset, whence);
}
static inline long cbtell(CBFILE *f)
{
return f->callbacks.tell_func(f->priv);
}
static inline int cbeof(CBFILE *f)
{
return f->eof;
}
static inline long cbfilelength(CBFILE *f)
{
long pos = f->callbacks.tell_func(f->priv);
long length;
int r;
if (pos < 0)
return EOF;
r = f->callbacks.seek_func(f->priv, 0, SEEK_END);
if (r < 0)
return EOF;
length = f->callbacks.tell_func(f->priv);
r = f->callbacks.seek_func(f->priv, pos, SEEK_SET);
return length;
}
static inline CBFILE *cbopen(void *priv, struct xmp_callbacks callbacks)
{
CBFILE *f;
if (priv == NULL || callbacks.read_func == NULL ||
callbacks.seek_func == NULL || callbacks.tell_func == NULL)
goto err;
f = (CBFILE *)calloc(1, sizeof(CBFILE));
if (f == NULL)
goto err;
f->priv = priv;
f->callbacks = callbacks;
f->eof = 0;
return f;
err:
if (priv && callbacks.close_func)
callbacks.close_func(priv);
return NULL;
}
static inline int cbclose(CBFILE *f)
{
int r = 0;
if (f->callbacks.close_func != NULL)
r = f->callbacks.close_func(f->priv);
free(f);
return r;
}
#ifdef __cplusplus
}
#endif
#endif /* LIBXMP_CALLBACKIO_H */

View file

@ -0,0 +1,573 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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 <ctype.h>
#ifndef LIBXMP_CORE_PLAYER
#if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <limits.h>
#elif defined(__OS2__) || defined(__EMX__)
#define INCL_DOS
#define INCL_DOSERRORS
#include <os2.h>
#elif defined(__DJGPP__)
#include <dos.h>
#include <io.h>
#elif defined(HAVE_DIRENT_H)
#include <dirent.h>
#endif
#endif /* LIBXMP_CORE_PLAYER */
#include "xmp.h"
#include "common.h"
#include "period.h"
#include "loader.h"
int libxmp_init_instrument(struct module_data *m)
{
struct xmp_module *mod = &m->mod;
if (mod->ins > 0) {
mod->xxi = calloc(sizeof (struct xmp_instrument), mod->ins);
if (mod->xxi == NULL)
return -1;
}
if (mod->smp > 0) {
int i;
/* Sanity check */
if (mod->smp > MAX_SAMPLES) {
D_(D_CRIT "sample count %d exceeds maximum (%d)",
mod->smp, MAX_SAMPLES);
return -1;
}
mod->xxs = calloc(sizeof (struct xmp_sample), mod->smp);
if (mod->xxs == NULL)
return -1;
m->xtra = calloc(sizeof (struct extra_sample_data), mod->smp);
if (m->xtra == NULL)
return -1;
for (i = 0; i < mod->smp; i++) {
m->xtra[i].c5spd = m->c4rate;
}
}
return 0;
}
/* Sample number adjustment (originally by Vitamin/CAIG).
* Only use this AFTER a previous usage of libxmp_init_instrument,
* and don't use this to free samples that have already been loaded. */
int libxmp_realloc_samples(struct module_data *m, int new_size)
{
struct xmp_module *mod = &m->mod;
struct xmp_sample *xxs;
struct extra_sample_data *xtra;
/* Sanity check */
if (new_size < 0)
return -1;
if (new_size == 0) {
/* Don't rely on implementation-defined realloc(x,0) behavior. */
mod->smp = 0;
free(mod->xxs);
mod->xxs = NULL;
free(m->xtra);
m->xtra = NULL;
return 0;
}
xxs = realloc(mod->xxs, sizeof(struct xmp_sample) * new_size);
if (xxs == NULL)
return -1;
mod->xxs = xxs;
xtra = realloc(m->xtra, sizeof(struct extra_sample_data) * new_size);
if (xtra == NULL)
return -1;
m->xtra = xtra;
if (new_size > mod->smp) {
int clear_size = new_size - mod->smp;
int i;
memset(xxs + mod->smp, 0, sizeof(struct xmp_sample) * clear_size);
memset(xtra + mod->smp, 0, sizeof(struct extra_sample_data) * clear_size);
for (i = mod->smp; i < new_size; i++) {
m->xtra[i].c5spd = m->c4rate;
}
}
mod->smp = new_size;
return 0;
}
int libxmp_alloc_subinstrument(struct xmp_module *mod, int i, int num)
{
if (num == 0)
return 0;
mod->xxi[i].sub = calloc(sizeof (struct xmp_subinstrument), num);
if (mod->xxi[i].sub == NULL)
return -1;
return 0;
}
int libxmp_init_pattern(struct xmp_module *mod)
{
mod->xxt = calloc(sizeof (struct xmp_track *), mod->trk);
if (mod->xxt == NULL)
return -1;
mod->xxp = calloc(sizeof (struct xmp_pattern *), mod->pat);
if (mod->xxp == NULL)
return -1;
return 0;
}
int libxmp_alloc_pattern(struct xmp_module *mod, int num)
{
/* Sanity check */
if (num < 0 || num >= mod->pat || mod->xxp[num] != NULL)
return -1;
mod->xxp[num] = calloc(1, sizeof (struct xmp_pattern) +
sizeof (int) * (mod->chn - 1));
if (mod->xxp[num] == NULL)
return -1;
return 0;
}
int libxmp_alloc_track(struct xmp_module *mod, int num, int rows)
{
/* Sanity check */
if (num < 0 || num >= mod->trk || mod->xxt[num] != NULL || rows <= 0)
return -1;
mod->xxt[num] = calloc(sizeof (struct xmp_track) +
sizeof (struct xmp_event) * (rows - 1), 1);
if (mod->xxt[num] == NULL)
return -1;
mod->xxt[num]->rows = rows;
return 0;
}
int libxmp_alloc_tracks_in_pattern(struct xmp_module *mod, int num)
{
int i;
D_(D_INFO "Alloc %d tracks of %d rows", mod->chn, mod->xxp[num]->rows);
for (i = 0; i < mod->chn; i++) {
int t = num * mod->chn + i;
int rows = mod->xxp[num]->rows;
if (libxmp_alloc_track(mod, t, rows) < 0)
return -1;
mod->xxp[num]->index[i] = t;
}
return 0;
}
int libxmp_alloc_pattern_tracks(struct xmp_module *mod, int num, int rows)
{
/* Sanity check */
if (rows <= 0 || rows > 256)
return -1;
if (libxmp_alloc_pattern(mod, num) < 0)
return -1;
mod->xxp[num]->rows = rows;
if (libxmp_alloc_tracks_in_pattern(mod, num) < 0)
return -1;
return 0;
}
/* 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.
*/
int libxmp_alloc_pattern_tracks_long(struct xmp_module *mod, int num, int rows)
{
/* Sanity check */
if (rows <= 0 || rows > 32768)
return -1;
if (libxmp_alloc_pattern(mod, num) < 0)
return -1;
mod->xxp[num]->rows = rows;
if (libxmp_alloc_tracks_in_pattern(mod, num) < 0)
return -1;
return 0;
}
char *libxmp_instrument_name(struct xmp_module *mod, int i, uint8 *r, int n)
{
CLAMP(n, 0, 31);
return libxmp_copy_adjust(mod->xxi[i].name, r, n);
}
char *libxmp_copy_adjust(char *s, uint8 *r, int n)
{
int i;
memset(s, 0, n + 1);
strncpy(s, (char *)r, n);
for (i = 0; s[i] && i < n; i++) {
if (!isprint((int)s[i]) || ((uint8)s[i] > 127))
s[i] = '.';
}
while (*s && (s[strlen(s) - 1] == ' '))
s[strlen(s) - 1] = 0;
return s;
}
void libxmp_read_title(HIO_HANDLE *f, char *t, int s)
{
uint8 buf[XMP_NAME_SIZE];
if (t == NULL)
return;
if (s >= XMP_NAME_SIZE)
s = XMP_NAME_SIZE -1;
memset(t, 0, s + 1);
hio_read(buf, 1, s, f); /* coverity[check_return] */
buf[s] = 0;
libxmp_copy_adjust(t, buf, s);
}
#ifndef LIBXMP_CORE_PLAYER
int libxmp_test_name(uint8 *s, int n)
{
int i;
for (i = 0; i < n; i++) {
if (s[i] > 0x7f)
return -1;
/* ACS_Team2.mod has a backspace in instrument name */
if (s[i] > 0 && s[i] < 32 && s[i] != 0x08)
return -1;
}
return 0;
}
int libxmp_copy_name_for_fopen(char *dest, const char *name, int n)
{
int converted_colon = 0;
int i;
/* libxmp_copy_adjust, but make sure the filename won't do anything
* malicious when given to fopen. This should only be used on song files.
*/
if (!strcmp(name, ".") || strstr(name, "..") ||
name[0] == '\\' || name[0] == '/' || name[0] == ':')
return -1;
for (i = 0; i < n - 1; i++) {
uint8 t = name[i];
if (!t)
break;
/* Reject non-ASCII symbols as they have poorly defined behavior.
*/
if (t < 32 || t >= 0x7f)
return -1;
/* Reject anything resembling a Windows-style root path. Allow
* converting a single : to / so things like ST-01:samplename
* work. (Leave the : as-is on Amiga.)
*/
if (i > 0 && t == ':' && !converted_colon) {
uint8 t2 = name[i + 1];
if (!t2 || t2 == '/' || t2 == '\\')
return -1;
converted_colon = 1;
#ifndef LIBXMP_AMIGA
dest[i] = '/';
continue;
#endif
}
if (t == '\\') {
dest[i] = '/';
continue;
}
dest[i] = t;
}
dest[i] = '\0';
return 0;
}
/*
* Honor Noisetracker effects:
*
* 0 - arpeggio
* 1 - portamento up
* 2 - portamento down
* 3 - Tone-portamento
* 4 - Vibrato
* A - Slide volume
* B - Position jump
* C - Set volume
* D - Pattern break
* E - Set filter (keep the led off, please!)
* F - Set speed (now up to $1F)
*
* Pex Tufvesson's notes from http://www.livet.se/mahoney/:
*
* Note that some of the modules will have bugs in the playback with all
* known PC module players. This is due to that in many demos where I synced
* events in the demo with the music, I used commands that these newer PC
* module players erroneously interpret as "newer-version-trackers commands".
* Which they aren't.
*/
void libxmp_decode_noisetracker_event(struct xmp_event *event, uint8 *mod_event)
{
int fxt;
memset(event, 0, sizeof (struct xmp_event));
event->note = libxmp_period_to_note((LSN(mod_event[0]) << 8) + mod_event[1]);
event->ins = ((MSN(mod_event[0]) << 4) | MSN(mod_event[2]));
fxt = LSN(mod_event[2]);
if (fxt <= 0x06 || (fxt >= 0x0a && fxt != 0x0e)) {
event->fxt = fxt;
event->fxp = mod_event[3];
}
libxmp_disable_continue_fx(event);
}
#endif
void libxmp_decode_protracker_event(struct xmp_event *event, uint8 *mod_event)
{
int fxt = LSN(mod_event[2]);
memset(event, 0, sizeof (struct xmp_event));
event->note = libxmp_period_to_note((LSN(mod_event[0]) << 8) + mod_event[1]);
event->ins = ((MSN(mod_event[0]) << 4) | MSN(mod_event[2]));
if (fxt != 0x08) {
event->fxt = fxt;
event->fxp = mod_event[3];
}
libxmp_disable_continue_fx(event);
}
void libxmp_disable_continue_fx(struct xmp_event *event)
{
if (event->fxp == 0) {
switch (event->fxt) {
case 0x05:
event->fxt = 0x03;
break;
case 0x06:
event->fxt = 0x04;
break;
case 0x01:
case 0x02:
case 0x0a:
event->fxt = 0x00;
}
} else if (event->fxt == 0x0e) {
if (event->fxp == 0xa0 || event->fxp == 0xb0) {
event->fxt = event->fxp = 0;
}
}
}
#ifndef LIBXMP_CORE_PLAYER
/* libxmp_check_filename_case(): */
/* Given a directory, see if file exists there, ignoring case */
#if defined(_WIN32)
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
{
char path[_MAX_PATH];
DWORD rc;
/* win32 is case-insensitive: directly probe the file. */
snprintf(path, sizeof(path), "%s/%s", dir, name);
rc = GetFileAttributesA(path);
if (rc == (DWORD)(-1)) return 0;
if (rc & FILE_ATTRIBUTE_DIRECTORY) return 0;
strncpy(new_name, name, size);
return 1;
}
#elif defined(__OS2__) || defined(__EMX__)
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
{
char path[CCHMAXPATH];
FILESTATUS3 fs;
/* os/2 is case-insensitive: directly probe the file. */
snprintf(path, sizeof(path), "%s/%s", dir, name);
if (DosQueryPathInfo(path, FIL_STANDARD, &fs, sizeof(fs)) != NO_ERROR) return 0;
if (fs.attrFile & FILE_DIRECTORY) return 0;
strncpy(new_name, name, size);
return 1;
}
#elif defined(__DJGPP__)
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
{
char path[256];
int attr;
/* dos is case-insensitive: directly probe the file. */
snprintf(path, sizeof(path), "%s/%s", dir, name);
attr = _chmod(path, 0);
if (attr == -1) return 0;
if (attr & (_A_SUBDIR|_A_VOLID)) return 0;
strncpy(new_name, name, size);
return 1;
}
#elif defined(HAVE_DIRENT_H)
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
{
int found = 0;
DIR *dirfd;
struct dirent *d;
dirfd = opendir(dir);
if (dirfd == NULL)
return 0;
while ((d = readdir(dirfd)) != NULL) {
if (!strcasecmp(d->d_name, name)) {
found = 1;
break;
}
}
if (found)
strncpy(new_name, d->d_name, size);
closedir(dirfd);
return found;
}
#else
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
{
return 0;
}
#endif
void libxmp_get_instrument_path(struct module_data *m, char *path, int size)
{
if (m->instrument_path) {
strncpy(path, m->instrument_path, size);
} else if (getenv("XMP_INSTRUMENT_PATH")) {
strncpy(path, getenv("XMP_INSTRUMENT_PATH"), size);
} else {
strncpy(path, ".", size);
}
}
#endif /* LIBXMP_CORE_PLAYER */
void libxmp_set_type(struct module_data *m, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vsnprintf(m->mod.type, XMP_NAME_SIZE, fmt, ap);
va_end(ap);
}
static int schism_tracker_date(int year, int month, int day)
{
int mm = (month + 9) % 12;
int yy = year - mm / 10;
yy = yy * 365 + (yy / 4) - (yy / 100) + (yy / 400);
mm = (mm * 306 + 5) / 10;
return yy + mm + (day - 1);
}
/* Generate a Schism Tracker version string.
* Schism Tracker versions are stored as follows:
*
* s_ver <= 0x50: 0.s_ver
* s_ver > 0x50, < 0xfff: days from epoch=(s_ver - 0x50)
* s_ver = 0xfff: days from epoch=l_ver
*/
void libxmp_schism_tracker_string(char *buf, size_t size, int s_ver, int l_ver)
{
if (s_ver >= 0x50) {
/* time_t epoch_sec = 1256947200; */
int t = schism_tracker_date(2009, 10, 31);
int year, month, day, dayofyear;
if (s_ver == 0xfff) {
t += l_ver;
} else
t += s_ver - 0x50;
/* Date algorithm reimplemented from OpenMPT.
*/
year = (int)(((int64)t * 10000L + 14780) / 3652425);
dayofyear = t - (365 * year + (year / 4) - (year / 100) + (year / 400));
if (dayofyear < 0) {
year--;
dayofyear = t - (365 * year + (year / 4) - (year / 100) + (year / 400));
}
month = (100 * dayofyear + 52) / 3060;
day = dayofyear - (month * 306 + 5) / 10 + 1;
year += (month + 2) / 12;
month = (month + 2) % 12 + 1;
snprintf(buf, size, "Schism Tracker %04d-%02d-%02d",
year, month, day);
} else {
snprintf(buf, size, "Schism Tracker 0.%x", s_ver);
}
}

View file

@ -0,0 +1,473 @@
#ifndef LIBXMP_COMMON_H
#define LIBXMP_COMMON_H
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "xmp.h"
#undef LIBXMP_EXPORT_VAR
#if defined(EMSCRIPTEN)
#include <emscripten.h>
#define LIBXMP_EXPORT_VAR EMSCRIPTEN_KEEPALIVE
#else
#define LIBXMP_EXPORT_VAR
#endif
#if defined(__MORPHOS__) || defined(__AROS__) || defined(AMIGAOS) || \
defined(__amigaos__) || defined(__amigaos4__) ||defined(__amigados__) || \
defined(AMIGA) || defined(_AMIGA) || defined(__AMIGA__)
#define LIBXMP_AMIGA 1 /* to identify amiga platforms. */
#endif
#if (defined(__GNUC__) || defined(__clang__)) && defined(XMP_SYM_VISIBILITY)
#if !defined(_WIN32) && !defined(__ANDROID__) && !defined(__APPLE__) && !defined(LIBXMP_AMIGA) && !defined(__MSDOS__) && !defined(B_BEOS_VERSION) && !defined(__ATHEOS__) && !defined(EMSCRIPTEN) && !defined(__MINT__)
#define USE_VERSIONED_SYMBOLS
#endif
#endif
/* AmigaOS fixes by Chris Young <cdyoung@ntlworld.com>, Nov 25, 2007
*/
#if defined B_BEOS_VERSION
# include <SupportDefs.h>
#elif defined __amigaos4__
# include <exec/types.h>
#else
typedef signed char int8;
typedef signed short int int16;
typedef signed int int32;
typedef unsigned char uint8;
typedef unsigned short int uint16;
typedef unsigned int uint32;
#endif
#ifdef _MSC_VER /* MSVC++6.0 has no long long */
typedef signed __int64 int64;
typedef unsigned __int64 uint64;
#elif !defined B_BEOS_VERSION /* BeOS has its own int64 definition */
typedef unsigned long long uint64;
typedef signed long long int64;
#endif
#ifndef LIBXMP_CORE_PLAYER
#define LIBXMP_PAULA_SIMULATOR
#endif
/* Constants */
#define PAL_RATE 250.0 /* 1 / (50Hz * 80us) */
#define NTSC_RATE 208.0 /* 1 / (60Hz * 80us) */
#define C4_PAL_RATE 8287 /* 7093789.2 / period (C4) * 2 */
#define C4_NTSC_RATE 8363 /* 7159090.5 / period (C4) * 2 */
/* [Amiga] PAL color carrier frequency (PCCF) = 4.43361825 MHz */
/* [Amiga] CPU clock = 1.6 * PCCF = 7.0937892 MHz */
#define DEFAULT_AMPLIFY 1
#define DEFAULT_MIX 100
#define MSN(x) (((x)&0xf0)>>4)
#define LSN(x) ((x)&0x0f)
#define SET_FLAG(a,b) ((a)|=(b))
#define RESET_FLAG(a,b) ((a)&=~(b))
#define TEST_FLAG(a,b) !!((a)&(b))
#define CLAMP(x,a,b) do { \
if ((x) < (a)) (x) = (a); \
else if ((x) > (b)) (x) = (b); \
} while (0)
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define MAX(x,y) ((x) > (y) ? (x) : (y))
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define TRACK_NUM(a,c) m->mod.xxp[a]->index[c]
#define EVENT(a,c,r) m->mod.xxt[TRACK_NUM((a),(c))]->event[r]
#ifdef _MSC_VER
#define D_CRIT " Error: "
#define D_WARN "Warning: "
#define D_INFO " Info: "
#ifndef CLIB_DECL
#define CLIB_DECL
#endif
#ifdef DEBUG
#ifndef ATTR_PRINTF
#define ATTR_PRINTF(x,y)
#endif
void CLIB_DECL D_(const char *text, ...) ATTR_PRINTF(1,2);
#else
// VS prior to VC7.1 does not support variadic macros. VC8.0 does not optimize unused parameters passing
#if _MSC_VER < 1400
void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); }
#else
#define D_(args, ...) do {} while (0)
#endif
#endif
#elif defined __ANDROID__
#ifdef DEBUG
#include <android/log.h>
#define D_CRIT " Error: "
#define D_WARN "Warning: "
#define D_INFO " Info: "
#define D_(args...) do { \
__android_log_print(ANDROID_LOG_DEBUG, "libxmp", args); \
} while (0)
#else
#define D_(args...) do {} while (0)
#endif
#elif defined(__WATCOMC__)
#ifdef DEBUG
#define D_INFO "\x1b[33m"
#define D_CRIT "\x1b[31m"
#define D_WARN "\x1b[36m"
#define D_(...) do { \
printf("\x1b[33m%s \x1b[37m[%s:%d] " D_INFO, __FUNCTION__, \
__FILE__, __LINE__); printf (__VA_ARGS__); printf ("\x1b[0m\n"); \
} while (0)
#else
#define D_(...) do {} while (0)
#endif
#else
#ifdef DEBUG
#define D_INFO "\x1b[33m"
#define D_CRIT "\x1b[31m"
#define D_WARN "\x1b[36m"
#define D_(args...) do { \
printf("\x1b[33m%s \x1b[37m[%s:%d] " D_INFO, __FUNCTION__, \
__FILE__, __LINE__); printf (args); printf ("\x1b[0m\n"); \
} while (0)
#else
#define D_(args...) do {} while (0)
#endif
#endif /* !_MSC_VER */
#ifdef _MSC_VER
#define dup _dup
#define fileno _fileno
#define strnicmp _strnicmp
#define strdup _strdup
#define fdopen _fdopen
#define open _open
#define close _close
#define unlink _unlink
#define S_ISDIR(x) (((x)&_S_IFDIR) != 0)
#endif
#if defined(_WIN32) || defined(__WATCOMC__) /* in win32.c */
#define USE_LIBXMP_SNPRINTF
/* MSVC 2015+ has C99 compliant snprintf and vsnprintf implementations.
* If __USE_MINGW_ANSI_STDIO is defined for MinGW (which it is by default),
* compliant implementations will be used instead of the broken MSVCRT
* functions. Additionally, GCC may optimize some calls to those functions. */
#if defined(_MSC_VER) && _MSC_VER >= 1900
#undef USE_LIBXMP_SNPRINTF
#endif
#if defined(__MINGW32__) && defined(__USE_MINGW_ANSI_STDIO) && (__USE_MINGW_ANSI_STDIO != 0)
#undef USE_LIBXMP_SNPRINTF
#endif
#ifdef USE_LIBXMP_SNPRINTF
int libxmp_vsnprintf(char *, size_t, const char *, va_list);
int libxmp_snprintf (char *, size_t, const char *, ...);
#define snprintf libxmp_snprintf
#define vsnprintf libxmp_vsnprintf
#endif
#endif
/* Quirks */
#define QUIRK_S3MLOOP (1 << 0) /* S3M loop mode */
#define QUIRK_ENVFADE (1 << 1) /* Fade at end of envelope */
#define QUIRK_PROTRACK (1 << 2) /* Use Protracker-specific quirks */
#define QUIRK_ST3BUGS (1 << 4) /* Scream Tracker 3 bug compatibility */
#define QUIRK_FINEFX (1 << 5) /* Enable 0xf/0xe for fine effects */
#define QUIRK_VSALL (1 << 6) /* Volume slides in all frames */
#define QUIRK_PBALL (1 << 7) /* Pitch bending in all frames */
#define QUIRK_PERPAT (1 << 8) /* Cancel persistent fx at pat start */
#define QUIRK_VOLPDN (1 << 9) /* Set priority to volume slide down */
#define QUIRK_UNISLD (1 << 10) /* Unified pitch slide/portamento */
#define QUIRK_ITVPOR (1 << 11) /* Disable fine bends in IT vol fx */
#define QUIRK_FTMOD (1 << 12) /* Flag for multichannel mods */
/*#define QUIRK_MODRNG (1 << 13)*/ /* Limit periods to MOD range */
#define QUIRK_INSVOL (1 << 14) /* Use instrument volume */
#define QUIRK_VIRTUAL (1 << 15) /* Enable virtual channels */
#define QUIRK_FILTER (1 << 16) /* Enable filter */
#define QUIRK_IGSTPOR (1 << 17) /* Ignore stray tone portamento */
#define QUIRK_KEYOFF (1 << 18) /* Keyoff doesn't reset fadeout */
#define QUIRK_VIBHALF (1 << 19) /* Vibrato is half as deep */
#define QUIRK_VIBALL (1 << 20) /* Vibrato in all frames */
#define QUIRK_VIBINV (1 << 21) /* Vibrato has inverse waveform */
#define QUIRK_PRENV (1 << 22) /* Portamento resets envelope & fade */
#define QUIRK_ITOLDFX (1 << 23) /* IT old effects mode */
#define QUIRK_S3MRTG (1 << 24) /* S3M-style retrig when count == 0 */
#define QUIRK_RTDELAY (1 << 25) /* Delay effect retrigs instrument */
#define QUIRK_FT2BUGS (1 << 26) /* FT2 bug compatibility */
#define QUIRK_MARKER (1 << 27) /* Patterns 0xfe and 0xff reserved */
#define QUIRK_NOBPM (1 << 28) /* Adjust speed only, no BPM */
#define QUIRK_ARPMEM (1 << 29) /* Arpeggio has memory (S3M_ARPEGGIO) */
#define QUIRK_RSTCHN (1 << 30) /* Reset channel on sample end */
#define HAS_QUIRK(x) (m->quirk & (x))
/* Format quirks */
#define QUIRKS_ST3 (QUIRK_S3MLOOP | QUIRK_VOLPDN | QUIRK_FINEFX | \
QUIRK_S3MRTG | QUIRK_MARKER | QUIRK_RSTCHN )
#define QUIRKS_FT2 (QUIRK_RTDELAY | QUIRK_FINEFX )
#define QUIRKS_IT (QUIRK_S3MLOOP | QUIRK_FINEFX | QUIRK_VIBALL | \
QUIRK_ENVFADE | QUIRK_ITVPOR | QUIRK_KEYOFF | \
QUIRK_VIRTUAL | QUIRK_FILTER | QUIRK_RSTCHN | \
QUIRK_IGSTPOR | QUIRK_S3MRTG | QUIRK_MARKER )
/* DSP effects */
#define DSP_EFFECT_CUTOFF 0x02
#define DSP_EFFECT_RESONANCE 0x03
#define DSP_EFFECT_FILTER_A0 0xb0
#define DSP_EFFECT_FILTER_B0 0xb1
#define DSP_EFFECT_FILTER_B1 0xb2
/* Time factor */
#define DEFAULT_TIME_FACTOR 10.0
#define MED_TIME_FACTOR 2.64
#define MAX_SEQUENCES 16
#define MAX_SAMPLE_SIZE 0x10000000
#define MAX_SAMPLES 1024
#define MAX_INSTRUMENTS 255
#define MAX_PATTERNS 256
#define IS_PLAYER_MODE_MOD() (m->read_event_type == READ_EVENT_MOD)
#define IS_PLAYER_MODE_FT2() (m->read_event_type == READ_EVENT_FT2)
#define IS_PLAYER_MODE_ST3() (m->read_event_type == READ_EVENT_ST3)
#define IS_PLAYER_MODE_IT() (m->read_event_type == READ_EVENT_IT)
#define IS_PLAYER_MODE_MED() (m->read_event_type == READ_EVENT_MED)
#define IS_PERIOD_MODRNG() (m->period_type == PERIOD_MODRNG)
#define IS_PERIOD_LINEAR() (m->period_type == PERIOD_LINEAR)
#define IS_PERIOD_CSPD() (m->period_type == PERIOD_CSPD)
#define IS_AMIGA_MOD() (IS_PLAYER_MODE_MOD() && IS_PERIOD_MODRNG())
struct ord_data {
int speed;
int bpm;
int gvl;
int time;
int start_row;
#ifndef LIBXMP_CORE_PLAYER
int st26_speed;
#endif
};
/* Context */
struct smix_data {
int chn;
int ins;
int smp;
struct xmp_instrument *xxi;
struct xmp_sample *xxs;
};
/* This will be added to the sample structure in the next API revision */
struct extra_sample_data {
double c5spd;
};
struct module_data {
struct xmp_module mod;
char *dirname; /* file dirname */
char *basename; /* file basename */
const char *filename; /* Module file name */
char *comment; /* Comments, if any */
uint8 md5[16]; /* MD5 message digest */
int size; /* File size */
double rrate; /* Replay rate */
double time_factor; /* Time conversion constant */
int c4rate; /* C4 replay rate */
int volbase; /* Volume base */
int gvolbase; /* Global volume base */
int gvol; /* Global volume */
int *vol_table; /* Volume translation table */
int quirk; /* player quirks */
#define READ_EVENT_MOD 0
#define READ_EVENT_FT2 1
#define READ_EVENT_ST3 2
#define READ_EVENT_IT 3
#define READ_EVENT_MED 4
int read_event_type;
#define PERIOD_AMIGA 0
#define PERIOD_MODRNG 1
#define PERIOD_LINEAR 2
#define PERIOD_CSPD 3
int period_type;
int smpctl; /* sample control flags */
int defpan; /* default pan setting */
struct ord_data xxo_info[XMP_MAX_MOD_LENGTH];
int num_sequences;
struct xmp_sequence seq_data[MAX_SEQUENCES];
char *instrument_path;
void *extra; /* format-specific extra fields */
uint8 **scan_cnt; /* scan counters */
struct extra_sample_data *xtra;
#ifndef LIBXMP_CORE_DISABLE_IT
struct xmp_sample *xsmp; /* sustain loop samples */
#endif
};
struct pattern_loop {
int start;
int count;
};
struct flow_control {
int pbreak;
int jump;
int delay;
int jumpline;
int loop_chn;
struct pattern_loop *loop;
int num_rows;
int end_point;
#define ROWDELAY_ON (1 << 0)
#define ROWDELAY_FIRST_FRAME (1 << 1)
int rowdelay; /* For IT pattern row delay */
int rowdelay_set;
};
struct virt_channel {
int count;
int map;
};
struct player_data {
int ord;
int pos;
int row;
int frame;
int speed;
int bpm;
int mode;
int player_flags;
int flags;
double current_time;
double frame_time;
int loop_count;
int sequence;
unsigned char sequence_control[XMP_MAX_MOD_LENGTH];
int smix_vol; /* SFX volume */
int master_vol; /* Music volume */
int gvol;
struct flow_control flow;
struct {
int time; /* replay time in ms */
int ord;
int row;
int num;
} scan[MAX_SEQUENCES];
struct channel_data *xc_data;
int channel_vol[XMP_MAX_CHANNELS];
char channel_mute[XMP_MAX_CHANNELS];
struct virt_control {
int num_tracks; /* Number of tracks */
int virt_channels; /* Number of virtual channels */
int virt_used; /* Number of voices currently in use */
int maxvoc; /* Number of sound card voices */
struct virt_channel *virt_channel;
struct mixer_voice *voice_array;
} virt;
struct xmp_event inject_event[XMP_MAX_CHANNELS];
struct {
int consumed;
int in_size;
char *in_buffer;
} buffer_data;
#ifndef LIBXMP_CORE_PLAYER
int st26_speed; /* For IceTracker speed effect */
#endif
int filter; /* Amiga led filter */
};
struct mixer_data {
int freq; /* sampling rate */
int format; /* sample format */
int amplify; /* amplification multiplier */
int mix; /* percentage of channel separation */
int interp; /* interpolation type */
int dsp; /* dsp effect flags */
char* buffer; /* output buffer */
int32* buf32; /* temporary buffer for 32 bit samples */
int numvoc; /* default softmixer voices number */
int ticksize;
int dtright; /* anticlick control, right channel */
int dtleft; /* anticlick control, left channel */
double pbase; /* period base */
};
struct context_data {
struct player_data p;
struct mixer_data s;
struct module_data m;
struct smix_data smix;
int state;
};
/* Prototypes */
char *libxmp_adjust_string (char *);
int libxmp_prepare_scan (struct context_data *);
void libxmp_free_scan (struct context_data *);
int libxmp_scan_sequences (struct context_data *);
int libxmp_get_sequence (struct context_data *, int);
int libxmp_set_player_mode (struct context_data *);
int8 read8s (FILE *, int *err);
uint8 read8 (FILE *, int *err);
uint16 read16l (FILE *, int *err);
uint16 read16b (FILE *, int *err);
uint32 read24l (FILE *, int *err);
uint32 read24b (FILE *, int *err);
uint32 read32l (FILE *, int *err);
uint32 read32b (FILE *, int *err);
static inline void write8 (FILE *f, uint8 b) {
fputc(b, f);
}
void write16l (FILE *, uint16);
void write16b (FILE *, uint16);
void write32l (FILE *, uint32);
void write32b (FILE *, uint32);
int move_data (FILE *, FILE *, int);
uint16 readmem16l (const uint8 *);
uint16 readmem16b (const uint8 *);
uint32 readmem24l (const uint8 *);
uint32 readmem24b (const uint8 *);
uint32 readmem32l (const uint8 *);
uint32 readmem32b (const uint8 *);
struct xmp_instrument *libxmp_get_instrument(struct context_data *, int);
struct xmp_sample *libxmp_get_sample(struct context_data *, int);
#endif /* LIBXMP_COMMON_H */

View file

@ -0,0 +1,585 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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 "format.h"
#include "virtual.h"
#include "mixer.h"
const char *xmp_version LIBXMP_EXPORT_VAR = XMP_VERSION;
const unsigned int xmp_vercode LIBXMP_EXPORT_VAR = XMP_VERCODE;
xmp_context xmp_create_context(void)
{
struct context_data *ctx;
ctx = calloc(1, sizeof(struct context_data));
if (ctx == NULL) {
return NULL;
}
ctx->state = XMP_STATE_UNLOADED;
ctx->m.defpan = 100;
ctx->s.numvoc = SMIX_NUMVOC;
return (xmp_context)ctx;
}
void xmp_free_context(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
struct module_data *m = &ctx->m;
if (ctx->state > XMP_STATE_UNLOADED)
xmp_release_module(opaque);
free(m->instrument_path);
free(opaque);
}
static void set_position(struct context_data *ctx, int pos, int dir)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
struct flow_control *f = &p->flow;
int seq;
int has_marker;
/* If dir is 0, we can jump to a different sequence */
if (dir == 0) {
seq = libxmp_get_sequence(ctx, pos);
} else {
seq = p->sequence;
}
if (seq == 0xff) {
return;
}
has_marker = HAS_QUIRK(QUIRK_MARKER);
if (seq >= 0) {
int start = m->seq_data[seq].entry_point;
p->sequence = seq;
if (pos >= 0) {
int pat;
while (has_marker && mod->xxo[pos] == 0xfe) {
if (dir < 0) {
if (pos > start) {
pos--;
}
} else {
pos++;
}
}
pat = mod->xxo[pos];
if (pat < mod->pat) {
if (has_marker && pat == 0xff) {
return;
}
if (pos > p->scan[seq].ord) {
f->end_point = 0;
} else {
f->num_rows = mod->xxp[pat]->rows;
f->end_point = p->scan[seq].num;
f->jumpline = 0;
}
}
}
if (pos < mod->len) {
if (pos == 0) {
p->pos = -1;
} else {
p->pos = pos;
}
f->jumpline = 0;
f->jump = -1;
f->pbreak = 0;
f->loop_chn = 0;
f->delay = 0;
f->rowdelay = 0;
f->rowdelay_set = 0;
}
}
}
int xmp_next_position(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
if (ctx->state < XMP_STATE_PLAYING)
return -XMP_ERROR_STATE;
if (p->pos < m->mod.len)
set_position(ctx, p->pos + 1, 1);
return p->pos;
}
int xmp_prev_position(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
if (ctx->state < XMP_STATE_PLAYING)
return -XMP_ERROR_STATE;
if (p->pos == m->seq_data[p->sequence].entry_point) {
set_position(ctx, -1, -1);
} else if (p->pos > m->seq_data[p->sequence].entry_point) {
set_position(ctx, p->pos - 1, -1);
}
return p->pos < 0 ? 0 : p->pos;
}
int xmp_set_position(xmp_context opaque, int pos)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
if (ctx->state < XMP_STATE_PLAYING)
return -XMP_ERROR_STATE;
if (pos >= m->mod.len)
return -XMP_ERROR_INVALID;
set_position(ctx, pos, 0);
return p->pos;
}
int xmp_set_row(xmp_context opaque, int row)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
struct flow_control *f = &p->flow;
int pos = p->pos;
int pattern = mod->xxo[pos];
if (pos < 0 || pos >= mod->len) {
pos = 0;
}
if (ctx->state < XMP_STATE_PLAYING)
return -XMP_ERROR_STATE;
if (row >= mod->xxp[pattern]->rows)
return -XMP_ERROR_INVALID;
/* See set_position. */
if (p->pos < 0)
p->pos = 0;
p->ord = p->pos;
p->row = row;
p->frame = -1;
f->num_rows = mod->xxp[mod->xxo[p->ord]]->rows;
return row;
}
void xmp_stop_module(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
if (ctx->state < XMP_STATE_PLAYING)
return;
p->pos = -2;
}
void xmp_restart_module(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
if (ctx->state < XMP_STATE_PLAYING)
return;
p->loop_count = 0;
p->pos = -1;
}
int xmp_seek_time(xmp_context opaque, int time)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
int i, t;
if (ctx->state < XMP_STATE_PLAYING)
return -XMP_ERROR_STATE;
for (i = m->mod.len - 1; i >= 0; i--) {
int pat = m->mod.xxo[i];
if (pat >= m->mod.pat) {
continue;
}
if (libxmp_get_sequence(ctx, i) != p->sequence) {
continue;
}
t = m->xxo_info[i].time;
if (time >= t) {
set_position(ctx, i, 1);
break;
}
}
if (i < 0) {
xmp_set_position(opaque, 0);
}
return p->pos < 0 ? 0 : p->pos;
}
int xmp_channel_mute(xmp_context opaque, int chn, int status)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
int ret;
if (ctx->state < XMP_STATE_PLAYING)
return -XMP_ERROR_STATE;
if (chn < 0 || chn >= XMP_MAX_CHANNELS) {
return -XMP_ERROR_INVALID;
}
ret = p->channel_mute[chn];
if (status >= 2) {
p->channel_mute[chn] = !p->channel_mute[chn];
} else if (status >= 0) {
p->channel_mute[chn] = status;
}
return ret;
}
int xmp_channel_vol(xmp_context opaque, int chn, int vol)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
int ret;
if (ctx->state < XMP_STATE_PLAYING)
return -XMP_ERROR_STATE;
if (chn < 0 || chn >= XMP_MAX_CHANNELS) {
return -XMP_ERROR_INVALID;
}
ret = p->channel_vol[chn];
if (vol >= 0 && vol <= 100) {
p->channel_vol[chn] = vol;
}
return ret;
}
#ifdef USE_VERSIONED_SYMBOLS
LIBXMP_EXPORT extern int xmp_set_player_v40__(xmp_context, int, int);
LIBXMP_EXPORT extern int xmp_set_player_v41__(xmp_context, int, int)
__attribute__((alias("xmp_set_player_v40__")));
LIBXMP_EXPORT extern int xmp_set_player_v43__(xmp_context, int, int)
__attribute__((alias("xmp_set_player_v40__")));
LIBXMP_EXPORT extern int xmp_set_player_v44__(xmp_context, int, int)
__attribute__((alias("xmp_set_player_v40__")));
asm(".symver xmp_set_player_v40__, xmp_set_player@XMP_4.0");
asm(".symver xmp_set_player_v41__, xmp_set_player@XMP_4.1");
asm(".symver xmp_set_player_v43__, xmp_set_player@XMP_4.3");
asm(".symver xmp_set_player_v44__, xmp_set_player@@XMP_4.4");
#define xmp_set_player__ xmp_set_player_v40__
#else
#define xmp_set_player__ xmp_set_player
#endif
int xmp_set_player__(xmp_context opaque, int parm, int val)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct mixer_data *s = &ctx->s;
int ret = -XMP_ERROR_INVALID;
if (parm == XMP_PLAYER_SMPCTL || parm == XMP_PLAYER_DEFPAN) {
/* these should be set before loading the module */
if (ctx->state >= XMP_STATE_LOADED) {
return -XMP_ERROR_STATE;
}
} else if (parm == XMP_PLAYER_VOICES) {
/* these should be set before start playing */
if (ctx->state >= XMP_STATE_PLAYING) {
return -XMP_ERROR_STATE;
}
} else if (ctx->state < XMP_STATE_PLAYING) {
return -XMP_ERROR_STATE;
}
switch (parm) {
case XMP_PLAYER_AMP:
if (val >= 0 && val <= 3) {
s->amplify = val;
ret = 0;
}
break;
case XMP_PLAYER_MIX:
if (val >= -100 && val <= 100) {
s->mix = val;
ret = 0;
}
break;
case XMP_PLAYER_INTERP:
if (val >= XMP_INTERP_NEAREST && val <= XMP_INTERP_SPLINE) {
s->interp = val;
ret = 0;
}
break;
case XMP_PLAYER_DSP:
s->dsp = val;
ret = 0;
break;
case XMP_PLAYER_FLAGS: {
p->player_flags = val;
ret = 0;
break; }
/* 4.1 */
case XMP_PLAYER_CFLAGS: {
int vblank = p->flags & XMP_FLAGS_VBLANK;
p->flags = val;
if (vblank != (p->flags & XMP_FLAGS_VBLANK))
libxmp_scan_sequences(ctx);
ret = 0;
break; }
case XMP_PLAYER_SMPCTL:
m->smpctl = val;
ret = 0;
break;
case XMP_PLAYER_VOLUME:
if (val >= 0 && val <= 200) {
p->master_vol = val;
ret = 0;
}
break;
case XMP_PLAYER_SMIX_VOLUME:
if (val >= 0 && val <= 200) {
p->smix_vol = val;
ret = 0;
}
break;
/* 4.3 */
case XMP_PLAYER_DEFPAN:
if (val >= 0 && val <= 100) {
m->defpan = val;
ret = 0;
}
break;
/* 4.4 */
case XMP_PLAYER_MODE:
p->mode = val;
libxmp_set_player_mode(ctx);
libxmp_scan_sequences(ctx);
ret = 0;
break;
case XMP_PLAYER_VOICES:
s->numvoc = val;
break;
}
return ret;
}
#ifdef USE_VERSIONED_SYMBOLS
LIBXMP_EXPORT extern int xmp_get_player_v40__(xmp_context, int);
LIBXMP_EXPORT extern int xmp_get_player_v41__(xmp_context, int)
__attribute__((alias("xmp_get_player_v40__")));
LIBXMP_EXPORT extern int xmp_get_player_v42__(xmp_context, int)
__attribute__((alias("xmp_get_player_v40__")));
LIBXMP_EXPORT extern int xmp_get_player_v43__(xmp_context, int)
__attribute__((alias("xmp_get_player_v40__")));
LIBXMP_EXPORT extern int xmp_get_player_v44__(xmp_context, int)
__attribute__((alias("xmp_get_player_v40__")));
asm(".symver xmp_get_player_v40__, xmp_get_player@XMP_4.0");
asm(".symver xmp_get_player_v41__, xmp_get_player@XMP_4.1");
asm(".symver xmp_get_player_v42__, xmp_get_player@XMP_4.2");
asm(".symver xmp_get_player_v43__, xmp_get_player@XMP_4.3");
asm(".symver xmp_get_player_v44__, xmp_get_player@@XMP_4.4");
#define xmp_get_player__ xmp_get_player_v40__
#else
#define xmp_get_player__ xmp_get_player
#endif
int xmp_get_player__(xmp_context opaque, int parm)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct mixer_data *s = &ctx->s;
int ret = -XMP_ERROR_INVALID;
if (parm == XMP_PLAYER_SMPCTL || parm == XMP_PLAYER_DEFPAN) {
// can read these at any time
} else if (parm != XMP_PLAYER_STATE && ctx->state < XMP_STATE_PLAYING) {
return -XMP_ERROR_STATE;
}
switch (parm) {
case XMP_PLAYER_AMP:
ret = s->amplify;
break;
case XMP_PLAYER_MIX:
ret = s->mix;
break;
case XMP_PLAYER_INTERP:
ret = s->interp;
break;
case XMP_PLAYER_DSP:
ret = s->dsp;
break;
case XMP_PLAYER_FLAGS:
ret = p->player_flags;
break;
/* 4.1 */
case XMP_PLAYER_CFLAGS:
ret = p->flags;
break;
case XMP_PLAYER_SMPCTL:
ret = m->smpctl;
break;
case XMP_PLAYER_VOLUME:
ret = p->master_vol;
break;
case XMP_PLAYER_SMIX_VOLUME:
ret = p->smix_vol;
break;
/* 4.2 */
case XMP_PLAYER_STATE:
ret = ctx->state;
break;
/* 4.3 */
case XMP_PLAYER_DEFPAN:
ret = m->defpan;
break;
/* 4.4 */
case XMP_PLAYER_MODE:
ret = p->mode;
break;
case XMP_PLAYER_MIXER_TYPE:
ret = XMP_MIXER_STANDARD;
if (p->flags & XMP_FLAGS_A500) {
if (IS_AMIGA_MOD()) {
#ifdef LIBXMP_PAULA_SIMULATOR
if (p->filter) {
ret = XMP_MIXER_A500F;
} else {
ret = XMP_MIXER_A500;
}
#endif
}
}
break;
case XMP_PLAYER_VOICES:
ret = s->numvoc;
break;
}
return ret;
}
const char *const *xmp_get_format_list(void)
{
return format_list();
}
void xmp_inject_event(xmp_context opaque, int channel, struct xmp_event *e)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
if (ctx->state < XMP_STATE_PLAYING)
return;
memcpy(&p->inject_event[channel], e, sizeof(struct xmp_event));
p->inject_event[channel]._flag = 1;
}
int xmp_set_instrument_path(xmp_context opaque, const char *path)
{
struct context_data *ctx = (struct context_data *)opaque;
struct module_data *m = &ctx->m;
if (m->instrument_path != NULL)
free(m->instrument_path);
m->instrument_path = strdup(path);
if (m->instrument_path == NULL) {
return -XMP_ERROR_SYSTEM;
}
return 0;
}
int xmp_set_tempo_factor(xmp_context opaque, double val)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct mixer_data *s = &ctx->s;
int ticksize;
if (val <= 0.0) {
return -1;
}
val *= 10;
ticksize = s->freq * val * m->rrate / p->bpm / 1000 * sizeof(int);
if (ticksize > XMP_MAX_FRAMESIZE) {
return -1;
}
m->time_factor = val;
return 0;
}

View file

@ -0,0 +1,269 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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 <errno.h>
#include "common.h"
#define read_byte(x) do { \
(x) = fgetc(f); \
if ((x) < 0) goto error; \
} while (0)
#define set_error(x) do { \
if (err != NULL) *err = (x); \
} while (0)
uint8 read8(FILE *f, int *err)
{
int a;
read_byte(a);
set_error(0);
return a;
error:
set_error(ferror(f) ? errno : EOF);
return 0xff;
}
int8 read8s(FILE *f, int *err)
{
int a;
read_byte(a);
set_error(0);
return (int8)a;
error:
set_error(ferror(f) ? errno : EOF);
return 0;
}
uint16 read16l(FILE *f, int *err)
{
int a, b;
read_byte(a);
read_byte(b);
set_error(0);
return ((uint16)b << 8) | a;
error:
set_error(ferror(f) ? errno : EOF);
return 0xffff;
}
uint16 read16b(FILE *f, int *err)
{
int a, b;
read_byte(a);
read_byte(b);
set_error(0);
return (a << 8) | b;
error:
set_error(ferror(f) ? errno : EOF);
return 0xffff;
}
uint32 read24l(FILE *f, int *err)
{
int a, b, c;
read_byte(a);
read_byte(b);
read_byte(c);
set_error(0);
return (c << 16) | (b << 8) | a;
error:
set_error(ferror(f) ? errno : EOF);
return 0xffffff;
}
uint32 read24b(FILE *f, int *err)
{
int a, b, c;
read_byte(a);
read_byte(b);
read_byte(c);
set_error(0);
return (a << 16) | (b << 8) | c;
error:
set_error(ferror(f) ? errno : EOF);
return 0xffffff;
}
uint32 read32l(FILE *f, int *err)
{
int a, b, c, d;
read_byte(a);
read_byte(b);
read_byte(c);
read_byte(d);
set_error(0);
return (d << 24) | (c << 16) | (b << 8) | a;
error:
set_error(ferror(f) ? errno : EOF);
return 0xffffffff;
}
uint32 read32b(FILE *f, int *err)
{
int a, b, c, d;
read_byte(a);
read_byte(b);
read_byte(c);
read_byte(d);
set_error(0);
return (a << 24) | (b << 16) | (c << 8) | d;
error:
set_error(ferror(f) ? errno : EOF);
return 0xffffffff;
}
uint16 readmem16l(const uint8 *m)
{
uint32 a, b;
a = m[0];
b = m[1];
return (b << 8) | a;
}
uint16 readmem16b(const uint8 *m)
{
uint32 a, b;
a = m[0];
b = m[1];
return (a << 8) | b;
}
uint32 readmem24l(const uint8 *m)
{
uint32 a, b, c;
a = m[0];
b = m[1];
c = m[2];
return (c << 16) | (b << 8) | a;
}
uint32 readmem24b(const uint8 *m)
{
uint32 a, b, c;
a = m[0];
b = m[1];
c = m[2];
return (a << 16) | (b << 8) | c;
}
uint32 readmem32l(const uint8 *m)
{
uint32 a, b, c, d;
a = m[0];
b = m[1];
c = m[2];
d = m[3];
return (d << 24) | (c << 16) | (b << 8) | a;
}
uint32 readmem32b(const uint8 *m)
{
uint32 a, b, c, d;
a = m[0];
b = m[1];
c = m[2];
d = m[3];
return (a << 24) | (b << 16) | (c << 8) | d;
}
#ifndef LIBXMP_CORE_PLAYER
void write16l(FILE *f, uint16 w)
{
write8(f, w & 0x00ff);
write8(f, (w & 0xff00) >> 8);
}
void write16b(FILE *f, uint16 w)
{
write8(f, (w & 0xff00) >> 8);
write8(f, w & 0x00ff);
}
void write32l(FILE *f, uint32 w)
{
write8(f, w & 0x000000ff);
write8(f, (w & 0x0000ff00) >> 8);
write8(f, (w & 0x00ff0000) >> 16);
write8(f, (w & 0xff000000) >> 24);
}
void write32b(FILE *f, uint32 w)
{
write8(f, (w & 0xff000000) >> 24);
write8(f, (w & 0x00ff0000) >> 16);
write8(f, (w & 0x0000ff00) >> 8);
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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,143 @@
#ifndef LIBXMP_EFFECTS_H
#define LIBXMP_EFFECTS_H
/* Protracker effects */
#define FX_ARPEGGIO 0x00
#define FX_PORTA_UP 0x01
#define FX_PORTA_DN 0x02
#define FX_TONEPORTA 0x03
#define FX_VIBRATO 0x04
#define FX_TONE_VSLIDE 0x05
#define FX_VIBRA_VSLIDE 0x06
#define FX_TREMOLO 0x07
#define FX_OFFSET 0x09
#define FX_VOLSLIDE 0x0a
#define FX_JUMP 0x0b
#define FX_VOLSET 0x0c
#define FX_BREAK 0x0d
#define FX_EXTENDED 0x0e
#define FX_SPEED 0x0f
/* Fast tracker effects */
#define FX_SETPAN 0x08
/* Fast Tracker II effects */
#define FX_GLOBALVOL 0x10
#define FX_GVOL_SLIDE 0x11
#define FX_KEYOFF 0x14
#define FX_ENVPOS 0x15
#define FX_PANSLIDE 0x19
#define FX_MULTI_RETRIG 0x1b
#define FX_TREMOR 0x1d
#define FX_XF_PORTA 0x21
/* Protracker extended effects */
#define EX_FILTER 0x00
#define EX_F_PORTA_UP 0x01
#define EX_F_PORTA_DN 0x02
#define EX_GLISS 0x03
#define EX_VIBRATO_WF 0x04
#define EX_FINETUNE 0x05
#define EX_PATTERN_LOOP 0x06
#define EX_TREMOLO_WF 0x07
#define EX_SETPAN 0x08
#define EX_RETRIG 0x09
#define EX_F_VSLIDE_UP 0x0a
#define EX_F_VSLIDE_DN 0x0b
#define EX_CUT 0x0c
#define EX_DELAY 0x0d
#define EX_PATT_DELAY 0x0e
#define EX_INVLOOP 0x0f
#ifndef LIBXMP_CORE_PLAYER
/* Oktalyzer effects */
#define FX_OKT_ARP3 0x70
#define FX_OKT_ARP4 0x71
#define FX_OKT_ARP5 0x72
#define FX_NSLIDE2_DN 0x73
#define FX_NSLIDE2_UP 0x74
#define FX_F_NSLIDE_DN 0x75
#define FX_F_NSLIDE_UP 0x76
/* Persistent effects -- for FNK and FAR */
#define FX_PER_PORTA_DN 0x78
#define FX_PER_PORTA_UP 0x79
#define FX_PER_TPORTA 0x7a
#define FX_PER_VIBRATO 0x7b
#define FX_PER_VSLD_UP 0x7c
#define FX_PER_VSLD_DN 0x7d
#define FX_SPEED_CP 0x7e
#define FX_PER_CANCEL 0x7f
/* 669 frequency based effects */
#define FX_669_PORTA_UP 0x60
#define FX_669_PORTA_DN 0x61
#define FX_669_TPORTA 0x62
#define FX_669_FINETUNE 0x63
#define FX_669_VIBRATO 0x64
#endif
#ifndef LIBXMP_CORE_DISABLE_IT
/* IT effects */
#define FX_TRK_VOL 0x80
#define FX_TRK_VSLIDE 0x81
#define FX_TRK_FVSLIDE 0x82
#define FX_IT_INSTFUNC 0x83
#define FX_FLT_CUTOFF 0x84
#define FX_FLT_RESN 0x85
#define FX_IT_BPM 0x87
#define FX_IT_ROWDELAY 0x88
#define FX_IT_PANSLIDE 0x89
#define FX_PANBRELLO 0x8a
#define FX_PANBRELLO_WF 0x8b
#define FX_HIOFFSET 0x8c
#define FX_IT_BREAK 0x8e /* like FX_BREAK with hex parameter */
#endif
#ifndef LIBXMP_CORE_PLAYER
/* MED effects */
#define FX_HOLD_DECAY 0x90
#define FX_SETPITCH 0x91
#define FX_VIBRATO2 0x92
/* PTM effects */
#define FX_NSLIDE_DN 0x9c /* IMF/PTM note slide down */
#define FX_NSLIDE_UP 0x9d /* IMF/PTM note slide up */
#define FX_NSLIDE_R_UP 0x9e /* PTM note slide down with retrigger */
#define FX_NSLIDE_R_DN 0x9f /* PTM note slide up with retrigger */
/* Extra effects */
#define FX_VOLSLIDE_UP 0xa0 /* SFX, MDL */
#define FX_VOLSLIDE_DN 0xa1
#define FX_F_VSLIDE 0xa5 /* IMF/MDL */
#define FX_CHORUS 0xa9 /* IMF */
#define FX_ICE_SPEED 0xa2
#define FX_REVERB 0xaa /* IMF */
#define FX_MED_HOLD 0xb1 /* MMD hold/decay */
#define FX_MEGAARP 0xb2 /* Smaksak effect 7: MegaArp */
#define FX_VOL_ADD 0xb6 /* SFX change volume up */
#define FX_VOL_SUB 0xb7 /* SFX change volume down */
#define FX_PITCH_ADD 0xb8 /* SFX add steps to current note */
#define FX_PITCH_SUB 0xb9 /* SFX add steps to current note */
#endif
#define FX_SURROUND 0x8d /* S3M/IT */
#define FX_S3M_SPEED 0xa3 /* S3M */
#define FX_VOLSLIDE_2 0xa4
#define FX_FINETUNE 0xa6
#define FX_S3M_BPM 0xab /* S3M */
#define FX_FINE_VIBRATO 0xac /* S3M/PTM/IMF/LIQ */
#define FX_F_VSLIDE_UP 0xad /* MMD */
#define FX_F_VSLIDE_DN 0xae /* MMD */
#define FX_F_PORTA_UP 0xaf /* MMD */
#define FX_F_PORTA_DN 0xb0 /* MMD */
#define FX_PATT_DELAY 0xb3 /* MMD */
#define FX_S3M_ARPEGGIO 0xb4
#define FX_PANSL_NOMEM 0xb5 /* XM volume column */
#define FX_VSLIDE_UP_2 0xc0 /* IT volume column volume slide */
#define FX_VSLIDE_DN_2 0xc1
#define FX_F_VSLIDE_UP_2 0xc2
#define FX_F_VSLIDE_DN_2 0xc3
#endif /* LIBXMP_EFFECTS_H */

View file

@ -0,0 +1,107 @@
/*
* Based on the public domain version by Olivier Lapicque
* Rewritten for libxmp by Claudio Matsuoka
*
* Copyright (C) 2012 Claudio Matsuoka
*
* 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.
*/
#ifndef LIBXMP_CORE_DISABLE_IT
#include <math.h>
#include "xmp.h"
#include "common.h"
#include "mixer.h"
/* LUT for 2 * damping factor */
static const float resonance_table[128] = {
1.0000000000000000f, 0.9786446094512940f, 0.9577452540397644f, 0.9372922182083130f,
0.9172759056091309f, 0.8976871371269226f, 0.8785166740417481f, 0.8597555756568909f,
0.8413951396942139f, 0.8234267830848694f, 0.8058421611785889f, 0.7886331081390381f,
0.7717915177345276f, 0.7553095817565918f, 0.7391796708106995f, 0.7233941555023193f,
0.7079457640647888f, 0.6928272843360901f, 0.6780316829681397f, 0.6635520458221436f,
0.6493816375732422f, 0.6355138421058655f, 0.6219421625137329f, 0.6086603403091431f,
0.5956621170043945f, 0.5829415321350098f, 0.5704925656318665f, 0.5583094954490662f,
0.5463865399360657f, 0.5347182154655457f, 0.5232990980148315f, 0.5121238231658936f,
0.5011872053146362f, 0.4904841780662537f, 0.4800096750259399f, 0.4697588682174683f,
0.4597269892692566f, 0.4499093294143677f, 0.4403013288974762f, 0.4308985173702240f,
0.4216965138912201f, 0.4126909971237183f, 0.4038778245449066f, 0.3952528536319733f,
0.3868120610713959f, 0.3785515129566193f, 0.3704673945903778f, 0.3625559210777283f,
0.3548133969306946f, 0.3472362160682678f, 0.3398208320140839f, 0.3325638175010681f,
0.3254617750644684f, 0.3185114264488220f, 0.3117094635963440f, 0.3050527870655060f,
0.2985382676124573f, 0.2921628654003143f, 0.2859236001968384f, 0.2798175811767578f,
0.2738419771194458f, 0.2679939568042755f, 0.2622708380222321f, 0.2566699385643005f,
0.2511886358261108f, 0.2458244115114212f, 0.2405747324228287f, 0.2354371547698975f,
0.2304092943668366f, 0.2254888117313385f, 0.2206734120845795f, 0.2159608304500580f,
0.2113489061594009f, 0.2068354636430740f, 0.2024184018373489f, 0.1980956792831421f,
0.1938652694225311f, 0.1897251904010773f, 0.1856735348701477f, 0.1817083954811096f,
0.1778279393911362f, 0.1740303486585617f, 0.1703138649463654f, 0.1666767448186874f,
0.1631172895431519f, 0.1596338599920273f, 0.1562248021364212f, 0.1528885662555695f,
0.1496235728263855f, 0.1464282870292664f, 0.1433012634515762f, 0.1402409970760346f,
0.1372461020946503f, 0.1343151479959488f, 0.1314467936754227f, 0.1286396980285645f,
0.1258925348520279f, 0.1232040524482727f, 0.1205729842185974f, 0.1179980933666229f,
0.1154781952500343f, 0.1130121126770973f, 0.1105986908078194f, 0.1082368120551109f,
0.1059253737330437f, 0.1036632955074310f, 0.1014495193958283f, 0.0992830246686935f,
0.0971627980470657f, 0.0950878411531448f, 0.0930572077631950f, 0.0910699293017387f,
0.0891250967979431f, 0.0872217938303947f, 0.0853591337800026f, 0.0835362523794174f,
0.0817523002624512f, 0.0800064504146576f, 0.0782978758215904f, 0.0766257941722870f,
0.0749894231557846f, 0.0733879879117012f, 0.0718207582831383f, 0.0702869966626167f,
0.0687859877943993f, 0.0673170387744904f, 0.0658794566988945f, 0.0644725710153580f,
};
#if !defined(HAVE_POWF) || defined(__DJGPP__) || defined(__WATCOMC__)
#define powf pow /* Watcom doesn't have powf. DJGPP have a C-only implementation in libm. */
#endif
/*
* Simple 2-poles resonant filter
*/
#define FREQ_PARAM_MULT (128.0f / (24.0f * 256.0f))
void libxmp_filter_setup(int srate, int cutoff, int res, int *a0, int *b0, int *b1)
{
float fc, fs = (float)srate;
float fg, fb0, fb1;
float r, d, e;
/* [0-255] => [100Hz-8000Hz] */
CLAMP(cutoff, 0, 255);
CLAMP(res, 0, 255);
fc = 110.0f * powf(2.0f, (float)cutoff * FREQ_PARAM_MULT + 0.25f);
if (fc > fs / 2.0f) {
fc = fs / 2.0f;
}
r = fs / (2.0 * 3.14159265358979f * fc);
d = resonance_table[res >> 1] * (r + 1.0) - 1.0;
e = r * r;
fg = 1.0 / (1.0 + d + e);
fb0 = (d + e + e) / (1.0 + d + e);
fb1 = -e / (1.0 + d + e);
*a0 = (int)(fg * (1 << FILTER_SHIFT));
*b0 = (int)(fb0 * (1 << FILTER_SHIFT));
*b1 = (int)(fb1 * (1 << FILTER_SHIFT));
}
#endif

View file

@ -0,0 +1,55 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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 "format.h"
extern const struct format_loader libxmp_loader_xm;
extern const struct format_loader libxmp_loader_mod;
extern const struct format_loader libxmp_loader_it;
extern const struct format_loader libxmp_loader_s3m;
const struct format_loader *const format_loaders[5] = {
&libxmp_loader_xm,
&libxmp_loader_mod,
#ifndef LIBXMP_CORE_DISABLE_IT
&libxmp_loader_it,
#endif
&libxmp_loader_s3m,
NULL
};
static const char *_farray[5] = { NULL };
const char *const *format_list(void)
{
int count, i;
if (_farray[0] == NULL) {
for (count = i = 0; format_loaders[i] != NULL; i++) {
_farray[count++] = format_loaders[i]->name;
}
_farray[count] = NULL;
}
return _farray;
}

View file

@ -0,0 +1,26 @@
#ifndef LIBXMP_FORMAT_H
#define LIBXMP_FORMAT_H
#include "common.h"
#include "hio.h"
struct format_loader {
const char *name;
int (*const test)(HIO_HANDLE *, char *, const int);
int (*const loader)(struct module_data *, HIO_HANDLE *, const int);
};
const char *const *format_list(void);
#ifndef LIBXMP_CORE_PLAYER
#define NUM_FORMATS 53
#define NUM_PW_FORMATS 43
#ifndef LIBXMP_NO_PROWIZARD
int pw_test_format(HIO_HANDLE *, char *, const int, struct xmp_test_info *);
#endif
#endif
#endif

View file

@ -0,0 +1,479 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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 <errno.h>
#include "common.h"
#include "hio.h"
#include "callbackio.h"
#include "mdataio.h"
static long get_size(FILE *f)
{
long size, pos;
pos = ftell(f);
if (pos >= 0) {
if (fseek(f, 0, SEEK_END) < 0) {
return -1;
}
size = ftell(f);
if (fseek(f, pos, SEEK_SET) < 0) {
return -1;
}
return size;
} else {
return pos;
}
}
int8 hio_read8s(HIO_HANDLE *h)
{
int err;
int8 ret;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read8s(h->handle.file, &err);
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread8s(h->handle.mem, &err);
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbread8s(h->handle.cbfile, &err);
break;
default:
return 0;
}
if (err != 0) {
h->error = err;
}
return ret;
}
uint8 hio_read8(HIO_HANDLE *h)
{
int err;
uint8 ret;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read8(h->handle.file, &err);
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread8(h->handle.mem, &err);
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbread8(h->handle.cbfile, &err);
break;
default:
return 0;
}
if (err != 0) {
h->error = err;
}
return ret;
}
uint16 hio_read16l(HIO_HANDLE *h)
{
int err;
uint16 ret;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read16l(h->handle.file, &err);
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread16l(h->handle.mem, &err);
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbread16l(h->handle.cbfile, &err);
break;
default:
return 0;
}
if (err != 0) {
h->error = err;
}
return ret;
}
uint16 hio_read16b(HIO_HANDLE *h)
{
int err;
uint16 ret;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read16b(h->handle.file, &err);
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread16b(h->handle.mem, &err);
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbread16b(h->handle.cbfile, &err);
break;
default:
return 0;
}
if (err != 0) {
h->error = err;
}
return ret;
}
uint32 hio_read24l(HIO_HANDLE *h)
{
int err;
uint32 ret;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read24l(h->handle.file, &err);
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread24l(h->handle.mem, &err);
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbread24l(h->handle.cbfile, &err);
break;
default:
return 0;
}
if (err != 0) {
h->error = err;
}
return ret;
}
uint32 hio_read24b(HIO_HANDLE *h)
{
int err;
uint32 ret;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read24b(h->handle.file, &err);
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread24b(h->handle.mem, &err);
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbread24b(h->handle.cbfile, &err);
break;
default:
return 0;
}
if (err != 0) {
h->error = err;
}
return ret;
}
uint32 hio_read32l(HIO_HANDLE *h)
{
int err;
uint32 ret;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read32l(h->handle.file, &err);
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread32l(h->handle.mem, &err);
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbread32l(h->handle.cbfile, &err);
break;
default:
return 0;
}
if (err != 0) {
h->error = err;
}
return ret;
}
uint32 hio_read32b(HIO_HANDLE *h)
{
int err;
uint32 ret;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read32b(h->handle.file, &err);
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread32b(h->handle.mem, &err);
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbread32b(h->handle.cbfile, &err);
break;
default:
return 0;
}
if (err != 0) {
h->error = err;
}
return ret;
}
size_t hio_read(void *buf, size_t size, size_t num, HIO_HANDLE *h)
{
size_t ret = 0;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = fread(buf, size, num, h->handle.file);
if (ret != num) {
if (ferror(h->handle.file)) {
h->error = errno;
} else {
h->error = feof(h->handle.file) ? EOF : -2;
}
}
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread(buf, size, num, h->handle.mem);
if (ret != num) {
h->error = EOF;
}
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbread(buf, size, num, h->handle.cbfile);
if (ret != num) {
h->error = EOF;
}
break;
}
return ret;
}
int hio_seek(HIO_HANDLE *h, long offset, int whence)
{
int ret = -1;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = fseek(h->handle.file, offset, whence);
if (ret < 0) {
h->error = errno;
}
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mseek(h->handle.mem, offset, whence);
if (ret < 0) {
h->error = EINVAL;
}
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbseek(h->handle.cbfile, offset, whence);
if (ret < 0) {
h->error = EINVAL;
}
break;
}
return ret;
}
long hio_tell(HIO_HANDLE *h)
{
long ret = -1;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = ftell(h->handle.file);
if (ret < 0) {
h->error = errno;
}
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mtell(h->handle.mem);
if (ret < 0) {
/* should _not_ happen! */
h->error = EINVAL;
}
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbtell(h->handle.cbfile);
if (ret < 0) {
h->error = EINVAL;
}
break;
}
return ret;
}
int hio_eof(HIO_HANDLE *h)
{
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
return feof(h->handle.file);
case HIO_HANDLE_TYPE_MEMORY:
return meof(h->handle.mem);
case HIO_HANDLE_TYPE_CBFILE:
return cbeof(h->handle.cbfile);
}
return EOF;
}
int hio_error(HIO_HANDLE *h)
{
int error = h->error;
h->error = 0;
return error;
}
HIO_HANDLE *hio_open(const char *path, const char *mode)
{
HIO_HANDLE *h;
h = (HIO_HANDLE *)calloc(1, sizeof (HIO_HANDLE));
if (h == NULL)
goto err;
h->type = HIO_HANDLE_TYPE_FILE;
h->handle.file = fopen(path, mode);
if (h->handle.file == NULL)
goto err2;
h->size = get_size(h->handle.file);
if (h->size < 0)
goto err3;
return h;
err3:
fclose(h->handle.file);
err2:
free(h);
err:
return NULL;
}
HIO_HANDLE *hio_open_mem(const void *ptr, long size)
{
HIO_HANDLE *h;
if (size <= 0) return NULL;
h = (HIO_HANDLE *)calloc(1, sizeof (HIO_HANDLE));
if (h == NULL)
return NULL;
h->type = HIO_HANDLE_TYPE_MEMORY;
h->handle.mem = mopen(ptr, size);
h->size = size;
return h;
}
HIO_HANDLE *hio_open_file(FILE *f)
{
HIO_HANDLE *h;
h = (HIO_HANDLE *)calloc(1, sizeof (HIO_HANDLE));
if (h == NULL)
return NULL;
h->noclose = 1;
h->type = HIO_HANDLE_TYPE_FILE;
h->handle.file = f;
h->size = get_size(f);
if (h->size < 0) {
free(h);
return NULL;
}
return h;
}
HIO_HANDLE *hio_open_file2(FILE *f)
{
HIO_HANDLE *h = hio_open_file(f);
if (h != NULL) {
h->noclose = 0;
}
else {
fclose(f);
}
return h;
}
HIO_HANDLE *hio_open_callbacks(void *priv, struct xmp_callbacks callbacks)
{
HIO_HANDLE *h;
CBFILE *f = cbopen(priv, callbacks);
if (!f)
return NULL;
h = (HIO_HANDLE *)calloc(1, sizeof(HIO_HANDLE));
if (h == NULL) {
cbclose(f);
return NULL;
}
h->type = HIO_HANDLE_TYPE_CBFILE;
h->handle.cbfile = f;
h->size = cbfilelength(f);
if (h->size < 0) {
cbclose(f);
free(h);
return NULL;
}
return h;
}
int hio_close(HIO_HANDLE *h)
{
int ret = -1;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = (h->noclose)? 0 : fclose(h->handle.file);
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mclose(h->handle.mem);
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbclose(h->handle.cbfile);
break;
}
free(h);
return ret;
}
long hio_size(HIO_HANDLE *h)
{
return h->size;
}

View file

@ -0,0 +1,48 @@
#ifndef XMP_HIO_H
#define XMP_HIO_H
#include "callbackio.h"
#include "memio.h"
#define HIO_HANDLE_TYPE(x) ((x)->type)
enum hio_type {
HIO_HANDLE_TYPE_FILE,
HIO_HANDLE_TYPE_MEMORY,
HIO_HANDLE_TYPE_CBFILE
};
typedef struct {
enum hio_type type;
long size;
union {
FILE *file;
MFILE *mem;
CBFILE *cbfile;
} handle;
int error;
int noclose;
} HIO_HANDLE;
int8 hio_read8s (HIO_HANDLE *);
uint8 hio_read8 (HIO_HANDLE *);
uint16 hio_read16l (HIO_HANDLE *);
uint16 hio_read16b (HIO_HANDLE *);
uint32 hio_read24l (HIO_HANDLE *);
uint32 hio_read24b (HIO_HANDLE *);
uint32 hio_read32l (HIO_HANDLE *);
uint32 hio_read32b (HIO_HANDLE *);
size_t hio_read (void *, size_t, size_t, HIO_HANDLE *);
int hio_seek (HIO_HANDLE *, long, int);
long hio_tell (HIO_HANDLE *);
int hio_eof (HIO_HANDLE *);
int hio_error (HIO_HANDLE *);
HIO_HANDLE *hio_open (const char *, const char *);
HIO_HANDLE *hio_open_mem (const void *, long);
HIO_HANDLE *hio_open_file (FILE *);
HIO_HANDLE *hio_open_file2 (FILE *);/* allows fclose()ing the file by libxmp */
HIO_HANDLE *hio_open_callbacks (void *, struct xmp_callbacks);
int hio_close (HIO_HANDLE *);
long hio_size (HIO_HANDLE *);
#endif

View file

@ -0,0 +1,181 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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.
*/
/* IT flags */
#define IT_STEREO 0x01
#define IT_VOL_OPT 0x02 /* Not recognized */
#define IT_USE_INST 0x04
#define IT_LINEAR_FREQ 0x08
#define IT_OLD_FX 0x10
#define IT_LINK_GXX 0x20
/* IT special */
#define IT_HAS_MSG 0x01
/* IT instrument flags */
#define IT_INST_SAMPLE 0x01
#define IT_INST_16BIT 0x02
#define IT_INST_STEREO 0x04
#define IT_INST_LOOP 0x10
#define IT_INST_SLOOP 0x20
#define IT_INST_BLOOP 0x40
#define IT_INST_BSLOOP 0x80
/* IT sample flags */
#define IT_SMP_SAMPLE 0x01
#define IT_SMP_16BIT 0x02
#define IT_SMP_STEREO 0x04 /* unsupported */
#define IT_SMP_COMP 0x08 /* unsupported */
#define IT_SMP_LOOP 0x10
#define IT_SMP_SLOOP 0x20
#define IT_SMP_BLOOP 0x40
#define IT_SMP_BSLOOP 0x80
/* IT sample conversion flags */
#define IT_CVT_SIGNED 0x01
#define IT_CVT_BIGEND 0x02 /* 'safe to ignore' according to ittech.txt */
#define IT_CVT_DIFF 0x04 /* Compressed sample flag */
#define IT_CVT_BYTEDIFF 0x08 /* 'safe to ignore' according to ittech.txt */
#define IT_CVT_12BIT 0x10 /* 'safe to ignore' according to ittech.txt */
/* IT envelope flags */
#define IT_ENV_ON 0x01
#define IT_ENV_LOOP 0x02
#define IT_ENV_SLOOP 0x04
#define IT_ENV_CARRY 0x08
#define IT_ENV_FILTER 0x80
struct it_file_header {
uint32 magic; /* 'IMPM' */
uint8 name[26]; /* ASCIIZ Song name */
uint8 hilite_min; /* Pattern editor highlight */
uint8 hilite_maj; /* Pattern editor highlight */
uint16 ordnum; /* Number of orders (must be even) */
uint16 insnum; /* Number of instruments */
uint16 smpnum; /* Number of samples */
uint16 patnum; /* Number of patterns */
uint16 cwt; /* Tracker ID and version */
uint16 cmwt; /* Format version */
uint16 flags; /* Flags */
uint16 special; /* More flags */
uint8 gv; /* Global volume */
uint8 mv; /* Master volume */
uint8 is; /* Initial speed */
uint8 it; /* Initial tempo */
uint8 sep; /* Panning separation */
uint8 pwd; /* Pitch wheel depth */
uint16 msglen; /* Message length */
uint32 msgofs; /* Message offset */
uint32 rsvd; /* Reserved */
uint8 chpan[64]; /* Channel pan settings */
uint8 chvol[64]; /* Channel volume settings */
};
struct it_instrument1_header {
uint32 magic; /* 'IMPI' */
uint8 dosname[12]; /* DOS filename */
uint8 zero; /* Always zero */
uint8 flags; /* Instrument flags */
uint8 vls; /* Volume loop start */
uint8 vle; /* Volume loop end */
uint8 sls; /* Sustain loop start */
uint8 sle; /* Sustain loop end */
uint16 rsvd1; /* Reserved */
uint16 fadeout; /* Fadeout (release) */
uint8 nna; /* New note action */
uint8 dnc; /* Duplicate note check */
uint16 trkvers; /* Tracker version */
uint8 nos; /* Number of samples */
uint8 rsvd2; /* Reserved */
uint8 name[26]; /* ASCIIZ Instrument name */
uint8 rsvd3[6]; /* Reserved */
uint8 keys[240];
uint8 epoint[200];
uint8 enode[50];
};
struct it_instrument2_header {
uint32 magic; /* 'IMPI' */
uint8 dosname[12]; /* DOS filename */
uint8 zero; /* Always zero */
uint8 nna; /* New Note Action */
uint8 dct; /* Duplicate Check Type */
uint8 dca; /* Duplicate Check Action */
uint16 fadeout;
uint8 pps; /* Pitch-Pan Separation */
uint8 ppc; /* Pitch-Pan Center */
uint8 gbv; /* Global Volume */
uint8 dfp; /* Default pan */
uint8 rv; /* Random volume variation */
uint8 rp; /* Random pan variation */
uint16 trkvers; /* Not used: tracked version */
uint8 nos; /* Not used: number of samples */
uint8 rsvd1; /* Reserved */
uint8 name[26]; /* ASCIIZ Instrument name */
uint8 ifc; /* Initial filter cutoff */
uint8 ifr; /* Initial filter resonance */
uint8 mch; /* MIDI channel */
uint8 mpr; /* MIDI program */
uint16 mbnk; /* MIDI bank */
uint8 keys[240];
};
struct it_envelope_node {
int8 y;
uint16 x;
};
struct it_envelope {
uint8 flg; /* Flags */
uint8 num; /* Number of node points */
uint8 lpb; /* Loop beginning */
uint8 lpe; /* Loop end */
uint8 slb; /* Sustain loop beginning */
uint8 sle; /* Sustain loop end */
struct it_envelope_node node[25];
uint8 unused;
};
struct it_sample_header {
uint32 magic; /* 'IMPS' */
uint8 dosname[12]; /* DOS filename */
uint8 zero; /* Always zero */
uint8 gvl; /* Global volume for instrument */
uint8 flags; /* Sample flags */
uint8 vol; /* Volume */
uint8 name[26]; /* ASCIIZ sample name */
uint8 convert; /* Sample flags */
uint8 dfp; /* Default pan */
uint32 length; /* Length */
uint32 loopbeg; /* Loop begin */
uint32 loopend; /* Loop end */
uint32 c5spd; /* C 5 speed */
uint32 sloopbeg; /* SusLoop begin */
uint32 sloopend; /* SusLoop end */
uint32 sample_ptr; /* Sample pointer */
uint8 vis; /* Vibrato speed */
uint8 vid; /* Vibrato depth */
uint8 vir; /* Vibrato rate */
uint8 vit; /* Vibrato waveform */
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,232 @@
#ifndef LIBXMP_CORE_DISABLE_IT
/* Public domain IT sample decompressor by Olivier Lapicque */
#include "loader.h"
static inline uint32 read_bits(HIO_HANDLE *ibuf, uint32 *bitbuf, int *bitnum, int n)
{
uint32 retval = 0;
int i = n;
int bnum = *bitnum, bbuf = *bitbuf;
if (n > 0) {
do {
if (bnum == 0) {
bbuf = hio_read8(ibuf);
bnum = 8;
}
retval >>= 1;
retval |= bbuf << 31;
bbuf >>= 1;
bnum--;
i--;
} while (i != 0);
i = n;
*bitnum = bnum;
*bitbuf = bbuf;
}
return (retval >> (32 - i));
}
int itsex_decompress8(HIO_HANDLE *src, uint8 *dst, int len, int it215)
{
/* uint32 size = 0; */
uint32 block_count = 0;
uint32 bitbuf = 0;
int bitnum = 0;
uint8 left = 0, temp = 0, temp2 = 0;
uint32 d, pos;
while (len) {
if (!block_count) {
block_count = 0x8000;
/*size =*/ hio_read16l(src);
left = 9;
temp = temp2 = 0;
bitbuf = bitnum = 0;
}
d = block_count;
if (d > len)
d = len;
/* Unpacking */
pos = 0;
do {
uint16 bits = read_bits(src, &bitbuf, &bitnum, left);
if (hio_eof(src))
return -1;
if (left < 7) {
uint32 i = 1 << (left - 1);
uint32 j = bits & 0xffff;
if (i != j)
goto unpack_byte;
bits = (read_bits(src, &bitbuf, &bitnum, 3)
+ 1) & 0xff;
if (hio_eof(src))
return -1;
left = ((uint8)bits < left) ? (uint8)bits :
(uint8)((bits + 1) & 0xff);
goto next;
}
if (left < 9) {
uint16 i = (0xff >> (9 - left)) + 4;
uint16 j = i - 8;
if ((bits <= j) || (bits > i))
goto unpack_byte;
bits -= j;
left = ((uint8)(bits & 0xff) < left) ?
(uint8)(bits & 0xff) :
(uint8)((bits + 1) & 0xff);
goto next;
}
if (left >= 10)
goto skip_byte;
if (bits >= 256) {
left = (uint8) (bits + 1) & 0xff;
goto next;
}
unpack_byte:
if (left < 8) {
uint8 shift = 8 - left;
signed char c = (signed char)(bits << shift);
c >>= shift;
bits = (uint16) c;
}
bits += temp;
temp = (uint8)bits;
temp2 += temp;
dst[pos] = it215 ? temp2 : temp;
skip_byte:
pos++;
next:
/* if (slen <= 0)
return -1 */;
} while (pos < d);
/* Move On */
block_count -= d;
len -= d;
dst += d;
}
return 0;
}
int itsex_decompress16(HIO_HANDLE *src, int16 *dst, int len, int it215)
{
/* uint32 size = 0; */
uint32 block_count = 0;
uint32 bitbuf = 0;
int bitnum = 0;
uint8 left = 0;
int16 temp = 0, temp2 = 0;
uint32 d, pos;
while (len) {
if (!block_count) {
block_count = 0x4000;
/*size =*/ hio_read16l(src);
left = 17;
temp = temp2 = 0;
bitbuf = bitnum = 0;
}
d = block_count;
if (d > len)
d = len;
/* Unpacking */
pos = 0;
do {
uint32 bits = read_bits(src, &bitbuf, &bitnum, left);
if (hio_eof(src))
return -1;
if (left < 7) {
uint32 i = 1 << (left - 1);
uint32 j = bits;
if (i != j)
goto unpack_byte;
bits = read_bits(src, &bitbuf, &bitnum, 4) + 1;
if (hio_eof(src))
return -1;
left = ((uint8)(bits & 0xff) < left) ?
(uint8)(bits & 0xff) :
(uint8)((bits + 1) & 0xff);
goto next;
}
if (left < 17) {
uint32 i = (0xffff >> (17 - left)) + 8;
uint32 j = (i - 16) & 0xffff;
if ((bits <= j) || (bits > (i & 0xffff)))
goto unpack_byte;
bits -= j;
left = ((uint8)(bits & 0xff) < left) ?
(uint8)(bits & 0xff) :
(uint8)((bits + 1) & 0xff);
goto next;
}
if (left >= 18)
goto skip_byte;
if (bits >= 0x10000) {
left = (uint8)(bits + 1) & 0xff;
goto next;
}
unpack_byte:
if (left < 16) {
uint8 shift = 16 - left;
int16 c = (int16)(bits << shift);
c >>= shift;
bits = (uint32) c;
}
bits += temp;
temp = (int16)bits;
temp2 += temp;
dst[pos] = (it215) ? temp2 : temp;
skip_byte:
pos++;
next:
/* if (slen <= 0)
return -1 */;
} while (pos < d);
/* Move On */
block_count -= d;
len -= d;
dst += d;
if (len <= 0)
break;
}
return 0;
}
#endif /* LIBXMP_CORE_DISABLE_IT */

View file

@ -0,0 +1,163 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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 "lfo.h"
#define WAVEFORM_SIZE 64
static const int sine_wave[WAVEFORM_SIZE] = {
0, 24, 49, 74, 97, 120, 141, 161, 180, 197, 212, 224,
235, 244, 250, 253, 255, 253, 250, 244, 235, 224, 212, 197,
180, 161, 141, 120, 97, 74, 49, 24, 0, -24, -49, -74,
-97,-120,-141,-161,-180,-197,-212,-224,-235,-244,-250,-253,
-255,-253,-250,-244,-235,-224,-212,-197,-180,-161,-141,-120,
-97, -74, -49, -24
};
/* LFO */
static int get_lfo_mod(struct lfo *lfo)
{
int val;
if (lfo->rate == 0)
return 0;
switch (lfo->type) {
case 0: /* sine */
val = sine_wave[lfo->phase];
break;
case 1: /* ramp down */
val = 255 - (lfo->phase << 3);
break;
case 2: /* square */
val = lfo->phase < WAVEFORM_SIZE / 2 ? 255 : -255;
break;
case 3: /* random */
val = ((rand() & 0x1ff) - 256);
break;
#ifndef LIBXMP_CORE_PLAYER
case 669: /* 669 vibrato */
val = lfo->phase & 1;
break;
#endif
default:
return 0;
}
return val * lfo->depth;
}
static int get_lfo_st3(struct lfo *lfo)
{
if (lfo->rate == 0) {
return 0;
}
/* S3M square */
if (lfo->type == 2) {
int val = lfo->phase < WAVEFORM_SIZE / 2 ? 255 : 0;
return val * lfo->depth;
}
return get_lfo_mod(lfo);
}
/* From OpenMPT VibratoWaveforms.xm:
* "Generally the vibrato and tremolo tables are identical to those that
* ProTracker uses, but the vibratos ramp down table is upside down."
*/
static int get_lfo_ft2(struct lfo *lfo)
{
if (lfo->rate == 0)
return 0;
/* FT2 ramp */
if (lfo->type == 1) {
int phase = (lfo->phase + (WAVEFORM_SIZE >> 1)) % WAVEFORM_SIZE;
int val = (phase << 3) - 255;
return val * lfo->depth;
}
return get_lfo_mod(lfo);
}
#ifndef LIBXMP_CORE_DISABLE_IT
static int get_lfo_it(struct lfo *lfo)
{
if (lfo->rate == 0)
return 0;
return get_lfo_st3(lfo);
}
#endif
int libxmp_lfo_get(struct context_data *ctx, struct lfo *lfo, int is_vibrato)
{
struct module_data *m = &ctx->m;
switch (m->read_event_type) {
case READ_EVENT_ST3:
return get_lfo_st3(lfo);
case READ_EVENT_FT2:
if (is_vibrato) {
return get_lfo_ft2(lfo);
} else {
return get_lfo_mod(lfo);
}
#ifndef LIBXMP_CORE_DISABLE_IT
case READ_EVENT_IT:
return get_lfo_it(lfo);
#endif
default:
return get_lfo_mod(lfo);
}
}
void libxmp_lfo_update(struct lfo *lfo)
{
lfo->phase += lfo->rate;
lfo->phase %= WAVEFORM_SIZE;
}
void libxmp_lfo_set_phase(struct lfo *lfo, int phase)
{
lfo->phase = phase;
}
void libxmp_lfo_set_depth(struct lfo *lfo, int depth)
{
lfo->depth = depth;
}
void libxmp_lfo_set_rate(struct lfo *lfo, int rate)
{
lfo->rate = rate;
}
void libxmp_lfo_set_waveform(struct lfo *lfo, int type)
{
lfo->type = type;
}

View file

@ -0,0 +1,20 @@
#ifndef LIBXMP_LFO_H
#define LIBXMP_LFO_H
#include "common.h"
struct lfo {
int type;
int rate;
int depth;
int phase;
};
int libxmp_lfo_get(struct context_data *, struct lfo *, int);
void libxmp_lfo_update(struct lfo *);
void libxmp_lfo_set_phase(struct lfo *, int);
void libxmp_lfo_set_depth(struct lfo *, int);
void libxmp_lfo_set_rate(struct lfo *, int);
void libxmp_lfo_set_waveform(struct lfo *, int);
#endif

View file

@ -0,0 +1,146 @@
#ifndef LIBXMP_LIST_H
#define LIBXMP_LIST_H
#ifdef _MSC_VER
#define __inline__ __inline
#endif
#ifdef __WATCOMC__
#define __inline__ inline
#endif
/*
* Simple doubly linked list implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static __inline__ void __list_add(struct list_head *_new,
struct list_head * prev,
struct list_head * next)
{
next->prev = _new;
_new->next = next;
_new->prev = prev;
prev->next = _new;
}
/**
* list_add - add a new entry
* @_new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static __inline__ void list_add(struct list_head *_new, struct list_head *head)
{
__list_add(_new, head, head->next);
}
/**
* list_add_tail - add a new entry
* @_new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static __inline__ void list_add_tail(struct list_head *_new, struct list_head *head)
{
__list_add(_new, head->prev, head);
}
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static __inline__ void __list_del(struct list_head * prev,
struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
*/
static __inline__ void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static __inline__ int list_empty(struct list_head *head)
{
return head->next == head;
}
/**
* list_splice - join two lists
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static __inline__ void list_splice(struct list_head *list, struct list_head *head)
{
struct list_head *first = list->next;
if (first != list) {
struct list_head *last = list->prev;
struct list_head *at = head->next;
first->prev = head;
head->next = first;
last->next = at;
at->prev = last;
}
}
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(size_t)(&((type *)0)->member)))
/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
#endif

View file

@ -0,0 +1,614 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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 <sys/stat.h>
#include <errno.h>
#include "format.h"
#include "list.h"
#include "hio.h"
#include "tempfile.h"
#include "loader.h"
#ifndef LIBXMP_NO_DEPACKERS
#include "depackers/depacker.h"
#endif
#ifndef LIBXMP_CORE_PLAYER
#include "md5.h"
#include "extras.h"
#endif
extern struct format_loader *format_loaders[];
void libxmp_load_prologue(struct context_data *);
void libxmp_load_epilogue(struct context_data *);
int libxmp_prepare_scan(struct context_data *);
#ifndef LIBXMP_CORE_PLAYER
#define BUFLEN 16384
#endif
#ifndef LIBXMP_CORE_PLAYER
static void set_md5sum(HIO_HANDLE *f, unsigned char *digest)
{
unsigned char buf[BUFLEN];
MD5_CTX ctx;
int bytes_read;
hio_seek(f, 0, SEEK_SET);
MD5Init(&ctx);
while ((bytes_read = hio_read(buf, 1, BUFLEN, f)) > 0) {
MD5Update(&ctx, buf, bytes_read);
}
MD5Final(digest, &ctx);
}
static char *get_dirname(const char *name)
{
char *dirname;
const char *div;
ptrdiff_t len;
if ((div = strrchr(name, '/')) != NULL) {
len = div - name + 1;
dirname = malloc(len + 1);
if (dirname != NULL) {
memcpy(dirname, name, len);
dirname[len] = 0;
}
} else {
dirname = strdup("");
}
return dirname;
}
static char *get_basename(const char *name)
{
const char *div;
char *basename;
if ((div = strrchr(name, '/')) != NULL) {
basename = strdup(div + 1);
} else {
basename = strdup(name);
}
return basename;
}
#endif /* LIBXMP_CORE_PLAYER */
static int test_module(struct xmp_test_info *info, HIO_HANDLE *h)
{
char buf[XMP_NAME_SIZE];
int i;
if (info != NULL) {
*info->name = 0; /* reset name prior to testing */
*info->type = 0; /* reset type prior to testing */
}
for (i = 0; format_loaders[i] != NULL; i++) {
hio_seek(h, 0, SEEK_SET);
if (format_loaders[i]->test(h, buf, 0) == 0) {
int is_prowizard = 0;
#if !defined(LIBXMP_CORE_PLAYER) && !defined(LIBXMP_NO_PROWIZARD)
if (strcmp(format_loaders[i]->name, "prowizard") == 0) {
hio_seek(h, 0, SEEK_SET);
pw_test_format(h, buf, 0, info);
is_prowizard = 1;
}
#endif
if (info != NULL && !is_prowizard) {
strncpy(info->name, buf, XMP_NAME_SIZE - 1);
info->name[XMP_NAME_SIZE - 1] = '\0';
strncpy(info->type, format_loaders[i]->name,
XMP_NAME_SIZE - 1);
info->type[XMP_NAME_SIZE - 1] = '\0';
}
return 0;
}
}
return -XMP_ERROR_FORMAT;
}
int xmp_test_module(const char *path, struct xmp_test_info *info)
{
HIO_HANDLE *h;
struct stat st;
int ret;
#ifndef LIBXMP_NO_DEPACKERS
char *temp = NULL;
#endif
if (stat(path, &st) < 0)
return -XMP_ERROR_SYSTEM;
if (S_ISDIR(st.st_mode)) {
errno = EISDIR;
return -XMP_ERROR_SYSTEM;
}
if ((h = hio_open(path, "rb")) == NULL)
return -XMP_ERROR_SYSTEM;
#ifndef LIBXMP_NO_DEPACKERS
if (libxmp_decrunch(&h, path, &temp) < 0) {
ret = -XMP_ERROR_DEPACK;
goto err;
}
/* get size after decrunch */
if (hio_size(h) < 256) { /* set minimum valid module size */
ret = -XMP_ERROR_FORMAT;
goto err;
}
#endif
ret = test_module(info, h);
#ifndef LIBXMP_NO_DEPACKERS
err:
hio_close(h);
unlink_temp_file(temp);
#else
hio_close(h);
#endif
return ret;
}
int xmp_test_module_from_memory(const void *mem, long size, struct xmp_test_info *info)
{
HIO_HANDLE *h;
int ret;
if (size <= 0) {
return -XMP_ERROR_INVALID;
}
if ((h = hio_open_mem(mem, size)) == NULL)
return -XMP_ERROR_SYSTEM;
ret = test_module(info, h);
hio_close(h);
return ret;
}
int xmp_test_module_from_file(void *file, struct xmp_test_info *info)
{
HIO_HANDLE *h;
int ret;
#ifndef LIBXMP_NO_DEPACKERS
char *temp = NULL;
#endif
if ((h = hio_open_file((FILE *)file)) == NULL)
return -XMP_ERROR_SYSTEM;
#ifndef LIBXMP_NO_DEPACKERS
if (libxmp_decrunch(&h, NULL, &temp) < 0) {
ret = -XMP_ERROR_DEPACK;
goto err;
}
/* get size after decrunch */
if (hio_size(h) < 256) { /* set minimum valid module size */
ret = -XMP_ERROR_FORMAT;
goto err;
}
#endif
ret = test_module(info, h);
#ifndef LIBXMP_NO_DEPACKERS
err:
hio_close(h);
unlink_temp_file(temp);
#else
hio_close(h);
#endif
return ret;
}
int xmp_test_module_from_callbacks(void *priv, struct xmp_callbacks callbacks,
struct xmp_test_info *info)
{
HIO_HANDLE *h;
int ret;
if ((h = hio_open_callbacks(priv, callbacks)) == NULL)
return -XMP_ERROR_SYSTEM;
ret = test_module(info, h);
hio_close(h);
return ret;
}
static int load_module(xmp_context opaque, HIO_HANDLE *h)
{
struct context_data *ctx = (struct context_data *)opaque;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
int i, j, ret;
int test_result, load_result;
libxmp_load_prologue(ctx);
D_(D_WARN "load");
test_result = load_result = -1;
for (i = 0; format_loaders[i] != NULL; i++) {
hio_seek(h, 0, SEEK_SET);
hio_error(h); /* reset error flag */
D_(D_WARN "test %s", format_loaders[i]->name);
test_result = format_loaders[i]->test(h, NULL, 0);
if (test_result == 0) {
hio_seek(h, 0, SEEK_SET);
D_(D_WARN "load format: %s", format_loaders[i]->name);
load_result = format_loaders[i]->loader(m, h, 0);
break;
}
}
#ifndef LIBXMP_CORE_PLAYER
if (test_result == 0 && load_result == 0)
set_md5sum(h, m->md5);
#endif
if (test_result < 0) {
xmp_release_module(opaque);
return -XMP_ERROR_FORMAT;
}
if (load_result < 0) {
goto err_load;
}
/* Sanity check: number of channels, module length */
if (mod->chn > XMP_MAX_CHANNELS || mod->len > XMP_MAX_MOD_LENGTH) {
goto err_load;
}
/* Sanity check: channel pan */
for (i = 0; i < mod->chn; i++) {
if (mod->xxc[i].vol < 0 || mod->xxc[i].vol > 0xff) {
goto err_load;
}
if (mod->xxc[i].pan < 0 || mod->xxc[i].pan > 0xff) {
goto err_load;
}
}
/* Sanity check: patterns */
if (mod->xxp == NULL) {
goto err_load;
}
for (i = 0; i < mod->pat; i++) {
if (mod->xxp[i] == NULL) {
goto err_load;
}
for (j = 0; j < mod->chn; j++) {
int t = mod->xxp[i]->index[j];
if (t < 0 || t >= mod->trk || mod->xxt[t] == NULL) {
goto err_load;
}
}
}
libxmp_adjust_string(mod->name);
for (i = 0; i < mod->ins; i++) {
libxmp_adjust_string(mod->xxi[i].name);
}
for (i = 0; i < mod->smp; i++) {
libxmp_adjust_string(mod->xxs[i].name);
}
libxmp_load_epilogue(ctx);
ret = libxmp_prepare_scan(ctx);
if (ret < 0) {
xmp_release_module(opaque);
return ret;
}
ret = libxmp_scan_sequences(ctx);
if (ret < 0) {
xmp_release_module(opaque);
return -XMP_ERROR_LOAD;
}
ctx->state = XMP_STATE_LOADED;
return 0;
err_load:
xmp_release_module(opaque);
return -XMP_ERROR_LOAD;
}
int xmp_load_module(xmp_context opaque, const char *path)
{
struct context_data *ctx = (struct context_data *)opaque;
#ifndef LIBXMP_CORE_PLAYER
struct module_data *m = &ctx->m;
long size;
#endif
#ifndef LIBXMP_NO_DEPACKERS
char *temp_name;
#endif
HIO_HANDLE *h;
struct stat st;
int ret;
D_(D_WARN "path = %s", path);
if (stat(path, &st) < 0) {
return -XMP_ERROR_SYSTEM;
}
if (S_ISDIR(st.st_mode)) {
errno = EISDIR;
return -XMP_ERROR_SYSTEM;
}
if ((h = hio_open(path, "rb")) == NULL) {
return -XMP_ERROR_SYSTEM;
}
#ifndef LIBXMP_NO_DEPACKERS
D_(D_INFO "decrunch");
if (libxmp_decrunch(&h, path, &temp_name) < 0) {
ret = -XMP_ERROR_DEPACK;
goto err;
}
#endif
#ifndef LIBXMP_CORE_PLAYER
size = hio_size(h);
if (size < 256) { /* get size after decrunch */
ret = -XMP_ERROR_FORMAT;
goto err;
}
#endif
if (ctx->state > XMP_STATE_UNLOADED)
xmp_release_module(opaque);
#ifndef LIBXMP_CORE_PLAYER
m->dirname = get_dirname(path);
if (m->dirname == NULL) {
ret = -XMP_ERROR_SYSTEM;
goto err;
}
m->basename = get_basename(path);
if (m->basename == NULL) {
ret = -XMP_ERROR_SYSTEM;
goto err;
}
m->filename = path; /* For ALM, SSMT, etc */
m->size = size;
#else
ctx->m.filename = NULL;
ctx->m.dirname = NULL;
ctx->m.basename = NULL;
#endif
ret = load_module(opaque, h);
hio_close(h);
#ifndef LIBXMP_NO_DEPACKERS
unlink_temp_file(temp_name);
#endif
return ret;
#ifndef LIBXMP_CORE_PLAYER
err:
hio_close(h);
#ifndef LIBXMP_NO_DEPACKERS
unlink_temp_file(temp_name);
#endif
return ret;
#endif
}
int xmp_load_module_from_memory(xmp_context opaque, const void *mem, long size)
{
struct context_data *ctx = (struct context_data *)opaque;
struct module_data *m = &ctx->m;
HIO_HANDLE *h;
int ret;
if (size <= 0) {
return -XMP_ERROR_INVALID;
}
if ((h = hio_open_mem(mem, size)) == NULL)
return -XMP_ERROR_SYSTEM;
if (ctx->state > XMP_STATE_UNLOADED)
xmp_release_module(opaque);
m->filename = NULL;
m->basename = NULL;
m->dirname = NULL;
m->size = size;
ret = load_module(opaque, h);
hio_close(h);
return ret;
}
int xmp_load_module_from_file(xmp_context opaque, void *file, long size)
{
struct context_data *ctx = (struct context_data *)opaque;
struct module_data *m = &ctx->m;
HIO_HANDLE *h;
int ret;
if ((h = hio_open_file((FILE *)file)) == NULL)
return -XMP_ERROR_SYSTEM;
if (ctx->state > XMP_STATE_UNLOADED)
xmp_release_module(opaque);
m->filename = NULL;
m->basename = NULL;
m->dirname = NULL;
m->size = hio_size(h);
ret = load_module(opaque, h);
hio_close(h);
return ret;
}
int xmp_load_module_from_callbacks(xmp_context opaque, void *priv,
struct xmp_callbacks callbacks)
{
struct context_data *ctx = (struct context_data *)opaque;
struct module_data *m = &ctx->m;
HIO_HANDLE *h;
int ret;
if ((h = hio_open_callbacks(priv, callbacks)) == NULL)
return -XMP_ERROR_SYSTEM;
if (ctx->state > XMP_STATE_UNLOADED)
xmp_release_module(opaque);
m->filename = NULL;
m->basename = NULL;
m->dirname = NULL;
m->size = hio_size(h);
ret = load_module(opaque, h);
hio_close(h);
return ret;
}
void xmp_release_module(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
int i;
/* can't test this here, we must call release_module to clean up
* load errors
if (ctx->state < XMP_STATE_LOADED)
return;
*/
if (ctx->state > XMP_STATE_LOADED)
xmp_end_player(opaque);
ctx->state = XMP_STATE_UNLOADED;
D_(D_INFO "Freeing memory");
#ifndef LIBXMP_CORE_PLAYER
libxmp_release_module_extras(ctx);
#endif
if (mod->xxt != NULL) {
for (i = 0; i < mod->trk; i++) {
free(mod->xxt[i]);
}
free(mod->xxt);
mod->xxt = NULL;
}
if (mod->xxp != NULL) {
for (i = 0; i < mod->pat; i++) {
free(mod->xxp[i]);
}
free(mod->xxp);
mod->xxp = NULL;
}
if (mod->xxi != NULL) {
for (i = 0; i < mod->ins; i++) {
free(mod->xxi[i].sub);
free(mod->xxi[i].extra);
}
free(mod->xxi);
mod->xxi = NULL;
}
if (mod->xxs != NULL) {
for (i = 0; i < mod->smp; i++) {
libxmp_free_sample(&mod->xxs[i]);
}
free(mod->xxs);
mod->xxs = NULL;
}
free(m->xtra);
m->xtra = NULL;
#ifndef LIBXMP_CORE_DISABLE_IT
if (m->xsmp != NULL) {
for (i = 0; i < mod->smp; i++) {
libxmp_free_sample(&m->xsmp[i]);
}
free(m->xsmp);
m->xsmp = NULL;
}
#endif
libxmp_free_scan(ctx);
free(m->comment);
m->comment = NULL;
D_("free dirname/basename");
free(m->dirname);
free(m->basename);
m->basename = NULL;
m->dirname = NULL;
}
void xmp_scan_module(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
if (ctx->state < XMP_STATE_LOADED)
return;
libxmp_scan_sequences(ctx);
}

View file

@ -0,0 +1,399 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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 <ctype.h>
#include "common.h"
#include "loader.h"
#ifndef LIBXMP_CORE_PLAYER
/*
* Handle special "module quirks" that can't be detected automatically
* such as Protracker 2.x compatibility, vblank timing, etc.
*/
struct module_quirk {
uint8 md5[16];
int flags;
int mode;
};
const struct module_quirk mq[] = {
/* "No Mercy" by Alf/VTL (added by Martin Willers) */
{
{ 0x36, 0x6e, 0xc0, 0xfa, 0x96, 0x2a, 0xeb, 0xee,
0x03, 0x4a, 0xa2, 0xdb, 0xaa, 0x49, 0xaa, 0xea },
0, XMP_MODE_PROTRACKER
},
/* mod.souvenir of china */
{
{ 0x93, 0xf1, 0x46, 0xae, 0xb7, 0x58, 0xc3, 0x9d,
0x8b, 0x5f, 0xbc, 0x98, 0xbf, 0x23, 0x7a, 0x43 },
XMP_FLAGS_FIXLOOP, XMP_MODE_AUTO
},
/* "siedler ii" (added by Daniel Åkerud) */
{
{ 0x70, 0xaa, 0x03, 0x4d, 0xfb, 0x2f, 0x1f, 0x73,
0xd9, 0xfd, 0xba, 0xfe, 0x13, 0x1b, 0xb7, 0x01 },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
},
/* "Klisje paa klisje" (added by Kjetil Torgrim Homme) */
{
{ 0xe9, 0x98, 0x01, 0x2c, 0x70, 0x0e, 0xb4, 0x3a,
0xf0, 0x32, 0x17, 0x11, 0x30, 0x58, 0x29, 0xb2 },
0, XMP_MODE_NOISETRACKER
},
#if 0
/* -- Already covered by Noisetracker fingerprinting -- */
/* Another version of Klisje paa klisje sent by Steve Fernandez */
{
{ 0x12, 0x19, 0x1c, 0x90, 0x41, 0xe3, 0xfd, 0x70,
0xb7, 0xe6, 0xb3, 0x94, 0x8b, 0x21, 0x07, 0x63 },
XMP_FLAGS_VBLANK
},
#endif
/* "((((( nebulos )))))" sent by Tero Auvinen (AMP version) */
{
{ 0x51, 0x6e, 0x8d, 0xcc, 0x35, 0x7d, 0x50, 0xde,
0xa9, 0x85, 0xbe, 0xbf, 0x90, 0x2e, 0x42, 0xdc },
0, XMP_MODE_NOISETRACKER
},
/* Purple Motion's Sundance.mod, Music Channel BBS edit */
{
{ 0x5d, 0x3e, 0x1e, 0x08, 0x28, 0x52, 0x12, 0xc7,
0x17, 0x64, 0x95, 0x75, 0x98, 0xe6, 0x95, 0xc1 },
0, XMP_MODE_ST3
},
/* Asle's Ode to Protracker */
{
{ 0x97, 0xa3, 0x7d, 0x30, 0xd7, 0xae, 0x6d, 0x50,
0xc9, 0x62, 0xe9, 0xd8, 0x87, 0x1b, 0x7e, 0x8a },
0, XMP_MODE_PROTRACKER
},
{
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
0, 0
}
};
static void module_quirks(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
int i;
for (i = 0; mq[i].flags != 0 || mq[i].mode != 0; i++) {
if (!memcmp(m->md5, mq[i].md5, 16)) {
p->flags |= mq[i].flags;
p->mode = mq[i].mode;
}
}
}
#endif /* LIBXMP_CORE_PLAYER */
char *libxmp_adjust_string(char *s)
{
int i;
for (i = 0; i < strlen(s); i++) {
if (!isprint((int)s[i]) || ((uint8) s[i] > 127))
s[i] = ' ';
}
while (*s && (s[strlen(s) - 1] == ' ')) {
s[strlen(s) - 1] = 0;
}
return s;
}
static void check_envelope(struct xmp_envelope *env)
{
/* Disable envelope if invalid number of points */
if (env->npt <= 0 || env->npt > XMP_MAX_ENV_POINTS) {
env->flg &= ~XMP_ENVELOPE_ON;
}
/* Disable envelope loop if invalid loop parameters */
if (env->lps >= env->npt || env->lpe >= env->npt) {
env->flg &= ~XMP_ENVELOPE_LOOP;
}
/* Disable envelope loop if invalid sustain */
if (env->sus >= env->npt) {
env->flg &= ~XMP_ENVELOPE_ON;
}
}
void libxmp_load_prologue(struct context_data *ctx)
{
struct module_data *m = &ctx->m;
int i;
/* Reset variables */
memset(&m->mod, 0, sizeof (struct xmp_module));
m->rrate = PAL_RATE;
m->c4rate = C4_PAL_RATE;
m->volbase = 0x40;
m->gvol = m->gvolbase = 0x40;
m->vol_table = NULL;
m->quirk = 0;
m->read_event_type = READ_EVENT_MOD;
m->period_type = PERIOD_AMIGA;
m->comment = NULL;
m->scan_cnt = NULL;
/* Set defaults */
m->mod.pat = 0;
m->mod.trk = 0;
m->mod.chn = 4;
m->mod.ins = 0;
m->mod.smp = 0;
m->mod.spd = 6;
m->mod.bpm = 125;
m->mod.len = 0;
m->mod.rst = 0;
#ifndef LIBXMP_CORE_PLAYER
m->extra = NULL;
#endif
#ifndef LIBXMP_CORE_DISABLE_IT
m->xsmp = NULL;
#endif
m->time_factor = DEFAULT_TIME_FACTOR;
for (i = 0; i < 64; i++) {
int pan = (((i + 1) / 2) % 2) * 0xff;
m->mod.xxc[i].pan = 0x80 + (pan - 0x80) * m->defpan / 100;
m->mod.xxc[i].vol = 0x40;
m->mod.xxc[i].flg = 0;
}
}
void libxmp_load_epilogue(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
int i, j;
mod->gvl = m->gvol;
/* Sanity check for module parameters */
CLAMP(mod->len, 0, XMP_MAX_MOD_LENGTH);
CLAMP(mod->pat, 0, 257); /* some formats have an extra pattern */
CLAMP(mod->ins, 0, 255);
CLAMP(mod->smp, 0, MAX_SAMPLES);
CLAMP(mod->chn, 0, XMP_MAX_CHANNELS);
/* Fix cases where the restart value is invalid e.g. kc_fall8.xm
* from http://aminet.net/mods/mvp/mvp_0002.lha (reported by
* Ralf Hoffmann <ralf@boomerangsworld.de>)
*/
if (mod->rst >= mod->len) {
mod->rst = 0;
}
/* Sanity check for tempo and BPM */
if (mod->spd <= 0 || mod->spd > 255) {
mod->spd = 6;
}
CLAMP(mod->bpm, XMP_MIN_BPM, 255);
/* Set appropriate values for instrument volumes and subinstrument
* global volumes when QUIRK_INSVOL is not set, to keep volume values
* consistent if the user inspects struct xmp_module. We can later
* set volumes in the loaders and eliminate the quirk.
*/
for (i = 0; i < mod->ins; i++) {
if (~m->quirk & QUIRK_INSVOL) {
mod->xxi[i].vol = m->volbase;
}
for (j = 0; j < mod->xxi[i].nsm; j++) {
if (~m->quirk & QUIRK_INSVOL) {
mod->xxi[i].sub[j].gvl = m->volbase;
}
}
}
/* Sanity check for envelopes
*/
for (i = 0; i < mod->ins; i++) {
check_envelope(&mod->xxi[i].aei);
check_envelope(&mod->xxi[i].fei);
check_envelope(&mod->xxi[i].pei);
}
p->filter = 0;
p->mode = XMP_MODE_AUTO;
p->flags = p->player_flags;
#ifndef LIBXMP_CORE_PLAYER
module_quirks(ctx);
#endif
libxmp_set_player_mode(ctx);
}
int libxmp_prepare_scan(struct context_data *ctx)
{
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
int i, ord;
if (mod->xxp == NULL || mod->xxt == NULL)
return -XMP_ERROR_LOAD;
ord = 0;
while (ord < mod->len && mod->xxo[ord] >= mod->pat) {
ord++;
}
if (ord >= mod->len) {
mod->len = 0;
return 0;
}
m->scan_cnt = calloc(sizeof (uint8 *), mod->len);
if (m->scan_cnt == NULL)
return -XMP_ERROR_SYSTEM;
for (i = 0; i < mod->len; i++) {
int pat_idx = mod->xxo[i];
struct xmp_pattern *pat;
/* Add pattern if referenced in orders */
if (pat_idx < mod->pat && !mod->xxp[pat_idx]) {
if (libxmp_alloc_pattern(mod, pat_idx) < 0) {
return -XMP_ERROR_SYSTEM;
}
}
pat = pat_idx >= mod->pat ? NULL : mod->xxp[pat_idx];
m->scan_cnt[i] = calloc(1, pat && pat->rows ? pat->rows : 1);
if (m->scan_cnt[i] == NULL)
return -XMP_ERROR_SYSTEM;
}
return 0;
}
void libxmp_free_scan(struct context_data *ctx)
{
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
int i;
if (m->scan_cnt) {
for (i = 0; i < mod->len; i++)
free(m->scan_cnt[i]);
free(m->scan_cnt);
m->scan_cnt = NULL;
}
}
/* Process player personality flags */
int libxmp_set_player_mode(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
int q;
switch (p->mode) {
case XMP_MODE_AUTO:
break;
case XMP_MODE_MOD:
m->c4rate = C4_PAL_RATE;
m->quirk = 0;
m->read_event_type = READ_EVENT_MOD;
m->period_type = PERIOD_AMIGA;
break;
case XMP_MODE_NOISETRACKER:
m->c4rate = C4_PAL_RATE;
m->quirk = QUIRK_NOBPM;
m->read_event_type = READ_EVENT_MOD;
m->period_type = PERIOD_MODRNG;
break;
case XMP_MODE_PROTRACKER:
m->c4rate = C4_PAL_RATE;
m->quirk = QUIRK_PROTRACK;
m->read_event_type = READ_EVENT_MOD;
m->period_type = PERIOD_MODRNG;
break;
case XMP_MODE_S3M:
q = m->quirk & (QUIRK_VSALL | QUIRK_ARPMEM);
m->c4rate = C4_NTSC_RATE;
m->quirk = QUIRKS_ST3 | q;
m->read_event_type = READ_EVENT_ST3;
break;
case XMP_MODE_ST3:
q = m->quirk & (QUIRK_VSALL | QUIRK_ARPMEM);
m->c4rate = C4_NTSC_RATE;
m->quirk = QUIRKS_ST3 | QUIRK_ST3BUGS | q;
m->read_event_type = READ_EVENT_ST3;
break;
case XMP_MODE_ST3GUS:
q = m->quirk & (QUIRK_VSALL | QUIRK_ARPMEM);
m->c4rate = C4_NTSC_RATE;
m->quirk = QUIRKS_ST3 | QUIRK_ST3BUGS | q;
m->quirk &= ~QUIRK_RSTCHN;
m->read_event_type = READ_EVENT_ST3;
break;
case XMP_MODE_XM:
m->c4rate = C4_NTSC_RATE;
m->quirk = QUIRKS_FT2;
m->read_event_type = READ_EVENT_FT2;
break;
case XMP_MODE_FT2:
m->c4rate = C4_NTSC_RATE;
m->quirk = QUIRKS_FT2 | QUIRK_FT2BUGS;
m->read_event_type = READ_EVENT_FT2;
break;
case XMP_MODE_IT:
m->c4rate = C4_NTSC_RATE;
m->quirk = QUIRKS_IT | QUIRK_VIBHALF | QUIRK_VIBINV;
m->read_event_type = READ_EVENT_IT;
break;
case XMP_MODE_ITSMP:
m->c4rate = C4_NTSC_RATE;
m->quirk = QUIRKS_IT | QUIRK_VIBHALF | QUIRK_VIBINV;
m->quirk &= ~(QUIRK_VIRTUAL | QUIRK_RSTCHN);
m->read_event_type = READ_EVENT_IT;
break;
default:
return -1;
}
return 0;
}

View file

@ -0,0 +1,65 @@
#ifndef XMP_LOADER_H
#define XMP_LOADER_H
#include "common.h"
#include "effects.h"
#include "format.h"
#include "hio.h"
/* Sample flags */
#define SAMPLE_FLAG_DIFF 0x0001 /* Differential */
#define SAMPLE_FLAG_UNS 0x0002 /* Unsigned */
#define SAMPLE_FLAG_8BDIFF 0x0004
#define SAMPLE_FLAG_7BIT 0x0008
#define SAMPLE_FLAG_NOLOAD 0x0010 /* Get from buffer, don't load */
#define SAMPLE_FLAG_BIGEND 0x0040 /* Big-endian */
#define SAMPLE_FLAG_VIDC 0x0080 /* Archimedes VIDC logarithmic */
/*#define SAMPLE_FLAG_STEREO 0x0100 Interleaved stereo sample */
#define SAMPLE_FLAG_FULLREP 0x0200 /* Play full sample before looping */
#define SAMPLE_FLAG_ADLIB 0x1000 /* Adlib synth instrument */
#define SAMPLE_FLAG_HSC 0x2000 /* HSC Adlib synth instrument */
#define SAMPLE_FLAG_ADPCM 0x4000 /* ADPCM4 encoded samples */
#define DEFPAN(x) (0x80 + ((x) - 0x80) * m->defpan / 100)
int libxmp_init_instrument (struct module_data *);
int libxmp_realloc_samples (struct module_data *, int);
int libxmp_alloc_subinstrument (struct xmp_module *, int, int);
int libxmp_init_pattern (struct xmp_module *);
int libxmp_alloc_pattern (struct xmp_module *, int);
int libxmp_alloc_track (struct xmp_module *, int, int);
int libxmp_alloc_tracks_in_pattern (struct xmp_module *, int);
int libxmp_alloc_pattern_tracks (struct xmp_module *, int, int);
int libxmp_alloc_pattern_tracks_long(struct xmp_module *, int, int);
char *libxmp_instrument_name (struct xmp_module *, int, uint8 *, int);
char *libxmp_copy_adjust (char *, uint8 *, int);
int libxmp_copy_name_for_fopen (char *, const char *, int);
int libxmp_test_name (uint8 *, int);
void libxmp_read_title (HIO_HANDLE *, char *, int);
void libxmp_set_xxh_defaults (struct xmp_module *);
void libxmp_decode_protracker_event (struct xmp_event *, uint8 *);
void libxmp_decode_noisetracker_event(struct xmp_event *, uint8 *);
void libxmp_disable_continue_fx (struct xmp_event *);
int libxmp_check_filename_case (const char *, const char *, char *, int);
void libxmp_get_instrument_path (struct module_data *, char *, int);
void libxmp_set_type (struct module_data *, const char *, ...);
int libxmp_load_sample (struct module_data *, HIO_HANDLE *, int,
struct xmp_sample *, const void *);
void libxmp_free_sample (struct xmp_sample *);
void libxmp_schism_tracker_string (char *, size_t, int, int);
extern uint8 libxmp_ord_xlat[];
extern const int libxmp_arch_vol_table[];
#define MAGIC4(a,b,c,d) \
(((uint32)(a)<<24)|((uint32)(b)<<16)|((uint32)(c)<<8)|(d))
#define LOAD_INIT()
#define MODULE_INFO() do { \
D_(D_WARN "Module title: \"%s\"", m->mod.name); \
D_(D_WARN "Module type: %s", m->mod.type); \
} while (0)
#endif

View file

@ -0,0 +1,240 @@
/*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
* written by Colin Plumb in 1993, no copyright is claimed.
* This code is in the public domain; do with it what you wish.
*
* Equivalent code is available from RSA Data Security, Inc.
* This code has been tested against that, and is equivalent,
* except that you don't need to include two pages of legalese
* with every copy.
*
* To compute the message digest of a chunk of bytes, declare an
* MD5Context structure, pass it to MD5Init, call MD5Update as
* needed on buffers full of bytes, and then call MD5Final, which
* will fill a supplied 16-byte array with the digest.
*/
#include "common.h"
#include "md5.h"
#define PUT_64BIT_LE(cp, value) do { \
(cp)[7] = (value) >> 56; \
(cp)[6] = (value) >> 48; \
(cp)[5] = (value) >> 40; \
(cp)[4] = (value) >> 32; \
(cp)[3] = (value) >> 24; \
(cp)[2] = (value) >> 16; \
(cp)[1] = (value) >> 8; \
(cp)[0] = (value); } while (0)
#define PUT_32BIT_LE(cp, value) do { \
(cp)[3] = (value) >> 24; \
(cp)[2] = (value) >> 16; \
(cp)[1] = (value) >> 8; \
(cp)[0] = (value); } while (0)
static uint8 PADDING[MD5_BLOCK_LENGTH] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/* The four core functions - F1 is optimized somewhat */
/* #define F1(x, y, z) (x & y | ~x & z) */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
/*
* The core of the MD5 algorithm, this alters an existing MD5 hash to
* reflect the addition of 16 longwords of new data. MD5Update blocks
* the data and converts bytes into longwords for this routine.
*/
static void MD5Transform(uint32 state[4], const uint8 block[MD5_BLOCK_LENGTH])
{
uint32 a, b, c, d, in[MD5_BLOCK_LENGTH / 4];
#ifndef WORDS_BIGENDIAN
memcpy(in, block, sizeof(in));
#else
for (a = 0; a < MD5_BLOCK_LENGTH / 4; a++) {
in[a] = (uint32)(
(uint32)(block[a * 4 + 0]) |
(uint32)(block[a * 4 + 1]) << 8 |
(uint32)(block[a * 4 + 2]) << 16 |
(uint32)(block[a * 4 + 3]) << 24);
}
#endif
a = state[0];
b = state[1];
c = state[2];
d = state[3];
MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[2 ] + 0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[7 ] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[5 ] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[3 ] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[1 ] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[8 ] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[6 ] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[4 ] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[2 ] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[9 ] + 0xeb86d391, 21);
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
}
/*
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
* initialization constants.
*/
void MD5Init(MD5_CTX *ctx)
{
ctx->count = 0;
ctx->state[0] = 0x67452301;
ctx->state[1] = 0xefcdab89;
ctx->state[2] = 0x98badcfe;
ctx->state[3] = 0x10325476;
}
/*
* Update context to reflect the concatenation of another buffer full
* of bytes.
*/
void MD5Update(MD5_CTX *ctx, const unsigned char *input, size_t len)
{
size_t have, need;
/* Check how many bytes we already have and how many more we need. */
have = (size_t)((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1));
need = MD5_BLOCK_LENGTH - have;
/* Update bitcount */
ctx->count += (uint64)len << 3;
if (len >= need) {
if (have != 0) {
memcpy(ctx->buffer + have, input, need);
MD5Transform(ctx->state, ctx->buffer);
input += need;
len -= need;
have = 0;
}
/* Process data in MD5_BLOCK_LENGTH-byte chunks. */
while (len >= MD5_BLOCK_LENGTH) {
MD5Transform(ctx->state, input);
input += MD5_BLOCK_LENGTH;
len -= MD5_BLOCK_LENGTH;
}
}
/* Handle any remaining bytes of data. */
if (len != 0)
memcpy(ctx->buffer + have, input, len);
}
/*
* Pad pad to 64-byte boundary with the bit pattern
* 1 0* (64-bit count of bits processed, MSB-first)
*/
static void MD5Pad(MD5_CTX *ctx)
{
uint8 count[8];
size_t padlen;
/* Convert count to 8 bytes in little endian order. */
PUT_64BIT_LE(count, ctx->count);
/* Pad out to 56 mod 64. */
padlen = MD5_BLOCK_LENGTH -
((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1));
if (padlen < 1 + 8)
padlen += MD5_BLOCK_LENGTH;
MD5Update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */
MD5Update(ctx, count, 8);
}
/*
* Final wrapup--call MD5Pad, fill in digest and zero out ctx.
*/
void MD5Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *ctx)
{
int i;
MD5Pad(ctx);
if (digest != NULL) {
for (i = 0; i < 4; i++)
PUT_32BIT_LE(digest + i * 4, ctx->state[i]);
memset(ctx, 0, sizeof(*ctx));
}
}

View file

@ -0,0 +1,39 @@
/*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
* written by Colin Plumb in 1993, no copyright is claimed.
* This code is in the public domain; do with it what you wish.
*
* Equivalent code is available from RSA Data Security, Inc.
* This code has been tested against that, and is equivalent,
* except that you don't need to include two pages of legalese
* with every copy.
*/
#ifndef LIBXMP_MD5_H
#define LIBXMP_MD5_H
#include "common.h"
#define MD5_BLOCK_LENGTH 64
#define MD5_DIGEST_LENGTH 16
#define MD5_DIGEST_STRING_LENGTH (MD5_DIGEST_LENGTH * 2 + 1)
typedef struct MD5Context {
uint32 state[4]; /* state */
uint64 count; /* number of bits, mod 2^64 */
uint8 buffer[MD5_BLOCK_LENGTH]; /* input buffer */
} MD5_CTX;
#ifdef __cplusplus
extern "C" {
#endif
void MD5Init(MD5_CTX *);
void MD5Update(MD5_CTX *, const unsigned char *, size_t);
void MD5Final(uint8[MD5_DIGEST_LENGTH], MD5_CTX *);
#ifdef __cplusplus
}
#endif
#endif /* LIBXMP_MD5_H */

View file

@ -0,0 +1,125 @@
#ifndef LIBXMP_MDATAIO_H
#define LIBXMP_MDATAIO_H
#include <stddef.h>
#include <limits.h>
#include "common.h"
static inline ptrdiff_t CAN_READ(MFILE *m)
{
if (m->size >= 0)
return m->pos >= 0 ? m->size - m->pos : 0;
return INT_MAX;
}
static inline uint8 mread8(MFILE *m, int *err)
{
uint8 x = 0xff;
size_t r = mread(&x, 1, 1, m);
if (err) {
*err = (r == 1) ? 0 : EOF;
}
return x;
}
static inline int8 mread8s(MFILE *m, int *err)
{
int r = mgetc(m);
if (err) {
*err = (r < 0) ? EOF : 0;
}
return (int8)r;
}
static inline uint16 mread16l(MFILE *m, int *err)
{
ptrdiff_t can_read = CAN_READ(m);
if (can_read >= 2) {
uint16 n = readmem16l(m->start + m->pos);
m->pos += 2;
if(err) *err = 0;
return n;
} else {
m->pos += can_read;
if(err) *err = EOF;
return EOF;
}
}
static inline uint16 mread16b(MFILE *m, int *err)
{
ptrdiff_t can_read = CAN_READ(m);
if (can_read >= 2) {
uint16 n = readmem16b(m->start + m->pos);
m->pos += 2;
if(err) *err = 0;
return n;
} else {
m->pos += can_read;
if(err) *err = EOF;
return EOF;
}
}
static inline uint32 mread24l(MFILE *m, int *err)
{
ptrdiff_t can_read = CAN_READ(m);
if (can_read >= 3) {
uint32 n = readmem24l(m->start + m->pos);
m->pos += 3;
if(err) *err = 0;
return n;
} else {
m->pos += can_read;
if(err) *err = EOF;
return EOF;
}
}
static inline uint32 mread24b(MFILE *m, int *err)
{
ptrdiff_t can_read = CAN_READ(m);
if (can_read >= 3) {
uint32 n = readmem24b(m->start + m->pos);
m->pos += 3;
if(err) *err = 0;
return n;
} else {
m->pos += can_read;
if(err) *err = EOF;
return EOF;
}
}
static inline uint32 mread32l(MFILE *m, int *err)
{
ptrdiff_t can_read = CAN_READ(m);
if (can_read >= 4) {
uint32 n = readmem32l(m->start + m->pos);
m->pos += 4;
if(err) *err = 0;
return n;
} else {
m->pos += can_read;
if(err) *err = EOF;
return EOF;
}
}
static inline uint32 mread32b(MFILE *m, int *err)
{
ptrdiff_t can_read = CAN_READ(m);
if (can_read >= 4) {
uint32 n = readmem32b(m->start + m->pos);
m->pos += 4;
if(err) *err = 0;
return n;
} else {
m->pos += can_read;
if(err) *err = EOF;
return EOF;
}
}
#endif

View file

@ -0,0 +1,112 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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 "memio.h"
static inline ptrdiff_t CAN_READ(MFILE *m)
{
return m->pos >= 0 ? m->size - m->pos : 0;
}
int mgetc(MFILE *m)
{
if (CAN_READ(m) >= 1)
return *(uint8 *)(m->start + m->pos++);
return EOF;
}
size_t mread(void *buf, size_t size, size_t num, MFILE *m)
{
size_t should_read = size * num;
ptrdiff_t can_read = CAN_READ(m);
if (!size || !num || can_read <= 0) {
return 0;
}
if (should_read > can_read) {
should_read = can_read;
}
memcpy(buf, m->start + m->pos, should_read);
m->pos += should_read;
return should_read / size;
}
int mseek(MFILE *m, long offset, int whence)
{
ptrdiff_t ofs = offset;
switch (whence) {
case SEEK_SET:
break;
case SEEK_CUR:
ofs += m->pos;
break;
case SEEK_END:
ofs += m->size;
break;
default:
return -1;
}
if (ofs < 0) return -1;
if (ofs > m->size)
ofs = m->size;
m->pos = ofs;
return 0;
}
long mtell(MFILE *m)
{
return (long)m->pos;
}
int meof(MFILE *m)
{
return CAN_READ(m) <= 0;
}
MFILE *mopen(const void *ptr, long size)
{
MFILE *m;
m = (MFILE *)malloc(sizeof (MFILE));
if (m == NULL)
return NULL;
m->start = ptr;
m->pos = 0;
m->size = size;
return m;
}
int mclose(MFILE *m)
{
free(m);
return 0;
}

View file

@ -0,0 +1,28 @@
#ifndef LIBXMP_MEMIO_H
#define LIBXMP_MEMIO_H
#include <stddef.h>
typedef struct {
const unsigned char *start;
ptrdiff_t pos;
ptrdiff_t size;
} MFILE;
#ifdef __cplusplus
extern "C" {
#endif
MFILE *mopen(const void *, long);
int mgetc(MFILE *stream);
size_t mread(void *, size_t, size_t, MFILE *);
int mseek(MFILE *, long, int);
long mtell(MFILE *);
int mclose(MFILE *);
int meof(MFILE *);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,30 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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 <errno.h>
#include "xmp.h"
int xmp_syserrno (void)
{
return errno;
}

View file

@ -0,0 +1,439 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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 "virtual.h"
#include "mixer.h"
#include "precomp_lut.h"
/* Mixers
*
* To increase performance eight mixers are defined, one for each
* combination of the following parameters: interpolation, resolution
* and number of channels.
*/
#define NEAREST_NEIGHBOR() do { \
smp_in = ((int16)sptr[pos] << 8); \
} while (0)
#define NEAREST_NEIGHBOR_16BIT() do { \
smp_in = sptr[pos]; \
} while (0)
#define LINEAR_INTERP() do { \
smp_l1 = ((int16)sptr[pos] << 8); \
smp_dt = ((int16)sptr[pos + 1] << 8) - smp_l1; \
smp_in = smp_l1 + (((frac >> 1) * smp_dt) >> (SMIX_SHIFT - 1)); \
} while (0)
#define LINEAR_INTERP_16BIT() do { \
smp_l1 = sptr[pos]; \
smp_dt = sptr[pos + 1] - smp_l1; \
smp_in = smp_l1 + (((frac >> 1) * smp_dt) >> (SMIX_SHIFT - 1)); \
} while (0)
/* The following lut settings are PRECOMPUTED. If you plan on changing these
* settings, you MUST also regenerate the arrays.
*/
/* number of bits used to scale spline coefs */
#define SPLINE_QUANTBITS 14
#define SPLINE_SHIFT (SPLINE_QUANTBITS)
/* log2(number) of precalculated splines (range is [4..14]) */
#define SPLINE_FRACBITS 10
#define SPLINE_LUTLEN (1L<<SPLINE_FRACBITS)
#define SPLINE_FRACSHIFT ((16 - SPLINE_FRACBITS) - 2)
#define SPLINE_FRACMASK (((1L << (16 - SPLINE_FRACSHIFT)) - 1) & ~3)
#define SPLINE_INTERP() do { \
int f = frac >> 6; \
smp_in = (cubic_spline_lut0[f] * sptr[(int)pos - 1] + \
cubic_spline_lut1[f] * sptr[pos ] + \
cubic_spline_lut3[f] * sptr[pos + 2] + \
cubic_spline_lut2[f] * sptr[pos + 1]) >> (SPLINE_SHIFT - 8); \
} while (0)
#define SPLINE_INTERP_16BIT() do { \
int f = frac >> 6; \
smp_in = (cubic_spline_lut0[f] * sptr[(int)pos - 1] + \
cubic_spline_lut1[f] * sptr[pos ] + \
cubic_spline_lut3[f] * sptr[pos + 2] + \
cubic_spline_lut2[f] * sptr[pos + 1]) >> SPLINE_SHIFT; \
} while (0)
#define LOOP_AC for (; count > ramp; count--)
#define LOOP for (; count; count--)
#define UPDATE_POS() do { \
frac += step; \
pos += frac >> SMIX_SHIFT; \
frac &= SMIX_MASK; \
} while (0)
#define MIX_MONO() do { \
*(buffer++) += smp_in * vl; \
} while (0)
#define MIX_MONO_AC() do { \
*(buffer++) += smp_in * (old_vl >> 8); old_vl += delta_l; \
} while (0)
#define MIX_MONO_FILTER() do { \
sl = (a0 * smp_in * vl + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \
fl2 = fl1; fl1 = sl; \
*(buffer++) += sl; \
} while (0)
#define MIX_MONO_FILTER_AC() do { \
int vl = old_vl >> 8; \
MIX_MONO_FILTER(); \
old_vl += delta_l; \
} while (0)
#define MIX_STEREO() do { \
*(buffer++) += smp_in * vr; \
*(buffer++) += smp_in * vl; \
} while (0)
#define MIX_STEREO_AC() do { \
*(buffer++) += smp_in * (old_vr >> 8); old_vr += delta_r; \
*(buffer++) += smp_in * (old_vl >> 8); old_vl += delta_l; \
} while (0)
#define MIX_STEREO_FILTER() do { \
sr = (a0 * smp_in * vr + b0 * fr1 + b1 * fr2) >> FILTER_SHIFT; \
fr2 = fr1; fr1 = sr; \
sl = (a0 * smp_in * vl + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \
fl2 = fl1; fl1 = sl; \
*(buffer++) += sr; \
*(buffer++) += sl; \
} while (0)
#define MIX_STEREO_FILTER_AC() do { \
int vr = old_vr >> 8; \
int vl = old_vl >> 8; \
MIX_STEREO_FILTER(); \
old_vr += delta_r; \
old_vl += delta_l; \
} while (0)
#define MIX_STEREO_FILTER_AC() do { \
int vr = old_vr >> 8; \
int vl = old_vl >> 8; \
MIX_STEREO_FILTER(); \
old_vr += delta_r; \
old_vl += delta_l; \
} while (0)
#define VAR_NORM(x) \
register int smp_in; \
x *sptr = vi->sptr; \
unsigned int pos = vi->pos; \
int frac = (1 << SMIX_SHIFT) * (vi->pos - (int)vi->pos)
#define VAR_LINEAR_MONO(x) \
VAR_NORM(x); \
int old_vl = vi->old_vl; \
int smp_l1, smp_dt
#define VAR_LINEAR_STEREO(x) \
VAR_LINEAR_MONO(x); \
int old_vr = vi->old_vr
#define VAR_SPLINE_MONO(x) \
int old_vl = vi->old_vl; \
VAR_NORM(x)
#define VAR_SPLINE_STEREO(x) \
VAR_SPLINE_MONO(x); \
int old_vr = vi->old_vr
#ifndef LIBXMP_CORE_DISABLE_IT
#define VAR_FILTER_MONO \
int fl1 = vi->filter.l1, fl2 = vi->filter.l2; \
int64 a0 = vi->filter.a0, b0 = vi->filter.b0, b1 = vi->filter.b1; \
int sl
#define VAR_FILTER_STEREO \
VAR_FILTER_MONO; \
int fr1 = vi->filter.r1, fr2 = vi->filter.r2; \
int sr
#define SAVE_FILTER_MONO() do { \
vi->filter.l1 = fl1; \
vi->filter.l2 = fl2; \
} while (0)
#define SAVE_FILTER_STEREO() do { \
SAVE_FILTER_MONO(); \
vi->filter.r1 = fr1; \
vi->filter.r2 = fr2; \
} while (0)
#endif
/*
* Nearest neighbor mixers
*/
/* Handler for 8 bit samples, nearest neighbor mono output
*/
MIXER(mono_8bit_nearest)
{
VAR_NORM(int8);
LOOP { NEAREST_NEIGHBOR(); MIX_MONO(); UPDATE_POS(); }
}
/* Handler for 16 bit samples, nearest neighbor mono output
*/
MIXER(mono_16bit_nearest)
{
VAR_NORM(int16);
LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_MONO(); UPDATE_POS(); }
}
/* Handler for 8 bit samples, nearest neighbor stereo output
*/
MIXER(stereo_8bit_nearest)
{
VAR_NORM(int8);
LOOP { NEAREST_NEIGHBOR(); MIX_STEREO(); UPDATE_POS(); }
}
/* Handler for 16 bit samples, nearest neighbor stereo output
*/
MIXER(stereo_16bit_nearest)
{
VAR_NORM(int16);
LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_STEREO(); UPDATE_POS(); }
}
/*
* Linear mixers
*/
/* Handler for 8 bit samples, linear interpolated mono output
*/
MIXER(mono_8bit_linear)
{
VAR_LINEAR_MONO(int8);
LOOP_AC { LINEAR_INTERP(); MIX_MONO_AC(); UPDATE_POS(); }
LOOP { LINEAR_INTERP(); MIX_MONO(); UPDATE_POS(); }
}
/* Handler for 16 bit samples, linear interpolated mono output
*/
MIXER(mono_16bit_linear)
{
VAR_LINEAR_MONO(int16);
LOOP_AC { LINEAR_INTERP_16BIT(); MIX_MONO_AC(); UPDATE_POS(); }
LOOP { LINEAR_INTERP_16BIT(); MIX_MONO(); UPDATE_POS(); }
}
/* Handler for 8 bit samples, linear interpolated stereo output
*/
MIXER(stereo_8bit_linear)
{
VAR_LINEAR_STEREO(int8);
LOOP_AC { LINEAR_INTERP(); MIX_STEREO_AC(); UPDATE_POS(); }
LOOP { LINEAR_INTERP(); MIX_STEREO(); UPDATE_POS(); }
}
/* Handler for 16 bit samples, linear interpolated stereo output
*/
MIXER(stereo_16bit_linear)
{
VAR_LINEAR_STEREO(int16);
LOOP_AC { LINEAR_INTERP_16BIT(); MIX_STEREO_AC(); UPDATE_POS(); }
LOOP { LINEAR_INTERP_16BIT(); MIX_STEREO(); UPDATE_POS(); }
}
#ifndef LIBXMP_CORE_DISABLE_IT
/* Handler for 8 bit samples, filtered linear interpolated mono output
*/
MIXER(mono_8bit_linear_filter)
{
VAR_LINEAR_MONO(int8);
VAR_FILTER_MONO;
LOOP_AC { LINEAR_INTERP(); MIX_MONO_FILTER_AC(); UPDATE_POS(); }
LOOP { LINEAR_INTERP(); MIX_MONO_FILTER(); UPDATE_POS(); }
SAVE_FILTER_MONO();
}
/* Handler for 16 bit samples, filtered linear interpolated mono output
*/
MIXER(mono_16bit_linear_filter)
{
VAR_LINEAR_MONO(int16);
VAR_FILTER_MONO;
LOOP_AC { LINEAR_INTERP_16BIT(); MIX_MONO_FILTER_AC(); UPDATE_POS(); }
LOOP { LINEAR_INTERP_16BIT(); MIX_MONO_FILTER(); UPDATE_POS(); }
SAVE_FILTER_MONO();
}
/* Handler for 8 bit samples, filtered linear interpolated stereo output
*/
MIXER(stereo_8bit_linear_filter)
{
VAR_LINEAR_STEREO(int8);
VAR_FILTER_STEREO;
LOOP_AC { LINEAR_INTERP(); MIX_STEREO_FILTER_AC(); UPDATE_POS(); }
LOOP { LINEAR_INTERP(); MIX_STEREO_FILTER(); UPDATE_POS(); }
SAVE_FILTER_STEREO();
}
/* Handler for 16 bit samples, filtered linear interpolated stereo output
*/
MIXER(stereo_16bit_linear_filter)
{
VAR_LINEAR_STEREO(int16);
VAR_FILTER_STEREO;
LOOP_AC { LINEAR_INTERP_16BIT(); MIX_STEREO_FILTER_AC(); UPDATE_POS(); }
LOOP { LINEAR_INTERP_16BIT(); MIX_STEREO_FILTER(); UPDATE_POS(); }
SAVE_FILTER_STEREO();
}
#endif
/*
* Spline mixers
*/
/* Handler for 8 bit samples, spline interpolated mono output
*/
MIXER(mono_8bit_spline)
{
VAR_SPLINE_MONO(int8);
LOOP_AC { SPLINE_INTERP(); MIX_MONO_AC(); UPDATE_POS(); }
LOOP { SPLINE_INTERP(); MIX_MONO(); UPDATE_POS(); }
}
/* Handler for 16 bit samples, spline interpolated mono output
*/
MIXER(mono_16bit_spline)
{
VAR_SPLINE_MONO(int16);
LOOP_AC { SPLINE_INTERP_16BIT(); MIX_MONO_AC(); UPDATE_POS(); }
LOOP { SPLINE_INTERP_16BIT(); MIX_MONO(); UPDATE_POS(); }
}
/* Handler for 8 bit samples, spline interpolated stereo output
*/
MIXER(stereo_8bit_spline)
{
VAR_SPLINE_STEREO(int8);
LOOP_AC { SPLINE_INTERP(); MIX_STEREO_AC(); UPDATE_POS(); }
LOOP { SPLINE_INTERP(); MIX_STEREO(); UPDATE_POS(); }
}
/* Handler for 16 bit samples, spline interpolated stereo output
*/
MIXER(stereo_16bit_spline)
{
VAR_SPLINE_STEREO(int16);
LOOP_AC { SPLINE_INTERP_16BIT(); MIX_STEREO_AC(); UPDATE_POS(); }
LOOP { SPLINE_INTERP_16BIT(); MIX_STEREO(); UPDATE_POS(); }
}
#ifndef LIBXMP_CORE_DISABLE_IT
/* Handler for 8 bit samples, filtered spline interpolated mono output
*/
MIXER(mono_8bit_spline_filter)
{
VAR_SPLINE_MONO(int8);
VAR_FILTER_MONO;
LOOP_AC { SPLINE_INTERP(); MIX_MONO_FILTER_AC(); UPDATE_POS(); }
LOOP { SPLINE_INTERP(); MIX_MONO_FILTER(); UPDATE_POS(); }
SAVE_FILTER_MONO();
}
/* Handler for 16 bit samples, filtered spline interpolated mono output
*/
MIXER(mono_16bit_spline_filter)
{
VAR_SPLINE_MONO(int16);
VAR_FILTER_MONO;
LOOP_AC { SPLINE_INTERP_16BIT(); MIX_MONO_FILTER_AC(); UPDATE_POS(); }
LOOP { SPLINE_INTERP_16BIT(); MIX_MONO_FILTER(); UPDATE_POS(); }
SAVE_FILTER_MONO();
}
/* Handler for 8 bit samples, filtered spline interpolated stereo output
*/
MIXER(stereo_8bit_spline_filter)
{
VAR_SPLINE_STEREO(int8);
VAR_FILTER_STEREO;
LOOP_AC { SPLINE_INTERP(); MIX_STEREO_FILTER_AC(); UPDATE_POS(); }
LOOP { SPLINE_INTERP(); MIX_STEREO_FILTER(); UPDATE_POS(); }
SAVE_FILTER_STEREO();
}
/* Handler for 16 bit samples, filtered spline interpolated stereo output
*/
MIXER(stereo_16bit_spline_filter)
{
VAR_SPLINE_STEREO(int16);
VAR_FILTER_STEREO;
LOOP_AC { SPLINE_INTERP_16BIT(); MIX_STEREO_FILTER_AC(); UPDATE_POS(); }
LOOP { SPLINE_INTERP_16BIT(); MIX_STEREO_FILTER(); UPDATE_POS(); }
SAVE_FILTER_STEREO();
}
#endif

View file

@ -0,0 +1,839 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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 <math.h>
#include "common.h"
#include "virtual.h"
#include "mixer.h"
#include "period.h"
#include "player.h" /* for set_sample_end() */
#ifdef LIBXMP_PAULA_SIMULATOR
#include "paula.h"
#endif
#define FLAG_16_BITS 0x01
#define FLAG_STEREO 0x02
#define FLAG_FILTER 0x04
#define FLAG_ACTIVE 0x10
/* #define FLAG_SYNTH 0x20 */
#define FIDX_FLAGMASK (FLAG_16_BITS | FLAG_STEREO | FLAG_FILTER)
#define DOWNMIX_SHIFT 12
#define LIM8_HI 127
#define LIM8_LO -128
#define LIM16_HI 32767
#define LIM16_LO -32768
#define MIX_FN(x) void libxmp_mix_##x(struct mixer_voice *, int32 *, int, int, int, int, int, int, int)
MIX_FN(mono_8bit_nearest);
MIX_FN(mono_8bit_linear);
MIX_FN(mono_16bit_nearest);
MIX_FN(mono_16bit_linear);
MIX_FN(stereo_8bit_nearest);
MIX_FN(stereo_8bit_linear);
MIX_FN(stereo_16bit_nearest);
MIX_FN(stereo_16bit_linear);
MIX_FN(mono_8bit_spline);
MIX_FN(mono_16bit_spline);
MIX_FN(stereo_8bit_spline);
MIX_FN(stereo_16bit_spline);
#ifndef LIBXMP_CORE_DISABLE_IT
MIX_FN(mono_8bit_linear_filter);
MIX_FN(mono_16bit_linear_filter);
MIX_FN(stereo_8bit_linear_filter);
MIX_FN(stereo_16bit_linear_filter);
MIX_FN(mono_8bit_spline_filter);
MIX_FN(mono_16bit_spline_filter);
MIX_FN(stereo_8bit_spline_filter);
MIX_FN(stereo_16bit_spline_filter);
#endif
#ifdef LIBXMP_PAULA_SIMULATOR
MIX_FN(mono_a500);
MIX_FN(mono_a500_filter);
MIX_FN(stereo_a500);
MIX_FN(stereo_a500_filter);
#endif
/* Mixers array index:
*
* bit 0: 0=8 bit sample, 1=16 bit sample
* bit 1: 0=mono output, 1=stereo output
* bit 2: 0=unfiltered, 1=filtered
*/
typedef void (*MIX_FP) (struct mixer_voice *, int32 *, int, int, int, int, int, int, int);
static MIX_FP nearest_mixers[] = {
libxmp_mix_mono_8bit_nearest,
libxmp_mix_mono_16bit_nearest,
libxmp_mix_stereo_8bit_nearest,
libxmp_mix_stereo_16bit_nearest,
#ifndef LIBXMP_CORE_DISABLE_IT
libxmp_mix_mono_8bit_nearest,
libxmp_mix_mono_16bit_nearest,
libxmp_mix_stereo_8bit_nearest,
libxmp_mix_stereo_16bit_nearest,
#endif
};
static MIX_FP linear_mixers[] = {
libxmp_mix_mono_8bit_linear,
libxmp_mix_mono_16bit_linear,
libxmp_mix_stereo_8bit_linear,
libxmp_mix_stereo_16bit_linear,
#ifndef LIBXMP_CORE_DISABLE_IT
libxmp_mix_mono_8bit_linear_filter,
libxmp_mix_mono_16bit_linear_filter,
libxmp_mix_stereo_8bit_linear_filter,
libxmp_mix_stereo_16bit_linear_filter
#endif
};
static MIX_FP spline_mixers[] = {
libxmp_mix_mono_8bit_spline,
libxmp_mix_mono_16bit_spline,
libxmp_mix_stereo_8bit_spline,
libxmp_mix_stereo_16bit_spline,
#ifndef LIBXMP_CORE_DISABLE_IT
libxmp_mix_mono_8bit_spline_filter,
libxmp_mix_mono_16bit_spline_filter,
libxmp_mix_stereo_8bit_spline_filter,
libxmp_mix_stereo_16bit_spline_filter
#endif
};
#ifdef LIBXMP_PAULA_SIMULATOR
static MIX_FP a500_mixers[] = {
libxmp_mix_mono_a500,
NULL,
libxmp_mix_stereo_a500,
NULL,
NULL,
NULL,
NULL,
NULL
};
static MIX_FP a500led_mixers[] = {
libxmp_mix_mono_a500_filter,
NULL,
libxmp_mix_stereo_a500_filter,
NULL,
NULL,
NULL,
NULL,
NULL
};
#endif
/* Downmix 32bit samples to 8bit, signed or unsigned, mono or stereo output */
static void downmix_int_8bit(char *dest, int32 *src, int num, int amp, int offs)
{
int smp;
int shift = DOWNMIX_SHIFT + 8 - amp;
for (; num--; src++, dest++) {
smp = *src >> shift;
if (smp > LIM8_HI) {
*dest = LIM8_HI;
} else if (smp < LIM8_LO) {
*dest = LIM8_LO;
} else {
*dest = smp;
}
if (offs) *dest += offs;
}
}
/* Downmix 32bit samples to 16bit, signed or unsigned, mono or stereo output */
static void downmix_int_16bit(int16 *dest, int32 *src, int num, int amp, int offs)
{
int smp;
int shift = DOWNMIX_SHIFT - amp;
for (; num--; src++, dest++) {
smp = *src >> shift;
if (smp > LIM16_HI) {
*dest = LIM16_HI;
} else if (smp < LIM16_LO) {
*dest = LIM16_LO;
} else {
*dest = smp;
}
if (offs) *dest += offs;
}
}
static void anticlick(struct mixer_voice *vi)
{
vi->flags |= ANTICLICK;
vi->old_vl = 0;
vi->old_vr = 0;
}
/* Ok, it's messy, but it works :-) Hipolito */
static void do_anticlick(struct context_data *ctx, int voc, int32 *buf, int count)
{
struct player_data *p = &ctx->p;
struct mixer_data *s = &ctx->s;
struct mixer_voice *vi = &p->virt.voice_array[voc];
int smp_l, smp_r, max_x2;
int discharge = s->ticksize >> ANTICLICK_SHIFT;
smp_r = vi->sright;
smp_l = vi->sleft;
vi->sright = vi->sleft = 0;
if (smp_l == 0 && smp_r == 0) {
return;
}
if (buf == NULL) {
buf = s->buf32;
count = discharge;
} else if (count > discharge) {
count = discharge;
}
if (count <= 0) {
return;
}
max_x2 = count * count;
while (count--) {
if (~s->format & XMP_FORMAT_MONO) {
*buf++ += (count * (smp_r >> 10) / max_x2 * count) << 10;
}
*buf++ += (count * (smp_l >> 10) / max_x2 * count) << 10;
}
}
static void set_sample_end(struct context_data *ctx, int voc, int end)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct mixer_voice *vi = &p->virt.voice_array[voc];
struct channel_data *xc;
if ((uint32)voc >= p->virt.maxvoc)
return;
xc = &p->xc_data[vi->chn];
if (end) {
SET_NOTE(NOTE_SAMPLE_END);
if (HAS_QUIRK(QUIRK_RSTCHN)) {
libxmp_virt_resetvoice(ctx, voc, 0);
}
} else {
RESET_NOTE(NOTE_SAMPLE_END);
}
}
static void adjust_voice_end(struct mixer_voice *vi, struct xmp_sample *xxs)
{
if (xxs->flg & XMP_SAMPLE_LOOP) {
if ((xxs->flg & XMP_SAMPLE_LOOP_FULL) && (~vi->flags & SAMPLE_LOOP)) {
vi->end = xxs->len;
} else {
vi->end = xxs->lpe;
}
} else {
vi->end = xxs->len;
}
}
static void loop_reposition(struct context_data *ctx, struct mixer_voice *vi, struct xmp_sample *xxs)
{
#ifndef LIBXMP_CORE_DISABLE_IT
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;
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
vi->end += loop_size; /* unrolled loop */
vi->pos -= loop_size; /* forward loop */
#ifndef LIBXMP_CORE_DISABLE_IT
/* OpenMPT Bidi-Loops.it: "In Impulse Trackers software mixer,
* ping-pong loops are shortened by one sample.
*/
if (IS_PLAYER_MODE_IT()) {
vi->end--;
vi->pos++;
}
#endif
}
}
/* Prepare the mixer for the next tick */
void libxmp_mixer_prepare(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct mixer_data *s = &ctx->s;
int bytelen;
s->ticksize = s->freq * m->time_factor * m->rrate / p->bpm / 1000;
bytelen = s->ticksize * sizeof(int);
if (~s->format & XMP_FORMAT_MONO) {
bytelen *= 2;
}
memset(s->buf32, 0, bytelen);
}
/* Fill the output buffer calling one of the handlers. The buffer contains
* sound for one tick (a PAL frame or 1/50s for standard vblank-timed mods)
*/
void libxmp_mixer_softmixer(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
struct mixer_data *s = &ctx->s;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
struct xmp_sample *xxs;
struct mixer_voice *vi;
double step;
int samples, size;
int vol_l, vol_r, voc, usmp;
int prev_l, prev_r = 0;
int lps, lpe;
int32 *buf_pos;
MIX_FP mix_fn;
MIX_FP *mixerset;
switch (s->interp) {
case XMP_INTERP_NEAREST:
mixerset = nearest_mixers;
break;
case XMP_INTERP_LINEAR:
mixerset = linear_mixers;
break;
case XMP_INTERP_SPLINE:
mixerset = spline_mixers;
break;
default:
mixerset = linear_mixers;
}
#ifdef LIBXMP_PAULA_SIMULATOR
if (p->flags & XMP_FLAGS_A500) {
if (IS_AMIGA_MOD()) {
if (p->filter) {
mixerset = a500led_mixers;
} else {
mixerset = a500_mixers;
}
}
}
#endif
libxmp_mixer_prepare(ctx);
for (voc = 0; voc < p->virt.maxvoc; voc++) {
int c5spd, rampsize, delta_l, delta_r;
vi = &p->virt.voice_array[voc];
if (vi->flags & ANTICLICK) {
if (s->interp > XMP_INTERP_NEAREST) {
do_anticlick(ctx, voc, NULL, 0);
}
vi->flags &= ~ANTICLICK;
}
if (vi->chn < 0) {
continue;
}
if (vi->period < 1) {
libxmp_virt_resetvoice(ctx, voc, 1);
continue;
}
vi->pos0 = vi->pos;
buf_pos = s->buf32;
if (vi->pan == PAN_SURROUND) {
vol_r = vi->vol * 0x80;
vol_l = -vi->vol * 0x80;
} else {
vol_r = vi->vol * (0x80 - vi->pan);
vol_l = vi->vol * (0x80 + vi->pan);
}
if (vi->smp < mod->smp) {
xxs = &mod->xxs[vi->smp];
c5spd = m->xtra[vi->smp].c5spd;
} else {
xxs = &ctx->smix.xxs[vi->smp - mod->smp];
c5spd = m->c4rate;
}
step = C4_PERIOD * c5spd / s->freq / vi->period;
if (step < 0.001) { /* otherwise m5v-nwlf.it crashes */
continue;
}
#ifndef LIBXMP_CORE_DISABLE_IT
if (xxs->flg & XMP_SAMPLE_SLOOP && vi->smp < mod->smp) {
if (~vi->flags & VOICE_RELEASE) {
if (vi->pos < m->xsmp[vi->smp].lpe) {
xxs = &m->xsmp[vi->smp];
}
}
}
adjust_voice_end(vi, xxs);
#endif
lps = xxs->lps;
lpe = xxs->lpe;
if (p->flags & XMP_FLAGS_FIXLOOP) {
lps >>= 1;
}
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
vi->end += lpe - lps;
#ifndef LIBXMP_CORE_DISABLE_IT
if (IS_PLAYER_MODE_IT()) {
vi->end--;
}
#endif
}
rampsize = s->ticksize >> ANTICLICK_SHIFT;
delta_l = (vol_l - vi->old_vl) / rampsize;
delta_r = (vol_r - vi->old_vr) / rampsize;
usmp = 0;
for (size = s->ticksize; size > 0; ) {
int split_noloop = 0;
if (p->xc_data[vi->chn].split) {
split_noloop = 1;
}
/* How many samples we can write before the loop break
* or sample end... */
if (vi->pos >= vi->end) {
samples = 0;
usmp = 1;
} else {
int s = ceil(((double)vi->end - vi->pos) / step);
/* ...inside the tick boundaries */
if (s > size) {
s = size;
}
samples = s;
if (samples > 0) {
usmp = 0;
}
}
if (vi->vol) {
int mix_size = samples;
int mixer_id = vi->fidx & FIDX_FLAGMASK;
if (~s->format & XMP_FORMAT_MONO) {
mix_size *= 2;
}
/* For Hipolito's anticlick routine */
if (samples > 0) {
if (~s->format & XMP_FORMAT_MONO) {
prev_r = buf_pos[mix_size - 2];
}
prev_l = buf_pos[mix_size - 1];
} else {
prev_r = prev_l = 0;
}
#ifndef LIBXMP_CORE_DISABLE_IT
/* See OpenMPT env-flt-max.it */
if (vi->filter.cutoff >= 0xfe &&
vi->filter.resonance == 0) {
mixer_id &= ~FLAG_FILTER;
}
#endif
mix_fn = mixerset[mixer_id];
/* Call the output handler */
if (samples > 0 && vi->sptr != NULL) {
int rsize = 0;
if (rampsize > samples) {
rampsize -= samples;
} else {
rsize = samples - rampsize;
rampsize = 0;
}
if (delta_l == 0 && delta_r == 0) {
/* no need to ramp */
rsize = samples;
}
if (mix_fn != NULL) {
mix_fn(vi, buf_pos, samples,
vol_l >> 8, vol_r >> 8, step * (1 << SMIX_SHIFT), rsize, delta_l, delta_r);
}
buf_pos += mix_size;
vi->old_vl += samples * delta_l;
vi->old_vr += samples * delta_r;
/* For Hipolito's anticlick routine */
if (~s->format & XMP_FORMAT_MONO) {
vi->sright = buf_pos[-2] - prev_r;
}
vi->sleft = buf_pos[-1] - prev_l;
}
}
vi->pos += step * samples;
/* No more samples in this tick */
size -= samples + usmp;
if (size <= 0) {
if (xxs->flg & XMP_SAMPLE_LOOP) {
if (vi->pos + step > vi->end) {
vi->pos += step;
loop_reposition(ctx, vi, xxs);
}
}
continue;
}
/* First sample loop run */
if ((~xxs->flg & XMP_SAMPLE_LOOP) || split_noloop) {
do_anticlick(ctx, voc, buf_pos, size);
set_sample_end(ctx, voc, 1);
size = 0;
continue;
}
loop_reposition(ctx, vi, xxs);
}
vi->old_vl = vol_l;
vi->old_vr = vol_r;
}
/* Render final frame */
size = s->ticksize;
if (~s->format & XMP_FORMAT_MONO) {
size *= 2;
}
if (size > XMP_MAX_FRAMESIZE) {
size = XMP_MAX_FRAMESIZE;
}
if (s->format & XMP_FORMAT_8BIT) {
downmix_int_8bit(s->buffer, s->buf32, size, s->amplify,
s->format & XMP_FORMAT_UNSIGNED ? 0x80 : 0);
} else {
downmix_int_16bit((int16 *)s->buffer, s->buf32, size,s->amplify,
s->format & XMP_FORMAT_UNSIGNED ? 0x8000 : 0);
}
s->dtright = s->dtleft = 0;
}
void libxmp_mixer_voicepos(struct context_data *ctx, int voc, double pos, int ac)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct mixer_voice *vi = &p->virt.voice_array[voc];
struct xmp_sample *xxs;
int lps;
if (vi->smp < m->mod.smp) {
xxs = &m->mod.xxs[vi->smp];
} else {
xxs = &ctx->smix.xxs[vi->smp - m->mod.smp];
}
if (xxs->flg & XMP_SAMPLE_SYNTH) {
return;
}
vi->pos = pos;
adjust_voice_end(vi, xxs);
if (vi->pos >= vi->end) {
if (xxs->flg & XMP_SAMPLE_LOOP) {
vi->pos = xxs->lps;
} else {
vi->pos = xxs->len;
}
}
lps = xxs->lps;
if (p->flags & XMP_FLAGS_FIXLOOP) {
lps >>= 1;
}
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
vi->end += (xxs->lpe - lps);
#ifndef LIBXMP_CORE_DISABLE_IT
if (IS_PLAYER_MODE_IT()) {
vi->end--;
}
#endif
}
if (ac) {
anticlick(vi);
}
}
double libxmp_mixer_getvoicepos(struct context_data *ctx, int voc)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi = &p->virt.voice_array[voc];
struct xmp_sample *xxs;
xxs = libxmp_get_sample(ctx, vi->smp);
if (xxs->flg & XMP_SAMPLE_SYNTH) {
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;
}
void libxmp_mixer_setpatch(struct context_data *ctx, int voc, int smp, int ac)
{
struct player_data *p = &ctx->p;
#ifndef LIBXMP_CORE_DISABLE_IT
struct module_data *m = &ctx->m;
#endif
struct mixer_data *s = &ctx->s;
struct mixer_voice *vi = &p->virt.voice_array[voc];
struct xmp_sample *xxs;
xxs = libxmp_get_sample(ctx, smp);
vi->smp = smp;
vi->vol = 0;
vi->pan = 0;
vi->flags &= ~SAMPLE_LOOP;
vi->fidx = 0;
if (~s->format & XMP_FORMAT_MONO) {
vi->fidx |= FLAG_STEREO;
}
set_sample_end(ctx, voc, 0);
/*mixer_setvol(ctx, voc, 0);*/
vi->sptr = xxs->data;
vi->fidx |= FLAG_ACTIVE;
#ifndef LIBXMP_CORE_DISABLE_IT
if (HAS_QUIRK(QUIRK_FILTER) && s->dsp & XMP_DSP_LOWPASS) {
vi->fidx |= FLAG_FILTER;
}
#endif
if (xxs->flg & XMP_SAMPLE_16BIT) {
vi->fidx |= FLAG_16_BITS;
}
libxmp_mixer_voicepos(ctx, voc, 0, ac);
}
void libxmp_mixer_setnote(struct context_data *ctx, int voc, int note)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi = &p->virt.voice_array[voc];
/* FIXME: Workaround for crash on notes that are too high
* see 6nations.it (+114 transposition on instrument 16)
*/
if (note > 149) {
note = 149;
}
vi->note = note;
vi->period = libxmp_note_to_period_mix(note, 0);
anticlick(vi);
}
void libxmp_mixer_setperiod(struct context_data *ctx, int voc, double period)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi = &p->virt.voice_array[voc];
vi->period = period;
}
void libxmp_mixer_setvol(struct context_data *ctx, int voc, int vol)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi = &p->virt.voice_array[voc];
if (vol == 0) {
anticlick(vi);
}
vi->vol = vol;
}
void libxmp_mixer_release(struct context_data *ctx, int voc, int rel)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi = &p->virt.voice_array[voc];
if (rel) {
vi->flags |= VOICE_RELEASE;
} else {
vi->flags &= ~VOICE_RELEASE;
}
}
void libxmp_mixer_seteffect(struct context_data *ctx, int voc, int type, int val)
{
#ifndef LIBXMP_CORE_DISABLE_IT
struct player_data *p = &ctx->p;
struct mixer_voice *vi = &p->virt.voice_array[voc];
switch (type) {
case DSP_EFFECT_CUTOFF:
vi->filter.cutoff = val;
break;
case DSP_EFFECT_RESONANCE:
vi->filter.resonance = val;
break;
case DSP_EFFECT_FILTER_A0:
vi->filter.a0 = val;
break;
case DSP_EFFECT_FILTER_B0:
vi->filter.b0 = val;
break;
case DSP_EFFECT_FILTER_B1:
vi->filter.b1 = val;
break;
}
#endif
}
void libxmp_mixer_setpan(struct context_data *ctx, int voc, int pan)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi = &p->virt.voice_array[voc];
vi->pan = pan;
}
int libxmp_mixer_numvoices(struct context_data *ctx, int num)
{
struct mixer_data *s = &ctx->s;
if (num > s->numvoc || num < 0) {
return s->numvoc;
} else {
return num;
}
}
int libxmp_mixer_on(struct context_data *ctx, int rate, int format, int c4rate)
{
struct mixer_data *s = &ctx->s;
s->buffer = (char *) calloc(2, XMP_MAX_FRAMESIZE);
if (s->buffer == NULL)
goto err;
s->buf32 = (int32 *) calloc(sizeof(int32), XMP_MAX_FRAMESIZE);
if (s->buf32 == NULL)
goto err1;
s->freq = rate;
s->format = format;
s->amplify = DEFAULT_AMPLIFY;
s->mix = DEFAULT_MIX;
/* s->pbase = C4_PERIOD * c4rate / s->freq; */(void) c4rate;
s->interp = XMP_INTERP_LINEAR; /* default interpolation type */
s->dsp = XMP_DSP_LOWPASS; /* enable filters by default */
/* s->numvoc = SMIX_NUMVOC; */
s->dtright = s->dtleft = 0;
return 0;
err1:
free(s->buffer);
s->buffer = NULL;
err:
return -1;
}
void libxmp_mixer_off(struct context_data *ctx)
{
struct mixer_data *s = &ctx->s;
free(s->buffer);
free(s->buf32);
s->buf32 = NULL;
s->buffer = NULL;
}

View file

@ -0,0 +1,78 @@
#ifndef LIBXMP_MIXER_H
#define LIBXMP_MIXER_H
#define C4_PERIOD 428.0
#define SMIX_NUMVOC 128 /* default number of softmixer voices */
#define SMIX_SHIFT 16
#define SMIX_MASK 0xffff
#define FILTER_SHIFT 16
#define ANTICLICK_SHIFT 3
#ifdef LIBXMP_PAULA_SIMULATOR
#include "paula.h"
#endif
#define MIXER(f) void libxmp_mix_##f(struct mixer_voice *vi, int *buffer, \
int count, int vl, int vr, int step, int ramp, int delta_l, int delta_r)
struct mixer_voice {
int chn; /* channel number */
int root; /* */
int note; /* */
#define PAN_SURROUND 0x8000
int pan; /* */
int vol; /* */
double period; /* current period */
double pos; /* position in sample */
int pos0; /* position in sample before mixing */
int fidx; /* mixer function index */
int ins; /* instrument number */
int smp; /* sample number */
int end; /* loop end */
int act; /* nna info & status of voice */
int old_vl; /* previous volume, left channel */
int old_vr; /* previous volume, right channel */
int sleft; /* last left sample output, in 32bit */
int sright; /* last right sample output, in 32bit */
#define VOICE_RELEASE (1 << 0)
#define ANTICLICK (1 << 1)
#define SAMPLE_LOOP (1 << 2)
int flags; /* flags */
void *sptr; /* sample pointer */
#ifdef LIBXMP_PAULA_SIMULATOR
struct paula_state *paula; /* paula simulation state */
#endif
#ifndef LIBXMP_CORE_DISABLE_IT
struct {
int r1; /* filter variables */
int r2;
int l1;
int l2;
int a0;
int b0;
int b1;
int cutoff;
int resonance;
} filter;
#endif
};
int libxmp_mixer_on (struct context_data *, int, int, int);
void libxmp_mixer_off (struct context_data *);
void libxmp_mixer_setvol (struct context_data *, int, int);
void libxmp_mixer_seteffect (struct context_data *, int, int, int);
void libxmp_mixer_setpan (struct context_data *, int, int);
int libxmp_mixer_numvoices (struct context_data *, int);
void libxmp_mixer_softmixer (struct context_data *);
void libxmp_mixer_reset (struct context_data *);
void libxmp_mixer_setpatch (struct context_data *, int, int, int);
void libxmp_mixer_voicepos (struct context_data *, int, double, int);
double libxmp_mixer_getvoicepos(struct context_data *, int);
void libxmp_mixer_setnote (struct context_data *, int, int);
void libxmp_mixer_setperiod (struct context_data *, int, double);
void libxmp_mixer_release (struct context_data *, int, int);
#endif /* LIBXMP_MIXER_H */

View file

@ -0,0 +1,55 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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.
*/
struct mod_instrument {
uint8 name[22]; /* Instrument name */
uint16 size; /* Sample length in 16-bit words */
int8 finetune; /* Finetune (signed nibble) */
int8 volume; /* Linear playback volume */
uint16 loop_start; /* Loop start in 16-bit words */
uint16 loop_size; /* Loop length in 16-bit words */
};
struct mod_header {
uint8 name[20];
struct mod_instrument ins[31];
uint8 len;
uint8 restart; /* Number of patterns in Soundtracker,
* Restart in Noisetracker/Startrekker,
* 0x7F in Protracker
*/
uint8 order[128];
uint8 magic[4];
};
#ifndef LIBXMP_CORE_PLAYER
/* Soundtracker 15-instrument module header */
struct st_header {
uint8 name[20];
struct mod_instrument ins[15];
uint8 len;
uint8 restart;
uint8 order[128];
};
#endif

View file

@ -0,0 +1,234 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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.
*/
/* This loader recognizes the following variants of the Protracker
* module format:
*
* - Protracker M.K.
* - Fasttracker ?CHN and ??CH
*/
#include <ctype.h>
#include <limits.h>
#include "loader.h"
#include "mod.h"
static int mod_test(HIO_HANDLE *, char *, const int);
static int mod_load(struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_mod = {
"Protracker",
mod_test,
mod_load
};
static int mod_test(HIO_HANDLE *f, char *t, const int start)
{
int i;
char buf[4];
hio_seek(f, start + 1080, SEEK_SET);
if (hio_read(buf, 1, 4, f) < 4)
return -1;
if (!strncmp(buf + 2, "CH", 2) && isdigit((int)buf[0])
&& isdigit((int)buf[1])) {
i = (buf[0] - '0') * 10 + buf[1] - '0';
if (i > 0 && i <= 32) {
goto found;
}
}
if (!strncmp(buf + 1, "CHN", 3) && isdigit((int)*buf)) {
if (*buf >= '0' && *buf <= '9') {
goto found;
}
}
if (memcmp(buf, "M.K.", 4))
return -1;
found:
hio_seek(f, start + 0, SEEK_SET);
libxmp_read_title(f, t, 20);
return 0;
}
static int mod_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
struct xmp_module *mod = &m->mod;
int i, j;
struct xmp_event *event;
struct mod_header mh;
uint8 mod_event[4];
char magic[8];
int ptkloop = 0; /* Protracker loop */
LOAD_INIT();
mod->ins = 31;
mod->smp = mod->ins;
mod->chn = 0;
m->quirk |= QUIRK_PROTRACK;
m->period_type = PERIOD_MODRNG;
hio_read(mh.name, 20, 1, f);
for (i = 0; i < 31; i++) {
hio_read(mh.ins[i].name, 22, 1, f); /* Instrument name */
mh.ins[i].size = hio_read16b(f); /* Length in 16-bit words */
mh.ins[i].finetune = hio_read8(f); /* Finetune (signed nibble) */
mh.ins[i].volume = hio_read8(f); /* Linear playback volume */
mh.ins[i].loop_start = hio_read16b(f); /* Loop start in 16-bit words */
mh.ins[i].loop_size = hio_read16b(f); /* Loop size in 16-bit words */
}
mh.len = hio_read8(f);
mh.restart = hio_read8(f);
hio_read(mh.order, 128, 1, f);
memset(magic, 0, 8);
hio_read(magic, 4, 1, f);
if (!memcmp(magic, "M.K.", 4)) {
mod->chn = 4;
} else if (!strncmp(magic + 2, "CH", 2) &&
isdigit((int)magic[0]) && isdigit((int)magic[1])) {
mod->chn = (*magic - '0') * 10 + magic[1] - '0';
} else if (!strncmp(magic + 1, "CHN", 3) && isdigit((int)*magic)) {
mod->chn = *magic - '0';
} else {
return -1;
}
strncpy(mod->name, (char *) mh.name, 20);
mod->len = mh.len;
/* mod->rst = mh.restart; */
if (mod->rst >= mod->len)
mod->rst = 0;
memcpy(mod->xxo, mh.order, 128);
for (i = 0; i < 128; i++) {
/* This fixes dragnet.mod (garbage in the order list) */
if (mod->xxo[i] > 0x7f)
break;
if (mod->xxo[i] > mod->pat)
mod->pat = mod->xxo[i];
}
mod->pat++;
if (libxmp_init_instrument(m) < 0)
return -1;
for (i = 0; i < mod->ins; i++) {
struct xmp_instrument *xxi;
struct xmp_subinstrument *sub;
struct xmp_sample *xxs;
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
return -1;
xxi = &mod->xxi[i];
sub = &xxi->sub[0];
xxs = &mod->xxs[i];
xxs->len = 2 * mh.ins[i].size;
xxs->lps = 2 * mh.ins[i].loop_start;
xxs->lpe = xxs->lps + 2 * mh.ins[i].loop_size;
if (xxs->lpe > xxs->len) {
xxs->lpe = xxs->len;
}
xxs->flg = (mh.ins[i].loop_size > 1 && xxs->lpe >= 4) ?
XMP_SAMPLE_LOOP : 0;
sub->fin = (int8) (mh.ins[i].finetune << 4);
sub->vol = mh.ins[i].volume;
sub->pan = 0x80;
sub->sid = i;
libxmp_instrument_name(mod, i, mh.ins[i].name, 22);
if (xxs->len > 0) {
xxi->nsm = 1;
}
}
mod->trk = mod->chn * mod->pat;
libxmp_set_type(m, (mod->chn == 4) ? "Protracker" : "Fasttracker");
MODULE_INFO();
for (i = 0; i < mod->ins; i++) {
D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c V%02x %+d %c\n",
i, mod->xxi[i].name,
mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe,
(mh.ins[i].loop_size > 1 && mod->xxs[i].lpe > 8) ?
'L' : ' ', mod->xxi[i].sub[0].vol,
mod->xxi[i].sub[0].fin >> 4,
ptkloop && mod->xxs[i].lps == 0 && mh.ins[i].loop_size > 1 &&
mod->xxs[i].len > mod->xxs[i].lpe ? '!' : ' ');
}
if (libxmp_init_pattern(mod) < 0)
return -1;
/* Load and convert patterns */
D_(D_INFO "Stored patterns: %d", mod->pat);
for (i = 0; i < mod->pat; i++) {
if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0)
return -1;
for (j = 0; j < (64 * mod->chn); j++) {
event = &EVENT(i, j % mod->chn, j / mod->chn);
if (hio_read(mod_event, 1, 4, f) < 4) {
return -1;
}
libxmp_decode_protracker_event(event, mod_event);
}
}
/* Load samples */
D_(D_INFO "Stored samples: %d", mod->smp);
for (i = 0; i < mod->smp; i++) {
int flags;
if (!mod->xxs[i].len)
continue;
flags = ptkloop ? SAMPLE_FLAG_FULLREP : 0;
if (libxmp_load_sample(m, f, flags, &mod->xxs[i], NULL) < 0)
return -1;
}
if (mod->chn > 4) {
m->quirk &= ~QUIRK_PROTRACK;
m->quirk |= QUIRKS_FT2 | QUIRK_FTMOD;
m->read_event_type = READ_EVENT_FT2;
m->period_type = PERIOD_AMIGA;
}
return 0;
}

View file

@ -0,0 +1,266 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "common.h"
#include "period.h"
#include <math.h>
#ifdef LIBXMP_PAULA_SIMULATOR
/*
* Period table from the Protracker V2.1A play routine
*/
static uint16 pt_period_table[16][36] = {
/* Tuning 0, Normal */
{
856,808,762,720,678,640,604,570,538,508,480,453,
428,404,381,360,339,320,302,285,269,254,240,226,
214,202,190,180,170,160,151,143,135,127,120,113
},
/* Tuning 1 */
{
850,802,757,715,674,637,601,567,535,505,477,450,
425,401,379,357,337,318,300,284,268,253,239,225,
213,201,189,179,169,159,150,142,134,126,119,113
},
/* Tuning 2 */
{
844,796,752,709,670,632,597,563,532,502,474,447,
422,398,376,355,335,316,298,282,266,251,237,224,
211,199,188,177,167,158,149,141,133,125,118,112
},
/* Tuning 3 */
{
838,791,746,704,665,628,592,559,528,498,470,444,
419,395,373,352,332,314,296,280,264,249,235,222,
209,198,187,176,166,157,148,140,132,125,118,111
},
/* Tuning 4 */
{
832,785,741,699,660,623,588,555,524,495,467,441,
416,392,370,350,330,312,294,278,262,247,233,220,
208,196,185,175,165,156,147,139,131,124,117,110
},
/* Tuning 5 */
{
826,779,736,694,655,619,584,551,520,491,463,437,
413,390,368,347,328,309,292,276,260,245,232,219,
206,195,184,174,164,155,146,138,130,123,116,109
},
/* Tuning 6 */
{
820,774,730,689,651,614,580,547,516,487,460,434,
410,387,365,345,325,307,290,274,258,244,230,217,
205,193,183,172,163,154,145,137,129,122,115,109
},
/* Tuning 7 */
{
814,768,725,684,646,610,575,543,513,484,457,431,
407,384,363,342,323,305,288,272,256,242,228,216,
204,192,181,171,161,152,144,136,128,121,114,108
},
/* Tuning -8 */
{
907,856,808,762,720,678,640,604,570,538,508,480,
453,428,404,381,360,339,320,302,285,269,254,240,
226,214,202,190,180,170,160,151,143,135,127,120
},
/* Tuning -7 */
{
900,850,802,757,715,675,636,601,567,535,505,477,
450,425,401,379,357,337,318,300,284,268,253,238,
225,212,200,189,179,169,159,150,142,134,126,119
},
/* Tuning -6 */
{
894,844,796,752,709,670,632,597,563,532,502,474,
447,422,398,376,355,335,316,298,282,266,251,237,
223,211,199,188,177,167,158,149,141,133,125,118
},
/* Tuning -5 */
{
887,838,791,746,704,665,628,592,559,528,498,470,
444,419,395,373,352,332,314,296,280,264,249,235,
222,209,198,187,176,166,157,148,140,132,125,118
},
/* Tuning -4 */
{
881,832,785,741,699,660,623,588,555,524,494,467,
441,416,392,370,350,330,312,294,278,262,247,233,
220,208,196,185,175,165,156,147,139,131,123,117
},
/* Tuning -3 */
{
875,826,779,736,694,655,619,584,551,520,491,463,
437,413,390,368,347,328,309,292,276,260,245,232,
219,206,195,184,174,164,155,146,138,130,123,116
},
/* Tuning -2 */
{
868,820,774,730,689,651,614,580,547,516,487,460,
434,410,387,365,345,325,307,290,274,258,244,230,
217,205,193,183,172,163,154,145,137,129,122,115
},
/* Tuning -1 */
{
862,814,768,725,684,646,610,575,543,513,484,457,
431,407,384,363,342,323,305,288,272,256,242,228,
216,203,192,181,171,161,152,144,136,128,121,114
}
};
#endif
#ifndef M_LN2
#define M_LN2 0.69314718055994530942
#endif
#if !defined(HAVE_ROUND) || defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DJGPP__)
static inline double libxmp_round(double val)
{
return (val >= 0.0)? floor(val + 0.5) : ceil(val - 0.5);
}
#else
#define libxmp_round round
#endif
#ifdef LIBXMP_PAULA_SIMULATOR
/* Get period from note using Protracker tuning */
static inline int libxmp_note_to_period_pt(int n, int f)
{
if (n < MIN_NOTE_MOD || n > MAX_NOTE_MOD) {
return -1;
}
n -= 48;
f >>= 4;
if (f < -8 || f > 7) {
return 0;
}
if (f < 0) {
f += 16;
}
return (int)pt_period_table[f][n];
}
#endif
/* Get period from note */
double libxmp_note_to_period(struct context_data *ctx, int n, int f, double adj)
{
double d, per;
struct module_data *m = &ctx->m;
#ifdef LIBXMP_PAULA_SIMULATOR
struct player_data *p = &ctx->p;
/* If mod replayer, modrng and Amiga mixing are active */
if (p->flags & XMP_FLAGS_A500) {
if (IS_AMIGA_MOD()) {
return libxmp_note_to_period_pt(n, f);
}
}
#endif
d = (double)n + (double)f / 128;
switch (m->period_type) {
case PERIOD_LINEAR:
per = (240.0 - d) * 16; /* Linear */
break;
case PERIOD_CSPD:
per = 8363.0 * pow(2, n / 12) / 32 + f; /* Hz */
break;
default:
per = PERIOD_BASE / pow(2, d / 12); /* Amiga */
}
#ifndef LIBXMP_CORE_PLAYER
if (adj > 0.1) {
per *= adj;
}
#endif
return per;
}
/* For the software mixer */
double libxmp_note_to_period_mix(int n, int b)
{
double d = (double)n + (double)b / 12800;
return PERIOD_BASE / pow(2, d / 12);
}
/* Get note from period */
/* This function is used only by the MOD loader */
int libxmp_period_to_note(int p)
{
if (p <= 0) {
return 0;
}
return libxmp_round(12.0 * log(PERIOD_BASE / p) / M_LN2) + 1;
}
/* Get pitchbend from base note and amiga period */
int libxmp_period_to_bend(struct context_data *ctx, double p, int n, double adj)
{
struct module_data *m = &ctx->m;
double d;
if (n == 0) {
return 0;
}
switch (m->period_type) {
case PERIOD_LINEAR:
return 100 * (8 * (((240 - n) << 4) - p));
case PERIOD_CSPD:
d = libxmp_note_to_period(ctx, n, 0, adj);
return libxmp_round(100.0 * (1536.0 / M_LN2) * log(p / d));
default:
/* Amiga */
d = libxmp_note_to_period(ctx, n, 0, adj);
return libxmp_round(100.0 * (1536.0 / M_LN2) * log(d / p));
}
}
/* Convert finetune = 1200 * log2(C2SPD/8363))
*
* c = (1200.0 * log(c2spd) - 1200.0 * log(c4_rate)) / M_LN2;
* xpo = c/100;
* fin = 128 * (c%100) / 100;
*/
void libxmp_c2spd_to_note(int c2spd, int *n, int *f)
{
int c;
if (c2spd == 0) {
*n = *f = 0;
return;
}
c = (int)(1536.0 * log((double)c2spd / 8363) / M_LN2);
*n = c / 128;
*f = c % 128;
}

View file

@ -0,0 +1,24 @@
#ifndef LIBXMP_PERIOD_H
#define LIBXMP_PERIOD_H
#define PERIOD_BASE 13696.0 /* C0 period */
/* Macros for period conversion */
#define NOTE_B0 11
#define NOTE_Bb0 (NOTE_B0 + 1)
#define MAX_NOTE (NOTE_B0 * 8)
#define MAX_PERIOD 0x1c56
#define MIN_PERIOD_A 0x0071
#define MAX_PERIOD_A 0x0358
#define MIN_PERIOD_L 0x0000
#define MAX_PERIOD_L 0x1e00
#define MIN_NOTE_MOD 48
#define MAX_NOTE_MOD 83
double libxmp_note_to_period (struct context_data *, int, int, double);
double libxmp_note_to_period_mix (int, int);
int libxmp_period_to_note (int);
int libxmp_period_to_bend (struct context_data *, double, int, double);
void libxmp_c2spd_to_note (int, int *, int *);
#endif /* LIBXMP_PERIOD_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,258 @@
#ifndef LIBXMP_PLAYER_H
#define LIBXMP_PLAYER_H
#include "lfo.h"
/* Quirk control */
#define HAS_QUIRK(x) (m->quirk & (x))
/* Channel flag control */
#define SET(f) SET_FLAG(xc->flags,(f))
#define RESET(f) RESET_FLAG(xc->flags,(f))
#define TEST(f) TEST_FLAG(xc->flags,(f))
/* Persistent effect flag control */
#define SET_PER(f) SET_FLAG(xc->per_flags,(f))
#define RESET_PER(f) RESET_FLAG(xc->per_flags,(f))
#define TEST_PER(f) TEST_FLAG(xc->per_flags,(f))
/* Note flag control */
#define SET_NOTE(f) SET_FLAG(xc->note_flags,(f))
#define RESET_NOTE(f) RESET_FLAG(xc->note_flags,(f))
#define TEST_NOTE(f) TEST_FLAG(xc->note_flags,(f))
struct retrig_control {
int s;
int m;
int d;
};
/* The following macros are used to set the flags for each channel */
#define VOL_SLIDE (1 << 0)
#define PAN_SLIDE (1 << 1)
#define TONEPORTA (1 << 2)
#define PITCHBEND (1 << 3)
#define VIBRATO (1 << 4)
#define TREMOLO (1 << 5)
#define FINE_VOLS (1 << 6)
#define FINE_BEND (1 << 7)
#define OFFSET (1 << 8)
#define TRK_VSLIDE (1 << 9)
#define TRK_FVSLIDE (1 << 10)
#define NEW_INS (1 << 11)
#define NEW_VOL (1 << 12)
#define VOL_SLIDE_2 (1 << 13)
#define NOTE_SLIDE (1 << 14)
#define FINE_NSLIDE (1 << 15)
#define NEW_NOTE (1 << 16)
#define FINE_TPORTA (1 << 17)
#define RETRIG (1 << 18)
#define PANBRELLO (1 << 19)
#define GVOL_SLIDE (1 << 20)
#define TEMPO_SLIDE (1 << 21)
#define VENV_PAUSE (1 << 22)
#define PENV_PAUSE (1 << 23)
#define FENV_PAUSE (1 << 24)
#define FINE_VOLS_2 (1 << 25)
#define KEY_OFF (1 << 26) /* for IT release on envloop end */
#define TREMOR (1 << 27) /* for XM tremor */
#define NOTE_FADEOUT (1 << 0)
#define NOTE_RELEASE (1 << 1)
#define NOTE_END (1 << 2)
#define NOTE_CUT (1 << 3)
#define NOTE_ENV_END (1 << 4)
#define NOTE_SAMPLE_END (1 << 5)
#define NOTE_SET (1 << 6) /* for IT portamento after keyoff */
#define NOTE_SUSEXIT (1 << 7) /* for delayed note release */
#define NOTE_KEY_CUT (1 << 8) /* note cut with XMP_KEY_CUT event */
#define NOTE_GLISSANDO (1 << 9)
#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))
struct instrument_vibrato {
int phase;
int sweep;
};
struct channel_data {
int flags; /* Channel flags */
int per_flags; /* Persistent effect channel flags */
int note_flags; /* Note release, fadeout or end */
int note; /* Note number */
int key; /* Key number */
double period; /* Amiga or linear period */
double per_adj; /* MED period/pitch adjustment factor hack */
int finetune; /* Guess what */
int ins; /* Instrument number */
int old_ins; /* Last instruemnt */
int smp; /* Sample number */
int mastervol; /* Master vol -- for IT track vol effect */
int delay; /* Note delay in frames */
int keyoff; /* Key off counter */
int fadeout; /* Current fadeout (release) value */
int ins_fade; /* Instrument fadeout value */
int volume; /* Current volume */
int gvl; /* Global volume for instrument for IT */
int rvv; /* Random volume variation */
int rpv; /* Random pan variation */
uint8 split; /* Split channel */
uint8 pair; /* Split channel pair */
int v_idx; /* Volume envelope index */
int p_idx; /* Pan envelope index */
int f_idx; /* Freq envelope index */
int key_porta; /* Key number for portamento target
* -- needed to handle IT portamento xpo */
struct {
struct lfo lfo;
int memory;
} vibrato;
struct {
struct lfo lfo;
int memory;
} tremolo;
#ifndef LIBXMP_CORE_DISABLE_IT
struct {
struct lfo lfo;
int memory;
} panbrello;
#endif
struct {
int8 val[16]; /* 16 for Smaksak MegaArps */
int size;
int count;
int memory;
} arpeggio;
struct {
struct lfo lfo;
int sweep;
} insvib;
struct {
int val;
int val2; /* For fx9 bug emulation */
int memory;
} offset;
struct {
int val; /* Retrig value */
int count; /* Retrig counter */
int type; /* Retrig type */
} retrig;
struct {
uint8 up,down; /* Tremor value */
uint8 count; /* Tremor counter */
uint8 memory; /* Tremor memory */
} tremor;
struct {
int slide; /* Volume slide value */
int fslide; /* Fine volume slide value */
int slide2; /* Volume slide value */
int memory; /* Volume slide effect memory */
#ifndef LIBXMP_CORE_DISABLE_IT
int fslide2;
int memory2; /* Volume slide effect memory */
#endif
} vol;
struct {
int up_memory; /* Fine volume slide up memory (XM) */
int down_memory;/* Fine volume slide up memory (XM) */
} fine_vol;
struct {
int slide; /* Global volume slide value */
int fslide; /* Fine global volume slide value */
int memory; /* Global volume memory is saved per channel */
} gvol;
struct {
int slide; /* Track volume slide value */
int fslide; /* Track fine volume slide value */
int memory; /* Track volume slide effect memory */
} trackvol;
struct {
int slide; /* Frequency slide value */
double fslide; /* Fine frequency slide value */
int memory; /* Portamento effect memory */
} freq;
struct {
double target; /* Target period for tone portamento */
int dir; /* Tone portamento up/down directionh */
int slide; /* Delta for tone portamento */
int memory; /* Tone portamento effect memory */
} porta;
struct {
int up_memory; /* FT2 has separate memories for these */
int down_memory;/* cases (see Porta-LinkMem.xm) */
} fine_porta;
struct {
int val; /* Current pan value */
int slide; /* Pan slide value */
int fslide; /* Pan fine slide value */
int memory; /* Pan slide effect memory */
int surround; /* Surround channel flag */
} pan;
struct {
int speed;
int count;
int pos;
} invloop;
#ifndef LIBXMP_CORE_DISABLE_IT
struct {
int slide; /* IT tempo slide */
} tempo;
struct {
int cutoff; /* IT filter cutoff frequency */
int resonance; /* IT filter resonance */
int envelope; /* IT filter envelope */
} filter;
#endif
#ifndef LIBXMP_CORE_PLAYER
struct {
int slide; /* PTM note slide amount */
int fslide; /* OKT fine note slide amount */
int speed; /* PTM note slide speed */
int count; /* PTM note slide counter */
} noteslide;
void *extra;
#endif
struct xmp_event delayed_event;
int delayed_ins; /* IT save instrument emulation */
int info_period; /* Period */
int info_pitchbend; /* Linear pitchbend */
int info_position; /* Position before mixing */
int info_finalvol; /* Final volume including envelopes */
int info_finalpan; /* Final pan including envelopes */
};
void libxmp_process_fx (struct context_data *, struct channel_data *,
int, struct xmp_event *, int);
void libxmp_filter_setup (int, int, int, int*, int*, int *);
int libxmp_read_event (struct context_data *, struct xmp_event *, int);
#endif /* LIBXMP_PLAYER_H */

View file

@ -0,0 +1,524 @@
static int16 cubic_spline_lut0[1024] = {
0, -8, -16, -24, -32, -40, -47, -55,
-63, -71, -78, -86, -94, -101, -109, -117,
-124, -132, -139, -146, -154, -161, -169, -176,
-183, -190, -198, -205, -212, -219, -226, -233,
-240, -247, -254, -261, -268, -275, -282, -289,
-295, -302, -309, -316, -322, -329, -336, -342,
-349, -355, -362, -368, -375, -381, -388, -394,
-400, -407, -413, -419, -425, -432, -438, -444,
-450, -456, -462, -468, -474, -480, -486, -492,
-498, -504, -510, -515, -521, -527, -533, -538,
-544, -550, -555, -561, -566, -572, -577, -583,
-588, -594, -599, -604, -610, -615, -620, -626,
-631, -636, -641, -646, -651, -656, -662, -667,
-672, -677, -682, -686, -691, -696, -701, -706,
-711, -715, -720, -725, -730, -734, -739, -744,
-748, -753, -757, -762, -766, -771, -775, -780,
-784, -788, -793, -797, -801, -806, -810, -814,
-818, -822, -826, -831, -835, -839, -843, -847,
-851, -855, -859, -863, -866, -870, -874, -878,
-882, -886, -889, -893, -897, -900, -904, -908,
-911, -915, -918, -922, -925, -929, -932, -936,
-939, -943, -946, -949, -953, -956, -959, -962,
-966, -969, -972, -975, -978, -981, -984, -987,
-991, -994, -997, -999, -1002, -1005, -1008, -1011,
-1014, -1017, -1020, -1022, -1025, -1028, -1031, -1033,
-1036, -1039, -1041, -1044, -1047, -1049, -1052, -1054,
-1057, -1059, -1062, -1064, -1066, -1069, -1071, -1074,
-1076, -1078, -1080, -1083, -1085, -1087, -1089, -1092,
-1094, -1096, -1098, -1100, -1102, -1104, -1106, -1108,
-1110, -1112, -1114, -1116, -1118, -1120, -1122, -1124,
-1125, -1127, -1129, -1131, -1133, -1134, -1136, -1138,
-1139, -1141, -1143, -1144, -1146, -1147, -1149, -1150,
-1152, -1153, -1155, -1156, -1158, -1159, -1161, -1162,
-1163, -1165, -1166, -1167, -1169, -1170, -1171, -1172,
-1174, -1175, -1176, -1177, -1178, -1179, -1180, -1181,
-1182, -1184, -1185, -1186, -1187, -1187, -1188, -1189,
-1190, -1191, -1192, -1193, -1194, -1195, -1195, -1196,
-1197, -1198, -1198, -1199, -1200, -1200, -1201, -1202,
-1202, -1203, -1204, -1204, -1205, -1205, -1206, -1206,
-1207, -1207, -1208, -1208, -1208, -1209, -1209, -1210,
-1210, -1210, -1211, -1211, -1211, -1212, -1212, -1212,
-1212, -1212, -1213, -1213, -1213, -1213, -1213, -1213,
-1213, -1213, -1214, -1214, -1214, -1214, -1214, -1214,
-1214, -1214, -1213, -1213, -1213, -1213, -1213, -1213,
-1213, -1213, -1212, -1212, -1212, -1212, -1211, -1211,
-1211, -1211, -1210, -1210, -1210, -1209, -1209, -1209,
-1208, -1208, -1207, -1207, -1207, -1206, -1206, -1205,
-1205, -1204, -1204, -1203, -1202, -1202, -1201, -1201,
-1200, -1199, -1199, -1198, -1197, -1197, -1196, -1195,
-1195, -1194, -1193, -1192, -1192, -1191, -1190, -1189,
-1188, -1187, -1187, -1186, -1185, -1184, -1183, -1182,
-1181, -1180, -1179, -1178, -1177, -1176, -1175, -1174,
-1173, -1172, -1171, -1170, -1169, -1168, -1167, -1166,
-1165, -1163, -1162, -1161, -1160, -1159, -1158, -1156,
-1155, -1154, -1153, -1151, -1150, -1149, -1148, -1146,
-1145, -1144, -1142, -1141, -1140, -1138, -1137, -1135,
-1134, -1133, -1131, -1130, -1128, -1127, -1125, -1124,
-1122, -1121, -1119, -1118, -1116, -1115, -1113, -1112,
-1110, -1109, -1107, -1105, -1104, -1102, -1101, -1099,
-1097, -1096, -1094, -1092, -1091, -1089, -1087, -1085,
-1084, -1082, -1080, -1079, -1077, -1075, -1073, -1071,
-1070, -1068, -1066, -1064, -1062, -1061, -1059, -1057,
-1055, -1053, -1051, -1049, -1047, -1046, -1044, -1042,
-1040, -1038, -1036, -1034, -1032, -1030, -1028, -1026,
-1024, -1022, -1020, -1018, -1016, -1014, -1012, -1010,
-1008, -1006, -1004, -1002, -999, -997, -995, -993,
-991, -989, -987, -985, -982, -980, -978, -976,
-974, -972, -969, -967, -965, -963, -961, -958,
-956, -954, -952, -950, -947, -945, -943, -941,
-938, -936, -934, -931, -929, -927, -924, -922,
-920, -918, -915, -913, -911, -908, -906, -903,
-901, -899, -896, -894, -892, -889, -887, -884,
-882, -880, -877, -875, -872, -870, -867, -865,
-863, -860, -858, -855, -853, -850, -848, -845,
-843, -840, -838, -835, -833, -830, -828, -825,
-823, -820, -818, -815, -813, -810, -808, -805,
-803, -800, -798, -795, -793, -790, -787, -785,
-782, -780, -777, -775, -772, -769, -767, -764,
-762, -759, -757, -754, -751, -749, -746, -744,
-741, -738, -736, -733, -730, -728, -725, -723,
-720, -717, -715, -712, -709, -707, -704, -702,
-699, -696, -694, -691, -688, -686, -683, -680,
-678, -675, -672, -670, -667, -665, -662, -659,
-657, -654, -651, -649, -646, -643, -641, -638,
-635, -633, -630, -627, -625, -622, -619, -617,
-614, -611, -609, -606, -603, -601, -598, -595,
-593, -590, -587, -585, -582, -579, -577, -574,
-571, -569, -566, -563, -561, -558, -555, -553,
-550, -547, -545, -542, -539, -537, -534, -531,
-529, -526, -523, -521, -518, -516, -513, -510,
-508, -505, -502, -500, -497, -495, -492, -489,
-487, -484, -481, -479, -476, -474, -471, -468,
-466, -463, -461, -458, -455, -453, -450, -448,
-445, -442, -440, -437, -435, -432, -430, -427,
-424, -422, -419, -417, -414, -412, -409, -407,
-404, -402, -399, -397, -394, -392, -389, -387,
-384, -382, -379, -377, -374, -372, -369, -367,
-364, -362, -359, -357, -354, -352, -349, -347,
-345, -342, -340, -337, -335, -332, -330, -328,
-325, -323, -320, -318, -316, -313, -311, -309,
-306, -304, -302, -299, -297, -295, -292, -290,
-288, -285, -283, -281, -278, -276, -274, -272,
-269, -267, -265, -263, -260, -258, -256, -254,
-251, -249, -247, -245, -243, -240, -238, -236,
-234, -232, -230, -228, -225, -223, -221, -219,
-217, -215, -213, -211, -209, -207, -205, -202,
-200, -198, -196, -194, -192, -190, -188, -186,
-184, -182, -180, -178, -176, -175, -173, -171,
-169, -167, -165, -163, -161, -159, -157, -156,
-154, -152, -150, -148, -146, -145, -143, -141,
-139, -137, -136, -134, -132, -130, -129, -127,
-125, -124, -122, -120, -119, -117, -115, -114,
-112, -110, -109, -107, -106, -104, -102, -101,
-99, -98, -96, -95, -93, -92, -90, -89,
-87, -86, -84, -83, -82, -80, -79, -77,
-76, -75, -73, -72, -70, -69, -68, -67,
-65, -64, -63, -61, -60, -59, -58, -57,
-55, -54, -53, -52, -51, -49, -48, -47,
-46, -45, -44, -43, -42, -41, -40, -39,
-38, -37, -36, -35, -34, -33, -32, -31,
-30, -29, -28, -27, -26, -26, -25, -24,
-23, -22, -22, -21, -20, -19, -19, -18,
-17, -16, -16, -15, -14, -14, -13, -13,
-12, -11, -11, -10, -10, -9, -9, -8,
-8, -7, -7, -6, -6, -6, -5, -5,
-4, -4, -4, -3, -3, -3, -2, -2,
-2, -2, -2, -1, -1, -1, -1, -1,
0, 0, 0, 0, 0, 0, 0, 0,
};
static int16 cubic_spline_lut1[1024] = {
16384, 16384, 16384, 16384, 16384, 16383, 16382, 16381,
16381, 16381, 16380, 16379, 16379, 16377, 16377, 16376,
16374, 16373, 16371, 16370, 16369, 16366, 16366, 16364,
16361, 16360, 16358, 16357, 16354, 16351, 16349, 16347,
16345, 16342, 16340, 16337, 16335, 16331, 16329, 16326,
16322, 16320, 16317, 16314, 16309, 16307, 16304, 16299,
16297, 16293, 16290, 16285, 16282, 16278, 16274, 16269,
16265, 16262, 16257, 16253, 16247, 16244, 16239, 16235,
16230, 16225, 16220, 16216, 16211, 16206, 16201, 16196,
16191, 16185, 16180, 16174, 16169, 16163, 16158, 16151,
16146, 16140, 16133, 16128, 16122, 16116, 16109, 16104,
16097, 16092, 16085, 16077, 16071, 16064, 16058, 16052,
16044, 16038, 16030, 16023, 16015, 16009, 16002, 15995,
15988, 15980, 15973, 15964, 15957, 15949, 15941, 15934,
15926, 15918, 15910, 15903, 15894, 15886, 15877, 15870,
15861, 15853, 15843, 15836, 15827, 15818, 15810, 15801,
15792, 15783, 15774, 15765, 15756, 15747, 15738, 15729,
15719, 15709, 15700, 15691, 15681, 15672, 15662, 15652,
15642, 15633, 15623, 15613, 15602, 15592, 15582, 15572,
15562, 15552, 15540, 15530, 15520, 15509, 15499, 15489,
15478, 15467, 15456, 15446, 15433, 15423, 15412, 15401,
15390, 15379, 15367, 15356, 15345, 15333, 15321, 15310,
15299, 15287, 15276, 15264, 15252, 15240, 15228, 15216,
15205, 15192, 15180, 15167, 15155, 15143, 15131, 15118,
15106, 15094, 15081, 15067, 15056, 15043, 15031, 15017,
15004, 14992, 14979, 14966, 14953, 14940, 14927, 14913,
14900, 14887, 14874, 14860, 14846, 14833, 14819, 14806,
14793, 14778, 14764, 14752, 14737, 14723, 14709, 14696,
14681, 14668, 14653, 14638, 14625, 14610, 14595, 14582,
14567, 14553, 14538, 14523, 14509, 14494, 14480, 14465,
14450, 14435, 14420, 14406, 14391, 14376, 14361, 14346,
14330, 14316, 14301, 14285, 14270, 14254, 14239, 14223,
14208, 14192, 14177, 14161, 14146, 14130, 14115, 14099,
14082, 14067, 14051, 14035, 14019, 14003, 13986, 13971,
13955, 13939, 13923, 13906, 13890, 13873, 13857, 13840,
13823, 13808, 13791, 13775, 13758, 13741, 13724, 13707,
13691, 13673, 13657, 13641, 13623, 13607, 13589, 13572,
13556, 13538, 13521, 13504, 13486, 13469, 13451, 13435,
13417, 13399, 13383, 13365, 13347, 13330, 13312, 13294,
13277, 13258, 13241, 13224, 13205, 13188, 13170, 13152,
13134, 13116, 13098, 13080, 13062, 13044, 13026, 13008,
12989, 12971, 12953, 12934, 12916, 12898, 12879, 12860,
12842, 12823, 12806, 12787, 12768, 12750, 12731, 12712,
12694, 12675, 12655, 12637, 12618, 12599, 12580, 12562,
12542, 12524, 12504, 12485, 12466, 12448, 12427, 12408,
12390, 12370, 12351, 12332, 12312, 12293, 12273, 12254,
12235, 12215, 12195, 12176, 12157, 12137, 12118, 12097,
12079, 12059, 12039, 12019, 11998, 11980, 11960, 11940,
11920, 11900, 11880, 11860, 11839, 11821, 11801, 11780,
11761, 11741, 11720, 11700, 11680, 11660, 11640, 11619,
11599, 11578, 11559, 11538, 11518, 11498, 11477, 11457,
11436, 11415, 11394, 11374, 11354, 11333, 11313, 11292,
11272, 11251, 11231, 11209, 11189, 11168, 11148, 11127,
11107, 11084, 11064, 11043, 11023, 11002, 10982, 10959,
10939, 10918, 10898, 10876, 10856, 10834, 10814, 10792,
10772, 10750, 10728, 10708, 10687, 10666, 10644, 10623,
10602, 10581, 10560, 10538, 10517, 10496, 10474, 10453,
10431, 10410, 10389, 10368, 10346, 10325, 10303, 10283,
10260, 10239, 10217, 10196, 10175, 10152, 10132, 10110,
10088, 10068, 10045, 10023, 10002, 9981, 9959, 9936,
9915, 9893, 9872, 9851, 9829, 9806, 9784, 9763,
9742, 9720, 9698, 9676, 9653, 9633, 9611, 9589,
9567, 9545, 9523, 9501, 9479, 9458, 9436, 9414,
9392, 9370, 9348, 9326, 9304, 9282, 9260, 9238,
9216, 9194, 9172, 9150, 9128, 9106, 9084, 9062,
9040, 9018, 8996, 8974, 8951, 8929, 8907, 8885,
8863, 8841, 8819, 8797, 8775, 8752, 8730, 8708,
8686, 8664, 8642, 8620, 8597, 8575, 8553, 8531,
8509, 8487, 8464, 8442, 8420, 8398, 8376, 8353,
8331, 8309, 8287, 8265, 8242, 8220, 8198, 8176,
8154, 8131, 8109, 8087, 8065, 8042, 8020, 7998,
7976, 7954, 7931, 7909, 7887, 7865, 7842, 7820,
7798, 7776, 7754, 7731, 7709, 7687, 7665, 7643,
7620, 7598, 7576, 7554, 7531, 7509, 7487, 7465,
7443, 7421, 7398, 7376, 7354, 7332, 7310, 7288,
7265, 7243, 7221, 7199, 7177, 7155, 7132, 7110,
7088, 7066, 7044, 7022, 7000, 6978, 6956, 6934,
6911, 6889, 6867, 6845, 6823, 6801, 6779, 6757,
6735, 6713, 6691, 6669, 6647, 6625, 6603, 6581,
6559, 6537, 6515, 6493, 6472, 6450, 6428, 6406,
6384, 6362, 6340, 6318, 6297, 6275, 6253, 6231,
6209, 6188, 6166, 6144, 6122, 6101, 6079, 6057,
6035, 6014, 5992, 5970, 5949, 5927, 5905, 5884,
5862, 5841, 5819, 5797, 5776, 5754, 5733, 5711,
5690, 5668, 5647, 5625, 5604, 5582, 5561, 5540,
5518, 5497, 5476, 5454, 5433, 5412, 5390, 5369,
5348, 5327, 5305, 5284, 5263, 5242, 5221, 5199,
5178, 5157, 5136, 5115, 5094, 5073, 5052, 5031,
5010, 4989, 4968, 4947, 4926, 4905, 4885, 4864,
4843, 4822, 4801, 4780, 4760, 4739, 4718, 4698,
4677, 4656, 4636, 4615, 4595, 4574, 4553, 4533,
4512, 4492, 4471, 4451, 4431, 4410, 4390, 4370,
4349, 4329, 4309, 4288, 4268, 4248, 4228, 4208,
4188, 4167, 4147, 4127, 4107, 4087, 4067, 4047,
4027, 4007, 3988, 3968, 3948, 3928, 3908, 3889,
3869, 3849, 3829, 3810, 3790, 3771, 3751, 3732,
3712, 3693, 3673, 3654, 3634, 3615, 3595, 3576,
3557, 3538, 3518, 3499, 3480, 3461, 3442, 3423,
3404, 3385, 3366, 3347, 3328, 3309, 3290, 3271,
3252, 3233, 3215, 3196, 3177, 3159, 3140, 3121,
3103, 3084, 3066, 3047, 3029, 3010, 2992, 2974,
2955, 2937, 2919, 2901, 2882, 2864, 2846, 2828,
2810, 2792, 2774, 2756, 2738, 2720, 2702, 2685,
2667, 2649, 2631, 2614, 2596, 2579, 2561, 2543,
2526, 2509, 2491, 2474, 2456, 2439, 2422, 2405,
2387, 2370, 2353, 2336, 2319, 2302, 2285, 2268,
2251, 2234, 2218, 2201, 2184, 2167, 2151, 2134,
2117, 2101, 2084, 2068, 2052, 2035, 2019, 2003,
1986, 1970, 1954, 1938, 1922, 1906, 1890, 1874,
1858, 1842, 1826, 1810, 1794, 1779, 1763, 1747,
1732, 1716, 1701, 1685, 1670, 1654, 1639, 1624,
1608, 1593, 1578, 1563, 1548, 1533, 1518, 1503,
1488, 1473, 1458, 1444, 1429, 1414, 1400, 1385,
1370, 1356, 1342, 1327, 1313, 1298, 1284, 1270,
1256, 1242, 1228, 1214, 1200, 1186, 1172, 1158,
1144, 1131, 1117, 1103, 1090, 1076, 1063, 1049,
1036, 1022, 1009, 996, 983, 970, 956, 943,
930, 917, 905, 892, 879, 866, 854, 841,
828, 816, 803, 791, 778, 766, 754, 742,
729, 717, 705, 693, 681, 669, 658, 646,
634, 622, 611, 599, 588, 576, 565, 553,
542, 531, 520, 508, 497, 486, 475, 464,
453, 443, 432, 421, 411, 400, 389, 379,
369, 358, 348, 338, 327, 317, 307, 297,
287, 277, 268, 258, 248, 238, 229, 219,
210, 200, 191, 182, 172, 163, 154, 145,
136, 127, 118, 109, 100, 92, 83, 75,
66, 58, 49, 41, 32, 24, 16, 8,
};
static int16 cubic_spline_lut2[1024] = {
0, 8, 16, 24, 32, 41, 49, 58,
66, 75, 83, 92, 100, 109, 118, 127,
136, 145, 154, 163, 172, 182, 191, 200,
210, 219, 229, 238, 248, 258, 268, 277,
287, 297, 307, 317, 327, 338, 348, 358,
369, 379, 389, 400, 411, 421, 432, 443,
453, 464, 475, 486, 497, 508, 520, 531,
542, 553, 565, 576, 588, 599, 611, 622,
634, 646, 658, 669, 681, 693, 705, 717,
729, 742, 754, 766, 778, 791, 803, 816,
828, 841, 854, 866, 879, 892, 905, 917,
930, 943, 956, 970, 983, 996, 1009, 1022,
1036, 1049, 1063, 1076, 1090, 1103, 1117, 1131,
1144, 1158, 1172, 1186, 1200, 1214, 1228, 1242,
1256, 1270, 1284, 1298, 1313, 1327, 1342, 1356,
1370, 1385, 1400, 1414, 1429, 1444, 1458, 1473,
1488, 1503, 1518, 1533, 1548, 1563, 1578, 1593,
1608, 1624, 1639, 1654, 1670, 1685, 1701, 1716,
1732, 1747, 1763, 1779, 1794, 1810, 1826, 1842,
1858, 1874, 1890, 1906, 1922, 1938, 1954, 1970,
1986, 2003, 2019, 2035, 2052, 2068, 2084, 2101,
2117, 2134, 2151, 2167, 2184, 2201, 2218, 2234,
2251, 2268, 2285, 2302, 2319, 2336, 2353, 2370,
2387, 2405, 2422, 2439, 2456, 2474, 2491, 2509,
2526, 2543, 2561, 2579, 2596, 2614, 2631, 2649,
2667, 2685, 2702, 2720, 2738, 2756, 2774, 2792,
2810, 2828, 2846, 2864, 2882, 2901, 2919, 2937,
2955, 2974, 2992, 3010, 3029, 3047, 3066, 3084,
3103, 3121, 3140, 3159, 3177, 3196, 3215, 3233,
3252, 3271, 3290, 3309, 3328, 3347, 3366, 3385,
3404, 3423, 3442, 3461, 3480, 3499, 3518, 3538,
3557, 3576, 3595, 3615, 3634, 3654, 3673, 3693,
3712, 3732, 3751, 3771, 3790, 3810, 3829, 3849,
3869, 3889, 3908, 3928, 3948, 3968, 3988, 4007,
4027, 4047, 4067, 4087, 4107, 4127, 4147, 4167,
4188, 4208, 4228, 4248, 4268, 4288, 4309, 4329,
4349, 4370, 4390, 4410, 4431, 4451, 4471, 4492,
4512, 4533, 4553, 4574, 4595, 4615, 4636, 4656,
4677, 4698, 4718, 4739, 4760, 4780, 4801, 4822,
4843, 4864, 4885, 4905, 4926, 4947, 4968, 4989,
5010, 5031, 5052, 5073, 5094, 5115, 5136, 5157,
5178, 5199, 5221, 5242, 5263, 5284, 5305, 5327,
5348, 5369, 5390, 5412, 5433, 5454, 5476, 5497,
5518, 5540, 5561, 5582, 5604, 5625, 5647, 5668,
5690, 5711, 5733, 5754, 5776, 5797, 5819, 5841,
5862, 5884, 5905, 5927, 5949, 5970, 5992, 6014,
6035, 6057, 6079, 6101, 6122, 6144, 6166, 6188,
6209, 6231, 6253, 6275, 6297, 6318, 6340, 6362,
6384, 6406, 6428, 6450, 6472, 6493, 6515, 6537,
6559, 6581, 6603, 6625, 6647, 6669, 6691, 6713,
6735, 6757, 6779, 6801, 6823, 6845, 6867, 6889,
6911, 6934, 6956, 6978, 7000, 7022, 7044, 7066,
7088, 7110, 7132, 7155, 7177, 7199, 7221, 7243,
7265, 7288, 7310, 7332, 7354, 7376, 7398, 7421,
7443, 7465, 7487, 7509, 7531, 7554, 7576, 7598,
7620, 7643, 7665, 7687, 7709, 7731, 7754, 7776,
7798, 7820, 7842, 7865, 7887, 7909, 7931, 7954,
7976, 7998, 8020, 8042, 8065, 8087, 8109, 8131,
8154, 8176, 8198, 8220, 8242, 8265, 8287, 8309,
8331, 8353, 8376, 8398, 8420, 8442, 8464, 8487,
8509, 8531, 8553, 8575, 8597, 8620, 8642, 8664,
8686, 8708, 8730, 8752, 8775, 8797, 8819, 8841,
8863, 8885, 8907, 8929, 8951, 8974, 8996, 9018,
9040, 9062, 9084, 9106, 9128, 9150, 9172, 9194,
9216, 9238, 9260, 9282, 9304, 9326, 9348, 9370,
9392, 9414, 9436, 9458, 9479, 9501, 9523, 9545,
9567, 9589, 9611, 9633, 9653, 9676, 9698, 9720,
9742, 9763, 9784, 9806, 9829, 9851, 9872, 9893,
9915, 9936, 9959, 9981, 10002, 10023, 10045, 10068,
10088, 10110, 10132, 10152, 10175, 10196, 10217, 10239,
10260, 10283, 10303, 10325, 10346, 10368, 10389, 10410,
10431, 10453, 10474, 10496, 10517, 10538, 10560, 10581,
10602, 10623, 10644, 10666, 10687, 10708, 10728, 10750,
10772, 10792, 10814, 10834, 10856, 10876, 10898, 10918,
10939, 10959, 10982, 11002, 11023, 11043, 11064, 11084,
11107, 11127, 11148, 11168, 11189, 11209, 11231, 11251,
11272, 11292, 11313, 11333, 11354, 11374, 11394, 11415,
11436, 11457, 11477, 11498, 11518, 11538, 11559, 11578,
11599, 11619, 11640, 11660, 11680, 11700, 11720, 11741,
11761, 11780, 11801, 11821, 11839, 11860, 11880, 11900,
11920, 11940, 11960, 11980, 11998, 12019, 12039, 12059,
12079, 12097, 12118, 12137, 12157, 12176, 12195, 12215,
12235, 12254, 12273, 12293, 12312, 12332, 12351, 12370,
12390, 12408, 12427, 12448, 12466, 12485, 12504, 12524,
12542, 12562, 12580, 12599, 12618, 12637, 12655, 12675,
12694, 12712, 12731, 12750, 12768, 12787, 12806, 12823,
12842, 12860, 12879, 12898, 12916, 12934, 12953, 12971,
12989, 13008, 13026, 13044, 13062, 13080, 13098, 13116,
13134, 13152, 13170, 13188, 13205, 13224, 13241, 13258,
13277, 13294, 13312, 13330, 13347, 13365, 13383, 13399,
13417, 13435, 13451, 13469, 13486, 13504, 13521, 13538,
13556, 13572, 13589, 13607, 13623, 13641, 13657, 13673,
13691, 13707, 13724, 13741, 13758, 13775, 13791, 13808,
13823, 13840, 13857, 13873, 13890, 13906, 13923, 13939,
13955, 13971, 13986, 14003, 14019, 14035, 14051, 14067,
14082, 14099, 14115, 14130, 14146, 14161, 14177, 14192,
14208, 14223, 14239, 14254, 14270, 14285, 14301, 14316,
14330, 14346, 14361, 14376, 14391, 14406, 14420, 14435,
14450, 14465, 14480, 14494, 14509, 14523, 14538, 14553,
14567, 14582, 14595, 14610, 14625, 14638, 14653, 14668,
14681, 14696, 14709, 14723, 14737, 14752, 14764, 14778,
14793, 14806, 14819, 14833, 14846, 14860, 14874, 14887,
14900, 14913, 14927, 14940, 14953, 14966, 14979, 14992,
15004, 15017, 15031, 15043, 15056, 15067, 15081, 15094,
15106, 15118, 15131, 15143, 15155, 15167, 15180, 15192,
15205, 15216, 15228, 15240, 15252, 15264, 15276, 15287,
15299, 15310, 15321, 15333, 15345, 15356, 15367, 15379,
15390, 15401, 15412, 15423, 15433, 15446, 15456, 15467,
15478, 15489, 15499, 15509, 15520, 15530, 15540, 15552,
15562, 15572, 15582, 15592, 15602, 15613, 15623, 15633,
15642, 15652, 15662, 15672, 15681, 15691, 15700, 15709,
15719, 15729, 15738, 15747, 15756, 15765, 15774, 15783,
15792, 15801, 15810, 15818, 15827, 15836, 15843, 15853,
15861, 15870, 15877, 15886, 15894, 15903, 15910, 15918,
15926, 15934, 15941, 15949, 15957, 15964, 15973, 15980,
15988, 15995, 16002, 16009, 16015, 16023, 16030, 16038,
16044, 16052, 16058, 16064, 16071, 16077, 16085, 16092,
16097, 16104, 16109, 16116, 16122, 16128, 16133, 16140,
16146, 16151, 16158, 16163, 16169, 16174, 16180, 16185,
16191, 16196, 16201, 16206, 16211, 16216, 16220, 16225,
16230, 16235, 16239, 16244, 16247, 16253, 16257, 16262,
16265, 16269, 16274, 16278, 16282, 16285, 16290, 16293,
16297, 16299, 16304, 16307, 16309, 16314, 16317, 16320,
16322, 16326, 16329, 16331, 16335, 16337, 16340, 16342,
16345, 16347, 16349, 16351, 16354, 16357, 16358, 16360,
16361, 16364, 16366, 16366, 16369, 16370, 16371, 16373,
16374, 16376, 16377, 16377, 16379, 16379, 16380, 16381,
16381, 16381, 16382, 16383, 16384, 16384, 16384, 16384,
};
static int16 cubic_spline_lut3[1024] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, -1, -1, -1, -1, -1, -2, -2,
-2, -2, -2, -3, -3, -3, -4, -4,
-4, -5, -5, -6, -6, -6, -7, -7,
-8, -8, -9, -9, -10, -10, -11, -11,
-12, -13, -13, -14, -14, -15, -16, -16,
-17, -18, -19, -19, -20, -21, -22, -22,
-23, -24, -25, -26, -26, -27, -28, -29,
-30, -31, -32, -33, -34, -35, -36, -37,
-38, -39, -40, -41, -42, -43, -44, -45,
-46, -47, -48, -49, -51, -52, -53, -54,
-55, -57, -58, -59, -60, -61, -63, -64,
-65, -67, -68, -69, -70, -72, -73, -75,
-76, -77, -79, -80, -82, -83, -84, -86,
-87, -89, -90, -92, -93, -95, -96, -98,
-99, -101, -102, -104, -106, -107, -109, -110,
-112, -114, -115, -117, -119, -120, -122, -124,
-125, -127, -129, -130, -132, -134, -136, -137,
-139, -141, -143, -145, -146, -148, -150, -152,
-154, -156, -157, -159, -161, -163, -165, -167,
-169, -171, -173, -175, -176, -178, -180, -182,
-184, -186, -188, -190, -192, -194, -196, -198,
-200, -202, -205, -207, -209, -211, -213, -215,
-217, -219, -221, -223, -225, -228, -230, -232,
-234, -236, -238, -240, -243, -245, -247, -249,
-251, -254, -256, -258, -260, -263, -265, -267,
-269, -272, -274, -276, -278, -281, -283, -285,
-288, -290, -292, -295, -297, -299, -302, -304,
-306, -309, -311, -313, -316, -318, -320, -323,
-325, -328, -330, -332, -335, -337, -340, -342,
-345, -347, -349, -352, -354, -357, -359, -362,
-364, -367, -369, -372, -374, -377, -379, -382,
-384, -387, -389, -392, -394, -397, -399, -402,
-404, -407, -409, -412, -414, -417, -419, -422,
-424, -427, -430, -432, -435, -437, -440, -442,
-445, -448, -450, -453, -455, -458, -461, -463,
-466, -468, -471, -474, -476, -479, -481, -484,
-487, -489, -492, -495, -497, -500, -502, -505,
-508, -510, -513, -516, -518, -521, -523, -526,
-529, -531, -534, -537, -539, -542, -545, -547,
-550, -553, -555, -558, -561, -563, -566, -569,
-571, -574, -577, -579, -582, -585, -587, -590,
-593, -595, -598, -601, -603, -606, -609, -611,
-614, -617, -619, -622, -625, -627, -630, -633,
-635, -638, -641, -643, -646, -649, -651, -654,
-657, -659, -662, -665, -667, -670, -672, -675,
-678, -680, -683, -686, -688, -691, -694, -696,
-699, -702, -704, -707, -709, -712, -715, -717,
-720, -723, -725, -728, -730, -733, -736, -738,
-741, -744, -746, -749, -751, -754, -757, -759,
-762, -764, -767, -769, -772, -775, -777, -780,
-782, -785, -787, -790, -793, -795, -798, -800,
-803, -805, -808, -810, -813, -815, -818, -820,
-823, -825, -828, -830, -833, -835, -838, -840,
-843, -845, -848, -850, -853, -855, -858, -860,
-863, -865, -867, -870, -872, -875, -877, -880,
-882, -884, -887, -889, -892, -894, -896, -899,
-901, -903, -906, -908, -911, -913, -915, -918,
-920, -922, -924, -927, -929, -931, -934, -936,
-938, -941, -943, -945, -947, -950, -952, -954,
-956, -958, -961, -963, -965, -967, -969, -972,
-974, -976, -978, -980, -982, -985, -987, -989,
-991, -993, -995, -997, -999, -1002, -1004, -1006,
-1008, -1010, -1012, -1014, -1016, -1018, -1020, -1022,
-1024, -1026, -1028, -1030, -1032, -1034, -1036, -1038,
-1040, -1042, -1044, -1046, -1047, -1049, -1051, -1053,
-1055, -1057, -1059, -1061, -1062, -1064, -1066, -1068,
-1070, -1071, -1073, -1075, -1077, -1079, -1080, -1082,
-1084, -1085, -1087, -1089, -1091, -1092, -1094, -1096,
-1097, -1099, -1101, -1102, -1104, -1105, -1107, -1109,
-1110, -1112, -1113, -1115, -1116, -1118, -1119, -1121,
-1122, -1124, -1125, -1127, -1128, -1130, -1131, -1133,
-1134, -1135, -1137, -1138, -1140, -1141, -1142, -1144,
-1145, -1146, -1148, -1149, -1150, -1151, -1153, -1154,
-1155, -1156, -1158, -1159, -1160, -1161, -1162, -1163,
-1165, -1166, -1167, -1168, -1169, -1170, -1171, -1172,
-1173, -1174, -1175, -1176, -1177, -1178, -1179, -1180,
-1181, -1182, -1183, -1184, -1185, -1186, -1187, -1187,
-1188, -1189, -1190, -1191, -1192, -1192, -1193, -1194,
-1195, -1195, -1196, -1197, -1197, -1198, -1199, -1199,
-1200, -1201, -1201, -1202, -1202, -1203, -1204, -1204,
-1205, -1205, -1206, -1206, -1207, -1207, -1207, -1208,
-1208, -1209, -1209, -1209, -1210, -1210, -1210, -1211,
-1211, -1211, -1211, -1212, -1212, -1212, -1212, -1213,
-1213, -1213, -1213, -1213, -1213, -1213, -1213, -1214,
-1214, -1214, -1214, -1214, -1214, -1214, -1214, -1213,
-1213, -1213, -1213, -1213, -1213, -1213, -1213, -1212,
-1212, -1212, -1212, -1212, -1211, -1211, -1211, -1210,
-1210, -1210, -1209, -1209, -1208, -1208, -1208, -1207,
-1207, -1206, -1206, -1205, -1205, -1204, -1204, -1203,
-1202, -1202, -1201, -1200, -1200, -1199, -1198, -1198,
-1197, -1196, -1195, -1195, -1194, -1193, -1192, -1191,
-1190, -1189, -1188, -1187, -1187, -1186, -1185, -1184,
-1182, -1181, -1180, -1179, -1178, -1177, -1176, -1175,
-1174, -1172, -1171, -1170, -1169, -1167, -1166, -1165,
-1163, -1162, -1161, -1159, -1158, -1156, -1155, -1153,
-1152, -1150, -1149, -1147, -1146, -1144, -1143, -1141,
-1139, -1138, -1136, -1134, -1133, -1131, -1129, -1127,
-1125, -1124, -1122, -1120, -1118, -1116, -1114, -1112,
-1110, -1108, -1106, -1104, -1102, -1100, -1098, -1096,
-1094, -1092, -1089, -1087, -1085, -1083, -1080, -1078,
-1076, -1074, -1071, -1069, -1066, -1064, -1062, -1059,
-1057, -1054, -1052, -1049, -1047, -1044, -1041, -1039,
-1036, -1033, -1031, -1028, -1025, -1022, -1020, -1017,
-1014, -1011, -1008, -1005, -1002, -999, -997, -994,
-991, -987, -984, -981, -978, -975, -972, -969,
-966, -962, -959, -956, -953, -949, -946, -943,
-939, -936, -932, -929, -925, -922, -918, -915,
-911, -908, -904, -900, -897, -893, -889, -886,
-882, -878, -874, -870, -866, -863, -859, -855,
-851, -847, -843, -839, -835, -831, -826, -822,
-818, -814, -810, -806, -801, -797, -793, -788,
-784, -780, -775, -771, -766, -762, -757, -753,
-748, -744, -739, -734, -730, -725, -720, -715,
-711, -706, -701, -696, -691, -686, -682, -677,
-672, -667, -662, -656, -651, -646, -641, -636,
-631, -626, -620, -615, -610, -604, -599, -594,
-588, -583, -577, -572, -566, -561, -555, -550,
-544, -538, -533, -527, -521, -515, -510, -504,
-498, -492, -486, -480, -474, -468, -462, -456,
-450, -444, -438, -432, -425, -419, -413, -407,
-400, -394, -388, -381, -375, -368, -362, -355,
-349, -342, -336, -329, -322, -316, -309, -302,
-295, -289, -282, -275, -268, -261, -254, -247,
-240, -233, -226, -219, -212, -205, -198, -190,
-183, -176, -169, -161, -154, -146, -139, -132,
-124, -117, -109, -101, -94, -86, -78, -71,
-63, -55, -47, -40, -32, -24, -16, -8,
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,116 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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.
*/
/* S3M packed pattern macros */
#define S3M_EOR 0 /* End of row */
#define S3M_CH_MASK 0x1f /* Channel */
#define S3M_NI_FOLLOW 0x20 /* Note and instrument follow */
#define S3M_VOL_FOLLOWS 0x40 /* Volume follows */
#define S3M_FX_FOLLOWS 0x80 /* Effect and parameter follow */
/* S3M channel info macros */
#define S3M_CH_ON 0x80 /* Psi says it's bit 8, I'll assume bit 7 */
#define S3M_CH_OFF 0xff
#define S3M_CH_PAN 0x7f /* Left/Right */
/* S3M channel pan macros */
#define S3M_PAN_SET 0x20
#define S3M_PAN_MASK 0x0f
/* S3M flags */
#define S3M_ST2_VIB 0x01 /* Not recognized */
#define S3M_ST2_TEMPO 0x02 /* Not recognized */
#define S3M_AMIGA_SLIDE 0x04 /* Not recognized */
#define S3M_VOL_OPT 0x08 /* Not recognized */
#define S3M_AMIGA_RANGE 0x10
#define S3M_SB_FILTER 0x20 /* Not recognized */
#define S3M_ST300_VOLS 0x40
#define S3M_CUSTOM_DATA 0x80 /* Not recognized */
/* S3M Adlib instrument types */
#define S3M_INST_SAMPLE 0x01
#define S3M_INST_AMEL 0x02
#define S3M_INST_ABD 0x03
#define S3M_INST_ASNARE 0x04
#define S3M_INST_ATOM 0x05
#define S3M_INST_ACYM 0x06
#define S3M_INST_AHIHAT 0x07
struct s3m_file_header {
uint8 name[28]; /* Song name */
uint8 doseof; /* 0x1a */
uint8 type; /* File type */
uint8 rsvd1[2]; /* Reserved */
uint16 ordnum; /* Number of orders (must be even) */
uint16 insnum; /* Number of instruments */
uint16 patnum; /* Number of patterns */
uint16 flags; /* Flags */
uint16 version; /* Tracker ID and version */
uint16 ffi; /* File format information */
uint32 magic; /* 'SCRM' */
uint8 gv; /* Global volume */
uint8 is; /* Initial speed */
uint8 it; /* Initial tempo */
uint8 mv; /* Master volume */
uint8 uc; /* Ultra click removal */
uint8 dp; /* Default pan positions if 0xfc */
uint8 rsvd2[8]; /* Reserved */
uint16 special; /* Ptr to special custom data */
uint8 chset[32]; /* Channel settings */
};
struct s3m_instrument_header {
uint8 dosname[13]; /* DOS file name */
uint16 memseg; /* Pointer to sample data */
uint32 length; /* Length */
uint32 loopbeg; /* Loop begin */
uint32 loopend; /* Loop end */
uint8 vol; /* Volume */
uint8 rsvd1; /* Reserved */
uint8 pack; /* Packing type (not used) */
uint8 flags; /* Loop/stereo/16bit samples flags */
uint16 c2spd; /* C 4 speed */
uint16 rsvd2; /* Reserved */
uint8 rsvd3[4]; /* Reserved */
uint16 int_gp; /* Internal - GUS pointer */
uint16 int_512; /* Internal - SB pointer */
uint32 int_last; /* Internal - SB index */
uint8 name[28]; /* Instrument name */
uint32 magic; /* 'SCRS' */
};
#ifndef LIBXMP_CORE_PLAYER
struct s3m_adlib_header {
uint8 dosname[12]; /* DOS file name */
uint8 rsvd1[3]; /* 0x00 0x00 0x00 */
uint8 reg[12]; /* Adlib registers */
uint8 vol;
uint8 dsk;
uint8 rsvd2[2];
uint16 c2spd; /* C 4 speed */
uint16 rsvd3; /* Reserved */
uint8 rsvd4[12]; /* Reserved */
uint8 name[28]; /* Instrument name */
uint32 magic; /* 'SCRI' */
};
#endif

View file

@ -0,0 +1,625 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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.
*/
/*
* Tue, 30 Jun 1998 20:23:11 +0200
* Reported by John v/d Kamp <blade_@dds.nl>:
* I have this song from Purple Motion called wcharts.s3m, the global
* volume was set to 0, creating a devide by 0 error in xmp. There should
* be an extra test if it's 0 or not.
*
* Claudio's fix: global volume ignored
*/
/*
* Sat, 29 Aug 1998 18:50:43 -0500 (CDT)
* Reported by Joel Jordan <scriber@usa.net>:
* S3M files support tempos outside the ranges defined by xmp (that is,
* the MOD/XM tempo ranges). S3M's can have tempos from 0 to 255 and speeds
* from 0 to 255 as well, since these are handled with separate effects
* unlike the MOD format. This becomes an issue in such songs as Skaven's
* "Catch that Goblin", which uses speeds above 0x1f.
*
* Claudio's fix: FX_S3M_SPEED added. S3M supports speeds from 0 to 255 and
* tempos from 32 to 255 (S3M speed == xmp tempo, S3M tempo == xmp BPM).
*/
/* Wed, 21 Oct 1998 15:03:44 -0500 Geoff Reedy <vader21@imsa.edu>
* It appears that xmp has issues loading/playing a specific instrument
* used in LUCCA.S3M.
* (Fixed by Hipolito in xmp-2.0.0dev34)
*/
/*
* From http://code.pui.ch/2007/02/18/turn-demoscene-modules-into-mp3s/
* The only flaw I noticed [in xmp] is a problem with portamento in Purple
* Motion's second reality soundtrack (1:06-1:17)
*
* Claudio's note: that's a dissonant beating between channels 6 and 7
* starting at pos12, caused by pitchbending effect F25.
*/
#include "loader.h"
#include "s3m.h"
#include "period.h"
#define MAGIC_SCRM MAGIC4('S','C','R','M')
#define MAGIC_SCRI MAGIC4('S','C','R','I')
#define MAGIC_SCRS MAGIC4('S','C','R','S')
static int s3m_test(HIO_HANDLE *, char *, const int);
static int s3m_load(struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_s3m = {
"Scream Tracker 3",
s3m_test,
s3m_load
};
static int s3m_test(HIO_HANDLE *f, char *t, const int start)
{
hio_seek(f, start + 44, SEEK_SET);
if (hio_read32b(f) != MAGIC_SCRM)
return -1;
hio_seek(f, start + 29, SEEK_SET);
if (hio_read8(f) != 0x10)
return -1;
hio_seek(f, start + 0, SEEK_SET);
libxmp_read_title(f, t, 28);
return 0;
}
#define NONE 0xff
#define FX_S3M_EXTENDED 0xfe
/* Effect conversion table */
static const uint8 fx[27] = {
NONE,
FX_S3M_SPEED, /* Axx Set speed to xx (the default is 06) */
FX_JUMP, /* Bxx Jump to order xx (hexadecimal) */
FX_BREAK, /* Cxx Break pattern to row xx (decimal) */
FX_VOLSLIDE, /* Dxy Volume slide down by y/up by x */
FX_PORTA_DN, /* Exx Slide down by xx */
FX_PORTA_UP, /* Fxx Slide up by xx */
FX_TONEPORTA, /* Gxx Tone portamento with speed xx */
FX_VIBRATO, /* Hxy Vibrato with speed x and depth y */
FX_TREMOR, /* Ixy Tremor with ontime x and offtime y */
FX_S3M_ARPEGGIO, /* Jxy Arpeggio with halfnote additions */
FX_VIBRA_VSLIDE, /* Kxy Dual command: H00 and Dxy */
FX_TONE_VSLIDE, /* Lxy Dual command: G00 and Dxy */
NONE,
NONE,
FX_OFFSET, /* Oxy Set sample offset */
NONE,
FX_MULTI_RETRIG, /* Qxy Retrig (+volumeslide) note */
FX_TREMOLO, /* Rxy Tremolo with speed x and depth y */
FX_S3M_EXTENDED, /* Sxx (misc effects) */
FX_S3M_BPM, /* Txx Tempo = xx (hex) */
FX_FINE_VIBRATO, /* Uxx Fine vibrato */
FX_GLOBALVOL, /* Vxx Set global volume */
NONE,
FX_SETPAN, /* Xxx Set pan */
NONE,
NONE
};
/* Effect translation */
static void xlat_fx(int c, struct xmp_event *e)
{
uint8 h = MSN(e->fxp), l = LSN(e->fxp);
if (e->fxt >= ARRAY_SIZE(fx)) {
D_(D_WARN "invalid effect %02x", e->fxt);
e->fxt = e->fxp = 0;
return;
}
switch (e->fxt = fx[e->fxt]) {
case FX_S3M_BPM:
if (e->fxp < 0x20) {
e->fxp = e->fxt = 0;
}
break;
case FX_S3M_EXTENDED: /* Extended effects */
e->fxt = FX_EXTENDED;
switch (h) {
case 0x1: /* Glissando */
e->fxp = LSN(e->fxp) | (EX_GLISS << 4);
break;
case 0x2: /* Finetune */
e->fxp =
((LSN(e->fxp) - 8) & 0x0f) | (EX_FINETUNE << 4);
break;
case 0x3: /* Vibrato wave */
e->fxp = LSN(e->fxp) | (EX_VIBRATO_WF << 4);
break;
case 0x4: /* Tremolo wave */
e->fxp = LSN(e->fxp) | (EX_TREMOLO_WF << 4);
break;
case 0x5:
case 0x6:
case 0x7:
case 0x9:
case 0xa: /* Ignore */
e->fxt = e->fxp = 0;
break;
case 0x8: /* Set pan */
e->fxt = FX_SETPAN;
e->fxp = l << 4;
break;
case 0xb: /* Pattern loop */
e->fxp = LSN(e->fxp) | (EX_PATTERN_LOOP << 4);
break;
case 0xc:
if (!l)
e->fxt = e->fxp = 0;
}
break;
case FX_SETPAN:
/* Saga Musix says: "The X effect in S3M files is not
* exclusive to IT and clones. You will find tons of S3Ms made
* with ST3 itself using this effect (and relying on an
* external player being used). X in S3M also behaves
* differently than in IT, which your code does not seem to
* handle: X00 - X80 is left... right, XA4 is surround (like
* S91 in IT), other values are not supposed to do anything.
*/
if (e->fxp == 0xa4) {
// surround
e->fxt = FX_SURROUND;
e->fxp = 1;
} else {
int pan = ((int)e->fxp) << 1;
if (pan > 0xff) {
pan = 0xff;
}
e->fxp = pan;
}
break;
case NONE: /* No effect */
e->fxt = e->fxp = 0;
break;
}
}
static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
{
struct xmp_module *mod = &m->mod;
int c, r, i;
struct xmp_event *event = 0, dummy;
struct s3m_file_header sfh;
struct s3m_instrument_header sih;
#ifndef LIBXMP_CORE_PLAYER
struct s3m_adlib_header sah;
char tracker_name[40];
#endif
int pat_len;
uint8 n, b;
uint16 *pp_ins; /* Parapointers to instruments */
uint16 *pp_pat; /* Parapointers to patterns */
int ret;
uint8 buf[96]
LOAD_INIT();
if (hio_read(buf, 1, 96, f) != 96) {
goto err;
}
memcpy(sfh.name, buf, 28); /* Song name */
sfh.type = buf[30]; /* File type */
sfh.ordnum = readmem16l(buf + 32); /* Number of orders (must be even) */
sfh.insnum = readmem16l(buf + 34); /* Number of instruments */
sfh.patnum = readmem16l(buf + 36); /* Number of patterns */
sfh.flags = readmem16l(buf + 38); /* Flags */
sfh.version = readmem16l(buf + 40); /* Tracker ID and version */
sfh.ffi = readmem16l(buf + 42); /* File format information */
/* Sanity check */
if (sfh.ffi != 1 && sfh.ffi != 2) {
goto err;
}
if (sfh.ordnum > 255 || sfh.insnum > 255 || sfh.patnum > 255) {
goto err;
}
sfh.magic = readmem32b(buf + 44); /* 'SCRM' */
sfh.gv = buf[48]; /* Global volume */
sfh.is = buf[49]; /* Initial speed */
sfh.it = buf[50]; /* Initial tempo */
sfh.mv = buf[51]; /* Master volume */
sfh.uc = buf[52]; /* Ultra click removal */
sfh.dp = buf[53]; /* Default pan positions if 0xfc */
memcpy(sfh.rsvd2, buf + 54, 8); /* Reserved */
sfh.special = readmem16l(buf + 62); /* Ptr to special custom data */
memcpy(sfh.chset, buf + 64, 32); /* Channel settings */
if (sfh.magic != MAGIC_SCRM) {
goto err;
}
libxmp_copy_adjust(mod->name, sfh.name, 28);
pp_ins = calloc(2, sfh.insnum);
if (pp_ins == NULL) {
goto err;
}
pp_pat = calloc(2, sfh.patnum);
if (pp_pat == NULL) {
goto err2;
}
if (sfh.flags & S3M_AMIGA_RANGE) {
m->period_type = PERIOD_MODRNG;
}
if (sfh.flags & S3M_ST300_VOLS) {
m->quirk |= QUIRK_VSALL;
}
/* m->volbase = 4096 / sfh.gv; */
mod->spd = sfh.is;
mod->bpm = sfh.it;
mod->chn = 0;
for (i = 0; i < 32; i++) {
if (sfh.chset[i] == S3M_CH_OFF)
continue;
mod->chn = i + 1;
if (sfh.mv & 0x80) { /* stereo */
int x = sfh.chset[i] & S3M_CH_PAN;
mod->xxc[i].pan = (x & 0x0f) < 8 ? 0x30 : 0xc0;
} else {
mod->xxc[i].pan = 0x80;
}
}
if (sfh.ordnum <= XMP_MAX_MOD_LENGTH) {
mod->len = sfh.ordnum;
if (hio_read(mod->xxo, 1, mod->len, f) != mod->len) {
goto err3;
}
} else {
mod->len = XMP_MAX_MOD_LENGTH;
if (hio_read(mod->xxo, 1, mod->len, f) != mod->len) {
goto err3;
}
if (hio_seek(f, sfh.ordnum - XMP_MAX_MOD_LENGTH, SEEK_CUR) < 0) {
goto err3;
}
}
/* Don't trust sfh.patnum */
mod->pat = -1;
for (i = 0; i < mod->len; ++i) {
if (mod->xxo[i] < 0xfe && mod->xxo[i] > mod->pat) {
mod->pat = mod->xxo[i];
}
}
mod->pat++;
if (mod->pat > sfh.patnum) {
mod->pat = sfh.patnum;
}
if (mod->pat == 0) {
goto err3;
}
mod->trk = mod->pat * mod->chn;
/* Load and convert header */
mod->ins = sfh.insnum;
mod->smp = mod->ins;
for (i = 0; i < sfh.insnum; i++) {
pp_ins[i] = hio_read16l(f);
}
for (i = 0; i < sfh.patnum; i++) {
pp_pat[i] = hio_read16l(f);
}
/* Default pan positions */
for (i = 0, sfh.dp -= 0xfc; !sfh.dp /* && n */ && (i < 32); i++) {
uint8 x = hio_read8(f);
if (x & S3M_PAN_SET) {
mod->xxc[i].pan = (x << 4) & 0xff;
} else {
mod->xxc[i].pan =
sfh.mv % 0x80 ? 0x30 + 0xa0 * (i & 1) : 0x80;
}
}
m->c4rate = C4_NTSC_RATE;
if (sfh.version == 0x1300) {
m->quirk |= QUIRK_VSALL;
}
#ifndef LIBXMP_CORE_PLAYER
switch (sfh.version >> 12) {
case 1:
snprintf(tracker_name, 40, "Scream Tracker %d.%02x",
(sfh.version & 0x0f00) >> 8, sfh.version & 0xff);
m->quirk |= QUIRK_ST3BUGS;
break;
case 2:
snprintf(tracker_name, 40, "Imago Orpheus %d.%02x",
(sfh.version & 0x0f00) >> 8, sfh.version & 0xff);
break;
case 3:
if (sfh.version == 0x3216) {
strcpy(tracker_name, "Impulse Tracker 2.14v3");
} else if (sfh.version == 0x3217) {
strcpy(tracker_name, "Impulse Tracker 2.14v5");
} else {
snprintf(tracker_name, 40, "Impulse Tracker %d.%02x",
(sfh.version & 0x0f00) >> 8,
sfh.version & 0xff);
}
break;
case 5:
snprintf(tracker_name, 40, "OpenMPT %d.%02x",
(sfh.version & 0x0f00) >> 8, sfh.version & 0xff);
m->quirk |= QUIRK_ST3BUGS;
break;
case 4:
if (sfh.version != 0x4100) {
libxmp_schism_tracker_string(tracker_name, 40,
(sfh.version & 0x0fff), sfh.rsvd2[0] | (sfh.rsvd2[1] << 8));
break;
}
/* fall through */
case 6:
snprintf(tracker_name, 40, "BeRoTracker %d.%02x",
(sfh.version & 0x0f00) >> 8, sfh.version & 0xff);
break;
default:
snprintf(tracker_name, 40, "unknown (%04x)", sfh.version);
}
libxmp_set_type(m, "%s S3M", tracker_name);
#else
libxmp_set_type(m, "Scream Tracker 3");
m->quirk |= QUIRK_ST3BUGS;
#endif
MODULE_INFO();
if (libxmp_init_pattern(mod) < 0)
goto err3;
/* Read patterns */
D_(D_INFO "Stored patterns: %d", mod->pat);
for (i = 0; i < mod->pat; i++) {
if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0)
goto err3;
if (pp_pat[i] == 0)
continue;
hio_seek(f, start + pp_pat[i] * 16, SEEK_SET);
r = 0;
pat_len = hio_read16l(f) - 2;
while (pat_len >= 0 && r < mod->xxp[i]->rows) {
b = hio_read8(f);
if (hio_error(f)) {
goto err3;
}
if (b == S3M_EOR) {
r++;
continue;
}
c = b & S3M_CH_MASK;
event = c >= mod->chn ? &dummy : &EVENT(i, c, r);
if (b & S3M_NI_FOLLOW) {
switch (n = hio_read8(f)) {
case 255:
n = 0;
break; /* Empty note */
case 254:
n = XMP_KEY_OFF;
break; /* Key off */
default:
n = 13 + 12 * MSN(n) + LSN(n);
}
event->note = n;
event->ins = hio_read8(f);
pat_len -= 2;
}
if (b & S3M_VOL_FOLLOWS) {
event->vol = hio_read8(f) + 1;
pat_len--;
}
if (b & S3M_FX_FOLLOWS) {
event->fxt = hio_read8(f);
event->fxp = hio_read8(f);
xlat_fx(c, event);
pat_len -= 2;
}
}
}
D_(D_INFO "Stereo enabled: %s", sfh.mv & 0x80 ? "yes" : "no");
D_(D_INFO "Pan settings: %s", sfh.dp ? "no" : "yes");
if (libxmp_init_instrument(m) < 0)
goto err3;
/* Read and convert instruments and samples */
D_(D_INFO "Instruments: %d", mod->ins);
for (i = 0; i < mod->ins; i++) {
struct xmp_instrument *xxi = &mod->xxi[i];
struct xmp_sample *xxs = &mod->xxs[i];
struct xmp_subinstrument *sub;
int load_sample_flags;
xxi->sub = calloc(sizeof(struct xmp_subinstrument), 1);
if (xxi->sub == NULL) {
goto err3;
}
sub = &xxi->sub[0];
hio_seek(f, start + pp_ins[i] * 16, SEEK_SET);
sub->pan = 0x80;
sub->sid = i;
if (hio_read(buf, 1, 80, f) != 80) {
goto err3;
}
if (buf[0] >= 2) {
#ifndef LIBXMP_CORE_PLAYER
/* OPL2 FM instrument */
memcpy(sah.dosname, buf + 1, 12); /* DOS file name */
memcpy(sah.reg, buf + 16, 12); /* Adlib registers */
sah.vol = buf[28];
sah.dsk = buf[29];
sah.c2spd = readmem16l(buf + 32); /* C4 speed */
memcpy(sah.name, buf + 48, 28); /* Instrument name */
sah.magic = readmem32b(buf + 76); /* 'SCRI' */
if (sah.magic != MAGIC_SCRI) {
D_(D_CRIT "error: FM instrument magic");
goto err3;
}
sah.magic = 0;
libxmp_instrument_name(mod, i, sah.name, 28);
xxi->nsm = 1;
sub->vol = sah.vol;
libxmp_c2spd_to_note(sah.c2spd, &sub->xpo, &sub->fin);
sub->xpo += 12;
ret =
libxmp_load_sample(m, f, SAMPLE_FLAG_ADLIB, xxs,
(char *)sah.reg);
if (ret < 0)
goto err3;
D_(D_INFO "[%2X] %-28.28s", i, xxi->name);
continue;
#else
goto err3;
#endif
}
memcpy(sih.dosname, buf + 1, 13); /* DOS file name */
sih.memseg = readmem16l(buf + 14); /* Pointer to sample data */
sih.length = readmem32l(buf + 16); /* Length */
#if 0
/* ST3 limit */
if ((sfh.version >> 12) == 1 && sih.length > 64000)
sih.length = 64000;
#endif
if (sih.length > MAX_SAMPLE_SIZE) {
goto err3;
}
sih.loopbeg = readmem32l(buf + 20); /* Loop begin */
sih.loopend = readmem32l(buf + 24); /* Loop end */
sih.vol = buf[28]; /* Volume */
sih.pack = buf[30]; /* Packing type */
sih.flags = buf[31]; /* Loop/stereo/16bit flags */
sih.c2spd = readmem16l(buf + 32); /* C4 speed */
memcpy(sih.name, buf + 48, 28); /* Instrument name */
sih.magic = readmem32b(buf + 76); /* 'SCRS' */
if (buf[0] == 1 && sih.magic != MAGIC_SCRS) {
D_(D_CRIT "error: instrument magic");
goto err3;
}
xxs->len = sih.length;
xxi->nsm = sih.length > 0 ? 1 : 0;
xxs->lps = sih.loopbeg;
xxs->lpe = sih.loopend;
xxs->flg = sih.flags & 1 ? XMP_SAMPLE_LOOP : 0;
if (sih.flags & 4) {
xxs->flg |= XMP_SAMPLE_16BIT;
}
load_sample_flags = (sfh.ffi == 1) ? 0 : SAMPLE_FLAG_UNS;
if (sih.pack == 4) {
load_sample_flags = SAMPLE_FLAG_ADPCM;
}
sub->vol = sih.vol;
sih.magic = 0;
libxmp_instrument_name(mod, i, sih.name, 28);
D_(D_INFO "[%2X] %-28.28s %04x%c%04x %04x %c V%02x %5d",
i, mod->xxi[i].name, mod->xxs[i].len,
xxs->flg & XMP_SAMPLE_16BIT ? '+' : ' ',
xxs->lps, mod->xxs[i].lpe,
xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', sub->vol, sih.c2spd);
libxmp_c2spd_to_note(sih.c2spd, &sub->xpo, &sub->fin);
if (hio_seek(f, start + 16L * sih.memseg, SEEK_SET) < 0) {
goto err3;
}
ret = libxmp_load_sample(m, f, load_sample_flags, xxs, NULL);
if (ret < 0) {
goto err3;
}
}
free(pp_pat);
free(pp_ins);
m->quirk |= QUIRKS_ST3 | QUIRK_ARPMEM;
m->read_event_type = READ_EVENT_ST3;
return 0;
err3:
free(pp_pat);
err2:
free(pp_ins);
err:
return -1;
}

View file

@ -0,0 +1,445 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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 "loader.h"
#ifndef LIBXMP_CORE_PLAYER
/*
* From the Audio File Formats (version 2.5)
* Submitted-by: Guido van Rossum <guido@cwi.nl>
* Last-modified: 27-Aug-1992
*
* The Acorn Archimedes uses a variation on U-LAW with the bit order
* reversed and the sign bit in bit 0. Being a 'minority' architecture,
* Arc owners are quite adept at converting sound/image formats from
* other machines, and it is unlikely that you'll ever encounter sound in
* one of the Arc's own formats (there are several).
*/
static const int8 vdic_table[128] = {
/* 0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 16 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 24 */ 1, 1, 1, 1, 1, 1, 1, 1,
/* 32 */ 1, 1, 1, 1, 2, 2, 2, 2,
/* 40 */ 2, 2, 2, 2, 3, 3, 3, 3,
/* 48 */ 3, 3, 4, 4, 4, 4, 5, 5,
/* 56 */ 5, 5, 6, 6, 6, 6, 7, 7,
/* 64 */ 7, 8, 8, 9, 9, 10, 10, 11,
/* 72 */ 11, 12, 12, 13, 13, 14, 14, 15,
/* 80 */ 15, 16, 17, 18, 19, 20, 21, 22,
/* 88 */ 23, 24, 25, 26, 27, 28, 29, 30,
/* 96 */ 31, 33, 34, 36, 38, 40, 42, 44,
/* 104 */ 46, 48, 50, 52, 54, 56, 58, 60,
/* 112 */ 62, 65, 68, 72, 77, 80, 84, 91,
/* 120 */ 95, 98, 103, 109, 114, 120, 126, 127
};
/* Convert 7 bit samples to 8 bit */
static void convert_7bit_to_8bit(uint8 *p, int l)
{
for (; l--; p++) {
*p <<= 1;
}
}
/* Convert Archimedes VIDC samples to linear */
static void convert_vidc_to_linear(uint8 *p, int l)
{
int i;
uint8 x;
for (i = 0; i < l; i++) {
x = p[i];
p[i] = vdic_table[x >> 1];
if (x & 0x01)
p[i] *= -1;
}
}
static void adpcm4_decoder(uint8 *inp, uint8 *outp, char *tab, int len)
{
char delta = 0;
uint8 b0, b1;
int i;
len = (len + 1) / 2;
for (i = 0; i < len; i++) {
b0 = *inp;
b1 = *inp++ >> 4;
delta += tab[b0 & 0x0f];
*outp++ = delta;
delta += tab[b1 & 0x0f];
*outp++ = delta;
}
}
#endif
/* Convert differential to absolute sample data */
static void convert_delta(uint8 *p, int l, int r)
{
uint16 *w = (uint16 *)p;
uint16 abs = 0;
if (r) {
for (; l--;) {
abs = *w + abs;
*w++ = abs;
}
} else {
for (; l--;) {
abs = *p + abs;
*p++ = (uint8) abs;
}
}
}
/* Convert signed to unsigned sample data */
static void convert_signal(uint8 *p, int l, int r)
{
uint16 *w = (uint16 *)p;
if (r) {
for (; l--; w++)
*w += 0x8000;
} else {
for (; l--; p++)
*p += (char)0x80; /* cast needed by MSVC++ */
}
}
/* Convert little-endian 16 bit samples to big-endian */
static void convert_endian(uint8 *p, int l)
{
uint8 b;
int i;
for (i = 0; i < l; i++) {
b = p[0];
p[0] = p[1];
p[1] = b;
p += 2;
}
}
#if 0
/* Downmix stereo samples to mono */
static void convert_stereo_to_mono(uint8 *p, int l, int r)
{
int16 *b = (int16 *)p;
int i;
if (r) {
l /= 2;
for (i = 0; i < l; i++)
b[i] = (b[i * 2] + b[i * 2 + 1]) / 2;
} else {
for (i = 0; i < l; i++)
p[i] = (p[i * 2] + p[i * 2 + 1]) / 2;
}
}
#endif
static void unroll_loop(struct xmp_sample *xxs)
{
int8 *s8;
int16 *s16;
int start, loop_size;
int i;
s16 = (int16 *)xxs->data;
s8 = (int8 *)xxs->data;
if (xxs->len > xxs->lpe) {
start = xxs->lpe;
} else {
start = xxs->len;
}
loop_size = xxs->lpe - xxs->lps;
if (xxs->flg & XMP_SAMPLE_16BIT) {
s16 += start;
for (i = 0; i < loop_size; i++) {
*(s16 + i) = *(s16 - i - 1);
}
} else {
s8 += start;
for (i = 0; i < loop_size; i++) {
*(s8 + i) = *(s8 - i - 1);
}
}
}
int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct xmp_sample *xxs, const void *buffer)
{
int bytelen, extralen, unroll_extralen, i;
#ifndef LIBXMP_CORE_PLAYER
/* Adlib FM patches */
if (flags & SAMPLE_FLAG_ADLIB) {
return 0;
}
#endif
/* Empty or invalid samples
*/
if (xxs->len <= 0) {
return 0;
}
/* Skip sample loading
* FIXME: fails for ADPCM samples
*
* + Sanity check: skip huge samples (likely corrupt module)
*/
if (xxs->len > MAX_SAMPLE_SIZE || (m && m->smpctl & XMP_SMPCTL_SKIP)) {
if (~flags & SAMPLE_FLAG_NOLOAD) {
/* coverity[check_return] */
hio_seek(f, xxs->len, SEEK_CUR);
}
return 0;
}
/* If this sample starts at or after EOF, skip it entirely.
*/
if (~flags & SAMPLE_FLAG_NOLOAD) {
long file_pos, file_len;
if (!f) {
return 0;
}
file_pos = hio_tell(f);
file_len = hio_size(f);
if (file_pos >= file_len) {
D_(D_WARN "ignoring sample at EOF");
return 0;
}
/* If this sample goes past EOF, truncate it. */
if (file_pos + xxs->len > file_len && (~flags & SAMPLE_FLAG_ADPCM)) {
D_(D_WARN "sample would extend %ld bytes past EOF; truncating to %ld",
file_pos + xxs->len - file_len, file_len - file_pos);
xxs->len = file_len - file_pos;
}
}
/* Loop parameters sanity check
*/
if (xxs->lps < 0) {
xxs->lps = 0;
}
if (xxs->lpe > xxs->len) {
xxs->lpe = xxs->len;
}
if (xxs->lps >= xxs->len || xxs->lps >= xxs->lpe) {
xxs->lps = xxs->lpe = 0;
xxs->flg &= ~(XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_BIDIR);
}
/* Patches with samples
* Allocate extra sample for interpolation.
*/
bytelen = xxs->len;
extralen = 4;
unroll_extralen = 0;
/* Disable birectional loop flag if sample is not looped
*/
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
if (~xxs->flg & XMP_SAMPLE_LOOP)
xxs->flg &= ~XMP_SAMPLE_LOOP_BIDIR;
}
/* Unroll bidirectional loops
*/
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
unroll_extralen = (xxs->lpe - xxs->lps) -
(xxs->len - xxs->lpe);
if (unroll_extralen < 0) {
unroll_extralen = 0;
}
}
if (xxs->flg & XMP_SAMPLE_16BIT) {
bytelen *= 2;
extralen *= 2;
unroll_extralen *= 2;
}
/* add guard bytes before the buffer for higher order interpolation */
xxs->data = malloc(bytelen + extralen + unroll_extralen + 4);
if (xxs->data == NULL) {
goto err;
}
*(uint32 *)xxs->data = 0;
xxs->data += 4;
if (flags & SAMPLE_FLAG_NOLOAD) {
memcpy(xxs->data, buffer, bytelen);
} else
#ifndef LIBXMP_CORE_PLAYER
if (flags & SAMPLE_FLAG_ADPCM) {
int x2 = (bytelen + 1) >> 1;
char table[16];
if (hio_read(table, 1, 16, f) != 16) {
goto err2;
}
if (hio_read(xxs->data + x2, 1, x2, f) != x2) {
goto err2;
}
adpcm4_decoder((uint8 *)xxs->data + x2,
(uint8 *)xxs->data, table, bytelen);
} else
#endif
{
int x = hio_read(xxs->data, 1, bytelen, f);
if (x != bytelen) {
D_(D_WARN "short read (%d) in sample load", x - bytelen);
memset(xxs->data + x, 0, bytelen - x);
}
}
#ifndef LIBXMP_CORE_PLAYER
if (flags & SAMPLE_FLAG_7BIT) {
convert_7bit_to_8bit(xxs->data, xxs->len);
}
#endif
/* Fix endianism if needed */
if (xxs->flg & XMP_SAMPLE_16BIT) {
#ifdef WORDS_BIGENDIAN
if (~flags & SAMPLE_FLAG_BIGEND)
convert_endian(xxs->data, xxs->len);
#else
if (flags & SAMPLE_FLAG_BIGEND)
convert_endian(xxs->data, xxs->len);
#endif
}
/* Convert delta samples */
if (flags & SAMPLE_FLAG_DIFF) {
convert_delta(xxs->data, xxs->len, xxs->flg & XMP_SAMPLE_16BIT);
} else if (flags & SAMPLE_FLAG_8BDIFF) {
int len = xxs->len;
if (xxs->flg & XMP_SAMPLE_16BIT) {
len *= 2;
}
convert_delta(xxs->data, len, 0);
}
/* Convert samples to signed */
if (flags & SAMPLE_FLAG_UNS) {
convert_signal(xxs->data, xxs->len,
xxs->flg & XMP_SAMPLE_16BIT);
}
#if 0
/* Downmix stereo samples */
if (flags & SAMPLE_FLAG_STEREO) {
convert_stereo_to_mono(xxs->data, xxs->len,
xxs->flg & XMP_SAMPLE_16BIT);
xxs->len /= 2;
}
#endif
#ifndef LIBXMP_CORE_PLAYER
if (flags & SAMPLE_FLAG_VIDC) {
convert_vidc_to_linear(xxs->data, xxs->len);
}
#endif
/* Check for full loop samples */
if (flags & SAMPLE_FLAG_FULLREP) {
if (xxs->lps == 0 && xxs->len > xxs->lpe)
xxs->flg |= XMP_SAMPLE_LOOP_FULL;
}
/* Unroll bidirectional loops */
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
unroll_loop(xxs);
bytelen += unroll_extralen;
}
/* Add extra samples at end */
if (xxs->flg & XMP_SAMPLE_16BIT) {
for (i = 0; i < 8; i++) {
xxs->data[bytelen + i] = xxs->data[bytelen - 2 + i];
}
} else {
for (i = 0; i < 4; i++) {
xxs->data[bytelen + i] = xxs->data[bytelen - 1 + i];
}
}
/* Add extra samples at start */
if (xxs->flg & XMP_SAMPLE_16BIT) {
xxs->data[-2] = xxs->data[0];
xxs->data[-1] = xxs->data[1];
} else {
xxs->data[-1] = xxs->data[0];
}
/* Fix sample at loop */
if (xxs->flg & XMP_SAMPLE_LOOP) {
int lpe = xxs->lpe;
int lps = xxs->lps;
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
lpe += lpe - lps;
}
if (xxs->flg & XMP_SAMPLE_16BIT) {
lpe <<= 1;
lps <<= 1;
for (i = 0; i < 8; i++) {
xxs->data[lpe + i] = xxs->data[lps + i];
}
} else {
for (i = 0; i < 4; i++) {
xxs->data[lpe + i] = xxs->data[lps + i];
}
}
}
return 0;
#ifndef LIBXMP_CORE_PLAYER
err2:
libxmp_free_sample(xxs);
#endif
err:
return -1;
}
void libxmp_free_sample(struct xmp_sample *s)
{
if (s->data) {
free(s->data - 4);
s->data = NULL; /* prevent double free in PCM load error */
}
}

View file

@ -0,0 +1,580 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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.
*/
/*
* Sun, 31 May 1998 17:50:02 -0600
* Reported by ToyKeeper <scriven@CS.ColoState.EDU>:
* For loop-prevention, I know a way to do it which lets most songs play
* fine once through even if they have backward-jumps. Just keep a small
* array (256 bytes, or even bits) of flags, each entry determining if a
* pattern in the song order has been played. If you get to an entry which
* is already non-zero, skip to the next song (assuming looping is off).
*/
/*
* Tue, 6 Oct 1998 21:23:17 +0200 (CEST)
* Reported by John v/d Kamp <blade_@dds.nl>:
* scan.c was hanging when it jumps to an invalid restart value.
* (Fixed by hipolito)
*/
#include "common.h"
#include "effects.h"
#include "mixer.h"
#define S3M_END 0xff
#define S3M_SKIP 0xfe
static int scan_module(struct context_data *ctx, int ep, int chain)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
int parm, gvol_memory, f1, f2, p1, p2, ord, ord2;
int row, last_row, break_row, row_count, row_count_total;
int orders_since_last_valid, any_valid;
int gvl, bpm, speed, base_time, chn;
int frame_count;
double time, start_time;
int loop_chn, loop_num, inside_loop;
int pdelay = 0;
int loop_count[XMP_MAX_CHANNELS];
int loop_row[XMP_MAX_CHANNELS];
struct xmp_event* event;
int i, pat;
int has_marker;
struct ord_data *info;
#ifndef LIBXMP_CORE_PLAYER
int st26_speed;
#endif
if (mod->len == 0)
return 0;
for (i = 0; i < mod->len; i++) {
int pat = mod->xxo[i];
memset(m->scan_cnt[i], 0, pat >= mod->pat ? 1 :
mod->xxp[pat]->rows ? mod->xxp[pat]->rows : 1);
}
for (i = 0; i < mod->chn; i++) {
loop_count[i] = 0;
loop_row[i] = -1;
}
loop_num = 0;
loop_chn = -1;
gvl = mod->gvl;
bpm = mod->bpm;
speed = mod->spd;
base_time = m->rrate;
#ifndef LIBXMP_CORE_PLAYER
st26_speed = 0;
#endif
has_marker = HAS_QUIRK(QUIRK_MARKER);
/* By erlk ozlr <erlk.ozlr@gmail.com>
*
* xmp doesn't handle really properly the "start" option (-s for the
* command-line) for DeusEx's .umx files. These .umx files contain
* several loop "tracks" that never join together. That's how they put
* multiple musics on each level with a file per level. Each "track"
* starts at the same order in all files. The problem is that xmp starts
* decoding the module at order 0 and not at the order specified with
* the start option. If we have a module that does "0 -> 2 -> 0 -> ...",
* we cannot play order 1, even with the supposed right option.
*
* was: ord2 = ord = -1;
*
* CM: Fixed by using different "sequences" for each loop or subsong.
* Each sequence has its entry point. Sequences don't overlap.
*/
ord2 = -1;
ord = ep - 1;
gvol_memory = break_row = row_count = row_count_total = frame_count = 0;
orders_since_last_valid = any_valid = 0;
start_time = time = 0.0;
inside_loop = 0;
while (42) {
/* Sanity check to prevent getting stuck due to broken patterns. */
if (orders_since_last_valid > 512) {
D_(D_CRIT "orders_since_last_valid = %d @ ord %d; ending scan", orders_since_last_valid, ord);
break;
}
orders_since_last_valid++;
if ((uint32)++ord >= mod->len) {
if (mod->rst > mod->len || mod->xxo[mod->rst] >= mod->pat) {
ord = ep;
} else {
if (libxmp_get_sequence(ctx, mod->rst) == chain) {
ord = mod->rst;
} else {
ord = ep;
}
}
pat = mod->xxo[ord];
if (has_marker && pat == S3M_END) {
break;
}
}
pat = mod->xxo[ord];
info = &m->xxo_info[ord];
/* Allow more complex order reuse only in main sequence */
if (ep != 0 && p->sequence_control[ord] != 0xff) {
break;
}
p->sequence_control[ord] = chain;
/* All invalid patterns skipped, only S3M_END aborts replay */
if (pat >= mod->pat) {
if (has_marker && pat == S3M_END) {
ord = mod->len;
continue;
}
continue;
}
if (break_row >= mod->xxp[pat]->rows) {
break_row = 0;
}
/* Loops can cross pattern boundaries, so check if we're not looping */
if (m->scan_cnt[ord][break_row] && !inside_loop) {
break;
}
/* Only update pattern information if we weren't here before. This also
* means that we don't update pattern information if we're inside a loop,
* otherwise a loop containing e.g. a global volume fade can make the
* pattern start with the wrong volume. (fixes xyce-dans_la_rue.xm replay,
* see https://github.com/libxmp/libxmp/issues/153 for more details).
*/
if (info->time < 0) {
info->gvl = gvl;
info->bpm = bpm;
info->speed = speed;
info->time = time + m->time_factor * frame_count * base_time / bpm;
#ifndef LIBXMP_CORE_PLAYER
info->st26_speed = st26_speed;
#endif
}
if (info->start_row == 0 && ord != 0) {
if (ord == ep) {
start_time = time + m->time_factor * frame_count * base_time / bpm;
}
info->start_row = break_row;
}
last_row = mod->xxp[pat]->rows;
for (row = break_row, break_row = 0; row < last_row; row++, row_count++, row_count_total++) {
/* Prevent crashes caused by large softmixer frames */
if (bpm < XMP_MIN_BPM) {
bpm = XMP_MIN_BPM;
}
/* Date: Sat, 8 Sep 2007 04:01:06 +0200
* Reported by Zbigniew Luszpinski <zbiggy@o2.pl>
* The scan routine falls into infinite looping and doesn't let
* xmp play jos-dr4k.xm.
* Claudio's workaround: we'll break infinite loops here.
*
* Date: Oct 27, 2007 8:05 PM
* From: Adric Riedel <adric.riedel@gmail.com>
* Jesper Kyd: Global Trash 3.mod (the 'Hardwired' theme) only
* plays the first 4:41 of what should be a 10 minute piece.
* (...) it dies at the end of position 2F
*/
if (row_count_total > 512) { /* was 255, but Global trash goes to 318. */
D_(D_CRIT "row_count_total = %d @ ord %d, pat %d, row %d; ending scan", row_count_total, ord, pat, row);
goto end_module;
}
if (!loop_num && m->scan_cnt[ord][row]) {
row_count--;
goto end_module;
}
m->scan_cnt[ord][row]++;
orders_since_last_valid = 0;
any_valid = 1;
/* If the scan count for this row overflows, break.
* A scan count of 0 will help break this loop in playback (storlek_11.it).
*/
if (!m->scan_cnt[ord][row]) {
goto end_module;
}
pdelay = 0;
for (chn = 0; chn < mod->chn; chn++) {
if (row >= mod->xxt[mod->xxp[pat]->index[chn]]->rows)
continue;
event = &EVENT(mod->xxo[ord], chn, row);
f1 = event->fxt;
p1 = event->fxp;
f2 = event->f2t;
p2 = event->f2p;
if (f1 == FX_GLOBALVOL || f2 == FX_GLOBALVOL) {
gvl = (f1 == FX_GLOBALVOL) ? p1 : p2;
gvl = gvl > m->gvolbase ? m->gvolbase : gvl < 0 ? 0 : gvl;
}
/* Process fine global volume slide */
if (f1 == FX_GVOL_SLIDE || f2 == FX_GVOL_SLIDE) {
int h, l;
parm = (f1 == FX_GVOL_SLIDE) ? p1 : p2;
process_gvol:
if (parm) {
gvol_memory = parm;
h = MSN(parm);
l = LSN(parm);
if (HAS_QUIRK(QUIRK_FINEFX)) {
if (l == 0xf && h != 0) {
gvl += h;
} else if (h == 0xf && l != 0) {
gvl -= l;
} else {
if (m->quirk & QUIRK_VSALL) {
gvl += (h - l) * speed;
} else {
gvl += (h - l) * (speed - 1);
}
}
} else {
if (m->quirk & QUIRK_VSALL) {
gvl += (h - l) * speed;
} else {
gvl += (h - l) * (speed - 1);
}
}
} else {
if ((parm = gvol_memory) != 0)
goto process_gvol;
}
}
if ((f1 == FX_SPEED && p1) || (f2 == FX_SPEED && p2)) {
parm = (f1 == FX_SPEED) ? p1 : p2;
frame_count += row_count * speed;
row_count = 0;
if (parm) {
if (HAS_QUIRK(QUIRK_NOBPM) || p->flags & XMP_FLAGS_VBLANK || parm < 0x20) {
if (parm > 0) {
speed = parm;
#ifndef LIBXMP_CORE_PLAYER
st26_speed = 0;
#endif
}
} else {
time += m->time_factor * frame_count * base_time / bpm;
frame_count = 0;
bpm = parm;
}
}
}
#ifndef LIBXMP_CORE_PLAYER
if (f1 == FX_SPEED_CP) {
f1 = FX_S3M_SPEED;
}
if (f2 == FX_SPEED_CP) {
f2 = FX_S3M_SPEED;
}
/* ST2.6 speed processing */
if (f1 == FX_ICE_SPEED && p1) {
if (LSN(p1)) {
st26_speed = (MSN(p1) << 8) | LSN(p1);
} else {
st26_speed = MSN(p1);
}
}
#endif
if ((f1 == FX_S3M_SPEED && p1) || (f2 == FX_S3M_SPEED && p2)) {
parm = (f1 == FX_S3M_SPEED) ? p1 : p2;
if (parm > 0) {
frame_count += row_count * speed;
row_count = 0;
speed = parm;
#ifndef LIBXMP_CORE_PLAYER
st26_speed = 0;
#endif
}
}
if ((f1 == FX_S3M_BPM && p1) || (f2 == FX_S3M_BPM && p2)) {
parm = (f1 == FX_S3M_BPM) ? p1 : p2;
if (parm >= 0x20) {
frame_count += row_count * speed;
row_count = 0;
time += m->time_factor * frame_count * base_time / bpm;
frame_count = 0;
bpm = parm;
}
}
#ifndef LIBXMP_CORE_DISABLE_IT
if ((f1 == FX_IT_BPM && p1) || (f2 == FX_IT_BPM && p2)) {
parm = (f1 == FX_IT_BPM) ? p1 : p2;
frame_count += row_count * speed;
row_count = 0;
time += m->time_factor * frame_count * base_time / bpm;
frame_count = 0;
if (MSN(parm) == 0) {
time += m->time_factor * base_time / bpm;
for (i = 1; i < speed; i++) {
bpm -= LSN(parm);
if (bpm < 0x20)
bpm = 0x20;
time += m->time_factor * base_time / bpm;
}
/* remove one row at final bpm */
time -= m->time_factor * speed * base_time / bpm;
} else if (MSN(parm) == 1) {
time += m->time_factor * base_time / bpm;
for (i = 1; i < speed; i++) {
bpm += LSN(parm);
if (bpm > 0xff)
bpm = 0xff;
time += m->time_factor * base_time / bpm;
}
/* remove one row at final bpm */
time -= m->time_factor * speed * base_time / bpm;
} else {
bpm = parm;
}
}
if (f1 == FX_IT_ROWDELAY) {
/* Don't allow the scan count for this row to overflow here. */
int x = m->scan_cnt[ord][row] + (p1 & 0x0f);
m->scan_cnt[ord][row] = MIN(x, 255);
frame_count += (p1 & 0x0f) * speed;
}
if (f1 == FX_IT_BREAK) {
break_row = p1;
last_row = 0;
}
#endif
if (f1 == FX_JUMP || f2 == FX_JUMP) {
ord2 = (f1 == FX_JUMP) ? p1 : p2;
break_row = 0;
last_row = 0;
/* prevent infinite loop, see OpenMPT PatLoop-Various.xm */
inside_loop = 0;
}
if (f1 == FX_BREAK || f2 == FX_BREAK) {
parm = (f1 == FX_BREAK) ? p1 : p2;
break_row = 10 * MSN(parm) + LSN(parm);
last_row = 0;
}
if (f1 == FX_EXTENDED || f2 == FX_EXTENDED) {
parm = (f1 == FX_EXTENDED) ? p1 : p2;
if ((parm >> 4) == EX_PATT_DELAY) {
if (m->read_event_type != READ_EVENT_ST3 || !pdelay) {
pdelay = parm & 0x0f;
frame_count += pdelay * speed;
}
}
if ((parm >> 4) == EX_PATTERN_LOOP) {
if (parm &= 0x0f) {
/* Loop end */
if (loop_count[chn]) {
if (--loop_count[chn]) {
/* next iteraction */
loop_chn = chn;
} else {
/* finish looping */
loop_num--;
inside_loop = 0;
if (m->quirk & QUIRK_S3MLOOP)
loop_row[chn] = row;
}
} else {
loop_count[chn] = parm;
loop_chn = chn;
loop_num++;
}
} else {
/* Loop start */
loop_row[chn] = row - 1;
inside_loop = 1;
if (HAS_QUIRK(QUIRK_FT2BUGS))
break_row = row;
}
}
}
}
if (loop_chn >= 0) {
row = loop_row[loop_chn];
loop_chn = -1;
}
#ifndef LIBXMP_CORE_PLAYER
if (st26_speed) {
frame_count += row_count * speed;
row_count = 0;
if (st26_speed & 0x10000) {
speed = (st26_speed & 0xff00) >> 8;
} else {
speed = st26_speed & 0xff;
}
st26_speed ^= 0x10000;
}
#endif
}
if (break_row && pdelay) {
break_row++;
}
if (ord2 >= 0) {
ord = ord2 - 1;
ord2 = -1;
}
frame_count += row_count * speed;
row_count_total = 0;
row_count = 0;
}
row = break_row;
end_module:
/* Sanity check */
{
if (!any_valid) {
return -1;
}
pat = mod->xxo[ord];
if (pat >= mod->pat || row >= mod->xxp[pat]->rows) {
row = 0;
}
}
p->scan[chain].num = m->scan_cnt[ord][row];
p->scan[chain].row = row;
p->scan[chain].ord = ord;
time -= start_time;
frame_count += row_count * speed;
return (time + m->time_factor * frame_count * base_time / bpm);
}
int libxmp_get_sequence(struct context_data *ctx, int ord)
{
struct player_data *p = &ctx->p;
return p->sequence_control[ord];
}
int libxmp_scan_sequences(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
int i, ep;
int seq;
unsigned char temp_ep[XMP_MAX_MOD_LENGTH];
/* Initialize order data to prevent overwrite when a position is used
* multiple times at different starting points (see janosik.xm).
*/
for (i = 0; i < XMP_MAX_MOD_LENGTH; i++) {
m->xxo_info[i].time = -1;
}
ep = 0;
memset(p->sequence_control, 0xff, XMP_MAX_MOD_LENGTH);
temp_ep[0] = 0;
p->scan[0].time = scan_module(ctx, ep, 0);
seq = 1;
if (p->scan[0].time < 0) {
D_(D_CRIT "scan was not able to find any valid orders");
return -1;
}
while (1) {
/* Scan song starting at given entry point */
/* Check if any patterns left */
for (i = 0; i < mod->len; i++) {
if (p->sequence_control[i] == 0xff) {
break;
}
}
if (i != mod->len && seq < MAX_SEQUENCES) {
/* New entry point */
ep = i;
temp_ep[seq] = ep;
p->scan[seq].time = scan_module(ctx, ep, seq);
if (p->scan[seq].time > 0)
seq++;
} else {
break;
}
}
m->num_sequences = seq;
/* Now place entry points in the public accessible array */
for (i = 0; i < m->num_sequences; i++) {
m->seq_data[i].entry_point = temp_ep[i];
m->seq_data[i].duration = p->scan[i].time;
}
return 0;
}

View file

@ -0,0 +1,331 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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 "period.h"
#include "player.h"
#include "hio.h"
#include "loader.h"
struct xmp_instrument *libxmp_get_instrument(struct context_data *ctx, int ins)
{
struct smix_data *smix = &ctx->smix;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
struct xmp_instrument *xxi;
if (ins < mod->ins) {
xxi = &mod->xxi[ins];
} else if (ins < mod->ins + smix->ins) {
xxi = &smix->xxi[ins - mod->ins];
} else {
xxi = NULL;
}
return xxi;
}
struct xmp_sample *libxmp_get_sample(struct context_data *ctx, int smp)
{
struct smix_data *smix = &ctx->smix;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
struct xmp_sample *xxs;
if (smp < mod->smp) {
xxs = &mod->xxs[smp];
} else if (smp < mod->smp + smix->smp) {
xxs = &smix->xxs[smp - mod->smp];
} else {
xxs = NULL;
}
return xxs;
}
int xmp_start_smix(xmp_context opaque, int chn, int smp)
{
struct context_data *ctx = (struct context_data *)opaque;
struct smix_data *smix = &ctx->smix;
if (ctx->state > XMP_STATE_LOADED) {
return -XMP_ERROR_STATE;
}
smix->xxi = calloc(sizeof (struct xmp_instrument), smp);
if (smix->xxi == NULL) {
goto err;
}
smix->xxs = calloc(sizeof (struct xmp_sample), smp);
if (smix->xxs == NULL) {
goto err1;
}
smix->chn = chn;
smix->ins = smix->smp = smp;
return 0;
err1:
free(smix->xxi);
smix->xxi = NULL;
err:
return -XMP_ERROR_INTERNAL;
}
int xmp_smix_play_instrument(xmp_context opaque, int ins, int note, int vol, int chn)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct smix_data *smix = &ctx->smix;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
struct xmp_event *event;
if (ctx->state < XMP_STATE_PLAYING) {
return -XMP_ERROR_STATE;
}
if (chn >= smix->chn || ins >= mod->ins) {
return -XMP_ERROR_INVALID;
}
if (note == 0) {
note = 60; /* middle C note number */
}
event = &p->inject_event[mod->chn + chn];
memset(event, 0, sizeof (struct xmp_event));
event->note = note + 1;
event->ins = ins + 1;
event->vol = vol + 1;
event->_flag = 1;
return 0;
}
int xmp_smix_play_sample(xmp_context opaque, int ins, int note, int vol, int chn)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct smix_data *smix = &ctx->smix;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
struct xmp_event *event;
if (ctx->state < XMP_STATE_PLAYING) {
return -XMP_ERROR_STATE;
}
if (chn >= smix->chn || ins >= smix->ins) {
return -XMP_ERROR_INVALID;
}
if (note == 0) {
note = 60; /* middle C note number */
}
event = &p->inject_event[mod->chn + chn];
memset(event, 0, sizeof (struct xmp_event));
event->note = note + 1;
event->ins = mod->ins + ins + 1;
event->vol = vol + 1;
event->_flag = 1;
return 0;
}
int xmp_smix_channel_pan(xmp_context opaque, int chn, int pan)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct smix_data *smix = &ctx->smix;
struct module_data *m = &ctx->m;
struct channel_data *xc;
if (chn >= smix->chn || pan < 0 || pan > 255) {
return -XMP_ERROR_INVALID;
}
xc = &p->xc_data[m->mod.chn + chn];
xc->pan.val = pan;
return 0;
}
int xmp_smix_load_sample(xmp_context opaque, int num, const char *path)
{
struct context_data *ctx = (struct context_data *)opaque;
struct smix_data *smix = &ctx->smix;
struct module_data *m = &ctx->m;
struct xmp_instrument *xxi;
struct xmp_sample *xxs;
HIO_HANDLE *h;
uint32 magic;
int chn, rate, bits, size;
int retval = -XMP_ERROR_INTERNAL;
if (num >= smix->ins) {
retval = -XMP_ERROR_INVALID;
goto err;
}
xxi = &smix->xxi[num];
xxs = &smix->xxs[num];
h = hio_open(path, "rb");
if (h == NULL) {
retval = -XMP_ERROR_SYSTEM;
goto err;
}
/* Init instrument */
xxi->sub = calloc(sizeof(struct xmp_subinstrument), 1);
if (xxi->sub == NULL) {
retval = -XMP_ERROR_SYSTEM;
goto err1;
}
xxi->vol = m->volbase;
xxi->nsm = 1;
xxi->sub[0].sid = num;
xxi->sub[0].vol = xxi->vol;
xxi->sub[0].pan = 0x80;
/* Load sample */
magic = hio_read32b(h);
if (magic != 0x52494646) { /* RIFF */
retval = -XMP_ERROR_FORMAT;
goto err2;
}
if (hio_seek(h, 22, SEEK_SET) < 0) {
retval = -XMP_ERROR_SYSTEM;
goto err2;
}
chn = hio_read16l(h);
if (chn != 1) {
retval = -XMP_ERROR_FORMAT;
goto err2;
}
rate = hio_read32l(h);
if (rate == 0) {
retval = -XMP_ERROR_FORMAT;
goto err2;
}
if (hio_seek(h, 34, SEEK_SET) < 0) {
retval = -XMP_ERROR_SYSTEM;
goto err2;
}
bits = hio_read16l(h);
if (bits == 0) {
retval = -XMP_ERROR_FORMAT;
goto err2;
}
if (hio_seek(h, 40, SEEK_SET) < 0) {
retval = -XMP_ERROR_SYSTEM;
goto err2;
}
size = hio_read32l(h);
if (size == 0) {
retval = -XMP_ERROR_FORMAT;
goto err2;
}
libxmp_c2spd_to_note(rate, &xxi->sub[0].xpo, &xxi->sub[0].fin);
xxs->len = 8 * size / bits;
xxs->lps = 0;
xxs->lpe = 0;
xxs->flg = bits == 16 ? XMP_SAMPLE_16BIT : 0;
xxs->data = malloc(size + 8);
if (xxs->data == NULL) {
retval = -XMP_ERROR_SYSTEM;
goto err2;
}
/* ugly hack to make the interpolator happy */
memset(xxs->data, 0, 4);
memset(xxs->data + 4 + size, 0, 4);
xxs->data += 4;
if (hio_seek(h, 44, SEEK_SET) < 0) {
retval = -XMP_ERROR_SYSTEM;
goto err2;
}
if (hio_read(xxs->data, 1, size, h) != size) {
retval = -XMP_ERROR_SYSTEM;
goto err2;
}
hio_close(h);
return 0;
err2:
free(xxi->sub);
xxi->sub = NULL;
err1:
hio_close(h);
err:
return retval;
}
int xmp_smix_release_sample(xmp_context opaque, int num)
{
struct context_data *ctx = (struct context_data *)opaque;
struct smix_data *smix = &ctx->smix;
if (num >= smix->ins) {
return -XMP_ERROR_INVALID;
}
libxmp_free_sample(&smix->xxs[num]);
free(smix->xxi[num].sub);
smix->xxs[num].data = NULL;
smix->xxi[num].sub = NULL;
return 0;
}
void xmp_end_smix(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
struct smix_data *smix = &ctx->smix;
int i;
for (i = 0; i < smix->smp; i++) {
xmp_smix_release_sample(opaque, i);
}
free(smix->xxs);
free(smix->xxi);
smix->xxs = NULL;
smix->xxi = NULL;
}

View file

@ -0,0 +1,7 @@
#ifndef XMP_PLATFORM_H
#define XMP_PLATFORM_H
FILE *make_temp_file(char **);
void unlink_temp_file(char *);
#endif

View file

@ -0,0 +1,609 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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 <limits.h>
#include "common.h"
#include "virtual.h"
#include "mixer.h"
#ifdef LIBXMP_PAULA_SIMULATOR
#include "paula.h"
#endif
#define FREE -1
/* For virt_pastnote() */
void libxmp_player_set_release(struct context_data *, int);
void libxmp_player_set_fadeout(struct context_data *, int);
/* Get parent channel */
int libxmp_virt_getroot(struct context_data *ctx, int chn)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi;
int voc;
voc = p->virt.virt_channel[chn].map;
if (voc < 0) {
return -1;
}
vi = &p->virt.voice_array[voc];
return vi->root;
}
void libxmp_virt_resetvoice(struct context_data *ctx, int voc, int mute)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi = &p->virt.voice_array[voc];
#ifdef LIBXMP_PAULA_SIMULATOR
struct paula_state *paula;
#endif
if ((uint32)voc >= p->virt.maxvoc) {
return;
}
if (mute) {
libxmp_mixer_setvol(ctx, voc, 0);
}
p->virt.virt_used--;
p->virt.virt_channel[vi->root].count--;
p->virt.virt_channel[vi->chn].map = FREE;
#ifdef LIBXMP_PAULA_SIMULATOR
paula = vi->paula;
#endif
memset(vi, 0, sizeof(struct mixer_voice));
#ifdef LIBXMP_PAULA_SIMULATOR
vi->paula = paula;
#endif
vi->chn = vi->root = FREE;
}
/* virt_on (number of tracks) */
int libxmp_virt_on(struct context_data *ctx, int num)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
int i;
p->virt.num_tracks = num;
num = libxmp_mixer_numvoices(ctx, -1);
p->virt.virt_channels = p->virt.num_tracks;
if (HAS_QUIRK(QUIRK_VIRTUAL)) {
p->virt.virt_channels += num;
} else if (num > p->virt.virt_channels) {
num = p->virt.virt_channels;
}
p->virt.maxvoc = libxmp_mixer_numvoices(ctx, num);
p->virt.voice_array = calloc(p->virt.maxvoc,
sizeof(struct mixer_voice));
if (p->virt.voice_array == NULL)
goto err;
for (i = 0; i < p->virt.maxvoc; i++) {
p->virt.voice_array[i].chn = FREE;
p->virt.voice_array[i].root = FREE;
}
#ifdef LIBXMP_PAULA_SIMULATOR
/* Initialize Paula simulator */
if (IS_AMIGA_MOD()) {
for (i = 0; i < p->virt.maxvoc; i++) {
p->virt.voice_array[i].paula = calloc(1, sizeof (struct paula_state));
if (p->virt.voice_array[i].paula == NULL) {
goto err2;
}
libxmp_paula_init(ctx, p->virt.voice_array[i].paula);
}
}
#endif
p->virt.virt_channel = malloc(p->virt.virt_channels *
sizeof(struct virt_channel));
if (p->virt.virt_channel == NULL)
goto err2;
for (i = 0; i < p->virt.virt_channels; i++) {
p->virt.virt_channel[i].map = FREE;
p->virt.virt_channel[i].count = 0;
}
p->virt.virt_used = 0;
return 0;
err2:
#ifdef LIBXMP_PAULA_SIMULATOR
if (IS_AMIGA_MOD()) {
for (i = 0; i < p->virt.maxvoc; i++) {
free(p->virt.voice_array[i].paula);
}
}
#endif
free(p->virt.voice_array);
p->virt.voice_array = NULL;
err:
return -1;
}
void libxmp_virt_off(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
#ifdef LIBXMP_PAULA_SIMULATOR
struct module_data *m = &ctx->m;
int i;
#endif
#ifdef LIBXMP_PAULA_SIMULATOR
/* Free Paula simulator state */
if (IS_AMIGA_MOD()) {
for (i = 0; i < p->virt.maxvoc; i++) {
free(p->virt.voice_array[i].paula);
}
}
#endif
p->virt.virt_used = p->virt.maxvoc = 0;
p->virt.virt_channels = 0;
p->virt.num_tracks = 0;
free(p->virt.voice_array);
free(p->virt.virt_channel);
p->virt.voice_array = NULL;
p->virt.virt_channel = NULL;
}
void libxmp_virt_reset(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
int i;
if (p->virt.virt_channels < 1) {
return;
}
/* CID 129203 (#1 of 1): Useless call (USELESS_CALL)
* Call is only useful for its return value, which is ignored.
*
* libxmp_mixer_numvoices(ctx, p->virt.maxvoc);
*/
for (i = 0; i < p->virt.maxvoc; i++) {
struct mixer_voice *vi = &p->virt.voice_array[i];
#ifdef LIBXMP_PAULA_SIMULATOR
struct paula_state *paula = vi->paula;
#endif
memset(vi, 0, sizeof(struct mixer_voice));
#ifdef LIBXMP_PAULA_SIMULATOR
vi->paula = paula;
#endif
vi->chn = FREE;
vi->root = FREE;
}
for (i = 0; i < p->virt.virt_channels; i++) {
p->virt.virt_channel[i].map = FREE;
p->virt.virt_channel[i].count = 0;
}
p->virt.virt_used = 0;
}
static int free_voice(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
int i, num, vol;
/* Find background voice with lowest volume*/
num = FREE;
vol = INT_MAX;
for (i = 0; i < p->virt.maxvoc; i++) {
struct mixer_voice *vi = &p->virt.voice_array[i];
if (vi->chn >= p->virt.num_tracks && vi->vol < vol) {
num = i;
vol = vi->vol;
}
}
/* Free voice */
if (num >= 0) {
p->virt.virt_channel[p->virt.voice_array[num].chn].map = FREE;
p->virt.virt_channel[p->virt.voice_array[num].root].count--;
p->virt.virt_used--;
}
return num;
}
static int alloc_voice(struct context_data *ctx, int chn)
{
struct player_data *p = &ctx->p;
int i;
/* Find free voice */
for (i = 0; i < p->virt.maxvoc; i++) {
if (p->virt.voice_array[i].chn == FREE)
break;
}
/* not found */
if (i == p->virt.maxvoc) {
i = free_voice(ctx);
}
if (i >= 0) {
p->virt.virt_channel[chn].count++;
p->virt.virt_used++;
p->virt.voice_array[i].chn = chn;
p->virt.voice_array[i].root = chn;
p->virt.virt_channel[chn].map = i;
}
return i;
}
static int map_virt_channel(struct player_data *p, int chn)
{
int voc;
if ((uint32)chn >= p->virt.virt_channels)
return -1;
voc = p->virt.virt_channel[chn].map;
if ((uint32)voc >= p->virt.maxvoc)
return -1;
return voc;
}
int libxmp_virt_mapchannel(struct context_data *ctx, int chn)
{
return map_virt_channel(&ctx->p, chn);
}
void libxmp_virt_resetchannel(struct context_data *ctx, int chn)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi;
#ifdef LIBXMP_PAULA_SIMULATOR
struct paula_state *paula;
#endif
int voc;
if ((voc = map_virt_channel(p, chn)) < 0)
return;
libxmp_mixer_setvol(ctx, voc, 0);
p->virt.virt_used--;
p->virt.virt_channel[p->virt.voice_array[voc].root].count--;
p->virt.virt_channel[chn].map = FREE;
vi = &p->virt.voice_array[voc];
#ifdef LIBXMP_PAULA_SIMULATOR
paula = vi->paula;
#endif
memset(vi, 0, sizeof(struct mixer_voice));
#ifdef LIBXMP_PAULA_SIMULATOR
vi->paula = paula;
#endif
vi->chn = vi->root = FREE;
}
void libxmp_virt_setvol(struct context_data *ctx, int chn, int vol)
{
struct player_data *p = &ctx->p;
int voc, root;
if ((voc = map_virt_channel(p, chn)) < 0) {
return;
}
root = p->virt.voice_array[voc].root;
if (root < XMP_MAX_CHANNELS && p->channel_mute[root]) {
vol = 0;
}
libxmp_mixer_setvol(ctx, voc, vol);
if (vol == 0 && chn >= p->virt.num_tracks) {
libxmp_virt_resetvoice(ctx, voc, 1);
}
}
void libxmp_virt_release(struct context_data *ctx, int chn, int rel)
{
struct player_data *p = &ctx->p;
int voc;
if ((voc = map_virt_channel(p, chn)) < 0) {
return;
}
libxmp_mixer_release(ctx, voc, rel);
}
void libxmp_virt_setpan(struct context_data *ctx, int chn, int pan)
{
struct player_data *p = &ctx->p;
int voc;
if ((voc = map_virt_channel(p, chn)) < 0) {
return;
}
libxmp_mixer_setpan(ctx, voc, pan);
}
void libxmp_virt_seteffect(struct context_data *ctx, int chn, int type, int val)
{
struct player_data *p = &ctx->p;
int voc;
if ((voc = map_virt_channel(p, chn)) < 0) {
return;
}
libxmp_mixer_seteffect(ctx, voc, type, val);
}
double libxmp_virt_getvoicepos(struct context_data *ctx, int chn)
{
struct player_data *p = &ctx->p;
int voc;
if ((voc = map_virt_channel(p, chn)) < 0) {
return -1;
}
return libxmp_mixer_getvoicepos(ctx, voc);
}
#ifndef LIBXMP_CORE_PLAYER
void libxmp_virt_setsmp(struct context_data *ctx, int chn, int smp)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi;
double pos;
int voc;
if ((voc = map_virt_channel(p, chn)) < 0) {
return;
}
vi = &p->virt.voice_array[voc];
if (vi->smp == smp) {
return;
}
pos = libxmp_mixer_getvoicepos(ctx, voc);
libxmp_mixer_setpatch(ctx, voc, smp, 0);
libxmp_mixer_voicepos(ctx, voc, pos, 0); /* Restore old position */
}
#endif
#ifndef LIBXMP_CORE_DISABLE_IT
void libxmp_virt_setnna(struct context_data *ctx, int chn, int nna)
{
struct player_data *p = &ctx->p;
int voc;
if ((voc = map_virt_channel(p, chn)) < 0) {
return;
}
p->virt.voice_array[voc].act = nna;
}
static void check_dct(struct context_data *ctx, int i, int chn, int ins,
int smp, int note, int nna, int dct, int dca)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi = &p->virt.voice_array[i];
int voc;
voc = p->virt.virt_channel[chn].map;
if (vi->root == chn && vi->ins == ins) {
if (nna == XMP_INST_NNA_CUT) {
libxmp_virt_resetvoice(ctx, i, 1);
return;
}
vi->act = nna;
if ((dct == XMP_INST_DCT_INST) ||
(dct == XMP_INST_DCT_SMP && vi->smp == smp) ||
(dct == XMP_INST_DCT_NOTE && vi->note == note)) {
if (nna == XMP_INST_NNA_OFF && dca == XMP_INST_DCA_FADE) {
vi->act = VIRT_ACTION_OFF;
} else if (dca) {
if (i != voc || vi->act) {
vi->act = dca;
}
} else {
libxmp_virt_resetvoice(ctx, i, 1);
}
}
}
}
#endif
/* For note slides */
void libxmp_virt_setnote(struct context_data *ctx, int chn, int note)
{
struct player_data *p = &ctx->p;
int voc;
if ((voc = map_virt_channel(p, chn)) < 0) {
return;
}
libxmp_mixer_setnote(ctx, voc, note);
}
int libxmp_virt_setpatch(struct context_data *ctx, int chn, int ins, int smp,
int note, int nna, int dct, int dca)
{
struct player_data *p = &ctx->p;
int voc, vfree;
if ((uint32)chn >= p->virt.virt_channels) {
return -1;
}
if (ins < 0) {
smp = -1;
}
#ifndef LIBXMP_CORE_DISABLE_IT
if (dct) {
int i;
for (i = 0; i < p->virt.maxvoc; i++) {
check_dct(ctx, i, chn, ins, smp, note, nna, dct, dca);
}
}
#endif
voc = p->virt.virt_channel[chn].map;
if (voc > FREE) {
if (p->virt.voice_array[voc].act) {
vfree = alloc_voice(ctx, chn);
if (vfree < 0) {
return -1;
}
for (chn = p->virt.num_tracks;
p->virt.virt_channel[chn++].map > FREE;) ;
p->virt.voice_array[voc].chn = --chn;
p->virt.virt_channel[chn].map = voc;
voc = vfree;
}
} else {
voc = alloc_voice(ctx, chn);
if (voc < 0) {
return -1;
}
}
if (smp < 0) {
libxmp_virt_resetvoice(ctx, voc, 1);
return chn; /* was -1 */
}
libxmp_mixer_setpatch(ctx, voc, smp, 1);
libxmp_mixer_setnote(ctx, voc, note);
p->virt.voice_array[voc].ins = ins;
p->virt.voice_array[voc].act = nna;
return chn;
}
void libxmp_virt_setperiod(struct context_data *ctx, int chn, double period)
{
struct player_data *p = &ctx->p;
int voc;
if ((voc = map_virt_channel(p, chn)) < 0) {
return;
}
libxmp_mixer_setperiod(ctx, voc, period);
}
void libxmp_virt_voicepos(struct context_data *ctx, int chn, double pos)
{
struct player_data *p = &ctx->p;
int voc;
if ((voc = map_virt_channel(p, chn)) < 0) {
return;
}
libxmp_mixer_voicepos(ctx, voc, pos, 1);
}
#ifndef LIBXMP_CORE_DISABLE_IT
void libxmp_virt_pastnote(struct context_data *ctx, int chn, int act)
{
struct player_data *p = &ctx->p;
int c, voc;
for (c = p->virt.num_tracks; c < p->virt.virt_channels; c++) {
if ((voc = map_virt_channel(p, c)) < 0)
continue;
if (p->virt.voice_array[voc].root == chn) {
switch (act) {
case VIRT_ACTION_CUT:
libxmp_virt_resetvoice(ctx, voc, 1);
break;
case VIRT_ACTION_OFF:
libxmp_player_set_release(ctx, c);
break;
case VIRT_ACTION_FADE:
libxmp_player_set_fadeout(ctx, c);
break;
}
}
}
}
#endif
int libxmp_virt_cstat(struct context_data *ctx, int chn)
{
struct player_data *p = &ctx->p;
int voc;
if ((voc = map_virt_channel(p, chn)) < 0) {
return VIRT_INVALID;
}
if (chn < p->virt.num_tracks) {
return VIRT_ACTIVE;
}
return p->virt.voice_array[voc].act;
}

View file

@ -0,0 +1,38 @@
#ifndef LIBXMP_VIRTUAL_H
#define LIBXMP_VIRTUAL_H
#include "common.h"
#define VIRT_ACTION_CUT XMP_INST_NNA_CUT
#define VIRT_ACTION_CONT XMP_INST_NNA_CONT
#define VIRT_ACTION_OFF XMP_INST_NNA_OFF
#define VIRT_ACTION_FADE XMP_INST_NNA_FADE
#define VIRT_ACTIVE 0x100
#define VIRT_INVALID -1
int libxmp_virt_on (struct context_data *, int);
void libxmp_virt_off (struct context_data *);
int libxmp_virt_mute (struct context_data *, int, int);
int libxmp_virt_setpatch (struct context_data *, int, int, int, int,
int, int, int);
int libxmp_virt_cvt8bit (void);
void libxmp_virt_setnote (struct context_data *, int, int);
void libxmp_virt_setsmp (struct context_data *, int, int);
void libxmp_virt_setnna (struct context_data *, int, int);
void libxmp_virt_pastnote (struct context_data *, int, int);
void libxmp_virt_setvol (struct context_data *, int, int);
void libxmp_virt_voicepos (struct context_data *, int, double);
double libxmp_virt_getvoicepos (struct context_data *, int);
void libxmp_virt_setperiod (struct context_data *, int, double);
void libxmp_virt_setpan (struct context_data *, int, int);
void libxmp_virt_seteffect (struct context_data *, int, int, int);
int libxmp_virt_cstat (struct context_data *, int);
int libxmp_virt_mapchannel (struct context_data *, int);
void libxmp_virt_resetchannel(struct context_data *, int);
void libxmp_virt_resetvoice (struct context_data *, int, int);
void libxmp_virt_reset (struct context_data *);
void libxmp_virt_release (struct context_data *, int, int);
int libxmp_virt_getroot (struct context_data *, int);
#endif /* LIBXMP_VIRTUAL_H */

View file

@ -0,0 +1,33 @@
/* _[v]snprintf() from msvcrt.dll might not nul terminate */
/* OpenWatcom-provided versions seem to behave the same... */
#include "common.h"
#if defined(USE_LIBXMP_SNPRINTF)
#undef snprintf
#undef vsnprintf
int libxmp_vsnprintf(char *str, size_t sz, const char *fmt, va_list ap)
{
int rc = _vsnprintf(str, sz, fmt, ap);
if (sz != 0) {
if (rc < 0) rc = (int)sz;
if ((size_t)rc >= sz) str[sz - 1] = '\0';
}
return rc;
}
int libxmp_snprintf (char *str, size_t sz, const char *fmt, ...)
{
va_list ap;
int rc;
va_start (ap, fmt);
rc = _vsnprintf(str, sz, fmt, ap);
va_end (ap);
return rc;
}
#endif

View file

@ -0,0 +1,101 @@
#ifndef LIBXMP_LOADERS_XM_H
#define LIBXMP_LOADERS_XM_H
#define XM_EVENT_PACKING 0x80
#define XM_EVENT_PACK_MASK 0x7f
#define XM_EVENT_NOTE_FOLLOWS 0x01
#define XM_EVENT_INSTRUMENT_FOLLOWS 0x02
#define XM_EVENT_VOLUME_FOLLOWS 0x04
#define XM_EVENT_FXTYPE_FOLLOWS 0x08
#define XM_EVENT_FXPARM_FOLLOWS 0x10
#define XM_LINEAR_FREQ 0x01
#define XM_LOOP_MASK 0x03
#define XM_LOOP_NONE 0
#define XM_LOOP_FORWARD 1
#define XM_LOOP_PINGPONG 2
#define XM_SAMPLE_16BIT 0x10
#define XM_ENVELOPE_ON 0x01
#define XM_ENVELOPE_SUSTAIN 0x02
#define XM_ENVELOPE_LOOP 0x04
#define XM_LINEAR_PERIOD_MODE 0x01
struct xm_file_header {
uint8 id[17]; /* ID text: "Extended module: " */
uint8 name[20]; /* Module name, padded with zeroes */
uint8 doseof; /* 0x1a */
uint8 tracker[20]; /* Tracker name */
uint16 version; /* Version number, minor-major */
uint32 headersz; /* Header size */
uint16 songlen; /* Song length (in patten order table) */
uint16 restart; /* Restart position */
uint16 channels; /* Number of channels (2,4,6,8,10,...,32) */
uint16 patterns; /* Number of patterns (max 256) */
uint16 instruments; /* Number of instruments (max 128) */
uint16 flags; /* bit 0: 0=Amiga freq table, 1=Linear */
uint16 tempo; /* Default tempo */
uint16 bpm; /* Default BPM */
uint8 order[256]; /* Pattern order table */
};
struct xm_pattern_header {
uint32 length; /* Pattern header length */
uint8 packing; /* Packing type (always 0) */
uint16 rows; /* Number of rows in pattern (1..256) */
uint16 datasize; /* Packed patterndata size */
};
struct xm_instrument_header {
uint32 size; /* Instrument size */
uint8 name[22]; /* Instrument name */
uint8 type; /* Instrument type (always 0) */
uint16 samples; /* Number of samples in instrument */
uint32 sh_size; /* Sample header size */
};
struct xm_instrument {
uint8 sample[96]; /* Sample number for all notes */
uint16 v_env[24]; /* Points for volume envelope */
uint16 p_env[24]; /* Points for panning envelope */
uint8 v_pts; /* Number of volume points */
uint8 p_pts; /* Number of panning points */
uint8 v_sus; /* Volume sustain point */
uint8 v_start; /* Volume loop start point */
uint8 v_end; /* Volume loop end point */
uint8 p_sus; /* Panning sustain point */
uint8 p_start; /* Panning loop start point */
uint8 p_end; /* Panning loop end point */
uint8 v_type; /* Bit 0: On; 1: Sustain; 2: Loop */
uint8 p_type; /* Bit 0: On; 1: Sustain; 2: Loop */
uint8 y_wave; /* Vibrato waveform */
uint8 y_sweep; /* Vibrato sweep */
uint8 y_depth; /* Vibrato depth */
uint8 y_rate; /* Vibrato rate */
uint16 v_fade; /* Volume fadeout */
#if 0
uint8 reserved[22]; /* Reserved; 2 bytes in specs, 22 in 1.04 */
#endif
};
struct xm_sample_header {
uint32 length; /* Sample length */
uint32 loop_start; /* Sample loop start */
uint32 loop_length; /* Sample loop length */
uint8 volume; /* Volume */
int8 finetune; /* Finetune (signed byte -128..+127) */
uint8 type; /* 0=No loop,1=Fwd loop,2=Ping-pong,16-bit */
uint8 pan; /* Panning (0-255) */
int8 relnote; /* Relative note number (signed byte) */
uint8 reserved; /* Reserved */
uint8 name[22]; /* Sample_name */
};
struct xm_event {
uint8 note; /* Note (0-71, 0 = C-0) */
uint8 instrument; /* Instrument (0-128) */
uint8 volume; /* Volume column byte */
uint8 fx_type; /* Effect type */
uint8 fx_parm; /* Effect parameter */
};
#endif

View file

@ -0,0 +1,800 @@
/* Extended Module Player
* Copyright (C) 1996-2021 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.
*/
/*
* Fri, 26 Jun 1998 17:45:59 +1000 Andrew Leahy <alf@cit.nepean.uws.edu.au>
* Finally got it working on the DEC Alpha running DEC UNIX! In the pattern
* reading loop I found I was getting "0" for (p-patbuf) and "0" for
* xph.datasize, the next if statement (where it tries to read the patbuf)
* would then cause a seg_fault.
*
* Sun Sep 27 12:07:12 EST 1998 Claudio Matsuoka <claudio@pos.inf.ufpr.br>
* Extended Module 1.02 stores data in a different order, we must handle
* this accordingly. MAX_SAMP used as a workaround to check the number of
* samples recognized by the player.
*/
#include "loader.h"
#include "xm.h"
static int xm_test(HIO_HANDLE *, char *, const int);
static int xm_load(struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_xm = {
"Fast Tracker II",
xm_test,
xm_load
};
static int xm_test(HIO_HANDLE *f, char *t, const int start)
{
char buf[20];
if (hio_read(buf, 1, 17, f) < 17) /* ID text */
return -1;
if (memcmp(buf, "Extended Module: ", 17))
return -1;
libxmp_read_title(f, t, 20);
return 0;
}
static int load_xm_pattern(struct module_data *m, int num, int version, HIO_HANDLE *f)
{
const int headsize = version > 0x0102 ? 9 : 8;
struct xmp_module *mod = &m->mod;
struct xm_pattern_header xph;
struct xmp_event *event;
uint8 *patbuf, *pat, b;
int j, r;
int size;
xph.length = hio_read32l(f);
xph.packing = hio_read8(f);
xph.rows = version > 0x0102 ? hio_read16l(f) : hio_read8(f) + 1;
/* Sanity check */
if (xph.rows > 256) {
goto err;
}
xph.datasize = hio_read16l(f);
hio_seek(f, xph.length - headsize, SEEK_CUR);
if (hio_error(f)) {
goto err;
}
r = xph.rows;
if (r == 0) {
r = 0x100;
}
if (libxmp_alloc_pattern_tracks(mod, num, r) < 0) {
goto err;
}
if (xph.datasize == 0) {
return 0;
}
size = xph.datasize;
pat = patbuf = calloc(1, size);
if (patbuf == NULL) {
goto err;
}
hio_read(patbuf, 1, size, f);
for (j = 0; j < (mod->chn * r); j++) {
/*if ((pat - patbuf) >= xph.datasize)
break; */
event = &EVENT(num, j % mod->chn, j / mod->chn);
if (--size < 0) {
goto err2;
}
if ((b = *pat++) & XM_EVENT_PACKING) {
if (b & XM_EVENT_NOTE_FOLLOWS) {
if (--size < 0)
goto err2;
event->note = *pat++;
}
if (b & XM_EVENT_INSTRUMENT_FOLLOWS) {
if (--size < 0)
goto err2;
event->ins = *pat++;
}
if (b & XM_EVENT_VOLUME_FOLLOWS) {
if (--size < 0)
goto err2;
event->vol = *pat++;
}
if (b & XM_EVENT_FXTYPE_FOLLOWS) {
if (--size < 0)
goto err2;
event->fxt = *pat++;
}
if (b & XM_EVENT_FXPARM_FOLLOWS) {
if (--size < 0)
goto err2;
event->fxp = *pat++;
}
} else {
size -= 4;
if (size < 0)
goto err2;
event->note = b;
event->ins = *pat++;
event->vol = *pat++;
event->fxt = *pat++;
event->fxp = *pat++;
}
/* Sanity check */
switch (event->fxt) {
case 18:
case 19:
case 22:
case 23:
case 24:
case 26:
case 28:
case 30:
case 31:
case 32:
event->fxt = 0;
}
if (event->fxt > 34) {
event->fxt = 0;
}
if (event->note == 0x61) {
/* See OpenMPT keyoff+instr.xm test case */
if (event->fxt == 0x0e && MSN(event->fxp) == 0x0d) {
event->note = XMP_KEY_OFF;
} else {
event->note =
event->ins ? XMP_KEY_FADE : XMP_KEY_OFF;
}
} 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->vol) {
continue;
}
/* Volume set */
if ((event->vol >= 0x10) && (event->vol <= 0x50)) {
event->vol -= 0x0f;
continue;
}
/* Volume column effects */
switch (event->vol >> 4) {
case 0x06: /* Volume slide down */
event->f2t = FX_VOLSLIDE_2;
event->f2p = event->vol - 0x60;
break;
case 0x07: /* Volume slide up */
event->f2t = FX_VOLSLIDE_2;
event->f2p = (event->vol - 0x70) << 4;
break;
case 0x08: /* Fine volume slide down */
event->f2t = FX_EXTENDED;
event->f2p =
(EX_F_VSLIDE_DN << 4) | (event->vol - 0x80);
break;
case 0x09: /* Fine volume slide up */
event->f2t = FX_EXTENDED;
event->f2p =
(EX_F_VSLIDE_UP << 4) | (event->vol - 0x90);
break;
case 0x0a: /* Set vibrato speed */
event->f2t = FX_VIBRATO;
event->f2p = (event->vol - 0xa0) << 4;
break;
case 0x0b: /* Vibrato */
event->f2t = FX_VIBRATO;
event->f2p = event->vol - 0xb0;
break;
case 0x0c: /* Set panning */
event->f2t = FX_SETPAN;
event->f2p = (event->vol - 0xc0) << 4;
break;
case 0x0d: /* Pan slide left */
event->f2t = FX_PANSL_NOMEM;
event->f2p = (event->vol - 0xd0) << 4;
break;
case 0x0e: /* Pan slide right */
event->f2t = FX_PANSL_NOMEM;
event->f2p = event->vol - 0xe0;
break;
case 0x0f: /* Tone portamento */
event->f2t = FX_TONEPORTA;
event->f2p = (event->vol - 0xf0) << 4;
/* From OpenMPT TonePortamentoMemory.xm:
* "Another nice bug (...) is the combination of both
* portamento commands (Mx and 3xx) in the same cell:
* The 3xx parameter is ignored completely, and the Mx
* parameter is doubled. (M2 3FF is the same as M4 000)
*/
if (event->fxt == FX_TONEPORTA
|| event->fxt == FX_TONE_VSLIDE) {
if (event->fxt == FX_TONEPORTA) {
event->fxt = 0;
} else {
event->fxt = FX_VOLSLIDE;
}
event->fxp = 0;
if (event->f2p < 0x80) {
event->f2p <<= 1;
} else {
event->f2p = 0xff;
}
}
/* From OpenMPT porta-offset.xm:
* "If there is a portamento command next to an offset
* command, the offset command is ignored completely. In
* particular, the offset parameter is not memorized."
*/
if (event->fxt == FX_OFFSET
&& event->f2t == FX_TONEPORTA) {
event->fxt = event->fxp = 0;
}
break;
}
event->vol = 0;
}
free(patbuf);
return 0;
err2:
free(patbuf);
err:
return -1;
}
static int load_patterns(struct module_data *m, int version, HIO_HANDLE *f)
{
struct xmp_module *mod = &m->mod;
int i, j;
mod->pat++;
if (libxmp_init_pattern(mod) < 0) {
return -1;
}
D_(D_INFO "Stored patterns: %d", mod->pat - 1);
for (i = 0; i < mod->pat - 1; i++) {
if (load_xm_pattern(m, i, version, f) < 0) {
goto err;
}
}
/* Alloc one extra pattern */
{
int t = i * mod->chn;
if (libxmp_alloc_pattern(mod, i) < 0) {
goto err;
}
mod->xxp[i]->rows = 64;
if (libxmp_alloc_track(mod, t, 64) < 0) {
goto err;
}
for (j = 0; j < mod->chn; j++) {
mod->xxp[i]->index[j] = t;
}
}
return 0;
err:
return -1;
}
/* Packed structures size */
#define XM_INST_HEADER_SIZE 33
#define XM_INST_SIZE 208
/* grass.near.the.house.xm defines 23 samples in instrument 1. FT2 docs
* specify at most 16. See https://github.com/libxmp/libxmp/issues/168
* for more details. */
#define XM_MAX_SAMPLES_PER_INST 32
static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
{
struct xmp_module *mod = &m->mod;
struct xm_instrument_header xih;
struct xm_instrument xi;
struct xm_sample_header xsh[XM_MAX_SAMPLES_PER_INST];
int sample_num = 0;
long total_sample_size;
int i, j;
uint8 buf[208];
D_(D_INFO "Instruments: %d", mod->ins);
/* ESTIMATED value! We don't know the actual value at this point */
mod->smp = MAX_SAMPLES;
if (libxmp_init_instrument(m) < 0) {
return -1;
}
for (i = 0; i < mod->ins; i++) {
long instr_pos = hio_tell(f);
struct xmp_instrument *xxi = &mod->xxi[i];
/* Modules converted with MOD2XM 1.0 always say we have 31
* instruments, but file may end abruptly before that. Also covers
* XMLiTE stripped modules and truncated files. This test will not
* work if file has trailing garbage.
*/
if (hio_read(buf, 33, 1, f) != 1) {
D_(D_WARN "short read in instrument header data");
break;
}
xih.size = readmem32l(buf); /* Instrument size */
memcpy(xih.name, buf + 4, 22); /* Instrument name */
xih.type = buf[26]; /* Instrument type (always 0) */
xih.samples = readmem16l(buf + 27); /* Number of samples */
xih.sh_size = readmem32l(buf + 29); /* Sample header size */
/* Sanity check */
if (xih.samples > XM_MAX_SAMPLES_PER_INST || (xih.samples > 0 && xih.sh_size > 0x100)) {
D_(D_CRIT "instrument %d: samples:%d sample header size:%d", i + 1, xih.samples, xih.sh_size);
return -1;
}
libxmp_instrument_name(mod, i, xih.name, 22);
xxi->nsm = xih.samples;
D_(D_INFO "instrument:%2X (%s) samples:%2d", i, xxi->name, xxi->nsm);
if (xxi->nsm == 0) {
/* Sample size should be in struct xm_instrument according to
* the official format description, but FT2 actually puts it in
* struct xm_instrument header. There's a tracker or converter
* that follow the specs, so we must handle both cases (see
* "Braintomb" by Jazztiz/ART).
*/
/* Umm, Cyke O'Path <cyker@heatwave.co.uk> sent me a couple of
* mods ("Breath of the Wind" and "Broken Dimension") that
* reserve the instrument data space after the instrument header
* even if the number of instruments is set to 0. In these modules
* the instrument header size is marked as 263. The following
* generalization should take care of both cases.
*/
if (hio_seek(f, (int)xih.size - XM_INST_HEADER_SIZE, SEEK_CUR) < 0) {
return -1;
}
continue;
}
if (libxmp_alloc_subinstrument(mod, i, xxi->nsm) < 0) {
return -1;
}
if (xih.size < XM_INST_HEADER_SIZE) {
return -1;
}
/* for BoobieSqueezer (see http://boobie.rotfl.at/)
* It works pretty much the same way as Impulse Tracker's sample
* only mode, where it will strip off the instrument data.
*/
if (xih.size < XM_INST_HEADER_SIZE + XM_INST_SIZE) {
memset(&xi, 0, sizeof(struct xm_instrument));
hio_seek(f, xih.size - XM_INST_HEADER_SIZE, SEEK_CUR);
} else {
uint8 *b = buf;
if (hio_read(buf, 208, 1, f) != 1) {
D_(D_CRIT "short read in instrument data");
return -1;
}
memcpy(xi.sample, b, 96); /* Sample map */
b += 96;
for (j = 0; j < 24; j++) {
xi.v_env[j] = readmem16l(b); /* Points for volume envelope */
b += 2;
}
for (j = 0; j < 24; j++) {
xi.p_env[j] = readmem16l(b); /* Points for pan envelope */
b += 2;
}
xi.v_pts = *b++; /* Number of volume points */
xi.p_pts = *b++; /* Number of pan points */
xi.v_sus = *b++; /* Volume sustain point */
xi.v_start = *b++; /* Volume loop start point */
xi.v_end = *b++; /* Volume loop end point */
xi.p_sus = *b++; /* Pan sustain point */
xi.p_start = *b++; /* Pan loop start point */
xi.p_end = *b++; /* Pan loop end point */
xi.v_type = *b++; /* Bit 0:On 1:Sustain 2:Loop */
xi.p_type = *b++; /* Bit 0:On 1:Sustain 2:Loop */
xi.y_wave = *b++; /* Vibrato waveform */
xi.y_sweep = *b++; /* Vibrato sweep */
xi.y_depth = *b++; /* Vibrato depth */
xi.y_rate = *b++; /* Vibrato rate */
xi.v_fade = readmem16l(b); /* Volume fadeout */
/* Skip reserved space */
if (hio_seek(f, (int)xih.size - (XM_INST_HEADER_SIZE + XM_INST_SIZE), SEEK_CUR) < 0) {
return -1;
}
/* Envelope */
xxi->rls = xi.v_fade << 1;
xxi->aei.npt = xi.v_pts;
xxi->aei.sus = xi.v_sus;
xxi->aei.lps = xi.v_start;
xxi->aei.lpe = xi.v_end;
xxi->aei.flg = xi.v_type;
xxi->pei.npt = xi.p_pts;
xxi->pei.sus = xi.p_sus;
xxi->pei.lps = xi.p_start;
xxi->pei.lpe = xi.p_end;
xxi->pei.flg = xi.p_type;
if (xxi->aei.npt <= 0 || xxi->aei.npt > 12 /*XMP_MAX_ENV_POINTS */ ) {
xxi->aei.flg &= ~XMP_ENVELOPE_ON;
} else {
memcpy(xxi->aei.data, xi.v_env, xxi->aei.npt * 4);
}
if (xxi->pei.npt <= 0 || xxi->pei.npt > 12 /*XMP_MAX_ENV_POINTS */ ) {
xxi->pei.flg &= ~XMP_ENVELOPE_ON;
} else {
memcpy(xxi->pei.data, xi.p_env, xxi->pei.npt * 4);
}
for (j = 12; j < 108; j++) {
xxi->map[j].ins = xi.sample[j - 12];
if (xxi->map[j].ins >= xxi->nsm)
xxi->map[j].ins = -1;
}
}
/* Read subinstrument and sample parameters */
for (j = 0; j < xxi->nsm; j++, sample_num++) {
struct xmp_subinstrument *sub = &xxi->sub[j];
struct xmp_sample *xxs;
uint8 *b = buf;
D_(D_INFO " sample index:%d sample id:%d", j, sample_num);
if (sample_num >= mod->smp) {
if (libxmp_realloc_samples(m, mod->smp * 3 / 2) < 0)
return -1;
}
xxs = &mod->xxs[sample_num];
if (hio_read(buf, 40, 1, f) != 1) {
D_(D_CRIT "short read in sample data");
return -1;
}
xsh[j].length = readmem32l(b); /* Sample length */
b += 4;
/* Sanity check */
if (xsh[j].length > MAX_SAMPLE_SIZE) {
D_(D_CRIT "sanity check: %d: bad sample size", j);
return -1;
}
xsh[j].loop_start = readmem32l(b); /* Sample loop start */
b += 4;
xsh[j].loop_length = readmem32l(b); /* Sample loop length */
b += 4;
xsh[j].volume = *b++; /* Volume */
xsh[j].finetune = *b++; /* Finetune (-128..+127) */
xsh[j].type = *b++; /* Flags */
xsh[j].pan = *b++; /* Panning (0-255) */
xsh[j].relnote = *(int8 *) b++; /* Relative note number */
xsh[j].reserved = *b++;
memcpy(xsh[j].name, b, 22);
sub->vol = xsh[j].volume;
sub->pan = xsh[j].pan;
sub->xpo = xsh[j].relnote;
sub->fin = xsh[j].finetune;
sub->vwf = xi.y_wave;
sub->vde = xi.y_depth << 2;
sub->vra = xi.y_rate;
sub->vsw = xi.y_sweep;
sub->sid = sample_num;
libxmp_copy_adjust(xxs->name, xsh[j].name, 22);
xxs->len = xsh[j].length;
xxs->lps = xsh[j].loop_start;
xxs->lpe = xsh[j].loop_start + xsh[j].loop_length;
xxs->flg = 0;
if (xsh[j].type & XM_SAMPLE_16BIT) {
xxs->flg |= XMP_SAMPLE_16BIT;
xxs->len >>= 1;
xxs->lps >>= 1;
xxs->lpe >>= 1;
}
xxs->flg |= xsh[j].type & XM_LOOP_FORWARD ? XMP_SAMPLE_LOOP : 0;
xxs->flg |= xsh[j].type & XM_LOOP_PINGPONG ? XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_BIDIR : 0;
D_(D_INFO " size:%06x loop start:%06x loop end:%06x %c V%02x F%+04d P%02x R%+03d %s",
mod->xxs[sub->sid].len,
mod->xxs[sub->sid].lps,
mod->xxs[sub->sid].lpe,
mod->xxs[sub->sid].flg & XMP_SAMPLE_LOOP_BIDIR ? 'B' :
mod->xxs[sub->sid].flg & XMP_SAMPLE_LOOP ? 'L' : ' ',
sub->vol, sub->fin, sub->pan, sub->xpo,
mod->xxs[sub->sid].flg & XMP_SAMPLE_16BIT ? " (16 bit)" : "");
}
/* Read actual sample data */
total_sample_size = 0;
for (j = 0; j < xxi->nsm; j++) {
struct xmp_subinstrument *sub = &xxi->sub[j];
int flags;
flags = SAMPLE_FLAG_DIFF;
#ifndef LIBXMP_CORE_PLAYER
if (xsh[j].reserved == 0xad) {
flags = SAMPLE_FLAG_ADPCM;
}
#endif
if (version > 0x0103) {
D_(D_INFO " read sample: index:%d sample id:%d", j, sub->sid);
if (libxmp_load_sample(m, f, flags, &mod->xxs[sub->sid], NULL) < 0) {
return -1;
}
if (flags & SAMPLE_FLAG_ADPCM) {
D_(D_INFO " sample is adpcm");
total_sample_size += 16 + ((xsh[j].length + 1) >> 1);
} else {
total_sample_size += xsh[j].length;
}
}
}
/* Reposition correctly in case of 16-bit sample having odd in-file length.
* See "Lead Lined for '99", reported by Dennis Mulleneers.
*/
if (hio_seek(f, instr_pos + xih.size + 40 * xih.samples + total_sample_size, SEEK_SET) < 0) {
return -1;
}
}
/* Final sample number adjustment */
if (libxmp_realloc_samples(m, sample_num) < 0) {
return -1;
}
return 0;
}
static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start)
{
struct xmp_module *mod = &m->mod;
int i, j;
struct xm_file_header xfh;
char tracker_name[21];
int len;
uint8 buf[80];
LOAD_INIT();
if (hio_read(buf, 80, 1, f) != 1) {
D_(D_CRIT "error reading header");
return -1;
}
memcpy(xfh.id, buf, 17); /* ID text */
memcpy(xfh.name, buf + 17, 20); /* Module name */
/* */ /* skip 0x1a */
memcpy(xfh.tracker, buf + 38, 20); /* Tracker name */
xfh.version = readmem16l(buf + 58); /* Version number, minor-major */
xfh.headersz = readmem32l(buf + 60); /* Header size */
xfh.songlen = readmem16l(buf + 64); /* Song length */
xfh.restart = readmem16l(buf + 66); /* Restart position */
xfh.channels = readmem16l(buf + 68); /* Number of channels */
xfh.patterns = readmem16l(buf + 70); /* Number of patterns */
xfh.instruments = readmem16l(buf + 72); /* Number of instruments */
xfh.flags = readmem16l(buf + 74); /* 0=Amiga freq table, 1=Linear */
xfh.tempo = readmem16l(buf + 76); /* Default tempo */
xfh.bpm = readmem16l(buf + 78); /* Default BPM */
/* Sanity checks */
if (xfh.songlen > 256 || xfh.patterns > 256 || xfh.instruments > 255) {
D_(D_CRIT "Sanity check: %d %d %d", xfh.songlen, xfh.patterns, xfh.instruments);
return -1;
}
if (xfh.restart > 255 || xfh.channels > XMP_MAX_CHANNELS) {
D_(D_CRIT "Sanity check: %d %d", xfh.restart, xfh.channels);
return -1;
}
if (xfh.tempo >= 32 || xfh.bpm < 32 || xfh.bpm > 255) {
if (memcmp("MED2XM", xfh.tracker, 6)) {
D_(D_CRIT "Sanity check: %d %d", xfh.tempo, xfh.bpm);
return -1;
}
}
/* Honor header size -- needed by BoobieSqueezer XMs */
len = xfh.headersz - 0x14;
if (len < 0 || len > 256) {
D_(D_CRIT "Sanity check: %d", len);
return -1;
}
if (hio_read(xfh.order, len, 1, f) != 1) { /* Pattern order table */
D_(D_CRIT "error reading orders");
return -1;
}
strncpy(mod->name, (char *)xfh.name, 20);
mod->len = xfh.songlen;
mod->chn = xfh.channels;
mod->pat = xfh.patterns;
mod->ins = xfh.instruments;
mod->rst = xfh.restart;
mod->spd = xfh.tempo;
mod->bpm = xfh.bpm;
mod->trk = mod->chn * mod->pat + 1;
m->c4rate = C4_NTSC_RATE;
m->period_type = xfh.flags & XM_LINEAR_PERIOD_MODE ? PERIOD_LINEAR : PERIOD_AMIGA;
memcpy(mod->xxo, xfh.order, mod->len);
/*tracker_name[20] = 0;*/
snprintf(tracker_name, 21, "%-20.20s", xfh.tracker);
for (i = 20; i >= 0; i--) {
if (tracker_name[i] == 0x20)
tracker_name[i] = 0;
if (tracker_name[i])
break;
}
/* OpenMPT accurately emulates weird FT2 bugs */
if (!strncmp(tracker_name, "FastTracker v2.00", 17) ||
!strncmp(tracker_name, "OpenMPT ", 8)) {
m->quirk |= QUIRK_FT2BUGS;
}
#ifndef LIBXMP_CORE_PLAYER
if (xfh.headersz == 0x0113) {
strcpy(tracker_name, "unknown tracker");
m->quirk &= ~QUIRK_FT2BUGS;
} else if (*tracker_name == 0) {
strcpy(tracker_name, "Digitrakker"); /* best guess */
m->quirk &= ~QUIRK_FT2BUGS;
}
/* See MMD1 loader for explanation */
if (!strncmp(tracker_name, "MED2XM by J.Pynnone", 19)) {
if (mod->bpm <= 10) {
mod->bpm = 125 * (0x35 - mod->bpm * 2) / 33;
}
m->quirk &= ~QUIRK_FT2BUGS;
}
if (!strncmp(tracker_name, "FastTracker v 2.00", 18)) {
strcpy(tracker_name, "old ModPlug Tracker");
m->quirk &= ~QUIRK_FT2BUGS;
}
libxmp_set_type(m, "%s XM %d.%02d", tracker_name, xfh.version >> 8, xfh.version & 0xff);
#else
libxmp_set_type(m, tracker_name);
#endif
MODULE_INFO();
/* Honor header size */
if (hio_seek(f, start + xfh.headersz + 60, SEEK_SET) < 0) {
return -1;
}
/* XM 1.02/1.03 has a different patterns and instruments order */
if (xfh.version <= 0x0103) {
if (load_instruments(m, xfh.version, f) < 0) {
return -1;
}
if (load_patterns(m, xfh.version, f) < 0) {
return -1;
}
} else {
if (load_patterns(m, xfh.version, f) < 0) {
return -1;
}
if (load_instruments(m, xfh.version, f) < 0) {
return -1;
}
}
D_(D_INFO "Stored samples: %d", mod->smp);
/* XM 1.02 stores all samples after the patterns */
if (xfh.version <= 0x0103) {
for (i = 0; i < mod->ins; i++) {
for (j = 0; j < mod->xxi[i].nsm; j++) {
int sid = mod->xxi[i].sub[j].sid;
if (libxmp_load_sample(m, f, SAMPLE_FLAG_DIFF, &mod->xxs[sid], NULL) < 0) {
return -1;
}
}
}
}
for (i = 0; i < mod->chn; i++) {
mod->xxc[i].pan = 0x80;
}
m->quirk |= QUIRKS_FT2;
m->read_event_type = READ_EVENT_FT2;
return 0;
}

View file

@ -0,0 +1,389 @@
#ifndef XMP_H
#define XMP_H
#ifdef __cplusplus
extern "C" {
#endif
#define XMP_VERSION "4.5.0"
#define XMP_VERCODE 0x040500
#define XMP_VER_MAJOR 4
#define XMP_VER_MINOR 5
#define XMP_VER_RELEASE 0
#if defined(_WIN32) && !defined(__CYGWIN__)
# if defined(BUILDING_STATIC)
# define LIBXMP_EXPORT
# elif defined(BUILDING_DLL)
# define LIBXMP_EXPORT __declspec(dllexport)
# else
# define LIBXMP_EXPORT __declspec(dllimport)
# endif
#elif defined(__OS2__) && defined(__WATCOMC__) && defined(__SW_BD)
# define LIBXMP_EXPORT __declspec(dllexport)
#elif (defined(__GNUC__) || defined(__clang__) || defined(__HP_cc)) && defined(XMP_SYM_VISIBILITY)
# define LIBXMP_EXPORT __attribute__((visibility ("default")))
#elif defined(__SUNPRO_C) && defined(XMP_LDSCOPE_GLOBAL)
# define LIBXMP_EXPORT __global
#elif defined(EMSCRIPTEN)
# include <emscripten.h>
# define LIBXMP_EXPORT EMSCRIPTEN_KEEPALIVE
# define LIBXMP_EXPORT_VAR
#else
# define LIBXMP_EXPORT
#endif
#if !defined (LIBXMP_EXPORT_VAR)
# define LIBXMP_EXPORT_VAR LIBXMP_EXPORT
#endif
#define XMP_NAME_SIZE 64 /* Size of module name and type */
#define XMP_KEY_OFF 0x81 /* Note number for key off event */
#define XMP_KEY_CUT 0x82 /* Note number for key cut event */
#define XMP_KEY_FADE 0x83 /* Note number for fade event */
/* mixer parameter macros */
/* sample format flags */
#define XMP_FORMAT_8BIT (1 << 0) /* Mix to 8-bit instead of 16 */
#define XMP_FORMAT_UNSIGNED (1 << 1) /* Mix to unsigned samples */
#define XMP_FORMAT_MONO (1 << 2) /* Mix to mono instead of stereo */
/* player parameters */
#define XMP_PLAYER_AMP 0 /* Amplification factor */
#define XMP_PLAYER_MIX 1 /* Stereo mixing */
#define XMP_PLAYER_INTERP 2 /* Interpolation type */
#define XMP_PLAYER_DSP 3 /* DSP effect flags */
#define XMP_PLAYER_FLAGS 4 /* Player flags */
#define XMP_PLAYER_CFLAGS 5 /* Player flags for current module */
#define XMP_PLAYER_SMPCTL 6 /* Sample control flags */
#define XMP_PLAYER_VOLUME 7 /* Player module volume */
#define XMP_PLAYER_STATE 8 /* Internal player state (read only) */
#define XMP_PLAYER_SMIX_VOLUME 9 /* SMIX volume */
#define XMP_PLAYER_DEFPAN 10 /* Default pan setting */
#define XMP_PLAYER_MODE 11 /* Player personality */
#define XMP_PLAYER_MIXER_TYPE 12 /* Current mixer (read only) */
#define XMP_PLAYER_VOICES 13 /* Maximum number of mixer voices */
/* interpolation types */
#define XMP_INTERP_NEAREST 0 /* Nearest neighbor */
#define XMP_INTERP_LINEAR 1 /* Linear (default) */
#define XMP_INTERP_SPLINE 2 /* Cubic spline */
/* dsp effect types */
#define XMP_DSP_LOWPASS (1 << 0) /* Lowpass filter effect */
#define XMP_DSP_ALL (XMP_DSP_LOWPASS)
/* player state */
#define XMP_STATE_UNLOADED 0 /* Context created */
#define XMP_STATE_LOADED 1 /* Module loaded */
#define XMP_STATE_PLAYING 2 /* Module playing */
/* player flags */
#define XMP_FLAGS_VBLANK (1 << 0) /* Use vblank timing */
#define XMP_FLAGS_FX9BUG (1 << 1) /* Emulate FX9 bug */
#define XMP_FLAGS_FIXLOOP (1 << 2) /* Emulate sample loop bug */
#define XMP_FLAGS_A500 (1 << 3) /* Use Paula mixer in Amiga modules */
/* player modes */
#define XMP_MODE_AUTO 0 /* Autodetect mode (default) */
#define XMP_MODE_MOD 1 /* Play as a generic MOD player */
#define XMP_MODE_NOISETRACKER 2 /* Play using Noisetracker quirks */
#define XMP_MODE_PROTRACKER 3 /* Play using Protracker quirks */
#define XMP_MODE_S3M 4 /* Play as a generic S3M player */
#define XMP_MODE_ST3 5 /* Play using ST3 bug emulation */
#define XMP_MODE_ST3GUS 6 /* Play using ST3+GUS quirks */
#define XMP_MODE_XM 7 /* Play as a generic XM player */
#define XMP_MODE_FT2 8 /* Play using FT2 bug emulation */
#define XMP_MODE_IT 9 /* Play using IT quirks */
#define XMP_MODE_ITSMP 10 /* Play using IT sample mode quirks */
/* mixer types */
#define XMP_MIXER_STANDARD 0 /* Standard mixer */
#define XMP_MIXER_A500 1 /* Amiga 500 */
#define XMP_MIXER_A500F 2 /* Amiga 500 with led filter */
/* sample flags */
#define XMP_SMPCTL_SKIP (1 << 0) /* Don't load samples */
/* limits */
#define XMP_MAX_KEYS 121 /* Number of valid keys */
#define XMP_MAX_ENV_POINTS 32 /* Max number of envelope points */
#define XMP_MAX_MOD_LENGTH 256 /* Max number of patterns in module */
#define XMP_MAX_CHANNELS 64 /* Max number of channels in module */
#define XMP_MAX_SRATE 49170 /* max sampling rate (Hz) */
#define XMP_MIN_SRATE 4000 /* min sampling rate (Hz) */
#define XMP_MIN_BPM 20 /* min BPM */
/* frame rate = (50 * bpm / 125) Hz */
/* frame size = (sampling rate * channels * size) / frame rate */
#define XMP_MAX_FRAMESIZE (5 * XMP_MAX_SRATE * 2 / XMP_MIN_BPM)
/* error codes */
#define XMP_END 1
#define XMP_ERROR_INTERNAL 2 /* Internal error */
#define XMP_ERROR_FORMAT 3 /* Unsupported module format */
#define XMP_ERROR_LOAD 4 /* Error loading file */
#define XMP_ERROR_DEPACK 5 /* Error depacking file */
#define XMP_ERROR_SYSTEM 6 /* System error */
#define XMP_ERROR_INVALID 7 /* Invalid parameter */
#define XMP_ERROR_STATE 8 /* Invalid player state */
struct xmp_channel {
int pan; /* Channel pan (0x80 is center) */
int vol; /* Channel volume */
#define XMP_CHANNEL_SYNTH (1 << 0) /* Channel is synthesized */
#define XMP_CHANNEL_MUTE (1 << 1) /* Channel is muted */
#define XMP_CHANNEL_SPLIT (1 << 2) /* Split Amiga channel in bits 5-4 */
#define XMP_CHANNEL_SURROUND (1 << 4) /* Surround channel */
int flg; /* Channel flags */
};
struct xmp_pattern {
int rows; /* Number of rows */
int index[1]; /* Track index */
};
struct xmp_event {
unsigned char note; /* Note number (0 means no note) */
unsigned char ins; /* Patch number */
unsigned char vol; /* Volume (0 to basevol) */
unsigned char fxt; /* Effect type */
unsigned char fxp; /* Effect parameter */
unsigned char f2t; /* Secondary effect type */
unsigned char f2p; /* Secondary effect parameter */
unsigned char _flag; /* Internal (reserved) flags */
};
struct xmp_track {
int rows; /* Number of rows */
struct xmp_event event[1]; /* Event data */
};
struct xmp_envelope {
#define XMP_ENVELOPE_ON (1 << 0) /* Envelope is enabled */
#define XMP_ENVELOPE_SUS (1 << 1) /* Envelope has sustain point */
#define XMP_ENVELOPE_LOOP (1 << 2) /* Envelope has loop */
#define XMP_ENVELOPE_FLT (1 << 3) /* Envelope is used for filter */
#define XMP_ENVELOPE_SLOOP (1 << 4) /* Envelope has sustain loop */
#define XMP_ENVELOPE_CARRY (1 << 5) /* Don't reset envelope position */
int flg; /* Flags */
int npt; /* Number of envelope points */
int scl; /* Envelope scaling */
int sus; /* Sustain start point */
int sue; /* Sustain end point */
int lps; /* Loop start point */
int lpe; /* Loop end point */
short data[XMP_MAX_ENV_POINTS * 2];
};
struct xmp_subinstrument {
int vol; /* Default volume */
int gvl; /* Global volume */
int pan; /* Pan */
int xpo; /* Transpose */
int fin; /* Finetune */
int vwf; /* Vibrato waveform */
int vde; /* Vibrato depth */
int vra; /* Vibrato rate */
int vsw; /* Vibrato sweep */
int rvv; /* Random volume/pan variation (IT) */
int sid; /* Sample number */
#define XMP_INST_NNA_CUT 0x00
#define XMP_INST_NNA_CONT 0x01
#define XMP_INST_NNA_OFF 0x02
#define XMP_INST_NNA_FADE 0x03
int nna; /* New note action */
#define XMP_INST_DCT_OFF 0x00
#define XMP_INST_DCT_NOTE 0x01
#define XMP_INST_DCT_SMP 0x02
#define XMP_INST_DCT_INST 0x03
int dct; /* Duplicate check type */
#define XMP_INST_DCA_CUT XMP_INST_NNA_CUT
#define XMP_INST_DCA_OFF XMP_INST_NNA_OFF
#define XMP_INST_DCA_FADE XMP_INST_NNA_FADE
int dca; /* Duplicate check action */
int ifc; /* Initial filter cutoff */
int ifr; /* Initial filter resonance */
};
struct xmp_instrument {
char name[32]; /* Instrument name */
int vol; /* Instrument volume */
int nsm; /* Number of samples */
int rls; /* Release (fadeout) */
struct xmp_envelope aei; /* Amplitude envelope info */
struct xmp_envelope pei; /* Pan envelope info */
struct xmp_envelope fei; /* Frequency envelope info */
struct {
unsigned char ins; /* Instrument number for each key */
signed char xpo; /* Instrument transpose for each key */
} map[XMP_MAX_KEYS];
struct xmp_subinstrument *sub;
void *extra; /* Extra fields */
};
struct xmp_sample {
char name[32]; /* Sample name */
int len; /* Sample length */
int lps; /* Loop start */
int lpe; /* Loop end */
#define XMP_SAMPLE_16BIT (1 << 0) /* 16bit sample */
#define XMP_SAMPLE_LOOP (1 << 1) /* Sample is looped */
#define XMP_SAMPLE_LOOP_BIDIR (1 << 2) /* Bidirectional sample loop */
#define XMP_SAMPLE_LOOP_REVERSE (1 << 3) /* Backwards sample loop */
#define XMP_SAMPLE_LOOP_FULL (1 << 4) /* Play full sample before looping */
#define XMP_SAMPLE_SLOOP (1 << 5) /* Sample has sustain loop */
#define XMP_SAMPLE_SLOOP_BIDIR (1 << 6) /* Bidirectional sustain loop */
#define XMP_SAMPLE_SYNTH (1 << 15) /* Data contains synth patch */
int flg; /* Flags */
unsigned char *data; /* Sample data */
};
struct xmp_sequence {
int entry_point;
int duration;
};
struct xmp_module {
char name[XMP_NAME_SIZE]; /* Module title */
char type[XMP_NAME_SIZE]; /* Module format */
int pat; /* Number of patterns */
int trk; /* Number of tracks */
int chn; /* Tracks per pattern */
int ins; /* Number of instruments */
int smp; /* Number of samples */
int spd; /* Initial speed */
int bpm; /* Initial BPM */
int len; /* Module length in patterns */
int rst; /* Restart position */
int gvl; /* Global volume */
struct xmp_pattern **xxp; /* Patterns */
struct xmp_track **xxt; /* Tracks */
struct xmp_instrument *xxi; /* Instruments */
struct xmp_sample *xxs; /* Samples */
struct xmp_channel xxc[XMP_MAX_CHANNELS]; /* Channel info */
unsigned char xxo[XMP_MAX_MOD_LENGTH]; /* Orders */
};
struct xmp_test_info {
char name[XMP_NAME_SIZE]; /* Module title */
char type[XMP_NAME_SIZE]; /* Module format */
};
struct xmp_module_info {
unsigned char md5[16]; /* MD5 message digest */
int vol_base; /* Volume scale */
struct xmp_module *mod; /* Pointer to module data */
char *comment; /* Comment text, if any */
int num_sequences; /* Number of valid sequences */
struct xmp_sequence *seq_data; /* Pointer to sequence data */
};
struct xmp_channel_info {
unsigned int period; /* Sample period (* 4096) */
unsigned int position; /* Sample position */
short pitchbend; /* Linear bend from base note*/
unsigned char note; /* Current base note number */
unsigned char instrument; /* Current instrument number */
unsigned char sample; /* Current sample number */
unsigned char volume; /* Current volume */
unsigned char pan; /* Current stereo pan */
unsigned char reserved; /* Reserved */
struct xmp_event event; /* Current track event */
};
struct xmp_frame_info { /* Current frame information */
int pos; /* Current position */
int pattern; /* Current pattern */
int row; /* Current row in pattern */
int num_rows; /* Number of rows in current pattern */
int frame; /* Current frame */
int speed; /* Current replay speed */
int bpm; /* Current bpm */
int time; /* Current module time in ms */
int total_time; /* Estimated replay time in ms*/
int frame_time; /* Frame replay time in us */
void *buffer; /* Pointer to sound buffer */
int buffer_size; /* Used buffer size */
int total_size; /* Total buffer size */
int volume; /* Current master volume */
int loop_count; /* Loop counter */
int virt_channels; /* Number of virtual channels */
int virt_used; /* Used virtual channels */
int sequence; /* Current sequence */
struct xmp_channel_info channel_info[XMP_MAX_CHANNELS]; /* Current channel information */
};
struct xmp_callbacks {
unsigned long (*read_func)(void *dest, unsigned long len,
unsigned long nmemb, void *priv);
int (*seek_func)(void *priv, long offset, int whence);
long (*tell_func)(void *priv);
int (*close_func)(void *priv);
};
typedef char *xmp_context;
LIBXMP_EXPORT_VAR extern const char *xmp_version;
LIBXMP_EXPORT_VAR extern const unsigned int xmp_vercode;
LIBXMP_EXPORT int xmp_syserrno (void);
LIBXMP_EXPORT xmp_context xmp_create_context (void);
LIBXMP_EXPORT void xmp_free_context (xmp_context);
LIBXMP_EXPORT int xmp_load_module (xmp_context, const char *);
LIBXMP_EXPORT int xmp_load_module_from_memory (xmp_context, const void *, long);
LIBXMP_EXPORT int xmp_load_module_from_file (xmp_context, void *, long);
LIBXMP_EXPORT int xmp_load_module_from_callbacks (xmp_context, void *, struct xmp_callbacks);
LIBXMP_EXPORT int xmp_test_module (const char *, struct xmp_test_info *);
LIBXMP_EXPORT int xmp_test_module_from_memory (const void *, long, struct xmp_test_info *);
LIBXMP_EXPORT int xmp_test_module_from_file (void *, struct xmp_test_info *);
LIBXMP_EXPORT int xmp_test_module_from_callbacks (void *, struct xmp_callbacks, struct xmp_test_info *);
LIBXMP_EXPORT void xmp_scan_module (xmp_context);
LIBXMP_EXPORT void xmp_release_module (xmp_context);
LIBXMP_EXPORT int xmp_start_player (xmp_context, int, int);
LIBXMP_EXPORT int xmp_play_frame (xmp_context);
LIBXMP_EXPORT int xmp_play_buffer (xmp_context, void *, int, int);
LIBXMP_EXPORT void xmp_get_frame_info (xmp_context, struct xmp_frame_info *);
LIBXMP_EXPORT void xmp_end_player (xmp_context);
LIBXMP_EXPORT void xmp_inject_event (xmp_context, int, struct xmp_event *);
LIBXMP_EXPORT void xmp_get_module_info (xmp_context, struct xmp_module_info *);
LIBXMP_EXPORT const char *const *xmp_get_format_list (void);
LIBXMP_EXPORT int xmp_next_position (xmp_context);
LIBXMP_EXPORT int xmp_prev_position (xmp_context);
LIBXMP_EXPORT int xmp_set_position (xmp_context, int);
LIBXMP_EXPORT int xmp_set_row (xmp_context, int);
LIBXMP_EXPORT int xmp_set_tempo_factor(xmp_context, double);
LIBXMP_EXPORT void xmp_stop_module (xmp_context);
LIBXMP_EXPORT void xmp_restart_module (xmp_context);
LIBXMP_EXPORT int xmp_seek_time (xmp_context, int);
LIBXMP_EXPORT int xmp_channel_mute (xmp_context, int, int);
LIBXMP_EXPORT int xmp_channel_vol (xmp_context, int, int);
LIBXMP_EXPORT int xmp_set_player (xmp_context, int, int);
LIBXMP_EXPORT int xmp_get_player (xmp_context, int);
LIBXMP_EXPORT int xmp_set_instrument_path (xmp_context, const char *);
/* External sample mixer API */
LIBXMP_EXPORT int xmp_start_smix (xmp_context, int, int);
LIBXMP_EXPORT void xmp_end_smix (xmp_context);
LIBXMP_EXPORT int xmp_smix_play_instrument(xmp_context, int, int, int, int);
LIBXMP_EXPORT int xmp_smix_play_sample (xmp_context, int, int, int, int);
LIBXMP_EXPORT int xmp_smix_channel_pan (xmp_context, int, int);
LIBXMP_EXPORT int xmp_smix_load_sample (xmp_context, int, const char *);
LIBXMP_EXPORT int xmp_smix_release_sample (xmp_context, int);
#ifdef __cplusplus
}
#endif
#endif /* XMP_H */

View file

@ -0,0 +1,505 @@
//----------------------------------------------------------------------------------------------------
// ___ ___ __ _ _ ___ ___ _ _ _ ___ _
// / _ \| _ ) / /| | || _ \ __| /_\ _ _ __| (_)___ | __|_ _ __ _(_)_ _ ___
// | (_) | _ \/ _ \_ _| _/ _| / _ \ || / _` | / _ \ | _|| ' \/ _` | | ' \/ -_)
// \__\_\___/\___/ |_||_| |___| /_/ \_\_,_\__,_|_\___/ |___|_||_\__, |_|_||_\___|
// |___/
//
// QB64-PE Audio Engine powered by miniaudio (https://miniaud.io/)
//
// This implements a data source that decodes MOD, S3M, XM & IT files using libxmp-lite
// https://github.com/libxmp/libxmp/tree/master/lite (MIT)
//
// Copyright (c) 2022 Samuel Gomes
// https://github.com/a740g
//
//-----------------------------------------------------------------------------------------------------
#pragma once
//-----------------------------------------------------------------------------------------------------
// HEADER FILES
//-----------------------------------------------------------------------------------------------------
#include "../miniaudio.h"
#define BUILDING_STATIC 1
#include "libxmp-lite/xmp.h"
//-----------------------------------------------------------------------------------------------------
struct ma_modplay {
// This part is for miniaudio
ma_data_source_base ds; /* The decoder can be used independently as a data source. */
ma_read_proc onRead;
ma_seek_proc onSeek;
ma_tell_proc onTell;
void *pReadSeekTellUserData;
ma_format format;
// This part is format specific
xmp_context xmpContext; // The player context
xmp_frame_info xmpFrameInfo; // LibXMP frameinfo - used to detect loops
ma_uint32 loopCount; // We'll maintain our own loop counter and check this against LibXMP's to detect new loops
};
static ma_result ma_modplay_seek_to_pcm_frame(ma_modplay *pModplay, ma_uint64 frameIndex) {
if (pModplay == NULL) {
return MA_INVALID_ARGS;
}
if (xmp_seek_time(pModplay->xmpContext, (int)((frameIndex * 1000) / MA_DEFAULT_SAMPLE_RATE)) == -XMP_ERROR_STATE) {
return MA_INVALID_OPERATION;
}
return MA_SUCCESS;
}
static ma_result ma_modplay_get_data_format(ma_modplay *pModplay, ma_format *pFormat, ma_uint32 *pChannels, ma_uint32 *pSampleRate, ma_channel *pChannelMap,
size_t channelMapCap) {
/* Defaults for safety. */
if (pFormat != NULL) {
*pFormat = ma_format_unknown;
}
if (pChannels != NULL) {
*pChannels = 0;
}
if (pSampleRate != NULL) {
*pSampleRate = 0;
}
if (pChannelMap != NULL) {
MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
}
if (pModplay == NULL) {
return MA_INVALID_OPERATION;
}
if (pFormat != NULL) {
*pFormat = pModplay->format;
}
if (pChannels != NULL) {
*pChannels = 2; // Stereo
}
if (pSampleRate != NULL) {
*pSampleRate = MA_DEFAULT_SAMPLE_RATE;
}
if (pChannelMap != NULL) {
ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, 2);
}
return MA_SUCCESS;
}
static ma_result ma_modplay_read_pcm_frames(ma_modplay *pModplay, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead) {
if (pFramesRead != NULL) {
*pFramesRead = 0;
}
if (frameCount == 0) {
return MA_INVALID_ARGS;
}
if (pModplay == NULL) {
return MA_INVALID_ARGS;
}
ma_result result = MA_SUCCESS; // Must be initialized to MA_SUCCESS
// Render some 16-bit stereo sample frames
int xmpError = xmp_play_buffer(pModplay->xmpContext, pFramesOut, (int)(frameCount * sizeof(ma_int16) * 2), 0);
// Get the frame information to detect if we are looping
xmp_get_frame_info(pModplay->xmpContext, &pModplay->xmpFrameInfo);
// Check if we have reached the end or are looping
if (pModplay->xmpFrameInfo.loop_count != pModplay->loopCount || xmpError == -XMP_END || xmpError == -XMP_ERROR_STATE) {
pModplay->loopCount = pModplay->xmpFrameInfo.loop_count;
result = MA_AT_END;
}
if (pFramesRead != NULL) {
*pFramesRead = frameCount;
}
return result;
}
static ma_result ma_modplay_get_cursor_in_pcm_frames(ma_modplay *pModplay, ma_uint64 *pCursor) {
if (!pCursor) {
return MA_INVALID_ARGS;
}
*pCursor = 0; /* Safety. */
if (!pModplay) {
return MA_INVALID_ARGS;
}
// Get the frame information
xmp_get_frame_info(pModplay->xmpContext, &pModplay->xmpFrameInfo);
ma_int64 offset = ((ma_int64)pModplay->xmpFrameInfo.time * MA_DEFAULT_SAMPLE_RATE) / 1000;
if (offset < 0) {
return MA_INVALID_FILE;
}
*pCursor = (ma_uint64)offset;
return MA_SUCCESS;
}
static ma_result ma_modplay_get_length_in_pcm_frames(ma_modplay *pModplay, ma_uint64 *pLength) {
if (!pLength) {
return MA_INVALID_ARGS;
}
*pLength = 0; /* Safety. */
if (!pModplay) {
return MA_INVALID_ARGS;
}
ma_int64 length = ((ma_int64)pModplay->xmpFrameInfo.total_time * MA_DEFAULT_SAMPLE_RATE) / 1000;
if (length < 0) {
return MA_INVALID_FILE;
}
*pLength = (ma_uint64)length;
return MA_SUCCESS;
}
static ma_result ma_modplay_ds_read(ma_data_source *pDataSource, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead) {
return ma_modplay_read_pcm_frames((ma_modplay *)pDataSource, pFramesOut, frameCount, pFramesRead);
}
static ma_result ma_modplay_ds_seek(ma_data_source *pDataSource, ma_uint64 frameIndex) {
return ma_modplay_seek_to_pcm_frame((ma_modplay *)pDataSource, frameIndex);
}
static ma_result ma_modplay_ds_get_data_format(ma_data_source *pDataSource, ma_format *pFormat, ma_uint32 *pChannels, ma_uint32 *pSampleRate,
ma_channel *pChannelMap, size_t channelMapCap) {
return ma_modplay_get_data_format((ma_modplay *)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
}
static ma_result ma_modplay_ds_get_cursor(ma_data_source *pDataSource, ma_uint64 *pCursor) {
return ma_modplay_get_cursor_in_pcm_frames((ma_modplay *)pDataSource, pCursor);
}
static ma_result ma_modplay_ds_get_length(ma_data_source *pDataSource, ma_uint64 *pLength) {
return ma_modplay_get_length_in_pcm_frames((ma_modplay *)pDataSource, pLength);
}
static ma_data_source_vtable ma_data_source_vtable_modplay = {ma_modplay_ds_read, ma_modplay_ds_seek, ma_modplay_ds_get_data_format, ma_modplay_ds_get_cursor,
ma_modplay_ds_get_length};
static int ma_modplay_of_callback__read(void *pUserData, unsigned char *pBufferOut, int bytesToRead) {
ma_modplay *pModplay = (ma_modplay *)pUserData;
ma_result result;
size_t bytesRead;
result = pModplay->onRead(pModplay->pReadSeekTellUserData, (void *)pBufferOut, bytesToRead, &bytesRead);
if (result != MA_SUCCESS) {
return -1;
}
return (int)bytesRead;
}
static int ma_modplay_of_callback__seek(void *pUserData, ma_int64 offset, int whence) {
ma_modplay *pModplay = (ma_modplay *)pUserData;
ma_result result;
ma_seek_origin origin;
if (whence == SEEK_SET) {
origin = ma_seek_origin_start;
} else if (whence == SEEK_END) {
origin = ma_seek_origin_end;
} else {
origin = ma_seek_origin_current;
}
result = pModplay->onSeek(pModplay->pReadSeekTellUserData, offset, origin);
if (result != MA_SUCCESS) {
return -1;
}
return 0;
}
static ma_int64 ma_modplay_of_callback__tell(void *pUserData) {
ma_modplay *pModplay = (ma_modplay *)pUserData;
ma_result result;
ma_int64 cursor;
if (pModplay->onTell == NULL) {
return -1;
}
result = pModplay->onTell(pModplay->pReadSeekTellUserData, &cursor);
if (result != MA_SUCCESS) {
return -1;
}
return cursor;
}
static ma_result ma_modplay_init_internal(const ma_decoding_backend_config *pConfig, ma_modplay *pModplay) {
ma_result result;
ma_data_source_config dataSourceConfig;
if (pModplay == NULL) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pModplay);
pModplay->format = ma_format::ma_format_s16; // We'll render 16-bit signed samples by default
if (pConfig != NULL && pConfig->preferredFormat == ma_format::ma_format_s16) {
pModplay->format = pConfig->preferredFormat;
} else {
/* Getting here means something other than s16 was specified. Just leave this unset to use the default format. */
}
dataSourceConfig = ma_data_source_config_init();
dataSourceConfig.vtable = &ma_data_source_vtable_modplay;
result = ma_data_source_init(&dataSourceConfig, &pModplay->ds);
if (result != MA_SUCCESS) {
return result; /* Failed to initialize the base data source. */
}
return MA_SUCCESS;
}
static ma_result ma_modplay_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void *pReadSeekTellUserData,
const ma_decoding_backend_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_modplay *pModplay) {
ma_result result;
(void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libopus. */
result = ma_modplay_init_internal(pConfig, pModplay);
if (result != MA_SUCCESS) {
return result;
}
if (onRead == NULL || onSeek == NULL) {
return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
}
pModplay->onRead = onRead;
pModplay->onSeek = onSeek;
pModplay->onTell = onTell;
pModplay->pReadSeekTellUserData = pReadSeekTellUserData;
// Find the size of the file
if (ma_modplay_of_callback__seek(pModplay, 0, SEEK_END) != 0) {
return MA_BAD_SEEK;
}
// Calculate the length
ma_int64 file_size = ma_modplay_of_callback__tell(pModplay);
if (file_size < 1) {
return MA_INVALID_FILE;
}
// Seek to the beginning of the file
if (ma_modplay_of_callback__seek(pModplay, 0, SEEK_SET) != 0) {
return MA_BAD_SEEK;
}
// Allocate some memory for the tune
ma_uint8 *tune = new ma_uint8[file_size];
if (tune == nullptr) {
return MA_OUT_OF_MEMORY;
}
// Read the file
if (ma_modplay_of_callback__read(pModplay, tune, (int)file_size) < 1) {
delete[] tune;
return MA_IO_ERROR;
}
// Check if the file is a valid module music
xmp_test_info xmpTestInfo;
int xmpError = xmp_test_module_from_memory(tune, (long)file_size, &xmpTestInfo);
if (xmpError != 0) {
delete[] tune;
return MA_INVALID_FILE;
}
// Initialize the player
pModplay->xmpContext = xmp_create_context();
if (!pModplay->xmpContext) {
delete[] tune;
return MA_OUT_OF_MEMORY;
}
// Load the module file
xmpError = xmp_load_module_from_memory(pModplay->xmpContext, tune, (long)file_size);
if (xmpError != 0) {
xmp_free_context(pModplay->xmpContext);
pModplay->xmpContext = nullptr;
delete[] tune;
return MA_INVALID_FILE;
}
// Free the memory now that we don't need it anymore
delete[] tune;
// Initialize the player
xmpError = xmp_start_player(pModplay->xmpContext, MA_DEFAULT_SAMPLE_RATE, 0);
if (xmpError != 0) {
xmp_release_module(pModplay->xmpContext);
xmp_free_context(pModplay->xmpContext);
pModplay->xmpContext = nullptr;
return MA_INVALID_FILE;
}
// Set some player properties. These are not critical. So, we will not check the return values
// These makes the sound quality much better when devices have sample rates other than 44100
xmpError = xmp_set_player(pModplay->xmpContext, XMP_PLAYER_INTERP, XMP_INTERP_SPLINE);
xmpError = xmp_set_player(pModplay->xmpContext, XMP_PLAYER_DSP, XMP_DSP_ALL);
xmp_get_frame_info(pModplay->xmpContext, &pModplay->xmpFrameInfo); // Get the frame information
pModplay->loopCount = pModplay->xmpFrameInfo.loop_count; // Save the loop counter
return MA_SUCCESS;
}
static ma_result ma_modplay_init_file(const char *pFilePath, const ma_decoding_backend_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks,
ma_modplay *pModplay) {
ma_result result;
(void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libopus. */
result = ma_modplay_init_internal(pConfig, pModplay);
if (result != MA_SUCCESS) {
return result;
}
// Check the file extension
if (!ma_path_extension_equal(pFilePath, "it") && !ma_path_extension_equal(pFilePath, "xm") && !ma_path_extension_equal(pFilePath, "s3m") &&
!ma_path_extension_equal(pFilePath, "mod")) {
return MA_INVALID_FILE;
}
// Check if the file is a valid module music
xmp_test_info xmpTestInfo;
int xmpError = xmp_test_module(pFilePath, &xmpTestInfo);
if (xmpError != 0) {
return MA_INVALID_FILE;
}
// Initialize the player
pModplay->xmpContext = xmp_create_context();
if (!pModplay->xmpContext) {
return MA_OUT_OF_MEMORY;
}
// Load the module file
xmpError = xmp_load_module(pModplay->xmpContext, pFilePath);
if (xmpError != 0) {
xmp_free_context(pModplay->xmpContext);
pModplay->xmpContext = nullptr;
return MA_INVALID_FILE;
}
// Initialize the player
xmpError = xmp_start_player(pModplay->xmpContext, MA_DEFAULT_SAMPLE_RATE, 0);
if (xmpError != 0) {
xmp_release_module(pModplay->xmpContext);
xmp_free_context(pModplay->xmpContext);
pModplay->xmpContext = nullptr;
return MA_INVALID_FILE;
}
// Set some player properties. These are not critical. So, we will not check the return values
// These makes the sound quality much better when devices have sample rates other than 44100
xmpError = xmp_set_player(pModplay->xmpContext, XMP_PLAYER_INTERP, XMP_INTERP_SPLINE);
xmpError = xmp_set_player(pModplay->xmpContext, XMP_PLAYER_DSP, XMP_DSP_ALL);
xmp_get_frame_info(pModplay->xmpContext, &pModplay->xmpFrameInfo); // Get the frame information
pModplay->loopCount = pModplay->xmpFrameInfo.loop_count; // Save the loop counter
return MA_SUCCESS;
}
static void ma_modplay_uninit(ma_modplay *pModplay, const ma_allocation_callbacks *pAllocationCallbacks) {
if (pModplay == NULL) {
return;
}
(void)pAllocationCallbacks;
xmp_end_player(pModplay->xmpContext);
xmp_release_module(pModplay->xmpContext);
xmp_free_context(pModplay->xmpContext);
pModplay->xmpContext = nullptr;
ma_data_source_uninit(&pModplay->ds);
}
static ma_result ma_decoding_backend_init__modplay(void *pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void *pReadSeekTellUserData,
const ma_decoding_backend_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks,
ma_data_source **ppBackend) {
ma_result result;
ma_modplay *pModplay;
(void)pUserData;
pModplay = (ma_modplay *)ma_malloc(sizeof(ma_modplay), pAllocationCallbacks);
if (pModplay == NULL) {
return MA_OUT_OF_MEMORY;
}
result = ma_modplay_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pModplay);
if (result != MA_SUCCESS) {
ma_free(pModplay, pAllocationCallbacks);
return result;
}
*ppBackend = pModplay;
return MA_SUCCESS;
}
static ma_result ma_decoding_backend_init_file__modplay(void *pUserData, const char *pFilePath, const ma_decoding_backend_config *pConfig,
const ma_allocation_callbacks *pAllocationCallbacks, ma_data_source **ppBackend) {
ma_result result;
ma_modplay *pModplay;
(void)pUserData;
pModplay = (ma_modplay *)ma_malloc(sizeof(ma_modplay), pAllocationCallbacks);
if (pModplay == NULL) {
return MA_OUT_OF_MEMORY;
}
result = ma_modplay_init_file(pFilePath, pConfig, pAllocationCallbacks, pModplay);
if (result != MA_SUCCESS) {
ma_free(pModplay, pAllocationCallbacks);
return result;
}
*ppBackend = pModplay;
return MA_SUCCESS;
}
static void ma_decoding_backend_uninit__modplay(void *pUserData, ma_data_source *pBackend, const ma_allocation_callbacks *pAllocationCallbacks) {
ma_modplay *pModplay = (ma_modplay *)pBackend;
(void)pUserData;
ma_modplay_uninit(pModplay, pAllocationCallbacks);
ma_free(pModplay, pAllocationCallbacks);
}
static ma_decoding_backend_vtable ma_decoding_backend_vtable_modplay = {ma_decoding_backend_init__modplay, ma_decoding_backend_init_file__modplay,
NULL, /* onInitFileW() */
NULL, /* onInitMemory() */
ma_decoding_backend_uninit__modplay};
//-----------------------------------------------------------------------------------------------------

View file

@ -0,0 +1,609 @@
//----------------------------------------------------------------------------------------------------
// ___ ___ __ _ _ ___ ___ _ _ _ ___ _
// / _ \| _ ) / /| | || _ \ __| /_\ _ _ __| (_)___ | __|_ _ __ _(_)_ _ ___
// | (_) | _ \/ _ \_ _| _/ _| / _ \ || / _` | / _ \ | _|| ' \/ _` | | ' \/ -_)
// \__\_\___/\___/ |_||_| |___| /_/ \_\_,_\__,_|_\___/ |___|_||_\__, |_|_||_\___|
// |___/
//
// QB64-PE Audio Engine powered by miniaudio (https://miniaud.io/)
//
// This implements a data source that decodes Reality Adlib Tracker 2 tunes
// https://realityproductions.itch.io/rad (Public Domain)
//
// From: Willy Reeve <shayde0@gmail.com>
// Sent: Sunday, 7 August, 2022 01:23 PM
// To: Samuel Gomes <v_2samg@hotmail.com>
// Cc: Carl Pettitt <carl@clonestudios.co.uk>
// Subject: Re: Contact from Reality website
//
// Hi Samuel,
//
// The player source code is Public Domain.
//
// Shayde
//
// On Fri, Aug 5, 2022 at 6:33 AM Reality website <rogue@3eality.com> wrote:
// Contact from the Reality website:
// Name: Samuel Gomes
// Email: v_2samg@hotmail.com
// Message:
//
// RADv2 - great stuff! I am planning to integrate the included player code in my projects. Can you please
// let me know the license type for the player code? If there is one, putting it somewhere on the site or
// zip would be fantastic!
//
// Thanks!
//
// Copyright (c) 2022 Samuel Gomes
// https://github.com/a740g
//
//-----------------------------------------------------------------------------------------------------
#pragma once
//-----------------------------------------------------------------------------------------------------
// HEADER FILES
//-----------------------------------------------------------------------------------------------------
#include "../miniaudio.h"
#include "radv2/opal.cpp"
#define RAD_DETECT_REPEATS 1
#include "radv2/player20.cpp"
#include "radv2/validate20.cpp"
//-----------------------------------------------------------------------------------------------------
struct ma_radv2 {
// This part is for miniaudio
ma_data_source_base ds; /* The decoder can be used independently as a data source. */
ma_read_proc onRead;
ma_seek_proc onSeek;
ma_tell_proc onTell;
void *pReadSeekTellUserData;
ma_format format;
// This part is format specific
RADPlayer *player; // RADv2 player object
Opal *adlib; // Opal Adlib emulator object
ma_uint8 *tune; // The song data (RADv2 needs this to be alive for rendering samples!)
uint32_t totalTime; // Total time in seconds
int sampleCount; // The number of samples generated in each update
int sampleUpdate; // Size of each update in samples after which the player must be updated
};
static ma_result ma_radv2_seek_to_pcm_frame(ma_radv2 *pRadv2, ma_uint64 frameIndex) {
if (!pRadv2) {
return MA_INVALID_ARGS;
}
// We can only reset the player to the beginning
if (frameIndex == 0) {
pRadv2->player->Stop();
return MA_SUCCESS;
}
return MA_INVALID_OPERATION; // Anything else is not seekable
}
static ma_result ma_radv2_get_data_format(ma_radv2 *pRadv2, ma_format *pFormat, ma_uint32 *pChannels, ma_uint32 *pSampleRate, ma_channel *pChannelMap,
size_t channelMapCap) {
/* Defaults for safety. */
if (pFormat != NULL) {
*pFormat = ma_format_unknown;
}
if (pChannels != NULL) {
*pChannels = 0;
}
if (pSampleRate != NULL) {
*pSampleRate = 0;
}
if (pChannelMap != NULL) {
MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
}
if (!pRadv2) {
return MA_INVALID_OPERATION;
}
if (pFormat != NULL) {
*pFormat = pRadv2->format;
}
if (pChannels != NULL) {
*pChannels = 2; // Stereo
}
if (pSampleRate != NULL) {
*pSampleRate = MA_DEFAULT_SAMPLE_RATE;
}
if (pChannelMap != NULL) {
ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, 2);
}
return MA_SUCCESS;
}
static ma_result ma_radv2_read_pcm_frames(ma_radv2 *pRadv2, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead) {
if (pFramesRead != NULL) {
*pFramesRead = 0;
}
if (frameCount == 0) {
return MA_INVALID_ARGS;
}
if (!pRadv2) {
return MA_INVALID_ARGS;
}
ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
ma_uint64 totalFramesRead = 0;
ma_int16 *buffer = (ma_int16 *)pFramesOut;
bool repeat = false;
while (totalFramesRead < frameCount) {
pRadv2->adlib->Sample(&buffer[0], &buffer[1]); // Get the left and right sample
buffer += 2; // Increment the buffer pointer twice for 2 channels
++totalFramesRead; // Increment the frame counter
// Time to update player?
pRadv2->sampleCount++;
if (pRadv2->sampleCount >= pRadv2->sampleUpdate) {
pRadv2->sampleCount = 0;
repeat = pRadv2->player->Update();
}
if (repeat) {
result = MA_AT_END;
break;
}
}
if (pFramesRead != NULL) {
*pFramesRead = totalFramesRead;
}
return result;
}
static ma_result ma_radv2_get_cursor_in_pcm_frames(ma_radv2 *pRadv2, ma_uint64 *pCursor) {
if (!pCursor) {
return MA_INVALID_ARGS;
}
*pCursor = 0; /* Safety. */
if (!pRadv2) {
return MA_INVALID_ARGS;
}
ma_int64 offset = (ma_int64)pRadv2->player->GetPlayTimeInSeconds() * MA_DEFAULT_SAMPLE_RATE;
if (offset < 0) {
return MA_INVALID_FILE;
}
*pCursor = (ma_uint64)offset;
return MA_SUCCESS;
}
static ma_result ma_radv2_get_length_in_pcm_frames(ma_radv2 *pRadv2, ma_uint64 *pLength) {
if (!pLength) {
return MA_INVALID_ARGS;
}
*pLength = 0; /* Safety. */
if (!pRadv2) {
return MA_INVALID_ARGS;
}
// Total time in seconds * Opal sample rate
ma_int64 length = (ma_int64)pRadv2->totalTime * MA_DEFAULT_SAMPLE_RATE;
if (length < 0) {
return MA_INVALID_FILE;
}
*pLength = (ma_uint64)length;
return MA_SUCCESS;
}
static ma_result ma_radv2_ds_read(ma_data_source *pDataSource, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead) {
return ma_radv2_read_pcm_frames((ma_radv2 *)pDataSource, pFramesOut, frameCount, pFramesRead);
}
static ma_result ma_radv2_ds_seek(ma_data_source *pDataSource, ma_uint64 frameIndex) { return ma_radv2_seek_to_pcm_frame((ma_radv2 *)pDataSource, frameIndex); }
static ma_result ma_radv2_ds_get_data_format(ma_data_source *pDataSource, ma_format *pFormat, ma_uint32 *pChannels, ma_uint32 *pSampleRate,
ma_channel *pChannelMap, size_t channelMapCap) {
return ma_radv2_get_data_format((ma_radv2 *)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
}
static ma_result ma_radv2_ds_get_cursor(ma_data_source *pDataSource, ma_uint64 *pCursor) {
return ma_radv2_get_cursor_in_pcm_frames((ma_radv2 *)pDataSource, pCursor);
}
static ma_result ma_radv2_ds_get_length(ma_data_source *pDataSource, ma_uint64 *pLength) {
return ma_radv2_get_length_in_pcm_frames((ma_radv2 *)pDataSource, pLength);
}
static ma_data_source_vtable ma_data_source_vtable_radv2 = {ma_radv2_ds_read, ma_radv2_ds_seek, ma_radv2_ds_get_data_format, ma_radv2_ds_get_cursor,
ma_radv2_ds_get_length};
static int ma_radv2_of_callback__read(void *pUserData, unsigned char *pBufferOut, int bytesToRead) {
ma_radv2 *pRadv2 = (ma_radv2 *)pUserData;
ma_result result;
size_t bytesRead;
result = pRadv2->onRead(pRadv2->pReadSeekTellUserData, (void *)pBufferOut, bytesToRead, &bytesRead);
if (result != MA_SUCCESS) {
return -1;
}
return (int)bytesRead;
}
static int ma_radv2_of_callback__seek(void *pUserData, ma_int64 offset, int whence) {
ma_radv2 *pRadv2 = (ma_radv2 *)pUserData;
ma_result result;
ma_seek_origin origin;
if (whence == SEEK_SET) {
origin = ma_seek_origin_start;
} else if (whence == SEEK_END) {
origin = ma_seek_origin_end;
} else {
origin = ma_seek_origin_current;
}
result = pRadv2->onSeek(pRadv2->pReadSeekTellUserData, offset, origin);
if (result != MA_SUCCESS) {
return -1;
}
return 0;
}
static ma_int64 ma_radv2_of_callback__tell(void *pUserData) {
ma_radv2 *pRadv2 = (ma_radv2 *)pUserData;
ma_result result;
ma_int64 cursor;
if (pRadv2->onTell == NULL) {
return -1;
}
result = pRadv2->onTell(pRadv2->pReadSeekTellUserData, &cursor);
if (result != MA_SUCCESS) {
return -1;
}
return cursor;
}
static ma_result ma_radv2_init_internal(const ma_decoding_backend_config *pConfig, ma_radv2 *pRadv2) {
ma_result result;
ma_data_source_config dataSourceConfig;
if (!pRadv2) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pRadv2);
pRadv2->format = ma_format::ma_format_s16; // RADv2 Opal outputs 16-bit signed samples by default
if (pConfig != NULL && pConfig->preferredFormat == ma_format::ma_format_s16) {
pRadv2->format = pConfig->preferredFormat;
} else {
/* Getting here means something other than s16 was specified. Just leave this unset to use the default format. */
}
dataSourceConfig = ma_data_source_config_init();
dataSourceConfig.vtable = &ma_data_source_vtable_radv2;
result = ma_data_source_init(&dataSourceConfig, &pRadv2->ds);
if (result != MA_SUCCESS) {
return result; /* Failed to initialize the base data source. */
}
return MA_SUCCESS;
}
static ma_result ma_radv2_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void *pReadSeekTellUserData,
const ma_decoding_backend_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_radv2 *pRadv2) {
ma_result result;
(void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libopus. */
result = ma_radv2_init_internal(pConfig, pRadv2);
if (result != MA_SUCCESS) {
return result;
}
if (onRead == NULL || onSeek == NULL) {
return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
}
pRadv2->onRead = onRead;
pRadv2->onSeek = onSeek;
pRadv2->onTell = onTell;
pRadv2->pReadSeekTellUserData = pReadSeekTellUserData;
// Find the size of the file
if (ma_radv2_of_callback__seek(pRadv2, 0, SEEK_END) != 0) {
return MA_BAD_SEEK;
}
// Calculate the length
ma_int64 file_size = ma_radv2_of_callback__tell(pRadv2);
if (file_size < 1) {
return MA_INVALID_FILE;
}
// See to the beginning of the file
if (ma_radv2_of_callback__seek(pRadv2, 0, SEEK_SET) != 0) {
return MA_BAD_SEEK;
}
// Allocate some memory for the tune
pRadv2->tune = new uint8_t[file_size];
if (!pRadv2->tune) {
return MA_OUT_OF_MEMORY;
}
// Read the file
if (ma_radv2_of_callback__read(pRadv2, pRadv2->tune, (int)file_size) < 1) {
delete[] pRadv2->tune;
pRadv2->tune = nullptr;
return MA_IO_ERROR;
}
// Create the RADv2 Player objects
pRadv2->player = new RADPlayer();
if (!pRadv2->player) {
delete[] pRadv2->tune;
pRadv2->tune = nullptr;
return MA_OUT_OF_MEMORY;
}
// Create the RADv2 Opal object
pRadv2->adlib = new Opal(MA_DEFAULT_SAMPLE_RATE);
if (!pRadv2->adlib) {
delete pRadv2->player;
pRadv2->player = nullptr;
delete[] pRadv2->tune;
pRadv2->tune = nullptr;
return MA_OUT_OF_MEMORY;
}
// Check if the file is valid
if (RADValidate(pRadv2->tune, file_size)) {
delete pRadv2->adlib;
pRadv2->adlib = nullptr;
delete pRadv2->player;
pRadv2->player = nullptr;
delete[] pRadv2->tune;
pRadv2->tune = nullptr;
return MA_INVALID_FILE;
}
// Initialize the player
// We'll use a lambda here and pass the pRadv2 pointer using 'arg'
pRadv2->player->Init(
pRadv2->tune, [](void *arg, uint16_t reg, uint8_t data) { ((ma_radv2 *)arg)->adlib->Port(reg, data); }, pRadv2);
// Get the playback rate
if (pRadv2->player->GetHertz() < 0) {
delete pRadv2->adlib;
pRadv2->adlib = nullptr;
delete pRadv2->player;
pRadv2->player = nullptr;
delete[] pRadv2->tune;
pRadv2->tune = nullptr;
return MA_INVALID_FILE;
}
// Get the total playback time
pRadv2->totalTime = pRadv2->player->ComputeTotalTime();
// Setup some stuff
pRadv2->sampleCount = 0;
pRadv2->sampleUpdate = MA_DEFAULT_SAMPLE_RATE / pRadv2->player->GetHertz();
return MA_SUCCESS;
}
static ma_result ma_radv2_init_file(const char *pFilePath, const ma_decoding_backend_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks,
ma_radv2 *pRadv2) {
ma_result result;
(void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libopus. */
result = ma_radv2_init_internal(pConfig, pRadv2);
if (result != MA_SUCCESS) {
return result;
}
// Check the file extension
if (!ma_path_extension_equal(pFilePath, "rad")) {
return MA_INVALID_FILE;
}
// Open the file for reading
FILE *fd = fopen(pFilePath, "rb");
if (!fd) {
return MA_INVALID_FILE;
}
// Find the size of the file
if (fseek(fd, 0, SEEK_END) != 0) {
fclose(fd);
return MA_BAD_SEEK;
}
// Calculate the length
ma_int64 file_size = ftell(fd);
if (file_size < 1) {
fclose(fd);
return MA_INVALID_FILE;
}
// Seek to the beginning of the file
if (fseek(fd, 0, SEEK_SET) != 0) {
fclose(fd);
return MA_BAD_SEEK;
}
// Allocate some memory for the tune
pRadv2->tune = new uint8_t[file_size];
if (!pRadv2->tune) {
fclose(fd);
return MA_OUT_OF_MEMORY;
}
// Read the file
if (fread(pRadv2->tune, file_size, sizeof(uint8_t), fd) < 1) {
delete[] pRadv2->tune;
pRadv2->tune = nullptr;
fclose(fd);
return MA_IO_ERROR;
}
// Close the file now that we've read it into memory
fclose(fd);
// Create the RADv2 Player objects
pRadv2->player = new RADPlayer();
if (!pRadv2->player) {
delete[] pRadv2->tune;
pRadv2->tune = nullptr;
return MA_OUT_OF_MEMORY;
}
// Create the RADv2 Opal object
pRadv2->adlib = new Opal(MA_DEFAULT_SAMPLE_RATE);
if (!pRadv2->adlib) {
delete pRadv2->player;
pRadv2->player = nullptr;
delete[] pRadv2->tune;
pRadv2->tune = nullptr;
return MA_OUT_OF_MEMORY;
}
// Check if the file is valid
if (RADValidate(pRadv2->tune, file_size)) {
delete pRadv2->adlib;
pRadv2->adlib = nullptr;
delete pRadv2->player;
pRadv2->player = nullptr;
delete[] pRadv2->tune;
pRadv2->tune = nullptr;
return MA_INVALID_FILE;
}
// Initialize the player
// We'll use a lambda here and pass the pRadv2 pointer using 'arg'
pRadv2->player->Init(
pRadv2->tune, [](void *arg, uint16_t reg, uint8_t data) { ((ma_radv2 *)arg)->adlib->Port(reg, data); }, pRadv2);
// Get the playback rate
if (pRadv2->player->GetHertz() < 0) {
delete pRadv2->adlib;
pRadv2->adlib = nullptr;
delete pRadv2->player;
pRadv2->player = nullptr;
delete[] pRadv2->tune;
pRadv2->tune = nullptr;
return MA_INVALID_FILE;
}
// Get the total playback time
pRadv2->totalTime = pRadv2->player->ComputeTotalTime();
// Setup some stuff
pRadv2->sampleCount = 0;
pRadv2->sampleUpdate = MA_DEFAULT_SAMPLE_RATE / pRadv2->player->GetHertz();
return MA_SUCCESS;
}
static void ma_radv2_uninit(ma_radv2 *pRadv2, const ma_allocation_callbacks *pAllocationCallbacks) {
if (!pRadv2) {
return;
}
(void)pAllocationCallbacks;
// Stop any ongoing playback
pRadv2->player->Stop();
delete pRadv2->adlib;
pRadv2->adlib = nullptr;
delete pRadv2->player;
pRadv2->player = nullptr;
delete[] pRadv2->tune;
pRadv2->tune = nullptr;
ma_data_source_uninit(&pRadv2->ds);
}
static ma_result ma_decoding_backend_init__radv2(void *pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void *pReadSeekTellUserData,
const ma_decoding_backend_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks,
ma_data_source **ppBackend) {
ma_result result;
ma_radv2 *pRadv2;
(void)pUserData;
pRadv2 = (ma_radv2 *)ma_malloc(sizeof(ma_radv2), pAllocationCallbacks);
if (!pRadv2) {
return MA_OUT_OF_MEMORY;
}
result = ma_radv2_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pRadv2);
if (result != MA_SUCCESS) {
ma_free(pRadv2, pAllocationCallbacks);
return result;
}
*ppBackend = pRadv2;
return MA_SUCCESS;
}
static ma_result ma_decoding_backend_init_file__radv2(void *pUserData, const char *pFilePath, const ma_decoding_backend_config *pConfig,
const ma_allocation_callbacks *pAllocationCallbacks, ma_data_source **ppBackend) {
ma_result result;
ma_radv2 *pRadv2;
(void)pUserData;
pRadv2 = (ma_radv2 *)ma_malloc(sizeof(ma_radv2), pAllocationCallbacks);
if (!pRadv2) {
return MA_OUT_OF_MEMORY;
}
result = ma_radv2_init_file(pFilePath, pConfig, pAllocationCallbacks, pRadv2);
if (result != MA_SUCCESS) {
ma_free(pRadv2, pAllocationCallbacks);
return result;
}
*ppBackend = pRadv2;
return MA_SUCCESS;
}
static void ma_decoding_backend_uninit__radv2(void *pUserData, ma_data_source *pBackend, const ma_allocation_callbacks *pAllocationCallbacks) {
ma_radv2 *pRadv2 = (ma_radv2 *)pBackend;
(void)pUserData;
ma_radv2_uninit(pRadv2, pAllocationCallbacks);
ma_free(pRadv2, pAllocationCallbacks);
}
static ma_decoding_backend_vtable ma_decoding_backend_vtable_radv2 = {ma_decoding_backend_init__radv2, ma_decoding_backend_init_file__radv2,
NULL, /* onInitFileW() */
NULL, /* onInitMemory() */
ma_decoding_backend_uninit__radv2};
//-----------------------------------------------------------------------------------------------------

View file

@ -0,0 +1,543 @@
//----------------------------------------------------------------------------------------------------
// ___ ___ __ _ _ ___ ___ _ _ _ ___ _
// / _ \| _ ) / /| | || _ \ __| /_\ _ _ __| (_)___ | __|_ _ __ _(_)_ _ ___
// | (_) | _ \/ _ \_ _| _/ _| / _ \ || / _` | / _ \ | _|| ' \/ _` | | ' \/ -_)
// \__\_\___/\___/ |_||_| |___| /_/ \_\_,_\__,_|_\___/ |___|_||_\__, |_|_||_\___|
// |___/
//
// QB64-PE Audio Engine powered by miniaudio (https://miniaud.io/)
//
// This implements a data source that decodes MIDI files using TinySoundFont + TinyMidiLoader
// https://github.com/schellingb/TinySoundFont (MIT)
//
// Soundfont (awe32rom.h) from dos-like
// https://github.com/mattiasgustavsson/dos-like (MIT)
//
// Copyright (c) 2022 Samuel Gomes
// https://github.com/a740g
//
//-----------------------------------------------------------------------------------------------------
#pragma once
//-----------------------------------------------------------------------------------------------------
// HEADER FILES
//-----------------------------------------------------------------------------------------------------
#include "../miniaudio.h"
#include "tinysoundfont/awe32rom.h"
#define TSF_IMPLEMENTATION
#include "tinysoundfont/tsf.h"
#define TML_IMPLEMENTATION
#include "tinysoundfont/tml.h"
//-----------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------
// CONSTANTS
//-----------------------------------------------------------------------------------------------------
#define TSF_DEFAULT_SOUNDFONT_FILENAME "soundfont.sf2"
//-----------------------------------------------------------------------------------------------------
struct ma_tsf {
// This part is for miniaudio
ma_data_source_base ds; /* The decoder can be used independently as a data source. */
ma_read_proc onRead;
ma_seek_proc onSeek;
ma_tell_proc onTell;
void *pReadSeekTellUserData;
ma_format format;
// This part is format specific
tsf *tinySoundFont; // TinySoundFont context
tml_message *tinyMidiLoader; // TinyMidiLoader context
ma_uint32 totalTime; // Total duration of the MIDI song in msec
double currentTime; // Current song playback time in msec
tml_message *midiMessage; // Next message to be played (this is set to NULL once the song is over)
};
static ma_result ma_tsf_seek_to_pcm_frame(ma_tsf *pTsf, ma_uint64 frameIndex) {
if (pTsf == NULL) {
return MA_INVALID_ARGS;
}
// We can only reset the player to the beginning
if (frameIndex == 0) {
tsf_reset(pTsf->tinySoundFont); // Stop playing whatever is playing
pTsf->midiMessage = pTsf->tinyMidiLoader; // Set up the global MidiMessage pointer to the first MIDI message
pTsf->currentTime = 0; // Reset playback time
return MA_SUCCESS;
}
return MA_INVALID_OPERATION; // Anything else is not seekable
}
static ma_result ma_tsf_get_data_format(ma_tsf *pTsf, ma_format *pFormat, ma_uint32 *pChannels, ma_uint32 *pSampleRate, ma_channel *pChannelMap,
size_t channelMapCap) {
/* Defaults for safety. */
if (pFormat != NULL) {
*pFormat = ma_format_unknown;
}
if (pChannels != NULL) {
*pChannels = 0;
}
if (pSampleRate != NULL) {
*pSampleRate = 0;
}
if (pChannelMap != NULL) {
MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
}
if (pTsf == NULL) {
return MA_INVALID_OPERATION;
}
if (pFormat != NULL) {
*pFormat = pTsf->format;
}
if (pChannels != NULL) {
*pChannels = 2; // Stereo
}
if (pSampleRate != NULL) {
*pSampleRate = MA_DEFAULT_SAMPLE_RATE;
}
if (pChannelMap != NULL) {
ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, 2);
}
return MA_SUCCESS;
}
static ma_result ma_tsf_read_pcm_frames(ma_tsf *pTsf, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead) {
if (pFramesRead != NULL) {
*pFramesRead = 0;
}
if (frameCount == 0) {
return MA_INVALID_ARGS;
}
if (pTsf == NULL) {
return MA_INVALID_ARGS;
}
ma_result result = MA_SUCCESS; // Must be initialized to MA_SUCCESS
ma_uint64 totalFramesRead = 0;
ma_uint8 *buffer = (ma_uint8 *)pFramesOut;
ma_int64 SampleBlock, SampleCount = frameCount; // Number of sample frames to process
for (SampleBlock = TSF_RENDER_EFFECTSAMPLEBLOCK; SampleCount; SampleCount -= SampleBlock, buffer += (SampleBlock * (sizeof(short) * 2))) {
// We progress the MIDI playback and then process TSF_RENDER_EFFECTSAMPLEBLOCK samples at once
if (SampleBlock > SampleCount)
SampleBlock = SampleCount;
// Loop through all MIDI messages which need to be played up until the current playback time
for (pTsf->currentTime += SampleBlock * (1000.0 / MA_DEFAULT_SAMPLE_RATE); pTsf->midiMessage && pTsf->currentTime >= pTsf->midiMessage->time;
pTsf->midiMessage = pTsf->midiMessage->next) {
switch (pTsf->midiMessage->type) {
case TML_PROGRAM_CHANGE: // Channel program (preset) change (special handling for 10th MIDI channel with drums)
tsf_channel_set_presetnumber(pTsf->tinySoundFont, pTsf->midiMessage->channel, pTsf->midiMessage->program, (pTsf->midiMessage->channel == 9));
tsf_channel_midi_control(pTsf->tinySoundFont, pTsf->midiMessage->channel, TML_ALL_NOTES_OFF,
0); // https://github.com/schellingb/TinySoundFont/issues/59
break;
case TML_NOTE_ON: // Play a note
tsf_channel_note_on(pTsf->tinySoundFont, pTsf->midiMessage->channel, pTsf->midiMessage->key, pTsf->midiMessage->velocity / 127.0f);
break;
case TML_NOTE_OFF: // Stop a note
tsf_channel_note_off(pTsf->tinySoundFont, pTsf->midiMessage->channel, pTsf->midiMessage->key);
break;
case TML_PITCH_BEND: // Pitch wheel modification
tsf_channel_set_pitchwheel(pTsf->tinySoundFont, pTsf->midiMessage->channel, pTsf->midiMessage->pitch_bend);
break;
case TML_CONTROL_CHANGE: // MIDI controller messages
tsf_channel_midi_control(pTsf->tinySoundFont, pTsf->midiMessage->channel, pTsf->midiMessage->control, pTsf->midiMessage->control_value);
break;
}
}
// Render the block of audio samples in int16 format
tsf_render_short(pTsf->tinySoundFont, (short *)buffer, (int)SampleBlock);
totalFramesRead += SampleBlock;
// Signal end of stream if we have reached the end
if (pTsf->midiMessage == NULL) {
result = MA_AT_END;
// However, also reset the position to the beginning just in case we want to loop
pTsf->midiMessage = pTsf->tinyMidiLoader; // Set up the global MidiMessage pointer to the first MIDI message
pTsf->currentTime = 0; // Reset playback time
break;
}
}
if (pFramesRead != NULL) {
*pFramesRead = totalFramesRead;
}
return result;
}
static ma_result ma_tsf_get_cursor_in_pcm_frames(ma_tsf *pTsf, ma_uint64 *pCursor) {
if (pCursor == NULL) {
return MA_INVALID_ARGS;
}
*pCursor = 0; /* Safety. */
if (pTsf == NULL) {
return MA_INVALID_ARGS;
}
ma_int64 offset = ((ma_int64)pTsf->currentTime * MA_DEFAULT_SAMPLE_RATE) / 1000;
if (offset < 0) {
return MA_INVALID_FILE;
}
*pCursor = (ma_uint64)offset;
return MA_SUCCESS;
}
static ma_result ma_tsf_get_length_in_pcm_frames(ma_tsf *pTsf, ma_uint64 *pLength) {
if (pLength == NULL) {
return MA_INVALID_ARGS;
}
*pLength = 0; /* Safety. */
if (pTsf == NULL) {
return MA_INVALID_ARGS;
}
// Total time in seconds * Opal sample rate
ma_int64 length = ((ma_int64)pTsf->totalTime * MA_DEFAULT_SAMPLE_RATE) / 1000;
if (length < 0) {
return MA_INVALID_FILE;
}
*pLength = (ma_uint64)length;
return MA_SUCCESS;
}
static ma_result ma_tsf_ds_read(ma_data_source *pDataSource, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead) {
return ma_tsf_read_pcm_frames((ma_tsf *)pDataSource, pFramesOut, frameCount, pFramesRead);
}
static ma_result ma_tsf_ds_seek(ma_data_source *pDataSource, ma_uint64 frameIndex) { return ma_tsf_seek_to_pcm_frame((ma_tsf *)pDataSource, frameIndex); }
static ma_result ma_tsf_ds_get_data_format(ma_data_source *pDataSource, ma_format *pFormat, ma_uint32 *pChannels, ma_uint32 *pSampleRate,
ma_channel *pChannelMap, size_t channelMapCap) {
return ma_tsf_get_data_format((ma_tsf *)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
}
static ma_result ma_tsf_ds_get_cursor(ma_data_source *pDataSource, ma_uint64 *pCursor) {
return ma_tsf_get_cursor_in_pcm_frames((ma_tsf *)pDataSource, pCursor);
}
static ma_result ma_tsf_ds_get_length(ma_data_source *pDataSource, ma_uint64 *pLength) {
return ma_tsf_get_length_in_pcm_frames((ma_tsf *)pDataSource, pLength);
}
static ma_data_source_vtable ma_data_source_vtable_tsf = {ma_tsf_ds_read, ma_tsf_ds_seek, ma_tsf_ds_get_data_format, ma_tsf_ds_get_cursor,
ma_tsf_ds_get_length};
static int ma_tsf_of_callback__read(void *pUserData, unsigned char *pBufferOut, int bytesToRead) {
ma_tsf *pTsf = (ma_tsf *)pUserData;
ma_result result;
size_t bytesRead;
result = pTsf->onRead(pTsf->pReadSeekTellUserData, (void *)pBufferOut, bytesToRead, &bytesRead);
if (result != MA_SUCCESS) {
return -1;
}
return (int)bytesRead;
}
static int ma_tsf_of_callback__seek(void *pUserData, ma_int64 offset, int whence) {
ma_tsf *pTsf = (ma_tsf *)pUserData;
ma_result result;
ma_seek_origin origin;
if (whence == SEEK_SET) {
origin = ma_seek_origin_start;
} else if (whence == SEEK_END) {
origin = ma_seek_origin_end;
} else {
origin = ma_seek_origin_current;
}
result = pTsf->onSeek(pTsf->pReadSeekTellUserData, offset, origin);
if (result != MA_SUCCESS) {
return -1;
}
return 0;
}
static ma_int64 ma_tsf_of_callback__tell(void *pUserData) {
ma_tsf *pTsf = (ma_tsf *)pUserData;
ma_result result;
ma_int64 cursor;
if (pTsf->onTell == NULL) {
return -1;
}
result = pTsf->onTell(pTsf->pReadSeekTellUserData, &cursor);
if (result != MA_SUCCESS) {
return -1;
}
return cursor;
}
static ma_result ma_tsf_init_internal(const ma_decoding_backend_config *pConfig, ma_tsf *pTsf) {
ma_result result;
ma_data_source_config dataSourceConfig;
if (pTsf == NULL) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pTsf);
pTsf->format = ma_format::ma_format_s16; // We'll render 16-bit signed samples by default
if (pConfig != NULL && pConfig->preferredFormat == ma_format::ma_format_s16) {
pTsf->format = pConfig->preferredFormat;
} else {
/* Getting here means something other than s16 was specified. Just leave this unset to use the default format. */
}
dataSourceConfig = ma_data_source_config_init();
dataSourceConfig.vtable = &ma_data_source_vtable_tsf;
result = ma_data_source_init(&dataSourceConfig, &pTsf->ds);
if (result != MA_SUCCESS) {
return result; /* Failed to initialize the base data source. */
}
return MA_SUCCESS;
}
static ma_result ma_tsf_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void *pReadSeekTellUserData,
const ma_decoding_backend_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_tsf *pTsf) {
ma_result result;
(void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libopus. */
result = ma_tsf_init_internal(pConfig, pTsf);
if (result != MA_SUCCESS) {
return result;
}
if (onRead == NULL || onSeek == NULL) {
return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
}
pTsf->onRead = onRead;
pTsf->onSeek = onSeek;
pTsf->onTell = onTell;
pTsf->pReadSeekTellUserData = pReadSeekTellUserData;
// Seek to the end of the file
if (ma_tsf_of_callback__seek(pTsf, 0, SEEK_END) != 0) {
return MA_BAD_SEEK;
}
// Calculate the length
ma_int64 file_size = ma_tsf_of_callback__tell(pTsf);
if (file_size < 1) {
return MA_INVALID_FILE;
}
// See to the beginning of the file
if (ma_tsf_of_callback__seek(pTsf, 0, SEEK_SET) != 0) {
return MA_BAD_SEEK;
}
// Allocate some memory for the tune
ma_uint8 *tune = new ma_uint8[file_size];
if (tune == nullptr) {
return MA_OUT_OF_MEMORY;
}
// Read the file
if (ma_tsf_of_callback__read(pTsf, tune, (int)file_size) < 1) {
delete[] tune;
return MA_IO_ERROR;
}
// Attempt to load a SoundFont from a file
pTsf->tinySoundFont = tsf_load_filename(TSF_DEFAULT_SOUNDFONT_FILENAME);
if (!pTsf->tinySoundFont) {
// Attempt to load the soundfont from memory
pTsf->tinySoundFont = tsf_load_memory(awe32rom, sizeof(awe32rom));
// Return failue if loading from memory also failed. This should not happen though
if (!pTsf->tinySoundFont) {
delete[] tune;
return MA_OUT_OF_MEMORY;
}
}
// Initialize preset on special 10th MIDI channel to use percussion sound bank (128) if available
tsf_channel_set_bank_preset(pTsf->tinySoundFont, 9, 128, 0);
// Set the SoundFont rendering output mode
tsf_set_output(pTsf->tinySoundFont, TSF_STEREO_INTERLEAVED, MA_DEFAULT_SAMPLE_RATE);
// Initialize TML
pTsf->tinyMidiLoader = tml_load_memory(tune, (int)file_size);
if (!pTsf->tinyMidiLoader) {
tsf_close(pTsf->tinySoundFont);
pTsf->tinySoundFont = TSF_NULL;
delete[] tune;
return MA_INVALID_FILE;
}
// Free the memory now that we don't need it anymore
delete[] tune;
// Get the total duration of the song ignoring the rest of the stuff
tml_get_info(pTsf->tinyMidiLoader, NULL, NULL, NULL, NULL, &pTsf->totalTime);
// Setup some stuff
pTsf->midiMessage = pTsf->tinyMidiLoader; // Set up the global MidiMessage pointer to the first MIDI message
pTsf->currentTime = 0; // Reset playback time
return MA_SUCCESS;
}
static ma_result ma_tsf_init_file(const char *pFilePath, const ma_decoding_backend_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks,
ma_tsf *pTsf) {
ma_result result;
(void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libopus. */
result = ma_tsf_init_internal(pConfig, pTsf);
if (result != MA_SUCCESS) {
return result;
}
// Check the file extension
if (!ma_path_extension_equal(pFilePath, "mid") && !ma_path_extension_equal(pFilePath, "midi")) {
return MA_INVALID_FILE;
}
// Attempt to load a SoundFont from a file
pTsf->tinySoundFont = tsf_load_filename(TSF_DEFAULT_SOUNDFONT_FILENAME);
if (!pTsf->tinySoundFont) {
// Attempt to load the soundfont from memory
pTsf->tinySoundFont = tsf_load_memory(awe32rom, sizeof(awe32rom));
// Return failue if loading from memory also failed. This should not happen though
if (!pTsf->tinySoundFont) {
return MA_OUT_OF_MEMORY;
}
}
// Initialize preset on special 10th MIDI channel to use percussion sound bank (128) if available
tsf_channel_set_bank_preset(pTsf->tinySoundFont, 9, 128, 0);
// Set the SoundFont rendering output mode
tsf_set_output(pTsf->tinySoundFont, TSF_STEREO_INTERLEAVED, MA_DEFAULT_SAMPLE_RATE);
// Initialize TML
pTsf->tinyMidiLoader = tml_load_filename(pFilePath);
if (!pTsf->tinyMidiLoader) {
tsf_close(pTsf->tinySoundFont);
pTsf->tinySoundFont = TSF_NULL;
return MA_INVALID_FILE;
}
// Get the total duration of the song ignoring the rest of the stuff
tml_get_info(pTsf->tinyMidiLoader, NULL, NULL, NULL, NULL, &pTsf->totalTime);
// Setup some stuff
pTsf->midiMessage = pTsf->tinyMidiLoader; // Set up the global MidiMessage pointer to the first MIDI message
pTsf->currentTime = 0; // Reset playback time
return MA_SUCCESS;
}
static void ma_tsf_uninit(ma_tsf *pTsf, const ma_allocation_callbacks *pAllocationCallbacks) {
if (pTsf == NULL) {
return;
}
(void)pAllocationCallbacks;
tsf_reset(pTsf->tinySoundFont); // Stop playing whatever is playing
tml_free(pTsf->tinyMidiLoader); // Free TML resources
pTsf->tinyMidiLoader = TML_NULL;
tsf_close(pTsf->tinySoundFont);
pTsf->tinySoundFont = TSF_NULL;
ma_data_source_uninit(&pTsf->ds);
}
static ma_result ma_decoding_backend_init__tsf(void *pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void *pReadSeekTellUserData,
const ma_decoding_backend_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks,
ma_data_source **ppBackend) {
ma_result result;
ma_tsf *pTsf;
(void)pUserData;
pTsf = (ma_tsf *)ma_malloc(sizeof(ma_tsf), pAllocationCallbacks);
if (pTsf == NULL) {
return MA_OUT_OF_MEMORY;
}
result = ma_tsf_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pTsf);
if (result != MA_SUCCESS) {
ma_free(pTsf, pAllocationCallbacks);
return result;
}
*ppBackend = pTsf;
return MA_SUCCESS;
}
static ma_result ma_decoding_backend_init_file__tsf(void *pUserData, const char *pFilePath, const ma_decoding_backend_config *pConfig,
const ma_allocation_callbacks *pAllocationCallbacks, ma_data_source **ppBackend) {
ma_result result;
ma_tsf *pTsf;
(void)pUserData;
pTsf = (ma_tsf *)ma_malloc(sizeof(ma_tsf), pAllocationCallbacks);
if (pTsf == NULL) {
return MA_OUT_OF_MEMORY;
}
result = ma_tsf_init_file(pFilePath, pConfig, pAllocationCallbacks, pTsf);
if (result != MA_SUCCESS) {
ma_free(pTsf, pAllocationCallbacks);
return result;
}
*ppBackend = pTsf;
return MA_SUCCESS;
}
static void ma_decoding_backend_uninit__tsf(void *pUserData, ma_data_source *pBackend, const ma_allocation_callbacks *pAllocationCallbacks) {
ma_tsf *pTsf = (ma_tsf *)pBackend;
(void)pUserData;
ma_tsf_uninit(pTsf, pAllocationCallbacks);
ma_free(pTsf, pAllocationCallbacks);
}
static ma_decoding_backend_vtable ma_decoding_backend_vtable_tsf = {ma_decoding_backend_init__tsf, ma_decoding_backend_init_file__tsf, NULL, /* onInitFileW() */
NULL, /* onInitMemory() */
ma_decoding_backend_uninit__tsf};
//-----------------------------------------------------------------------------------------------------

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,298 @@
/*
Code to check a RAD V2 tune file is valid. That is, it will check the tune
can be played without crashing the player. It doesn't exhaustively check
the tune except where needed to prevent a possible player crash.
Call RADValidate with a pointer to your tune and the size in bytes. It
will return either NULL for all okay, or a pointer to a null-terminated
string describing what is wrong with the data.
*/
#include <stdint.h>
//==================================================================================================
// The error strings are all supplied here in case you want to translate them to another language
// (or supply your own more descriptive error messages).
//==================================================================================================
const char *g_RADNotATuneFile = "Not a RAD tune file.";
const char *g_RADNotAVersion21Tune = "Not a version 2.1 file format RAD tune.";
const char *g_RADTruncated = "Tune file has been truncated and is incomplete.";
const char *g_RADBadFlags = "Tune file has invalid flags.";
const char *g_RADBadBPMValue = "Tune's BPM value is out of range.";
const char *g_RADBadInstrument = "Tune file contains a bad instrument definition.";
const char *g_RADUnknownMIDIVersion = "Tune file contains an unknown MIDI instrument version.";
const char *g_RADOrderListTooLarge = "Order list in tune file is an invalid size.";
const char *g_RADBadJumpMarker = "Order list jump marker is invalid.";
const char *g_RADBadOrderEntry = "Order list entry is invalid.";
const char *g_RADBadPattNum = "Tune file contains a bad pattern index.";
const char *g_RADPattTruncated = "Tune file contains a truncated pattern.";
const char *g_RADPattExtraData = "Tune file contains a pattern with extraneous data.";
const char *g_RADPattBadLineNum = "Tune file contains a pattern with a bad line definition.";
const char *g_RADPattBadChanNum = "Tune file contains a pattern with a bad channel definition.";
const char *g_RADPattBadNoteNum = "Pattern contains a bad note number.";
const char *g_RADPattBadInstNum = "Pattern contains a bad instrument number.";
const char *g_RADPattBadEffect = "Pattern contains a bad effect and/or parameter.";
const char *g_RADBadRiffNum = "Tune file contains a bad riff index.";
const char *g_RADExtraBytes = "Tune file contains extra bytes.";
//==================================================================================================
// Validate a RAD V2 (file format 2.1) tune file. Note, this uses no C++ standard library code.
//==================================================================================================
static const char *RADCheckPattern(const uint8_t *&s, const uint8_t *e, bool riff) {
// Get pattern size
if (s + 2 > e)
return g_RADTruncated;
uint16_t pattsize = s[0] | (uint16_t(s[1]) << 8);
s += 2;
// Calculate end of pattern
const uint8_t *pe = s + pattsize;
if (pe > e)
return g_RADTruncated;
uint8_t linedef, chandef;
do {
// Check line of pattern
if (s >= pe)
return g_RADPattTruncated;
linedef = *s++;
uint8_t linenum = linedef & 0x7F;
if (linenum >= 64)
return g_RADPattBadLineNum;
do {
// Check channel of pattern
if (s >= pe)
return g_RADPattTruncated;
chandef = *s++;
uint8_t channum = chandef & 0x0F;
if (!riff && channum >= 9)
return g_RADPattBadChanNum;
// Check note
if (chandef & 0x40) {
if (s >= pe)
return g_RADPattTruncated;
uint8_t note = *s++;
uint8_t notenum = note & 15;
uint8_t octave = (note >> 4) & 7;
if (notenum == 0 || notenum == 13 || notenum == 14)
return g_RADPattBadNoteNum;
}
// Check instrument. This shouldn't be supplied if bit 7 of the note byte is set,
// but it doesn't break anything if it is so we don't check for it
if (chandef & 0x20) {
if (s >= pe)
return g_RADPattTruncated;
uint8_t inst = *s++;
if (inst == 0 || inst >= 128)
return g_RADPattBadInstNum;
}
// Check effect. A non-existent effect could be supplied, but it'll just be
// ignored by the player so we don't care
if (chandef & 0x10) {
if (s + 2 > pe)
return g_RADPattTruncated;
uint8_t effect = *s++;
uint8_t param = *s++;
if (effect > 31 || param > 99)
return g_RADPattBadEffect;
}
} while (!(chandef & 0x80));
} while (!(linedef & 0x80));
if (s != pe)
return g_RADPattExtraData;
return 0;
}
//--------------------------------------------------------------------------------------------------
const char *RADValidate(const void *data, size_t data_size) {
const uint8_t *s = (const uint8_t *)data;
const uint8_t *e = s + data_size;
// Check header
if (data_size < 16)
return g_RADNotATuneFile;
const char *hdrtxt = "RAD by REALiTY!!";
for (int i = 0; i < 16; i++)
if (char(*s++) != *hdrtxt++)
return g_RADNotATuneFile;
// Check version
if (s >= e || *s++ != 0x21)
return g_RADNotAVersion21Tune;
// Check flags
if (s >= e)
return g_RADTruncated;
uint8_t flags = *s++;
if (flags & 0x80)
return g_RADBadFlags; // Bit 7 is unused
if (flags & 0x40) {
if (s + 2 > e)
return g_RADTruncated;
uint16_t bpm = s[0] | (uint16_t(s[1]) << 8);
s += 2;
if (bpm < 46 || bpm > 300)
return g_RADBadBPMValue;
}
// Check description. This is actually freeform text so there's not a lot to check, just that
// it's a null-terminated string
do {
if (s >= e)
return g_RADTruncated;
} while (*s++);
// Check instruments. We don't actually validate the individual instrument fields as the tune
// file will still play with bad instrument data. We're only concerned that the tune file
// doesn't crash the player
uint8_t last_inst = 0;
while (1) {
// Get instrument number, or 0 for end of instrument list
if (s >= e)
return g_RADTruncated;
uint8_t inst = *s++;
if (inst == 0)
break;
// RAD always saves the instruments out in order
if (inst > 127 || inst <= last_inst)
return g_RADBadInstrument;
last_inst = inst;
// Check the name
if (s >= e)
return g_RADTruncated;
uint8_t namelen = *s++;
s += namelen;
// Get algorithm
if (s > e)
return g_RADTruncated;
uint8_t alg = *s;
if ((alg & 7) == 7) {
// MIDI instrument. We need to check the version as this can affect the following
// data size
if (s + 6 > e)
return g_RADTruncated;
if (s[2] >> 4)
return g_RADUnknownMIDIVersion;
s += 6;
} else {
s += 24;
if (s > e)
return g_RADTruncated;
}
// Riff track supplied?
if (alg & 0x80) {
const char *err = RADCheckPattern(s, e, false);
if (err)
return err;
}
}
// Get the order list
if (s >= e)
return g_RADTruncated;
uint8_t order_size = *s++;
const uint8_t *order_list = s;
if (order_size > 128)
return g_RADOrderListTooLarge;
s += order_size;
for (uint8_t i = 0; i < order_size; i++) {
uint8_t order = order_list[i];
if (order & 0x80) {
// Check jump marker
order &= 0x7F;
if (order >= order_size)
return g_RADBadJumpMarker;
} else {
// Check pattern number. It doesn't matter if there is no pattern with this number
// defined later, as missing patterns are treated as empty
if (order >= 100)
return g_RADBadOrderEntry;
}
}
// Check the patterns
while (1) {
// Get pattern number
if (s >= e)
return g_RADTruncated;
uint8_t pattnum = *s++;
// Last pattern?
if (pattnum == 0xFF)
break;
if (pattnum >= 100)
return g_RADBadPattNum;
const char *err = RADCheckPattern(s, e, false);
if (err)
return err;
}
// Check the riffs
while (1) {
// Get riff number
if (s >= e)
return g_RADTruncated;
uint8_t riffnum = *s++;
// Last riff?
if (riffnum == 0xFF)
break;
uint8_t riffpatt = riffnum >> 4;
uint8_t riffchan = riffnum & 15;
if (riffpatt > 9 || riffchan == 0 || riffchan > 9)
return g_RADBadRiffNum;
const char *err = RADCheckPattern(s, e, true);
if (err)
return err;
}
// We should be at the end of the file now. Note, you can safely remove this check if you
// like - extra bytes won't affect playback
if (s != e)
return g_RADExtraBytes;
// Tune file is all good
return 0;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,531 @@
/* TinyMidiLoader - v0.7 - Minimalistic midi parsing library - https://github.com/schellingb/TinySoundFont
no warranty implied; use at your own risk
Do this:
#define TML_IMPLEMENTATION
before you include this file in *one* C or C++ file to create the implementation.
// i.e. it should look like this:
#include ...
#include ...
#define TML_IMPLEMENTATION
#include "tml.h"
[OPTIONAL] #define TML_NO_STDIO to remove stdio dependency
[OPTIONAL] #define TML_MALLOC, TML_REALLOC, and TML_FREE to avoid stdlib.h
[OPTIONAL] #define TML_MEMCPY to avoid string.h
LICENSE (ZLIB)
Copyright (C) 2017, 2018, 2020 Bernhard Schelling
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef TML_INCLUDE_TML_INL
#define TML_INCLUDE_TML_INL
#ifdef __cplusplus
extern "C" {
#endif
// Define this if you want the API functions to be static
#ifdef TML_STATIC
#define TMLDEF static
#else
#define TMLDEF extern
#endif
// Channel message type
enum TMLMessageType
{
TML_NOTE_OFF = 0x80, TML_NOTE_ON = 0x90, TML_KEY_PRESSURE = 0xA0, TML_CONTROL_CHANGE = 0xB0, TML_PROGRAM_CHANGE = 0xC0, TML_CHANNEL_PRESSURE = 0xD0, TML_PITCH_BEND = 0xE0, TML_SET_TEMPO = 0x51
};
// Midi controller numbers
enum TMLController
{
TML_BANK_SELECT_MSB, TML_MODULATIONWHEEL_MSB, TML_BREATH_MSB, TML_FOOT_MSB = 4, TML_PORTAMENTO_TIME_MSB, TML_DATA_ENTRY_MSB, TML_VOLUME_MSB,
TML_BALANCE_MSB, TML_PAN_MSB = 10, TML_EXPRESSION_MSB, TML_EFFECTS1_MSB, TML_EFFECTS2_MSB, TML_GPC1_MSB = 16, TML_GPC2_MSB, TML_GPC3_MSB, TML_GPC4_MSB,
TML_BANK_SELECT_LSB = 32, TML_MODULATIONWHEEL_LSB, TML_BREATH_LSB, TML_FOOT_LSB = 36, TML_PORTAMENTO_TIME_LSB, TML_DATA_ENTRY_LSB, TML_VOLUME_LSB,
TML_BALANCE_LSB, TML_PAN_LSB = 42, TML_EXPRESSION_LSB, TML_EFFECTS1_LSB, TML_EFFECTS2_LSB, TML_GPC1_LSB = 48, TML_GPC2_LSB, TML_GPC3_LSB, TML_GPC4_LSB,
TML_SUSTAIN_SWITCH = 64, TML_PORTAMENTO_SWITCH, TML_SOSTENUTO_SWITCH, TML_SOFT_PEDAL_SWITCH, TML_LEGATO_SWITCH, TML_HOLD2_SWITCH,
TML_SOUND_CTRL1, TML_SOUND_CTRL2, TML_SOUND_CTRL3, TML_SOUND_CTRL4, TML_SOUND_CTRL5, TML_SOUND_CTRL6,
TML_SOUND_CTRL7, TML_SOUND_CTRL8, TML_SOUND_CTRL9, TML_SOUND_CTRL10, TML_GPC5, TML_GPC6, TML_GPC7, TML_GPC8,
TML_PORTAMENTO_CTRL, TML_FX_REVERB = 91, TML_FX_TREMOLO, TML_FX_CHORUS, TML_FX_CELESTE_DETUNE, TML_FX_PHASER,
TML_DATA_ENTRY_INCR, TML_DATA_ENTRY_DECR, TML_NRPN_LSB, TML_NRPN_MSB, TML_RPN_LSB, TML_RPN_MSB,
TML_ALL_SOUND_OFF = 120, TML_ALL_CTRL_OFF, TML_LOCAL_CONTROL, TML_ALL_NOTES_OFF, TML_OMNI_OFF, TML_OMNI_ON, TML_POLY_OFF, TML_POLY_ON
};
// A single MIDI message linked to the next message in time
typedef struct tml_message
{
// Time of the message in milliseconds
unsigned int time;
// Type (see TMLMessageType) and channel number
unsigned char type, channel;
// 2 byte of parameter data based on the type:
// - key, velocity for TML_NOTE_ON and TML_NOTE_OFF messages
// - key, key_pressure for TML_KEY_PRESSURE messages
// - control, control_value for TML_CONTROL_CHANGE messages (see TMLController)
// - program for TML_PROGRAM_CHANGE messages
// - channel_pressure for TML_CHANNEL_PRESSURE messages
// - pitch_bend for TML_PITCH_BEND messages
union
{
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4201) //nonstandard extension used: nameless struct/union
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic" //ISO C++ prohibits anonymous structs
#endif
struct { union { char key, control, program, channel_pressure; }; union { char velocity, key_pressure, control_value; }; };
struct { unsigned short pitch_bend; };
#ifdef _MSC_VER
#pragma warning( pop )
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
};
// The pointer to the next message in time following this event
struct tml_message* next;
} tml_message;
// The load functions will return a pointer to a struct tml_message.
// Normally the linked list gets traversed by following the next pointers.
// Make sure to keep the pointer to the first message to free the memory.
// On error the tml_load* functions will return NULL most likely due to an
// invalid MIDI stream (or if the file did not exist in tml_load_filename).
#ifndef TML_NO_STDIO
// Directly load a MIDI file from a .mid file path
TMLDEF tml_message* tml_load_filename(const char* filename);
#endif
// Load a MIDI file from a block of memory
TMLDEF tml_message* tml_load_memory(const void* buffer, int size);
// Get infos about this loaded MIDI file, returns the note count
// NULL can be passed for any output value pointer if not needed.
// used_channels: Will be set to how many channels play notes
// (i.e. 1 if channel 15 is used but no other)
// used_programs: Will be set to how many different programs are used
// total_notes: Will be set to the total number of note on messages
// time_first_note: Will be set to the time of the first note on message
// time_length: Will be set to the total time in milliseconds
TMLDEF int tml_get_info(tml_message* first_message, int* used_channels, int* used_programs, int* total_notes, unsigned int* time_first_note, unsigned int* time_length);
// Read the tempo (microseconds per quarter note) value from a message with the type TML_SET_TEMPO
TMLDEF int tml_get_tempo_value(tml_message* set_tempo_message);
// Free all the memory of the linked message list (can also call free() manually)
TMLDEF void tml_free(tml_message* f);
// Stream structure for the generic loading
struct tml_stream
{
// Custom data given to the functions as the first parameter
void* data;
// Function pointer will be called to read 'size' bytes into ptr (returns number of read bytes)
int (*read)(void* data, void* ptr, unsigned int size);
};
// Generic Midi loading method using the stream structure above
TMLDEF tml_message* tml_load(struct tml_stream* stream);
// If this library is used together with TinySoundFont, tsf_stream (equivalent to tml_stream) can also be used
struct tsf_stream;
TMLDEF tml_message* tml_load_tsf_stream(struct tsf_stream* stream);
#ifdef __cplusplus
}
#endif
// end header
// ---------------------------------------------------------------------------------------------------------
#endif //TML_INCLUDE_TML_INL
#ifdef TML_IMPLEMENTATION
#if !defined(TML_MALLOC) || !defined(TML_FREE) || !defined(TML_REALLOC)
# include <stdlib.h>
# define TML_MALLOC malloc
# define TML_FREE free
# define TML_REALLOC realloc
#endif
#if !defined(TML_MEMCPY)
# include <string.h>
# define TML_MEMCPY memcpy
#endif
#ifndef TML_NO_STDIO
# include <stdio.h>
#endif
#define TML_NULL 0
////crash on errors and warnings to find broken midi files while debugging
//#define TML_ERROR(msg) *(int*)0 = 0xbad;
//#define TML_WARN(msg) *(int*)0 = 0xf00d;
////print errors and warnings
//#define TML_ERROR(msg) printf("ERROR: %s\n", msg);
//#define TML_WARN(msg) printf("WARNING: %s\n", msg);
#ifndef TML_ERROR
#define TML_ERROR(msg)
#endif
#ifndef TML_WARN
#define TML_WARN(msg)
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifndef TML_NO_STDIO
static int tml_stream_stdio_read(FILE* f, void* ptr, unsigned int size) { return (int)fread(ptr, 1, size, f); }
TMLDEF tml_message* tml_load_filename(const char* filename)
{
struct tml_message* res;
struct tml_stream stream = { TML_NULL, (int(*)(void*,void*,unsigned int))&tml_stream_stdio_read };
#if __STDC_WANT_SECURE_LIB__
FILE* f = TML_NULL; fopen_s(&f, filename, "rb");
#else
FILE* f = fopen(filename, "rb");
#endif
if (!f) { TML_ERROR("File not found"); return 0; }
stream.data = f;
res = tml_load(&stream);
fclose(f);
return res;
}
#endif
struct tml_stream_memory { const char* buffer; unsigned int total, pos; };
static int tml_stream_memory_read(struct tml_stream_memory* m, void* ptr, unsigned int size) { if (size > m->total - m->pos) size = m->total - m->pos; TML_MEMCPY(ptr, m->buffer+m->pos, size); m->pos += size; return size; }
TMLDEF struct tml_message* tml_load_memory(const void* buffer, int size)
{
struct tml_stream stream = { TML_NULL, (int(*)(void*,void*,unsigned int))&tml_stream_memory_read };
struct tml_stream_memory f = { 0, 0, 0 };
f.buffer = (const char*)buffer;
f.total = size;
stream.data = &f;
return tml_load(&stream);
}
struct tml_track
{
unsigned int Idx, End, Ticks;
};
struct tml_tempomsg
{
unsigned int time;
unsigned char type, Tempo[3];
tml_message* next;
};
struct tml_parser
{
unsigned char *buf, *buf_end;
int last_status, message_array_size, message_count;
};
enum TMLSystemType
{
TML_TEXT = 0x01, TML_COPYRIGHT = 0x02, TML_TRACK_NAME = 0x03, TML_INST_NAME = 0x04, TML_LYRIC = 0x05, TML_MARKER = 0x06, TML_CUE_POINT = 0x07,
TML_EOT = 0x2f, TML_SMPTE_OFFSET = 0x54, TML_TIME_SIGNATURE = 0x58, TML_KEY_SIGNATURE = 0x59, TML_SEQUENCER_EVENT = 0x7f,
TML_SYSEX = 0xf0, TML_TIME_CODE = 0xf1, TML_SONG_POSITION = 0xf2, TML_SONG_SELECT = 0xf3, TML_TUNE_REQUEST = 0xf6, TML_EOX = 0xf7, TML_SYNC = 0xf8,
TML_TICK = 0xf9, TML_START = 0xfa, TML_CONTINUE = 0xfb, TML_STOP = 0xfc, TML_ACTIVE_SENSING = 0xfe, TML_SYSTEM_RESET = 0xff
};
static int tml_readbyte(struct tml_parser* p)
{
return (p->buf == p->buf_end ? -1 : *(p->buf++));
}
static int tml_readvariablelength(struct tml_parser* p)
{
unsigned int res = 0, i = 0;
unsigned char c;
for (; i != 4; i++)
{
if (p->buf == p->buf_end) { TML_WARN("Unexpected end of file"); return -1; }
c = *(p->buf++);
if (c & 0x80) res = ((res | (c & 0x7F)) << 7);
else return (int)(res | c);
}
TML_WARN("Invalid variable length byte count"); return -1;
}
static int tml_parsemessage(tml_message** f, struct tml_parser* p)
{
int deltatime = tml_readvariablelength(p), status = tml_readbyte(p);
tml_message* evt;
if (deltatime & 0xFFF00000) deltatime = 0; //throw away delays that are insanely high for malformatted midis
if (status < 0) { TML_WARN("Unexpected end of file"); return -1; }
if ((status & 0x80) == 0)
{
// Invalid, use same status as before
if ((p->last_status & 0x80) == 0) { TML_WARN("Undefined status and invalid running status"); return -1; }
p->buf--;
status = p->last_status;
}
else p->last_status = status;
if (p->message_array_size == p->message_count)
{
//start allocated memory size of message array at 64, double each time until 8192, then add 1024 entries until done
p->message_array_size += (!p->message_array_size ? 64 : (p->message_array_size > 4096 ? 1024 : p->message_array_size));
*f = (tml_message*)TML_REALLOC(*f, p->message_array_size * sizeof(tml_message));
if (!*f) { TML_ERROR("Out of memory"); return -1; }
}
evt = *f + p->message_count;
//check what message we have
if ((status == TML_SYSEX) || (status == TML_EOX)) //sysex
{
//sysex messages are not handled
p->buf += tml_readvariablelength(p);
if (p->buf > p->buf_end) { TML_WARN("Unexpected end of file"); p->buf = p->buf_end; return -1; }
evt->type = 0;
}
else if (status == 0xFF) //meta events
{
int meta_type = tml_readbyte(p), buflen = tml_readvariablelength(p);
unsigned char* metadata = p->buf;
if (meta_type < 0) { TML_WARN("Unexpected end of file"); return -1; }
if (buflen > 0 && (p->buf += buflen) > p->buf_end) { TML_WARN("Unexpected end of file"); p->buf = p->buf_end; return -1; }
switch (meta_type)
{
case TML_EOT:
if (buflen != 0) { TML_WARN("Invalid length for EndOfTrack event"); return -1; }
if (!deltatime) return TML_EOT; //no need to store this message
evt->type = TML_EOT;
break;
case TML_SET_TEMPO:
if (buflen != 3) { TML_WARN("Invalid length for SetTempo meta event"); return -1; }
evt->type = TML_SET_TEMPO;
((struct tml_tempomsg*)evt)->Tempo[0] = metadata[0];
((struct tml_tempomsg*)evt)->Tempo[1] = metadata[1];
((struct tml_tempomsg*)evt)->Tempo[2] = metadata[2];
break;
default:
evt->type = 0;
}
}
else //channel message
{
int param;
if ((param = tml_readbyte(p)) < 0) { TML_WARN("Unexpected end of file"); return -1; }
evt->key = (param & 0x7f);
evt->channel = (status & 0x0f);
switch (evt->type = (status & 0xf0))
{
case TML_NOTE_OFF:
case TML_NOTE_ON:
case TML_KEY_PRESSURE:
case TML_CONTROL_CHANGE:
if ((param = tml_readbyte(p)) < 0) { TML_WARN("Unexpected end of file"); return -1; }
evt->velocity = (param & 0x7f);
break;
case TML_PITCH_BEND:
if ((param = tml_readbyte(p)) < 0) { TML_WARN("Unexpected end of file"); return -1; }
evt->pitch_bend = ((param & 0x7f) << 7) | evt->key;
break;
case TML_PROGRAM_CHANGE:
case TML_CHANNEL_PRESSURE:
evt->velocity = 0;
break;
default: //ignore system/manufacture messages
evt->type = 0;
break;
}
}
if (deltatime || evt->type)
{
evt->time = deltatime;
p->message_count++;
}
return evt->type;
}
TMLDEF tml_message* tml_load(struct tml_stream* stream)
{
int num_tracks, division, trackbufsize = 0;
unsigned char midi_header[14], *trackbuf = TML_NULL;
struct tml_message* messages = TML_NULL;
struct tml_track *tracks, *t, *tracksEnd;
struct tml_parser p = { TML_NULL, TML_NULL, 0, 0, 0 };
// Parse MIDI header
if (stream->read(stream->data, midi_header, 14) != 14) { TML_ERROR("Unexpected end of file"); return messages; }
if (midi_header[0] != 'M' || midi_header[1] != 'T' || midi_header[2] != 'h' || midi_header[3] != 'd' ||
midi_header[7] != 6 || midi_header[9] > 2) { TML_ERROR("Doesn't look like a MIDI file: invalid MThd header"); return messages; }
if (midi_header[12] & 0x80) { TML_ERROR("File uses unsupported SMPTE timing"); return messages; }
num_tracks = (int)(midi_header[10] << 8) | midi_header[11];
division = (int)(midi_header[12] << 8) | midi_header[13]; //division is ticks per beat (quarter-note)
if (num_tracks <= 0 && division <= 0) { TML_ERROR("Doesn't look like a MIDI file: invalid track or division values"); return messages; }
// Allocate temporary tracks array for parsing
tracks = (struct tml_track*)TML_MALLOC(sizeof(struct tml_track) * num_tracks);
tracksEnd = &tracks[num_tracks];
for (t = tracks; t != tracksEnd; t++) t->Idx = t->End = t->Ticks = 0;
// Read all messages for all tracks
for (t = tracks; t != tracksEnd; t++)
{
unsigned char track_header[8];
int track_length;
if (stream->read(stream->data, track_header, 8) != 8) { TML_WARN("Unexpected end of file"); break; }
if (track_header[0] != 'M' || track_header[1] != 'T' || track_header[2] != 'r' || track_header[3] != 'k')
{ TML_WARN("Invalid MTrk header"); break; }
// Get size of track data and read into buffer (allocate bigger buffer if needed)
track_length = track_header[7] | (track_header[6] << 8) | (track_header[5] << 16) | (track_header[4] << 24);
if (track_length < 0) { TML_WARN("Invalid MTrk header"); break; }
if (trackbufsize < track_length) { TML_FREE(trackbuf); trackbuf = (unsigned char*)TML_MALLOC(trackbufsize = track_length); }
if (stream->read(stream->data, trackbuf, track_length) != track_length) { TML_WARN("Unexpected end of file"); break; }
t->Idx = p.message_count;
for (p.buf_end = (p.buf = trackbuf) + track_length; p.buf != p.buf_end;)
{
int type = tml_parsemessage(&messages, &p);
if (type == TML_EOT || type < 0) break; //file end or illegal data encountered
}
if (p.buf != p.buf_end) { TML_WARN( "Track length did not match data length"); }
t->End = p.message_count;
}
TML_FREE(trackbuf);
// Change message time signature from delta ticks to actual msec values and link messages ordered by time
if (p.message_count)
{
tml_message *PrevMessage = TML_NULL, *Msg, *MsgEnd, Swap;
unsigned int ticks = 0, tempo_ticks = 0; //tick counter and value at last tempo change
int step_smallest, msec, tempo_msec = 0; //msec value at last tempo change
double ticks2time = 500000 / (1000.0 * division); //milliseconds per tick
// Loop through all messages over all tracks ordered by time
for (step_smallest = 0; step_smallest != 0x7fffffff; ticks += step_smallest)
{
step_smallest = 0x7fffffff;
msec = tempo_msec + (int)((ticks - tempo_ticks) * ticks2time);
for (t = tracks; t != tracksEnd; t++)
{
if (t->Idx == t->End) continue;
for (Msg = &messages[t->Idx], MsgEnd = &messages[t->End]; Msg != MsgEnd && t->Ticks + Msg->time == ticks; Msg++, t->Idx++)
{
t->Ticks += Msg->time;
if (Msg->type == TML_SET_TEMPO)
{
unsigned char* Tempo = ((struct tml_tempomsg*)Msg)->Tempo;
ticks2time = ((Tempo[0]<<16)|(Tempo[1]<<8)|Tempo[2])/(1000.0 * division);
tempo_msec = msec;
tempo_ticks = ticks;
}
if (Msg->type)
{
Msg->time = msec;
if (PrevMessage) { PrevMessage->next = Msg; PrevMessage = Msg; }
else { Swap = *Msg; *Msg = *messages; *messages = Swap; PrevMessage = messages; }
}
}
if (Msg != MsgEnd && t->Ticks + Msg->time > ticks)
{
int step = (int)(t->Ticks + Msg->time - ticks);
if (step < step_smallest) step_smallest = step;
}
}
}
if (PrevMessage) PrevMessage->next = TML_NULL;
else p.message_count = 0;
}
TML_FREE(tracks);
if (p.message_count == 0)
{
TML_FREE(messages);
messages = TML_NULL;
}
return messages;
}
TMLDEF tml_message* tml_load_tsf_stream(struct tsf_stream* stream)
{
return tml_load((struct tml_stream*)stream);
}
TMLDEF int tml_get_info(tml_message* Msg, int* out_used_channels, int* out_used_programs, int* out_total_notes, unsigned int* out_time_first_note, unsigned int* out_time_length)
{
int used_programs = 0, used_channels = 0, total_notes = 0;
unsigned int time_first_note = 0xffffffff, time_length = 0;
unsigned char channels[16] = { 0 }, programs[128] = { 0 };
for (;Msg; Msg = Msg->next)
{
time_length = Msg->time;
if (Msg->type == TML_PROGRAM_CHANGE && !programs[(int)Msg->program]) { programs[(int)Msg->program] = 1; used_programs++; }
if (Msg->type != TML_NOTE_ON) continue;
if (time_first_note == 0xffffffff) time_first_note = time_length;
if (!channels[Msg->channel]) { channels[Msg->channel] = 1; used_channels++; }
total_notes++;
}
if (time_first_note == 0xffffffff) time_first_note = 0;
if (out_used_channels ) *out_used_channels = used_channels;
if (out_used_programs ) *out_used_programs = used_programs;
if (out_total_notes ) *out_total_notes = total_notes;
if (out_time_first_note) *out_time_first_note = time_first_note;
if (out_time_length ) *out_time_length = time_length;
return total_notes;
}
TMLDEF int tml_get_tempo_value(tml_message* msg)
{
unsigned char* Tempo;
if (!msg || msg->type != TML_SET_TEMPO) return 0;
Tempo = ((struct tml_tempomsg*)msg)->Tempo;
return ((Tempo[0]<<16)|(Tempo[1]<<8)|Tempo[2]);
}
TMLDEF void tml_free(tml_message* f)
{
TML_FREE(f);
}
#ifdef __cplusplus
}
#endif
#endif //TML_IMPLEMENTATION

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,63 @@
//----------------------------------------------------------------------------------------------------
// ___ ___ __ _ _ ___ ___ _ _ _ ___ _
// / _ \| _ ) / /| | || _ \ __| /_\ _ _ __| (_)___ | __|_ _ __ _(_)_ _ ___
// | (_) | _ \/ _ \_ _| _/ _| / _ \ || / _` | / _ \ | _|| ' \/ _` | | ' \/ -_)
// \__\_\___/\___/ |_||_| |___| /_/ \_\_,_\__,_|_\___/ |___|_||_\__, |_|_||_\___|
// |___/
//
// QB64-PE Audio Engine powered by miniaudio (https://miniaud.io/)
//
// Copyright (c) 2022 Samuel Gomes
// https://github.com/a740g
//
//-----------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------
// HEADER FILES
//-----------------------------------------------------------------------------------------------------
// Enable Ogg Vorbis decoding
#define STB_VORBIS_HEADER_ONLY
#include "extras/stb_vorbis.c"
// PulseAudio has serious stuttering issues in ChromeOS Linux (Crostini) and possibly others
// This may be due to this - https://github.com/mackron/miniaudio/issues/427
// And https://wiki.archlinux.org/title/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling
// We'll have to look at this closely later. If this is fixed, then remove this define from here & audio.cpp
#define MA_NO_PULSEAUDIO
// The main miniaudio header
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
// The stb_vorbis implementation must come after the implementation of miniaudio
#undef STB_VORBIS_HEADER_ONLY
#include "extras/stb_vorbis.c"
// RADv2 format support
#include "extras/miniaudio_radv2.h"
// MIDI format support
// #include "extras/miniaudio_tinysoundfont.h"
// MOD, S3M, XM & IT support
#include "extras/miniaudio_libxmp-lite.h"
//-----------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------
// GLOBAL VARIABLES
//-----------------------------------------------------------------------------------------------------
// Add custom backend (format) vtables here
// The order in the array defines the order of priority
// The vtables will be passed in to the resource manager config
ma_decoding_backend_vtable *maCustomBackendVTables[] = {&ma_decoding_backend_vtable_radv2, // &ma_decoding_backend_vtable_tsf,
&ma_decoding_backend_vtable_modplay};
//-----------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------
// FUNCTIONS
//-----------------------------------------------------------------------------------------------------
/// <summary>
/// This simply attaches the format decode VTables array to ma_resource_manager_config
/// </summary>
/// <param name="maResourceManagerConfig">Pointer to a miniaudio resource manager config object. This cannot be NULL</param>
void AudioEngineAttachCustomBackendVTables(ma_resource_manager_config *maResourceManagerConfig) {
// Attach the VTable
maResourceManagerConfig->ppCustomDecodingBackendVTables = maCustomBackendVTables;
maResourceManagerConfig->customDecodingBackendCount = ma_countof(maCustomBackendVTables);
}
//-----------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------

View file

@ -0,0 +1,28 @@
//----------------------------------------------------------------------------------------------------
// ___ ___ __ _ _ ___ ___ _ _ _ ___ _
// / _ \| _ ) / /| | || _ \ __| /_\ _ _ __| (_)___ | __|_ _ __ _(_)_ _ ___
// | (_) | _ \/ _ \_ _| _/ _| / _ \ || / _` | / _ \ | _|| ' \/ _` | | ' \/ -_)
// \__\_\___/\___/ |_||_| |___| /_/ \_\_,_\__,_|_\___/ |___|_||_\__, |_|_||_\___|
// |___/
//
// QB64-PE Audio Engine powered by miniaudio (https://miniaud.io/)
//
// Copyright (c) 2022 Samuel Gomes
// https://github.com/a740g
//
//-----------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------
// HEADER FILES
//-----------------------------------------------------------------------------------------------------
#include "audio.h"
//-----------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------
// FUNCTIONS
//-----------------------------------------------------------------------------------------------------
void snd_mainloop() { return; }
void snd_init() { return; }
void snd_un_init() { return; }
//-----------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------

View file

@ -1,102 +0,0 @@
=== IMPORTANT LICENSING NOTE FOR QB64 PROGRAMMERS ===
All executables which perform any kind of sound operation are subject to the LGPL license (due to incorporation of mpglibdll and OpenAL).
Other components are licensed under various permissive licences.
When sound components are included (thus the LGPL is in effect), the easiest way to meet terms of the LGPL is to make your program's source code (.BAS) available.
If you are not using sound components, you do not need to release the program's source.
If you are using fonts, you are bound by the terms of FreeType's license. Somewhere in your software package should include a notice that your program includes the FreeType library (see licence_freetype_ftl.txt for details)
In all cases, you should distribute the licenses folder with your program.
It should be noted that providing source code is not the only way to meet the conditions of the LGPL (eg dynamic linking) but it is by far the easiest from a technical point of view at this current time.
=====================================================
The license requirements for components of QB64 are as follows:
Software/Library Name: QB64 Phoenix Edition (inclusive of all parts of this distribution not covered by separate licence below)
Website(s): https://qb64phoenix.com
License: MIT
License Website: https://opensource.org/licenses/MIT
License:
Copyright 2007-2020 Galleon & The QB64 Team
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.
Software/Library Name: MinGW 64
Website: http://mingw-w64.sourceforge.net/
License: (see below)
Each of the various packages, which is distributed by MinGW.org, is governed by its own individual copyright and licensing terms. In summary, for the most commonly deployed packages:
MinGW runtime: The MinGW base runtime package has been placed in the public domain, and is not governed by copyright. This basically means that you can do what you like with the code.
w32api: You are free to use, modify and copy this package. No restrictions are imposed on programs or object files linked with this library. You may not restrict the the usage of this library. You may distribute this library as part of another package or as a modified package if, and only if, you do not restrict the usage of the portions consisting of this (optionally modified) library. If distributed as a modified package, then a copy of this notice must be included.
This library is distributed in the hope that it will be useful, but WITHOUT WARRANTY OF ANY KIND; without even the implied warranties of MERCHANTABILITY or of FITNESS FOR A PARTICULAR PURPOSE.
MinGW profiling code: MinGW profiling code is distributed under the terms of the GNU General Public License.
Binutils, GCC, GDB, GNU Make: All of the GNU development tools, such as GNU binutils, GCC, GDB and GNU Make, are governed by the terms of the GNU General Public License.
License Website: http://www.gnu.org/licenses/licenses.html#GPL
License File: license_gnu_gpl_3.txt
Location in QB64 distribution: internal/c/c_compiler/
Software/Library Name: Opus Tools
Website: http://www.opus-codec.org/
License: BSD 2-clause license
License Website: http://opensource.org/licenses/BSD-2-Clause
License File: license_opus.txt
Location in QB64 distribution: internal/c/parts/audio/conversion/
OpusInfo, which is under a GPL licence, was included in Opus Tools but has been removed from the QB64 distribution.
Software/Library Name: mpglibdll
Website(s):
http://www.rz.uni-frankfurt.de/~pesch
http://www.mpg123.de
http://www.sulaco.org/mp3
License: LGPL 2.1
License Website: http://www.gnu.org/licenses/licenses.html#LGPL
License File: license_gnu_lgpl_2_1.txt
Location in QB64 distribution: internal/c/parts/audio/decode/mp3/
Software/Library Name: Ogg Vorbis I audio decoder version 1.05
Website:http://nothings.org/stb_vorbis/
Date:Written in April 2007
Author:Sean Barrett, sponsored by RAD Game Tools
License: Placed in the public domain April 2007 by the author: no copyright is claimed, and you may use it for any purpose you like.
License Website: N/A - public domain
License File: license_stbvorbis.txt
Location in QB64 distribution: internal/c/parts/audio/decode/ogg/
Software/Library Name: OpenAL-soft
Website:http://kcat.strangesoft.net/openal.html
License: LGPL 2
License Website: http://www.gnu.org/licenses/licenses.html#LGPL
License File: license_gnu_lgpl_2.txt
Location in QB64 distribution: internal/c/parts/audio/out/
Software/Library Name: FreeGLUT
Website: http://freeglut.sourceforge.net/
License: LGPL (note: website states LGPL, license file is not a copy of GNU LGPL)
License Website: http://www.gnu.org/licenses/licenses.html#LGPL
License File: license_freeglut.txt
Location in QB64 distribution: internal/c/parts/core/
Software/Library Name: FreeTYPE
Website: http://www.freetype.org/
License: GPL or FreeTYPE's FTL (programs must abide by one of these licenses)
License Website(s): (see below)
http://www.freetype.org/license.html
http://www.gnu.org/licenses/licenses.html#GPL
License File(s): (see below)
license_freetype_ftl.txt
license_gnu_gpl_2.txt
Location in QB64 distribution: internal/c/parts/video/font/ttf/
Software/Library Name: NanoJPEG - KeyJ's Tiny Baseline JPEG Decoder
Author: Martin J. Fiedler <martin.fiedler@gmx.net>
License: MIT
License File: license_nanojpeg.txt
Location in QB64 distribution: internal/c/parts/video/image/decode/jpg
Software/Library Name: LodePNG
License: (refer to license file)
License File: license_lodepng.txt
Location in QB64 distribution: internal/c/parts/video/image/decode/png

103
licenses/COPYING.md Normal file
View file

@ -0,0 +1,103 @@
QB64 Phoenix Edition Licensing information
==========================================
QB64-PE makes extensive use of third party libraries to provide
functionality. These third party libraries have their own licenses that you
must respect when distributing any programs compiled by QB64-PE.
As a general note, almost third party libraries used by QB64-PE are either MIT,
Public Domain, or some other permissive license. Meeting their requirements can
be done by simply distributing the licenses in the `./licenses` folder with
your compiled program.
A few of the libraries are LGPL and require more careful handling to meet their
license requirements (either by providing source code or object files before
linking). Those are noted on this page and avoidable. Note that QB64-PE does
not give the option of using dynamic linking, all third party libraries are
statically linked.
Additionally, QB64-PE contains logic to avoid compiling in third party libraries
if they are not used by the program, those situations are noted on this page.
If a component is not compiled into your program then you do not need to meet
its license requirements.
## QB64 Phoenix Edition Runtime
This is the licensing of the provided QB64-PE runtime that compiled programs make use of.
| Library | License | License file | Location |
| :------ | :-----: | :----------- | :------- |
| libqb | MIT | license_qb64.txt | internal/c/libqb.cpp, internal/c/libqb/, internal/c/qbx.cpp |
## Windows C and C++ Runtime
On Windows MinGW-w64 is used to compiled the C++ code produced by QB64-PE, and some runtime components are compiled into your code. On Linux and Mac OS this section does not apply.
| Library | License | License file | Location |
| :------ | :-----: | :----------- | :------- |
| MinGW-w64 C and C++ runtime | Various Permissive Licenses | license_mingw-base-runtime.txt | internal/c/c_compiler |
| libstdc++ | GPLv3 with Exception | license_libstdc++.txt | internal/c/c_compiler |
## Display Support
This is always used unless you use `$CONSOLE:ONLY`. On Mac OS the system's own GLUT implementation is used rather than `FreeGLUT`.
| Library | License | License file | Location |
| :------ | :-----: | :----------- | :------- |
| FreeGLUT | MIT | license_freeglut.txt | internal/c/parts/core |
## Image Support
These libraries are pulled in if `_LOADIMAGE()` functionality is used.
| Library | License | License file | Location |
| :------ | :-----: | :----------- | :------- |
| NanoJPEG | MIT | license_nanojpeg.txt | internal/c/parts/video/image/decode/jpg |
| LodePNG | ZLIB | license_lodepng.txt | internal/c/parts/video/image/decode/png |
## Font Support
These libraries are pulled in if `_LOADFONT()` functionality is used.
| Library | License | License file | Location |
| :------ | :-----: | :----------- | :------- |
| FreeTYPE | FLT | license_freetype_ftl.txt | internal/c/parts/video/font/tff/ |
## Compression Support
These libraries are pulled in if `_INFLATE$()` or `_DEFLATE$()` are used.
| Library | License | License file | Location |
| :------ | :-----: | :----------- | :------- |
| ZLib | ZLIB | license_zlib.txt | internal/c/c_compiler (provide by MinGW on Windows, system library otherwise) |
## Sound Support
These libraries are pulled in when using any sound-related functionality.
| Library | License | License file | Location |
| :------ | :-----: | :----------- | :------- |
| Miniaudio | MIT | license_miniaudio.txt | internal/c/parts/audio/miniaudio.h |
| libxmp-lite | MIT | license_libxmp-lite.txt | internal/c/parts/audio/extras/libxmp-lite/ |
| RADv2 | Public Domain | license_radv2.txt | internal/c/parts/audio/extras/radv2/ |
| std_vorbus | Public Domain | license_stdvorbis.txt | internal/c/parts/audio/extras/std_vorbis.c |
## MIDI Support
These are used if you make use of MIDI support.
| Library | License | License file | Location |
| :------ | :-----: | :----------- | :------- |
| TinySoundFont | MIT | license_tinysoundfont.txt | internal/c/parts/audio/extras/tinysoundfont/tsf.h
| TinyMidiLoader | ZLIB | license_tinymidiloader.txt | internal/c/parts/audio/extras/tinysoundfont.tml.h |
## Legacy OpenAL audio backend
The below licenses apply when making use of the legacy OpenAL audio backend (can be enabled in `Compiler Settings`). These replace all other sound related libraries:
| Library | License | License file | Location |
| :------ | :-----: | :----------- | :------- |
| mpg123 | LGPL 2.1 | license_mpg123.txt | internal/c/parts/audio/decode/mp3/ |
| OpenAL-soft | LGPL 2 | license_openal.txt | internal/c/parts/audio/out/ |
| Opus Tools | BSD 2-clause | license_opus.txt | internal/c/parts/audio/conversion/ |
| stb_vorbis | Public Domain | license_stdvorbis.txt | internal/c/parts/audio/decode/ogg/ |

View file

@ -1,340 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

View file

@ -1,674 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View file

@ -0,0 +1,72 @@
GCC RUNTIME LIBRARY EXCEPTION
Version 3.1, 31 March 2009
Copyright (C) 2009 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
This GCC Runtime Library Exception ("Exception") is an additional
permission under section 7 of the GNU General Public License, version
3 ("GPLv3"). It applies to a given file (the "Runtime Library") that
bears a notice placed by the copyright holder of the file stating that
the file is governed by GPLv3 along with this Exception.
When you use GCC to compile a program, GCC may combine portions of
certain GCC header files and runtime libraries with the compiled
program. The purpose of this Exception is to allow compilation of
non-GPL (including proprietary) programs to use, in this way, the
header files and runtime libraries covered by this Exception.
0. Definitions.
A file is an "Independent Module" if it either requires the Runtime
Library for execution after a Compilation Process, or makes use of an
interface provided by the Runtime Library, but is not otherwise based
on the Runtime Library.
"GCC" means a version of the GNU Compiler Collection, with or without
modifications, governed by version 3 (or a specified later version) of
the GNU General Public License (GPL) with the option of using any
subsequent versions published by the FSF.
"GPL-compatible Software" is software whose conditions of propagation,
modification and use would permit combination with GCC in accord with
the license of GCC.
"Target Code" refers to output from any compiler for a real or virtual
target processor architecture, in executable form or suitable for
input to an assembler, loader, linker and/or execution
phase. Notwithstanding that, Target Code does not include data in any
format that is used as a compiler intermediate representation, or used
for producing a compiler intermediate representation.
The "Compilation Process" transforms code entirely represented in
non-intermediate languages designed for human-written code, and/or in
Java Virtual Machine byte code, into Target Code. Thus, for example,
use of source code generators and preprocessors need not be considered
part of the Compilation Process, since the Compilation Process can be
understood as starting with the output of the generators or
preprocessors.
A Compilation Process is "Eligible" if it is done using GCC, alone or
with other GPL-compatible software, or if it is done without using any
work based on GCC. For example, using non-GPL-compatible Software to
optimize any GCC intermediate representations would not qualify as an
Eligible Compilation Process.
1. Grant of Additional Permission.
You have permission to propagate a work of Target Code formed by
combining the Runtime Library with Independent Modules, even if such
propagation would otherwise violate the terms of GPLv3, provided that
all Target Code was generated by Eligible Compilation Processes. You
may then convey such a combination under terms of your choice,
consistent with the licensing of the Independent Modules.
2. No Weakening of GCC Copyleft.
The availability of this Exception does not imply any general
presumption that third-party software is unaffected by the copyleft
requirements of the license of GCC.

View file

@ -0,0 +1,42 @@
__ _ __ ___ __
/ / (_) / __ __ __ _ ___ ____/ (_) /____
/ /__/ / _ \\ \ // ' \/ _ \/___/ / / __/ -_)
/____/_/_.__/_\_\/_/_/_/ .__/ /_/_/\__/\__/
/_/
Libxmp-lite is a lean and lightweight subset of Libxmp that plays MOD, S3M,
XM, and IT modules and retains full compatibility with the original API.
It's intended for games and small or embedded applications where module
format diversity and file depacking are not required.
Library size can be further reduced by disabling Impulse Tracker format
support (configure with --disable-it). This option will also disable IT
effects and lowpass filtering.
Please refer to http://xmp.sf.net/libxmp.html for details on the current
Libxmp API.
LICENSE
Extended Module Player Lite
Copyright (C) 1996-2021 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.

View file

@ -0,0 +1,240 @@
MinGW-w64 runtime licensing
***************************
This program or library was built using MinGW-w64 and statically
linked against the MinGW-w64 runtime. Some parts of the runtime
are under licenses which require that the copyright and license
notices are included when distributing the code in binary form.
These notices are listed below.
========================
Overall copyright notice
========================
Copyright (c) 2009, 2010, 2011, 2012, 2013 by the mingw-w64 project
This license has been certified as open source. It has also been designated
as GPL compatible by the Free Software Foundation (FSF).
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions in source code must retain the accompanying copyright
notice, this list of conditions, and the following disclaimer.
2. Redistributions in binary form must reproduce the accompanying
copyright notice, this list of conditions, and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
3. Names of the copyright holders must not be used to endorse or promote
products derived from this software without prior written permission
from the copyright holders.
4. The right to distribute this software or to use it for any purpose does
not give you the right to use Servicemarks (sm) or Trademarks (tm) of
the copyright holders. Use of them is covered by separate agreement
with the copyright holders.
5. If any files are modified, you must cause the modified files to carry
prominent notices stating that you changed the files and the date of
any change.
Disclaimer
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
========================================
getopt, getopt_long, and getop_long_only
========================================
Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Sponsored in part by the Defense Advanced Research Projects
Agency (DARPA) and Air Force Research Laboratory, Air Force
Materiel Command, USAF, under agreement number F39502-99-1-0512.
* * * * * * *
Copyright (c) 2000 The NetBSD Foundation, Inc.
All rights reserved.
This code is derived from software contributed to The NetBSD Foundation
by Dieter Baron and Thomas Klausner.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
===============================================================
gdtoa: Converting between IEEE floating point numbers and ASCII
===============================================================
The author of this software is David M. Gay.
Copyright (C) 1997, 1998, 1999, 2000, 2001 by Lucent Technologies
All Rights Reserved
Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appear in all
copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name of Lucent or any of its entities
not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior
permission.
LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
* * * * * * *
The author of this software is David M. Gay.
Copyright (C) 2005 by David M. Gay
All Rights Reserved
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that the copyright notice and this permission notice and warranty
disclaimer appear in supporting documentation, and that the name of
the author or any of his current or former employers not be used in
advertising or publicity pertaining to distribution of the software
without specific, written prior permission.
THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
NO EVENT SHALL THE AUTHOR OR ANY OF HIS CURRENT OR FORMER EMPLOYERS BE
LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
* * * * * * *
The author of this software is David M. Gay.
Copyright (C) 2004 by David M. Gay.
All Rights Reserved
Based on material in the rest of /netlib/fp/gdota.tar.gz,
which is copyright (C) 1998, 2000 by Lucent Technologies.
Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appear in all
copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name of Lucent or any of its entities
not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior
permission.
LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
=========================
Parts of the math library
=========================
Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
Developed at SunSoft, a Sun Microsystems, Inc. business.
Permission to use, copy, modify, and distribute this
software is freely granted, provided that this notice
is preserved.
* * * * * * *
Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
Developed at SunPro, a Sun Microsystems, Inc. business.
Permission to use, copy, modify, and distribute this
software is freely granted, provided that this notice
is preserved.
* * * * * * *
FIXME: Cephes math lib
Copyright (C) 1984-1998 Stephen L. Moshier
It sounds vague, but as to be found at
<http://lists.debian.org/debian-legal/2004/12/msg00295.html>, it gives an
impression that the author could be willing to give an explicit
permission to distribute those files e.g. under a BSD style license. So
probably there is no problem here, although it could be good to get a
permission from the author and then add a license into the Cephes files
in MinGW runtime. At least on follow-up it is marked that debian sees the
version a-like BSD one. As MinGW.org (where those cephes parts are coming
from) distributes them now over 6 years, it should be fine.
===================================
Headers and IDLs imported from Wine
===================================
Some header and IDL files were imported from the Wine project. These files
are prominent maked in source. Their copyright belongs to contributors and
they are distributed under LGPL license.
Disclaimer
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

View file

@ -0,0 +1,47 @@
This software is available as a choice of the following licenses. Choose
whichever you prefer.
===============================================================================
ALTERNATIVE 1 - Public Domain (www.unlicense.org)
===============================================================================
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
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 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.
For more information, please refer to <http://unlicense.org/>
===============================================================================
ALTERNATIVE 2 - MIT No Attribution
===============================================================================
Copyright 2020 David Reid
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.
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.

View file

@ -1,17 +1,15 @@
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
@ -53,7 +51,7 @@ library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original
version, so that any problems introduced by others will not reflect on
the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that companies distributing free
software will individually obtain patent licenses, thus in effect
@ -100,8 +98,8 @@ works together with the library.
Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one.
GNU LIBRARY GENERAL PUBLIC LICENSE
GNU LIBRARY GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which
@ -147,7 +145,7 @@ Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
@ -205,7 +203,7 @@ instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
@ -256,7 +254,7 @@ Library will still fall under Section 6.)
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
@ -310,7 +308,7 @@ restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
@ -351,7 +349,7 @@ subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
@ -403,7 +401,7 @@ conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
@ -413,7 +411,7 @@ decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
@ -436,49 +434,4 @@ FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
END OF TERMS AND CONDITIONS

View file

@ -0,0 +1,8 @@
Copyright 2007-2022 Galleon & The QB64 Team
Copyright 2022-2023 QB64 Phoenix Edition Team
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.

View file

@ -0,0 +1,23 @@
From: Willy Reeve <shayde0@gmail.com>
Sent: Sunday, 7 August, 2022 01:23 PM
To: Samuel Gomes <v_2samg@hotmail.com>
Cc: Carl Pettitt <carl@clonestudios.co.uk>
Subject: Re: Contact from Reality website
Hi Samuel,
The player source code is Public Domain.
Shayde
On Fri, Aug 5, 2022 at 6:33 AM Reality website <rogue@3eality.com> wrote:
Contact from the Reality website:
Name: Samuel Gomes
Email: v_2samg@hotmail.com
Message:
RADv2 - great stuff! I am planning to integrate the included player code in my projects. Can you please
let me know the license type for the player code? If there is one, putting it somewhere on the site or
zip would be fantastic!
Thanks!

View file

@ -1,11 +1,37 @@
// Ogg Vorbis audio decoder - v1.05 - public domain
// http://nothings.org/stb_vorbis/
//
// Written by Sean Barrett in 2007, last updated in 2014
// Sponsored by RAD Game Tools.
//
// Placed in the public domain April 2007 by the author: no copyright
// is claimed, and you may use it for any purpose you like.
//
// No warranty for any purpose is expressed or implied by the author (nor
// by RAD Game Tools). Report bugs and send enhancements to the author.
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
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.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
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 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.

View file

@ -0,0 +1,17 @@
Copyright (C) 2017, 2018, 2020 Bernhard Schelling
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

View file

@ -0,0 +1,19 @@
Copyright (C) 2017-2018 Bernhard Schelling (Based on SFZero, Copyright (C) 2012 Steve Folta, https://github.com/stevefolta/SFZero)
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.

View file

@ -25,6 +25,7 @@ DIM SHARED MaxParallelProcesses AS _UNSIGNED LONG
DIM SHARED ExtraCppFlags AS STRING, ExtraLinkerFlags AS STRING
DIM SHARED StripDebugSymbols AS _UNSIGNED LONG
DIM SHARED OptimizeCppProgram AS _UNSIGNED LONG
DIM SHARED UseMiniaudioBackend AS _UNSIGNED LONG
ConfigFile$ = "internal/config.ini"
iniFolderIndex$ = STR$(tempfolderindex)
@ -535,5 +536,7 @@ MaxParallelProcesses = ReadWriteLongSettingValue&(compilerSettingsSection$, "Max
ExtraCppFlags = ReadWriteStringSettingValue$(compilerSettingsSection$, "ExtraCppFlags", "")
ExtraLinkerFlags = ReadWriteStringSettingValue$(compilerSettingsSection$, "ExtraLinkerFlags", "")
UseMiniaudioBackend = ReadWriteBooleanSettingValue%(compilerSettingsSection$, "UseMiniaudioBackend", -1)
'End of initial settings ------------------------------------------------------

View file

@ -399,8 +399,8 @@ FUNCTION ide2 (ignore)
menuDesc$(m, i - 1) = "Changes or customizes IDE color scheme"
menu$(m, i) = "#Code Layout...": i = i + 1
menuDesc$(m, i - 1) = "Changes auto-format features"
menu$(m, i) = "C++ Co#mpiler Settings...": i = i + 1
menuDesc$(m, i - 1) = "Change settings for compiling the C++ code"
menu$(m, i) = "Co#mpiler Settings...": i = i + 1
menuDesc$(m, i - 1) = "Change settings for compiling your code"
menu$(m, i) = "-": i = i + 1
menu$(m, i) = "#Language...": i = i + 1
menuDesc$(m, i - 1) = "Changes code page to use with TTF fonts"
@ -5103,7 +5103,7 @@ FUNCTION ide2 (ignore)
GOTO ideloop
END IF
IF menu$(m, s) = "C++ Co#mpiler Settings..." THEN
IF menu$(m, s) = "Co#mpiler Settings..." THEN
PCOPY 2, 0
retval = ideCompilerSettingsBox
IF retval THEN idechangemade = 1: idelayoutallow = 2: startPausedPending = 0 'recompile if options changed
@ -15123,6 +15123,7 @@ FUNCTION ideCompilerSettingsBox
DIM maxParallelTextBox AS LONG
DIM extraCppFlagsTextBox AS LONG
DIM extraLinkerFlagsTextBox AS LONG
DIM useOldAudioBackend AS LONG
sep = CHR$(0)
'-------- end of generic dialog box header --------
@ -15185,6 +15186,15 @@ FUNCTION ideCompilerSettingsBox
y = y + 1 ' Blank line
i = i + 1
useOldAudioBackend = i
o(i).typ = 4 'check box
y = y + 1: o(i).y = y
o(i).nam = idenewtxt("#Use old audio backend (LGPL)")
o(i).sel = NOT UseMiniaudioBackend
y = y + 1 ' Blank line
i = i + 1
buttonsid = i
o(i).typ = 3
@ -15192,7 +15202,7 @@ FUNCTION ideCompilerSettingsBox
o(i).txt = idenewtxt("#OK" + sep + "#Cancel")
o(i).dft = 1
idepar p, 60, y, "C++ Compiler Settings"
idepar p, 60, y, "Compiler Settings"
'-------- end of init --------
'-------- generic init --------
@ -15289,6 +15299,12 @@ FUNCTION ideCompilerSettingsBox
WriteConfigSetting generalSettingsSection$, "DebugInfo", BoolToTFString$(Include_GDB_Debugging_Info)
END IF
v% = o(useOldAudioBackend).sel: IF v% <> 0 THEN v% = -1
IF UseMiniaudioBackend <> NOT v% THEN
UseMiniaudioBackend = NOT v%
WriteConfigSetting compilerSettingsSection$, "UseMiniaudioBackend", BoolToTFString$(UseMiniaudioBackend)
END IF
v% = VAL(idetxt(o(maxParallelTextBox).txt))
IF v% > 0 AND v% <> MaxParallelProcesses THEN
MaxParallelProcesses = v%

View file

@ -12366,15 +12366,21 @@ IF DEPENDENCY(DEPENDENCY_ICON) THEN makedeps$ = makedeps$ + " DEP_ICON=y"
IF DEPENDENCY(DEPENDENCY_SCREENIMAGE) THEN makedeps$ = makedeps$ + " DEP_SCREENIMAGE=y"
IF DEPENDENCY(DEPENDENCY_LOADFONT) THEN makedeps$ = makedeps$ + " DEP_FONT=y"
IF DEPENDENCY(DEPENDENCY_DEVICEINPUT) THEN makedeps$ = makedeps$ + " DEP_DEVICEINPUT=y"
IF DEPENDENCY(DEPENDENCY_AUDIO_DECODE) THEN makedeps$ = makedeps$ + " DEP_AUDIO_DECODE=y"
IF DEPENDENCY(DEPENDENCY_AUDIO_CONVERSION) THEN makedeps$ = makedeps$ + " DEP_AUDIO_CONVERSION=y"
IF DEPENDENCY(DEPENDENCY_AUDIO_DECODE) THEN makedeps$ = makedeps$ + " DEP_AUDIO_DECODE=y"
IF DEPENDENCY(DEPENDENCY_AUDIO_OUT) THEN makedeps$ = makedeps$ + " DEP_AUDIO_OUT=y"
IF DEPENDENCY(DEPENDENCY_ZLIB) THEN makedeps$ = makedeps$ + " DEP_ZLIB=y"
IF inline_DATA = 0 AND DataOffset THEN makedeps$ = makedeps$ + " DEP_DATA=y"
IF Console THEN makedeps$ = makedeps$ + " DEP_CONSOLE=y"
IF ExeIconSet OR VersionInfoSet THEN makedeps$ = makedeps$ + " DEP_ICON_RC=y"
IF UseMiniaudioBackend = 0 THEN
IF DEPENDENCY(DEPENDENCY_AUDIO_DECODE) THEN makedeps$ = makedeps$ + " DEP_AUDIO_DECODE=y"
IF DEPENDENCY(DEPENDENCY_AUDIO_CONVERSION) THEN makedeps$ = makedeps$ + " DEP_AUDIO_CONVERSION=y"
IF DEPENDENCY(DEPENDENCY_AUDIO_OUT) THEN makedeps$ = makedeps$ + " DEP_AUDIO_OUT=y"
ELSE
IF DEPENDENCY(DEPENDENCY_AUDIO_DECODE) OR DEPENDENCY(DEPENDENCY_AUDIO_CONVERSION) OR DEPENDENCY(DEPENDENCY_AUDIO_OUT) THEN
makedeps$ = makedeps$ + " DEP_AUDIO_MINIAUDIO=y"
END IF
END IF
IF tempfolderindex > 1 THEN makedeps$ = makedeps$ + " TEMP_ID=" + str2$(tempfolderindex)
CxxFlagsExtra$ = ExtraCppFlags