summaryrefslogtreecommitdiff
path: root/plugins/dumb/dumb-kode54/src
diff options
context:
space:
mode:
authorGravatar Alexey Yakovenko <wakeroid@gmail.com>2010-04-11 15:01:21 +0200
committerGravatar Alexey Yakovenko <wakeroid@gmail.com>2010-04-11 15:01:21 +0200
commite763dfa3ddf5c499875a6e1e9ec91d7fc154c076 (patch)
tree8fddc5ecbb1d867afa4690cfdde245929cafb21c /plugins/dumb/dumb-kode54/src
parentdc0b9121ff16d57f92f4ab2b16bf78518d3a8b9b (diff)
moved DUMB to dynamic plugin
Diffstat (limited to 'plugins/dumb/dumb-kode54/src')
-rw-r--r--plugins/dumb/dumb-kode54/src/Makefile.am41
-rw-r--r--plugins/dumb/dumb-kode54/src/allegro/alplay.c277
-rw-r--r--plugins/dumb/dumb-kode54/src/allegro/datduh.c60
-rw-r--r--plugins/dumb/dumb-kode54/src/allegro/datit.c62
-rw-r--r--plugins/dumb/dumb-kode54/src/allegro/datmod.c61
-rw-r--r--plugins/dumb/dumb-kode54/src/allegro/dats3m.c61
-rw-r--r--plugins/dumb/dumb-kode54/src/allegro/datunld.c31
-rw-r--r--plugins/dumb/dumb-kode54/src/allegro/datxm.c62
-rw-r--r--plugins/dumb/dumb-kode54/src/allegro/packfile.c98
-rw-r--r--plugins/dumb/dumb-kode54/src/core/atexit.c71
-rw-r--r--plugins/dumb/dumb-kode54/src/core/duhlen.c42
-rw-r--r--plugins/dumb/dumb-kode54/src/core/duhtag.c38
-rw-r--r--plugins/dumb/dumb-kode54/src/core/dumbfile.c401
-rw-r--r--plugins/dumb/dumb-kode54/src/core/loadduh.c42
-rw-r--r--plugins/dumb/dumb-kode54/src/core/makeduh.c132
-rw-r--r--plugins/dumb/dumb-kode54/src/core/rawsig.c44
-rw-r--r--plugins/dumb/dumb-kode54/src/core/readduh.c107
-rw-r--r--plugins/dumb/dumb-kode54/src/core/register.c104
-rw-r--r--plugins/dumb/dumb-kode54/src/core/rendduh.c184
-rw-r--r--plugins/dumb/dumb-kode54/src/core/rendsig.c344
-rw-r--r--plugins/dumb/dumb-kode54/src/core/unload.c64
-rw-r--r--plugins/dumb/dumb-kode54/src/helpers/barray.c159
-rw-r--r--plugins/dumb/dumb-kode54/src/helpers/clickrem.c281
-rw-r--r--plugins/dumb/dumb-kode54/src/helpers/memfile.c96
-rw-r--r--plugins/dumb/dumb-kode54/src/helpers/resamp2.inc160
-rw-r--r--plugins/dumb/dumb-kode54/src/helpers/resamp3.inc376
-rw-r--r--plugins/dumb/dumb-kode54/src/helpers/resample.c393
-rw-r--r--plugins/dumb/dumb-kode54/src/helpers/resample.inc264
-rw-r--r--plugins/dumb/dumb-kode54/src/helpers/riff.c88
-rw-r--r--plugins/dumb/dumb-kode54/src/helpers/sampbuf.c64
-rw-r--r--plugins/dumb/dumb-kode54/src/helpers/silence.c29
-rw-r--r--plugins/dumb/dumb-kode54/src/helpers/stdfile.c93
-rw-r--r--plugins/dumb/dumb-kode54/src/it/itload.c43
-rw-r--r--plugins/dumb/dumb-kode54/src/it/itload2.c29
-rw-r--r--plugins/dumb/dumb-kode54/src/it/itmisc.c247
-rw-r--r--plugins/dumb/dumb-kode54/src/it/itorder.c63
-rw-r--r--plugins/dumb/dumb-kode54/src/it/itread.c1332
-rw-r--r--plugins/dumb/dumb-kode54/src/it/itread2.c29
-rw-r--r--plugins/dumb/dumb-kode54/src/it/itrender.c5542
-rw-r--r--plugins/dumb/dumb-kode54/src/it/itunload.c72
-rw-r--r--plugins/dumb/dumb-kode54/src/it/load669.c42
-rw-r--r--plugins/dumb/dumb-kode54/src/it/load6692.c34
-rw-r--r--plugins/dumb/dumb-kode54/src/it/loadasy.c42
-rw-r--r--plugins/dumb/dumb-kode54/src/it/loadasy2.c34
-rw-r--r--plugins/dumb/dumb-kode54/src/it/loadmod.c42
-rw-r--r--plugins/dumb/dumb-kode54/src/it/loadmod2.c29
-rw-r--r--plugins/dumb/dumb-kode54/src/it/loadmtm.c42
-rw-r--r--plugins/dumb/dumb-kode54/src/it/loadmtm2.c34
-rw-r--r--plugins/dumb/dumb-kode54/src/it/loadoldpsm.c43
-rw-r--r--plugins/dumb/dumb-kode54/src/it/loadoldpsm2.c34
-rw-r--r--plugins/dumb/dumb-kode54/src/it/loadpsm.c42
-rw-r--r--plugins/dumb/dumb-kode54/src/it/loadpsm2.c34
-rw-r--r--plugins/dumb/dumb-kode54/src/it/loadptm.c42
-rw-r--r--plugins/dumb/dumb-kode54/src/it/loadptm2.c34
-rw-r--r--plugins/dumb/dumb-kode54/src/it/loadriff.c42
-rw-r--r--plugins/dumb/dumb-kode54/src/it/loadriff2.c29
-rw-r--r--plugins/dumb/dumb-kode54/src/it/loads3m.c42
-rw-r--r--plugins/dumb/dumb-kode54/src/it/loads3m2.c29
-rw-r--r--plugins/dumb/dumb-kode54/src/it/loadstm.c42
-rw-r--r--plugins/dumb/dumb-kode54/src/it/loadstm2.c29
-rw-r--r--plugins/dumb/dumb-kode54/src/it/loadxm.c42
-rw-r--r--plugins/dumb/dumb-kode54/src/it/loadxm2.c29
-rw-r--r--plugins/dumb/dumb-kode54/src/it/ptmeffect.c125
-rw-r--r--plugins/dumb/dumb-kode54/src/it/read669.c447
-rw-r--r--plugins/dumb/dumb-kode54/src/it/read6692.c0
-rw-r--r--plugins/dumb/dumb-kode54/src/it/readam.c752
-rw-r--r--plugins/dumb/dumb-kode54/src/it/readasy.c331
-rw-r--r--plugins/dumb/dumb-kode54/src/it/readdsmf.c373
-rw-r--r--plugins/dumb/dumb-kode54/src/it/readmod.c780
-rw-r--r--plugins/dumb/dumb-kode54/src/it/readmod2.c29
-rw-r--r--plugins/dumb/dumb-kode54/src/it/readmtm.c412
-rw-r--r--plugins/dumb/dumb-kode54/src/it/readoldpsm.c727
-rw-r--r--plugins/dumb/dumb-kode54/src/it/readpsm.c1273
-rw-r--r--plugins/dumb/dumb-kode54/src/it/readptm.c574
-rw-r--r--plugins/dumb/dumb-kode54/src/it/readriff.c73
-rw-r--r--plugins/dumb/dumb-kode54/src/it/reads3m.c864
-rw-r--r--plugins/dumb/dumb-kode54/src/it/reads3m2.c29
-rw-r--r--plugins/dumb/dumb-kode54/src/it/readstm.c397
-rw-r--r--plugins/dumb/dumb-kode54/src/it/readstm2.c29
-rw-r--r--plugins/dumb/dumb-kode54/src/it/readxm.c1210
-rw-r--r--plugins/dumb/dumb-kode54/src/it/readxm2.c29
-rw-r--r--plugins/dumb/dumb-kode54/src/it/xmeffect.c243
-rw-r--r--plugins/dumb/dumb-kode54/src/sigtypes/combine.c243
-rw-r--r--plugins/dumb/dumb-kode54/src/sigtypes/sample.c340
-rw-r--r--plugins/dumb/dumb-kode54/src/sigtypes/sequence.c592
-rw-r--r--plugins/dumb/dumb-kode54/src/sigtypes/sterpan.c206
-rw-r--r--plugins/dumb/dumb-kode54/src/tools/it/load_it.cpp824
-rw-r--r--plugins/dumb/dumb-kode54/src/tools/it/modulus.h193
-rw-r--r--plugins/dumb/dumb-kode54/src/tools/it/typedef.hpp3
89 files changed, 23596 insertions, 0 deletions
diff --git a/plugins/dumb/dumb-kode54/src/Makefile.am b/plugins/dumb/dumb-kode54/src/Makefile.am
new file mode 100644
index 00000000..ecd71af7
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/Makefile.am
@@ -0,0 +1,41 @@
+INCLUDES = \
+ -I $(top_srcdir)/include
+
+lib_LTLIBRARIES = libdumb.la
+
+libdumb_la_SOURCES = \
+ core/atexit.c \
+ core/duhlen.c \
+ core/duhtag.c \
+ core/dumbfile.c \
+ core/loadduh.c \
+ core/makeduh.c \
+ core/rawsig.c \
+ core/readduh.c \
+ core/register.c \
+ core/rendduh.c \
+ core/rendsig.c \
+ core/unload.c \
+ helpers/clickrem.c \
+ helpers/memfile.c \
+ helpers/resample.c \
+ helpers/resample.inc \
+ helpers/sampbuf.c \
+ helpers/silence.c \
+ helpers/stdfile.c \
+ it/itload.c \
+ it/itmisc.c \
+ it/itorder.c \
+ it/itread.c \
+ it/itrender.c \
+ it/itunload.c \
+ it/loadmod.c \
+ it/loads3m.c \
+ it/loadxm.c \
+ it/readmod.c \
+ it/reads3m.c \
+ it/readxm.c \
+ it/xmeffect.c
+
+libdumb_la_LDFLAGS = \
+ -release $(VERSION)
diff --git a/plugins/dumb/dumb-kode54/src/allegro/alplay.c b/plugins/dumb/dumb-kode54/src/allegro/alplay.c
new file mode 100644
index 00000000..85e2079a
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/allegro/alplay.c
@@ -0,0 +1,277 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * alplay.c - Functions to play a DUH through / / \ \
+ * an Allegro audio stream. | < / \_
+ * | \/ /\ /
+ * By entheh. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+
+#include <allegro.h>
+
+#include "aldumb.h"
+
+
+
+#define ADP_PLAYING 1
+
+struct AL_DUH_PLAYER
+{
+ int flags;
+ long bufsize;
+ int freq;
+ AUDIOSTREAM *stream;
+ DUH_SIGRENDERER *sigrenderer; /* If this is NULL, stream is invalid. */
+ float volume;
+ int silentcount;
+};
+
+
+
+AL_DUH_PLAYER *al_start_duh(DUH *duh, int n_channels, long pos, float volume, long bufsize, int freq)
+{
+ AL_DUH_PLAYER *dp;
+
+ /* This restriction is imposed by Allegro. */
+ ASSERT(n_channels > 0);
+ ASSERT(n_channels <= 2);
+
+ if (!duh)
+ return NULL;
+
+ dp = malloc(sizeof(*dp));
+ if (!dp)
+ return NULL;
+
+ dp->flags = ADP_PLAYING;
+ dp->bufsize = bufsize;
+ dp->freq = freq;
+
+ dp->stream = play_audio_stream(bufsize, 16, n_channels - 1, freq, 255, 128);
+
+ if (!dp->stream) {
+ free(dp);
+ return NULL;
+ }
+
+ voice_set_priority(dp->stream->voice, 255);
+
+ dp->sigrenderer = duh_start_sigrenderer(duh, 0, n_channels, pos);
+
+ if (!dp->sigrenderer) {
+ stop_audio_stream(dp->stream);
+ free(dp);
+ return NULL;
+ }
+
+ dp->volume = volume;
+ dp->silentcount = 0;
+
+ return dp;
+}
+
+
+
+void al_stop_duh(AL_DUH_PLAYER *dp)
+{
+ if (dp) {
+ if (dp->sigrenderer) {
+ duh_end_sigrenderer(dp->sigrenderer);
+ stop_audio_stream(dp->stream);
+ }
+ free(dp);
+ }
+}
+
+
+
+void al_pause_duh(AL_DUH_PLAYER *dp)
+{
+ if (dp && dp->sigrenderer && (dp->flags & ADP_PLAYING)) {
+ voice_stop(dp->stream->voice);
+ dp->flags &= ~ADP_PLAYING;
+ }
+}
+
+
+
+void al_resume_duh(AL_DUH_PLAYER *dp)
+{
+ if (dp && dp->sigrenderer && !(dp->flags & ADP_PLAYING)) {
+ voice_start(dp->stream->voice);
+ dp->flags |= ADP_PLAYING;
+ }
+}
+
+
+
+void al_duh_set_priority(AL_DUH_PLAYER *dp, int priority)
+{
+ if (dp && dp->sigrenderer)
+ voice_set_priority(dp->stream->voice, priority);
+}
+
+
+
+void al_duh_set_volume(AL_DUH_PLAYER *dp, float volume)
+{
+ if (dp)
+ dp->volume = volume;
+}
+
+
+
+float al_duh_get_volume(AL_DUH_PLAYER *dp)
+{
+ return dp ? dp->volume : 0;
+}
+
+
+
+int al_poll_duh(AL_DUH_PLAYER *dp)
+{
+ unsigned short *sptr;
+ long n;
+ long size;
+ int n_channels;
+
+ if (!dp || !dp->sigrenderer)
+ return 1;
+
+ if (!(dp->flags & ADP_PLAYING))
+ return 0;
+
+ sptr = get_audio_stream_buffer(dp->stream);
+
+ if (!sptr)
+ return 0;
+
+ n = duh_render(dp->sigrenderer, 16, 1, dp->volume, 65536.0 / dp->freq, dp->bufsize, sptr);
+
+ if (n == 0) {
+ if (++dp->silentcount >= 2) {
+ duh_end_sigrenderer(dp->sigrenderer);
+ free_audio_stream_buffer(dp->stream);
+ stop_audio_stream(dp->stream);
+ dp->sigrenderer = NULL;
+ return 1;
+ }
+ }
+
+ n_channels = duh_sigrenderer_get_n_channels(dp->sigrenderer);
+ n *= n_channels;
+ size = dp->bufsize * n_channels;
+ for (; n < size; n++)
+ sptr[n] = 0x8000;
+
+ free_audio_stream_buffer(dp->stream);
+
+ return 0;
+}
+
+
+
+long al_duh_get_position(AL_DUH_PLAYER *dp)
+{
+ return dp ? duh_sigrenderer_get_position(dp->sigrenderer) : -1;
+}
+
+
+
+AL_DUH_PLAYER *al_duh_encapsulate_sigrenderer(DUH_SIGRENDERER *sigrenderer, float volume, long bufsize, int freq)
+{
+ AL_DUH_PLAYER *dp;
+ int n_channels;
+
+ if (!sigrenderer)
+ return NULL;
+
+ dp = malloc(sizeof(*dp));
+ if (!dp)
+ return NULL;
+
+ n_channels = duh_sigrenderer_get_n_channels(sigrenderer);
+
+ /* This restriction is imposed by Allegro. */
+ ASSERT(n_channels > 0);
+ ASSERT(n_channels <= 2);
+
+ dp->flags = ADP_PLAYING;
+ dp->bufsize = bufsize;
+ dp->freq = freq;
+
+ dp->stream = play_audio_stream(bufsize, 16, n_channels - 1, freq, 255, 128);
+
+ if (!dp->stream) {
+ free(dp);
+ return NULL;
+ }
+
+ voice_set_priority(dp->stream->voice, 255);
+
+ dp->sigrenderer = sigrenderer;
+
+ dp->volume = volume;
+ dp->silentcount = 0;
+
+ return dp;
+}
+
+
+
+DUH_SIGRENDERER *al_duh_get_sigrenderer(AL_DUH_PLAYER *dp)
+{
+ return dp ? dp->sigrenderer : NULL;
+}
+
+
+
+/* IMPORTANT: This function will return NULL if the music has ended. */
+// Should this be changed? User might want to hack the underlying SIGRENDERER
+// and resurrect it (e.g. change pattern number), before it gets destroyed...
+DUH_SIGRENDERER *al_duh_decompose_to_sigrenderer(AL_DUH_PLAYER *dp)
+{
+ if (dp) {
+ DUH_SIGRENDERER *sigrenderer = dp->sigrenderer;
+ if (sigrenderer) stop_audio_stream(dp->stream);
+ free(dp);
+ return sigrenderer;
+ }
+ return NULL;
+}
+
+
+
+/* DEPRECATED */
+AL_DUH_PLAYER *al_duh_encapsulate_renderer(DUH_SIGRENDERER *dr, float volume, long bufsize, int freq)
+{
+ return al_duh_encapsulate_sigrenderer(dr, volume, bufsize, freq);
+}
+
+
+
+/* DEPRECATED */
+DUH_SIGRENDERER *al_duh_get_renderer(AL_DUH_PLAYER *dp)
+{
+ return al_duh_get_sigrenderer(dp);
+}
+
+
+
+/* DEPRECATED */
+DUH_SIGRENDERER *al_duh_decompose_to_renderer(AL_DUH_PLAYER *dp)
+{
+ return al_duh_decompose_to_sigrenderer(dp);
+}
diff --git a/plugins/dumb/dumb-kode54/src/allegro/datduh.c b/plugins/dumb/dumb-kode54/src/allegro/datduh.c
new file mode 100644
index 00000000..5dec3975
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/allegro/datduh.c
@@ -0,0 +1,60 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * datduh.c - Integration with Allegro's / / \ \
+ * datafiles. | < / \_
+ * | \/ /\ /
+ * By entheh. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <allegro.h>
+
+#include "aldumb.h"
+#include "internal/aldumb.h"
+
+
+
+static void *dat_read_duh(PACKFILE *f, long size)
+{
+ DUMBFILE *df;
+ DUH *duh;
+
+ (void)size;
+
+ df = dumbfile_open_packfile(f);
+
+ if (!df)
+ return NULL;
+
+ duh = read_duh(df);
+
+ dumbfile_close(df);
+
+ return duh;
+}
+
+
+
+/* dumb_register_dat_duh(): tells Allegro about the DUH datafile object. If
+ * you intend to load a datafile containing a DUH object, you must call this
+ * function first. It is recommended you pass DAT_DUH, but you may have a
+ * reason to use a different type (apart from pride, that doesn't count).
+ */
+void dumb_register_dat_duh(long type)
+{
+ register_datafile_object(
+ type,
+ &dat_read_duh,
+ &_dat_unload_duh
+ );
+}
diff --git a/plugins/dumb/dumb-kode54/src/allegro/datit.c b/plugins/dumb/dumb-kode54/src/allegro/datit.c
new file mode 100644
index 00000000..1a6ce2f6
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/allegro/datit.c
@@ -0,0 +1,62 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * datit.c - Integration of IT files with / / \ \
+ * Allegro's datafiles. | < / \_
+ * | \/ /\ /
+ * By entheh. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <allegro.h>
+
+#include "aldumb.h"
+#include "internal/aldumb.h"
+
+
+
+static void *dat_read_it(PACKFILE *f, long size)
+{
+ DUMBFILE *df;
+ DUH *duh;
+
+ (void)size;
+
+ df = dumbfile_open_packfile(f);
+
+ if (!df)
+ return NULL;
+
+ duh = dumb_read_it(df);
+
+ dumbfile_close(df);
+
+ return duh;
+}
+
+
+
+/* dumb_register_dat_it(): tells Allegro about the IT datafile object. If you
+ * intend to load a datafile containing an IT object, you must call this
+ * function first. It is recommended you pass DUMB_DAT_IT, but you may have a
+ * reason to use a different type (perhaps you already have a datafile with
+ * IT files in and they use a different type).
+ */
+void dumb_register_dat_it(long type)
+{
+ register_datafile_object(
+ type,
+ &dat_read_it,
+ &_dat_unload_duh
+ );
+}
+
diff --git a/plugins/dumb/dumb-kode54/src/allegro/datmod.c b/plugins/dumb/dumb-kode54/src/allegro/datmod.c
new file mode 100644
index 00000000..abbc1d9a
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/allegro/datmod.c
@@ -0,0 +1,61 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * datmod.c - Integration of MOD files with / / \ \
+ * Allegro's datafiles. | < / \_
+ * | \/ /\ /
+ * By entheh. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <allegro.h>
+
+#include "aldumb.h"
+#include "internal/aldumb.h"
+
+
+
+static void *dat_read_mod(PACKFILE *f, long size)
+{
+ DUMBFILE *df;
+ DUH *duh;
+
+ (void)size;
+
+ df = dumbfile_open_packfile(f);
+
+ if (!df)
+ return NULL;
+
+ duh = dumb_read_mod(df);
+
+ dumbfile_close(df);
+
+ return duh;
+}
+
+
+
+/* dumb_register_dat_mod(): tells Allegro about the MOD datafile object. If
+ * you intend to load a datafile containing a MOD object, you must call this
+ * function first. It is recommended you pass DUMB_DAT_MOD, but you may have
+ * a reason to use a different type (perhaps you already have a datafile with
+ * MOD files in and they use a different type).
+ */
+void dumb_register_dat_mod(long type)
+{
+ register_datafile_object(
+ type,
+ &dat_read_mod,
+ &_dat_unload_duh
+ );
+}
diff --git a/plugins/dumb/dumb-kode54/src/allegro/dats3m.c b/plugins/dumb/dumb-kode54/src/allegro/dats3m.c
new file mode 100644
index 00000000..8fe21666
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/allegro/dats3m.c
@@ -0,0 +1,61 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * dats3m.c - Integration of S3M files with / / \ \
+ * Allegro's datafiles. | < / \_
+ * | \/ /\ /
+ * By entheh. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <allegro.h>
+
+#include "aldumb.h"
+#include "internal/aldumb.h"
+
+
+
+static void *dat_read_s3m(PACKFILE *f, long size)
+{
+ DUMBFILE *df;
+ DUH *duh;
+
+ (void)size;
+
+ df = dumbfile_open_packfile(f);
+
+ if (!df)
+ return NULL;
+
+ duh = dumb_read_s3m(df);
+
+ dumbfile_close(df);
+
+ return duh;
+}
+
+
+
+/* dumb_register_dat_s3m(): tells Allegro about the S3M datafile object. If
+ * you intend to load a datafile containing an S3M object, you must call this
+ * function first. It is recommended you pass DUMB_DAT_S3M, but you may have
+ * a reason to use a different type (perhaps you already have a datafile with
+ * S3M files in and they use a different type).
+ */
+void dumb_register_dat_s3m(long type)
+{
+ register_datafile_object(
+ type,
+ &dat_read_s3m,
+ &_dat_unload_duh
+ );
+}
diff --git a/plugins/dumb/dumb-kode54/src/allegro/datunld.c b/plugins/dumb/dumb-kode54/src/allegro/datunld.c
new file mode 100644
index 00000000..68d359fb
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/allegro/datunld.c
@@ -0,0 +1,31 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * datunld.c - Unload function for integration / / \ \
+ * with Allegro's datafiles. | < / \_
+ * | \/ /\ /
+ * By entheh. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <allegro.h>
+
+#include "aldumb.h"
+#include "internal/aldumb.h"
+
+
+
+void _dat_unload_duh(void *duh)
+{
+ unload_duh(duh);
+}
+
diff --git a/plugins/dumb/dumb-kode54/src/allegro/datxm.c b/plugins/dumb/dumb-kode54/src/allegro/datxm.c
new file mode 100644
index 00000000..ff3ff25a
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/allegro/datxm.c
@@ -0,0 +1,62 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * datxm.c - Integration of XM files with / / \ \
+ * Allegro's datafiles. | < / \_
+ * | \/ /\ /
+ * By entheh. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <allegro.h>
+
+#include "aldumb.h"
+#include "internal/aldumb.h"
+
+
+
+static void *dat_read_xm(PACKFILE *f, long size)
+{
+ DUMBFILE *df;
+ DUH *duh;
+
+ (void)size;
+
+ df = dumbfile_open_packfile(f);
+
+ if (!df)
+ return NULL;
+
+ duh = dumb_read_xm(df);
+
+ dumbfile_close(df);
+
+ return duh;
+}
+
+
+
+/* dumb_register_dat_xm(): tells Allegro about the XM datafile object. If you
+ * intend to load a datafile containing an XM object, you must call this
+ * function first. It is recommended you pass DUMB_DAT_XM, but you may have a
+ * reason to use a different type (perhaps you already have a datafile with
+ * XM files in and they use a different type).
+ */
+void dumb_register_dat_xm(long type)
+{
+ register_datafile_object(
+ type,
+ &dat_read_xm,
+ &_dat_unload_duh
+ );
+}
+
diff --git a/plugins/dumb/dumb-kode54/src/allegro/packfile.c b/plugins/dumb/dumb-kode54/src/allegro/packfile.c
new file mode 100644
index 00000000..5a07bdb5
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/allegro/packfile.c
@@ -0,0 +1,98 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * packfile.c - Packfile input module. / / \ \
+ * | < / \_
+ * By entheh. | \/ /\ /
+ * \_ / > /
+ * Note that this does not use file compression; | \ / /
+ * for that you must open the file yourself and | ' /
+ * then use dumbfile_open_packfile(). \__/
+ */
+
+#include <allegro.h>
+
+#include "aldumb.h"
+
+
+
+static void *dumb_packfile_open(const char *filename)
+{
+ return pack_fopen(filename, F_READ);
+}
+
+
+
+static int dumb_packfile_skip(void *f, long n)
+{
+ return pack_fseek(f, n);
+}
+
+
+
+static int dumb_packfile_getc(void *f)
+{
+ return pack_getc(f);
+}
+
+
+
+static long dumb_packfile_getnc(char *ptr, long n, void *f)
+{
+ return pack_fread(ptr, n, f);
+}
+
+
+
+static void dumb_packfile_close(void *f)
+{
+ pack_fclose(f);
+}
+
+
+
+static DUMBFILE_SYSTEM packfile_dfs = {
+ &dumb_packfile_open,
+ &dumb_packfile_skip,
+ &dumb_packfile_getc,
+ &dumb_packfile_getnc,
+ &dumb_packfile_close
+};
+
+
+
+void dumb_register_packfiles(void)
+{
+ register_dumbfile_system(&packfile_dfs);
+}
+
+
+
+static DUMBFILE_SYSTEM packfile_dfs_leave_open = {
+ NULL,
+ &dumb_packfile_skip,
+ &dumb_packfile_getc,
+ &dumb_packfile_getnc,
+ NULL
+};
+
+
+
+DUMBFILE *dumbfile_open_packfile(PACKFILE *p)
+{
+ return dumbfile_open_ex(p, &packfile_dfs_leave_open);
+}
+
+
+
+DUMBFILE *dumbfile_from_packfile(PACKFILE *p)
+{
+ return p ? dumbfile_open_ex(p, &packfile_dfs) : NULL;
+}
diff --git a/plugins/dumb/dumb-kode54/src/core/atexit.c b/plugins/dumb/dumb-kode54/src/core/atexit.c
new file mode 100644
index 00000000..64814efd
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/core/atexit.c
@@ -0,0 +1,71 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * atexit.c - Library Clean-up Management. / / \ \
+ * | < / \_
+ * By entheh. | \/ /\ /
+ * \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+typedef struct DUMB_ATEXIT_PROC
+{
+ struct DUMB_ATEXIT_PROC *next;
+ void (*proc)(void);
+}
+DUMB_ATEXIT_PROC;
+
+
+
+static DUMB_ATEXIT_PROC *dumb_atexit_proc = NULL;
+
+
+
+int dumb_atexit(void (*proc)(void))
+{
+ DUMB_ATEXIT_PROC *dap = dumb_atexit_proc;
+
+ while (dap) {
+ if (dap->proc == proc) return 0;
+ dap = dap->next;
+ }
+
+ dap = malloc(sizeof(*dap));
+
+ if (!dap)
+ return -1;
+
+ dap->next = dumb_atexit_proc;
+ dap->proc = proc;
+ dumb_atexit_proc = dap;
+
+ return 0;
+}
+
+
+
+void dumb_exit(void)
+{
+ while (dumb_atexit_proc) {
+ DUMB_ATEXIT_PROC *next = dumb_atexit_proc->next;
+ (*dumb_atexit_proc->proc)();
+ free(dumb_atexit_proc);
+ dumb_atexit_proc = next;
+ }
+}
diff --git a/plugins/dumb/dumb-kode54/src/core/duhlen.c b/plugins/dumb/dumb-kode54/src/core/duhlen.c
new file mode 100644
index 00000000..2c3a3576
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/core/duhlen.c
@@ -0,0 +1,42 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * duhlen.c - Functions to set and return the / / \ \
+ * length of a DUH. | < / \_
+ * | \/ /\ /
+ * By entheh. \_ / > /
+ * | \ / /
+ * Note that the length of a DUH is a constant | ' /
+ * stored in the DUH struct and in the DUH disk \__/
+ * format. It will be calculated on loading for
+ * other formats in which the length is not explicitly stored. Also note that
+ * it does not necessarily correspond to the length of time for which the DUH
+ * will generate samples. Rather it represents a suitable point for a player
+ * such as Winamp to stop, and in any good DUH it will allow for any final
+ * flourish to fade out and be appreciated.
+ */
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+long duh_get_length(DUH *duh)
+{
+ return duh ? duh->length : 0;
+}
+
+
+
+void duh_set_length(DUH *duh, long length)
+{
+ if (duh)
+ duh->length = length;
+}
diff --git a/plugins/dumb/dumb-kode54/src/core/duhtag.c b/plugins/dumb/dumb-kode54/src/core/duhtag.c
new file mode 100644
index 00000000..77061094
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/core/duhtag.c
@@ -0,0 +1,38 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * duhtag.c - Function to return the tags stored / / \ \
+ * in a DUH struct (typically author | < / \_
+ * information). | \/ /\ /
+ * \_ / > /
+ * By entheh. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <string.h>
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+const char *duh_get_tag(DUH *duh, const char *key)
+{
+ int i;
+ ASSERT(key);
+ if (!duh || !duh->tag) return NULL;
+
+ for (i = 0; i < duh->n_tags; i++)
+ if (strcmp(key, duh->tag[i][0]) == 0)
+ return duh->tag[i][1];
+
+ return NULL;
+}
diff --git a/plugins/dumb/dumb-kode54/src/core/dumbfile.c b/plugins/dumb/dumb-kode54/src/core/dumbfile.c
new file mode 100644
index 00000000..ae738bf7
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/core/dumbfile.c
@@ -0,0 +1,401 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * dumbfile.c - Hookable, strictly sequential / / \ \
+ * file input functions. | < / \_
+ * | \/ /\ /
+ * By entheh. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+
+#include "dumb.h"
+
+
+
+static DUMBFILE_SYSTEM *the_dfs = NULL;
+
+
+
+void register_dumbfile_system(DUMBFILE_SYSTEM *dfs)
+{
+ ASSERT(dfs);
+ ASSERT(dfs->open);
+ ASSERT(dfs->getc);
+ ASSERT(dfs->close);
+ the_dfs = dfs;
+}
+
+
+
+struct DUMBFILE
+{
+ DUMBFILE_SYSTEM *dfs;
+ void *file;
+ long pos;
+};
+
+
+
+DUMBFILE *dumbfile_open(const char *filename)
+{
+ DUMBFILE *f;
+
+ ASSERT(the_dfs);
+
+ f = malloc(sizeof(*f));
+
+ if (!f)
+ return NULL;
+
+ f->dfs = the_dfs;
+
+ f->file = (*the_dfs->open)(filename);
+
+ if (!f->file) {
+ free(f);
+ return NULL;
+ }
+
+ f->pos = 0;
+
+ return f;
+}
+
+
+
+DUMBFILE *dumbfile_open_ex(void *file, DUMBFILE_SYSTEM *dfs)
+{
+ DUMBFILE *f;
+
+ ASSERT(dfs);
+ ASSERT(dfs->getc);
+ ASSERT(file);
+
+ f = malloc(sizeof(*f));
+
+ if (!f) {
+ if (dfs->close)
+ (*dfs->close)(file);
+ return NULL;
+ }
+
+ f->dfs = dfs;
+ f->file = file;
+
+ f->pos = 0;
+
+ return f;
+}
+
+
+
+long dumbfile_pos(DUMBFILE *f)
+{
+ ASSERT(f);
+
+ return f->pos;
+}
+
+
+
+int dumbfile_skip(DUMBFILE *f, long n)
+{
+ int rv;
+
+ ASSERT(f);
+ ASSERT(n >= 0);
+
+ if (f->pos < 0)
+ return -1;
+
+ f->pos += n;
+
+ if (f->dfs->skip) {
+ rv = (*f->dfs->skip)(f->file, n);
+ if (rv) {
+ f->pos = -1;
+ return rv;
+ }
+ } else {
+ while (n) {
+ rv = (*f->dfs->getc)(f->file);
+ if (rv < 0) {
+ f->pos = -1;
+ return rv;
+ }
+ n--;
+ }
+ }
+
+ return 0;
+}
+
+
+
+int dumbfile_getc(DUMBFILE *f)
+{
+ int rv;
+
+ ASSERT(f);
+
+ if (f->pos < 0)
+ return -1;
+
+ rv = (*f->dfs->getc)(f->file);
+
+ if (rv < 0) {
+ f->pos = -1;
+ return rv;
+ }
+
+ f->pos++;
+
+ return rv;
+}
+
+
+
+int dumbfile_igetw(DUMBFILE *f)
+{
+ int l, h;
+
+ ASSERT(f);
+
+ if (f->pos < 0)
+ return -1;
+
+ l = (*f->dfs->getc)(f->file);
+ if (l < 0) {
+ f->pos = -1;
+ return l;
+ }
+
+ h = (*f->dfs->getc)(f->file);
+ if (h < 0) {
+ f->pos = -1;
+ return h;
+ }
+
+ f->pos += 2;
+
+ return l | (h << 8);
+}
+
+
+
+int dumbfile_mgetw(DUMBFILE *f)
+{
+ int l, h;
+
+ ASSERT(f);
+
+ if (f->pos < 0)
+ return -1;
+
+ h = (*f->dfs->getc)(f->file);
+ if (h < 0) {
+ f->pos = -1;
+ return h;
+ }
+
+ l = (*f->dfs->getc)(f->file);
+ if (l < 0) {
+ f->pos = -1;
+ return l;
+ }
+
+ f->pos += 2;
+
+ return l | (h << 8);
+}
+
+
+
+long dumbfile_igetl(DUMBFILE *f)
+{
+ unsigned long rv, b;
+
+ ASSERT(f);
+
+ if (f->pos < 0)
+ return -1;
+
+ rv = (*f->dfs->getc)(f->file);
+ if ((signed long)rv < 0) {
+ f->pos = -1;
+ return rv;
+ }
+
+ b = (*f->dfs->getc)(f->file);
+ if ((signed long)b < 0) {
+ f->pos = -1;
+ return b;
+ }
+ rv |= b << 8;
+
+ b = (*f->dfs->getc)(f->file);
+ if ((signed long)b < 0) {
+ f->pos = -1;
+ return b;
+ }
+ rv |= b << 16;
+
+ b = (*f->dfs->getc)(f->file);
+ if ((signed long)b < 0) {
+ f->pos = -1;
+ return b;
+ }
+ rv |= b << 24;
+
+ f->pos += 4;
+
+ return rv;
+}
+
+
+
+long dumbfile_mgetl(DUMBFILE *f)
+{
+ unsigned long rv, b;
+
+ ASSERT(f);
+
+ if (f->pos < 0)
+ return -1;
+
+ rv = (*f->dfs->getc)(f->file);
+ if ((signed long)rv < 0) {
+ f->pos = -1;
+ return rv;
+ }
+ rv <<= 24;
+
+ b = (*f->dfs->getc)(f->file);
+ if ((signed long)b < 0) {
+ f->pos = -1;
+ return b;
+ }
+ rv |= b << 16;
+
+ b = (*f->dfs->getc)(f->file);
+ if ((signed long)b < 0) {
+ f->pos = -1;
+ return b;
+ }
+ rv |= b << 8;
+
+ b = (*f->dfs->getc)(f->file);
+ if ((signed long)b < 0) {
+ f->pos = -1;
+ return b;
+ }
+ rv |= b;
+
+ f->pos += 4;
+
+ return rv;
+}
+
+
+
+unsigned long dumbfile_cgetul(DUMBFILE *f)
+{
+ unsigned long rv = 0;
+ int v;
+
+ do {
+ v = dumbfile_getc(f);
+
+ if (v < 0)
+ return v;
+
+ rv <<= 7;
+ rv |= v & 0x7F;
+ } while (v & 0x80);
+
+ return rv;
+}
+
+
+
+signed long dumbfile_cgetsl(DUMBFILE *f)
+{
+ unsigned long rv = dumbfile_cgetul(f);
+
+ if (f->pos < 0)
+ return rv;
+
+ return (rv >> 1) | (rv << 31);
+}
+
+
+
+long dumbfile_getnc(char *ptr, long n, DUMBFILE *f)
+{
+ long rv;
+
+ ASSERT(f);
+ ASSERT(n >= 0);
+
+ if (f->pos < 0)
+ return -1;
+
+ if (f->dfs->getnc) {
+ rv = (*f->dfs->getnc)(ptr, n, f->file);
+ if (rv < n) {
+ f->pos = -1;
+ return MAX(rv, 0);
+ }
+ } else {
+ for (rv = 0; rv < n; rv++) {
+ int c = (*f->dfs->getc)(f->file);
+ if (c < 0) {
+ f->pos = -1;
+ return rv;
+ }
+ *ptr++ = c;
+ }
+ }
+
+ f->pos += rv;
+
+ return rv;
+}
+
+
+
+int dumbfile_error(DUMBFILE *f)
+{
+ ASSERT(f);
+
+ return f->pos < 0;
+}
+
+
+
+int dumbfile_close(DUMBFILE *f)
+{
+ int rv;
+
+ ASSERT(f);
+
+ rv = f->pos < 0;
+
+ if (f->dfs->close)
+ (*f->dfs->close)(f->file);
+
+ free(f);
+
+ return rv;
+}
diff --git a/plugins/dumb/dumb-kode54/src/core/loadduh.c b/plugins/dumb/dumb-kode54/src/core/loadduh.c
new file mode 100644
index 00000000..e954fe24
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/core/loadduh.c
@@ -0,0 +1,42 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loadduh.c - Code to read a DUH from a file, / / \ \
+ * opening and closing the file for | < / \_
+ * you. | \/ /\ /
+ * \_ / > /
+ * By entheh. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+/* load_duh(): loads a .duh file, returning a pointer to a DUH struct.
+ * When you have finished with it, you must pass the pointer to unload_duh()
+ * so that the memory can be freed.
+ */
+DUH *load_duh(const char *filename)
+{
+ DUH *duh;
+ DUMBFILE *f = dumbfile_open(filename);
+
+ if (!f)
+ return NULL;
+
+ duh = read_duh(f);
+
+ dumbfile_close(f);
+
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/core/makeduh.c b/plugins/dumb/dumb-kode54/src/core/makeduh.c
new file mode 100644
index 00000000..1edf2b1f
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/core/makeduh.c
@@ -0,0 +1,132 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * makeduh.c - Function to construct a DUH from / / \ \
+ * its components. | < / \_
+ * | \/ /\ /
+ * By entheh. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+static DUH_SIGNAL *make_signal(DUH_SIGTYPE_DESC *desc, sigdata_t *sigdata)
+{
+ DUH_SIGNAL *signal;
+
+ ASSERT((desc->start_sigrenderer && desc->end_sigrenderer) || (!desc->start_sigrenderer && !desc->end_sigrenderer));
+ ASSERT(desc->sigrenderer_generate_samples && desc->sigrenderer_get_current_sample);
+
+ signal = malloc(sizeof(*signal));
+
+ if (!signal) {
+ if (desc->unload_sigdata)
+ if (sigdata)
+ (*desc->unload_sigdata)(sigdata);
+ return NULL;
+ }
+
+ signal->desc = desc;
+ signal->sigdata = sigdata;
+
+ return signal;
+}
+
+
+
+DUH *make_duh(
+ long length,
+ int n_tags,
+ const char *const tags[][2],
+ int n_signals,
+ DUH_SIGTYPE_DESC *desc[],
+ sigdata_t *sigdata[]
+)
+{
+ DUH *duh = malloc(sizeof(*duh));
+ int i;
+ int fail;
+
+ if (duh) {
+ duh->n_signals = n_signals;
+
+ duh->signal = malloc(n_signals * sizeof(*duh->signal));
+
+ if (!duh->signal) {
+ free(duh);
+ duh = NULL;
+ }
+ }
+
+ if (!duh) {
+ for (i = 0; i < n_signals; i++)
+ if (desc[i]->unload_sigdata)
+ if (sigdata[i])
+ (*desc[i]->unload_sigdata)(sigdata[i]);
+ return NULL;
+ }
+
+ duh->n_tags = 0;
+ duh->tag = NULL;
+
+ fail = 0;
+
+ for (i = 0; i < n_signals; i++) {
+ duh->signal[i] = make_signal(desc[i], sigdata[i]);
+ if (!duh->signal[i])
+ fail = 1;
+ }
+
+ if (fail) {
+ unload_duh(duh);
+ return NULL;
+ }
+
+ duh->length = length;
+
+ {
+ int mem = n_tags * 2; /* account for NUL terminators here */
+ char *ptr;
+
+ for (i = 0; i < n_tags; i++)
+ mem += strlen(tags[i][0]) + strlen(tags[i][1]);
+
+ if (mem <= 0) return duh;
+
+ duh->tag = malloc(n_tags * sizeof(*duh->tag));
+ if (!duh->tag) return duh;
+ duh->tag[0][0] = malloc(mem);
+ if (!duh->tag[0][0]) {
+ free(duh->tag);
+ duh->tag = NULL;
+ return duh;
+ }
+ duh->n_tags = n_tags;
+ ptr = duh->tag[0][0];
+ for (i = 0; i < n_tags; i++) {
+ duh->tag[i][0] = ptr;
+ strcpy(ptr, tags[i][0]);
+ ptr += strlen(tags[i][0]) + 1;
+ duh->tag[i][1] = ptr;
+ strcpy(ptr, tags[i][1]);
+ ptr += strlen(tags[i][1]) + 1;
+ }
+ }
+
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/core/rawsig.c b/plugins/dumb/dumb-kode54/src/core/rawsig.c
new file mode 100644
index 00000000..75c41eb0
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/core/rawsig.c
@@ -0,0 +1,44 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * rawsig.c - Function to retrieve raw signal / / \ \
+ * data from a DUH provided you know | < / \_
+ * what type of signal it is. | \/ /\ /
+ * \_ / > /
+ * By entheh. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+/* You have to specify the type of sigdata, proving you know what to do with
+ * the pointer. If you get it wrong, you can expect NULL back.
+ */
+sigdata_t *duh_get_raw_sigdata(DUH *duh, int sig, long type)
+{
+ DUH_SIGNAL *signal;
+
+ if (!duh) return NULL;
+
+ if ((unsigned int)sig >= (unsigned int)duh->n_signals) return NULL;
+
+ signal = duh->signal[sig];
+
+ if (signal && signal->desc->type == type)
+ return signal->sigdata;
+
+ return NULL;
+}
diff --git a/plugins/dumb/dumb-kode54/src/core/readduh.c b/plugins/dumb/dumb-kode54/src/core/readduh.c
new file mode 100644
index 00000000..0fb775b2
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/core/readduh.c
@@ -0,0 +1,107 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * readduh.c - Code to read a DUH from an open / / \ \
+ * file. | < / \_
+ * | \/ /\ /
+ * By entheh. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+static DUH_SIGNAL *read_signal(DUH *duh, DUMBFILE *f)
+{
+ DUH_SIGNAL *signal;
+ long type;
+
+ signal = malloc(sizeof(*signal));
+
+ if (!signal)
+ return NULL;
+
+ type = dumbfile_mgetl(f);
+ if (dumbfile_error(f)) {
+ free(signal);
+ return NULL;
+ }
+
+ signal->desc = _dumb_get_sigtype_desc(type);
+ if (!signal->desc) {
+ free(signal);
+ return NULL;
+ }
+
+ if (signal->desc->load_sigdata) {
+ signal->sigdata = (*signal->desc->load_sigdata)(duh, f);
+ if (!signal->sigdata) {
+ free(signal);
+ return NULL;
+ }
+ } else
+ signal->sigdata = NULL;
+
+ return signal;
+}
+
+
+
+/* read_duh(): reads a DUH from an already open DUMBFILE, and returns its
+ * pointer, or null on error. The file is not closed.
+ */
+DUH *read_duh(DUMBFILE *f)
+{
+ DUH *duh;
+ int i;
+
+ if (dumbfile_mgetl(f) != DUH_SIGNATURE)
+ return NULL;
+
+ duh = malloc(sizeof(*duh));
+ if (!duh)
+ return NULL;
+
+ duh->length = dumbfile_igetl(f);
+ if (dumbfile_error(f) || duh->length <= 0) {
+ free(duh);
+ return NULL;
+ }
+
+ duh->n_signals = dumbfile_igetl(f);
+ if (dumbfile_error(f) || duh->n_signals <= 0) {
+ free(duh);
+ return NULL;
+ }
+
+ duh->signal = malloc(sizeof(*duh->signal) * duh->n_signals);
+ if (!duh->signal) {
+ free(duh);
+ return NULL;
+ }
+
+ for (i = 0; i < duh->n_signals; i++)
+ duh->signal[i] = NULL;
+
+ for (i = 0; i < duh->n_signals; i++) {
+ if (!(duh->signal[i] = read_signal(duh, f))) {
+ unload_duh(duh);
+ return NULL;
+ }
+ }
+
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/core/register.c b/plugins/dumb/dumb-kode54/src/core/register.c
new file mode 100644
index 00000000..2e16c9a7
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/core/register.c
@@ -0,0 +1,104 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * register.c - Signal type registration. / / \ \
+ * | < / \_
+ * By entheh. | \/ /\ /
+ * \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+static DUH_SIGTYPE_DESC_LINK *sigtype_desc = NULL;
+static DUH_SIGTYPE_DESC_LINK **sigtype_desc_tail = &sigtype_desc;
+
+
+
+/* destroy_sigtypes(): frees all memory allocated while registering signal
+ * types. This function is set up to be called by dumb_exit().
+ */
+static void destroy_sigtypes(void)
+{
+ DUH_SIGTYPE_DESC_LINK *desc_link = sigtype_desc, *next;
+ sigtype_desc = NULL;
+ sigtype_desc_tail = &sigtype_desc;
+
+ while (desc_link) {
+ next = desc_link->next;
+ free(desc_link);
+ desc_link = next;
+ }
+}
+
+
+
+/* dumb_register_sigtype(): registers a new signal type with DUMB. The signal
+ * type is identified by a four-character string (e.g. "WAVE"), which you can
+ * encode using the the DUMB_ID() macro (e.g. DUMB_ID('W','A','V','E')). The
+ * signal's behaviour is defined by four functions, whose pointers you pass
+ * here. See the documentation for details.
+ *
+ * If a DUH tries to use a signal that has not been registered using this
+ * function, then the library will fail to load the DUH.
+ */
+void dumb_register_sigtype(DUH_SIGTYPE_DESC *desc)
+{
+ DUH_SIGTYPE_DESC_LINK *desc_link = sigtype_desc;
+
+ ASSERT((desc->load_sigdata && desc->unload_sigdata) || (!desc->load_sigdata && !desc->unload_sigdata));
+ ASSERT((desc->start_sigrenderer && desc->end_sigrenderer) || (!desc->start_sigrenderer && !desc->end_sigrenderer));
+ ASSERT(desc->sigrenderer_generate_samples && desc->sigrenderer_get_current_sample);
+
+ if (desc_link) {
+ do {
+ if (desc_link->desc->type == desc->type) {
+ desc_link->desc = desc;
+ return;
+ }
+ desc_link = desc_link->next;
+ } while (desc_link);
+ } else
+ dumb_atexit(&destroy_sigtypes);
+
+ desc_link = *sigtype_desc_tail = malloc(sizeof(DUH_SIGTYPE_DESC_LINK));
+
+ if (!desc_link)
+ return;
+
+ desc_link->next = NULL;
+ sigtype_desc_tail = &desc_link->next;
+
+ desc_link->desc = desc;
+}
+
+
+
+/* _dumb_get_sigtype_desc(): searches the registered functions for a signal
+ * type matching the parameter. If such a sigtype is found, it returns a
+ * pointer to a sigtype descriptor containing the necessary functions to
+ * manage the signal. If none is found, it returns NULL.
+ */
+DUH_SIGTYPE_DESC *_dumb_get_sigtype_desc(long type)
+{
+ DUH_SIGTYPE_DESC_LINK *desc_link = sigtype_desc;
+
+ while (desc_link && desc_link->desc->type != type)
+ desc_link = desc_link->next;
+
+ return desc_link ? desc_link->desc : NULL;
+}
diff --git a/plugins/dumb/dumb-kode54/src/core/rendduh.c b/plugins/dumb/dumb-kode54/src/core/rendduh.c
new file mode 100644
index 00000000..1effa3eb
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/core/rendduh.c
@@ -0,0 +1,184 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * rendduh.c - Functions for rendering a DUH into / / \ \
+ * an end-user sample format. | < / \_
+ * | \/ /\ /
+ * By entheh. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+#include <limits.h>
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+/* On the x86, we can use some tricks to speed stuff up */
+#if (defined _MSC_VER) || (defined __DJGPP__) || (defined __MINGW__)
+// Can't we detect Linux and other x86 platforms here? :/
+
+#define FAST_MID(var, min, max) { \
+ var -= (min); \
+ var &= (~var) >> (sizeof(var) * CHAR_BIT - 1); \
+ var += (min); \
+ var -= (max); \
+ var &= var >> (sizeof(var) * CHAR_BIT - 1); \
+ var += (max); \
+}
+
+#define CONVERT8(src, pos, signconv) { \
+ signed int f = (src + 0x8000) >> 16; \
+ FAST_MID(f, -128, 127); \
+ ((char*)sptr)[pos] = (char)f ^ signconv; \
+}
+
+#define CONVERT16(src, pos, signconv) { \
+ signed int f = (src + 0x80) >> 8; \
+ FAST_MID(f, -32768, 32767); \
+ ((short*)sptr)[pos] = (short)(f ^ signconv); \
+}
+
+#else
+
+#define CONVERT8(src, pos, signconv) \
+{ \
+ signed int f = (src + 0x8000) >> 16; \
+ f = MID(-128, f, 127); \
+ ((char *)sptr)[pos] = (char)f ^ signconv; \
+}
+
+
+
+#define CONVERT16(src, pos, signconv) \
+{ \
+ signed int f = (src + 0x80) >> 8; \
+ f = MID(-32768, f, 32767); \
+ ((short *)sptr)[pos] = (short)(f ^ signconv); \
+}
+
+#endif
+
+
+
+/* DEPRECATED */
+DUH_SIGRENDERER *duh_start_renderer(DUH *duh, int n_channels, long pos)
+{
+ return duh_start_sigrenderer(duh, 0, n_channels, pos);
+}
+
+
+
+long duh_render(
+ DUH_SIGRENDERER *sigrenderer,
+ int bits, int unsign,
+ float volume, float delta,
+ long size, void *sptr
+)
+{
+ long n;
+
+ sample_t **sampptr;
+
+ int n_channels;
+
+ ASSERT(bits == 8 || bits == 16);
+ ASSERT(sptr);
+
+ if (!sigrenderer)
+ return 0;
+
+ n_channels = duh_sigrenderer_get_n_channels(sigrenderer);
+
+ ASSERT(n_channels > 0);
+ /* This restriction will be removed when need be. At the moment, tightly
+ * optimised loops exist for exactly one or two channels.
+ */
+ ASSERT(n_channels <= 2);
+
+ sampptr = allocate_sample_buffer(n_channels, size);
+
+ if (!sampptr)
+ return 0;
+
+ dumb_silence(sampptr[0], n_channels * size);
+
+ size = duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, sampptr);
+
+ if (bits == 16) {
+ int signconv = unsign ? 0x8000 : 0x0000;
+
+ for (n = 0; n < size * n_channels; n++) {
+ CONVERT16(sampptr[0][n], n, signconv);
+ }
+ } else {
+ char signconv = unsign ? 0x80 : 0x00;
+
+ for (n = 0; n < size * n_channels; n++) {
+ CONVERT8(sampptr[0][n], n, signconv);
+ }
+ }
+
+ destroy_sample_buffer(sampptr);
+
+ return size;
+}
+
+
+
+/* DEPRECATED */
+int duh_renderer_get_n_channels(DUH_SIGRENDERER *dr)
+{
+ return duh_sigrenderer_get_n_channels(dr);
+}
+
+
+
+/* DEPRECATED */
+long duh_renderer_get_position(DUH_SIGRENDERER *dr)
+{
+ return duh_sigrenderer_get_position(dr);
+}
+
+
+
+/* DEPRECATED */
+void duh_end_renderer(DUH_SIGRENDERER *dr)
+{
+ duh_end_sigrenderer(dr);
+}
+
+
+
+/* DEPRECATED */
+DUH_SIGRENDERER *duh_renderer_encapsulate_sigrenderer(DUH_SIGRENDERER *sigrenderer)
+{
+ return sigrenderer;
+}
+
+
+
+/* DEPRECATED */
+DUH_SIGRENDERER *duh_renderer_get_sigrenderer(DUH_SIGRENDERER *dr)
+{
+ return dr;
+}
+
+
+
+/* DEPRECATED */
+DUH_SIGRENDERER *duh_renderer_decompose_to_sigrenderer(DUH_SIGRENDERER *dr)
+{
+ return dr;
+}
diff --git a/plugins/dumb/dumb-kode54/src/core/rendsig.c b/plugins/dumb/dumb-kode54/src/core/rendsig.c
new file mode 100644
index 00000000..b8f866c5
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/core/rendsig.c
@@ -0,0 +1,344 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * rendsig.c - Wrappers to render samples from / / \ \
+ * the signals in a DUH. | < / \_
+ * | \/ /\ /
+ * By entheh. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+struct DUH_SIGRENDERER
+{
+ DUH_SIGTYPE_DESC *desc;
+
+ sigrenderer_t *sigrenderer;
+
+ int n_channels;
+
+ long pos;
+ int subpos;
+
+ DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK callback;
+ void *callback_data;
+};
+
+
+
+DUH_SIGRENDERER *duh_start_sigrenderer(DUH *duh, int sig, int n_channels, long pos)
+{
+ DUH_SIGRENDERER *sigrenderer;
+
+ DUH_SIGNAL *signal;
+ DUH_START_SIGRENDERER proc;
+
+ if (!duh)
+ return NULL;
+
+ if ((unsigned int)sig >= (unsigned int)duh->n_signals)
+ return NULL;
+
+ signal = duh->signal[sig];
+ if (!signal)
+ return NULL;
+
+ sigrenderer = malloc(sizeof(*sigrenderer));
+ if (!sigrenderer)
+ return NULL;
+
+ sigrenderer->desc = signal->desc;
+
+ proc = sigrenderer->desc->start_sigrenderer;
+
+ if (proc) {
+ duh->signal[sig] = NULL;
+ sigrenderer->sigrenderer = (*proc)(duh, signal->sigdata, n_channels, pos);
+ duh->signal[sig] = signal;
+
+ if (!sigrenderer->sigrenderer) {
+ free(sigrenderer);
+ return NULL;
+ }
+ } else
+ sigrenderer->sigrenderer = NULL;
+
+ sigrenderer->n_channels = n_channels;
+
+ sigrenderer->pos = pos;
+ sigrenderer->subpos = 0;
+
+ sigrenderer->callback = NULL;
+
+ return sigrenderer;
+}
+
+
+
+#include <stdio.h>
+void duh_sigrenderer_set_callback(
+ DUH_SIGRENDERER *sigrenderer,
+ DUH_SIGRENDERER_CALLBACK callback, void *data
+)
+{
+ (void)sigrenderer;
+ (void)callback;
+ (void)data;
+ /*fprintf(stderr,
+ "Call to deprecated function duh_sigrenderer_set_callback(). The callback\n"
+ "was not installed. See dumb/docs/deprec.txt for how to fix this.\n");*/
+}
+
+
+
+void duh_sigrenderer_set_analyser_callback(
+ DUH_SIGRENDERER *sigrenderer,
+ DUH_SIGRENDERER_ANALYSER_CALLBACK callback, void *data
+)
+{
+ (void)sigrenderer;
+ (void)callback;
+ (void)data;
+ fprintf(stderr,
+ "Call to deprecated function duh_sigrenderer_set_analyser_callback(). The\n"
+ "callback was not installed. See dumb/docs/deprec.txt for how to fix this.\n");
+}
+
+
+
+void duh_sigrenderer_set_sample_analyser_callback(
+ DUH_SIGRENDERER *sigrenderer,
+ DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK callback, void *data
+)
+{
+ if (sigrenderer) {
+ sigrenderer->callback = callback;
+ sigrenderer->callback_data = data;
+ }
+}
+
+
+
+int duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer)
+{
+ return sigrenderer ? sigrenderer->n_channels : 0;
+}
+
+
+
+long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer)
+{
+ return sigrenderer ? sigrenderer->pos : -1;
+}
+
+
+
+void duh_sigrenderer_set_sigparam(
+ DUH_SIGRENDERER *sigrenderer,
+ unsigned char id, long value
+)
+{
+ DUH_SIGRENDERER_SET_SIGPARAM proc;
+
+ if (!sigrenderer) return;
+
+ proc = sigrenderer->desc->sigrenderer_set_sigparam;
+ if (proc)
+ (*proc)(sigrenderer->sigrenderer, id, value);
+ else
+ TRACE("Parameter #%d = %ld for signal %c%c%c%c, which does not take parameters.\n",
+ (int)id,
+ value,
+ (int)(sigrenderer->desc->type >> 24),
+ (int)(sigrenderer->desc->type >> 16),
+ (int)(sigrenderer->desc->type >> 8),
+ (int)(sigrenderer->desc->type));
+}
+
+
+
+long duh_sigrenderer_generate_samples(
+ DUH_SIGRENDERER *sigrenderer,
+ float volume, float delta,
+ long size, sample_t **samples
+)
+{
+ long rendered;
+ LONG_LONG t;
+
+ if (!sigrenderer) return 0;
+
+ rendered = (*sigrenderer->desc->sigrenderer_generate_samples)
+ (sigrenderer->sigrenderer, volume, delta, size, samples);
+
+ if (rendered) {
+ if (sigrenderer->callback)
+ (*sigrenderer->callback)(sigrenderer->callback_data,
+ (const sample_t *const *)samples, sigrenderer->n_channels, rendered);
+
+ t = sigrenderer->subpos + (LONG_LONG)(delta * 65536.0 + 0.5) * rendered;
+
+ sigrenderer->pos += (long)(t >> 16);
+ sigrenderer->subpos = (int)t & 65535;
+ }
+
+ return rendered;
+}
+
+
+
+/* DEPRECATED */
+long duh_sigrenderer_get_samples(
+ DUH_SIGRENDERER *sigrenderer,
+ float volume, float delta,
+ long size, sample_t **samples
+)
+{
+ sample_t **s;
+ long rendered;
+ long i;
+ int j;
+ if (!samples) return duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, NULL);
+ s = allocate_sample_buffer(sigrenderer->n_channels, size);
+ if (!s) return 0;
+ dumb_silence(s[0], sigrenderer->n_channels * size);
+ rendered = duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, s);
+ for (j = 0; j < sigrenderer->n_channels; j++)
+ for (i = 0; i < rendered; i++)
+ samples[j][i] += s[0][i*sigrenderer->n_channels+j];
+ destroy_sample_buffer(s);
+ return rendered;
+}
+
+
+
+/* DEPRECATED */
+long duh_render_signal(
+ DUH_SIGRENDERER *sigrenderer,
+ float volume, float delta,
+ long size, sample_t **samples
+)
+{
+ sample_t **s;
+ long rendered;
+ long i;
+ int j;
+ if (!samples) return duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, NULL);
+ s = allocate_sample_buffer(sigrenderer->n_channels, size);
+ if (!s) return 0;
+ dumb_silence(s[0], sigrenderer->n_channels * size);
+ rendered = duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, s);
+ for (j = 0; j < sigrenderer->n_channels; j++)
+ for (i = 0; i < rendered; i++)
+ samples[j][i] += s[0][i*sigrenderer->n_channels+j] >> 8;
+ destroy_sample_buffer(s);
+ return rendered;
+}
+
+
+
+void duh_sigrenderer_get_current_sample(DUH_SIGRENDERER *sigrenderer, float volume, sample_t *samples)
+{
+ if (sigrenderer)
+ (*sigrenderer->desc->sigrenderer_get_current_sample)(sigrenderer->sigrenderer, volume, samples);
+}
+
+
+
+void duh_end_sigrenderer(DUH_SIGRENDERER *sigrenderer)
+{
+ if (sigrenderer) {
+ if (sigrenderer->desc->end_sigrenderer)
+ if (sigrenderer->sigrenderer)
+ (*sigrenderer->desc->end_sigrenderer)(sigrenderer->sigrenderer);
+
+ free(sigrenderer);
+ }
+}
+
+
+
+DUH_SIGRENDERER *duh_encapsulate_raw_sigrenderer(sigrenderer_t *vsigrenderer, DUH_SIGTYPE_DESC *desc, int n_channels, long pos)
+{
+ DUH_SIGRENDERER *sigrenderer;
+
+ if (desc->start_sigrenderer && !vsigrenderer) return NULL;
+
+ sigrenderer = malloc(sizeof(*sigrenderer));
+ if (!sigrenderer) {
+ if (desc->end_sigrenderer)
+ if (vsigrenderer)
+ (*desc->end_sigrenderer)(vsigrenderer);
+ return NULL;
+ }
+
+ sigrenderer->desc = desc;
+ sigrenderer->sigrenderer = vsigrenderer;
+
+ sigrenderer->n_channels = n_channels;
+
+ sigrenderer->pos = pos;
+ sigrenderer->subpos = 0;
+
+ sigrenderer->callback = NULL;
+
+ return sigrenderer;
+}
+
+
+
+sigrenderer_t *duh_get_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, long type)
+{
+ if (sigrenderer && sigrenderer->desc->type == type)
+ return sigrenderer->sigrenderer;
+
+ return NULL;
+}
+
+
+
+#if 0
+// This function is disabled because we don't know whether we want to destroy
+// the sigrenderer if the type doesn't match. We don't even know if we need
+// the function at all. Who would want to keep an IT_SIGRENDERER (for
+// instance) without keeping the DUH_SIGRENDERER?
+sigrenderer_t *duh_decompose_to_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, long type)
+{
+ if (sigrenderer && sigrenderer->desc->type == type) {
+
+
+
+ if (sigrenderer) {
+ if (sigrenderer->desc->end_sigrenderer)
+ if (sigrenderer->sigrenderer)
+ (*sigrenderer->desc->end_sigrenderer)(sigrenderer->sigrenderer);
+
+ free(sigrenderer);
+ }
+
+
+
+
+
+
+ return sigrenderer->sigrenderer;
+ }
+
+ return NULL;
+}
+#endif
diff --git a/plugins/dumb/dumb-kode54/src/core/unload.c b/plugins/dumb/dumb-kode54/src/core/unload.c
new file mode 100644
index 00000000..f241f718
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/core/unload.c
@@ -0,0 +1,64 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * unload.c - Code to free a DUH from memory. / / \ \
+ * | < / \_
+ * By entheh. | \/ /\ /
+ * \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+static void destroy_signal(DUH_SIGNAL *signal)
+{
+ if (signal) {
+ if (signal->desc)
+ if (signal->desc->unload_sigdata)
+ if (signal->sigdata)
+ (*signal->desc->unload_sigdata)(signal->sigdata);
+
+ free(signal);
+ }
+}
+
+
+
+/* unload_duh(): destroys a DUH struct. You must call this for every DUH
+ * struct created, when you've finished with it.
+ */
+void unload_duh(DUH *duh)
+{
+ int i;
+
+ if (duh) {
+ if (duh->signal) {
+ for (i = 0; i < duh->n_signals; i++)
+ destroy_signal(duh->signal[i]);
+
+ free(duh->signal);
+ }
+
+ if (duh->tag) {
+ if (duh->tag[0][0])
+ free(duh->tag[0][0]);
+ free(duh->tag);
+ }
+
+ free(duh);
+ }
+}
diff --git a/plugins/dumb/dumb-kode54/src/helpers/barray.c b/plugins/dumb/dumb-kode54/src/helpers/barray.c
new file mode 100644
index 00000000..95fe7af1
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/helpers/barray.c
@@ -0,0 +1,159 @@
+#include "internal/barray.h"
+
+#include <string.h>
+
+
+void * bit_array_create(size_t size)
+{
+ size_t bsize = ((size + 7) >> 3) + sizeof(size_t);
+ void * ret = calloc(1, bsize);
+ if (ret) *(size_t *)ret = size;
+ return ret;
+}
+
+void bit_array_destroy(void * array)
+{
+ if (array) free(array);
+}
+
+void * bit_array_dup(void * array)
+{
+ if (array)
+ {
+ size_t * size = (size_t *) array;
+ size_t bsize = ((*size + 7) >> 3) + sizeof(*size);
+ void * ret = malloc(bsize);
+ if (ret) memcpy(ret, array, bsize);
+ return ret;
+ }
+ return NULL;
+}
+
+void bit_array_reset(void * array)
+{
+ if (array)
+ {
+ size_t * size = (size_t *) array;
+ size_t bsize = (*size + 7) >> 3;
+ memset(size + 1, 0, bsize);
+ }
+}
+
+
+void bit_array_set(void * array, size_t bit)
+{
+ if (array)
+ {
+ size_t * size = (size_t *) array;
+ if (bit < *size)
+ {
+ unsigned char * ptr = (unsigned char *)(size + 1);
+ ptr[bit >> 3] |= (1U << (bit & 7));
+ }
+ }
+}
+
+int bit_array_test(void * array, size_t bit)
+{
+ if (array)
+ {
+ size_t * size = (size_t *) array;
+ if (bit < *size)
+ {
+ unsigned char * ptr = (unsigned char *)(size + 1);
+ if (ptr[bit >> 3] & (1U << (bit & 7)))
+ {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+int bit_array_test_range(void * array, size_t bit, size_t count)
+{
+ if (array)
+ {
+ size_t * size = (size_t *) array;
+ if (bit < *size)
+ {
+ unsigned char * ptr = (unsigned char *)(size + 1);
+ if ((bit & 7) && (count > 8))
+ {
+ while ((bit < *size) && count && (bit & 7))
+ {
+ if (ptr[bit >> 3] & (1U << (bit & 7))) return 1;
+ bit++;
+ count--;
+ }
+ }
+ if (!(bit & 7))
+ {
+ while (((*size - bit) >= 8) && (count >= 8))
+ {
+ if (ptr[bit >> 3]) return 1;
+ bit += 8;
+ count -= 8;
+ }
+ }
+ while ((bit < *size) && count)
+ {
+ if (ptr[bit >> 3] & (1U << (bit & 7))) return 1;
+ bit++;
+ count--;
+ }
+ }
+ }
+ return 0;
+}
+
+void bit_array_clear(void * array, size_t bit)
+{
+ if (array)
+ {
+ size_t * size = (size_t *) array;
+ if (bit < *size)
+ {
+ unsigned char * ptr = (unsigned char *)(size + 1);
+ ptr[bit >> 3] &= ~(1U << (bit & 7));
+ }
+ }
+}
+
+void bit_array_merge(void * dest, void * source, size_t offset)
+{
+ if (dest && source)
+ {
+ size_t * dsize = (size_t *) dest;
+ size_t * ssize = (size_t *) source;
+ size_t soffset = 0;
+ while (offset < *dsize && soffset < *ssize)
+ {
+ if (bit_array_test(source, soffset))
+ {
+ bit_array_set(dest, offset);
+ }
+ soffset++;
+ offset++;
+ }
+ }
+}
+
+void bit_array_mask(void * dest, void * source, size_t offset)
+{
+ if (dest && source)
+ {
+ size_t * dsize = (size_t *) dest;
+ size_t * ssize = (size_t *) source;
+ size_t soffset = 0;
+ while (offset < *dsize && soffset < *ssize)
+ {
+ if (bit_array_test(source, soffset))
+ {
+ bit_array_clear(dest, offset);
+ }
+ soffset++;
+ offset++;
+ }
+ }
+}
diff --git a/plugins/dumb/dumb-kode54/src/helpers/clickrem.c b/plugins/dumb/dumb-kode54/src/helpers/clickrem.c
new file mode 100644
index 00000000..336b492d
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/helpers/clickrem.c
@@ -0,0 +1,281 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * clickrem.c - Click removal helpers. / / \ \
+ * | < / \_
+ * By entheh. | \/ /\ /
+ * \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include "dumb.h"
+
+
+
+typedef struct DUMB_CLICK DUMB_CLICK;
+
+
+struct DUMB_CLICK_REMOVER
+{
+ DUMB_CLICK *click;
+ int n_clicks;
+
+ int offset;
+};
+
+
+struct DUMB_CLICK
+{
+ DUMB_CLICK *next;
+ long pos;
+ sample_t step;
+};
+
+
+
+DUMB_CLICK_REMOVER *dumb_create_click_remover(void)
+{
+ DUMB_CLICK_REMOVER *cr = malloc(sizeof(*cr));
+ if (!cr) return NULL;
+
+ cr->click = NULL;
+ cr->n_clicks = 0;
+
+ cr->offset = 0;
+
+ return cr;
+}
+
+
+
+void dumb_record_click(DUMB_CLICK_REMOVER *cr, long pos, sample_t step)
+{
+ DUMB_CLICK *click;
+
+ ASSERT(pos >= 0);
+
+ if (!cr || !step) return;
+
+ if (pos == 0) {
+ cr->offset -= step;
+ return;
+ }
+
+ click = malloc(sizeof(*click));
+ if (!click) return;
+
+ click->pos = pos;
+ click->step = step;
+
+ click->next = cr->click;
+ cr->click = click;
+ cr->n_clicks++;
+}
+
+
+
+static DUMB_CLICK *dumb_click_mergesort(DUMB_CLICK *click, int n_clicks)
+{
+ int i;
+ DUMB_CLICK *c1, *c2, **cp;
+
+ if (n_clicks <= 1) return click;
+
+ /* Split the list into two */
+ c1 = click;
+ cp = &c1;
+ for (i = 0; i < n_clicks; i += 2) cp = &(*cp)->next;
+ c2 = *cp;
+ *cp = NULL;
+
+ /* Sort the sublists */
+ c1 = dumb_click_mergesort(c1, (n_clicks + 1) >> 1);
+ c2 = dumb_click_mergesort(c2, n_clicks >> 1);
+
+ /* Merge them */
+ cp = &click;
+ while (c1 && c2) {
+ if (c1->pos > c2->pos) {
+ *cp = c2;
+ c2 = c2->next;
+ } else {
+ *cp = c1;
+ c1 = c1->next;
+ }
+ cp = &(*cp)->next;
+ }
+ if (c2)
+ *cp = c2;
+ else
+ *cp = c1;
+
+ return click;
+}
+
+
+
+void dumb_remove_clicks(DUMB_CLICK_REMOVER *cr, sample_t *samples, long length, int step, float halflife)
+{
+ DUMB_CLICK *click;
+ long pos = 0;
+ int offset;
+ int factor;
+
+ if (!cr) return;
+
+ factor = (int)floor(pow(0.5, 1.0/halflife) * (1U << 31));
+
+ click = dumb_click_mergesort(cr->click, cr->n_clicks);
+ cr->click = NULL;
+ cr->n_clicks = 0;
+
+ length *= step;
+
+ while (click) {
+ DUMB_CLICK *next = click->next;
+ int end = click->pos * step;
+ ASSERT(end <= length);
+ offset = cr->offset;
+ if (offset < 0) {
+ offset = -offset;
+ while (pos < end) {
+ samples[pos] -= offset;
+ offset = (int)(((LONG_LONG)(offset << 1) * factor) >> 32);
+ pos += step;
+ }
+ offset = -offset;
+ } else {
+ while (pos < end) {
+ samples[pos] += offset;
+ offset = (int)(((LONG_LONG)(offset << 1) * factor) >> 32);
+ pos += step;
+ }
+ }
+ cr->offset = offset - click->step;
+ free(click);
+ click = next;
+ }
+
+ offset = cr->offset;
+ if (offset < 0) {
+ offset = -offset;
+ while (pos < length) {
+ samples[pos] -= offset;
+ offset = (int)((LONG_LONG)(offset << 1) * factor >> 32);
+ pos += step;
+ }
+ offset = -offset;
+ } else {
+ while (pos < length) {
+ samples[pos] += offset;
+ offset = (int)((LONG_LONG)(offset << 1) * factor >> 32);
+ pos += step;
+ }
+ }
+ cr->offset = offset;
+}
+
+
+
+sample_t dumb_click_remover_get_offset(DUMB_CLICK_REMOVER *cr)
+{
+ return cr ? cr->offset : 0;
+}
+
+
+
+void dumb_destroy_click_remover(DUMB_CLICK_REMOVER *cr)
+{
+ if (cr) {
+ DUMB_CLICK *click = cr->click;
+ while (click) {
+ DUMB_CLICK *next = click->next;
+ free(click);
+ click = next;
+ }
+ free(cr);
+ }
+}
+
+
+
+DUMB_CLICK_REMOVER **dumb_create_click_remover_array(int n)
+{
+ int i;
+ DUMB_CLICK_REMOVER **cr;
+ if (n <= 0) return NULL;
+ cr = malloc(n * sizeof(*cr));
+ if (!cr) return NULL;
+ for (i = 0; i < n; i++) cr[i] = dumb_create_click_remover();
+ return cr;
+}
+
+
+
+void dumb_record_click_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step)
+{
+ if (cr) {
+ int i;
+ for (i = 0; i < n; i++)
+ dumb_record_click(cr[i], pos, step[i]);
+ }
+}
+
+
+
+void dumb_record_click_negative_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step)
+{
+ if (cr) {
+ int i;
+ for (i = 0; i < n; i++)
+ dumb_record_click(cr[i], pos, -step[i]);
+ }
+}
+
+
+
+void dumb_remove_clicks_array(int n, DUMB_CLICK_REMOVER **cr, sample_t **samples, long length, float halflife)
+{
+ if (cr) {
+ int i;
+ for (i = 0; i < n >> 1; i++) {
+ dumb_remove_clicks(cr[i << 1], samples[i], length, 2, halflife);
+ dumb_remove_clicks(cr[(i << 1) + 1], samples[i] + 1, length, 2, halflife);
+ }
+ if (n & 1)
+ dumb_remove_clicks(cr[i << 1], samples[i], length, 1, halflife);
+ }
+}
+
+
+
+void dumb_click_remover_get_offset_array(int n, DUMB_CLICK_REMOVER **cr, sample_t *offset)
+{
+ if (cr) {
+ int i;
+ for (i = 0; i < n; i++)
+ if (cr[i]) offset[i] += cr[i]->offset;
+ }
+}
+
+
+
+void dumb_destroy_click_remover_array(int n, DUMB_CLICK_REMOVER **cr)
+{
+ if (cr) {
+ int i;
+ for (i = 0; i < n; i++) dumb_destroy_click_remover(cr[i]);
+ free(cr);
+ }
+}
diff --git a/plugins/dumb/dumb-kode54/src/helpers/memfile.c b/plugins/dumb/dumb-kode54/src/helpers/memfile.c
new file mode 100644
index 00000000..6fcbbc7c
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/helpers/memfile.c
@@ -0,0 +1,96 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * memfile.c - Module for reading data from / / \ \
+ * memory using a DUMBFILE. | < / \_
+ * | \/ /\ /
+ * By entheh. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+
+
+
+typedef struct MEMFILE MEMFILE;
+
+struct MEMFILE
+{
+ const char *ptr;
+ long left;
+};
+
+
+
+static int dumb_memfile_skip(void *f, long n)
+{
+ MEMFILE *m = f;
+ if (n > m->left) return -1;
+ m->ptr += n;
+ m->left -= n;
+ return 0;
+}
+
+
+
+static int dumb_memfile_getc(void *f)
+{
+ MEMFILE *m = f;
+ if (m->left <= 0) return -1;
+ m->left--;
+ return *(const unsigned char *)m->ptr++;
+}
+
+
+
+static long dumb_memfile_getnc(char *ptr, long n, void *f)
+{
+ MEMFILE *m = f;
+ if (n > m->left) n = m->left;
+ memcpy(ptr, m->ptr, n);
+ m->ptr += n;
+ m->left -= n;
+ return n;
+}
+
+
+
+static void dumb_memfile_close(void *f)
+{
+ free(f);
+}
+
+
+
+static const DUMBFILE_SYSTEM memfile_dfs = {
+ NULL,
+ &dumb_memfile_skip,
+ &dumb_memfile_getc,
+ &dumb_memfile_getnc,
+ &dumb_memfile_close
+};
+
+
+
+DUMBFILE *dumbfile_open_memory(const char *data, long size)
+{
+ MEMFILE *m = malloc(sizeof(*m));
+ if (!m) return NULL;
+
+ m->ptr = data;
+ m->left = size;
+
+ return dumbfile_open_ex(m, &memfile_dfs);
+}
diff --git a/plugins/dumb/dumb-kode54/src/helpers/resamp2.inc b/plugins/dumb/dumb-kode54/src/helpers/resamp2.inc
new file mode 100644
index 00000000..ecdc400c
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/helpers/resamp2.inc
@@ -0,0 +1,160 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * resamp2.inc - Resampling helper template. / / \ \
+ * | < / \_
+ * By Bob and entheh. | \/ /\ /
+ * \_ / > /
+ * In order to find a good trade-off between | \ / /
+ * speed and accuracy in this code, some tests | ' /
+ * were carried out regarding the behaviour of \__/
+ * long long ints with gcc. The following code
+ * was tested:
+ *
+ * int a, b, c;
+ * c = ((long long)a * b) >> 16;
+ *
+ * DJGPP GCC Version 3.0.3 generated the following assembly language code for
+ * the multiplication and scaling, leaving the 32-bit result in EAX.
+ *
+ * movl -8(%ebp), %eax ; read one int into EAX
+ * imull -4(%ebp) ; multiply by the other; result goes in EDX:EAX
+ * shrdl $16, %edx, %eax ; shift EAX right 16, shifting bits in from EDX
+ *
+ * Note that a 32*32->64 multiplication is performed, allowing for high
+ * accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally),
+ * so it is a minor concern when four multiplications are being performed
+ * (the cubic resampler). On the Pentium MMX and earlier, it takes four or
+ * more cycles, so this method is unsuitable for use in the low-quality
+ * resamplers.
+ *
+ * Since "long long" is a gcc-specific extension, we use LONG_LONG instead,
+ * defined in dumb.h. We may investigate later what code MSVC generates, but
+ * if it seems too slow then we suggest you use a good compiler.
+ *
+ * FIXME: these comments are somewhat out of date now.
+ */
+
+
+
+#define SUFFIX3 _1
+
+/* For convenience, returns nonzero on stop. */
+static int process_pickup(DUMB_RESAMPLER *resampler)
+{
+ if (resampler->overshot < 0) {
+ resampler->overshot = 0;
+ dumb_resample(resampler, NULL, 2, MONO_DEST_VOLUME_ZEROS, 1.0f); /* Doesn't matter which SUFFIX3. */
+ COPYSRC(resampler->X, 0, resampler->X, 1);
+ }
+
+ for (;;) {
+ SRCTYPE *src = resampler->src;
+
+ if (resampler->dir < 0) {
+ if (resampler->overshot >= 3 && resampler->pos+3 >= resampler->start) COPYSRC(resampler->X, 0, src, resampler->pos+3);
+ if (resampler->overshot >= 2 && resampler->pos+2 >= resampler->start) COPYSRC(resampler->X, 1, src, resampler->pos+2);
+ if (resampler->overshot >= 1 && resampler->pos+1 >= resampler->start) COPYSRC(resampler->X, 2, src, resampler->pos+1);
+ resampler->overshot = resampler->start - resampler->pos - 1;
+ } else {
+ if (resampler->overshot >= 3 && resampler->pos-3 < resampler->end) COPYSRC(resampler->X, 0, src, resampler->pos-3);
+ if (resampler->overshot >= 2 && resampler->pos-2 < resampler->end) COPYSRC(resampler->X, 1, src, resampler->pos-2);
+ if (resampler->overshot >= 1 && resampler->pos-1 < resampler->end) COPYSRC(resampler->X, 2, src, resampler->pos-1);
+ resampler->overshot = resampler->pos - resampler->end;
+ }
+
+ if (resampler->overshot < 0) {
+ resampler->overshot = 0;
+ return 0;
+ }
+
+ if (!resampler->pickup) {
+ resampler->dir = 0;
+ return 1;
+ }
+ (*resampler->pickup)(resampler, resampler->pickup_data);
+ if (resampler->dir == 0) return 1;
+ ASSERT(resampler->dir == -1 || resampler->dir == 1);
+ }
+}
+
+
+
+/* Create mono destination resampler. */
+/* SUFFIX3 was set above. */
+#define VOLUME_PARAMETERS MONO_DEST_VOLUME_PARAMETERS
+#define VOLUME_VARIABLES MONO_DEST_VOLUME_VARIABLES
+#define SET_VOLUME_VARIABLES SET_MONO_DEST_VOLUME_VARIABLES
+#define RETURN_VOLUME_VARIABLES RETURN_MONO_DEST_VOLUME_VARIABLES
+#define VOLUMES_ARE_ZERO MONO_DEST_VOLUMES_ARE_ZERO
+#define MIX_ALIAS(op, upd, offset) MONO_DEST_MIX_ALIAS(op, upd, offset)
+#define MIX_LINEAR(op, upd, o0, o1) MONO_DEST_MIX_LINEAR(op, upd, o0, o1)
+#define MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) MONO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3)
+#define MIX_ZEROS(op) *dst++ op 0
+#include "resamp3.inc"
+
+/* Create stereo destination resampler. */
+#define SUFFIX3 _2
+#define VOLUME_PARAMETERS DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right
+#define VOLUME_VARIABLES lvol=0, lvolr=0, lvold=0, lvolt=0, lvolm=0, rvol=0, rvolr=0, rvold=0, rvolt=0, rvolm=0
+#define SET_VOLUME_VARIABLES { \
+ if ( volume_left ) { \
+ lvolr = (int)(volume_left->volume * 16777216.0); \
+ lvold = (int)(volume_left->delta * 16777216.0); \
+ lvolt = (int)(volume_left->target * 16777216.0); \
+ lvolm = (int)(volume_left->mix * 16777216.0); \
+ lvol = MULSCV( lvolr, lvolm ); \
+ if ( lvolr == lvolt ) volume_left = NULL; \
+ } else { \
+ lvol = 0; \
+ lvolt = 0; \
+ } \
+ if ( volume_right ) { \
+ rvolr = (int)(volume_right->volume * 16777216.0); \
+ rvold = (int)(volume_right->delta * 16777216.0); \
+ rvolt = (int)(volume_right->target * 16777216.0); \
+ rvolm = (int)(volume_right->mix * 16777216.0); \
+ rvol = MULSCV( rvolr, rvolm ); \
+ if ( rvolr == rvolt ) volume_right = NULL; \
+ } else { \
+ rvol = 0; \
+ rvolt = 0; \
+ } \
+}
+#define RETURN_VOLUME_VARIABLES { \
+ if ( volume_left ) volume_left->volume = (float)lvolr / 16777216.0f; \
+ if ( volume_right ) volume_right->volume = (float)rvolr / 16777216.0f; \
+}
+#define VOLUMES_ARE_ZERO (lvol == 0 && lvolt == 0 && rvol == 0 && rvolt == 0)
+#define MIX_ALIAS(op, upd, offset) STEREO_DEST_MIX_ALIAS(op, upd, offset)
+#define MIX_LINEAR(op, upd, o0, o1) STEREO_DEST_MIX_LINEAR(op, upd, o0, o1)
+#define MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) STEREO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3)
+#define MIX_ZEROS(op) { *dst++ op 0; *dst++ op 0; }
+#include "resamp3.inc"
+
+
+
+#undef STEREO_DEST_MIX_CUBIC
+#undef MONO_DEST_MIX_CUBIC
+#undef STEREO_DEST_MIX_LINEAR
+#undef MONO_DEST_MIX_LINEAR
+#undef STEREO_DEST_MIX_ALIAS
+#undef MONO_DEST_MIX_ALIAS
+#undef MONO_DEST_VOLUMES_ARE_ZERO
+#undef SET_MONO_DEST_VOLUME_VARIABLES
+#undef RETURN_MONO_DEST_VOLUME_VARIABLES
+#undef MONO_DEST_VOLUME_ZEROS
+#undef MONO_DEST_VOLUME_VARIABLES
+#undef MONO_DEST_VOLUME_PARAMETERS
+#undef COPYSRC2
+#undef COPYSRC
+#undef DIVIDE_BY_SRC_CHANNELS
+#undef SRC_CHANNELS
+#undef SUFFIX2
diff --git a/plugins/dumb/dumb-kode54/src/helpers/resamp3.inc b/plugins/dumb/dumb-kode54/src/helpers/resamp3.inc
new file mode 100644
index 00000000..0c13f201
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/helpers/resamp3.inc
@@ -0,0 +1,376 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * resamp3.inc - Resampling helper template. / / \ \
+ * | < / \_
+ * By Bob and entheh. | \/ /\ /
+ * \_ / > /
+ * In order to find a good trade-off between | \ / /
+ * speed and accuracy in this code, some tests | ' /
+ * were carried out regarding the behaviour of \__/
+ * long long ints with gcc. The following code
+ * was tested:
+ *
+ * int a, b, c;
+ * c = ((long long)a * b) >> 16;
+ *
+ * DJGPP GCC Version 3.0.3 generated the following assembly language code for
+ * the multiplication and scaling, leaving the 32-bit result in EAX.
+ *
+ * movl -8(%ebp), %eax ; read one int into EAX
+ * imull -4(%ebp) ; multiply by the other; result goes in EDX:EAX
+ * shrdl $16, %edx, %eax ; shift EAX right 16, shifting bits in from EDX
+ *
+ * Note that a 32*32->64 multiplication is performed, allowing for high
+ * accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally),
+ * so it is a minor concern when four multiplications are being performed
+ * (the cubic resampler). On the Pentium MMX and earlier, it takes four or
+ * more cycles, so this method is unsuitable for use in the low-quality
+ * resamplers.
+ *
+ * Since "long long" is a gcc-specific extension, we use LONG_LONG instead,
+ * defined in dumb.h. We may investigate later what code MSVC generates, but
+ * if it seems too slow then we suggest you use a good compiler.
+ *
+ * FIXME: these comments are somewhat out of date now.
+ */
+
+
+
+long dumb_resample(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, VOLUME_PARAMETERS, float delta)
+{
+ int dt;
+ int VOLUME_VARIABLES;
+ long done;
+ long todo;
+ LONG_LONG todo64;
+ int quality;
+
+ if (!resampler || resampler->dir == 0) return 0;
+ ASSERT(resampler->dir == -1 || resampler->dir == 1);
+
+ done = 0;
+ dt = (int)(delta * 65536.0 + 0.5);
+ if (dt == 0) return 0;
+ SET_VOLUME_VARIABLES;
+
+ if (VOLUMES_ARE_ZERO) dst = NULL;
+
+ init_cubic();
+
+ quality = resampler->quality;
+
+ while (done < dst_size) {
+ if (process_pickup(resampler)) {
+ RETURN_VOLUME_VARIABLES;
+ return done;
+ }
+
+ if ((resampler->dir ^ dt) < 0)
+ dt = -dt;
+
+ if (resampler->dir < 0)
+ todo64 = ((((LONG_LONG)(resampler->pos - resampler->start) << 16) + resampler->subpos - dt) / -dt);
+ else
+ todo64 = ((((LONG_LONG)(resampler->end - resampler->pos) << 16) - resampler->subpos - 1 + dt) / dt);
+
+ if (todo64 < 0)
+ todo = 0;
+ else if (todo64 > dst_size - done)
+ todo = dst_size - done;
+ else
+ todo = (long) todo64;
+
+ done += todo;
+
+ {
+ SRCTYPE *src = resampler->src;
+ long pos = resampler->pos;
+ int subpos = resampler->subpos;
+ long diff = pos;
+ long overshot;
+ if (resampler->dir < 0) {
+ if (!dst) {
+ /* Silence or simulation */
+ LONG_LONG new_subpos = subpos + (LONG_LONG)dt * todo;
+ pos += (long)(new_subpos >> 16);
+ subpos = (long)new_subpos & 65535;
+ } else if (quality <= DUMB_RQ_ALIASING) {
+ /* Aliasing, backwards */
+ SRCTYPE xbuf[2*SRC_CHANNELS];
+ SRCTYPE *x = &xbuf[0];
+ SRCTYPE *xstart;
+ COPYSRC(xbuf, 0, resampler->X, 1);
+ COPYSRC(xbuf, 1, resampler->X, 2);
+ while (todo && x < &xbuf[2*SRC_CHANNELS]) {
+ // TODO: check what happens when multiple tempo slides occur per row
+ HEAVYASSERT(pos >= resampler->start);
+ MIX_ALIAS(+=, 1, 0);
+ subpos += dt;
+ pos += subpos >> 16;
+ x -= (subpos >> 16) * SRC_CHANNELS;
+ subpos &= 65535;
+ todo--;
+ }
+ x = xstart = &src[pos*SRC_CHANNELS];
+ LOOP4(todo,
+ MIX_ALIAS(+=, 1, 2);
+ subpos += dt;
+ x += (subpos >> 16) * SRC_CHANNELS;
+ subpos &= 65535;
+ );
+ pos += DIVIDE_BY_SRC_CHANNELS(x - xstart);
+ } else if (quality <= DUMB_RQ_LINEAR) {
+ /* Linear interpolation, backwards */
+ SRCTYPE xbuf[3*SRC_CHANNELS];
+ SRCTYPE *x = &xbuf[1*SRC_CHANNELS];
+ COPYSRC(xbuf, 0, resampler->X, 1);
+ COPYSRC(xbuf, 1, resampler->X, 2);
+ COPYSRC(xbuf, 2, src, pos);
+ while (todo && x < &xbuf[3*SRC_CHANNELS]) {
+ HEAVYASSERT(pos >= resampler->start);
+ MIX_LINEAR(+=, 1, 0, -1);
+ subpos += dt;
+ pos += subpos >> 16;
+ x -= (subpos >> 16) * SRC_CHANNELS;
+ subpos &= 65535;
+ todo--;
+ }
+ // TODO: use xstart for others too
+ x = &src[pos*SRC_CHANNELS];
+ LOOP4(todo,
+ HEAVYASSERT(pos >= resampler->start);
+ MIX_LINEAR(+=, 1, 1, 2);
+ subpos += dt;
+ pos += subpos >> 16;
+ x += (subpos >> 16) * SRC_CHANNELS;
+ subpos &= 65535;
+ );
+ } else {
+ /* Cubic interpolation, backwards */
+ SRCTYPE xbuf[6*SRC_CHANNELS];
+ SRCTYPE *x = &xbuf[3*SRC_CHANNELS];
+ COPYSRC(xbuf, 0, resampler->X, 0);
+ COPYSRC(xbuf, 1, resampler->X, 1);
+ COPYSRC(xbuf, 2, resampler->X, 2);
+ COPYSRC(xbuf, 3, src, pos);
+ if (pos-1 >= resampler->start) COPYSRC(xbuf, 4, src, pos-1);
+ if (pos-2 >= resampler->start) COPYSRC(xbuf, 5, src, pos-2);
+ while (todo && x < &xbuf[6*SRC_CHANNELS]) {
+ HEAVYASSERT(pos >= resampler->start);
+ MIX_CUBIC(+=, 1, x, x, 0, -1, -2, -3);
+ subpos += dt;
+ pos += subpos >> 16;
+ x -= (subpos >> 16) * SRC_CHANNELS;
+ subpos &= 65535;
+ todo--;
+ }
+ x = &src[pos*SRC_CHANNELS];
+ LOOP4(todo,
+ HEAVYASSERT(pos >= resampler->start);
+ MIX_CUBIC(+=, 1, x, x, 0, 1, 2, 3);
+ subpos += dt;
+ pos += subpos >> 16;
+ x += (subpos >> 16) * SRC_CHANNELS;
+ subpos &= 65535;
+ );
+ }
+ diff = diff - pos;
+ overshot = resampler->start - pos - 1;
+ if (diff >= 3) {
+ COPYSRC2(resampler->X, 0, overshot < 3, src, pos+3);
+ COPYSRC2(resampler->X, 1, overshot < 2, src, pos+2);
+ COPYSRC2(resampler->X, 2, overshot < 1, src, pos+1);
+ } else if (diff >= 2) {
+ COPYSRC(resampler->X, 0, resampler->X, 2);
+ COPYSRC2(resampler->X, 1, overshot < 2, src, pos+2);
+ COPYSRC2(resampler->X, 2, overshot < 1, src, pos+1);
+ } else if (diff >= 1) {
+ COPYSRC(resampler->X, 0, resampler->X, 1);
+ COPYSRC(resampler->X, 1, resampler->X, 2);
+ COPYSRC2(resampler->X, 2, overshot < 1, src, pos+1);
+ }
+ } else {
+ if (!dst) {
+ /* Silence or simulation */
+ LONG_LONG new_subpos = subpos + (LONG_LONG)dt * todo;
+ pos += (long)(new_subpos >> 16);
+ subpos = (long)new_subpos & 65535;
+ } else if (quality <= DUMB_RQ_ALIASING) {
+ /* Aliasing, forwards */
+ SRCTYPE xbuf[2*SRC_CHANNELS];
+ SRCTYPE *x = &xbuf[0];
+ SRCTYPE *xstart;
+ COPYSRC(xbuf, 0, resampler->X, 1);
+ COPYSRC(xbuf, 1, resampler->X, 2);
+ while (todo && x < &xbuf[2*SRC_CHANNELS]) {
+ HEAVYASSERT(pos < resampler->end);
+ MIX_ALIAS(+=, 1, 0);
+ subpos += dt;
+ pos += subpos >> 16;
+ x += (subpos >> 16) * SRC_CHANNELS;
+ subpos &= 65535;
+ todo--;
+ }
+ x = xstart = &src[pos*SRC_CHANNELS];
+ LOOP4(todo,
+ MIX_ALIAS(+=, 1, -2);
+ subpos += dt;
+ x += (subpos >> 16) * SRC_CHANNELS;
+ subpos &= 65535;
+ );
+ pos += DIVIDE_BY_SRC_CHANNELS(x - xstart);
+ } else if (quality <= DUMB_RQ_LINEAR) {
+ /* Linear interpolation, forwards */
+ SRCTYPE xbuf[3*SRC_CHANNELS];
+ SRCTYPE *x = &xbuf[1*SRC_CHANNELS];
+ COPYSRC(xbuf, 0, resampler->X, 1);
+ COPYSRC(xbuf, 1, resampler->X, 2);
+ COPYSRC(xbuf, 2, src, pos);
+ while (todo && x < &xbuf[3*SRC_CHANNELS]) {
+ HEAVYASSERT(pos < resampler->end);
+ MIX_LINEAR(+=, 1, -1, 0);
+ subpos += dt;
+ pos += subpos >> 16;
+ x += (subpos >> 16) * SRC_CHANNELS;
+ subpos &= 65535;
+ todo--;
+ }
+ x = &src[pos*SRC_CHANNELS];
+ LOOP4(todo,
+ HEAVYASSERT(pos < resampler->end);
+ MIX_LINEAR(+=, 1, -2, -1);
+ subpos += dt;
+ pos += subpos >> 16;
+ x += (subpos >> 16) * SRC_CHANNELS;
+ subpos &= 65535;
+ );
+ } else {
+ /* Cubic interpolation, forwards */
+ SRCTYPE xbuf[6*SRC_CHANNELS];
+ SRCTYPE *x = &xbuf[3*SRC_CHANNELS];
+ COPYSRC(xbuf, 0, resampler->X, 0);
+ COPYSRC(xbuf, 1, resampler->X, 1);
+ COPYSRC(xbuf, 2, resampler->X, 2);
+ COPYSRC(xbuf, 3, src, pos);
+ if (pos+1 < resampler->end) COPYSRC(xbuf, 4, src, pos+1);
+ if (pos+2 < resampler->end) COPYSRC(xbuf, 5, src, pos+2);
+ while (todo && x < &xbuf[6*SRC_CHANNELS]) {
+ HEAVYASSERT(pos < resampler->end);
+ MIX_CUBIC(+=, 1, x, x, -3, -2, -1, 0);
+ subpos += dt;
+ pos += subpos >> 16;
+ x += (subpos >> 16) * SRC_CHANNELS;
+ subpos &= 65535;
+ todo--;
+ }
+ x = &src[pos*SRC_CHANNELS];
+ LOOP4(todo,
+ HEAVYASSERT(pos < resampler->end);
+ MIX_CUBIC(+=, 1, x, x, -3, -2, -1, 0);
+ subpos += dt;
+ pos += subpos >> 16;
+ x += (subpos >> 16) * SRC_CHANNELS;
+ subpos &= 65535;
+ );
+ }
+ diff = pos - diff;
+ overshot = pos - resampler->end;
+ if (diff >= 3) {
+ COPYSRC2(resampler->X, 0, overshot < 3, src, pos-3);
+ COPYSRC2(resampler->X, 1, overshot < 2, src, pos-2);
+ COPYSRC2(resampler->X, 2, overshot < 1, src, pos-1);
+ } else if (diff >= 2) {
+ COPYSRC(resampler->X, 0, resampler->X, 2);
+ COPYSRC2(resampler->X, 1, overshot < 2, src, pos-2);
+ COPYSRC2(resampler->X, 2, overshot < 1, src, pos-1);
+ } else if (diff >= 1) {
+ COPYSRC(resampler->X, 0, resampler->X, 1);
+ COPYSRC(resampler->X, 1, resampler->X, 2);
+ COPYSRC2(resampler->X, 2, overshot < 1, src, pos-1);
+ }
+ }
+ resampler->pos = pos;
+ resampler->subpos = subpos;
+ }
+ }
+
+ RETURN_VOLUME_VARIABLES;
+ return done;
+}
+
+
+
+void dumb_resample_get_current_sample(DUMB_RESAMPLER *resampler, VOLUME_PARAMETERS, sample_t *dst)
+{
+ int VOLUME_VARIABLES;
+ SRCTYPE *src;
+ long pos;
+ int subpos;
+ int quality;
+ SRCTYPE *x;
+
+ if (!resampler || resampler->dir == 0) { MIX_ZEROS(=); return; }
+ ASSERT(resampler->dir == -1 || resampler->dir == 1);
+
+ if (process_pickup(resampler)) { MIX_ZEROS(=); return; }
+
+ SET_VOLUME_VARIABLES;
+
+ if (VOLUMES_ARE_ZERO) { MIX_ZEROS(=); return; }
+
+ init_cubic();
+
+ quality = resampler->quality;
+
+ src = resampler->src;
+ pos = resampler->pos;
+ subpos = resampler->subpos;
+ x = resampler->X;
+
+ if (resampler->dir < 0) {
+ HEAVYASSERT(pos >= resampler->start);
+ if (dumb_resampling_quality <= DUMB_RQ_ALIASING) {
+ /* Aliasing, backwards */
+ MIX_ALIAS(=, 0, 1);
+ } else if (quality <= DUMB_RQ_LINEAR) {
+ /* Linear interpolation, backwards */
+ MIX_LINEAR(=, 0, 2, 1);
+ } else {
+ /* Cubic interpolation, backwards */
+ MIX_CUBIC(=, 0, src, x, pos, 2, 1, 0);
+ }
+ } else {
+ HEAVYASSERT(pos < resampler->end);
+ if (dumb_resampling_quality <= DUMB_RQ_ALIASING) {
+ /* Aliasing */
+ MIX_ALIAS(=, 0, 1);
+ } else if (dumb_resampling_quality <= DUMB_RQ_LINEAR) {
+ /* Linear interpolation, forwards */
+ MIX_LINEAR(=, 0, 1, 2);
+ } else {
+ /* Cubic interpolation, forwards */
+ MIX_CUBIC(=, 0, x, src, 0, 1, 2, pos);
+ }
+ }
+}
+
+
+
+#undef MIX_ZEROS
+#undef MIX_CUBIC
+#undef MIX_LINEAR
+#undef MIX_ALIAS
+#undef VOLUMES_ARE_ZERO
+#undef SET_VOLUME_VARIABLES
+#undef RETURN_VOLUME_VARIABLES
+#undef VOLUME_VARIABLES
+#undef VOLUME_PARAMETERS
+#undef SUFFIX3
diff --git a/plugins/dumb/dumb-kode54/src/helpers/resample.c b/plugins/dumb/dumb-kode54/src/helpers/resample.c
new file mode 100644
index 00000000..033538c5
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/helpers/resample.c
@@ -0,0 +1,393 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * resample.c - Resampling helpers. / / \ \
+ * | < / \_
+ * By Bob and entheh. | \/ /\ /
+ * \_ / > /
+ * In order to find a good trade-off between | \ / /
+ * speed and accuracy in this code, some tests | ' /
+ * were carried out regarding the behaviour of \__/
+ * long long ints with gcc. The following code
+ * was tested:
+ *
+ * int a, b, c;
+ * c = ((long long)a * b) >> 16;
+ *
+ * DJGPP GCC Version 3.0.3 generated the following assembly language code for
+ * the multiplication and scaling, leaving the 32-bit result in EAX.
+ *
+ * movl -8(%ebp), %eax ; read one int into EAX
+ * imull -4(%ebp) ; multiply by the other; result goes in EDX:EAX
+ * shrdl $16, %edx, %eax ; shift EAX right 16, shifting bits in from EDX
+ *
+ * Note that a 32*32->64 multiplication is performed, allowing for high
+ * accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally),
+ * so it is a minor concern when four multiplications are being performed
+ * (the cubic resampler). On the Pentium MMX and earlier, it takes four or
+ * more cycles, so this method is unsuitable for use in the low-quality
+ * resamplers.
+ *
+ * Since "long long" is a gcc-specific extension, we use LONG_LONG instead,
+ * defined in dumb.h. We may investigate later what code MSVC generates, but
+ * if it seems too slow then we suggest you use a good compiler.
+ *
+ * FIXME: these comments are somewhat out of date now.
+ */
+
+#include <math.h>
+#include "dumb.h"
+
+
+
+/* Compile with -DHEAVYDEBUG if you want to make sure the pick-up function is
+ * called when it should be. There will be a considerable performance hit,
+ * since at least one condition has to be tested for every sample generated.
+ */
+#ifdef HEAVYDEBUG
+#define HEAVYASSERT(cond) ASSERT(cond)
+#else
+#define HEAVYASSERT(cond)
+#endif
+
+
+
+/* Make MSVC shut the hell up about if ( upd ) UPDATE_VOLUME() conditions being constant */
+#ifdef _MSC_VER
+#pragma warning(disable:4127 4701)
+#endif
+
+
+
+/* A global variable for controlling resampling quality wherever a local
+ * specification doesn't override it. The following values are valid:
+ *
+ * 0 - DUMB_RQ_ALIASING - fastest
+ * 1 - DUMB_RQ_LINEAR
+ * 2 - DUMB_RQ_CUBIC - nicest
+ *
+ * Values outside the range 0-2 will behave the same as the nearest
+ * value within the range.
+ */
+int dumb_resampling_quality = DUMB_RQ_CUBIC;
+
+
+
+//#define MULSC(a, b) ((int)((LONG_LONG)(a) * (b) >> 16))
+//#define MULSC(a, b) ((a) * ((b) >> 2) >> 14)
+#define MULSCV(a, b) ((int)((LONG_LONG)(a) * (b) >> 32))
+#define MULSC(a, b) ((int)((LONG_LONG)((a) << 4) * ((b) << 12) >> 32))
+#define MULSC16(a, b) ((int)((LONG_LONG)((a) << 12) * ((b) << 12) >> 32))
+
+
+
+/* Executes the content 'iterator' times.
+ * Clobbers the 'iterator' variable.
+ * The loop is unrolled by four.
+ */
+#define LOOP4(iterator, CONTENT) \
+{ \
+ if ((iterator) & 2) { \
+ CONTENT; \
+ CONTENT; \
+ } \
+ if ((iterator) & 1) { \
+ CONTENT; \
+ } \
+ (iterator) >>= 2; \
+ while (iterator) { \
+ CONTENT; \
+ CONTENT; \
+ CONTENT; \
+ CONTENT; \
+ (iterator)--; \
+ } \
+}
+
+
+
+#define PASTERAW(a, b) a ## b /* This does not expand macros in b ... */
+#define PASTE(a, b) PASTERAW(a, b) /* ... but b is expanded during this substitution. */
+
+#define X PASTE(x.x, SRCBITS)
+
+
+
+/* Cubic resampler: look-up tables
+ *
+ * a = 1.5*x1 - 1.5*x2 + 0.5*x3 - 0.5*x0
+ * b = 2*x2 + x0 - 2.5*x1 - 0.5*x3
+ * c = 0.5*x2 - 0.5*x0
+ * d = x1
+ *
+ * x = a*t*t*t + b*t*t + c*t + d
+ * = (-0.5*x0 + 1.5*x1 - 1.5*x2 + 0.5*x3) * t*t*t +
+ * ( 1*x0 - 2.5*x1 + 2 *x2 - 0.5*x3) * t*t +
+ * (-0.5*x0 + 0.5*x2 ) * t +
+ * ( 1*x1 )
+ * = (-0.5*t*t*t + 1 *t*t - 0.5*t ) * x0 +
+ * ( 1.5*t*t*t - 2.5*t*t + 1) * x1 +
+ * (-1.5*t*t*t + 2 *t*t + 0.5*t ) * x2 +
+ * ( 0.5*t*t*t - 0.5*t*t ) * x3
+ * = A0(t) * x0 + A1(t) * x1 + A2(t) * x2 + A3(t) * x3
+ *
+ * A0, A1, A2 and A3 stay within the range [-1,1].
+ * In the tables, they are scaled with 14 fractional bits.
+ *
+ * Turns out we don't need to store A2 and A3; they are symmetrical to A1 and A0.
+ *
+ * TODO: A0 and A3 stay very small indeed. Consider different scale/resolution?
+ */
+
+static short cubicA0[1025], cubicA1[1025];
+
+/*static*/ void init_cubic(void)
+{
+ unsigned int t; /* 3*1024*1024*1024 is within range if it's unsigned */
+ static int done = 0;
+ if (done) return;
+ done = 1;
+ for (t = 0; t < 1025; t++) {
+ /* int casts to pacify warnings about negating unsigned values */
+ cubicA0[t] = -(int)( t*t*t >> 17) + (int)( t*t >> 6) - (int)(t << 3);
+ cubicA1[t] = (int)(3*t*t*t >> 17) - (int)(5*t*t >> 7) + (int)(1 << 14);
+ }
+}
+
+
+
+/* Create resamplers for 24-in-32-bit source samples. */
+
+/* #define SUFFIX
+ * MSVC warns if we try to paste a null SUFFIX, so instead we define
+ * special macros for the function names that don't bother doing the
+ * corresponding paste. The more generic definitions are further down.
+ */
+#define process_pickup PASTE(process_pickup, SUFFIX2)
+#define dumb_resample PASTE(PASTE(dumb_resample, SUFFIX2), SUFFIX3)
+#define dumb_resample_get_current_sample PASTE(PASTE(dumb_resample_get_current_sample, SUFFIX2), SUFFIX3)
+
+#define SRCTYPE sample_t
+#define SRCBITS 24
+#define ALIAS(x, vol) MULSC(x, vol)
+#define LINEAR(x0, x1) (x0 + MULSC(x1 - x0, subpos))
+/*
+#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \
+ a = (3 * (x1 - x2) + (x3 - x0)) >> 1; \
+ b = ((x2 << 2) + (x0 << 1) - (5 * x1 + x3)) >> 1; \
+ c = (x2 - x0) >> 1; \
+}
+#define CUBIC(d) MULSC(MULSC(MULSC(MULSC(a, subpos) + b, subpos) + c, subpos) + d, vol)
+*/
+#define CUBIC(x0, x1, x2, x3) ( \
+ MULSC(x0, cubicA0[subpos >> 6] << 2) + \
+ MULSC(x1, cubicA1[subpos >> 6] << 2) + \
+ MULSC(x2, cubicA1[1 + (subpos >> 6 ^ 1023)] << 2) + \
+ MULSC(x3, cubicA0[1 + (subpos >> 6 ^ 1023)] << 2))
+#define CUBICVOL(x, vol) MULSC(x, vol)
+#include "resample.inc"
+
+/* Undefine the simplified macros. */
+#undef dumb_resample_get_current_sample
+#undef dumb_resample
+#undef process_pickup
+
+
+/* Now define the proper ones that use SUFFIX. */
+#define dumb_reset_resampler PASTE(dumb_reset_resampler, SUFFIX)
+#define dumb_start_resampler PASTE(dumb_start_resampler, SUFFIX)
+#define process_pickup PASTE(PASTE(process_pickup, SUFFIX), SUFFIX2)
+#define dumb_resample PASTE(PASTE(PASTE(dumb_resample, SUFFIX), SUFFIX2), SUFFIX3)
+#define dumb_resample_get_current_sample PASTE(PASTE(PASTE(dumb_resample_get_current_sample, SUFFIX), SUFFIX2), SUFFIX3)
+#define dumb_end_resampler PASTE(dumb_end_resampler, SUFFIX)
+
+/* Create resamplers for 16-bit source samples. */
+#define SUFFIX _16
+#define SRCTYPE short
+#define SRCBITS 16
+#define ALIAS(x, vol) (x * vol >> 8)
+#define LINEAR(x0, x1) ((x0 << 8) + MULSC16(x1 - x0, subpos))
+/*
+#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \
+ a = (3 * (x1 - x2) + (x3 - x0)) << 7; \
+ b = ((x2 << 2) + (x0 << 1) - (5 * x1 + x3)) << 7; \
+ c = (x2 - x0) << 7; \
+}
+#define CUBIC(d) MULSC(MULSC(MULSC(MULSC(a, subpos) + b, subpos) + c, subpos) + (d << 8), vol)
+*/
+#define CUBIC(x0, x1, x2, x3) ( \
+ x0 * cubicA0[subpos >> 6] + \
+ x1 * cubicA1[subpos >> 6] + \
+ x2 * cubicA1[1 + (subpos >> 6 ^ 1023)] + \
+ x3 * cubicA0[1 + (subpos >> 6 ^ 1023)])
+#define CUBICVOL(x, vol) (int)((LONG_LONG)(x) * (vol << 10) >> 32)
+#include "resample.inc"
+
+/* Create resamplers for 8-bit source samples. */
+#define SUFFIX _8
+#define SRCTYPE signed char
+#define SRCBITS 8
+#define ALIAS(x, vol) (x * vol)
+#define LINEAR(x0, x1) ((x0 << 16) + (x1 - x0) * subpos)
+/*
+#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \
+ a = 3 * (x1 - x2) + (x3 - x0); \
+ b = ((x2 << 2) + (x0 << 1) - (5 * x1 + x3)) << 15; \
+ c = (x2 - x0) << 15; \
+}
+#define CUBIC(d) MULSC(MULSC(MULSC((a * subpos >> 1) + b, subpos) + c, subpos) + (d << 16), vol)
+*/
+#define CUBIC(x0, x1, x2, x3) (( \
+ x0 * cubicA0[subpos >> 6] + \
+ x1 * cubicA1[subpos >> 6] + \
+ x2 * cubicA1[1 + (subpos >> 6 ^ 1023)] + \
+ x3 * cubicA0[1 + (subpos >> 6 ^ 1023)]) << 6)
+#define CUBICVOL(x, vol) (int)((LONG_LONG)(x) * (vol << 12) >> 32)
+#include "resample.inc"
+
+
+#undef dumb_reset_resampler
+#undef dumb_start_resampler
+#undef process_pickup
+#undef dumb_resample
+#undef dumb_resample_get_current_sample
+#undef dumb_end_resampler
+
+
+
+void dumb_reset_resampler_n(int n, DUMB_RESAMPLER *resampler, void *src, int src_channels, long pos, long start, long end, int quality)
+{
+ if (n == 8)
+ dumb_reset_resampler_8(resampler, src, src_channels, pos, start, end, quality);
+ else if (n == 16)
+ dumb_reset_resampler_16(resampler, src, src_channels, pos, start, end, quality);
+ else
+ dumb_reset_resampler(resampler, src, src_channels, pos, start, end, quality);
+}
+
+
+
+DUMB_RESAMPLER *dumb_start_resampler_n(int n, void *src, int src_channels, long pos, long start, long end, int quality)
+{
+ if (n == 8)
+ return dumb_start_resampler_8(src, src_channels, pos, start, end, quality);
+ else if (n == 16)
+ return dumb_start_resampler_16(src, src_channels, pos, start, end, quality);
+ else
+ return dumb_start_resampler(src, src_channels, pos, start, end, quality);
+}
+
+
+
+long dumb_resample_n_1_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume, float delta)
+{
+ if (n == 8)
+ return dumb_resample_8_1_1(resampler, dst, dst_size, volume, delta);
+ else if (n == 16)
+ return dumb_resample_16_1_1(resampler, dst, dst_size, volume, delta);
+ else
+ return dumb_resample_1_1(resampler, dst, dst_size, volume, delta);
+}
+
+
+
+long dumb_resample_n_1_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta)
+{
+ if (n == 8)
+ return dumb_resample_8_1_2(resampler, dst, dst_size, volume_left, volume_right, delta);
+ else if (n == 16)
+ return dumb_resample_16_1_2(resampler, dst, dst_size, volume_left, volume_right, delta);
+ else
+ return dumb_resample_1_2(resampler, dst, dst_size, volume_left, volume_right, delta);
+}
+
+
+
+long dumb_resample_n_2_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta)
+{
+ if (n == 8)
+ return dumb_resample_8_2_1(resampler, dst, dst_size, volume_left, volume_right, delta);
+ else if (n == 16)
+ return dumb_resample_16_2_1(resampler, dst, dst_size, volume_left, volume_right, delta);
+ else
+ return dumb_resample_2_1(resampler, dst, dst_size, volume_left, volume_right, delta);
+}
+
+
+
+long dumb_resample_n_2_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta)
+{
+ if (n == 8)
+ return dumb_resample_8_2_2(resampler, dst, dst_size, volume_left, volume_right, delta);
+ else if (n == 16)
+ return dumb_resample_16_2_2(resampler, dst, dst_size, volume_left, volume_right, delta);
+ else
+ return dumb_resample_2_2(resampler, dst, dst_size, volume_left, volume_right, delta);
+}
+
+
+
+void dumb_resample_get_current_sample_n_1_1(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume, sample_t *dst)
+{
+ if (n == 8)
+ dumb_resample_get_current_sample_8_1_1(resampler, volume, dst);
+ else if (n == 16)
+ dumb_resample_get_current_sample_16_1_1(resampler, volume, dst);
+ else
+ dumb_resample_get_current_sample_1_1(resampler, volume, dst);
+}
+
+
+
+void dumb_resample_get_current_sample_n_1_2(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst)
+{
+ if (n == 8)
+ dumb_resample_get_current_sample_8_1_2(resampler, volume_left, volume_right, dst);
+ else if (n == 16)
+ dumb_resample_get_current_sample_16_1_2(resampler, volume_left, volume_right, dst);
+ else
+ dumb_resample_get_current_sample_1_2(resampler, volume_left, volume_right, dst);
+}
+
+
+
+void dumb_resample_get_current_sample_n_2_1(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst)
+{
+ if (n == 8)
+ dumb_resample_get_current_sample_8_2_1(resampler, volume_left, volume_right, dst);
+ else if (n == 16)
+ dumb_resample_get_current_sample_16_2_1(resampler, volume_left, volume_right, dst);
+ else
+ dumb_resample_get_current_sample_2_1(resampler, volume_left, volume_right, dst);
+}
+
+
+
+void dumb_resample_get_current_sample_n_2_2(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst)
+{
+ if (n == 8)
+ dumb_resample_get_current_sample_8_2_2(resampler, volume_left, volume_right, dst);
+ else if (n == 16)
+ dumb_resample_get_current_sample_16_2_2(resampler, volume_left, volume_right, dst);
+ else
+ dumb_resample_get_current_sample_2_2(resampler, volume_left, volume_right, dst);
+}
+
+
+
+void dumb_end_resampler_n(int n, DUMB_RESAMPLER *resampler)
+{
+ if (n == 8)
+ dumb_end_resampler_8(resampler);
+ else if (n == 16)
+ dumb_end_resampler_16(resampler);
+ else
+ dumb_end_resampler(resampler);
+}
diff --git a/plugins/dumb/dumb-kode54/src/helpers/resample.inc b/plugins/dumb/dumb-kode54/src/helpers/resample.inc
new file mode 100644
index 00000000..d3253cd5
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/helpers/resample.inc
@@ -0,0 +1,264 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * resample.inc - Resampling helper template. / / \ \
+ * | < / \_
+ * By Bob and entheh. | \/ /\ /
+ * \_ / > /
+ * In order to find a good trade-off between | \ / /
+ * speed and accuracy in this code, some tests | ' /
+ * were carried out regarding the behaviour of \__/
+ * long long ints with gcc. The following code
+ * was tested:
+ *
+ * int a, b, c;
+ * c = ((long long)a * b) >> 16;
+ *
+ * DJGPP GCC Version 3.0.3 generated the following assembly language code for
+ * the multiplication and scaling, leaving the 32-bit result in EAX.
+ *
+ * movl -8(%ebp), %eax ; read one int into EAX
+ * imull -4(%ebp) ; multiply by the other; result goes in EDX:EAX
+ * shrdl $16, %edx, %eax ; shift EAX right 16, shifting bits in from EDX
+ *
+ * Note that a 32*32->64 multiplication is performed, allowing for high
+ * accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally),
+ * so it is a minor concern when four multiplications are being performed
+ * (the cubic resampler). On the Pentium MMX and earlier, it takes four or
+ * more cycles, so this method is unsuitable for use in the low-quality
+ * resamplers.
+ *
+ * Since "long long" is a gcc-specific extension, we use LONG_LONG instead,
+ * defined in dumb.h. We may investigate later what code MSVC generates, but
+ * if it seems too slow then we suggest you use a good compiler.
+ *
+ * FIXME: these comments are somewhat out of date now.
+ */
+
+
+
+void dumb_reset_resampler(DUMB_RESAMPLER *resampler, SRCTYPE *src, int src_channels, long pos, long start, long end, int quality)
+{
+ int i;
+ resampler->src = src;
+ resampler->pos = pos;
+ resampler->subpos = 0;
+ resampler->start = start;
+ resampler->end = end;
+ resampler->dir = 1;
+ resampler->pickup = NULL;
+ resampler->pickup_data = NULL;
+ if (quality < 0)
+ {
+ resampler->quality = 0;
+ }
+ else if (quality > DUMB_RQ_N_LEVELS - 1)
+ {
+ resampler->quality = DUMB_RQ_N_LEVELS - 1;
+ }
+ else
+ {
+ resampler->quality = quality;
+ }
+ for (i = 0; i < src_channels*3; i++) resampler->X[i] = 0;
+ resampler->overshot = -1;
+}
+
+
+
+DUMB_RESAMPLER *dumb_start_resampler(SRCTYPE *src, int src_channels, long pos, long start, long end, int quality)
+{
+ DUMB_RESAMPLER *resampler = malloc(sizeof(*resampler));
+ if (!resampler) return NULL;
+ dumb_reset_resampler(resampler, src, src_channels, pos, start, end, quality);
+ return resampler;
+}
+
+
+
+#define UPDATE_VOLUME( pvol, vol ) { \
+ if (pvol) { \
+ vol##r += vol##d; \
+ if ((vol##d < 0 && vol##r <= vol##t) || \
+ (vol##d > 0 && vol##r >= vol##t)) { \
+ pvol->volume = pvol->target; \
+ pvol = NULL; \
+ vol = MULSCV( vol##t, vol##m ); \
+ } else { \
+ vol = MULSCV( vol##r, vol##m ); \
+ } \
+ } \
+}
+
+
+
+/* Create mono source resampler. */
+#define SUFFIX2 _1
+#define SRC_CHANNELS 1
+#define DIVIDE_BY_SRC_CHANNELS(x) (x)
+#define COPYSRC(dstarray, dstindex, srcarray, srcindex) (dstarray)[dstindex] = (srcarray)[srcindex]
+#define COPYSRC2(dstarray, dstindex, condition, srcarray, srcindex) (dstarray)[dstindex] = condition ? (srcarray)[srcindex] : 0
+#define MONO_DEST_VOLUME_PARAMETERS DUMB_VOLUME_RAMP_INFO * volume
+#define MONO_DEST_VOLUME_VARIABLES vol=0, volr=0, vold=0, volt=0, volm=0
+#define MONO_DEST_VOLUME_ZEROS 0
+#define SET_MONO_DEST_VOLUME_VARIABLES { \
+ if ( volume ) { \
+ volr = (int)(volume->volume * 16777216.0); \
+ vold = (int)(volume->delta * 16777216.0); \
+ volt = (int)(volume->target * 16777216.0); \
+ volm = (int)(volume->mix * 16777216.0); \
+ vol = MULSCV( volr, volm ); \
+ if ( volr == volt ) volume = NULL; \
+ } else { \
+ vol = 0; \
+ volt = 0; \
+ } \
+}
+#define RETURN_MONO_DEST_VOLUME_VARIABLES if ( volume ) volume->volume = (float)volr / 16777216.0f
+#define MONO_DEST_VOLUMES_ARE_ZERO (vol == 0 && volt == 0)
+#define MONO_DEST_MIX_ALIAS(op, upd, offset) { \
+ *dst++ op ALIAS(x[offset], vol); \
+ if ( upd ) UPDATE_VOLUME( volume, vol ); \
+}
+#define STEREO_DEST_MIX_ALIAS(op, upd, offset) { \
+ int xm = x[offset]; \
+ *dst++ op ALIAS(xm, lvol); \
+ *dst++ op ALIAS(xm, rvol); \
+ if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \
+ if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \
+}
+#define MONO_DEST_MIX_LINEAR(op, upd, o0, o1) { \
+ *dst++ op MULSC(LINEAR(x[o0], x[o1]), vol); \
+ if ( upd ) UPDATE_VOLUME( volume, vol ); \
+}
+#define STEREO_DEST_MIX_LINEAR(op, upd, o0, o1) { \
+ int xm = LINEAR(x[o0], x[o1]); \
+ *dst++ op MULSC(xm, lvol); \
+ *dst++ op MULSC(xm, rvol); \
+ if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \
+ if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \
+}
+#define MONO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) { \
+ *dst++ op CUBICVOL(CUBIC(x0[o0], x[o1], x[o2], x3[o3]), vol); \
+ if ( upd ) UPDATE_VOLUME( volume, vol ); \
+}
+#define STEREO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) { \
+ int xm = CUBIC(x0[o0], x[o1], x[o2], x3[o3]); \
+ *dst++ op CUBICVOL(xm, lvol); \
+ *dst++ op CUBICVOL(xm, rvol); \
+ if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \
+ if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \
+}
+#include "resamp2.inc"
+
+/* Create stereo source resampler. */
+#define SUFFIX2 _2
+#define SRC_CHANNELS 2
+#define DIVIDE_BY_SRC_CHANNELS(x) ((x) >> 1)
+#define COPYSRC(dstarray, dstindex, srcarray, srcindex) { \
+ (dstarray)[(dstindex)*2] = (srcarray)[(srcindex)*2]; \
+ (dstarray)[(dstindex)*2+1] = (srcarray)[(srcindex)*2+1]; \
+}
+#define COPYSRC2(dstarray, dstindex, condition, srcarray, srcindex) { \
+ if (condition) { \
+ (dstarray)[(dstindex)*2] = (srcarray)[(srcindex)*2]; \
+ (dstarray)[(dstindex)*2+1] = (srcarray)[(srcindex)*2+1]; \
+ } else { \
+ (dstarray)[(dstindex)*2] = 0; \
+ (dstarray)[(dstindex)*2+1] = 0; \
+ } \
+}
+
+#define MONO_DEST_VOLUME_PARAMETERS DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right
+#define MONO_DEST_VOLUME_VARIABLES lvol=0, lvolr=0, lvold=0, lvolt=0, lvolm=0, rvol=0, rvolr=0, rvold=0, rvolt=0, rvolm=0
+#define MONO_DEST_VOLUME_ZEROS 0, 0
+#define SET_MONO_DEST_VOLUME_VARIABLES { \
+ if ( volume_left ) { \
+ lvolr = (int)(volume_left->volume * 16777216.0); \
+ lvold = (int)(volume_left->delta * 16777216.0); \
+ lvolt = (int)(volume_left->target * 16777216.0); \
+ lvolm = (int)(volume_left->mix * 16777216.0); \
+ lvol = MULSCV( lvolr, lvolm ); \
+ if ( lvolr == lvolt ) volume_left = NULL; \
+ } else { \
+ lvol = 0; \
+ lvolt = 0; \
+ } \
+ if ( volume_right ) { \
+ rvolr = (int)(volume_right->volume * 16777216.0); \
+ rvold = (int)(volume_right->delta * 16777216.0); \
+ rvolt = (int)(volume_right->target * 16777216.0); \
+ rvolm = (int)(volume_right->mix * 16777216.0); \
+ rvol = MULSCV( rvolr, rvolm ); \
+ if ( rvolr == rvolt ) volume_right = NULL; \
+ } else { \
+ rvol = 0; \
+ rvolt = 0; \
+ } \
+}
+#define RETURN_MONO_DEST_VOLUME_VARIABLES { \
+ if ( volume_left ) volume_left->volume = (float)lvolr / 16777216.0f; \
+ if ( volume_right ) volume_right->volume = (float)rvolr / 16777216.0f; \
+}
+#define MONO_DEST_VOLUMES_ARE_ZERO (lvol == 0 && lvolt == 0 && rvol == 0 && rvolt == 0)
+#define MONO_DEST_MIX_ALIAS(op, upd, offset) { \
+ *dst++ op ALIAS(x[(offset)*2], lvol) + ALIAS(x[(offset)*2+1], rvol); \
+ if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \
+ if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \
+}
+#define STEREO_DEST_MIX_ALIAS(op, upd, offset) { \
+ *dst++ op ALIAS(x[(offset)*2], lvol); \
+ *dst++ op ALIAS(x[(offset)*2+1], rvol); \
+ if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \
+ if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \
+}
+#define MONO_DEST_MIX_LINEAR(op, upd, o0, o1) { \
+ *dst++ op MULSC(LINEAR(x[(o0)*2], x[(o1)*2]), lvol) + MULSC(LINEAR(x[(o0)*2+1], x[(o1)*2+1]), rvol); \
+ if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \
+ if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \
+}
+#define STEREO_DEST_MIX_LINEAR(op, upd, o0, o1) { \
+ *dst++ op MULSC(LINEAR(x[(o0)*2], x[(o1)*2]), lvol); \
+ *dst++ op MULSC(LINEAR(x[(o0)*2+1], x[(o1)*2+1]), rvol); \
+ if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \
+ if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \
+}
+#define MONO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) { \
+ *dst++ op \
+ CUBICVOL(CUBIC(x0[(o0)*2], x[(o1)*2], x[(o2)*2], x3[(o3)*2]), lvol) + \
+ CUBICVOL(CUBIC(x0[(o0)*2+1], x[(o1)*2+1], x[(o2)*2+1], x3[(o3)*2+1]), rvol); \
+ if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \
+ if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \
+}
+#define STEREO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) { \
+ *dst++ op CUBICVOL(CUBIC(x0[(o0)*2], x[(o1)*2], x[(o2)*2], x3[(o3)*2]), lvol); \
+ *dst++ op CUBICVOL(CUBIC(x0[(o0)*2+1], x[(o1)*2+1], x[(o2)*2+1], x3[(o3)*2+1]), rvol); \
+ if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \
+ if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \
+}
+#include "resamp2.inc"
+
+
+
+void dumb_end_resampler(DUMB_RESAMPLER *resampler)
+{
+ if (resampler)
+ free(resampler);
+}
+
+
+
+#undef CUBICVOL
+#undef CUBIC
+#undef LINEAR
+#undef ALIAS
+#undef SRCBITS
+#undef SRCTYPE
+#undef SUFFIX
diff --git a/plugins/dumb/dumb-kode54/src/helpers/riff.c b/plugins/dumb/dumb-kode54/src/helpers/riff.c
new file mode 100644
index 00000000..62a7eccc
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/helpers/riff.c
@@ -0,0 +1,88 @@
+#include "internal/riff.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+struct riff * riff_parse( unsigned char * ptr, unsigned size, unsigned proper )
+{
+ unsigned stream_size;
+ struct riff * stream;
+
+ if ( size < 8 ) return 0;
+
+ if ( ptr[0] != 'R' || ptr[1] != 'I' || ptr[2] != 'F' || ptr[3] != 'F' ) return 0;
+
+ stream_size = ptr[4] | ( ptr[5] << 8 ) | ( ptr[6] << 16 ) | ( ptr[7] << 24 );
+ if ( stream_size + 8 > size ) return 0;
+ if ( stream_size < 4 ) return 0;
+
+ stream = malloc( sizeof( struct riff ) );
+ if ( ! stream ) return 0;
+
+ stream->type = ( ptr[8] << 24 ) | ( ptr[9] << 16 ) | ( ptr[10] << 8 ) | ptr[11];
+ stream->chunk_count = 0;
+ stream->chunks = 0;
+
+ ptr += 12;
+ stream_size -= 4;
+
+ while ( stream_size )
+ {
+ struct riff_chunk * chunk;
+ if ( stream_size < 8 ) break;
+ stream->chunks = realloc( stream->chunks, ( stream->chunk_count + 1 ) * sizeof( struct riff_chunk ) );
+ if ( ! stream->chunks ) break;
+ chunk = stream->chunks + stream->chunk_count;
+ chunk->type = ( ptr[0] << 24 ) | ( ptr[1] << 16 ) | ( ptr[2] << 8 ) | ptr[3];
+ chunk->size = ptr[4] | ( ptr[5] << 8 ) | ( ptr[6] << 16 ) | ( ptr[7] << 24 );
+ ptr += 8;
+ stream_size -= 8;
+ if ( stream_size < chunk->size ) break;
+ if ( chunk->type == 'RIFF' )
+ {
+ chunk->data = riff_parse( ptr - 8, chunk->size + 8, proper );
+ if ( ! chunk->data ) break;
+ }
+ else
+ {
+ chunk->data = malloc( chunk->size );
+ if ( ! chunk->data ) break;
+ memcpy( chunk->data, ptr, chunk->size );
+ }
+ ptr += chunk->size;
+ stream_size -= chunk->size;
+ if ( proper && ( chunk->size & 1 ) )
+ {
+ ++ ptr;
+ -- stream_size;
+ }
+ ++stream->chunk_count;
+ }
+
+ if ( stream_size )
+ {
+ riff_free( stream );
+ stream = 0;
+ }
+
+ return stream;
+}
+
+void riff_free( struct riff * stream )
+{
+ if ( stream )
+ {
+ if ( stream->chunks )
+ {
+ unsigned i;
+ for ( i = 0; i < stream->chunk_count; ++i )
+ {
+ struct riff_chunk * chunk = stream->chunks + i;
+ if ( chunk->type == 'RIFF' ) riff_free( ( struct riff * ) chunk->data );
+ else free( chunk->data );
+ }
+ free( stream->chunks );
+ }
+ free( stream );
+ }
+}
diff --git a/plugins/dumb/dumb-kode54/src/helpers/sampbuf.c b/plugins/dumb/dumb-kode54/src/helpers/sampbuf.c
new file mode 100644
index 00000000..488db449
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/helpers/sampbuf.c
@@ -0,0 +1,64 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * sampbuf.c - Helper for allocating sample / / \ \
+ * buffers. | < / \_
+ * | \/ /\ /
+ * By entheh. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+#include "dumb.h"
+
+
+
+/* DEPRECATED */
+sample_t **create_sample_buffer(int n_channels, long length)
+{
+ int i;
+ sample_t **samples = malloc(n_channels * sizeof(*samples));
+ if (!samples) return NULL;
+ samples[0] = malloc(n_channels * length * sizeof(*samples[0]));
+ if (!samples[0]) {
+ free(samples);
+ return NULL;
+ }
+ for (i = 1; i < n_channels; i++) samples[i] = samples[i-1] + length;
+ return samples;
+}
+
+
+
+sample_t **allocate_sample_buffer(int n_channels, long length)
+{
+ int i;
+ sample_t **samples = malloc(((n_channels + 1) >> 1) * sizeof(*samples));
+ if (!samples) return NULL;
+ samples[0] = malloc(n_channels * length * sizeof(*samples[0]));
+ if (!samples[0]) {
+ free(samples);
+ return NULL;
+ }
+ for (i = 1; i < (n_channels + 1) >> 1; i++) samples[i] = samples[i-1] + length*2;
+ return samples;
+}
+
+
+
+void destroy_sample_buffer(sample_t **samples)
+{
+ if (samples) {
+ free(samples[0]);
+ free(samples);
+ }
+}
diff --git a/plugins/dumb/dumb-kode54/src/helpers/silence.c b/plugins/dumb/dumb-kode54/src/helpers/silence.c
new file mode 100644
index 00000000..794ae831
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/helpers/silence.c
@@ -0,0 +1,29 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * silence.c - Silencing helper. / / \ \
+ * | < / \_
+ * By entheh. | \/ /\ /
+ * \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <string.h>
+#include "dumb.h"
+
+
+
+void dumb_silence(sample_t *samples, long length)
+{
+ memset(samples, 0, length * sizeof(*samples));
+}
+
diff --git a/plugins/dumb/dumb-kode54/src/helpers/stdfile.c b/plugins/dumb/dumb-kode54/src/helpers/stdfile.c
new file mode 100644
index 00000000..aa398f50
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/helpers/stdfile.c
@@ -0,0 +1,93 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * stdfile.c - stdio file input module. / / \ \
+ * | < / \_
+ * By entheh. | \/ /\ /
+ * \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdio.h>
+
+#include "dumb.h"
+
+
+
+static void *dumb_stdfile_open(const char *filename)
+{
+ return fopen(filename, "rb");
+}
+
+
+
+static int dumb_stdfile_skip(void *f, long n)
+{
+ return fseek(f, n, SEEK_CUR);
+}
+
+
+
+static int dumb_stdfile_getc(void *f)
+{
+ return fgetc(f);
+}
+
+
+
+static long dumb_stdfile_getnc(char *ptr, long n, void *f)
+{
+ return fread(ptr, 1, n, f);
+}
+
+
+
+static void dumb_stdfile_close(void *f)
+{
+ fclose(f);
+}
+
+
+
+static DUMBFILE_SYSTEM stdfile_dfs = {
+ &dumb_stdfile_open,
+ &dumb_stdfile_skip,
+ &dumb_stdfile_getc,
+ &dumb_stdfile_getnc,
+ &dumb_stdfile_close
+};
+
+
+
+void dumb_register_stdfiles(void)
+{
+ register_dumbfile_system(&stdfile_dfs);
+}
+
+
+
+static DUMBFILE_SYSTEM stdfile_dfs_leave_open = {
+ NULL,
+ &dumb_stdfile_skip,
+ &dumb_stdfile_getc,
+ &dumb_stdfile_getnc,
+ NULL
+};
+
+
+
+DUMBFILE *dumbfile_open_stdfile(FILE *p)
+{
+ DUMBFILE *d = dumbfile_open_ex(p, &stdfile_dfs_leave_open);
+
+ return d;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/itload.c b/plugins/dumb/dumb-kode54/src/it/itload.c
new file mode 100644
index 00000000..30004233
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/itload.c
@@ -0,0 +1,43 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * itload.c - Code to read an Impulse Tracker / / \ \
+ * file, opening and closing it for | < / \_
+ * you. | \/ /\ /
+ * \_ / > /
+ * By entheh. Don't worry Bob, you're credited | \ / /
+ * in itread.c! | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_it_quick(): loads an IT file into a DUH struct, returning a
+ * pointer to the DUH struct. When you have finished with it, you must pass
+ * the pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_it_quick(const char *filename)
+{
+ DUH *duh;
+ DUMBFILE *f = dumbfile_open(filename);
+
+ if (!f)
+ return NULL;
+
+ duh = dumb_read_it_quick(f);
+
+ dumbfile_close(f);
+
+ return duh;
+}
+
diff --git a/plugins/dumb/dumb-kode54/src/it/itload2.c b/plugins/dumb/dumb-kode54/src/it/itload2.c
new file mode 100644
index 00000000..15cff1d0
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/itload2.c
@@ -0,0 +1,29 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * itload2.c - Function to read an Impulse Tracker / / \ \
+ * file, opening and closing it for | < / \_
+ * you, and do an initial run-through. | \/ /\ /
+ * \_ / > /
+ * Split off from itload.c by entheh. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+
+
+
+DUH *dumb_load_it(const char *filename)
+{
+ DUH *duh = dumb_load_it_quick(filename);
+ dumb_it_do_initial_runthrough(duh);
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/itmisc.c b/plugins/dumb/dumb-kode54/src/it/itmisc.c
new file mode 100644
index 00000000..22e18b78
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/itmisc.c
@@ -0,0 +1,247 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * itmisc.c - Miscellaneous functions relating / / \ \
+ * to module files. | < / \_
+ * | \/ /\ /
+ * By entheh. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+DUMB_IT_SIGDATA *duh_get_it_sigdata(DUH *duh)
+{
+ return duh_get_raw_sigdata(duh, 0, SIGTYPE_IT);
+}
+
+
+
+const unsigned char *dumb_it_sd_get_song_message(DUMB_IT_SIGDATA *sd)
+{
+ return sd ? sd->song_message : NULL;
+}
+
+
+
+int dumb_it_sd_get_n_orders(DUMB_IT_SIGDATA *sd)
+{
+ return sd ? sd->n_orders : 0;
+}
+
+
+
+int dumb_it_sd_get_n_samples(DUMB_IT_SIGDATA *sd)
+{
+ return sd ? sd->n_samples : 0;
+}
+
+
+
+int dumb_it_sd_get_n_instruments(DUMB_IT_SIGDATA *sd)
+{
+ return sd ? sd->n_instruments : 0;
+}
+
+
+
+const unsigned char *dumb_it_sd_get_sample_name(DUMB_IT_SIGDATA *sd, int i)
+{
+ ASSERT(sd && sd->sample && i >= 0 && i < sd->n_samples);
+ return sd->sample[i].name;
+}
+
+
+
+const unsigned char *dumb_it_sd_get_sample_filename(DUMB_IT_SIGDATA *sd, int i)
+{
+ ASSERT(sd && sd->sample && i >= 0 && i < sd->n_samples);
+ return sd->sample[i].filename;
+}
+
+
+
+const unsigned char *dumb_it_sd_get_instrument_name(DUMB_IT_SIGDATA *sd, int i)
+{
+ ASSERT(sd && sd->instrument && i >= 0 && i < sd->n_instruments);
+ return sd->instrument[i].name;
+}
+
+
+
+const unsigned char *dumb_it_sd_get_instrument_filename(DUMB_IT_SIGDATA *sd, int i)
+{
+ ASSERT(sd && sd->instrument && i >= 0 && i < sd->n_instruments);
+ return sd->instrument[i].filename;
+}
+
+
+
+int dumb_it_sd_get_initial_global_volume(DUMB_IT_SIGDATA *sd)
+{
+ return sd ? sd->global_volume : 0;
+}
+
+
+
+void dumb_it_sd_set_initial_global_volume(DUMB_IT_SIGDATA *sd, int gv)
+{
+ if (sd) sd->global_volume = gv;
+}
+
+
+
+int dumb_it_sd_get_mixing_volume(DUMB_IT_SIGDATA *sd)
+{
+ return sd ? sd->mixing_volume : 0;
+}
+
+
+
+void dumb_it_sd_set_mixing_volume(DUMB_IT_SIGDATA *sd, int mv)
+{
+ if (sd) sd->mixing_volume = mv;
+}
+
+
+
+int dumb_it_sd_get_initial_speed(DUMB_IT_SIGDATA *sd)
+{
+ return sd ? sd->speed : 0;
+}
+
+
+
+void dumb_it_sd_set_initial_speed(DUMB_IT_SIGDATA *sd, int speed)
+{
+ if (sd) sd->speed = speed;
+}
+
+
+
+int dumb_it_sd_get_initial_tempo(DUMB_IT_SIGDATA *sd)
+{
+ return sd ? sd->tempo : 0;
+}
+
+
+
+void dumb_it_sd_set_initial_tempo(DUMB_IT_SIGDATA *sd, int tempo)
+{
+ if (sd) sd->tempo = tempo;
+}
+
+
+
+int dumb_it_sd_get_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel)
+{
+ ASSERT(channel >= 0 && channel < DUMB_IT_N_CHANNELS);
+ return sd ? sd->channel_volume[channel] : 0;
+}
+
+void dumb_it_sd_set_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel, int volume)
+{
+ ASSERT(channel >= 0 && channel < DUMB_IT_N_CHANNELS);
+ if (sd) sd->channel_volume[channel] = volume;
+}
+
+
+
+int dumb_it_sr_get_current_order(DUMB_IT_SIGRENDERER *sr)
+{
+ return sr ? sr->order : -1;
+}
+
+
+
+int dumb_it_sr_get_current_row(DUMB_IT_SIGRENDERER *sr)
+{
+ return sr ? sr->row : -1;
+}
+
+
+
+int dumb_it_sr_get_global_volume(DUMB_IT_SIGRENDERER *sr)
+{
+ return sr ? sr->globalvolume : 0;
+}
+
+
+
+void dumb_it_sr_set_global_volume(DUMB_IT_SIGRENDERER *sr, int gv)
+{
+ if (sr) sr->globalvolume = gv;
+}
+
+
+
+int dumb_it_sr_get_tempo(DUMB_IT_SIGRENDERER *sr)
+{
+ return sr ? sr->tempo : 0;
+}
+
+
+
+void dumb_it_sr_set_tempo(DUMB_IT_SIGRENDERER *sr, int tempo)
+{
+ if (sr) sr->tempo = tempo;
+}
+
+
+
+int dumb_it_sr_get_speed(DUMB_IT_SIGRENDERER *sr)
+{
+ return sr ? sr->speed : 0;
+}
+
+
+
+void dumb_it_sr_set_speed(DUMB_IT_SIGRENDERER *sr, int speed)
+{
+ if (sr) sr->speed = speed;
+}
+
+
+
+int dumb_it_sr_get_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel)
+{
+ return sr ? sr->channel[channel].channelvolume : 0;
+}
+
+
+
+void dumb_it_sr_set_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel, int volume)
+{
+ if (sr) sr->channel[channel].channelvolume = volume;
+}
+
+
+
+void dumb_it_sr_set_channel_muted(DUMB_IT_SIGRENDERER *sr, int channel, int muted)
+{
+ if (sr) {
+ if (muted)
+ sr->channel[channel].flags |= IT_CHANNEL_MUTED;
+ else
+ sr->channel[channel].flags &= ~IT_CHANNEL_MUTED;
+ }
+}
+
+
+
+int dumb_it_sr_get_channel_muted(DUMB_IT_SIGRENDERER *sr, int channel)
+{
+ return sr ? (sr->channel[channel].flags & IT_CHANNEL_MUTED) != 0 : 0;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/itorder.c b/plugins/dumb/dumb-kode54/src/it/itorder.c
new file mode 100644
index 00000000..c3fe51cb
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/itorder.c
@@ -0,0 +1,63 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * itorder.c - Code to fix invalid patterns in / / \ \
+ * the pattern table. | < / \_
+ * | \/ /\ /
+ * By Julien Cugniere. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+
+
+#include <stdlib.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* This function ensures that any pattern mentioned in the order table but
+ * not present in the pattern table is treated as an empty 64 rows pattern.
+ * This is done by adding such a dummy pattern at the end of the pattern
+ * table, and redirect invalid orders to it.
+ * Patterns 254 and 255 are left untouched, unless the signal is an XM.
+ */
+int _dumb_it_fix_invalid_orders(DUMB_IT_SIGDATA *sigdata)
+{
+ int i;
+ int found_some = 0;
+
+ int first_invalid = sigdata->n_patterns;
+ int last_invalid = (sigdata->flags & IT_WAS_AN_XM) ? 255 : 253;
+
+ for (i = 0; i < sigdata->n_orders; i++) {
+ if (sigdata->order[i] >= first_invalid && sigdata->order[i] <= last_invalid) {
+ sigdata->order[i] = sigdata->n_patterns;
+ found_some = 1;
+ }
+ }
+
+ if (found_some) {
+ IT_PATTERN *new_pattern = realloc(sigdata->pattern, sizeof(*sigdata->pattern) * (sigdata->n_patterns + 1));
+ if (!new_pattern)
+ return -1;
+
+ new_pattern[sigdata->n_patterns].n_rows = 64;
+ new_pattern[sigdata->n_patterns].n_entries = 0;
+ new_pattern[sigdata->n_patterns].entry = NULL;
+ sigdata->pattern = new_pattern;
+ sigdata->n_patterns++;
+ }
+
+ return 0;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/itread.c b/plugins/dumb/dumb-kode54/src/it/itread.c
new file mode 100644
index 00000000..8f5e7ef6
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/itread.c
@@ -0,0 +1,1332 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * itread.c - Code to read an Impulse Tracker / / \ \
+ * module from an open file. | < / \_
+ * | \/ /\ /
+ * Based on the loader from an IT player by Bob. \_ / > /
+ * Adapted for DUMB by entheh. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>//might not be necessary later; required for memset
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+#define INVESTIGATE_OLD_INSTRUMENTS
+
+
+
+static int it_seek(DUMBFILE *f, long offset)
+{
+ long pos = dumbfile_pos(f);
+
+ if (pos > offset)
+ return -1;
+
+ if (pos < offset)
+ if (dumbfile_skip(f, offset - pos))
+ return -1;
+
+ return 0;
+}
+
+
+
+typedef unsigned char byte;
+typedef unsigned short word;
+typedef unsigned long dword;
+
+typedef struct readblock_crap readblock_crap;
+
+struct readblock_crap {
+ unsigned char *sourcebuf;
+ unsigned char *sourcepos;
+ unsigned char *sourceend;
+ int rembits;
+};
+
+
+static int readblock(DUMBFILE *f, readblock_crap * crap)
+{
+ long size;
+ int c;
+
+ size = dumbfile_igetw(f);
+ if (size < 0)
+ return size;
+
+ crap->sourcebuf = malloc(size);
+ if (!crap->sourcebuf)
+ return -1;
+
+ c = dumbfile_getnc((char *)crap->sourcebuf, size, f);
+ if (c < size) {
+ free(crap->sourcebuf);
+ crap->sourcebuf = NULL;
+ return -1;
+ }
+
+ crap->sourcepos = crap->sourcebuf;
+ crap->sourceend = crap->sourcebuf + size;
+ crap->rembits = 8;
+ return 0;
+}
+
+
+
+static void freeblock(readblock_crap * crap)
+{
+ free(crap->sourcebuf);
+ crap->sourcebuf = NULL;
+}
+
+
+
+static int readbits(int bitwidth, readblock_crap * crap)
+{
+ int val = 0;
+ int b = 0;
+
+ if (crap->sourcepos >= crap->sourceend) return val;
+
+ while (bitwidth > crap->rembits) {
+ val |= *crap->sourcepos++ << b;
+ if (crap->sourcepos >= crap->sourceend) return val;
+ b += crap->rembits;
+ bitwidth -= crap->rembits;
+ crap->rembits = 8;
+ }
+
+ val |= (*crap->sourcepos & ((1 << bitwidth) - 1)) << b;
+ *crap->sourcepos >>= bitwidth;
+ crap->rembits -= bitwidth;
+
+ return val;
+}
+
+
+
+/** WARNING - do we even need to pass `right`? */
+/** WARNING - why bother memsetting at all? The whole array is written... */
+// if we do memset, dumb_silence() would be neater...
+static int decompress8(DUMBFILE *f, signed char *data, int len, int it215)
+{
+ int blocklen, blockpos;
+ byte bitwidth;
+ word val;
+ char d1, d2;
+ readblock_crap crap;
+
+ memset(&crap, 0, sizeof(crap));
+
+ memset(data, 0, len * sizeof(*data));
+
+ while (len > 0) {
+ //Read a block of compressed data:
+ if (readblock(f, &crap))
+ return -1;
+ //Set up a few variables
+ blocklen = (len < 0x8000) ? len : 0x8000; //Max block length is 0x8000 bytes
+ blockpos = 0;
+ bitwidth = 9;
+ d1 = d2 = 0;
+ //Start the decompression:
+ while (blockpos < blocklen) {
+ //Read a value:
+ val = (word)readbits(bitwidth, &crap);
+ //Check for bit width change:
+
+ if (bitwidth < 7) { //Method 1:
+ if (val == (1 << (bitwidth - 1))) {
+ val = (word)readbits(3, &crap) + 1;
+ bitwidth = (val < bitwidth) ? val : val + 1;
+ continue;
+ }
+ }
+ else if (bitwidth < 9) { //Method 2
+ byte border = (0xFF >> (9 - bitwidth)) - 4;
+
+ if (val > border && val <= (border + 8)) {
+ val -= border;
+ bitwidth = (val < bitwidth) ? val : val + 1;
+ continue;
+ }
+ }
+ else if (bitwidth == 9) { //Method 3
+ if (val & 0x100) {
+ bitwidth = (val + 1) & 0xFF;
+ continue;
+ }
+ }
+ else { //Illegal width, abort ?
+ freeblock(&crap);
+ return -1;
+ }
+
+ //Expand the value to signed byte:
+ {
+ char v; //The sample value:
+ if (bitwidth < 8) {
+ byte shift = 8 - bitwidth;
+ v = (val << shift);
+ v >>= shift;
+ }
+ else
+ v = (char)val;
+
+ //And integrate the sample value
+ //(It always has to end with integration doesn't it ? ;-)
+ d1 += v;
+ d2 += d1;
+ }
+
+ //Store !
+ /* Version 2.15 was an unofficial version with hacked compression
+ * code. Yay, better compression :D
+ */
+ *data++ = it215 ? d2 : d1;
+ len--;
+ blockpos++;
+ }
+ freeblock(&crap);
+ }
+ return 0;
+}
+
+
+
+static int decompress16(DUMBFILE *f, short *data, int len, int it215)
+{
+ int blocklen, blockpos;
+ byte bitwidth;
+ long val;
+ short d1, d2;
+ readblock_crap crap;
+
+ memset(&crap, 0, sizeof(crap));
+
+ memset(data, 0, len * sizeof(*data));
+
+ while (len > 0) {
+ //Read a block of compressed data:
+ if (readblock(f, &crap))
+ return -1;
+ //Set up a few variables
+ blocklen = (len < 0x4000) ? len : 0x4000; // Max block length is 0x4000 bytes
+ blockpos = 0;
+ bitwidth = 17;
+ d1 = d2 = 0;
+ //Start the decompression:
+ while (blockpos < blocklen) {
+ val = readbits(bitwidth, &crap);
+ //Check for bit width change:
+
+ if (bitwidth < 7) { //Method 1:
+ if (val == (1 << (bitwidth - 1))) {
+ val = readbits(4, &crap) + 1;
+ bitwidth = (val < bitwidth) ? val : val + 1;
+ continue;
+ }
+ }
+ else if (bitwidth < 17) { //Method 2
+ word border = (0xFFFF >> (17 - bitwidth)) - 8;
+
+ if (val > border && val <= (border + 16)) {
+ val -= border;
+ bitwidth = val < bitwidth ? val : val + 1;
+ continue;
+ }
+ }
+ else if (bitwidth == 17) { //Method 3
+ if (val & 0x10000) {
+ bitwidth = (val + 1) & 0xFF;
+ continue;
+ }
+ }
+ else { //Illegal width, abort ?
+ freeblock(&crap);
+ return -1;
+ }
+
+ //Expand the value to signed byte:
+ {
+ short v; //The sample value:
+ if (bitwidth < 16) {
+ byte shift = 16 - bitwidth;
+ v = (short)(val << shift);
+ v >>= shift;
+ }
+ else
+ v = (short)val;
+
+ //And integrate the sample value
+ //(It always has to end with integration doesn't it ? ;-)
+ d1 += v;
+ d2 += d1;
+ }
+
+ //Store !
+ /* Version 2.15 was an unofficial version with hacked compression
+ * code. Yay, better compression :D
+ */
+ *data++ = it215 ? d2 : d1;
+ len--;
+ blockpos++;
+ }
+ freeblock(&crap);
+ }
+ return 0;
+}
+
+
+
+static int it_read_envelope(IT_ENVELOPE *envelope, DUMBFILE *f)
+{
+ int n;
+
+ envelope->flags = dumbfile_getc(f);
+ envelope->n_nodes = dumbfile_getc(f);
+ envelope->loop_start = dumbfile_getc(f);
+ envelope->loop_end = dumbfile_getc(f);
+ envelope->sus_loop_start = dumbfile_getc(f);
+ envelope->sus_loop_end = dumbfile_getc(f);
+ for (n = 0; n < envelope->n_nodes; n++) {
+ envelope->node_y[n] = dumbfile_getc(f);
+ envelope->node_t[n] = dumbfile_igetw(f);
+ }
+ dumbfile_skip(f, 75 - envelope->n_nodes * 3 + 1);
+
+ if (envelope->n_nodes <= 0)
+ envelope->flags &= ~IT_ENVELOPE_ON;
+ else {
+ if (envelope->loop_end >= envelope->n_nodes || envelope->loop_start > envelope->loop_end) envelope->flags &= ~IT_ENVELOPE_LOOP_ON;
+ if (envelope->sus_loop_end >= envelope->n_nodes || envelope->sus_loop_start > envelope->sus_loop_end) envelope->flags &= ~IT_ENVELOPE_SUSTAIN_LOOP;
+ }
+
+ return dumbfile_error(f);
+}
+
+
+
+static int it_read_old_instrument(IT_INSTRUMENT *instrument, DUMBFILE *f)
+{
+ int n;
+
+ /*if (dumbfile_mgetl(f) != IT_INSTRUMENT_SIGNATURE)
+ return -1;*/
+ // XXX
+ dumbfile_skip(f, 4);
+
+ dumbfile_getnc(instrument->filename, 13, f);
+ instrument->filename[13] = 0;
+
+ instrument->volume_envelope.flags = dumbfile_getc(f);
+ instrument->volume_envelope.loop_start = dumbfile_getc(f);
+ instrument->volume_envelope.loop_end = dumbfile_getc(f);
+ instrument->volume_envelope.sus_loop_start = dumbfile_getc(f);
+ instrument->volume_envelope.sus_loop_end = dumbfile_getc(f);
+
+ /* Skip two unused bytes. */
+ dumbfile_skip(f, 2);
+
+ /* In the old instrument format, fadeout ranges from 0 to 64, and is
+ * subtracted at intervals from a value starting at 512. In the new
+ * format, all these values are doubled. Therefore we double when loading
+ * from the old instrument format - that way we don't have to think about
+ * it later.
+ */
+ instrument->fadeout = dumbfile_igetw(f) << 1;
+ instrument->new_note_action = dumbfile_getc(f);
+ instrument->dup_check_type = dumbfile_getc(f);
+ instrument->dup_check_action = DCA_NOTE_CUT; // This might be wrong!
+ /** WARNING - what is the duplicate check action for old-style instruments? */
+
+ /* Skip Tracker Version and Number of Samples. These are only used in
+ * separate instrument files. Also skip unused byte.
+ */
+ dumbfile_skip(f, 4);
+
+ dumbfile_getnc(instrument->name, 26, f);
+ instrument->name[26] = 0;
+
+ /* Skip unused bytes following the Instrument Name. */
+ dumbfile_skip(f, 6);
+
+ instrument->pp_separation = 0;
+ instrument->pp_centre = 60;
+ instrument->global_volume = 128;
+ /** WARNING - should global_volume be 64 or something? */
+ instrument->default_pan = 32;
+ /** WARNING - should default_pan be 128, meaning don`t use? */
+ instrument->random_volume = 0;
+ instrument->random_pan = 0;
+
+ for (n = 0; n < 120; n++) {
+ instrument->map_note[n] = dumbfile_getc(f);
+ instrument->map_sample[n] = dumbfile_getc(f);
+ }
+
+ /* Skip "Volume envelope (200 bytes)". */
+ // - need to know better what this is for though.
+ dumbfile_skip(f, 200);
+
+#ifdef INVESTIGATE_OLD_INSTRUMENTS
+ fprintf(stderr, "Inst %02d Env:", n);
+#endif
+
+ for (n = 0; n < 25; n++)
+ {
+ instrument->volume_envelope.node_t[n] = dumbfile_getc(f);
+ instrument->volume_envelope.node_y[n] = dumbfile_getc(f);
+
+#ifdef INVESTIGATE_OLD_INSTRUMENTS
+ fprintf(stderr, " %d,%d",
+ instrument->volume_envelope.node_t[n],
+ instrument->volume_envelope.node_y[n]);
+#endif
+
+ // This loop is unfinished, as we can probably escape from it before
+ // the end if we want to. Hence the otherwise useless dumbfile_skip()
+ // call below.
+ }
+ dumbfile_skip(f, 50 - (n << 1));
+ instrument->volume_envelope.n_nodes = n;
+
+#ifdef INVESTIGATE_OLD_INSTRUMENTS
+ fprintf(stderr, "\n");
+#endif
+
+ if (dumbfile_error(f))
+ return -1;
+
+ {
+ IT_ENVELOPE *envelope = &instrument->volume_envelope;
+ if (envelope->n_nodes <= 0)
+ envelope->flags &= ~IT_ENVELOPE_ON;
+ else {
+ if (envelope->loop_end >= envelope->n_nodes || envelope->loop_start > envelope->loop_end) envelope->flags &= ~IT_ENVELOPE_LOOP_ON;
+ if (envelope->sus_loop_end >= envelope->n_nodes || envelope->sus_loop_start > envelope->sus_loop_end) envelope->flags &= ~IT_ENVELOPE_SUSTAIN_LOOP;
+ }
+ }
+
+ instrument->filter_cutoff = 127;
+ instrument->filter_resonance = 0;
+
+ instrument->pan_envelope.flags = 0;
+ instrument->pitch_envelope.flags = 0;
+
+ return 0;
+}
+
+
+
+static int it_read_instrument(IT_INSTRUMENT *instrument, DUMBFILE *f, int maxlen)
+{
+ int n, len;
+
+ /*if (dumbfile_mgetl(f) != IT_INSTRUMENT_SIGNATURE)
+ return -1;*/
+ // XXX
+
+ if (maxlen) len = dumbfile_pos(f);
+
+ dumbfile_skip(f, 4);
+
+ dumbfile_getnc(instrument->filename, 13, f);
+ instrument->filename[13] = 0;
+
+ instrument->new_note_action = dumbfile_getc(f);
+ instrument->dup_check_type = dumbfile_getc(f);
+ instrument->dup_check_action = dumbfile_getc(f);
+ instrument->fadeout = dumbfile_igetw(f);
+ instrument->pp_separation = dumbfile_getc(f);
+ instrument->pp_centre = dumbfile_getc(f);
+ instrument->global_volume = dumbfile_getc(f);
+ instrument->default_pan = dumbfile_getc(f);
+ instrument->random_volume = dumbfile_getc(f);
+ instrument->random_pan = dumbfile_getc(f);
+
+ /* Skip Tracker Version and Number of Samples. These are only used in
+ * separate instrument files. Also skip unused byte.
+ */
+ dumbfile_skip(f, 4);
+
+ dumbfile_getnc(instrument->name, 26, f);
+ instrument->name[26] = 0;
+
+ instrument->filter_cutoff = dumbfile_getc(f);
+ instrument->filter_resonance = dumbfile_getc(f);
+
+ /* Skip MIDI Channel, Program and Bank. */
+ //dumbfile_skip(f, 4);
+ /*instrument->output = dumbfile_getc(f);
+ if ( instrument->output > 16 ) {
+ instrument->output -= 128;
+ } else {
+ instrument->output = 0;
+ }
+ dumbfile_skip(f, 3);*/
+ dumbfile_skip(f, 4);
+
+ for (n = 0; n < 120; n++) {
+ instrument->map_note[n] = dumbfile_getc(f);
+ instrument->map_sample[n] = dumbfile_getc(f);
+ }
+
+ if (dumbfile_error(f))
+ return -1;
+
+ if (it_read_envelope(&instrument->volume_envelope, f)) return -1;
+ if (it_read_envelope(&instrument->pan_envelope, f)) return -1;
+ if (it_read_envelope(&instrument->pitch_envelope, f)) return -1;
+
+ if (maxlen) {
+ len = dumbfile_pos(f) - len;
+ if ( maxlen - len < 124 ) return 0;
+ }
+
+ if ( dumbfile_mgetl(f) == IT_MPTX_SIGNATURE ) {
+ for ( n = 0; n < 120; n++ ) {
+ instrument->map_sample[ n ] += dumbfile_getc( f ) << 8;
+ }
+
+ if (dumbfile_error(f))
+ return -1;
+ }
+
+ /*if ( dumbfile_mgetl(f) == IT_INSM_SIGNATURE ) {
+ long end = dumbfile_igetl(f);
+ end += dumbfile_pos(f);
+ while ( dumbfile_pos(f) < end ) {
+ int chunkid = dumbfile_igetl(f);
+ switch ( chunkid ) {
+ case DUMB_ID('P','L','U','G'):
+ instrument->output = dumbfile_getc(f);
+ break;
+ default:
+ chunkid = chunkid / 0x100 + dumbfile_getc(f) * 0x1000000;
+ break;
+ }
+ }
+
+ if (dumbfile_error(f))
+ return -1;
+ }*/
+
+ return 0;
+}
+
+
+
+static int it_read_sample_header(IT_SAMPLE *sample, unsigned char *convert, long *offset, DUMBFILE *f)
+{
+ /* XXX
+ if (dumbfile_mgetl(f) != IT_SAMPLE_SIGNATURE)
+ return -1;*/
+ int hax = 0;
+ long s = dumbfile_mgetl(f);
+ if (s != IT_SAMPLE_SIGNATURE) {
+ if ( s == ( IT_SAMPLE_SIGNATURE >> 16 ) ) {
+ s <<= 16;
+ s |= dumbfile_mgetw(f);
+ if ( s != IT_SAMPLE_SIGNATURE )
+ return -1;
+ hax = 1;
+ }
+ }
+
+ dumbfile_getnc(sample->filename, 13, f);
+ sample->filename[13] = 0;
+
+ sample->global_volume = dumbfile_getc(f);
+ sample->flags = dumbfile_getc(f);
+ sample->default_volume = dumbfile_getc(f);
+
+ dumbfile_getnc(sample->name, 26, f);
+ sample->name[26] = 0;
+
+ *convert = dumbfile_getc(f);
+ sample->default_pan = dumbfile_getc(f);
+ sample->length = dumbfile_igetl(f);
+ sample->loop_start = dumbfile_igetl(f);
+ sample->loop_end = dumbfile_igetl(f);
+ sample->C5_speed = dumbfile_igetl(f);
+ sample->sus_loop_start = dumbfile_igetl(f);
+ sample->sus_loop_end = dumbfile_igetl(f);
+
+#ifdef STEREO_SAMPLES_COUNT_AS_TWO
+ if (sample->flags & IT_SAMPLE_STEREO) {
+ sample->length >>= 1;
+ sample->loop_start >>= 1;
+ sample->loop_end >>= 1;
+ sample->C5_speed >>= 1;
+ sample->sus_loop_start >>= 1;
+ sample->sus_loop_end >>= 1;
+ }
+#endif
+
+ if (sample->flags & IT_SAMPLE_EXISTS) {
+ if (sample->length <= 0)
+ sample->flags &= ~IT_SAMPLE_EXISTS;
+ else {
+ if ((unsigned int)sample->loop_end > (unsigned int)sample->length)
+ sample->flags &= ~IT_SAMPLE_LOOP;
+ else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end)
+ sample->flags &= ~IT_SAMPLE_LOOP;
+
+ if ((unsigned int)sample->sus_loop_end > (unsigned int)sample->length)
+ sample->flags &= ~IT_SAMPLE_SUS_LOOP;
+ else if ((unsigned int)sample->sus_loop_start >= (unsigned int)sample->sus_loop_end)
+ sample->flags &= ~IT_SAMPLE_SUS_LOOP;
+
+ /* We may be able to truncate the sample to save memory. */
+ if (sample->flags & IT_SAMPLE_LOOP &&
+ *convert != 0xFF) { /* not truncating compressed samples, for now... */
+ if ((sample->flags & IT_SAMPLE_SUS_LOOP) && sample->sus_loop_end >= sample->loop_end)
+ sample->length = sample->sus_loop_end;
+ else
+ sample->length = sample->loop_end;
+ }
+ }
+ }
+
+ *offset = dumbfile_igetl(f);
+
+ sample->vibrato_speed = dumbfile_getc(f);
+ sample->vibrato_depth = dumbfile_getc(f);
+ if ( ! hax ) {
+ sample->vibrato_rate = dumbfile_getc(f);
+ sample->vibrato_waveform = dumbfile_getc(f);
+ } else {
+ sample->vibrato_rate = 0;
+ sample->vibrato_waveform = 0;
+ }
+ sample->finetune = 0;
+ sample->max_resampling_quality = -1;
+
+ return dumbfile_error(f);
+}
+
+long _dumb_it_read_sample_data_adpcm4(IT_SAMPLE *sample, DUMBFILE *f)
+{
+ long n, len, delta;
+ signed char * ptr, * end;
+ signed char compression_table[16];
+ if (dumbfile_getnc(compression_table, 16, f) != 16)
+ return -1;
+ ptr = (signed char *) sample->data;
+ delta = 0;
+
+ end = ptr + sample->length;
+ len = (sample->length + 1) / 2;
+ for (n = 0; n < len; n++) {
+ int b = dumbfile_getc(f);
+ if (b < 0) return -1;
+ delta += compression_table[b & 0x0F];
+ *ptr++ = delta;
+ if (ptr >= end) break;
+ delta += compression_table[b >> 4];
+ *ptr++ = delta;
+ }
+
+ return 0;
+}
+
+
+static long it_read_sample_data(int cmwt, IT_SAMPLE *sample, unsigned char convert, DUMBFILE *f)
+{
+ long n;
+
+ long datasize = sample->length;
+ if (sample->flags & IT_SAMPLE_STEREO) datasize <<= 1;
+
+ sample->data = malloc(datasize * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1));
+ if (!sample->data)
+ return -1;
+
+ if (!(sample->flags & IT_SAMPLE_16BIT) && (convert == 0xFF)) {
+ if (_dumb_it_read_sample_data_adpcm4(sample, f) < 0)
+ return -1;
+ } else if (sample->flags & 8) {
+ /* If the sample is packed, then we must unpack it. */
+
+ /** WARNING - unresolved business here... test with ModPlug? */
+
+ if (sample->flags & IT_SAMPLE_STEREO)
+ //exit(37); // TODO: if this ever happens, maybe sample->length should be doubled below?
+ return -1;
+
+/*
+//#ifndef STEREO_SAMPLES_COUNT_AS_TWO
+ ASSERT(!(sample->flags & IT_SAMPLE_STEREO));
+//#endif
+*/
+ if (sample->flags & IT_SAMPLE_16BIT)
+ decompress16(f, sample->data, datasize, ((cmwt >= 0x215) && (convert & 4)));
+ else
+ decompress8(f, sample->data, datasize, ((cmwt >= 0x215) && (convert & 4)));
+ } else if (sample->flags & IT_SAMPLE_16BIT) {
+ if (convert & 2)
+ for (n = 0; n < datasize; n++)
+ ((short *)sample->data)[n] = dumbfile_mgetw(f);
+ else
+ for (n = 0; n < datasize; n++)
+ ((short *)sample->data)[n] = dumbfile_igetw(f);
+ } else
+ for (n = 0; n < datasize; n++)
+ ((signed char *)sample->data)[n] = dumbfile_getc(f);
+
+ if (dumbfile_error(f))
+ return -1;
+
+ if (!(convert & 1)) {
+ /* Convert to signed. */
+ if (sample->flags & IT_SAMPLE_16BIT)
+ for (n = 0; n < datasize; n++)
+ ((short *)sample->data)[n] ^= 0x8000;
+ else
+ for (n = 0; n < datasize; n++)
+ ((signed char *)sample->data)[n] ^= 0x80;
+ }
+
+ /* NOT SUPPORTED:
+ *
+ * convert & 4 - Samples stored as delta values
+ * convert & 16 - Samples stored as TX-Wave 12-bit values
+ * convert & 32 - Left/Right/All Stereo prompt
+ */
+
+ return 0;
+}
+
+
+
+//#define DETECT_DUPLICATE_CHANNELS
+#ifdef DETECT_DUPLICATE_CHANNELS
+#include <stdio.h>
+#endif
+static int it_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer)
+{
+ unsigned char cmask[DUMB_IT_N_CHANNELS];
+ unsigned char cnote[DUMB_IT_N_CHANNELS];
+ unsigned char cinstrument[DUMB_IT_N_CHANNELS];
+ unsigned char cvolpan[DUMB_IT_N_CHANNELS];
+ unsigned char ceffect[DUMB_IT_N_CHANNELS];
+ unsigned char ceffectvalue[DUMB_IT_N_CHANNELS];
+#ifdef DETECT_DUPLICATE_CHANNELS
+ IT_ENTRY *dupentry[DUMB_IT_N_CHANNELS];
+#endif
+
+ int n_entries = 0;
+ int buflen;
+ int bufpos = 0;
+
+ IT_ENTRY *entry;
+
+ unsigned char channel;
+ unsigned char mask;
+
+ memset(cmask, 0, sizeof(cmask));
+ memset(cnote, 0, sizeof(cnote));
+ memset(cinstrument, 0, sizeof(cinstrument));
+ memset(cvolpan, 0, sizeof(cvolpan));
+ memset(ceffect, 0, sizeof(ceffect));
+ memset(ceffectvalue, 0, sizeof(ceffectvalue));
+#ifdef DETECT_DUPLICATE_CHANNELS
+ {
+ int i;
+ for (i = 0; i < DUMB_IT_N_CHANNELS; i++) dupentry[i] = NULL;
+ }
+#endif
+
+ buflen = dumbfile_igetw(f);
+ pattern->n_rows = dumbfile_igetw(f);
+
+ /* Skip four unused bytes. */
+ dumbfile_skip(f, 4);
+
+ if (dumbfile_error(f))
+ return -1;
+
+ /* Read in the pattern data. */
+ dumbfile_getnc(buffer, buflen, f);
+
+ if (dumbfile_error(f))
+ return -1;
+
+ /* Scan the pattern data, and work out how many entries we need room for. */
+ while (bufpos < buflen) {
+ unsigned char b = buffer[bufpos++];
+
+ if (b == 0) {
+ /* End of row */
+ n_entries++;
+ continue;
+ }
+
+ channel = (b - 1) & 63;
+
+ if (b & 128)
+ cmask[channel] = mask = buffer[bufpos++];
+ else
+ mask = cmask[channel];
+
+ {
+ static const unsigned char used[16] = {0, 1, 1, 2, 1, 2, 2, 3, 2, 3, 3, 4, 3, 4, 4, 5};
+ n_entries += (mask != 0);
+ bufpos += used[mask & 15];
+ }
+ }
+
+ pattern->n_entries = n_entries;
+
+ pattern->entry = malloc(n_entries * sizeof(*pattern->entry));
+
+ if (!pattern->entry)
+ return -1;
+
+ bufpos = 0;
+ memset(cmask, 0, sizeof(cmask));
+
+ entry = pattern->entry;
+
+ while (bufpos < buflen) {
+ unsigned char b = buffer[bufpos++];
+
+ if (b == 0) {
+ /* End of row */
+ IT_SET_END_ROW(entry);
+ entry++;
+#ifdef DETECT_DUPLICATE_CHANNELS
+ {
+ int i;
+ for (i = 0; i < DUMB_IT_N_CHANNELS; i++) dupentry[i] = NULL;
+ }
+#endif
+ continue;
+ }
+
+ channel = (b - 1) & 63;
+
+ if (b & 128)
+ cmask[channel] = mask = buffer[bufpos++];
+ else
+ mask = cmask[channel];
+
+ if (mask) {
+ entry->mask = (mask & 15) | (mask >> 4);
+ entry->channel = channel;
+
+ if (mask & IT_ENTRY_NOTE)
+ cnote[channel] = entry->note = buffer[bufpos++];
+ else if (mask & (IT_ENTRY_NOTE << 4))
+ entry->note = cnote[channel];
+
+ if (mask & IT_ENTRY_INSTRUMENT)
+ cinstrument[channel] = entry->instrument = buffer[bufpos++];
+ else if (mask & (IT_ENTRY_INSTRUMENT << 4))
+ entry->instrument = cinstrument[channel];
+
+ if (mask & IT_ENTRY_VOLPAN)
+ cvolpan[channel] = entry->volpan = buffer[bufpos++];
+ else if (mask & (IT_ENTRY_VOLPAN << 4))
+ entry->volpan = cvolpan[channel];
+
+ if (mask & IT_ENTRY_EFFECT) {
+ ceffect[channel] = entry->effect = buffer[bufpos++];
+ ceffectvalue[channel] = entry->effectvalue = buffer[bufpos++];
+ } else {
+ entry->effect = ceffect[channel];
+ entry->effectvalue = ceffectvalue[channel];
+ }
+
+#ifdef DETECT_DUPLICATE_CHANNELS
+ if (dupentry[channel]) {
+ FILE *f = fopen("dupentry.txt", "a");
+ if (!f) abort();
+ fprintf(f, "Two events on channel %d:", channel);
+ fprintf(f, " Event #1:");
+ if (dupentry[channel]->mask & IT_ENTRY_NOTE ) fprintf(f, " %03d", dupentry[channel]->note ); else fprintf(f, " ...");
+ if (dupentry[channel]->mask & IT_ENTRY_INSTRUMENT) fprintf(f, " %03d", dupentry[channel]->instrument); else fprintf(f, " ...");
+ if (dupentry[channel]->mask & IT_ENTRY_VOLPAN ) fprintf(f, " %03d", dupentry[channel]->volpan ); else fprintf(f, " ...");
+ if (dupentry[channel]->mask & IT_ENTRY_EFFECT) fprintf(f, " %c%02X\n", 'A' - 1 + dupentry[channel]->effect, dupentry[channel]->effectvalue); else fprintf(f, " ...\n");
+ fprintf(f, " Event #2:");
+ if (entry->mask & IT_ENTRY_NOTE ) fprintf(f, " %03d", entry->note ); else fprintf(f, " ...");
+ if (entry->mask & IT_ENTRY_INSTRUMENT) fprintf(f, " %03d", entry->instrument); else fprintf(f, " ...");
+ if (entry->mask & IT_ENTRY_VOLPAN ) fprintf(f, " %03d", entry->volpan ); else fprintf(f, " ...");
+ if (entry->mask & IT_ENTRY_EFFECT) fprintf(f, " %c%02X\n", 'A' - 1 + entry->effect, entry->effectvalue); else fprintf(f, " ...\n");
+ fclose(f);
+ }
+ dupentry[channel] = entry;
+#endif
+
+ entry++;
+ }
+ }
+
+ ASSERT(entry == pattern->entry + n_entries);
+
+ return 0;
+}
+
+
+
+/* Currently we assume the sample data are stored after the sample headers in
+ * module files. This assumption may be unjustified; let me know if you have
+ * trouble.
+ */
+
+#define IT_COMPONENT_SONG_MESSAGE 1
+#define IT_COMPONENT_INSTRUMENT 2
+#define IT_COMPONENT_PATTERN 3
+#define IT_COMPONENT_SAMPLE 4
+
+typedef struct IT_COMPONENT
+{
+ unsigned char type;
+ unsigned short n;
+ long offset;
+ short sampfirst; /* component[sampfirst] = first sample data after this */
+ short sampnext; /* sampnext is used to create linked lists of sample data */
+}
+IT_COMPONENT;
+
+
+
+static int it_component_compare(const void *e1, const void *e2)
+{
+ return ((const IT_COMPONENT *)e1)->offset -
+ ((const IT_COMPONENT *)e2)->offset;
+}
+
+
+
+static sigdata_t *it_load_sigdata(DUMBFILE *f)
+{
+ DUMB_IT_SIGDATA *sigdata;
+
+ int cwt, cmwt;
+ int special;
+ int message_length, message_offset;
+
+ IT_COMPONENT *component;
+ int n_components = 0;
+
+ unsigned char sample_convert[4096];
+
+ int n;
+
+ unsigned char *buffer;
+
+ if (dumbfile_mgetl(f) != IT_SIGNATURE)
+ return NULL;
+
+ sigdata = malloc(sizeof(*sigdata));
+
+ if (!sigdata)
+ return NULL;
+
+ sigdata->song_message = NULL;
+ sigdata->order = NULL;
+ sigdata->instrument = NULL;
+ sigdata->sample = NULL;
+ sigdata->pattern = NULL;
+ sigdata->midi = NULL;
+ sigdata->checkpoint = NULL;
+
+ dumbfile_getnc(sigdata->name, 26, f);
+ sigdata->name[26] = 0;
+
+ /* Skip pattern row highlight info. */
+ dumbfile_skip(f, 2);
+
+ sigdata->n_orders = dumbfile_igetw(f);
+ sigdata->n_instruments = dumbfile_igetw(f);
+ sigdata->n_samples = dumbfile_igetw(f);
+ sigdata->n_patterns = dumbfile_igetw(f);
+
+ cwt = dumbfile_igetw(f);
+ cmwt = dumbfile_igetw(f);
+
+ sigdata->flags = dumbfile_igetw(f);
+ special = dumbfile_igetw(f);
+
+ sigdata->global_volume = dumbfile_getc(f);
+ sigdata->mixing_volume = dumbfile_getc(f);
+ sigdata->speed = dumbfile_getc(f);
+ if (sigdata->speed == 0) sigdata->speed = 6; // Should we? What about tempo?
+ sigdata->tempo = dumbfile_getc(f);
+ sigdata->pan_separation = dumbfile_getc(f); /** WARNING: use this */
+
+ /* Skip Pitch Wheel Depth */
+ dumbfile_skip(f, 1);
+
+ message_length = dumbfile_igetw(f);
+ message_offset = dumbfile_igetl(f);
+
+ /* Skip Reserved. */
+ dumbfile_skip(f, 4);
+
+ dumbfile_getnc(sigdata->channel_pan, DUMB_IT_N_CHANNELS, f);
+ dumbfile_getnc(sigdata->channel_volume, DUMB_IT_N_CHANNELS, f);
+
+ // XXX sample count
+ if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_instruments > 256 || sigdata->n_samples > 4000 || sigdata->n_patterns > 256) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ sigdata->order = malloc(sigdata->n_orders);
+ if (!sigdata->order) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ if (sigdata->n_instruments) {
+ sigdata->instrument = malloc(sigdata->n_instruments * sizeof(*sigdata->instrument));
+ if (!sigdata->instrument) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ }
+
+ if (sigdata->n_samples) {
+ sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
+ if (!sigdata->sample) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ for (n = 0; n < sigdata->n_samples; n++)
+ sigdata->sample[n].data = NULL;
+ }
+
+ if (sigdata->n_patterns) {
+ sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
+ if (!sigdata->pattern) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ for (n = 0; n < sigdata->n_patterns; n++)
+ sigdata->pattern[n].entry = NULL;
+ }
+
+ dumbfile_getnc(sigdata->order, sigdata->n_orders, f);
+ sigdata->restart_position = 0;
+
+ component = malloc(769 * sizeof(*component));
+ if (!component) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ if (special & 1) {
+ component[n_components].type = IT_COMPONENT_SONG_MESSAGE;
+ component[n_components].offset = message_offset;
+ component[n_components].sampfirst = -1;
+ n_components++;
+ }
+
+ for (n = 0; n < sigdata->n_instruments; n++) {
+ component[n_components].type = IT_COMPONENT_INSTRUMENT;
+ component[n_components].n = n;
+ component[n_components].offset = dumbfile_igetl(f);
+ component[n_components].sampfirst = -1;
+ n_components++;
+ }
+
+ for (n = 0; n < sigdata->n_samples; n++) {
+ component[n_components].type = IT_COMPONENT_SAMPLE;
+ component[n_components].n = n;
+ component[n_components].offset = dumbfile_igetl(f);
+ component[n_components].sampfirst = -1;
+ n_components++;
+ }
+
+ for (n = 0; n < sigdata->n_patterns; n++) {
+ long offset = dumbfile_igetl(f);
+ if (offset) {
+ component[n_components].type = IT_COMPONENT_PATTERN;
+ component[n_components].n = n;
+ component[n_components].offset = offset;
+ component[n_components].sampfirst = -1;
+ n_components++;
+ } else {
+ /* Empty 64-row pattern */
+ sigdata->pattern[n].n_rows = 64;
+ sigdata->pattern[n].n_entries = 0;
+ }
+ }
+
+ if (dumbfile_error(f)) {
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ /*
+ if (!(sigdata->flags & 128) != !(special & 8)) {
+ fprintf(stderr, "Flags Bit 7 (\"Request embedded MIDI configuration\"): %s\n", sigdata->flags & 128 ? "=SET=" : "clear");
+ fprintf(stderr, "Special Bit 3 (\"MIDI configuration embedded\") : %s\n", special & 8 ? "=SET=" : "clear");
+ fprintf(stderr, "entheh would like to investigate this IT file.\n");
+ fprintf(stderr, "Please contact him! entheh@users.sf.net\n");
+ }
+ */
+
+ if (special & 8) {
+ /* MIDI configuration is embedded. */
+ unsigned char mididata[32];
+ int i;
+ sigdata->midi = malloc(sizeof(*sigdata->midi));
+ if (!sigdata->midi) {
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ // Should we be happy with this outcome in some situations?
+ }
+ // What are we skipping?
+ i = dumbfile_igetw(f);
+ if (dumbfile_error(f) || dumbfile_skip(f, 8*i)) {
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ /* Read embedded MIDI configuration */
+ // What are the first 9 commands for?
+ if (dumbfile_skip(f, 32*9)) {
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ for (i = 0; i < 16; i++) {
+ unsigned char len = 0;
+ int j, leftdigit = -1;
+ if (dumbfile_getnc(mididata, 32, f) < 32) {
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ sigdata->midi->SFmacroz[i] = 0;
+ for (j = 0; j < 32; j++) {
+ if (leftdigit >= 0) {
+ if (mididata[j] == 0) {
+ sigdata->midi->SFmacro[i][len++] = leftdigit;
+ break;
+ } else if (mididata[j] == ' ')
+ sigdata->midi->SFmacro[i][len++] = leftdigit;
+ else if (mididata[j] >= '0' && mididata[j] <= '9')
+ sigdata->midi->SFmacro[i][len++] = (leftdigit << 4) | (mididata[j] - '0');
+ else if (mididata[j] >= 'A' && mididata[j] <= 'F')
+ sigdata->midi->SFmacro[i][len++] = (leftdigit << 4) | (mididata[j] - 'A' + 0xA);
+ leftdigit = -1;
+ } else if (mididata[j] == 0)
+ break;
+ else if (mididata[j] == 'z')
+ sigdata->midi->SFmacroz[i] |= 1 << len++;
+ else if (mididata[j] >= '0' && mididata[j] <= '9')
+ leftdigit = mididata[j] - '0';
+ else if (mididata[j] >= 'A' && mididata[j] <= 'F')
+ leftdigit = mididata[j] - 'A' + 0xA;
+ }
+ sigdata->midi->SFmacrolen[i] = len;
+ }
+ for (i = 0; i < 128; i++) {
+ unsigned char len = 0;
+ int j, leftdigit = -1;
+ dumbfile_getnc(mididata, 32, f);
+ for (j = 0; j < 32; j++) {
+ if (leftdigit >= 0) {
+ if (mididata[j] == 0) {
+ sigdata->midi->Zmacro[i][len++] = leftdigit;
+ break;
+ } else if (mididata[j] == ' ')
+ sigdata->midi->Zmacro[i][len++] = leftdigit;
+ else if (mididata[j] >= '0' && mididata[j] <= '9')
+ sigdata->midi->Zmacro[i][len++] = (leftdigit << 4) | (mididata[j] - '0');
+ else if (mididata[j] >= 'A' && mididata[j] <= 'F')
+ sigdata->midi->Zmacro[i][len++] = (leftdigit << 4) | (mididata[j] - 'A' + 0xA);
+ leftdigit = -1;
+ } else if (mididata[j] == 0)
+ break;
+ else if (mididata[j] >= '0' && mididata[j] <= '9')
+ leftdigit = mididata[j] - '0';
+ else if (mididata[j] >= 'A' && mididata[j] <= 'F')
+ leftdigit = mididata[j] - 'A' + 0xA;
+ }
+ sigdata->midi->Zmacrolen[i] = len;
+ }
+ }
+
+ sigdata->flags &= IT_REAL_FLAGS;
+
+ qsort(component, n_components, sizeof(IT_COMPONENT), &it_component_compare);
+
+ buffer = malloc(65536);
+ if (!buffer) {
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ for (n = 0; n < n_components; n++) {
+ long offset;
+ int m;
+
+ /* XXX */
+ if ( component[n].offset == 0 ) {
+ switch (component[n].type) {
+ case IT_COMPONENT_INSTRUMENT:
+ memset( &sigdata->instrument[component[n].n], 0, sizeof(IT_INSTRUMENT) );
+ break;
+ case IT_COMPONENT_SAMPLE:
+ memset( &sigdata->sample[component[n].n], 0, sizeof(IT_SAMPLE) );
+ break;
+ case IT_COMPONENT_PATTERN:
+ {
+ IT_PATTERN * p = &sigdata->pattern[component[n].n];
+ p->entry = 0;
+ p->n_rows = 64;
+ p->n_entries = 0;
+ }
+ break;
+ }
+ continue;
+ }
+
+ if (it_seek(f, component[n].offset)) {
+ free(buffer);
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ switch (component[n].type) {
+
+ case IT_COMPONENT_SONG_MESSAGE:
+ if ( n < n_components ) {
+ message_length = min( message_length, component[n+1].offset - component[n].offset );
+ }
+ sigdata->song_message = malloc(message_length + 1);
+ if (sigdata->song_message) {
+ if (dumbfile_getnc(sigdata->song_message, message_length, f) < message_length) {
+ free(buffer);
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ sigdata->song_message[message_length] = 0;
+ }
+ break;
+
+ case IT_COMPONENT_INSTRUMENT:
+ if (cmwt < 0x200)
+ m = it_read_old_instrument(&sigdata->instrument[component[n].n], f);
+ else
+ m = it_read_instrument(&sigdata->instrument[component[n].n], f, (n + 1 < n_components) ? (component[n+1].offset - component[n].offset) : 0);
+
+ if (m) {
+ free(buffer);
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ break;
+
+ case IT_COMPONENT_PATTERN:
+ if (it_read_pattern(&sigdata->pattern[component[n].n], f, buffer)) {
+ free(buffer);
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ break;
+
+ case IT_COMPONENT_SAMPLE:
+ if (it_read_sample_header(&sigdata->sample[component[n].n], &sample_convert[component[n].n], &offset, f)) {
+ free(buffer);
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ if (sigdata->sample[component[n].n].flags & IT_SAMPLE_EXISTS) {
+ short *sample;
+
+ for (m = n + 1; m < n_components; m++)
+ if (component[m].offset > offset)
+ break;
+ m--;
+
+ sample = &component[m].sampfirst;
+
+ while (*sample >= 0 && component[*sample].offset <= offset)
+ sample = &component[*sample].sampnext;
+
+ component[n].sampnext = *sample;
+ *sample = n;
+
+ component[n].offset = offset;
+ }
+ }
+
+ m = component[n].sampfirst;
+
+ while (m >= 0) {
+ if (it_seek(f, component[m].offset)) {
+ free(buffer);
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ if (it_read_sample_data(cmwt, &sigdata->sample[component[m].n], sample_convert[component[m].n], f)) {
+ free(buffer);
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ m = component[m].sampnext;
+ }
+ }
+
+ free(buffer);
+ free(component);
+
+ _dumb_it_fix_invalid_orders(sigdata);
+
+ return sigdata;
+}
+
+
+
+DUH *dumb_read_it_quick(DUMBFILE *f)
+{
+ sigdata_t *sigdata;
+
+ DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+ sigdata = it_load_sigdata(f);
+
+ if (!sigdata)
+ return NULL;
+
+ {
+ const char *tag[1][2];
+ tag[0][0] = "TITLE";
+ tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+ return make_duh(-1, 1, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
+ }
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/itread2.c b/plugins/dumb/dumb-kode54/src/it/itread2.c
new file mode 100644
index 00000000..202b8bb9
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/itread2.c
@@ -0,0 +1,29 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * itread2.c - Function to read an Impulse Tracker / / \ \
+ * module from an open file and do an | < / \_
+ * initial run-through. | \/ /\ /
+ * \_ / > /
+ * Split off from itread.c by entheh. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+
+
+
+DUH *dumb_read_it(DUMBFILE *f)
+{
+ DUH *duh = dumb_read_it_quick(f);
+ dumb_it_do_initial_runthrough(duh);
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/itrender.c b/plugins/dumb/dumb-kode54/src/it/itrender.c
new file mode 100644
index 00000000..5dd8e4ef
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/itrender.c
@@ -0,0 +1,5542 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * itrender.c - Code to render an Impulse Tracker / / \ \
+ * module. | < / \_
+ * | \/ /\ /
+ * Written - painstakingly - by entheh. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "dumb.h"
+#include "internal/dumb.h"
+#include "internal/it.h"
+
+// #define BIT_ARRAY_BULLSHIT
+
+#define END_RAMPING
+#define RAMP_DOWN
+
+static IT_PLAYING *dup_playing(IT_PLAYING *src, IT_CHANNEL *dstchannel, IT_CHANNEL *srcchannel)
+{
+ IT_PLAYING *dst;
+
+ if (!src) return NULL;
+
+ dst = malloc(sizeof(*dst));
+ if (!dst) return NULL;
+
+ dst->flags = src->flags;
+ dst->resampling_quality = src->resampling_quality;
+
+ ASSERT(src->channel);
+ dst->channel = &dstchannel[src->channel - srcchannel];
+ dst->sample = src->sample;
+ dst->instrument = src->instrument;
+ dst->env_instrument = src->env_instrument;
+
+ dst->sampnum = src->sampnum;
+ dst->instnum = src->instnum;
+
+#ifdef END_RAMPING
+ dst->declick_stage = src->declick_stage;
+ dst->declick_volume = src->declick_volume;
+#endif
+
+ dst->float_volume[0] = src->float_volume[0];
+ dst->float_volume[1] = src->float_volume[1];
+
+ dst->ramp_volume[0] = src->ramp_volume[0];
+ dst->ramp_volume[1] = src->ramp_volume[1];
+
+ dst->ramp_delta[0] = src->ramp_delta[0];
+ dst->ramp_delta[1] = src->ramp_delta[1];
+
+ dst->channel_volume = src->channel_volume;
+
+ dst->volume = src->volume;
+ dst->pan = src->pan;
+
+ dst->volume_offset = src->volume_offset;
+ dst->panning_offset = src->panning_offset;
+
+ dst->note = src->note;
+
+ dst->enabled_envelopes = src->enabled_envelopes;
+
+ dst->filter_cutoff = src->filter_cutoff;
+ dst->filter_resonance = src->filter_resonance;
+
+ dst->true_filter_cutoff = src->true_filter_cutoff;
+ dst->true_filter_resonance = src->true_filter_resonance;
+
+ dst->vibrato_speed = src->vibrato_speed;
+ dst->vibrato_depth = src->vibrato_depth;
+ dst->vibrato_n = src->vibrato_n;
+ dst->vibrato_time = src->vibrato_time;
+ dst->vibrato_waveform = src->vibrato_waveform;
+
+ dst->tremolo_speed = src->tremolo_speed;
+ dst->tremolo_depth = src->tremolo_depth;
+ dst->tremolo_time = src->tremolo_time;
+ dst->tremolo_waveform = src->tremolo_waveform;
+
+ dst->panbrello_speed = src->panbrello_speed;
+ dst->panbrello_depth = src->panbrello_depth;
+ dst->panbrello_time = src->panbrello_time;
+ dst->panbrello_waveform = src->panbrello_waveform;
+ dst->panbrello_random = src->panbrello_random;
+
+ dst->sample_vibrato_time = src->sample_vibrato_time;
+ dst->sample_vibrato_waveform = src->sample_vibrato_waveform;
+ dst->sample_vibrato_depth = src->sample_vibrato_depth;
+
+ dst->slide = src->slide;
+ dst->delta = src->delta;
+ dst->finetune = src->finetune;
+
+ dst->volume_envelope = src->volume_envelope;
+ dst->pan_envelope = src->pan_envelope;
+ dst->pitch_envelope = src->pitch_envelope;
+
+ dst->fadeoutcount = src->fadeoutcount;
+
+ dst->filter_state[0] = src->filter_state[0];
+ dst->filter_state[1] = src->filter_state[1];
+
+ dst->resampler = src->resampler;
+ dst->resampler.pickup_data = dst;
+ dst->time_lost = src->time_lost;
+
+ //dst->output = src->output;
+
+ return dst;
+}
+
+
+
+static void dup_channel(IT_CHANNEL *dst, IT_CHANNEL *src)
+{
+ dst->flags = src->flags;
+
+ dst->volume = src->volume;
+ dst->volslide = src->volslide;
+ dst->xm_volslide = src->xm_volslide;
+ dst->panslide = src->panslide;
+
+ dst->pan = src->pan;
+ dst->truepan = src->truepan;
+
+ dst->channelvolume = src->channelvolume;
+ dst->channelvolslide = src->channelvolslide;
+
+ dst->instrument = src->instrument;
+ dst->note = src->note;
+
+ dst->SFmacro = src->SFmacro;
+
+ dst->filter_cutoff = src->filter_cutoff;
+ dst->filter_resonance = src->filter_resonance;
+
+ dst->key_off_count = src->key_off_count;
+ dst->note_cut_count = src->note_cut_count;
+ dst->note_delay_count = src->note_delay_count;
+ dst->note_delay_entry = src->note_delay_entry;
+
+ dst->new_note_action = src->new_note_action;
+
+ dst->arpeggio = src->arpeggio;
+ dst->retrig = src->retrig;
+ dst->xm_retrig = src->xm_retrig;
+ dst->retrig_tick = src->retrig_tick;
+
+ dst->tremor_time = src->tremor_time;
+
+ dst->vibrato_waveform = src->vibrato_waveform;
+ dst->tremolo_waveform = src->tremolo_waveform;
+ dst->panbrello_waveform = src->panbrello_waveform;
+
+ dst->portamento = src->portamento;
+ dst->toneporta = src->toneporta;
+ dst->toneslide = src->toneslide;
+ dst->toneslide_tick = src->toneslide_tick;
+ dst->last_toneslide_tick = src->last_toneslide_tick;
+ dst->ptm_toneslide = src->ptm_toneslide;
+ dst->ptm_last_toneslide = src->ptm_last_toneslide;
+ dst->destnote = src->destnote;
+
+ dst->glissando = src->glissando;
+
+ dst->sample = src->sample;
+ dst->truenote = src->truenote;
+
+ dst->midi_state = src->midi_state;
+
+ dst->lastvolslide = src->lastvolslide;
+ dst->lastDKL = src->lastDKL;
+ dst->lastEF = src->lastEF;
+ dst->lastG = src->lastG;
+ dst->lastHspeed = src->lastHspeed;
+ dst->lastHdepth = src->lastHdepth;
+ dst->lastRspeed = src->lastRspeed;
+ dst->lastRdepth = src->lastRdepth;
+ dst->lastYspeed = src->lastYspeed;
+ dst->lastYdepth = src->lastYdepth;
+ dst->lastI = src->lastI;
+ dst->lastJ = src->lastJ;
+ dst->lastN = src->lastN;
+ dst->lastO = src->lastO;
+ dst->high_offset = src->high_offset;
+ dst->lastP = src->lastP;
+ dst->lastQ = src->lastQ;
+ dst->lastS = src->lastS;
+ dst->pat_loop_row = src->pat_loop_row;
+ dst->pat_loop_count = src->pat_loop_count;
+ dst->pat_loop_end_row = src->pat_loop_end_row;
+ dst->lastW = src->lastW;
+
+ dst->xm_lastE1 = src->xm_lastE1;
+ dst->xm_lastE2 = src->xm_lastE2;
+ dst->xm_lastEA = src->xm_lastEA;
+ dst->xm_lastEB = src->xm_lastEB;
+ dst->xm_lastX1 = src->xm_lastX1;
+ dst->xm_lastX2 = src->xm_lastX2;
+
+ dst->playing = dup_playing(src->playing, dst, src);
+
+
+#ifdef BIT_ARRAY_BULLSHIT
+ dst->played_patjump = bit_array_dup(src->played_patjump);
+ dst->played_patjump_order = src->played_patjump_order;
+#endif
+
+ //dst->output = src->output;
+}
+
+
+
+/* Allocate the new callbacks first, then pass them to this function!
+ * It will free them on failure.
+ */
+static DUMB_IT_SIGRENDERER *dup_sigrenderer(DUMB_IT_SIGRENDERER *src, int n_channels, IT_CALLBACKS *callbacks)
+{
+ DUMB_IT_SIGRENDERER *dst;
+ int i;
+
+ if (!src) {
+ if (callbacks) free(callbacks);
+ return NULL;
+ }
+
+ dst = malloc(sizeof(*dst));
+ if (!dst) {
+ if (callbacks) free(callbacks);
+ return NULL;
+ }
+
+ dst->sigdata = src->sigdata;
+
+ dst->n_channels = n_channels;
+
+ dst->resampling_quality = src->resampling_quality;
+
+ dst->globalvolume = src->globalvolume;
+ dst->globalvolslide = src->globalvolslide;
+
+ dst->tempo = src->tempo;
+ dst->temposlide = src->temposlide;
+
+ for (i = 0; i < DUMB_IT_N_CHANNELS; i++)
+ dup_channel(&dst->channel[i], &src->channel[i]);
+
+ for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++)
+ dst->playing[i] = dup_playing(src->playing[i], dst->channel, src->channel);
+
+ dst->tick = src->tick;
+ dst->speed = src->speed;
+ dst->rowcount = src->rowcount;
+
+ dst->order = src->order;
+ dst->row = src->row;
+ dst->processorder = src->processorder;
+ dst->processrow = src->processrow;
+ dst->breakrow = src->breakrow;
+
+ dst->restart_position = src->restart_position;
+
+ dst->n_rows = src->n_rows;
+
+ dst->entry_start = src->entry_start;
+ dst->entry = src->entry;
+ dst->entry_end = src->entry_end;
+
+ dst->time_left = src->time_left;
+ dst->sub_time_left = src->sub_time_left;
+
+ dst->click_remover = NULL;
+
+ dst->callbacks = callbacks;
+
+#ifdef BIT_ARRAY_BULLSHIT
+ dst->played = bit_array_dup(src->played);
+#endif
+
+ dst->gvz_time = src->gvz_time;
+ dst->gvz_sub_time = src->gvz_sub_time;
+
+ //dst->max_output = src->max_output;
+
+ return dst;
+}
+
+
+
+static const IT_MIDI default_midi = {
+ /* unsigned char SFmacro[16][16]; */
+ {
+ {0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+ },
+ /* unsigned char SFmacrolen[16]; */
+ {4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* unsigned short SFmacroz[16]; */
+ /* Bitfield; bit 0 set = z in first position */
+ {
+ 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+ },
+ /* unsigned char Zmacro[128][16]; */
+ {
+ {0xF0, 0xF0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xF0, 0xF0, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xF0, 0xF0, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xF0, 0xF0, 0x01, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xF0, 0xF0, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xF0, 0xF0, 0x01, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xF0, 0xF0, 0x01, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xF0, 0xF0, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xF0, 0xF0, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xF0, 0xF0, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xF0, 0xF0, 0x01, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xF0, 0xF0, 0x01, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xF0, 0xF0, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xF0, 0xF0, 0x01, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xF0, 0xF0, 0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xF0, 0xF0, 0x01, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+ },
+ /* unsigned char Zmacrolen[128]; */
+ {
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }
+};
+
+
+
+static void it_reset_filter_state(IT_FILTER_STATE *state)
+{
+ state->currsample = 0;
+ state->prevsample = 0;
+}
+
+
+
+#if 1
+#define LOG10 2.30258509299
+
+/* IMPORTANT: This function expects one extra sample in 'src' so it can apply
+ * click removal. It reads size samples, starting from src[0], and writes its
+ * output starting at dst[pos]. The pos parameter is required for getting
+ * click removal right.
+ */
+
+static void it_filter(DUMB_CLICK_REMOVER *cr, IT_FILTER_STATE *state, sample_t *dst, long pos, sample_t *src, long size, int step, int sampfreq, int cutoff, int resonance)
+{
+ sample_t currsample = state->currsample;
+ sample_t prevsample = state->prevsample;
+
+ float a, b, c;
+
+ long datasize;
+
+ {
+ float inv_angle = (float)(sampfreq * pow(0.5, 0.25 + cutoff*(1.0/(24<<IT_ENVELOPE_SHIFT))) * (1.0/(2*3.14159265358979323846*110.0)));
+ float loss = (float)exp(resonance*(-LOG10*1.2/128.0));
+ float d, e;
+#if 0
+ loss *= 2; // This is the mistake most players seem to make!
+#endif
+
+#if 1
+ d = (1.0f - loss) / inv_angle;
+ if (d > 2.0f) d = 2.0f;
+ d = (loss - d) * inv_angle;
+ e = inv_angle * inv_angle;
+ a = 1.0f / (1.0f + d + e);
+ c = -e * a;
+ b = 1.0f - a - c;
+#else
+ a = 1.0f / (inv_angle*inv_angle + inv_angle*loss + loss);
+ c = -(inv_angle*inv_angle) * a;
+ b = 1.0f - a - c;
+#endif
+ }
+
+ dst += pos * step;
+ datasize = size * step;
+
+#define INT_FILTERS
+#ifdef INT_FILTERS
+#define MULSCA(a, b) ((int)((LONG_LONG)((a) << 4) * (b) >> 32))
+#define SCALEB 12
+ {
+ int ai = (int)(a * (1 << (16+SCALEB)));
+ int bi = (int)(b * (1 << (16+SCALEB)));
+ int ci = (int)(c * (1 << (16+SCALEB)));
+ int i;
+
+ if (cr) {
+ sample_t startstep = MULSCA(src[0], ai) + MULSCA(currsample, bi) + MULSCA(prevsample, ci);
+ dumb_record_click(cr, pos, startstep);
+ }
+
+ for (i = 0; i < datasize; i += step) {
+ {
+ sample_t newsample = MULSCA(src[i], ai) + MULSCA(currsample, bi) + MULSCA(prevsample, ci);
+ prevsample = currsample;
+ currsample = newsample;
+ }
+ dst[i] += currsample;
+ }
+
+ if (cr) {
+ sample_t endstep = MULSCA(src[datasize], ai) + MULSCA(currsample, bi) + MULSCA(prevsample, ci);
+ dumb_record_click(cr, pos + size, -endstep);
+ }
+ }
+#else
+#error This version is broken - it does not use step, and state should contain floats for it
+ if (cr) {
+ float startstep = src[0]*a + currsample*b + prevsample*c;
+ dumb_record_click(cr, pos, (sample_t)startstep);
+ }
+
+ {
+ int i = size % 3;
+ while (i > 0) {
+ {
+ float newsample = *src++*a + currsample*b + prevsample*c;
+ prevsample = currsample;
+ currsample = newsample;
+ }
+ *dst++ += (sample_t)currsample;
+ i--;
+ }
+ i = size / 3;
+ while (i > 0) {
+ float newsample;
+ /* Gotta love unrolled loops! */
+ *dst++ += (sample_t)(newsample = *src++*a + currsample*b + prevsample*c);
+ *dst++ += (sample_t)(prevsample = *src++*a + newsample*b + currsample*c);
+ *dst++ += (sample_t)(currsample = *src++*a + prevsample*b + newsample*c);
+ i--;
+ }
+ }
+
+ if (cr) {
+ float endstep = src[datasize]*a + currsample*b + prevsample*c;
+ dumb_record_click(cr, pos + size, -(sample_t)endstep);
+ }
+#endif
+
+ state->currsample = currsample;
+ state->prevsample = prevsample;
+}
+#undef LOG10
+#endif
+
+
+
+static const signed char it_sine[256] = {
+ 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23,
+ 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59,
+ 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60,
+ 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46,
+ 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26,
+ 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2,
+ 0, -2, -3, -5, -6, -8, -9,-11,-12,-14,-16,-17,-19,-20,-22,-23,
+ -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44,
+ -45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59,
+ -59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64,
+ -64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60,
+ -59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,
+ -45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26,
+ -24,-23,-22,-20,-19,-17,-16,-14,-12,-11, -9, -8, -6, -5, -3, -2
+};
+
+
+
+#if 1
+/** WARNING: use these! */
+/** JULIEN: Plus for XM compatibility it could be interesting to rename
+ * it_sawtooth[] to it_rampdown[], and add an it_rampup[].
+ * Also, still for XM compat', twood be good if it was possible to tell the
+ * the player not to retrig' the waveform on a new instrument.
+ * Both of these are only for completness though, as I don't think it would
+ * be very noticeable ;)
+ */
+/** ENTHEH: IT also has the 'don't retrig' thingy :) */
+static const signed char it_sawtooth[256] = {
+ 64, 63, 63, 62, 62, 61, 61, 60, 60, 59, 59, 58, 58, 57, 57, 56,
+ 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48,
+ 48, 47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 40,
+ 40, 39, 39, 38, 38, 37, 37, 36, 36, 35, 35, 34, 34, 33, 33, 32,
+ 32, 31, 31, 30, 30, 29, 29, 28, 28, 27, 27, 26, 26, 25, 25, 24,
+ 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18, 17, 17, 16,
+ 16, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8,
+ 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0,
+ 0, -1, -1, -2, -2, -3, -3, -4, -4, -5, -5, -6, -6, -7, -7, -8,
+ -8, -9, -9,-10,-10,-11,-11,-12,-12,-13,-13,-14,-14,-15,-15,-16,
+ -16,-17,-17,-18,-18,-19,-19,-20,-20,-21,-21,-22,-22,-23,-23,-24,
+ -24,-25,-25,-26,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32,
+ -32,-33,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40,
+ -40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-46,-47,-47,-48,
+ -48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-54,-54,-55,-55,-56,
+ -56,-57,-57,-58,-58,-59,-59,-60,-60,-61,-61,-62,-62,-63,-63,-64
+};
+
+static const signed char it_squarewave[256] = {
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const signed char it_xm_ramp[256] = {
+ 0, -1, -1, -2, -2, -3, -3, -4, -4, -5, -5, -6, -6, -7, -7, -8,
+ -8, -9, -9,-10,-10,-11,-11,-12,-12,-13,-13,-14,-14,-15,-15,-16,
+ -16,-17,-17,-18,-18,-19,-19,-20,-20,-21,-21,-22,-22,-23,-23,-24,
+ -24,-25,-25,-26,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32,
+ -32,-33,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40,
+ -40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-46,-47,-47,-48,
+ -48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-54,-54,-55,-55,-56,
+ -56,-57,-57,-58,-58,-59,-59,-60,-60,-61,-61,-62,-62,-63,-63,-64,
+ 64, 63, 63, 62, 62, 61, 61, 60, 60, 59, 59, 58, 58, 57, 57, 56,
+ 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48,
+ 48, 47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 40,
+ 40, 39, 39, 38, 38, 37, 37, 36, 36, 35, 35, 34, 34, 33, 33, 32,
+ 32, 31, 31, 30, 30, 29, 29, 28, 28, 27, 27, 26, 26, 25, 25, 24,
+ 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18, 17, 17, 16,
+ 16, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8,
+ 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0
+};
+
+static const signed char it_xm_squarewave[256] = {
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
+ -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
+ -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
+ -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
+ -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
+ -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
+ -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
+ -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64
+};
+
+#endif
+
+
+
+static void reset_tick_counts(DUMB_IT_SIGRENDERER *sigrenderer)
+{
+ int i;
+
+ for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+ IT_CHANNEL *channel = &sigrenderer->channel[i];
+ channel->key_off_count = 0;
+ channel->note_cut_count = 0;
+ channel->note_delay_count = 0;
+ }
+}
+
+
+
+static void reset_channel_effects(IT_CHANNEL *channel)
+{
+ channel->volslide = 0;
+ channel->xm_volslide = 0;
+ channel->panslide = 0;
+ channel->channelvolslide = 0;
+ channel->arpeggio = 0;
+ channel->retrig = 0;
+ if (channel->xm_retrig) {
+ channel->xm_retrig = 0;
+ channel->retrig_tick = 0;
+ }
+ channel->tremor_time &= 127;
+ channel->portamento = 0;
+ channel->toneporta = 0;
+ if (channel->ptm_toneslide) {
+ channel->ptm_last_toneslide = channel->ptm_toneslide;
+ channel->last_toneslide_tick = channel->toneslide_tick;
+ } else
+ channel->ptm_last_toneslide = 0;
+ channel->ptm_toneslide = 0;
+ channel->toneslide_tick = 0;
+ if (channel->playing) {
+ channel->playing->vibrato_n = 0;
+ channel->playing->tremolo_speed = 0;
+ channel->playing->tremolo_depth = 0;
+ channel->playing->panbrello_speed = 0;
+ }
+}
+
+static void reset_effects(DUMB_IT_SIGRENDERER *sigrenderer)
+{
+ int i;
+
+ sigrenderer->globalvolslide = 0;
+ sigrenderer->temposlide = 0;
+
+ for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+ reset_channel_effects(&sigrenderer->channel[i]);
+ }
+}
+
+
+
+static void update_tremor(IT_CHANNEL *channel)
+{
+ if ((channel->tremor_time & 128) && channel->playing) {
+ if (channel->tremor_time == 128)
+ channel->tremor_time = (channel->lastI >> 4) | 192;
+ else if (channel->tremor_time == 192)
+ channel->tremor_time = (channel->lastI & 15) | 128;
+ else
+ channel->tremor_time--;
+ }
+}
+
+
+
+static void it_pickup_loop(DUMB_RESAMPLER *resampler, void *data)
+{
+ resampler->pos -= resampler->end - resampler->start;
+ ((IT_PLAYING *)data)->time_lost += resampler->end - resampler->start;
+}
+
+
+
+static void it_pickup_pingpong_loop(DUMB_RESAMPLER *resampler, void *data)
+{
+ if (resampler->dir < 0) {
+ resampler->pos = (resampler->start << 1) - 1 - resampler->pos;
+ resampler->subpos ^= 65535;
+ resampler->dir = 1;
+ ((IT_PLAYING *)data)->time_lost += (resampler->end - resampler->start) << 1;
+ } else {
+ resampler->pos = (resampler->end << 1) - 1 - resampler->pos;
+ resampler->subpos ^= 65535;
+ resampler->dir = -1;
+ }
+}
+
+
+
+static void it_pickup_stop_at_end(DUMB_RESAMPLER *resampler, void *data)
+{
+ (void)data;
+
+ if (resampler->dir < 0) {
+ resampler->pos = (resampler->start << 1) - 1 - resampler->pos;
+ resampler->subpos ^= 65535;
+ /* By rights, time_lost would be updated here. However, there is no
+ * need at this point; it will not be used.
+ *
+ * ((IT_PLAYING *)data)->time_lost += (resampler->src_end - resampler->src_start) << 1;
+ */
+ resampler->dir = 1;
+ } else
+ resampler->dir = 0;
+}
+
+
+
+static void it_playing_update_resamplers(IT_PLAYING *playing)
+{
+ if ((playing->sample->flags & IT_SAMPLE_SUS_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF)) {
+ playing->resampler.start = playing->sample->sus_loop_start;
+ playing->resampler.end = playing->sample->sus_loop_end;
+ if (playing->resampler.start == playing->resampler.end)
+ playing->resampler.pickup = &it_pickup_stop_at_end;
+ else if (playing->sample->flags & IT_SAMPLE_PINGPONG_SUS_LOOP)
+ playing->resampler.pickup = &it_pickup_pingpong_loop;
+ else
+ playing->resampler.pickup = &it_pickup_loop;
+ } else if (playing->sample->flags & IT_SAMPLE_LOOP) {
+ playing->resampler.start = playing->sample->loop_start;
+ playing->resampler.end = playing->sample->loop_end;
+ if (playing->resampler.start == playing->resampler.end)
+ playing->resampler.pickup = &it_pickup_stop_at_end;
+ else if (playing->sample->flags & IT_SAMPLE_PINGPONG_LOOP)
+ playing->resampler.pickup = &it_pickup_pingpong_loop;
+ else
+ playing->resampler.pickup = &it_pickup_loop;
+ } else {
+ if (playing->sample->flags & IT_SAMPLE_SUS_LOOP)
+ playing->resampler.start = playing->sample->sus_loop_start;
+ else
+ playing->resampler.start = 0;
+ playing->resampler.end = playing->sample->length;
+ playing->resampler.pickup = &it_pickup_stop_at_end;
+ }
+ ASSERT(playing->resampler.pickup_data == playing);
+}
+
+
+
+/* This should be called whenever the sample or sample position changes. */
+static void it_playing_reset_resamplers(IT_PLAYING *playing, long pos)
+{
+ int bits = playing->sample->flags & IT_SAMPLE_16BIT ? 16 : 8;
+ int quality = playing->resampling_quality;
+ int channels = playing->sample->flags & IT_SAMPLE_STEREO ? 2 : 1;
+ if (playing->sample->max_resampling_quality >= 0 && quality > playing->sample->max_resampling_quality)
+ quality = playing->sample->max_resampling_quality;
+ dumb_reset_resampler_n(bits, &playing->resampler, playing->sample->data, channels, pos, 0, 0, quality);
+ playing->resampler.pickup_data = playing;
+ playing->time_lost = 0;
+ playing->flags &= ~IT_PLAYING_DEAD;
+ it_playing_update_resamplers(playing);
+}
+
+static void it_retrigger_note(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel);
+
+/* Should we only be retriggering short samples on XM? */
+
+static void update_retrig(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel)
+{
+ if (channel->xm_retrig) {
+ channel->retrig_tick--;
+ if (channel->retrig_tick <= 0) {
+ if (channel->playing) {
+ it_playing_reset_resamplers(channel->playing, 0);
+#ifdef END_RAMPING
+ channel->playing->declick_stage = 0;
+ channel->playing->declick_volume = 1.f / 256.f;
+#endif
+ } else if (sigrenderer->sigdata->flags & IT_WAS_AN_XM) it_retrigger_note(sigrenderer, channel);
+ channel->retrig_tick = channel->xm_retrig;
+ }
+ } else if (channel->retrig & 0x0F) {
+ channel->retrig_tick--;
+ if (channel->retrig_tick <= 0) {
+ if (channel->retrig < 0x10) {
+ } else if (channel->retrig < 0x20) {
+ channel->volume--;
+ if (channel->volume > 64) channel->volume = 0;
+ } else if (channel->retrig < 0x30) {
+ channel->volume -= 2;
+ if (channel->volume > 64) channel->volume = 0;
+ } else if (channel->retrig < 0x40) {
+ channel->volume -= 4;
+ if (channel->volume > 64) channel->volume = 0;
+ } else if (channel->retrig < 0x50) {
+ channel->volume -= 8;
+ if (channel->volume > 64) channel->volume = 0;
+ } else if (channel->retrig < 0x60) {
+ channel->volume -= 16;
+ if (channel->volume > 64) channel->volume = 0;
+ } else if (channel->retrig < 0x70) {
+ channel->volume <<= 1;
+ channel->volume /= 3;
+ } else if (channel->retrig < 0x80) {
+ channel->volume >>= 1;
+ } else if (channel->retrig < 0x90) {
+ } else if (channel->retrig < 0xA0) {
+ channel->volume++;
+ if (channel->volume > 64) channel->volume = 64;
+ } else if (channel->retrig < 0xB0) {
+ channel->volume += 2;
+ if (channel->volume > 64) channel->volume = 64;
+ } else if (channel->retrig < 0xC0) {
+ channel->volume += 4;
+ if (channel->volume > 64) channel->volume = 64;
+ } else if (channel->retrig < 0xD0) {
+ channel->volume += 8;
+ if (channel->volume > 64) channel->volume = 64;
+ } else if (channel->retrig < 0xE0) {
+ channel->volume += 16;
+ if (channel->volume > 64) channel->volume = 64;
+ } else if (channel->retrig < 0xF0) {
+ channel->volume *= 3;
+ channel->volume >>= 1;
+ if (channel->volume > 64) channel->volume = 64;
+ } else {
+ channel->volume <<= 1;
+ if (channel->volume > 64) channel->volume = 64;
+ }
+ if (channel->playing) {
+ it_playing_reset_resamplers(channel->playing, 0);
+#ifdef END_RAMPING
+ channel->playing->declick_stage = 0;
+ channel->playing->declick_volume = 1.f / 256.f;
+#endif
+ } else if (sigrenderer->sigdata->flags & IT_WAS_AN_XM) it_retrigger_note(sigrenderer, channel);
+ channel->retrig_tick = channel->retrig & 0x0F;
+ }
+ }
+}
+
+
+
+static void update_smooth_effects(DUMB_IT_SIGRENDERER *sigrenderer)
+{
+ int i;
+
+ for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+ IT_CHANNEL *channel = &sigrenderer->channel[i];
+ IT_PLAYING *playing = channel->playing;
+
+ if (playing) {
+ playing->vibrato_time += playing->vibrato_n *
+ (playing->vibrato_speed << 2);
+ playing->tremolo_time += playing->tremolo_speed << 2;
+ playing->panbrello_time += playing->panbrello_speed;
+ if (playing->panbrello_waveform == 3)
+ playing->panbrello_random = (rand() % 129) - 64;
+ }
+ }
+}
+
+
+
+static void update_effects(DUMB_IT_SIGRENDERER *sigrenderer)
+{
+ int i;
+
+ if (sigrenderer->globalvolslide) {
+ sigrenderer->globalvolume += sigrenderer->globalvolslide;
+ if (sigrenderer->globalvolume > 128) {
+ if (sigrenderer->globalvolslide >= 0)
+ sigrenderer->globalvolume = 128;
+ else
+ sigrenderer->globalvolume = 0;
+ }
+ }
+
+ if (sigrenderer->temposlide) {
+ sigrenderer->tempo += sigrenderer->temposlide;
+ if (sigrenderer->tempo < 32) {
+ if (sigrenderer->temposlide >= 0)
+ sigrenderer->tempo = 255;
+ else
+ sigrenderer->tempo = 32;
+ }
+ }
+
+ for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+ IT_CHANNEL *channel = &sigrenderer->channel[i];
+ IT_PLAYING *playing = channel->playing;
+
+ if (channel->xm_volslide) {
+ channel->volume += channel->xm_volslide;
+ if (channel->volume > 64) {
+ if (channel->xm_volslide >= 0)
+ channel->volume = 64;
+ else
+ channel->volume = 0;
+ }
+ }
+
+ if (channel->volslide) {
+ channel->volume += channel->volslide;
+ if (channel->volume > 64) {
+ if (channel->volslide >= 0)
+ channel->volume = 64;
+ else
+ channel->volume = 0;
+ }
+ }
+
+ if (channel->panslide) {
+ if (sigrenderer->sigdata->flags & IT_WAS_AN_XM) {
+ if (IT_IS_SURROUND(channel->pan))
+ {
+ channel->pan = 32;
+ channel->truepan = 32 + 128 * 64;
+ }
+ if (channel->panslide == -128)
+ channel->truepan = 32;
+ else
+ channel->truepan = MID(32, channel->truepan + channel->panslide*64, 32+255*64);
+ } else {
+ if (IT_IS_SURROUND(channel->pan))
+ {
+ channel->pan = 32;
+ }
+ channel->pan += channel->panslide;
+ if (channel->pan > 64) {
+ if (channel->panslide >= 0)
+ channel->pan = 64;
+ else
+ channel->pan = 0;
+ }
+ channel->truepan = channel->pan << IT_ENVELOPE_SHIFT;
+ }
+ }
+
+ if (channel->channelvolslide) {
+ channel->channelvolume += channel->channelvolslide;
+ if (channel->channelvolume > 64) {
+ if (channel->channelvolslide >= 0)
+ channel->channelvolume = 64;
+ else
+ channel->channelvolume = 0;
+ }
+ if (channel->playing)
+ channel->playing->channel_volume = channel->channelvolume;
+ }
+
+ update_tremor(channel);
+
+ channel->arpeggio = (channel->arpeggio << 4) | (channel->arpeggio >> 8);
+ channel->arpeggio &= 0xFFF;
+
+ update_retrig(sigrenderer, channel);
+
+ if (playing) {
+ playing->slide += channel->portamento;
+
+ if (channel->ptm_toneslide) {
+ if (--channel->toneslide_tick == 0) {
+ channel->toneslide_tick = channel->ptm_toneslide;
+ playing->note += channel->toneslide;
+ if (playing->note >= 120) {
+ if (channel->toneslide < 0) playing->note = 0;
+ else playing->note = 119;
+ }
+ channel->note = channel->truenote = playing->note;
+ if (channel->toneslide_retrig) {
+ it_playing_reset_resamplers(playing, 0);
+#ifdef END_RAMPING
+ playing->declick_stage = 0;
+ playing->declick_volume = 1.f / 256.f;
+#endif
+ }
+ }
+ }
+
+ if (sigrenderer->sigdata->flags & IT_LINEAR_SLIDES) {
+ if (channel->toneporta && channel->destnote < 120) {
+ int currpitch = ((playing->note - 60) << 8) + playing->slide;
+ int destpitch = (channel->destnote - 60) << 8;
+ if (currpitch > destpitch) {
+ currpitch -= channel->toneporta;
+ if (currpitch < destpitch) {
+ currpitch = destpitch;
+ channel->destnote = IT_NOTE_OFF;
+ }
+ } else if (currpitch < destpitch) {
+ currpitch += channel->toneporta;
+ if (currpitch > destpitch) {
+ currpitch = destpitch;
+ channel->destnote = IT_NOTE_OFF;
+ }
+ }
+ playing->slide = currpitch - ((playing->note - 60) << 8);
+ }
+ } else {
+ if (channel->toneporta && channel->destnote < 120) {
+ float amiga_multiplier = playing->sample->C5_speed * (1.0f / AMIGA_DIVISOR);
+
+ float deltanote = (float)pow(DUMB_SEMITONE_BASE, 60 - playing->note);
+ /* deltanote is 1.0 for C-5, 0.5 for C-6, etc. */
+
+ float deltaslid = deltanote - playing->slide * amiga_multiplier;
+
+ float destdelta = (float)pow(DUMB_SEMITONE_BASE, 60 - channel->destnote);
+ if (deltaslid < destdelta) {
+ playing->slide -= channel->toneporta;
+ deltaslid = deltanote - playing->slide * amiga_multiplier;
+ if (deltaslid > destdelta) {
+ playing->note = channel->destnote;
+ playing->slide = 0;
+ channel->destnote = IT_NOTE_OFF;
+ }
+ } else {
+ playing->slide += channel->toneporta;
+ deltaslid = deltanote - playing->slide * amiga_multiplier;
+ if (deltaslid < destdelta) {
+ playing->note = channel->destnote;
+ playing->slide = 0;
+ channel->destnote = IT_NOTE_OFF;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ update_smooth_effects(sigrenderer);
+}
+
+
+static void it_note_off(IT_PLAYING *playing);
+
+// This function should be renamed; it doesn't do the 'Update Pattern Variables' operation ittech.txt describes
+/* Returns 1 if a pattern loop is happening. */
+static int update_pattern_variables(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry)
+{
+ IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel];
+
+ if (entry->mask & IT_ENTRY_EFFECT) {
+ switch (entry->effect) {
+ case IT_JUMP_TO_ORDER:
+ /* XXX jump and break in same row */
+ if ( ( ( sigrenderer->processrow | 0xC00 ) == 0xFFFE ) &&
+ ! ( sigrenderer->processrow & 0x800 ) ) {
+ sigrenderer->processrow = 0xFFFE & ~0xC00;
+ } else {
+ sigrenderer->breakrow = 0;
+ sigrenderer->processrow = 0xFFFE & ~0x400;
+ }
+ sigrenderer->processorder = entry->effectvalue - 1;
+ break;
+
+ case IT_S:
+ {
+ unsigned char effectvalue = entry->effectvalue;
+ if (effectvalue == 0)
+ effectvalue = channel->lastS;
+ channel->lastS = effectvalue;
+ switch (effectvalue >> 4) {
+ case IT_S_PATTERN_LOOP:
+ {
+ unsigned char v = effectvalue & 15;
+ if (v == 0) {
+#ifdef BIT_ARRAY_BULLSHIT
+ if (!channel->played_patjump)
+ channel->played_patjump = bit_array_create(256);
+ else {
+ //if (channel->played_patjump_order != sigrenderer->order)
+ bit_array_reset(channel->played_patjump);
+ }
+ channel->played_patjump_order = sigrenderer->order;
+#endif
+ channel->pat_loop_row = sigrenderer->processrow;
+ } else {
+ if (channel->pat_loop_count == 0) {
+#ifdef BIT_ARRAY_BULLSHIT
+ /* wft, uninitialized and no start marker yet... */
+ if (channel->played_patjump_order == 0xFFFE) {
+ int n;
+ bit_array_destroy(channel->played_patjump);
+ channel->played_patjump = bit_array_create(256);
+ for (n = 0; n < 256; n++)
+ bit_array_clear(sigrenderer->played, sigrenderer->order * 256 + n);
+ channel->played_patjump_order = sigrenderer->order;
+ } else if (channel->played_patjump_order == sigrenderer->order) {
+ bit_array_set(channel->played_patjump, sigrenderer->row);
+ bit_array_mask(sigrenderer->played, channel->played_patjump, channel->played_patjump_order * 256);
+ bit_array_reset(channel->played_patjump);
+ }
+#endif
+ channel->pat_loop_count = v;
+ sigrenderer->breakrow = channel->pat_loop_row;
+ if ((sigrenderer->sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM) {
+ /* For XM files, if a loop occurs by itself, keep breakrow set for when the pattern ends - fun bug in FT2! */
+ if ((sigrenderer->processrow|0xC00) < 0xFFFE) {
+ /* Infinite pattern loops are possible, so we check whether the pattern loop we're hitting now is earlier than the last one we hit. */
+ if (sigrenderer->processrow < channel->pat_loop_end_row)
+ sigrenderer->processorder = 0xFFFE; /* suspect infinite loop, so trigger loop callback */
+ else
+ sigrenderer->processorder = 0xFFFF; /* don't trigger loop callback */
+ channel->pat_loop_end_row = sigrenderer->processrow;
+ sigrenderer->processrow = 0xFFFF; /* special case: don't reset breakrow or pat_loop_end_row */
+ }
+ } else {
+ /* IT files do this regardless of other flow control effects seen here. */
+ sigrenderer->processorder = 0xFFFF; /* special case: don't trigger loop callback */
+ sigrenderer->processrow = 0xFFFE;
+ }
+ return 1;
+ } else if (--channel->pat_loop_count) {
+#ifdef BIT_ARRAY_BULLSHIT
+ if (channel->played_patjump_order == sigrenderer->order) {
+ bit_array_set(channel->played_patjump, sigrenderer->row);
+ bit_array_mask(sigrenderer->played, channel->played_patjump, channel->played_patjump_order * 256);
+ bit_array_reset(channel->played_patjump);
+ }
+#endif
+ sigrenderer->breakrow = channel->pat_loop_row;
+ if ((sigrenderer->sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM) {
+ /* For XM files, if a loop occurs by itself, keep breakrow set for when the pattern ends - fun bug in FT2! */
+ if ((sigrenderer->processrow|0xC00) < 0xFFFE) {
+ /* Infinite pattern loops are possible, so we check whether the pattern loop we're hitting now is earlier than the last one we hit. */
+ if (sigrenderer->processrow < channel->pat_loop_end_row)
+ sigrenderer->processorder = 0xFFFE; /* suspect infinite loop, so trigger loop callback */
+ else
+ sigrenderer->processorder = 0xFFFF; /* don't trigger loop callback */
+ channel->pat_loop_end_row = sigrenderer->processrow;
+ sigrenderer->processrow = 0xFFFF; /* special case: don't reset breakrow or pat_loop_end_row */
+ }
+ } else {
+ /* IT files do this regardless of other flow control effects seen here. */
+ sigrenderer->processorder = 0xFFFF; /* special case: don't trigger loop callback */
+ sigrenderer->processrow = 0xFFFE;
+ }
+ return 1;
+ } else if ((sigrenderer->sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM) {
+ channel->pat_loop_end_row = 0;
+ // TODO
+ /* Findings:
+ - If a pattern loop completes successfully, and then the pattern terminates, then the next pattern will start on the row corresponding to the E60.
+ - If a pattern loop doesn't do any loops, and then the pattern terminates, then the next pattern will start on the first row.
+ - If a break appears to the left of the pattern loop, it jumps into the relevant position in the next pattern, and that's it.
+ - If a break appears to the right of the pattern loop, it jumps to the start of the next pattern, and that's it.
+ - If we jump, then effect a loop using an old E60, and then the pattern ends, the next pattern starts on the row corresponding to the E60.
+ - Theory: breakrow is not cleared when it's a pattern loop effect!
+ */
+ //if (sigrenderer->processrow < 0xFFFE) // I have no idea if this is correct or not - FT2 is so weird :(
+ // sigrenderer->breakrow = channel->pat_loop_row; /* emulate bug in FT2 */
+ } else
+ channel->pat_loop_row = sigrenderer->processrow + 1;
+#ifdef BIT_ARRAY_BULLSHIT
+ /*channel->played_patjump_order |= 0x8000;*/
+ if (channel->played_patjump_order == sigrenderer->order) {
+ bit_array_destroy(channel->played_patjump);
+ channel->played_patjump = 0;
+ channel->played_patjump_order = 0xFFFE;
+ }
+ bit_array_clear(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row);
+#endif
+ }
+ }
+ break;
+ case IT_S_PATTERN_DELAY:
+ sigrenderer->rowcount = 1 + (effectvalue & 15);
+ break;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+
+/* This function guarantees that channel->sample will always be valid if it
+ * is nonzero. In other words, to check if it is valid, simply check if it is
+ * nonzero.
+ */
+static void instrument_to_sample(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel)
+{
+ if (sigdata->flags & IT_USE_INSTRUMENTS) {
+ if (channel->instrument >= 1 && channel->instrument <= sigdata->n_instruments) {
+ if (channel->note < 120) {
+ channel->sample = sigdata->instrument[channel->instrument-1].map_sample[channel->note];
+ channel->truenote = sigdata->instrument[channel->instrument-1].map_note[channel->note];
+ } else
+ channel->sample = 0;
+ } else
+ channel->sample = 0;
+ } else {
+ channel->sample = channel->instrument;
+ channel->truenote = channel->note;
+ }
+ if (!(channel->sample >= 1 && channel->sample <= sigdata->n_samples && (sigdata->sample[channel->sample-1].flags & IT_SAMPLE_EXISTS) && sigdata->sample[channel->sample-1].C5_speed))
+ channel->sample = 0;
+}
+
+
+
+static void fix_sample_looping(IT_PLAYING *playing)
+{
+ if ((playing->sample->flags & (IT_SAMPLE_LOOP | IT_SAMPLE_SUS_LOOP)) ==
+ (IT_SAMPLE_LOOP | IT_SAMPLE_SUS_LOOP)) {
+ if (playing->resampler.dir < 0) {
+ playing->resampler.pos = (playing->sample->sus_loop_end << 1) - 1 - playing->resampler.pos;
+ playing->resampler.subpos ^= 65535;
+ playing->resampler.dir = 1;
+ }
+
+ playing->resampler.pos += playing->time_lost;
+ // XXX what
+ playing->time_lost = 0;
+ }
+}
+
+
+
+static void it_compatible_gxx_retrigger(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel)
+{
+ int flags = 0;
+ if (channel->sample) {
+ if (sigdata->flags & IT_USE_INSTRUMENTS) {
+ if (channel->playing->env_instrument == &sigdata->instrument[channel->instrument-1]) {
+ if (channel->playing->env_instrument->volume_envelope.flags & IT_ENVELOPE_CARRY)
+ flags |= 1;
+ if (channel->playing->env_instrument->pan_envelope.flags & IT_ENVELOPE_CARRY)
+ flags |= 2;
+ if (channel->playing->env_instrument->pitch_envelope.flags & IT_ENVELOPE_CARRY)
+ flags |= 4;
+ }
+ }
+ }
+ if (!(flags & 1)) {
+ channel->playing->volume_envelope.next_node = 0;
+ channel->playing->volume_envelope.tick = 0;
+ }
+ if (!(flags & 2)) {
+ channel->playing->pan_envelope.next_node = 0;
+ channel->playing->pan_envelope.tick = 0;
+ }
+ if (!(flags & 4)) {
+ channel->playing->pitch_envelope.next_node = 0;
+ channel->playing->pitch_envelope.tick = 0;
+ }
+ channel->playing->fadeoutcount = 1024;
+ // Should we remove IT_PLAYING_BACKGROUND? Test with sample with sustain loop...
+ channel->playing->flags &= ~(IT_PLAYING_BACKGROUND | IT_PLAYING_SUSTAINOFF | IT_PLAYING_FADING | IT_PLAYING_DEAD);
+ it_playing_update_resamplers(channel->playing);
+
+ if (!flags && channel->sample)
+ if (sigdata->flags & IT_USE_INSTRUMENTS)
+ channel->playing->env_instrument = &sigdata->instrument[channel->instrument-1];
+}
+
+
+
+static void it_note_off(IT_PLAYING *playing)
+{
+ if (playing) {
+ playing->enabled_envelopes |= IT_ENV_VOLUME;
+ playing->flags |= IT_PLAYING_BACKGROUND | IT_PLAYING_SUSTAINOFF;
+ fix_sample_looping(playing);
+ it_playing_update_resamplers(playing);
+ if (playing->instrument)
+ if ((playing->instrument->volume_envelope.flags & (IT_ENVELOPE_ON | IT_ENVELOPE_LOOP_ON)) != IT_ENVELOPE_ON)
+ playing->flags |= IT_PLAYING_FADING;
+ }
+}
+
+
+
+static void xm_note_off(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel)
+{
+ if (channel->playing) {
+ if (channel->instrument > sigdata->n_instruments ||
+ !(sigdata->instrument[channel->instrument-1].volume_envelope.flags & IT_ENVELOPE_ON))
+ //if (!(entry->mask & IT_ENTRY_INSTRUMENT))
+ // dunno what that was there for ...
+ channel->volume = 0;
+ channel->playing->flags |= IT_PLAYING_SUSTAINOFF | IT_PLAYING_FADING;
+ it_playing_update_resamplers(channel->playing);
+ }
+}
+
+
+
+static void it_retrigger_note(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel)
+{
+ IT_PLAYING_ENVELOPE volume_envelope;
+ IT_PLAYING_ENVELOPE pan_envelope;
+ IT_PLAYING_ENVELOPE pitch_envelope;
+
+ DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata;
+ unsigned char nna;
+ int i, flags = 0;
+
+ if (channel->playing) {
+#ifdef INVALID_NOTES_CAUSE_NOTE_CUT
+ if (channel->note == IT_NOTE_OFF)
+ nna = NNA_NOTE_OFF;
+ else if (channel->note >= 120 || !channel->playing->instrument || (channel->playing->flags & IT_PLAYING_DEAD))
+ nna = NNA_NOTE_CUT;
+ else if (channel->new_note_action != 0xFF)
+ {
+ nna = channel->new_note_action;
+ }
+ else
+ nna = channel->playing->instrument->new_note_action;
+#else
+ if (channel->note == IT_NOTE_CUT)
+ nna = NNA_NOTE_CUT;
+ if (channel->note >= 120)
+ nna = NNA_NOTE_OFF;
+ else if (!channel->playing->instrument || (channel->playing->flags & IT_PLAYING_DEAD))
+ nna = NNA_NOTE_CUT;
+ else if (channel->new_note_action != 0xFF)
+ {
+ nna = channel->new_note_action;
+ }
+ else
+ nna = channel->playing->instrument->new_note_action;
+#endif
+
+ if ((sigdata->flags & IT_USE_INSTRUMENTS) && (channel->playing->enabled_envelopes) && channel->playing->instnum == channel->instrument) {
+ IT_PLAYING * playing = channel->playing;
+ IT_INSTRUMENT * inst = &sigdata->instrument[channel->instrument-1];
+ if ((playing->enabled_envelopes & IT_ENV_VOLUME) && (inst->volume_envelope.flags & IT_ENVELOPE_CARRY)) {
+ flags |= 1;
+ volume_envelope = playing->volume_envelope;
+ }
+ if ((playing->enabled_envelopes & IT_ENV_PANNING) && (inst->pan_envelope.flags & IT_ENVELOPE_CARRY)) {
+ flags |= 2;
+ pan_envelope = playing->pan_envelope;
+ }
+ if ((playing->enabled_envelopes & IT_ENV_PITCH) && (inst->pitch_envelope.flags & IT_ENVELOPE_CARRY)) {
+ flags |= 4;
+ pitch_envelope = playing->pitch_envelope;
+ }
+ }
+
+ switch (nna) {
+ case NNA_NOTE_CUT:
+#ifdef RAMP_DOWN
+ channel->playing->declick_stage = 2;
+#else
+ free(channel->playing);
+ channel->playing = NULL;
+#endif
+ break;
+ case NNA_NOTE_OFF:
+ it_note_off(channel->playing);
+ break;
+ case NNA_NOTE_FADE:
+ channel->playing->flags |= IT_PLAYING_BACKGROUND | IT_PLAYING_FADING;
+ break;
+ }
+ }
+
+ channel->new_note_action = 0xFF;
+
+ if (channel->sample == 0 || channel->note >= 120)
+ return;
+
+ channel->destnote = IT_NOTE_OFF;
+
+ if (channel->playing) {
+ for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+ if (!sigrenderer->playing[i]) {
+ sigrenderer->playing[i] = channel->playing;
+ channel->playing = NULL;
+ break;
+ }
+ }
+
+ if (sigrenderer->sigdata->flags & IT_USE_INSTRUMENTS)
+ {
+ for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+ IT_PLAYING * playing = sigrenderer->playing[i];
+ if (playing && playing->channel == channel && playing->instrument->dup_check_type) {
+ int match = 0;
+ switch (playing->instrument->dup_check_type)
+ {
+ case DCT_NOTE:
+ match = (channel->truenote == playing->note);
+ case DCT_SAMPLE:
+ match = match && (channel->sample == playing->sampnum);
+ case DCT_INSTRUMENT:
+ match = match && (channel->instrument == playing->instnum);
+ break;
+ }
+
+ if (match)
+ {
+ switch (playing->instrument->dup_check_action)
+ {
+ case DCA_NOTE_CUT:
+#ifdef RAMP_DOWN
+ playing->declick_stage = 2;
+#else
+ free(playing);
+ sigrenderer->playing[i] = NULL;
+#endif
+ if (channel->playing == playing) channel->playing = NULL;
+ break;
+ case DCA_NOTE_OFF:
+ if (!(playing->flags & IT_PLAYING_SUSTAINOFF))
+ it_note_off(playing);
+ break;
+ case DCA_NOTE_FADE:
+ playing->flags |= IT_PLAYING_BACKGROUND | IT_PLAYING_FADING;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+/** WARNING - come up with some more heuristics for replacing old notes */
+#if 0
+ if (channel->playing) {
+ for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+ if (sigrenderer->playing[i]->flags & IT_PLAYING_BACKGROUND) {
+ write_seqtime();
+ sequence_c(SEQUENCE_STOP_SIGNAL);
+ sequence_c(i);
+ channel->VChannel = &module->VChannel[i];
+ break;
+ }
+ }
+ }
+#endif
+ }
+
+ if (channel->playing)
+ free(channel->playing);
+
+ channel->playing = malloc(sizeof(*channel->playing));
+
+ if (!channel->playing)
+ return;
+
+ if (!flags && sigdata->flags & IT_USE_INSTRUMENTS) {
+ for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+ IT_PLAYING * playing = sigrenderer->playing[i];
+ if (playing && (playing->enabled_envelopes) && playing->instnum == channel->instrument) {
+ IT_INSTRUMENT * inst = &sigdata->instrument[channel->instrument-1];
+ if ((playing->enabled_envelopes & IT_ENV_VOLUME) && (inst->volume_envelope.flags & IT_ENVELOPE_CARRY)) {
+ flags |= 1;
+ volume_envelope = playing->volume_envelope;
+ }
+ if ((playing->enabled_envelopes & IT_ENV_PANNING) && (inst->pan_envelope.flags & IT_ENVELOPE_CARRY)) {
+ flags |= 2;
+ pan_envelope = playing->pan_envelope;
+ }
+ if ((playing->enabled_envelopes & IT_ENV_PITCH) && (inst->pitch_envelope.flags & IT_ENVELOPE_CARRY)) {
+ flags |= 4;
+ pitch_envelope = playing->pitch_envelope;
+ }
+ break;
+ }
+ }
+ }
+
+ channel->playing->flags = 0;
+ channel->playing->resampling_quality = sigrenderer->resampling_quality;
+ channel->playing->channel = channel;
+ channel->playing->sample = &sigdata->sample[channel->sample-1];
+ if (sigdata->flags & IT_USE_INSTRUMENTS)
+ channel->playing->instrument = &sigdata->instrument[channel->instrument-1];
+ else
+ channel->playing->instrument = NULL;
+ channel->playing->env_instrument = channel->playing->instrument;
+ channel->playing->sampnum = channel->sample;
+ channel->playing->instnum = channel->instrument;
+#ifdef END_RAMPING
+ channel->playing->declick_stage = 0;
+ channel->playing->declick_volume = 1.f / 256.f;
+#endif
+ channel->playing->channel_volume = channel->channelvolume;
+ channel->playing->ramp_volume[0] = 31337.0; /* special */
+ channel->playing->note = channel->truenote;
+ channel->playing->enabled_envelopes = 0;
+ channel->playing->volume_offset = 0;
+ channel->playing->panning_offset = 0;
+ //channel->playing->output = channel->output;
+ if (sigdata->flags & IT_USE_INSTRUMENTS) {
+ IT_PLAYING * playing = channel->playing;
+ IT_INSTRUMENT * instrument = playing->instrument;
+ if (instrument->volume_envelope.flags & IT_ENVELOPE_ON) playing->enabled_envelopes |= IT_ENV_VOLUME;
+ if (instrument->pan_envelope.flags & IT_ENVELOPE_ON) playing->enabled_envelopes |= IT_ENV_PANNING;
+ if (instrument->pitch_envelope.flags & IT_ENVELOPE_ON) playing->enabled_envelopes |= IT_ENV_PITCH;
+ if (instrument->random_volume) playing->volume_offset = (rand() % (instrument->random_volume * 2 + 1)) - instrument->random_volume;
+ if (instrument->random_pan) playing->panning_offset = (rand() % (instrument->random_pan * 2 + 1)) - instrument->random_pan;
+ //if (instrument->output) playing->output = instrument->output;
+ }
+ channel->playing->filter_cutoff = 127;
+ channel->playing->filter_resonance = 0;
+ channel->playing->true_filter_cutoff = 127 << 8;
+ channel->playing->true_filter_resonance = 0;
+ channel->playing->vibrato_speed = 0;
+ channel->playing->vibrato_depth = 0;
+ channel->playing->vibrato_n = 0;
+ channel->playing->vibrato_time = 0;
+ channel->playing->vibrato_waveform = channel->vibrato_waveform;
+ channel->playing->tremolo_speed = 0;
+ channel->playing->tremolo_depth = 0;
+ channel->playing->tremolo_time = 0;
+ channel->playing->tremolo_waveform = channel->tremolo_waveform;
+ channel->playing->panbrello_speed = 0;
+ channel->playing->panbrello_depth = 0;
+ channel->playing->panbrello_time = 0;
+ channel->playing->panbrello_waveform = channel->panbrello_waveform;
+ channel->playing->panbrello_random = 0;
+ channel->playing->sample_vibrato_time = 0;
+ channel->playing->sample_vibrato_waveform = channel->playing->sample->vibrato_waveform;
+ channel->playing->sample_vibrato_depth = 0;
+ channel->playing->slide = 0;
+ channel->playing->finetune = channel->playing->sample->finetune;
+
+ if (flags & 1) {
+ channel->playing->volume_envelope = volume_envelope;
+ } else {
+ channel->playing->volume_envelope.next_node = 0;
+ channel->playing->volume_envelope.tick = 0;
+ }
+ if (flags & 2) {
+ channel->playing->pan_envelope = pan_envelope;
+ } else {
+ channel->playing->pan_envelope.next_node = 0;
+ channel->playing->pan_envelope.tick = 0;
+ }
+ if (flags & 4) {
+ channel->playing->pitch_envelope = pitch_envelope;
+ } else {
+ channel->playing->pitch_envelope.next_node = 0;
+ channel->playing->pitch_envelope.tick = 0;
+ }
+ channel->playing->fadeoutcount = 1024;
+ it_reset_filter_state(&channel->playing->filter_state[0]);
+ it_reset_filter_state(&channel->playing->filter_state[1]);
+ it_playing_reset_resamplers(channel->playing, 0);
+
+ /** WARNING - is everything initialised? */
+}
+
+
+
+static void get_default_volpan(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel)
+{
+ if (channel->sample == 0)
+ return;
+
+ channel->volume = sigdata->sample[channel->sample-1].default_volume;
+
+ if (sigdata->flags & IT_WAS_AN_XM) {
+ if (!(sigdata->flags & IT_WAS_A_MOD))
+ channel->truepan = 32 + sigdata->sample[channel->sample-1].default_pan*64;
+ return;
+ }
+
+ {
+ int pan = sigdata->sample[channel->sample-1].default_pan;
+ if (pan >= 128 && pan <= 192) {
+ channel->pan = pan - 128;
+ return;
+ }
+ }
+
+ if (sigdata->flags & IT_USE_INSTRUMENTS) {
+ IT_INSTRUMENT *instrument = &sigdata->instrument[channel->instrument-1];
+ if (instrument->default_pan <= 64)
+ channel->pan = instrument->default_pan;
+ if (instrument->filter_cutoff >= 128)
+ channel->filter_cutoff = instrument->filter_cutoff - 128;
+ if (instrument->filter_resonance >= 128)
+ channel->filter_resonance = instrument->filter_resonance - 128;
+ }
+}
+
+
+
+static void get_true_pan(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel)
+{
+ channel->truepan = channel->pan << IT_ENVELOPE_SHIFT;
+
+ if (channel->sample && !IT_IS_SURROUND_SHIFTED(channel->truepan) && (sigdata->flags & IT_USE_INSTRUMENTS)) {
+ IT_INSTRUMENT *instrument = &sigdata->instrument[channel->instrument-1];
+ int truepan = channel->truepan;
+ truepan += (channel->note - instrument->pp_centre) * instrument->pp_separation << (IT_ENVELOPE_SHIFT - 3);
+ channel->truepan = (unsigned short)MID(0, truepan, 64 << IT_ENVELOPE_SHIFT);
+ }
+}
+
+
+
+static void post_process_it_volpan(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry)
+{
+ IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel];
+
+ if (entry->mask & IT_ENTRY_VOLPAN) {
+ if (entry->volpan <= 84) {
+ /* Volume */
+ /* Fine volume slide up */
+ /* Fine volume slide down */
+ } else if (entry->volpan <= 94) {
+ /* Volume slide up */
+ unsigned char v = entry->volpan - 85;
+ if (v == 0)
+ v = channel->lastvolslide;
+ channel->lastvolslide = v;
+ /* = effect Dx0 where x == entry->volpan - 85 */
+ channel->volslide = v;
+ } else if (entry->volpan <= 104) {
+ /* Volume slide down */
+ unsigned char v = entry->volpan - 95;
+ if (v == 0)
+ v = channel->lastvolslide;
+ channel->lastvolslide = v;
+ /* = effect D0x where x == entry->volpan - 95 */
+ channel->volslide = -v;
+ } else if (entry->volpan <= 114) {
+ /* Portamento down */
+ unsigned char v = (entry->volpan - 105) << 2;
+ if (v == 0)
+ v = channel->lastEF;
+ channel->lastEF = v;
+ channel->portamento -= v << 4;
+ } else if (entry->volpan <= 124) {
+ /* Portamento up */
+ unsigned char v = (entry->volpan - 115) << 2;
+ if (v == 0)
+ v = channel->lastEF;
+ channel->lastEF = v;
+ channel->portamento += v << 4;
+ } else if (entry->volpan <= 202) {
+ /* Pan */
+ /* Tone Portamento */
+ } else if (entry->volpan <= 212) {
+ /* Vibrato */
+ /* This is unaffected by IT_OLD_EFFECTS. However, if v == 0, then any doubling of depth that happened before (with Hxy in the effect column) will be preserved. */
+ unsigned char v = entry->volpan - 203;
+ if (v == 0)
+ v = channel->lastHdepth;
+ else {
+ v <<= 2;
+ channel->lastHdepth = v;
+ }
+ if (channel->playing) {
+ channel->playing->vibrato_speed = channel->lastHspeed;
+ channel->playing->vibrato_depth = v;
+ channel->playing->vibrato_n++;
+ }
+ }
+ }
+}
+
+
+
+static void it_send_midi(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel, unsigned char midi_byte)
+{
+ if (sigrenderer->callbacks->midi)
+ if ((*sigrenderer->callbacks->midi)(sigrenderer->callbacks->midi_data, channel - sigrenderer->channel, midi_byte))
+ return;
+
+ switch (channel->midi_state) {
+ case 4: /* Ready to receive resonance parameter */
+ if (midi_byte < 0x80) channel->filter_resonance = midi_byte;
+ channel->midi_state = 0;
+ break;
+ case 3: /* Ready to receive cutoff parameter */
+ if (midi_byte < 0x80) channel->filter_cutoff = midi_byte;
+ channel->midi_state = 0;
+ break;
+ case 2: /* Ready for byte specifying which parameter will follow */
+ if (midi_byte == 0) /* Cutoff */
+ channel->midi_state = 3;
+ else if (midi_byte == 1) /* Resonance */
+ channel->midi_state = 4;
+ else
+ channel->midi_state = 0;
+ break;
+ default: /* Counting initial F0 bytes */
+ switch (midi_byte) {
+ case 0xF0:
+ channel->midi_state++;
+ break;
+ case 0xFA:
+ case 0xFC:
+ case 0xFF:
+ /* Reset filter parameters for all channels */
+ {
+ int i;
+ for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+ sigrenderer->channel[i].filter_cutoff = 127;
+ sigrenderer->channel[i].filter_resonance = 0;
+ //// should we be resetting channel[i].playing->filter_* here?
+ }
+ }
+ /* Fall through */
+ default:
+ channel->midi_state = 0;
+ break;
+ }
+ }
+}
+
+
+
+static void xm_envelope_calculate_value(IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe)
+{
+ if (pe->next_node <= 0)
+ pe->value = envelope->node_y[0] << IT_ENVELOPE_SHIFT;
+ else if (pe->next_node >= envelope->n_nodes)
+ pe->value = envelope->node_y[envelope->n_nodes-1] << IT_ENVELOPE_SHIFT;
+ else {
+ int ys = envelope->node_y[pe->next_node-1] << IT_ENVELOPE_SHIFT;
+ int ts = envelope->node_t[pe->next_node-1];
+ int te = envelope->node_t[pe->next_node];
+
+ if (ts == te)
+ pe->value = ys;
+ else {
+ int ye = envelope->node_y[pe->next_node] << IT_ENVELOPE_SHIFT;
+ int t = pe->tick;
+
+ pe->value = ys + (ye - ys) * (t - ts) / (te - ts);
+ }
+ }
+}
+
+
+
+extern const char xm_convert_vibrato[];
+
+/* Returns 1 if a callback caused termination of playback. */
+static int process_effects(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry, int ignore_cxx)
+{
+ DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata;
+
+ IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel];
+
+ if (entry->mask & IT_ENTRY_EFFECT) {
+ switch (entry->effect) {
+/*
+Notes about effects (as compared to other module formats)
+
+C This is now in *HEX*. (Used to be in decimal in ST3)
+E/F/G/H/U You need to check whether the song uses Amiga/Linear slides.
+H/U Vibrato in Impulse Tracker is two times finer than in
+ any other tracker and is updated EVERY tick.
+ If "Old Effects" is *ON*, then the vibrato is played in the
+ normal manner (every non-row tick and normal depth)
+E/F/G These commands ALL share the same memory.
+Oxx Offsets to samples are to the 'xx00th' SAMPLE. (ie. for
+ 16 bit samples, the offset is xx00h*2)
+ Oxx past the sample end will be ignored, unless "Old Effects"
+ is ON, in which case the Oxx will play from the end of the
+ sample.
+Yxy This uses a table 4 times larger (hence 4 times slower) than
+ vibrato or tremelo. If the waveform is set to random, then
+ the 'speed' part of the command is interpreted as a delay.
+*/
+ case IT_SET_SPEED:
+ if (entry->effectvalue)
+ {
+ /*if (entry->effectvalue == 255)
+ if (sigrenderer->callbacks->xm_speed_zero && (*sigrenderer->callbacks->xm_speed_zero)(sigrenderer->callbacks->xm_speed_zero_data))
+ return 1;*/
+ sigrenderer->tick = sigrenderer->speed = entry->effectvalue;
+ }
+ else if ((sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM) {
+#ifdef BIT_ARRAY_BULLSHIT
+ bit_array_set(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row);
+#endif
+ sigrenderer->speed = 0;
+ if (sigrenderer->callbacks->xm_speed_zero && (*sigrenderer->callbacks->xm_speed_zero)(sigrenderer->callbacks->xm_speed_zero_data))
+ return 1;
+ }
+ break;
+
+ case IT_BREAK_TO_ROW:
+ if (ignore_cxx) break;
+ sigrenderer->breakrow = entry->effectvalue;
+ /* XXX jump and break on the same row */
+ if ( ( ( sigrenderer->processrow | 0xC00 ) == 0xFFFE ) &&
+ ! ( sigrenderer->processrow & 0x400 ) ) {
+ sigrenderer->processrow = 0xFFFE & ~0xC00;
+ } else {
+ sigrenderer->processorder = sigrenderer->order;
+ sigrenderer->processrow = 0xFFFE & ~0x800;
+ }
+ break;
+
+ case IT_VOLSLIDE_VIBRATO:
+ if (channel->playing) {
+ channel->playing->vibrato_speed = channel->lastHspeed;
+ channel->playing->vibrato_depth = channel->lastHdepth;
+ channel->playing->vibrato_n++;
+ }
+ /* Fall through and process volume slide. */
+ case IT_VOLUME_SLIDE:
+ case IT_VOLSLIDE_TONEPORTA:
+ /* The tone portamento component is handled elsewhere. */
+ {
+ unsigned char v = entry->effectvalue;
+ if (!(sigdata->flags & IT_WAS_A_MOD)) {
+ if (v == 0)
+ v = channel->lastDKL;
+ channel->lastDKL = v;
+ }
+ if ((v & 0x0F) == 0) { /* Dx0 */
+ channel->volslide = v >> 4;
+ if (channel->volslide == 15 && !(sigdata->flags & IT_WAS_AN_XM)) {
+ channel->volume += 15;
+ if (channel->volume > 64) channel->volume = 64;
+ }
+ } else if ((v & 0xF0) == 0) { /* D0x */
+ channel->volslide = -v;
+ if (channel->volslide == -15 && !(sigdata->flags & IT_WAS_AN_XM)) {
+ channel->volume -= 15;
+ if (channel->volume > 64) channel->volume = 0;
+ }
+ } else if ((v & 0x0F) == 0x0F) { /* DxF */
+ channel->volume += v >> 4;
+ if (channel->volume > 64) channel->volume = 64;
+ } else if ((v & 0xF0) == 0xF0) { /* DFx */
+ channel->volume -= v & 15;
+ if (channel->volume > 64) channel->volume = 0;
+ }
+ }
+ break;
+ case IT_XM_FINE_VOLSLIDE_DOWN:
+ {
+ unsigned char v = entry->effectvalue;
+ if (v == 0)
+ v = channel->xm_lastEB;
+ channel->xm_lastEB = v;
+ channel->volume -= v;
+ if (channel->volume > 64) channel->volume = 0;
+ }
+ break;
+ case IT_XM_FINE_VOLSLIDE_UP:
+ {
+ unsigned char v = entry->effectvalue;
+ if (v == 0)
+ v = channel->xm_lastEA;
+ channel->xm_lastEA = v;
+ channel->volume += v;
+ if (channel->volume > 64) channel->volume = 64;
+ }
+ break;
+ case IT_PORTAMENTO_DOWN:
+ {
+ unsigned char v = entry->effectvalue;
+ if (sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_669)) {
+ if (!(sigdata->flags & IT_WAS_A_MOD)) {
+ if (v == 0xF0)
+ v |= channel->xm_lastE2;
+ else if (v >= 0xF0)
+ channel->xm_lastE2 = v & 15;
+ else if (v == 0xE0)
+ v |= channel->xm_lastX2;
+ else
+ channel->xm_lastX2 = v & 15;
+ }
+ } else {
+ if (v == 0)
+ v = channel->lastEF;
+ channel->lastEF = v;
+ }
+ if (channel->playing) {
+ if ((v & 0xF0) == 0xF0)
+ channel->playing->slide -= (v & 15) << 4;
+ else if ((v & 0xF0) == 0xE0)
+ channel->playing->slide -= (v & 15) << 2;
+ else if (sigdata->flags & IT_WAS_A_669)
+ channel->portamento -= v << 3;
+ else
+ channel->portamento -= v << 4;
+ }
+ }
+ break;
+ case IT_PORTAMENTO_UP:
+ {
+ unsigned char v = entry->effectvalue;
+ if (sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_669)) {
+ if (!(sigdata->flags & IT_WAS_A_MOD)) {
+ if (v == 0xF0)
+ v |= channel->xm_lastE1;
+ else if (v >= 0xF0)
+ channel->xm_lastE1 = v & 15;
+ else if (v == 0xE0)
+ v |= channel->xm_lastX1;
+ else
+ channel->xm_lastX1 = v & 15;
+ }
+ } else {
+ if (v == 0)
+ v = channel->lastEF;
+ channel->lastEF = v;
+ }
+ if (channel->playing) {
+ if ((v & 0xF0) == 0xF0)
+ channel->playing->slide += (v & 15) << 4;
+ else if ((v & 0xF0) == 0xE0)
+ channel->playing->slide += (v & 15) << 2;
+ else if (sigdata->flags & IT_WAS_A_669)
+ channel->portamento += v << 3;
+ else
+ channel->portamento += v << 4;
+ }
+ }
+ break;
+ case IT_XM_PORTAMENTO_DOWN:
+ {
+ unsigned char v = entry->effectvalue;
+ if (!(sigdata->flags & IT_WAS_A_MOD)) {
+ if (v == 0)
+ v = channel->lastJ;
+ channel->lastJ = v;
+ }
+ if (channel->playing)
+ channel->portamento -= v << 4;
+ }
+ break;
+ case IT_XM_PORTAMENTO_UP:
+ {
+ unsigned char v = entry->effectvalue;
+ if (!(sigdata->flags & IT_WAS_A_MOD)) {
+ if (v == 0)
+ v = channel->lastEF;
+ channel->lastEF = v;
+ }
+ if (channel->playing)
+ channel->portamento += v << 4;
+ }
+ break;
+ case IT_XM_KEY_OFF:
+ channel->key_off_count = entry->effectvalue;
+ if (!channel->key_off_count) xm_note_off(sigdata, channel);
+ break;
+ case IT_VIBRATO:
+ {
+ if (entry->effectvalue || !(sigdata->flags & IT_WAS_A_669)) {
+ unsigned char speed = entry->effectvalue >> 4;
+ unsigned char depth = entry->effectvalue & 15;
+ if (speed == 0)
+ speed = channel->lastHspeed;
+ channel->lastHspeed = speed;
+ if (depth == 0)
+ depth = channel->lastHdepth;
+ else {
+ if (sigdata->flags & IT_OLD_EFFECTS)
+ depth <<= 3;
+ else
+ depth <<= 2;
+ channel->lastHdepth = depth;
+ }
+ if (channel->playing) {
+ channel->playing->vibrato_speed = speed;
+ channel->playing->vibrato_depth = depth;
+ channel->playing->vibrato_n++;
+ }
+ }
+ }
+ break;
+ case IT_TREMOR:
+ {
+ unsigned char v = entry->effectvalue;
+ if (v == 0)
+ v = channel->lastI;
+ else if (!(sigdata->flags & IT_OLD_EFFECTS)) {
+ if (v & 0xF0) v -= 0x10;
+ if (v & 0x0F) v -= 0x01;
+ }
+ channel->lastI = v;
+ channel->tremor_time |= 128;
+ }
+ update_tremor(channel);
+ break;
+ case IT_ARPEGGIO:
+ {
+ unsigned char v = entry->effectvalue;
+ /* XM files have no memory for arpeggio (000 = no effect)
+ * and we use lastJ for portamento down instead.
+ */
+ if (!(sigdata->flags & IT_WAS_AN_XM)) {
+ if (v == 0)
+ v = channel->lastJ;
+ channel->lastJ = v;
+ }
+ channel->arpeggio = v;
+ }
+ break;
+ case IT_SET_CHANNEL_VOLUME:
+ if (sigdata->flags & IT_WAS_AN_XM)
+ channel->volume = MIN(entry->effectvalue, 64);
+ else if (entry->effectvalue <= 64)
+ channel->channelvolume = entry->effectvalue;
+#ifdef VOLUME_OUT_OF_RANGE_SETS_MAXIMUM
+ else
+ channel->channelvolume = 64;
+#endif
+ if (channel->playing)
+ channel->playing->channel_volume = channel->channelvolume;
+ break;
+ case IT_CHANNEL_VOLUME_SLIDE:
+ {
+ unsigned char v = entry->effectvalue;
+ if (v == 0)
+ v = channel->lastN;
+ channel->lastN = v;
+ if ((v & 0x0F) == 0) { /* Nx0 */
+ channel->channelvolslide = v >> 4;
+ } else if ((v & 0xF0) == 0) { /* N0x */
+ channel->channelvolslide = -v;
+ } else {
+ if ((v & 0x0F) == 0x0F) { /* NxF */
+ channel->channelvolume += v >> 4;
+ if (channel->channelvolume > 64) channel->channelvolume = 64;
+ } else if ((v & 0xF0) == 0xF0) { /* NFx */
+ channel->channelvolume -= v & 15;
+ if (channel->channelvolume > 64) channel->channelvolume = 0;
+ } else
+ break;
+ if (channel->playing)
+ channel->playing->channel_volume = channel->channelvolume;
+ }
+ }
+ break;
+ case IT_SET_SAMPLE_OFFSET:
+ {
+ unsigned char v = entry->effectvalue;
+ /*if (sigdata->flags & IT_WAS_A_MOD) {
+ if (v == 0) break;
+ } else*/ {
+ if (v == 0)
+ v = channel->lastO;
+ channel->lastO = v;
+ }
+ /* Note: we set the offset even if tone portamento is
+ * specified. Impulse Tracker does the same.
+ */
+ if (entry->mask & IT_ENTRY_NOTE) {
+ if (channel->playing) {
+ int offset = ((int)channel->high_offset << 16) | ((int)v << 8);
+ IT_PLAYING *playing = channel->playing;
+ IT_SAMPLE *sample = playing->sample;
+ int end;
+ if ((sample->flags & IT_SAMPLE_SUS_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF))
+ end = sample->sus_loop_end;
+ else if (sample->flags & IT_SAMPLE_LOOP)
+ end = sample->loop_end;
+ else
+ end = sample->length;
+ if ((sigdata->flags & IT_WAS_A_PTM) && (sample->flags & IT_SAMPLE_16BIT))
+ offset >>= 1;
+ if (offset < end) {
+ it_playing_reset_resamplers(playing, offset);
+#ifdef END_RAMPING
+ playing->declick_stage = 0;
+ playing->declick_volume = 1.f / 256.f;
+#endif
+ } else if (sigdata->flags & IT_OLD_EFFECTS) {
+ it_playing_reset_resamplers(playing, end);
+#ifdef END_RAMPING
+ playing->declick_stage = 0;
+ playing->declick_volume = 1.f / 256.f;
+#endif
+ }
+ }
+ }
+ }
+ break;
+ case IT_PANNING_SLIDE:
+ /** JULIEN: guess what? the docs are wrong! (how unusual ;)
+ * Pxy seems to memorize its previous value... and there
+ * might be other mistakes like that... (sigh!)
+ */
+ /** ENTHEH: umm... but... the docs say that Pxy memorises its
+ * value... don't they? :o
+ */
+ {
+ unsigned char v = entry->effectvalue;
+ int p = channel->truepan;
+ if (sigdata->flags & IT_WAS_AN_XM)
+ {
+ if (IT_IS_SURROUND(channel->pan))
+ {
+ channel->pan = 32;
+ p = 32 + 128 * 64;
+ }
+ p >>= 6;
+ }
+ else {
+ if (IT_IS_SURROUND(channel->pan)) p = 32 << 8;
+ p = (p + 128) >> 8;
+ channel->pan = p;
+ }
+ if (v == 0)
+ v = channel->lastP;
+ channel->lastP = v;
+ if ((v & 0x0F) == 0) { /* Px0 */
+ channel->panslide = -(v >> 4);
+ } else if ((v & 0xF0) == 0) { /* P0x */
+ channel->panslide = v;
+ } else if ((v & 0x0F) == 0x0F) { /* PxF */
+ p -= v >> 4;
+ } else if ((v & 0xF0) == 0xF0) { /* PFx */
+ p += v & 15;
+ }
+ if (sigdata->flags & IT_WAS_AN_XM)
+ channel->truepan = 32 + MID(0, p, 255) * 64;
+ else {
+ if (p < 0) p = 0;
+ else if (p > 64) p = 64;
+ channel->pan = p;
+ channel->truepan = p << 8;
+ }
+ }
+ break;
+ case IT_RETRIGGER_NOTE:
+ {
+ unsigned char v = entry->effectvalue;
+ if (sigdata->flags & IT_WAS_AN_XM) {
+ if ((v & 0x0F) == 0) v |= channel->lastQ & 0x0F;
+ if ((v & 0xF0) == 0) v |= channel->lastQ & 0xF0;
+ } else {
+ if (v == 0)
+ v = channel->lastQ;
+ }
+ channel->lastQ = v;
+ if ((v & 0x0F) == 0) v |= 0x01;
+ channel->retrig = v;
+ if (entry->mask & IT_ENTRY_NOTE) {
+ channel->retrig_tick = v & 0x0F;
+ /* Emulate a bug */
+ if (sigdata->flags & IT_WAS_AN_XM)
+ update_retrig(sigrenderer, channel);
+ } else
+ update_retrig(sigrenderer, channel);
+ }
+ break;
+ case IT_XM_RETRIGGER_NOTE:
+ channel->retrig_tick = channel->xm_retrig = entry->effectvalue;
+ if (entry->effectvalue == 0)
+ if (channel->playing) {
+ it_playing_reset_resamplers(channel->playing, 0);
+#ifdef END_RAMPING
+ channel->playing->declick_stage = 0;
+ channel->playing->declick_volume = 1.f / 256.f;
+#endif
+ }
+ break;
+ case IT_TREMOLO:
+ {
+ unsigned char speed = entry->effectvalue >> 4;
+ unsigned char depth = entry->effectvalue & 15;
+ if (speed == 0)
+ speed = channel->lastRspeed;
+ channel->lastRspeed = speed;
+ if (depth == 0)
+ depth = channel->lastRdepth;
+ channel->lastRdepth = depth;
+ if (channel->playing) {
+ channel->playing->tremolo_speed = speed;
+ channel->playing->tremolo_depth = depth;
+ }
+ }
+ break;
+ case IT_S:
+ {
+ /* channel->lastS was set in update_pattern_variables(). */
+ unsigned char effectvalue = channel->lastS;
+ switch (effectvalue >> 4) {
+ //case IT_S_SET_FILTER:
+ /* Waveforms for commands S3x, S4x and S5x:
+ * 0: Sine wave
+ * 1: Ramp down
+ * 2: Square wave
+ * 3: Random wave
+ */
+ case IT_S_SET_GLISSANDO_CONTROL:
+ channel->glissando = effectvalue & 15;
+ break;
+
+ case IT_S_FINETUNE:
+ if (channel->playing) {
+ channel->playing->finetune = ((int)(effectvalue & 15) - 8) << 5;
+ }
+ break;
+
+ case IT_S_SET_VIBRATO_WAVEFORM:
+ {
+ int waveform = effectvalue & 3;
+ if (sigdata->flags & IT_WAS_AN_XM) waveform = xm_convert_vibrato[waveform];
+ channel->vibrato_waveform = waveform;
+ if (channel->playing) {
+ channel->playing->vibrato_waveform = waveform;
+ if (!(effectvalue & 4))
+ channel->playing->vibrato_time = 0;
+ }
+ }
+ break;
+ case IT_S_SET_TREMOLO_WAVEFORM:
+ {
+ int waveform = effectvalue & 3;
+ if (sigdata->flags & IT_WAS_AN_XM) waveform = xm_convert_vibrato[waveform];
+ channel->tremolo_waveform = waveform;
+ if (channel->playing) {
+ channel->playing->tremolo_waveform = waveform;
+ if (!(effectvalue & 4))
+ channel->playing->tremolo_time = 0;
+ }
+ }
+ break;
+ case IT_S_SET_PANBRELLO_WAVEFORM:
+ channel->panbrello_waveform = effectvalue & 3;
+ if (channel->playing) {
+ channel->playing->panbrello_waveform = effectvalue & 3;
+ if (!(effectvalue & 4))
+ channel->playing->panbrello_time = 0;
+ }
+ break;
+
+ case IT_S_FINE_PATTERN_DELAY:
+ sigrenderer->tick += effectvalue & 15;
+ break;
+#if 1
+ case IT_S7:
+ {
+ if (sigrenderer->sigdata->flags & IT_USE_INSTRUMENTS)
+ {
+ int i;
+ switch (effectvalue & 15)
+ {
+ case 0: /* cut background notes */
+ for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++)
+ {
+ IT_PLAYING * playing = sigrenderer->playing[i];
+ if (playing && channel == playing->channel)
+ {
+#ifdef RAMP_DOWN
+ playing->declick_stage = 2;
+#else
+ free(playing);
+ sigrenderer->playing[i] = NULL;
+#endif
+ if (channel->playing == playing) channel->playing = NULL;
+ }
+ }
+ break;
+ case 1: /* release background notes */
+ for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++)
+ {
+ IT_PLAYING * playing = sigrenderer->playing[i];
+ if (playing && channel == playing->channel && !(playing->flags & IT_PLAYING_SUSTAINOFF))
+ {
+ it_note_off(playing);
+ }
+ }
+ break;
+ case 2: /* fade background notes */
+ for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++)
+ {
+ IT_PLAYING * playing = sigrenderer->playing[i];
+ if (playing && channel == playing->channel)
+ {
+ //playing->flags &= IT_PLAYING_SUSTAINOFF;
+ playing->flags |= IT_PLAYING_FADING;
+ }
+ }
+ break;
+ case 3:
+ channel->new_note_action = NNA_NOTE_CUT;
+ break;
+ case 4:
+ channel->new_note_action = NNA_NOTE_CONTINUE;
+ break;
+ case 5:
+ channel->new_note_action = NNA_NOTE_OFF;
+ break;
+ case 6:
+ channel->new_note_action = NNA_NOTE_FADE;
+ break;
+
+ case 7:
+ if (channel->playing)
+ channel->playing->enabled_envelopes &= ~IT_ENV_VOLUME;
+ break;
+ case 8:
+ if (channel->playing)
+ channel->playing->enabled_envelopes |= IT_ENV_VOLUME;
+ break;
+
+ case 9:
+ if (channel->playing)
+ channel->playing->enabled_envelopes &= ~IT_ENV_PANNING;
+ break;
+ case 10:
+ if (channel->playing)
+ channel->playing->enabled_envelopes |= IT_ENV_PANNING;
+ break;
+
+ case 11:
+ if (channel->playing)
+ channel->playing->enabled_envelopes &= ~IT_ENV_PITCH;
+ break;
+ case 12:
+ if (channel->playing)
+ channel->playing->enabled_envelopes |= IT_ENV_PITCH;
+ break;
+ }
+ }
+ }
+ break;
+#endif
+ case IT_S_SET_PAN:
+ //ASSERT(!(sigdata->flags & IT_WAS_AN_XM));
+ channel->pan =
+ ((effectvalue & 15) << 2) |
+ ((effectvalue & 15) >> 2);
+ channel->truepan = channel->pan << IT_ENVELOPE_SHIFT;
+
+ if (channel->playing)
+ channel->playing->panbrello_depth = 0;
+ break;
+ case IT_S_SET_SURROUND_SOUND:
+ if ((effectvalue & 15) == 1) {
+ channel->pan = IT_SURROUND;
+ channel->truepan = channel->pan << IT_ENVELOPE_SHIFT;
+ }
+ if (channel->playing)
+ channel->playing->panbrello_depth = 0;
+ break;
+ case IT_S_SET_HIGH_OFFSET:
+ channel->high_offset = effectvalue & 15;
+ break;
+ //case IT_S_PATTERN_LOOP:
+ case IT_S_DELAYED_NOTE_CUT:
+ channel->note_cut_count = effectvalue & 15;
+ if (!channel->note_cut_count) {
+ if (sigdata->flags & (IT_WAS_AN_XM | IT_WAS_A_PTM))
+ channel->volume = 0;
+ else
+ channel->note_cut_count = 1;
+ }
+ break;
+ case IT_S_SET_MIDI_MACRO:
+ channel->SFmacro = effectvalue & 15;
+ break;
+ }
+ }
+ break;
+ case IT_SET_SONG_TEMPO:
+ {
+ unsigned char v = entry->effectvalue;
+ if (v == 0)
+ v = channel->lastW;
+ channel->lastW = v;
+ if (v < 0x10)
+ sigrenderer->temposlide = -v;
+ else if (v < 0x20)
+ sigrenderer->temposlide = v & 15;
+ else
+ sigrenderer->tempo = v;
+ }
+ break;
+ case IT_FINE_VIBRATO:
+ {
+ unsigned char speed = entry->effectvalue >> 4;
+ unsigned char depth = entry->effectvalue & 15;
+ if (speed == 0)
+ speed = channel->lastHspeed;
+ channel->lastHspeed = speed;
+ if (depth == 0)
+ depth = channel->lastHdepth;
+ else {
+ if (sigdata->flags & IT_OLD_EFFECTS)
+ depth <<= 1;
+ channel->lastHdepth = depth;
+ }
+ if (channel->playing) {
+ channel->playing->vibrato_speed = speed;
+ channel->playing->vibrato_depth = depth;
+ channel->playing->vibrato_n++;
+ }
+ }
+ break;
+ case IT_SET_GLOBAL_VOLUME:
+ if (entry->effectvalue <= 128)
+ sigrenderer->globalvolume = entry->effectvalue;
+#ifdef VOLUME_OUT_OF_RANGE_SETS_MAXIMUM
+ else
+ sigrenderer->globalvolume = 128;
+#endif
+ break;
+ case IT_GLOBAL_VOLUME_SLIDE:
+ {
+ unsigned char v = entry->effectvalue;
+ if (v == 0)
+ v = channel->lastW;
+ channel->lastW = v;
+ if ((v & 0x0F) == 0) { /* Wx0 */
+ sigrenderer->globalvolslide =
+ (sigdata->flags & IT_WAS_AN_XM) ? (v >> 4)*2 : (v >> 4);
+ } else if ((v & 0xF0) == 0) { /* W0x */
+ sigrenderer->globalvolslide =
+ (sigdata->flags & IT_WAS_AN_XM) ? (-v)*2 : (-v);
+ } else if ((v & 0x0F) == 0x0F) { /* WxF */
+ sigrenderer->globalvolume += v >> 4;
+ if (sigrenderer->globalvolume > 128) sigrenderer->globalvolume = 128;
+ } else if ((v & 0xF0) == 0xF0) { /* WFx */
+ sigrenderer->globalvolume -= v & 15;
+ if (sigrenderer->globalvolume > 128) sigrenderer->globalvolume = 0;
+ }
+ }
+ break;
+ case IT_SET_PANNING:
+ if (sigdata->flags & IT_WAS_AN_XM) {
+ if (sigdata->flags & IT_WAS_A_MOD)
+ channel->truepan = entry->effectvalue*128;
+ else
+ channel->truepan = 32 + entry->effectvalue*64;
+ } else {
+ if (sigdata->flags & IT_WAS_AN_S3M)
+ channel->pan = (entry->effectvalue + 1) >> 1;
+ else
+ channel->pan = (entry->effectvalue + 2) >> 2;
+ channel->truepan = channel->pan << IT_ENVELOPE_SHIFT;
+ }
+ if (channel->playing)
+ channel->playing->panbrello_depth = 0;
+ break;
+ case IT_PANBRELLO:
+ {
+ unsigned char speed = entry->effectvalue >> 4;
+ unsigned char depth = entry->effectvalue & 15;
+ if (speed == 0)
+ speed = channel->lastYspeed;
+ channel->lastYspeed = speed;
+ if (depth == 0)
+ depth = channel->lastYdepth;
+ channel->lastYdepth = depth;
+ if (channel->playing) {
+ channel->playing->panbrello_speed = speed;
+ channel->playing->panbrello_depth = depth;
+ }
+ }
+ break;
+ case IT_MIDI_MACRO:
+ {
+ const IT_MIDI *midi = sigdata->midi ? sigdata->midi : &default_midi;
+ if (entry->effectvalue >= 0x80) {
+ int n = midi->Zmacrolen[entry->effectvalue-0x80];
+ int i;
+ for (i = 0; i < n; i++)
+ it_send_midi(sigrenderer, channel, midi->Zmacro[entry->effectvalue-0x80][i]);
+ } else {
+ int n = midi->SFmacrolen[channel->SFmacro];
+ int i, j;
+ for (i = 0, j = 1; i < n; i++, j <<= 1)
+ it_send_midi(sigrenderer, channel,
+ (unsigned char)(midi->SFmacroz[channel->SFmacro] & j ?
+ entry->effectvalue : midi->SFmacro[channel->SFmacro][i]));
+ }
+ }
+ break;
+ case IT_XM_SET_ENVELOPE_POSITION:
+ if (channel->playing && channel->playing->env_instrument) {
+ IT_ENVELOPE *envelope = &channel->playing->env_instrument->volume_envelope;
+ if (envelope->flags & IT_ENVELOPE_ON) {
+ IT_PLAYING_ENVELOPE *pe = &channel->playing->volume_envelope;
+ pe->tick = entry->effectvalue;
+ if (pe->tick >= envelope->node_t[envelope->n_nodes-1])
+ pe->tick = envelope->node_t[envelope->n_nodes-1];
+ pe->next_node = 0;
+ while (pe->tick > envelope->node_t[pe->next_node]) pe->next_node++;
+ xm_envelope_calculate_value(envelope, pe);
+ }
+ }
+ break;
+
+ /* uggly plain portamento for now */
+ case IT_PTM_NOTE_SLIDE_DOWN:
+ case IT_PTM_NOTE_SLIDE_DOWN_RETRIG:
+ {
+ channel->toneslide_retrig = (entry->effect == IT_PTM_NOTE_SLIDE_DOWN_RETRIG);
+
+ if (channel->ptm_last_toneslide) {
+ channel->toneslide_tick = channel->last_toneslide_tick;
+
+ if (--channel->toneslide_tick == 0) {
+ channel->truenote += channel->toneslide;
+ if (channel->truenote >= 120) {
+ if (channel->toneslide < 0) channel->truenote = 0;
+ else channel->truenote = 119;
+ }
+ channel->note += channel->toneslide;
+ if (channel->note >= 120) {
+ if (channel->toneslide < 0) channel->note = 0;
+ else channel->note = 119;
+ }
+
+ if (channel->playing) {
+ if (channel->sample) channel->playing->note = channel->truenote;
+ else channel->playing->note = channel->note;
+ it_playing_reset_resamplers(channel->playing, 0);
+#ifdef END_RAMPING
+ channel->playing->declick_stage = 0;
+ channel->playing->declick_volume = 1.f / 256.f;
+#endif
+ }
+ }
+ }
+
+ channel->ptm_last_toneslide = 0;
+
+ channel->toneslide = -(entry->effectvalue & 15);
+ channel->ptm_toneslide = (entry->effectvalue & 0xF0) >> 4;
+ channel->toneslide_tick += channel->ptm_toneslide;
+ }
+ break;
+ case IT_PTM_NOTE_SLIDE_UP:
+ case IT_PTM_NOTE_SLIDE_UP_RETRIG:
+ {
+ channel->toneslide_retrig = (entry->effect == IT_PTM_NOTE_SLIDE_UP_RETRIG);
+
+ if (channel->ptm_last_toneslide) {
+ channel->toneslide_tick = channel->last_toneslide_tick;
+
+ if (--channel->toneslide_tick == 0) {
+ channel->truenote += channel->toneslide;
+ if (channel->truenote >= 120) {
+ if (channel->toneslide < 0) channel->truenote = 0;
+ else channel->truenote = 119;
+ }
+ channel->note += channel->toneslide;
+ if (channel->note >= 120) {
+ if (channel->toneslide < 0) channel->note = 0;
+ else channel->note = 119;
+ }
+
+ if (channel->playing) {
+ if (channel->sample) channel->playing->note = channel->truenote;
+ else channel->playing->note = channel->note;
+ it_playing_reset_resamplers(channel->playing, 0);
+#ifdef END_RAMPING
+ channel->playing->declick_stage = 0;
+ channel->playing->declick_volume = 1.f / 256.f;
+#endif
+ }
+ }
+ }
+
+ channel->ptm_last_toneslide = 0;
+
+ channel->toneslide = -(entry->effectvalue & 15);
+ channel->ptm_toneslide = (entry->effectvalue & 0xF0) >> 4;
+ channel->toneslide_tick += channel->ptm_toneslide;
+ }
+ break;
+ }
+ }
+
+ if (!(sigdata->flags & IT_WAS_AN_XM))
+ post_process_it_volpan(sigrenderer, entry);
+
+ return 0;
+}
+
+
+
+static int process_it_note_data(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry)
+{
+ DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata;
+ IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel];
+
+ // When tone portamento and instrument are specified:
+ // If Gxx is off:
+ // - same sample, do nothing but portamento
+ // - diff sample, retrigger all but keep current note+slide + do porta
+ // - if instrument is invalid, nothing; if sample is invalid, cut
+ // If Gxx is on:
+ // - same sample or new sample invalid, retrigger envelopes and initialise note value for portamento to 'seek' to
+ // - diff sample/inst, start using new envelopes
+ // When tone portamento is specified alone, sample won't change.
+ // TODO: consider what happens with instrument alone after all this...
+
+ if (entry->mask & (IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT)) {
+ if (entry->mask & IT_ENTRY_INSTRUMENT)
+ channel->instrument = entry->instrument;
+ instrument_to_sample(sigdata, channel);
+ if (channel->note < 120) {
+ if ((sigdata->flags & IT_USE_INSTRUMENTS) && channel->sample == 0)
+ it_retrigger_note(sigrenderer, channel); /* Stop the note */ /*return 1;*/
+ if (entry->mask & IT_ENTRY_INSTRUMENT)
+ get_default_volpan(sigdata, channel);
+ } else
+ it_retrigger_note(sigrenderer, channel); /* Stop the note */
+ }
+
+ /** WARNING: This is not ideal, since channel->playing might not get allocated owing to lack of memory... */
+ if (((entry->mask & IT_ENTRY_VOLPAN) && entry->volpan >= 193 && entry->volpan <= 202) ||
+ ((entry->mask & IT_ENTRY_EFFECT) && (entry->effect == IT_TONE_PORTAMENTO || entry->effect == IT_VOLSLIDE_TONEPORTA)))
+ {
+ if (channel->playing && (entry->mask & IT_ENTRY_INSTRUMENT)) {
+ if (sigdata->flags & IT_COMPATIBLE_GXX)
+ it_compatible_gxx_retrigger(sigdata, channel);
+ else if ((!(sigdata->flags & IT_USE_INSTRUMENTS) ||
+ (channel->instrument >= 1 && channel->instrument <= sigdata->n_instruments)) &&
+ channel->sample != channel->playing->sampnum)
+ {
+ unsigned char note = channel->playing->note;
+ int slide = channel->playing->slide;
+ it_retrigger_note(sigrenderer, channel);
+ if (channel->playing) {
+ channel->playing->note = note;
+ channel->playing->slide = slide;
+ // Should we be preserving sample_vibrato_time? depth?
+ }
+ }
+ }
+
+ if ((entry->mask & IT_ENTRY_VOLPAN) && entry->volpan >= 193 && entry->volpan <= 202) {
+ /* Tone Portamento in the volume column */
+ static const unsigned char slidetable[] = {0, 1, 4, 8, 16, 32, 64, 96, 128, 255};
+ unsigned char v = slidetable[entry->volpan - 193];
+ if (sigdata->flags & IT_COMPATIBLE_GXX) {
+ if (v == 0)
+ v = channel->lastG;
+ channel->lastG = v;
+ } else {
+ if (v == 0)
+ v = channel->lastEF;
+ channel->lastEF = v;
+ }
+ if ((entry->mask & IT_ENTRY_NOTE) || ((sigdata->flags & IT_COMPATIBLE_GXX) && (entry->mask & IT_ENTRY_INSTRUMENT))) {
+ if (channel->note < 120) {
+ if (channel->sample)
+ channel->destnote = channel->truenote;
+ else
+ channel->destnote = channel->note;
+ }
+ }
+ channel->toneporta = v << 4;
+ } else {
+ /* Tone Portamento in the effect column */
+ unsigned char v;
+ if (entry->effect == IT_TONE_PORTAMENTO)
+ v = entry->effectvalue;
+ else
+ v = 0;
+ if (sigdata->flags & IT_COMPATIBLE_GXX) {
+ if (v == 0)
+ v = channel->lastG;
+ channel->lastG = v;
+ } else {
+ if (v == 0 && !(sigdata->flags & IT_WAS_A_669))
+ v = channel->lastEF;
+ channel->lastEF = v;
+ }
+ if ((entry->mask & IT_ENTRY_NOTE) || ((sigdata->flags & IT_COMPATIBLE_GXX) && (entry->mask & IT_ENTRY_INSTRUMENT))) {
+ if (channel->note < 120) {
+ if (channel->sample)
+ channel->destnote = channel->truenote;
+ else
+ channel->destnote = channel->note;
+ }
+ }
+ channel->toneporta = v << 4;
+ }
+ if (channel->playing) goto skip_start_note;
+ }
+
+ if ((entry->mask & IT_ENTRY_NOTE) ||
+ ((entry->mask & IT_ENTRY_INSTRUMENT) && (!channel->playing || entry->instrument != channel->playing->instnum)))
+ {
+ if (channel->note < 120) {
+ get_true_pan(sigdata, channel);
+ if ((entry->mask & IT_ENTRY_NOTE) || !(sigdata->flags & (IT_WAS_AN_S3M|IT_WAS_A_PTM)))
+ it_retrigger_note(sigrenderer, channel);
+ }
+ }
+
+ skip_start_note:
+
+ if (entry->mask & IT_ENTRY_VOLPAN) {
+ if (entry->volpan <= 64) {
+ /* Volume */
+ channel->volume = entry->volpan;
+ } else if (entry->volpan <= 74) {
+ /* Fine volume slide up */
+ unsigned char v = entry->volpan - 65;
+ if (v == 0)
+ v = channel->lastvolslide;
+ channel->lastvolslide = v;
+ /* = effect DxF where x == entry->volpan - 65 */
+ channel->volume += v;
+ if (channel->volume > 64) channel->volume = 64;
+ } else if (entry->volpan <= 84) {
+ /* Fine volume slide down */
+ unsigned char v = entry->volpan - 75;
+ if (v == 0)
+ v = channel->lastvolslide;
+ channel->lastvolslide = v;
+ /* = effect DFx where x == entry->volpan - 75 */
+ channel->volume -= v;
+ if (channel->volume > 64) channel->volume = 0;
+ } else if (entry->volpan < 128) {
+ /* Volume slide up */
+ /* Volume slide down */
+ /* Portamento down */
+ /* Portamento up */
+ } else if (entry->volpan <= 192) {
+ /* Pan */
+ channel->pan = entry->volpan - 128;
+ channel->truepan = channel->pan << IT_ENVELOPE_SHIFT;
+ }
+ /* else */
+ /* Tone Portamento */
+ /* Vibrato */
+ }
+ return 0;
+}
+
+
+
+static void retrigger_xm_envelopes(IT_PLAYING *playing)
+{
+ playing->volume_envelope.next_node = 0;
+ playing->volume_envelope.tick = -1;
+ playing->pan_envelope.next_node = 0;
+ playing->pan_envelope.tick = -1;
+ playing->fadeoutcount = 1024;
+}
+
+
+
+static void process_xm_note_data(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry)
+{
+ DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata;
+ IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel];
+ IT_PLAYING playing;
+
+ playing.sample = 0;
+
+ if (entry->mask & IT_ENTRY_INSTRUMENT) {
+ int oldsample = channel->sample;
+ int oldvolume = channel->volume;
+ channel->instrument = entry->instrument;
+ instrument_to_sample(sigdata, channel);
+ if (channel->playing &&
+ !((entry->mask & IT_ENTRY_NOTE) && entry->note >= 120) &&
+ !((entry->mask & IT_ENTRY_EFFECT) && entry->effect == IT_XM_KEY_OFF && entry->effectvalue == 0)) {
+ playing = *channel->playing;
+ if (!(sigdata->flags & IT_WAS_A_MOD)) {
+ /* Retrigger vol/pan envelopes if enabled, and cancel fadeout.
+ * Also reset vol/pan to that of _original_ instrument.
+ */
+ channel->playing->flags &= ~(IT_PLAYING_SUSTAINOFF | IT_PLAYING_FADING);
+ it_playing_update_resamplers(channel->playing);
+
+ channel->volume = channel->playing->sample->default_volume;
+ channel->truepan = 32 + channel->playing->sample->default_pan*64;
+
+ retrigger_xm_envelopes(channel->playing);
+ } else {
+ /* Switch if sample changed */
+ if (oldsample != channel->sample) {
+#ifdef RAMP_DOWN
+ int i;
+ for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+ if (!sigrenderer->playing[i]) {
+ channel->playing->declick_stage = 2;
+ sigrenderer->playing[i] = channel->playing;
+ channel->playing = NULL;
+ break;
+ }
+ }
+
+ if (!channel->sample) {
+ if (channel->playing)
+ {
+ free(channel->playing);
+ channel->playing = NULL;
+ }
+ } else {
+ if (!channel->playing) {
+ channel->playing = malloc(sizeof(*channel->playing));
+ if (!channel->playing) return;
+ }
+ *channel->playing = playing;
+ playing.sample = (IT_SAMPLE *)-1;
+ channel->playing->declick_stage = 0;
+ channel->playing->declick_volume = 1.f / 256.f;
+#else
+ if (!channel->sample) {
+ free(channel->playing);
+ channel->playing = NULL;
+ } else {
+#endif
+ channel->playing->sampnum = channel->sample;
+ channel->playing->sample = &sigdata->sample[channel->sample-1];
+ it_playing_reset_resamplers(channel->playing, 0);
+ }
+ }
+ get_default_volpan(sigdata, channel);
+ }
+#ifdef END_RAMPING
+ if (channel->volume && !oldvolume) {
+ channel->playing->declick_stage = 0;
+ channel->playing->declick_volume = 1.f / 256.f;
+ }
+#endif
+ }
+ }
+
+ if (!((entry->mask & IT_ENTRY_EFFECT) && entry->effect == IT_XM_KEY_OFF && entry->effectvalue == 0) &&
+ (entry->mask & IT_ENTRY_NOTE))
+ {
+ if (!(entry->mask & IT_ENTRY_INSTRUMENT))
+ instrument_to_sample(sigdata, channel);
+
+ if (channel->note >= 120)
+ xm_note_off(sigdata, channel);
+ else if (channel->sample == 0) {
+ /** If we get here, one of the following is the case:
+ ** 1. The instrument has never been specified on this channel.
+ ** 2. The specified instrument is invalid.
+ ** 3. The instrument has no sample mapped to the selected note.
+ ** What should happen?
+ **
+ ** Experimentation shows that any existing note stops and cannot
+ ** be brought back. A subsequent instrument change fixes that.
+ **/
+ if (channel->playing) {
+#ifdef RAMP_DOWN
+ int i;
+ if (playing.sample) *channel->playing = playing;
+ for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+ if (!sigrenderer->playing[i]) {
+ channel->playing->declick_stage = 2;
+ sigrenderer->playing[i] = channel->playing;
+ channel->playing = NULL;
+ break;
+ }
+ }
+ if (channel->playing) {
+ free(channel->playing);
+ channel->playing = NULL;
+ }
+#else
+ free(channel->playing);
+ channel->playing = NULL;
+#endif
+ }
+ return;
+ } else if (channel->playing && (entry->mask & IT_ENTRY_VOLPAN) && ((entry->volpan>>4) == 0xF)) {
+ /* Don't retrigger note; portamento in the volume column. */
+ } else if (channel->playing &&
+ (entry->mask & IT_ENTRY_EFFECT) &&
+ (entry->effect == IT_TONE_PORTAMENTO ||
+ entry->effect == IT_VOLSLIDE_TONEPORTA)) {
+ /* Don't retrigger note; portamento in the effects column. */
+ } else {
+ channel->destnote = IT_NOTE_OFF;
+
+ if (!channel->playing) {
+ channel->playing = malloc(sizeof(*channel->playing));
+ if (!channel->playing)
+ return;
+ // Adding the following seems to do the trick for the case where a piece starts with an instrument alone and then some notes alone.
+ retrigger_xm_envelopes(channel->playing);
+ }
+#ifdef RAMP_DOWN
+ else if (playing.sample != (IT_SAMPLE *)-1) {
+ /* volume rampy stuff! move note to NNA */
+ int i;
+ IT_PLAYING * ptemp = malloc(sizeof(*channel->playing));
+ if (!ptemp) return;
+ if (playing.sample) *ptemp = playing;
+ else *ptemp = *channel->playing;
+ for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+ if (!sigrenderer->playing[i]) {
+ ptemp->declick_stage = 2;
+ ptemp->flags |= IT_PLAYING_SUSTAINOFF | IT_PLAYING_FADING;
+ sigrenderer->playing[i] = ptemp;
+ ptemp = NULL;
+ break;
+ }
+ }
+ if (ptemp) free(ptemp);
+ }
+#endif
+
+ channel->playing->flags = 0;
+ channel->playing->resampling_quality = sigrenderer->resampling_quality;
+ channel->playing->channel = channel;
+ channel->playing->sample = &sigdata->sample[channel->sample-1];
+ if (sigdata->flags & IT_USE_INSTRUMENTS)
+ channel->playing->instrument = &sigdata->instrument[channel->instrument-1];
+ else
+ channel->playing->instrument = NULL;
+ channel->playing->env_instrument = channel->playing->instrument;
+ channel->playing->sampnum = channel->sample;
+ channel->playing->instnum = channel->instrument;
+#ifdef END_RAMPING
+ channel->playing->declick_stage = 0;
+ channel->playing->declick_volume = 1.f / 256.f;
+#endif
+ channel->playing->channel_volume = channel->channelvolume;
+ channel->playing->ramp_volume[0] = 31337.0; /* special */
+ channel->playing->note = channel->truenote;
+ channel->playing->enabled_envelopes = 0;
+ channel->playing->volume_offset = 0;
+ channel->playing->panning_offset = 0;
+ //channel->playing->output = channel->output;
+ if (sigdata->flags & IT_USE_INSTRUMENTS) {
+ IT_PLAYING * playing = channel->playing;
+ IT_INSTRUMENT * instrument = playing->instrument;
+ if (instrument->volume_envelope.flags & IT_ENVELOPE_ON) playing->enabled_envelopes |= IT_ENV_VOLUME;
+ if (instrument->pan_envelope.flags & IT_ENVELOPE_ON) playing->enabled_envelopes |= IT_ENV_PANNING;
+ //if (instrument->output) playing->output = instrument->output;
+ }
+ channel->playing->filter_cutoff = 127;
+ channel->playing->filter_resonance = 0;
+ channel->playing->true_filter_cutoff = 127 << 8;
+ channel->playing->true_filter_resonance = 0;
+ channel->playing->vibrato_speed = 0;
+ channel->playing->vibrato_depth = 0;
+ channel->playing->vibrato_n = 0;
+ channel->playing->vibrato_time = 0;
+ channel->playing->vibrato_waveform = 0;
+ channel->playing->tremolo_speed = 0;
+ channel->playing->tremolo_depth = 0;
+ channel->playing->tremolo_time = 0;
+ channel->playing->tremolo_waveform = 0;
+ channel->playing->panbrello_speed = 0;
+ channel->playing->panbrello_depth = 0;
+ channel->playing->panbrello_time = 0;
+ channel->playing->panbrello_waveform = 0;
+ channel->playing->panbrello_random = 0;
+ channel->playing->sample_vibrato_time = 0;
+ channel->playing->sample_vibrato_waveform = channel->playing->sample->vibrato_waveform;
+ channel->playing->sample_vibrato_depth = 0;
+ channel->playing->slide = 0;
+ channel->playing->finetune = channel->playing->sample->finetune;
+ it_reset_filter_state(&channel->playing->filter_state[0]); // Are these
+ it_reset_filter_state(&channel->playing->filter_state[1]); // necessary?
+ it_playing_reset_resamplers(channel->playing, 0);
+
+ /** WARNING - is everything initialised? */
+ }
+ }
+
+ if (!((entry->mask & IT_ENTRY_EFFECT) && entry->effect == IT_XM_KEY_OFF && entry->effectvalue == 0) &&
+ !((entry->mask & IT_ENTRY_NOTE) && entry->note >= 120) &&
+ (entry->mask & (IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT)) == (IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT))
+ {
+ if (channel->playing) retrigger_xm_envelopes(channel->playing);
+ get_default_volpan(sigdata, channel);
+ }
+
+ if ((entry->mask & IT_ENTRY_VOLPAN) && ((entry->volpan>>4) == 0xF)) {
+ /* Tone Portamento */
+ unsigned char v = (entry->volpan & 15) << 4;
+ if (v == 0)
+ v = channel->lastG;
+ channel->lastG = v;
+ if (entry->mask & IT_ENTRY_NOTE)
+ if (channel->sample && channel->note < 120)
+ channel->destnote = channel->truenote;
+ channel->toneporta = v << 4;
+ } else if ((entry->mask & IT_ENTRY_EFFECT) &&
+ (entry->effect == IT_TONE_PORTAMENTO ||
+ entry->effect == IT_VOLSLIDE_TONEPORTA)) {
+ unsigned char v;
+ if (entry->effect == IT_TONE_PORTAMENTO)
+ v = entry->effectvalue;
+ else
+ v = 0;
+ if (v == 0)
+ v = channel->lastG;
+ channel->lastG = v;
+ if (entry->mask & IT_ENTRY_NOTE)
+ if (channel->sample && channel->note < 120)
+ channel->destnote = channel->truenote;
+ channel->toneporta = v << 4;
+ }
+
+ if (entry->mask & IT_ENTRY_VOLPAN) {
+ int effect = entry->volpan >> 4;
+ int value = entry->volpan & 15;
+ switch (effect) {
+ case 0x6: /* Volume slide down */
+ channel->xm_volslide = -value;
+ break;
+ case 0x7: /* Volume slide up */
+ channel->xm_volslide = value;
+ break;
+ case 0x8: /* Fine volume slide down */
+ channel->volume -= value;
+ if (channel->volume > 64) channel->volume = 0;
+ break;
+ case 0x9: /* Fine volume slide up */
+ channel->volume += value;
+ if (channel->volume > 64) channel->volume = 64;
+ break;
+ case 0xA: /* Set vibrato speed */
+ if (value)
+ channel->lastHspeed = value;
+ if (channel->playing)
+ channel->playing->vibrato_speed = channel->lastHspeed;
+ break;
+ case 0xB: /* Vibrato */
+ if (value)
+ channel->lastHdepth = value << 2; /** WARNING: correct ? */
+ if (channel->playing) {
+ channel->playing->vibrato_depth = channel->lastHdepth;
+ channel->playing->vibrato_speed = channel->lastHspeed;
+ channel->playing->vibrato_n++;
+ }
+ break;
+ case 0xC: /* Set panning */
+ channel->truepan = 32 + value*(17*64);
+ break;
+ case 0xD: /* Pan slide left */
+ /* -128 is a special case for emulating a 'feature' in FT2.
+ * As soon as effects are processed, it goes hard left.
+ */
+ channel->panslide = value ? -value : -128;
+ break;
+ case 0xE: /* Pan slide Right */
+ channel->panslide = value;
+ break;
+ case 0xF: /* Tone porta */
+ break;
+ default: /* Volume */
+ channel->volume = entry->volpan - 0x10;
+ break;
+ }
+ }
+}
+
+
+
+/* This function assumes !IT_IS_END_ROW(entry). */
+static int process_note_data(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry, int ignore_cxx)
+{
+ DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata;
+
+ if (sigdata->flags & IT_WAS_AN_XM)
+ process_xm_note_data(sigrenderer, entry);
+ else
+ if (process_it_note_data(sigrenderer, entry)) return 0;
+
+ return process_effects(sigrenderer, entry, ignore_cxx);
+}
+
+
+
+static int process_entry(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry, int ignore_cxx)
+{
+ IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel];
+
+ if (entry->mask & IT_ENTRY_NOTE)
+ channel->note = entry->note;
+
+ if ((entry->mask & (IT_ENTRY_NOTE|IT_ENTRY_EFFECT)) && (sigrenderer->sigdata->flags & IT_WAS_A_669)) {
+ reset_channel_effects(channel);
+ // XXX unknown
+ if (channel->playing) channel->playing->finetune = 0;
+ }
+
+ if ((entry->mask & IT_ENTRY_EFFECT) && entry->effect == IT_S) {
+ /* channel->lastS was set in update_pattern_variables(). */
+ unsigned char effectvalue = channel->lastS;
+ if (effectvalue >> 4 == IT_S_NOTE_DELAY) {
+ channel->note_delay_count = effectvalue & 15;
+ if (channel->note_delay_count == 0)
+ channel->note_delay_count = 1;
+ channel->note_delay_entry = entry;
+ return 0;
+ }
+ }
+
+ return process_note_data(sigrenderer, entry, ignore_cxx);
+}
+
+
+
+static void update_tick_counts(DUMB_IT_SIGRENDERER *sigrenderer)
+{
+ int i;
+
+ for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+ IT_CHANNEL *channel = &sigrenderer->channel[i];
+
+ if (channel->key_off_count) {
+ channel->key_off_count--;
+ if (channel->key_off_count == 0)
+ xm_note_off(sigrenderer->sigdata, channel);
+ } else if (channel->note_cut_count) {
+ channel->note_cut_count--;
+ if (channel->note_cut_count == 0) {
+ if (sigrenderer->sigdata->flags & (IT_WAS_AN_XM | IT_WAS_A_PTM))
+ channel->volume = 0;
+ else if (channel->playing) {
+#ifdef RAMP_DOWN
+ int i;
+ for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+ if (!sigrenderer->playing[i]) {
+ channel->playing->declick_stage = 2;
+ sigrenderer->playing[i] = channel->playing;
+ channel->playing = NULL;
+ break;
+ }
+ }
+ if (channel->playing) {
+ free(channel->playing);
+ channel->playing = NULL;
+ }
+#else
+ free(channel->playing);
+ channel->playing = NULL;
+#endif
+ }
+ }
+ } else if (channel->note_delay_count) {
+ channel->note_delay_count--;
+ if (channel->note_delay_count == 0)
+ process_note_data(sigrenderer, channel->note_delay_entry, 0);
+ /* Don't bother checking the return value; if the note
+ * was delayed, there can't have been a speed=0.
+ */
+ }
+ }
+}
+
+
+
+static int envelope_get_y(IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe)
+{
+#if 1
+ (void)envelope; //TODO: remove the parameter
+ return pe->value;
+#else
+ int ys, ye;
+ int ts, te;
+ int t;
+
+ if (pe->next_node <= 0)
+ return envelope->node_y[0] << IT_ENVELOPE_SHIFT;
+
+ if (pe->next_node >= envelope->n_nodes)
+ return envelope->node_y[envelope->n_nodes-1] << IT_ENVELOPE_SHIFT;
+
+ ys = envelope->node_y[pe->next_node-1] << IT_ENVELOPE_SHIFT;
+ ts = envelope->node_t[pe->next_node-1];
+ te = envelope->node_t[pe->next_node];
+
+ if (ts == te)
+ return ys;
+
+ ye = envelope->node_y[pe->next_node] << IT_ENVELOPE_SHIFT;
+
+ t = pe->tick;
+
+ return ys + (ye - ys) * (t - ts) / (te - ts);
+#endif
+}
+
+
+
+#if 0
+static int it_envelope_end(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe)
+{
+ if (pe->next_node >= envelope->n_nodes)
+ return 1;
+
+ if (pe->tick < envelope->node_t[pe->next_node]) return 0;
+
+ if ((envelope->flags & IT_ENVELOPE_LOOP_ON) &&
+ envelope->loop_end >= pe->next_node &&
+ envelope->node_t[envelope->loop_end] <= pe->tick) return 0;
+
+ if ((envelope->flags & IT_ENVELOPE_SUSTAIN_LOOP) &&
+ !(playing->flags & IT_PLAYING_SUSTAINOFF) &&
+ envelope->sus_loop_end >= pe->next_node &&
+ envelope->node_t[envelope->sus_loop_end] <= pe->tick) return 0;
+
+ if (envelope->node_t[envelope->n_nodes-1] <= pe->tick) return 1;
+
+ return 0;
+}
+#endif
+
+
+
+/* Returns 1 when fading should be initiated for a volume envelope. */
+static int update_it_envelope(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe, int flags)
+{
+ if (!(playing->enabled_envelopes & flags))
+ return 0;
+
+ ASSERT(envelope->n_nodes > 0);
+
+ if (pe->next_node <= 0)
+ pe->value = envelope->node_y[0] << IT_ENVELOPE_SHIFT;
+ else if (pe->next_node >= envelope->n_nodes) {
+ pe->value = envelope->node_y[envelope->n_nodes-1] << IT_ENVELOPE_SHIFT;
+ return 1;
+ } else {
+ int ys = envelope->node_y[pe->next_node-1] << IT_ENVELOPE_SHIFT;
+ int ts = envelope->node_t[pe->next_node-1];
+ int te = envelope->node_t[pe->next_node];
+
+ if (ts == te)
+ pe->value = ys;
+ else {
+ int ye = envelope->node_y[pe->next_node] << IT_ENVELOPE_SHIFT;
+ int t = pe->tick;
+
+ pe->value = ys + (ye - ys) * (t - ts) / (te - ts);
+ }
+ }
+
+ pe->tick++;
+ while (pe->tick >= envelope->node_t[pe->next_node]) {
+ pe->next_node++;
+ if ((envelope->flags & IT_ENVELOPE_SUSTAIN_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF)) {
+ if (pe->next_node > envelope->sus_loop_end) {
+ pe->next_node = envelope->sus_loop_start;
+ ASSERT(pe->next_node < envelope->n_nodes);
+ pe->tick = envelope->node_t[envelope->sus_loop_start];
+ return 0;
+ }
+ } else if (envelope->flags & IT_ENVELOPE_LOOP_ON) {
+ if (pe->next_node > envelope->loop_end) {
+ pe->next_node = envelope->loop_start;
+ ASSERT(pe->next_node < envelope->n_nodes);
+ pe->tick = envelope->node_t[envelope->loop_start];
+ return 0;
+ }
+ }
+ if (pe->next_node >= envelope->n_nodes)
+ return 0;
+ }
+ return 0;
+}
+
+
+
+static void update_it_envelopes(IT_PLAYING *playing)
+{
+ IT_ENVELOPE *envelope = &playing->env_instrument->volume_envelope;
+ IT_PLAYING_ENVELOPE *pe = &playing->volume_envelope;
+
+ if (update_it_envelope(playing, envelope, pe, IT_ENV_VOLUME)) {
+ playing->flags |= IT_PLAYING_FADING;
+ if (pe->value == 0)
+ playing->flags |= IT_PLAYING_DEAD;
+ }
+
+ update_it_envelope(playing, &playing->env_instrument->pan_envelope, &playing->pan_envelope, IT_ENV_PANNING);
+ update_it_envelope(playing, &playing->env_instrument->pitch_envelope, &playing->pitch_envelope, IT_ENV_PITCH);
+}
+
+
+
+static int xm_envelope_is_sustaining(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe)
+{
+ if ((envelope->flags & IT_ENVELOPE_SUSTAIN_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF))
+ if (envelope->sus_loop_start < envelope->n_nodes)
+ if (pe->tick == envelope->node_t[envelope->sus_loop_start])
+ return 1;
+ return 0;
+}
+
+
+
+static void update_xm_envelope(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe)
+{
+ if (!(envelope->flags & IT_ENVELOPE_ON))
+ return;
+
+ if (xm_envelope_is_sustaining(playing, envelope, pe))
+ return;
+
+ if (pe->tick >= envelope->node_t[envelope->n_nodes-1])
+ return;
+
+ pe->tick++;
+
+ /* pe->next_node must be kept up to date for envelope_get_y(). */
+ while (pe->tick > envelope->node_t[pe->next_node])
+ pe->next_node++;
+
+ if ((envelope->flags & IT_ENVELOPE_LOOP_ON) && envelope->loop_end < envelope->n_nodes) {
+ if (pe->tick == envelope->node_t[envelope->loop_end]) {
+ pe->next_node = MID(0, envelope->loop_start, envelope->n_nodes - 1);
+ pe->tick = envelope->node_t[pe->next_node];
+ }
+ }
+
+ xm_envelope_calculate_value(envelope, pe);
+}
+
+
+
+static void update_xm_envelopes(IT_PLAYING *playing)
+{
+ update_xm_envelope(playing, &playing->env_instrument->volume_envelope, &playing->volume_envelope);
+ update_xm_envelope(playing, &playing->env_instrument->pan_envelope, &playing->pan_envelope);
+}
+
+
+
+static void update_fadeout(DUMB_IT_SIGDATA *sigdata, IT_PLAYING *playing)
+{
+ if (playing->flags & IT_PLAYING_FADING) {
+ playing->fadeoutcount -= playing->env_instrument->fadeout;
+ if (playing->fadeoutcount <= 0) {
+ playing->fadeoutcount = 0;
+ if (!(sigdata->flags & IT_WAS_AN_XM))
+ playing->flags |= IT_PLAYING_DEAD;
+ }
+ }
+}
+
+static int apply_pan_envelope(IT_PLAYING *playing);
+static float calculate_volume(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume);
+
+static void playing_volume_setup(DUMB_IT_SIGRENDERER * sigrenderer, IT_PLAYING * playing, float invt2g)
+{
+ DUMB_IT_SIGDATA * sigdata = sigrenderer->sigdata;
+ int pan;
+ float volume, vol, span;
+
+ if ((sigrenderer->n_channels == 2) && (sigdata->flags & IT_STEREO)) {
+ pan = apply_pan_envelope(playing);
+ if ((sigdata->flags & IT_WAS_AN_S3M) && IT_IS_SURROUND_SHIFTED(pan)) {
+ volume = -1.0f * (float)(pan) * (1.0f / (64.0f * 256.0f));
+ } else {
+ volume = 1.0f;
+ }
+ span = (pan - (32<<8)) * sigdata->pan_separation * (1.0f / ((32<<8) * 128));
+ vol = volume;
+ if (!IT_IS_SURROUND_SHIFTED(pan)) vol *= 1.0f - span;
+ playing->float_volume[0] = vol;
+ vol = -vol;
+ if (!IT_IS_SURROUND_SHIFTED(pan)) vol += 2.0f * volume;
+ playing->float_volume[1] = vol;
+ } else {
+ if ((sigdata->flags & IT_STEREO) && (playing->sample->flags & IT_SAMPLE_STEREO)) {
+ pan = apply_pan_envelope(playing);
+ span = (pan - (32<<8)) * sigdata->pan_separation * (1.0f / ((32<<8) * 128));
+ vol = 0.5f;
+ if (!IT_IS_SURROUND_SHIFTED(pan)) vol *= 1.0f - span;
+ playing->float_volume[0] = vol;
+ if (!IT_IS_SURROUND_SHIFTED(pan)) vol = 2.0f - vol;
+ playing->float_volume[1] = vol;
+ } else {
+ playing->float_volume[0] = 1.0f;
+ playing->float_volume[1] = 1.0f;
+ }
+ }
+
+ vol = calculate_volume(sigrenderer, playing, 1.0f);
+ playing->float_volume[0] *= vol;
+ playing->float_volume[1] *= vol;
+
+ if (!sigrenderer->ramp_style || playing->ramp_volume[0] == 31337.0) {
+ playing->ramp_volume[0] = playing->float_volume[0];
+ playing->ramp_volume[1] = playing->float_volume[1];
+ playing->ramp_delta[0] = 0;
+ playing->ramp_delta[1] = 0;
+ } else {
+ playing->ramp_delta[0] = invt2g * (playing->float_volume[0] - playing->ramp_volume[0]);
+ playing->ramp_delta[1] = invt2g * (playing->float_volume[1] - playing->ramp_volume[1]);
+ }
+}
+
+static void process_playing(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float invt2g)
+{
+ DUMB_IT_SIGDATA * sigdata = sigrenderer->sigdata;
+
+ if (playing->instrument) {
+ if (sigdata->flags & IT_WAS_AN_XM)
+ update_xm_envelopes(playing);
+ else
+ update_it_envelopes(playing);
+ update_fadeout(sigdata, playing);
+ }
+
+ playing_volume_setup(sigrenderer, playing, invt2g);
+
+ if (sigdata->flags & IT_WAS_AN_XM) {
+ /* 'depth' is used to store the tick number for XM files. */
+ if (playing->sample_vibrato_depth < playing->sample->vibrato_rate)
+ playing->sample_vibrato_depth++;
+ } else {
+ playing->sample_vibrato_depth += playing->sample->vibrato_rate;
+ if (playing->sample_vibrato_depth > playing->sample->vibrato_depth << 8)
+ playing->sample_vibrato_depth = playing->sample->vibrato_depth << 8;
+ }
+
+ playing->sample_vibrato_time += playing->sample->vibrato_speed;
+}
+
+//static float log2(float x) {return (float)log(x)/(float)log(2.0f);}
+#ifndef __linux__
+static inline float log2(float x) {return (float)log(x)/(float)log(2.0f);}
+#endif
+
+static int delta_to_note(float delta, int base)
+{
+ float note;
+ note = log2(delta * 65536.f / (float)base)*12.0f+60.5f;
+ if (note > 119) note = 119;
+ else if (note < 0) note = 0;
+ return (int)note;
+}
+
+// Period table for Protracker octaves 0-5:
+static const unsigned short ProTrackerPeriodTable[6*12] =
+{
+ 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,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,113,
+ 107,101,95,90,85,80,75,71,67,63,60,56,
+ 53,50,47,45,42,40,37,35,33,31,30,28
+};
+
+
+static const unsigned short ProTrackerTunedPeriods[16*12] =
+{
+ 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,907,
+ 1700,1604,1514,1430,1348,1274,1202,1134,1070,1010,954,900,
+ 1688,1592,1504,1418,1340,1264,1194,1126,1064,1004,948,894,
+ 1676,1582,1492,1408,1330,1256,1184,1118,1056,996,940,888,
+ 1664,1570,1482,1398,1320,1246,1176,1110,1048,990,934,882,
+ 1652,1558,1472,1388,1310,1238,1168,1102,1040,982,926,874,
+ 1640,1548,1460,1378,1302,1228,1160,1094,1032,974,920,868,
+ 1628,1536,1450,1368,1292,1220,1150,1086,1026,968,914,862,
+ 1814,1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,
+ 1800,1700,1604,1514,1430,1350,1272,1202,1134,1070,1010,954,
+ 1788,1688,1592,1504,1418,1340,1264,1194,1126,1064,1004,948,
+ 1774,1676,1582,1492,1408,1330,1256,1184,1118,1056,996,940,
+ 1762,1664,1570,1482,1398,1320,1246,1176,1110,1048,988,934,
+ 1750,1652,1558,1472,1388,1310,1238,1168,1102,1040,982,926,
+ 1736,1640,1548,1460,1378,1302,1228,1160,1094,1032,974,920,
+ 1724,1628,1536,1450,1368,1292,1220,1150,1086,1026,968,914
+};
+
+static void process_all_playing(DUMB_IT_SIGRENDERER *sigrenderer)
+{
+ DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata;
+ int i;
+
+ float invt2g = 1.0f / ((float)TICK_TIME_DIVIDEND / (float)sigrenderer->tempo);
+
+ for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+ IT_CHANNEL *channel = &sigrenderer->channel[i];
+ IT_PLAYING *playing = channel->playing;
+
+ if (playing) {
+ int vibrato_shift;
+ switch (playing->vibrato_waveform)
+ {
+ default:
+ vibrato_shift = it_sine[playing->vibrato_time];
+ break;
+ case 1:
+ vibrato_shift = it_sawtooth[playing->vibrato_time];
+ break;
+ case 2:
+ vibrato_shift = it_squarewave[playing->vibrato_time];
+ break;
+ case 3:
+ vibrato_shift = (rand() % 129) - 64;
+ break;
+ case 4:
+ vibrato_shift = it_xm_squarewave[playing->vibrato_time];
+ break;
+ case 5:
+ vibrato_shift = it_xm_ramp[playing->vibrato_time];
+ break;
+ case 6:
+ vibrato_shift = it_xm_ramp[255-playing->vibrato_time];
+ break;
+ }
+ vibrato_shift *= playing->vibrato_n;
+ vibrato_shift *= playing->vibrato_depth;
+ vibrato_shift >>= 4;
+
+ if (sigdata->flags & IT_OLD_EFFECTS)
+ vibrato_shift = -vibrato_shift;
+
+ playing->volume = channel->volume;
+ playing->pan = channel->truepan;
+
+ if (playing->volume_offset) {
+ playing->volume += (playing->volume_offset * playing->volume) >> 7;
+ if (playing->volume > 64) {
+ if (playing->volume_offset < 0) playing->volume = 0;
+ else playing->volume = 64;
+ }
+ }
+
+ if (playing->panning_offset && !IT_IS_SURROUND_SHIFTED(playing->pan)) {
+ playing->pan += playing->panning_offset << IT_ENVELOPE_SHIFT;
+ if (playing->pan > 64 << IT_ENVELOPE_SHIFT) {
+ if (playing->panning_offset < 0) playing->pan = 0;
+ else playing->pan = 64 << IT_ENVELOPE_SHIFT;
+ }
+ }
+
+ if (sigdata->flags & IT_LINEAR_SLIDES) {
+ int currpitch = ((playing->note - 60) << 8) + playing->slide
+ + vibrato_shift
+ + playing->finetune;
+
+ /* We add a feature here, which is that of keeping the pitch
+ * within range. Otherwise it crashes. Trust me. It happened.
+ * The limit 32768 gives almost 11 octaves either way.
+ */
+ if (currpitch < -32768)
+ currpitch = -32768;
+ else if (currpitch > 32767)
+ currpitch = 32767;
+
+ playing->delta = (float)pow(DUMB_PITCH_BASE, currpitch);
+ playing->delta *= playing->sample->C5_speed * (1.f / 65536.0f);
+ } else {
+ int slide = playing->slide + vibrato_shift;
+
+ playing->delta = (float)pow(DUMB_PITCH_BASE, ((60 - playing->note) << 8) - playing->finetune );
+ /* playing->delta is 1.0 for C-5, 0.5 for C-6, etc. */
+
+ playing->delta *= 1.0f / playing->sample->C5_speed;
+
+ playing->delta -= slide / AMIGA_DIVISOR;
+
+ if (playing->delta < (1.0f / 65536.0f) / 32768.0f) {
+ // Should XM notes die if Amiga slides go out of range?
+ playing->flags |= IT_PLAYING_DEAD;
+ playing->delta = 1. / 32768.;
+ continue;
+ }
+
+ playing->delta = (1.0f / 65536.0f) / playing->delta;
+ }
+
+ if (playing->channel->glissando && playing->channel->toneporta && playing->channel->destnote < 120) {
+ playing->delta = (float)pow(DUMB_SEMITONE_BASE, delta_to_note(playing->delta, playing->sample->C5_speed) - 60)
+ * playing->sample->C5_speed * (1.f / 65536.f);
+ }
+
+ /*
+ if ( channel->arpeggio ) { // another FT2 bug...
+ if ((sigdata->flags & (IT_LINEAR_SLIDES|IT_WAS_AN_XM|IT_WAS_A_MOD)) == (IT_WAS_AN_XM|IT_LINEAR_SLIDES) &&
+ playing->flags & IT_PLAYING_SUSTAINOFF)
+ {
+ if ( channel->arpeggio > 0xFF )
+ playing->delta = playing->sample->C5_speed * (1.f / 65536.f);
+ }
+ else*/ playing->delta *= (float)pow(DUMB_SEMITONE_BASE, channel->arpeggio >> 8);/*
+ }*/
+
+ playing->filter_cutoff = channel->filter_cutoff;
+ playing->filter_resonance = channel->filter_resonance;
+ }
+ }
+
+ for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+ if (sigrenderer->channel[i].playing) {
+ process_playing(sigrenderer, sigrenderer->channel[i].playing, invt2g);
+ if (!(sigdata->flags & IT_WAS_AN_XM)) {
+ //if ((sigrenderer->channel[i].playing->flags & (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) == (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) {
+ // This change was made so Gxx would work correctly when a note faded out or whatever. Let's hope nothing else was broken by it.
+ if (sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD) {
+ free(sigrenderer->channel[i].playing);
+ sigrenderer->channel[i].playing = NULL;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+ if (sigrenderer->playing[i]) {
+ process_playing(sigrenderer, sigrenderer->playing[i], invt2g);
+ if (sigrenderer->playing[i]->flags & IT_PLAYING_DEAD) {
+ free(sigrenderer->playing[i]);
+ sigrenderer->playing[i] = NULL;
+ }
+ }
+ }
+}
+
+
+
+static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer)
+{
+ DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata;
+
+ // Set note vol/freq to vol/freq set for each channel
+
+ if (sigrenderer->speed && --sigrenderer->tick == 0) {
+ reset_tick_counts(sigrenderer);
+ sigrenderer->tick = sigrenderer->speed;
+ sigrenderer->rowcount--;
+ if (sigrenderer->rowcount == 0) {
+ sigrenderer->rowcount = 1;
+
+#ifdef BIT_ARRAY_BULLSHIT
+ if (sigrenderer->n_rows)
+ {
+#if 1
+ /*
+ if (bit_array_test(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row))
+ {
+ if (sigrenderer->callbacks->loop) {
+ if ((*sigrenderer->callbacks->loop)(sigrenderer->callbacks->loop_data))
+ return 1;
+ bit_array_reset(sigrenderer->played);
+ if (sigrenderer->speed == 0)
+ goto speed0; /* I love goto *
+ }
+ }
+ */
+#endif
+ bit_array_set(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row);
+ {
+ int n;
+ for (n = 0; n < DUMB_IT_N_CHANNELS; n++)
+ {
+ IT_CHANNEL * channel = &sigrenderer->channel[n];
+ if (channel->played_patjump)
+ {
+ if (channel->played_patjump_order == sigrenderer->order)
+ {
+ bit_array_set(channel->played_patjump, sigrenderer->row);
+ }
+ /*
+ else if ((channel->played_patjump_order & 0x7FFF) == sigrenderer->order)
+ {
+ channel->played_patjump_order |= 0x4000;
+ }
+ else if ((channel->played_patjump_order & 0x3FFF) == sigrenderer->order)
+ {
+ if ((sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM)
+ {
+ /* joy, was XM, pattern loop bug triggered break to row in same order
+ bit_array_mask(sigrenderer->played, channel->played_patjump, sigrenderer->order * 256);
+ }
+ bit_array_destroy(channel->played_patjump);
+ channel->played_patjump = 0;
+ channel->played_patjump_order = 0xFFFE;
+ }
+ */
+ else
+ {
+ bit_array_destroy(channel->played_patjump);
+ channel->played_patjump = 0;
+ channel->played_patjump_order = 0xFFFE;
+ }
+ }
+ }
+ }
+ }
+#endif
+
+ sigrenderer->processrow++;
+
+ if (sigrenderer->processrow >= sigrenderer->n_rows) {
+ IT_PATTERN *pattern;
+ int n;
+ int processorder = sigrenderer->processorder;
+
+ if ((sigrenderer->processrow|0xC00) == 0xFFFE + 1) { /* It was incremented above! */
+ sigrenderer->processrow = sigrenderer->breakrow;
+ sigrenderer->breakrow = 0;
+ for (n = 0; n < DUMB_IT_N_CHANNELS; n++) sigrenderer->channel[n].pat_loop_end_row = 0;
+ } else
+ sigrenderer->processrow = sigrenderer->breakrow;
+
+ if (sigrenderer->processorder == 0xFFFF)
+ sigrenderer->processorder = sigrenderer->order - 1;
+
+ for (;;) {
+ sigrenderer->processorder++;
+
+ if (sigrenderer->processorder >= sigdata->n_orders) {
+ sigrenderer->processorder = sigrenderer->restart_position;
+ if (sigrenderer->processorder >= sigdata->n_orders) {
+ /* Restarting beyond end. We'll loop for now. */
+ sigrenderer->processorder = -1;
+ continue;
+ }
+ }
+
+ n = sigdata->order[sigrenderer->processorder];
+
+ if (n < sigdata->n_patterns)
+ break;
+
+#ifdef INVALID_ORDERS_END_SONG
+ if (n != IT_ORDER_SKIP)
+#else
+ if (n == IT_ORDER_END)
+#endif
+ {
+ sigrenderer->processorder = sigrenderer->restart_position - 1;
+ }
+ }
+
+ pattern = &sigdata->pattern[n];
+
+ n = sigrenderer->n_rows;
+ sigrenderer->n_rows = pattern->n_rows;
+
+ if (sigrenderer->processrow >= sigrenderer->n_rows)
+ sigrenderer->processrow = 0;
+
+/** WARNING - everything pertaining to a new pattern initialised? */
+
+ sigrenderer->entry = sigrenderer->entry_start = pattern->entry;
+ sigrenderer->entry_end = sigrenderer->entry + pattern->n_entries;
+
+ /* If n_rows was 0, we're only just starting. Don't do anything weird here. */
+ /* added: process row check, for break to row spooniness */
+ if (n && (processorder == 0xFFFF ? sigrenderer->order > sigrenderer->processorder : sigrenderer->order >= sigrenderer->processorder)
+#ifdef BIT_ARRAY_BULLSHIT
+ && bit_array_test(sigrenderer->played, sigrenderer->processorder * 256 + sigrenderer->processrow)
+#endif
+ ) {
+ if (sigrenderer->callbacks->loop) {
+ if ((*sigrenderer->callbacks->loop)(sigrenderer->callbacks->loop_data))
+ return 1;
+#ifdef BIT_ARRAY_BULLSHIT
+ bit_array_reset(sigrenderer->played);
+#endif
+ if (sigrenderer->speed == 0)
+ goto speed0; /* I love goto */
+ }
+ }
+ sigrenderer->order = sigrenderer->processorder;
+
+ n = sigrenderer->processrow;
+ while (n) {
+ while (sigrenderer->entry < sigrenderer->entry_end) {
+ if (IT_IS_END_ROW(sigrenderer->entry)) {
+ sigrenderer->entry++;
+ break;
+ }
+ sigrenderer->entry++;
+ }
+ n--;
+ }
+ sigrenderer->row = sigrenderer->processrow;
+ } else {
+ if (sigrenderer->entry) {
+ while (sigrenderer->entry < sigrenderer->entry_end) {
+ if (IT_IS_END_ROW(sigrenderer->entry)) {
+ sigrenderer->entry++;
+ break;
+ }
+ sigrenderer->entry++;
+ }
+ sigrenderer->row++;
+ } else {
+#ifdef BIT_ARRAY_BULLSHIT
+ bit_array_clear(sigrenderer->played, sigrenderer->order * 256);
+#endif
+ sigrenderer->entry = sigrenderer->entry_start;
+ sigrenderer->row = 0;
+ }
+ }
+
+ if (!(sigdata->flags & IT_WAS_A_669))
+ reset_effects(sigrenderer);
+
+ {
+ IT_ENTRY *entry = sigrenderer->entry;
+ int ignore_cxx = 0;
+
+ while (entry < sigrenderer->entry_end && !IT_IS_END_ROW(entry))
+ ignore_cxx |= update_pattern_variables(sigrenderer, entry++);
+
+ entry = sigrenderer->entry;
+
+ while (entry < sigrenderer->entry_end && !IT_IS_END_ROW(entry))
+ if (process_entry(sigrenderer, entry++, sigdata->flags & IT_WAS_AN_XM ? 0 : ignore_cxx))
+ return 1;
+ }
+
+ if (!(sigdata->flags & IT_OLD_EFFECTS))
+ update_smooth_effects(sigrenderer);
+ } else {
+ {
+ IT_ENTRY *entry = sigrenderer->entry;
+
+ while (entry < sigrenderer->entry_end && !IT_IS_END_ROW(entry)) {
+ if (entry->mask & IT_ENTRY_EFFECT && entry->effect != IT_SET_SAMPLE_OFFSET)
+ process_effects(sigrenderer, entry, 0);
+ /* Don't bother checking the return value; if there
+ * was a pattern delay, there can't be a speed=0.
+ */
+ entry++;
+ }
+ }
+
+ update_effects(sigrenderer);
+ }
+ } else {
+ speed0:
+ update_effects(sigrenderer);
+ update_tick_counts(sigrenderer);
+ }
+
+ if (sigrenderer->globalvolume == 0) {
+ if (sigrenderer->callbacks->global_volume_zero) {
+ LONG_LONG t = sigrenderer->gvz_sub_time + ((LONG_LONG)TICK_TIME_DIVIDEND << 16) / sigrenderer->tempo;
+ sigrenderer->gvz_time += (int)(t >> 16);
+ sigrenderer->gvz_sub_time = (int)t & 65535;
+ if (sigrenderer->gvz_time >= 65536 * 12) {
+ if ((*sigrenderer->callbacks->global_volume_zero)(sigrenderer->callbacks->global_volume_zero_data))
+ return 1;
+ }
+ }
+ } else {
+ if (sigrenderer->callbacks->global_volume_zero) {
+ sigrenderer->gvz_time = 0;
+ sigrenderer->gvz_sub_time = 0;
+ }
+ }
+
+ process_all_playing(sigrenderer);
+
+ {
+ LONG_LONG t = sigrenderer->sub_time_left + ((LONG_LONG)TICK_TIME_DIVIDEND << 16) / sigrenderer->tempo;
+ sigrenderer->time_left += (int)(t >> 16);
+ sigrenderer->sub_time_left = (int)t & 65535;
+ }
+
+ return 0;
+}
+
+
+
+int dumb_it_max_to_mix = 64;
+
+static const int aiMODVol[] =
+{
+ 0,
+ 16, 24, 32, 48, 64, 80, 96, 112,
+ 128, 144, 160, 176, 192, 208, 224, 240,
+ 256, 272, 288, 304, 320, 336, 352, 368,
+ 384, 400, 416, 432, 448, 464, 480, 496,
+ 529, 545, 561, 577, 593, 609, 625, 641,
+ 657, 673, 689, 705, 721, 737, 753, 769,
+ 785, 801, 817, 833, 849, 865, 881, 897,
+ 913, 929, 945, 961, 977, 993, 1009, 1024
+};
+
+static const int aiPTMVolScaled[] =
+{
+ 0,
+ 31, 54, 73, 96, 111, 130, 153, 172,
+ 191, 206, 222, 237, 252, 275, 298, 317,
+ 336, 351, 370, 386, 401, 416, 428, 443,
+ 454, 466, 477, 489, 512, 531, 553, 573,
+ 592, 611, 626, 645, 660, 679, 695, 710,
+ 725, 740, 756, 767, 782, 798, 809, 820,
+ 836, 847, 859, 870, 881, 897, 908, 916,
+ 927, 939, 950, 962, 969, 983, 1005, 1024
+};
+
+static float calculate_volume(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume)
+{
+ if (volume != 0) {
+ int vol;
+
+ if (playing->channel->flags & IT_CHANNEL_MUTED)
+ return 0;
+
+ if ((playing->channel->tremor_time & 192) == 128)
+ return 0;
+
+ switch (playing->tremolo_waveform)
+ {
+ default:
+ vol = it_sine[playing->tremolo_time];
+ break;
+ case 1:
+ vol = it_sawtooth[playing->tremolo_time];
+ break;
+ case 2:
+ vol = it_squarewave[playing->tremolo_time];
+ break;
+ case 3:
+ vol = (rand() % 129) - 64;
+ break;
+ case 4:
+ vol = it_xm_squarewave[playing->vibrato_time];
+ break;
+ case 5:
+ vol = it_xm_ramp[playing->vibrato_time];
+ break;
+ case 6:
+ vol = it_xm_ramp[255-playing->vibrato_time];
+ break;
+ }
+ vol *= playing->tremolo_depth;
+
+ vol = (playing->volume << 5) + vol;
+
+ if (vol <= 0)
+ return 0;
+
+ if (vol > 64 << 5)
+ vol = 64 << 5;
+
+ if ( sigrenderer->sigdata->flags & IT_WAS_A_PTM )
+ {
+ int v = aiPTMVolScaled[ vol >> 5 ];
+ if ( vol < 64 << 5 )
+ {
+ int f = vol & ( ( 1 << 5 ) - 1 );
+ int f2 = ( 1 << 5 ) - f;
+ int v2 = aiPTMVolScaled[ ( vol >> 5 ) + 1 ];
+ v = ( v * f2 + v2 * f ) >> 5;
+ }
+ vol = v << 1;
+ }
+
+ volume *= vol; /* 64 << 5 */
+ volume *= playing->sample->global_volume; /* 64 */
+ volume *= playing->channel_volume; /* 64 */
+ volume *= sigrenderer->globalvolume; /* 128 */
+ volume *= sigrenderer->sigdata->mixing_volume; /* 128 */
+ volume *= 1.0f / ((64 << 5) * 64.0f * 64.0f * 128.0f * 128.0f);
+
+ if (volume && playing->instrument) {
+ if (playing->enabled_envelopes & IT_ENV_VOLUME) {
+ volume *= envelope_get_y(&playing->env_instrument->volume_envelope, &playing->volume_envelope);
+ volume *= 1.0f / (64 << IT_ENVELOPE_SHIFT);
+ }
+ volume *= playing->instrument->global_volume; /* 128 */
+ volume *= playing->fadeoutcount; /* 1024 */
+ volume *= 1.0f / (128.0f * 1024.0f);
+ }
+ }
+
+ return volume;
+}
+
+
+
+static int apply_pan_envelope(IT_PLAYING *playing)
+{
+ if (playing->pan <= 64 << IT_ENVELOPE_SHIFT) {
+ int pan;
+ if (playing->panbrello_depth) {
+ switch (playing->panbrello_waveform) {
+ default:
+ pan = it_sine[playing->panbrello_time];
+ break;
+ case 1:
+ pan = it_sawtooth[playing->panbrello_time];
+ break;
+ case 2:
+ pan = it_squarewave[playing->panbrello_time];
+ break;
+ case 3:
+ pan = playing->panbrello_random;
+ break;
+ }
+ pan *= playing->panbrello_depth << 3;
+
+ pan += playing->pan;
+ if (pan < 0) pan = 0;
+ else if (pan > 64 << IT_ENVELOPE_SHIFT) pan = 64 << IT_ENVELOPE_SHIFT;
+ } else {
+ pan = playing->pan;
+ }
+
+ if (playing->env_instrument && (playing->enabled_envelopes & IT_ENV_PANNING)) {
+ int p = envelope_get_y(&playing->env_instrument->pan_envelope, &playing->pan_envelope);
+ if (pan > 32 << IT_ENVELOPE_SHIFT)
+ p *= (64 << IT_ENVELOPE_SHIFT) - pan;
+ else
+ p *= pan;
+ pan += p >> (5 + IT_ENVELOPE_SHIFT);
+ }
+ return pan;
+ }
+ return playing->pan;
+}
+
+
+
+/* Note: if a click remover is provided, and store_end_sample is set, then
+ * the end point will be computed twice. This situation should not arise.
+ */
+static long render_playing(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume, float main_delta, float delta, long pos, long size, sample_t **samples, int store_end_sample, int *left_to_mix, int cr_record_which)
+{
+ int bits;
+
+ long size_rendered;
+
+ DUMB_VOLUME_RAMP_INFO lvol, rvol;
+
+ if (playing->flags & IT_PLAYING_DEAD)
+ return 0;
+
+ if (*left_to_mix <= 0)
+ volume = 0;
+
+#ifndef END_RAMPING
+ {
+ int quality = sigrenderer->resampling_quality;
+ if (playing->sample->max_resampling_quality >= 0 && quality > playing->sample->max_resampling_quality)
+ quality = playing->sample->max_resampling_quality;
+ playing->resampler.quality = quality;
+ }
+#endif
+
+ bits = playing->sample->flags & IT_SAMPLE_16BIT ? 16 : 8;
+
+ if (volume == 0) {
+ if (playing->sample->flags & IT_SAMPLE_STEREO)
+ size_rendered = dumb_resample_n_2_1(bits, &playing->resampler, NULL, size, 0, 0, delta);
+ else
+ size_rendered = dumb_resample_n_1_1(bits, &playing->resampler, NULL, size, 0, delta);
+ } else {
+ lvol.volume = playing->ramp_volume [0];
+ rvol.volume = playing->ramp_volume [1];
+ lvol.delta = playing->ramp_delta [0] * main_delta;
+ rvol.delta = playing->ramp_delta [1] * main_delta;
+ lvol.target = playing->float_volume [0];
+ rvol.target = playing->float_volume [1];
+ rvol.mix = lvol.mix = volume;
+ if (sigrenderer->n_channels == 2) {
+ if (playing->sample->flags & IT_SAMPLE_STEREO) {
+ if ((cr_record_which & 1) && sigrenderer->click_remover) {
+ sample_t click[2];
+ dumb_resample_get_current_sample_n_2_2(bits, &playing->resampler, &lvol, &rvol, click);
+ dumb_record_click(sigrenderer->click_remover[0], pos, click[0]);
+ dumb_record_click(sigrenderer->click_remover[1], pos, click[1]);
+ }
+ size_rendered = dumb_resample_n_2_2(bits, &playing->resampler, samples[0] + pos*2, size, &lvol, &rvol, delta);
+ if (store_end_sample) {
+ sample_t click[2];
+ dumb_resample_get_current_sample_n_2_2(bits, &playing->resampler, &lvol, &rvol, click);
+ samples[0][(pos + size_rendered) * 2] = click[0];
+ samples[0][(pos + size_rendered) * 2 + 1] = click[1];
+ }
+ if ((cr_record_which & 2) && sigrenderer->click_remover) {
+ sample_t click[2];
+ dumb_resample_get_current_sample_n_2_2(bits, &playing->resampler, &lvol, &rvol, click);
+ dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click[0]);
+ dumb_record_click(sigrenderer->click_remover[1], pos + size_rendered, -click[1]);
+ }
+ } else {
+ if ((cr_record_which & 1) && sigrenderer->click_remover) {
+ sample_t click[2];
+ dumb_resample_get_current_sample_n_1_2(bits, &playing->resampler, &lvol, &rvol, click);
+ dumb_record_click(sigrenderer->click_remover[0], pos, click[0]);
+ dumb_record_click(sigrenderer->click_remover[1], pos, click[1]);
+ }
+ size_rendered = dumb_resample_n_1_2(bits, &playing->resampler, samples[0] + pos*2, size, &lvol, &rvol, delta);
+ if (store_end_sample) {
+ sample_t click[2];
+ dumb_resample_get_current_sample_n_1_2(bits, &playing->resampler, &lvol, &rvol, click);
+ samples[0][(pos + size_rendered) * 2] = click[0];
+ samples[0][(pos + size_rendered) * 2 + 1] = click[1];
+ }
+ if ((cr_record_which & 2) && sigrenderer->click_remover) {
+ sample_t click[2];
+ dumb_resample_get_current_sample_n_1_2(bits, &playing->resampler, &lvol, &rvol, click);
+ dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click[0]);
+ dumb_record_click(sigrenderer->click_remover[1], pos + size_rendered, -click[1]);
+ }
+ }
+ } else {
+ if (playing->sample->flags & IT_SAMPLE_STEREO) {
+ if ((cr_record_which & 1) && sigrenderer->click_remover) {
+ sample_t click;
+ dumb_resample_get_current_sample_n_2_1(bits, &playing->resampler, &lvol, &rvol, &click);
+ dumb_record_click(sigrenderer->click_remover[0], pos, click);
+ }
+ size_rendered = dumb_resample_n_2_1(bits, &playing->resampler, samples[0] + pos, size, &lvol, &rvol, delta);
+ if (store_end_sample)
+ dumb_resample_get_current_sample_n_2_1(bits, &playing->resampler, &lvol, &rvol, &samples[0][pos + size_rendered]);
+ if ((cr_record_which & 2) && sigrenderer->click_remover) {
+ sample_t click;
+ dumb_resample_get_current_sample_n_2_1(bits, &playing->resampler, &lvol, &rvol, &click);
+ dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click);
+ }
+ } else {
+ if ((cr_record_which & 1) && sigrenderer->click_remover) {
+ sample_t click;
+ dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, &lvol, &click);
+ dumb_record_click(sigrenderer->click_remover[0], pos, click);
+ }
+ size_rendered = dumb_resample_n_1_1(bits, &playing->resampler, samples[0] + pos, size, &lvol, delta);
+ if (store_end_sample)
+ dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, &lvol, &samples[0][pos + size_rendered]);
+ if ((cr_record_which & 2) && sigrenderer->click_remover) {
+ sample_t click;
+ dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, &lvol, &click);
+ dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click);
+ }
+ }
+ }
+ playing->ramp_volume [0] = lvol.volume;
+ playing->ramp_volume [1] = rvol.volume;
+ (*left_to_mix)--;
+ }
+
+ if (playing->resampler.dir == 0)
+ playing->flags |= IT_PLAYING_DEAD;
+
+ return size_rendered;
+}
+
+#ifdef END_RAMPING
+#if 1
+static long render_playing_part(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, DUMB_VOLUME_RAMP_INFO * lvol, DUMB_VOLUME_RAMP_INFO * rvol, int bits, float delta, long pos, long size, sample_t **samples, int store_end_sample, int cr_record_which)
+{
+ long size_rendered = 0;
+
+ if (sigrenderer->n_channels == 2) {
+ if (playing->sample->flags & IT_SAMPLE_STEREO) {
+ if ((cr_record_which & 1) && sigrenderer->click_remover) {
+ sample_t click[2];
+ dumb_resample_get_current_sample_n_2_2(bits, &playing->resampler, lvol, rvol, click);
+ dumb_record_click(sigrenderer->click_remover[0], pos, click[0]);
+ dumb_record_click(sigrenderer->click_remover[1], pos, click[1]);
+ }
+ size_rendered = dumb_resample_n_2_2(bits, &playing->resampler, samples[0] + pos*2, size, lvol, rvol, delta);
+ if (store_end_sample) {
+ sample_t click[2];
+ dumb_resample_get_current_sample_n_2_2(bits, &playing->resampler, lvol, rvol, click);
+ samples[0][(pos + size_rendered) * 2] = click[0];
+ samples[0][(pos + size_rendered) * 2 + 1] = click[1];
+ }
+ if ((cr_record_which & 2) && sigrenderer->click_remover) {
+ sample_t click[2];
+ dumb_resample_get_current_sample_n_2_2(bits, &playing->resampler, lvol, rvol, click);
+ dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click[0]);
+ dumb_record_click(sigrenderer->click_remover[1], pos + size_rendered, -click[1]);
+ }
+ } else {
+ if ((cr_record_which & 1) && sigrenderer->click_remover) {
+ sample_t click[2];
+ dumb_resample_get_current_sample_n_1_2(bits, &playing->resampler, lvol, rvol, click);
+ dumb_record_click(sigrenderer->click_remover[0], pos, click[0]);
+ dumb_record_click(sigrenderer->click_remover[1], pos, click[1]);
+ }
+ size_rendered = dumb_resample_n_1_2(bits, &playing->resampler, samples[0] + pos*2, size, lvol, rvol, delta);
+ if (store_end_sample) {
+ sample_t click[2];
+ dumb_resample_get_current_sample_n_1_2(bits, &playing->resampler, lvol, rvol, click);
+ samples[0][(pos + size_rendered) * 2] = click[0];
+ samples[0][(pos + size_rendered) * 2 + 1] = click[1];
+ }
+ if ((cr_record_which & 2) && sigrenderer->click_remover) {
+ sample_t click[2];
+ dumb_resample_get_current_sample_n_1_2(bits, &playing->resampler, lvol, rvol, click);
+ dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click[0]);
+ dumb_record_click(sigrenderer->click_remover[1], pos + size_rendered, -click[1]);
+ }
+ }
+ } else {
+ if (playing->sample->flags & IT_SAMPLE_STEREO) {
+ if ((cr_record_which & 1) && sigrenderer->click_remover) {
+ sample_t click;
+ dumb_resample_get_current_sample_n_2_1(bits, &playing->resampler, lvol, rvol, &click);
+ dumb_record_click(sigrenderer->click_remover[0], pos, click);
+ }
+ size_rendered = dumb_resample_n_2_1(bits, &playing->resampler, samples[0] + pos, size, lvol, rvol, delta);
+ if (store_end_sample)
+ dumb_resample_get_current_sample_n_2_1(bits, &playing->resampler, lvol, rvol, &samples[0][pos + size_rendered]);
+ if ((cr_record_which & 2) && sigrenderer->click_remover) {
+ sample_t click;
+ dumb_resample_get_current_sample_n_2_1(bits, &playing->resampler, lvol, rvol, &click);
+ dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click);
+ }
+ } else {
+ if ((cr_record_which & 1) && sigrenderer->click_remover) {
+ sample_t click;
+ dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, lvol, &click);
+ dumb_record_click(sigrenderer->click_remover[0], pos, click);
+ }
+ size_rendered = dumb_resample_n_1_1(bits, &playing->resampler, samples[0] + pos, size, lvol, delta);
+ if (store_end_sample)
+ dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, lvol, &samples[0][pos + size_rendered]);
+ if ((cr_record_which & 2) && sigrenderer->click_remover) {
+ sample_t click;
+ dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, lvol, &click);
+ dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click);
+ }
+ }
+ }
+ return size_rendered;
+}
+
+static long render_playing_ramp(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume, float main_delta, float delta, long pos, long size, sample_t **samples, int store_end_sample, int *left_to_mix, int ramp_style)
+{
+ int bits;
+
+ long size_rendered;
+
+ DUMB_VOLUME_RAMP_INFO lvol, rvol;
+
+ if (!size) return 0;
+
+ if (playing->flags & IT_PLAYING_DEAD)
+ return 0;
+
+ if (*left_to_mix <= 0)
+ volume = 0;
+
+ {
+ int quality = sigrenderer->resampling_quality;
+ if (playing->sample->max_resampling_quality >= 0 && quality > playing->sample->max_resampling_quality)
+ quality = playing->sample->max_resampling_quality;
+ playing->resampler.quality = quality;
+ }
+
+ bits = playing->sample->flags & IT_SAMPLE_16BIT ? 16 : 8;
+ size_rendered = size;
+
+ if (volume == 0) {
+ if (playing->declick_stage < 2) {
+ if (playing->sample->flags & IT_SAMPLE_STEREO)
+ size_rendered = dumb_resample_n_2_1(bits, &playing->resampler, NULL, size, 0, 0, delta);
+ else
+ size_rendered = dumb_resample_n_1_1(bits, &playing->resampler, NULL, size, 0, delta);
+ } else {
+ playing->declick_stage = 3;
+ }
+ } else {
+ lvol.volume = playing->ramp_volume [0];
+ rvol.volume = playing->ramp_volume [1];
+ lvol.delta = playing->ramp_delta [0] * main_delta;
+ rvol.delta = playing->ramp_delta [1] * main_delta;
+ lvol.target = playing->float_volume [0];
+ rvol.target = playing->float_volume [1];
+ rvol.mix = lvol.mix = volume;
+ if (ramp_style) {
+ if (playing->declick_stage == 1) {
+ size_rendered = render_playing_part(sigrenderer, playing, &lvol, &rvol, bits, delta, pos, size, samples, store_end_sample, 3);
+ } else if (playing->declick_stage == 0 || playing->declick_stage == 2) {
+ float declick_count = ((ramp_style == 2) ? 327.68 : 49.152) / main_delta; /* 5ms / 0.75ms */
+ float declick_remain = playing->declick_volume * declick_count;
+ float declick_dir = -1;
+ float declick_target;
+ int remain;
+ DUMB_VOLUME_RAMP_INFO declick_lvol, declick_rvol;
+ if (playing->declick_stage == 0) {
+ declick_remain = declick_count - declick_remain;
+ declick_dir = 1;
+ }
+ if (size < declick_remain) declick_remain = size;
+ remain = declick_remain;
+ declick_target = playing->declick_volume + declick_dir / declick_count * declick_remain;
+ declick_lvol.volume = lvol.volume * playing->declick_volume;
+ declick_rvol.volume = rvol.volume * playing->declick_volume;
+ lvol.volume += lvol.delta * declick_remain;
+ rvol.volume += rvol.delta * declick_remain;
+ declick_lvol.target = lvol.volume * declick_target;
+ declick_rvol.target = rvol.volume * declick_target;
+ declick_lvol.delta = (declick_lvol.target - declick_lvol.volume) / declick_remain;
+ declick_rvol.delta = (declick_rvol.target - declick_rvol.volume) / declick_remain;
+ declick_lvol.mix = declick_rvol.mix = volume;
+ if (remain < size) {
+ size_rendered = render_playing_part(sigrenderer, playing, &declick_lvol, &declick_rvol, bits, delta, pos, remain, samples, 0, 1);
+ if (size_rendered == remain) {
+ if (playing->declick_stage == 0) {
+ size_rendered += render_playing_part(sigrenderer, playing, &lvol, &rvol, bits, delta, pos + remain, size - remain, samples, store_end_sample, 2);
+ } else {
+ size_rendered = size;
+ }
+ }
+ playing->declick_stage++;
+ playing->declick_volume = 1;
+ } else {
+ size_rendered = render_playing_part(sigrenderer, playing, &declick_lvol, &declick_rvol, bits, delta, pos, remain, samples, store_end_sample, 3);
+ playing->declick_volume = declick_target;
+ }
+ } else /*if (playing->declick_stage == 3)*/ {
+ (*left_to_mix)++;
+ }
+ } else if (playing->declick_stage < 2) {
+ size_rendered = render_playing_part(sigrenderer, playing, &lvol, &rvol, bits, delta, pos, size, samples, store_end_sample, 3);
+ } else {
+ playing->declick_stage = 3;
+ (*left_to_mix)++;
+ }
+ playing->ramp_volume [0] = lvol.volume;
+ playing->ramp_volume [1] = rvol.volume;
+ (*left_to_mix)--;
+ }
+
+ if (playing->resampler.dir == 0)
+ playing->flags |= IT_PLAYING_DEAD;
+
+ return size_rendered;
+}
+#else
+static long render_playing_ramp(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume, float main_delta, float delta, long pos, long size, sample_t **samples, int store_end_sample, int *left_to_mix, int ramp_style)
+{
+ long rv, trv;
+ int l_t_m_temp, cr_which;
+ if (!size) return 0;
+
+ rv = 0;
+ cr_which = 3;
+
+ {
+ int quality = sigrenderer->resampling_quality;
+ if (playing->sample->max_resampling_quality >= 0 && quality > playing->sample->max_resampling_quality)
+ quality = playing->sample->max_resampling_quality;
+ playing->resampler.quality = quality;
+ }
+
+ if (ramp_style)
+ {
+ if (playing->declick_stage == 0) {
+ /* ramp up! */
+ cr_which = 1;
+ while (playing->declick_stage == 0 && size) {
+ l_t_m_temp = *left_to_mix;
+ if (size == 1) cr_which |= 2;
+ trv = render_playing(sigrenderer, playing, volume * playing->declick_volume, main_delta, delta, pos, 1, samples, store_end_sample, &l_t_m_temp, cr_which);
+ cr_which &= 2;
+ if (ramp_style == 2) playing->declick_volume += (1.f / 256.f) * main_delta;
+ else playing->declick_volume *= (float)pow(2.0f, main_delta);
+ if (playing->declick_volume >= 1.0f) {
+ playing->declick_volume = 1.0f;
+ playing->declick_stage = 1;
+ }
+ if (!trv) {
+ *left_to_mix = l_t_m_temp;
+ return rv;
+ }
+ rv += trv;
+ pos += trv;
+ size -= trv;
+ }
+
+ if (!size) {
+ *left_to_mix = l_t_m_temp;
+ return rv;
+ }
+
+ cr_which = 2;
+
+ }
+#ifdef RAMP_DOWN
+ else if (playing->declick_stage == 2) {
+ float halflife = pow(0.5, 1.0 / 64.0 * main_delta);
+ cr_which = 1;
+ while (playing->declick_stage == 2 && size) {
+ l_t_m_temp = *left_to_mix;
+ if (size == 1) cr_which |= 2;
+ trv = render_playing(sigrenderer, playing, volume * playing->declick_volume, main_delta, delta, pos, 1, samples, store_end_sample, &l_t_m_temp, cr_which);
+ cr_which &= 2;
+ /*if (ramp_style == 2) playing->declick_volume -= (1.f / 256.f) * main_delta;
+ else playing->declick_volume *= (float)pow(0.5f, main_delta);*/
+ playing->declick_volume *= halflife;
+ if (playing->declick_volume < (1.f / 256.f)) {
+ playing->declick_stage = 3;
+ }
+ if (!trv) {
+ *left_to_mix = l_t_m_temp;
+ return rv;
+ }
+ rv += trv;
+ pos += trv;
+ size -= trv;
+ }
+
+ if (size) rv += render_playing(sigrenderer, playing, volume * playing->declick_volume, main_delta, delta, pos, 1, samples, store_end_sample, left_to_mix, 2);
+ else *left_to_mix = l_t_m_temp;
+
+ return rv;
+
+ } else if (playing->declick_stage == 3) {
+ return size;
+ }
+#endif
+ } else if (playing->declick_stage >= 2) {
+ playing->declick_stage = 3;
+ return size;
+ }
+
+ return rv + render_playing(sigrenderer, playing, volume, main_delta, delta, pos, size, samples, store_end_sample, left_to_mix, cr_which);
+}
+#endif
+#else
+#define render_playing_ramp(sigrenderer, playing, volume, main_delta, delta, pos, size, samples, store_end_sample, left_to_mix, ramp_style) render_playing(sigrenderer, playing, volume, main_delta, delta, pos, size, samples, store_end_sample, left_to_mix, 3)
+#endif
+
+typedef struct IT_TO_MIX
+{
+ IT_PLAYING *playing;
+ float volume;
+}
+IT_TO_MIX;
+
+
+
+static int it_to_mix_compare(const void *e1, const void *e2)
+{
+ if (((const IT_TO_MIX *)e1)->volume > ((const IT_TO_MIX *)e2)->volume)
+ return -1;
+
+ if (((const IT_TO_MIX *)e1)->volume < ((const IT_TO_MIX *)e2)->volume)
+ return 1;
+
+ return 0;
+}
+
+
+
+static void apply_pitch_modifications(DUMB_IT_SIGDATA *sigdata, IT_PLAYING *playing, float *delta, int *cutoff)
+{
+ {
+ int sample_vibrato_shift;
+ switch (playing->sample_vibrato_waveform)
+ {
+ default:
+ sample_vibrato_shift = it_sine[playing->sample_vibrato_time];
+ break;
+ case 1:
+ sample_vibrato_shift = it_sawtooth[playing->sample_vibrato_time];
+ break;
+ case 2:
+ sample_vibrato_shift = it_squarewave[playing->sample_vibrato_time];
+ break;
+ case 3:
+ sample_vibrato_shift = (rand() % 129) - 64;
+ break;
+ case 4:
+ sample_vibrato_shift = it_xm_squarewave[playing->sample_vibrato_time];
+ break;
+ case 5:
+ sample_vibrato_shift = it_xm_ramp[playing->sample_vibrato_time];
+ break;
+ case 6:
+ sample_vibrato_shift = it_xm_ramp[255-playing->sample_vibrato_time];
+ break;
+ }
+
+ if (sigdata->flags & IT_WAS_AN_XM) {
+ int depth = playing->sample->vibrato_depth; /* True depth */
+ if (playing->sample->vibrato_rate) {
+ depth *= playing->sample_vibrato_depth; /* Tick number */
+ depth /= playing->sample->vibrato_rate; /* XM sweep */
+ }
+ sample_vibrato_shift *= depth;
+ } else
+ sample_vibrato_shift *= playing->sample_vibrato_depth >> 8;
+
+ sample_vibrato_shift >>= 4;
+
+ if (sample_vibrato_shift) {
+ if ((sigdata->flags & IT_LINEAR_SLIDES) || !(sigdata->flags & IT_WAS_AN_XM))
+ *delta *= (float)pow(DUMB_PITCH_BASE, sample_vibrato_shift);
+ else {
+ /* complicated! */
+ float scale = *delta / playing->delta;
+
+ *delta = (1.0f / 65536.0f) / playing->delta;
+
+ *delta -= sample_vibrato_shift / AMIGA_DIVISOR;
+
+ if (*delta < (1.0f / 65536.0f) / 32767.0f) {
+ *delta = (1.0f / 65536.0f) / 32767.0f;
+ }
+
+ *delta = (1.0f / 65536.0f) / *delta * scale;
+ }
+ }
+ }
+
+ if (playing->env_instrument &&
+ (playing->enabled_envelopes & IT_ENV_PITCH))
+ {
+ int p = envelope_get_y(&playing->env_instrument->pitch_envelope, &playing->pitch_envelope);
+ if (playing->env_instrument->pitch_envelope.flags & IT_ENVELOPE_PITCH_IS_FILTER)
+ *cutoff = (*cutoff * (p+(32<<IT_ENVELOPE_SHIFT))) >> (6 + IT_ENVELOPE_SHIFT);
+ else
+ *delta *= (float)pow(DUMB_PITCH_BASE, p >> (IT_ENVELOPE_SHIFT - 7));
+ }
+}
+
+
+
+static void render(DUMB_IT_SIGRENDERER *sigrenderer, float volume, float delta, long pos, long size, sample_t **samples)
+{
+ int i;
+
+ int n_to_mix = 0;
+ IT_TO_MIX to_mix[DUMB_IT_TOTAL_CHANNELS];
+ int left_to_mix = dumb_it_max_to_mix;
+
+ sample_t **samples_to_filter = NULL;
+
+ int ramp_style = sigrenderer->ramp_style;
+
+ //int max_output = sigrenderer->max_output;
+
+ if (ramp_style > 2) {
+ if ((sigrenderer->sigdata->flags & (IT_WAS_AN_XM | IT_WAS_A_MOD)) == IT_WAS_AN_XM) ramp_style = 2;
+ else ramp_style -= 3;
+ }
+
+ for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+ if (sigrenderer->channel[i].playing && !(sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD)) {
+ to_mix[n_to_mix].playing = sigrenderer->channel[i].playing;
+ to_mix[n_to_mix].volume = volume == 0 ? 0 : calculate_volume(sigrenderer, sigrenderer->channel[i].playing, volume);
+ n_to_mix++;
+ }
+ }
+
+ for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+ if (sigrenderer->playing[i]) { /* Won't be dead; it would have been freed. */
+ to_mix[n_to_mix].playing = sigrenderer->playing[i];
+ to_mix[n_to_mix].volume = volume == 0 ? 0 : calculate_volume(sigrenderer, sigrenderer->playing[i], volume);
+ n_to_mix++;
+ }
+ }
+
+ if (volume != 0)
+ qsort(to_mix, n_to_mix, sizeof(IT_TO_MIX), &it_to_mix_compare);
+
+ for (i = 0; i < n_to_mix; i++) {
+ IT_PLAYING *playing = to_mix[i].playing;
+ float note_delta = delta * playing->delta;
+ int cutoff = playing->filter_cutoff << IT_ENVELOPE_SHIFT;
+ //int output = min( playing->output, max_output );
+
+ apply_pitch_modifications(sigrenderer->sigdata, playing, &note_delta, &cutoff);
+
+ if (cutoff != 127 << IT_ENVELOPE_SHIFT || playing->filter_resonance != 0) {
+ playing->true_filter_cutoff = cutoff;
+ playing->true_filter_resonance = playing->filter_resonance;
+ }
+
+ if (volume && (playing->true_filter_cutoff != 127 << IT_ENVELOPE_SHIFT || playing->true_filter_resonance != 0)) {
+ if (!samples_to_filter) {
+ samples_to_filter = allocate_sample_buffer(sigrenderer->n_channels, size + 1);
+ if (!samples_to_filter) {
+ render_playing_ramp(sigrenderer, playing, 0, delta, note_delta, pos, size, NULL, 0, &left_to_mix, ramp_style);
+ continue;
+ }
+ }
+ {
+ long size_rendered;
+ DUMB_CLICK_REMOVER **cr = sigrenderer->click_remover;
+ dumb_silence(samples_to_filter[0], sigrenderer->n_channels * (size + 1));
+ sigrenderer->click_remover = NULL;
+ size_rendered = render_playing_ramp(sigrenderer, playing, volume, delta, note_delta, 0, size, samples_to_filter, 1, &left_to_mix, ramp_style);
+ sigrenderer->click_remover = cr;
+ if (sigrenderer->n_channels == 2) {
+ it_filter(cr ? cr[0] : NULL, &playing->filter_state[0], samples[0 /*output*/], pos, samples_to_filter[0], size_rendered,
+ 2, (int)(65536.0f/delta), playing->true_filter_cutoff, playing->true_filter_resonance);
+ it_filter(cr ? cr[1] : NULL, &playing->filter_state[1], samples[0 /*output*/]+1, pos, samples_to_filter[0]+1, size_rendered,
+ 2, (int)(65536.0f/delta), playing->true_filter_cutoff, playing->true_filter_resonance);
+ } else {
+ it_filter(cr ? cr[0] : NULL, &playing->filter_state[0], samples[0 /*output*/], pos, samples_to_filter[0], size_rendered,
+ 1, (int)(65536.0f/delta), playing->true_filter_cutoff, playing->true_filter_resonance);
+ }
+ // FIXME: filtering is not prevented by low left_to_mix!
+ // FIXME: change 'warning' to 'FIXME' everywhere
+ }
+ } else {
+ it_reset_filter_state(&playing->filter_state[0]);
+ it_reset_filter_state(&playing->filter_state[1]);
+ render_playing_ramp(sigrenderer, playing, volume, delta, note_delta, pos, size, samples /*&samples[output]*/, 0, &left_to_mix, ramp_style);
+ }
+ }
+
+ destroy_sample_buffer(samples_to_filter);
+
+ for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+ if (sigrenderer->channel[i].playing) {
+ //if ((sigrenderer->channel[i].playing->flags & (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) == (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) {
+ // This change was made so Gxx would work correctly when a note faded out or whatever. Let's hope nothing else was broken by it.
+ if (
+#ifdef RAMP_DOWN
+ (sigrenderer->channel[i].playing->declick_stage == 3) ||
+#endif
+ (sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD)) {
+ free(sigrenderer->channel[i].playing);
+ sigrenderer->channel[i].playing = NULL;
+ }
+ }
+ }
+
+ for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+ if (sigrenderer->playing[i]) {
+ if (
+#ifdef RAMP_DOWN
+ (sigrenderer->playing[i]->declick_stage == 3) ||
+#endif
+ (sigrenderer->playing[i]->flags & IT_PLAYING_DEAD)) {
+ free(sigrenderer->playing[i]);
+ sigrenderer->playing[i] = NULL;
+ }
+ }
+ }
+}
+
+
+
+static DUMB_IT_SIGRENDERER *init_sigrenderer(DUMB_IT_SIGDATA *sigdata, int n_channels, int startorder, IT_CALLBACKS *callbacks, DUMB_CLICK_REMOVER **cr)
+{
+ DUMB_IT_SIGRENDERER *sigrenderer;
+ int i;
+
+ if (startorder > sigdata->n_orders) {
+ free(callbacks);
+ dumb_destroy_click_remover_array(n_channels, cr);
+ return NULL;
+ }
+
+ sigrenderer = malloc(sizeof(*sigrenderer));
+ if (!sigrenderer) {
+ free(callbacks);
+ dumb_destroy_click_remover_array(n_channels, cr);
+ return NULL;
+ }
+
+ sigrenderer->callbacks = callbacks;
+ sigrenderer->click_remover = cr;
+
+ sigrenderer->sigdata = sigdata;
+ sigrenderer->n_channels = n_channels;
+ sigrenderer->resampling_quality = dumb_resampling_quality;
+ sigrenderer->ramp_style = 0;
+ sigrenderer->globalvolume = sigdata->global_volume;
+ sigrenderer->tempo = sigdata->tempo;
+
+ for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+ IT_CHANNEL *channel = &sigrenderer->channel[i];
+#if IT_CHANNEL_MUTED != 1
+#error this is wrong
+#endif
+ channel->flags = sigdata->channel_pan[i] >> 7;
+ channel->volume = (sigdata->flags & IT_WAS_AN_XM) ? 0 : 64;
+ channel->pan = sigdata->channel_pan[i] & 0x7F;
+ channel->truepan = channel->pan << IT_ENVELOPE_SHIFT;
+ channel->channelvolume = sigdata->channel_volume[i];
+ channel->instrument = 0;
+ channel->sample = 0;
+ channel->note = 0;
+ channel->SFmacro = 0;
+ channel->filter_cutoff = 127;
+ channel->filter_resonance = 0;
+ channel->new_note_action = 0xFF;
+ channel->xm_retrig = 0;
+ channel->retrig_tick = 0;
+ channel->tremor_time = 0;
+ channel->vibrato_waveform = 0;
+ channel->tremolo_waveform = 0;
+ channel->panbrello_waveform = 0;
+ channel->glissando = 0;
+ channel->toneslide = 0;
+ channel->ptm_toneslide = 0;
+ channel->ptm_last_toneslide = 0;
+ channel->midi_state = 0;
+ channel->lastvolslide = 0;
+ channel->lastDKL = 0;
+ channel->lastEF = 0;
+ channel->lastG = 0;
+ channel->lastHspeed = 0;
+ channel->lastHdepth = 0;
+ channel->lastRspeed = 0;
+ channel->lastRdepth = 0;
+ channel->lastYspeed = 0;
+ channel->lastYdepth = 0;
+ channel->lastI = 0;
+ channel->lastJ = 0;
+ channel->lastN = 0;
+ channel->lastO = 0;
+ channel->high_offset = 0;
+ channel->lastP = 0;
+ channel->lastQ = 0;
+ channel->lastS = 0;
+ channel->pat_loop_row = 0;
+ channel->pat_loop_count = 0;
+ channel->pat_loop_end_row = 0;
+ channel->lastW = 0;
+ channel->xm_lastE1 = 0;
+ channel->xm_lastE2 = 0;
+ channel->xm_lastEA = 0;
+ channel->xm_lastEB = 0;
+ channel->xm_lastX1 = 0;
+ channel->xm_lastX2 = 0;
+ channel->playing = NULL;
+#ifdef BIT_ARRAY_BULLSHIT
+ channel->played_patjump = NULL;
+ channel->played_patjump_order = 0xFFFE;
+#endif
+ //channel->output = 0;
+ }
+
+ if (sigdata->flags & IT_WAS_A_669)
+ reset_effects(sigrenderer);
+
+ for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++)
+ sigrenderer->playing[i] = NULL;
+
+ sigrenderer->speed = sigdata->speed;
+
+ sigrenderer->processrow = 0xFFFE;
+ sigrenderer->n_rows = 0;
+ sigrenderer->breakrow = 0;
+ sigrenderer->rowcount = 1;
+ sigrenderer->order = startorder;
+ /* meh!
+ if (startorder > 0) {
+ int n;
+ for (n = startorder - 1; n >= 0; n--) {
+ if (sigdata->order[n] > sigdata->n_patterns) {
+ sigrenderer->restart_position = n + 1;
+ break;
+ }
+ }
+ }
+ */
+ if (startorder > 0) {
+ sigrenderer->restart_position = startorder;
+ } else {
+ sigrenderer->restart_position = sigdata->restart_position;
+ }
+
+ sigrenderer->row = 0;
+ sigrenderer->processorder = startorder - 1;
+ sigrenderer->tick = 1;
+
+ {
+ int order;
+ for (order = 0; order < sigdata->n_orders; order++) {
+ int n = sigdata->order[order];
+ if (n < sigdata->n_patterns) goto found_valid_order;
+#ifdef INVALID_ORDERS_END_SONG
+ if (n != IT_ORDER_SKIP)
+#else
+ if (n == IT_ORDER_END)
+#endif
+ break;
+ }
+ /* If we get here, there were no valid orders in the song. */
+ _dumb_it_end_sigrenderer(sigrenderer);
+ return NULL;
+ }
+ found_valid_order:
+
+ sigrenderer->time_left = 0;
+ sigrenderer->sub_time_left = 0;
+
+#ifdef BIT_ARRAY_BULLSHIT
+ sigrenderer->played = bit_array_create(sigdata->n_orders * 256);
+#endif
+
+ sigrenderer->gvz_time = 0;
+ sigrenderer->gvz_sub_time = 0;
+
+ //sigrenderer->max_output = 0;
+
+ return sigrenderer;
+}
+
+
+void dumb_it_set_resampling_quality(DUMB_IT_SIGRENDERER * sigrenderer, int quality)
+{
+ if (sigrenderer && quality >= 0 && quality < DUMB_RQ_N_LEVELS)
+ {
+ int i;
+ sigrenderer->resampling_quality = quality;
+ for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+ if (sigrenderer->channel[i].playing)
+ {
+ IT_PLAYING * playing = sigrenderer->channel[i].playing;
+ playing->resampling_quality = quality;
+ playing->resampler.quality = quality;
+ }
+ }
+ for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+ if (sigrenderer->playing[i]) {
+ IT_PLAYING * playing = sigrenderer->playing[i];
+ playing->resampling_quality = quality;
+ playing->resampler.quality = quality;
+ }
+ }
+ }
+}
+
+
+void dumb_it_set_ramp_style(DUMB_IT_SIGRENDERER * sigrenderer, int ramp_style) {
+ if (sigrenderer && ramp_style >= 0 && ramp_style <= 4) {
+ sigrenderer->ramp_style = ramp_style;
+ }
+}
+
+
+void dumb_it_set_loop_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data)
+{
+ if (sigrenderer) {
+ sigrenderer->callbacks->loop = callback;
+ sigrenderer->callbacks->loop_data = data;
+ }
+}
+
+
+
+void dumb_it_set_xm_speed_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data)
+{
+ if (sigrenderer) {
+ sigrenderer->callbacks->xm_speed_zero = callback;
+ sigrenderer->callbacks->xm_speed_zero_data = data;
+ }
+}
+
+
+
+void dumb_it_set_midi_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data, int channel, unsigned char midi_byte), void *data)
+{
+ if (sigrenderer) {
+ sigrenderer->callbacks->midi = callback;
+ sigrenderer->callbacks->midi_data = data;
+ }
+}
+
+
+
+void dumb_it_set_global_volume_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data)
+{
+ if (sigrenderer) {
+ sigrenderer->callbacks->global_volume_zero = callback;
+ sigrenderer->callbacks->global_volume_zero_data = data;
+ }
+}
+
+
+
+static IT_CALLBACKS *create_callbacks(void)
+{
+ IT_CALLBACKS *callbacks = malloc(sizeof(*callbacks));
+ if (!callbacks) return NULL;
+ callbacks->loop = NULL;
+ callbacks->xm_speed_zero = NULL;
+ callbacks->midi = NULL;
+ callbacks->global_volume_zero = NULL;
+ return callbacks;
+}
+
+
+
+static DUMB_IT_SIGRENDERER *dumb_it_init_sigrenderer(DUMB_IT_SIGDATA *sigdata, int n_channels, int startorder)
+{
+ IT_CALLBACKS *callbacks;
+
+ if (!sigdata) return NULL;
+
+ callbacks = create_callbacks();
+ if (!callbacks) return NULL;
+
+ return init_sigrenderer(sigdata, n_channels, startorder, callbacks,
+ dumb_create_click_remover_array(n_channels));
+}
+
+
+
+DUH_SIGRENDERER *dumb_it_start_at_order(DUH *duh, int n_channels, int startorder)
+{
+ DUMB_IT_SIGDATA *itsd = duh_get_it_sigdata(duh);
+ DUMB_IT_SIGRENDERER *itsr = dumb_it_init_sigrenderer(itsd, n_channels, startorder);
+ /*duh->length = dumb_it_build_checkpoints(itsd, startorder);*/
+ return duh_encapsulate_it_sigrenderer(itsr, n_channels, 0);
+}
+
+
+
+static sigrenderer_t *it_start_sigrenderer(DUH *duh, sigdata_t *vsigdata, int n_channels, long pos)
+{
+ DUMB_IT_SIGDATA *sigdata = vsigdata;
+ DUMB_IT_SIGRENDERER *sigrenderer;
+
+ (void)duh;
+
+ {
+ IT_CALLBACKS *callbacks = create_callbacks();
+ if (!callbacks) return NULL;
+
+ if (sigdata->checkpoint) {
+ IT_CHECKPOINT *checkpoint = sigdata->checkpoint;
+ while (checkpoint->next && checkpoint->next->time < pos)
+ checkpoint = checkpoint->next;
+ sigrenderer = dup_sigrenderer(checkpoint->sigrenderer, n_channels, callbacks);
+ if (!sigrenderer) return NULL;
+ sigrenderer->click_remover = dumb_create_click_remover_array(n_channels);
+ pos -= checkpoint->time;
+ } else {
+ sigrenderer = init_sigrenderer(sigdata, n_channels, 0, callbacks,
+ dumb_create_click_remover_array(n_channels));
+ if (!sigrenderer) return NULL;
+ }
+ }
+
+ while (pos >= sigrenderer->time_left) {
+ render(sigrenderer, 0, 1.0f, 0, sigrenderer->time_left, NULL);
+
+ pos -= sigrenderer->time_left;
+ sigrenderer->time_left = 0;
+
+ if (process_tick(sigrenderer)) {
+ _dumb_it_end_sigrenderer(sigrenderer);
+ return NULL;
+ }
+ }
+
+ render(sigrenderer, 0, 1.0f, 0, pos, NULL);
+ sigrenderer->time_left -= pos;
+
+ return sigrenderer;
+}
+
+
+
+static long it_sigrenderer_get_samples(
+ sigrenderer_t *vsigrenderer,
+ float volume, float delta,
+ long size, sample_t **samples
+)
+{
+ DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer;
+ long pos;
+ int dt;
+ long todo;
+ LONG_LONG t;
+
+ if (sigrenderer->order < 0) return 0; // problematic
+
+ pos = 0;
+ dt = (int)(delta * 65536.0f + 0.5f);
+
+ /* When samples is finally used in render_playing(), it won't be used if
+ * volume is 0.
+ */
+ if (!samples) volume = 0;
+
+ for (;;) {
+ todo = (long)((((LONG_LONG)sigrenderer->time_left << 16) | sigrenderer->sub_time_left) / dt);
+
+ if (todo >= size)
+ break;
+
+ render(sigrenderer, volume, delta, pos, todo, samples);
+
+ pos += todo;
+ size -= todo;
+
+ t = sigrenderer->sub_time_left - (LONG_LONG)todo * dt;
+ sigrenderer->sub_time_left = (long)t & 65535;
+ sigrenderer->time_left += (long)(t >> 16);
+
+ if (process_tick(sigrenderer)) {
+ sigrenderer->order = -1;
+ sigrenderer->row = -1;
+ return pos;
+ }
+ }
+
+ render(sigrenderer, volume, delta, pos, size, samples);
+
+ pos += size;
+
+ t = sigrenderer->sub_time_left - (LONG_LONG)size * dt;
+ sigrenderer->sub_time_left = (long)t & 65535;
+ sigrenderer->time_left += (long)(t >> 16);
+
+ if (samples)
+ dumb_remove_clicks_array(sigrenderer->n_channels, sigrenderer->click_remover, samples, pos, 512.0f / delta);
+
+ return pos;
+}
+
+
+
+static void it_sigrenderer_get_current_sample(sigrenderer_t *vsigrenderer, float volume, sample_t *samples)
+{
+ DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer;
+ (void)volume; // for consideration: in any of these such functions, is 'volume' going to be required?
+ dumb_click_remover_get_offset_array(sigrenderer->n_channels, sigrenderer->click_remover, samples);
+}
+
+
+
+void _dumb_it_end_sigrenderer(sigrenderer_t *vsigrenderer)
+{
+ DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer;
+
+ int i;
+
+ if (sigrenderer) {
+ for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+ if (sigrenderer->channel[i].playing)
+ free(sigrenderer->channel[i].playing);
+#ifdef BIT_ARRAY_BULLSHIT
+ bit_array_destroy(sigrenderer->channel[i].played_patjump);
+#endif
+ }
+
+ for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++)
+ if (sigrenderer->playing[i])
+ free(sigrenderer->playing[i]);
+
+ dumb_destroy_click_remover_array(sigrenderer->n_channels, sigrenderer->click_remover);
+
+ if (sigrenderer->callbacks)
+ free(sigrenderer->callbacks);
+
+#ifdef BIT_ARRAY_BULLSHIT
+ bit_array_destroy(sigrenderer->played);
+#endif
+
+ free(vsigrenderer);
+ }
+}
+
+
+
+DUH_SIGTYPE_DESC _dumb_sigtype_it = {
+ SIGTYPE_IT,
+ NULL,
+ &it_start_sigrenderer,
+ NULL,
+ &it_sigrenderer_get_samples,
+ &it_sigrenderer_get_current_sample,
+ &_dumb_it_end_sigrenderer,
+ &_dumb_it_unload_sigdata
+};
+
+
+
+DUH_SIGRENDERER *duh_encapsulate_it_sigrenderer(DUMB_IT_SIGRENDERER *it_sigrenderer, int n_channels, long pos)
+{
+ return duh_encapsulate_raw_sigrenderer(it_sigrenderer, &_dumb_sigtype_it, n_channels, pos);
+}
+
+
+
+DUMB_IT_SIGRENDERER *duh_get_it_sigrenderer(DUH_SIGRENDERER *sigrenderer)
+{
+ return duh_get_raw_sigrenderer(sigrenderer, SIGTYPE_IT);
+}
+
+
+
+/* Values of 64 or more will access NNA channels here. */
+void dumb_it_sr_get_channel_state(DUMB_IT_SIGRENDERER *sr, int channel, DUMB_IT_CHANNEL_STATE *state)
+{
+ IT_PLAYING *playing;
+ int t; /* temporary var for holding accurate pan and filter cutoff */
+ float delta;
+ ASSERT(channel < DUMB_IT_TOTAL_CHANNELS);
+ if (!sr) { state->sample = 0; return; }
+ if (channel >= DUMB_IT_N_CHANNELS) {
+ playing = sr->playing[channel - DUMB_IT_N_CHANNELS];
+ if (!playing) { state->sample = 0; return; }
+ } else {
+ playing = sr->channel[channel].playing;
+ if (!playing) { state->sample = 0; return; }
+ }
+
+ if (playing->flags & IT_PLAYING_DEAD) { state->sample = 0; return; }
+
+ state->channel = playing->channel - sr->channel;
+ state->sample = playing->sampnum;
+ state->volume = calculate_volume(sr, playing, 1.0f);
+
+ t = apply_pan_envelope(playing);
+ state->pan = (unsigned char)((t + 128) >> IT_ENVELOPE_SHIFT);
+ state->subpan = (signed char)t;
+
+ delta = playing->delta * 65536.0f;
+ t = playing->filter_cutoff << IT_ENVELOPE_SHIFT;
+ apply_pitch_modifications(sr->sigdata, playing, &delta, &t);
+ state->freq = (int)delta;
+ if (t == 127 << IT_ENVELOPE_SHIFT && playing->filter_resonance == 0) {
+ state->filter_resonance = playing->true_filter_resonance;
+ t = playing->true_filter_cutoff;
+ } else
+ state->filter_resonance = playing->filter_resonance;
+ state->filter_cutoff = (unsigned char)(t >> 8);
+ state->filter_subcutoff = (unsigned char)t;
+}
+
+
+
+int dumb_it_callback_terminate(void *data)
+{
+ (void)data;
+ return 1;
+}
+
+
+
+int dumb_it_callback_midi_block(void *data, int channel, unsigned char midi_byte)
+{
+ (void)data;
+ (void)channel;
+ (void)midi_byte;
+ return 1;
+}
+
+
+
+#define IT_CHECKPOINT_INTERVAL (30 * 65536) /* Half a minute */
+
+#define FUCKIT_THRESHOLD (120 * 60 * 65536) /* two hours? probably a pattern loop mess... */
+
+/* Returns the length of the module, up until it first loops. */
+long dumb_it_build_checkpoints(DUMB_IT_SIGDATA *sigdata, int startorder)
+{
+ IT_CHECKPOINT *checkpoint;
+ if (!sigdata) return 0;
+ checkpoint = sigdata->checkpoint;
+ while (checkpoint) {
+ IT_CHECKPOINT *next = checkpoint->next;
+ _dumb_it_end_sigrenderer(checkpoint->sigrenderer);
+ free(checkpoint);
+ checkpoint = next;
+ }
+ sigdata->checkpoint = NULL;
+ checkpoint = malloc(sizeof(*checkpoint));
+ if (!checkpoint) return 0;
+ checkpoint->time = 0;
+ checkpoint->sigrenderer = dumb_it_init_sigrenderer(sigdata, 0, startorder);
+ if (!checkpoint->sigrenderer) {
+ free(checkpoint);
+ return 0;
+ }
+ checkpoint->sigrenderer->callbacks->loop = &dumb_it_callback_terminate;
+ checkpoint->sigrenderer->callbacks->xm_speed_zero = &dumb_it_callback_terminate;
+ checkpoint->sigrenderer->callbacks->global_volume_zero = &dumb_it_callback_terminate;
+
+ if (sigdata->checkpoint)
+ {
+ IT_CHECKPOINT *checkpoint = sigdata->checkpoint;
+ while (checkpoint) {
+ IT_CHECKPOINT *next = checkpoint->next;
+ _dumb_it_end_sigrenderer(checkpoint->sigrenderer);
+ free(checkpoint);
+ checkpoint = next;
+ }
+ }
+
+ sigdata->checkpoint = checkpoint;
+
+ for (;;) {
+ long l;
+ DUMB_IT_SIGRENDERER *sigrenderer = dup_sigrenderer(checkpoint->sigrenderer, 0, checkpoint->sigrenderer->callbacks);
+ checkpoint->sigrenderer->callbacks = NULL;
+ if (!sigrenderer) {
+ checkpoint->next = NULL;
+ return checkpoint->time;
+ }
+
+ l = it_sigrenderer_get_samples(sigrenderer, 0, 1.0f, IT_CHECKPOINT_INTERVAL, NULL);
+ if (l < IT_CHECKPOINT_INTERVAL) {
+ _dumb_it_end_sigrenderer(sigrenderer);
+ checkpoint->next = NULL;
+ return checkpoint->time + l;
+ }
+
+ checkpoint->next = malloc(sizeof(*checkpoint->next));
+ if (!checkpoint->next) {
+ _dumb_it_end_sigrenderer(sigrenderer);
+ return checkpoint->time + IT_CHECKPOINT_INTERVAL;
+ }
+
+ checkpoint->next->time = checkpoint->time + IT_CHECKPOINT_INTERVAL;
+ checkpoint = checkpoint->next;
+ checkpoint->sigrenderer = sigrenderer;
+
+ if (checkpoint->time >= FUCKIT_THRESHOLD) {
+ checkpoint->next = NULL;
+ return 0;
+ }
+ }
+}
+
+
+
+void dumb_it_do_initial_runthrough(DUH *duh)
+{
+ if (duh) {
+ DUMB_IT_SIGDATA *sigdata = duh_get_it_sigdata(duh);
+
+ if (sigdata)
+ duh_set_length(duh, dumb_it_build_checkpoints(sigdata, 0));
+ }
+}
+
+static int is_pattern_silent(IT_PATTERN * pattern, int order) {
+ int ret = 1;
+ IT_ENTRY * entry, * end;
+ if (!pattern || !pattern->n_rows || !pattern->n_entries || !pattern->entry) return 2;
+
+ if ( pattern->n_entries == pattern->n_rows ) {
+ int n;
+ entry = pattern->entry;
+ for ( n = 0; n < pattern->n_entries; ++n, ++entry ) {
+ if ( !IT_IS_END_ROW(entry) ) break;
+ }
+ if ( n == pattern->n_entries ) return 2;
+ // broken?
+ }
+
+ entry = pattern->entry;
+ end = entry + pattern->n_entries;
+
+ while (entry < end) {
+ if (!IT_IS_END_ROW(entry)) {
+ if (entry->mask & (IT_ENTRY_INSTRUMENT | IT_ENTRY_VOLPAN))
+ return 0;
+ if (entry->mask & IT_ENTRY_NOTE && entry->note < 120)
+ return 0;
+ if (entry->mask & IT_ENTRY_EFFECT) {
+ switch (entry->effect) {
+ case IT_SET_GLOBAL_VOLUME:
+ if (entry->effectvalue) return 0;
+ break;
+
+ case IT_SET_SPEED:
+ if (entry->effectvalue > 64) ret++;
+ break;
+
+ case IT_SET_SONG_TEMPO:
+ case IT_XM_KEY_OFF:
+ break;
+
+ case IT_JUMP_TO_ORDER:
+ if (entry->effectvalue != order)
+ return 0;
+ break;
+
+ case IT_S:
+ switch (entry->effectvalue >> 4) {
+ case 0: // meh bastard
+ if ( entry->effectvalue != 0 ) return 0;
+ break;
+
+ case IT_S_FINE_PATTERN_DELAY:
+ case IT_S_PATTERN_LOOP:
+ case IT_S_PATTERN_DELAY:
+ ret++;
+ break;
+
+ case IT_S7:
+ if ((entry->effectvalue & 15) > 2)
+ return 0;
+ break;
+
+ default:
+ return 0;
+ }
+ break;
+
+ // clever idiot with his S L O W crap; do nothing
+ case IT_VOLSLIDE_TONEPORTA:
+ case IT_SET_SAMPLE_OFFSET:
+ case IT_GLOBAL_VOLUME_SLIDE:
+ if ( entry->effectvalue != 0 ) return 0;
+ break;
+
+ // genius also uses this instead of jump to order by mistake, meh, and it's bloody BCD
+ case IT_BREAK_TO_ROW:
+ if ( ( ( entry->effectvalue >> 4 ) * 10 + ( entry->effectvalue & 15 ) ) != order ) return 0;
+ break;
+
+ default:
+ return 0;
+ }
+ }
+ }
+ entry++;
+ }
+
+ return ret;
+}
+
+int dumb_it_trim_silent_patterns(DUH * duh) {
+ int n;
+ DUMB_IT_SIGDATA *sigdata;
+
+ if (!duh) return -1;
+
+ sigdata = duh_get_it_sigdata(duh);
+
+ if (!sigdata || !sigdata->order || !sigdata->pattern) return -1;
+
+ for (n = 0; n < sigdata->n_orders; n++) {
+ int p = sigdata->order[n];
+ if (p < sigdata->n_patterns) {
+ IT_PATTERN * pattern = &sigdata->pattern[p];
+ if (is_pattern_silent(pattern, n) > 1) {
+ pattern->n_rows = 1;
+ pattern->n_entries = 0;
+ if (pattern->entry)
+ {
+ free(pattern->entry);
+ pattern->entry = NULL;
+ }
+ } else
+ break;
+ }
+ }
+
+ if (n == sigdata->n_orders) return -1;
+
+ for (n = sigdata->n_orders - 1; n >= 0; n--) {
+ int p = sigdata->order[n];
+ if (p < sigdata->n_patterns) {
+ IT_PATTERN * pattern = &sigdata->pattern[p];
+ if (is_pattern_silent(pattern, n) > 1) {
+ pattern->n_rows = 1;
+ pattern->n_entries = 0;
+ if (pattern->entry)
+ {
+ free(pattern->entry);
+ pattern->entry = NULL;
+ }
+ } else
+ break;
+ }
+ }
+
+ if (n < 0) return -1;
+
+ /*duh->length = dumb_it_build_checkpoints(sigdata, 0);*/
+
+ return 0;
+}
+
+int dumb_it_scan_for_playable_orders(DUMB_IT_SIGDATA *sigdata, dumb_scan_callback callback, void * callback_data)
+{
+ int n;
+ long length;
+ void * ba_played;
+ DUMB_IT_SIGRENDERER * sigrenderer;
+
+ if (!sigdata->n_orders || !sigdata->order) return -1;
+
+ ba_played = bit_array_create(sigdata->n_orders * 256);
+ if (!ba_played) return -1;
+
+ for (n = 0; n < sigdata->n_orders; n++) {
+ if ((sigdata->order[n] >= sigdata->n_patterns) ||
+ (is_pattern_silent(&sigdata->pattern[sigdata->order[n]], n) > 1))
+ bit_array_set(ba_played, n * 256);
+ }
+
+ for (;;) {
+ for (n = 0; n < sigdata->n_orders; n++) {
+ if (!bit_array_test_range(ba_played, n * 256, 256)) break;
+ }
+
+ if (n == sigdata->n_orders) break;
+
+ sigrenderer = dumb_it_init_sigrenderer(sigdata, 0, n);
+ if (!sigrenderer) {
+ bit_array_destroy(ba_played);
+ return -1;
+ }
+ sigrenderer->callbacks->loop = &dumb_it_callback_terminate;
+ sigrenderer->callbacks->xm_speed_zero = &dumb_it_callback_terminate;
+ sigrenderer->callbacks->global_volume_zero = &dumb_it_callback_terminate;
+
+ length = 0;
+
+ for (;;) {
+ long l;
+
+ l = it_sigrenderer_get_samples(sigrenderer, 0, 1.0f, IT_CHECKPOINT_INTERVAL, NULL);
+ length += l;
+ if (l < IT_CHECKPOINT_INTERVAL || length >= FUCKIT_THRESHOLD) {
+ /* SONG OVA! */
+ break;
+ }
+ }
+
+ if ((*callback)(callback_data, n, length) < 0) return -1;
+
+ bit_array_merge(ba_played, sigrenderer->played, 0);
+
+ _dumb_it_end_sigrenderer(sigrenderer);
+ }
+
+ bit_array_destroy(ba_played);
+
+ return 0;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/itunload.c b/plugins/dumb/dumb-kode54/src/it/itunload.c
new file mode 100644
index 00000000..136fd5c5
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/itunload.c
@@ -0,0 +1,72 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * itunload.c - Code to free an Impulse Tracker / / \ \
+ * module from memory. | < / \_
+ * | \/ /\ /
+ * By entheh. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+void _dumb_it_unload_sigdata(sigdata_t *vsigdata)
+{
+ if (vsigdata) {
+ DUMB_IT_SIGDATA *sigdata = vsigdata;
+ int n;
+
+ if (sigdata->song_message)
+ free(sigdata->song_message);
+
+ if (sigdata->order)
+ free(sigdata->order);
+
+ if (sigdata->instrument)
+ free(sigdata->instrument);
+
+ if (sigdata->sample) {
+ for (n = 0; n < sigdata->n_samples; n++)
+ if (sigdata->sample[n].data)
+ free(sigdata->sample[n].data);
+
+ free(sigdata->sample);
+ }
+
+ if (sigdata->pattern) {
+ for (n = 0; n < sigdata->n_patterns; n++)
+ if (sigdata->pattern[n].entry)
+ free(sigdata->pattern[n].entry);
+ free(sigdata->pattern);
+ }
+
+ if (sigdata->midi)
+ free(sigdata->midi);
+
+ {
+ IT_CHECKPOINT *checkpoint = sigdata->checkpoint;
+ while (checkpoint) {
+ IT_CHECKPOINT *next = checkpoint->next;
+ _dumb_it_end_sigrenderer(checkpoint->sigrenderer);
+ free(checkpoint);
+ checkpoint = next;
+ }
+ }
+
+ free(vsigdata);
+ }
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/load669.c b/plugins/dumb/dumb-kode54/src/it/load669.c
new file mode 100644
index 00000000..e5a3fc3f
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/load669.c
@@ -0,0 +1,42 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loadmod.c - Code to read a 669 Composer module / / \ \
+ * file, opening and closing it for | < / \_
+ * you. | \/ /\ /
+ * \_ / > /
+ * By Chris Moeller | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_669_quick(): loads a 669 file into a DUH struct, returning a
+ * pointer to the DUH struct. When you have finished with it, you must
+ * pass the pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_669_quick(const char *filename)
+{
+ DUH *duh;
+ DUMBFILE *f = dumbfile_open(filename);
+
+ if (!f)
+ return NULL;
+
+ duh = dumb_read_669_quick(f);
+
+ dumbfile_close(f);
+
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/load6692.c b/plugins/dumb/dumb-kode54/src/it/load6692.c
new file mode 100644
index 00000000..2358838f
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/load6692.c
@@ -0,0 +1,34 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loadmod2.c - Code to read a 669 Composer module / / \ \
+ * file, opening and closing it for | < / \_
+ * you, and do an initial run-through. | \/ /\ /
+ * \_ / > /
+ * By Chris Moeller | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_669(): loads a 669 file into a DUH struct, returning a pointer
+ * to the DUH struct. When you have finished with it, you must pass the
+ * pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_669(const char *filename)
+{
+ DUH *duh = dumb_load_669_quick(filename);
+ dumb_it_do_initial_runthrough(duh);
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/loadasy.c b/plugins/dumb/dumb-kode54/src/it/loadasy.c
new file mode 100644
index 00000000..86c6ad8c
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/loadasy.c
@@ -0,0 +1,42 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loadasy.c - Code to read an ASYLUM Music Format / / \ \
+ * module file, opening and closing it | < / \_
+ * for you. | \/ /\ /
+ * \_ / > /
+ * By Chris Moeller. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_asy_quick(): loads a AMF file into a DUH struct, returning a
+ * pointer to the DUH struct. When you have finished with it, you must
+ * pass the pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_asy_quick(const char *filename)
+{
+ DUH *duh;
+ DUMBFILE *f = dumbfile_open(filename);
+
+ if (!f)
+ return NULL;
+
+ duh = dumb_read_asy_quick(f);
+
+ dumbfile_close(f);
+
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/loadasy2.c b/plugins/dumb/dumb-kode54/src/it/loadasy2.c
new file mode 100644
index 00000000..7b253320
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/loadasy2.c
@@ -0,0 +1,34 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loadasy2.c - Code to read an ASYLUM Music Format / / \ \
+ * module file, opening and closing it | < / \_
+ * for you, and do an initial run- | \/ /\ /
+ * through. \_ / > /
+ * | \ / /
+ * By Chris Moeller. | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_asy(): loads a AMF file into a DUH struct, returning a pointer
+ * to the DUH struct. When you have finished with it, you must pass the
+ * pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_asy(const char *filename)
+{
+ DUH *duh = dumb_load_asy_quick(filename);
+ dumb_it_do_initial_runthrough(duh);
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/loadmod.c b/plugins/dumb/dumb-kode54/src/it/loadmod.c
new file mode 100644
index 00000000..1c76302d
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/loadmod.c
@@ -0,0 +1,42 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loadmod.c - Code to read a good old-fashioned / / \ \
+ * Amiga module file, opening and | < / \_
+ * closing it for you. | \/ /\ /
+ * \_ / > /
+ * By entheh. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_mod_quick(): loads a MOD file into a DUH struct, returning a
+ * pointer to the DUH struct. When you have finished with it, you must
+ * pass the pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_mod_quick(const char *filename, int restrict)
+{
+ DUH *duh;
+ DUMBFILE *f = dumbfile_open(filename);
+
+ if (!f)
+ return NULL;
+
+ duh = dumb_read_mod_quick(f, restrict);
+
+ dumbfile_close(f);
+
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/loadmod2.c b/plugins/dumb/dumb-kode54/src/it/loadmod2.c
new file mode 100644
index 00000000..f5ca5310
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/loadmod2.c
@@ -0,0 +1,29 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loadmod2.c - Function to read a good old- / / \ \
+ * fashioned Amiga module file, | < / \_
+ * opening and closing it for you, | \/ /\ /
+ * and do an initial run-through. \_ / > /
+ * | \ / /
+ * Split off from loadmod.c by entheh. | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+
+
+
+DUH *dumb_load_mod(const char *filename, int restrict)
+{
+ DUH *duh = dumb_load_mod_quick(filename, restrict);
+ dumb_it_do_initial_runthrough(duh);
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/loadmtm.c b/plugins/dumb/dumb-kode54/src/it/loadmtm.c
new file mode 100644
index 00000000..3c974880
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/loadmtm.c
@@ -0,0 +1,42 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loadmtm.c - Code to read a MultiTracker Module / / \ \
+ * file, opening and closing it for | < / \_
+ * you. | \/ /\ /
+ * \_ / > /
+ * By Chris Moeller | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_mtm_quick(): loads a MTM file into a DUH struct, returning a
+ * pointer to the DUH struct. When you have finished with it, you must
+ * pass the pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_mtm_quick(const char *filename)
+{
+ DUH *duh;
+ DUMBFILE *f = dumbfile_open(filename);
+
+ if (!f)
+ return NULL;
+
+ duh = dumb_read_mtm_quick(f);
+
+ dumbfile_close(f);
+
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/loadmtm2.c b/plugins/dumb/dumb-kode54/src/it/loadmtm2.c
new file mode 100644
index 00000000..9fc23890
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/loadmtm2.c
@@ -0,0 +1,34 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loadmtm2.c - Code to read a MultiTracker Module / / \ \
+ * file, opening and closing it for | < / \_
+ * you, and do an initial run-through. | \/ /\ /
+ * \_ / > /
+ * By Chris Moeller | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_mtm(): loads a MTM file into a DUH struct, returning a pointer
+ * to the DUH struct. When you have finished with it, you must pass the
+ * pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_mtm(const char *filename)
+{
+ DUH *duh = dumb_load_mtm_quick(filename);
+ dumb_it_do_initial_runthrough(duh);
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/loadoldpsm.c b/plugins/dumb/dumb-kode54/src/it/loadoldpsm.c
new file mode 100644
index 00000000..547294b3
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/loadoldpsm.c
@@ -0,0 +1,43 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loadoldpsm.c - Code to read a ProTracker Studio / / \ \
+ * file, opening and closing it for | < / \_
+ * you. | \/ /\ /
+ * \_ / > /
+ * By Chris Moeller. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_old_psm_quick(): loads an old PSM file into a DUH struct,
+ * returning a pointer to the DUH struct. When you have finished with it,
+ * you must pass the pointer to unload_duh() so that the memory can be
+ * freed.
+ */
+DUH *dumb_load_old_psm_quick(const char *filename)
+{
+ DUH *duh;
+ DUMBFILE *f = dumbfile_open(filename);
+
+ if (!f)
+ return NULL;
+
+ duh = dumb_read_old_psm_quick(f);
+
+ dumbfile_close(f);
+
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/loadoldpsm2.c b/plugins/dumb/dumb-kode54/src/it/loadoldpsm2.c
new file mode 100644
index 00000000..ff2e427c
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/loadoldpsm2.c
@@ -0,0 +1,34 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loadoldpsm2.c - Code to read a ProTracker Studio / / \ \
+ * file, opening and closing it for | < / \_
+ * you, and do an initial run- | \/ /\ /
+ * through. \_ / > /
+ * | \ / /
+ * By Chris Moeller. | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_old_psm(): loads an old PSM file into a DUH struct, returning
+ * a pointer to the DUH struct. When you have finished with it, you must
+ * pass the pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_old_psm(const char *filename)
+{
+ DUH *duh = dumb_load_old_psm_quick(filename);
+ dumb_it_do_initial_runthrough(duh);
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/loadpsm.c b/plugins/dumb/dumb-kode54/src/it/loadpsm.c
new file mode 100644
index 00000000..a6851d5b
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/loadpsm.c
@@ -0,0 +1,42 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loadpsm.c - Code to read a ProTracker Studio / / \ \
+ * file, opening and closing it for | < / \_
+ * you. | \/ /\ /
+ * \_ / > /
+ * By Chris Moeller. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_psm_quick(): loads a PSM file into a DUH struct, returning a
+ * pointer to the DUH struct. When you have finished with it, you must
+ * pass the pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_psm_quick(const char *filename, int subsong)
+{
+ DUH *duh;
+ DUMBFILE *f = dumbfile_open(filename);
+
+ if (!f)
+ return NULL;
+
+ duh = dumb_read_psm_quick(f, subsong);
+
+ dumbfile_close(f);
+
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/loadpsm2.c b/plugins/dumb/dumb-kode54/src/it/loadpsm2.c
new file mode 100644
index 00000000..7a12257a
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/loadpsm2.c
@@ -0,0 +1,34 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loadpsm2.c - Code to read a ProTracker Studio / / \ \
+ * file, opening and closing it for | < / \_
+ * you, and do an initial run-through. | \/ /\ /
+ * \_ / > /
+ * By Chris Moeller. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_psm(): loads a PSM file into a DUH struct, returning a pointer
+ * to the DUH struct. When you have finished with it, you must pass the
+ * pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_psm(const char *filename, int subsong)
+{
+ DUH *duh = dumb_load_psm_quick(filename, subsong);
+ dumb_it_do_initial_runthrough(duh);
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/loadptm.c b/plugins/dumb/dumb-kode54/src/it/loadptm.c
new file mode 100644
index 00000000..ddcaaec9
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/loadptm.c
@@ -0,0 +1,42 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loadptm.c - Code to read a Poly Tracker v2.03 / / \ \
+ * file, opening and closing it for | < / \_
+ * you. | \/ /\ /
+ * \_ / > /
+ * By Chris Moeller. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_ptm_quick(): loads a PTM file into a DUH struct, returning a
+ * pointer to the DUH struct. When you have finished with it, you must
+ * pass the pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_ptm_quick(const char *filename)
+{
+ DUH *duh;
+ DUMBFILE *f = dumbfile_open(filename);
+
+ if (!f)
+ return NULL;
+
+ duh = dumb_read_ptm_quick(f);
+
+ dumbfile_close(f);
+
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/loadptm2.c b/plugins/dumb/dumb-kode54/src/it/loadptm2.c
new file mode 100644
index 00000000..ea8982f2
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/loadptm2.c
@@ -0,0 +1,34 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loadptm2.c - Code to read a Poly Tracker v2.03 / / \ \
+ * file, opening and closing it for | < / \_
+ * you, and do an initial run-through. | \/ /\ /
+ * \_ / > /
+ * By Chris Moeller. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_ptm(): loads a PTM file into a DUH struct, returning a pointer
+ * to the DUH struct. When you have finished with it, you must pass the
+ * pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_ptm(const char *filename)
+{
+ DUH *duh = dumb_load_ptm_quick(filename);
+ dumb_it_do_initial_runthrough(duh);
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/loadriff.c b/plugins/dumb/dumb-kode54/src/it/loadriff.c
new file mode 100644
index 00000000..07994b0f
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/loadriff.c
@@ -0,0 +1,42 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loadriff.c - Code to read a RIFF module file / / \ \
+ * opening and closing it for you. | < / \_
+ * | \/ /\ /
+ * \_ / > /
+ * By Chris Moeller. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_riff_quick(): loads a RIFF file into a DUH struct, returning
+ * a pointer to the DUH struct. When you have finished with it, you must
+ * pass the pointer to unload_duh() so that the memory can be freed.
+ */
+DUH * dumb_load_riff_quick( const char *filename )
+{
+ DUH * duh;
+ DUMBFILE * f = dumbfile_open( filename );
+
+ if ( ! f )
+ return NULL;
+
+ duh = dumb_read_riff_quick( f );
+
+ dumbfile_close( f );
+
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/loadriff2.c b/plugins/dumb/dumb-kode54/src/it/loadriff2.c
new file mode 100644
index 00000000..11c5d497
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/loadriff2.c
@@ -0,0 +1,29 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loadriff2.c - Code to read a RIFF module file / / \ \
+ * opening and closing it for you, | < / \_
+ * and do an initial run-through. | \/ /\ /
+ * \_ / > /
+ * By Chris Moeller. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+
+
+
+DUH *dumb_load_riff(const char *filename)
+{
+ DUH *duh = dumb_load_riff_quick(filename);
+ dumb_it_do_initial_runthrough(duh);
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/loads3m.c b/plugins/dumb/dumb-kode54/src/it/loads3m.c
new file mode 100644
index 00000000..41af114e
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/loads3m.c
@@ -0,0 +1,42 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loads3m.c - Code to read a ScreamTracker 3 / / \ \
+ * file, opening and closing it for | < / \_
+ * you. | \/ /\ /
+ * \_ / > /
+ * By entheh. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_s3m_quick(): loads an S3M file into a DUH struct, returning
+ * a pointer to the DUH struct. When you have finished with it, you must
+ * pass the pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_s3m_quick(const char *filename)
+{
+ DUH *duh;
+ DUMBFILE *f = dumbfile_open(filename);
+
+ if (!f)
+ return NULL;
+
+ duh = dumb_read_s3m_quick(f);
+
+ dumbfile_close(f);
+
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/loads3m2.c b/plugins/dumb/dumb-kode54/src/it/loads3m2.c
new file mode 100644
index 00000000..de81e09a
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/loads3m2.c
@@ -0,0 +1,29 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loads3m2.c - Function to read a ScreamTracker 3 / / \ \
+ * file, opening and closing it for | < / \_
+ * you, and do an initial run-through. | \/ /\ /
+ * \_ / > /
+ * Split off from loads3m.c by entheh. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+
+
+
+DUH *dumb_load_s3m(const char *filename)
+{
+ DUH *duh = dumb_load_s3m_quick(filename);
+ dumb_it_do_initial_runthrough(duh);
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/loadstm.c b/plugins/dumb/dumb-kode54/src/it/loadstm.c
new file mode 100644
index 00000000..84785208
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/loadstm.c
@@ -0,0 +1,42 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loadstm.c - Code to read a ScreamTracker 2 / / \ \
+ * file, opening and closing it for | < / \_
+ * you. | \/ /\ /
+ * \_ / > /
+ * By Chris Moeller. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_stm_quick(): loads an STM file into a DUH struct, returning a
+ * pointer to the DUH struct. When you have finished with it, you must
+ * pass the pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_stm_quick(const char *filename)
+{
+ DUH *duh;
+ DUMBFILE *f = dumbfile_open(filename);
+
+ if (!f)
+ return NULL;
+
+ duh = dumb_read_stm_quick(f);
+
+ dumbfile_close(f);
+
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/loadstm2.c b/plugins/dumb/dumb-kode54/src/it/loadstm2.c
new file mode 100644
index 00000000..4655b52d
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/loadstm2.c
@@ -0,0 +1,29 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loadstm2.c - Function to read a ScreamTracker 2 / / \ \
+ * file, opening and closing it for | < / \_
+ * you, and do an initial run-through. | \/ /\ /
+ * \_ / > /
+ * By Chris Moeller. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+
+
+
+DUH *dumb_load_stm(const char *filename)
+{
+ DUH *duh = dumb_load_stm_quick(filename);
+ dumb_it_do_initial_runthrough(duh);
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/loadxm.c b/plugins/dumb/dumb-kode54/src/it/loadxm.c
new file mode 100644
index 00000000..a0eba616
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/loadxm.c
@@ -0,0 +1,42 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loadxm.c - Code to read a Fast Tracker II / / \ \
+ * file, opening and closing it for | < / \_
+ * you. | \/ /\ /
+ * \_ / > /
+ * By entheh. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_xm_quick(): loads an XM file into a DUH struct, returning a
+ * pointer to the DUH struct. When you have finished with it, you must
+ * pass the pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_xm_quick(const char *filename)
+{
+ DUH *duh;
+ DUMBFILE *f = dumbfile_open(filename);
+
+ if (!f)
+ return NULL;
+
+ duh = dumb_read_xm_quick(f);
+
+ dumbfile_close(f);
+
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/loadxm2.c b/plugins/dumb/dumb-kode54/src/it/loadxm2.c
new file mode 100644
index 00000000..242a586d
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/loadxm2.c
@@ -0,0 +1,29 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * loadxm2.c - Function to read a Fast Tracker II / / \ \
+ * file, opening and closing it for | < / \_
+ * you, and do an initial run-through. | \/ /\ /
+ * \_ / > /
+ * Split off from loadxm.c by entheh. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+
+
+
+DUH *dumb_load_xm(const char *filename)
+{
+ DUH *duh = dumb_load_xm_quick(filename);
+ dumb_it_do_initial_runthrough(duh);
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/ptmeffect.c b/plugins/dumb/dumb-kode54/src/it/ptmeffect.c
new file mode 100644
index 00000000..b05a5d74
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/ptmeffect.c
@@ -0,0 +1,125 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * ptmeffect.c - Code for converting PTM / / \ \
+ * effects to IT effects. | < / \_
+ * | \/ /\ /
+ * By Chris Moeller. Based on xmeffect.c \_ / > /
+ * by Julien Cugniere. | \ / /
+ * | ' /
+ * \__/
+ */
+
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+void _dumb_it_ptm_convert_effect(int effect, int value, IT_ENTRY *entry)
+{
+ if (effect >= PTM_N_EFFECTS)
+ return;
+
+ /* Linearisation of the effect number... */
+ if (effect == PTM_E) {
+ effect = PTM_EBASE + HIGH(value);
+ value = LOW(value);
+ }
+
+ /* convert effect */
+ entry->mask |= IT_ENTRY_EFFECT;
+ switch (effect) {
+
+ case PTM_APPREGIO: effect = IT_ARPEGGIO; break;
+ case PTM_PORTAMENTO_UP: effect = IT_PORTAMENTO_UP; break;
+ case PTM_PORTAMENTO_DOWN: effect = IT_PORTAMENTO_DOWN; break;
+ case PTM_TONE_PORTAMENTO: effect = IT_TONE_PORTAMENTO; break;
+ case PTM_VIBRATO: effect = IT_VIBRATO; break;
+ case PTM_VOLSLIDE_TONEPORTA: effect = IT_VOLSLIDE_TONEPORTA; break;
+ case PTM_VOLSLIDE_VIBRATO: effect = IT_VOLSLIDE_VIBRATO; break;
+ case PTM_TREMOLO: effect = IT_TREMOLO; break;
+ case PTM_SAMPLE_OFFSET: effect = IT_SET_SAMPLE_OFFSET; break;
+ case PTM_VOLUME_SLIDE: effect = IT_VOLUME_SLIDE; break;
+ case PTM_POSITION_JUMP: effect = IT_JUMP_TO_ORDER; break;
+ case PTM_SET_CHANNEL_VOLUME: effect = IT_SET_CHANNEL_VOLUME; break;
+ case PTM_PATTERN_BREAK: effect = IT_BREAK_TO_ROW; break;
+ case PTM_SET_GLOBAL_VOLUME: effect = IT_SET_GLOBAL_VOLUME; break;
+ case PTM_RETRIGGER: effect = IT_RETRIGGER_NOTE; break;
+ case PTM_FINE_VIBRATO: effect = IT_FINE_VIBRATO; break;
+
+ /* TODO properly */
+ case PTM_NOTE_SLIDE_UP: effect = IT_PTM_NOTE_SLIDE_UP; break;
+ case PTM_NOTE_SLIDE_DOWN: effect = IT_PTM_NOTE_SLIDE_DOWN; break;
+ case PTM_NOTE_SLIDE_UP_RETRIG: effect = IT_PTM_NOTE_SLIDE_UP_RETRIG; break;
+ case PTM_NOTE_SLIDE_DOWN_RETRIG: effect = IT_PTM_NOTE_SLIDE_DOWN_RETRIG; break;
+
+ case PTM_SET_TEMPO_BPM:
+ effect = (value < 0x20) ? (IT_SET_SPEED) : (IT_SET_SONG_TEMPO);
+ break;
+
+ case PTM_EBASE+PTM_E_SET_FINETUNE: effect = SBASE+IT_S_FINETUNE; break; /** TODO */
+ case PTM_EBASE+PTM_E_SET_LOOP: effect = SBASE+IT_S_PATTERN_LOOP; break;
+ case PTM_EBASE+PTM_E_NOTE_CUT: effect = SBASE+IT_S_DELAYED_NOTE_CUT; break;
+ case PTM_EBASE+PTM_E_NOTE_DELAY: effect = SBASE+IT_S_NOTE_DELAY; break;
+ case PTM_EBASE+PTM_E_PATTERN_DELAY: effect = SBASE+IT_S_PATTERN_DELAY; break;
+ case PTM_EBASE+PTM_E_SET_PANNING: effect = SBASE+IT_S_SET_PAN; break;
+
+ case PTM_EBASE+PTM_E_FINE_VOLSLIDE_UP:
+ effect = IT_VOLUME_SLIDE;
+ value = EFFECT_VALUE(value, 0xF);
+ break;
+
+ case PTM_EBASE + PTM_E_FINE_VOLSLIDE_DOWN:
+ effect = IT_VOLUME_SLIDE;
+ value = EFFECT_VALUE(0xF, value);
+ break;
+
+ case PTM_EBASE + PTM_E_FINE_PORTA_UP:
+ effect = IT_PORTAMENTO_UP;
+ value = EFFECT_VALUE(0xF, value);
+ break;
+
+ case PTM_EBASE + PTM_E_FINE_PORTA_DOWN:
+ effect = IT_PORTAMENTO_DOWN;
+ value = EFFECT_VALUE(0xF, value);
+ break;
+
+ case PTM_EBASE + PTM_E_RETRIG_NOTE:
+ effect = IT_XM_RETRIGGER_NOTE;
+ value = EFFECT_VALUE(0, value);
+ break;
+
+ case PTM_EBASE + PTM_E_SET_VIBRATO_CONTROL:
+ effect = SBASE+IT_S_SET_VIBRATO_WAVEFORM;
+ value &= ~4; /** TODO: value&4 -> don't retrig wave */
+ break;
+
+ case PTM_EBASE + PTM_E_SET_TREMOLO_CONTROL:
+ effect = SBASE+IT_S_SET_TREMOLO_WAVEFORM;
+ value &= ~4; /** TODO: value&4 -> don't retrig wave */
+ break;
+
+ default:
+ /* user effect (often used in demos for synchronisation) */
+ entry->mask &= ~IT_ENTRY_EFFECT;
+ }
+
+ /* Inverse linearisation... */
+ if (effect >= SBASE && effect < SBASE+16) {
+ value = EFFECT_VALUE(effect-SBASE, value);
+ effect = IT_S;
+ }
+
+ entry->effect = effect;
+ entry->effectvalue = value;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/read669.c b/plugins/dumb/dumb-kode54/src/it/read669.c
new file mode 100644
index 00000000..4cb88d30
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/read669.c
@@ -0,0 +1,447 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * read669.c - Code to read a 669 Composer module / / \ \
+ * from an open file. | < / \_
+ * | \/ /\ /
+ * By Chris Moeller. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+static int it_669_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, int tempo, int breakpoint, unsigned char *buffer, int * used_channels)
+{
+ int pos;
+ int channel;
+ int row;
+ IT_ENTRY *entry;
+
+ pattern->n_rows = 64;
+
+ if (dumbfile_getnc(buffer, 64 * 3 * 8, f) < 64 * 3 * 8)
+ return -1;
+
+ /* compute number of entries */
+ pattern->n_entries = 64 + 1; /* Account for the row end markers, speed command */
+ if (breakpoint < 63) pattern->n_entries++; /* and break to row 0 */
+
+ pos = 0;
+ for (row = 0; row < 64; row++) {
+ for (channel = 0; channel < 8; channel++) {
+ if (buffer[pos+0] != 0xFF || buffer[pos+2] != 0xFF)
+ pattern->n_entries++;
+ pos += 3;
+ }
+ }
+
+ pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry));
+ if (!pattern->entry)
+ return -1;
+
+ if (breakpoint == 63) breakpoint++;
+
+ entry = pattern->entry;
+
+ entry->channel = 8;
+ entry->mask = IT_ENTRY_EFFECT;
+ entry->effect = IT_SET_SPEED;
+ entry->effectvalue = tempo;
+ entry++;
+
+ pos = 0;
+ for (row = 0; row < 64; row++) {
+
+ if (row == breakpoint) {
+ entry->channel = 8;
+ entry->mask = IT_ENTRY_EFFECT;
+ entry->effect = IT_BREAK_TO_ROW;
+ entry->effectvalue = 0;
+ entry++;
+ }
+
+ for (channel = 0; channel < 8; channel++) {
+ if (buffer[pos+0] != 0xFF || buffer[pos+2] != 0xFF) {
+ entry->channel = channel;
+ entry->mask = 0;
+
+ if (buffer[pos+0] < 0xFE) {
+ entry->mask |= IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT;
+ entry->note = (buffer[pos+0] >> 2) + 36;
+ entry->instrument = (((buffer[pos+0] << 4) | (buffer[pos+1] >> 4)) & 0x3F) + 1;
+ }
+ if (buffer[pos+0] <= 0xFE) {
+ entry->mask |= IT_ENTRY_VOLPAN;
+ entry->volpan = ((buffer[pos+1] & 15) << 6) / 15;
+ if (*used_channels < channel + 1) *used_channels = channel + 1;
+ }
+ if (buffer[pos+2] != 0xFF) {
+ entry->mask |= IT_ENTRY_EFFECT;
+ entry->effectvalue = buffer[pos+2] & 15;
+ switch (buffer[pos+2] >> 4) {
+ case 0:
+ entry->effect = IT_PORTAMENTO_UP;
+ break;
+ case 1:
+ entry->effect = IT_PORTAMENTO_DOWN;
+ break;
+ case 2:
+ entry->effect = IT_TONE_PORTAMENTO;
+ break;
+ case 3:
+ entry->effect = IT_S;
+ entry->effectvalue += IT_S_FINETUNE * 16 + 8;
+ break;
+ case 4:
+ entry->effect = IT_VIBRATO;
+ // XXX speed unknown
+ entry->effectvalue |= 0x10;
+ break;
+ case 5:
+ if (entry->effectvalue) {
+ entry->effect = IT_SET_SPEED;
+ } else {
+ entry->mask &= ~IT_ENTRY_EFFECT;
+ }
+ break;
+#if 0
+ /* dunno about this, really... */
+ case 6:
+ if (entry->effectvalue == 0) {
+ entry->effect = IT_PANNING_SLIDE;
+ entry->effectvalue = 0xFE;
+ } else if (entry->effectvalue == 1) {
+ entry->effect = IT_PANNING_SLIDE;
+ entry->effectvalue = 0xEF;
+ } else {
+ entry->mask &= ~IT_ENTRY_EFFECT;
+ }
+ break;
+#endif
+ default:
+ entry->mask &= ~IT_ENTRY_EFFECT;
+ break;
+ }
+ if (*used_channels < channel + 1) *used_channels = channel + 1;
+ }
+
+ entry++;
+ }
+ pos += 3;
+ }
+ IT_SET_END_ROW(entry);
+ entry++;
+ }
+
+ return 0;
+}
+
+
+
+static int it_669_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f)
+{
+ dumbfile_getnc(sample->name, 13, f);
+ sample->name[13] = 0;
+
+ sample->filename[0] = 0;
+
+ sample->length = dumbfile_igetl(f);
+ sample->loop_start = dumbfile_igetl(f);
+ sample->loop_end = dumbfile_igetl(f);
+
+ if (dumbfile_error(f))
+ return -1;
+
+ if (sample->length <= 0) {
+ sample->flags = 0;
+ return 0;
+ }
+
+ sample->flags = IT_SAMPLE_EXISTS;
+
+ sample->global_volume = 64;
+ sample->default_volume = 64;
+
+ sample->default_pan = 0;
+ sample->C5_speed = 8363;
+ // the above line might be wrong
+
+ if ((sample->loop_end > sample->length) && !(sample->loop_start))
+ sample->loop_end = 0;
+
+ if (sample->loop_end > sample->length)
+ sample->loop_end = sample->length;
+
+ if (sample->loop_end - sample->loop_start > 2)
+ sample->flags |= IT_SAMPLE_LOOP;
+
+ sample->vibrato_speed = 0;
+ sample->vibrato_depth = 0;
+ sample->vibrato_rate = 0;
+ sample->vibrato_waveform = 0; // do we have to set _all_ these?
+ sample->finetune = 0;
+ sample->max_resampling_quality = -1;
+
+ return 0;
+}
+
+
+
+static int it_669_read_sample_data(IT_SAMPLE *sample, DUMBFILE *f)
+{
+ long i;
+ long truncated_size;
+
+ /* let's get rid of the sample data coming after the end of the loop */
+ if ((sample->flags & IT_SAMPLE_LOOP) && sample->loop_end < sample->length) {
+ truncated_size = sample->length - sample->loop_end;
+ sample->length = sample->loop_end;
+ } else {
+ truncated_size = 0;
+ }
+
+ sample->data = malloc(sample->length);
+
+ if (!sample->data)
+ return -1;
+
+ if (sample->length)
+ {
+ i = dumbfile_getnc(sample->data, sample->length, f);
+
+ if (i < sample->length) {
+ //return -1;
+ // ficking truncated files
+ if (i <= 0) {
+ sample->flags = 0;
+ return 0;
+ }
+ sample->length = i;
+ if (sample->loop_end > i) sample->loop_end = i;
+ } else {
+ /* skip truncated data */
+ dumbfile_skip(f, truncated_size);
+ // Should we be truncating it?
+ if (dumbfile_error(f))
+ return -1;
+ }
+
+ for (i = 0; i < sample->length; i++)
+ ((signed char *)sample->data)[i] ^= 0x80;
+ }
+
+ return 0;
+}
+
+
+static DUMB_IT_SIGDATA *it_669_load_sigdata(DUMBFILE *f, int * ext)
+{
+ DUMB_IT_SIGDATA *sigdata;
+ int n_channels;
+ int i;
+ unsigned char tempolist[128];
+ unsigned char breaklist[128];
+
+ i = dumbfile_igetw(f);
+ if (i != 0x6669 && i != 0x4E4A) return NULL;
+
+ *ext = (i == 0x4E4A);
+
+ sigdata = malloc(sizeof(*sigdata));
+ if (!sigdata) {
+ return NULL;
+ }
+
+ if (dumbfile_getnc(sigdata->name, 36, f) < 36) {
+ free(sigdata);
+ return NULL;
+ }
+ sigdata->name[36] = 0;
+
+ sigdata->order = NULL;
+ sigdata->instrument = NULL;
+ sigdata->pattern = NULL;
+ sigdata->midi = NULL;
+ sigdata->checkpoint = NULL;
+ sigdata->sample = NULL;
+
+ sigdata->n_instruments = 0;
+
+ sigdata->song_message = malloc(72 + 2 + 1);
+ if (!sigdata->song_message) {
+ free(sigdata);
+ return NULL;
+ }
+ if (dumbfile_getnc(sigdata->song_message, 36, f) < 36) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ sigdata->song_message[36] = 13;
+ sigdata->song_message[36 + 1] = 10;
+ if (dumbfile_getnc(sigdata->song_message + 38, 36, f) < 36) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ sigdata->song_message[38 + 36] = 0;
+
+ sigdata->n_samples = dumbfile_getc(f);
+ sigdata->n_patterns = dumbfile_getc(f);
+ sigdata->restart_position = dumbfile_getc(f);
+
+ if ((sigdata->n_samples) > 64 || (sigdata->n_patterns > 128)) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ sigdata->order = malloc(128); /* We may need to scan the extra ones! */
+ if (!sigdata->order) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ if (dumbfile_getnc(sigdata->order, 128, f) < 128) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ for (i = 0; i < 128; i++) {
+ if (sigdata->order[i] == 255) break;
+ if (sigdata->order[i] >= sigdata->n_patterns) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ }
+ if (!i) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ sigdata->n_orders = i;
+
+ if (dumbfile_getnc(tempolist, 128, f) < 128) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ if (dumbfile_getnc(breaklist, 128, f) < 128) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
+ if (!sigdata->sample) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ for (i = 0; i < sigdata->n_samples; i++)
+ sigdata->sample[i].data = NULL;
+
+ for (i = 0; i < sigdata->n_samples; i++) {
+ if (it_669_read_sample_header(&sigdata->sample[i], f)) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ }
+
+ /* May as well try to save a tiny bit of memory. */
+ if (sigdata->n_orders < 128) {
+ unsigned char *order = realloc(sigdata->order, sigdata->n_orders);
+ if (order) sigdata->order = order;
+ }
+
+ sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
+ if (!sigdata->pattern) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ for (i = 0; i < sigdata->n_patterns; i++)
+ sigdata->pattern[i].entry = NULL;
+
+ n_channels = 0;
+
+ /* Read in the patterns */
+ {
+ unsigned char *buffer = malloc(64 * 3 * 8); /* 64 rows * 3 bytes * 8 channels */
+ if (!buffer) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ for (i = 0; i < sigdata->n_patterns; i++) {
+ if (it_669_read_pattern(&sigdata->pattern[i], f, tempolist[i], breaklist[i], buffer, &n_channels) != 0) {
+ free(buffer);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ }
+ free(buffer);
+ }
+
+ sigdata->n_pchannels = n_channels;
+
+ /* And finally, the sample data */
+ for (i = 0; i < sigdata->n_samples; i++) {
+ if (it_669_read_sample_data(&sigdata->sample[i], f)) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ }
+
+ /* Now let's initialise the remaining variables, and we're done! */
+ sigdata->flags = IT_OLD_EFFECTS | IT_LINEAR_SLIDES | IT_STEREO | IT_WAS_A_669;
+
+ sigdata->global_volume = 128;
+ sigdata->mixing_volume = 48;
+ sigdata->speed = 4;
+ sigdata->tempo = 78;
+ sigdata->pan_separation = 128;
+
+ memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
+
+ for (i = 0; i < DUMB_IT_N_CHANNELS; i += 2) {
+ sigdata->channel_pan[i+0] = 48;
+ sigdata->channel_pan[i+1] = 16;
+ }
+
+ _dumb_it_fix_invalid_orders(sigdata);
+
+ return sigdata;
+}
+
+
+
+DUH *dumb_read_669_quick(DUMBFILE *f)
+{
+ sigdata_t *sigdata;
+ int ext;
+
+ DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+ sigdata = it_669_load_sigdata(f, &ext);
+
+ if (!sigdata)
+ return NULL;
+
+ {
+ const char *tag[2][2];
+ tag[0][0] = "TITLE";
+ tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+ tag[1][0] = "FORMAT";
+ tag[1][1] = ext ? "669 Extended" : "669";
+ return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
+ }
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/read6692.c b/plugins/dumb/dumb-kode54/src/it/read6692.c
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/read6692.c
diff --git a/plugins/dumb/dumb-kode54/src/it/readam.c b/plugins/dumb/dumb-kode54/src/it/readam.c
new file mode 100644
index 00000000..14ad3c07
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/readam.c
@@ -0,0 +1,752 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * readam.c - Code to read a RIFF AM module / / \ \
+ * from a parsed RIFF structure. | < / \_
+ * | \/ /\ /
+ * By Chris Moeller. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+#include "internal/riff.h"
+
+static int it_riff_am_process_sample( IT_SAMPLE * sample, const unsigned char * data, int len, int ver )
+{
+ int header_length;
+ int default_pan;
+ int default_volume;
+ int flags;
+ int length;
+ int length_bytes;
+ int loop_start;
+ int loop_end;
+ int sample_rate;
+
+ if ( ver == 0 )
+ {
+ if ( len < 0x38 )
+ return -1;
+
+ header_length = 0x38;
+
+ memcpy( sample->name, data, 28 );
+ sample->name[ 28 ] = 0;
+
+ default_pan = data[ 0x1C ];
+ default_volume = data[ 0x1D ];
+ flags = data[ 0x1E ] | ( data[ 0x1F ] << 8 );
+ length = data[ 0x20 ] | ( data[ 0x21 ] << 8 ) | ( data[ 0x22 ] << 16 ) | ( data[ 0x23 ] << 24 );
+ loop_start = data[ 0x24 ] | ( data[ 0x25 ] << 8 ) | ( data[ 0x26 ] << 16 ) | ( data[ 0x27 ] << 24 );
+ loop_end = data[ 0x28 ] | ( data[ 0x29 ] << 8 ) | ( data[ 0x2A ] << 16 ) | ( data[ 0x2B ] << 24 );
+ sample_rate = data[ 0x2C ] | ( data[ 0x2D ] << 8 ) | ( data[ 0x2E ] << 16 ) | ( data[ 0x2F ] << 24 );
+ }
+ else
+ {
+ if (len < 4) return -1;
+
+ header_length = data[ 0 ] | ( data[ 1 ] << 8 ) | ( data[ 2 ] << 16 ) | ( data[ 3 ] << 24 );
+ if ( header_length < 0x40 )
+ return -1;
+ if ( header_length + 4 > len )
+ return -1;
+
+ data += 4;
+ len -= 4;
+
+ memcpy( sample->name, data, 32 );
+ sample->name[ 32 ] = 0;
+
+ default_pan = data[ 0x20 ] | ( data[ 0x21 ] << 8 );
+ default_volume = data[ 0x22 ] | ( data[ 0x23 ] << 8 );
+ flags = data[ 0x24 ] | ( data[ 0x25 ] << 8 ); /* | ( data[ 0x26 ] << 16 ) | ( data[ 0x27 ] << 24 );*/
+ length = data[ 0x28 ] | ( data[ 0x29 ] << 8 ) | ( data[ 0x2A ] << 16 ) | ( data[ 0x2B ] << 24 );
+ loop_start = data[ 0x2C ] | ( data[ 0x2D ] << 8 ) | ( data[ 0x2E ] << 16 ) | ( data[ 0x2F ] << 24 );
+ loop_end = data[ 0x30 ] | ( data[ 0x31 ] << 8 ) | ( data[ 0x32 ] << 16 ) | ( data[ 0x33 ] << 24 );
+ sample_rate = data[ 0x34 ] | ( data[ 0x35 ] << 8 ) | ( data[ 0x36 ] << 16 ) | ( data[ 0x37 ] << 24 );
+
+ if ( default_pan > 0x7FFF || default_volume > 0x7FFF )
+ return -1;
+
+ default_pan = default_pan * 64 / 32767;
+ default_volume = default_volume * 64 / 32767;
+ }
+
+ /*if ( data[ 0x38 ] || data[ 0x39 ] || data[ 0x3A ] || data[ 0x3B ] )
+ return -1;*/
+
+ if ( ! length ) {
+ sample->flags &= ~IT_SAMPLE_EXISTS;
+ return 0;
+ }
+
+ if ( flags & ~( 0x8000 | 0x80 | 0x20 | 0x10 | 0x08 | 0x04 ) )
+ return -1;
+
+ length_bytes = length << ( ( flags & 0x04 ) >> 2 );
+
+ if ( length_bytes + header_length > len )
+ return -1;
+
+ sample->flags = 0;
+
+ if ( flags & 0x80 ) sample->flags |= IT_SAMPLE_EXISTS;
+ if ( flags & 0x04 ) sample->flags |= IT_SAMPLE_16BIT;
+
+ sample->length = length;
+ sample->loop_start = loop_start;
+ sample->loop_end = loop_end;
+ sample->C5_speed = sample_rate;
+ sample->default_volume = default_volume;
+ sample->default_pan = default_pan | ( ( flags & 0x20 ) << 2 );
+ sample->filename[0] = 0;
+ sample->global_volume = 64;
+ sample->vibrato_speed = 0;
+ sample->vibrato_depth = 0;
+ sample->vibrato_rate = 0;
+ sample->vibrato_waveform = IT_VIBRATO_SINE;
+ sample->finetune = 0;
+ sample->max_resampling_quality = -1;
+
+ if ( flags & 0x08 )
+ {
+ if (((unsigned int)sample->loop_end <= (unsigned int)sample->length) &&
+ ((unsigned int)sample->loop_start < (unsigned int)sample->loop_end))
+ {
+ sample->length = sample->loop_end;
+ sample->flags |= IT_SAMPLE_LOOP;
+ if ( flags & 0x10 ) sample->flags |= IT_SAMPLE_PINGPONG_LOOP;
+ }
+ }
+
+ length_bytes = sample->length << ( ( flags & 0x04 ) >> 2 );
+
+ sample->data = malloc( length_bytes );
+ if ( ! sample->data )
+ return -1;
+
+ memcpy( sample->data, data + header_length, length_bytes );
+
+ return 0;
+}
+
+static int it_riff_am_process_pattern( IT_PATTERN * pattern, const unsigned char * data, int len, int ver )
+{
+ int nrows, row, pos;
+ unsigned flags;
+ IT_ENTRY * entry;
+
+ nrows = data[0] + 1;
+
+ pattern->n_rows = nrows;
+
+ data += 1;
+ len -= 1;
+
+ pattern->n_entries = 0;
+
+ row = 0;
+ pos = 0;
+
+ while ( (row < nrows) && (pos < len) ) {
+ if ( ! data[ pos ] ) {
+ ++ row;
+ ++ pos;
+ continue;
+ }
+
+ flags = data[ pos++ ] & 0xE0;
+
+ if (flags) {
+ ++ pattern->n_entries;
+ if (flags & 0x80) pos += 2;
+ if (flags & 0x40) pos += 2;
+ if (flags & 0x20) pos ++;
+ }
+ }
+
+ if ( ! pattern->n_entries ) return 0;
+
+ pattern->n_entries += nrows;
+
+ pattern->entry = malloc( pattern->n_entries * sizeof( * pattern->entry ) );
+ if ( ! pattern->entry ) return -1;
+
+ entry = pattern->entry;
+
+ row = 0;
+ pos = 0;
+
+ while ( ( row < nrows ) && ( pos < len ) )
+ {
+ if ( ! data[ pos ] )
+ {
+ IT_SET_END_ROW( entry );
+ ++ entry;
+ ++ row;
+ ++ pos;
+ continue;
+ }
+
+ flags = data[ pos++ ];
+ entry->channel = flags & 0x1F;
+ entry->mask = 0;
+
+ if (flags & 0xE0)
+ {
+ if ( flags & 0x80 )
+ {
+ _dumb_it_xm_convert_effect( data[ pos + 1 ], data[ pos ], entry, 0 );
+ pos += 2;
+ }
+
+ if ( flags & 0x40 )
+ {
+ if ( data[ pos ] )
+ {
+ entry->mask |= IT_ENTRY_INSTRUMENT;
+ entry->instrument = data[ pos ];
+ }
+ if ( data[ pos + 1 ] )
+ {
+ entry->mask |= IT_ENTRY_NOTE;
+ entry->note = data[ pos + 1 ] - 1;
+ }
+ pos += 2;
+ }
+
+ if ( flags & 0x20 )
+ {
+ entry->mask |= IT_ENTRY_VOLPAN;
+ if ( ver == 0 ) entry->volpan = data[ pos ];
+ else entry->volpan = data[ pos ] * 64 / 127;
+ ++ pos;
+ }
+
+ if (entry->mask) entry++;
+ }
+ }
+
+ while ( row < nrows )
+ {
+ IT_SET_END_ROW( entry );
+ ++ entry;
+ ++ row;
+ }
+
+ pattern->n_entries = entry - pattern->entry;
+ if ( ! pattern->n_entries ) return -1;
+
+ return 0;
+}
+
+static DUMB_IT_SIGDATA *it_riff_amff_load_sigdata( struct riff * stream )
+{
+ DUMB_IT_SIGDATA *sigdata;
+
+ int n, o, found;
+
+ unsigned char * ptr;
+
+ if ( ! stream ) goto error;
+
+ if ( stream->type != DUMB_ID( 'A', 'M', 'F', 'F' ) ) goto error;
+
+ sigdata = malloc( sizeof( *sigdata ) );
+ if ( ! sigdata ) goto error;
+
+ sigdata->n_patterns = 0;
+ sigdata->n_samples = 0;
+ sigdata->name[0] = 0;
+
+ found = 0;
+
+ for ( n = 0; n < stream->chunk_count; ++n )
+ {
+ struct riff_chunk * c = stream->chunks + n;
+ switch( c->type )
+ {
+ case DUMB_ID( 'M', 'A', 'I', 'N' ):
+ /* initialization data */
+ if ( ( found & 1 ) || ( c->size < 0x48 ) ) goto error_sd;
+ found |= 1;
+ break;
+
+ case DUMB_ID( 'O', 'R', 'D', 'R' ):
+ if ( ( found & 2 ) || ( c->size < 1 ) ) goto error_sd;
+ found |= 2;
+ break;
+
+ case DUMB_ID( 'P', 'A', 'T', 'T' ):
+ ptr = ( unsigned char * ) c->data;
+ if ( ptr[ 0 ] >= sigdata->n_patterns ) sigdata->n_patterns = ptr[ 0 ] + 1;
+ o = ptr[ 1 ] | ( ptr[ 2 ] << 8 ) | ( ptr[ 3 ] << 16 ) | ( ptr[ 4 ] << 24 );
+ if ( o + 5 > c->size ) goto error_sd;
+ break;
+
+ case DUMB_ID( 'I', 'N', 'S', 'T' ):
+ {
+ if ( c->size < 0xE1 ) goto error;
+ ptr = ( unsigned char * ) c->data;
+ if ( ptr[ 1 ] >= sigdata->n_samples ) sigdata->n_samples = ptr[ 1 ] + 1;
+ if ( c->size >= 0x121 && ( ptr[ 0xE1 ] == 'S' && ptr[ 0xE2 ] == 'A' &&
+ ptr[ 0xE3 ] == 'M' && ptr[ 0xE4 ] == 'P' ) )
+ {
+ unsigned size = ptr[ 0xE5 ] | ( ptr[ 0xE6 ] << 8 ) | ( ptr[ 0xE7 ] << 16 ) | ( ptr[ 0xE8 ] << 24 );
+ if ( size + 0xE1 + 8 > c->size ) goto error;
+ }
+ }
+ break;
+ }
+ }
+
+ if ( found != 3 || !sigdata->n_samples || !sigdata->n_patterns ) goto error_sd;
+
+ if ( sigdata->n_samples > 255 || sigdata->n_patterns > 255 ) goto error_sd;
+
+ sigdata->song_message = NULL;
+ sigdata->order = NULL;
+ sigdata->instrument = NULL;
+ sigdata->sample = NULL;
+ sigdata->pattern = NULL;
+ sigdata->midi = NULL;
+ sigdata->checkpoint = NULL;
+
+ sigdata->mixing_volume = 48;
+ sigdata->pan_separation = 128;
+
+ sigdata->n_instruments = 0;
+ sigdata->n_orders = 0;
+
+ memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
+
+ for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) {
+ sigdata->channel_pan[n ] = 16;
+ sigdata->channel_pan[n+1] = 48;
+ sigdata->channel_pan[n+2] = 48;
+ sigdata->channel_pan[n+3] = 16;
+ }
+
+ for ( n = 0; n < stream->chunk_count; ++n )
+ {
+ struct riff_chunk * c = stream->chunks + n;
+ switch ( c->type )
+ {
+ case DUMB_ID( 'M', 'A', 'I', 'N' ):
+ ptr = ( unsigned char * ) c->data;
+ memcpy( sigdata->name, c->data, 64 );
+ sigdata->name[ 64 ] = 0;
+ sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_AN_S3M;
+ if ( ! ( ptr[ 0x40 ] & 1 ) ) sigdata->flags |= IT_LINEAR_SLIDES;
+ if ( ( ptr[ 0x40 ] & ~3 ) || ! ( ptr[ 0x40 ] & 2 ) ) goto error_usd; // unknown flags
+ sigdata->n_pchannels = ptr[ 0x41 ];
+ sigdata->speed = ptr[ 0x42 ];
+ sigdata->tempo = ptr[ 0x43 ];
+
+ sigdata->global_volume = ptr[ 0x48 ];
+
+ if ( c->size < 0x48 + sigdata->n_pchannels ) goto error_usd;
+
+ for ( o = 0; o < sigdata->n_pchannels; ++o )
+ {
+ sigdata->channel_pan[ o ] = ptr[ 0x49 + o ];
+ if ( ptr[ 0x49 + o ] >= 128 )
+ {
+ sigdata->channel_volume[ o ] = 0;
+ }
+ }
+ break;
+ }
+ }
+
+ sigdata->pattern = malloc( sigdata->n_patterns * sizeof( *sigdata->pattern ) );
+ if ( ! sigdata->pattern ) goto error_usd;
+ for ( n = 0; n < sigdata->n_patterns; ++n )
+ sigdata->pattern[ n ].entry = NULL;
+
+ sigdata->sample = malloc( sigdata->n_samples * sizeof( *sigdata->sample ) );
+ if ( ! sigdata->sample ) goto error_usd;
+ for ( n = 0; n < sigdata->n_samples; ++n )
+ {
+ IT_SAMPLE * sample = sigdata->sample + n;
+ sample->data = NULL;
+ sample->flags = 0;
+ sample->name[ 0 ] = 0;
+ }
+
+ for ( n = 0; n < stream->chunk_count; ++n )
+ {
+ struct riff_chunk * c = stream->chunks + n;
+ switch ( c->type )
+ {
+ case DUMB_ID( 'O', 'R', 'D', 'R' ):
+ ptr = ( unsigned char * ) c->data;
+ sigdata->n_orders = ptr[ 0 ] + 1;
+ if ( sigdata->n_orders + 1 > c->size ) goto error_usd;
+ sigdata->order = malloc( sigdata->n_orders );
+ if ( ! sigdata->order ) goto error_usd;
+ memcpy( sigdata->order, ptr + 1, sigdata->n_orders );
+ break;
+
+ case DUMB_ID( 'P', 'A', 'T', 'T' ):
+ ptr = ( unsigned char * ) c->data;
+ o = ptr[ 1 ] | ( ptr[ 2 ] << 8 ) | ( ptr[ 3 ] << 16 ) | ( ptr[ 4 ] << 24 );
+ if ( it_riff_am_process_pattern( sigdata->pattern + ptr[ 0 ], ptr + 5, o, 0 ) ) goto error_usd;
+ break;
+
+ case DUMB_ID( 'I', 'N', 'S', 'T' ):
+ {
+ IT_SAMPLE * sample;
+ ptr = ( unsigned char * ) c->data;
+ sample = sigdata->sample + ptr[ 1 ];
+ if ( c->size >= 0x121 && ( ptr[ 0xE1 ] == 'S' && ptr[ 0xE2 ] == 'A' &&
+ ptr[ 0xE3 ] == 'M' && ptr[ 0xE4 ] == 'P' ) )
+ {
+ unsigned size = ptr[ 0xE5 ] | ( ptr[ 0xE6 ] << 8 ) | ( ptr[ 0xE7 ] << 16 ) | ( ptr[ 0xE8 ] << 24 );
+ if ( it_riff_am_process_sample( sample, ptr + 0xE1 + 8, size, 0 ) ) goto error_usd;
+ }
+ else
+ {
+ memcpy( sample->name, ptr + 2, 28 );
+ sample->name[ 28 ] = 0;
+ }
+ }
+ break;
+ }
+ }
+
+ _dumb_it_fix_invalid_orders( sigdata );
+
+ return sigdata;
+
+error_usd:
+ _dumb_it_unload_sigdata( sigdata );
+ goto error;
+error_sd:
+ free( sigdata );
+error:
+ return NULL;
+}
+
+static DUMB_IT_SIGDATA *it_riff_am_load_sigdata( struct riff * stream )
+{
+ DUMB_IT_SIGDATA *sigdata;
+
+ int n, o, p, found;
+
+ unsigned char * ptr;
+
+ if ( ! stream ) goto error;
+
+ if ( stream->type != DUMB_ID( 'A', 'M', ' ', ' ' ) ) goto error;
+
+ sigdata = malloc(sizeof(*sigdata));
+ if ( ! sigdata ) goto error;
+
+ sigdata->n_patterns = 0;
+ sigdata->n_samples = 0;
+ sigdata->name[0] = 0;
+
+ found = 0;
+
+ for ( n = 0; n < stream->chunk_count; ++n )
+ {
+ struct riff_chunk * c = stream->chunks + n;
+ switch( c->type )
+ {
+ case DUMB_ID( 'I' ,'N' ,'I' ,'T' ):
+ /* initialization data */
+ if ( ( found & 1 ) || ( c->size < 0x48 ) ) goto error_sd;
+ found |= 1;
+ break;
+
+ case DUMB_ID( 'O', 'R', 'D', 'R' ):
+ if ( ( found & 2 ) || ( c->size < 1 ) ) goto error_sd;
+ found |= 2;
+ break;
+
+ case DUMB_ID( 'P', 'A', 'T', 'T' ):
+ ptr = ( unsigned char * ) c->data;
+ if ( ptr[ 0 ] >= sigdata->n_patterns ) sigdata->n_patterns = ptr[ 0 ] + 1;
+ o = ptr[ 1 ] | ( ptr[ 2 ] << 8 ) | ( ptr[ 3 ] << 16 ) | ( ptr[ 4 ] << 24 );
+ if ( o + 5 > c->size ) goto error_sd;
+ break;
+
+ case DUMB_ID( 'R', 'I', 'F', 'F' ):
+ {
+ struct riff * str = ( struct riff * ) c->data;
+ switch ( str->type )
+ {
+ case DUMB_ID( 'A', 'I', ' ', ' ' ):
+ for ( o = 0; o < str->chunk_count; ++o )
+ {
+ struct riff_chunk * chk = str->chunks + o;
+ switch( chk->type )
+ {
+ case DUMB_ID( 'I', 'N', 'S', 'T' ):
+ {
+ struct riff * temp;
+ unsigned size;
+ unsigned sample_found;
+ ptr = ( unsigned char * ) chk->data;
+ size = ptr[ 0 ] | ( ptr[ 1 ] << 8 ) | ( ptr[ 2 ] << 16 ) | ( ptr[ 3 ] << 24 );
+ if ( size < 0x142 ) goto error;
+ sample_found = 0;
+ if ( ptr[ 5 ] >= sigdata->n_samples ) sigdata->n_samples = ptr[ 5 ] + 1;
+ temp = riff_parse( ptr + 4 + size, chk->size - size - 4, 1 );
+ if ( temp )
+ {
+ if ( temp->type == DUMB_ID( 'A', 'S', ' ', ' ' ) )
+ {
+ for ( p = 0; p < temp->chunk_count; ++p )
+ {
+ if ( temp->chunks[ p ].type == DUMB_ID( 'S', 'A', 'M', 'P' ) )
+ {
+ if ( sample_found )
+ {
+ riff_free( temp );
+ goto error;
+ }
+ sample_found = 1;
+ }
+ }
+ }
+ riff_free( temp );
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ if ( found != 3 || !sigdata->n_samples || !sigdata->n_patterns ) goto error_sd;
+
+ if ( sigdata->n_samples > 255 || sigdata->n_patterns > 255 ) goto error_sd;
+
+ sigdata->song_message = NULL;
+ sigdata->order = NULL;
+ sigdata->instrument = NULL;
+ sigdata->sample = NULL;
+ sigdata->pattern = NULL;
+ sigdata->midi = NULL;
+ sigdata->checkpoint = NULL;
+
+ sigdata->mixing_volume = 48;
+ sigdata->pan_separation = 128;
+
+ sigdata->n_instruments = 0;
+ sigdata->n_orders = 0;
+
+ memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
+
+ for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) {
+ sigdata->channel_pan[n ] = 16;
+ sigdata->channel_pan[n+1] = 48;
+ sigdata->channel_pan[n+2] = 48;
+ sigdata->channel_pan[n+3] = 16;
+ }
+
+ for ( n = 0; n < stream->chunk_count; ++n )
+ {
+ struct riff_chunk * c = stream->chunks + n;
+ switch ( c->type )
+ {
+ case DUMB_ID( 'I', 'N', 'I', 'T' ):
+ ptr = ( unsigned char * ) c->data;
+ memcpy( sigdata->name, c->data, 64 );
+ sigdata->name[ 64 ] = 0;
+ sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_AN_S3M;
+ if ( ! ( ptr[ 0x40 ] & 1 ) ) sigdata->flags |= IT_LINEAR_SLIDES;
+ if ( ( ptr[ 0x40 ] & ~3 ) || ! ( ptr[ 0x40 ] & 2 ) ) goto error_usd; // unknown flags
+ sigdata->n_pchannels = ptr[ 0x41 ];
+ sigdata->speed = ptr[ 0x42 ];
+ sigdata->tempo = ptr[ 0x43 ];
+
+ sigdata->global_volume = ptr[ 0x48 ];
+
+ if ( c->size < 0x48 + sigdata->n_pchannels ) goto error_usd;
+
+ for ( o = 0; o < sigdata->n_pchannels; ++o )
+ {
+ if ( ptr[ 0x49 + o ] <= 128 )
+ {
+ sigdata->channel_pan[ o ] = ptr[ 0x49 + o ] / 2;
+ }
+ else
+ {
+ sigdata->channel_volume[ o ] = 0;
+ }
+ }
+ break;
+ }
+ }
+
+ sigdata->pattern = malloc( sigdata->n_patterns * sizeof( *sigdata->pattern ) );
+ if ( ! sigdata->pattern ) goto error_usd;
+ for ( n = 0; n < sigdata->n_patterns; ++n )
+ sigdata->pattern[ n ].entry = NULL;
+
+ sigdata->sample = malloc( sigdata->n_samples * sizeof( *sigdata->sample ) );
+ if ( ! sigdata->sample ) goto error_usd;
+ for ( n = 0; n < sigdata->n_samples; ++n )
+ {
+ IT_SAMPLE * sample = sigdata->sample + n;
+ sample->data = NULL;
+ sample->flags = 0;
+ sample->name[ 0 ] = 0;
+ }
+
+ for ( n = 0; n < stream->chunk_count; ++n )
+ {
+ struct riff_chunk * c = stream->chunks + n;
+ switch ( c->type )
+ {
+ case DUMB_ID( 'O', 'R', 'D', 'R' ):
+ ptr = ( unsigned char * ) c->data;
+ sigdata->n_orders = ptr[ 0 ] + 1;
+ if ( sigdata->n_orders + 1 > c->size ) goto error_usd;
+ sigdata->order = malloc( sigdata->n_orders );
+ if ( ! sigdata->order ) goto error_usd;
+ memcpy( sigdata->order, ptr + 1, sigdata->n_orders );
+ break;
+
+ case DUMB_ID( 'P', 'A', 'T', 'T' ):
+ ptr = ( unsigned char * ) c->data;
+ o = ptr[ 1 ] | ( ptr[ 2 ] << 8 ) | ( ptr[ 3 ] << 16 ) | ( ptr[ 4 ] << 24 );
+ if ( it_riff_am_process_pattern( sigdata->pattern + ptr[ 0 ], ptr + 5, o, 1 ) ) goto error_usd;
+ break;
+
+ case DUMB_ID( 'R', 'I', 'F', 'F' ):
+ {
+ struct riff * str = ( struct riff * ) c->data;
+ switch ( str->type )
+ {
+ case DUMB_ID('A', 'I', ' ', ' '):
+ for ( o = 0; o < str->chunk_count; ++o )
+ {
+ struct riff_chunk * chk = str->chunks + o;
+ switch( chk->type )
+ {
+ case DUMB_ID( 'I', 'N', 'S', 'T' ):
+ {
+ struct riff * temp;
+ unsigned size;
+ unsigned sample_found;
+ IT_SAMPLE * sample;
+ ptr = ( unsigned char * ) chk->data;
+ size = ptr[ 0 ] | ( ptr[ 1 ] << 8 ) | ( ptr[ 2 ] << 16 ) | ( ptr[ 3 ] << 24 );
+ temp = riff_parse( ptr + 4 + size, chk->size - size - 4, 1 );
+ sample_found = 0;
+ sample = sigdata->sample + ptr[ 5 ];
+ if ( temp )
+ {
+ if ( temp->type == DUMB_ID( 'A', 'S', ' ', ' ' ) )
+ {
+ for ( p = 0; p < temp->chunk_count; ++p )
+ {
+ struct riff_chunk * c = temp->chunks + p;
+ if ( c->type == DUMB_ID( 'S', 'A', 'M', 'P' ) )
+ {
+ if ( sample_found )
+ {
+ riff_free( temp );
+ goto error_usd;
+ }
+ if ( it_riff_am_process_sample( sigdata->sample + ptr[ 5 ], ( unsigned char * ) c->data, c->size, 1 ) )
+ {
+ riff_free( temp );
+ goto error_usd;
+ }
+ sample_found = 1;
+ }
+ }
+ }
+ riff_free( temp );
+ }
+ if ( ! sample_found )
+ {
+ memcpy( sample->name, ptr + 6, 32 );
+ sample->name[ 32 ] = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ _dumb_it_fix_invalid_orders( sigdata );
+
+ return sigdata;
+
+error_usd:
+ _dumb_it_unload_sigdata( sigdata );
+ goto error;
+error_sd:
+ free( sigdata );
+error:
+ return NULL;
+}
+
+DUH *dumb_read_riff_amff( struct riff * stream )
+{
+ sigdata_t *sigdata;
+ long length;
+
+ DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+ sigdata = it_riff_amff_load_sigdata( stream );
+
+ if (!sigdata)
+ return NULL;
+
+ length = 0;/*_dumb_it_build_checkpoints(sigdata, 0);*/
+
+ {
+ const char *tag[2][2];
+ tag[0][0] = "TITLE";
+ tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+ tag[1][0] = "FORMAT";
+ tag[1][1] = "RIFF AMFF";
+ return make_duh( length, 2, ( const char * const (*) [ 2 ] ) tag, 1, & descptr, & sigdata );
+ }
+}
+
+DUH *dumb_read_riff_am( struct riff * stream )
+{
+ sigdata_t *sigdata;
+
+ DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+ sigdata = it_riff_am_load_sigdata( stream );
+
+ if (!sigdata)
+ return NULL;
+
+ {
+ const char *tag[2][2];
+ tag[0][0] = "TITLE";
+ tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+ tag[1][0] = "FORMAT";
+ tag[1][1] = "RIFF AM";
+ return make_duh( -1, 2, ( const char * const (*) [ 2 ] ) tag, 1, & descptr, & sigdata );
+ }
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/readasy.c b/plugins/dumb/dumb-kode54/src/it/readasy.c
new file mode 100644
index 00000000..4c1c09f8
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/readasy.c
@@ -0,0 +1,331 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * readasy.c - Code to read an ASYLUM Music Format / / \ \
+ * module from an open file. | < / \_
+ * | \/ /\ /
+ * By Chris Moeller. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+static int it_asy_read_pattern( IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer )
+{
+ int pos;
+ int channel;
+ int row;
+ IT_ENTRY *entry;
+
+ pattern->n_rows = 64;
+
+ if ( dumbfile_getnc( buffer, 64 * 8 * 4, f ) != 64 * 8 * 4 )
+ return -1;
+
+ /* compute number of entries */
+ pattern->n_entries = 64; /* Account for the row end markers */
+ pos = 0;
+ for ( row = 0; row < 64; ++row ) {
+ for ( channel = 0; channel < 8; ++channel ) {
+ if ( buffer[ pos + 0 ] | buffer[ pos + 1 ] | buffer[ pos + 2 ] | buffer[ pos + 3 ] )
+ ++pattern->n_entries;
+ pos += 4;
+ }
+ }
+
+ pattern->entry = malloc( pattern->n_entries * sizeof( *pattern->entry ) );
+ if ( !pattern->entry )
+ return -1;
+
+ entry = pattern->entry;
+ pos = 0;
+ for ( row = 0; row < 64; ++row ) {
+ for ( channel = 0; channel < 8; ++channel ) {
+ if ( buffer[ pos + 0 ] | buffer[ pos + 1 ] | buffer[ pos + 2 ] | buffer[ pos + 3 ] ) {
+ entry->channel = channel;
+ entry->mask = 0;
+
+ if ( buffer[ pos + 0 ] && buffer[ pos + 0 ] < 96 ) {
+ entry->note = buffer[ pos + 0 ];
+ entry->mask |= IT_ENTRY_NOTE;
+ }
+
+ if ( buffer[ pos + 1 ] && buffer[ pos + 1 ] <= 64 ) {
+ entry->instrument = buffer[ pos + 1 ];
+ entry->mask |= IT_ENTRY_INSTRUMENT;
+ }
+
+ _dumb_it_xm_convert_effect( buffer[ pos + 2 ] & 0x0F, buffer[ pos + 3 ], entry, 1 );
+
+ if ( entry->mask ) ++entry;
+ }
+ pos += 4;
+ }
+ IT_SET_END_ROW( entry );
+ ++entry;
+ }
+
+ pattern->n_entries = entry - pattern->entry;
+
+ return 0;
+}
+
+
+
+static int it_asy_read_sample_header( IT_SAMPLE *sample, DUMBFILE *f )
+{
+ int finetune;
+
+/**
+ 21 22 Chars Sample 1 name. If the name is not a full
+ 22 chars in length, it will be null
+ terminated.
+
+If
+the sample name begins with a '#' character (ASCII $23 (35)) then this is
+assumed not to be an instrument name, and is probably a message.
+*/
+ dumbfile_getnc( sample->name, 22, f );
+ sample->name[22] = 0;
+
+ sample->filename[0] = 0;
+
+/** Each finetune step changes the note 1/8th of a semitone. */
+ finetune = ( signed char ) ( dumbfile_getc( f ) << 4 ) >> 4; /* signed nibble */
+ sample->default_volume = dumbfile_getc( f ); // Should we be setting global_volume to this instead?
+ sample->global_volume = 64;
+ if ( sample->default_volume > 64 ) sample->default_volume = 64;
+ dumbfile_skip( f, 1 ); /* XXX unknown */
+ sample->length = dumbfile_igetl( f );
+ sample->loop_start = dumbfile_igetl( f );
+ sample->loop_end = sample->loop_start + dumbfile_igetl( f );
+
+ if ( sample->length <= 0 ) {
+ sample->flags = 0;
+ return 0;
+ }
+
+ sample->flags = IT_SAMPLE_EXISTS;
+
+ sample->default_pan = 0;
+ sample->C5_speed = (int)( AMIGA_CLOCK / 214.0 );//( long )( 16726.0 * pow( DUMB_PITCH_BASE, finetune * 32 ) );
+ sample->finetune = finetune * 32;
+ // the above line might be wrong
+
+ if ( ( sample->loop_end - sample->loop_start > 2 ) && ( sample->loop_end <= sample->length ) )
+ sample->flags |= IT_SAMPLE_LOOP;
+
+ sample->vibrato_speed = 0;
+ sample->vibrato_depth = 0;
+ sample->vibrato_rate = 0;
+ sample->vibrato_waveform = 0; // do we have to set _all_ these?
+ sample->max_resampling_quality = -1;
+
+ return dumbfile_error(f);
+}
+
+
+
+static int it_asy_read_sample_data( IT_SAMPLE *sample, DUMBFILE *f )
+{
+ long truncated_size;
+
+ /* let's get rid of the sample data coming after the end of the loop */
+ if ( ( sample->flags & IT_SAMPLE_LOOP ) && sample->loop_end < sample->length ) {
+ truncated_size = sample->length - sample->loop_end;
+ sample->length = sample->loop_end;
+ } else {
+ truncated_size = 0;
+ }
+
+ sample->data = malloc( sample->length );
+
+ if ( !sample->data )
+ return -1;
+
+ if ( sample->length )
+ dumbfile_getnc( sample->data, sample->length, f );
+
+ dumbfile_skip( f, truncated_size );
+
+ return dumbfile_error( f );
+}
+
+
+
+static DUMB_IT_SIGDATA *it_asy_load_sigdata(DUMBFILE *f)
+{
+ DUMB_IT_SIGDATA *sigdata;
+ int i;
+
+ static const char sig_part[] = "ASYLUM Music Format";
+ static const char sig_rest[] = " V1.0"; /* whee, string space optimization with format type below */
+
+ char signature [32];
+
+ if ( dumbfile_getnc( signature, 32, f ) != 32 ||
+ memcmp( signature, sig_part, 19 ) ||
+ memcmp( signature + 19, sig_rest, 5 ) ) {
+ return NULL;
+ }
+
+ sigdata = malloc(sizeof(*sigdata));
+ if (!sigdata) {
+ return NULL;
+ }
+
+ sigdata->speed = dumbfile_getc( f ); /* XXX seems to fit the files I have */
+ sigdata->tempo = dumbfile_getc( f ); /* ditto */
+ sigdata->n_samples = dumbfile_getc( f ); /* ditto */
+ sigdata->n_patterns = dumbfile_getc( f );
+ sigdata->n_orders = dumbfile_getc( f );
+ sigdata->restart_position = dumbfile_getc( f );
+
+ if ( dumbfile_error( f ) || !sigdata->n_samples || sigdata->n_samples > 64 || !sigdata->n_patterns ||
+ !sigdata->n_orders ) {
+ free( sigdata );
+ return NULL;
+ }
+
+ if ( sigdata->restart_position > sigdata->n_orders ) /* XXX */
+ sigdata->restart_position = 0;
+
+ sigdata->order = malloc( sigdata->n_orders );
+ if ( !sigdata->order ) {
+ free( sigdata );
+ return NULL;
+ }
+
+ if ( dumbfile_getnc( sigdata->order, sigdata->n_orders, f ) != sigdata->n_orders ||
+ dumbfile_skip( f, 256 - sigdata->n_orders ) ) {
+ free( sigdata->order );
+ free( sigdata );
+ return NULL;
+ }
+
+ sigdata->sample = malloc( sigdata->n_samples * sizeof( *sigdata->sample ) );
+ if ( !sigdata->sample ) {
+ free( sigdata->order );
+ free( sigdata );
+ return NULL;
+ }
+
+ sigdata->song_message = NULL;
+ sigdata->instrument = NULL;
+ sigdata->pattern = NULL;
+ sigdata->midi = NULL;
+ sigdata->checkpoint = NULL;
+
+ sigdata->n_instruments = 0;
+
+ for ( i = 0; i < sigdata->n_samples; ++i )
+ sigdata->sample[i].data = NULL;
+
+ for ( i = 0; i < sigdata->n_samples; ++i ) {
+ if ( it_asy_read_sample_header( &sigdata->sample[i], f ) ) {
+ _dumb_it_unload_sigdata( sigdata );
+ return NULL;
+ }
+ }
+
+ if ( dumbfile_skip( f, 37 * ( 64 - sigdata->n_samples ) ) ) {
+ _dumb_it_unload_sigdata( sigdata );
+ return NULL;
+ }
+
+ sigdata->pattern = malloc( sigdata->n_patterns * sizeof( *sigdata->pattern ) );
+ if ( !sigdata->pattern ) {
+ _dumb_it_unload_sigdata( sigdata );
+ return NULL;
+ }
+ for (i = 0; i < sigdata->n_patterns; ++i)
+ sigdata->pattern[i].entry = NULL;
+
+ /* Read in the patterns */
+ {
+ unsigned char *buffer = malloc( 64 * 8 * 4 ); /* 64 rows * 8 channels * 4 bytes */
+ if ( !buffer ) {
+ _dumb_it_unload_sigdata( sigdata );
+ return NULL;
+ }
+ for ( i = 0; i < sigdata->n_patterns; ++i ) {
+ if ( it_asy_read_pattern( &sigdata->pattern[i], f, buffer ) != 0 ) {
+ free( buffer );
+ _dumb_it_unload_sigdata( sigdata );
+ return NULL;
+ }
+ }
+ free( buffer );
+ }
+
+ /* And finally, the sample data */
+ for ( i = 0; i < sigdata->n_samples; ++i ) {
+ if ( it_asy_read_sample_data( &sigdata->sample[i], f ) ) {
+ _dumb_it_unload_sigdata( sigdata );
+ return NULL;
+ }
+ }
+
+ /* Now let's initialise the remaining variables, and we're done! */
+ sigdata->flags = IT_WAS_AN_XM | IT_WAS_A_MOD | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO;
+
+ sigdata->global_volume = 128;
+ sigdata->mixing_volume = 48;
+ sigdata->pan_separation = 128;
+
+ sigdata->n_pchannels = 8;
+
+ sigdata->name[0] = 0;
+
+ memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
+
+ for (i = 0; i < DUMB_IT_N_CHANNELS; i += 4) {
+ sigdata->channel_pan[i+0] = 16;
+ sigdata->channel_pan[i+1] = 48;
+ sigdata->channel_pan[i+2] = 48;
+ sigdata->channel_pan[i+3] = 16;
+ }
+
+ _dumb_it_fix_invalid_orders(sigdata);
+
+ return sigdata;
+}
+
+
+
+DUH *dumb_read_asy_quick(DUMBFILE *f)
+{
+ sigdata_t *sigdata;
+
+ DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+ sigdata = it_asy_load_sigdata(f);
+
+ if (!sigdata)
+ return NULL;
+
+ {
+ const char *tag[2][2];
+ tag[0][0] = "TITLE";
+ tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+ tag[1][0] = "FORMAT";
+ tag[1][1] = "ASYLUM Music Format";
+ return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
+ }
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/readdsmf.c b/plugins/dumb/dumb-kode54/src/it/readdsmf.c
new file mode 100644
index 00000000..2a4f23e6
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/readdsmf.c
@@ -0,0 +1,373 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * readam.c - Code to read a RIFF DSMF module / / \ \
+ * from a parsed RIFF structure. | < / \_
+ * | \/ /\ /
+ * By Chris Moeller. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+#include "internal/riff.h"
+
+static int it_riff_dsmf_process_sample( IT_SAMPLE * sample, const unsigned char * data, int len )
+{
+ int flags;
+
+ memcpy( sample->filename, data, 13 );
+ sample->filename[ 14 ] = 0;
+
+ flags = data[ 13 ] | ( data[ 14 ] << 8 );
+ sample->default_volume = data[ 15 ];
+ sample->length = data[ 16 ] | ( data[ 17 ] << 8 ) | ( data[ 18 ] << 16 ) | ( data[ 19 ] << 24 );
+ sample->loop_start = data[ 20 ] | ( data[ 21 ] << 8 ) | ( data[ 22 ] << 16 ) | ( data[ 23 ] << 24 );
+ sample->loop_end = data[ 24 ] | ( data[ 25 ] << 8 ) | ( data[ 26 ] << 16 ) | ( data[ 27 ] << 24 );
+ sample->C5_speed = ( data[ 32 ] | ( data[ 33 ] << 8 ) ) * 2;
+ memcpy( sample->name, data + 36, 28 );
+ sample->name[ 28 ] = 0;
+
+ /*if ( data[ 0x38 ] || data[ 0x39 ] || data[ 0x3A ] || data[ 0x3B ] )
+ return -1;*/
+
+ if ( ! sample->length ) {
+ sample->flags &= ~IT_SAMPLE_EXISTS;
+ return 0;
+ }
+
+ /*if ( flags & ~( 2 | 1 ) )
+ return -1;*/
+
+ if ( sample->length + 64 > len )
+ return -1;
+
+ sample->flags = IT_SAMPLE_EXISTS;
+
+ sample->default_pan = 0;
+ sample->global_volume = 64;
+ sample->vibrato_speed = 0;
+ sample->vibrato_depth = 0;
+ sample->vibrato_rate = 0;
+ sample->vibrato_waveform = IT_VIBRATO_SINE;
+ sample->finetune = 0;
+ sample->max_resampling_quality = -1;
+
+ if ( flags & 1 )
+ {
+ if (((unsigned int)sample->loop_end <= (unsigned int)sample->length) &&
+ ((unsigned int)sample->loop_start < (unsigned int)sample->loop_end))
+ {
+ sample->length = sample->loop_end;
+ sample->flags |= IT_SAMPLE_LOOP;
+ if ( flags & 0x10 ) sample->flags |= IT_SAMPLE_PINGPONG_LOOP;
+ }
+ }
+
+ sample->data = malloc( sample->length );
+ if ( ! sample->data )
+ return -1;
+
+ memcpy( sample->data, data + 64, sample->length );
+
+ if ( ! ( flags & 2 ) )
+ {
+ for ( flags = 0; flags < sample->length; ++flags )
+ ( ( signed char * ) sample->data ) [ flags ] ^= 0x80;
+ }
+
+ return 0;
+}
+
+static int it_riff_dsmf_process_pattern( IT_PATTERN * pattern, const unsigned char * data, int len )
+{
+ int length, row, pos;
+ unsigned flags;
+ IT_ENTRY * entry;
+
+ length = data[ 0 ] | ( data[ 1 ] << 8 );
+ if ( length > len ) return -1;
+
+ data += 2;
+ len = length - 2;
+
+ pattern->n_rows = 64;
+ pattern->n_entries = 64;
+
+ row = 0;
+ pos = 0;
+
+ while ( (row < 64) && (pos < len) ) {
+ if ( ! data[ pos ] ) {
+ ++ row;
+ ++ pos;
+ continue;
+ }
+
+ flags = data[ pos++ ] & 0xF0;
+
+ if (flags) {
+ ++ pattern->n_entries;
+ if (flags & 0x80) pos ++;
+ if (flags & 0x40) pos ++;
+ if (flags & 0x20) pos ++;
+ if (flags & 0x10) pos += 2;
+ }
+ }
+
+ if ( pattern->n_entries == 64 ) return 0;
+
+ pattern->entry = malloc( pattern->n_entries * sizeof( * pattern->entry ) );
+ if ( ! pattern->entry ) return -1;
+
+ entry = pattern->entry;
+
+ row = 0;
+ pos = 0;
+
+ while ( ( row < 64 ) && ( pos < len ) )
+ {
+ if ( ! data[ pos ] )
+ {
+ IT_SET_END_ROW( entry );
+ ++ entry;
+ ++ row;
+ ++ pos;
+ continue;
+ }
+
+ flags = data[ pos++ ];
+ entry->channel = flags & 0x0F;
+ entry->mask = 0;
+
+ if ( flags & 0xF0 )
+ {
+ if ( flags & 0x80 )
+ {
+ if ( data[ pos ] )
+ {
+ entry->mask |= IT_ENTRY_NOTE;
+ entry->note = data[ pos ] - 1;
+ }
+ ++ pos;
+ }
+
+ if ( flags & 0x40 )
+ {
+ if ( data[ pos ] )
+ {
+ entry->mask |= IT_ENTRY_INSTRUMENT;
+ entry->instrument = data[ pos ];
+ }
+ ++ pos;
+ }
+
+ if ( flags & 0x20 )
+ {
+ entry->mask |= IT_ENTRY_VOLPAN;
+ entry->volpan = data[ pos ];
+ ++ pos;
+ }
+
+ if ( flags & 0x10 )
+ {
+ _dumb_it_xm_convert_effect( data[ pos ], data[ pos + 1 ], entry, 0 );
+ pos += 2;
+ }
+
+ if (entry->mask) entry++;
+ }
+ }
+
+ while ( row < 64 )
+ {
+ IT_SET_END_ROW( entry );
+ ++ entry;
+ ++ row;
+ }
+
+ pattern->n_entries = entry - pattern->entry;
+ if ( ! pattern->n_entries ) return -1;
+
+ return 0;
+}
+
+static DUMB_IT_SIGDATA *it_riff_dsmf_load_sigdata( struct riff * stream )
+{
+ DUMB_IT_SIGDATA *sigdata;
+
+ int n, o, found;
+
+ unsigned char * ptr;
+
+ if ( ! stream ) goto error;
+
+ if ( stream->type != DUMB_ID( 'D', 'S', 'M', 'F' ) ) goto error;
+
+ sigdata = malloc(sizeof(*sigdata));
+ if ( ! sigdata ) goto error;
+
+ sigdata->n_patterns = 0;
+ sigdata->n_samples = 0;
+ sigdata->name[0] = 0;
+
+ found = 0;
+
+ for ( n = 0; n < stream->chunk_count; ++n )
+ {
+ struct riff_chunk * c = stream->chunks + n;
+ switch( c->type )
+ {
+ case DUMB_ID( 'S' ,'O' ,'N' ,'G' ):
+ /* initialization data */
+ if ( ( found ) || ( c->size < 192 ) ) goto error_sd;
+ found = 1;
+ break;
+
+ case DUMB_ID( 'P', 'A', 'T', 'T' ):
+ ++ sigdata->n_patterns;
+ break;
+
+ case DUMB_ID( 'I', 'N', 'S', 'T' ):
+ ++ sigdata->n_samples;
+ break;
+ }
+ }
+
+ if ( !found || !sigdata->n_samples || !sigdata->n_patterns ) goto error_sd;
+
+ if ( sigdata->n_samples > 255 || sigdata->n_patterns > 255 ) goto error_sd;
+
+ sigdata->song_message = NULL;
+ sigdata->order = NULL;
+ sigdata->instrument = NULL;
+ sigdata->sample = NULL;
+ sigdata->pattern = NULL;
+ sigdata->midi = NULL;
+ sigdata->checkpoint = NULL;
+
+ sigdata->mixing_volume = 48;
+ sigdata->pan_separation = 128;
+
+ sigdata->n_instruments = 0;
+ sigdata->n_orders = 0;
+
+ memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
+
+ for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) {
+ sigdata->channel_pan[n ] = 16;
+ sigdata->channel_pan[n+1] = 48;
+ sigdata->channel_pan[n+2] = 48;
+ sigdata->channel_pan[n+3] = 16;
+ }
+
+ for ( n = 0; n < stream->chunk_count; ++n )
+ {
+ struct riff_chunk * c = stream->chunks + n;
+ switch ( c->type )
+ {
+ case DUMB_ID( 'S', 'O', 'N', 'G' ):
+ ptr = ( unsigned char * ) c->data;
+ memcpy( sigdata->name, c->data, 28 );
+ sigdata->name[ 28 ] = 0;
+ sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX;
+ sigdata->n_orders = ptr[ 36 ] | ( ptr[ 37 ] << 8 );
+ //sigdata->n_samples = ptr[ 38 ] | ( ptr[ 39 ] << 8 ); // whatever
+ //sigdata->n_patterns = ptr[ 40 ] | ( ptr[ 41 ] << 8 );
+ sigdata->n_pchannels = ptr[ 42 ] | ( ptr[ 43 ] << 8 );
+ sigdata->global_volume = ptr[ 44 ];
+ sigdata->mixing_volume = ptr[ 45 ];
+ sigdata->speed = ptr[ 46 ];
+ sigdata->tempo = ptr[ 47 ];
+
+ for ( o = 0; o < 16; ++o )
+ {
+ sigdata->channel_pan[ o ] = ptr[ 48 + o ] / 2;
+ }
+
+ sigdata->order = malloc( 128 );
+ if ( ! sigdata->order ) goto error_usd;
+ memcpy( sigdata->order, ptr + 64, 128 );
+
+ break;
+ }
+ }
+
+ sigdata->pattern = malloc( sigdata->n_patterns * sizeof( *sigdata->pattern ) );
+ if ( ! sigdata->pattern ) goto error_usd;
+ for ( n = 0; n < sigdata->n_patterns; ++n )
+ sigdata->pattern[ n ].entry = NULL;
+
+ sigdata->sample = malloc( sigdata->n_samples * sizeof( *sigdata->sample ) );
+ if ( ! sigdata->sample ) goto error_usd;
+ for ( n = 0; n < sigdata->n_samples; ++n )
+ {
+ IT_SAMPLE * sample = sigdata->sample + n;
+ sample->data = NULL;
+ }
+
+ sigdata->n_samples = 0;
+ sigdata->n_patterns = 0;
+
+ for ( n = 0; n < stream->chunk_count; ++n )
+ {
+ struct riff_chunk * c = stream->chunks + n;
+ switch ( c->type )
+ {
+ case DUMB_ID( 'P', 'A', 'T', 'T' ):
+ if ( it_riff_dsmf_process_pattern( sigdata->pattern + sigdata->n_patterns, ( unsigned char * ) c->data, c->size ) ) goto error_usd;
+ ++ sigdata->n_patterns;
+ break;
+
+ case DUMB_ID( 'I', 'N', 'S', 'T' ):
+ if ( it_riff_dsmf_process_sample( sigdata->sample + sigdata->n_samples, ( unsigned char * ) c->data, c->size ) ) goto error_usd;
+ ++ sigdata->n_samples;
+ break;
+ }
+ }
+
+ _dumb_it_fix_invalid_orders( sigdata );
+
+ return sigdata;
+
+error_usd:
+ _dumb_it_unload_sigdata( sigdata );
+ goto error;
+error_sd:
+ free( sigdata );
+error:
+ return NULL;
+}
+
+DUH *dumb_read_riff_dsmf( struct riff * stream )
+{
+ sigdata_t *sigdata;
+
+ DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+ sigdata = it_riff_dsmf_load_sigdata( stream );
+
+ if (!sigdata)
+ return NULL;
+
+ {
+ const char *tag[2][2];
+ tag[0][0] = "TITLE";
+ tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+ tag[1][0] = "FORMAT";
+ tag[1][1] = "RIFF DSMF";
+ return make_duh( -1, 2, ( const char * const (*) [ 2 ] ) tag, 1, & descptr, & sigdata );
+ }
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/readmod.c b/plugins/dumb/dumb-kode54/src/it/readmod.c
new file mode 100644
index 00000000..0963c6d9
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/readmod.c
@@ -0,0 +1,780 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * readmod.c - Code to read a good old-fashioned / / \ \
+ * Amiga module from an open file. | < / \_
+ * | \/ /\ /
+ * By entheh. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+static int it_mod_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, int n_channels, unsigned char *buffer)
+{
+ int pos;
+ int channel;
+ int row;
+ IT_ENTRY *entry;
+
+ pattern->n_rows = 64;
+
+ if (n_channels == 0) {
+ /* Read the first four channels, leaving gaps for the rest. */
+ for (pos = 0; pos < 64*8*4; pos += 8*4)
+ dumbfile_getnc(buffer + pos, 4*4, f);
+ /* Read the other channels into the gaps we left. */
+ for (pos = 4*4; pos < 64*8*4; pos += 8*4)
+ dumbfile_getnc(buffer + pos, 4*4, f);
+
+ n_channels = 8;
+ } else
+ dumbfile_getnc(buffer, 64 * n_channels * 4, f);
+
+ if (dumbfile_error(f))
+ return -1;
+
+ /* compute number of entries */
+ pattern->n_entries = 64; /* Account for the row end markers */
+ pos = 0;
+ for (row = 0; row < 64; row++) {
+ for (channel = 0; channel < n_channels; channel++) {
+ if (buffer[pos+0] | buffer[pos+1] | buffer[pos+2] | buffer[pos+3])
+ pattern->n_entries++;
+ pos += 4;
+ }
+ }
+
+ pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry));
+ if (!pattern->entry)
+ return -1;
+
+ entry = pattern->entry;
+ pos = 0;
+ for (row = 0; row < 64; row++) {
+ for (channel = 0; channel < n_channels; channel++) {
+ if (buffer[pos+0] | buffer[pos+1] | buffer[pos+2] | buffer[pos+3]) {
+ unsigned char sample = (buffer[pos+0] & 0xF0) | (buffer[pos+2] >> 4);
+ int period = ((int)(buffer[pos+0] & 0x0F) << 8) | buffer[pos+1];
+
+ entry->channel = channel;
+ entry->mask = 0;
+
+ if (period) {
+ int note;
+ entry->mask |= IT_ENTRY_NOTE;
+
+ /* frequency = (AMIGA_DIVISOR / 8) / (period * 2)
+ * C-1: period = 214 -> frequency = 16726
+ * so, set C5_speed to 16726
+ * and period = 214 should translate to C5 aka 60
+ * halve the period, go up an octive
+ *
+ * period = 214 / pow(DUMB_SEMITONE_BASE, note - 60)
+ * pow(DUMB_SEMITONE_BASE, note - 60) = 214 / period
+ * note - 60 = log(214/period) / log(DUMB_SEMITONE_BASE)
+ */
+ note = (int)floor(log(214.0/period) / log(DUMB_SEMITONE_BASE) + 60.5);
+ entry->note = MID(0, note, 119);
+ // or should we preserve the period?
+ //entry->note = buffer[pos+0] & 0x0F; /* High nibble */
+ //entry->volpan = buffer[pos+1]; /* Low byte */
+ // and what about finetune?
+ }
+
+ if (sample) {
+ entry->mask |= IT_ENTRY_INSTRUMENT;
+ entry->instrument = sample;
+ }
+
+ _dumb_it_xm_convert_effect(buffer[pos+2] & 0x0F, buffer[pos+3], entry, 1);
+
+ entry++;
+ }
+ pos += 4;
+ }
+ IT_SET_END_ROW(entry);
+ entry++;
+ }
+
+ return 0;
+}
+
+
+
+static int it_mod_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f)
+{
+ int finetune, loop_start, loop_length;
+
+/**
+ 21 22 Chars Sample 1 name. If the name is not a full
+ 22 chars in length, it will be null
+ terminated.
+
+If
+the sample name begins with a '#' character (ASCII $23 (35)) then this is
+assumed not to be an instrument name, and is probably a message.
+*/
+ dumbfile_getnc(sample->name, 22, f);
+ sample->name[22] = 0;
+
+ sample->filename[0] = 0;
+
+ sample->length = dumbfile_mgetw(f) << 1;
+ finetune = (signed char)(dumbfile_getc(f) << 4) >> 4; /* signed nibble */
+/** Each finetune step changes the note 1/8th of a semitone. */
+ sample->global_volume = 64;
+ sample->default_volume = dumbfile_getc(f); // Should we be setting global_volume to this instead?
+ loop_start = dumbfile_mgetw(f) << 1;
+ loop_length = dumbfile_mgetw(f) << 1;
+ if ( loop_length > 2 && loop_start + loop_length > sample->length && loop_start / 2 + loop_length <= sample->length )
+ loop_start /= 2;
+ sample->loop_start = loop_start;
+ sample->loop_end = loop_start + loop_length;
+/**
+Once this sample has been played completely from beginning
+to end, if the repeat length (next field) is greater than two bytes it
+will loop back to this position in the sample and continue playing. Once
+it has played for the repeat length, it continues to loop back to the
+repeat start offset. This means the sample continues playing until it is
+told to stop.
+*/
+
+ if (sample->length <= 0) {
+ sample->flags = 0;
+ return 0;
+ }
+
+ sample->flags = IT_SAMPLE_EXISTS;
+
+ sample->default_pan = 0;
+ sample->C5_speed = (int)( AMIGA_CLOCK / 214.0 ); //(long)(16726.0*pow(DUMB_PITCH_BASE, finetune*32));
+ sample->finetune = finetune * 32;
+ // the above line might be wrong
+
+ if (sample->loop_end > sample->length)
+ sample->loop_end = sample->length;
+
+ if (sample->loop_end - sample->loop_start > 2)
+ sample->flags |= IT_SAMPLE_LOOP;
+
+ sample->vibrato_speed = 0;
+ sample->vibrato_depth = 0;
+ sample->vibrato_rate = 0;
+ sample->vibrato_waveform = 0; // do we have to set _all_ these?
+ sample->max_resampling_quality = -1;
+
+ return dumbfile_error(f);
+}
+
+
+
+static int it_mod_read_sample_data(IT_SAMPLE *sample, DUMBFILE *f, unsigned long fft)
+{
+ long i;
+ long truncated_size;
+
+ /* let's get rid of the sample data coming after the end of the loop */
+ if ((sample->flags & IT_SAMPLE_LOOP) && sample->loop_end < sample->length) {
+ truncated_size = sample->length - sample->loop_end;
+ sample->length = sample->loop_end;
+ } else {
+ truncated_size = 0;
+ }
+
+ if (sample->length) {
+ sample->data = malloc(sample->length);
+
+ if (!sample->data)
+ return -1;
+
+ /* Sample data are stored in "8-bit two's compliment format" (sic). */
+ /*
+ for (i = 0; i < sample->length; i++)
+ ((signed char *)sample->left)[i] = dumbfile_getc(f);
+ */
+ /* F U Olivier Lapicque */
+ if (sample->length >= 5)
+ {
+ i = dumbfile_getnc(sample->data, 5, f);
+ if (i == 5)
+ {
+ if (!memcmp(sample->data, "ADPCM", 5))
+ {
+ if (_dumb_it_read_sample_data_adpcm4(sample, f) < 0)
+ return -1;
+
+ return 0;
+ }
+ else
+ {
+ i += dumbfile_getnc(((char *)sample->data) + 5, sample->length - 5, f);
+ }
+ }
+ }
+ else
+ {
+ i = dumbfile_getnc(sample->data, sample->length, f);
+ }
+ if (i < sample->length)
+ {
+ if (i <= 0)
+ {
+ sample->flags = 0;
+ return 0;
+ }
+ sample->length = i;
+ if (sample->loop_end > i) sample->loop_end = i;
+ // holy crap!
+ if (sample->loop_start > i) sample->flags &= ~IT_SAMPLE_LOOP;
+ }
+ else
+ {
+ /* skip truncated data */
+ int feh = dumbfile_error(f);
+
+ if (truncated_size) dumbfile_skip(f, truncated_size);
+ // Should we be truncating it?
+
+ if (feh)
+ return -1;
+ }
+
+ if (fft == DUMB_ID('M',0,0,0) || fft == DUMB_ID('8',0,0,0)) {
+ int delta = 0;
+ for (i = 0; i < sample->length; i++) {
+ delta += ((signed char *)sample->data)[i];
+ ((signed char *)sample->data)[i] = delta;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+
+typedef struct BUFFERED_MOD BUFFERED_MOD;
+
+struct BUFFERED_MOD
+{
+ unsigned char *buffered;
+ long ptr, len;
+ DUMBFILE *remaining;
+};
+
+
+
+static int buffer_mod_skip(void *f, long n)
+{
+ BUFFERED_MOD *bm = f;
+ if (bm->buffered) {
+ bm->ptr += n;
+ if (bm->ptr >= bm->len) {
+ free(bm->buffered);
+ bm->buffered = NULL;
+ return dumbfile_skip(bm->remaining, bm->ptr - bm->len);
+ }
+ return 0;
+ }
+ return dumbfile_skip(bm->remaining, n);
+}
+
+
+
+static int buffer_mod_getc(void *f)
+{
+ BUFFERED_MOD *bm = f;
+ if (bm->buffered) {
+ int rv = bm->buffered[bm->ptr++];
+ if (bm->ptr >= bm->len) {
+ free(bm->buffered);
+ bm->buffered = NULL;
+ }
+ return rv;
+ }
+ return dumbfile_getc(bm->remaining);
+}
+
+
+
+static long buffer_mod_getnc(char *ptr, long n, void *f)
+{
+ BUFFERED_MOD *bm = f;
+ if (bm->buffered) {
+ int left = bm->len - bm->ptr;
+ if (n >= left) {
+ memcpy(ptr, bm->buffered + bm->ptr, left);
+ free(bm->buffered);
+ bm->buffered = NULL;
+ if (n - left) {
+ int rv = dumbfile_getnc(ptr + left, n - left, bm->remaining);
+ return left + MAX(rv, 0);
+ } else {
+ return left;
+ }
+ }
+ memcpy(ptr, bm->buffered + bm->ptr, n);
+ bm->ptr += n;
+ return n;
+ }
+ return dumbfile_getnc(ptr, n, bm->remaining);
+}
+
+
+
+static void buffer_mod_close(void *f)
+{
+ BUFFERED_MOD *bm = f;
+ if (bm->buffered) free(bm->buffered);
+ /* Do NOT close bm->remaining */
+ free(f);
+}
+
+
+
+DUMBFILE_SYSTEM buffer_mod_dfs = {
+ NULL,
+ &buffer_mod_skip,
+ &buffer_mod_getc,
+ &buffer_mod_getnc,
+ &buffer_mod_close
+};
+
+
+
+#define MOD_FFT_OFFSET (20 + 31*(22+2+1+1+2+2) + 1 + 1 + 128)
+
+static DUMBFILE *dumbfile_buffer_mod(DUMBFILE *f, unsigned long *fft)
+{
+ BUFFERED_MOD *bm = malloc(sizeof(*bm));
+ if (!bm) return NULL;
+
+ bm->buffered = malloc(MOD_FFT_OFFSET + 4);
+ if (!bm->buffered) {
+ free(bm);
+ return NULL;
+ }
+
+ bm->len = dumbfile_getnc(bm->buffered, MOD_FFT_OFFSET + 4, f);
+
+ if (bm->len > 0) {
+ if (bm->len >= MOD_FFT_OFFSET + 4)
+ *fft = (unsigned long)bm->buffered[MOD_FFT_OFFSET ] << 24
+ | (unsigned long)bm->buffered[MOD_FFT_OFFSET+1] << 16
+ | (unsigned long)bm->buffered[MOD_FFT_OFFSET+2] << 8
+ | (unsigned long)bm->buffered[MOD_FFT_OFFSET+3];
+ else
+ *fft = 0;
+ bm->ptr = 0;
+ } else {
+ free(bm->buffered);
+ bm->buffered = NULL;
+ }
+
+ bm->remaining = f;
+
+ return dumbfile_open_ex(bm, &buffer_mod_dfs);
+}
+
+static DUMBFILE *dumbfile_buffer_mod_2(DUMBFILE *f, long *remain)
+{
+ long read;
+ BUFFERED_MOD *bm = malloc(sizeof(*bm));
+ if (!bm) return NULL;
+
+ bm->buffered = malloc(32768);
+ if (!bm->buffered) {
+ free(bm);
+ return NULL;
+ }
+
+ bm->len = 0;
+ *remain = 0;
+
+ read = dumbfile_getnc(bm->buffered, 32768, f);
+
+ if (read >= 0) {
+ bm->len += read;
+ *remain += read;
+
+ while (read >= 32768) {
+ bm->buffered = realloc(bm->buffered, *remain + 32768);
+ if (!bm->buffered) {
+ free(bm);
+ return 0;
+ }
+ read = dumbfile_getnc(bm->buffered + *remain, 32768, f);
+ if (read >= 0) {
+ bm->len += read;
+ *remain += read;
+ }
+ }
+ }
+
+ if (*remain) {
+ bm->ptr = 0;
+ } else {
+ free(bm->buffered);
+ bm->buffered = NULL;
+ }
+
+ bm->remaining = f;
+
+ return dumbfile_open_ex(bm, &buffer_mod_dfs);
+}
+
+
+static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int restrict)
+{
+ DUMB_IT_SIGDATA *sigdata;
+ int n_channels;
+ int i;
+ unsigned long fft;
+ DUMBFILE *rem;
+
+ f = dumbfile_buffer_mod(f, &fft);
+ if (!f)
+ return NULL;
+
+ sigdata = malloc(sizeof(*sigdata));
+ if (!sigdata) {
+ dumbfile_close(f);
+ return NULL;
+ }
+
+ /**
+ 1 20 Chars Title of the song. If the title is not a
+ full 20 chars in length, it will be null-
+ terminated.
+ */
+ if (dumbfile_getnc(sigdata->name, 20, f) < 20) {
+ free(sigdata);
+ dumbfile_close(f);
+ return NULL;
+ }
+ sigdata->name[20] = 0;
+
+ sigdata->n_samples = 31;
+
+ switch (fft) {
+ case DUMB_ID('M','.','K','.'):
+ case DUMB_ID('M','!','K','!'):
+ case DUMB_ID('M','&','K','!'):
+ case DUMB_ID('N','.','T','.'):
+ case DUMB_ID('N','S','M','S'):
+ case DUMB_ID('F','L','T','4'):
+ case DUMB_ID('M',0,0,0):
+ case DUMB_ID('8',0,0,0):
+ n_channels = 4;
+ break;
+ case DUMB_ID('F','L','T','8'):
+ n_channels = 0;
+ /* 0 indicates a special case; two four-channel patterns must be
+ * combined into one eight-channel pattern. Pattern indexes must
+ * be halved. Why oh why do they obfuscate so?
+ */
+ /*for (i = 0; i < 128; i++)
+ sigdata->order[i] >>= 1;*/
+ break;
+ case DUMB_ID('C','D','8','1'):
+ case DUMB_ID('O','C','T','A'):
+ case DUMB_ID('O','K','T','A'):
+ n_channels = 8;
+ break;
+ case DUMB_ID('1','6','C','N'):
+ n_channels = 16;
+ break;
+ case DUMB_ID('3','2','C','N'):
+ n_channels = 32;
+ break;
+ default:
+ /* If we get an illegal tag, assume 4 channels 15 samples. */
+ if ((fft & 0x0000FFFFL) == DUMB_ID(0,0,'C','H')) {
+ if (fft >= '1' << 24 && fft < '4' << 24) {
+ n_channels = ((fft & 0x00FF0000L) >> 16) - '0';
+ if ((unsigned int)n_channels >= 10) {
+ /* Rightmost character wasn't a digit. */
+ n_channels = 4;
+ sigdata->n_samples = 15;
+ } else {
+ n_channels += (((fft & 0xFF000000L) >> 24) - '0') * 10;
+ /* MODs should really only go up to 32 channels, but we're lenient. */
+ if ((unsigned int)(n_channels - 1) >= DUMB_IT_N_CHANNELS - 1) {
+ /* No channels or too many? Can't be right... */
+ n_channels = 4;
+ sigdata->n_samples = 15;
+ }
+ }
+ } else {
+ n_channels = 4;
+ sigdata->n_samples = 15;
+ }
+ } else if ((fft & 0x00FFFFFFL) == DUMB_ID(0,'C','H','N')) {
+ n_channels = (fft >> 24) - '0';
+ if ((unsigned int)(n_channels - 1) >= 9) {
+ /* Character was '0' or it wasn't a digit */
+ n_channels = 4;
+ sigdata->n_samples = 15;
+ }
+ } else if ((fft & 0xFFFFFF00L) == DUMB_ID('T','D','Z',0)) {
+ n_channels = (fft & 0x000000FFL) - '0';
+ if ((unsigned int)(n_channels - 1) >= 9) {
+ /* We've been very lenient, given that it should have
+ * been 1, 2 or 3, but this MOD has been very naughty and
+ * must be punished.
+ */
+ n_channels = 4;
+ sigdata->n_samples = 15;
+ }
+ } else {
+ n_channels = 4;
+ sigdata->n_samples = 15;
+ }
+ }
+
+ // moo
+ if ( restrict && sigdata->n_samples == 15 )
+ {
+ free(sigdata);
+ dumbfile_close(f);
+ return NULL;
+ }
+
+ sigdata->n_pchannels = n_channels ? n_channels : 8; /* special case for 0, see above */
+
+ sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
+ if (!sigdata->sample) {
+ free(sigdata);
+ dumbfile_close(f);
+ return NULL;
+ }
+
+ sigdata->song_message = NULL;
+ sigdata->order = NULL;
+ sigdata->instrument = NULL;
+ sigdata->pattern = NULL;
+ sigdata->midi = NULL;
+ sigdata->checkpoint = NULL;
+
+ sigdata->n_instruments = 0;
+
+ for (i = 0; i < sigdata->n_samples; i++)
+ sigdata->sample[i].data = NULL;
+
+ for (i = 0; i < sigdata->n_samples; i++) {
+ if (it_mod_read_sample_header(&sigdata->sample[i], f)) {
+ _dumb_it_unload_sigdata(sigdata);
+ dumbfile_close(f);
+ return NULL;
+ }
+ }
+
+ sigdata->n_orders = dumbfile_getc(f);
+ sigdata->restart_position = dumbfile_getc(f);
+ // what if this is >= 127? what about with Fast Tracker II?
+
+/* if (sigdata->n_orders <= 0 || sigdata->n_orders > 128) { // is this right?
+ _dumb_it_unload_sigdata(sigdata);
+ dumbfile_close(f);
+ return NULL;
+ }*/
+
+ //if (sigdata->restart_position >= sigdata->n_orders)
+ //sigdata->restart_position = 0;
+
+ sigdata->order = malloc(128); /* We may need to scan the extra ones! */
+ if (!sigdata->order) {
+ _dumb_it_unload_sigdata(sigdata);
+ dumbfile_close(f);
+ return NULL;
+ }
+ if (dumbfile_getnc(sigdata->order, 128, f) < 128) {
+ _dumb_it_unload_sigdata(sigdata);
+ dumbfile_close(f);
+ return NULL;
+ }
+
+ if (sigdata->n_orders <= 0 || sigdata->n_orders > 128) { // is this right?
+ sigdata->n_orders = 128;
+ //while (sigdata->n_orders > 1 && !sigdata->order[sigdata->n_orders - 1]) sigdata->n_orders--;
+ }
+
+ if ( ! n_channels )
+ for (i = 0; i < 128; i++)
+ sigdata->order[i] >>= 1;
+
+ /* "The old NST format contains only 15 samples (instead of 31). Further
+ * it doesn't contain a file format tag (id). So Pattern data offset is
+ * at 20+15*30+1+1+128."
+ * - Then I shall assume the File Format Tag never exists if there are
+ * only 15 samples. I hope this isn't a faulty assumption...
+ */
+ if (sigdata->n_samples == 31)
+ dumbfile_skip(f, 4);
+
+ /* Work out how many patterns there are. */
+ sigdata->n_patterns = -1;
+ for (i = 0; i < 128; i++)
+ if (sigdata->n_patterns < sigdata->order[i])
+ sigdata->n_patterns = sigdata->order[i];
+ sigdata->n_patterns++;
+
+ /* May as well try to save a tiny bit of memory. */
+ if (sigdata->n_orders < 128) {
+ unsigned char *order = realloc(sigdata->order, sigdata->n_orders);
+ if (order) sigdata->order = order;
+ }
+
+ sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
+ if (!sigdata->pattern) {
+ _dumb_it_unload_sigdata(sigdata);
+ dumbfile_close(f);
+ return NULL;
+ }
+ for (i = 0; i < sigdata->n_patterns; i++)
+ sigdata->pattern[i].entry = NULL;
+
+ /* Read in the patterns */
+ {
+ unsigned char *buffer = malloc(256 * n_channels); /* 64 rows * 4 bytes */
+ if (!buffer) {
+ _dumb_it_unload_sigdata(sigdata);
+ dumbfile_close(f);
+ return NULL;
+ }
+ for (i = 0; i < sigdata->n_patterns; i++) {
+ if (it_mod_read_pattern(&sigdata->pattern[i], f, n_channels, buffer) != 0) {
+ free(buffer);
+ _dumb_it_unload_sigdata(sigdata);
+ dumbfile_close(f);
+ return NULL;
+ }
+ }
+ free(buffer);
+ }
+
+ rem = NULL;
+
+ /* uggly */
+ if (fft == DUMB_ID('M',0,0,0) || fft == DUMB_ID('8',0,0,0)) {
+ long skip;
+ long remain;
+ rem = f;
+ f = dumbfile_buffer_mod_2(rem, &remain);
+ if (!f) {
+ _dumb_it_unload_sigdata(sigdata);
+ dumbfile_close(rem);
+ return NULL;
+ }
+ for (skip = 0, i = 0; i < sigdata->n_samples; i++) {
+ if (sigdata->sample[i].flags & IT_SAMPLE_EXISTS) {
+ skip += sigdata->sample[i].length;
+ }
+ }
+ if (remain - skip) {
+ if (dumbfile_skip(f, remain - skip)) {
+ _dumb_it_unload_sigdata(sigdata);
+ dumbfile_close(f);
+ dumbfile_close(rem);
+ return NULL;
+ }
+ }
+ }
+
+ /* And finally, the sample data */
+ for (i = 0; i < sigdata->n_samples; i++) {
+ if (it_mod_read_sample_data(&sigdata->sample[i], f, fft)) {
+ _dumb_it_unload_sigdata(sigdata);
+ dumbfile_close(f);
+ if (rem) dumbfile_close(rem);
+ return NULL;
+ }
+ }
+
+ /* w00t! */
+ /*if ( n_channels == 4 &&
+ ( sigdata->n_samples == 15 ||
+ ( ( fft & 240 ) != DUMB_ID( 0, 0, 'C', 0 ) &&
+ ( fft & 240 ) != DUMB_ID( 0, 0, 'H', 0 ) &&
+ ( fft & 240 ) != 0 ) ) ) {
+ for ( i = 0; i < sigdata->n_samples; ++i ) {
+ IT_SAMPLE * sample = &sigdata->sample [i];
+ if ( sample && ( sample->flags & IT_SAMPLE_EXISTS ) ) {
+ int n, o;
+ o = sample->length;
+ if ( o > 4 ) o = 4;
+ for ( n = 0; n < o; ++n )
+ ( ( char * ) sample->data ) [n] = 0;
+ }
+ }
+ }*/
+
+ dumbfile_close(f); /* Destroy the BUFFERED_MOD DUMBFILE we were using. */
+ /* The DUMBFILE originally passed to our function is intact. */
+ if (rem) dumbfile_close(rem);
+
+ /* Now let's initialise the remaining variables, and we're done! */
+ sigdata->flags = IT_WAS_AN_XM | IT_WAS_A_MOD | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO;
+
+ sigdata->global_volume = 128;
+ sigdata->mixing_volume = 48;
+ /* We want 50 ticks per second; 50/6 row advances per second;
+ * 50*10=500 row advances per minute; 500/4=125 beats per minute.
+ */
+ sigdata->speed = 6;
+ sigdata->tempo = 125;
+ sigdata->pan_separation = 128;
+
+ memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
+
+ for (i = 0; i < DUMB_IT_N_CHANNELS; i += 4) {
+ sigdata->channel_pan[i+0] = 16;
+ sigdata->channel_pan[i+1] = 48;
+ sigdata->channel_pan[i+2] = 48;
+ sigdata->channel_pan[i+3] = 16;
+ }
+
+ _dumb_it_fix_invalid_orders(sigdata);
+
+ return sigdata;
+}
+
+
+
+DUH *dumb_read_mod_quick(DUMBFILE *f, int restrict)
+{
+ sigdata_t *sigdata;
+
+ DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+ sigdata = it_mod_load_sigdata(f, restrict);
+
+ if (!sigdata)
+ return NULL;
+
+ {
+ const char *tag[2][2];
+ tag[0][0] = "TITLE";
+ tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+ tag[1][0] = "FORMAT";
+ tag[1][1] = "MOD";
+ return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
+ }
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/readmod2.c b/plugins/dumb/dumb-kode54/src/it/readmod2.c
new file mode 100644
index 00000000..a22548a1
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/readmod2.c
@@ -0,0 +1,29 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * readmod2.c - Function to read a good old- / / \ \
+ * fashioned Amiga module from an | < / \_
+ * open file and do an initial | \/ /\ /
+ * run-through. \_ / > /
+ * | \ / /
+ * Split off from readmod.c by entheh. | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+
+
+
+DUH *dumb_read_mod(DUMBFILE *f, int restrict)
+{
+ DUH *duh = dumb_read_mod_quick(f, restrict);
+ dumb_it_do_initial_runthrough(duh);
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/readmtm.c b/plugins/dumb/dumb-kode54/src/it/readmtm.c
new file mode 100644
index 00000000..8f0c2e4c
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/readmtm.c
@@ -0,0 +1,412 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * readmtm.c - Code to read a MultiTracker Module / / \ \
+ * from an open file. | < / \_
+ * | \/ /\ /
+ * By Chris Moeller. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+size_t strlen_max(const char * ptr, size_t max)
+{
+ const char * end, * start;
+ if (ptr==0) return 0;
+ start = ptr;
+ end = ptr + max;
+ while(*ptr && ptr < end) ptr++;
+ return ptr - start;
+}
+
+static int it_mtm_assemble_pattern(IT_PATTERN *pattern, const unsigned char * track, const unsigned short * sequence, int n_rows)
+{
+ int n, o, note, sample;
+ const unsigned char * t;
+ IT_ENTRY * entry;
+
+ pattern->n_rows = n_rows;
+ pattern->n_entries = n_rows;
+
+ for (n = 0; n < 32; n++) {
+ if (sequence[n]) {
+ t = &track[192 * (sequence[n] - 1)];
+ for (o = 0; o < n_rows; o++) {
+ if (t[0] || t[1] || t[2]) pattern->n_entries++;
+ t += 3;
+ }
+ }
+ }
+
+ entry = malloc(pattern->n_entries * sizeof(*entry));
+ if (!entry) return -1;
+ pattern->entry = entry;
+
+ for (n = 0; n < n_rows; n++) {
+ for (o = 0; o < 32; o++) {
+ if (sequence[o]) {
+ t = &track[192 * (sequence[o] - 1) + (n * 3)];
+ if (t[0] || t[1] || t[2]) {
+ entry->channel = o;
+ entry->mask = 0;
+ note = t[0] >> 2;
+ sample = ((t[0] << 4) | (t[1] >> 4)) & 0x3F;
+
+ if (note) {
+ entry->mask |= IT_ENTRY_NOTE;
+ entry->note = note + 24;
+ }
+
+ if (sample) {
+ entry->mask |= IT_ENTRY_INSTRUMENT;
+ entry->instrument = sample;
+ }
+
+ _dumb_it_xm_convert_effect(t[1] & 0xF, t[2], entry, 1);
+
+ if (entry->mask) entry++;
+ }
+ }
+ }
+ IT_SET_END_ROW(entry);
+ entry++;
+ }
+
+ pattern->n_entries = entry - pattern->entry;
+
+ return 0;
+}
+
+static int it_mtm_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f)
+{
+ int finetune, flags;
+
+ dumbfile_getnc(sample->name, 22, f);
+ sample->name[22] = 0;
+
+ sample->filename[0] = 0;
+
+ sample->length = dumbfile_igetl(f);
+ sample->loop_start = dumbfile_igetl(f);
+ sample->loop_end = dumbfile_igetl(f);
+ finetune = (signed char)(dumbfile_getc(f) << 4) >> 4; /* signed nibble */
+ sample->global_volume = 64;
+ sample->default_volume = dumbfile_getc(f);
+
+ flags = dumbfile_getc(f);
+
+ if (sample->length <= 0) {
+ sample->flags = 0;
+ return 0;
+ }
+
+ sample->flags = IT_SAMPLE_EXISTS;
+
+ if (flags & 1) {
+ sample->flags |= IT_SAMPLE_16BIT;
+ sample->length >>= 1;
+ sample->loop_start >>= 1;
+ sample->loop_end >>= 1;
+ }
+
+ sample->default_pan = 0;
+ sample->C5_speed = (int)( AMIGA_CLOCK / 214.0 );//(long)(16726.0*pow(DUMB_PITCH_BASE, finetune*32));
+ sample->finetune = finetune * 32;
+ // the above line might be wrong
+
+ if (sample->loop_end > sample->length)
+ sample->loop_end = sample->length;
+
+ if (sample->loop_end - sample->loop_start > 2)
+ sample->flags |= IT_SAMPLE_LOOP;
+
+ sample->vibrato_speed = 0;
+ sample->vibrato_depth = 0;
+ sample->vibrato_rate = 0;
+ sample->vibrato_waveform = 0; // do we have to set _all_ these?
+ sample->max_resampling_quality = -1;
+
+ return dumbfile_error(f);
+}
+
+static int it_mtm_read_sample_data(IT_SAMPLE *sample, DUMBFILE *f)
+{
+ long i;
+ long truncated_size;
+
+ /* let's get rid of the sample data coming after the end of the loop */
+ if ((sample->flags & IT_SAMPLE_LOOP) && sample->loop_end < sample->length) {
+ truncated_size = sample->length - sample->loop_end;
+ sample->length = sample->loop_end;
+ } else {
+ truncated_size = 0;
+ }
+
+ sample->data = malloc(sample->length);
+
+ if (!sample->data)
+ return -1;
+
+ dumbfile_getnc((char *)sample->data, sample->length, f);
+ dumbfile_skip(f, truncated_size);
+
+ if (dumbfile_error(f))
+ return -1;
+
+ for (i = 0; i < sample->length; i++)
+ ((signed char *)sample->data)[i] ^= 0x80;
+
+ return 0;
+}
+
+static DUMB_IT_SIGDATA *it_mtm_load_sigdata(DUMBFILE *f, int * version)
+{
+ DUMB_IT_SIGDATA *sigdata;
+
+ int n, o, n_tracks, l_comment, n_rows, n_channels;
+
+ unsigned char * track;
+
+ unsigned short * sequence;
+
+ char * comment;
+
+ if (dumbfile_getc(f) != 'M' ||
+ dumbfile_getc(f) != 'T' ||
+ dumbfile_getc(f) != 'M') goto error;
+
+ *version = dumbfile_getc(f);
+
+ sigdata = malloc(sizeof(*sigdata));
+ if (!sigdata) goto error;
+
+ dumbfile_getnc(sigdata->name, 20, f);
+ sigdata->name[20] = 0;
+
+ n_tracks = dumbfile_igetw(f);
+ sigdata->n_patterns = dumbfile_getc(f) + 1;
+ sigdata->n_orders = dumbfile_getc(f) + 1;
+ l_comment = dumbfile_igetw(f);
+ sigdata->n_samples = dumbfile_getc(f);
+ //if (dumbfile_getc(f)) goto error_sd;
+ dumbfile_getc(f);
+ n_rows = dumbfile_getc(f);
+ n_channels = dumbfile_getc(f);
+
+ if (dumbfile_error(f) ||
+ (n_tracks <= 0) ||
+ (sigdata->n_samples <= 0) ||
+ (n_rows <= 0 || n_rows > 64) ||
+ (n_channels <= 0 || n_channels > 32)) goto error_sd;
+
+ memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
+
+ if (dumbfile_getnc(sigdata->channel_pan, 32, f) < 32) goto error_sd;
+
+ for (n = 0; n < 32; n++) {
+ if (sigdata->channel_pan[n] <= 15) {
+ sigdata->channel_pan[n] -= (sigdata->channel_pan[n] & 8) >> 3;
+ sigdata->channel_pan[n] = (sigdata->channel_pan[n] * 32) / 7;
+ } else {
+ sigdata->channel_volume[n] = 0;
+ sigdata->channel_pan[n] = 7;
+ }
+ }
+
+ for (n = 32; n < DUMB_IT_N_CHANNELS; n += 4) {
+ sigdata->channel_pan[n ] = 16;
+ sigdata->channel_pan[n+1] = 48;
+ sigdata->channel_pan[n+2] = 48;
+ sigdata->channel_pan[n+3] = 16;
+ }
+
+ sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
+ if (!sigdata->sample) goto error_sd;
+
+ sigdata->flags = IT_WAS_AN_XM | IT_WAS_A_MOD | IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX;
+
+ sigdata->global_volume = 128;
+ sigdata->mixing_volume = 48;
+ sigdata->speed = 6;
+ sigdata->tempo = 125;
+ sigdata->pan_separation = 128;
+
+ sigdata->song_message = NULL;
+ sigdata->order = NULL;
+ sigdata->instrument = NULL;
+ sigdata->pattern = NULL;
+ sigdata->midi = NULL;
+ sigdata->checkpoint = NULL;
+
+ sigdata->n_instruments = 0;
+
+ sigdata->restart_position = 0;
+ sigdata->n_pchannels = n_channels;
+
+ for (n = 0; n < sigdata->n_samples; n++)
+ sigdata->sample[n].data = NULL;
+
+ for (n = 0; n < sigdata->n_samples; n++) {
+ if (it_mtm_read_sample_header(&sigdata->sample[n], f)) goto error_usd;
+ }
+
+ sigdata->order = malloc(sigdata->n_orders);
+ if (!sigdata->order) goto error_usd;
+
+ if (dumbfile_getnc(sigdata->order, sigdata->n_orders, f) < sigdata->n_orders) goto error_usd;
+ if (sigdata->n_orders < 128)
+ if (dumbfile_skip(f, 128 - sigdata->n_orders)) goto error_usd;
+
+ track = malloc(192 * n_tracks);
+ if (!track) goto error_usd;
+
+ if (dumbfile_getnc(track, 192 * n_tracks, f) < 192 * n_tracks) goto error_ft;
+
+ sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
+ if (!sigdata->pattern) goto error_ft;
+ for (n = 0; n < sigdata->n_patterns; n++)
+ sigdata->pattern[n].entry = NULL;
+
+ sequence = malloc(sigdata->n_patterns * 32 * sizeof(*sequence));
+ if (!sequence) goto error_ft;
+
+ for (n = 0; n < sigdata->n_patterns; n++) {
+ for (o = 0; o < 32; o++) {
+ sequence[(n * 32) + o] = dumbfile_igetw(f);
+ if (sequence[(n * 32) + o] > n_tracks)
+ {
+ //goto error_fs;
+ // illegal track number, silence instead of rejecting the file
+ sequence[(n * 32) + o] = 0;
+ }
+ }
+ }
+
+ for (n = 0; n < sigdata->n_patterns; n++) {
+ if (it_mtm_assemble_pattern(&sigdata->pattern[n], track, &sequence[n * 32], n_rows)) goto error_fs;
+ }
+
+ if (l_comment) {
+ comment = malloc(l_comment);
+ if (!comment) goto error_fs;
+ if (dumbfile_getnc(comment, l_comment, f) < l_comment) goto error_fc;
+
+ /* Time for annoying "logic", yes. We want each line which has text,
+ * and each blank line in between all the valid lines.
+ */
+
+ /* Find last actual line. */
+ for (o = -1, n = 0; n < l_comment; n += 40) {
+ if (comment[n]) o = n;
+ }
+
+ if (o >= 0) {
+
+ int l, m;
+
+ for (l = 0, n = 0; n <= o; n += 40) {
+ l += strlen_max(&comment[n], 40) + 2;
+ }
+
+ l -= 1;
+
+ sigdata->song_message = malloc(l);
+ if (!sigdata->song_message) goto error_fc;
+
+ for (m = 0, n = 0; n <= o; n += 40) {
+ int p = strlen_max(&comment[n], 40);
+ if (p) {
+ memcpy(sigdata->song_message + m, &comment[n], p);
+ m += p;
+ }
+ if (l - m > 1) {
+ sigdata->song_message[m++] = 13;
+ sigdata->song_message[m++] = 10;
+ }
+ }
+
+ sigdata->song_message[m] = 0;
+ }
+
+ free(comment);
+ }
+
+ for (n = 0; n < sigdata->n_samples; n++) {
+ if (it_mtm_read_sample_data(&sigdata->sample[n], f)) goto error_fs;
+ }
+
+ _dumb_it_fix_invalid_orders(sigdata);
+
+ free(sequence);
+ free(track);
+
+ return sigdata;
+
+error_fc:
+ free(comment);
+error_fs:
+ free(sequence);
+error_ft:
+ free(track);
+error_usd:
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+
+error_sd:
+ free(sigdata);
+error:
+ return NULL;
+}
+
+static char hexdigit(int in)
+{
+ if (in < 10) return in + '0';
+ else return in + 'A' - 10;
+}
+
+DUH *dumb_read_mtm_quick(DUMBFILE *f)
+{
+ sigdata_t *sigdata;
+ int ver;
+
+ DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+ sigdata = it_mtm_load_sigdata(f, &ver);
+
+ if (!sigdata)
+ return NULL;
+
+ {
+ char version[16];
+ const char *tag[2][2];
+ tag[0][0] = "TITLE";
+ tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+ tag[1][0] = "FORMAT";
+ version[0] = 'M';
+ version[1] = 'T';
+ version[2] = 'M';
+ version[3] = ' ';
+ version[4] = 'v';
+ version[5] = hexdigit(ver >> 4);
+ version[6] = '.';
+ version[7] = hexdigit(ver & 15);
+ version[8] = 0;
+ tag[1][1] = (const char *) &version;
+ return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
+ }
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/readoldpsm.c b/plugins/dumb/dumb-kode54/src/it/readoldpsm.c
new file mode 100644
index 00000000..e7830f2a
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/readoldpsm.c
@@ -0,0 +1,727 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * readpsm.c - Code to read an old Protracker / / \ \
+ * Studio module from an open file. | < / \_
+ * | \/ /\ /
+ * By Chris Moeller. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+static int psm_sample_compare(const void *e1, const void *e2)
+{
+ const unsigned char * pa = e1;
+ const unsigned char * pb = e2;
+ int a = pa[37] | (pa[38] << 8) | (pa[39] << 16) | (pa[40] << 24);
+ int b = pb[37] | (pb[38] << 8) | (pb[39] << 16) | (pb[40] << 24);
+ return a - b;
+}
+
+static int it_old_psm_read_samples(IT_SAMPLE ** sample, DUMBFILE * f, int * num, const unsigned char * prebuffer, long data_pos, long data_size)
+{
+ int n, o, pos, count = *num, true_num, snum, offset, flags, finetune, delta;
+
+ unsigned char * buffer, * sbuffer = 0;
+ const unsigned char * sdata;
+
+ buffer = malloc(count * 64);
+ if (!buffer) goto error;
+
+ if (dumbfile_getnc(buffer, count * 64, f) < count * 64) goto error_fb;
+
+ pos = dumbfile_pos(f);
+
+ true_num = 0;
+
+ for (n = 0; n < count; n++) {
+ snum = buffer[(n * 64) + 45] | (buffer[(n * 64) + 46] << 8);
+ if ((snum < 1) || (snum > 255)) goto error_fb;
+ if (true_num < snum) true_num = snum;
+ }
+
+ if (true_num > count) {
+ IT_SAMPLE * meh = realloc(*sample, true_num * sizeof(*meh));
+ if (!meh) goto error_fb;
+ for (n = count; n < true_num; n++) {
+ meh[n].data = NULL;
+ }
+ *sample = meh;
+ *num = true_num;
+ }
+
+ qsort(buffer, count, 64, &psm_sample_compare);
+
+ for (n = 0; n < true_num; n++) {
+ (*sample)[n].flags = 0;
+ }
+
+ for (n = 0; n < count; n++) {
+ IT_SAMPLE * s;
+ snum = buffer[(n * 64) + 45] | (buffer[(n * 64) + 46] << 8);
+ s = &((*sample)[snum - 1]);
+ memcpy(s->filename, buffer + (n * 64), 13);
+ s->filename[13] = 0;
+ memcpy(s->name, buffer + (n * 64) + 13, 24);
+ s->name[24] = 0;
+ offset = buffer[(n * 64) + 37] | (buffer[(n * 64) + 38] << 8) |
+ (buffer[(n * 64) + 39] << 16) | (buffer[(n * 64) + 40] << 24);
+ flags = buffer[(n * 64) + 47];
+ s->length = buffer[(n * 64) + 48] | (buffer[(n * 64) + 49] << 8) |
+ (buffer[(n * 64) + 50] << 16) | (buffer[(n * 64) + 51] << 24);
+ s->loop_start = buffer[(n * 64) + 52] | (buffer[(n * 64) + 53] << 8) |
+ (buffer[(n * 64) + 54] << 16) | (buffer[(n * 64) + 55] << 24);
+ s->loop_end = buffer[(n * 64) + 56] | (buffer[(n * 64) + 57] << 8) |
+ (buffer[(n * 64) + 58] << 16) | (buffer[(n * 64) + 59] << 24);
+
+ if (s->length <= 0) continue;
+
+ finetune = buffer[(n * 64) + 60];
+ s->default_volume = buffer[(n * 64) + 61];
+ s->C5_speed = buffer[(n * 64) + 62] | (buffer[(n * 64) + 63] << 8);
+ if (finetune < 16) {
+ if (finetune >= 8) finetune -= 16;
+ //s->C5_speed = (long)((double)s->C5_speed * pow(DUMB_PITCH_BASE, finetune*32));
+ s->finetune = finetune * 32;
+ }
+ else s->finetune = 0;
+
+ s->flags |= IT_SAMPLE_EXISTS;
+ if (flags & 0x41) {
+ s->flags &= ~IT_SAMPLE_EXISTS;
+ continue;
+ }
+ if (flags & 0x20) s->flags |= IT_SAMPLE_PINGPONG_LOOP;
+ if (flags & 4) s->flags |= IT_SAMPLE_16BIT;
+
+ if (flags & 0x80) {
+ s->flags |= IT_SAMPLE_LOOP;
+ if ((unsigned int)s->loop_end > (unsigned int)s->length)
+ s->loop_end = s->length;
+ else if ((unsigned int)s->loop_start >= (unsigned int)s->loop_end)
+ s->flags &= ~IT_SAMPLE_LOOP;
+ else
+ s->length = s->loop_end;
+ }
+
+ s->global_volume = 64;
+
+ s->vibrato_speed = 0;
+ s->vibrato_depth = 0;
+ s->vibrato_rate = 0;
+ s->vibrato_waveform = IT_VIBRATO_SINE;
+ s->max_resampling_quality = -1;
+
+ s->data = malloc(s->length * ((flags & 4) ? 2 : 1));
+ if (!s->data) goto error_fb;
+
+ if ((offset >= data_pos) &&
+ ((offset + s->length * ((flags & 4) ? 2 : 1)) <= (data_pos + data_size))) {
+ sdata = prebuffer + offset - data_pos;
+ } else if (offset >= pos) {
+ if (dumbfile_skip(f, offset - pos)) goto error_fb;
+ pos = offset;
+ offset = s->length * ((flags & 4) ? 2 : 1);
+ sbuffer = malloc(offset);
+ if (!sbuffer) goto error_fb;
+ if (dumbfile_getnc(sbuffer, offset, f) < offset) goto error_fsb;
+ sdata = sbuffer;
+ } else
+ goto error_fb;
+
+ if (flags & 0x10) {
+ if (flags & 8) {
+ if (flags & 4) {
+ for (o = 0; o < s->length; o++)
+ ((short *)s->data)[o] = (sdata[o * 2] | (sdata[(o * 2) + 1] << 8)) ^ 0x8000;
+ } else {
+ for (o = 0; o < s->length; o++)
+ ((signed char *)s->data)[o] = sdata[o] ^ 0x80;
+ }
+ } else {
+ if (flags & 4) {
+ for (o = 0; o < s->length; o++)
+ ((short *)s->data)[o] = sdata[o * 2] | (sdata[(o * 2) + 1] << 8);
+ } else {
+ memcpy(s->data, sdata, s->length);
+ }
+ }
+ } else {
+ delta = 0;
+ if (flags & 8) {
+ /* unsigned delta? mehhh, does anything even use this? */
+ if (flags & 4) {
+ for (o = 0; o < s->length; o++) {
+ delta += (short)(sdata[o * 2] | (sdata[(o * 2) + 1] << 8));
+ ((short *)s->data)[o] = delta ^ 0x8000;
+ }
+ } else {
+ for (o = 0; o < s->length; o++) {
+ delta += (signed char)sdata[o];
+ ((signed char *)s->data)[o] = delta ^ 0x80;
+ }
+ }
+ } else {
+ if (flags & 4) {
+ for (o = 0; o < s->length; o++) {
+ delta += (short)(sdata[o * 2] | (sdata[(o * 2) + 1] << 8));
+ ((short *)s->data)[o] = delta;
+ }
+ } else {
+ for (o = 0; o < s->length; o++) {
+ delta += (signed char)sdata[o];
+ ((signed char *)s->data)[o] = delta;
+ }
+ }
+ }
+ }
+
+ if (sbuffer) {
+ free(sbuffer);
+ sbuffer = 0;
+ }
+ }
+
+ free(buffer);
+
+ return 0;
+
+error_fsb:
+ if (sbuffer) free(sbuffer);
+error_fb:
+ free(buffer);
+error:
+ return -1;
+}
+
+static int it_old_psm_read_patterns(IT_PATTERN * pattern, DUMBFILE * f, int num, int size, int pchans, int sflags)
+{
+ int n, offset, psize, rows, chans, row, flags, channel;
+
+ unsigned char * buffer, * ptr, * end;
+
+ IT_ENTRY * entry;
+
+ buffer = malloc(size);
+ if (!buffer) goto error;
+
+ if (dumbfile_getnc(buffer, size, f) < size) goto error_fb;
+
+ offset = 0;
+
+ for (n = 0; n < num; n++) {
+ IT_PATTERN * p = &pattern[n];
+
+ if (offset >= size) goto error_fb;
+
+ ptr = buffer + offset;
+ psize = ptr[0] | (ptr[1] << 8);
+ rows = ptr[2];
+ chans = ptr[3];
+
+ if (!rows || !chans) {
+ p->n_rows = 1;
+ p->n_entries = 0;
+ continue;
+ }
+
+ psize = (psize + 15) & ~15;
+
+ end = ptr + psize;
+ ptr += 4;
+
+ p->n_rows = rows;
+ p->n_entries = rows;
+ row = 0;
+
+ while ((row < rows) && (ptr < end)) {
+ flags = *ptr++;
+ if (!flags) {
+ row++;
+ continue;
+ }
+ if (flags & 0xE0) {
+ p->n_entries++;
+ if (flags & 0x80) ptr += 2;
+ if (flags & 0x40) ptr++;
+ if (flags & 0x20) {
+ ptr++;
+ if (*ptr == 40) ptr += 3;
+ else ptr++;
+ }
+ }
+ }
+
+ entry = malloc(p->n_entries * sizeof(*p->entry));
+ if (!entry) goto error_fb;
+
+ p->entry = entry;
+
+ ptr = buffer + offset + 4;
+ row = 0;
+
+ while ((row < rows) && (ptr < end)) {
+ flags = *ptr++;
+ if (!flags) {
+ IT_SET_END_ROW(entry);
+ entry++;
+ row++;
+ continue;
+ }
+ if (flags & 0xE0) {
+ entry->mask = 0;
+ entry->channel = channel = flags & 0x1F;
+ if (channel >= chans)
+ {
+ //channel = 0;
+ goto error_fb;
+ }
+ if (flags & 0x80) {
+ if ((*ptr < 60) && (channel < pchans)) {
+ entry->mask |= IT_ENTRY_NOTE;
+ entry->note = *ptr + 36;
+ }
+ ptr++;
+ if (*ptr) {
+ entry->mask |= IT_ENTRY_INSTRUMENT;
+ entry->instrument = *ptr;
+ }
+ ptr++;
+ }
+ if (flags & 0x40) {
+ if (*ptr <= 64) {
+ entry->mask |= IT_ENTRY_VOLPAN;
+ entry->volpan = *ptr;
+ }
+ ptr++;
+ }
+ if (flags & 0x20) {
+ entry->mask |= IT_ENTRY_EFFECT;
+
+ switch (*ptr) {
+ case 1:
+ entry->effect = IT_XM_FINE_VOLSLIDE_UP;
+ entry->effectvalue = ptr[1];
+ break;
+
+ case 2:
+ entry->effect = IT_VOLUME_SLIDE;
+ entry->effectvalue = (ptr[1] << 4) & 0xF0;
+ break;
+
+ case 3:
+ entry->effect = IT_XM_FINE_VOLSLIDE_DOWN;
+ entry->effectvalue = ptr[1];
+ break;
+
+ case 4:
+ entry->effect = IT_VOLUME_SLIDE;
+ entry->effectvalue = ptr[1] & 0xF;
+ break;
+
+ case 10:
+ entry->effect = IT_PORTAMENTO_UP;
+ entry->effectvalue = EFFECT_VALUE(0xF, ptr[1]);
+ break;
+
+ case 11:
+ entry->effect = IT_PORTAMENTO_UP;
+ entry->effectvalue = ptr[1];
+ break;
+
+ case 12:
+ entry->effect = IT_PORTAMENTO_DOWN;
+ entry->effectvalue = EFFECT_VALUE(ptr[1], 0xF);
+ break;
+
+ case 13:
+ entry->effect = IT_PORTAMENTO_DOWN;
+ entry->effectvalue = ptr[1];
+ break;
+
+ case 14:
+ entry->effect = IT_TONE_PORTAMENTO;
+ entry->effectvalue = ptr[1];
+ break;
+
+ case 15:
+ entry->effect = IT_S;
+ entry->effectvalue = EFFECT_VALUE(IT_S_SET_GLISSANDO_CONTROL, ptr[1] & 15);
+ break;
+
+ case 16:
+ entry->effect = IT_VOLSLIDE_TONEPORTA;
+ entry->effectvalue = ptr[1] << 4;
+ break;
+
+ case 17:
+ entry->effect = IT_VOLSLIDE_TONEPORTA;
+ entry->effectvalue = ptr[1] & 0xF;
+ break;
+
+ case 20:
+ entry->effect = IT_VIBRATO;
+ entry->effectvalue = ptr[1];
+ break;
+
+ case 21:
+ entry->effect = IT_S;
+ entry->effectvalue = EFFECT_VALUE(IT_S_SET_VIBRATO_WAVEFORM, ptr[1] & 11);
+ break;
+
+ case 22:
+ entry->effect = IT_VOLSLIDE_VIBRATO;
+ entry->effectvalue = ptr[1] << 4;
+ break;
+
+ case 23:
+ entry->effect = IT_VOLSLIDE_VIBRATO;
+ entry->effectvalue = ptr[1] & 0xF;
+ break;
+
+ case 30:
+ entry->effect = IT_TREMOLO;
+ entry->effectvalue = ptr[1];
+ break;
+
+ case 31:
+ entry->effect = IT_S;
+ entry->effectvalue = EFFECT_VALUE(IT_S_SET_TREMOLO_WAVEFORM, ptr[1] & 11);
+ break;
+
+ case 40:
+ entry->effect = IT_SET_SAMPLE_OFFSET;
+ entry->effectvalue = ptr[2];
+ ptr += 2;
+ break;
+
+ case 41:
+ entry->effect = IT_XM_RETRIGGER_NOTE;
+ entry->effectvalue = ptr[1];
+ break;
+
+ case 42:
+ entry->effect = IT_S;
+ entry->effectvalue = EFFECT_VALUE(IT_S_DELAYED_NOTE_CUT, ptr[1] & 0xF);
+ break;
+
+ case 43:
+ entry->effect = IT_S;
+ entry->effectvalue = EFFECT_VALUE(IT_S_NOTE_DELAY, ptr[1] & 0xF);
+ break;
+
+ case 50:
+ entry->effect = IT_JUMP_TO_ORDER;
+ entry->effectvalue = ptr[1];
+ break;
+
+ case 51:
+ entry->effect = IT_BREAK_TO_ROW;
+ entry->effectvalue = ptr[1];
+ break;
+
+ case 52:
+ entry->effect = IT_S;
+ entry->effectvalue = EFFECT_VALUE(IT_S_PATTERN_LOOP, ptr[1] & 0xF);
+ break;
+
+ case 53:
+ entry->effect = IT_S;
+ entry->effectvalue = EFFECT_VALUE(IT_S_PATTERN_DELAY, ptr[1] & 0xF);
+ break;
+
+ case 60:
+ entry->effect = IT_SET_SPEED;
+ entry->effectvalue = ptr[1];
+ break;
+
+ case 61:
+ entry->effect = IT_SET_SONG_TEMPO;
+ entry->effectvalue = ptr[1];
+ break;
+
+ case 70:
+ entry->effect = IT_ARPEGGIO;
+ entry->effectvalue = ptr[1];
+ break;
+
+ case 71:
+ entry->effect = IT_S;
+ entry->effectvalue = EFFECT_VALUE(IT_S_FINETUNE, ptr[1] & 0xF);
+ break;
+
+ case 72:
+ /* "balance" ... panning? */
+ entry->effect = IT_SET_PANNING;
+ entry->effectvalue = ((ptr[1] - ((ptr[1] & 8) >> 3)) << 5) / 7;
+ break;
+
+ default:
+ entry->mask &= ~IT_ENTRY_EFFECT;
+ }
+
+ ptr += 2;
+ }
+ if (entry->mask) entry++;
+ }
+ }
+
+ p->n_entries = entry - p->entry;
+ offset += psize;
+ }
+
+ free(buffer);
+
+ return 0;
+
+error_fb:
+ free(buffer);
+error:
+ return -1;
+}
+
+#define PSM_COMPONENT_ORDERS 0
+#define PSM_COMPONENT_PANPOS 1
+#define PSM_COMPONENT_PATTERNS 2
+#define PSM_COMPONENT_SAMPLE_HEADERS 3
+#define PSM_COMPONENT_COMMENTS 4
+
+typedef struct PSM_COMPONENT
+{
+ unsigned char type;
+ long offset;
+}
+PSM_COMPONENT;
+
+static int psm_component_compare(const void *e1, const void *e2)
+{
+ return ((const PSM_COMPONENT *)e1)->offset -
+ ((const PSM_COMPONENT *)e2)->offset;
+}
+
+static DUMB_IT_SIGDATA *it_old_psm_load_sigdata(DUMBFILE *f)
+{
+ DUMB_IT_SIGDATA *sigdata;
+
+ unsigned char * ptr = 0;
+
+ PSM_COMPONENT *component;
+ int n_components = 0;
+
+ int n, flags, version, pver, n_orders, n_channels, total_pattern_size;
+
+ if (dumbfile_mgetl(f) != DUMB_ID('P','S','M',254)) goto error;
+
+ sigdata = malloc(sizeof(*sigdata));
+ if (!sigdata) goto error;
+
+ if (dumbfile_getnc(sigdata->name, 60, f) < 60 ||
+ sigdata->name[59] != 0x1A) goto error_sd;
+ sigdata->name[59] = 0;
+
+ flags = dumbfile_getc(f);
+ version = dumbfile_getc(f);
+ pver = dumbfile_getc(f);
+ sigdata->speed = dumbfile_getc(f);
+ sigdata->tempo = dumbfile_getc(f);
+ sigdata->mixing_volume = dumbfile_getc(f);
+ sigdata->n_orders = dumbfile_igetw(f);
+ n_orders = dumbfile_igetw(f);
+ sigdata->n_patterns = dumbfile_igetw(f);
+ sigdata->n_samples = dumbfile_igetw(f);
+ sigdata->n_pchannels = dumbfile_igetw(f);
+ n_channels = dumbfile_igetw(f);
+
+ if (dumbfile_error(f) ||
+ (flags & 1) ||
+ (version != 1 && version != 0x10) ||
+ (pver) ||
+ (sigdata->n_orders <= 0) ||
+ (sigdata->n_orders > 255) ||
+ (n_orders > 255) ||
+ (n_orders < sigdata->n_orders) ||
+ (sigdata->n_patterns > 255) ||
+ (sigdata->n_samples > 255) ||
+ (sigdata->n_pchannels > DUMB_IT_N_CHANNELS) ||
+ (sigdata->n_pchannels > n_channels) ||
+ (n_channels > DUMB_IT_N_CHANNELS))
+ goto error_sd;
+
+ sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX;
+
+ sigdata->global_volume = 128;
+ sigdata->pan_separation = 128;
+
+ sigdata->song_message = NULL;
+ sigdata->order = NULL;
+ sigdata->instrument = NULL;
+ sigdata->sample = NULL;
+ sigdata->pattern = NULL;
+ sigdata->midi = NULL;
+ sigdata->checkpoint = NULL;
+
+ sigdata->n_instruments = 0;
+
+ sigdata->restart_position = 0;
+
+ sigdata->order = malloc(sigdata->n_orders);
+ if (!sigdata->order) goto error_usd;
+
+ if (sigdata->n_samples) {
+ sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
+ if (!sigdata->sample) goto error_usd;
+ for (n = 0; n < sigdata->n_samples; n++)
+ sigdata->sample[n].data = NULL;
+ }
+
+ if (sigdata->n_patterns) {
+ sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
+ if (!sigdata->pattern) goto error_usd;
+ for (n = 0; n < sigdata->n_patterns; n++)
+ sigdata->pattern[n].entry = NULL;
+ }
+
+ component = malloc(5 * sizeof(*component));
+ if (!component) goto error_usd;
+
+ for (n = 0; n < 5; n++) {
+ component[n_components].offset = dumbfile_igetl(f);
+ if (component[n_components].offset) {
+ component[n_components].type = n;
+ n_components++;
+ }
+ }
+
+ if (!n_components) goto error_fc;
+
+ total_pattern_size = dumbfile_igetl(f);
+ if (!total_pattern_size) goto error_fc;
+
+ qsort(component, n_components, sizeof(PSM_COMPONENT), &psm_component_compare);
+
+ memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
+
+ for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) {
+ sigdata->channel_pan[n ] = 16;
+ sigdata->channel_pan[n+1] = 48;
+ sigdata->channel_pan[n+2] = 48;
+ sigdata->channel_pan[n+3] = 16;
+ }
+
+ for (n = 0; n < n_components; n++)
+ {
+ int o;
+ long data_pos, data_size;
+
+ /* Whee, sample data may be before the sample headers */
+
+ data_pos = dumbfile_pos(f);
+ if (data_pos > component[n].offset) goto error_fc;
+
+ data_size = component[n].offset - data_pos;
+
+ if (data_size) {
+ ptr = malloc(data_size);
+ if (!ptr) goto error_fc;
+
+ if (dumbfile_getnc(ptr, data_size, f) < data_size) goto error_fp;
+ }
+
+ switch (component[n].type) {
+
+ case PSM_COMPONENT_ORDERS:
+ if (dumbfile_getnc(sigdata->order, sigdata->n_orders, f) < sigdata->n_orders) goto error_fp;
+ if (n_orders > sigdata->n_orders)
+ if (dumbfile_skip(f, n_orders - sigdata->n_orders))
+ goto error_fp;
+ if (dumbfile_igetw(f)) goto error_fp;
+ break;
+
+ case PSM_COMPONENT_PANPOS:
+ if (dumbfile_getnc(sigdata->channel_pan, sigdata->n_pchannels, f) < sigdata->n_pchannels) goto error_fp;
+ for (o = 0; o < sigdata->n_pchannels; o++) {
+ sigdata->channel_pan[o] -= (sigdata->channel_pan[o] & 8) >> 3;
+ sigdata->channel_pan[o] = ((int)sigdata->channel_pan[o] << 5) / 7;
+ }
+ break;
+
+ case PSM_COMPONENT_PATTERNS:
+ if (it_old_psm_read_patterns(sigdata->pattern, f, sigdata->n_patterns, total_pattern_size, sigdata->n_pchannels, flags)) goto error_fp;
+ break;
+
+ case PSM_COMPONENT_SAMPLE_HEADERS:
+ if (it_old_psm_read_samples(&sigdata->sample, f, &sigdata->n_samples, ptr, data_pos, data_size)) goto error_fp;
+ break;
+
+ case PSM_COMPONENT_COMMENTS:
+ if (dumbfile_mgetl(f) == DUMB_ID('T','E','X','T')) {
+ o = dumbfile_igetw(f);
+ if (o > 0) {
+ sigdata->song_message = malloc(o + 1);
+ if (dumbfile_getnc(sigdata->song_message, o, f) < o) goto error_fp;
+ sigdata->song_message[o] = 0;
+ }
+ }
+ break;
+ }
+
+ if (ptr) {
+ free(ptr);
+ ptr = 0;
+ }
+ }
+
+ _dumb_it_fix_invalid_orders(sigdata);
+
+ free(component);
+
+ return sigdata;
+
+error_fp:
+ if (ptr) free(ptr);
+error_fc:
+ free(component);
+error_usd:
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+error_sd:
+ free(sigdata);
+error:
+ return NULL;
+}
+
+DUH *dumb_read_old_psm_quick(DUMBFILE *f)
+{
+ sigdata_t *sigdata;
+
+ DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+ sigdata = it_old_psm_load_sigdata(f);
+
+ if (!sigdata)
+ return NULL;
+
+ {
+ const char *tag[2][2];
+ tag[0][0] = "TITLE";
+ tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+ tag[1][0] = "FORMAT";
+ tag[1][1] = "PSM (old)";
+ return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
+ }
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/readpsm.c b/plugins/dumb/dumb-kode54/src/it/readpsm.c
new file mode 100644
index 00000000..129cdf6f
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/readpsm.c
@@ -0,0 +1,1273 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * readpsm.c - Code to read a Protracker Studio / / \ \
+ * module from an open file. | < / \_
+ * | \/ /\ /
+ * By Chris Moeller. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+#define PSMV_OLD 940730
+#define PSMV_NEW 940902
+
+typedef struct _PSMCHUNK
+{
+ int id;
+ int len;
+ unsigned char * data;
+} PSMCHUNK;
+
+typedef struct _PSMEVENT
+{
+ int type;
+ unsigned char data[8];
+} PSMEVENT;
+
+#define PSM_EVENT_END 0
+#define PSM_EVENT_PLAY_PATTERN 1
+#define PSM_EVENT_JUMP_TO_LINE 4
+#define PSM_EVENT_SET_SPEED 7
+#define PSM_EVENT_SET_BPM 8
+#define PSM_EVENT_SAMPLE_MAP_TABLE 12
+#define PSM_EVENT_CHANGE_PAN 13
+#define PSM_EVENT_CHANGE_VOL 14
+
+static int it_psm_process_sample(IT_SAMPLE * sample, const unsigned char * data, int len, int id, int version) {
+ int flags;
+ int insno;
+ int length;
+ int loopstart;
+ int loopend;
+ int panpos;
+ int defvol;
+ int samplerate;
+
+ if (len < 0x60) return -1;
+
+ flags = data[0];
+
+ if (version == PSMV_OLD) {
+ memcpy(sample->name, data + 0x0D, 34);
+ sample->name[34] = 0;
+
+ insno = data[0x34] | (data[0x35] << 8);
+ length = data[0x36] | (data[0x37] << 8) | (data[0x38] << 16) | (data[0x39] << 24);
+ loopstart = data[0x3A] | (data[0x3B] << 8) | (data[0x3C] << 16) | (data[0x3D] << 24);
+ loopend = data[0x3E] | (data[0x3F] << 8) | (data[0x40] << 16) | (data[0x41] << 24);
+ panpos = data[0x43];
+ defvol = data[0x44];
+ samplerate = data[0x49] | (data[0x4A] << 8) | (data[0x4B] << 16) | (data[0x4C] << 24);
+ } else if (version == PSMV_NEW) {
+ memcpy(sample->name, data + 0x11, 34);
+ sample->name[34] = 0;
+
+ insno = data[0x38] | (data[0x39] << 8);
+ length = data[0x3A] | (data[0x3B] << 8) | (data[0x3C] << 16) | (data[0x3D] << 24);
+ loopstart = data[0x3E] | (data[0x3F] << 8) | (data[0x40] << 16) | (data[0x41] << 24);
+ loopend = data[0x42] | (data[0x43] << 8) | (data[0x44] << 16) | (data[0x45] << 24);
+ panpos = data[0x48];
+ defvol = data[0x49];
+ samplerate = data[0x4E] | (data[0x4F] << 8) | (data[0x50] << 16) | (data[0x51] << 24);
+ }
+
+ if (insno != id) return -1;
+
+ if (!length) {
+ sample->flags &= ~IT_SAMPLE_EXISTS;
+ return 0;
+ }
+
+ if ((length > len - 0x60) || ((flags & 0x7F) != 0)) return -1;
+
+ sample->flags = IT_SAMPLE_EXISTS;
+ sample->length = length;
+ sample->loop_start = loopstart;
+ sample->loop_end = loopend;
+ sample->C5_speed = samplerate;
+ sample->default_volume = defvol >> 1;
+ sample->default_pan = 0;
+ sample->filename[0] = 0;
+ sample->global_volume = 64;
+ sample->vibrato_speed = 0;
+ sample->vibrato_depth = 0;
+ sample->vibrato_rate = 0;
+ sample->vibrato_waveform = IT_VIBRATO_SINE;
+ sample->finetune = 0;
+ sample->max_resampling_quality = -1;
+
+ if (flags & 0x80) {
+ if (((unsigned int)sample->loop_end <= (unsigned int)sample->length) &&
+ ((unsigned int)sample->loop_start < (unsigned int)sample->loop_end)) {
+ sample->length = sample->loop_end;
+ sample->flags |= IT_SAMPLE_LOOP;
+ }
+ }
+
+ sample->data = malloc(sample->length);
+ if (!sample->data)
+ return -1;
+
+ flags = 0;
+ data += 0x60;
+
+ for (insno = 0; insno < sample->length; insno++) {
+ flags += (signed char)(*data++);
+ ((signed char *)sample->data)[insno] = flags;
+ }
+
+ return 0;
+}
+
+static int it_psm_process_pattern(IT_PATTERN * pattern, const unsigned char * data, int len, int speed, int bpm, const unsigned char * pan, const int * vol, int version) {
+ int length, nrows, row, rowlen, pos;
+ unsigned flags, chan;
+ IT_ENTRY * entry;
+
+ length = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+ if (len > length) len = length;
+
+ if (version == PSMV_OLD) {
+ if (len < 10) return -1;
+ data += 8;
+ len -= 8;
+ } else if (version == PSMV_NEW) {
+ if (len < 14) return -1;
+ data += 12;
+ len -= 12;
+ }
+
+ nrows = data[0] | (data[1] << 8);
+
+ if (!nrows) return 0;
+
+ pattern->n_rows = nrows;
+
+ data += 2;
+ len -= 2;
+
+ pattern->n_entries = 0;
+
+ row = 0;
+ pos = 2;
+ rowlen = data[0] | (data[1] << 8);
+
+ while ((row < nrows) && (pos < len)) {
+ if (pos >= rowlen) {
+ row++;
+ rowlen += data[pos] | (data[pos+1] << 8);
+ pos += 2;
+ continue;
+ }
+
+ flags = data[pos++];
+ chan = data[pos++];
+
+ if (chan > 63) return -1;
+
+ if (flags & 0xF0) {
+ pattern->n_entries++;
+ if (flags & 0x80) pos++;
+ if (flags & 0x40) pos++;
+ if (flags & 0x20) pos++;
+ if (flags & 0x10) {
+ switch (data[pos]) {
+ case 0x29:
+ pos++;
+ case 0x33:
+ pos++;
+ default:
+ pos += 2;
+ }
+ }
+ }
+ }
+
+ if (!pattern->n_entries) return 0;
+
+ pattern->n_entries += nrows;
+ if (speed) pattern->n_entries++;
+ if (bpm >= 0x20) pattern->n_entries++;
+
+ for (pos = 0; pos < 32; pos++) {
+ if (!(pan[pos*2+1] & 0xF9)) pattern->n_entries++;
+ if (vol[pos] != -1) pattern->n_entries++;
+ }
+
+ pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry));
+ if (!pattern->entry) return -1;
+
+ entry = pattern->entry;
+
+ if (speed) {
+ entry->channel = 0;
+ entry->mask = IT_ENTRY_EFFECT;
+ entry->effect = IT_SET_SPEED;
+ entry->effectvalue = speed;
+ entry++;
+ }
+
+ if (bpm >= 0x20) {
+ entry->channel = 0;
+ entry->mask = IT_ENTRY_EFFECT;
+ entry->effect = IT_SET_SONG_TEMPO;
+ entry->effectvalue = bpm;
+ entry++;
+ }
+
+ for (pos = 0; pos < 32; pos++) {
+ if (!(pan[pos*2+1] & 0xF9)) {
+ entry->channel = pos;
+ entry->mask = IT_ENTRY_EFFECT;
+ switch (pan[pos*2+1]) {
+ case 0:
+ entry->effect = IT_SET_PANNING;
+ entry->effectvalue = pan[pos*2] ^ 128;
+ break;
+ case 2:
+ entry->effect = IT_S;
+ entry->effectvalue = EFFECT_VALUE(IT_S_SET_SURROUND_SOUND,1);
+ break;
+ case 4:
+ entry->effect = IT_SET_PANNING;
+ entry->effectvalue = 128;
+ break;
+ }
+ entry++;
+ }
+ if (vol[pos] != -1) {
+ entry->channel = pos;
+ entry->mask = IT_ENTRY_EFFECT;
+ entry->effect = IT_SET_CHANNEL_VOLUME;
+ entry->effectvalue = (vol[pos] + 2) >> 2;
+ entry++;
+ }
+ }
+
+ row = 0;
+ pos = 2;
+ rowlen = data[0] | (data[1] << 8);
+
+ while ((row < nrows) && (pos < len)) {
+ if (pos >= rowlen) {
+ IT_SET_END_ROW(entry);
+ entry++;
+ row++;
+ rowlen += data[pos] | (data[pos+1] << 8);
+ pos += 2;
+ continue;
+ }
+
+ flags = data[pos++];
+ entry->channel = data[pos++];
+ entry->mask = 0;
+
+ if (flags & 0xF0) {
+ if (flags & 0x80) {
+ entry->mask |= IT_ENTRY_NOTE;
+ if (version == PSMV_OLD) {
+ if ((data[pos] < 0x80)) entry->note = (data[pos]>>4)*12+(data[pos]&0x0f)+12;
+ else entry->mask &= ~IT_ENTRY_NOTE;
+ } else if (version == PSMV_NEW) {
+ if ((data[pos]) && (data[pos] < 84)) entry->note = data[pos] + 35;
+ else entry->mask &= ~IT_ENTRY_NOTE;
+ }
+ pos++;
+ }
+
+ if (flags & 0x40) {
+ entry->mask |= IT_ENTRY_INSTRUMENT;
+ entry->instrument = data[pos++] + 1;
+ }
+
+ if (flags & 0x20) {
+ entry->mask |= IT_ENTRY_VOLPAN;
+ entry->volpan = (data[pos++] + 1) >> 1;
+ }
+
+ if (flags & 0x10) {
+ entry->mask |= IT_ENTRY_EFFECT;
+ length = data[pos+1];
+ switch (data[pos]) {
+ case 1:
+ entry->effect = IT_VOLUME_SLIDE;
+ if (version == PSMV_OLD) entry->effectvalue = ((length&0x1e)<<3) | 0xF;
+ else if (version == PSMV_NEW) entry->effectvalue = (length<<4) | 0xF;
+ break;
+
+ case 2:
+ entry->effect = IT_VOLUME_SLIDE;
+ if (version == PSMV_OLD) entry->effectvalue = (length << 3) & 0xF0;
+ else if (version == PSMV_NEW) entry->effectvalue = (length << 4) & 0xF0;
+ break;
+
+ case 3:
+ entry->effect = IT_VOLUME_SLIDE;
+ if (version == PSMV_OLD) entry->effectvalue = (length >> 1) | 0xF0;
+ else if (version == PSMV_NEW) entry->effectvalue = length | 0xF0;
+ break;
+
+ case 4:
+ entry->effect = IT_VOLUME_SLIDE;
+ if (version == PSMV_OLD) entry->effectvalue = (length >> 1) & 0xF;
+ else if (version == PSMV_NEW) entry->effectvalue = length & 0xF;
+ break;
+
+ case 12:
+ entry->effect = IT_PORTAMENTO_UP;
+ if (version == PSMV_OLD) {
+ if (length < 4) entry->effectvalue = length | 0xF0;
+ else entry->effectvalue = length >> 2;
+ } else if (version == PSMV_NEW) {
+ entry->effectvalue = length;
+ }
+ break;
+
+ case 14:
+ entry->effect = IT_PORTAMENTO_DOWN;
+ if (version == PSMV_OLD) {
+ if (length < 4) entry->effectvalue = length | 0xF0;
+ else entry->effectvalue = length >> 2;
+ } else if (version == PSMV_NEW) {
+ entry->effectvalue = length;
+ }
+ break;
+
+ case 15:
+ entry->effect = IT_TONE_PORTAMENTO;
+ if (version == PSMV_OLD) entry->effectvalue = length >> 2;
+ else if (version == PSMV_NEW) entry->effectvalue = length;
+ break;
+
+ case 0x15:
+ entry->effect = IT_VIBRATO;
+ entry->effectvalue = length;
+ break;
+
+ case 0x18:
+ entry->effect = IT_VOLSLIDE_VIBRATO;
+ entry->effectvalue = length;
+ break;
+
+ case 0x29:
+ entry->effect = IT_SET_SAMPLE_OFFSET;
+ entry->effectvalue = data[pos+2];
+ pos += 2;
+ break;
+
+ case 0x2A:
+ entry->effect = IT_RETRIGGER_NOTE;
+ entry->effectvalue = length;
+ break;
+
+ case 0x33:
+#if 0
+ entry->effect = IT_POSITION_JUMP;
+ entry->effectvalue = data[pos+2];
+#else
+ entry->mask &= ~IT_ENTRY_EFFECT;
+#endif
+ pos++;
+ break;
+
+ case 0x34:
+ entry->effect = IT_BREAK_TO_ROW;
+ entry->effectvalue = length;
+ break;
+
+ case 0x3D:
+ entry->effect = IT_SET_SPEED;
+ entry->effectvalue = length;
+ break;
+
+ case 0x3E:
+ if (length >= 0x20) {
+ entry->effect = IT_SET_SONG_TEMPO;
+ entry->effectvalue = length;
+ } else {
+ entry->mask &= ~IT_ENTRY_EFFECT;
+ }
+ break;
+
+ case 0x47:
+ entry->effect = IT_ARPEGGIO;
+ entry->effectvalue = length;
+ break;
+
+ default:
+ return -1;
+ }
+ pos += 2;
+ }
+ if (entry->mask) entry++;
+ }
+ }
+
+ while (row < nrows) {
+ IT_SET_END_ROW(entry);
+ entry++;
+ row++;
+ }
+
+ pattern->n_entries = entry - pattern->entry;
+ if (!pattern->n_entries) return -1;
+
+ return 0;
+}
+
+
+static void free_chunks(PSMCHUNK * chunk, int count) {
+ int n;
+
+ for (n = 0; n < count; n++) {
+ if (chunk[n].data)
+ free(chunk[n].data);
+ }
+
+ free(chunk);
+}
+
+static void dumb_it_optimize_orders(DUMB_IT_SIGDATA * sigdata);
+
+static int pattcmp( const unsigned char *, const unsigned char *, size_t );
+
+static DUMB_IT_SIGDATA *it_psm_load_sigdata(DUMBFILE *f, int * ver, int subsong)
+{
+ DUMB_IT_SIGDATA *sigdata;
+
+ PSMCHUNK *chunk;
+ int n_chunks = 0;
+
+ PSMCHUNK *songchunk;
+ int n_song_chunks = 0;
+
+ PSMEVENT *event;
+ int n_events = 0;
+
+ unsigned char * ptr;
+
+ int n, length, o;
+
+ int found;
+
+ int n_patterns = 0;
+
+ int first_pattern_line = -1;
+ int first_pattern;
+
+ int speed, bpm;
+ unsigned char pan[64];
+ int vol[32];
+
+ if (dumbfile_mgetl(f) != DUMB_ID('P','S','M',' ')) goto error;
+
+ length = dumbfile_igetl(f);
+
+ if (dumbfile_mgetl(f) != DUMB_ID('F','I','L','E')) goto error;
+
+ chunk = calloc(768, sizeof(*chunk));
+
+ while (length >= 8) {
+ chunk[n_chunks].id = dumbfile_mgetl(f);
+ n = dumbfile_igetl(f);
+ length -= 8;
+ if (n < 0 || n > length)
+ goto error_fc;
+ chunk[n_chunks].len = n;
+ if (n) {
+ ptr = malloc(n);
+ if (!ptr) goto error_fc;
+ if (dumbfile_getnc(ptr, n, f) < n)
+ {
+ free(ptr);
+ goto error_fc;
+ }
+ chunk[n_chunks].data = ptr;
+ }
+ n_chunks++;
+ length -= n;
+ }
+
+ if (!n_chunks) goto error_fc;
+
+ sigdata = malloc(sizeof(*sigdata));
+ if (!sigdata) goto error_fc;
+
+ sigdata->n_patterns = 0;
+ sigdata->n_samples = 0;
+ sigdata->name[0] = 0;
+
+ found = 0;
+
+ for (n = 0; n < n_chunks; n++) {
+ PSMCHUNK * c = &chunk[n];
+ switch(c->id) {
+ case DUMB_ID('S','D','F','T'):
+ /* song data format? */
+ if ((found & 1) || (c->len != 8) || memcmp(c->data, "MAINSONG", 8)) goto error_sd;
+ found |= 1;
+ break;
+
+ case DUMB_ID('S','O','N','G'):
+ if (/*(found & 2) ||*/ (c->len < 11) /*|| memcmp(c->data, "MAINSONG", 8)*/) goto error_sd;
+ found |= 2;
+ break;
+
+ case DUMB_ID('D','S','M','P'):
+ sigdata->n_samples++;
+ break;
+
+ case DUMB_ID('T','I','T','L'):
+ length = min(sizeof(sigdata->name) - 1, c->len);
+ memcpy(sigdata->name, c->data, length);
+ sigdata->name[length] = 0;
+ }
+ }
+
+ if (found != 3 || !sigdata->n_samples) goto error_sd;
+
+ sigdata->song_message = NULL;
+ sigdata->order = NULL;
+ sigdata->instrument = NULL;
+ sigdata->sample = NULL;
+ sigdata->pattern = NULL;
+ sigdata->midi = NULL;
+ sigdata->checkpoint = NULL;
+
+ sigdata->n_instruments = 0;
+ sigdata->n_orders = 0;
+
+ for (n = 0; n < n_chunks; n++) {
+ PSMCHUNK * c = &chunk[n];
+ if (c->id == DUMB_ID('S','O','N','G')) {
+ if (subsong == 0) break;
+ subsong--;
+ }
+ }
+
+ if (n == n_chunks) return NULL;
+ subsong = n;
+
+ /*for (n = 0; n < n_chunks; n++) {
+ PSMCHUNK * c = &chunk[n];
+ if (c->id == DUMB_ID('S','O','N','G')) {*/
+ {
+ PSMCHUNK * c = &chunk[subsong];
+ {
+ ptr = c->data;
+ if (ptr[10] > 32) goto error_usd;
+ sigdata->n_pchannels = ptr[10];
+ length = c->len - 11;
+ ptr += 11;
+ songchunk = 0;
+ if (length >= 8) {
+ songchunk = malloc(128 * sizeof(*songchunk));
+ if (!songchunk) goto error_usd;
+ while (length >= 8) {
+ songchunk[n_song_chunks].id = DUMB_ID(ptr[0], ptr[1], ptr[2], ptr[3]);
+ n = ptr[4] | (ptr[5] << 8) | (ptr[6] << 16) | (ptr[7] << 24);
+ length -= 8;
+ if (n > length) goto error_sc;
+ songchunk[n_song_chunks].len = n;
+ songchunk[n_song_chunks].data = ptr + 8;
+ n_song_chunks++;
+ length -= n;
+ ptr += 8 + n;
+ }
+ }
+ /*break;*/
+ }
+ }
+
+ if (!n_song_chunks) goto error_sc;
+
+ found = 0;
+
+ for (n = 0; n < n_song_chunks; n++) {
+ PSMCHUNK * c = &songchunk[n];
+
+ if (c->id == DUMB_ID('D','A','T','E')) {
+ /* date of the library build / format spec */
+ if (c->len == 6) {
+ length = c->len;
+ ptr = c->data;
+ while (length > 0) {
+ if (*ptr >= '0' && *ptr <= '9') {
+ found = (found * 10) + (*ptr - '0');
+ } else {
+ found = 0;
+ break;
+ }
+ ptr++;
+ length--;
+ }
+ }
+ break;
+ }
+ }
+
+ /*
+ if (found != 940506 &&
+ found != 940509 &&
+ found != 940510 &&
+ found != 940530 &&
+ found != 940629 &&
+ found != PSMV_OLD &&
+ found != 941011 &&
+ found != PSMV_NEW &&
+ found != 940906 &&
+ found != 940903 &&
+ found != 940914 &&
+ found != 941213 &&
+ found != 800211) /* WTF?
+ goto error_sc;
+ */
+
+ *ver = found;
+
+ if (found == 800211 ||
+ found == PSMV_NEW ||
+ found == 940903 ||
+ found == 940906 ||
+ found == 940914 ||
+ found == 941213) found = PSMV_NEW;
+ else found = PSMV_OLD;
+
+ memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
+
+ for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) {
+ sigdata->channel_pan[n ] = 16;
+ sigdata->channel_pan[n+1] = 48;
+ sigdata->channel_pan[n+2] = 48;
+ sigdata->channel_pan[n+3] = 16;
+ }
+
+ for (n = 0; n < n_song_chunks; n++) {
+ PSMCHUNK * c = &songchunk[n];
+
+ switch (c->id) {
+ case DUMB_ID('O','P','L','H'):
+ if (c->len < 2) goto error_sc;
+ ptr = c->data;
+ o = ptr[0] | (ptr[1] << 8);
+ if (!o) goto error_sc;
+ event = malloc(o * sizeof(*event));
+ if (!event) goto error_sc;
+ length = c->len - 2;
+ ptr += 2;
+ while ((length > 0) && (n_events < o)) {
+ event[n_events].type = *ptr;
+ switch (*ptr) {
+ case PSM_EVENT_END:
+ ptr++;
+ length--;
+ break;
+
+ case PSM_EVENT_PLAY_PATTERN:
+ if (found == PSMV_OLD) {
+ if (length < 5) goto error_ev;
+ memcpy(event[n_events].data, ptr + 1, 4);
+ ptr += 5;
+ length -= 5;
+ } else if (found == PSMV_NEW) {
+ if (length < 9) goto error_ev;
+ memcpy(event[n_events].data, ptr + 1, 8);
+ ptr += 9;
+ length -= 9;
+ }
+ break;
+
+ case PSM_EVENT_SET_SPEED:
+ case PSM_EVENT_SET_BPM:
+ if (length < 2) goto error_ev;
+ event[n_events].data[0] = ptr[1];
+ ptr += 2;
+ length -= 2;
+ break;
+
+ case PSM_EVENT_JUMP_TO_LINE:
+ case PSM_EVENT_CHANGE_VOL:
+ if (length < 3) goto error_ev;
+ memcpy(event[n_events].data, ptr + 1, 2);
+ ptr += 3;
+ length -= 3;
+ break;
+
+ case PSM_EVENT_SAMPLE_MAP_TABLE:
+ if (length < 7) goto error_ev;
+ memcpy(event[n_events].data, ptr + 1, 6);
+ ptr += 7;
+ length -= 7;
+ break;
+
+ case PSM_EVENT_CHANGE_PAN:
+ if (length < 4) goto error_ev;
+ memcpy(event[n_events].data, ptr + 1, 3);
+ ptr += 4;
+ length -= 4;
+ break;
+
+ default:
+ goto error_ev;
+ }
+ n_events++;
+ }
+ break;
+
+ case DUMB_ID('P','P','A','N'):
+ length = c->len;
+ if (length & 1) goto error_ev;
+ ptr = c->data;
+ o = 0;
+ while (length > 0) {
+ switch (ptr[0]) {
+ case 0:
+ sigdata->channel_pan[o] = ((((int)(signed char)ptr[1]) * 32) / 127) + 32;
+ break;
+ case 2:
+ sigdata->channel_pan[o] = IT_SURROUND;
+ break;
+ case 4:
+ sigdata->channel_pan[o] = 32;
+ break;
+ }
+ ptr += 2;
+ length -= 2;
+ if (++o >= DUMB_IT_N_CHANNELS) break;
+ }
+ break;
+
+ /*
+ case DUMB_ID('P','A','T','T'):
+ case DUMB_ID('D','S','A','M'):
+ */
+ }
+ }
+
+ sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX;
+
+ sigdata->global_volume = 128;
+ sigdata->speed = 6;
+ sigdata->tempo = 125;
+ sigdata->mixing_volume = 48;
+ sigdata->pan_separation = 128;
+
+ speed = 0;
+ bpm = 0;
+ memset(pan, 255, sizeof(pan));
+ memset(vol, 255, sizeof(vol));
+
+ sigdata->n_patterns = n_events;
+ sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
+ if (!sigdata->pattern) goto error_ev;
+ for (n = 0; n < sigdata->n_patterns; n++)
+ sigdata->pattern[n].entry = NULL;
+
+ for (n = 0; n < n_events; n++) {
+ PSMEVENT * e = &event[n];
+ switch (e->type) {
+ case PSM_EVENT_END:
+ n = n_events;
+ break;
+
+ case PSM_EVENT_PLAY_PATTERN:
+ for (o = 0; o < n_chunks; o++) {
+ PSMCHUNK * c = &chunk[o];
+ if (c->id == DUMB_ID('P','B','O','D')) {
+ ptr = c->data;
+ length = c->len;
+ if (found == PSMV_OLD) {
+ if (length < 8) goto error_ev;
+ if (!pattcmp(ptr + 4, e->data, 4)) {
+ if (it_psm_process_pattern(&sigdata->pattern[n_patterns], ptr, length, speed, bpm, pan, vol, found)) goto error_ev;
+ if (first_pattern_line < 0) {
+ first_pattern_line = n;
+ first_pattern = o;
+ }
+ e->data[0] = n_patterns;
+ e->data[1] = n_patterns >> 8;
+ n_patterns++;
+ break;
+ }
+ } else if (found == PSMV_NEW) {
+ if (length < 12) goto error_ev;
+ if (!pattcmp(ptr + 4, e->data, 8)) {
+ if (it_psm_process_pattern(&sigdata->pattern[n_patterns], ptr, length, speed, bpm, pan, vol, found)) goto error_ev;
+ if (first_pattern_line < 0) {
+ first_pattern_line = n;
+ first_pattern = o;
+ }
+ e->data[0] = n_patterns;
+ e->data[1] = n_patterns >> 8;
+ n_patterns++;
+ break;
+ }
+ }
+ }
+ }
+ if (o == n_chunks) goto error_ev;
+
+ speed = 0;
+ bpm = 0;
+ memset(pan, 255, sizeof(pan));
+ memset(vol, 255, sizeof(vol));
+
+ e->type = PSM_EVENT_END;
+ break;
+
+ case PSM_EVENT_JUMP_TO_LINE:
+ o = e->data[0] | (e->data[1] << 8);
+ if (o >= n_events) goto error_ev;
+ if (o == 0) {
+ /* whew! easy case! */
+ sigdata->restart_position = 0;
+ n = n_events;
+ } else if (o == n) {
+ /* freeze */
+ n = n_events;
+ } else if (o > n) {
+ /* jump ahead, setting played event numbers to zero will prevent endless looping */
+ n = o - 1;
+ } else if (o >= first_pattern_line) {
+ /* another semi-easy case */
+ sigdata->restart_position = event[o].data[0] | (event[o].data[1] << 8);
+ n = n_events;
+ } else {
+ /* crud, try to simulate rerunning all of the commands from the indicated
+ * line up to the first pattern, then dupe the first pattern again.
+ */
+ /*
+ PSMCHUNK * c = &chunk[first_pattern];
+
+ for (; o < first_pattern_line; o++) {
+ PSMEVENT * ev = &event[o];
+ switch (ev->type) {
+ case PSM_EVENT_SET_SPEED:
+ speed = ev->data[0];
+ break;
+ case PSM_EVENT_SET_BPM:
+ bpm = ev->data[0];
+ break;
+ case PSM_EVENT_CHANGE_PAN:
+ if (ev->data[0] > 31) goto error_ev;
+ pan[ev->data[0] * 2] = ev->data[1];
+ pan[ev->data[0] * 2 + 1] = ev->data[2];
+ break;
+ case PSM_EVENT_CHANGE_VOL:
+ if (ev->data[0] > 31) goto error_ev;
+ vol[ev->data[0]] = ev->data[1];
+ break;
+ }
+ }
+
+ if (it_psm_process_pattern(&sigdata->pattern[n_patterns], c->data, c->len, speed, bpm, pan, vol, found)) goto error_ev;
+ n_patterns++;
+ sigdata->restart_position = 1;
+ n = n_events;
+
+ Eh, what the hell? PSM has no panning commands anyway.
+ */
+ sigdata->restart_position = 0;
+ n = n_events;
+ }
+ e->type = PSM_EVENT_END;
+ break;
+
+ case PSM_EVENT_SET_SPEED:
+ speed = e->data[0];
+ break;
+
+ case PSM_EVENT_SET_BPM:
+ bpm = e->data[0];
+ break;
+
+ case PSM_EVENT_CHANGE_PAN:
+ o = e->data[0];
+ if (o > 31) goto error_ev;
+ pan[o * 2] = e->data[1];
+ pan[o * 2 + 1] = e->data[2];
+ break;
+
+ case PSM_EVENT_CHANGE_VOL:
+ o = e->data[0];
+ if (o > 31) goto error_ev;
+ vol[o] = e->data[1];
+ break;
+
+ case PSM_EVENT_SAMPLE_MAP_TABLE:
+ if (e->data[0] != 0 || e->data[1] != 0xFF ||
+ e->data[2] != 0 || e->data[3] != 0 ||
+ e->data[4] != 1 || e->data[5] != 0)
+ goto error_ev;
+ break;
+ }
+ }
+
+ if (n_patterns > 256) goto error_ev;
+
+ sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
+ if (!sigdata->sample) goto error_ev;
+ for (n = 0; n < sigdata->n_samples; n++)
+ sigdata->sample[n].data = NULL;
+
+ o = 0;
+ for (n = 0; n < n_chunks; n++) {
+ PSMCHUNK * c = &chunk[n];
+ if (c->id == DUMB_ID('D','S','M','P')) {
+ if (it_psm_process_sample(&sigdata->sample[o], c->data, c->len, o, found)) goto error_ev;
+ o++;
+ }
+ }
+
+ sigdata->n_orders = n_patterns;
+ sigdata->n_patterns = n_patterns;
+
+ sigdata->order = malloc(n_patterns);
+
+ for (n = 0; n < n_patterns; n++) {
+ sigdata->order[n] = n;
+ }
+
+ free(event);
+ free(songchunk);
+ free_chunks(chunk, n_chunks);
+
+ _dumb_it_fix_invalid_orders(sigdata);
+
+ dumb_it_optimize_orders(sigdata);
+
+ return sigdata;
+
+error_ev:
+ free(event);
+error_sc:
+ if (songchunk) free(songchunk);
+error_usd:
+ _dumb_it_unload_sigdata(sigdata);
+ goto error_fc;
+error_sd:
+ free(sigdata);
+error_fc:
+ free_chunks(chunk, n_chunks);
+error:
+ return NULL;
+}
+
+static int it_order_compare(const void *e1, const void *e2) {
+ if (*((const char *)e1) < *((const char *)e2))
+ return -1;
+
+ if (*((const char *)e1) > *((const char *)e2))
+ return 1;
+
+ return 0;
+}
+
+static int it_optimize_compare(const void *e1, const void *e2) {
+ if (((const IT_ENTRY *)e1)->channel < ((const IT_ENTRY *)e2)->channel)
+ return -1;
+
+ if (((const IT_ENTRY *)e1)->channel > ((const IT_ENTRY *)e2)->channel)
+ return 1;
+
+ return 0;
+}
+
+static int it_entry_compare(const IT_ENTRY * e1, const IT_ENTRY * e2) {
+ if (IT_IS_END_ROW(e1) && IT_IS_END_ROW(e2)) return 1;
+ if (e1->channel != e2->channel) return 0;
+ if (e1->mask != e2->mask) return 0;
+ if ((e1->mask & IT_ENTRY_NOTE) && (e1->note != e2->note)) return 0;
+ if ((e1->mask & IT_ENTRY_INSTRUMENT) && (e1->instrument != e2->instrument)) return 0;
+ if ((e1->mask & IT_ENTRY_VOLPAN) && (e1->volpan != e2->volpan)) return 0;
+ if ((e1->mask & IT_ENTRY_EFFECT) && ((e1->effect != e2->effect) || (e1->effectvalue != e2->effectvalue))) return 0;
+ return 1;
+}
+
+/*
+static void dumb_it_optimize_pattern(IT_PATTERN * pattern) {
+ IT_ENTRY * entry, * end;
+ IT_ENTRY * rowstart, * rowend;
+ IT_ENTRY * current;
+
+ if (!pattern->n_entries || !pattern->entry) return;
+
+ current = entry = pattern->entry;
+ end = entry + pattern->n_entries;
+
+ while (entry < end) {
+ rowstart = entry;
+ while (!IT_IS_END_ROW(entry)) entry++;
+ rowend = entry;
+ if (rowend > rowstart + 1)
+ qsort(rowstart, rowend - rowstart, sizeof(IT_ENTRY), &it_optimize_compare);
+ entry = rowstart;
+ while (entry < rowend) {
+ if (!(entry->mask)) {}
+ else if (it_entry_compare(entry, current)) {}
+ else if (!(current->mask) ||
+ ((entry->channel == current->channel) &&
+ ((entry->mask | current->mask) == (entry->mask ^ current->mask)))) {
+ current->mask |= entry->mask;
+ if (entry->mask & IT_ENTRY_NOTE) current->note = entry->note;
+ if (entry->mask & IT_ENTRY_INSTRUMENT) current->instrument = entry->instrument;
+ if (entry->mask & IT_ENTRY_VOLPAN) current->volpan = entry->volpan;
+ if (entry->mask & IT_ENTRY_EFFECT) {
+ current->effect = entry->effect;
+ current->effectvalue = entry->effectvalue;
+ }
+ } else {
+ if (++current < entry) *current = *entry;
+ }
+ entry++;
+ }
+ if (++current < entry) *current = *entry;
+ entry++;
+ }
+
+ current++;
+
+ if (current < end) {
+ IT_ENTRY * opt;
+ pattern->n_entries = current - pattern->entry;
+ opt = realloc(pattern->entry, pattern->n_entries * sizeof(*pattern->entry));
+ if (opt) pattern->entry = opt;
+ }
+}
+*/
+
+static int it_pattern_compare(const IT_PATTERN * p1, const IT_PATTERN * p2) {
+ IT_ENTRY * e1, * end;
+ IT_ENTRY * e2;
+
+ if (p1 == p2) return 1;
+ if (p1->n_entries != p2->n_entries) return 0;
+
+ e1 = p1->entry; end = e1 + p1->n_entries;
+ e2 = p2->entry;
+
+ while (e1 < end) {
+ if (!it_entry_compare(e1, e2)) return 0;
+ e1++; e2++;
+ }
+
+ return 1;
+}
+
+static void dumb_it_optimize_orders(DUMB_IT_SIGDATA * sigdata) {
+ int n, o, p;
+
+ int last_invalid = (sigdata->flags & IT_WAS_AN_XM) ? 255 : 253;
+
+ unsigned char * order_list;
+ int n_patterns;
+
+ IT_PATTERN * pattern;
+
+ if (!sigdata->n_orders || !sigdata->n_patterns) return;
+
+ n_patterns = 0;
+ order_list = malloc(sigdata->n_orders);
+
+ if (!order_list) return;
+
+ for (n = 0; n < sigdata->n_orders; n++) {
+ if (sigdata->order[n] < sigdata->n_patterns) {
+ for (o = 0; o < n_patterns; o++) {
+ if (sigdata->order[n] == order_list[o]) break;
+ }
+ if (o == n_patterns) {
+ order_list[n_patterns++] = sigdata->order[n];
+ }
+ }
+ }
+
+ if (!n_patterns) {
+ free(order_list);
+ return;
+ }
+
+ /*for (n = 0; n < n_patterns; n++) {
+ dumb_it_optimize_pattern(&sigdata->pattern[order_list[n]]);
+ }*/
+
+ for (n = 0; n < n_patterns; n++) {
+ for (o = n + 1; o < n_patterns; o++) {
+ if ((order_list[n] != order_list[o]) &&
+ it_pattern_compare(&sigdata->pattern[order_list[n]], &sigdata->pattern[order_list[o]])) {
+ for (p = 0; p < sigdata->n_orders; p++) {
+ if (sigdata->order[p] == order_list[o]) {
+ sigdata->order[p] = order_list[n];
+ }
+ }
+ for (p = o + 1; p < n_patterns; p++) {
+ if (order_list[p] == order_list[o]) {
+ order_list[p] = order_list[n];
+ }
+ }
+ order_list[o] = order_list[n];
+ }
+ }
+ }
+
+ qsort(order_list, n_patterns, sizeof(*order_list), &it_order_compare);
+
+ for (n = 0, o = 0; n < n_patterns; n++) {
+ if (order_list[n] != order_list[o]) {
+ if (++o < n) order_list[o] = order_list[n];
+ }
+ }
+
+ n_patterns = o + 1;
+
+ pattern = malloc(n_patterns * sizeof(*pattern));
+ if (!pattern) {
+ free(order_list);
+ return;
+ }
+
+ for (n = 0; n < n_patterns; n++) {
+ pattern[n] = sigdata->pattern[order_list[n]];
+ }
+
+ for (n = 0; n < sigdata->n_patterns; n++) {
+ for (o = 0; o < n_patterns; o++) {
+ if (order_list[o] == n) break;
+ }
+ if (o == n_patterns) {
+ if (sigdata->pattern[n].entry)
+ free(sigdata->pattern[n].entry);
+ }
+ }
+
+ free(sigdata->pattern);
+ sigdata->pattern = pattern;
+ sigdata->n_patterns = n_patterns;
+
+ for (n = 0; n < sigdata->n_orders; n++) {
+ for (o = 0; o < n_patterns; o++) {
+ if (sigdata->order[n] == order_list[o]) {
+ sigdata->order[n] = o;
+ break;
+ }
+ }
+ }
+
+ free(order_list);
+}
+
+int dumb_get_psm_subsong_count(DUMBFILE *f) {
+ int length, subsongs;
+ long l;
+
+ if (dumbfile_mgetl(f) != DUMB_ID('P','S','M',' ')) return 0;
+
+ length = dumbfile_igetl(f);
+
+ if (dumbfile_mgetl(f) != DUMB_ID('F','I','L','E')) return 0;
+
+ subsongs = 0;
+
+ while (length >= 8 && !dumbfile_error(f)) {
+ if (dumbfile_mgetl(f) == DUMB_ID('S','O','N','G')) subsongs++;
+ l = dumbfile_igetl(f);
+ dumbfile_skip(f, l);
+ length -= l + 8;
+ }
+
+ if (dumbfile_error(f)) return 0;
+
+ return subsongs;
+}
+
+
+
+/* Eww */
+int pattcmp( const unsigned char * a, const unsigned char * b, size_t l )
+{
+ int i, j, na, nb;
+ char * p;
+
+ i = memcmp( a, b, l );
+ if ( !i ) return i;
+
+ /* damnit */
+
+ for ( i = 0; i < l; ++i )
+ {
+ if ( a [i] >= '0' && a [i] <= '9' ) break;
+ }
+
+ if ( i < l )
+ {
+ na = strtoul( a + i, &p, 10 );
+ if ( (const unsigned char *)p == a + i ) return 1;
+ }
+
+ for ( j = 0; j < l; ++j )
+ {
+ if ( b [j] >= '0' && b [j] <= '9' ) break;
+ }
+
+ if ( j < l )
+ {
+ nb = strtoul( b + j, &p, 10 );
+ if ( (const unsigned char *)p == b + j ) return -1;
+ }
+
+ if ( i < j ) return -1;
+ else if ( j > i ) return 1;
+
+ i = memcmp( a, b, j );
+ if ( i ) return i;
+
+ return na - nb;
+}
+
+
+
+DUH *dumb_read_psm_quick(DUMBFILE *f, int subsong)
+{
+ sigdata_t *sigdata;
+ int ver;
+
+ DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+ sigdata = it_psm_load_sigdata(f, &ver, subsong);
+
+ if (!sigdata)
+ return NULL;
+
+ {
+ int n_tags = 2;
+ char version[16];
+ const char *tag[3][2];
+ tag[0][0] = "TITLE";
+ tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+ tag[1][0] = "FORMAT";
+ tag[1][1] = "PSM";
+ if ( ver )
+ {
+ tag[2][0] = "FORMATVERSION";
+ snprintf (version, 10, "%d", ver);
+ tag[2][1] = (const char *) &version;
+ ++n_tags;
+ }
+ return make_duh(-1, n_tags, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
+ }
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/readptm.c b/plugins/dumb/dumb-kode54/src/it/readptm.c
new file mode 100644
index 00000000..d8c77297
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/readptm.c
@@ -0,0 +1,574 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * readptm.c - Code to read a Poly Tracker v2.03 / / \ \
+ * module from an open file. | < / \_
+ * | \/ /\ /
+ * By Chris Moeller. Based on reads3m.c \_ / > /
+ * by entheh. | \ / /
+ * | ' /
+ * \__/
+ */
+
+// IT_STEREO... :o
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/** WARNING: this is duplicated in itread.c */
+static int it_seek(DUMBFILE *f, long offset)
+{
+ long pos = dumbfile_pos(f);
+
+ if (pos > offset)
+ return -1;
+
+ if (pos < offset)
+ if (dumbfile_skip(f, offset - pos))
+ return -1;
+
+ return 0;
+}
+
+
+
+static int it_ptm_read_sample_header(IT_SAMPLE *sample, long *offset, DUMBFILE *f)
+{
+ int flags;
+
+ flags = dumbfile_getc(f);
+
+ dumbfile_getnc(sample->filename, 12, f);
+ sample->filename[12] = 0;
+
+ sample->default_volume = dumbfile_getc(f);
+
+ sample->C5_speed = dumbfile_igetw(f) << 1;
+
+ dumbfile_skip(f, 2); /* segment */
+
+ *offset = dumbfile_igetl(f);
+
+ sample->length = dumbfile_igetl(f);
+ sample->loop_start = dumbfile_igetl(f);
+ sample->loop_end = dumbfile_igetl(f);
+
+ /* GUSBegin, GUSLStart, GUSLEnd, GUSLoop, reserverd */
+ dumbfile_skip(f, 4+4+4+1+1);
+
+ dumbfile_getnc(sample->name, 28, f);
+ sample->name[28] = 0;
+
+ /*
+ if (dumbfile_mgetl(f) != DUMB_ID('P','T','M','S'))
+ return -1;
+ */
+
+ /* BLAH! Shit likes to have broken or missing sample IDs */
+ dumbfile_skip(f, 4);
+
+ if ((flags & 3) == 0) {
+ /* Looks like no sample */
+ sample->flags &= ~IT_SAMPLE_EXISTS;
+ return dumbfile_error(f);
+ }
+
+ sample->global_volume = 64;
+
+ sample->flags = IT_SAMPLE_EXISTS;
+ if (flags & 4) sample->flags |= IT_SAMPLE_LOOP;
+ if (flags & 8) sample->flags |= IT_SAMPLE_PINGPONG_LOOP;
+
+ if (flags & 16) {
+ sample->flags |= IT_SAMPLE_16BIT;
+
+ sample->length >>= 1;
+ sample->loop_start >>= 1;
+ sample->loop_end >>= 1;
+ }
+
+ if (sample->loop_end) sample->loop_end--;
+
+ sample->default_pan = 0; // 0 = don't use, or 160 = centre?
+
+ if (sample->length <= 0)
+ sample->flags &= ~IT_SAMPLE_EXISTS;
+ else if (sample->flags & IT_SAMPLE_LOOP) {
+ if ((unsigned int)sample->loop_end > (unsigned int)sample->length)
+ sample->flags &= ~IT_SAMPLE_LOOP;
+ else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end)
+ sample->flags &= ~IT_SAMPLE_LOOP;
+ else
+ sample->length = sample->loop_end;
+ }
+
+
+ //Do we need to set all these?
+ sample->vibrato_speed = 0;
+ sample->vibrato_depth = 0;
+ sample->vibrato_rate = 0;
+ sample->vibrato_waveform = IT_VIBRATO_SINE;
+ sample->finetune = 0;
+ sample->max_resampling_quality = -1;
+
+ return dumbfile_error(f);
+}
+
+
+static int it_ptm_read_byte(DUMBFILE *f)
+{
+ int meh = dumbfile_getc(f);
+ if (meh < 0) return 0;
+ return meh;
+}
+
+static int it_ptm_read_sample_data(IT_SAMPLE *sample, int last, DUMBFILE *f)
+{
+ long n;
+ int s;
+
+ sample->data = malloc(sample->length * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1));
+ if (!sample->data)
+ return -1;
+
+ s = 0;
+
+ if (sample->flags & IT_SAMPLE_16BIT) {
+ unsigned char a, b;
+ for (n = 0; n < sample->length; n++) {
+ a = s += (signed char) it_ptm_read_byte(f);
+ b = s += (signed char) it_ptm_read_byte(f);
+ ((short *)sample->data)[n] = a | (b << 8);
+ }
+ } else {
+ for (n = 0; n < sample->length; n++) {
+ s += (signed char) it_ptm_read_byte(f);
+ ((signed char *)sample->data)[n] = s;
+ }
+ }
+
+ if (dumbfile_error(f) && !last)
+ return -1;
+
+ return 0;
+}
+
+
+
+static int it_ptm_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer, int length)
+{
+ int buflen = 0;
+ int bufpos = 0;
+ int effect, effectvalue;
+
+ IT_ENTRY *entry;
+
+ unsigned char channel;
+
+ if (!length)
+ return -1;
+
+ pattern->n_rows = 0;
+ pattern->n_entries = 0;
+
+ /* Read in the pattern data, little by little, and work out how many
+ * entries we need room for. Sorry, but this is just so funny...
+ */
+ for (;;) {
+ unsigned char b = buffer[buflen++] = dumbfile_getc(f);
+
+#if 1
+ static const unsigned char used[8] = {0, 2, 2, 4, 1, 3, 3, 5};
+ channel = b & 31;
+ b >>= 5;
+ pattern->n_entries++;
+ if (b) {
+ if (buflen + used[b] >= 65536) return -1;
+ dumbfile_getnc(buffer + buflen, used[b], f);
+ buflen += used[b];
+ } else {
+ /* End of row */
+ if (++pattern->n_rows == 64) break;
+ if (buflen >= 65536) return -1;
+ }
+#else
+ if (b == 0) {
+ /* End of row */
+ pattern->n_entries++;
+ if (++pattern->n_rows == 64) break;
+ if (buflen >= 65536) return -1;
+ } else {
+ static const unsigned char used[8] = {0, 2, 2, 4, 1, 3, 3, 5};
+ channel = b & 31;
+ b >>= 5;
+ if (b) {
+ pattern->n_entries++;
+ if (buflen + used[b] >= 65536) return -1;
+ dumbfile_getnc(buffer + buflen, used[b], f);
+ buflen += used[b];
+ }
+ }
+#endif
+
+ /* We have ensured that buflen < 65536 at this point, so it is safe
+ * to iterate and read at least one more byte without checking.
+ * However, now would be a good time to check for errors reading from
+ * the file.
+ */
+
+ if (dumbfile_error(f))
+ return -1;
+
+ /* Great. We ran out of data, but there should be data for more rows.
+ * Fill the rest with null data...
+ */
+ if (buflen >= length && pattern->n_rows < 64)
+ {
+ while (pattern->n_rows < 64)
+ {
+ if (buflen >= 65536) return -1;
+ buffer[buflen++] = 0;
+ pattern->n_entries++;
+ pattern->n_rows++;
+ }
+ break;
+ }
+ }
+
+ pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry));
+
+ if (!pattern->entry)
+ return -1;
+
+ entry = pattern->entry;
+
+ while (bufpos < buflen) {
+ unsigned char b = buffer[bufpos++];
+
+ if (b == 0)
+ {
+ /* End of row */
+ IT_SET_END_ROW(entry);
+ entry++;
+ continue;
+ }
+
+ channel = b & 31;
+
+ if (b & 224) {
+ entry->mask = 0;
+ entry->channel = channel;
+
+ if (b & 32) {
+ unsigned char n = buffer[bufpos++];
+ if (n == 254 || (n >= 1 && n <= 120)) {
+ if (n == 254)
+ entry->note = IT_NOTE_CUT;
+ else
+ entry->note = n - 1;
+ entry->mask |= IT_ENTRY_NOTE;
+ }
+
+ entry->instrument = buffer[bufpos++];
+ if (entry->instrument)
+ entry->mask |= IT_ENTRY_INSTRUMENT;
+ }
+
+ if (b & 64) {
+ effect = buffer[bufpos++];
+ effectvalue = buffer[bufpos++];
+ _dumb_it_ptm_convert_effect(effect, effectvalue, entry);
+ }
+
+ if (b & 128) {
+ entry->volpan = buffer[bufpos++];
+ if (entry->volpan <= 64)
+ entry->mask |= IT_ENTRY_VOLPAN;
+ }
+
+ entry++;
+ }
+ }
+
+ ASSERT(entry == pattern->entry + pattern->n_entries);
+
+ return 0;
+}
+
+
+
+/** WARNING: this is duplicated in itread.c - also bad practice to use the same struct name unless they are unified in a header */
+/* Currently we assume the sample data are stored after the sample headers in
+ * module files. This assumption may be unjustified; let me know if you have
+ * trouble.
+ */
+
+#define PTM_COMPONENT_INSTRUMENT 1
+#define PTM_COMPONENT_PATTERN 2
+#define PTM_COMPONENT_SAMPLE 3
+
+typedef struct PTM_COMPONENT
+{
+ unsigned char type;
+ unsigned char n;
+ long offset;
+}
+PTM_COMPONENT;
+
+
+
+static int ptm_component_compare(const void *e1, const void *e2)
+{
+ return ((const PTM_COMPONENT *)e1)->offset -
+ ((const PTM_COMPONENT *)e2)->offset;
+}
+
+
+
+static DUMB_IT_SIGDATA *it_ptm_load_sigdata(DUMBFILE *f)
+{
+ DUMB_IT_SIGDATA *sigdata;
+
+ PTM_COMPONENT *component;
+ int n_components = 0;
+
+ int n;
+
+ unsigned char *buffer;
+
+ sigdata = malloc(sizeof(*sigdata));
+ if (!sigdata) return NULL;
+
+ /* Skip song name. */
+ dumbfile_getnc(sigdata->name, 28, f);
+ sigdata->name[28] = 0;
+
+ if (dumbfile_getc(f) != 0x1A || dumbfile_igetw(f) != 0x203) {
+ free(sigdata);
+ return NULL;
+ }
+
+ dumbfile_skip(f, 1);
+
+ sigdata->song_message = NULL;
+ sigdata->order = NULL;
+ sigdata->instrument = NULL;
+ sigdata->sample = NULL;
+ sigdata->pattern = NULL;
+ sigdata->midi = NULL;
+ sigdata->checkpoint = NULL;
+
+ sigdata->n_orders = dumbfile_igetw(f);
+ sigdata->n_instruments = 0;
+ sigdata->n_samples = dumbfile_igetw(f);
+ sigdata->n_patterns = dumbfile_igetw(f);
+
+ if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_samples > 255 || sigdata->n_patterns > 128) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ sigdata->n_pchannels = dumbfile_igetw(f);
+
+ if (dumbfile_igetw(f) != 0) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ dumbfile_skip(f, 2);
+
+ if (dumbfile_mgetl(f) != DUMB_ID('P','T','M','F')) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ dumbfile_skip(f, 16);
+
+ sigdata->order = malloc(sigdata->n_orders);
+ if (!sigdata->order) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ if (sigdata->n_samples) {
+ sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
+ if (!sigdata->sample) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ for (n = 0; n < sigdata->n_samples; n++)
+ sigdata->sample[n].data = NULL;
+ }
+
+ if (sigdata->n_patterns) {
+ sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
+ if (!sigdata->pattern) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ for (n = 0; n < sigdata->n_patterns; n++)
+ sigdata->pattern[n].entry = NULL;
+ }
+
+ /** WARNING: which ones? */
+ sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_A_PTM;
+
+ sigdata->global_volume = 128;
+ sigdata->speed = 6;
+ sigdata->tempo = 125;
+ sigdata->mixing_volume = 48;
+
+ /* Panning positions for 32 channels */
+ {
+ int i;
+ for (i = 0; i < 32; i++) {
+ int c = dumbfile_getc(f);
+ if (c <= 15) {
+ sigdata->channel_volume[i] = 64;
+ sigdata->channel_pan[i] = c;
+ } else {
+ /** WARNING: this could be improved if we support channel muting... */
+ sigdata->channel_volume[i] = 0;
+ sigdata->channel_pan[i] = 7;
+ }
+ }
+ }
+
+ /* Orders, byte each, length = sigdata->n_orders (should be even) */
+ dumbfile_getnc(sigdata->order, sigdata->n_orders, f);
+ sigdata->restart_position = 0;
+
+ component = malloc(768*sizeof(*component));
+ if (!component) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ if (it_seek(f, 352)) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ for (n = 0; n < sigdata->n_patterns; n++) {
+ component[n_components].type = PTM_COMPONENT_PATTERN;
+ component[n_components].n = n;
+ component[n_components].offset = dumbfile_igetw(f) << 4;
+ n_components++;
+ }
+
+ if (it_seek(f, 608)) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ for (n = 0; n < sigdata->n_samples; n++) {
+ if (it_ptm_read_sample_header(&sigdata->sample[n], &component[n_components].offset, f)) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ if (!(sigdata->sample[n].flags & IT_SAMPLE_EXISTS)) continue;
+ component[n_components].type = PTM_COMPONENT_SAMPLE;
+ component[n_components].n = n;
+ n_components++;
+ }
+
+ qsort(component, n_components, sizeof(PTM_COMPONENT), &ptm_component_compare);
+
+ {
+ int i;
+ for (i = 0; i < 32; i++) {
+ sigdata->channel_pan[i] -= (sigdata->channel_pan[i] & 8) >> 3;
+ sigdata->channel_pan[i] = ((int)sigdata->channel_pan[i] << 5) / 7;
+ if (sigdata->channel_pan[i] > 64) sigdata->channel_pan[i] = 64;
+ }
+ }
+
+ sigdata->pan_separation = 128;
+
+ if (dumbfile_error(f)) {
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ buffer = malloc(65536);
+ if (!buffer) {
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ for (n = 0; n < n_components; n++) {
+ if (it_seek(f, component[n].offset)) {
+ free(buffer);
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ switch (component[n].type) {
+
+ case PTM_COMPONENT_PATTERN:
+ if (it_ptm_read_pattern(&sigdata->pattern[component[n].n], f, buffer, (n + 1 < n_components) ? (component[n+1].offset - component[n].offset) : 0)) {
+ free(buffer);
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ break;
+
+ case PTM_COMPONENT_SAMPLE:
+ if (it_ptm_read_sample_data(&sigdata->sample[component[n].n], (n + 1 == n_components), f)) {
+ free(buffer);
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ }
+ }
+
+ free(buffer);
+ free(component);
+
+ _dumb_it_fix_invalid_orders(sigdata);
+
+ return sigdata;
+}
+
+static char hexdigit(int in)
+{
+ if (in < 10) return in + '0';
+ else return in + 'A' - 10;
+}
+
+DUH *dumb_read_ptm_quick(DUMBFILE *f)
+{
+ sigdata_t *sigdata;
+
+ DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+ sigdata = it_ptm_load_sigdata(f);
+
+ if (!sigdata)
+ return NULL;
+
+ {
+ const char *tag[2][2];
+ tag[0][0] = "TITLE";
+ tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+ tag[1][0] = "FORMAT";
+ tag[1][1] = "PTM";
+ return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
+ }
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/readriff.c b/plugins/dumb/dumb-kode54/src/it/readriff.c
new file mode 100644
index 00000000..615e555c
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/readriff.c
@@ -0,0 +1,73 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * readriff.c - Code to read a RIFF module file / / \ \
+ * from memory. | < / \_
+ * | \/ /\ /
+ * \_ / > /
+ * By Chris Moeller. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+#include "internal/riff.h"
+
+
+DUH *dumb_read_riff_amff( struct riff * stream );
+DUH *dumb_read_riff_am( struct riff * stream );
+DUH *dumb_read_riff_dsmf( struct riff * stream );
+
+/* dumb_read_riff_quick(): reads a RIFF file into a DUH struct, returning a
+ * pointer to the DUH struct. When you have finished with it, you must pass
+ * the pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_read_riff_quick( DUMBFILE * f )
+{
+ DUH * duh;
+ struct riff * stream;
+
+ {
+ unsigned char * buffer = 0;
+ unsigned size = 0;
+ unsigned read;
+ do
+ {
+ buffer = realloc( buffer, 32768 + size );
+ if ( ! buffer ) return 0;
+ read = dumbfile_getnc( buffer + size, 32768, f );
+ if ( read < 0 )
+ {
+ free( buffer );
+ return 0;
+ }
+ size += read;
+ }
+ while ( read == 32768 );
+ stream = riff_parse( buffer, size, 1 );
+ if ( ! stream ) stream = riff_parse( buffer, size, 0 );
+ free( buffer );
+ }
+
+ if ( ! stream ) return 0;
+
+ if ( stream->type == DUMB_ID( 'A', 'M', ' ', ' ' ) )
+ duh = dumb_read_riff_am( stream );
+ else if ( stream->type == DUMB_ID( 'A', 'M', 'F', 'F' ) )
+ duh = dumb_read_riff_amff( stream );
+ else if ( stream->type == DUMB_ID( 'D', 'S', 'M', 'F' ) )
+ duh = dumb_read_riff_dsmf( stream );
+ else duh = 0;
+
+ riff_free( stream );
+
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/reads3m.c b/plugins/dumb/dumb-kode54/src/it/reads3m.c
new file mode 100644
index 00000000..0dac8331
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/reads3m.c
@@ -0,0 +1,864 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * reads3m.c - Code to read a ScreamTracker 3 / / \ \
+ * module from an open file. | < / \_
+ * | \/ /\ /
+ * By entheh. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+// IT_STEREO... :o
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+//#define S3M_BROKEN_OVERLAPPED_SAMPLES
+
+/** WARNING: this is duplicated in itread.c */
+static int it_seek(DUMBFILE *f, long offset)
+{
+ long pos = dumbfile_pos(f);
+
+ if (pos > offset) {
+ return -1;
+ }
+
+ if (pos < offset)
+ if (dumbfile_skip(f, offset - pos))
+ return -1;
+
+ return 0;
+}
+
+
+
+static int it_s3m_read_sample_header(IT_SAMPLE *sample, long *offset, unsigned char *pack, int cwtv, DUMBFILE *f)
+{
+ unsigned char type;
+ int flags;
+
+ type = dumbfile_getc(f);
+
+ dumbfile_getnc(sample->filename, 12, f);
+ sample->filename[12] = 0;
+
+ if (type > 1) {
+ /** WARNING: no adlib support */
+ dumbfile_skip(f, 3 + 12 + 1 + 1 + 2 + 2 + 2 + 12);
+ dumbfile_getnc(sample->name, 28, f);
+ sample->name[28] = 0;
+ dumbfile_skip(f, 4);
+ sample->flags &= ~IT_SAMPLE_EXISTS;
+ return -1; // return error so that another plugin could pick that file up
+ }
+
+ *offset = dumbfile_getc(f) << 20;
+ *offset += dumbfile_igetw(f) << 4;
+
+ sample->length = dumbfile_igetl(f);
+ sample->loop_start = dumbfile_igetl(f);
+ sample->loop_end = dumbfile_igetl(f);
+
+ sample->default_volume = dumbfile_getc(f);
+
+ dumbfile_skip(f, 1);
+
+ flags = dumbfile_getc(f);
+
+ if (flags < 0 || (flags != 0 && flags != 4))
+ /* Sample is packed apparently (or error reading from file). We don't
+ * know how to read packed samples.
+ */
+ return -1;
+
+ *pack = flags;
+
+ flags = dumbfile_getc(f);
+
+ sample->C5_speed = dumbfile_igetl(f) << 1;
+
+ /* Skip four unused bytes and three internal variables. */
+ dumbfile_skip(f, 4+2+2+4);
+
+ dumbfile_getnc(sample->name, 28, f);
+ sample->name[28] = 0;
+
+ if (type == 0 || sample->length <= 0) {
+ /* Looks like no-existy. Anyway, there's for sure no 'SCRS' ... */
+ sample->flags &= ~IT_SAMPLE_EXISTS;
+ return dumbfile_error(f);
+ }
+
+ if (dumbfile_mgetl(f) != DUMB_ID('S','C','R','S'))
+ return -1;
+
+ sample->global_volume = 64;
+
+ sample->flags = IT_SAMPLE_EXISTS;
+ if (flags & 1) sample->flags |= IT_SAMPLE_LOOP;
+
+ /* The ST3 TECH.DOC is unclear on this, but IMAGO Orpheus is not. Piece of crap. */
+
+ if (flags & 2) {
+ sample->flags |= IT_SAMPLE_STEREO;
+
+ if ((cwtv & 0xF000) == 0x2000) {
+ sample->length >>= 1;
+ sample->loop_start >>= 1;
+ sample->loop_end >>= 1;
+ }
+ }
+
+ if (flags & 4) {
+ sample->flags |= IT_SAMPLE_16BIT;
+
+ if ((cwtv & 0xF000) == 0x2000) {
+ sample->length >>= 1;
+ sample->loop_start >>= 1;
+ sample->loop_end >>= 1;
+ }
+ }
+
+ sample->default_pan = 0; // 0 = don't use, or 160 = centre?
+
+ if (sample->flags & IT_SAMPLE_LOOP) {
+ if ((unsigned int)sample->loop_end > (unsigned int)sample->length)
+ /*sample->flags &= ~IT_SAMPLE_LOOP;*/
+ sample->loop_end = sample->length;
+ else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end)
+ sample->flags &= ~IT_SAMPLE_LOOP;
+ else
+ /* ScreamTracker seems not to save what comes after the loop end
+ * point, but rather to assume it is a duplicate of what comes at
+ * the loop start point. I am not completely sure of this though.
+ * It is easy to evade; simply truncate the sample.
+ */
+ sample->length = sample->loop_end;
+ }
+
+
+ //Do we need to set all these?
+ sample->vibrato_speed = 0;
+ sample->vibrato_depth = 0;
+ sample->vibrato_rate = 0;
+ sample->vibrato_waveform = IT_VIBRATO_SINE;
+ sample->finetune = 0;
+ sample->max_resampling_quality = -1;
+
+ return dumbfile_error(f);
+}
+
+
+
+static int it_s3m_read_sample_data(IT_SAMPLE *sample, int ffi, unsigned char pack, DUMBFILE *f)
+{
+ long n;
+
+ long datasize = sample->length;
+ if (sample->flags & IT_SAMPLE_STEREO) datasize <<= 1;
+
+ sample->data = malloc(datasize * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1));
+ if (!sample->data)
+ return -1;
+
+ if (pack == 4) {
+ if (_dumb_it_read_sample_data_adpcm4(sample, f) < 0)
+ return -1;
+ }
+ else if (sample->flags & IT_SAMPLE_STEREO) {
+ if (sample->flags & IT_SAMPLE_16BIT) {
+ for (n = 0; n < datasize; n += 2)
+ ((short *)sample->data)[n] = dumbfile_igetw(f);
+ for (n = 1; n < datasize; n += 2)
+ ((short *)sample->data)[n] = dumbfile_igetw(f);
+ } else {
+ for (n = 0; n < datasize; n += 2)
+ ((signed char *)sample->data)[n] = dumbfile_getc(f);
+ for (n = 1; n < datasize; n += 2)
+ ((signed char *)sample->data)[n] = dumbfile_getc(f);
+ }
+ } else if (sample->flags & IT_SAMPLE_16BIT)
+ for (n = 0; n < sample->length; n++)
+ ((short *)sample->data)[n] = dumbfile_igetw(f);
+ else
+ for (n = 0; n < sample->length; n++)
+ ((signed char *)sample->data)[n] = dumbfile_getc(f);
+
+ if (dumbfile_error(f))
+ return -1;
+
+ if (ffi != 1) {
+ /* Convert to signed. */
+ if (sample->flags & IT_SAMPLE_16BIT)
+ for (n = 0; n < datasize; n++)
+ ((short *)sample->data)[n] ^= 0x8000;
+ else
+ for (n = 0; n < datasize; n++)
+ ((signed char *)sample->data)[n] ^= 0x80;
+ }
+
+ return 0;
+}
+
+
+
+static int it_s3m_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer, int maxlen)
+{
+ int length;
+ int buflen = 0;
+ int bufpos = 0;
+
+ IT_ENTRY *entry;
+
+ unsigned char channel;
+
+ /* Haha, this is hilarious!
+ *
+ * Well, after some experimentation, it seems that different S3M writers
+ * define the format in different ways. The S3M docs say that the first
+ * two bytes hold the "length of [the] packed pattern", and the packed
+ * pattern data follow. Judging by the contents of ARMANI.S3M, packaged
+ * with ScreamTracker itself, the measure of length _includes_ the two
+ * bytes used to store the length; in other words, we should read
+ * (length - 2) more bytes. However, aryx.s3m, packaged with ModPlug
+ * Tracker, excludes these two bytes, so (length) more bytes must be
+ * read.
+ *
+ * Call me crazy, but I just find it insanely funny that the format was
+ * misunderstood in this way :D
+ *
+ * Now we can't just risk reading two extra bytes, because then we
+ * overshoot, and DUMBFILEs don't support backward seeking (for a good
+ * reason). Luckily, there is a way. We can read the data little by
+ * little, and stop when we have 64 rows in memory. Provided we protect
+ * against buffer overflow, this method should work with all sensibly
+ * written S3M files. If you find one for which it does not work, please
+ * let me know at entheh@users.sf.net so I can look at it.
+ */
+
+ /* Discard the length. */
+ /* read at most length bytes, in case of retarded crap */
+ length = dumbfile_igetw(f);
+
+ if (maxlen)
+ {
+ maxlen -= 2;
+ if (length > maxlen) length = maxlen;
+ }
+
+ if (dumbfile_error(f) || !length)
+ return -1;
+
+ pattern->n_rows = 0;
+ pattern->n_entries = 0;
+
+ /* Read in the pattern data, little by little, and work out how many
+ * entries we need room for. Sorry, but this is just so funny...
+ */
+ for (;;) {
+ unsigned char b = buffer[buflen++] = dumbfile_getc(f);
+
+#if 1
+ static const unsigned char used[8] = {0, 2, 1, 3, 2, 4, 3, 5};
+ channel = b & 31;
+ b >>= 5;
+ pattern->n_entries++;
+ if (b) {
+ if (buflen + used[b] >= 65536) return -1;
+ if (buflen + used[b] <= length)
+ dumbfile_getnc(buffer + buflen, used[b], f);
+ else
+ memset(buffer + buflen, 0, used[b]);
+ buflen += used[b];
+ } else {
+ /* End of row */
+ if (++pattern->n_rows == 64) break;
+ if (buflen >= 65536) return -1;
+ }
+#else
+ if (b == 0) {
+ /* End of row */
+ pattern->n_entries++;
+ if (++pattern->n_rows == 64) break;
+ if (buflen >= 65536) return -1;
+ } else {
+ static const unsigned char used[8] = {0, 2, 1, 3, 2, 4, 3, 5};
+ channel = b & 31;
+ b >>= 5;
+ if (b) {
+ pattern->n_entries++;
+ if (buflen + used[b] >= 65536) return -1;
+ dumbfile_getnc(buffer + buflen, used[b], f);
+ buflen += used[b];
+ }
+ }
+#endif
+
+ /* We have ensured that buflen < 65536 at this point, so it is safe
+ * to iterate and read at least one more byte without checking.
+ * However, now would be a good time to check for errors reading from
+ * the file.
+ */
+
+ if (dumbfile_error(f))
+ return -1;
+
+ /* Great. We ran out of data, but there should be data for more rows.
+ * Fill the rest with null data...
+ */
+ if (buflen >= length && pattern->n_rows < 64)
+ {
+ while (pattern->n_rows < 64)
+ {
+ if (buflen >= 65536) return -1;
+ buffer[buflen++] = 0;
+ pattern->n_entries++;
+ pattern->n_rows++;
+ }
+ break;
+ }
+ }
+
+ pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry));
+
+ if (!pattern->entry)
+ return -1;
+
+ entry = pattern->entry;
+
+ while (bufpos < buflen) {
+ unsigned char b = buffer[bufpos++];
+
+#if 1
+ if (!(b & ~31))
+#else
+ if (b == 0)
+#endif
+ {
+ /* End of row */
+ IT_SET_END_ROW(entry);
+ entry++;
+ continue;
+ }
+
+ channel = b & 31;
+
+ if (b & 224) {
+ entry->mask = 0;
+ entry->channel = channel;
+
+ if (b & 32) {
+ unsigned char n = buffer[bufpos++];
+ if (n != 255) {
+ if (n == 254)
+ entry->note = IT_NOTE_CUT;
+ else
+ entry->note = (n >> 4) * 12 + (n & 15);
+ entry->mask |= IT_ENTRY_NOTE;
+ }
+
+ entry->instrument = buffer[bufpos++];
+ if (entry->instrument)
+ entry->mask |= IT_ENTRY_INSTRUMENT;
+ }
+
+ if (b & 64) {
+ entry->volpan = buffer[bufpos++];
+ if (entry->volpan != 255)
+ entry->mask |= IT_ENTRY_VOLPAN;
+ }
+
+ if (b & 128) {
+ entry->effect = buffer[bufpos++];
+ entry->effectvalue = buffer[bufpos++];
+ // XXX woot
+ if (entry->effect && entry->effect < IT_MIDI_MACRO /*!= 255*/) {
+ entry->mask |= IT_ENTRY_EFFECT;
+ switch (entry->effect) {
+ case IT_BREAK_TO_ROW:
+ entry->effectvalue -= (entry->effectvalue >> 4) * 6;
+ break;
+
+ case IT_SET_CHANNEL_VOLUME:
+ case IT_CHANNEL_VOLUME_SLIDE:
+ case IT_PANNING_SLIDE:
+ case IT_GLOBAL_VOLUME_SLIDE:
+ case IT_PANBRELLO:
+ case IT_MIDI_MACRO:
+ entry->mask &= ~IT_ENTRY_EFFECT;
+ break;
+
+ case IT_S:
+ switch (entry->effectvalue >> 4) {
+ case IT_S_SET_PANBRELLO_WAVEFORM:
+ case IT_S_FINE_PATTERN_DELAY:
+ case IT_S7:
+ case IT_S_SET_SURROUND_SOUND:
+ case IT_S_SET_MIDI_MACRO:
+ entry->mask &= ~IT_ENTRY_EFFECT;
+ break;
+ }
+ break;
+ }
+ }
+ /** WARNING: ARGH! CONVERT TEH EFFECTS!@~ */
+ }
+
+ entry++;
+ }
+ }
+
+ ASSERT(entry == pattern->entry + pattern->n_entries);
+
+ return 0;
+}
+
+
+
+/** WARNING: this is duplicated in itread.c - also bad practice to use the same struct name unless they are unified in a header */
+/* Currently we assume the sample data are stored after the sample headers in
+ * module files. This assumption may be unjustified; let me know if you have
+ * trouble.
+ */
+
+#define S3M_COMPONENT_INSTRUMENT 1
+#define S3M_COMPONENT_PATTERN 2
+#define S3M_COMPONENT_SAMPLE 3
+
+typedef struct S3M_COMPONENT
+{
+ unsigned char type;
+ unsigned char n;
+ long offset;
+ short sampfirst; /* component[sampfirst] = first sample data after this */
+ short sampnext; /* sampnext is used to create linked lists of sample data */
+}
+S3M_COMPONENT;
+
+
+
+static int s3m_component_compare(const void *e1, const void *e2)
+{
+ return ((const S3M_COMPONENT *)e1)->offset -
+ ((const S3M_COMPONENT *)e2)->offset;
+}
+
+
+
+static DUMB_IT_SIGDATA *it_s3m_load_sigdata(DUMBFILE *f, int * cwtv)
+{
+ DUMB_IT_SIGDATA *sigdata;
+
+ int flags, ffi;
+ int default_pan_present;
+
+ int master_volume;
+
+ unsigned char sample_pack[256];
+
+ S3M_COMPONENT *component;
+ int n_components = 0;
+
+ int n;
+
+ unsigned char *buffer;
+
+ sigdata = malloc(sizeof(*sigdata));
+ if (!sigdata) return NULL;
+
+ dumbfile_getnc(sigdata->name, 28, f);
+ sigdata->name[28] = 0;
+
+ n = dumbfile_getc(f);
+
+ if (n != 0x1A && n != 0) {
+ free(sigdata);
+ return NULL;
+ }
+
+ if (dumbfile_getc(f) != 16) {
+ free(sigdata);
+ return NULL;
+ }
+
+ dumbfile_skip(f, 2);
+
+ sigdata->song_message = NULL;
+ sigdata->order = NULL;
+ sigdata->instrument = NULL;
+ sigdata->sample = NULL;
+ sigdata->pattern = NULL;
+ sigdata->midi = NULL;
+ sigdata->checkpoint = NULL;
+
+ sigdata->n_orders = dumbfile_igetw(f);
+ sigdata->n_instruments = 0;
+ sigdata->n_samples = dumbfile_igetw(f);
+ sigdata->n_patterns = dumbfile_igetw(f);
+
+ if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_samples > 256 || sigdata->n_patterns > 256) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ sigdata->order = malloc(sigdata->n_orders);
+ if (!sigdata->order) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ if (sigdata->n_samples) {
+ sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
+ if (!sigdata->sample) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ for (n = 0; n < sigdata->n_samples; n++)
+ sigdata->sample[n].data = NULL;
+ }
+
+ if (sigdata->n_patterns) {
+ sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
+ if (!sigdata->pattern) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ for (n = 0; n < sigdata->n_patterns; n++)
+ sigdata->pattern[n].entry = NULL;
+ }
+
+ flags = dumbfile_igetw(f);
+
+ *cwtv = dumbfile_igetw(f);
+
+ if (*cwtv == 0x1300) {
+ /** WARNING: volume slides on every frame */
+ }
+
+ ffi = dumbfile_igetw(f);
+
+ /** WARNING: which ones? */
+ sigdata->flags = IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_AN_S3M;
+
+ if (dumbfile_mgetl(f) != DUMB_ID('S','C','R','M')) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ sigdata->global_volume = dumbfile_getc(f) << 1;
+ if ( !sigdata->global_volume || sigdata->global_volume > 128 ) sigdata->global_volume = 128;
+ sigdata->speed = dumbfile_getc(f);
+ if (sigdata->speed == 0) sigdata->speed = 6; // Should we? What about tempo?
+ sigdata->tempo = dumbfile_getc(f);
+ master_volume = dumbfile_getc(f); // 7 bits; +128 for stereo
+ //what do we do with master_volume? it's not the same as mixing volume...
+ sigdata->mixing_volume = 48;
+
+ if (master_volume & 128) sigdata->flags |= IT_STEREO;
+
+ /* Skip GUS Ultra Click Removal byte. */
+ dumbfile_getc(f);
+
+ default_pan_present = dumbfile_getc(f);
+
+ dumbfile_skip(f, 8);
+
+ /* Skip Special Custom Data Pointer. */
+ /** WARNING: investigate this? */
+ dumbfile_igetw(f);
+
+ sigdata->n_pchannels = 0;
+ /* Channel settings for 32 channels, 255=unused, +128=disabled */
+ {
+ int i;
+ for (i = 0; i < 32; i++) {
+ int c = dumbfile_getc(f);
+ if (!(c & (128 | 16))) { /* +128=disabled, +16=Adlib */
+ if (sigdata->n_pchannels < i + 1) sigdata->n_pchannels = i + 1;
+ sigdata->channel_volume[i] = 64;
+ sigdata->channel_pan[i] = c & 8 ? 12 : 3;
+ /** WARNING: ah, but it should be 7 for mono... */
+ } else {
+ /** WARNING: this could be improved if we support channel muting... */
+ sigdata->channel_volume[i] = 0;
+ sigdata->channel_pan[i] = 7;
+ }
+ }
+ }
+
+ /* Orders, byte each, length = sigdata->n_orders (should be even) */
+ dumbfile_getnc(sigdata->order, sigdata->n_orders, f);
+ sigdata->restart_position = 0;
+
+ component = malloc(768*sizeof(*component));
+ if (!component) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ for (n = 0; n < sigdata->n_samples; n++) {
+ component[n_components].type = S3M_COMPONENT_SAMPLE;
+ component[n_components].n = n;
+ component[n_components].offset = dumbfile_igetw(f) << 4;
+ component[n_components].sampfirst = -1;
+ n_components++;
+ }
+
+ for (n = 0; n < sigdata->n_patterns; n++) {
+ long offset = dumbfile_igetw(f) << 4;
+ if (offset) {
+ component[n_components].type = S3M_COMPONENT_PATTERN;
+ component[n_components].n = n;
+ component[n_components].offset = offset;
+ component[n_components].sampfirst = -1;
+ n_components++;
+ } else {
+ /** WARNING: Empty 64-row pattern ... ? (this does happen!) */
+ sigdata->pattern[n].n_rows = 64;
+ sigdata->pattern[n].n_entries = 0;
+ }
+ }
+
+ qsort(component, n_components, sizeof(S3M_COMPONENT), &s3m_component_compare);
+
+ /* I found a really dumb S3M file that claimed to contain default pan
+ * data but didn't contain any. Programs would load it by reading part of
+ * the first instrument header, assuming the data to be default pan
+ * positions, and then rereading the instrument module. We cannot do this
+ * without obfuscating the file input model, so we insert an extra check
+ * here that we won't overrun the start of the first component.
+ */
+ if (default_pan_present == 252 && component[0].offset >= dumbfile_pos(f) + 32) {
+ /* Channel default pan positions */
+ int i;
+ for (i = 0; i < 32; i++) {
+ int c = dumbfile_getc(f);
+ if (c & 32)
+ sigdata->channel_pan[i] = c & 15;
+ }
+ }
+
+ {
+ int i;
+ for (i = 0; i < 32; i++) {
+ sigdata->channel_pan[i] -= (sigdata->channel_pan[i] & 8) >> 3;
+ sigdata->channel_pan[i] = ((int)sigdata->channel_pan[i] << 5) / 7;
+ }
+ }
+
+ sigdata->pan_separation = 128;
+
+ if (dumbfile_error(f)) {
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ buffer = malloc(65536);
+ if (!buffer) {
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ /* Voila, I must deal with a very dumb S3M myself. This file refers to the same file offset twice
+ * for two different patterns. Solution: Eliminate it.
+ */
+
+ for (n = 0; n < n_components; n++) {
+ if (component[n].type == S3M_COMPONENT_PATTERN) {
+ int m;
+ for (m = n + 1; m < n_components; m++) {
+ if (component[m].type == S3M_COMPONENT_PATTERN) {
+ if (component[n].offset == component[m].offset) {
+ int o, pattern;
+ pattern = component[m].n;
+ n_components--;
+ for (o = m; o < n_components; o++) {
+ component[o] = component[o + 1];
+ }
+ for (o = 0; o < sigdata->n_orders; o++) {
+ if (sigdata->order[o] == pattern) {
+ sigdata->order[o] = component[n].n;
+ }
+ }
+ sigdata->pattern[pattern].n_rows = 64;
+ sigdata->pattern[pattern].n_entries = 0;
+ m--;
+ } else
+ break;
+ }
+ }
+ }
+ }
+
+ for (n = 0; n < n_components; n++) {
+ long offset;
+ int m;
+#ifdef S3M_BROKEN_OVERLAPPED_SAMPLES
+ int last;
+#endif
+
+ if (it_seek(f, component[n].offset)) {
+ free(buffer);
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ switch (component[n].type) {
+
+ case S3M_COMPONENT_PATTERN:
+ if (it_s3m_read_pattern(&sigdata->pattern[component[n].n], f, buffer, (n + 1 < n_components) ? (component[n+1].offset - component[n].offset) : 0)) {
+ free(buffer);
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ break;
+
+ case S3M_COMPONENT_SAMPLE:
+ {
+ int err = it_s3m_read_sample_header(&sigdata->sample[component[n].n], &offset, &sample_pack[component[n].n], *cwtv, f);
+ if (err) {
+ free(buffer);
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ }
+
+ if (sigdata->sample[component[n].n].flags & IT_SAMPLE_EXISTS) {
+ short *sample;
+
+ for (m = n + 1; m < n_components; m++)
+ if (component[m].offset > offset)
+ break;
+ m--;
+
+ sample = &component[m].sampfirst;
+
+ while (*sample >= 0 && component[*sample].offset <= offset)
+ sample = &component[*sample].sampnext;
+
+ component[n].sampnext = *sample;
+ *sample = n;
+
+ component[n].offset = offset;
+ }
+ }
+
+ m = component[n].sampfirst;
+
+#ifdef S3M_BROKEN_OVERLAPPED_SAMPLES
+ last = -1;
+#endif
+
+ while (m >= 0) {
+ // XXX
+#ifdef S3M_BROKEN_OVERLAPPED_SAMPLES
+ if ( last >= 0 ) {
+ if ( dumbfile_pos( f ) > component[m].offset ) {
+ IT_SAMPLE * s1 = &sigdata->sample[component[last].n];
+ IT_SAMPLE * s2 = &sigdata->sample[component[m].n];
+ if ( ( s1->flags | s2->flags ) & ( IT_SAMPLE_16BIT | IT_SAMPLE_STEREO ) ) {
+ free(buffer);
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ if ( component[m].offset >= component[last].offset &&
+ component[m].offset + s2->length <= component[last].offset + s1->length ) {
+ s2->left = malloc( s2->length );
+ if ( ! s2->left ) {
+ free(buffer);
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ memcpy( s2->left, ( const char * ) s1->left + component[m].offset - component[last].offset, s2->length );
+ last = -1;
+ }
+ }
+ } else last = 0;
+
+ if ( last >= 0 ) {
+#endif
+ if (it_seek(f, component[m].offset)) {
+ free(buffer);
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ if (it_s3m_read_sample_data(&sigdata->sample[component[m].n], ffi, sample_pack[component[m].n], f)) {
+ free(buffer);
+ free(component);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+#ifdef S3M_BROKEN_OVERLAPPED_SAMPLES
+ last = m;
+ }
+#endif
+
+ m = component[m].sampnext;
+ }
+ }
+
+ free(buffer);
+ free(component);
+
+ _dumb_it_fix_invalid_orders(sigdata);
+
+ return sigdata;
+}
+
+static char hexdigit(int in)
+{
+ if (in < 10) return in + '0';
+ else return in + 'A' - 10;
+}
+
+DUH *dumb_read_s3m_quick(DUMBFILE *f)
+{
+ sigdata_t *sigdata;
+ int cwtv;
+
+ DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+ sigdata = it_s3m_load_sigdata(f, &cwtv);
+
+ if (!sigdata)
+ return NULL;
+
+ {
+ char version[8];
+ const char *tag[3][2];
+ tag[0][0] = "TITLE";
+ tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+ tag[1][0] = "FORMAT";
+ tag[1][1] = "S3M";
+ tag[2][0] = "TRACKERVERSION";
+ version[0] = hexdigit((cwtv >> 8) & 15);
+ version[1] = '.';
+ version[2] = hexdigit((cwtv >> 4) & 15);
+ version[3] = hexdigit(cwtv & 15);
+ version[4] = 0;
+ tag[2][1] = (const char *) &version;
+ return make_duh(-1, 3, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
+ }
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/reads3m2.c b/plugins/dumb/dumb-kode54/src/it/reads3m2.c
new file mode 100644
index 00000000..0499eecd
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/reads3m2.c
@@ -0,0 +1,29 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * reads3m2.c - Function to read a ScreamTracker 3 / / \ \
+ * module from an open file and do an | < / \_
+ * initial run-through. | \/ /\ /
+ * \_ / > /
+ * Split off from reads3m.c by entheh. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+
+
+
+DUH *dumb_read_s3m(DUMBFILE *f)
+{
+ DUH *duh = dumb_read_s3m_quick(f);
+ dumb_it_do_initial_runthrough(duh);
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/readstm.c b/plugins/dumb/dumb-kode54/src/it/readstm.c
new file mode 100644
index 00000000..5972a0f7
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/readstm.c
@@ -0,0 +1,397 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * readstm.c - Code to read a ScreamTracker 2 / / \ \
+ * module from an open file. | < / \_
+ * | \/ /\ /
+ * By Chris Moeller. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+// IT_STEREO... :o
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+/** WARNING: this is duplicated in itread.c */
+static int it_seek(DUMBFILE *f, long offset)
+{
+ long pos = dumbfile_pos(f);
+
+ if (pos > offset) {
+ return -1;
+ }
+
+ if (pos < offset)
+ if (dumbfile_skip(f, offset - pos))
+ return -1;
+
+ return 0;
+}
+
+static int it_stm_read_sample_header( IT_SAMPLE *sample, DUMBFILE *f )
+{
+ dumbfile_getnc( sample->filename, 12, f );
+ sample->filename[12] = 0;
+
+ memcpy( sample->name, sample->filename, 13 );
+
+ dumbfile_skip( f, 2 + 2 );
+
+ sample->length = dumbfile_igetw( f );
+ sample->loop_start = dumbfile_igetw( f );
+ sample->loop_end = dumbfile_igetw( f );
+
+ sample->default_volume = dumbfile_getc( f );
+
+ dumbfile_skip( f, 1 );
+
+ sample->C5_speed = dumbfile_igetw( f ) << 3;
+
+ dumbfile_skip( f, 6 );
+
+ if ( sample->length < 4 || !sample->default_volume ) {
+ /* Looks like no-existy. */
+ sample->flags &= ~IT_SAMPLE_EXISTS;
+ sample->length = 0;
+ return dumbfile_error( f );
+ }
+
+ sample->flags = IT_SAMPLE_EXISTS;
+ sample->global_volume = 64;
+ sample->default_pan = 0; // 0 = don't use, or 160 = centre?
+
+ if ( ( sample->loop_start < sample->length ) &&
+ ( sample->loop_end > sample->loop_start ) &&
+ ( sample->loop_end != 0xFFFF ) ) {
+ sample->flags |= IT_SAMPLE_LOOP;
+ if ( sample->loop_end > sample->length ) sample->loop_end = sample->length;
+ }
+
+ //Do we need to set all these?
+ sample->vibrato_speed = 0;
+ sample->vibrato_depth = 0;
+ sample->vibrato_rate = 0;
+ sample->vibrato_waveform = IT_VIBRATO_SINE;
+ sample->finetune = 0;
+ sample->max_resampling_quality = -1;
+
+ return dumbfile_error(f);
+}
+
+static int it_stm_read_sample_data( IT_SAMPLE *sample, DUMBFILE *f )
+{
+ long n;
+
+ if ( ! sample->length ) return 0;
+
+ n = dumbfile_pos( f );
+ if ( n & 15 ) {
+ if ( dumbfile_skip( f, 16 - ( n & 15 ) ) )
+ return -1;
+ }
+
+ sample->data = malloc( sample->length );
+ if (!sample->data)
+ return -1;
+
+ if ( dumbfile_getnc( sample->data, sample->length, f ) != sample->length )
+ return -1;
+
+ return 0;
+}
+
+static int it_stm_read_pattern( IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer )
+{
+ int pos;
+ int channel;
+ int row;
+ IT_ENTRY *entry;
+
+ pattern->n_rows = 64;
+
+ if ( dumbfile_getnc( buffer, 64 * 4 * 4, f ) != 64 * 4 * 4 )
+ return -1;
+
+ pattern->n_entries = 64;
+ pos = 0;
+ for ( row = 0; row < 64; ++row ) {
+ for ( channel = 0; channel < 4; ++channel ) {
+ if ( buffer[ pos + 0 ] | buffer[ pos + 1 ] | buffer[ pos + 2 ] | buffer[ pos + 3 ] )
+ ++pattern->n_entries;
+ pos += 4;
+ }
+ }
+
+ pattern->entry = malloc( pattern->n_entries * sizeof( *pattern->entry ) );
+ if ( !pattern->entry )
+ return -1;
+
+ entry = pattern->entry;
+ pos = 0;
+ for ( row = 0; row < 64; ++row ) {
+ for ( channel = 0; channel < 4; ++channel ) {
+ if ( buffer[ pos + 0 ] | buffer[ pos + 1 ] | buffer[ pos + 2 ] | buffer[ pos + 3 ] ) {
+ unsigned note;
+ note = buffer[ pos + 0 ];
+ entry->channel = channel;
+ entry->mask = 0;
+ entry->instrument = buffer[ pos + 1 ] >> 3;
+ entry->volpan = ( buffer[ pos + 1 ] & 0x07 ) + ( buffer[ pos + 2 ] >> 1 );
+ entry->effect = buffer[ pos + 2 ] & 0x0F;
+ entry->effectvalue = buffer[ pos + 3 ];
+ if ( entry->instrument && entry->instrument < 32 )
+ entry->mask |= IT_ENTRY_INSTRUMENT;
+ if ( note == 0xFC || note == 0xFE ) {
+ entry->mask |= IT_ENTRY_NOTE;
+ entry->note = IT_NOTE_CUT;
+ }
+ if ( note < 251 ) {
+ entry->mask |= IT_ENTRY_NOTE;
+ entry->note = ( note >> 4 ) * 12 + ( note & 0x0F );
+ }
+ if ( entry->volpan <= 64 )
+ entry->mask |= IT_ENTRY_VOLPAN;
+ entry->mask |= IT_ENTRY_EFFECT;
+ switch ( entry->effect ) {
+ case IT_SET_SPEED:
+ entry->effectvalue >>= 4;
+ break;
+
+ case IT_BREAK_TO_ROW:
+ entry->effectvalue -= (entry->effectvalue >> 4) * 6;
+ break;
+
+ case IT_JUMP_TO_ORDER:
+ case IT_VOLUME_SLIDE:
+ case IT_PORTAMENTO_DOWN:
+ case IT_PORTAMENTO_UP:
+ case IT_TONE_PORTAMENTO:
+ case IT_VIBRATO:
+ case IT_TREMOR:
+ case IT_ARPEGGIO:
+ case IT_VOLSLIDE_VIBRATO:
+ case IT_VOLSLIDE_TONEPORTA:
+ break;
+
+ default:
+ entry->mask &= ~IT_ENTRY_EFFECT;
+ break;
+ }
+ if ( entry->mask ) ++entry;
+ }
+ pos += 4;
+ }
+ IT_SET_END_ROW(entry);
+ ++entry;
+ }
+
+ pattern->n_entries = entry - pattern->entry;
+
+ return 0;
+}
+
+
+
+static DUMB_IT_SIGDATA *it_stm_load_sigdata(DUMBFILE *f /*, int * version*/)
+{
+ DUMB_IT_SIGDATA *sigdata;
+
+ char tracker_name[ 8 ];
+
+ int n;
+
+ sigdata = malloc(sizeof(*sigdata));
+ if (!sigdata) return NULL;
+
+ /* Skip song name. */
+ dumbfile_getnc(sigdata->name, 20, f);
+ sigdata->name[20] = 0;
+
+ dumbfile_getnc(tracker_name, 8, f);
+ n = dumbfile_getc(f);
+ if ( n != 0x02 && n != 0x1A && n != 0x1B )
+ {
+ free( sigdata );
+ return NULL;
+ }
+ if ( dumbfile_getc(f) != 2 ) /* only support modules */
+ {
+ free( sigdata );
+ return NULL;
+ }
+ if ( strncasecmp( tracker_name, "!Scream!", 8 ) &&
+ strncasecmp( tracker_name, "BMOD2STM", 8 ) &&
+ strncasecmp( tracker_name, "WUZAMOD!", 8 ) )
+ {
+ free( sigdata );
+ return NULL;
+ }
+
+ /* *version = dumbfile_mgetw(f); */
+ dumbfile_skip( f, 2 );
+
+ sigdata->song_message = NULL;
+ sigdata->order = NULL;
+ sigdata->instrument = NULL;
+ sigdata->sample = NULL;
+ sigdata->pattern = NULL;
+ sigdata->midi = NULL;
+ sigdata->checkpoint = NULL;
+
+ sigdata->n_instruments = 0;
+ sigdata->n_samples = 31;
+ sigdata->n_pchannels = 4;
+
+ sigdata->tempo = 125;
+ sigdata->mixing_volume = 48;
+ sigdata->pan_separation = 128;
+
+ /** WARNING: which ones? */
+ sigdata->flags = IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_AN_S3M;
+
+ sigdata->speed = dumbfile_getc(f) >> 4;
+ if ( sigdata->speed < 1 ) sigdata->speed = 1;
+ sigdata->n_patterns = dumbfile_getc(f);
+ sigdata->global_volume = dumbfile_getc(f) << 1;
+ if ( sigdata->global_volume > 128 ) sigdata->global_volume = 128;
+
+ dumbfile_skip(f, 13);
+
+ if ( dumbfile_error(f) || sigdata->n_patterns < 1 || sigdata->n_patterns > 99 ) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
+ if (!sigdata->sample) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ for (n = 0; n < sigdata->n_samples; n++)
+ sigdata->sample[n].data = NULL;
+
+ if (sigdata->n_patterns) {
+ sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
+ if (!sigdata->pattern) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ for (n = 0; n < sigdata->n_patterns; n++)
+ sigdata->pattern[n].entry = NULL;
+ }
+
+ memset( sigdata->channel_volume, 64, 4 );
+ sigdata->channel_pan[ 0 ] = 48;
+ sigdata->channel_pan[ 1 ] = 16;
+ sigdata->channel_pan[ 2 ] = 48;
+ sigdata->channel_pan[ 3 ] = 16;
+
+ for ( n = 0; n < sigdata->n_samples; ++n ) {
+ if ( it_stm_read_sample_header( &sigdata->sample[ n ], f ) ) {
+ _dumb_it_unload_sigdata( sigdata );
+ return NULL;
+ }
+ }
+
+ sigdata->order = malloc( 128 );
+ if ( !sigdata->order ) {
+ _dumb_it_unload_sigdata( sigdata );
+ return NULL;
+ }
+
+ /* Orders, byte each, length = sigdata->n_orders (should be even) */
+ dumbfile_getnc( sigdata->order, 128, f );
+ sigdata->restart_position = 0;
+
+ for ( n = 127; n >= 0; --n ) {
+ if ( sigdata->order[ n ] < sigdata->n_patterns ) break;
+ }
+ if ( n < 0 ) {
+ _dumb_it_unload_sigdata( sigdata );
+ return NULL;
+ }
+ sigdata->n_orders = n + 1;
+
+ for ( n = 0; n < 128; ++n ) {
+ if ( sigdata->order[ n ] >= 99 ) sigdata->order[ n ] = 0xFF;
+ }
+
+ if ( sigdata->n_patterns ) {
+ unsigned char * buffer = malloc( 64 * 4 * 4 );
+ if ( ! buffer ) {
+ _dumb_it_unload_sigdata( sigdata );
+ return NULL;
+ }
+ for ( n = 0; n < sigdata->n_patterns; ++n ) {
+ if ( it_stm_read_pattern( &sigdata->pattern[ n ], f, buffer ) ) {
+ free( buffer );
+ _dumb_it_unload_sigdata( sigdata );
+ return NULL;
+ }
+ }
+ free( buffer );
+ }
+
+ for ( n = 0; n < sigdata->n_samples; ++n ) {
+ if ( it_stm_read_sample_data( &sigdata->sample[ n ], f ) ) {
+ _dumb_it_unload_sigdata( sigdata );
+ return NULL;
+ }
+ }
+
+ _dumb_it_fix_invalid_orders(sigdata);
+
+ return sigdata;
+}
+
+/*static char hexdigit(int in)
+{
+ if (in < 10) return in + '0';
+ else return in + 'A' - 10;
+}*/
+
+DUH *dumb_read_stm_quick(DUMBFILE *f)
+{
+ sigdata_t *sigdata;
+ /*int ver;*/
+
+ DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+ sigdata = it_stm_load_sigdata(f /*, &ver*/);
+
+ if (!sigdata)
+ return NULL;
+
+ {
+ /*char version[16];*/
+ const char *tag[2][2];
+ tag[0][0] = "TITLE";
+ tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+ tag[1][0] = "FORMAT";
+ tag[1][1] = "STM";
+ /*version[0] = 'S';
+ version[1] = 'T';
+ version[2] = 'M';
+ version[3] = ' ';
+ version[4] = 'v';
+ version[5] = hexdigit((ver >> 8) & 15);
+ version[6] = '.';
+ version[7] = hexdigit((ver >> 4) & 15);
+ version[8] = hexdigit(ver & 15);
+ version[9] = 0;
+ tag[1][1] = (const char *) &version;*/
+ return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
+ }
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/readstm2.c b/plugins/dumb/dumb-kode54/src/it/readstm2.c
new file mode 100644
index 00000000..c336b2a3
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/readstm2.c
@@ -0,0 +1,29 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * readstm2.c - Function to read a ScreamTracker 2 / / \ \
+ * module from an open file and do an | < / \_
+ * initial run-through. | \/ /\ /
+ * \_ / > /
+ * By Chris Moeller. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+
+
+
+DUH *dumb_read_stm(DUMBFILE *f)
+{
+ DUH *duh = dumb_read_stm_quick(f);
+ dumb_it_do_initial_runthrough(duh);
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/readxm.c b/plugins/dumb/dumb-kode54/src/it/readxm.c
new file mode 100644
index 00000000..c76d0f3e
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/readxm.c
@@ -0,0 +1,1210 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * readxm.c - Code to read a Fast Tracker II / / \ \
+ * module from an open file. | < / \_
+ * | \/ /\ /
+ * By Julien Cugniere. Some bits of code taken \_ / > /
+ * from reads3m.c. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/** TODO:
+
+ * XM_TREMOLO doesn't sound quite right...
+ * XM_SET_ENVELOPE_POSITION todo.
+
+ * VIBRATO conversion needs to be checked (sample/effect/volume). Plus check
+ that effect memory is correct when using XM_VOLSLIDE_VIBRATO.
+ - sample vibrato (instrument vibrato) is now handled correctly. - entheh
+
+ * XM_E_SET_VIBRATO/TREMOLO_CONTROL: effectvalue&4 -> don't retrig wave when
+ a new instrument is played. In retrigger_note()?. Is it worth implementing?
+
+ * Lossy fadeout approximation. 0..31 converted to 0 --> won't fade at all.
+
+ * Replace DUMB's sawtooth by ramp_down/ramp_up. Update XM loader.
+
+ * A lot of things need to be reset when the end of the song is reached.
+
+ * It seems that IT and XM don't behave the same way when dealing with
+ mixed loops. When IT encounters multiple SBx (x>0) commands on the same
+ row, it decrements the loop count for all, but only execute the loop of
+ the last one (highest channel). FT2 only decrements the loop count of the
+ last one. Not that I know of any modules using so convoluted combinations!
+
+ * Maybe we could remove patterns that don't appear in the order table ? Or
+ provide a function to "optimize" a DUMB_IT_SIGDATA ?
+
+*/
+
+
+
+#define XM_LINEAR_FREQUENCY 1 /* otherwise, use amiga slides */
+
+#define XM_ENTRY_PACKED 128
+#define XM_ENTRY_NOTE 1
+#define XM_ENTRY_INSTRUMENT 2
+#define XM_ENTRY_VOLUME 4
+#define XM_ENTRY_EFFECT 8
+#define XM_ENTRY_EFFECTVALUE 16
+
+#define XM_NOTE_OFF 97
+
+#define XM_ENVELOPE_ON 1
+#define XM_ENVELOPE_SUSTAIN 2
+#define XM_ENVELOPE_LOOP 4
+
+#define XM_SAMPLE_NO_LOOP 0
+#define XM_SAMPLE_FORWARD_LOOP 1
+#define XM_SAMPLE_PINGPONG_LOOP 2
+#define XM_SAMPLE_16BIT 16
+#define XM_SAMPLE_STEREO 32
+
+#define XM_VIBRATO_SINE 0
+#define XM_VIBRATO_SQUARE 1
+#define XM_VIBRATO_RAMP_DOWN 2
+#define XM_VIBRATO_RAMP_UP 3
+
+
+
+/* Probably useless :) */
+const char xm_convert_vibrato[] = {
+ IT_VIBRATO_SINE,
+ IT_VIBRATO_XM_SQUARE,
+ IT_VIBRATO_RAMP_DOWN,
+ IT_VIBRATO_RAMP_UP,
+ IT_VIBRATO_RANDOM
+};
+
+
+
+#define XM_MAX_SAMPLES_PER_INSTRUMENT 16
+
+
+
+/* Extra data that doesn't fit inside IT_INSTRUMENT */
+typedef struct XM_INSTRUMENT_EXTRA
+{
+ int n_samples;
+ int vibrato_type;
+ int vibrato_sweep; /* 0-0xFF */
+ int vibrato_depth; /* 0-0x0F */
+ int vibrato_speed; /* 0-0x3F */
+}
+XM_INSTRUMENT_EXTRA;
+
+
+
+/* Frees the original block if it can't resize it or if size is 0, and acts
+ * as malloc if ptr is NULL.
+ */
+static void *safe_realloc(void *ptr, size_t size)
+{
+ if (ptr == NULL)
+ return malloc(size);
+
+ if (size == 0) {
+ free(ptr);
+ return NULL;
+ } else {
+ void *new_block = realloc(ptr, size);
+ if (!new_block)
+ free(ptr);
+ return new_block;
+ }
+}
+
+
+
+/* The interpretation of the XM volume column is left to the player. Here, we
+ * just filter bad values.
+ */
+// This function is so tiny now, should we inline it?
+static void it_xm_convert_volume(int volume, IT_ENTRY *entry)
+{
+ entry->mask |= IT_ENTRY_VOLPAN;
+ entry->volpan = volume;
+
+ switch (volume >> 4) {
+ case 0xA: /* set vibrato speed */
+ case 0xB: /* vibrato */
+ case 0xF: /* tone porta */
+ case 0x6: /* vol slide up */
+ case 0x7: /* vol slide down */
+ case 0x8: /* fine vol slide up */
+ case 0x9: /* fine vol slide down */
+ case 0xC: /* set panning */
+ case 0xD: /* pan slide left */
+ case 0xE: /* pan slide right */
+ case 0x1: /* set volume */
+ case 0x2: /* set volume */
+ case 0x3: /* set volume */
+ case 0x4: /* set volume */
+ break;
+
+ case 0x5:
+ if (volume == 0x50)
+ break; /* set volume */
+ /* else fall through */
+
+ default:
+ entry->mask &= ~IT_ENTRY_VOLPAN;
+ break;
+ }
+}
+
+
+
+static int it_xm_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, int n_channels, unsigned char *buffer, int version)
+{
+ int size;
+ int pos;
+ int channel;
+ int row;
+ int effect, effectvalue;
+ IT_ENTRY *entry;
+
+ /* pattern header size */
+ if (dumbfile_igetl(f) != ( version == 0x0102 ? 0x08 : 0x09 ) ) {
+ TRACE("XM error: unexpected pattern header size\n");
+ return -1;
+ }
+
+ /* pattern data packing type */
+ if (dumbfile_getc(f) != 0) {
+ TRACE("XM error: unexpected pattern packing type\n");
+ return -1;
+ }
+
+ if ( version == 0x0102 )
+ pattern->n_rows = dumbfile_getc(f) + 1;
+ else
+ pattern->n_rows = dumbfile_igetw(f); /* 1..256 */
+ size = dumbfile_igetw(f);
+ pattern->n_entries = 0;
+
+ if (dumbfile_error(f))
+ return -1;
+
+ if (size == 0)
+ return 0;
+
+ if (size > 1280 * n_channels) {
+ TRACE("XM error: pattern data size > %d bytes\n", 1280 * n_channels);
+ return -1;
+ }
+
+ if (dumbfile_getnc(buffer, size, f) < size)
+ return -1;
+
+ /* compute number of entries */
+ pattern->n_entries = 0;
+ pos = channel = row = 0;
+ while (pos < size) {
+ if (!(buffer[pos] & XM_ENTRY_PACKED) || (buffer[pos] & 31))
+ pattern->n_entries++;
+
+ channel++;
+ if (channel >= n_channels) {
+ channel = 0;
+ row++;
+ pattern->n_entries++;
+ }
+
+ if (buffer[pos] & XM_ENTRY_PACKED) {
+ static const char offset[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5 };
+ pos += 1 + offset[buffer[pos] & 31];
+ } else {
+ pos += 5;
+ }
+ }
+
+ if (row > pattern->n_rows) {
+ TRACE("XM error: wrong number of rows in pattern data\n");
+ return -1;
+ }
+
+ /* Whoops, looks like some modules may be short, a few channels, maybe even rows... */
+
+ while (row < pattern->n_rows)
+ {
+ pattern->n_entries++;
+ row++;
+ }
+
+ pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry));
+ if (!pattern->entry)
+ return -1;
+
+ /* read the entries */
+ entry = pattern->entry;
+ pos = channel = row = 0;
+ while (pos < size) {
+ unsigned char mask;
+
+ if (buffer[pos] & XM_ENTRY_PACKED)
+ mask = buffer[pos++] & 31;
+ else
+ mask = 31;
+
+ if (mask) {
+ ASSERT(entry < pattern->entry + pattern->n_entries);
+
+ entry->channel = channel;
+ entry->mask = 0;
+
+ if (mask & XM_ENTRY_NOTE) {
+ int note = buffer[pos++]; /* 1-96 <=> C0-B7 */
+ entry->note = (note == XM_NOTE_OFF) ? (IT_NOTE_OFF) : (note-1);
+ entry->mask |= IT_ENTRY_NOTE;
+ }
+
+ if (mask & XM_ENTRY_INSTRUMENT) {
+ entry->instrument = buffer[pos++]; /* 1-128 */
+ entry->mask |= IT_ENTRY_INSTRUMENT;
+ }
+
+ if (mask & XM_ENTRY_VOLUME)
+ it_xm_convert_volume(buffer[pos++], entry);
+
+ effect = effectvalue = 0;
+ if (mask & XM_ENTRY_EFFECT) effect = buffer[pos++];
+ if (mask & XM_ENTRY_EFFECTVALUE) effectvalue = buffer[pos++];
+ _dumb_it_xm_convert_effect(effect, effectvalue, entry, 0);
+
+ entry++;
+ }
+
+ channel++;
+ if (channel >= n_channels) {
+ channel = 0;
+ row++;
+ IT_SET_END_ROW(entry);
+ entry++;
+ }
+ }
+
+ while (row < pattern->n_rows)
+ {
+ row++;
+ IT_SET_END_ROW(entry);
+ entry++;
+ }
+
+ return 0;
+}
+
+
+
+static int it_xm_make_envelope(IT_ENVELOPE *envelope, const unsigned short *data, int y_offset)
+{
+ int i, pos;
+
+ if (envelope->n_nodes > 12) {
+ /* XXX
+ TRACE("XM error: wrong number of envelope nodes (%d)\n", envelope->n_nodes);
+ envelope->n_nodes = 0;
+ return -1; */
+ envelope->n_nodes = 12;
+ }
+
+ if (envelope->sus_loop_start >= 12) envelope->flags &= ~IT_ENVELOPE_SUSTAIN_LOOP;
+ if (envelope->loop_end >= 12) envelope->loop_end = 0;
+ if (envelope->loop_start >= envelope->loop_end) envelope->flags &= ~IT_ENVELOPE_LOOP_ON;
+
+ pos = 0;
+ for (i = 0; i < envelope->n_nodes; i++) {
+ envelope->node_t[i] = data[pos++];
+ if (data[pos] > 64) {
+ TRACE("XM error: out-of-range envelope node (node_y[%d]=%d)\n", i, data[pos]);
+ envelope->n_nodes = 0;
+ return -1;
+ }
+ envelope->node_y[i] = (signed char)(data[pos++] + y_offset);
+ }
+
+ return 0;
+}
+
+
+
+static int it_xm_read_instrument(IT_INSTRUMENT *instrument, XM_INSTRUMENT_EXTRA *extra, DUMBFILE *f)
+{
+ unsigned long size, bytes_read;
+ unsigned short vol_points[24];
+ unsigned short pan_points[24];
+ int i, type;
+
+ /* Header size. Tends to be more than the actual size of the structure.
+ * So unread bytes must be skipped before reading the first sample
+ * header.
+ */
+ size = dumbfile_igetl(f);
+
+ dumbfile_getnc(instrument->name, 22, f);
+ instrument->name[22] = 0;
+ instrument->filename[0] = 0;
+ dumbfile_skip(f, 1); /* Instrument type. Should be 0, but seems random. */
+ extra->n_samples = dumbfile_igetw(f);
+
+ if (dumbfile_error(f) || (unsigned int)extra->n_samples > XM_MAX_SAMPLES_PER_INSTRUMENT)
+ return -1;
+
+ bytes_read = 4 + 22 + 1 + 2;
+
+ if (extra->n_samples) {
+ /* sample header size */
+ dumbfile_skip(f, 4); // XXX can't be trusted, as there are trackers that write the wrong value here
+ /*i = dumbfile_igetl(f);
+ if (i && i != 0x28) { // XXX some crap with 0 here
+ TRACE("XM error: unexpected sample header size\n");
+ return -1;
+ }*/
+
+ /* sample map */
+ for (i = 0; i < 96; i++) {
+ instrument->map_sample[i] = dumbfile_getc(f) + 1;
+ instrument->map_note[i] = i;
+ }
+
+ if (dumbfile_error(f))
+ return 1;
+
+ /* volume/panning envelopes */
+ for (i = 0; i < 24; i++)
+ vol_points[i] = dumbfile_igetw(f);
+ for (i = 0; i < 24; i++)
+ pan_points[i] = dumbfile_igetw(f);
+
+ instrument->volume_envelope.n_nodes = dumbfile_getc(f);
+ instrument->pan_envelope.n_nodes = dumbfile_getc(f);
+
+ if (dumbfile_error(f))
+ return -1;
+
+ instrument->volume_envelope.sus_loop_start = dumbfile_getc(f);
+ instrument->volume_envelope.loop_start = dumbfile_getc(f);
+ instrument->volume_envelope.loop_end = dumbfile_getc(f);
+
+ instrument->pan_envelope.sus_loop_start = dumbfile_getc(f);
+ instrument->pan_envelope.loop_start = dumbfile_getc(f);
+ instrument->pan_envelope.loop_end = dumbfile_getc(f);
+
+ /* The envelope handler for XM files won't use sus_loop_end. */
+
+ type = dumbfile_getc(f);
+ instrument->volume_envelope.flags = 0;
+ if ((type & XM_ENVELOPE_ON) && instrument->volume_envelope.n_nodes)
+ instrument->volume_envelope.flags |= IT_ENVELOPE_ON;
+ if (type & XM_ENVELOPE_LOOP) instrument->volume_envelope.flags |= IT_ENVELOPE_LOOP_ON;
+#if 1
+ if (type & XM_ENVELOPE_SUSTAIN) instrument->volume_envelope.flags |= IT_ENVELOPE_SUSTAIN_LOOP;
+#else // This is now handled in itrender.c
+ /* let's avoid fading out when reaching the last envelope node */
+ if (!(type & XM_ENVELOPE_LOOP)) {
+ instrument->volume_envelope.loop_start = instrument->volume_envelope.n_nodes-1;
+ instrument->volume_envelope.loop_end = instrument->volume_envelope.n_nodes-1;
+ }
+ instrument->volume_envelope.flags |= IT_ENVELOPE_LOOP_ON;
+#endif
+
+ type = dumbfile_getc(f);
+ instrument->pan_envelope.flags = 0;
+ if ((type & XM_ENVELOPE_ON) && instrument->pan_envelope.n_nodes)
+ instrument->pan_envelope.flags |= IT_ENVELOPE_ON;
+ if (type & XM_ENVELOPE_LOOP) instrument->pan_envelope.flags |= IT_ENVELOPE_LOOP_ON; // should this be here?
+ if (type & XM_ENVELOPE_SUSTAIN) instrument->pan_envelope.flags |= IT_ENVELOPE_SUSTAIN_LOOP;
+
+ if (it_xm_make_envelope(&instrument->volume_envelope, vol_points, 0) != 0) {
+ TRACE("XM error: volume envelope\n");
+ if (instrument->volume_envelope.flags & IT_ENVELOPE_ON) return -1;
+ }
+
+ if (it_xm_make_envelope(&instrument->pan_envelope, pan_points, -32) != 0) {
+ TRACE("XM error: pan envelope\n");
+ if (instrument->pan_envelope.flags & IT_ENVELOPE_ON) return -1;
+ }
+
+ instrument->pitch_envelope.flags = 0;
+
+ extra->vibrato_type = dumbfile_getc(f);
+ extra->vibrato_sweep = dumbfile_getc(f);
+ extra->vibrato_depth = dumbfile_getc(f);
+ extra->vibrato_speed = dumbfile_getc(f);
+
+ if (dumbfile_error(f) || extra->vibrato_type > 4) // XXX
+ return -1;
+
+ /** WARNING: lossy approximation */
+ instrument->fadeout = (dumbfile_igetw(f)*128 + 64)/0xFFF;
+
+ dumbfile_skip(f, 2); /* reserved */
+
+ bytes_read += 4 + 96 + 48 + 48 + 14*1 + 2 + 2;
+ } else
+ for (i = 0; i < 96; i++)
+ instrument->map_sample[i] = 0;
+
+ if (dumbfile_skip(f, size - bytes_read))
+ return -1;
+
+ instrument->new_note_action = NNA_NOTE_CUT;
+ instrument->dup_check_type = DCT_OFF;
+ instrument->dup_check_action = DCA_NOTE_CUT;
+ instrument->pp_separation = 0;
+ instrument->pp_centre = 60; /* C-5 */
+ instrument->global_volume = 128;
+ instrument->default_pan = 32;
+ instrument->random_volume = 0;
+ instrument->random_pan = 0;
+ instrument->filter_cutoff = 0;
+ instrument->filter_resonance = 0;
+
+ return 0;
+}
+
+
+
+/* I (entheh) have two XM files saved by a very naughty program. After a
+ * 16-bit sample, it saved a rogue byte. The length of the sample was indeed
+ * an odd number, incremented to include the rogue byte.
+ *
+ * In this function we are converting sample lengths and loop points so they
+ * are measured in samples. This means we forget about the extra bytes, and
+ * they don't get skipped. So we fail trying to read the next instrument.
+ *
+ * To get around this, this function returns the number of rogue bytes that
+ * won't be accounted for by reading sample->length samples. It returns a
+ * negative number on failure.
+ */
+static int it_xm_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f)
+{
+ int type;
+ int relative_note_number; /* relative to C4 */
+ int finetune;
+ int roguebytes;
+ int roguebytesmask;
+ int reserved;
+
+ sample->length = dumbfile_igetl(f);
+ sample->loop_start = dumbfile_igetl(f);
+ sample->loop_end = sample->loop_start + dumbfile_igetl(f);
+ sample->global_volume = 64;
+ sample->default_volume = dumbfile_getc(f);
+ finetune = (signed char)dumbfile_getc(f); /* -128..127 <=> -1 semitone .. +127/128 of a semitone */
+ type = dumbfile_getc(f);
+ sample->default_pan = dumbfile_getc(f); /* 0-255 */
+ relative_note_number = (signed char)dumbfile_getc(f);
+
+ /*dumbfile_skip(f, 1); /* reserved */
+ reserved = dumbfile_getc(f);
+
+ dumbfile_getnc(sample->name, 22, f);
+ sample->name[22] = 0;
+
+ sample->filename[0] = 0;
+
+ if (dumbfile_error(f))
+ return -1;
+
+ sample->C5_speed = (long)(16726.0*pow(DUMB_SEMITONE_BASE, relative_note_number) /**pow(DUMB_PITCH_BASE, )*/ );
+ sample->finetune = finetune*2;
+
+ sample->flags = IT_SAMPLE_EXISTS;
+
+ if (reserved == 0xAD &&
+ (!(type & (XM_SAMPLE_16BIT | XM_SAMPLE_STEREO))))
+ {
+ /* F U Olivier Lapicque */
+ roguebytes = 4;
+ roguebytesmask = 4 << 2;
+ }
+ else
+ {
+ roguebytes = (int)sample->length;
+ roguebytesmask = 3;
+ }
+
+ if (type & XM_SAMPLE_16BIT) {
+ sample->flags |= IT_SAMPLE_16BIT;
+ sample->length >>= 1;
+ sample->loop_start >>= 1;
+ sample->loop_end >>= 1;
+ } else
+ roguebytesmask >>= 1;
+
+ if (type & XM_SAMPLE_STEREO) {
+ sample->flags |= IT_SAMPLE_STEREO;
+ sample->length >>= 1;
+ sample->loop_start >>= 1;
+ sample->loop_end >>= 1;
+ } else
+ roguebytesmask >>= 1;
+
+ roguebytes &= roguebytesmask;
+
+ if ((unsigned int)sample->loop_start < (unsigned int)sample->loop_end) {
+ if (type & XM_SAMPLE_FORWARD_LOOP) sample->flags |= IT_SAMPLE_LOOP;
+ if (type & XM_SAMPLE_PINGPONG_LOOP) sample->flags |= IT_SAMPLE_LOOP | IT_SAMPLE_PINGPONG_LOOP;
+ }
+
+ if (sample->length <= 0)
+ sample->flags &= ~IT_SAMPLE_EXISTS;
+ else if ((unsigned int)sample->loop_end > (unsigned int)sample->length)
+ sample->flags &= ~IT_SAMPLE_LOOP;
+ else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end)
+ sample->flags &= ~IT_SAMPLE_LOOP;
+
+ return roguebytes;
+}
+
+
+
+static int it_xm_read_sample_data(IT_SAMPLE *sample, unsigned char roguebytes, DUMBFILE *f)
+{
+ int old;
+ long i;
+ long truncated_size;
+ int n_channels;
+ long datasize;
+
+ if (!(sample->flags & IT_SAMPLE_EXISTS))
+ return dumbfile_skip(f, roguebytes);
+
+ /* let's get rid of the sample data coming after the end of the loop */
+ if ((sample->flags & IT_SAMPLE_LOOP) && sample->loop_end < sample->length && roguebytes != 4) {
+ truncated_size = sample->length - sample->loop_end;
+ sample->length = sample->loop_end;
+ } else {
+ truncated_size = 0;
+ }
+
+ n_channels = sample->flags & IT_SAMPLE_STEREO ? 2 : 1;
+ datasize = sample->length * n_channels;
+
+ sample->data = malloc(datasize * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1));
+ if (!sample->data)
+ return -1;
+
+ if (roguebytes == 4)
+ {
+ if (_dumb_it_read_sample_data_adpcm4(sample, f) < 0)
+ return -1;
+ roguebytes = 0;
+ }
+ else
+ {
+ /* sample data is stored as signed delta values */
+ old = 0;
+ if (sample->flags & IT_SAMPLE_16BIT)
+ for (i = 0; i < sample->length; i++)
+ ((short *)sample->data)[i*n_channels] = old += dumbfile_igetw(f);
+ else
+ for (i = 0; i < sample->length; i++)
+ ((signed char *)sample->data)[i*n_channels] = old += dumbfile_getc(f);
+ }
+
+ /* skip truncated data */
+ dumbfile_skip(f, (sample->flags & IT_SAMPLE_16BIT) ? (2*truncated_size) : (truncated_size));
+
+ if (sample->flags & IT_SAMPLE_STEREO) {
+ old = 0;
+ if (sample->flags & IT_SAMPLE_16BIT)
+ for (i = 1; i < datasize; i += 2)
+ ((short *)sample->data)[i] = old += dumbfile_igetw(f);
+ else
+ for (i = 1; i < datasize; i += 2)
+ ((signed char *)sample->data)[i] = old += dumbfile_getc(f);
+
+ /* skip truncated data */
+ dumbfile_skip(f, (sample->flags & IT_SAMPLE_16BIT) ? (2*truncated_size) : (truncated_size));
+ }
+
+ dumbfile_skip(f, roguebytes);
+
+ if (dumbfile_error(f))
+ return -1;
+
+ return 0;
+}
+
+
+
+/* "Real programmers don't document. If it was hard to write,
+ * it should be hard to understand."
+ *
+ * (Never trust the documentation provided with a tracker.
+ * Real files are the only truth...)
+ */
+static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version)
+{
+ DUMB_IT_SIGDATA *sigdata;
+ char id_text[18];
+
+ int header_size;
+ int flags;
+ int n_channels;
+ int total_samples;
+ int i, j;
+
+ /* check ID text */
+ if (dumbfile_getnc(id_text, 17, f) < 17)
+ return NULL;
+ id_text[17] = 0;
+ if (strcmp(id_text, "Extended Module: ") != 0) {
+ TRACE("XM error: Not an Extended Module\n");
+ return NULL;
+ }
+
+ sigdata = malloc(sizeof(*sigdata));
+ if (!sigdata)
+ return NULL;
+
+ /* song name */
+ if (dumbfile_getnc(sigdata->name, 20, f) < 20) {
+ free(sigdata);
+ return NULL;
+ }
+ sigdata->name[20] = 0;
+
+ if (dumbfile_getc(f) != 0x1A) {
+ TRACE("XM error: 0x1A not found\n");
+ free(sigdata);
+ return NULL;
+ }
+
+ /* tracker name */
+ if (dumbfile_skip(f, 20)) {
+ free(sigdata);
+ return NULL;
+ }
+
+ /* version number */
+ * version = dumbfile_igetw(f);
+ if (* version > 0x0104 || * version < 0x0102) {
+ TRACE("XM error: wrong format version\n");
+ free(sigdata);
+ return NULL;
+ }
+
+ /*
+ ------------------
+ --- Header ---
+ ------------------
+ */
+
+ /* header size */
+ header_size = dumbfile_igetl(f);
+ if (header_size < (4 + 2*8 + 1) || header_size > 0x114) {
+ TRACE("XM error: unexpected header size\n");
+ free(sigdata);
+ return NULL;
+ }
+
+ sigdata->song_message = NULL;
+ sigdata->order = NULL;
+ sigdata->instrument = NULL;
+ sigdata->sample = NULL;
+ sigdata->pattern = NULL;
+ sigdata->midi = NULL;
+ sigdata->checkpoint = NULL;
+
+ sigdata->n_samples = 0;
+ sigdata->n_orders = dumbfile_igetw(f);
+ sigdata->restart_position = dumbfile_igetw(f);
+ n_channels = dumbfile_igetw(f); /* max 32 but we'll be lenient */
+ sigdata->n_pchannels = n_channels;
+ sigdata->n_patterns = dumbfile_igetw(f);
+ sigdata->n_instruments = dumbfile_igetw(f); /* max 128 */ /* XXX upped to 256 */
+ flags = dumbfile_igetw(f);
+ sigdata->speed = dumbfile_igetw(f);
+ if (sigdata->speed == 0) sigdata->speed = 6; // Should we? What about tempo?
+ sigdata->tempo = dumbfile_igetw(f);
+
+ /* sanity checks */
+ // XXX
+ i = header_size - 4 - 2 * 8; /* Maximum number of orders expected */
+ if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_orders > i || sigdata->n_patterns > 256 || sigdata->n_instruments > 256 || n_channels > DUMB_IT_N_CHANNELS) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ //if (sigdata->restart_position >= sigdata->n_orders)
+ //sigdata->restart_position = 0;
+
+ /* order table */
+ sigdata->order = malloc(sigdata->n_orders*sizeof(*sigdata->order));
+ if (!sigdata->order) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ dumbfile_getnc(sigdata->order, sigdata->n_orders, f);
+ dumbfile_skip(f, i - sigdata->n_orders);
+
+ if (dumbfile_error(f)) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ if ( * version > 0x103 ) {
+ /*
+ --------------------
+ --- Patterns ---
+ --------------------
+ */
+
+ sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
+ if (!sigdata->pattern) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ for (i = 0; i < sigdata->n_patterns; i++)
+ sigdata->pattern[i].entry = NULL;
+
+ {
+ unsigned char *buffer = malloc(1280 * n_channels); /* 256 rows * 5 bytes */
+ if (!buffer) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ for (i = 0; i < sigdata->n_patterns; i++) {
+ if (it_xm_read_pattern(&sigdata->pattern[i], f, n_channels, buffer, * version) != 0) {
+ free(buffer);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ }
+ free(buffer);
+ }
+
+ /*
+ -----------------------------------
+ --- Instruments and Samples ---
+ -----------------------------------
+ */
+
+ sigdata->instrument = malloc(sigdata->n_instruments * sizeof(*sigdata->instrument));
+ if (!sigdata->instrument) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ /* With XM, samples are not global, they're part of an instrument. In a
+ * file, each instrument is stored with its samples. Because of this, I
+ * don't know how to find how many samples are present in the file. Thus
+ * I have to do n_instruments reallocation on sigdata->sample.
+ * Looking at FT2, it doesn't seem possible to have more than 16 samples
+ * per instrument (even though n_samples is stored as 2 bytes). So maybe
+ * we could allocate a 128*16 array of samples, and shrink it back to the
+ * correct size when we know it?
+ * Alternatively, I could allocate samples by blocks of N (still O(n)),
+ * or double the number of allocated samples when I need more (O(log n)).
+ */
+ total_samples = 0;
+ sigdata->sample = NULL;
+
+ for (i = 0; i < sigdata->n_instruments; i++) {
+ XM_INSTRUMENT_EXTRA extra;
+
+ if (it_xm_read_instrument(&sigdata->instrument[i], &extra, f) < 0) {
+ // XXX
+ if ( ! i )
+ {
+ TRACE("XM error: instrument %d\n", i+1);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ else
+ {
+ sigdata->n_instruments = i;
+ break;
+ }
+ }
+
+ if (extra.n_samples) {
+ unsigned char roguebytes[XM_MAX_SAMPLES_PER_INSTRUMENT];
+
+ /* adjust instrument sample map (make indices absolute) */
+ for (j = 0; j < 96; j++)
+ sigdata->instrument[i].map_sample[j] += total_samples;
+
+ sigdata->sample = safe_realloc(sigdata->sample, sizeof(*sigdata->sample)*(total_samples+extra.n_samples));
+ if (!sigdata->sample) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ for (j = total_samples; j < total_samples+extra.n_samples; j++)
+ sigdata->sample[j].data = NULL;
+
+ /* read instrument's samples */
+ for (j = 0; j < extra.n_samples; j++) {
+ IT_SAMPLE *sample = &sigdata->sample[total_samples+j];
+ int b = it_xm_read_sample_header(sample, f);
+ if (b < 0) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ roguebytes[j] = b;
+ // Any reason why these can't be set inside it_xm_read_sample_header()?
+ sample->vibrato_speed = extra.vibrato_speed;
+ sample->vibrato_depth = extra.vibrato_depth;
+ sample->vibrato_rate = extra.vibrato_sweep;
+ /* Rate and sweep don't match, but the difference is
+ * accounted for in itrender.c.
+ */
+ sample->vibrato_waveform = xm_convert_vibrato[extra.vibrato_type];
+ sample->max_resampling_quality = -1;
+ }
+ for (j = 0; j < extra.n_samples; j++) {
+ if (it_xm_read_sample_data(&sigdata->sample[total_samples+j], roguebytes[j], f) != 0) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ }
+ total_samples += extra.n_samples;
+ }
+ }
+
+ sigdata->n_samples = total_samples;
+ } else {
+ // ahboy! old layout!
+ // first instruments and sample headers, then patterns, then sample data!
+
+ /*
+ -----------------------------------
+ --- Instruments and Samples ---
+ -----------------------------------
+ */
+
+ unsigned char * roguebytes = malloc( sigdata->n_instruments * XM_MAX_SAMPLES_PER_INSTRUMENT );
+ if (!roguebytes) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ sigdata->instrument = malloc(sigdata->n_instruments * sizeof(*sigdata->instrument));
+ if (!sigdata->instrument) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ total_samples = 0;
+ sigdata->sample = NULL;
+
+ for (i = 0; i < sigdata->n_instruments; i++) {
+ XM_INSTRUMENT_EXTRA extra;
+
+ if (it_xm_read_instrument(&sigdata->instrument[i], &extra, f) < 0) {
+ TRACE("XM error: instrument %d\n", i+1);
+ free(roguebytes);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ if (extra.n_samples) {
+ /* adjust instrument sample map (make indices absolute) */
+ for (j = 0; j < 96; j++)
+ sigdata->instrument[i].map_sample[j] += total_samples;
+
+ sigdata->sample = safe_realloc(sigdata->sample, sizeof(*sigdata->sample)*(total_samples+extra.n_samples));
+ if (!sigdata->sample) {
+ free(roguebytes);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ for (j = total_samples; j < total_samples+extra.n_samples; j++)
+ sigdata->sample[j].data = NULL;
+
+ /* read instrument's samples */
+ for (j = 0; j < extra.n_samples; j++) {
+ IT_SAMPLE *sample = &sigdata->sample[total_samples+j];
+ int b = it_xm_read_sample_header(sample, f);
+ if (b < 0) {
+ free(roguebytes);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ roguebytes[total_samples+j] = b;
+ // Any reason why these can't be set inside it_xm_read_sample_header()?
+ sample->vibrato_speed = extra.vibrato_speed;
+ sample->vibrato_depth = extra.vibrato_depth;
+ sample->vibrato_rate = extra.vibrato_sweep;
+ /* Rate and sweep don't match, but the difference is
+ * accounted for in itrender.c.
+ */
+ sample->vibrato_waveform = xm_convert_vibrato[extra.vibrato_type];
+ sample->max_resampling_quality = -1;
+ }
+ total_samples += extra.n_samples;
+ }
+ }
+
+ sigdata->n_samples = total_samples;
+
+ /*
+ --------------------
+ --- Patterns ---
+ --------------------
+ */
+
+ sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
+ if (!sigdata->pattern) {
+ free(roguebytes);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ for (i = 0; i < sigdata->n_patterns; i++)
+ sigdata->pattern[i].entry = NULL;
+
+ {
+ unsigned char *buffer = malloc(1280 * n_channels); /* 256 rows * 5 bytes */
+ if (!buffer) {
+ free(roguebytes);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ for (i = 0; i < sigdata->n_patterns; i++) {
+ if (it_xm_read_pattern(&sigdata->pattern[i], f, n_channels, buffer, * version) != 0) {
+ free(buffer);
+ free(roguebytes);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ }
+ free(buffer);
+ }
+
+ // and now we load the sample data
+ for (j = 0; j < total_samples; j++) {
+ if (it_xm_read_sample_data(&sigdata->sample[j], roguebytes[j], f) != 0) {
+ free(roguebytes);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+ }
+
+ free(roguebytes);
+ }
+
+
+ sigdata->flags = IT_WAS_AN_XM | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO | IT_USE_INSTRUMENTS;
+ // Are we OK with IT_COMPATIBLE_GXX off?
+ //
+ // When specifying note + instr + tone portamento, and an old note is still playing (even after note off):
+ // - If Compatible Gxx is on, the new note will be triggered only if the instrument _changes_.
+ // - If Compatible Gxx is off, the new note will always be triggered, provided the instrument is specified.
+ // - FT2 seems to do the latter (unconfirmed).
+
+ // Err, wait. XM playback has its own code. The change made to the IT
+ // playbackc code didn't affect XM playback. Forget this then. There's
+ // still a bug in XM playback though, and it'll need some investigation...
+ // tomorrow...
+
+ // UPDATE: IT_COMPATIBLE_GXX is required to be on, so that tone porta has
+ // separate memory from portamento.
+
+ if (flags & XM_LINEAR_FREQUENCY)
+ sigdata->flags |= IT_LINEAR_SLIDES;
+
+ sigdata->global_volume = 128;
+ sigdata->mixing_volume = 48;
+ sigdata->pan_separation = 128;
+
+ memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
+ memset(sigdata->channel_pan, 32, DUMB_IT_N_CHANNELS);
+
+ _dumb_it_fix_invalid_orders(sigdata);
+
+ return sigdata;
+}
+
+
+
+#if 0 // no fucking way, dude!
+
+/* The length returned is the time required to play from the beginning of the
+ * file to the last row of the last order (which is when the player will
+ * loop). Depending on the song, the sound might stop sooner.
+ * Due to fixed point roundoffs, I think this is only reliable to the second.
+ * Full precision could be achieved by using a double during the computation,
+ * or maybe a LONG_LONG.
+ */
+long it_compute_length(const DUMB_IT_SIGDATA *sigdata)
+{
+ IT_PATTERN *pattern;
+ int tempo, speed;
+ int loop_start[IT_N_CHANNELS];
+ char loop_count[IT_N_CHANNELS];
+ int order, entry;
+ int row_first_entry = 0;
+ int jump, jump_dest;
+ int delay, fine_delay;
+ int i;
+ long t;
+
+ if (!sigdata)
+ return 0;
+
+ tempo = sigdata->tempo;
+ speed = sigdata->speed;
+ order = entry = 0;
+ jump = jump_dest = 0;
+ t = 0;
+
+ /* for each PATTERN */
+ for (order = 0; order < sigdata->n_orders; order++) {
+
+ if (sigdata->order[order] == IT_ORDER_END) break;
+ if (sigdata->order[order] == IT_ORDER_SKIP) continue;
+
+ for (i = 0; i < IT_N_CHANNELS; i++)
+ loop_count[i] = -1;
+
+ pattern = &sigdata->pattern[ sigdata->order[order] ];
+ entry = 0;
+ if (jump == IT_BREAK_TO_ROW) {
+ int row = 0;
+ while (row < jump_dest)
+ if (pattern->entry[entry++].channel >= IT_N_CHANNELS)
+ row++;
+ }
+
+ /* for each ROW */
+ while (entry < pattern->n_entries) {
+ row_first_entry = entry;
+ delay = fine_delay = 0;
+ jump = 0;
+
+ /* for each note NOTE */
+ while (entry < pattern->n_entries && pattern->entry[entry].channel < IT_N_CHANNELS) {
+ int value = pattern->entry[entry].effectvalue;
+ int channel = pattern->entry[entry].channel;
+
+ switch (pattern->entry[entry].effect) {
+
+ case IT_SET_SPEED: speed = value; break;
+
+ case IT_JUMP_TO_ORDER:
+ if (value <= order) /* infinite loop */
+ return 0;
+ jump = IT_JUMP_TO_ORDER;
+ jump_dest = value;
+ break;
+
+ case IT_BREAK_TO_ROW:
+ jump = IT_BREAK_TO_ROW;
+ jump_dest = value;
+ break;
+
+ case IT_S:
+ switch (HIGH(value)) {
+ case IT_S_PATTERN_DELAY: delay = LOW(value); break;
+ case IT_S_FINE_PATTERN_DELAY: fine_delay = LOW(value); break;
+ case IT_S_PATTERN_LOOP:
+ if (LOW(value) == 0) {
+ loop_start[channel] = row_first_entry;
+ } else {
+ if (loop_count[channel] == -1)
+ loop_count[channel] = LOW(value);
+
+ if (loop_count[channel]) {
+ jump = IT_S_PATTERN_LOOP;
+ jump_dest = loop_start[channel];
+ }
+ loop_count[channel]--;
+ }
+ break;
+ }
+ break;
+
+ case IT_SET_SONG_TEMPO:
+ switch (HIGH(value)) { /* slides happen every non-row frames */
+ case 0: tempo = tempo - LOW(value)*(speed-1); break;
+ case 1: tempo = tempo + LOW(value)*(speed-1); break;
+ default: tempo = value;
+ }
+ tempo = MID(32, tempo, 255);
+ break;
+ }
+
+ entry++;
+ }
+
+ /* end of ROW */
+ entry++;
+ t += TICK_TIME_DIVIDEND * (speed*(1+delay) + fine_delay) / tempo;
+
+ if (jump == IT_JUMP_TO_ORDER) {
+ order = jump_dest - 1;
+ break;
+ } else if (jump == IT_BREAK_TO_ROW)
+ break;
+ else if (jump == IT_S_PATTERN_LOOP)
+ entry = jump_dest - 1;
+ }
+
+ /* end of PATTERN */
+ }
+
+ return t;
+}
+
+#endif /* 0 */
+
+
+static char hexdigit(int in)
+{
+ if (in < 10) return in + '0';
+ else return in + 'A' - 10;
+}
+
+DUH *dumb_read_xm_quick(DUMBFILE *f)
+{
+ sigdata_t *sigdata;
+ int ver;
+
+ DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+ sigdata = it_xm_load_sigdata(f, &ver);
+
+ if (!sigdata)
+ return NULL;
+
+ {
+ char version[16];
+ const char *tag[2][2];
+ tag[0][0] = "TITLE";
+ tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+ tag[1][0] = "FORMAT";
+ version[0] = 'X';
+ version[1] = 'M';
+ version[2] = ' ';
+ version[3] = 'v';
+ version[4] = hexdigit( ( ver >> 8 ) & 15 );
+ version[5] = '.';
+ version[6] = hexdigit( ( ver >> 4 ) & 15 );
+ version[7] = hexdigit( ver & 15 );
+ version[8] = 0;
+ tag[1][1] = ( const char * ) & version;
+ return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
+ }
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/readxm2.c b/plugins/dumb/dumb-kode54/src/it/readxm2.c
new file mode 100644
index 00000000..c79f753f
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/readxm2.c
@@ -0,0 +1,29 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * readxm2.c - Function to read a Fast Tracker II / / \ \
+ * module from an open file and do an | < / \_
+ * initial run-through. | \/ /\ /
+ * \_ / > /
+ * Split off from readxm.c by entheh. | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include "dumb.h"
+
+
+
+DUH *dumb_read_xm(DUMBFILE *f)
+{
+ DUH *duh = dumb_read_xm_quick(f);
+ dumb_it_do_initial_runthrough(duh);
+ return duh;
+}
diff --git a/plugins/dumb/dumb-kode54/src/it/xmeffect.c b/plugins/dumb/dumb-kode54/src/it/xmeffect.c
new file mode 100644
index 00000000..2187a0b3
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/it/xmeffect.c
@@ -0,0 +1,243 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * xmeffect.c - Code for converting MOD/XM / / \ \
+ * effects to IT effects. | < / \_
+ * | \/ /\ /
+ * By Julien Cugniere. Ripped out of readxm.c \_ / > /
+ * by entheh. | \ / /
+ * | ' /
+ * \__/
+ */
+
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+#if 0
+unsigned char **_dumb_malloc2(int w, int h)
+{
+ unsigned char **line = malloc(h * sizeof(*line));
+ int i;
+ if (!line) return NULL;
+
+ line[0] = malloc(w * h * sizeof(*line[0]));
+ if (!line[0]) {
+ free(line);
+ return NULL;
+ }
+
+ for (i = 1; i < h; i++)
+ line[i] = line[i-1] + w;
+
+ memset(line[0], 0, w*h);
+
+ return line;
+}
+
+
+
+void _dumb_free2(unsigned char **line)
+{
+ if (line) {
+ if (line[0])
+ free(line[0]);
+ free(line);
+ }
+}
+
+
+
+/* Effects having a memory. 2 means that the two parts of the effectvalue
+ * should be handled separately.
+ */
+static const char xm_has_memory[] = {
+/* 0 1 2 3 4 5 6 7 8 9 A B C D (E) F G H K L P R T (X) */
+ 0, 1, 1, 1, 2, 1, 1, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0,
+
+/* E0 E1 E2 E3 E4 E5 E6 E7 E9 EA EB EC ED EE X1 X2 */
+ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+#endif
+
+
+
+/* Effects marked with 'special' are handled specifically in itrender.c */
+void _dumb_it_xm_convert_effect(int effect, int value, IT_ENTRY *entry, int mod)
+{
+const int log = 0;
+
+ if ((!effect && !value) || (effect >= XM_N_EFFECTS))
+ return;
+
+if (log) printf("%c%02X", (effect<10)?('0'+effect):('A'+effect-10), value);
+
+ /* Linearisation of the effect number... */
+ if (effect == XM_E) {
+ effect = EBASE + HIGH(value);
+ value = LOW(value);
+ } else if (effect == XM_X) {
+ effect = XBASE + HIGH(value);
+ value = LOW(value);
+ }
+
+if (log) printf(" - %2d %02X", effect, value);
+
+#if 0 // This should be handled in itrender.c!
+ /* update effect memory */
+ switch (xm_has_memory[effect]) {
+ case 1:
+ if (!value)
+ value = memory[entry->channel][effect];
+ else
+ memory[entry->channel][effect] = value;
+ break;
+
+ case 2:
+ if (!HIGH(value))
+ SET_HIGH(value, HIGH(memory[entry->channel][effect]));
+ else
+ SET_HIGH(memory[entry->channel][effect], HIGH(value));
+
+ if (!LOW(value))
+ SET_LOW(value, LOW(memory[entry->channel][effect]));
+ else
+ SET_LOW(memory[entry->channel][effect], LOW(value));
+ break;
+ }
+#endif
+
+ /* convert effect */
+ entry->mask |= IT_ENTRY_EFFECT;
+ switch (effect) {
+
+ case XM_APPREGIO: effect = IT_ARPEGGIO; break;
+ case XM_VIBRATO: effect = IT_VIBRATO; break;
+ case XM_TONE_PORTAMENTO: effect = IT_TONE_PORTAMENTO; break;
+ case XM_TREMOLO: effect = IT_TREMOLO; break;
+ case XM_SET_PANNING: effect = IT_SET_PANNING; break;
+ case XM_SAMPLE_OFFSET: effect = IT_SET_SAMPLE_OFFSET; break;
+ case XM_POSITION_JUMP: effect = IT_JUMP_TO_ORDER; break;
+ case XM_MULTI_RETRIG: effect = IT_RETRIGGER_NOTE; break;
+ case XM_TREMOR: effect = IT_TREMOR; break;
+ case XM_PORTAMENTO_UP: effect = IT_XM_PORTAMENTO_UP; break;
+ case XM_PORTAMENTO_DOWN: effect = IT_XM_PORTAMENTO_DOWN; break;
+ case XM_SET_CHANNEL_VOLUME: effect = IT_SET_CHANNEL_VOLUME; break; /* special */
+ case XM_VOLSLIDE_TONEPORTA: effect = IT_VOLSLIDE_TONEPORTA; break; /* special */
+ case XM_VOLSLIDE_VIBRATO: effect = IT_VOLSLIDE_VIBRATO; break; /* special */
+
+ case XM_PATTERN_BREAK:
+ effect = IT_BREAK_TO_ROW;
+ value = BCD_TO_NORMAL(value);
+ if (value > 63) value = 0; /* FT2, maybe ProTracker? */
+ break;
+
+ case XM_VOLUME_SLIDE: /* special */
+ effect = IT_VOLUME_SLIDE;
+ value = HIGH(value) ? EFFECT_VALUE(HIGH(value), 0) : EFFECT_VALUE(0, LOW(value));
+ break;
+
+ case XM_PANNING_SLIDE:
+ effect = IT_PANNING_SLIDE;
+ //value = HIGH(value) ? EFFECT_VALUE(HIGH(value), 0) : EFFECT_VALUE(0, LOW(value));
+ value = HIGH(value) ? EFFECT_VALUE(0, HIGH(value)) : EFFECT_VALUE(LOW(value), 0);
+ break;
+
+ case XM_GLOBAL_VOLUME_SLIDE: /* special */
+ effect = IT_GLOBAL_VOLUME_SLIDE;
+ value = HIGH(value) ? EFFECT_VALUE(HIGH(value), 0) : EFFECT_VALUE(0, LOW(value));
+ break;
+
+ case XM_SET_TEMPO_BPM:
+ if (mod) effect = (value <= 0x20) ? (IT_SET_SPEED) : (IT_SET_SONG_TEMPO);
+ else effect = (value < 0x20) ? (IT_SET_SPEED) : (IT_SET_SONG_TEMPO);
+ break;
+
+ case XM_SET_GLOBAL_VOLUME:
+ effect = IT_SET_GLOBAL_VOLUME;
+ value *= 2;
+ break;
+
+ case XM_KEY_OFF:
+ effect = IT_XM_KEY_OFF;
+ break;
+
+ case XM_SET_ENVELOPE_POSITION:
+ effect = IT_XM_SET_ENVELOPE_POSITION;
+ break;
+
+ case EBASE+XM_E_SET_FILTER: effect = SBASE+IT_S_SET_FILTER; break;
+ case EBASE+XM_E_SET_GLISSANDO_CONTROL: effect = SBASE+IT_S_SET_GLISSANDO_CONTROL; break; /** TODO */
+ case EBASE+XM_E_SET_FINETUNE: effect = SBASE+IT_S_FINETUNE; break;
+ case EBASE+XM_E_SET_LOOP: effect = SBASE+IT_S_PATTERN_LOOP; break;
+ case EBASE+XM_E_NOTE_CUT: effect = SBASE+IT_S_DELAYED_NOTE_CUT; break;
+ case EBASE+XM_E_NOTE_DELAY: effect = SBASE+IT_S_NOTE_DELAY; break;
+ case EBASE+XM_E_PATTERN_DELAY: effect = SBASE+IT_S_PATTERN_DELAY; break;
+ case EBASE+XM_E_SET_PANNING: effect = SBASE+IT_S_SET_PAN; break;
+ case EBASE+XM_E_FINE_VOLSLIDE_UP: effect = IT_XM_FINE_VOLSLIDE_UP; break;
+ case EBASE+XM_E_FINE_VOLSLIDE_DOWN: effect = IT_XM_FINE_VOLSLIDE_DOWN; break;
+
+ case EBASE + XM_E_FINE_PORTA_UP:
+ effect = IT_PORTAMENTO_UP;
+ value = EFFECT_VALUE(0xF, value);
+ break;
+
+ case EBASE + XM_E_FINE_PORTA_DOWN:
+ effect = IT_PORTAMENTO_DOWN;
+ value = EFFECT_VALUE(0xF, value);
+ break;
+
+ case EBASE + XM_E_RETRIG_NOTE:
+ effect = IT_XM_RETRIGGER_NOTE;
+ value = EFFECT_VALUE(0, value);
+ break;
+
+ case EBASE + XM_E_SET_VIBRATO_CONTROL:
+ effect = SBASE+IT_S_SET_VIBRATO_WAVEFORM;
+ value &= ~4;
+ break;
+
+ case EBASE + XM_E_SET_TREMOLO_CONTROL:
+ effect = SBASE+IT_S_SET_TREMOLO_WAVEFORM;
+ value &= ~4;
+ break;
+
+ case XBASE + XM_X_EXTRAFINE_PORTA_UP:
+ effect = IT_PORTAMENTO_UP;
+ value = EFFECT_VALUE(0xE, value);
+ break;
+
+ case XBASE + XM_X_EXTRAFINE_PORTA_DOWN:
+ effect = IT_PORTAMENTO_DOWN;
+ value = EFFECT_VALUE(0xE, value);
+ break;
+
+ default:
+ /* user effect (often used in demos for synchronisation) */
+ entry->mask &= ~IT_ENTRY_EFFECT;
+ }
+
+if (log) printf(" - %2d %02X", effect, value);
+
+ /* Inverse linearisation... */
+ if (effect >= SBASE && effect < SBASE+16) {
+ value = EFFECT_VALUE(effect-SBASE, value);
+ effect = IT_S;
+ }
+
+if (log) printf(" - %c%02X\n", 'A'+effect-1, value);
+
+ entry->effect = effect;
+ entry->effectvalue = value;
+}
diff --git a/plugins/dumb/dumb-kode54/src/sigtypes/combine.c b/plugins/dumb/dumb-kode54/src/sigtypes/combine.c
new file mode 100644
index 00000000..497f085c
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/sigtypes/combine.c
@@ -0,0 +1,243 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * combine.c - The combining (COMB) signal type. / / \ \
+ * | < / \_
+ * By entheh. | \/ /\ /
+ * \_ / > /
+ * This takes multiple monaural signals and | \ / /
+ * combines them into a single multichannel | ' /
+ * signal. It assumes the correct number of \__/
+ * channels is passed. An ASSERT() is in place
+ * to check the number of channels when you
+ * compile with -DDEBUGMODE. As an exception, if one channel is passed the
+ * signals are all mixed together.
+ */
+
+#include <stdlib.h>
+
+#include "dumb.h"
+
+
+
+#define SIGTYPE_COMBINING DUMB_ID('C','O','M','B')
+
+
+
+typedef struct COMBINING_SIGNAL
+{
+ int n_signals;
+ int sig[ZERO_SIZE];
+}
+COMBINING_SIGNAL;
+
+
+
+typedef struct COMBINING_SAMPINFO
+{
+ int n_signals;
+ int downmix;
+ DUH_SIGNAL_SAMPINFO *csampinfo[ZERO_SIZE];
+}
+COMBINING_SAMPINFO;
+
+
+
+static void *combining_load_signal(DUH *duh, DUMBFILE *file)
+{
+ COMBINING_SIGNAL *signal;
+ int n_signals;
+
+ (void)duh;
+
+ n_signals = dumbfile_getc(file);
+
+ /* No point in combining only one signal! */
+ if (dumbfile_error(file) || n_signals <= 1)
+ return NULL;
+
+ signal = malloc(sizeof(*signal) + n_signals * sizeof(*signal->sig));
+ if (!signal)
+ return NULL;
+
+ signal->n_signals = n_signals;
+
+ {
+ int n;
+ for (n = 0; n < signal->n_signals; n++) {
+ signal->sig[n] = dumbfile_igetl(file);
+ if (dumbfile_error(file)) {
+ free(signal);
+ return NULL;
+ }
+ }
+ }
+
+ return signal;
+}
+
+
+
+static void *combining_start_samples(DUH *duh, void *signal, int n_channels, long pos)
+{
+#define signal ((COMBINING_SIGNAL *)signal)
+
+ COMBINING_SAMPINFO *sampinfo;
+
+ sampinfo = malloc(sizeof(*sampinfo) + signal->n_signals * sizeof(*sampinfo->csampinfo));
+ if (!sampinfo)
+ return NULL;
+
+ sampinfo->n_signals = signal->n_signals;
+ if (n_channels == 1)
+ sampinfo->downmix = 1;
+ else if (n_channels == signal->n_signals)
+ sampinfo->downmix = 0;
+ else {
+ TRACE("Combining signal discrepancy: %d signals, %d channels.\n", signal->n_signals, n_channels);
+ free(sampinfo);
+ return NULL;
+ }
+
+ {
+ int worthwhile = 0;
+
+ {
+ int n;
+ for (n = 0; n < signal->n_signals; n++) {
+ sampinfo->csampinfo[n] = duh_signal_start_samples(duh, signal->sig[n], 1, pos);
+ if (sampinfo->csampinfo[n])
+ worthwhile = 1;
+ }
+ }
+
+ if (!worthwhile) {
+ free(sampinfo);
+ return NULL;
+ }
+ }
+
+ return sampinfo;
+
+#undef signal
+}
+
+
+
+static long combining_render_samples(
+ void *sampinfo,
+ float volume, float delta,
+ long size, sample_t **samples
+)
+{
+#define sampinfo ((COMBINING_SAMPINFO *)sampinfo)
+
+ long max_size;
+
+ int n;
+
+ if (sampinfo->downmix)
+ volume /= sampinfo->n_signals;
+
+ max_size = duh_signal_render_samples(sampinfo->csampinfo[0], volume, delta, size, samples);
+
+ if (sampinfo->downmix) {
+
+ long s;
+ long sz;
+
+ sample_t *sampbuf = malloc(size * sizeof(sample_t));
+
+ if (!sampbuf)
+ return 0;
+
+ for (n = 1; n < sampinfo->n_signals; n++) {
+ sz = duh_signal_render_samples(sampinfo->csampinfo[n], volume, delta, size, &sampbuf);
+ if (sz > max_size) {
+ for (s = max_size; s < sz; s++)
+ samples[0][s] = sampbuf[s];
+ sz = max_size;
+ max_size = s;
+ }
+ for (s = 0; s < sz; s++)
+ samples[0][s] += sampbuf[s];
+ }
+
+ free(sampbuf);
+
+ } else {
+
+ long *sz = malloc(size * sizeof(*sz));
+ long s;
+
+ if (!sz)
+ return 0;
+
+ sz[0] = max_size;
+
+ for (n = 1; n < sampinfo->n_signals; n++) {
+ sz[n] = duh_signal_render_samples(sampinfo->csampinfo[n], volume, delta, size, samples + n);
+ if (sz[n] > max_size)
+ max_size = sz[n];
+ }
+
+ for (n = 0; n < sampinfo->n_signals; n++)
+ for (s = sz[n]; s < max_size; s++)
+ samples[n][s] = 0;
+
+ free(sz);
+
+ }
+
+ return max_size;
+
+#undef sampinfo
+}
+
+
+
+static void combining_end_samples(void *sampinfo)
+{
+#define sampinfo ((COMBINING_SAMPINFO *)sampinfo)
+
+ int n;
+
+ for (n = 0; n < sampinfo->n_signals; n++)
+ duh_signal_end_samples(sampinfo->csampinfo[n]);
+
+ free(sampinfo);
+
+#undef sampinfo
+}
+
+
+
+static void combining_unload_signal(void *signal)
+{
+ free(signal);
+}
+
+
+
+static DUH_SIGTYPE_DESC sigtype_combining = {
+ SIGTYPE_COMBINING,
+ &combining_load_signal,
+ &combining_start_samples,
+ NULL,
+ &combining_render_samples,
+ &combining_end_samples,
+ &combining_unload_signal
+};
+
+
+void dumb_register_sigtype_combining(void)
+{
+ dumb_register_sigtype(&sigtype_combining);
+}
diff --git a/plugins/dumb/dumb-kode54/src/sigtypes/sample.c b/plugins/dumb/dumb-kode54/src/sigtypes/sample.c
new file mode 100644
index 00000000..4695de91
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/sigtypes/sample.c
@@ -0,0 +1,340 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * sample.c - The sample (SAMP) signal type. / / \ \
+ * | < / \_
+ * By entheh. | \/ /\ /
+ * \_ / > /
+ * This only supports monaural samples. For | \ / /
+ * multiple channels, use multiple samples and | ' /
+ * a combining signal (COMB). \__/
+ */
+
+/* NOTE: filters need not be credited yet, as they will be moved elsewhere. */
+/** WARNING don't forget to move these filters somewhere */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+
+
+/** WARNING move these things somewhere useful, for DUH writing - this applies to other signal types too */
+#define SIGTYPE_SAMPLE DUMB_ID('S','A','M','P')
+
+
+
+#define SAMPFLAG_16BIT 1 /* sample in file is 16 bit, rather than 8 bit */
+#define SAMPFLAG_LOOP 2 /* loop indefinitely */
+#define SAMPFLAG_XLOOP 4 /* loop x times; only relevant if LOOP not set */
+#define SAMPFLAG_PINGPONG 8 /* loop back and forth, if LOOP or XLOOP set */
+
+
+/* SAMPPARAM_N_LOOPS: add 'value' iterations to the loop. 'value' is assumed
+ * to be positive.
+ */
+#define SAMPPARAM_N_LOOPS 0
+
+
+
+typedef struct SAMPLE_SIGDATA
+{
+ long size;
+ int flags;
+ long loop_start;
+ long loop_end;
+ sample_t *samples;
+}
+SAMPLE_SIGDATA;
+
+
+
+typedef struct SAMPLE_SIGRENDERER
+{
+ SAMPLE_SIGDATA *sigdata;
+ int n_channels;
+ DUMB_RESAMPLER r;
+ int n_loops;
+}
+SAMPLE_SIGRENDERER;
+
+
+
+static sigdata_t *sample_load_sigdata(DUH *duh, DUMBFILE *file)
+{
+ SAMPLE_SIGDATA *sigdata;
+ long size;
+ long n;
+ int flags;
+
+ (void)duh;
+
+ size = dumbfile_igetl(file);
+
+ if (dumbfile_error(file) || size <= 0)
+ return NULL;
+
+ flags = dumbfile_getc(file);
+ if (flags < 0)
+ return NULL;
+
+ sigdata = malloc(sizeof(*sigdata));
+ if (!sigdata)
+ return NULL;
+
+ sigdata->samples = malloc(size * sizeof(sample_t));
+ if (!sigdata->samples) {
+ free(sigdata);
+ return NULL;
+ }
+
+ sigdata->size = size;
+ sigdata->flags = flags;
+
+ if (sigdata->flags & (SAMPFLAG_LOOP | SAMPFLAG_XLOOP)) {
+ sigdata->loop_start = dumbfile_igetl(file);
+
+ if (dumbfile_error(file) || (unsigned long)sigdata->loop_start >= (unsigned long)size) {
+ free(sigdata->samples);
+ free(sigdata);
+ return NULL;
+ }
+
+ if (sigdata->flags & SAMPFLAG_LOOP)
+ sigdata->loop_end = size;
+ else {
+ sigdata->loop_end = dumbfile_igetl(file);
+
+ if (dumbfile_error(file) || sigdata->loop_end <= sigdata->loop_start || sigdata->loop_end > size) {
+ free(sigdata->samples);
+ free(sigdata);
+ return NULL;
+ }
+ }
+ } else {
+ sigdata->loop_start = 0;
+ sigdata->loop_end = size;
+ }
+
+ if (sigdata->flags & SAMPFLAG_16BIT) {
+ for (n = 0; n < size; n++) {
+ int m = dumbfile_igetw(file);
+ if (m < 0) {
+ free(sigdata->samples);
+ free(sigdata);
+ return NULL;
+ }
+ sigdata->samples[n] = (int)(signed short)m << 8;
+ }
+ } else {
+ for (n = 0; n < size; n++) {
+ int m = dumbfile_getc(file);
+ if (m < 0) {
+ free(sigdata->samples);
+ free(sigdata);
+ return NULL;
+ }
+ sigdata->samples[n] = (int)(signed char)m << 16;
+ }
+ }
+
+ return sigdata;
+}
+
+
+
+static void sample_pickup(DUMB_RESAMPLER *r, void *data)
+{
+ SAMPLE_SIGRENDERER *sigrenderer = data;
+
+ if (!(sigrenderer->sigdata->flags & (SAMPFLAG_LOOP | SAMPFLAG_XLOOP))) {
+ r->dir = 0;
+ return;
+ }
+
+ if (!(sigrenderer->sigdata->flags & SAMPFLAG_LOOP) && sigrenderer->n_loops == 0) {
+ r->dir = 0;
+ return;
+ }
+
+ if (sigrenderer->sigdata->flags & SAMPFLAG_PINGPONG) {
+ if (r->dir < 0) {
+ r->pos = (r->start << 1) - 1 - r->pos;
+ r->subpos ^= 65535;
+ r->dir = 1;
+ } else {
+ r->pos = (r->end << 1) - 1 - r->pos;
+ r->subpos ^= 65535;
+ r->dir = -1;
+ }
+ } else
+ r->pos -= r->end - r->start;
+
+ if (!(sigrenderer->sigdata->flags & SAMPFLAG_LOOP)) {
+ if (sigrenderer->n_loops > 0) {
+ sigrenderer->n_loops--;
+ if (sigrenderer->n_loops == 0) {
+ r->start = 0;
+ r->end = sigrenderer->sigdata->size;
+ }
+ }
+ }
+}
+
+
+
+static sigrenderer_t *sample_start_sigrenderer(DUH *duh, sigdata_t *data, int n_channels, long pos)
+{
+ SAMPLE_SIGDATA *sigdata = data;
+ SAMPLE_SIGRENDERER *sigrenderer;
+
+ (void)duh;
+
+ sigrenderer = malloc(sizeof(*sigrenderer));
+ if (!sigrenderer) return NULL;
+
+ sigrenderer->sigdata = data;
+ sigrenderer->n_channels = n_channels;
+ dumb_reset_resampler(&sigrenderer->r, sigdata->samples, pos, 0, sigdata->size);
+ sigrenderer->r.pickup = &sample_pickup;
+ sigrenderer->r.pickup_data = sigrenderer;
+ sigrenderer->n_loops = 0;
+
+ return sigrenderer;
+}
+
+
+
+#if 0
+/* The name says it all ;-) */
+static void sample_cheap_low_pass_filter(sample *src, long size, float max_freq) {
+
+ long i;
+ float fact = max_freq / 44100.0f;
+
+ for (i = 0; i < size-1; i++) {
+ float d = src[i+1] - src[i];
+ if (d > fact)
+ src[i+1] += fact - d;
+ else if (d < -fact)
+ src[i+1] += -d - fact;
+ }
+
+ return;
+}
+
+
+
+/* Dithering with noise shaping filter. Set shape = 0 for no shaping. */
+static void sample_dither_filter(float *src, long size, float shape) {
+ float r1 = 0, r2 = 0;
+ float s1 = 0, s2 = 0; /* Feedback buffer */
+ float o = 0.5f / 255;
+ float tmp;
+ int i;
+
+ for (i = 0; i < size; i++) {
+ r2 = r1;
+ r1 = rand() / (float)RAND_MAX;
+
+ tmp = src[i] + shape * (s1 + s1 - s2);
+ src[i] = tmp + o * (r1 - r2);
+ src[i] = MID(-1.0f, src[i], 1.0f);
+
+ s2 = s1;
+ s1 = tmp - src[i];
+ }
+ return;
+}
+#endif
+
+
+
+static void sample_sigrenderer_set_sigparam(sigrenderer_t *data, unsigned char id, long value)
+{
+ SAMPLE_SIGRENDERER *sigrenderer = data;
+
+ if (id == SAMPPARAM_N_LOOPS) {
+ if ((sigrenderer->sigdata->flags & (SAMPFLAG_LOOP | SAMPFLAG_XLOOP)) == SAMPFLAG_XLOOP) {
+ sigrenderer->n_loops += value;
+ sigrenderer->r.start = sigrenderer->n_loops ? sigrenderer->sigdata->loop_start : 0;
+ sigrenderer->r.end = sigrenderer->n_loops ? sigrenderer->sigdata->loop_end : sigrenderer->sigdata->size;
+ }
+ }
+}
+
+
+
+static long sample_sigrenderer_get_samples(
+ sigrenderer_t *data,
+ float volume, float delta,
+ long size, sample_t **samples
+)
+{
+ SAMPLE_SIGRENDERER *sigrenderer = data;
+
+ DUMB_RESAMPLER initial_r = sigrenderer->r;
+
+ long s = dumb_resample(&sigrenderer->r, samples[0], size, volume, delta);
+
+ int n;
+ for (n = 1; n < sigrenderer->n_channels; n++) {
+ sigrenderer->r = initial_r;
+ dumb_resample(&sigrenderer->r, samples[n], size, volume, delta);
+ }
+
+ return s;
+}
+
+
+
+static void sample_sigrenderer_get_current_sample(sigrenderer_t *data, float volume, sample_t *samples)
+{
+ SAMPLE_SIGRENDERER *sigrenderer = data;
+ int n;
+ for (n = 0; n < sigrenderer->n_channels; n++)
+ samples[n] = dumb_resample_get_current_sample(&sigrenderer->r, volume);
+}
+
+
+
+static void sample_end_sigrenderer(sigrenderer_t *sigrenderer)
+{
+ free(sigrenderer);
+}
+
+
+
+static void sample_unload_sigdata(sigdata_t *data)
+{
+ SAMPLE_SIGDATA *sigdata = data;
+ free(sigdata->samples);
+ free(data);
+}
+
+
+
+static DUH_SIGTYPE_DESC sigtype_sample = {
+ SIGTYPE_SAMPLE,
+ &sample_load_sigdata,
+ &sample_start_sigrenderer,
+ &sample_sigrenderer_set_sigparam,
+ &sample_sigrenderer_get_samples,
+ &sample_sigrenderer_get_current_sample,
+ &sample_end_sigrenderer,
+ &sample_unload_sigdata
+};
+
+
+
+void dumb_register_sigtype_sample(void)
+{
+ dumb_register_sigtype(&sigtype_sample);
+}
diff --git a/plugins/dumb/dumb-kode54/src/sigtypes/sequence.c b/plugins/dumb/dumb-kode54/src/sigtypes/sequence.c
new file mode 100644
index 00000000..cfade4d6
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/sigtypes/sequence.c
@@ -0,0 +1,592 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * sequence.c - The sequence (SEQU) signal type. / / \ \
+ * | < / \_
+ * By entheh. | \/ /\ /
+ * \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+
+
+
+#define SIGTYPE_SEQUENCE DUMB_ID('S','E','Q','U')
+
+
+
+/* We have 256 intervals per semitone, 12 * 256 per octave
+ 2 ** (1 / (12 * 256)) = 1.000225659305069791926712241547647863626
+
+ pow(DUMB_PITCH_BASE, x) = 1.5
+ x = log2(1.5) / log2(DUMB_PITCH_BASE)
+ x = log2(1.5) * 12 * 256
+ x = 1797.004802
+ cf.
+ x = 7 * 256 = 1792
+
+ so, for the perfect fifth temperament, use an interval of 1797.
+*/
+
+
+
+/* Sequencing format
+ * -----------------
+ *
+ * NOTE: A LOT OF THIS IS NOW REDUNDANT. PLEASE REFER TO duhspecs.txt.
+ *
+ * When a signal is initiated, it claims a reference number. If any other
+ * currently playing signal has the same number, that signal becomes
+ * anonymous and inaccessible; that is, if multiple signals were initiated
+ * with the same reference, the reference belongs to the most recent.
+ *
+ * Signals can be stopped, or have their pitch, volume or parameters changed,
+ * using the reference number. A signal may stop prematurely if it runs out
+ * of data, in which case the reference number becomes void, and operations
+ * on it will be ignored. Such a situation will not flag any kind of warning,
+ * since it may be the result of inaccuracies when resampling.
+ *
+ * The sequence consists of a series of commands. All commands begin with a
+ * long int, which is the time to wait after the last command (or the
+ * beginning of the sequence) before executing this command. 65536 represents
+ * one second. A time of -1 (or in fact any negative time) terminates the
+ * sequence, but any currently playing signals will continue until they run
+ * out. Make sure no non-terminating signals are playing when the sequence
+ * ends!
+ *
+ * The time, if nonnegative, is followed by one byte indicating the type of
+ * command. This byte can have the following values:
+ *
+ * SEQUENCE_START_SIGNAL
+ * unsigned char ref; - Reference. Need more than 256? Use two sequences,
+ * and get your brain seen to.
+ * int sig; - The index of the signal to initiate.
+ * long pos; - The position at which to start. 65536 represents one second.
+ * unsigned short volume; - Volume. 65535 represents the maximum volume, so
+ * you will want to go lower than this if you are
+ * playing more than one signal at once.
+ * signed short pitch; - Pitch. 0 represents a frequency of 65536 Hz. Scale
+ * is logarithmic. Add 256 to increase pitch by one
+ * semitone in the even temperament, or add 12*256 to
+ * increase pitch by one octave in any temperament
+ * (i.e. double the frequency).
+ *
+ * SEQUENCE_SET_VOLUME
+ * unsigned char ref;
+ * unsigned short volume;
+ *
+ * SEQUENCE_SET_PITCH
+ * unsigned char ref;
+ * signed short pitch;
+ *
+ * SEQUENCE_SET_PARAMETER
+ * unsigned char ref;
+ * unsigned char id;
+ * long value;
+ * - see the description of the set_parameter function pointer for
+ * information on 'id' and 'value'.
+ *
+ * SEQUENCE_STOP_SIGNAL
+ * unsigned char ref;
+ */
+
+
+
+#define SEQUENCE_START_SIGNAL 0
+#define SEQUENCE_SET_VOLUME 1
+#define SEQUENCE_SET_PITCH 2
+#define SEQUENCE_SET_PARAMETER 3
+#define SEQUENCE_STOP_SIGNAL 4
+
+
+
+typedef struct SEQUENCE_PLAYING
+{
+ struct SEQUENCE_PLAYING *next;
+
+ DUH_SIGNAL_SAMPINFO *sampinfo;
+
+ int ref;
+
+ int pitch;
+ int volume;
+}
+SEQUENCE_PLAYING;
+
+
+
+typedef struct SEQUENCE_SAMPINFO
+{
+ DUH *duh;
+
+ int n_channels;
+
+ unsigned char *signal;
+
+ long time_left;
+ int sub_time_left;
+
+ SEQUENCE_PLAYING *playing;
+}
+SEQUENCE_SAMPINFO;
+
+
+
+#define sequence_c(signal) ((int)*((*(signal))++))
+
+
+static int sequence_w(unsigned char **signal)
+{
+ int v = (*signal)[0] | ((*signal)[1] << 8);
+ *signal += 2;
+ return v;
+}
+
+
+static long sequence_l(unsigned char **signal)
+{
+ long v = (*signal)[0] | ((*signal)[1] << 8) | ((*signal)[2] << 16) | ((*signal)[3] << 24);
+ *signal += 4;
+ return v;
+}
+
+
+static long sequence_cl(unsigned char **signal)
+{
+ long v = sequence_c(signal);
+ if (v & 0x80) {
+ v &= 0x7F;
+ v |= sequence_c(signal) << 7;
+ if (v & 0x4000) {
+ v &= 0x3FFF;
+ v |= sequence_c(signal) << 14;
+ if (v & 0x200000) {
+ v &= 0x1FFFFF;
+ v |= sequence_c(signal) << 21;
+ if (v & 0x10000000) {
+ v &= 0x0FFFFFFF;
+ v |= sequence_c(signal) << 28;
+ }
+ }
+ }
+ }
+ return v;
+}
+
+
+
+static void *sequence_load_signal(DUH *duh, DUMBFILE *file)
+{
+ long size;
+ unsigned char *signal;
+
+ (void)duh;
+
+ size = dumbfile_igetl(file);
+ if (dumbfile_error(file) || size <= 0)
+ return NULL;
+
+ signal = malloc(size);
+ if (!signal)
+ return NULL;
+
+ if (dumbfile_getnc((char *)signal, size, file) < size) {
+ free(signal);
+ return NULL;
+ }
+
+ return signal;
+}
+
+
+
+static long render(
+ SEQUENCE_SAMPINFO *sampinfo,
+ float volume, float delta,
+ long pos, long size, sample_t **samples
+)
+{
+ sample_t **splptr;
+
+ SEQUENCE_PLAYING **playing_p = &sampinfo->playing;
+
+ long max_size = 0;
+ long part_size;
+
+ int n;
+ long i;
+
+ for (n = 0; n < sampinfo->n_channels; n++)
+ memset(samples[n] + pos, 0, size * sizeof(sample_t));
+
+ splptr = malloc(sampinfo->n_channels * sizeof(*splptr));
+ if (!splptr)
+ return 0;
+
+ splptr[0] = malloc(sampinfo->n_channels * size * sizeof(sample_t));
+ if (!splptr[0]) {
+ free(splptr);
+ return 0;
+ }
+
+ for (n = 1; n < sampinfo->n_channels; n++)
+ splptr[n] = splptr[n - 1] + size;
+
+ while (*playing_p) {
+ SEQUENCE_PLAYING *playing = *playing_p;
+
+ part_size = duh_signal_render_samples(
+ playing->sampinfo,
+ volume * (float)playing->volume * (1.0f / 65535.0f),
+ (float)(pow(DUMB_PITCH_BASE, playing->pitch) * delta),
+ size, splptr
+ );
+
+ for (n = 0; n < sampinfo->n_channels; n++)
+ for (i = 0; i < part_size; i++)
+ samples[n][pos+i] += splptr[n][i];
+
+ if (part_size > max_size)
+ max_size = part_size;
+
+ if (part_size < size) {
+ *playing_p = playing->next;
+ duh_signal_end_samples(playing->sampinfo);
+ free(playing);
+ } else
+ playing_p = &playing->next;
+ }
+
+ free(splptr[0]);
+ free(splptr);
+
+ return max_size;
+}
+
+
+
+/* 'offset' is added to the position at which the signal should start. It is
+ * currently assumed to be positive, and is currently only used when seeking
+ * forwards in the sequence.
+ */
+static void sequence_command(SEQUENCE_SAMPINFO *sampinfo, long offset)
+{
+ int command = sequence_c(&sampinfo->signal);
+
+ if (command == SEQUENCE_START_SIGNAL) {
+
+ int ref = sequence_c(&sampinfo->signal);
+ int sig = sequence_cl(&sampinfo->signal);
+ long pos = sequence_cl(&sampinfo->signal);
+ int volume = sequence_w(&sampinfo->signal);
+ int pitch = (int)(signed short)sequence_w(&sampinfo->signal);
+
+ SEQUENCE_PLAYING *playing = sampinfo->playing;
+
+ while (playing) {
+ if (playing->ref == ref)
+ playing->ref = -1;
+ playing = playing->next;
+ }
+
+ playing = malloc(sizeof(SEQUENCE_PLAYING));
+
+ if (playing) {
+ playing->sampinfo = duh_signal_start_samples(sampinfo->duh, sig, sampinfo->n_channels, pos + offset);
+
+ if (playing->sampinfo) {
+ playing->ref = ref;
+ playing->pitch = pitch;
+ playing->volume = volume;
+
+ playing->next = sampinfo->playing;
+ sampinfo->playing = playing;
+ } else
+ free(playing);
+ }
+
+ } else if (command == SEQUENCE_SET_VOLUME) {
+
+ int ref = sequence_c(&sampinfo->signal);
+ int volume = sequence_w(&sampinfo->signal);
+
+ SEQUENCE_PLAYING *playing = sampinfo->playing;
+
+ while (playing) {
+ if (playing->ref == ref) {
+ playing->volume = volume;
+ break;
+ }
+ playing = playing->next;
+ }
+
+ } else if (command == SEQUENCE_SET_PITCH) {
+
+ int ref = sequence_c(&sampinfo->signal);
+ int pitch = (int)(signed short)sequence_w(&sampinfo->signal);
+
+ SEQUENCE_PLAYING *playing = sampinfo->playing;
+
+ while (playing) {
+ if (playing->ref == ref) {
+ playing->pitch = pitch;
+ break;
+ }
+ playing = playing->next;
+ }
+
+ } else if (command == SEQUENCE_SET_PARAMETER) {
+
+ int ref = sequence_c(&sampinfo->signal);
+ unsigned char id = sequence_c(&sampinfo->signal);
+ long value = sequence_l(&sampinfo->signal);
+
+ SEQUENCE_PLAYING *playing = sampinfo->playing;
+
+ while (playing) {
+ if (playing->ref == ref) {
+ duh_signal_set_parameter(playing->sampinfo, id, value);
+ break;
+ }
+ playing = playing->next;
+ }
+
+ } else if (command == SEQUENCE_STOP_SIGNAL) {
+
+ int ref = sequence_c(&sampinfo->signal);
+
+ SEQUENCE_PLAYING **playing_p = &sampinfo->playing;
+
+ while (*playing_p) {
+ SEQUENCE_PLAYING *playing = *playing_p;
+
+ if (playing->ref == ref) {
+ duh_signal_end_samples(playing->sampinfo);
+ *playing_p = playing->next;
+ free(playing);
+ break;
+ }
+
+ playing_p = &playing->next;
+ }
+
+ } else {
+
+ TRACE("Error in sequence: unknown command %d.\n", command);
+ sampinfo->signal = NULL;
+
+ }
+}
+
+
+
+static void *sequence_start_samples(DUH *duh, void *signal, int n_channels, long pos)
+{
+ SEQUENCE_SAMPINFO *sampinfo;
+ long time = sequence_cl((unsigned char **)&signal);
+
+ if (time < 0)
+ return NULL;
+
+ sampinfo = malloc(sizeof(SEQUENCE_SAMPINFO));
+ if (!sampinfo)
+ return NULL;
+
+ sampinfo->duh = duh;
+ sampinfo->n_channels = n_channels;
+ sampinfo->signal = signal;
+ sampinfo->playing = NULL;
+
+ /* Seek to 'pos'. */
+ while (time < pos) {
+ pos -= time;
+
+ sequence_command(sampinfo, pos);
+
+ time = sequence_cl(&sampinfo->signal);
+
+ if (time < 0) {
+ sampinfo->signal = NULL;
+ return sampinfo;
+ }
+ }
+
+ sampinfo->time_left = time - pos;
+ sampinfo->sub_time_left = 0;
+
+ return sampinfo;
+}
+
+
+
+static long sequence_render_samples(
+ void *sampinfo,
+ float volume, float delta,
+ long size, sample_t **samples
+)
+{
+
+#define sampinfo ((SEQUENCE_SAMPINFO *)sampinfo)
+
+ long pos = 0;
+
+ int dt = (int)(delta * 65536.0f + 0.5f);
+
+ long todo;
+ LONG_LONG t;
+
+ while (sampinfo->signal) {
+ todo = (long)((((LONG_LONG)sampinfo->time_left << 16) | sampinfo->sub_time_left) / dt);
+
+ if (todo >= size)
+ break;
+
+ if (todo) {
+ render(sampinfo, volume, delta, pos, todo, samples);
+
+ pos += todo;
+ size -= todo;
+
+ todo = (long)((((LONG_LONG)sampinfo->time_left << 16) | sampinfo->sub_time_left) / dt);
+ t = sampinfo->sub_time_left - (LONG_LONG)todo * dt;
+ sampinfo->sub_time_left = (long)t & 65535;
+ sampinfo->time_left += (long)(t >> 16);
+ }
+
+ sequence_command(sampinfo, 0);
+
+ todo = sequence_cl(&sampinfo->signal);
+
+ if (todo >= 0)
+ sampinfo->time_left += todo;
+ else
+ sampinfo->signal = NULL;
+ }
+
+ if (sampinfo->signal) {
+ render(sampinfo, volume, delta, pos, size, samples);
+
+ pos += size;
+
+ t = sampinfo->sub_time_left - (LONG_LONG)size * dt;
+ sampinfo->sub_time_left = (long)t & 65535;
+ sampinfo->time_left += (long)(t >> 16);
+ } else
+ pos += render(sampinfo, volume, delta, pos, size, samples);
+
+ return pos;
+
+
+/** WARNING - remove this... */
+#if 0
+ float size_unified = size * delta;
+
+ int n;
+
+ sample_t **samples2 = malloc(sampinfo->n_channels * sizeof(*samples2));
+ memcpy(samples2, samples, sampinfo->n_channels * sizeof(*samples2));
+
+ while (sampinfo->signal && sampinfo->time < size_unified) {
+
+ {
+ long sz = (long)(sampinfo->time / delta);
+
+ if (sz)
+ render(sampinfo, volume, delta, sz, samples2);
+
+ for (n = 0; n < sampinfo->n_channels; n++)
+ samples2[n] += sz;
+
+ size -= sz;
+ pos += sz;
+
+ sampinfo->time -= sz * delta;
+ }
+
+ sequence_command(sampinfo, 0);
+
+ {
+ long time = sequence_cl(&sampinfo->signal);
+
+ if (time >= 0)
+ sampinfo->time += (float)time;
+ else
+ sampinfo->signal = NULL;
+ }
+
+ size_unified = size * delta;
+ }
+
+ if (sampinfo->signal) {
+ render(sampinfo, volume, delta, size, samples2);
+ sampinfo->time -= size_unified;
+ pos += size;
+ } else
+ pos += render(sampinfo, volume, delta, size, samples2);
+
+ free(samples2);
+
+ return pos;
+#endif
+
+#undef sampinfo
+
+}
+
+
+
+static void sequence_end_samples(void *sampinfo)
+{
+ SEQUENCE_PLAYING *playing = ((SEQUENCE_SAMPINFO *)sampinfo)->playing;
+
+ while (playing) {
+ SEQUENCE_PLAYING *next = playing->next;
+
+ duh_signal_end_samples(playing->sampinfo);
+ free(playing);
+
+ playing = next;
+ }
+
+ free(sampinfo);
+}
+
+
+
+static void sequence_unload_signal(void *signal)
+{
+ free(signal);
+}
+
+
+
+static DUH_SIGTYPE_DESC sigtype_sequence = {
+ SIGTYPE_SEQUENCE,
+ &sequence_load_signal,
+ &sequence_start_samples,
+ NULL,
+ &sequence_render_samples,
+ &sequence_end_samples,
+ &sequence_unload_signal
+};
+
+
+
+void dumb_register_sigtype_sequence(void)
+{
+ dumb_register_sigtype(&sigtype_sequence);
+}
diff --git a/plugins/dumb/dumb-kode54/src/sigtypes/sterpan.c b/plugins/dumb/dumb-kode54/src/sigtypes/sterpan.c
new file mode 100644
index 00000000..22fe5d28
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/sigtypes/sterpan.c
@@ -0,0 +1,206 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * sterpan.c - The stereo pan (SPAN) signal type. / / \ \
+ * | < / \_
+ * By entheh. | \/ /\ /
+ * \_ / > /
+ * This takes a single monaural signal and | \ / /
+ * expands it to two channels, applying a | ' /
+ * stereo pan in the process. The stereo pan \__/
+ * is generated by delaying and damping the
+ * channel opposite the sound source. If only
+ * one channel is requested of this signal, it will simply chain to the other
+ * signal.
+ *
+ * In order for the delay to work properly, this must be played at 65536 Hz.
+ * The pitch at which you want the sample to play can be passed in parameter
+ * #1. Parameter #0 specifies the panning position, -256 to 256.
+ *
+ * NOTE: THIS IS NOT HOW IT WORKS AT THE MOMENT. AT THE MOMENT, THIS ROUTINE
+ * SIMPLY VARIES THE VOLUMES.
+ */
+
+#include <stdlib.h>
+
+#include "dumb.h"
+
+
+
+#define SIGTYPE_STEREOPAN DUMB_ID('S','P','A','N')
+
+
+
+#define SPANPARAM_PAN 0
+
+
+
+typedef struct STEREOPAN_SIGNAL
+{
+ int sig;
+}
+STEREOPAN_SIGNAL;
+
+
+
+typedef struct STEREOPAN_SAMPINFO
+{
+ float pan;
+ int stereo;
+ DUH_SIGNAL_SAMPINFO *csampinfo;
+}
+STEREOPAN_SAMPINFO;
+
+
+
+static void *stereopan_load_signal(DUH *duh, DUMBFILE *file)
+{
+ STEREOPAN_SIGNAL *signal;
+
+ (void)duh;
+
+ signal = malloc(sizeof(*signal));
+
+ if (!signal)
+ return NULL;
+
+ signal->sig = dumbfile_igetl(file);
+
+ if (dumbfile_error(file)) {
+ free(signal);
+ return NULL;
+ }
+
+ return signal;
+}
+
+
+
+static void *stereopan_start_samples(DUH *duh, void *signal, int n_channels, long pos)
+{
+ STEREOPAN_SAMPINFO *sampinfo;
+
+#define signal ((STEREOPAN_SIGNAL *)signal)
+
+ if ((unsigned int)(n_channels - 1) >= 2) {
+ TRACE("Stereo pan signal requiring 1 or 2 channels called with %d channels.\n", n_channels);
+ return NULL;
+ }
+
+ sampinfo = malloc(sizeof(*sampinfo));
+ if (!sampinfo)
+ return NULL;
+
+ sampinfo->pan = 0;
+
+ sampinfo->stereo = n_channels - 1;
+
+ sampinfo->csampinfo = duh_signal_start_samples(duh, signal->sig, 1, pos);
+ if (!sampinfo->csampinfo) {
+ free(sampinfo);
+ return NULL;
+ }
+
+#undef signal
+
+ return sampinfo;
+}
+
+
+
+static void stereopan_set_parameter(void *sampinfo, unsigned char id, long value)
+{
+#define sampinfo ((STEREOPAN_SAMPINFO *)sampinfo)
+
+ if (id == SPANPARAM_PAN && value >= -256 && value <= 256)
+ sampinfo->pan = value * (1.0f / 256.0f);
+
+#undef sampinfo
+}
+
+
+
+static long stereopan_render_samples(
+ void *sampinfo,
+ float volume, float delta,
+ long size, sample_t **samples
+)
+{
+#define sampinfo ((STEREOPAN_SAMPINFO *)sampinfo)
+
+ if (!sampinfo->stereo)
+ return duh_signal_render_samples(sampinfo->csampinfo, volume, delta, size, samples);
+
+ if (sampinfo->pan >= 0) {
+ long sz = duh_signal_render_samples(sampinfo->csampinfo, volume * (1.0f + sampinfo->pan), delta, size, samples + 1);
+ long s;
+ int vol;
+
+ volume = (1.0f - sampinfo->pan) / (1.0f + sampinfo->pan);
+ vol = (int)(volume * 65536 + 0.5);
+
+ for (s = 0; s < sz; s++)
+ samples[0][s] = (samples[1][s] * vol) >> 16;
+
+ return sz;
+ } else {
+ long sz = duh_signal_render_samples(sampinfo->csampinfo, volume * (1.0f - sampinfo->pan), delta, size, samples);
+ long s;
+ int vol;
+
+ volume = (1.0f + sampinfo->pan) / (1.0f - sampinfo->pan);
+ vol = (int)(volume * 65536 + 0.5);
+
+ for (s = 0; s < sz; s++)
+ samples[1][s] = (samples[0][s] * vol) >> 16;
+
+ return sz;
+ }
+
+#undef sampinfo
+}
+
+
+
+static void stereopan_end_samples(void *sampinfo)
+{
+#define sampinfo ((STEREOPAN_SAMPINFO *)sampinfo)
+
+ duh_signal_end_samples(sampinfo->csampinfo);
+ free(sampinfo);
+
+#undef sampinfo
+}
+
+
+
+static void stereopan_unload_signal(void *signal)
+{
+ free(signal);
+}
+
+
+
+static DUH_SIGTYPE_DESC sigtype_stereopan = {
+ SIGTYPE_STEREOPAN,
+ &stereopan_load_signal,
+ &stereopan_start_samples,
+ &stereopan_set_parameter,
+ &stereopan_render_samples,
+ &stereopan_end_samples,
+ &stereopan_unload_signal
+};
+
+
+
+void dumb_register_sigtype_stereopan(void)
+{
+ dumb_register_sigtype(&sigtype_stereopan);
+}
diff --git a/plugins/dumb/dumb-kode54/src/tools/it/load_it.cpp b/plugins/dumb/dumb-kode54/src/tools/it/load_it.cpp
new file mode 100644
index 00000000..ed88e706
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/tools/it/load_it.cpp
@@ -0,0 +1,824 @@
+#ifdef FORTIFY
+#include "fortify.h"
+#endif
+#include <stdio.h>
+#ifdef MSS
+#include "mss.h"
+#endif
+
+#include <string.h>
+
+#include "allegro.h"
+#include "modulus.h"
+#include "typedef.hpp"
+
+int detect_it(char *f) {
+ int sig;
+ PACKFILE *fn = pack_fopen(f, "rb");
+
+ if (fn == NULL)
+ return FALSE;
+
+ sig = pack_mgetl(fn);
+ if (sig != AL_ID('I','M','P','M')) {
+ pack_fclose(fn);
+ return FALSE;
+ }
+ pack_fclose(fn);
+
+ return TRUE;
+}
+
+MODULUS *create_it() {
+ MODULUS *m = (MODULUS*)malloc(sizeof(MODULUS));
+ if (!m)
+ return NULL;
+ memset(m, 0, sizeof(MODULUS));
+ return m;
+}
+
+void destroy_it(MODULUS *j) {
+
+ if (song->Music == j)
+ stop_it();
+
+ //remove patterns:
+ for (int i=0; i<j->NumPatterns; i++) {
+ free(j->Pattern[i].Note);
+ }
+ if (j->Pattern)
+ free(j->Pattern);
+ //remove instruments;
+ if (j->Instrument)
+ free(j->Instrument);
+ //remove samples;
+ for (int i=0; i<j->NumSamples; i++) {
+ destroy_sample(j->Sample[i].Sample);
+ }
+ if (j->Sample)
+ free(j->Sample);
+ //remove orders:
+ if (j->Order)
+ free(j->Order);
+ //remove channels:
+ for (int i=0; i<64; i++) {
+ if (j->Channel[i].VChannel) {
+ MODULUS_VCHANNEL *vchn = song->Music->Channel[i].VChannel;
+ MODULUS_VCHANNEL *prev = NULL;
+
+ if (!vchn)
+ continue;
+
+ for (;;) {
+ deallocate_voice(vchn->voice);
+
+ prev = vchn;
+ vchn = vchn->next;
+ free(prev);
+
+ if (!vchn)
+ break;
+ }
+ }
+ }
+ free(j);
+}
+
+//#define DEBUG_IT_SIZE
+
+int get_module_size(MODULUS *j) {
+ int a, b, c, d = 0, e;
+ a = sizeof(MODULUS) + j->NumOrders;
+ b = j->NumInstruments * sizeof(MODULUS_INSTRUMENT);
+ c = j->NumSamples * sizeof(MODULUS_SAMPLE);
+
+ for (int i=0; i<j->NumSamples; i++)
+ d += j->Sample[i].SampleLength * (j->Sample[i].Flag & 2 ? sizeof(short) : 1) * (j->Sample[i].Flag & 4 ? 2: 1);
+
+ e = 4 + sizeof(MODULUS_PATTERN) * j->NumPatterns;
+
+ for (int i=0; i<j->NumPatterns; i++)
+ e += j->Pattern[i].NumNotes * sizeof(MODULUS_NOTE);
+ #ifdef DEBUG_IT_SIZE
+ printf("Base: %i, Instruments(%i): %i, Samples(%i): %i, Data: %i, Patterns(%i): %i\n", a, j->NumInstruments, b, j->NumSamples, c, d, j->NumPatterns, e);
+ #endif
+
+ return a+b+c+d+e;
+}
+
+#define MAX_IT_CHN 64
+
+//#define DEBUG_HEADER
+//#define DEBUG_INSTRUMENTS
+//#define DEBUG_SAMPLES
+//#define DEBUG_PATTERNS
+
+static dword *sourcebuf = NULL;
+static dword *sourcepos = NULL;
+static byte rembits = 0;
+
+int readblock(PACKFILE *f) {
+ long size;
+ int c = pack_igetw(f);
+ if (c == -1)
+ return 0;
+ size = c;
+
+ sourcebuf = (dword*)malloc(size+4);
+ if (!sourcebuf)
+ return 0;
+
+ c = pack_fread(sourcebuf, size, f);
+ if (c < 1) {
+ free(sourcebuf);
+ sourcebuf = NULL;
+ return 0;
+ }
+ sourcepos = sourcebuf;
+ rembits = 32;
+ return 1;
+}
+
+void freeblock() {
+ if (sourcebuf)
+ free(sourcebuf);
+ sourcebuf = NULL;
+}
+
+dword readbits(char b) {
+ dword val;
+ if (b <= rembits) {
+ val = *sourcepos & ((1 << b) - 1);
+ *sourcepos >>= b;
+ rembits -= b;
+ }
+ else {
+ dword nbits = b - rembits;
+ val = *sourcepos;
+ sourcepos++;
+ val |= ((*sourcepos & ((1 << nbits) - 1)) << rembits);
+ *sourcepos >>= nbits;
+ rembits = 32 - nbits;
+ }
+ return val;
+}
+
+void decompress8(PACKFILE *f, void *data, int len, int tver) {
+ char *destbuf = (char*)data;
+ char *destpos = destbuf;
+ int blocklen, blockpos;
+ byte bitwidth;
+ word val;
+ char d1, d2;
+
+ memset(destbuf, 0, len);
+
+ while (len>0) {
+ //Read a block of compressed data:
+ if (!readblock(f))
+ return;
+ //Set up a few variables
+ blocklen = (len < 0x8000) ? len : 0x8000; //Max block length is 0x8000 bytes
+ blockpos = 0;
+ bitwidth = 9;
+ d1 = d2 = 0;
+ //Start the decompression:
+ while (blockpos < blocklen) {
+ //Read a value:
+ val = readbits(bitwidth);
+ //Check for bit width change:
+
+ if (bitwidth < 7) { //Method 1:
+ if (val == (1 << (bitwidth - 1))) {
+ val = readbits(3) + 1;
+ bitwidth = (val < bitwidth) ? val : val + 1;
+ continue;
+ }
+ }
+ else if (bitwidth < 9) { //Method 2
+ byte border = (0xFF >> (9 - bitwidth)) - 4;
+
+ if (val > border && val <= (border + 8)) {
+ val -= border;
+ bitwidth = (val < bitwidth) ? val : val + 1;
+ continue;
+ }
+ }
+ else if (bitwidth == 9) { //Method 3
+ if (val & 0x100) {
+ bitwidth = (val + 1) & 0xFF;
+ continue;
+ }
+ }
+ else { //Illegal width, abort ?
+ freeblock();
+ return;
+ }
+
+ //Expand the value to signed byte:
+ char v; //The sample value:
+ if (bitwidth < 8) {
+ byte shift = 8 - bitwidth;
+ v = (val << shift);
+ v >>= shift;
+ }
+ else
+ v = (char)val;
+
+ //And integrate the sample value
+ //(It always has to end with integration doesn't it ? ;-)
+ d1 += v;
+ d2 += d1;
+
+ //Store !
+ *destpos = ((tver == 0x215) ? d2 : d1);
+ destpos++;
+ blockpos++;
+ }
+ freeblock();
+ len -= blocklen;
+ }
+ return;
+}
+
+void decompress16(PACKFILE *f, void *data, int len, int tver) {
+ //make the output buffer:
+ short *destbuf = (short*)data;
+ short *destpos = destbuf;
+ int blocklen, blockpos;
+ byte bitwidth;
+ long val;
+ short d1, d2;
+
+ memset(destbuf, 0, len);
+
+ while (len>0) {
+ //Read a block of compressed data:
+ if (!readblock(f))
+ return;
+ //Set up a few variables
+ blocklen = (len < 0x4000) ? len : 0x4000; // Max block length is 0x4000 bytes
+ blockpos = 0;
+ bitwidth = 17;
+ d1 = d2 = 0;
+ //Start the decompression:
+ while (blockpos < blocklen) {
+ val = readbits(bitwidth);
+ //Check for bit width change:
+
+ if (bitwidth < 7) { //Method 1:
+ if (val == (1 << (bitwidth - 1))) {
+ val = readbits(4) + 1;
+ bitwidth = (val < bitwidth) ? val : val + 1;
+ continue;
+ }
+ }
+ else if (bitwidth < 17) { //Method 2
+ word border = (0xFFFF >> (17 - bitwidth)) - 8;
+
+ if (val > border && val <= (border + 16)) {
+ val -= border;
+ bitwidth = val < bitwidth ? val : val + 1;
+ continue;
+ }
+ }
+ else if (bitwidth == 17) { //Method 3
+ if (val & 0x10000) {
+ bitwidth = (val + 1) & 0xFF;
+ continue;
+ }
+ }
+ else { //Illegal width, abort ?
+ freeblock();
+ return;
+ }
+
+ //Expand the value to signed byte:
+ short v; //The sample value:
+ if (bitwidth < 16) {
+ byte shift = 16 - bitwidth;
+ v = (val << shift);
+ v >>= shift;
+ }
+ else
+ v = (short)val;
+
+ //And integrate the sample value
+ //(It always has to end with integration doesn't it ? ;-)
+ d1 += v;
+ d2 += d1;
+
+ //Store !
+ *destpos = ((tver == 0x215) ? d2 : d1);
+ destpos++;
+ blockpos++;
+ }
+ freeblock();
+ len -= blocklen;
+ }
+ return;
+}
+
+MODULUS *load_it(char *file) {
+ PACKFILE *f;
+ MODULUS *j = create_it();
+ int tver, tver2, flag, msglen, msgoffs;
+ int *insoffs = NULL, *samoffs = NULL, *patoffs = NULL;
+
+ if (!j)
+ return NULL;
+
+ if (!detect_it(file))
+ return NULL;
+
+ f = pack_fopen(file, "rb");
+
+ if (!f) {
+ #ifdef DEBUG_HEADER
+ printf("Error Opening!\n");
+ #endif
+ return NULL;
+ }
+
+ pack_fseek(f, 30);
+ pack_igetw(f); //I have no idea...
+
+ j->NumOrders = pack_igetw(f);
+ j->NumInstruments = pack_igetw(f);
+ j->NumSamples = pack_igetw(f);
+ j->NumPatterns = pack_igetw(f);
+
+ #ifdef DEBUG_HEADER
+ printf("Loading IT: %i Orders %i Instruments, %i Samples, %i Patterns\n", j->NumOrders, j->NumInstruments, j->NumSamples, j->NumPatterns);
+ #endif
+
+ tver = pack_igetw(f);
+ j->Version = tver2 = pack_igetw(f);
+
+ #ifdef DEBUG_HEADER
+ printf("Tracker ver: %X, %X\n", tver, tver2);
+ #endif
+
+ j->Flags = pack_igetw(f);
+ flag = pack_igetw(f);
+
+ j->GlobalVolume = pack_getc(f);
+ j->MixVolume = pack_getc(f);
+ j->Speed = pack_getc(f);
+ j->Tempo = pack_getc(f);
+ j->PanningSeperation = pack_getc(f);
+
+ #ifdef DEBUG_HEADER
+ printf("Global Volume: %i, Mixing Volume: %i, Speed: %i, Tempo: %i, PanSep: %i\n", j->GlobalVolume, j->MixVolume, j->Speed, j->Tempo, j->PanningSeperation);
+ #endif
+
+ pack_getc(f); //Damn....I need more info on this.
+
+ msglen = pack_igetw(f);
+ msgoffs = pack_igetl(f);
+
+ pack_fseek(f, 4);
+
+ #ifdef DEBUG_HEADER
+ printf("Channel Pan:");
+ #endif
+
+ for (int i=0; i<MAX_IT_CHN; i++) {
+ j->Channel[i].Pan = pack_getc(f);
+ #ifdef DEBUG_HEADER
+ printf(" %i", j->Channel[i].Pan);
+ #endif
+ }
+ #ifdef DEBUG_HEADER
+ printf("\nChannel Vol:");
+ #endif
+ for (int i=0; i<MAX_IT_CHN; i++) {
+ j->Channel[i].Volume = pack_getc(f);
+ #ifdef DEBUG_HEADER
+ printf(" %i", j->Channel[i].Volume);
+ #endif
+ }
+ #ifdef DEBUG_HEADER
+ printf("\n");
+ #endif
+
+ j->Order = (unsigned char *)malloc(j->NumOrders);
+ pack_fread(j->Order, j->NumOrders, f);
+
+ if (j->NumInstruments)
+ insoffs = (int*)malloc(4 * j->NumInstruments);
+ if (j->NumSamples)
+ samoffs = (int*)malloc(4 * j->NumSamples);
+ if (j->NumPatterns)
+ patoffs = (int*)malloc(4 * j->NumPatterns);
+
+ pack_fread(insoffs, 4 * j->NumInstruments, f);
+ pack_fread(samoffs, 4 * j->NumSamples, f);
+ pack_fread(patoffs, 4 * j->NumPatterns, f);
+
+ if (flag&1) { //Song message attached
+ //Ignore.
+ }
+ if (flag & 4) { //skip something:
+ short u;
+ char dummy[8];
+ u = pack_igetw(f);
+ for (int i=0; i<u; u++)
+ pack_fread(dummy, 8, f);
+ }
+ if (flag & 8) { //MIDI commands ???
+ char dummy[33];
+ for (int i=0; i<9+16+128; i++)
+ pack_fread(dummy, 32, f);
+
+ }
+
+ if (j->NumInstruments)
+ j->Instrument = (MODULUS_INSTRUMENT*)malloc(sizeof(MODULUS_INSTRUMENT) * j->NumInstruments);
+ #ifdef DEBUG_INSTRUMENTS
+ if (!j->Instrument)
+ printf("No Mem for Instruments!\n");
+ #endif
+
+
+ for (int i=0; i<j->NumInstruments; i++) {
+ pack_fclose(f);
+ f = pack_fopen(file, "rb");
+ #ifdef DEBUG_INSTRUMENTS
+ if (!f)
+ printf("Error Opening!\n");
+ #endif
+ pack_fseek(f, insoffs[i] + 17);
+
+ j->Instrument[i].NewNoteAction = pack_getc(f);
+ j->Instrument[i].DuplicateCheckType = pack_getc(f);
+ j->Instrument[i].DuplicateCheckAction = pack_getc(f);
+ j->Instrument[i].FadeOut = pack_igetw(f);
+ j->Instrument[i].PitchPanSeperation = pack_getc(f);
+ j->Instrument[i].PitchPanCenter = pack_getc(f);
+ j->Instrument[i].GlobalVolume = pack_getc(f);
+ j->Instrument[i].DefaultPan = pack_getc(f);
+ #ifdef DEBUG_INSTRUMENTS
+ printf("I%02i @ 0x%X, NNA %i, DCT %i, DCA %i, FO %i, PPS %i, PPC %i, GVol %i, DPan %i\n", i, insoffs[i], j->Instrument[i].NewNoteAction, j->Instrument[i].DuplicateCheckType, j->Instrument[i].DuplicateCheckAction, j->Instrument[i].FadeOut, j->Instrument[i].PitchPanSeperation, j->Instrument[i].PitchPanCenter, j->Instrument[i].GlobalVolume, j->Instrument[i].DefaultPan);
+ #endif
+
+ pack_fseek(f, 38);
+
+ for (int k=0; k<120; k++) {
+ j->Instrument[i].NoteNote[k] = pack_getc(f);
+ j->Instrument[i].NoteSample[k] = pack_getc(f) - 1;
+ }
+
+ j->Instrument[i].VolumeEnvelope.Flag = pack_getc(f);
+ j->Instrument[i].VolumeEnvelope.NumNodes = pack_getc(f);
+ j->Instrument[i].VolumeEnvelope.LoopBegin = pack_getc(f);
+ j->Instrument[i].VolumeEnvelope.LoopEnd = pack_getc(f);
+ j->Instrument[i].VolumeEnvelope.SustainLoopBegin = pack_getc(f);
+ j->Instrument[i].VolumeEnvelope.SustainLoopEnd = pack_getc(f);
+ for (int k=0; k<j->Instrument[i].VolumeEnvelope.NumNodes; k++) {
+ j->Instrument[i].VolumeEnvelope.NodeY[k] = pack_getc(f);
+ j->Instrument[i].VolumeEnvelope.NodeTick[k] = pack_igetw(f);
+ }
+ pack_fseek(f, 75 - j->Instrument[i].VolumeEnvelope.NumNodes * 3);
+
+ j->Instrument[i].PanningEnvelope.Flag = pack_getc(f);
+ j->Instrument[i].PanningEnvelope.NumNodes = pack_getc(f);
+ j->Instrument[i].PanningEnvelope.LoopBegin = pack_getc(f);
+ j->Instrument[i].PanningEnvelope.LoopEnd = pack_getc(f);
+ j->Instrument[i].PanningEnvelope.SustainLoopBegin = pack_getc(f);
+ j->Instrument[i].PanningEnvelope.SustainLoopEnd = pack_getc(f);
+ for (int k=0; k<j->Instrument[i].PanningEnvelope.NumNodes; k++) {
+ j->Instrument[i].PanningEnvelope.NodeY[k] = pack_getc(f);
+ j->Instrument[i].PanningEnvelope.NodeTick[k] = pack_igetw(f);
+ }
+ pack_fseek(f, 75 - j->Instrument[i].PanningEnvelope.NumNodes * 3);
+
+ j->Instrument[i].PitchEnvelope.Flag = pack_getc(f);
+ j->Instrument[i].PitchEnvelope.NumNodes = pack_getc(f);
+ j->Instrument[i].PitchEnvelope.LoopBegin = pack_getc(f);
+ j->Instrument[i].PitchEnvelope.LoopEnd = pack_getc(f);
+ j->Instrument[i].PitchEnvelope.SustainLoopBegin = pack_getc(f);
+ j->Instrument[i].PitchEnvelope.SustainLoopEnd = pack_getc(f);
+ for (int k=0; k<j->Instrument[i].PitchEnvelope.NumNodes; k++) {
+ j->Instrument[i].PitchEnvelope.NodeY[k] = pack_getc(f);
+ j->Instrument[i].PitchEnvelope.NodeTick[k] = pack_igetw(f);
+ }
+ }
+
+ if (j->NumSamples)
+ j->Sample = (MODULUS_SAMPLE*)malloc(sizeof(MODULUS_SAMPLE) * j->NumSamples);
+
+ #ifdef DEBUG_SAMPLES
+ if (!j->Sample)
+ printf("No Mem for Samples!\n");
+ #endif
+
+ for (int i=0; i<j->NumSamples; i++) {
+ int sam_samptr, convert;
+
+ pack_fclose(f);
+ f = pack_fopen(file, "rb");
+ #ifdef DEBUG_SAMPLES
+ if (!f)
+ printf("Error opening!\n");
+ #endif
+
+ pack_fseek(f, samoffs[i] + 17);
+
+ j->Sample[i].GlobalVolume = pack_getc(f);
+ j->Sample[i].Flag = pack_getc(f);
+ j->Sample[i].Volume = pack_getc(f);
+
+ #ifdef DEBUG_SAMPLES
+ printf("S%02i @ 0x%X, Vol: %i/%i, Flag: %i", i, samoffs[i], j->Sample[i].GlobalVolume, j->Sample[i].Volume, j->Sample[i].Flag);
+ #endif
+
+ pack_fseek(f, 26);
+
+ convert = pack_getc(f);
+ pack_getc(f); //Panning ?
+
+ j->Sample[i].SampleLength = pack_igetl(f);
+ j->Sample[i].LoopBegin = pack_igetl(f);
+ j->Sample[i].LoopEnd = pack_igetl(f);
+ j->Sample[i].C5Speed = pack_igetl(f);
+ j->Sample[i].SustainLoopBegin = pack_igetl(f);
+ j->Sample[i].SustainLoopEnd = pack_igetl(f);
+
+ #ifdef DEBUG_SAMPLES
+ printf(", SLen: %i, LpB: %i, LpE: %i, C5S: %i\n", j->Sample[i].SampleLength, j->Sample[i].LoopBegin, j->Sample[i].LoopEnd, j->Sample[i].C5Speed);
+ #endif
+
+ sam_samptr = pack_igetl(f);
+
+ j->Sample[i].VibratoSpeed = pack_getc(f);
+ j->Sample[i].VibratoDepth = pack_getc(f);
+ j->Sample[i].VibratoRate = pack_getc(f);
+ j->Sample[i].VibratoWaveForm = pack_getc(f);
+
+ #ifdef DEBUG_SAMPLES
+ printf("SusLpB: %i, SusLpE: %i, VibSp: %i, VibDep: %i, VibWav: %i, VibRat: %i\n", j->Sample[i].SustainLoopBegin, j->Sample[i].SustainLoopEnd, j->Sample[i].VibratoSpeed, j->Sample[i].VibratoDepth, j->Sample[i].VibratoWaveForm, j->Sample[i].VibratoRate);
+ #endif
+
+ if (j->Sample[i].Flag & 1 == 0)
+ continue;
+
+ pack_fclose(f);
+ f = pack_fopen(file, "rb");
+ pack_fseek(f, sam_samptr);
+
+ int len = j->Sample[i].SampleLength * (j->Sample[i].Flag & 2 ? sizeof(short) : 1) * (j->Sample[i].Flag & 4 ? 2: 1);
+
+ #ifdef DEBUG_SAMPLES
+ printf("Len: %i, Size: %i KB\n", j->Sample[i].SampleLength, len/1024);
+ #endif
+
+ SAMPLE *sam = create_sample(j->Sample[i].Flag & 2 ? 16 : 8, j->Sample[i].Flag & 4 ? TRUE : FALSE, j->Sample[i].C5Speed, j->Sample[i].SampleLength);
+
+ if (j->Sample[i].Flag & 8) { // If the sample is packed, then we must unpack it
+ if (j->Sample[i].Flag & 2)
+ decompress16(f, sam->data, j->Sample[i].SampleLength, tver2);
+ else
+ decompress8(f, sam->data, j->Sample[i].SampleLength, tver2);
+ } else {
+ pack_fread(sam->data, len, f);
+ }
+
+ if (j->Sample[i].Flag & SAMPLE_USELOOP) {
+ sam->loop_start = j->Sample[i].LoopBegin;
+ sam->loop_end = j->Sample[i].LoopEnd;
+ }
+
+ j->Sample[i].Sample = sam;
+
+ void *dat = sam->data;
+
+ if (convert & 2) { //Change the byte order for 16-bit samples:
+ if (sam->bits == 16) {
+ for (int k=0; k<len; k+=2) {
+ int l = ((char*)dat)[k];
+ ((char*)dat)[k] = ((char*)dat)[k+1];
+ ((char*)dat)[k+1] = l;
+
+ }
+ }
+ else {
+ for (int k=0; k<len; k+=2) {
+ int l = ((char*)dat)[k];
+ ((char*)dat)[k] = ((char*)dat)[k+1];
+ ((char*)dat)[k+1] = l;
+
+ }
+ }
+ }
+ if (convert & 1) { //Convert to unsigned
+ if (sam->bits == 8) {
+ for (int k=0; k<len; k++) {
+ ((char*)dat)[k] ^= 0x80;
+ }
+ }
+ else {
+ for (int k=0; k<(len>>1); k++) {
+ ((short*)dat)[k] ^= 0x8000;
+ }
+ }
+ }
+ }
+
+ if (j->NumPatterns)
+ j->Pattern = (MODULUS_PATTERN*)malloc(sizeof(MODULUS_PATTERN) * j->NumPatterns);
+ unsigned char *buf = (unsigned char*)alloca(65536);
+ unsigned char *cmask = (unsigned char*)alloca(64),
+ *cnote = (unsigned char*)alloca(64),
+ *cinstrument = (unsigned char*)alloca(64),
+ *cvol = (unsigned char*)alloca(64),
+ *ccom = (unsigned char*)alloca(64),
+ *ccomval = (unsigned char*)alloca(64);
+
+ for (int i=0; i<j->NumPatterns; i++) {
+ int numnotes = 0, len, pos = 0, mask = 0, chn = 0;
+
+ memset(cmask, 0, 64);
+ memset(cnote, 0, 64);
+ memset(cinstrument, 0, 64);
+ memset(cvol, 0, 64);
+ memset(ccom, 0, 64);
+ memset(ccomval, 0, 64);
+
+ pack_fclose(f);
+ f = pack_fopen(file, "rb");
+ pack_fseek(f, patoffs[i]);
+
+ len = pack_igetw(f);
+ j->Pattern[i].NumRows = pack_igetw(f);
+
+ pack_fseek(f, 4);
+ pack_fread(buf, len, f);
+
+ while (pos < len) {
+ int b = buf[pos];
+ pos++;
+ if (!b) { //If end of row:
+ numnotes++;
+ continue;
+ }
+ chn = (b - 1) & 63;
+
+ if (b & 128) {
+ mask = buf[pos];
+ pos++;
+ cmask[chn] = mask;
+ }
+ else
+ mask = cmask[chn];
+
+ if (mask)
+ numnotes++;
+ if (mask & 1)
+ pos++;
+ if (mask & 2)
+ pos++;
+ if (mask & 4)
+ pos++;
+ if (mask & 8)
+ pos+=2; //Guessing here
+ }
+ j->Pattern[i].NumNotes = numnotes;
+ j->Pattern[i].Note = (MODULUS_NOTE*)malloc(sizeof(MODULUS_NOTE) * numnotes);
+ memset(j->Pattern[i].Note, 0, sizeof(MODULUS_NOTE) * numnotes);
+
+ pos = 0;
+ memset(cmask, 0, 64);
+ mask = 0;
+ numnotes = 0;
+ while (pos < len) {
+ int b = buf[pos];
+ #ifdef DEBUG_PATTERNS
+ printf("NumNote: %i ", numnotes);
+ #endif
+
+ pos++;
+ if (!b) { //If end of row:
+ j->Pattern[i].Note[numnotes].Channel = -1;
+ numnotes++;
+ #ifdef DEBUG_PATTERNS
+ printf("Channel: -1\n");
+ #endif
+ continue;
+ }
+ chn = (b - 1) & 63;
+
+ if (b & 128) {
+ mask = buf[pos];
+ pos++;
+ cmask[chn] = mask;
+ }
+ else
+ mask = cmask[chn];
+ #ifdef DEBUG_PATTERNS
+ printf("Channel: %i Mask: %i ", chn, mask);
+ #endif
+
+ if (mask)
+ j->Pattern[i].Note[numnotes].Channel = chn;
+
+ if (mask & 1) {
+ j->Pattern[i].Note[numnotes].Note = buf[pos];
+ j->Pattern[i].Note[numnotes].Mask |= 1;
+ cnote[chn] = buf[pos];
+ #ifdef DEBUG_PATTERNS
+ printf("Note: %i ", buf[pos]);
+ #endif
+ pos++;
+ }
+ if (mask & 2) {
+ j->Pattern[i].Note[numnotes].Instrument = buf[pos];
+ j->Pattern[i].Note[numnotes].Mask |= 2;
+ cinstrument[chn] = buf[pos];
+ #ifdef DEBUG_PATTERNS
+ printf("Inst: %i ", buf[pos]);
+ #endif
+ pos++;
+ }
+ if (mask & 4) {
+ if (buf[pos] <= 64 || (buf[pos] >= 128 && buf[pos] <= 192))
+ if (buf[pos] <= 64) {
+ j->Pattern[i].Note[numnotes].Volume = buf[pos];
+ j->Pattern[i].Note[numnotes].Mask |= 4;
+ }
+ else {
+ j->Pattern[i].Note[numnotes].Panning = buf[pos] - 128;
+ j->Pattern[i].Note[numnotes].Mask |= 8;
+ }
+ #ifdef DEBUG_PATTERNS
+ printf("Vol: %i ", buf[pos]);
+ #endif
+ cvol[chn] = buf[pos];
+ pos++;
+ }
+ if (mask & 8) {
+ j->Pattern[i].Note[numnotes].Command = buf[pos];
+ j->Pattern[i].Note[numnotes].CommandValue = buf[pos+1];
+ j->Pattern[i].Note[numnotes].Mask |= 16;
+ ccom[chn] = buf[pos];
+ ccomval[chn] = buf[pos+1];
+ #ifdef DEBUG_PATTERNS
+ printf("Com: %i CommArg: %i ", buf[pos], buf[pos+1]);
+ #endif
+ pos+=2;
+ }
+ if (mask & 16) {
+ j->Pattern[i].Note[numnotes].Note = cnote[chn];
+ j->Pattern[i].Note[numnotes].Mask |= 1;
+ #ifdef DEBUG_PATTERNS
+ printf("LNote: %i ", cnote[chn]);
+ #endif
+ }
+ if (mask & 32) {
+ j->Pattern[i].Note[numnotes].Instrument = cinstrument[chn];
+ j->Pattern[i].Note[numnotes].Mask |= 2;
+ #ifdef DEBUG_PATTERNS
+ printf("LInst: %i ", cinstrument[chn]);
+ #endif
+ }
+ if (mask & 64) {
+ if (cvol[chn] <= 64 || (cvol[chn] >= 128 && cvol[chn] <= 192))
+ if (cvol[chn] <= 64) {
+ j->Pattern[i].Note[numnotes].Volume = cvol[chn];
+ j->Pattern[i].Note[numnotes].Mask |= 4;
+ }
+ else {
+ j->Pattern[i].Note[numnotes].Panning = cvol[chn] - 128;
+ j->Pattern[i].Note[numnotes].Mask |= 8;
+ }
+ #ifdef DEBUG_PATTERNS
+ printf("LVol: %i ", cvol[chn]);
+ #endif
+ }
+ if (mask & 128) {
+ j->Pattern[i].Note[numnotes].Command = ccom[chn];
+ j->Pattern[i].Note[numnotes].CommandValue = ccomval[chn];
+ j->Pattern[i].Note[numnotes].Mask |= 16;
+ #ifdef DEBUG_PATTERNS
+ printf("LCom: %i LComArg: %i ", ccom[chn], ccomval[chn]);
+ #endif
+ }
+ #ifdef DEBUG_PATTERNS
+ printf("\n");
+ #endif
+ if (mask)
+ numnotes++;
+ #ifdef DEBUG_PATTERNS
+ rest(1000);
+ #endif
+ }
+ }
+ if (insoffs)
+ free(insoffs);
+ if (samoffs)
+ free(samoffs);
+ if (patoffs)
+ free(patoffs);
+
+ return j;
+}
diff --git a/plugins/dumb/dumb-kode54/src/tools/it/modulus.h b/plugins/dumb/dumb-kode54/src/tools/it/modulus.h
new file mode 100644
index 00000000..b17d6ff6
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/tools/it/modulus.h
@@ -0,0 +1,193 @@
+
+#define MUSIC_IT AL_ID('I','M','P','M')
+
+typedef struct MODULUS_MUSIC_INFO {
+ char Name[29];
+ int Type;
+} MODULUS_MUSIC_INFO;
+
+#define ENVELOPE_ON 1
+#define ENVELOPE_LOOP_ON 2
+#define ENVELOPE_SUSTAINLOOP 4
+
+typedef struct MODULUS_ENVELOPE {
+ unsigned char Flag,
+ NumNodes,
+ LoopBegin, LoopEnd, SustainLoopBegin, SustainLoopEnd; //in nodes.
+ char NodeY[25];
+ short NodeTick[25];
+} MODULUS_ENVELOPE;
+
+typedef struct MODULUS_VENVELOPE {
+ char CurNode;
+ unsigned char CurTick;
+ char End;
+ //float CVolume;
+ MODULUS_ENVELOPE *Envelope;
+} MODULUS_VENVELOPE;
+
+#define NNA_NOTECUT 1
+#define NNA_NOTECONTINUE 2
+#define NNA_NOTEOFF 3
+#define NNA_NOTEFADE 4
+
+#define DCT_OFF 0
+#define DCT_NOTE 1
+#define DCT_SAMPLE 2
+#define DCT_INSTRUMENT 3
+
+#define DCA_CUT 0
+#define DCA_NOTEOFF 1
+#define DCA_NOTEFADE 2
+
+typedef struct MOULUS_INSTRUMENT {
+ unsigned char Flag;
+ char VolumeLoopNodeStart, VolumeLoopNodeEnd;
+ char SustainLoopNodeStart, SustainLoopNodeEnd;
+ char DuplicateCheckType;
+ char DuplicateCheckAction;
+ char NewNoteAction;
+ int FadeOut;
+
+ unsigned char PitchPanSeperation, //0->64, Bit7: Don't use
+ PitchPanCenter; //Note, from C-0 to B-9
+ unsigned char GlobalVolume, //0->128
+ DefaultPan; //0->64, Bit7: Don't use
+
+
+ unsigned char NoteSample[120];
+ unsigned char NoteNote[120];
+
+ MODULUS_ENVELOPE VolumeEnvelope, PanningEnvelope, PitchEnvelope;
+} MODULUS_INSTRUMENT;
+
+#define SAMPLE_HASSAMPLE 1
+#define SAMPLE_16BIT 2
+#define SAMPLE_STEREO 4
+#define SAMPLE_USELOOP 16
+#define SAMPLE_USESUSTAINLOOP 32
+#define SAMPLE_PINGPONGLOOP 64
+#define SAMPLE_PINGPONGSUSTAINLOOP 128
+
+#define VIBRATO_SINE 0
+#define VIBRATO_RAMPDOWN 1
+#define VIBRATO_SQUARE 2
+#define VIBRATO_RANDOM 3
+
+typedef struct MODULUS_SAMPLE {
+ unsigned char GlobalVolume; //0->64
+ unsigned char Flag;
+ unsigned char Volume;
+ int SampleLength; //in samples, not bytes !
+ int LoopBegin, LoopEnd; //in samples
+ int SustainLoopBegin, SustainLoopEnd;
+ int C5Speed; //Number of bytes/sec for C-5
+
+ SAMPLE *Sample;
+
+ char VibratoSpeed; //0->64
+ char VibratoDepth; //0->64
+ char VibratoWaveForm;
+ char VibratoRate; //0->64
+} MODULUS_SAMPLE;
+
+typedef struct MODULUS_NOTE {
+ char Mask; //If Bit0: Note, Bit1: Instrument, Bit2: Volume, Bit3: Panning, Bit4: Command
+ char Channel; //if -1, then end of row.
+ unsigned char Note;
+ char Instrument;
+ unsigned char Volume, Panning;
+ unsigned char Command, CommandValue;
+} MODULUS_NOTE;
+
+typedef struct MODULUS_PATTERN {
+ int NumRows;
+ int NumNotes;
+ MODULUS_NOTE *Note;
+} MODULUS_PATTERN;
+
+typedef struct MODULUS_VCHANNEL {
+ MODULUS_SAMPLE *Sample; //NULL is unused
+ char voice;
+ char ChannelVolume;
+ char NoteOn;
+ char NNA;
+ short FadeOutCount, FadeOut;
+ float MixVolume, MixPan;
+ MODULUS_VENVELOPE *VVolumeEnvelope, *VPanningEnvelope, *VPitchEnvelope;
+ MODULUS_VCHANNEL *next, *prev;
+} MODULUS_VCHANNEL;
+
+typedef struct MODULUS_CHANNEL {
+ unsigned char Volume; //0->64
+ unsigned char Pan; //0->32->64, 100 = surround, Bit7: Disable
+ char LastNote, LastInstrument, LastSample;
+ MODULUS_VCHANNEL *VChannel;
+} MODULUS_CHANNEL;
+
+#define FLAG_STEREO 1
+#define FLAG_USEINSTRUMENTS 4
+#define FLAG_LINEARSLIDES 8
+#define FLAG_OLDEFFECT 16
+
+typedef struct MODULUS {
+ MODULUS_INSTRUMENT *Instrument;
+ MODULUS_SAMPLE *Sample;
+ MODULUS_PATTERN *Pattern;
+
+ int NumOrders;
+ int NumInstruments;
+ int NumSamples;
+ int NumPatterns;
+ int Flags;
+ short Version;
+ char GlobalVolume;
+ char MixVolume;
+ unsigned char Speed, Tempo;
+ char PanningSeperation;
+
+ unsigned char *Order;
+
+ MODULUS_CHANNEL Channel[64];
+
+} MODULUS;
+
+#define COMMAND_SET_SONG_SPEED 1
+#define COMMAND_JUMP_TO_ORDER 2
+#define COMMAND_PATTERN_BREAK_TO_ROW 3
+#define COMMAND_SET_CHANNEL_VOLUME 13
+#define COMMAND_SET_SONG_TEMPO 20
+#define COMMAND_SET_GLOBAL_VOLUME 22
+
+typedef struct MODULUS_PLAY {
+ MODULUS *Music;
+ int Loop, Tick;
+ int CurOrder, CurPattern, CurPos;
+ int Command, CommandVal0, CommandVal1, CommandVal2;
+ int pos;
+} MODULUS_PLAY;
+extern MODULUS_PLAY *song;
+
+extern int IT_Play_Method;
+
+MODULUS *load_it(char*);
+int get_module_size(MODULUS *);
+
+int play_it(MODULUS *j, int loop);
+void install_modulus();
+void set_mix_volume(int i);
+
+void stop_it();
+int is_music_done();
+void destroy_it(MODULUS *j);
+
+//Should be internal:
+extern MODULUS_PLAY *song;
+extern int note_freq[120];
+
+extern void MOD_Interrupt(...);
+extern int MOD_Poller(void*);
+
+#define IT_TIMER 0
+#define IT_POLL 1
+
diff --git a/plugins/dumb/dumb-kode54/src/tools/it/typedef.hpp b/plugins/dumb/dumb-kode54/src/tools/it/typedef.hpp
new file mode 100644
index 00000000..6d4eefee
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/tools/it/typedef.hpp
@@ -0,0 +1,3 @@
+typedef unsigned char byte;
+typedef unsigned short word;
+typedef unsigned int dword;