From d715918e45d37a1a5268216ec713fbd9badfedcd Mon Sep 17 00:00:00 2001 From: hendricks266 Date: Fri, 9 Jun 2017 06:39:37 +0000 Subject: [PATCH] Add unmodified libxmp-lite sources from its Git repository. git-svn-id: https://svn.eduke32.com/eduke32@6158 1a8010ca-5511-0410-912e-c29ae57300e0 --- source/libxmp-lite/README | 42 + source/libxmp-lite/include/libxmp-lite/xmp.h | 360 ++++ source/libxmp-lite/src/common.c | 366 ++++ source/libxmp-lite/src/common.h | 437 ++++ source/libxmp-lite/src/control.c | 529 +++++ source/libxmp-lite/src/dataio.c | 269 +++ source/libxmp-lite/src/effects.c | 1069 ++++++++++ source/libxmp-lite/src/effects.h | 143 ++ source/libxmp-lite/src/filter.c | 107 + source/libxmp-lite/src/format.c | 62 + source/libxmp-lite/src/format.h | 25 + source/libxmp-lite/src/hio.c | 380 ++++ source/libxmp-lite/src/hio.h | 42 + source/libxmp-lite/src/it.h | 181 ++ source/libxmp-lite/src/it_load.c | 1404 +++++++++++++ source/libxmp-lite/src/itsex.c | 232 +++ source/libxmp-lite/src/lfo.c | 164 ++ source/libxmp-lite/src/lfo.h | 20 + source/libxmp-lite/src/list.h | 146 ++ source/libxmp-lite/src/load.c | 690 +++++++ source/libxmp-lite/src/load_helpers.c | 425 ++++ source/libxmp-lite/src/loader.h | 64 + source/libxmp-lite/src/mdataio.h | 105 + source/libxmp-lite/src/memio.c | 136 ++ source/libxmp-lite/src/memio.h | 31 + source/libxmp-lite/src/mix_all.c | 439 ++++ source/libxmp-lite/src/mixer.c | 840 ++++++++ source/libxmp-lite/src/mixer.h | 78 + source/libxmp-lite/src/mod.h | 55 + source/libxmp-lite/src/mod_load.c | 232 +++ source/libxmp-lite/src/period.c | 266 +++ source/libxmp-lite/src/period.h | 24 + source/libxmp-lite/src/player.c | 1899 ++++++++++++++++++ source/libxmp-lite/src/player.h | 258 +++ source/libxmp-lite/src/precomp_lut.h | 524 +++++ source/libxmp-lite/src/read_event.c | 1613 +++++++++++++++ source/libxmp-lite/src/s3m.h | 116 ++ source/libxmp-lite/src/s3m_load.c | 660 ++++++ source/libxmp-lite/src/sample.c | 417 ++++ source/libxmp-lite/src/scan.c | 549 +++++ source/libxmp-lite/src/smix.c | 322 +++ source/libxmp-lite/src/tempfile.h | 7 + source/libxmp-lite/src/virtual.c | 608 ++++++ source/libxmp-lite/src/virtual.h | 38 + source/libxmp-lite/src/xm.h | 101 + source/libxmp-lite/src/xm_load.c | 773 +++++++ 46 files changed, 17248 insertions(+) create mode 100644 source/libxmp-lite/README create mode 100644 source/libxmp-lite/include/libxmp-lite/xmp.h create mode 100644 source/libxmp-lite/src/common.c create mode 100644 source/libxmp-lite/src/common.h create mode 100644 source/libxmp-lite/src/control.c create mode 100644 source/libxmp-lite/src/dataio.c create mode 100644 source/libxmp-lite/src/effects.c create mode 100644 source/libxmp-lite/src/effects.h create mode 100644 source/libxmp-lite/src/filter.c create mode 100644 source/libxmp-lite/src/format.c create mode 100644 source/libxmp-lite/src/format.h create mode 100644 source/libxmp-lite/src/hio.c create mode 100644 source/libxmp-lite/src/hio.h create mode 100644 source/libxmp-lite/src/it.h create mode 100644 source/libxmp-lite/src/it_load.c create mode 100644 source/libxmp-lite/src/itsex.c create mode 100644 source/libxmp-lite/src/lfo.c create mode 100644 source/libxmp-lite/src/lfo.h create mode 100644 source/libxmp-lite/src/list.h create mode 100644 source/libxmp-lite/src/load.c create mode 100644 source/libxmp-lite/src/load_helpers.c create mode 100644 source/libxmp-lite/src/loader.h create mode 100644 source/libxmp-lite/src/mdataio.h create mode 100644 source/libxmp-lite/src/memio.c create mode 100644 source/libxmp-lite/src/memio.h create mode 100644 source/libxmp-lite/src/mix_all.c create mode 100644 source/libxmp-lite/src/mixer.c create mode 100644 source/libxmp-lite/src/mixer.h create mode 100644 source/libxmp-lite/src/mod.h create mode 100644 source/libxmp-lite/src/mod_load.c create mode 100644 source/libxmp-lite/src/period.c create mode 100644 source/libxmp-lite/src/period.h create mode 100644 source/libxmp-lite/src/player.c create mode 100644 source/libxmp-lite/src/player.h create mode 100644 source/libxmp-lite/src/precomp_lut.h create mode 100644 source/libxmp-lite/src/read_event.c create mode 100644 source/libxmp-lite/src/s3m.h create mode 100644 source/libxmp-lite/src/s3m_load.c create mode 100644 source/libxmp-lite/src/sample.c create mode 100644 source/libxmp-lite/src/scan.c create mode 100644 source/libxmp-lite/src/smix.c create mode 100644 source/libxmp-lite/src/tempfile.h create mode 100644 source/libxmp-lite/src/virtual.c create mode 100644 source/libxmp-lite/src/virtual.h create mode 100644 source/libxmp-lite/src/xm.h create mode 100644 source/libxmp-lite/src/xm_load.c diff --git a/source/libxmp-lite/README b/source/libxmp-lite/README new file mode 100644 index 000000000..c309e6b08 --- /dev/null +++ b/source/libxmp-lite/README @@ -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-2016 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. + diff --git a/source/libxmp-lite/include/libxmp-lite/xmp.h b/source/libxmp-lite/include/libxmp-lite/xmp.h new file mode 100644 index 000000000..52a8257e4 --- /dev/null +++ b/source/libxmp-lite/include/libxmp-lite/xmp.h @@ -0,0 +1,360 @@ +#ifndef XMP_H +#define XMP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define XMP_VERSION "4.4.2" +#define XMP_VERCODE 0x040402 +#define XMP_VER_MAJOR 4 +#define XMP_VER_MINOR 4 +#define XMP_VER_RELEASE 2 + +#if defined(_WIN32) && !defined(__CYGWIN__) +# if defined(BUILDING_STATIC) +# define EXPORT +# elif defined(BUILDING_DLL) +# define EXPORT __declspec(dllexport) +# else +# define EXPORT __declspec(dllimport) +# endif +#elif defined(__OS2__) && defined(__WATCOMC__) && defined(__SW_BD) +# define EXPORT __declspec(dllexport) +#elif (defined(__GNUC__) || defined(__clang__) || defined(__HP_cc)) && defined(XMP_SYM_VISIBILITY) +# define EXPORT __attribute__((visibility ("default"))) +#elif defined(__SUNPRO_C) && defined(XMP_LDSCOPE_GLOBAL) +# define EXPORT __global +#elif defined(EMSCRIPTEN) +# define EXPORT EMSCRIPTEN_KEEPALIVE +#else +# define 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_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 { + 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 */ + } *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_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 { /* Current channel information */ + 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 */ + } channel_info[XMP_MAX_CHANNELS]; +}; + + +typedef char *xmp_context; + +EXPORT extern const char *xmp_version; +EXPORT extern const unsigned int xmp_vercode; + +EXPORT xmp_context xmp_create_context (void); +EXPORT void xmp_free_context (xmp_context); +EXPORT int xmp_test_module (char *, struct xmp_test_info *); +EXPORT int xmp_load_module (xmp_context, char *); +EXPORT void xmp_scan_module (xmp_context); +EXPORT void xmp_release_module (xmp_context); +EXPORT int xmp_start_player (xmp_context, int, int); +EXPORT int xmp_play_frame (xmp_context); +EXPORT int xmp_play_buffer (xmp_context, void *, int, int); +EXPORT void xmp_get_frame_info (xmp_context, struct xmp_frame_info *); +EXPORT void xmp_end_player (xmp_context); +EXPORT void xmp_inject_event (xmp_context, int, struct xmp_event *); +EXPORT void xmp_get_module_info (xmp_context, struct xmp_module_info *); +EXPORT char **xmp_get_format_list (void); +EXPORT int xmp_next_position (xmp_context); +EXPORT int xmp_prev_position (xmp_context); +EXPORT int xmp_set_position (xmp_context, int); +EXPORT void xmp_stop_module (xmp_context); +EXPORT void xmp_restart_module (xmp_context); +EXPORT int xmp_seek_time (xmp_context, int); +EXPORT int xmp_channel_mute (xmp_context, int, int); +EXPORT int xmp_channel_vol (xmp_context, int, int); +EXPORT int xmp_set_player (xmp_context, int, int); +EXPORT int xmp_get_player (xmp_context, int); +EXPORT int xmp_set_instrument_path (xmp_context, char *); +EXPORT int xmp_load_module_from_memory (xmp_context, void *, long); +EXPORT int xmp_load_module_from_file (xmp_context, void *, long); + +/* External sample mixer API */ +EXPORT int xmp_start_smix (xmp_context, int, int); +EXPORT void xmp_end_smix (xmp_context); +EXPORT int xmp_smix_play_instrument(xmp_context, int, int, int, int); +EXPORT int xmp_smix_play_sample (xmp_context, int, int, int, int); +EXPORT int xmp_smix_channel_pan (xmp_context, int, int); +EXPORT int xmp_smix_load_sample (xmp_context, int, char *); +EXPORT int xmp_smix_release_sample (xmp_context, int); + +#ifdef __cplusplus +} +#endif + +#endif /* XMP_H */ diff --git a/source/libxmp-lite/src/common.c b/source/libxmp-lite/src/common.c new file mode 100644 index 000000000..ff411c824 --- /dev/null +++ b/source/libxmp-lite/src/common.c @@ -0,0 +1,366 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 +#include +#include +#ifdef __WATCOMC__ +#include +#elif !defined(_WIN32) +#include +#endif + +#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; + + 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; +} + +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; +} + +/* Sample number adjustment by Vitamin/CAIG */ +struct xmp_sample *libxmp_realloc_samples(struct xmp_sample *buf, int *size, int new_size) +{ + buf = realloc(buf, sizeof (struct xmp_sample) * new_size); + if (buf == NULL) + return NULL; + if (new_size > *size) + memset(buf + *size, 0, sizeof (struct xmp_sample) * (new_size - *size)); + *size = new_size; + + return buf; +} + +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; +} + +/* + * 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 +#ifndef WIN32 + +/* Given a directory, see if file exists there, ignoring case */ + +int libxmp_check_filename_case(char *dir, 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))) { + if (!strcasecmp(d->d_name, name)) { + found = 1; + break; + } + } + + if (found) + strncpy(new_name, d->d_name, size); + + closedir(dirfd); + + return found; +} + +#else + +/* FIXME: implement functionality for Win32 */ + +int libxmp_check_filename_case(char *dir, 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); +} diff --git a/source/libxmp-lite/src/common.h b/source/libxmp-lite/src/common.h new file mode 100644 index 000000000..65a9f57f0 --- /dev/null +++ b/source/libxmp-lite/src/common.h @@ -0,0 +1,437 @@ +#ifndef LIBXMP_COMMON_H +#define LIBXMP_COMMON_H + +#ifdef __AROS__ +#define __AMIGA__ +#endif + +#include +#include +#include +#include "xmp.h" + +#if (defined(__GNUC__) || defined(__clang__)) && defined(XMP_SYM_VISIBILITY) +#if !defined(WIN32) && !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__AMIGA__) && !defined(__MSDOS__) && !defined(B_BEOS_VERSION) && !defined(__ATHEOS__) && !defined(EMSCRIPTEN) && !defined(__MINT__) +#define USE_VERSIONED_SYMBOLS +#endif +#endif + +/* AmigaOS fixes by Chris Young , Nov 25, 2007 + */ +#if defined B_BEOS_VERSION +# include +#elif defined __amigaos4__ +# include +#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 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 +#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 +#endif +#if defined(_WIN32) || defined(__WATCOMC__) /* in win32.c */ +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 + +/* 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 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 */ + 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 */ + char **scan_cnt; /* scan counters */ + struct extra_sample_data *xtra; +#ifndef LIBXMP_CORE_DISABLE_IT + struct xmp_sample *xsmp; /* sustain loop samples */ +#endif +}; + + +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 { + int pbreak; + int jump; + int delay; + int jumpline; + int loop_chn; + + struct pattern_loop { + int start; + int count; + } *loop; + + int num_rows; + int end_point; + int rowdelay; /* For IT pattern row delay */ + int rowdelay_set; + } 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 { + int count; + int map; + } *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_exclude_match (const char *); +int libxmp_prepare_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 */ diff --git a/source/libxmp-lite/src/control.c b/source/libxmp-lite/src/control.c new file mode 100644 index 000000000..30a26482c --- /dev/null +++ b/source/libxmp-lite/src/control.c @@ -0,0 +1,529 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 +#include +#include +#include +#include + +#include "format.h" +#include "virtual.h" +#include "mixer.h" + +const char *xmp_version = XMP_VERSION; +const unsigned int xmp_vercode = XMP_VERCODE; + +xmp_context xmp_create_context() +{ + 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; + + if (ctx->state > XMP_STATE_UNLOADED) + xmp_release_module(opaque); + + 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; + } + } + } +} + +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; +} + +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 +EXPORT extern int xmp_set_player_v40__(xmp_context, int, int); +EXPORT extern int xmp_set_player_v41__(xmp_context, int, int) + __attribute__((alias("xmp_set_player_v40__"))); +EXPORT extern int xmp_set_player_v43__(xmp_context, int, int) + __attribute__((alias("xmp_set_player_v40__"))); +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 +EXPORT extern int xmp_get_player_v40__(xmp_context, int); +EXPORT extern int xmp_get_player_v41__(xmp_context, int) + __attribute__((alias("xmp_get_player_v40__"))); +EXPORT extern int xmp_get_player_v42__(xmp_context, int) + __attribute__((alias("xmp_get_player_v40__"))); +EXPORT extern int xmp_get_player_v43__(xmp_context, int) + __attribute__((alias("xmp_get_player_v40__"))); +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; +} + +char **xmp_get_format_list() +{ + 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, 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; +} diff --git a/source/libxmp-lite/src/dataio.c b/source/libxmp-lite/src/dataio.c new file mode 100644 index 000000000..4f24f0f76 --- /dev/null +++ b/source/libxmp-lite/src/dataio.c @@ -0,0 +1,269 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 +#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 diff --git a/source/libxmp-lite/src/effects.c b/source/libxmp-lite/src/effects.c new file mode 100644 index 000000000..5d128020f --- /dev/null +++ b/source/libxmp-lite/src/effects.c @@ -0,0 +1,1069 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 "player.h" +#include "effects.h" +#include "period.h" +#include "virtual.h" +#include "mixer.h" +#ifndef LIBXMP_CORE_PLAYER +#include "extras.h" +#endif + +#define NOT_IMPLEMENTED +#define HAS_QUIRK(x) (m->quirk & (x)) + +#define SET_LFO_NOTZERO(lfo, depth, rate) do { \ + if ((depth) != 0) libxmp_lfo_set_depth(lfo, depth); \ + if ((rate) != 0) libxmp_lfo_set_rate(lfo, rate); \ +} while (0) + +#define EFFECT_MEMORY__(p, m) do { \ + if ((p) == 0) { (p) = (m); } else { (m) = (p); } \ +} while (0) + +/* ST3 effect memory is not a bug, but it's a weird implementation and it's + * unlikely to be supported in anything other than ST3 (or OpenMPT). + */ +#define EFFECT_MEMORY(p, m) do { \ + if (HAS_QUIRK(QUIRK_ST3BUGS)) { \ + EFFECT_MEMORY__((p), xc->vol.memory); \ + } else { \ + EFFECT_MEMORY__((p), (m)); \ + } \ +} while (0) + +#define EFFECT_MEMORY_SETONLY(p, m) do { \ + EFFECT_MEMORY__((p), (m)); \ + if (HAS_QUIRK(QUIRK_ST3BUGS)) { \ + if ((p) != 0) { xc->vol.memory = (p); } \ + } \ +} while (0) + +#define EFFECT_MEMORY_S3M(p) do { \ + if (HAS_QUIRK(QUIRK_ST3BUGS)) { \ + EFFECT_MEMORY__((p), xc->vol.memory); \ + } \ +} while (0) + + +static void do_toneporta(struct context_data *ctx, + struct channel_data *xc, int note) +{ + struct module_data *m = &ctx->m; + struct xmp_instrument *instrument = &m->mod.xxi[xc->ins]; + int mapped = instrument->map[xc->key].ins; + struct xmp_subinstrument *sub; + + if (mapped >= instrument->nsm) { + mapped = 0; + } + + sub = &instrument->sub[mapped]; + + if (note >= 1 && note <= 0x80 && (uint32)xc->ins < m->mod.ins) { + note--; + xc->porta.target = libxmp_note_to_period(ctx, note + sub->xpo + + instrument->map[xc->key_porta].xpo, xc->finetune, + xc->per_adj); + } + xc->porta.dir = xc->period < xc->porta.target ? 1 : -1; +} + +void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int chn, + struct xmp_event *e, int fnum) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct flow_control *f = &p->flow; + uint8 note, fxp, fxt; + int h, l; + + /* key_porta is IT only */ + if (m->read_event_type != READ_EVENT_IT) { + xc->key_porta = xc->key; + } + + note = e->note; + if (fnum == 0) { + fxt = e->fxt; + fxp = e->fxp; + } else { + fxt = e->f2t; + fxp = e->f2p; + } + + switch (fxt) { + case FX_ARPEGGIO: + fx_arpeggio: + if (!HAS_QUIRK(QUIRK_ARPMEM) || fxp != 0) { + xc->arpeggio.val[0] = 0; + xc->arpeggio.val[1] = MSN(fxp); + xc->arpeggio.val[2] = LSN(fxp); + xc->arpeggio.size = 3; + } + break; + case FX_S3M_ARPEGGIO: + EFFECT_MEMORY(fxp, xc->arpeggio.memory); + goto fx_arpeggio; + +#ifndef LIBXMP_CORE_PLAYER + case FX_OKT_ARP3: + if (fxp != 0) { + xc->arpeggio.val[0] = -MSN(fxp); + xc->arpeggio.val[1] = 0; + xc->arpeggio.val[2] = LSN(fxp); + xc->arpeggio.size = 3; + } + break; + case FX_OKT_ARP4: + if (fxp != 0) { + xc->arpeggio.val[0] = 0; + xc->arpeggio.val[1] = LSN(fxp); + xc->arpeggio.val[2] = 0; + xc->arpeggio.val[3] = -MSN(fxp); + xc->arpeggio.size = 4; + } + break; + case FX_OKT_ARP5: + if (fxp != 0) { + xc->arpeggio.val[0] = LSN(fxp); + xc->arpeggio.val[1] = LSN(fxp); + xc->arpeggio.val[2] = 0; + xc->arpeggio.size = 3; + } + break; +#endif + case FX_PORTA_UP: /* Portamento up */ + EFFECT_MEMORY(fxp, xc->freq.memory); + + if (HAS_QUIRK(QUIRK_FINEFX) + && (fnum == 0 || !HAS_QUIRK(QUIRK_ITVPOR))) { + switch (MSN(fxp)) { + case 0xf: + fxp &= 0x0f; + goto fx_f_porta_up; + case 0xe: + fxp &= 0x0f; + fxp |= 0x10; + goto fx_xf_porta; + } + } + + SET(PITCHBEND); + + if (fxp != 0) { + xc->freq.slide = -fxp; + if (HAS_QUIRK(QUIRK_UNISLD)) + xc->porta.memory = fxp; + } else if (xc->freq.slide > 0) { + xc->freq.slide *= -1; + } + break; + case FX_PORTA_DN: /* Portamento down */ + EFFECT_MEMORY(fxp, xc->freq.memory); + + if (HAS_QUIRK(QUIRK_FINEFX) + && (fnum == 0 || !HAS_QUIRK(QUIRK_ITVPOR))) { + switch (MSN(fxp)) { + case 0xf: + fxp &= 0x0f; + goto fx_f_porta_dn; + case 0xe: + fxp &= 0x0f; + fxp |= 0x20; + goto fx_xf_porta; + } + } + + SET(PITCHBEND); + + if (fxp != 0) { + xc->freq.slide = fxp; + if (HAS_QUIRK(QUIRK_UNISLD)) + xc->porta.memory = fxp; + } else if (xc->freq.slide < 0) { + xc->freq.slide *= -1; + } + break; + case FX_TONEPORTA: /* Tone portamento */ + EFFECT_MEMORY_SETONLY(fxp, xc->porta.memory); + + if (fxp != 0) { + if (HAS_QUIRK(QUIRK_UNISLD)) /* IT compatible Gxx off */ + xc->freq.memory = fxp; + xc->porta.slide = fxp; + } + + if (HAS_QUIRK(QUIRK_IGSTPOR)) { + if (note == 0 && xc->porta.dir == 0) + break; + } + + if (!IS_VALID_INSTRUMENT(xc->ins)) + break; + + do_toneporta(ctx, xc, note); + + SET(TONEPORTA); + break; + + case FX_VIBRATO: /* Vibrato */ + EFFECT_MEMORY_SETONLY(fxp, xc->vibrato.memory); + + SET(VIBRATO); + SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp) << 2, MSN(fxp)); + break; + case FX_FINE_VIBRATO: /* Fine vibrato */ + EFFECT_MEMORY_SETONLY(fxp, xc->vibrato.memory); + + SET(VIBRATO); + SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp), MSN(fxp)); + break; + + case FX_TONE_VSLIDE: /* Toneporta + vol slide */ + if (!IS_VALID_INSTRUMENT(xc->ins)) + break; + do_toneporta(ctx, xc, note); + SET(TONEPORTA); + goto fx_volslide; + case FX_VIBRA_VSLIDE: /* Vibrato + vol slide */ + SET(VIBRATO); + goto fx_volslide; + + case FX_TREMOLO: /* Tremolo */ + EFFECT_MEMORY(fxp, xc->tremolo.memory); + SET(TREMOLO); + SET_LFO_NOTZERO(&xc->tremolo.lfo, LSN(fxp), MSN(fxp)); + break; + + case FX_SETPAN: /* Set pan */ + if (HAS_QUIRK(QUIRK_PROTRACK)) { + break; + } + fx_setpan: + /* From OpenMPT PanOff.xm: + * "Another chapter of weird FT2 bugs: Note-Off + Note Delay + * + Volume Column Panning = Panning effect is ignored." + */ + if (!HAS_QUIRK(QUIRK_FT2BUGS) /* If not FT2 */ + || fnum == 0 /* or not vol column */ + || e->note != XMP_KEY_OFF /* or not keyoff */ + || e->fxt != FX_EXTENDED /* or not delay */ + || MSN(e->fxp) != EX_DELAY) { + xc->pan.val = fxp; + xc->pan.surround = 0; + } + xc->rpv = 0; /* storlek_20: set pan overrides random pan */ + xc->pan.surround = 0; + break; + case FX_OFFSET: /* Set sample offset */ + EFFECT_MEMORY(fxp, xc->offset.memory); + SET(OFFSET); + if (note) { + xc->offset.val &= xc->offset.val & ~0xffff; + xc->offset.val |= fxp << 8; + xc->offset.val2 = fxp << 8; + } + if (e->ins) { + xc->offset.val2 = fxp << 8; + } + break; + case FX_VOLSLIDE: /* Volume slide */ + fx_volslide: + /* S3M file volume slide note: + * DFy Fine volume down by y (...) If y is 0, the command will + * be treated as a volume slide up with a value of f (15). + * If a DFF command is specified, the volume will be slid + * up. + */ + if (HAS_QUIRK(QUIRK_FINEFX)) { + h = MSN(fxp); + l = LSN(fxp); + if (l == 0xf && h != 0) { + xc->vol.memory = fxp; + fxp >>= 4; + goto fx_f_vslide_up; + } else if (h == 0xf && l != 0) { + xc->vol.memory = fxp; + fxp &= 0x0f; + goto fx_f_vslide_dn; + } + } + + /* recover memory */ + if (fxp == 0x00) { + if ((fxp = xc->vol.memory) != 0) + goto fx_volslide; + } + + SET(VOL_SLIDE); + + /* Skaven's 2nd reality (S3M) has volslide parameter D7 => pri + * down. Other trackers only compute volumes if the other + * parameter is 0, Fall from sky.xm has 2C => do nothing. + * Also don't assign xc->vol.memory if fxp is 0, see Guild + * of Sounds.xm + */ + if (fxp) { + xc->vol.memory = fxp; + h = MSN(fxp); + l = LSN(fxp); + if (fxp) { + if (HAS_QUIRK(QUIRK_VOLPDN)) { + xc->vol.slide = l ? -l : h; + } else { + xc->vol.slide = h ? h : -l; + } + } + } + + /* Mirko reports that a S3M with D0F effects created with ST321 + * should process volume slides in all frames like ST300. I + * suspect ST3/IT could be handling D0F effects like this. + */ + if (HAS_QUIRK(QUIRK_FINEFX)) { + if (MSN(xc->vol.memory) == 0xf + || LSN(xc->vol.memory) == 0xf) { + SET(FINE_VOLS); + xc->vol.fslide = xc->vol.slide; + } + } + break; + case FX_VOLSLIDE_2: /* Secondary volume slide */ + SET(VOL_SLIDE_2); + if (fxp) { + h = MSN(fxp); + l = LSN(fxp); + xc->vol.slide2 = h ? h : -l; + } + break; + case FX_JUMP: /* Order jump */ + p->flow.pbreak = 1; + p->flow.jump = fxp; + /* effect B resets effect D in lower channels */ + p->flow.jumpline = 0; + break; + case FX_VOLSET: /* Volume set */ + SET(NEW_VOL); + xc->volume = fxp; + if (xc->split) { + p->xc_data[xc->pair].volume = xc->volume; + } + break; + case FX_BREAK: /* Pattern break */ + p->flow.pbreak = 1; + p->flow.jumpline = 10 * MSN(fxp) + LSN(fxp); + break; + case FX_EXTENDED: /* Extended effect */ + EFFECT_MEMORY_S3M(fxp); + fxt = fxp >> 4; + fxp &= 0x0f; + switch (fxt) { + case EX_FILTER: /* Amiga led filter */ + if (IS_AMIGA_MOD()) { + p->filter = !(fxp & 1); + } + break; + case EX_F_PORTA_UP: /* Fine portamento up */ + EFFECT_MEMORY(fxp, xc->fine_porta.up_memory); + goto fx_f_porta_up; + case EX_F_PORTA_DN: /* Fine portamento down */ + EFFECT_MEMORY(fxp, xc->fine_porta.down_memory); + goto fx_f_porta_dn; + case EX_GLISS: /* Glissando toggle */ + if (fxp) { + SET_NOTE(NOTE_GLISSANDO); + } else { + RESET_NOTE(NOTE_GLISSANDO); + } + break; + case EX_VIBRATO_WF: /* Set vibrato waveform */ + fxp &= 3; + libxmp_lfo_set_waveform(&xc->vibrato.lfo, fxp); + break; + case EX_FINETUNE: /* Set finetune */ + if (!HAS_QUIRK(QUIRK_FT2BUGS) || note > 0) { + xc->finetune = (int8)(fxp << 4); + } + break; + case EX_PATTERN_LOOP: /* Loop pattern */ + if (fxp == 0) { + /* mark start of loop */ + f->loop[chn].start = p->row; + if (HAS_QUIRK(QUIRK_FT2BUGS)) + p->flow.jumpline = p->row; + } else { + /* end of loop */ + if (f->loop[chn].count) { + if (--f->loop[chn].count) { + /* **** H:FIXME **** */ + f->loop_chn = ++chn; + } else { + if (HAS_QUIRK(QUIRK_S3MLOOP)) + f->loop[chn].start = + p->row + 1; + } + } else { + f->loop[chn].count = fxp; + f->loop_chn = ++chn; + } + } + break; + case EX_TREMOLO_WF: /* Set tremolo waveform */ + libxmp_lfo_set_waveform(&xc->tremolo.lfo, fxp & 3); + break; + case EX_SETPAN: + fxp <<= 4; + goto fx_setpan; + case EX_RETRIG: /* Retrig note */ + SET(RETRIG); + xc->retrig.val = fxp; + xc->retrig.count = LSN(xc->retrig.val) + 1; + xc->retrig.type = 0; + break; + case EX_F_VSLIDE_UP: /* Fine volume slide up */ + EFFECT_MEMORY(fxp, xc->fine_vol.up_memory); + goto fx_f_vslide_up; + case EX_F_VSLIDE_DN: /* Fine volume slide down */ + EFFECT_MEMORY(fxp, xc->fine_vol.down_memory); + goto fx_f_vslide_dn; + case EX_CUT: /* Cut note */ + SET(RETRIG); + SET_NOTE(NOTE_CUT); /* for IT cut-carry */ + xc->retrig.val = fxp + 1; + xc->retrig.count = xc->retrig.val; + xc->retrig.type = 0x10; + break; + case EX_DELAY: /* Note delay */ + /* computed at frame loop */ + break; + case EX_PATT_DELAY: /* Pattern delay */ + goto fx_patt_delay; + case EX_INVLOOP: /* Invert loop / funk repeat */ + xc->invloop.speed = fxp; + break; + } + break; + case FX_SPEED: /* Set speed */ + if (HAS_QUIRK(QUIRK_NOBPM) || p->flags & XMP_FLAGS_VBLANK) { + goto fx_s3m_speed; + } + + /* speedup.xm needs BPM = 20 */ + if (fxp < 0x20) { + goto fx_s3m_speed; + } else { + goto fx_s3m_bpm; + } + break; + + case FX_FINETUNE: + xc->finetune = (int16) (fxp - 0x80); + break; + + case FX_F_VSLIDE_UP: /* Fine volume slide up */ + EFFECT_MEMORY(fxp, xc->fine_vol.up_memory); + fx_f_vslide_up: + SET(FINE_VOLS); + xc->vol.fslide = fxp; + break; + case FX_F_VSLIDE_DN: /* Fine volume slide down */ + EFFECT_MEMORY(fxp, xc->fine_vol.up_memory); + fx_f_vslide_dn: + SET(FINE_VOLS); + xc->vol.fslide = -fxp; + break; + + case FX_F_PORTA_UP: /* Fine portamento up */ + fx_f_porta_up: + if (fxp) { + SET(FINE_BEND); + xc->freq.fslide = -fxp; + } + break; + case FX_F_PORTA_DN: /* Fine portamento down */ + fx_f_porta_dn: + if (fxp) { + SET(FINE_BEND); + xc->freq.fslide = fxp; + } + break; + case FX_PATT_DELAY: + fx_patt_delay: + if (m->read_event_type != READ_EVENT_ST3 || !p->flow.delay) { + p->flow.delay = fxp; + } + break; + + case FX_S3M_SPEED: /* Set S3M speed */ + EFFECT_MEMORY_S3M(fxp); + fx_s3m_speed: + if (fxp) { + p->speed = fxp; +#ifndef LIBXMP_CORE_PLAYER + p->st26_speed = 0; +#endif + } + break; + case FX_S3M_BPM: /* Set S3M BPM */ + fx_s3m_bpm: { + /* Lower time factor in MED allows lower BPM values */ + int min_bpm = (int)(0.5 + m->time_factor * XMP_MIN_BPM / 10); + if (fxp < min_bpm) + fxp = min_bpm; + p->bpm = fxp; + p->frame_time = m->time_factor * m->rrate / p->bpm; + break; + } + +#ifndef LIBXMP_CORE_DISABLE_IT + case FX_IT_BPM: /* Set IT BPM */ + if (MSN(fxp) == 0) { /* T0x - Tempo slide down by x */ + SET(TEMPO_SLIDE); + xc->tempo.slide = -LSN(fxp); + } else if (MSN(fxp) == 1) { /* T1x - Tempo slide up by x */ + SET(TEMPO_SLIDE); + xc->tempo.slide = LSN(fxp); + } else { + if (fxp < XMP_MIN_BPM) + fxp = XMP_MIN_BPM; + p->bpm = fxp; + } + p->frame_time = m->time_factor * m->rrate / p->bpm; + break; + case FX_IT_ROWDELAY: + if (!f->rowdelay_set) { + f->rowdelay = fxp; + f->rowdelay_set = 3; + } + break; + + /* From the OpenMPT VolColMemory.it test case: + * "Volume column commands a, b, c and d (volume slide) share one + * effect memory, but it should not be shared with Dxy in the effect + * column. + */ + case FX_VSLIDE_UP_2: /* Fine volume slide up */ + EFFECT_MEMORY(fxp, xc->vol.memory2); + SET(VOL_SLIDE_2); + xc->vol.slide2 = fxp; + break; + case FX_VSLIDE_DN_2: /* Fine volume slide down */ + EFFECT_MEMORY(fxp, xc->vol.memory2); + SET(VOL_SLIDE_2); + xc->vol.slide2 = -fxp; + break; + case FX_F_VSLIDE_UP_2: /* Fine volume slide up */ + EFFECT_MEMORY(fxp, xc->vol.memory2); + SET(FINE_VOLS_2); + xc->vol.fslide2 = fxp; + break; + case FX_F_VSLIDE_DN_2: /* Fine volume slide down */ + EFFECT_MEMORY(fxp, xc->vol.memory2); + SET(FINE_VOLS_2); + xc->vol.fslide2 = -fxp; + break; + case FX_IT_BREAK: /* Pattern break with hex parameter */ + p->flow.pbreak = 1; + p->flow.jumpline = fxp; + break; + +#endif + + case FX_GLOBALVOL: /* Set global volume */ + if (fxp > m->gvolbase) { + p->gvol = m->gvolbase; + } else { + p->gvol = fxp; + } + break; + case FX_GVOL_SLIDE: /* Global volume slide */ + fx_gvolslide: + if (fxp) { + SET(GVOL_SLIDE); + xc->gvol.memory = fxp; + h = MSN(fxp); + l = LSN(fxp); + + if (HAS_QUIRK(QUIRK_FINEFX)) { + if (l == 0xf && h != 0) { + xc->gvol.slide = 0; + xc->gvol.fslide = h; + } else if (h == 0xf && l != 0) { + xc->gvol.slide = 0; + xc->gvol.fslide = -l; + } else { + xc->gvol.slide = h ? h : -l; + xc->gvol.fslide = 0; + } + } else { + xc->gvol.slide = h ? h : -l; + xc->gvol.fslide = 0; + } + } else { + if ((fxp = xc->gvol.memory) != 0) { + goto fx_gvolslide; + } + } + break; + case FX_KEYOFF: /* Key off */ + xc->keyoff = fxp + 1; + break; + case FX_ENVPOS: /* Set envelope position */ + /* From OpenMPT SetEnvPos.xm: + * "When using the Lxx effect, Fasttracker 2 only sets the + * panning envelope position if the volume envelope’s sustain + * flag is set. + */ + if (HAS_QUIRK(QUIRK_FT2BUGS)) { + struct xmp_instrument *instrument; + instrument = libxmp_get_instrument(ctx, xc->ins); + if (instrument != NULL) { + if (instrument->aei.flg & XMP_ENVELOPE_SUS) { + xc->p_idx = fxp; + } + } + } else { + xc->p_idx = fxp; + } + xc->v_idx = fxp; + xc->f_idx = fxp; + break; + case FX_PANSLIDE: /* Pan slide (XM) */ + EFFECT_MEMORY(fxp, xc->pan.memory); + SET(PAN_SLIDE); + xc->pan.slide = LSN(fxp) - MSN(fxp); + break; + case FX_PANSL_NOMEM: /* Pan slide (XM volume column) */ + SET(PAN_SLIDE); + xc->pan.slide = LSN(fxp) - MSN(fxp); + break; + +#ifndef LIBXMP_CORE_DISABLE_IT + case FX_IT_PANSLIDE: /* Pan slide w/ fine pan (IT) */ + SET(PAN_SLIDE); + if (fxp) { + if (MSN(fxp) == 0xf) { + xc->pan.slide = 0; + xc->pan.fslide = LSN(fxp); + } else if (LSN(fxp) == 0xf) { + xc->pan.slide = 0; + xc->pan.fslide = -MSN(fxp); + } else { + SET(PAN_SLIDE); + xc->pan.slide = LSN(fxp) - MSN(fxp); + xc->pan.fslide = 0; + } + } + break; +#endif + + case FX_MULTI_RETRIG: /* Multi retrig */ + EFFECT_MEMORY_S3M(fxp); + if (fxp) { + xc->retrig.val = fxp; + } + if (note) { + xc->retrig.count = LSN(xc->retrig.val) + 1; + xc->retrig.type = MSN(xc->retrig.val); + } + SET(RETRIG); + break; + case FX_TREMOR: /* Tremor */ + EFFECT_MEMORY(fxp, xc->tremor.memory); + xc->tremor.up = MSN(fxp); + xc->tremor.down = LSN(fxp); + if (IS_PLAYER_MODE_FT2()) { + xc->tremor.count |= 0x80; + } else { + if (xc->tremor.up == 0) { + xc->tremor.up++; + } + if (xc->tremor.down == 0) { + xc->tremor.down++; + } + } + SET(TREMOR); + break; + case FX_XF_PORTA: /* Extra fine portamento */ + fx_xf_porta: + SET(FINE_BEND); + switch (MSN(fxp)) { + case 1: + xc->freq.fslide = -0.25 * LSN(fxp); + break; + case 2: + xc->freq.fslide = 0.25 * LSN(fxp); + break; + } + break; + case FX_SURROUND: + xc->pan.surround = fxp; + break; + +#ifndef LIBXMP_CORE_DISABLE_IT + case FX_TRK_VOL: /* Track volume setting */ + if (fxp <= m->volbase) { + xc->mastervol = fxp; + } + break; + case FX_TRK_VSLIDE: /* Track volume slide */ + if (fxp == 0) { + if ((fxp = xc->trackvol.memory) == 0) + break; + } + + if (HAS_QUIRK(QUIRK_FINEFX)) { + h = MSN(fxp); + l = LSN(fxp); + if (h == 0xf && l != 0) { + xc->trackvol.memory = fxp; + fxp &= 0x0f; + goto fx_trk_fvslide; + } else if (l == 0xf && h != 0) { + xc->trackvol.memory = fxp; + fxp &= 0xf0; + goto fx_trk_fvslide; + } + } + + SET(TRK_VSLIDE); + if (fxp) { + h = MSN(fxp); + l = LSN(fxp); + + xc->trackvol.memory = fxp; + if (HAS_QUIRK(QUIRK_VOLPDN)) { + xc->trackvol.slide = l ? -l : h; + } else { + xc->trackvol.slide = h ? h : -l; + } + } + + break; + case FX_TRK_FVSLIDE: /* Track fine volume slide */ + fx_trk_fvslide: + SET(TRK_FVSLIDE); + if (fxp) { + xc->trackvol.fslide = MSN(fxp) - LSN(fxp); + } + break; + + case FX_IT_INSTFUNC: + switch (fxp) { + case 0: /* Past note cut */ + libxmp_virt_pastnote(ctx, chn, VIRT_ACTION_CUT); + break; + case 1: /* Past note off */ + libxmp_virt_pastnote(ctx, chn, VIRT_ACTION_OFF); + break; + case 2: /* Past note fade */ + libxmp_virt_pastnote(ctx, chn, VIRT_ACTION_FADE); + break; + case 3: /* Set NNA to note cut */ + libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_CUT); + break; + case 4: /* Set NNA to continue */ + libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_CONT); + break; + case 5: /* Set NNA to note off */ + libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_OFF); + break; + case 6: /* Set NNA to note fade */ + libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_FADE); + break; + case 7: /* Turn off volume envelope */ + SET_PER(VENV_PAUSE); + break; + case 8: /* Turn on volume envelope */ + RESET_PER(VENV_PAUSE); + break; + case 9: /* Turn off pan envelope */ + SET_PER(PENV_PAUSE); + break; + case 0xa: /* Turn on pan envelope */ + RESET_PER(PENV_PAUSE); + break; + case 0xb: /* Turn off pitch envelope */ + SET_PER(FENV_PAUSE); + break; + case 0xc: /* Turn on pitch envelope */ + RESET_PER(FENV_PAUSE); + break; + } + break; + case FX_FLT_CUTOFF: + if (fxp < 0xfe || xc->filter.resonance > 0) { + xc->filter.cutoff = fxp; + } + break; + case FX_FLT_RESN: + xc->filter.resonance = fxp; + break; + case FX_PANBRELLO: /* Panbrello */ + SET(PANBRELLO); + SET_LFO_NOTZERO(&xc->panbrello.lfo, LSN(fxp) << 4, MSN(fxp)); + break; + case FX_PANBRELLO_WF: /* Panbrello waveform */ + libxmp_lfo_set_waveform(&xc->panbrello.lfo, fxp & 3); + break; + case FX_HIOFFSET: /* High offset */ + xc->offset.val &= 0xffff; + xc->offset.val |= fxp << 16; + break; +#endif + +#ifndef LIBXMP_CORE_PLAYER + + /* SFX effects */ + case FX_VOL_ADD: + if (!IS_VALID_INSTRUMENT(xc->ins)) { + break; + } + SET(NEW_VOL); + xc->volume = m->mod.xxi[xc->ins].sub[0].vol + fxp; + if (xc->volume > m->volbase) { + xc->volume = m->volbase; + } + break; + case FX_VOL_SUB: + if (!IS_VALID_INSTRUMENT(xc->ins)) { + break; + } + SET(NEW_VOL); + xc->volume = m->mod.xxi[xc->ins].sub[0].vol - fxp; + if (xc->volume < 0) { + xc->volume =0; + } + break; + case FX_PITCH_ADD: + SET_PER(TONEPORTA); + xc->porta.target = libxmp_note_to_period(ctx, note - 1, xc->finetune, 0) + + fxp; + xc->porta.slide = 2; + xc->porta.dir = 1; + break; + case FX_PITCH_SUB: + SET_PER(TONEPORTA); + xc->porta.target = libxmp_note_to_period(ctx, note - 1, xc->finetune, 0) + - fxp; + xc->porta.slide = 2; + xc->porta.dir = -1; + break; + + /* Saga Musix says: + * + * "When both nibbles of an Fxx command are set, SoundTracker 2.6 + * applies the both values alternatingly, first the high nibble, + * then the low nibble on the next row, then the high nibble again... + * If only the high nibble is set, it should act like if only the low + * nibble is set (i.e. F30 is the same as F03). + */ + case FX_ICE_SPEED: + if (fxp) { + if (LSN(fxp)) { + p->st26_speed = (MSN(fxp) << 8) | LSN(fxp); + } else { + p->st26_speed = MSN(fxp); + } + } + break; + + case FX_VOLSLIDE_UP: /* Vol slide with uint8 arg */ + if (HAS_QUIRK(QUIRK_FINEFX)) { + h = MSN(fxp); + l = LSN(fxp); + if (h == 0xf && l != 0) { + fxp &= 0x0f; + goto fx_f_vslide_up; + } + } + + if (fxp) + xc->vol.slide = fxp; + SET(VOL_SLIDE); + break; + case FX_VOLSLIDE_DN: /* Vol slide with uint8 arg */ + if (HAS_QUIRK(QUIRK_FINEFX)) { + h = MSN(fxp); + l = LSN(fxp); + if (h == 0xf && l != 0) { + fxp &= 0x0f; + goto fx_f_vslide_dn; + } + } + + if (fxp) + xc->vol.slide = -fxp; + SET(VOL_SLIDE); + break; + case FX_F_VSLIDE: /* Fine volume slide */ + SET(FINE_VOLS); + if (fxp) { + h = MSN(fxp); + l = LSN(fxp); + xc->vol.fslide = h ? h : -l; + } + break; + case FX_NSLIDE_DN: + case FX_NSLIDE_UP: + case FX_NSLIDE_R_DN: + case FX_NSLIDE_R_UP: + if (fxp != 0) { + if (fxt == FX_NSLIDE_R_DN || fxt == FX_NSLIDE_R_UP) { + xc->retrig.val = MSN(fxp); + xc->retrig.count = MSN(fxp) + 1; + xc->retrig.type = 0; + } + + if (fxt == FX_NSLIDE_UP || fxt == FX_NSLIDE_R_UP) + xc->noteslide.slide = LSN(fxp); + else + xc->noteslide.slide = -LSN(fxp); + + xc->noteslide.count = xc->noteslide.speed = MSN(fxp); + } + if (fxt == FX_NSLIDE_R_DN || fxt == FX_NSLIDE_R_UP) + SET(RETRIG); + SET(NOTE_SLIDE); + break; + case FX_NSLIDE2_DN: + SET(NOTE_SLIDE); + xc->noteslide.slide = -fxp; + xc->noteslide.count = xc->noteslide.speed = 1; + break; + case FX_NSLIDE2_UP: + SET(NOTE_SLIDE); + xc->noteslide.slide = fxp; + xc->noteslide.count = xc->noteslide.speed = 1; + break; + case FX_F_NSLIDE_DN: + SET(FINE_NSLIDE); + xc->noteslide.fslide = -fxp; + break; + case FX_F_NSLIDE_UP: + SET(FINE_NSLIDE); + xc->noteslide.fslide = fxp; + break; + + case FX_PER_VIBRATO: /* Persistent vibrato */ + if (LSN(fxp) != 0) { + SET_PER(VIBRATO); + } else { + RESET_PER(VIBRATO); + } + SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp) << 2, MSN(fxp)); + break; + case FX_PER_PORTA_UP: /* Persistent portamento up */ + SET_PER(PITCHBEND); + xc->freq.slide = -fxp; + if ((xc->freq.memory = fxp) == 0) + RESET_PER(PITCHBEND); + break; + case FX_PER_PORTA_DN: /* Persistent portamento down */ + SET_PER(PITCHBEND); + xc->freq.slide = fxp; + if ((xc->freq.memory = fxp) == 0) + RESET_PER(PITCHBEND); + break; + case FX_PER_TPORTA: /* Persistent tone portamento */ + if (!IS_VALID_INSTRUMENT(xc->ins)) + break; + SET_PER(TONEPORTA); + do_toneporta(ctx, xc, note); + xc->porta.slide = fxp; + if (fxp == 0) + RESET_PER(TONEPORTA); + break; + case FX_PER_VSLD_UP: /* Persistent volslide up */ + SET_PER(VOL_SLIDE); + xc->vol.slide = fxp; + if (fxp == 0) + RESET_PER(VOL_SLIDE); + break; + case FX_PER_VSLD_DN: /* Persistent volslide down */ + SET_PER(VOL_SLIDE); + xc->vol.slide = -fxp; + if (fxp == 0) + RESET_PER(VOL_SLIDE); + break; + case FX_VIBRATO2: /* Deep vibrato (2x) */ + SET(VIBRATO); + SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp) << 3, MSN(fxp)); + break; + case FX_SPEED_CP: /* Set speed and ... */ + if (fxp) { + p->speed = fxp; + p->st26_speed = 0; + } + /* fall through */ + case FX_PER_CANCEL: /* Cancel persistent effects */ + xc->per_flags = 0; + break; + + /* 669 effects */ + + case FX_669_PORTA_UP: /* 669 portamento up */ + SET_PER(PITCHBEND); + xc->freq.slide = 80 * fxp; + if ((xc->freq.memory = fxp) == 0) + RESET_PER(PITCHBEND); + break; + case FX_669_PORTA_DN: /* 669 portamento down */ + SET_PER(PITCHBEND); + xc->freq.slide = -80 * fxp; + if ((xc->freq.memory = fxp) == 0) + RESET_PER(PITCHBEND); + break; + case FX_669_TPORTA: /* 669 tone portamento */ + if (!IS_VALID_INSTRUMENT(xc->ins)) + break; + SET_PER(TONEPORTA); + do_toneporta(ctx, xc, note); + xc->porta.slide = 40 * fxp; + if (fxp == 0) + RESET_PER(TONEPORTA); + break; + case FX_669_FINETUNE: /* 669 finetune */ + xc->finetune = 80 * (int8)fxp; + break; + case FX_669_VIBRATO: /* 669 vibrato */ + if (LSN(fxp) != 0) { + libxmp_lfo_set_waveform(&xc->vibrato.lfo, 669); + SET_PER(VIBRATO); + } else { + RESET_PER(VIBRATO); + } + SET_LFO_NOTZERO(&xc->vibrato.lfo, 669, 1); + break; +#endif + + default: +#ifndef LIBXMP_CORE_PLAYER + libxmp_extras_process_fx(ctx, xc, chn, note, fxt, fxp, fnum); +#endif + break; + } +} diff --git a/source/libxmp-lite/src/effects.h b/source/libxmp-lite/src/effects.h new file mode 100644 index 000000000..b269bac43 --- /dev/null +++ b/source/libxmp-lite/src/effects.h @@ -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 */ diff --git a/source/libxmp-lite/src/filter.c b/source/libxmp-lite/src/filter.c new file mode 100644 index 000000000..d15d89c9e --- /dev/null +++ b/source/libxmp-lite/src/filter.c @@ -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 +#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 diff --git a/source/libxmp-lite/src/format.c b/source/libxmp-lite/src/format.c new file mode 100644 index 000000000..e19f070e7 --- /dev/null +++ b/source/libxmp-lite/src/format.c @@ -0,0 +1,62 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 +#include +#ifndef LIBXMP_CORE_PLAYER +#include "loaders/prowizard/prowiz.h" +#endif +#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; + +extern const struct pw_format *const pw_format[]; + +const struct format_loader *const format_loader[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 }; + +char **format_list() +{ + int count, i; + + if (_farray[0] == NULL) { + for (count = i = 0; format_loader[i] != NULL; i++) { + _farray[count++] = format_loader[i]->name; + } + + _farray[count] = NULL; + } + + return (char **)_farray; +} diff --git a/source/libxmp-lite/src/format.h b/source/libxmp-lite/src/format.h new file mode 100644 index 000000000..4d90458bb --- /dev/null +++ b/source/libxmp-lite/src/format.h @@ -0,0 +1,25 @@ +#ifndef LIBXMP_FORMAT_H +#define LIBXMP_FORMAT_H + +#include +#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); +}; + +char **format_list(void); + +#ifndef LIBXMP_CORE_PLAYER + +#define NUM_FORMATS 52 +#define NUM_PW_FORMATS 43 + +int pw_test_format(HIO_HANDLE *, char *, const int, struct xmp_test_info *); +#endif + +#endif + diff --git a/source/libxmp-lite/src/hio.c b/source/libxmp-lite/src/hio.c new file mode 100644 index 000000000..b94b74de8 --- /dev/null +++ b/source/libxmp-lite/src/hio.c @@ -0,0 +1,380 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 +#include +#include +#include +#include "common.h" +#include "hio.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 = 0; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read8s(h->handle.file, &err); + if (err != 0) { + h->error = err; + } + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread8s(h->handle.mem); + break; + } + + return ret; +} + +uint8 hio_read8(HIO_HANDLE *h) +{ + int err; + uint8 ret = 0; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read8(h->handle.file, &err); + if (err != 0) { + h->error = err; + } + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread8(h->handle.mem); + break; + } + + return ret; +} + +uint16 hio_read16l(HIO_HANDLE *h) +{ + int err; + uint16 ret = 0; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read16l(h->handle.file, &err); + if (err != 0) { + h->error = err; + } + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread16l(h->handle.mem); + break; + } + + return ret; +} + +uint16 hio_read16b(HIO_HANDLE *h) +{ + int err; + uint16 ret = 0; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read16b(h->handle.file, &err); + if (err != 0) { + h->error = err; + } + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread16b(h->handle.mem); + break; + } + + return ret; +} + +uint32 hio_read24l(HIO_HANDLE *h) +{ + int err; + uint32 ret = 0; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read24l(h->handle.file, &err); + if (err != 0) { + h->error = err; + } + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread24l(h->handle.mem); + break; + } + + return ret; +} + +uint32 hio_read24b(HIO_HANDLE *h) +{ + int err; + uint32 ret = 0; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read24b(h->handle.file, &err); + if (err != 0) { + h->error = err; + } + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread24b(h->handle.mem); + break; + } + + return ret; +} + +uint32 hio_read32l(HIO_HANDLE *h) +{ + int err; + uint32 ret = 0; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read32l(h->handle.file, &err); + if (err != 0) { + h->error = err; + } + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread32l(h->handle.mem); + break; + } + + return ret; +} + +uint32 hio_read32b(HIO_HANDLE *h) +{ + int err; + uint32 ret = 0; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read32b(h->handle.file, &err); + if (err != 0) { + h->error = err; + } + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread32b(h->handle.mem); + } + + 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 = errno; + } + 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 = errno; + } + 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) { + h->error = errno; + } + 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); + default: + return EOF; + } +} + +int hio_error(HIO_HANDLE *h) +{ + int error = h->error; + h->error = 0; + return error; +} + +HIO_HANDLE *hio_open(const void *path, const char *mode) +{ + HIO_HANDLE *h; + + h = (HIO_HANDLE *)malloc(sizeof (HIO_HANDLE)); + if (h == NULL) + goto err; + + h->error = 0; + 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; + + h = (HIO_HANDLE *)malloc(sizeof (HIO_HANDLE)); + if (h == NULL) + return NULL; + + h->error = 0; + 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 *)malloc(sizeof (HIO_HANDLE)); + if (h == NULL) + return NULL; + + h->error = 0; + h->type = HIO_HANDLE_TYPE_FILE; + h->handle.file = f /*fdopen(fileno(f), "rb")*/; + h->size = get_size(f); + + return h; +} + +int hio_close(HIO_HANDLE *h) +{ + int ret; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = fclose(h->handle.file); + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mclose(h->handle.mem); + break; + default: + ret = -1; + } + + free(h); + return ret; +} + +long hio_size(HIO_HANDLE *h) +{ + return h->size; +} diff --git a/source/libxmp-lite/src/hio.h b/source/libxmp-lite/src/hio.h new file mode 100644 index 000000000..91f2c04a0 --- /dev/null +++ b/source/libxmp-lite/src/hio.h @@ -0,0 +1,42 @@ +#ifndef XMP_HIO_H +#define XMP_HIO_H + +#include +#include +#include +#include "memio.h" + +#define HIO_HANDLE_TYPE(x) ((x)->type) + +typedef struct { +#define HIO_HANDLE_TYPE_FILE 0 +#define HIO_HANDLE_TYPE_MEMORY 1 + int type; + long size; + union { + FILE *file; + MFILE *mem; + } handle; + int error; +} 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 void *, const char *); +HIO_HANDLE *hio_open_mem (const void *, long); +HIO_HANDLE *hio_open_file (FILE *); +int hio_close (HIO_HANDLE *); +long hio_size (HIO_HANDLE *); + +#endif diff --git a/source/libxmp-lite/src/it.h b/source/libxmp-lite/src/it.h new file mode 100644 index 000000000..b574c67e8 --- /dev/null +++ b/source/libxmp-lite/src/it.h @@ -0,0 +1,181 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 */ +}; + diff --git a/source/libxmp-lite/src/it_load.c b/source/libxmp-lite/src/it_load.c new file mode 100644 index 000000000..2b45c76d1 --- /dev/null +++ b/source/libxmp-lite/src/it_load.c @@ -0,0 +1,1404 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 LIBXMP_CORE_DISABLE_IT + +#include +#include "loader.h" +#include "it.h" +#include "period.h" + +#define MAGIC_IMPM MAGIC4('I','M','P','M') +#define MAGIC_IMPI MAGIC4('I','M','P','I') +#define MAGIC_IMPS MAGIC4('I','M','P','S') + + +static int it_test(HIO_HANDLE *, char *, const int); +static int it_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_it = { + "Impulse Tracker", + it_test, + it_load +}; + +#if defined(__WATCOMC__) +#undef localtime_r +#define localtime_r _localtime + +#elif !defined(HAVE_LOCALTIME_R) || defined(_WIN32) +#undef localtime_r +struct tm *localtime_r(const time_t * timep, struct tm *result) +{ + /* Note: Win32 localtime() is thread-safe */ + memcpy(result, localtime(timep), sizeof(struct tm)); + return result; +} +#endif + +static int it_test(HIO_HANDLE * f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_IMPM) + return -1; + + libxmp_read_title(f, t, 26); + + return 0; +} + + +#define FX_NONE 0xff +#define FX_XTND 0xfe +#define L_CHANNELS 64 + + +static const uint8 fx[] = { + /* */ FX_NONE, + /* A */ FX_S3M_SPEED, + /* B */ FX_JUMP, + /* C */ FX_IT_BREAK, + /* D */ FX_VOLSLIDE, + /* E */ FX_PORTA_DN, + /* F */ FX_PORTA_UP, + /* G */ FX_TONEPORTA, + /* H */ FX_VIBRATO, + /* I */ FX_TREMOR, + /* J */ FX_S3M_ARPEGGIO, + /* K */ FX_VIBRA_VSLIDE, + /* L */ FX_TONE_VSLIDE, + /* M */ FX_TRK_VOL, + /* N */ FX_TRK_VSLIDE, + /* O */ FX_OFFSET, + /* P */ FX_IT_PANSLIDE, + /* Q */ FX_MULTI_RETRIG, + /* R */ FX_TREMOLO, + /* S */ FX_XTND, + /* T */ FX_IT_BPM, + /* U */ FX_FINE_VIBRATO, + /* V */ FX_GLOBALVOL, + /* W */ FX_GVOL_SLIDE, + /* X */ FX_SETPAN, + /* Y */ FX_PANBRELLO, + /* Z */ FX_FLT_CUTOFF +}; + + +int itsex_decompress8 (HIO_HANDLE *, void *, int, int); +int itsex_decompress16 (HIO_HANDLE *, void *, int, int); + + +static void xlat_fx(int c, struct xmp_event *e, uint8 *last_fxp, int new_fx) +{ + uint8 h = MSN(e->fxp), l = LSN(e->fxp); + + switch (e->fxt = fx[e->fxt]) { + case FX_XTND: /* Extended effect */ + e->fxt = FX_EXTENDED; + + if (h == 0 && e->fxp == 0) { + e->fxp = last_fxp[c]; + h = MSN(e->fxp); + l = LSN(e->fxp); + } else { + last_fxp[c] = e->fxp; + } + + switch (h) { + case 0x1: /* Glissando */ + e->fxp = 0x30 | l; + break; + case 0x2: /* Finetune -- not supported */ + e->fxt = e->fxp = 0; + break; + case 0x3: /* Vibrato wave */ + e->fxp = 0x40 | l; + break; + case 0x4: /* Tremolo wave */ + e->fxp = 0x70 | l; + break; + case 0x5: /* Panbrello wave */ + if (l <= 3) { + e->fxt = FX_PANBRELLO_WF; + e->fxp = l; + } else { + e->fxt = e->fxp = 0; + } + break; + case 0x6: /* Pattern delay */ + e->fxp = 0xe0 | l; + break; + case 0x7: /* Instrument functions */ + e->fxt = FX_IT_INSTFUNC; + e->fxp &= 0x0f; + break; + case 0x8: /* Set pan position */ + e->fxt = FX_SETPAN; + e->fxp = l << 4; + break; + case 0x9: /* 0x91 = set surround */ + e->fxt = FX_SURROUND; + e->fxp = l; + break; + case 0xa: /* High offset */ + e->fxt = FX_HIOFFSET; + e->fxp = l; + break; + case 0xb: /* Pattern loop */ + e->fxp = 0x60 | l; + break; + case 0xc: /* Note cut */ + case 0xd: /* Note delay */ + if ((e->fxp = l) == 0) + e->fxp++; /* SD0 and SC0 become SD1 and SC1 */ + e->fxp |= h << 4; + break; + case 0xe: /* Pattern row delay */ + e->fxt = FX_IT_ROWDELAY; + e->fxp = l; + break; + default: + e->fxt = e->fxp = 0; + } + break; + case FX_FLT_CUTOFF: + if (e->fxp > 0x7f && e->fxp < 0x90) { /* Resonance */ + e->fxt = FX_FLT_RESN; + e->fxp = (e->fxp - 0x80) * 16; + } else { /* Cutoff */ + e->fxp *= 2; + } + break; + case FX_TREMOR: + if (!new_fx && e->fxp != 0) { + e->fxp = ((MSN(e->fxp) + 1) << 4) | (LSN(e->fxp) + 1); + } + break; + case FX_GLOBALVOL: + if (e->fxp > 0x80) { /* See storlek test 16 */ + e->fxt = e->fxp = 0; + } + break; + case FX_NONE: /* No effect */ + e->fxt = e->fxp = 0; + break; + } +} + + +static void xlat_volfx(struct xmp_event *event) +{ + int b; + + b = event->vol; + event->vol = 0; + + if (b <= 0x40) { + event->vol = b + 1; + } else if (b >= 65 && b <= 74) { /* A */ + event->f2t = FX_F_VSLIDE_UP_2; + event->f2p = b - 65; + } else if (b >= 75 && b <= 84) { /* B */ + event->f2t = FX_F_VSLIDE_DN_2; + event->f2p = b - 75; + } else if (b >= 85 && b <= 94) { /* C */ + event->f2t = FX_VSLIDE_UP_2; + event->f2p = b - 85; + } else if (b >= 95 && b <= 104) { /* D */ + event->f2t = FX_VSLIDE_DN_2; + event->f2p = b - 95; + } else if (b >= 105 && b <= 114) { /* E */ + event->f2t = FX_PORTA_DN; + event->f2p = (b - 105) << 2; + } else if (b >= 115 && b <= 124) { /* F */ + event->f2t = FX_PORTA_UP; + event->f2p = (b - 115) << 2; + } else if (b >= 128 && b <= 192) { /* pan */ + if (b == 192) { + event->f2p = 0xff; + } else { + event->f2p = (b - 128) << 2; + } + event->f2t = FX_SETPAN; + } else if (b >= 193 && b <= 202) { /* G */ + uint8 val[10] = { + 0x00, 0x01, 0x04, 0x08, 0x10, + 0x20, 0x40, 0x60, 0x80, 0xff + }; + event->f2t = FX_TONEPORTA; + event->f2p = val[b - 193]; + } else if (b >= 203 && b <= 212) { /* H */ + event->f2t = FX_VIBRATO; + event->f2p = b - 203; + } +} + + +static void fix_name(uint8 *s, int l) +{ + int i; + + /* IT names can have 0 at start of data, replace with space */ + for (l--, i = 0; i < l; i++) { + if (s[i] == 0) + s[i] = ' '; + } + for (i--; i >= 0 && s[i] == ' '; i--) { + if (s[i] == ' ') + s[i] = 0; + } +} + + +static int read_envelope(struct xmp_envelope *ei, struct it_envelope *env, + HIO_HANDLE *f) +{ + int i; + uint8 buf[82]; + + if (hio_read(buf, 1, 82, f) != 82) { + return -1; + } + + env->flg = buf[0]; + env->num = buf[1]; + + /* Sanity check */ + if (env->num >= XMP_MAX_ENV_POINTS) { + env->flg = 0; + env->num = 0; + return -1; + } + + env->lpb = buf[2]; + env->lpe = buf[3]; + env->slb = buf[4]; + env->sle = buf[5]; + + for (i = 0; i < 25; i++) { + env->node[i].y = buf[6 + i * 3]; + env->node[i].x = readmem16l(buf + 7 + i * 3); + } + + ei->flg = env->flg & IT_ENV_ON ? XMP_ENVELOPE_ON : 0; + + if (env->flg & IT_ENV_LOOP) { + ei->flg |= XMP_ENVELOPE_LOOP; + } + + if (env->flg & IT_ENV_SLOOP) { + ei->flg |= XMP_ENVELOPE_SUS | XMP_ENVELOPE_SLOOP; + } + + if (env->flg & IT_ENV_CARRY) { + ei->flg |= XMP_ENVELOPE_CARRY; + } + + ei->npt = env->num; + ei->sus = env->slb; + ei->sue = env->sle; + ei->lps = env->lpb; + ei->lpe = env->lpe; + + if (ei->npt > 0 && ei->npt <= 25 /* XMP_MAX_ENV_POINTS */ ) { + for (i = 0; i < ei->npt; i++) { + ei->data[i * 2] = env->node[i].x; + ei->data[i * 2 + 1] = env->node[i].y; + } + } else { + ei->flg &= ~XMP_ENVELOPE_ON; + } + + return 0; +} + +static void identify_tracker(struct module_data *m, struct it_file_header *ifh) +{ +#ifndef LIBXMP_CORE_PLAYER + char tracker_name[40]; + int sample_mode = ~ifh->flags & IT_USE_INST; + + switch (ifh->cwt >> 8) { + case 0x00: + strcpy(tracker_name, "unmo3"); + break; + case 0x01: + case 0x02: /* test from Schism Tracker sources */ + if (ifh->cmwt == 0x0200 && ifh->cwt == 0x0214 + && ifh->flags == 9 && ifh->special == 0 + && ifh->hilite_maj == 0 && ifh->hilite_min == 0 + && ifh->insnum == 0 && ifh->patnum + 1 == ifh->ordnum + && ifh->gv == 128 && ifh->mv == 100 && ifh->is == 1 + && ifh->sep == 128 && ifh->pwd == 0 + && ifh->msglen == 0 && ifh->msgofs == 0 && ifh->rsvd == 0) { + strcpy(tracker_name, "OpenSPC conversion"); + } else if (ifh->cmwt == 0x0200 && ifh->cwt == 0x0217) { + strcpy(tracker_name, "ModPlug Tracker 1.16"); + /* ModPlug Tracker files aren't really IMPM 2.00 */ + ifh->cmwt = sample_mode ? 0x100 : 0x214; + } else if (ifh->cwt == 0x0216) { + strcpy(tracker_name, "Impulse Tracker 2.14v3"); + } else if (ifh->cwt == 0x0217) { + strcpy(tracker_name, "Impulse Tracker 2.14v5"); + } else if (ifh->cwt == 0x0214 && !memcmp(&ifh->rsvd, "CHBI", 4)) { + strcpy(tracker_name, "Chibi Tracker"); + } else { + snprintf(tracker_name, 40, "Impulse Tracker %d.%02x", + (ifh->cwt & 0x0f00) >> 8, ifh->cwt & 0xff); + } + break; + case 0x08: + case 0x7f: + if (ifh->cwt == 0x0888) { + strcpy(tracker_name, "OpenMPT 1.17"); + } else if (ifh->cwt == 0x7fff) { + strcpy(tracker_name, "munch.py"); + } else { + snprintf(tracker_name, 40, "unknown (%04x)", ifh->cwt); + } + break; + default: + switch (ifh->cwt >> 12) { + case 0x1:{ + uint16 cwtv = ifh->cwt & 0x0fff; + struct tm version; + time_t version_sec; + + if (cwtv > 0x50) { + version_sec = ((cwtv - 0x050) * 86400) + 1254355200; + if (localtime_r(&version_sec, &version)) { + snprintf(tracker_name, 40, + "Schism Tracker %04d-%02d-%02d", + version.tm_year + 1900, + version.tm_mon + 1, + version.tm_mday); + } + } else { + snprintf(tracker_name, 40, + "Schism Tracker 0.%x", cwtv); + } + break; } + case 0x5: + snprintf(tracker_name, 40, "OpenMPT %d.%02x", + (ifh->cwt & 0x0f00) >> 8, ifh->cwt & 0xff); + if (memcmp(&ifh->rsvd, "OMPT", 4)) + strncat(tracker_name, " (compat.)", 39); + break; + case 0x06: + snprintf(tracker_name, 40, "BeRoTracker %d.%02x", + (ifh->cwt & 0x0f00) >> 8, ifh->cwt & 0xff); + break; + default: + snprintf(tracker_name, 40, "unknown (%04x)", ifh->cwt); + } + } + + libxmp_set_type(m, "%s IT %d.%02x", tracker_name, ifh->cmwt >> 8, + ifh->cmwt & 0xff); +#else + libxmp_set_type(m, "Impulse Tracker"); +#endif +} + +static int load_old_it_instrument(struct xmp_instrument *xxi, HIO_HANDLE *f) +{ + int inst_map[120], inst_rmap[XMP_MAX_KEYS]; + struct it_instrument1_header i1h; + int c, k, j; + uint8 buf[64]; + + if (hio_read(buf, 1, 64, f) != 64) { + return -1; + } + + i1h.magic = readmem32b(buf); + if (i1h.magic != MAGIC_IMPI) { + D_(D_CRIT "bad instrument magic"); + return -1; + } + memcpy(i1h.dosname, buf + 4, 12); + i1h.zero = buf[16]; + i1h.flags = buf[17]; + i1h.vls = buf[18]; + i1h.vle = buf[19]; + i1h.sls = buf[20]; + i1h.sle = buf[21]; + i1h.fadeout = readmem16l(buf + 24); + i1h.nna = buf[26]; + i1h.dnc = buf[27]; + i1h.trkvers = readmem16l(buf + 28); + i1h.nos = buf[30]; + + memcpy(i1h.name, buf + 32, 26); + fix_name(i1h.name, 26); + + if (hio_read(&i1h.keys, 1, 240, f) != 240) { + return -1; + } + if (hio_read(&i1h.epoint, 1, 200, f) != 200) { + return -1; + } + if (hio_read(&i1h.enode, 1, 50, f) != 50) { + return -1; + } + + libxmp_copy_adjust(xxi->name, i1h.name, 25); + + xxi->rls = i1h.fadeout << 7; + + xxi->aei.flg = 0; + if (i1h.flags & IT_ENV_ON) { + xxi->aei.flg |= XMP_ENVELOPE_ON; + } + if (i1h.flags & IT_ENV_LOOP) { + xxi->aei.flg |= XMP_ENVELOPE_LOOP; + } + if (i1h.flags & IT_ENV_SLOOP) { + xxi->aei.flg |= XMP_ENVELOPE_SUS | XMP_ENVELOPE_SLOOP; + } + if (i1h.flags & IT_ENV_CARRY) { + xxi->aei.flg |= XMP_ENVELOPE_SUS | XMP_ENVELOPE_CARRY; + } + xxi->aei.lps = i1h.vls; + xxi->aei.lpe = i1h.vle; + xxi->aei.sus = i1h.sls; + xxi->aei.sue = i1h.sle; + + for (k = 0; k < 25 && i1h.enode[k * 2] != 0xff; k++) ; + + /* Sanity check */ + if (k >= 25 || i1h.enode[k * 2] != 0xff) { + return -1; + } + + for (xxi->aei.npt = k; k--;) { + xxi->aei.data[k * 2] = i1h.enode[k * 2]; + xxi->aei.data[k * 2 + 1] = i1h.enode[k * 2 + 1]; + } + + /* See how many different instruments we have */ + for (j = 0; j < 120; j++) + inst_map[j] = -1; + + for (k = j = 0; j < XMP_MAX_KEYS; j++) { + c = j < 120 ? i1h.keys[j * 2 + 1] - 1 : -1; + if (c < 0 || c >= 120) { + xxi->map[j].ins = 0; + xxi->map[j].xpo = 0; + continue; + } + if (inst_map[c] == -1) { + inst_map[c] = k; + inst_rmap[k] = c; + k++; + } + xxi->map[j].ins = inst_map[c]; + xxi->map[j].xpo = i1h.keys[j * 2] - j; + } + + xxi->nsm = k; + xxi->vol = 0x40; + + if (k) { + xxi->sub = calloc(sizeof(struct xmp_subinstrument), k); + if (xxi->sub == NULL) { + return -1; + } + + for (j = 0; j < k; j++) { + struct xmp_subinstrument *sub = &xxi->sub[j]; + + sub->sid = inst_rmap[j]; + sub->nna = i1h.nna; + sub->dct = + i1h.dnc ? XMP_INST_DCT_NOTE : XMP_INST_DCT_OFF; + sub->dca = XMP_INST_DCA_CUT; + sub->pan = -1; + } + } + + D_(D_INFO "[ ] %-26.26s %d %-4.4s %4d %2d %c%c%c %3d", + /*i,*/ i1h.name, + i1h.nna, + i1h.dnc ? "on" : "off", + i1h.fadeout, + xxi->aei.npt, + xxi->aei.flg & XMP_ENVELOPE_ON ? 'V' : '-', + xxi->aei.flg & XMP_ENVELOPE_LOOP ? 'L' : '-', + xxi->aei.flg & XMP_ENVELOPE_SUS ? 'S' : '-', xxi->nsm); + + return 0; +} + +static int load_new_it_instrument(struct xmp_instrument *xxi, HIO_HANDLE *f) +{ + int inst_map[120], inst_rmap[XMP_MAX_KEYS]; + struct it_instrument2_header i2h; + struct it_envelope env; + int dca2nna[] = { 0, 2, 3 }; + int c, k, j; + uint8 buf[64]; + + if (hio_read(buf, 1, 64, f) != 64) { + return -1; + } + + i2h.magic = readmem32b(buf); + if (i2h.magic != MAGIC_IMPI) { + D_(D_CRIT "bad instrument magic"); + return -1; + } + memcpy(i2h.dosname, buf + 4, 12); + i2h.zero = buf[16]; + i2h.nna = buf[17]; + i2h.dct = buf[18]; + i2h.dca = buf[19]; + + /* Sanity check */ + if (i2h.dca > 3) { + /* Northern Sky has an instrument with DCA 3 */ + D_(D_WARN "bad instrument dca: %d", i2h.dca); + i2h.dca = 0; + } + + i2h.fadeout = readmem16l(buf + 20); + i2h.pps = buf[22]; + i2h.ppc = buf[23]; + i2h.gbv = buf[24]; + i2h.dfp = buf[25]; + i2h.rv = buf[26]; + i2h.rp = buf[27]; + i2h.trkvers = readmem16l(buf + 28); + i2h.nos = buf[30]; + + memcpy(i2h.name, buf + 32, 26); + fix_name(i2h.name, 26); + + i2h.ifc = buf[58]; + i2h.ifr = buf[59]; + i2h.mch = buf[60]; + i2h.mpr = buf[61]; + i2h.mbnk = readmem16l(buf + 62); + + if (hio_read(&i2h.keys, 1, 240, f) != 240) { + D_(D_CRIT "key map read error"); + return -1; + } + + libxmp_copy_adjust(xxi->name, i2h.name, 25); + xxi->rls = i2h.fadeout << 6; + + /* Envelopes */ + + if (read_envelope(&xxi->aei, &env, f) < 0) { + return -1; + } + if (read_envelope(&xxi->pei, &env, f) < 0) { + return -1; + } + if (read_envelope(&xxi->fei, &env, f) < 0) { + return -1; + } + + if (xxi->pei.flg & XMP_ENVELOPE_ON) { + for (j = 0; j < xxi->pei.npt; j++) + xxi->pei.data[j * 2 + 1] += 32; + } + + if (xxi->aei.flg & XMP_ENVELOPE_ON && xxi->aei.npt == 0) { + xxi->aei.npt = 1; + } + if (xxi->pei.flg & XMP_ENVELOPE_ON && xxi->pei.npt == 0) { + xxi->pei.npt = 1; + } + if (xxi->fei.flg & XMP_ENVELOPE_ON && xxi->fei.npt == 0) { + xxi->fei.npt = 1; + } + + if (env.flg & IT_ENV_FILTER) { + xxi->fei.flg |= XMP_ENVELOPE_FLT; + for (j = 0; j < env.num; j++) { + xxi->fei.data[j * 2 + 1] += 32; + xxi->fei.data[j * 2 + 1] *= 4; + } + } else { + /* Pitch envelope is *50 to get fine interpolation */ + for (j = 0; j < env.num; j++) + xxi->fei.data[j * 2 + 1] *= 50; + } + + /* See how many different instruments we have */ + for (j = 0; j < 120; j++) + inst_map[j] = -1; + + for (k = j = 0; j < 120; j++) { + c = i2h.keys[j * 2 + 1] - 1; + if (c < 0 || c >= 120) { + xxi->map[j].ins = 0xff; /* No sample */ + xxi->map[j].xpo = 0; + continue; + } + if (inst_map[c] == -1) { + inst_map[c] = k; + inst_rmap[k] = c; + k++; + } + xxi->map[j].ins = inst_map[c]; + xxi->map[j].xpo = i2h.keys[j * 2] - j; + } + + xxi->nsm = k; + xxi->vol = i2h.gbv >> 1; + + if (k) { + xxi->sub = calloc(sizeof(struct xmp_subinstrument), k); + if (xxi->sub == NULL) + return -1; + + for (j = 0; j < k; j++) { + struct xmp_subinstrument *sub = &xxi->sub[j]; + + sub->sid = inst_rmap[j]; + sub->nna = i2h.nna; + sub->dct = i2h.dct; + sub->dca = dca2nna[i2h.dca]; + sub->pan = i2h.dfp & 0x80 ? -1 : i2h.dfp * 4; + sub->ifc = i2h.ifc; + sub->ifr = i2h.ifr; + sub->rvv = ((int)i2h.rp << 8) | i2h.rv; + } + } + + D_(D_INFO "[ ] %-26.26s %d %d %d %4d %4d %2x " + "%02x %c%c%c %3d %02x %02x", + /*i,*/ i2h.name, + i2h.nna, i2h.dct, i2h.dca, + i2h.fadeout, + i2h.gbv, + i2h.dfp & 0x80 ? 0x80 : i2h.dfp * 4, + i2h.rv, + xxi->aei.flg & XMP_ENVELOPE_ON ? 'V' : '-', + xxi->pei.flg & XMP_ENVELOPE_ON ? 'P' : '-', + env.flg & 0x01 ? env.flg & 0x80 ? 'F' : 'P' : '-', + xxi->nsm, i2h.ifc, i2h.ifr); + + return 0; +} + +static int load_it_sample(struct module_data *m, int i, int start, + int sample_mode, HIO_HANDLE *f) +{ + struct it_sample_header ish; + struct xmp_module *mod = &m->mod; + struct xmp_sample *xxs, *xsmp; + int j, k; + uint8 buf[80]; + + if (sample_mode) { + mod->xxi[i].sub = calloc(sizeof(struct xmp_subinstrument), 1); + if (mod->xxi[i].sub == NULL) { + return -1; + } + } + + if (hio_read(buf, 1, 80, f) != 80) { + return -1; + } + + ish.magic = readmem32b(buf); + /* Changed to continue to allow use-brdg.it and use-funk.it to + * load correctly (both IT 2.04) + */ + if (ish.magic != MAGIC_IMPS) { + return 0; + } + + xxs = &mod->xxs[i]; + xsmp = &m->xsmp[i]; + + memcpy(ish.dosname, buf + 4, 12); + ish.zero = buf[16]; + ish.gvl = buf[17]; + ish.flags = buf[18]; + ish.vol = buf[19]; + + memcpy(ish.name, buf + 20, 26); + fix_name(ish.name, 26); + + ish.convert = buf[46]; + ish.dfp = buf[47]; + ish.length = readmem32l(buf + 48); + ish.loopbeg = readmem32l(buf + 52); + ish.loopend = readmem32l(buf + 56); + ish.c5spd = readmem32l(buf + 60); + ish.sloopbeg = readmem32l(buf + 64); + ish.sloopend = readmem32l(buf + 68); + ish.sample_ptr = readmem32l(buf + 72); + ish.vis = buf[76]; + ish.vid = buf[77]; + ish.vir = buf[78]; + ish.vit = buf[79]; + + if (ish.flags & IT_SMP_16BIT) { + xxs->flg = XMP_SAMPLE_16BIT; + } + xxs->len = ish.length; + + /* Sanity check */ + if (xxs->len > MAX_SAMPLE_SIZE) { + return -1; + } + + xxs->lps = ish.loopbeg; + xxs->lpe = ish.loopend; + xxs->flg |= ish.flags & IT_SMP_LOOP ? XMP_SAMPLE_LOOP : 0; + xxs->flg |= ish.flags & IT_SMP_BLOOP ? XMP_SAMPLE_LOOP_BIDIR : 0; + xxs->flg |= ish.flags & IT_SMP_SLOOP ? XMP_SAMPLE_SLOOP : 0; + xxs->flg |= ish.flags & IT_SMP_BSLOOP ? XMP_SAMPLE_SLOOP_BIDIR : 0; + + if (ish.flags & IT_SMP_SLOOP) { + memcpy(xsmp, xxs, sizeof (struct xmp_sample)); + xsmp->lps = ish.sloopbeg; + xsmp->lpe = ish.sloopend; + xsmp->flg |= XMP_SAMPLE_LOOP; + xsmp->flg &= ~XMP_SAMPLE_LOOP_BIDIR; + if (ish.flags & IT_SMP_BSLOOP) { + xsmp->flg |= XMP_SAMPLE_LOOP_BIDIR; + } + } + + if (sample_mode) { + /* Create an instrument for each sample */ + mod->xxi[i].sub[0].vol = ish.vol; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + mod->xxi[i].nsm = !!(xxs->len); + libxmp_instrument_name(mod, i, ish.name, 25); + } else { + libxmp_copy_adjust(xxs->name, ish.name, 25); + } + + D_(D_INFO "\n[%2X] %-26.26s %05x%c%05x %05x %05x %05x " + "%02x%02x %02x%02x %5d ", + i, sample_mode ? xxs->name : mod->xxi[i].name, + xxs->len, + ish.flags & IT_SMP_16BIT ? '+' : ' ', + MIN(xxs->lps, 0xfffff), MIN(xxs->lpe, 0xfffff), + MIN(ish.sloopbeg, 0xfffff), MIN(ish.sloopend, 0xfffff), + ish.flags, ish.convert, ish.vol, ish.gvl, ish.c5spd); + + /* Convert C5SPD to relnote/finetune + * + * In IT we can have a sample associated with two or more + * instruments, but c5spd is a sample attribute -- so we must + * scan all xmp instruments to set the correct transposition + */ + + for (j = 0; j < mod->ins; j++) { + for (k = 0; k < mod->xxi[j].nsm; k++) { + struct xmp_subinstrument *sub = &mod->xxi[j].sub[k]; + if (sub->sid == i) { + sub->vol = ish.vol; + sub->gvl = ish.gvl; + sub->vra = ish.vis; /* sample to sub-instrument vibrato */ + sub->vde = ish.vid >> 1; + sub->vwf = ish.vit; + sub->vsw = (0xff - ish.vir) >> 1; + + libxmp_c2spd_to_note(ish.c5spd, + &mod->xxi[j].sub[k].xpo, + &mod->xxi[j].sub[k].fin); + + /* Set sample pan (overrides subinstrument) */ + if (ish.dfp & 0x80) { + sub->pan = (ish.dfp & 0x7f) * 4; + } else if (sample_mode) { + sub->pan = -1; + } + } + } + } + + if (ish.flags & IT_SMP_SAMPLE && xxs->len > 1) { + int cvt = 0; + + if (0 != hio_seek(f, start + ish.sample_ptr, SEEK_SET)) + return -1; + + if (xxs->lpe > xxs->len || xxs->lps >= xxs->lpe) + xxs->flg &= ~XMP_SAMPLE_LOOP; + + if (~ish.convert & IT_CVT_SIGNED) + cvt |= SAMPLE_FLAG_UNS; + + /* compressed samples */ + if (ish.flags & IT_SMP_COMP) { + uint8 *buf; + int ret; + + buf = calloc(1, xxs->len * 2); + if (buf == NULL) + return -1; + + if (ish.flags & IT_SMP_16BIT) { + itsex_decompress16(f, buf, xxs->len, + ish.convert & IT_CVT_DIFF); + +#ifdef WORDS_BIGENDIAN + /* decompression generates native-endian + * samples, but we want little-endian + */ + cvt |= SAMPLE_FLAG_BIGEND; +#endif + } else { + itsex_decompress8(f, buf, xxs->len, + ish.convert & IT_CVT_DIFF); + } + + if (ish.flags & IT_SMP_SLOOP) { + long pos = hio_tell(f); + if (pos < 0) { + free(buf); + return -1; + } + ret = libxmp_load_sample(m, NULL, SAMPLE_FLAG_NOLOAD | + cvt, &m->xsmp[i], buf); + if (ret < 0) { + free(buf); + return -1; + } + hio_seek(f, pos, SEEK_SET); + } + + ret = libxmp_load_sample(m, NULL, SAMPLE_FLAG_NOLOAD | cvt, + &mod->xxs[i], buf); + if (ret < 0) { + free(buf); + return -1; + } + + free(buf); + } else { + if (ish.flags & IT_SMP_SLOOP) { + long pos = hio_tell(f); + if (pos < 0) { + return -1; + } + if (libxmp_load_sample(m, f, cvt, &m->xsmp[i], NULL) < 0) + return -1; + hio_seek(f, pos, SEEK_SET); + } + + if (libxmp_load_sample(m, f, cvt, &mod->xxs[i], NULL) < 0) + return -1; + } + } + + return 0; +} + +static int load_it_pattern(struct module_data *m, int i, int new_fx, + HIO_HANDLE *f) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event, dummy, lastevent[L_CHANNELS]; + uint8 mask[L_CHANNELS]; + uint8 last_fxp[64]; + + int r, c, pat_len; + uint8 b; + + r = 0; + + memset(last_fxp, 0, 64); + memset(lastevent, 0, L_CHANNELS * sizeof(struct xmp_event)); + memset(&dummy, 0, sizeof(struct xmp_event)); + + pat_len = hio_read16l(f) /* - 4 */ ; + mod->xxp[i]->rows = hio_read16l(f); + + if (libxmp_alloc_tracks_in_pattern(mod, i) < 0) { + return -1; + } + + memset(mask, 0, L_CHANNELS); + hio_read16l(f); + hio_read16l(f); + + while (--pat_len >= 0) { + b = hio_read8(f); + if (!b) { + r++; + continue; + } + c = (b - 1) & 63; + + if (b & 0x80) { + mask[c] = hio_read8(f); + pat_len--; + } + /* + * WARNING: we IGNORE events in disabled channels. Disabled + * channels should be muted only, but we don't know the + * real number of channels before loading the patterns and + * we don't want to set it to 64 channels. + */ + if (c >= mod->chn || r >= mod->xxp[i]->rows) { + event = &dummy; + } else { + event = &EVENT(i, c, r); + } + + if (mask[c] & 0x01) { + b = hio_read8(f); + + /* From ittech.txt: + * Note ranges from 0->119 (C-0 -> B-9) + * 255 = note off, 254 = notecut + * Others = note fade (already programmed into IT's player + * but not available in the editor) + */ + switch (b) { + case 0xff: /* key off */ + b = XMP_KEY_OFF; + break; + case 0xfe: /* cut */ + b = XMP_KEY_CUT; + break; + default: + if (b > 119) { /* fade */ + b = XMP_KEY_FADE; + } else { + b++; /* note */ + } + } + lastevent[c].note = event->note = b; + pat_len--; + } + if (mask[c] & 0x02) { + b = hio_read8(f); + lastevent[c].ins = event->ins = b; + pat_len--; + } + if (mask[c] & 0x04) { + b = hio_read8(f); + lastevent[c].vol = event->vol = b; + xlat_volfx(event); + pat_len--; + } + if (mask[c] & 0x08) { + b = hio_read8(f); + if (b > 31) { + D_(D_WARN "invalid effect %#02x", b); + hio_read8(f); + + } else { + event->fxt = b; + event->fxp = hio_read8(f); + + xlat_fx(c, event, last_fxp, new_fx); + lastevent[c].fxt = event->fxt; + lastevent[c].fxp = event->fxp; + } + pat_len -= 2; + } + if (mask[c] & 0x10) { + event->note = lastevent[c].note; + } + if (mask[c] & 0x20) { + event->ins = lastevent[c].ins; + } + if (mask[c] & 0x40) { + event->vol = lastevent[c].vol; + xlat_volfx(event); + } + if (mask[c] & 0x80) { + event->fxt = lastevent[c].fxt; + event->fxp = lastevent[c].fxp; + } + } + + return 0; +} + +static int it_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int c, i, j; + struct it_file_header ifh; + int max_ch; + uint32 *pp_ins; /* Pointers to instruments */ + uint32 *pp_smp; /* Pointers to samples */ + uint32 *pp_pat; /* Pointers to patterns */ + int new_fx, sample_mode; + + LOAD_INIT(); + + /* Load and convert header */ + ifh.magic = hio_read32b(f); + if (ifh.magic != MAGIC_IMPM) { + return -1; + } + + hio_read(&ifh.name, 26, 1, f); + ifh.hilite_min = hio_read8(f); + ifh.hilite_maj = hio_read8(f); + + ifh.ordnum = hio_read16l(f); + ifh.insnum = hio_read16l(f); + ifh.smpnum = hio_read16l(f); + ifh.patnum = hio_read16l(f); + + ifh.cwt = hio_read16l(f); + ifh.cmwt = hio_read16l(f); + ifh.flags = hio_read16l(f); + ifh.special = hio_read16l(f); + + ifh.gv = hio_read8(f); + ifh.mv = hio_read8(f); + ifh.is = hio_read8(f); + ifh.it = hio_read8(f); + ifh.sep = hio_read8(f); + ifh.pwd = hio_read8(f); + + /* Sanity check */ + if (ifh.gv > 0x80 || ifh.mv > 0x80) { + goto err; + } + + ifh.msglen = hio_read16l(f); + ifh.msgofs = hio_read32l(f); + ifh.rsvd = hio_read32l(f); + + hio_read(&ifh.chpan, 64, 1, f); + hio_read(&ifh.chvol, 64, 1, f); + + strncpy(mod->name, (char *)ifh.name, XMP_NAME_SIZE); + mod->len = ifh.ordnum; + mod->ins = ifh.insnum; + mod->smp = ifh.smpnum; + mod->pat = ifh.patnum; + + /* Sanity check */ + if (mod->ins > 255 || mod->smp > 255 || mod->pat > 255) { + goto err; + } + + if (mod->ins) { + pp_ins = calloc(4, mod->ins); + if (pp_ins == NULL) + goto err; + } else { + pp_ins = NULL; + } + + pp_smp = calloc(4, mod->smp); + if (pp_smp == NULL) + goto err2; + + pp_pat = calloc(4, mod->pat); + if (pp_pat == NULL) + goto err3; + + mod->spd = ifh.is; + mod->bpm = ifh.it; + + sample_mode = ~ifh.flags & IT_USE_INST; + + if (ifh.flags & IT_LINEAR_FREQ) { + m->period_type = PERIOD_LINEAR; + } + + if (!sample_mode && ifh.cmwt >= 0x200) { + m->quirk |= QUIRK_INSVOL; + } + + for (i = 0; i < 64; i++) { + struct xmp_channel *xxc = &mod->xxc[i]; + + if (ifh.chpan[i] == 100) { /* Surround -> center */ + xxc->flg |= XMP_CHANNEL_SURROUND; + } + + if (ifh.chpan[i] & 0x80) { /* Channel mute */ + ifh.chvol[i] = 0; + xxc->flg |= XMP_CHANNEL_MUTE; + } + + if (ifh.flags & IT_STEREO) { + xxc->pan = (int)ifh.chpan[i] * 0x80 >> 5; + if (xxc->pan > 0xff) + xxc->pan = 0xff; + } else { + xxc->pan = 0x80; + } + + xxc->vol = ifh.chvol[i]; + } + if (mod->len <= XMP_MAX_MOD_LENGTH) { + hio_read(mod->xxo, 1, mod->len, f); + } else { + hio_read(mod->xxo, 1, XMP_MAX_MOD_LENGTH, f); + hio_seek(f, mod->len - XMP_MAX_MOD_LENGTH, SEEK_CUR); + mod->len = XMP_MAX_MOD_LENGTH; + } + + new_fx = ifh.flags & IT_OLD_FX ? 0 : 1; + + for (i = 0; i < mod->ins; i++) + pp_ins[i] = hio_read32l(f); + for (i = 0; i < mod->smp; i++) + pp_smp[i] = hio_read32l(f); + for (i = 0; i < mod->pat; i++) + pp_pat[i] = hio_read32l(f); + + m->c4rate = C4_NTSC_RATE; + + identify_tracker(m, &ifh); + + MODULE_INFO(); + + D_(D_INFO "Instrument/FX mode: %s/%s", + sample_mode ? "sample" : ifh.cmwt >= 0x200 ? + "new" : "old", ifh.flags & IT_OLD_FX ? "old" : "IT"); + + if (sample_mode) + mod->ins = mod->smp; + + if (libxmp_init_instrument(m) < 0) + goto err4; + + /* Alloc extra samples for sustain loop */ + if (mod->smp > 0) { + m->xsmp = calloc(sizeof (struct xmp_sample), mod->smp); + if (m->xsmp == NULL) { + goto err4; + } + } + + D_(D_INFO "Instruments: %d", mod->ins); + + for (i = 0; i < mod->ins; i++) { + /* + * IT files can have three different instrument types: 'New' + * instruments, 'old' instruments or just samples. We need a + * different loader for each of them. + */ + + struct xmp_instrument *xxi = &mod->xxi[i]; + + if (!sample_mode && ifh.cmwt >= 0x200) { + /* New instrument format */ + if (hio_seek(f, start + pp_ins[i], SEEK_SET) < 0) { + goto err4; + } + + if (load_new_it_instrument(xxi, f) < 0) { + goto err4; + } + + } else if (!sample_mode) { + /* Old instrument format */ + if (hio_seek(f, start + pp_ins[i], SEEK_SET) < 0) { + goto err4; + } + + if (load_old_it_instrument(xxi, f) < 0) { + goto err4; + } + } + } + + D_(D_INFO "Stored Samples: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + + if (hio_seek(f, start + pp_smp[i], SEEK_SET) < 0) { + goto err4; + } + + if (load_it_sample(m, i, start, sample_mode, f) < 0) { + goto err4; + } + } + + D_(D_INFO "Stored patterns: %d", mod->pat); + + /* Effects in muted channels are processed, so scan patterns first to + * see the real number of channels + */ + max_ch = 0; + for (i = 0; i < mod->pat; i++) { + uint8 mask[L_CHANNELS]; + int pat_len; + + /* If the offset to a pattern is 0, the pattern is empty */ + if (pp_pat[i] == 0) + continue; + + hio_seek(f, start + pp_pat[i], SEEK_SET); + pat_len = hio_read16l(f) /* - 4 */ ; + hio_read16l(f); + memset(mask, 0, L_CHANNELS); + hio_read16l(f); + hio_read16l(f); + + while (--pat_len >= 0) { + int b = hio_read8(f); + if (b == 0) + continue; + + c = (b - 1) & 63; + + if (c > max_ch) + max_ch = c; + + if (b & 0x80) { + mask[c] = hio_read8(f); + pat_len--; + } + + if (mask[c] & 0x01) { + hio_read8(f); + pat_len--; + } + if (mask[c] & 0x02) { + hio_read8(f); + pat_len--; + } + if (mask[c] & 0x04) { + hio_read8(f); + pat_len--; + } + if (mask[c] & 0x08) { + hio_read8(f); + hio_read8(f); + pat_len -= 2; + } + } + } + + /* Set the number of channels actually used + */ + mod->chn = max_ch + 1; + mod->trk = mod->pat * mod->chn; + + if (libxmp_init_pattern(mod) < 0) { + goto err4; + } + + /* Read patterns */ + for (i = 0; i < mod->pat; i++) { + + if (libxmp_alloc_pattern(mod, i) < 0) { + goto err4; + } + + /* If the offset to a pattern is 0, the pattern is empty */ + if (pp_pat[i] == 0) { + mod->xxp[i]->rows = 64; + for (j = 0; j < mod->chn; j++) { + int tnum = i * mod->chn + j; + if (libxmp_alloc_track(mod, tnum, 64) < 0) + goto err4; + mod->xxp[i]->index[j] = tnum; + } + continue; + } + + if (hio_seek(f, start + pp_pat[i], SEEK_SET) < 0) { + D_(D_CRIT "error seeking to %d", start + pp_pat[i]); + goto err4; + } + + if (load_it_pattern(m, i, new_fx, f) < 0) { + D_(D_CRIT "error loading pattern %d", i); + goto err4; + } + } + + free(pp_pat); + free(pp_smp); + free(pp_ins); + + /* Song message */ + + if (ifh.special & IT_HAS_MSG) { + if ((m->comment = malloc(ifh.msglen)) != NULL) { + hio_seek(f, start + ifh.msgofs, SEEK_SET); + + D_(D_INFO "Message length : %d", ifh.msglen); + + for (j = 0; j < ifh.msglen - 1; j++) { + int b = hio_read8(f); + if (b == '\r') { + b = '\n'; + } else if ((b < 32 || b > 127) && b != '\n' + && b != '\t') { + b = '.'; + } + m->comment[j] = b; + } + + if (ifh.msglen > 0) { + m->comment[j-1] = 0; + } + } + } + + /* Format quirks */ + + m->quirk |= QUIRKS_IT | QUIRK_ARPMEM; + + if (ifh.flags & IT_LINK_GXX) { + m->quirk |= QUIRK_PRENV; + } else { + m->quirk |= QUIRK_UNISLD; + } + + if (new_fx) { + m->quirk |= QUIRK_VIBHALF | QUIRK_VIBINV; + } else { + m->quirk &= ~QUIRK_VIBALL; + m->quirk |= QUIRK_ITOLDFX; + } + + if (sample_mode) { + m->quirk &= ~(QUIRK_VIRTUAL | QUIRK_RSTCHN); + } + + m->gvolbase = 0x80; + m->gvol = ifh.gv; + m->read_event_type = READ_EVENT_IT; + + return 0; + +err4: + free(pp_pat); +err3: + free(pp_smp); +err2: + free(pp_ins); +err: + return -1; +} + +#endif /* LIBXMP_CORE_DISABLE_IT */ diff --git a/source/libxmp-lite/src/itsex.c b/source/libxmp-lite/src/itsex.c new file mode 100644 index 000000000..55b93fe30 --- /dev/null +++ b/source/libxmp-lite/src/itsex.c @@ -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 */ diff --git a/source/libxmp-lite/src/lfo.c b/source/libxmp-lite/src/lfo.c new file mode 100644 index 000000000..5cdf4fb25 --- /dev/null +++ b/source/libxmp-lite/src/lfo.c @@ -0,0 +1,164 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 +#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 vibrato’s “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; +} diff --git a/source/libxmp-lite/src/lfo.h b/source/libxmp-lite/src/lfo.h new file mode 100644 index 000000000..a269ac3db --- /dev/null +++ b/source/libxmp-lite/src/lfo.h @@ -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 diff --git a/source/libxmp-lite/src/list.h b/source/libxmp-lite/src/list.h new file mode 100644 index 000000000..e284a5eef --- /dev/null +++ b/source/libxmp-lite/src/list.h @@ -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 diff --git a/source/libxmp-lite/src/load.c b/source/libxmp-lite/src/load.c new file mode 100644 index 000000000..6f7bf2db9 --- /dev/null +++ b/source/libxmp-lite/src/load.c @@ -0,0 +1,690 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 +#include +#include +#include +#include +#ifdef __native_client__ +#include +#else +#include +#endif + +#include "format.h" +#include "list.h" +#include "hio.h" +#include "tempfile.h" + +#ifndef LIBXMP_CORE_PLAYER +#if !defined(HAVE_POPEN) && defined(WIN32) +#include "win32/ptpopen.h" +#define HAVE_POPEN 1 +#endif +#if defined(__WATCOMC__) +#define popen _popen +#define pclose _pclose +#define HAVE_POPEN 1 +#endif +#include "md5.h" +#include "extras.h" +#endif + + +extern struct format_loader *format_loader[]; + +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 + +#include "depacker.h" + +static struct depacker *depacker_list[] = { +#if defined __AMIGA__ && !defined __AROS__ + &libxmp_depacker_xfd, +#endif + &libxmp_depacker_zip, + &libxmp_depacker_lha, + &libxmp_depacker_gzip, + &libxmp_depacker_bzip2, + &libxmp_depacker_xz, + &libxmp_depacker_compress, + &libxmp_depacker_pp, + &libxmp_depacker_sqsh, + &libxmp_depacker_arcfs, + &libxmp_depacker_mmcmp, + &libxmp_depacker_muse, + &libxmp_depacker_lzx, + &libxmp_depacker_s404, + &libxmp_depacker_arc, + NULL +}; + +int test_oxm (FILE *); + +#define BUFLEN 16384 + +#ifndef HAVE_POPEN +static int execute_command(const char *cmd, const char *filename, FILE *t) { + return -1; +} +#else +static int execute_command(const char *cmd, const char *filename, FILE *t) +{ + char line[1024], buf[BUFLEN]; + FILE *p; + int n; + + snprintf(line, 1024, cmd, filename); + +#if defined(_WIN32) || defined(__OS2__) || defined(__EMX__) + /* Note: The _popen function returns an invalid file opaque, if + * used in a Windows program, that will cause the program to hang + * indefinitely. _popen works properly in a Console application. + * To create a Windows application that redirects input and output, + * read the section "Creating a Child Process with Redirected Input + * and Output" in the Win32 SDK. -- Mirko + */ + p = popen(line, "rb"); +#else + /* Linux popen fails with "rb" */ + p = popen(line, "r"); +#endif + + if (p == NULL) { + return -1; + } + + while ((n = fread(buf, 1, BUFLEN, p)) > 0) { + fwrite(buf, 1, n, t); + } + + pclose (p); + + return 0; +} +#endif + +static int decrunch(HIO_HANDLE **h, const char *filename, char **temp) +{ + unsigned char b[1024]; + const char *cmd; + FILE *f, *t; + int res; + int headersize; + int i; + struct depacker *depacker = NULL; + + cmd = NULL; + res = 0; + *temp = NULL; + f = (*h)->handle.file; + + headersize = fread(b, 1, 1024, f); + if (headersize < 100) { /* minimum valid file size */ + return 0; + } + + /* Check built-in depackers */ + for (i = 0; depacker_list[i] != NULL; i++) { + if (depacker_list[i]->test(b)) { + depacker = depacker_list[i]; + D_(D_INFO "Use depacker %d", i); + break; + } + } + + /* Check external commands */ + if (depacker == NULL) { + if (b[0] == 'M' && b[1] == 'O' && b[2] == '3') { + /* MO3 */ + D_(D_INFO "mo3"); + cmd = "unmo3 -s \"%s\" STDOUT"; + } else if (memcmp(b, "Rar", 3) == 0) { + /* rar */ + D_(D_INFO "rar"); + cmd = "unrar p -inul -xreadme -x*.diz -x*.nfo -x*.txt " + "-x*.exe -x*.com \"%s\""; + } else if (test_oxm(f) == 0) { + /* oggmod */ + D_(D_INFO "oggmod"); + depacker = &libxmp_depacker_oxm; + } + } + + if (fseek(f, 0, SEEK_SET) < 0) { + goto err; + } + + if (depacker == NULL && cmd == NULL) { + D_(D_INFO "Not packed"); + return 0; + } + +#if defined __ANDROID__ || defined __native_client__ + /* Don't use external helpers in android */ + if (cmd) { + return 0; + } +#endif + + D_(D_WARN "Depacking file... "); + + if ((t = make_temp_file(temp)) == NULL) { + goto err; + } + + /* Depack file */ + if (cmd) { + D_(D_INFO "External depacker: %s", cmd); + if (execute_command(cmd, filename, t) < 0) { + D_(D_CRIT "failed"); + goto err2; + } + } else if (depacker) { + D_(D_INFO "Internal depacker"); + if (depacker->depack(f, t) < 0) { + D_(D_CRIT "failed"); + goto err2; + } + } + + D_(D_INFO "done"); + + if (fseek(t, 0, SEEK_SET) < 0) { + D_(D_CRIT "fseek error"); + goto err2; + } + + hio_close(*h); + *h = hio_open_file(t); + + return res; + + err2: + fclose(t); + err: + return -1; +} + +static void set_md5sum(HIO_HANDLE *f, unsigned char *digest) +{ + unsigned char buf[BUFLEN]; + MD5_CTX ctx; + int bytes_read; + + if (hio_size(f) <= 0) { + memset(digest, 0, 16); + return; + } + + 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(char *name) +{ + char *div, *dirname; + int len; + + if ((div = strrchr(name, '/'))) { + 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(char *name) +{ + char *div, *basename; + + if ((div = strrchr(name, '/'))) { + basename = strdup(div + 1); + } else { + basename = strdup(name); + } + + return basename; +} +#endif /* LIBXMP_CORE_PLAYER */ + +int xmp_test_module(char *path, struct xmp_test_info *info) +{ + HIO_HANDLE *h; + struct stat st; + char buf[XMP_NAME_SIZE]; + int i; + int ret = -XMP_ERROR_FORMAT; +#ifndef LIBXMP_CORE_PLAYER + char *temp = NULL; +#endif + + if (stat(path, &st) < 0) + return -XMP_ERROR_SYSTEM; + +#ifndef _MSC_VER + if (S_ISDIR(st.st_mode)) { + errno = EISDIR; + return -XMP_ERROR_SYSTEM; + } +#endif + + if ((h = hio_open(path, "rb")) == NULL) + return -XMP_ERROR_SYSTEM; + +#ifndef LIBXMP_CORE_PLAYER + if (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 + + if (info != NULL) { + *info->name = 0; /* reset name prior to testing */ + *info->type = 0; /* reset type prior to testing */ + } + + for (i = 0; format_loader[i] != NULL; i++) { + hio_seek(h, 0, SEEK_SET); + if (format_loader[i]->test(h, buf, 0) == 0) { + int is_prowizard = 0; + +#ifndef LIBXMP_CORE_PLAYER + if (strcmp(format_loader[i]->name, "prowizard") == 0) { + hio_seek(h, 0, SEEK_SET); + pw_test_format(h, buf, 0, info); + is_prowizard = 1; + } +#endif + + fclose(h->handle.file); + +#ifndef LIBXMP_CORE_PLAYER + unlink_temp_file(temp); +#endif + + if (info != NULL && !is_prowizard) { + strncpy(info->name, buf, XMP_NAME_SIZE - 1); + strncpy(info->type, format_loader[i]->name, + XMP_NAME_SIZE - 1); + } + return 0; + } + } + +#ifndef LIBXMP_CORE_PLAYER + err: + hio_close(h); + unlink_temp_file(temp); +#else + hio_close(h); +#endif + 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_loader[i] != NULL; i++) { + hio_seek(h, 0, SEEK_SET); + + if (hio_error(h)) { + /* reset error flag */ + } + + D_(D_WARN "test %s", format_loader[i]->name); + test_result = format_loader[i]->test(h, NULL, 0); + if (test_result == 0) { + hio_seek(h, 0, SEEK_SET); + D_(D_WARN "load format: %s", format_loader[i]->name); + load_result = format_loader[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) { + free(m->basename); + free(m->dirname); + 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; + } + + libxmp_scan_sequences(ctx); + + ctx->state = XMP_STATE_LOADED; + + return 0; + + err_load: + xmp_release_module(opaque); + return -XMP_ERROR_LOAD; +} + +int xmp_load_module(xmp_context opaque, char *path) +{ + struct context_data *ctx = (struct context_data *)opaque; +#ifndef LIBXMP_CORE_PLAYER + struct module_data *m = &ctx->m; + long size; + 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; + } + +#ifndef _MSC_VER + if (S_ISDIR(st.st_mode)) { + errno = EISDIR; + return -XMP_ERROR_SYSTEM; + } +#endif + + if ((h = hio_open(path, "rb")) == NULL) { + return -XMP_ERROR_SYSTEM; + } + +#ifndef LIBXMP_CORE_PLAYER + D_(D_INFO "decrunch"); + if (decrunch(&h, path, &temp_name) < 0) { + ret = -XMP_ERROR_DEPACK; + goto err; + } + + 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; +#endif + + ret = load_module(opaque, h); + hio_close(h); + +#ifndef LIBXMP_CORE_PLAYER + unlink_temp_file(temp_name); +#endif + + return ret; + +#ifndef LIBXMP_CORE_PLAYER + err: + hio_close(h); + unlink_temp_file(temp_name); + return ret; +#endif +} + +int xmp_load_module_from_memory(xmp_context opaque, void *mem, long size) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct module_data *m = &ctx->m; + HIO_HANDLE *h; + int ret; + + /* Use size < 0 for unknown/undetermined size */ + if (size == 0) + size--; + + 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; + FILE *f = fdopen(fileno((FILE *)file), "rb"); + int ret; + + if ((h = hio_open_file(f)) == 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); + } + + if (mod->xxp != NULL) { + for (i = 0; i < mod->pat; i++) { + free(mod->xxp[i]); + } + free(mod->xxp); + } + + if (mod->xxi != NULL) { + for (i = 0; i < mod->ins; i++) { + free(mod->xxi[i].sub); + free(mod->xxi[i].extra); + } + free(mod->xxi); + } + + if (mod->xxs != NULL) { + for (i = 0; i < mod->smp; i++) { + if (mod->xxs[i].data != NULL) { + free(mod->xxs[i].data - 4); + } + } + free(mod->xxs); + free(m->xtra); + } + +#ifndef LIBXMP_CORE_DISABLE_IT + if (m->xsmp != NULL) { + for (i = 0; i < mod->smp; i++) { + if (m->xsmp[i].data != NULL) { + free(m->xsmp[i].data - 4); + } + } + free(m->xsmp); + } +#endif + + if (m->scan_cnt) { + for (i = 0; i < mod->len; i++) + free(m->scan_cnt[i]); + free(m->scan_cnt); + } + + free(m->comment); + + D_("free dirname/basename"); + free(m->dirname); + free(m->basename); +} + +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); +} diff --git a/source/libxmp-lite/src/load_helpers.c b/source/libxmp-lite/src/load_helpers.c new file mode 100644 index 000000000..4f442857f --- /dev/null +++ b/source/libxmp-lite/src/load_helpers.c @@ -0,0 +1,425 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 +#include +#include +#include +#include "common.h" +#include "loaders/loader.h" + + +#ifndef LIBXMP_CORE_PLAYER + +#ifdef __ANDROID__ +#include +#include +#include +#endif + +#include + +/* + * 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; + } + } +} + +/* + * Check whether the given string matches one of the blacklisted glob + * patterns. Used to filter file names stored in archive files. + */ +int libxmp_exclude_match(const char *name) +{ + int i; + + static const char *const exclude[] = { + "README", "readme", + "*.DIZ", "*.diz", + "*.NFO", "*.nfo", + "*.DOC", "*.Doc", "*.doc", + "*.INFO", "*.info", "*.Info", + "*.TXT", "*.txt", + "*.EXE", "*.exe", + "*.COM", "*.com", + "*.README", "*.readme", "*.Readme", "*.ReadMe", + NULL + }; + + for (i = 0; exclude[i] != NULL; i++) { + if (fnmatch(exclude[i], name, 0) == 0) { + return 1; + } + } + + return 0; +} + +#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 ) + */ + 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 (char *), 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; +} + +/* 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; +} + diff --git a/source/libxmp-lite/src/loader.h b/source/libxmp-lite/src/loader.h new file mode 100644 index 000000000..23acfd0ce --- /dev/null +++ b/source/libxmp-lite/src/loader.h @@ -0,0 +1,64 @@ +#ifndef XMP_LOADER_H +#define XMP_LOADER_H + +#include +#include +#include +#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_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); +char *libxmp_instrument_name (struct xmp_module *, int, uint8 *, int); +struct xmp_sample* libxmp_realloc_samples(struct xmp_sample *, int *, int); + +char *libxmp_copy_adjust (char *, uint8 *, 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 (char *, 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 *); + +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 diff --git a/source/libxmp-lite/src/mdataio.h b/source/libxmp-lite/src/mdataio.h new file mode 100644 index 000000000..4fa8dc8e5 --- /dev/null +++ b/source/libxmp-lite/src/mdataio.h @@ -0,0 +1,105 @@ +#ifndef LIBXMP_MDATAIO_H +#define LIBXMP_MDATAIO_H + +#include +#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) +{ + uint8 x = 0xff; + mread(&x, 1, 1, m); + return x; +} + +static inline int8 mread8s(MFILE *m) +{ + return (int8)mgetc(m); +} + +static inline uint16 mread16l(MFILE *m) +{ + ptrdiff_t can_read = CAN_READ(m); + if (can_read >= 2) { + uint16 n = readmem16l(m->start + m->pos); + m->pos += 2; + return n; + } else { + m->pos += can_read; + return EOF; + } +} + +static inline uint16 mread16b(MFILE *m) +{ + ptrdiff_t can_read = CAN_READ(m); + if (can_read >= 2) { + uint16 n = readmem16b(m->start + m->pos); + m->pos += 2; + return n; + } else { + m->pos += can_read; + return EOF; + } +} + +static inline uint32 mread24l(MFILE *m) +{ + ptrdiff_t can_read = CAN_READ(m); + if (can_read >= 3) { + uint32 n = readmem24l(m->start + m->pos); + m->pos += 3; + return n; + } else { + m->pos += can_read; + return EOF; + } +} + +static inline uint32 mread24b(MFILE *m) +{ + ptrdiff_t can_read = CAN_READ(m); + if (can_read >= 3) { + uint32 n = readmem24b(m->start + m->pos); + m->pos += 3; + return n; + } else { + m->pos += can_read; + return EOF; + } +} + +static inline uint32 mread32l(MFILE *m) +{ + ptrdiff_t can_read = CAN_READ(m); + if (can_read >= 4) { + uint32 n = readmem32l(m->start + m->pos); + m->pos += 4; + return n; + } else { + m->pos += can_read; + return EOF; + } +} + +static inline uint32 mread32b(MFILE *m) +{ + ptrdiff_t can_read = CAN_READ(m); + if (can_read >= 4) { + uint32 n = readmem32b(m->start + m->pos); + m->pos += 4; + return n; + } else { + m->pos += can_read; + return EOF; + } +} + +#endif diff --git a/source/libxmp-lite/src/memio.c b/source/libxmp-lite/src/memio.c new file mode 100644 index 000000000..8439bdb8a --- /dev/null +++ b/source/libxmp-lite/src/memio.c @@ -0,0 +1,136 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 +#include +#include +#ifndef LIBXMP_CORE_PLAYER +#include +#include +#endif +#include "common.h" +#include "memio.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; +} + + +int mgetc(MFILE *m) +{ + if (CAN_READ(m) >= 1) + return *(uint8 *)(m->start + m->pos++); + else + 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) +{ + switch (whence) { + default: + case SEEK_SET: + if (m->size >= 0 && (offset > m->size || offset < 0)) + return -1; + m->pos = offset; + return 0; + case SEEK_CUR: + if (m->size >= 0 && (offset > CAN_READ(m) || offset < -m->pos)) + return -1; + m->pos += offset; + return 0; + case SEEK_END: + if (m->size < 0) + return -1; + m->pos = m->size + offset; + return 0; + } +} + +long mtell(MFILE *m) +{ + return (long)m->pos; +} + +int meof(MFILE *m) +{ + if (m->size <= 0) + return 0; + else + 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; +} + +#ifndef LIBXMP_CORE_PLAYER + +int mstat(MFILE *m, struct stat *st) +{ + memset(st, 0, sizeof (struct stat)); + st->st_size = m->size; + return 0; +} + +#endif + diff --git a/source/libxmp-lite/src/memio.h b/source/libxmp-lite/src/memio.h new file mode 100644 index 000000000..8e8c04fa3 --- /dev/null +++ b/source/libxmp-lite/src/memio.h @@ -0,0 +1,31 @@ +#ifndef LIBXMP_MEMIO_H +#define LIBXMP_MEMIO_H + +#include + +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 *); +#ifndef LIBXMP_CORE_PLAYER +int mstat(MFILE *, struct stat *); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/libxmp-lite/src/mix_all.c b/source/libxmp-lite/src/mix_all.c new file mode 100644 index 000000000..4802c81a4 --- /dev/null +++ b/source/libxmp-lite/src/mix_all.c @@ -0,0 +1,439 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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<> 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 diff --git a/source/libxmp-lite/src/mixer.c b/source/libxmp-lite/src/mixer.c new file mode 100644 index 000000000..e1e70b25f --- /dev/null +++ b/source/libxmp-lite/src/mixer.c @@ -0,0 +1,840 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 +#include +#include +#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 (*mixer_set[])(struct mixer_voice *, int32 *, int, int, int, int, int, int, int); + +static mixer_set 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 mixer_set 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 mixer_set 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 mixer_set a500_mixers = { + libxmp_mix_mono_a500, + NULL, + libxmp_mix_stereo_a500, + NULL, + NULL, + NULL, + NULL, + NULL +}; + + +static mixer_set 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 Tracker’s 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; + void (*mix_fn)(struct mixer_voice *, int32 *, int, int, int, int, int, int, int); + mixer_set *mixers; + + switch (s->interp) { + case XMP_INTERP_NEAREST: + mixers = &nearest_mixers; + break; + case XMP_INTERP_LINEAR: + mixers = &linear_mixers; + break; + case XMP_INTERP_SPLINE: + mixers = &spline_mixers; + break; + default: + mixers = &linear_mixers; + } + +#ifdef LIBXMP_PAULA_SIMULATOR + if (p->flags & XMP_FLAGS_A500) { + if (IS_AMIGA_MOD()) { + if (p->filter) { + mixers = &a500led_mixers; + } else { + mixers = &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 = 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 &= ~FLAG_FILTER; + } +#endif + + mix_fn = (*mixers)[mixer]; + + /* 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 = calloc(2, XMP_MAX_FRAMESIZE); + if (s->buffer == NULL) + goto err; + + s->buf32 = calloc(sizeof(int), 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; */ + 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); + 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; +} diff --git a/source/libxmp-lite/src/mixer.h b/source/libxmp-lite/src/mixer.h new file mode 100644 index 000000000..5ed3b9763 --- /dev/null +++ b/source/libxmp-lite/src/mixer.h @@ -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 */ diff --git a/source/libxmp-lite/src/mod.h b/source/libxmp-lite/src/mod.h new file mode 100644 index 000000000..707d47cf2 --- /dev/null +++ b/source/libxmp-lite/src/mod.h @@ -0,0 +1,55 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 diff --git a/source/libxmp-lite/src/mod_load.c b/source/libxmp-lite/src/mod_load.c new file mode 100644 index 000000000..9ca541351 --- /dev/null +++ b/source/libxmp-lite/src/mod_load.c @@ -0,0 +1,232 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 +#include +#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); + hio_read(mod_event, 1, 4, f); + 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; +} diff --git a/source/libxmp-lite/src/period.c b/source/libxmp-lite/src/period.c new file mode 100644 index 000000000..95a707d26 --- /dev/null +++ b/source/libxmp-lite/src/period.c @@ -0,0 +1,266 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 +#include +#include "common.h" +#include "period.h" + +#include + +#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__) +static inline double round(double val) +{ + return (val >= 0.0)? floor(val + 0.5) : ceil(val - 0.5); +} +#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 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 round(100.0 * (1536.0 / M_LN2) * log(p / d)); + default: + /* Amiga */ + d = libxmp_note_to_period(ctx, n, 0, adj); + return 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; +} diff --git a/source/libxmp-lite/src/period.h b/source/libxmp-lite/src/period.h new file mode 100644 index 000000000..10a8c67dc --- /dev/null +++ b/source/libxmp-lite/src/period.h @@ -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 */ diff --git a/source/libxmp-lite/src/player.c b/source/libxmp-lite/src/player.c new file mode 100644 index 000000000..e2e7f8116 --- /dev/null +++ b/source/libxmp-lite/src/player.c @@ -0,0 +1,1899 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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. + */ + +/* + * Sat, 18 Apr 1998 20:23:07 +0200 Frederic Bujon + * Pan effect bug fixed: In Fastracker II the track panning effect erases + * the instrument panning effect, and the same should happen in xmp. + */ + +/* + * Fri, 26 Jun 1998 13:29:25 -0400 (EDT) + * Reported by Jared Spiegel + * when the volume envelope is not enabled (disabled) on a sample, and a + * notoff is delivered to ft2 (via either a noteoff in the note column or + * command Kxx [where xx is # of ticks into row to give a noteoff to the + * sample]), ft2 will set the volume of playback of the sample to 00h. + * + * Claudio's fix: implementing effect K + */ + +#include +#include +#include "virtual.h" +#include "period.h" +#include "effects.h" +#include "player.h" +#include "mixer.h" +#ifndef LIBXMP_CORE_PLAYER +#include "extras.h" +#endif + +/* Values for multi-retrig */ +static const struct retrig_control rval[] = { + { 0, 1, 1 }, { -1, 1, 1 }, { -2, 1, 1 }, { -4, 1, 1 }, + { -8, 1, 1 }, { -16, 1, 1 }, { 0, 2, 3 }, { 0, 1, 2 }, + { 0, 1, 1 }, { 1, 1, 1 }, { 2, 1, 1 }, { 4, 1, 1 }, + { 8, 1, 1 }, { 16, 1, 1 }, { 0, 3, 2 }, { 0, 2, 1 }, + { 0, 0, 1 } /* Note cut */ + +}; + + +/* + * "Anyway I think this is the most brilliant piece of crap we + * have managed to put up!" + * -- Ice of FC about "Mental Surgery" + */ + + +/* Envelope */ + +static int check_envelope_end(struct xmp_envelope *env, int x) +{ + int16 *data = env->data; + int index; + + if (~env->flg & XMP_ENVELOPE_ON || env->npt <= 0) + return 0; + + index = (env->npt - 1) * 2; + + /* last node */ + if (x >= data[index] || index == 0) { + if (~env->flg & XMP_ENVELOPE_LOOP) { + return 1; + } + } + + return 0; +} + +static int get_envelope(struct xmp_envelope *env, int x, int def) +{ + int x1, x2, y1, y2; + int16 *data = env->data; + int index; + + if (x < 0 || ~env->flg & XMP_ENVELOPE_ON || env->npt <= 0) + return def; + + index = (env->npt - 1) * 2; + + x1 = data[index]; /* last node */ + if (x >= x1 || index == 0) { + return data[index + 1]; + } + + do { + index -= 2; + x1 = data[index]; + } while (index > 0 && x1 > x); + + /* interpolate */ + y1 = data[index + 1]; + x2 = data[index + 2]; + y2 = data[index + 3]; + + return x2 == x1 ? y2 : ((y2 - y1) * (x - x1) / (x2 - x1)) + y1; +} + +static int update_envelope_xm(struct xmp_envelope *env, int x, int release) +{ + int16 *data = env->data; + int has_loop, has_sus; + int lpe, lps, sus; + + has_loop = env->flg & XMP_ENVELOPE_LOOP; + has_sus = env->flg & XMP_ENVELOPE_SUS; + + lps = env->lps << 1; + lpe = env->lpe << 1; + sus = env->sus << 1; + + /* FT2 and IT envelopes behave in a different way regarding loops, + * sustain and release. When the sustain point is at the end of the + * envelope loop end and the key is released, FT2 escapes the loop + * while IT runs another iteration. (See EnvLoops.xm in the OpenMPT + * test cases.) + */ + if (has_loop && has_sus && sus == lpe) { + if (!release) + has_sus = 0; + } + + /* If the envelope point is set to somewhere after the sustain point + * or sustain loop, enable release to prevent the envelope point to + * return to the sustain point or loop start. (See Filip Skutela's + * farewell_tear.xm.) + */ + if (has_loop && x > data[lpe] + 1) { + release = 1; + } else if (has_sus && x > data[sus] + 1) { + release = 1; + } + + /* If enabled, stay at the sustain point */ + if (has_sus && !release) { + if (x >= data[sus]) { + x = data[sus]; + } + } + + /* Envelope loops */ + if (has_loop && x >= data[lpe]) { + if (!(release && has_sus && sus == lpe)) + x = data[lps]; + } + + return x; +} + +#ifndef LIBXMP_CORE_DISABLE_IT + +static int update_envelope_it(struct xmp_envelope *env, int x, int release, int key_off) +{ + int16 *data = env->data; + int has_loop, has_sus; + int lpe, lps, sus, sue; + + has_loop = env->flg & XMP_ENVELOPE_LOOP; + has_sus = env->flg & XMP_ENVELOPE_SUS; + + lps = env->lps << 1; + lpe = env->lpe << 1; + sus = env->sus << 1; + sue = env->sue << 1; + + /* Release at the end of a sustain loop, run another loop */ + if (has_sus && key_off && x == data[sue] + 1) { + x = data[sus]; + } else + /* If enabled, stay in the sustain loop */ + if (has_sus && !release) { + if (x == data[sue] + 1) { + x = data[sus]; + } + } else + /* Finally, execute the envelope loop */ + if (has_loop) { + if (x > data[lpe]) { + x = data[lps]; + } + } + + return x; +} + +#endif + +static int update_envelope(struct xmp_envelope *env, int x, int release, int key_off, int it_env) +{ + if (x < 0xffff) { /* increment tick */ + x++; + } + + if (x < 0) { + return -1; + } + + if (~env->flg & XMP_ENVELOPE_ON || env->npt <= 0) { + return x; + } + +#ifndef LIBXMP_CORE_DISABLE_IT + return it_env ? + update_envelope_it(env, x, release, key_off) : + update_envelope_xm(env, x, release); +#else + return update_envelope_xm(env, x, release); +#endif +} + + +/* Returns: 0 if do nothing, <0 to reset channel, >0 if has fade */ +static int check_envelope_fade(struct xmp_envelope *env, int x) +{ + int16 *data = env->data; + int index; + + if (~env->flg & XMP_ENVELOPE_ON) + return 0; + + index = (env->npt - 1) * 2; /* last node */ + if (x > data[index]) { + if (data[index + 1] == 0) + return -1; + else + return 1; + } + + return 0; +} + + +#ifndef LIBXMP_CORE_PLAYER + +/* From http://www.un4seen.com/forum/?topic=7554.0 + * + * "Invert loop" effect replaces (!) sample data bytes within loop with their + * bitwise complement (NOT). The parameter sets speed of altering the samples. + * This effectively trashes the sample data. Because of that this effect was + * supposed to be removed in the very next ProTracker versions, but it was + * never removed. + * + * Prior to [Protracker 1.1A] this effect is called "Funk Repeat" and it moves + * loop of the instrument (just the loop information - sample data is not + * altered). The parameter is the speed of moving the loop. + */ + +static const int invloop_table[] = { + 0, 5, 6, 7, 8, 10, 11, 13, 16, 19, 22, 26, 32, 43, 64, 128 +}; + +static void update_invloop(struct module_data *m, struct channel_data *xc) +{ + struct xmp_sample *xxs = &m->mod.xxs[xc->smp]; + int len; + + xc->invloop.count += invloop_table[xc->invloop.speed]; + + if ((xxs->flg & XMP_SAMPLE_LOOP) && xc->invloop.count >= 128) { + xc->invloop.count = 0; + len = xxs->lpe - xxs->lps; + + if (++xc->invloop.pos > len) { + xc->invloop.pos = 0; + } + + if (~xxs->flg & XMP_SAMPLE_16BIT) { + xxs->data[xxs->lps + xc->invloop.pos] ^= 0xff; + } + } +} + +#endif + +/* + * From OpenMPT Arpeggio.xm test: + * + * "[FT2] Arpeggio behavior is very weird with more than 16 ticks per row. This + * comes from the fact that Fasttracker 2 uses a LUT for computing the arpeggio + * note (instead of doing something like tick%3 or similar). The LUT only has + * 16 entries, so when there are more than 16 ticks, it reads beyond array + * boundaries. The vibrato table happens to be stored right after arpeggio + * table. The tables look like this in memory: + * + * ArpTab: 0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0 + * VibTab: 0,24,49,74,97,120,141,161,180,197,... + * + * All values except for the first in the vibrato table are greater than 1, so + * they trigger the third arpeggio note. Keep in mind that Fasttracker 2 counts + * downwards, so the table has to be read from back to front, i.e. at 16 ticks + * per row, the 16th entry in the LUT is the first to be read. This is also the + * reason why Arpeggio is played 'backwards' in Fasttracker 2." + */ +static int ft2_arpeggio(struct context_data *ctx, struct channel_data *xc) +{ + struct player_data *p = &ctx->p; + int i; + + if (xc->arpeggio.val[1] == 0 && xc->arpeggio.val[2] == 0) { + return 0; + } + + if (p->frame == 0) { + return 0; + } + + i = p->speed - (p->frame % p->speed); + + if (i == 16) { + return 0; + } else if (i > 16) { + return xc->arpeggio.val[2]; + } + + return xc->arpeggio.val[i % 3]; +} + +static int is_first_frame(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + + switch (m->read_event_type) { +#ifndef LIBXMP_CORE_DISABLE_IT + case READ_EVENT_IT: + /* fall through */ +#endif + case READ_EVENT_ST3: + return p->frame % p->speed == 0; + default: + return p->frame == 0; + } +} + +static void reset_channels(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct smix_data *smix = &ctx->smix; + struct channel_data *xc; + int i; + +#ifndef LIBXMP_CORE_PLAYER + for (i = 0; i < p->virt.virt_channels; i++) { + void *extra; + + xc = &p->xc_data[i]; + extra = xc->extra; + memset(xc, 0, sizeof (struct channel_data)); + xc->extra = extra; + libxmp_reset_channel_extras(ctx, xc); + xc->ins = -1; + xc->old_ins = 1; /* raw value */ + xc->key = -1; + xc->volume = m->volbase; + } +#else + for (i = 0; i < p->virt.virt_channels; i++) { + xc = &p->xc_data[i]; + memset(xc, 0, sizeof (struct channel_data)); + xc->ins = -1; + xc->old_ins = 1; /* raw value */ + xc->key = -1; + xc->volume = m->volbase; + } +#endif + + for (i = 0; i < p->virt.num_tracks; i++) { + xc = &p->xc_data[i]; + + if (i >= mod->chn && i < mod->chn + smix->chn) { + xc->mastervol = 0x40; + xc->pan.val = 0x80; + } else { + xc->mastervol = mod->xxc[i].vol; + xc->pan.val = mod->xxc[i].pan; + } + +#ifndef LIBXMP_CORE_DISABLE_IT + xc->filter.cutoff = 0xff; + + /* Amiga split channel */ + if (mod->xxc[i].flg & XMP_CHANNEL_SPLIT) { + int j; + + xc->split = ((mod->xxc[i].flg & 0x30) >> 4) + 1; + /* Connect split channel pairs */ + for (j = 0; j < i; j++) { + if (mod->xxc[j].flg & XMP_CHANNEL_SPLIT) { + if (p->xc_data[j].split == xc->split) { + p->xc_data[j].pair = i; + xc->pair = j; + } + } + } + } else { + xc->split = 0; + } +#endif + + /* Surround channel */ + if (mod->xxc[i].flg & XMP_CHANNEL_SURROUND) { + xc->pan.surround = 1; + } + } +} + +static int check_delay(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + struct module_data *m = &ctx->m; + + /* Tempo affects delay and must be computed first */ + if ((e->fxt == FX_SPEED && e->fxp < 0x20) || e->fxt == FX_S3M_SPEED) { + if (e->fxp) { + p->speed = e->fxp; + } + } + if ((e->f2t == FX_SPEED && e->f2p < 0x20) || e->f2t == FX_S3M_SPEED) { + if (e->f2p) { + p->speed = e->f2p; + } + } + + /* Delay event read */ + if (e->fxt == FX_EXTENDED && MSN(e->fxp) == EX_DELAY && LSN(e->fxp)) { + xc->delay = LSN(e->fxp) + 1; + goto do_delay; + } + if (e->f2t == FX_EXTENDED && MSN(e->f2p) == EX_DELAY && LSN(e->f2p)) { + xc->delay = LSN(e->f2p) + 1; + goto do_delay; + } + + return 0; + + do_delay: + memcpy(&xc->delayed_event, e, sizeof (struct xmp_event)); + + if (e->ins) { + xc->delayed_ins = e->ins; + } + + if (HAS_QUIRK(QUIRK_RTDELAY)) { + if (e->vol == 0 && e->f2t == 0 && e->ins == 0 && e->note != XMP_KEY_OFF) + xc->delayed_event.vol = xc->volume + 1; + if (e->note == 0) + xc->delayed_event.note = xc->key + 1; + if (e->ins == 0) + xc->delayed_event.ins = xc->old_ins; + } + + return 1; +} + +static inline void read_row(struct context_data *ctx, int pat, int row) +{ + int chn; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct player_data *p = &ctx->p; + struct flow_control *f = &p->flow; + struct xmp_event ev; + + for (chn = 0; chn < mod->chn; chn++) { + const int num_rows = mod->xxt[TRACK_NUM(pat, chn)]->rows; + if (row < num_rows) { + memcpy(&ev, &EVENT(pat, chn, row), sizeof(ev)); + } else { + memset(&ev, 0, sizeof(ev)); + } + + if (ev.note == XMP_KEY_OFF) { + int env_on = 0; + int ins = ev.ins - 1; + + if (IS_VALID_INSTRUMENT(ins) && + (mod->xxi[ins].aei.flg & XMP_ENVELOPE_ON)) { + env_on = 1; + } + + if (ev.fxt == FX_EXTENDED && MSN(ev.fxp) == EX_DELAY) { + if (ev.ins && (LSN(ev.fxp) || env_on)) { + if (LSN(ev.fxp)) { + ev.note = 0; + } + ev.fxp = ev.fxt = 0; + } + } + } + + if (check_delay(ctx, &ev, chn) == 0) { + if (!f->rowdelay_set || f->rowdelay > 0) { + libxmp_read_event(ctx, &ev, chn); +#ifndef LIBXMP_CORE_PLAYER + libxmp_med_hold_hack(ctx, pat, chn, row); +#endif + } + } else { + if (IS_PLAYER_MODE_IT()) { + /* Reset flags. See SlideDelay.it */ + p->xc_data[chn].flags = 0; + } + } + } +} + +static inline int get_channel_vol(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + int root; + + /* channel is a root channel */ + if (chn < p->virt.num_tracks) + return p->channel_vol[chn]; + + /* channel is invalid */ + if (chn >= p->virt.virt_channels) + return 0; + + /* root is invalid */ + root = libxmp_virt_getroot(ctx, chn); + if (root < 0) + return 0; + + return p->channel_vol[root]; +} + +static int tremor_ft2(struct context_data *ctx, int chn, int finalvol) +{ + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + + if (xc->tremor.count & 0x80) { + if (TEST(TREMOR) && p->frame != 0) { + xc->tremor.count &= ~0x20; + if (xc->tremor.count == 0x80) { + /* end of down cycle, set up counter for up */ + xc->tremor.count = xc->tremor.up | 0xc0; + } else if (xc->tremor.count == 0xc0) { + /* end of up cycle, set up counter for down */ + xc->tremor.count = xc->tremor.down | 0x80; + } else { + xc->tremor.count--; + } + } + + if ((xc->tremor.count & 0xe0) == 0x80) { + finalvol = 0; + } + } + + return finalvol; +} + +static int tremor_s3m(struct context_data *ctx, int chn, int finalvol) +{ + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + + if (TEST(TREMOR)) { + if (xc->tremor.count == 0) { + /* end of down cycle, set up counter for up */ + xc->tremor.count = xc->tremor.up | 0x80; + } else if (xc->tremor.count == 0x80) { + /* end of up cycle, set up counter for down */ + xc->tremor.count = xc->tremor.down; + } + + xc->tremor.count--; + + if (~xc->tremor.count & 0x80) { + finalvol = 0; + } + } + + return finalvol; +} + +/* + * Update channel data + */ + +#define DOENV_RELEASE ((TEST_NOTE(NOTE_RELEASE) || act == VIRT_ACTION_OFF)) + +static void process_volume(struct context_data *ctx, int chn, int act) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct channel_data *xc = &p->xc_data[chn]; + struct xmp_instrument *instrument; + int finalvol; + uint16 vol_envelope; + int fade = 0; + + instrument = libxmp_get_instrument(ctx, xc->ins); + + /* Keyoff and fadeout */ + + /* Keyoff event in IT doesn't reset fadeout (see jeff93.it) + * In XM it depends on envelope (see graff-strange_land.xm vs + * Decibelter - Cosmic 'Wegian Mamas.xm) + */ + if (HAS_QUIRK(QUIRK_KEYOFF)) { + /* If IT, only apply fadeout on note release if we don't + * have envelope, or if we have envelope loop + */ + if (TEST_NOTE(NOTE_RELEASE) || act == VIRT_ACTION_OFF) { + if ((~instrument->aei.flg & XMP_ENVELOPE_ON) || + (instrument->aei.flg & XMP_ENVELOPE_LOOP)) { + fade = 1; + } + } + } else { + if (~instrument->aei.flg & XMP_ENVELOPE_ON) { + if (TEST_NOTE(NOTE_RELEASE)) { + xc->fadeout = 0; + } + } + + if (TEST_NOTE(NOTE_RELEASE) || act == VIRT_ACTION_OFF) { + fade = 1; + } + } + + if (TEST_NOTE(NOTE_FADEOUT) || act == VIRT_ACTION_FADE) { + fade = 1; + } + + if (fade) { + if (xc->fadeout > xc->ins_fade) { + xc->fadeout -= xc->ins_fade; + } else { + xc->fadeout = 0; + SET_NOTE(NOTE_END); + } + } + + switch (check_envelope_fade(&instrument->aei, xc->v_idx)) { + case -1: + SET_NOTE(NOTE_END); + /* Don't reset channel, we may have a tone portamento later + * virt_resetchannel(ctx, chn); + */ + break; + case 0: + break; + default: + if (HAS_QUIRK(QUIRK_ENVFADE)) { + SET_NOTE(NOTE_FADEOUT); + } + } + + if (!TEST_PER(VENV_PAUSE)) { + xc->v_idx = update_envelope(&instrument->aei, xc->v_idx, + DOENV_RELEASE, TEST(KEY_OFF), IS_PLAYER_MODE_IT()); + } + + vol_envelope = get_envelope(&instrument->aei, xc->v_idx, 64); + if (check_envelope_end(&instrument->aei, xc->v_idx)) { + if (vol_envelope == 0) { + SET_NOTE(NOTE_END); + } + SET_NOTE(NOTE_ENV_END); + } + + /* If note ended in background channel, we can safely reset it */ + if (TEST_NOTE(NOTE_END) && chn >= p->virt.num_tracks) { + libxmp_virt_resetchannel(ctx, chn); + return; + } + +#ifndef LIBXMP_CORE_PLAYER + finalvol = libxmp_extras_get_volume(ctx, xc); +#else + finalvol = xc->volume; +#endif + + if (IS_PLAYER_MODE_IT()) { + finalvol = xc->volume * (100 - xc->rvv) / 100; + } + + if (TEST(TREMOLO)) { + /* OpenMPT VibratoReset.mod */ + if (!is_first_frame(ctx) || !HAS_QUIRK(QUIRK_PROTRACK)) { + finalvol += libxmp_lfo_get(ctx, &xc->tremolo.lfo, 0) / (1 << 6); + } + + if (!is_first_frame(ctx) || HAS_QUIRK(QUIRK_VIBALL)) { + libxmp_lfo_update(&xc->tremolo.lfo); + } + } + + CLAMP(finalvol, 0, m->volbase); + + finalvol = (finalvol * xc->fadeout) >> 6; /* 16 bit output */ + + finalvol = (uint32)(vol_envelope * p->gvol * xc->mastervol / + m->gvolbase * ((int)finalvol * 0x40 / m->volbase)) >> 18; + + /* Apply channel volume */ + finalvol = finalvol * get_channel_vol(ctx, chn) / 100; + +#ifndef LIBXMP_CORE_PLAYER + /* Volume translation table (for PTM, ARCH, COCO) */ + if (m->vol_table) { + finalvol = m->volbase == 0xff ? + m->vol_table[finalvol >> 2] << 2 : + m->vol_table[finalvol >> 4] << 4; + } +#endif + + if (HAS_QUIRK(QUIRK_INSVOL)) { + finalvol = (finalvol * instrument->vol * xc->gvl) >> 12; + } + + if (IS_PLAYER_MODE_FT2()) { + finalvol = tremor_ft2(ctx, chn, finalvol); + } else { + finalvol = tremor_s3m(ctx, chn, finalvol); + } + + if (chn < m->mod.chn) { + finalvol = finalvol * p->master_vol / 100; + } else { + finalvol = finalvol * p->smix_vol / 100; + } + + xc->info_finalvol = TEST_NOTE(NOTE_SAMPLE_END) ? 0 : finalvol; + + libxmp_virt_setvol(ctx, chn, finalvol); + + /* Check Amiga split channel */ + if (xc->split) { + libxmp_virt_setvol(ctx, xc->pair, finalvol); + } +} + +static void process_frequency(struct context_data *ctx, int chn, int act) +{ +#ifndef LIBXMP_CORE_DISABLE_IT + struct mixer_data *s = &ctx->s; +#endif + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct channel_data *xc = &p->xc_data[chn]; + struct xmp_instrument *instrument; + double period, vibrato; + int linear_bend; + int frq_envelope; + int arp; +#ifndef LIBXMP_CORE_DISABLE_IT + int cutoff, resonance; +#endif + + instrument = libxmp_get_instrument(ctx, xc->ins); + + if (!TEST_PER(FENV_PAUSE)) { + xc->f_idx = update_envelope(&instrument->fei, xc->f_idx, + DOENV_RELEASE, TEST(KEY_OFF), IS_PLAYER_MODE_IT()); + } + frq_envelope = get_envelope(&instrument->fei, xc->f_idx, 0); + +#ifndef LIBXMP_CORE_PLAYER + /* Do note slide */ + + if (TEST(NOTE_SLIDE)) { + if (xc->noteslide.count == 0) { + xc->note += xc->noteslide.slide; + xc->period = libxmp_note_to_period(ctx, xc->note, + xc->finetune, xc->per_adj); + xc->noteslide.count = xc->noteslide.speed; + } + xc->noteslide.count--; + + libxmp_virt_setnote(ctx, chn, xc->note); + } +#endif + + /* Instrument vibrato */ + vibrato = 1.0 * libxmp_lfo_get(ctx, &xc->insvib.lfo, 1) / + (4096 * (1 + xc->insvib.sweep)); + libxmp_lfo_update(&xc->insvib.lfo); + if (xc->insvib.sweep > 1) { + xc->insvib.sweep -= 2; + } else { + xc->insvib.sweep = 0; + } + + /* Vibrato */ + if (TEST(VIBRATO) || TEST_PER(VIBRATO)) { + /* OpenMPT VibratoReset.mod */ + if (!is_first_frame(ctx) || !HAS_QUIRK(QUIRK_PROTRACK)) { + int shift = HAS_QUIRK(QUIRK_VIBHALF) ? 10 : 9; + int vib = libxmp_lfo_get(ctx, &xc->vibrato.lfo, 1) / (1 << shift); + + if (HAS_QUIRK(QUIRK_VIBINV)) { + vibrato -= vib; + } else { + vibrato += vib; + } + } + + if (!is_first_frame(ctx) || HAS_QUIRK(QUIRK_VIBALL)) { + libxmp_lfo_update(&xc->vibrato.lfo); + } + } + + period = xc->period; +#ifndef LIBXMP_CORE_PLAYER + period += libxmp_extras_get_period(ctx, xc); +#endif + + /* Sanity check */ + if (period < 0.1) { + period = 0.1; + } + + /* Arpeggio */ + + if (HAS_QUIRK(QUIRK_FT2BUGS)) { + arp = ft2_arpeggio(ctx, xc); + } else { + arp = xc->arpeggio.val[xc->arpeggio.count]; + } + + /* Pitch bend */ + + /* From OpenMPT PeriodLimit.s3m: + * "ScreamTracker 3 limits the final output period to be at least 64, + * i.e. when playing a note that is too high or when sliding the + * period lower than 64, the output period will simply be clamped to + * 64. However, when reaching a period of 0 through slides, the + * output on the channel should be stopped." + */ + /* ST3 uses periods*4, so the limit is 16. Adjusted to the exact + * A6 value because we compute periods in floating point. + */ + if (HAS_QUIRK(QUIRK_ST3BUGS)) { + if (period < 16.239270) { /* A6 */ + period = 16.239270; + } + } + + linear_bend = libxmp_period_to_bend(ctx, period + vibrato, xc->note, + xc->per_adj); + + if (TEST_NOTE(NOTE_GLISSANDO) && TEST(TONEPORTA)) { + if (linear_bend > 0) { + linear_bend = (linear_bend + 6400) / 12800 * 12800; + } else if (linear_bend < 0) { + linear_bend = (linear_bend - 6400) / 12800 * 12800; + } + } + + if (HAS_QUIRK(QUIRK_FT2BUGS)) { + if (arp) { + /* OpenMPT ArpSlide.xm */ + linear_bend = linear_bend / 12800 * 12800 + + xc->finetune * 100; + + /* OpenMPT ArpeggioClamp.xm */ + if (xc->note + arp > 107) { + if (p->speed - (p->frame % p->speed) > 0) { + arp = 108 - xc->note; + } + } + } + } + + /* Envelope */ + + if (xc->f_idx >= 0 && (~instrument->fei.flg & XMP_ENVELOPE_FLT)) { + /* IT pitch envelopes are always linear, even in Amiga period + * mode. Each unit in the envelope scale is 1/25 semitone. + */ + linear_bend += frq_envelope << 7; + } + + /* Arpeggio */ + + if (arp != 0) { + linear_bend += (100 << 7) * arp; + + /* OpenMPT ArpWrapAround.mod */ + if (HAS_QUIRK(QUIRK_PROTRACK)) { + if (xc->note + arp > MAX_NOTE_MOD + 1) { + linear_bend -= 12800 * (3 * 12); + } else if (xc->note + arp > MAX_NOTE_MOD) { + libxmp_virt_setvol(ctx, chn, 0); + } + } + } + + +#ifndef LIBXMP_CORE_PLAYER + linear_bend += libxmp_extras_get_linear_bend(ctx, xc); +#endif + + period = libxmp_note_to_period_mix(xc->note, linear_bend); + libxmp_virt_setperiod(ctx, chn, period); + + /* For xmp_get_frame_info() */ + xc->info_pitchbend = linear_bend >> 7; + xc->info_period = period * 4096; + + if (IS_PERIOD_MODRNG()) { + CLAMP(xc->info_period, + libxmp_note_to_period(ctx, MAX_NOTE_MOD, xc->finetune, 0) * 4096, + libxmp_note_to_period(ctx, MIN_NOTE_MOD, xc->finetune, 0) * 4096); + } else if (xc->info_period < (1 << 12)) { + xc->info_period = (1 << 12); + } + + +#ifndef LIBXMP_CORE_DISABLE_IT + + /* Process filter */ + + if (!HAS_QUIRK(QUIRK_FILTER)) { + return; + } + + if (xc->f_idx >= 0 && (instrument->fei.flg & XMP_ENVELOPE_FLT)) { + if (frq_envelope < 0xfe) { + xc->filter.envelope = frq_envelope; + } + cutoff = xc->filter.cutoff * xc->filter.envelope >> 8; + } else { + cutoff = xc->filter.cutoff; + } + resonance = xc->filter.resonance; + + if (cutoff > 0xff) { + cutoff = 0xff; + } else if (cutoff < 0xff) { + int a0, b0, b1; + libxmp_filter_setup(s->freq, cutoff, resonance, &a0, &b0, &b1); + libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_A0, a0); + libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_B0, b0); + libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_B1, b1); + libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_RESONANCE, resonance); + } + + /* Always set cutoff */ + libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_CUTOFF, cutoff); + +#endif +} + +static void process_pan(struct context_data *ctx, int chn, int act) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct mixer_data *s = &ctx->s; + struct channel_data *xc = &p->xc_data[chn]; + struct xmp_instrument *instrument; + int finalpan, panbrello = 0; + int pan_envelope; + int channel_pan; + + instrument = libxmp_get_instrument(ctx, xc->ins); + + if (!TEST_PER(PENV_PAUSE)) { + xc->p_idx = update_envelope(&instrument->pei, xc->p_idx, + DOENV_RELEASE, TEST(KEY_OFF), IS_PLAYER_MODE_IT()); + } + pan_envelope = get_envelope(&instrument->pei, xc->p_idx, 32); + +#ifndef LIBXMP_CORE_DISABLE_IT + if (TEST(PANBRELLO)) { + panbrello = libxmp_lfo_get(ctx, &xc->panbrello.lfo, 0) / 512; + if (is_first_frame(ctx)) { + libxmp_lfo_update(&xc->panbrello.lfo); + } + } +#endif + + channel_pan = xc->pan.val; + +#if 0 +#ifdef LIBXMP_PAULA_SIMULATOR + /* Always use 100% pan separation in Amiga mode */ + if (p->flags & XMP_FLAGS_A500) { + if (IS_AMIGA_MOD()) { + channel_pan = channel_pan < 0x80 ? 0 : 0xff; + } + } +#endif +#endif + + finalpan = channel_pan + panbrello + (pan_envelope - 32) * + (128 - abs(xc->pan.val - 128)) / 32; + + if (IS_PLAYER_MODE_IT()) { + finalpan = finalpan + xc->rpv * 4; + } + + CLAMP(finalpan, 0, 255); + + if (s->format & XMP_FORMAT_MONO || xc->pan.surround) { + finalpan = 0; + } else { + finalpan = (finalpan - 0x80) * s->mix / 100; + } + + xc->info_finalpan = finalpan + 0x80; + + if (xc->pan.surround) { + libxmp_virt_setpan(ctx, chn, PAN_SURROUND); + } else { + libxmp_virt_setpan(ctx, chn, finalpan); + } +} + +static void update_volume(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; +#ifndef LIBXMP_CORE_DISABLE_IT + struct flow_control *f = &p->flow; +#endif + struct channel_data *xc = &p->xc_data[chn]; + + /* Volume slides happen in all frames but the first, except when the + * "volume slide on all frames" flag is set. + */ + if (p->frame % p->speed != 0 || HAS_QUIRK(QUIRK_VSALL)) { + if (TEST(GVOL_SLIDE)) { + p->gvol += xc->gvol.slide; + } + + if (TEST(VOL_SLIDE) || TEST_PER(VOL_SLIDE)) { + xc->volume += xc->vol.slide; + } + +#ifndef LIBXMP_CORE_PLAYER + if (TEST_PER(VOL_SLIDE)) { + if (xc->vol.slide > 0 && xc->volume > m->volbase) { + xc->volume = m->volbase; + RESET_PER(VOL_SLIDE); + } + if (xc->vol.slide < 0 && xc->volume < 0) { + xc->volume = 0; + RESET_PER(VOL_SLIDE); + } + } +#endif + + if (TEST(VOL_SLIDE_2)) { + xc->volume += xc->vol.slide2; + } + if (TEST(TRK_VSLIDE)) { + xc->mastervol += xc->trackvol.slide; + } + } + + if (p->frame % p->speed == 0) { + /* Process "fine" effects */ + if (TEST(FINE_VOLS)) { + xc->volume += xc->vol.fslide; + } + +#ifndef LIBXMP_CORE_DISABLE_IT + if (TEST(FINE_VOLS_2)) { + /* OpenMPT FineVolColSlide.it: + * Unlike fine volume slides in the effect column, + * fine volume slides in the volume column are only + * ever executed on the first tick -- not on multiples + * of the first tick if there is a pattern delay. + */ + if (!f->rowdelay_set || f->rowdelay_set & 2) { + xc->volume += xc->vol.fslide2; + } + } + f->rowdelay_set &= ~2; +#endif + + if (TEST(TRK_FVSLIDE)) { + xc->mastervol += xc->trackvol.fslide; + } + + if (TEST(GVOL_SLIDE)) { + p->gvol += xc->gvol.fslide; + } + } + + /* Clamp volumes */ + CLAMP(xc->volume, 0, m->volbase); + CLAMP(p->gvol, 0, m->gvolbase); + CLAMP(xc->mastervol, 0, m->volbase); + + if (xc->split) { + p->xc_data[xc->pair].volume = xc->volume; + } +} + +static void update_frequency(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct channel_data *xc = &p->xc_data[chn]; + + if (!is_first_frame(ctx) || HAS_QUIRK(QUIRK_PBALL)) { + if (TEST(PITCHBEND) || TEST_PER(PITCHBEND)) { + xc->period += xc->freq.slide; + if (HAS_QUIRK(QUIRK_PROTRACK)) { + xc->porta.target = xc->period; + } + } + + /* Do tone portamento */ + if (TEST(TONEPORTA) || TEST_PER(TONEPORTA)) { + if (xc->porta.target > 0) { + int end = 0; + if (xc->porta.dir > 0) { + xc->period += xc->porta.slide; + if (xc->period >= xc->porta.target) + end = 1; + } else { + xc->period -= xc->porta.slide; + if (xc->period <= xc->porta.target) + end = 1; + } + + if (end) { + /* reached end */ + xc->period = xc->porta.target; + xc->porta.dir = 0; + RESET(TONEPORTA); + RESET_PER(TONEPORTA); + + if (HAS_QUIRK(QUIRK_PROTRACK)) { + xc->porta.target = -1; + } + } + } + } + } + + if (is_first_frame(ctx)) { + if (TEST(FINE_BEND)) { + xc->period += xc->freq.fslide; + } + +#ifndef LIBXMP_CORE_PLAYER + if (TEST(FINE_NSLIDE)) { + xc->note += xc->noteslide.fslide; + xc->period = libxmp_note_to_period(ctx, xc->note, + xc->finetune, xc->per_adj); + } +#endif + } + + switch (m->period_type) { + case PERIOD_LINEAR: + CLAMP(xc->period, MIN_PERIOD_L, MAX_PERIOD_L); + break; + case PERIOD_MODRNG: + CLAMP(xc->period, + libxmp_note_to_period(ctx, MAX_NOTE_MOD, xc->finetune, 0), + libxmp_note_to_period(ctx, MIN_NOTE_MOD, xc->finetune, 0)); + break; + } + + xc->arpeggio.count++; + xc->arpeggio.count %= xc->arpeggio.size; + + /* Check for invalid periods (from Toru Egashira's NSPmod) + * panic.s3m has negative periods + * ambio.it uses low (~8) period values + */ + if (xc->period < 0.25) { + libxmp_virt_setvol(ctx, chn, 0); + } +} + +static void update_pan(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + + if (TEST(PAN_SLIDE)) { + if (is_first_frame(ctx)) { + xc->pan.val += xc->pan.fslide; + } else { + xc->pan.val += xc->pan.slide; + } + + if (xc->pan.val < 0) { + xc->pan.val = 0; + } else if (xc->pan.val > 0xff) { + xc->pan.val = 0xff; + } + } +} + +static void play_channel(struct context_data *ctx, int chn) +{ + 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 channel_data *xc = &p->xc_data[chn]; + int act; + + xc->info_finalvol = 0; + +#ifndef LIBXMP_CORE_DISABLE_IT + /* IT tempo slide */ + if (!is_first_frame(ctx) && TEST(TEMPO_SLIDE)) { + p->bpm += xc->tempo.slide; + CLAMP(p->bpm, 0x20, 0xff); + } +#endif + + /* Do delay */ + if (xc->delay > 0) { + if (--xc->delay == 0) { + libxmp_read_event(ctx, &xc->delayed_event, chn); + } + } + + act = libxmp_virt_cstat(ctx, chn); + if (act == VIRT_INVALID) { + /* We need this to keep processing global volume slides */ + update_volume(ctx, chn); + return; + } + + if (p->frame == 0 && act != VIRT_ACTIVE) { + if (!IS_VALID_INSTRUMENT_OR_SFX(xc->ins) || act == VIRT_ACTION_CUT) { + libxmp_virt_resetchannel(ctx, chn); + return; + } + } + + if (!IS_VALID_INSTRUMENT_OR_SFX(xc->ins)) + return; + +#ifndef LIBXMP_CORE_PLAYER + libxmp_play_extras(ctx, xc, chn); +#endif + + /* Do cut/retrig */ + if (TEST(RETRIG)) { + int cond = HAS_QUIRK(QUIRK_S3MRTG) ? + --xc->retrig.count <= 0 : + --xc->retrig.count == 0; + + if (cond) { + if (xc->retrig.type < 0x10) { + /* don't retrig on cut */ + libxmp_virt_voicepos(ctx, chn, 0); + } else { + SET_NOTE(NOTE_END); + } + xc->volume += rval[xc->retrig.type].s; + xc->volume *= rval[xc->retrig.type].m; + xc->volume /= rval[xc->retrig.type].d; + xc->retrig.count = LSN(xc->retrig.val); + } + } + + /* Do keyoff */ + if (xc->keyoff) { + if (--xc->keyoff == 0) + SET_NOTE(NOTE_RELEASE); + } + + libxmp_virt_release(ctx, chn, TEST_NOTE(NOTE_RELEASE)); + + process_volume(ctx, chn, act); + process_frequency(ctx, chn, act); + process_pan(ctx, chn, act); + + update_volume(ctx, chn); + update_frequency(ctx, chn); + update_pan(ctx, chn); + +#ifndef LIBXMP_CORE_PLAYER + if (HAS_QUIRK(QUIRK_PROTRACK) && xc->ins < mod->ins) { + update_invloop(m, xc); + } +#endif + + if (TEST_NOTE(NOTE_SUSEXIT)) { + SET_NOTE(NOTE_RELEASE); + } + + xc->info_position = libxmp_virt_getvoicepos(ctx, chn); +} + +/* + * Event injection + */ + +static void inject_event(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct smix_data *smix = &ctx->smix; + int chn; + + for (chn = 0; chn < mod->chn + smix->chn; chn++) { + struct xmp_event *e = &p->inject_event[chn]; + if (e->_flag > 0) { + libxmp_read_event(ctx, e, chn); + e->_flag = 0; + } + } +} + +/* + * Sequencing + */ + +static void next_order(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct flow_control *f = &p->flow; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + int mark; + + do { + p->ord++; + + /* Restart module */ + mark = HAS_QUIRK(QUIRK_MARKER) && mod->xxo[p->ord] == 0xff; + if (p->ord >= mod->len || mark) { + if (mod->rst > mod->len || + mod->xxo[mod->rst] >= mod->pat || + p->ord < m->seq_data[p->sequence].entry_point) { + p->ord = m->seq_data[p->sequence].entry_point; + } else { + if (libxmp_get_sequence(ctx, mod->rst) == p->sequence) { + p->ord = mod->rst; + } else { + p->ord = m->seq_data[p->sequence].entry_point; + } + } + + p->gvol = m->xxo_info[p->ord].gvl; + } + } while (mod->xxo[p->ord] >= mod->pat); + + p->current_time = m->xxo_info[p->ord].time; + + f->num_rows = mod->xxp[mod->xxo[p->ord]]->rows; + if (f->jumpline >= f->num_rows) + f->jumpline = 0; + p->row = f->jumpline; + f->jumpline = 0; + + p->pos = p->ord; + p->frame = 0; + +#ifndef LIBXMP_CORE_PLAYER + /* Reset persistent effects at new pattern */ + if (HAS_QUIRK(QUIRK_PERPAT)) { + int chn; + for (chn = 0; chn < mod->chn; chn++) { + p->xc_data[chn].per_flags = 0; + } + } +#endif +} + +static void next_row(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct flow_control *f = &p->flow; + + p->frame = 0; + f->delay = 0; + + if (f->pbreak) { + f->pbreak = 0; + + if (f->jump != -1) { + p->ord = f->jump - 1; + f->jump = -1; + } + + next_order(ctx); + } else { + if (f->loop_chn) { + p->row = f->loop[f->loop_chn - 1].start - 1; + f->loop_chn = 0; + } + + if (f->rowdelay == 0) { + p->row++; + f->rowdelay_set = 0; + } else { + f->rowdelay--; + } + + /* check end of pattern */ + if (p->row >= f->num_rows) { + next_order(ctx); + } + } +} + +#ifndef LIBXMP_CORE_DISABLE_IT + +/* + * Set note action for libxmp_virt_pastnote + */ +void libxmp_player_set_release(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + + SET_NOTE(NOTE_RELEASE); +} + +void libxmp_player_set_fadeout(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + + SET_NOTE(NOTE_FADEOUT); +} + +#endif + +static void update_from_ord_info(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct ord_data *oinfo = &m->xxo_info[p->ord]; + + if (oinfo->speed) + p->speed = oinfo->speed; + p->bpm = oinfo->bpm; + p->gvol = oinfo->gvl; + p->current_time = oinfo->time; + p->frame_time = m->time_factor * m->rrate / p->bpm; + +#ifndef LIBXMP_CORE_PLAYER + p->st26_speed = oinfo->st26_speed; +#endif +} + +int xmp_start_player(xmp_context opaque, int rate, int format) +{ + 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 flow_control *f = &p->flow; + int i; + int ret = 0; + + if (rate < XMP_MIN_SRATE || rate > XMP_MAX_SRATE) + return -XMP_ERROR_INVALID; + + if (ctx->state < XMP_STATE_LOADED) + return -XMP_ERROR_STATE; + + if (ctx->state > XMP_STATE_LOADED) + xmp_end_player(opaque); + + if (libxmp_mixer_on(ctx, rate, format, m->c4rate) < 0) + return -XMP_ERROR_INTERNAL; + + p->master_vol = 100; + p->smix_vol = 100; + p->gvol = m->volbase; + p->pos = p->ord = 0; + p->frame = -1; + p->row = 0; + p->current_time = 0; + p->loop_count = 0; + p->sequence = 0; + + /* Unmute all channels and set default volume */ + for (i = 0; i < XMP_MAX_CHANNELS; i++) { + p->channel_mute[i] = 0; + p->channel_vol[i] = 100; + } + + /* Skip invalid patterns at start (the seventh laboratory.it) */ + while (p->ord < mod->len && mod->xxo[p->ord] >= mod->pat) { + p->ord++; + } + /* Check if all positions skipped */ + if (p->ord >= mod->len) { + mod->len = 0; + } + + if (mod->len == 0 || mod->chn == 0) { + /* set variables to sane state */ + p->ord = p->scan[0].ord = 0; + p->row = p->scan[0].row = 0; + f->end_point = 0; + f->num_rows = 0; + } else { + f->num_rows = mod->xxp[mod->xxo[p->ord]]->rows; + f->end_point = p->scan[0].num; + } + + update_from_ord_info(ctx); + + if (libxmp_virt_on(ctx, mod->chn + smix->chn) != 0) { + ret = -XMP_ERROR_INTERNAL; + goto err; + } + + f->delay = 0; + f->jumpline = 0; + f->jump = -1; + f->pbreak = 0; + f->rowdelay_set = 0; + + f->loop = calloc(p->virt.virt_channels, sizeof(struct pattern_loop)); + if (f->loop == NULL) { + ret = -XMP_ERROR_SYSTEM; + goto err; + } + + p->xc_data = calloc(p->virt.virt_channels, sizeof(struct channel_data)); + if (p->xc_data == NULL) { + ret = -XMP_ERROR_SYSTEM; + goto err1; + } + +#ifndef LIBXMP_CORE_PLAYER + for (i = 0; i < p->virt.virt_channels; i++) { + struct channel_data *xc = &p->xc_data[i]; + if (libxmp_new_channel_extras(ctx, xc) < 0) + goto err2; + } +#endif + reset_channels(ctx); + + ctx->state = XMP_STATE_PLAYING; + + return 0; + +#ifndef LIBXMP_CORE_PLAYER + err2: + free(p->xc_data); +#endif + err1: + free(f->loop); + err: + return ret; +} + +static void check_end_of_module(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct flow_control *f = &p->flow; + + /* check end of module */ + if (p->ord == p->scan[p->sequence].ord && + p->row == p->scan[p->sequence].row) { + if (f->end_point == 0) { + p->loop_count++; + f->end_point = p->scan[p->sequence].num; + /* return -1; */ + } + f->end_point--; + } +} + +int xmp_play_frame(xmp_context opaque) +{ + 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 i; + + if (ctx->state < XMP_STATE_PLAYING) + return -XMP_ERROR_STATE; + + if (mod->len <= 0) { + return -XMP_END; + } + + if (HAS_QUIRK(QUIRK_MARKER) && mod->xxo[p->ord] == 0xff) { + return -XMP_END; + } + + /* check reposition */ + if (p->ord != p->pos) { + int start = m->seq_data[p->sequence].entry_point; + + if (p->pos == -2) { /* set by xmp_module_stop */ + return -XMP_END; /* that's all folks */ + } + + if (p->pos == -1) { + /* restart sequence */ + p->pos = start; + } + + if (p->pos == start) { + f->end_point = p->scan[p->sequence].num; + } + + /* Check if lands after a loop point */ + if (p->pos > p->scan[p->sequence].ord) { + f->end_point = 0; + } + + f->jumpline = 0; + f->jump = -1; + + p->ord = p->pos - 1; + + /* Stay inside our subsong */ + if (p->ord < start) { + p->ord = start - 1; + } + + next_order(ctx); + + update_from_ord_info(ctx); + + libxmp_virt_reset(ctx); + reset_channels(ctx); + } else { + p->frame++; + if (p->frame >= (p->speed * (1 + f->delay))) { + /* If break during pattern delay, next row is skipped. + * See corruption.mod order 1D (pattern 0D) last line: + * EE2 + D31 ignores D00 in order 1C line 31. Reported + * by The Welder , Jan 14 2012 + */ + if (HAS_QUIRK(QUIRK_PROTRACK) && f->delay && f->pbreak) + { + next_row(ctx); + check_end_of_module(ctx); + } + next_row(ctx); + } + } + + for (i = 0; i < mod->chn; i++) { + struct channel_data *xc = &p->xc_data[i]; + RESET(KEY_OFF); + } + + /* check new row */ + + if (p->frame == 0) { /* first frame in row */ + check_end_of_module(ctx); + read_row(ctx, mod->xxo[p->ord], p->row); + +#ifndef LIBXMP_CORE_PLAYER + if (p->st26_speed) { + if (p->st26_speed & 0x10000) { + p->speed = (p->st26_speed & 0xff00) >> 8; + } else { + p->speed = p->st26_speed & 0xff; + } + p->st26_speed ^= 0x10000; + } +#endif + } + + inject_event(ctx); + + /* play_frame */ + for (i = 0; i < p->virt.virt_channels; i++) { + play_channel(ctx, i); + } + + p->frame_time = m->time_factor * m->rrate / p->bpm; + p->current_time += p->frame_time; + + libxmp_mixer_softmixer(ctx); + + return 0; +} + +int xmp_play_buffer(xmp_context opaque, void *out_buffer, int size, int loop) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + int ret = 0, filled = 0, copy_size; + struct xmp_frame_info fi; + + /* Reset internal state + * Syncs buffer start with frame start */ + if (out_buffer == NULL) { + p->loop_count = 0; + p->buffer_data.consumed = 0; + p->buffer_data.in_size = 0; + return 0; + } + + if (ctx->state < XMP_STATE_PLAYING) + return -XMP_ERROR_STATE; + + /* Fill buffer */ + while (filled < size) { + /* Check if buffer full */ + if (p->buffer_data.consumed == p->buffer_data.in_size) { + ret = xmp_play_frame(opaque); + xmp_get_frame_info(opaque, &fi); + + /* Check end of module */ + if (ret < 0 || (loop > 0 && fi.loop_count >= loop)) { + /* Start of frame, return end of replay */ + if (filled == 0) { + p->buffer_data.consumed = 0; + p->buffer_data.in_size = 0; + return -1; + } + + /* Fill remaining of this buffer */ + memset((char *)out_buffer + filled, 0, size - filled); + return 0; + } + + p->buffer_data.consumed = 0; + p->buffer_data.in_buffer = fi.buffer; + p->buffer_data.in_size = fi.buffer_size; + } + + /* Copy frame data to user buffer */ + copy_size = MIN(size - filled, p->buffer_data.in_size - + p->buffer_data.consumed); + memcpy((char *)out_buffer + filled, p->buffer_data.in_buffer + + p->buffer_data.consumed, copy_size); + p->buffer_data.consumed += copy_size; + filled += copy_size; + } + + return ret; +} + +void xmp_end_player(xmp_context opaque) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct flow_control *f = &p->flow; +#ifndef LIBXMP_CORE_PLAYER + struct channel_data *xc; + int i; +#endif + + if (ctx->state < XMP_STATE_PLAYING) + return; + + ctx->state = XMP_STATE_LOADED; + +#ifndef LIBXMP_CORE_PLAYER + /* Free channel extras */ + for (i = 0; i < p->virt.virt_channels; i++) { + xc = &p->xc_data[i]; + libxmp_release_channel_extras(ctx, xc); + } +#endif + + libxmp_virt_off(ctx); + + free(p->xc_data); + free(f->loop); + + p->xc_data = NULL; + f->loop = NULL; + + libxmp_mixer_off(ctx); +} + +void xmp_get_module_info(xmp_context opaque, struct xmp_module_info *info) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + + if (ctx->state < XMP_STATE_LOADED) + return; + + memcpy(info->md5, m->md5, 16); + info->mod = mod; + info->comment = m->comment; + info->num_sequences = m->num_sequences; + info->seq_data = m->seq_data; + info->vol_base = m->volbase; +} + +void xmp_get_frame_info(xmp_context opaque, struct xmp_frame_info *info) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct mixer_data *s = &ctx->s; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + int chn, i; + + if (ctx->state < XMP_STATE_LOADED) + return; + + chn = mod->chn; + + if (p->pos >= 0 && p->pos < mod->len) { + info->pos = p->pos; + } else { + info->pos = 0; + } + + info->pattern = mod->xxo[info->pos]; + + if (info->pattern < mod->pat) { + info->num_rows = mod->xxp[info->pattern]->rows; + } else { + info->num_rows = 0; + } + + info->row = p->row; + info->frame = p->frame; + info->speed = p->speed; + info->bpm = p->bpm; + info->total_time = p->scan[p->sequence].time; + info->frame_time = p->frame_time * 1000; + info->time = p->current_time; + info->buffer = s->buffer; + + info->total_size = XMP_MAX_FRAMESIZE; + info->buffer_size = s->ticksize; + if (~s->format & XMP_FORMAT_MONO) { + info->buffer_size *= 2; + } + if (~s->format & XMP_FORMAT_8BIT) { + info->buffer_size *= 2; + } + + info->volume = p->gvol; + info->loop_count = p->loop_count; + info->virt_channels = p->virt.virt_channels; + info->virt_used = p->virt.virt_used; + + info->sequence = p->sequence; + + if (p->xc_data != NULL) { + for (i = 0; i < chn; i++) { + struct channel_data *c = &p->xc_data[i]; + struct xmp_channel_info *ci = &info->channel_info[i]; + struct xmp_track *track; + struct xmp_event *event; + int trk; + + ci->note = c->key; + ci->pitchbend = c->info_pitchbend; + ci->period = c->info_period; + ci->position = c->info_position; + ci->instrument = c->ins; + ci->sample = c->smp; + ci->volume = c->info_finalvol >> 4; + ci->pan = c->info_finalpan; + ci->reserved = 0; + memset(&ci->event, 0, sizeof(*event)); + + if (info->pattern < mod->pat && info->row < info->num_rows) { + trk = mod->xxp[info->pattern]->index[i]; + track = mod->xxt[trk]; + if (info->row < track->rows) { + event = &track->event[info->row]; + memcpy(&ci->event, event, sizeof(*event)); + } + } + } + } +} diff --git a/source/libxmp-lite/src/player.h b/source/libxmp-lite/src/player.h new file mode 100644 index 000000000..4b46e608e --- /dev/null +++ b/source/libxmp-lite/src/player.h @@ -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 */ diff --git a/source/libxmp-lite/src/precomp_lut.h b/source/libxmp-lite/src/precomp_lut.h new file mode 100644 index 000000000..67146725d --- /dev/null +++ b/source/libxmp-lite/src/precomp_lut.h @@ -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, +}; + diff --git a/source/libxmp-lite/src/read_event.c b/source/libxmp-lite/src/read_event.c new file mode 100644 index 000000000..168f95d44 --- /dev/null +++ b/source/libxmp-lite/src/read_event.c @@ -0,0 +1,1613 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 +#include +#include "common.h" +#include "player.h" +#include "effects.h" +#include "virtual.h" +#include "period.h" + +#ifndef LIBXMP_CORE_PLAYER +#include "med_extras.h" +#endif + + +static inline int is_valid_note(int note) +{ + return (note >= 0 && note < XMP_MAX_KEYS); +} + +static struct xmp_subinstrument *get_subinstrument(struct context_data *ctx, + int ins, int key) +{ + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct xmp_instrument *instrument; + + if (IS_VALID_INSTRUMENT(ins)) { + instrument = &mod->xxi[ins]; + if (is_valid_note(key)) { + int mapped = instrument->map[key].ins; + if (mapped != 0xff && mapped >= 0 && mapped < instrument->nsm) + return &instrument->sub[mapped]; + } else { + if (mod->xxi[ins].nsm > 0) { + return &instrument->sub[0]; + } + } + } + + return NULL; +} + +static void reset_envelopes(struct context_data *ctx, struct channel_data *xc) +{ + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + + if (!IS_VALID_INSTRUMENT(xc->ins)) + return; + + RESET_NOTE(NOTE_ENV_END); + + xc->v_idx = -1; + xc->p_idx = -1; + xc->f_idx = -1; +} + +#ifndef LIBXMP_CORE_DISABLE_IT + +static void reset_envelopes_carry(struct context_data *ctx, + struct channel_data *xc) +{ + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct xmp_instrument *xxi; + + if (!IS_VALID_INSTRUMENT(xc->ins)) + return; + + RESET_NOTE(NOTE_ENV_END); + + xxi = libxmp_get_instrument(ctx, xc->ins); + + /* Reset envelope positions */ + if (~xxi->aei.flg & XMP_ENVELOPE_CARRY) { + xc->v_idx = -1; + } + if (~xxi->pei.flg & XMP_ENVELOPE_CARRY) { + xc->p_idx = -1; + } + if (~xxi->fei.flg & XMP_ENVELOPE_CARRY) { + xc->f_idx = -1; + } +} + +#endif + +static void set_effect_defaults(struct context_data *ctx, int note, + struct xmp_subinstrument *sub, + struct channel_data *xc, int is_toneporta) +{ + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct smix_data *smix = &ctx->smix; + + if (sub != NULL && note >= 0) { + struct xmp_instrument *xxi; + + if (xc->ins >= mod->ins) { + xxi = &smix->xxi[xc->ins - mod->ins]; + } else { + xxi = &mod->xxi[xc->ins]; + } + + if (!HAS_QUIRK(QUIRK_PROTRACK)) { + xc->finetune = sub->fin; + } + xc->gvl = sub->gvl; + +#ifndef LIBXMP_CORE_DISABLE_IT + if (sub->ifc & 0x80) { + xc->filter.cutoff = (sub->ifc - 0x80) * 2; + } else if (~xxi->fei.flg & XMP_ENVELOPE_FLT) { + xc->filter.cutoff = 0xff; + } + xc->filter.envelope = 0x100; + + if (sub->ifr & 0x80) { + xc->filter.resonance = (sub->ifr - 0x80) * 2; + } /* else { + xc->filter.resonance = 0; + } */ +#endif + + libxmp_lfo_set_depth(&xc->insvib.lfo, sub->vde); + libxmp_lfo_set_rate(&xc->insvib.lfo, sub->vra >> 2); + libxmp_lfo_set_waveform(&xc->insvib.lfo, sub->vwf); + xc->insvib.sweep = sub->vsw; + + libxmp_lfo_set_phase(&xc->vibrato.lfo, 0); + libxmp_lfo_set_phase(&xc->tremolo.lfo, 0); + } + + xc->delay = 0; + xc->tremor.up = xc->tremor.down = 0; + + /* Reset arpeggio */ + xc->arpeggio.val[0] = 0; + xc->arpeggio.count = 0; + xc->arpeggio.size = 1; +} + +/* From OpenMPT PortaTarget.mod: + * "A new note (with no portamento command next to it) does not reset the + * portamento target. That is, if a previous portamento has not finished yet, + * calling 3xx or 5xx after the new note will slide it towards the old target. + * Once the portamento target period is reached, the target is reset. This + * means that if the period is modified by another slide (e.g. 1xx or 2xx), + * a following 3xx will not slide back to the original target." + */ +static void set_period(struct context_data *ctx, int note, + struct xmp_subinstrument *sub, + struct channel_data *xc, int is_toneporta) +{ + struct module_data *m = &ctx->m; + + if (sub != NULL && note >= 0) { + double per = libxmp_note_to_period(ctx, note, xc->finetune, + xc->per_adj); + + if (!HAS_QUIRK(QUIRK_PROTRACK) || (note > 0 && is_toneporta)) { + xc->porta.target = per; + } + + if (xc->period < 1 || !is_toneporta) { + xc->period = per; + } + } +} + +/* From OpenMPT Porta-Pickup.xm: + * "An instrument number should not reset the current portamento target. The + * portamento target is valid until a new target is specified by combining a + * note and a portamento effect." + */ +static void set_period_ft2(struct context_data *ctx, int note, + struct xmp_subinstrument *sub, + struct channel_data *xc, int is_toneporta) +{ + if (note > 0 && is_toneporta) { + xc->porta.target = libxmp_note_to_period(ctx, note, xc->finetune, + xc->per_adj); + } + if (sub != NULL && note >= 0) { + if (xc->period < 1 || !is_toneporta) { + xc->period = libxmp_note_to_period(ctx, note, xc->finetune, + xc->per_adj); + } + } +} + + +#ifndef LIBXMP_CORE_PLAYER +#define IS_SFX_PITCH(x) ((x) == FX_PITCH_ADD || (x) == FX_PITCH_SUB) +#define IS_TONEPORTA(x) ((x) == FX_TONEPORTA || (x) == FX_TONE_VSLIDE \ + || (x) == FX_PER_TPORTA) +#else +#define IS_TONEPORTA(x) ((x) == FX_TONEPORTA || (x) == FX_TONE_VSLIDE) +#endif + +#define set_patch(ctx,chn,ins,smp,note) \ + libxmp_virt_setpatch(ctx, chn, ins, smp, note, 0, 0, 0) + +static int read_event_mod(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct channel_data *xc = &p->xc_data[chn]; + int note; + struct xmp_subinstrument *sub; + int new_invalid_ins = 0; + int is_toneporta; + int use_ins_vol; + + xc->flags = 0; + note = -1; + is_toneporta = 0; + use_ins_vol = 0; + + if (IS_TONEPORTA(e->fxt) || IS_TONEPORTA(e->f2t)) { + is_toneporta = 1; + } + + /* Check instrument */ + + if (e->ins) { + int ins = e->ins - 1; + use_ins_vol = 1; + SET(NEW_INS); + xc->fadeout = 0x10000; /* for painlace.mod pat 0 ch 3 echo */ + xc->per_flags = 0; + xc->offset.val = 0; + RESET_NOTE(NOTE_RELEASE|NOTE_FADEOUT); + + if (IS_VALID_INSTRUMENT(ins)) { + sub = get_subinstrument(ctx, ins, e->note - 1); + + if (is_toneporta) { + /* Get new instrument volume */ + if (sub != NULL) { + /* Dennis Lindroos: instrument volume + * is not used on split channels + */ + if (!xc->split) { + xc->volume = sub->vol; + } + use_ins_vol = 0; + } + } else { + xc->ins = ins; + xc->ins_fade = mod->xxi[ins].rls; + + if (sub != NULL) { + if (HAS_QUIRK(QUIRK_PROTRACK)) { + xc->finetune = sub->fin; + } + } + } + } else { + new_invalid_ins = 1; + libxmp_virt_resetchannel(ctx, chn); + } + } + + /* Check note */ + + if (e->note) { + SET(NEW_NOTE); + + if (e->note == XMP_KEY_OFF) { + SET_NOTE(NOTE_RELEASE); + use_ins_vol = 0; + } else if (!is_toneporta) { + xc->key = e->note - 1; + RESET_NOTE(NOTE_END); + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + if (!new_invalid_ins && sub != NULL) { + int transp = mod->xxi[xc->ins].map[xc->key].xpo; + int smp; + + note = xc->key + sub->xpo + transp; + smp = sub->sid; + + if (mod->xxs[smp].len == 0) { + smp = -1; + } + + if (smp >= 0 && smp < mod->smp) { + set_patch(ctx, chn, xc->ins, smp, note); + xc->smp = smp; + } + } else { + xc->flags = 0; + use_ins_vol = 0; + } + } + } + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + set_effect_defaults(ctx, note, sub, xc, is_toneporta); + if (e->ins && sub != NULL) { + reset_envelopes(ctx, xc); + } + + /* Process new volume */ + if (e->vol) { + xc->volume = e->vol - 1; + SET(NEW_VOL); + } + + /* Secondary effect handled first */ + libxmp_process_fx(ctx, xc, chn, e, 1); + libxmp_process_fx(ctx, xc, chn, e, 0); + +#ifndef LIBXMP_CORE_PLAYER + if (IS_SFX_PITCH(e->fxt)) { + xc->period = libxmp_note_to_period(ctx, note, xc->finetune, + xc->per_adj); + } else +#endif + { + set_period(ctx, note, sub, xc, is_toneporta); + } + + if (sub == NULL) { + return 0; + } + + if (note >= 0) { + xc->note = note; + libxmp_virt_voicepos(ctx, chn, xc->offset.val); + } + + if (TEST(OFFSET)) { + if (HAS_QUIRK(QUIRK_PROTRACK) || p->flags & XMP_FLAGS_FX9BUG) { + xc->offset.val += xc->offset.val2; + } + RESET(OFFSET); + } + + if (use_ins_vol && !TEST(NEW_VOL) && !xc->split) { + xc->volume = sub->vol; + } + + return 0; +} + +static int sustain_check(struct xmp_envelope *env, int idx) +{ + return (env && + (env->flg & XMP_ENVELOPE_ON) && + (~env->flg & XMP_ENVELOPE_LOOP) && + idx == env->data[env->sus << 1]); +} + +static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct channel_data *xc = &p->xc_data[chn]; + int note, key, ins; + struct xmp_subinstrument *sub; + int new_invalid_ins; + int is_toneporta; + int use_ins_vol; + int k00 = 0; + struct xmp_event ev; + + /* From the OpenMPT DelayCombination.xm test case: + * "Naturally, Fasttracker 2 ignores notes next to an out-of-range + * note delay. However, to check whether the delay is out of range, + * it is simply compared against the current song speed, not taking + * any pattern delays into account." + */ + if (p->frame >= p->speed) { + return 0; + } + + memcpy(&ev, e, sizeof (struct xmp_event)); + + /* From OpenMPT TremorReset.xm test case: + * "Even if a tremor effect muted the sample on a previous row, volume + * commands should be able to override this effect." + */ + if (ev.vol) { + xc->tremor.count &= ~0x80; + } + + xc->flags = 0; + note = -1; + key = ev.note; + ins = ev.ins; + new_invalid_ins = 0; + is_toneporta = 0; + use_ins_vol = 0; + + /* From the OpenMPT key_off.xm test case: + * "Key off at tick 0 (K00) is very dodgy command. If there is a note + * next to it, the note is ignored. If there is a volume column + * command or instrument next to it and the current instrument has + * no volume envelope, the note is faded out instead of being cut." + */ + if (ev.fxt == FX_KEYOFF && ev.fxp == 0) { + k00 = 1; + key = 0; + + if (ins || ev.vol || ev.f2t) { + if (IS_VALID_INSTRUMENT(xc->ins) && + ~mod->xxi[xc->ins].aei.flg & XMP_ENVELOPE_ON) { + SET_NOTE(NOTE_FADEOUT); + ev.fxt = 0; + } + } + } + + if (IS_TONEPORTA(ev.fxt) || IS_TONEPORTA(ev.f2t)) { + is_toneporta = 1; + } + + /* Check instrument */ + + /* Ignore invalid instruments. The last instrument, invalid or + * not, is preserved in channel data (see read_event() below). + * Fixes stray delayed notes in forgotten_city.xm. + */ + if (ins > 0 && !IS_VALID_INSTRUMENT(ins - 1)) { + ins = 0; + } + + /* FT2: Retrieve old instrument volume */ + if (ins) { + if (key == 0 || key >= XMP_KEY_OFF) { + struct xmp_subinstrument *sub; + + /* Previous instrument */ + sub = get_subinstrument(ctx, xc->ins, xc->key); + + /* No note */ + if (sub != NULL) { + int p = mod->xxc[chn].pan - 128; + xc->volume = sub->vol; + + if (!HAS_QUIRK(QUIRK_FTMOD)) { + xc->pan.val = p + ((sub->pan - 128) * + (128 - abs(p))) / 128 + 128; + } + + xc->ins_fade = mod->xxi[xc->ins].rls; + SET(NEW_VOL); + } + } + } + + /* Do this regardless if the instrument is invalid or not */ + if (ev.ins) { + SET(NEW_INS); + use_ins_vol = 1; + xc->fadeout = 0x10000; + xc->per_flags = 0; + RESET_NOTE(NOTE_RELEASE|NOTE_SUSEXIT); + if (!k00) { + RESET_NOTE(NOTE_FADEOUT); + } + + if (IS_VALID_INSTRUMENT(ins - 1)) { + if (!is_toneporta) + xc->ins = ins - 1; + } else { + new_invalid_ins = 1; + + /* If no note is set FT2 doesn't cut on invalid + * instruments (it keeps playing the previous one). + * If a note is set it cuts the current sample. + */ + xc->flags = 0; + + if (is_toneporta) { + key = 0; + } + } + + xc->tremor.count = 0x20; + } + + /* Check note */ + + if (ins) { + if (key > 0 && key < XMP_KEY_OFF) { + struct xmp_subinstrument *sub; + + /* Retrieve volume when we have note */ + + /* and only if we have instrument, otherwise we're in + * case 1: new note and no instrument + */ + + /* Current instrument */ + sub = get_subinstrument(ctx, xc->ins, key - 1); + if (sub != NULL) { + int p = mod->xxc[chn].pan - 128; + xc->volume = sub->vol; + + if (!HAS_QUIRK(QUIRK_FTMOD)) { + xc->pan.val = p + ((sub->pan - 128) * + (128 - abs(p))) / 128 + 128; + } + + xc->ins_fade = mod->xxi[xc->ins].rls; + } else { + xc->volume = 0; + } + SET(NEW_VOL); + } + } + + if (key) { + SET(NEW_NOTE); + + if (key == XMP_KEY_OFF) { + int env_on = 0; + int vol_set = ev.vol != 0 || ev.fxt == FX_VOLSET; + int delay_fx = ev.fxt == FX_EXTENDED && ev.fxp == 0xd0; + struct xmp_envelope *env = NULL; + + /* OpenMPT NoteOffVolume.xm: + * "If an instrument has no volume envelope, a note-off + * command should cut the sample completely - unless + * there is a volume command next it. This applies to + * both volume commands (volume and effect column)." + * + * ...and unless we have a keyoff+delay without setting + * an instrument. See OffDelay.xm. + */ + if (IS_VALID_INSTRUMENT(xc->ins)) { + env = &mod->xxi[xc->ins].aei; + if (env->flg & XMP_ENVELOPE_ON) { + env_on = 1; + } + } + + if (env_on || (!vol_set && (!ev.ins || !delay_fx))) { + if (sustain_check(env, xc->v_idx)) { + /* See OpenMPT EnvOff.xm. In certain + * cases a release event is effective + * only in the next frame + */ + SET_NOTE(NOTE_SUSEXIT); + } else { + SET_NOTE(NOTE_RELEASE); + } + use_ins_vol = 0; + } else { + SET_NOTE(NOTE_FADEOUT); + } + + /* See OpenMPT keyoff+instr.xm, pattern 2 row 0x40 */ + if (env_on && ev.fxt == FX_EXTENDED && + (ev.fxp >> 4) == EX_DELAY) { + /* See OpenMPT OffDelay.xm test case */ + if ((ev.fxp & 0xf) != 0) { + RESET_NOTE(NOTE_RELEASE|NOTE_SUSEXIT); + } + } + } else if (key == XMP_KEY_FADE) { + /* Handle keyoff + instrument case (NoteOff2.xm) */ + SET_NOTE(NOTE_FADEOUT); + } else if (is_toneporta) { + /* set key to 0 so we can have the tone portamento from + * the original note (see funky_stars.xm pos 5 ch 9) + */ + key = 0; + + /* And do the same if there's no keyoff (see comic + * bakery remix.xm pos 1 ch 3) + */ + } + + if (ev.ins == 0 && !IS_VALID_INSTRUMENT(xc->old_ins - 1)) { + new_invalid_ins = 1; + } + + if (new_invalid_ins) { + libxmp_virt_resetchannel(ctx, chn); + } + } + + + /* Check note range -- from the OpenMPT test NoteLimit.xm: + * "I think one of the first things Fasttracker 2 does when parsing a + * pattern cell is calculating the “real” note (i.e. pattern note + + * sample transpose), and if this “real” note falls out of its note + * range, it is ignored completely (wiped from its internal channel + * memory). The instrument number next it, however, is not affected + * and remains in the memory." + */ + sub = NULL; + if (is_valid_note(key - 1)) { + int k = key - 1; + sub = get_subinstrument(ctx, xc->ins, k); + if (!new_invalid_ins && sub != NULL) { + int transp = mod->xxi[xc->ins].map[k].xpo; + int k2 = k + sub->xpo + transp; + if (k2 < 12 || k2 > 130) { + key = 0; + RESET(NEW_NOTE); + } + } + } + + if (is_valid_note(key - 1)) { + xc->key = --key; + xc->fadeout = 0x10000; + RESET_NOTE(NOTE_END); + + if (sub != NULL) { + if (~mod->xxi[xc->ins].aei.flg & XMP_ENVELOPE_ON) { + RESET_NOTE(NOTE_RELEASE|NOTE_FADEOUT); + } + } + + if (!new_invalid_ins && sub != NULL) { + int transp = mod->xxi[xc->ins].map[key].xpo; + int smp; + + note = key + sub->xpo + transp; + smp = sub->sid; + + if (mod->xxs[smp].len == 0) { + smp = -1; + } + + if (smp >= 0 && smp < mod->smp) { + set_patch(ctx, chn, xc->ins, smp, note); + xc->smp = smp; + } + } else { + xc->flags = 0; + use_ins_vol = 0; + } + } + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + set_effect_defaults(ctx, note, sub, xc, is_toneporta); + + if (ins && sub != NULL && !k00) { + /* Reset envelopes on new instrument, see olympic.xm pos 10 + * But make sure we have an instrument set, see Letting go + * pos 4 chn 20 + */ + reset_envelopes(ctx, xc); + } + + /* Process new volume */ + if (ev.vol) { + xc->volume = ev.vol - 1; + SET(NEW_VOL); + if (TEST_NOTE(NOTE_END)) { /* m5v-nine.xm */ + xc->fadeout = 0x10000; /* OpenMPT NoteOff.xm */ + RESET_NOTE(NOTE_RELEASE|NOTE_FADEOUT); + } + } + + /* FT2: always reset sample offset */ + xc->offset.val = 0; + + /* Secondary effect handled first */ + libxmp_process_fx(ctx, xc, chn, &ev, 1); + libxmp_process_fx(ctx, xc, chn, &ev, 0); + set_period_ft2(ctx, note, sub, xc, is_toneporta); + + if (sub == NULL) { + return 0; + } + + if (note >= 0) { + xc->note = note; + + /* From the OpenMPT test cases (3xx-no-old-samp.xm): + * "An offset effect that points beyond the sample end should + * stop playback on this channel." + * + * ... except in Skale Tracker (and possibly others), so make this a + * FastTracker2 quirk. See Armada Tanks game.it (actually an XM). + * Reported by Vladislav Suschikh. + */ + if (HAS_QUIRK(QUIRK_FT2BUGS) && xc->offset.val >= mod->xxs[sub->sid].len) { + libxmp_virt_resetchannel(ctx, chn); + } else { + + /* (From Decibelter - Cosmic 'Wegian Mamas.xm p04 ch7) + * We retrigger the sample only if we have a new note + * without tone portamento, otherwise we won't play + * sweeps and loops correctly. + */ + libxmp_virt_voicepos(ctx, chn, xc->offset.val); + } + } + + if (use_ins_vol && !TEST(NEW_VOL)) { + xc->volume = sub->vol; + } + + return 0; +} + +static int read_event_st3(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct channel_data *xc = &p->xc_data[chn]; + int note; + struct xmp_subinstrument *sub; + int not_same_ins; + int is_toneporta; + int use_ins_vol; + + xc->flags = 0; + note = -1; + not_same_ins = 0; + is_toneporta = 0; + use_ins_vol = 0; + + if (IS_TONEPORTA(e->fxt) || IS_TONEPORTA(e->f2t)) { + is_toneporta = 1; + } + + if (libxmp_virt_mapchannel(ctx, chn) < 0 && xc->ins != e->ins - 1) { + is_toneporta = 0; + } + + /* Check instrument */ + + if (e->ins) { + int ins = e->ins - 1; + SET(NEW_INS); + use_ins_vol = 1; + xc->fadeout = 0x10000; + xc->per_flags = 0; + xc->offset.val = 0; + RESET_NOTE(NOTE_RELEASE|NOTE_FADEOUT); + + if (IS_VALID_INSTRUMENT(ins)) { + /* valid ins */ + if (xc->ins != ins) { + not_same_ins = 1; + if (!is_toneporta) { + xc->ins = ins; + xc->ins_fade = mod->xxi[ins].rls; + } else { + /* Get new instrument volume */ + sub = get_subinstrument(ctx, ins, e->note - 1); + if (sub != NULL) { + xc->volume = sub->vol; + use_ins_vol = 0; + } + } + } + } else { + /* invalid ins */ + + /* Ignore invalid instruments */ + xc->flags = 0; + use_ins_vol = 0; + } + } + + /* Check note */ + + if (e->note) { + SET(NEW_NOTE); + + if (e->note == XMP_KEY_OFF) { + SET_NOTE(NOTE_RELEASE); + use_ins_vol = 0; + } else if (is_toneporta) { + /* Always retrig in tone portamento: Fix portamento in + * 7spirits.s3m, mod.Biomechanoid + */ + if (not_same_ins) { + xc->offset.val = 0; + } + } else { + xc->key = e->note - 1; + RESET_NOTE(NOTE_END); + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + if (sub != NULL) { + int transp = mod->xxi[xc->ins].map[xc->key].xpo; + int smp; + + note = xc->key + sub->xpo + transp; + smp = sub->sid; + + if (mod->xxs[smp].len == 0) { + smp = -1; + } + + if (smp >= 0 && smp < mod->smp) { + set_patch(ctx, chn, xc->ins, smp, note); + xc->smp = smp; + } + } else { + xc->flags = 0; + use_ins_vol = 0; + } + } + } + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + set_effect_defaults(ctx, note, sub, xc, is_toneporta); + if (e->ins && sub != NULL) { + reset_envelopes(ctx, xc); + } + + /* Process new volume */ + if (e->vol) { + xc->volume = e->vol - 1; + SET(NEW_VOL); + } + + /* Secondary effect handled first */ + libxmp_process_fx(ctx, xc, chn, e, 1); + libxmp_process_fx(ctx, xc, chn, e, 0); + set_period(ctx, note, sub, xc, is_toneporta); + + if (sub == NULL) { + return 0; + } + + if (note >= 0) { + xc->note = note; + libxmp_virt_voicepos(ctx, chn, xc->offset.val); + } + + if (use_ins_vol && !TEST(NEW_VOL)) { + xc->volume = sub->vol; + } + + if (HAS_QUIRK(QUIRK_ST3BUGS) && TEST(NEW_VOL)) { + xc->volume = xc->volume * p->gvol / m->volbase; + } + + return 0; +} + +#ifndef LIBXMP_CORE_DISABLE_IT + +static inline void copy_channel(struct player_data *p, int to, int from) +{ + if (to > 0 && to != from) { + memcpy(&p->xc_data[to], &p->xc_data[from], + sizeof (struct channel_data)); + } +} + +static inline int has_note_event(struct xmp_event *e) +{ + return (e->note && e->note <= XMP_MAX_KEYS); +} + +static int check_fadeout(struct context_data *ctx, struct channel_data *xc, int ins) +{ + struct xmp_instrument *xxi = libxmp_get_instrument(ctx, ins); + + if (xxi == NULL) { + return 1; + } + + return (~xxi->aei.flg & XMP_ENVELOPE_ON || + ~xxi->aei.flg & XMP_ENVELOPE_CARRY || + xc->ins_fade == 0 || + xc->fadeout <= xc->ins_fade); +} + +static int check_invalid_sample(struct context_data *ctx, int ins, int key) +{ + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + + if (ins < mod->ins) { + int smp = mod->xxi[ins].map[key].ins; + if (smp == 0xff || smp >= mod->smp) { + return 1; + }; + } + + return 0; +} + +static void fix_period(struct context_data *ctx, int chn, struct xmp_subinstrument *sub) +{ + if (sub->nna == XMP_INST_NNA_CONT) { + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + struct xmp_instrument *xxi = libxmp_get_instrument(ctx, xc->ins); + + xc->period = libxmp_note_to_period(ctx, xc->key + sub->xpo + + xxi->map[xc->key_porta].xpo, xc->finetune, xc->per_adj); + } +} + +static int is_same_sid(struct context_data *ctx, int chn, int ins, int key) +{ + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + struct xmp_subinstrument *s1, *s2; + + s1 = get_subinstrument(ctx, ins, key); + s2 = get_subinstrument(ctx, xc->ins, xc->key); + + return (s1 && s2 && s1->sid == s2->sid); +} + +static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct channel_data *xc = &p->xc_data[chn]; + int note, key; + struct xmp_subinstrument *sub; + int not_same_ins, not_same_smp; + int new_invalid_ins; + int is_toneporta, is_release; + int candidate_ins; + int reset_env; + int use_ins_vol; + int sample_mode; + int toneporta_offset; + int disabled_toneporta; + int retrig_ins; + struct xmp_event ev; + + memcpy(&ev, e, sizeof (struct xmp_event)); + + /* Emulate Impulse Tracker "always read instrument" bug */ + if (ev.ins) { + xc->delayed_ins = 0; + } else if (ev.note && xc->delayed_ins) { + ev.ins = xc->delayed_ins; + xc->delayed_ins = 0; + } + + xc->flags = 0; + note = -1; + key = ev.note; + not_same_ins = 0; + not_same_smp = 0; + new_invalid_ins = 0; + is_toneporta = 0; + is_release = 0; + reset_env = 0; + use_ins_vol = 0; + candidate_ins = xc->ins; + sample_mode = !HAS_QUIRK(QUIRK_VIRTUAL); + toneporta_offset = 0; + disabled_toneporta = 0; + retrig_ins = 0; + + /* Keyoff + instrument retrigs current instrument in old fx mode */ + if (HAS_QUIRK(QUIRK_ITOLDFX)) { + if (ev.note == XMP_KEY_OFF && IS_VALID_INSTRUMENT(ev.ins -1)) { + retrig_ins = 1; + } + } + + /* Notes with unmapped instruments are ignored */ + if (ev.ins) { + if (ev.ins <= mod->ins && has_note_event(&ev)) { + int ins = ev.ins - 1; + if (check_invalid_sample(ctx, ins, ev.note - 1)) { + candidate_ins = ins; + memset(&ev, 0, sizeof (ev)); + } + } + } else { + if (has_note_event(&ev)) { + int ins = xc->old_ins - 1; + if (!IS_VALID_INSTRUMENT(ins)) { + new_invalid_ins = 1; + } else if (check_invalid_sample(ctx, ins, ev.note - 1)) { + memset(&ev, 0, sizeof (ev)); + } + } + } + + if (IS_TONEPORTA(ev.fxt) || IS_TONEPORTA(ev.f2t)) { + is_toneporta = 1; + } + + if (TEST_NOTE(NOTE_RELEASE | NOTE_FADEOUT)) { + is_release = 1; + } + + if (xc->period <= 0 || TEST_NOTE(NOTE_END)) { + is_toneporta = 0; + } + + /* Off-Porta.it */ + if (is_toneporta && ev.fxt == FX_OFFSET) { + disabled_toneporta = 1; + is_toneporta = 0; + if (!HAS_QUIRK(QUIRK_PRENV)) { + toneporta_offset = 1; + RESET_NOTE(NOTE_ENV_END); + } + } + + /* Check instrument */ + + if (ev.ins) { + int ins = ev.ins - 1; + int set_new_ins = 1; + + /* portamento_after_keyoff.it test case */ + if (is_release && !key) { + if (is_toneporta) { + if (HAS_QUIRK(QUIRK_PRENV) || TEST_NOTE(NOTE_SET)) { + is_toneporta = 0; + reset_envelopes_carry(ctx, xc); + } + } else { + /* fixes OpenMPT wnoteoff.it */ + reset_envelopes_carry(ctx, xc); + } + } + + if (is_toneporta && xc->ins == ins) { + if (!HAS_QUIRK(QUIRK_PRENV)) { + if (is_same_sid(ctx, chn, ins, key - 1)) { + /* same instrument and same sample */ + set_new_ins = !is_release; + } else { + /* same instrument, different sample */ + not_same_ins = 1; /* need this too */ + not_same_smp = 1; + } + } + } + + if (set_new_ins) { + SET(NEW_INS); + use_ins_vol = 1; + reset_env = 1; + } + xc->per_flags = 0; + + if (IS_VALID_INSTRUMENT(ins)) { + /* valid ins */ + + /* See OpenMPT StoppedInstrSwap.it for cut case */ + if (!key && !TEST_NOTE(NOTE_KEY_CUT)) { + /* Retrig in new ins in sample mode */ + if (sample_mode && TEST_NOTE(NOTE_END)) { + libxmp_virt_voicepos(ctx, chn, 0); + } + + /* IT: Reset note for every new != ins */ + if (xc->ins == ins) { + SET(NEW_INS); + use_ins_vol = 1; + } else { + key = xc->key + 1; + } + + RESET_NOTE(NOTE_SET); + } + + if (xc->ins != ins && (!is_toneporta || !HAS_QUIRK(QUIRK_PRENV))) { + candidate_ins = ins; + + if (!is_same_sid(ctx, chn, ins, key - 1)) { + not_same_ins = 1; + if (is_toneporta) { + /* Get new instrument volume */ + sub = get_subinstrument(ctx, ins, key); + if (sub != NULL) { + xc->volume = sub->vol; + use_ins_vol = 0; + } + } + } + } + } else { + /* In sample mode invalid ins cut previous ins */ + if (sample_mode) { + xc->volume = 0; + } + + /* Ignore invalid instruments */ + new_invalid_ins = 1; + xc->flags = 0; + use_ins_vol = 0; + } + } + + /* Check note */ + + if (key && !new_invalid_ins) { + SET(NEW_NOTE); + SET_NOTE(NOTE_SET); + + if (key == XMP_KEY_FADE) { + SET_NOTE(NOTE_FADEOUT); + reset_env = 0; + use_ins_vol = 0; + } else if (key == XMP_KEY_CUT) { + SET_NOTE(NOTE_END | NOTE_CUT | NOTE_KEY_CUT); + xc->period = 0; + libxmp_virt_resetchannel(ctx, chn); + } else if (key == XMP_KEY_OFF) { + struct xmp_envelope *env = NULL; + if (IS_VALID_INSTRUMENT(xc->ins)) { + env = &mod->xxi[xc->ins].aei; + } + if (sustain_check(env, xc->v_idx)) { + SET_NOTE(NOTE_SUSEXIT); + } else { + SET_NOTE(NOTE_RELEASE); + } + SET(KEY_OFF); + reset_env = 0; + use_ins_vol = 0; + } else { + /* portamento_after_keyoff.it test case */ + /* also see suburban_streets o13 c45 */ + if (ev.ins || !is_toneporta) { + if (!disabled_toneporta) { + reset_env = 1; + } + } + + if (is_toneporta) { + if (not_same_ins || TEST_NOTE(NOTE_END)) { + SET(NEW_INS); + RESET_NOTE(NOTE_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT); + } else { + if (is_valid_note(key - 1)) { + xc->key_porta = key - 1; + } + key = 0; + } + } + } + } + + if (is_valid_note(key - 1) && !new_invalid_ins) { + if (TEST_NOTE(NOTE_CUT)) { + use_ins_vol = 1; /* See OpenMPT NoteOffInstr.it */ + } + xc->key = --key; + RESET_NOTE(NOTE_END); + + sub = get_subinstrument(ctx, candidate_ins, key); + + if (sub != NULL) { + int transp = mod->xxi[candidate_ins].map[key].xpo; + int smp, to; + int rvv; + + note = key + sub->xpo + transp; + smp = sub->sid; + if (smp >= mod->smp || mod->xxs[smp].len == 0) { + smp = -1; + } + + if (not_same_smp) { + fix_period(ctx, chn, sub); + libxmp_virt_resetchannel(ctx, chn); + } + to = libxmp_virt_setpatch(ctx, chn, candidate_ins, smp, + note, sub->nna, sub->dct, sub->dca); + + /* Random value for volume swing */ + rvv = sub->rvv & 0xff; + if (rvv) { + CLAMP(rvv, 0, 100); + xc->rvv = rand() % (rvv + 1); + } else { + xc->rvv = 0; + } + + /* Random value for pan swing */ + rvv = (sub->rvv & 0xff00) >> 8; + if (rvv) { + CLAMP(rvv, 0, 64); + xc->rpv = rand() % (rvv + 1) - (rvv / 2); + } else { + xc->rpv = 0; + } + + if (to < 0) + return -1; + if (to != chn) { + copy_channel(p, to, chn); + p->xc_data[to].flags = 0; + } + + if (smp >= 0) { /* Not sure if needed */ + xc->smp = smp; + } + } else { + xc->flags = 0; + use_ins_vol = 0; + } + } + + /* Do after virtual channel copy */ + if (is_toneporta || retrig_ins) { + if (HAS_QUIRK(QUIRK_PRENV) && ev.ins) { + reset_envelopes_carry(ctx, xc); + } + } + + if (IS_VALID_INSTRUMENT(candidate_ins)) { + if (xc->ins != candidate_ins) { + /* Reset envelopes if instrument changes */ + reset_envelopes(ctx, xc); + } + xc->ins = candidate_ins; + xc->ins_fade = mod->xxi[candidate_ins].rls; + } + + /* Reset in case of new instrument and the previous envelope has + * finished (OpenMPT test EnvReset.it). This must take place after + * channel copies in case of NNA (see test/test.it) + * Also if we have envelope in carry mode, check fadeout + */ + if (ev.ins && TEST_NOTE(NOTE_ENV_END)) { + if (check_fadeout(ctx, xc, candidate_ins)) { + reset_envelopes(ctx, xc); + } else { + reset_env = 0; + } + } + + if (reset_env) { + if (ev.note) { + RESET_NOTE(NOTE_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT); + } + /* Set after copying to new virtual channel (see ambio.it) */ + xc->fadeout = 0x10000; + } + + /* See OpenMPT wnoteoff.it vs noteoff3.it */ + if (retrig_ins && not_same_ins) { + SET(NEW_INS); + libxmp_virt_voicepos(ctx, chn, 0); + xc->fadeout = 0x10000; + RESET_NOTE(NOTE_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT); + } + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + set_effect_defaults(ctx, note, sub, xc, is_toneporta); + if (sub != NULL) { + if (note >= 0) { + /* Reset pan, see OpenMPT PanReset.it */ + if (sub->pan >= 0) { + xc->pan.val = sub->pan; + xc->pan.surround = 0; + } + + if (TEST_NOTE(NOTE_CUT)) { + reset_envelopes(ctx, xc); + } else if (!toneporta_offset) { + reset_envelopes_carry(ctx, xc); + } + RESET_NOTE(NOTE_CUT); + } + } + + /* Process new volume */ + if (ev.vol && (!TEST_NOTE(NOTE_CUT) || ev.ins != 0)) { + if (key != XMP_KEY_OFF) { /* See OpenMPT NoteOffInstr.it */ + xc->volume = ev.vol - 1; + SET(NEW_VOL); + } + } + + /* IT: always reset sample offset */ + xc->offset.val &= ~0xffff; + + /* According to Storlek test 25, Impulse Tracker handles the volume + * column effects after the standard effects. + */ + libxmp_process_fx(ctx, xc, chn, &ev, 0); + libxmp_process_fx(ctx, xc, chn, &ev, 1); + set_period(ctx, note, sub, xc, is_toneporta); + + if (sub == NULL) { + return 0; + } + + if (note >= 0) { + xc->note = note; + libxmp_virt_voicepos(ctx, chn, xc->offset.val); + } + + if (use_ins_vol && !TEST(NEW_VOL)) { + xc->volume = sub->vol; + } + + return 0; +} + +#endif + +#ifndef LIBXMP_CORE_PLAYER + +static int read_event_med(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct channel_data *xc = &p->xc_data[chn]; + int note; + struct xmp_subinstrument *sub; + int new_invalid_ins = 0; + int is_toneporta; + int use_ins_vol; + int finetune; + + xc->flags = 0; + note = -1; + is_toneporta = 0; + use_ins_vol = 0; + + if (e->fxt == FX_TONEPORTA || e->fxt == FX_TONE_VSLIDE) { + is_toneporta = 1; + } + + /* Check instrument */ + + if (e->ins && e->note) { + int ins = e->ins - 1; + use_ins_vol = 1; + SET(NEW_INS); + xc->fadeout = 0x10000; + xc->offset.val = 0; + RESET_NOTE(NOTE_RELEASE|NOTE_FADEOUT); + + if (IS_VALID_INSTRUMENT(ins)) { + if (is_toneporta) { + /* Get new instrument volume */ + sub = get_subinstrument(ctx, ins, e->note - 1); + if (sub != NULL) { + xc->volume = sub->vol; + use_ins_vol = 0; + } + } else { + xc->ins = ins; + xc->ins_fade = mod->xxi[ins].rls; + } + } else { + new_invalid_ins = 1; + libxmp_virt_resetchannel(ctx, chn); + } + + MED_CHANNEL_EXTRAS(*xc)->arp = 0; + MED_CHANNEL_EXTRAS(*xc)->aidx = 0; + } else { + /* Hold */ + if (e->ins && !e->note) { + use_ins_vol = 1; + } + } + + /* Check note */ + + if (e->note) { + SET(NEW_NOTE); + + if (e->note == XMP_KEY_OFF) { + SET_NOTE(NOTE_RELEASE); + use_ins_vol = 0; + } else if (e->note == XMP_KEY_CUT) { + SET_NOTE(NOTE_END); + xc->period = 0; + libxmp_virt_resetchannel(ctx, chn); + } else if (!is_toneporta && IS_VALID_INSTRUMENT(xc->ins)) { + struct xmp_instrument *xxi = &mod->xxi[xc->ins]; + + xc->key = e->note - 1; + RESET_NOTE(NOTE_END); + + xc->per_adj = 0.0; + if (xxi->nsm > 1 && HAS_MED_INSTRUMENT_EXTRAS(*xxi)) { + /* synth or iffoct */ + if (MED_INSTRUMENT_EXTRAS(*xxi)->vts == 0 && + MED_INSTRUMENT_EXTRAS(*xxi)->wts == 0) { + /* iffoct */ + xc->per_adj = 2.0; + } + } + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + if (!new_invalid_ins && sub != NULL) { + int transp = xxi->map[xc->key].xpo; + int smp; + + note = xc->key + sub->xpo + transp; + smp = sub->sid; + + if (mod->xxs[smp].len == 0) { + smp = -1; + } + + if (smp >= 0 && smp < mod->smp) { + set_patch(ctx, chn, xc->ins, smp, note); + xc->smp = smp; + } + } else { + xc->flags = 0; + use_ins_vol = 0; + } + } + } + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + /* Keep effect-set finetune if no instrument set */ + finetune = xc->finetune; + set_effect_defaults(ctx, note, sub, xc, is_toneporta); + if (!e->ins) { + xc->finetune = finetune; + } + + if (e->ins && sub != NULL) { + reset_envelopes(ctx, xc); + } + + /* Process new volume */ + if (e->vol) { + xc->volume = e->vol - 1; + SET(NEW_VOL); + } + + /* Secondary effect handled first */ + libxmp_process_fx(ctx, xc, chn, e, 1); + libxmp_process_fx(ctx, xc, chn, e, 0); + set_period(ctx, note, sub, xc, is_toneporta); + + if (sub == NULL) { + return 0; + } + + if (note >= 0) { + xc->note = note; + libxmp_virt_voicepos(ctx, chn, xc->offset.val); + } + + if (use_ins_vol && !TEST(NEW_VOL)) { + xc->volume = sub->vol; + } + + return 0; +} + +#endif + +static int read_event_smix(struct context_data *ctx, struct xmp_event *e, int chn) +{ + 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 channel_data *xc = &p->xc_data[chn]; + struct xmp_subinstrument *sub; + int is_smix_ins; + int ins, note, transp, smp; + + xc->flags = 0; + + if (!e->ins) + return 0; + + is_smix_ins = 0; + ins = e->ins - 1; + SET(NEW_INS); + xc->fadeout = 0x10000; + xc->per_flags = 0; + xc->offset.val = 0; + RESET_NOTE(NOTE_RELEASE); + + xc->ins = ins; + + if (ins >= mod->ins && ins < mod->ins + smix->ins) { + is_smix_ins = 1; + xc->ins_fade = smix->xxi[xc->ins - mod->ins].rls; + } + + SET(NEW_NOTE); + + if (e->note == XMP_KEY_OFF) { + SET_NOTE(NOTE_RELEASE); + return 0; + } + + xc->key = e->note - 1; + RESET_NOTE(NOTE_END); + + if (is_smix_ins) { + sub = &smix->xxi[xc->ins - mod->ins].sub[0]; + if (sub == NULL) { + return 0; + } + + note = xc->key + sub->xpo; + smp = sub->sid; + if (smix->xxs[smp].len == 0) + smp = -1; + if (smp >= 0 && smp < smix->smp) { + smp += mod->smp; + set_patch(ctx, chn, xc->ins, smp, note); + xc->smp = smp; + } + } else { + transp = mod->xxi[xc->ins].map[xc->key].xpo; + sub = get_subinstrument(ctx, xc->ins, xc->key); + if (sub == NULL) { + return 0; + } + note = xc->key + sub->xpo + transp; + smp = sub->sid; + if (mod->xxs[smp].len == 0) + smp = -1; + if (smp >= 0 && smp < mod->smp) { + set_patch(ctx, chn, xc->ins, smp, note); + xc->smp = smp; + } + } + + set_effect_defaults(ctx, note, sub, xc, 0); + set_period(ctx, note, sub, xc, 0); + + if (e->ins) { + reset_envelopes(ctx, xc); + } + + xc->volume = e->vol - 1; + + xc->note = note; + libxmp_virt_voicepos(ctx, chn, xc->offset.val); + + return 0; +} + +int libxmp_read_event(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct channel_data *xc = &p->xc_data[chn]; + + if (e->ins != 0) + xc->old_ins = e->ins; + + if (TEST_NOTE(NOTE_SAMPLE_END)) { + SET_NOTE(NOTE_END); + } + + if (chn >= m->mod.chn) { + return read_event_smix(ctx, e, chn); + } else switch (m->read_event_type) { + case READ_EVENT_MOD: + return read_event_mod(ctx, e, chn); + case READ_EVENT_FT2: + return read_event_ft2(ctx, e, chn); + case READ_EVENT_ST3: + return read_event_st3(ctx, e, chn); +#ifndef LIBXMP_CORE_DISABLE_IT + case READ_EVENT_IT: + return read_event_it(ctx, e, chn); +#endif +#ifndef LIBXMP_CORE_PLAYER + case READ_EVENT_MED: + return read_event_med(ctx, e, chn); +#endif + default: + return read_event_mod(ctx, e, chn); + } +} diff --git a/source/libxmp-lite/src/s3m.h b/source/libxmp-lite/src/s3m.h new file mode 100644 index 000000000..122e2d711 --- /dev/null +++ b/source/libxmp-lite/src/s3m.h @@ -0,0 +1,116 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 + diff --git a/source/libxmp-lite/src/s3m_load.c b/source/libxmp-lite/src/s3m_load.c new file mode 100644 index 000000000..6f55a781a --- /dev/null +++ b/source/libxmp-lite/src/s3m_load.c @@ -0,0 +1,660 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 : + * 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 : + * 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 + * 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. + */ + +/* + * From: Ralf Hoffmann + * Date: Wed, 26 Sep 2007 17:12:41 +0200 + * ftp://ftp.scenesp.org/pub/compilations/modplanet/normal/bonuscd/artists/ + * Iq/return%20of%20litmus.s3m doesn't start playing, just uses 100% cpu, + * the number of patterns is unusually high + * + * Claudio's fix: this module seems to be a bad conversion, bad rip or + * simply corrupted since it has many instances of 0x87 instead of 0x00 + * in the module and instrument headers. I'm adding a simple workaround + * to be able to load/play the module as is, see the fix87() macro below. + */ + +#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 + +#define fix87(x) do { \ + int i; for (i = 0; i < sizeof(x); i++) { \ + if (*((uint8 *)&x + i) == 0x87) *((uint8 *)&x + i) = 0; } \ + } while (0) + +/* Effect conversion table */ +static const uint8 fx[] = { + 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 > 26) { + 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]; + int quirk87 = 0; +#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 */ + /* 54-61 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; + } + +#ifndef LIBXMP_CORE_PLAYER + /* S3M anomaly in return_of_litmus.s3m */ + if (sfh.version == 0x1301 && sfh.name[27] == 0x87) + quirk87 = 1; + + if (quirk87) { + fix87(sfh.name); + fix87(sfh.patnum); + fix87(sfh.flags); + } +#endif + + 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) { + snprintf(tracker_name, 40, "Schism Tracker %d.%02x", + (sfh.version & 0x0f00) >> 8, + sfh.version & 0xff); + 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; + + 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 (not used) */ + 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; + } +#ifndef LIBXMP_CORE_PLAYER + if (quirk87) { + fix87(sih.length); + fix87(sih.loopbeg); + fix87(sih.loopend); + fix87(sih.flags); + } +#endif + + 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; + } + + 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, sfh.ffi == 1 ? 0 : SAMPLE_FLAG_UNS, + 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; +} diff --git a/source/libxmp-lite/src/sample.c b/source/libxmp-lite/src/sample.c new file mode 100644 index 000000000..a31c6558b --- /dev/null +++ b/source/libxmp-lite/src/sample.c @@ -0,0 +1,417 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 + * 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; + } + + /* 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: + free(xxs->data - 4); + xxs->data = NULL; /* prevent double free in PCM load error */ +#endif + err: + return -1; +} diff --git a/source/libxmp-lite/src/scan.c b/source/libxmp-lite/src/scan.c new file mode 100644 index 000000000..1bbbea82c --- /dev/null +++ b/source/libxmp-lite/src/scan.c @@ -0,0 +1,549 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 : + * 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 : + * scan.c was hanging when it jumps to an invalid restart value. + * (Fixed by hipolito) + */ + + +#include +#include +#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; + 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 + * + * 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 = frame_count = 0; + start_time = time = 0.0; + inside_loop = 0; + + while (42) { + 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; + } + + /* 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. + */ + if (!inside_loop && info->gvl < 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++) { + /* 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 + * 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 + * 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 > 512) /* was 255, but Global trash goes to 318 */ + goto end_module; + + if (!loop_num && m->scan_cnt[ord][row]) { + row_count--; + goto end_module; + } + m->scan_cnt[ord][row]++; + + 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) { + m->scan_cnt[ord][row] += p1 & 0x0f; + 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 = 0; + } + row = break_row; + +end_module: + + /* Sanity check */ + { + 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].gvl = -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; + + 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; +} diff --git a/source/libxmp-lite/src/smix.c b/source/libxmp-lite/src/smix.c new file mode 100644 index 000000000..ba801c589 --- /dev/null +++ b/source/libxmp-lite/src/smix.c @@ -0,0 +1,322 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 +#include "common.h" +#include "period.h" +#include "player.h" +#include "hio.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); + 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, 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) / (bits / 8); + 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); + if (xxs->data == NULL) { + retval = -XMP_ERROR_SYSTEM; + goto err2; + } + 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; + } + + free(smix->xxs[num].data); + 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); +} diff --git a/source/libxmp-lite/src/tempfile.h b/source/libxmp-lite/src/tempfile.h new file mode 100644 index 000000000..f3c8f77d9 --- /dev/null +++ b/source/libxmp-lite/src/tempfile.h @@ -0,0 +1,7 @@ +#ifndef XMP_PLATFORM_H +#define XMP_PLATFORM_H + +FILE *make_temp_file(char **); +void unlink_temp_file(char *); + +#endif diff --git a/source/libxmp-lite/src/virtual.c b/source/libxmp-lite/src/virtual.c new file mode 100644 index 000000000..f28ca5194 --- /dev/null +++ b/source/libxmp-lite/src/virtual.c @@ -0,0 +1,608 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 +#include +#include +#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); + 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); +} + +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; +} diff --git a/source/libxmp-lite/src/virtual.h b/source/libxmp-lite/src/virtual.h new file mode 100644 index 000000000..a7668c562 --- /dev/null +++ b/source/libxmp-lite/src/virtual.h @@ -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 */ diff --git a/source/libxmp-lite/src/xm.h b/source/libxmp-lite/src/xm.h new file mode 100644 index 000000000..443a369ce --- /dev/null +++ b/source/libxmp-lite/src/xm.h @@ -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 diff --git a/source/libxmp-lite/src/xm_load.c b/source/libxmp-lite/src/xm_load.c new file mode 100644 index 000000000..eaf0fd029 --- /dev/null +++ b/source/libxmp-lite/src/xm_load.c @@ -0,0 +1,773 @@ +/* Extended Module Player + * Copyright (C) 1996-2016 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 + * 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 + * 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 + +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[16]; + int sample_num = 0; + 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++) { + 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 > 0x10 || (xih.samples > 0 && xih.sh_size > 0x100)) { + D_(D_CRIT "Sanity check: %d %d", xih.samples, xih.sh_size); + return -1; + } + + libxmp_instrument_name(mod, i, xih.name, 22); + + xxi->nsm = xih.samples; + + D_(D_INFO "[%2X] %-22.22s %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 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; + } + } + + for (j = 0; j < xxi->nsm; j++, sample_num++) { + struct xmp_subinstrument *sub = &xxi->sub[j]; + struct xmp_sample *xxs; + uint8 *b = buf; + + if (sample_num >= mod->smp) { + mod->xxs = libxmp_realloc_samples(mod->xxs, &mod->smp, mod->smp * 3 / 2); + if (mod->xxs == NULL) + 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; + } + + for (j = 0; j < xxi->nsm; j++) { + struct xmp_subinstrument *sub = &xxi->sub[j]; + int flags; + + D_(D_INFO " %1x: %06x%c%06x %06x %c V%02x F%+04d P%02x R%+03d", + j, mod->xxs[sub->sid].len, + mod->xxs[sub->sid].flg & XMP_SAMPLE_16BIT ? '+' : ' ', + 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); + + flags = SAMPLE_FLAG_DIFF; +#ifndef LIBXMP_CORE_PLAYER + if (xsh[j].reserved == 0xad) { + flags = SAMPLE_FLAG_ADPCM; + } +#endif + + if (version > 0x0103) { + if (libxmp_load_sample(m, f, flags, &mod->xxs[sub->sid], NULL) < 0) { + return -1; + } + } + } + } + + /* Final sample number adjustment */ + mod->xxs = libxmp_realloc_samples(mod->xxs, &mod->smp, sample_num); + if (mod->xxs == NULL) { + 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; +}