summaryrefslogtreecommitdiff
path: root/apps/codecs/dumb/src
diff options
context:
space:
mode:
authorMichiel Van Der Kolk <not.valid@email.address>2005-03-17 20:50:03 +0000
committerMichiel Van Der Kolk <not.valid@email.address>2005-03-17 20:50:03 +0000
commit27be5bc72855a0fbbdae230bc144624c9eb85f5e (patch)
treeb553f1321df924c4b744ffcab48dce5f4f081f7d /apps/codecs/dumb/src
parent7e7662bb716917ca431204f0113d400c1014f2e8 (diff)
downloadrockbox-27be5bc72855a0fbbdae230bc144624c9eb85f5e.zip
rockbox-27be5bc72855a0fbbdae230bc144624c9eb85f5e.tar.gz
rockbox-27be5bc72855a0fbbdae230bc144624c9eb85f5e.tar.bz2
rockbox-27be5bc72855a0fbbdae230bc144624c9eb85f5e.tar.xz
Initial check in dumb 0.9.2 - has a few usages of floating point that should
be rewritten to fixed point. seems to compile cleanly for iriver. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6197 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/codecs/dumb/src')
-rw-r--r--apps/codecs/dumb/src/allegro/alplay.c270
-rw-r--r--apps/codecs/dumb/src/allegro/datduh.c60
-rw-r--r--apps/codecs/dumb/src/allegro/datit.c62
-rw-r--r--apps/codecs/dumb/src/allegro/datmod.c61
-rw-r--r--apps/codecs/dumb/src/allegro/dats3m.c61
-rw-r--r--apps/codecs/dumb/src/allegro/datunld.c31
-rw-r--r--apps/codecs/dumb/src/allegro/datxm.c62
-rw-r--r--apps/codecs/dumb/src/allegro/packfile.c98
-rw-r--r--apps/codecs/dumb/src/core/atexit.c71
-rw-r--r--apps/codecs/dumb/src/core/duhlen.c34
-rw-r--r--apps/codecs/dumb/src/core/dumbfile.c401
-rw-r--r--apps/codecs/dumb/src/core/loadduh.c42
-rw-r--r--apps/codecs/dumb/src/core/makeduh.c92
-rw-r--r--apps/codecs/dumb/src/core/rawsig.c44
-rw-r--r--apps/codecs/dumb/src/core/readduh.c107
-rw-r--r--apps/codecs/dumb/src/core/register.c104
-rw-r--r--apps/codecs/dumb/src/core/rendduh.c202
-rw-r--r--apps/codecs/dumb/src/core/rendsig.c299
-rw-r--r--apps/codecs/dumb/src/core/unload.c58
-rw-r--r--apps/codecs/dumb/src/helpers/clickrem.c270
-rw-r--r--apps/codecs/dumb/src/helpers/memfile.c96
-rw-r--r--apps/codecs/dumb/src/helpers/resample.c1177
-rw-r--r--apps/codecs/dumb/src/helpers/sampbuf.c47
-rw-r--r--apps/codecs/dumb/src/helpers/silence.c29
-rw-r--r--apps/codecs/dumb/src/helpers/stdfile.c93
-rw-r--r--apps/codecs/dumb/src/it/itload.c43
-rw-r--r--apps/codecs/dumb/src/it/itmisc.c175
-rw-r--r--apps/codecs/dumb/src/it/itorder.c63
-rw-r--r--apps/codecs/dumb/src/it/itread.c1181
-rw-r--r--apps/codecs/dumb/src/it/itrender.c3512
-rw-r--r--apps/codecs/dumb/src/it/itunload.c71
-rw-r--r--apps/codecs/dumb/src/it/loadmod.c42
-rw-r--r--apps/codecs/dumb/src/it/loads3m.c42
-rw-r--r--apps/codecs/dumb/src/it/loadxm.c42
-rw-r--r--apps/codecs/dumb/src/it/readmod.c594
-rw-r--r--apps/codecs/dumb/src/it/reads3m.c668
-rw-r--r--apps/codecs/dumb/src/it/readxm.c998
-rw-r--r--apps/codecs/dumb/src/it/xmeffect.c242
38 files changed, 11544 insertions, 0 deletions
diff --git a/apps/codecs/dumb/src/allegro/alplay.c b/apps/codecs/dumb/src/allegro/alplay.c
new file mode 100644
index 0000000..983bde1
--- /dev/null
+++ b/apps/codecs/dumb/src/allegro/alplay.c
@@ -0,0 +1,270 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/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;
+}
+
+
+
+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/apps/codecs/dumb/src/allegro/datduh.c b/apps/codecs/dumb/src/allegro/datduh.c
new file mode 100644
index 0000000..672e3c8
--- /dev/null
+++ b/apps/codecs/dumb/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/apps/codecs/dumb/src/allegro/datit.c b/apps/codecs/dumb/src/allegro/datit.c
new file mode 100644
index 0000000..8f58f14
--- /dev/null
+++ b/apps/codecs/dumb/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/apps/codecs/dumb/src/allegro/datmod.c b/apps/codecs/dumb/src/allegro/datmod.c
new file mode 100644
index 0000000..850b17b
--- /dev/null
+++ b/apps/codecs/dumb/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/apps/codecs/dumb/src/allegro/dats3m.c b/apps/codecs/dumb/src/allegro/dats3m.c
new file mode 100644
index 0000000..a0fc744
--- /dev/null
+++ b/apps/codecs/dumb/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/apps/codecs/dumb/src/allegro/datunld.c b/apps/codecs/dumb/src/allegro/datunld.c
new file mode 100644
index 0000000..7190644
--- /dev/null
+++ b/apps/codecs/dumb/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/apps/codecs/dumb/src/allegro/datxm.c b/apps/codecs/dumb/src/allegro/datxm.c
new file mode 100644
index 0000000..6cb98d8
--- /dev/null
+++ b/apps/codecs/dumb/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/apps/codecs/dumb/src/allegro/packfile.c b/apps/codecs/dumb/src/allegro/packfile.c
new file mode 100644
index 0000000..525baeb
--- /dev/null
+++ b/apps/codecs/dumb/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/apps/codecs/dumb/src/core/atexit.c b/apps/codecs/dumb/src/core/atexit.c
new file mode 100644
index 0000000..16c6abd
--- /dev/null
+++ b/apps/codecs/dumb/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/apps/codecs/dumb/src/core/duhlen.c b/apps/codecs/dumb/src/core/duhlen.c
new file mode 100644
index 0000000..4500d0a
--- /dev/null
+++ b/apps/codecs/dumb/src/core/duhlen.c
@@ -0,0 +1,34 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * duhlen.c - Function to 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;
+}
diff --git a/apps/codecs/dumb/src/core/dumbfile.c b/apps/codecs/dumb/src/core/dumbfile.c
new file mode 100644
index 0000000..71108c0
--- /dev/null
+++ b/apps/codecs/dumb/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/apps/codecs/dumb/src/core/loadduh.c b/apps/codecs/dumb/src/core/loadduh.c
new file mode 100644
index 0000000..7dfe5cc
--- /dev/null
+++ b/apps/codecs/dumb/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/apps/codecs/dumb/src/core/makeduh.c b/apps/codecs/dumb/src/core/makeduh.c
new file mode 100644
index 0000000..1e422fb
--- /dev/null
+++ b/apps/codecs/dumb/src/core/makeduh.c
@@ -0,0 +1,92 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * makeduh.c - Function to construct a DUH from / / \ \
+ * its components. | < / \_
+ * | \/ /\ /
+ * By entheh. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.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_get_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_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;
+ }
+
+ 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;
+
+ return duh;
+}
diff --git a/apps/codecs/dumb/src/core/rawsig.c b/apps/codecs/dumb/src/core/rawsig.c
new file mode 100644
index 0000000..926c990
--- /dev/null
+++ b/apps/codecs/dumb/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/apps/codecs/dumb/src/core/readduh.c b/apps/codecs/dumb/src/core/readduh.c
new file mode 100644
index 0000000..514b04a
--- /dev/null
+++ b/apps/codecs/dumb/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/apps/codecs/dumb/src/core/register.c b/apps/codecs/dumb/src/core/register.c
new file mode 100644
index 0000000..9eed45f
--- /dev/null
+++ b/apps/codecs/dumb/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_get_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;
+}
diff --git a/apps/codecs/dumb/src/core/rendduh.c b/apps/codecs/dumb/src/core/rendduh.c
new file mode 100644
index 0000000..39db8ab
--- /dev/null
+++ b/apps/codecs/dumb/src/core/rendduh.c
@@ -0,0 +1,202 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/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 = create_sample_buffer(n_channels, size);
+
+ if (!sampptr)
+ return 0;
+
+ dumb_silence(sampptr[0], n_channels * size);
+
+ size = duh_sigrenderer_get_samples(sigrenderer, volume, delta, size, sampptr);
+
+ if (bits == 16) {
+ int signconv = unsign ? 0x8000 : 0x0000;
+
+ if (n_channels == 2) {
+ for (n = 0; n < size; n++) {
+ CONVERT16(sampptr[0][n], n << 1, signconv);
+ }
+ for (n = 0; n < size; n++) {
+ CONVERT16(sampptr[1][n], (n << 1) + 1, signconv);
+ }
+ } else {
+ for (n = 0; n < size; n++) {
+ CONVERT16(sampptr[0][n], n, signconv);
+ }
+ }
+ } else {
+ char signconv = unsign ? 0x80 : 0x00;
+
+ if (n_channels == 2) {
+ for (n = 0; n < size; n++) {
+ CONVERT8(sampptr[0][n], n << 1, signconv);
+ }
+ for (n = 0; n < size; n++) {
+ CONVERT8(sampptr[1][n], (n << 1) + 1, signconv);
+ }
+ } else {
+ for (n = 0; n < size; 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/apps/codecs/dumb/src/core/rendsig.c b/apps/codecs/dumb/src/core/rendsig.c
new file mode 100644
index 0000000..a36ceb4
--- /dev/null
+++ b/apps/codecs/dumb/src/core/rendsig.c
@@ -0,0 +1,299 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/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_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 ((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;
+/* FIXME
+ 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
+)
+{
+ 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_get_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_get_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_render_signal(
+ DUH_SIGRENDERER *sigrenderer,
+ float volume, float delta,
+ long size, sample_t **samples
+)
+{
+ sample_t **s = create_sample_buffer(sigrenderer->n_channels, size);
+ long rendered;
+ long i;
+ int j;
+ if (!s) return 0;
+ rendered = duh_sigrenderer_get_samples(sigrenderer, volume, delta, size, s);
+ for (j = 0; j < sigrenderer->n_channels; j++)
+ for (i = 0; i < rendered; i++)
+ samples[j][i] += s[j][i] >> 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/apps/codecs/dumb/src/core/unload.c b/apps/codecs/dumb/src/core/unload.c
new file mode 100644
index 0000000..3bf0285
--- /dev/null
+++ b/apps/codecs/dumb/src/core/unload.c
@@ -0,0 +1,58 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/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);
+ }
+
+ free(duh);
+ }
+}
diff --git a/apps/codecs/dumb/src/helpers/clickrem.c b/apps/codecs/dumb/src/helpers/clickrem.c
new file mode 100644
index 0000000..ddc861d
--- /dev/null
+++ b/apps/codecs/dumb/src/helpers/clickrem.c
@@ -0,0 +1,270 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/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, 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;
+
+ while (click) {
+ DUMB_CLICK *next = click->next;
+ ASSERT(click->pos <= length);
+ offset = cr->offset;
+ if (offset < 0) {
+ offset = -offset;
+ while (pos < click->pos) {
+ samples[pos++] -= offset;
+ offset = (int)((LONG_LONG)(offset << 1) * factor >> 32);
+ }
+ offset = -offset;
+ } else {
+ while (pos < click->pos) {
+ samples[pos++] += offset;
+ offset = (int)((LONG_LONG)(offset << 1) * factor >> 32);
+ }
+ }
+ 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);
+ }
+ offset = -offset;
+ } else {
+ while (pos < length) {
+ samples[pos++] += offset;
+ offset = (int)((LONG_LONG)(offset << 1) * factor >> 32);
+ }
+ }
+ 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; i++)
+ dumb_remove_clicks(cr[i], samples[i], length, 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/apps/codecs/dumb/src/helpers/memfile.c b/apps/codecs/dumb/src/helpers/memfile.c
new file mode 100644
index 0000000..b65ab5f
--- /dev/null
+++ b/apps/codecs/dumb/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 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/apps/codecs/dumb/src/helpers/resample.c b/apps/codecs/dumb/src/helpers/resample.c
new file mode 100644
index 0000000..d8b238f
--- /dev/null
+++ b/apps/codecs/dumb/src/helpers/resample.c
@@ -0,0 +1,1177 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * resample.c - Resampling helper. / / \ \
+ * | < / \_
+ * 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
+
+
+
+//#define MULSC(a, b) ((int)((LONG_LONG)(a) * (b) >> 16))
+//#define MULSC(a, b) ((a) * ((b) >> 2) >> 14)
+#define MULSC(a, b) ((int)((LONG_LONG)((a) << 4) * ((b) << 12) >> 32))
+
+
+
+/* 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 = 2;
+
+
+
+void dumb_reset_resampler(DUMB_RESAMPLER *resampler, sample_t *src, long pos, long start, long end)
+{
+ 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;
+ resampler->min_quality = 0;
+ resampler->max_quality = DUMB_RQ_N_LEVELS - 1;
+ resampler->x[2] = resampler->x[1] = resampler->x[0] = 0;
+ resampler->overshot = -1;
+}
+
+
+
+DUMB_RESAMPLER *dumb_start_resampler(sample_t *src, long pos, long start, long end)
+{
+ DUMB_RESAMPLER *resampler = malloc(sizeof(*resampler));
+ if (!resampler) return NULL;
+ dumb_reset_resampler(resampler, src, pos, start, end);
+ return resampler;
+}
+
+
+
+/* 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, 0, 1.0f);
+ resampler->x[0] = resampler->x[1];
+ }
+
+ for (;;) {
+ if (resampler->dir < 0) {
+ if (resampler->overshot >= 3 && resampler->pos+3 >= resampler->start) resampler->x[0] = resampler->src[resampler->pos+3];
+ if (resampler->overshot >= 2 && resampler->pos+2 >= resampler->start) resampler->x[1] = resampler->src[resampler->pos+2];
+ if (resampler->overshot >= 1 && resampler->pos+1 >= resampler->start) resampler->x[2] = resampler->src[resampler->pos+1];
+ resampler->overshot = resampler->start - resampler->pos - 1;
+ } else {
+ if (resampler->overshot >= 3 && resampler->pos-3 < resampler->end) resampler->x[0] = resampler->src[resampler->pos-3];
+ if (resampler->overshot >= 2 && resampler->pos-2 < resampler->end) resampler->x[1] = resampler->src[resampler->pos-2];
+ if (resampler->overshot >= 1 && resampler->pos-1 < resampler->end) resampler->x[2] = resampler->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);
+ }
+}
+
+
+
+/* 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)--; \
+ } \
+}
+
+
+
+long dumb_resample(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume, float delta)
+{
+ int dt;
+ int vol;
+ long done;
+ long todo;
+ 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);
+ vol = (int)floor(volume * 65536.0 + 0.5);
+
+ if (vol == 0) dst = NULL;
+
+ quality = dumb_resampling_quality;
+ if (quality > resampler->max_quality) quality = resampler->max_quality;
+ else if (quality < resampler->min_quality) quality = resampler->min_quality;
+
+ while (done < dst_size) {
+ if (process_pickup(resampler)) return done;
+
+ if ((resampler->dir ^ dt) < 0)
+ dt = -dt;
+
+ if (resampler->dir < 0)
+ todo = (long)((((LONG_LONG)(resampler->pos - resampler->start) << 16) + resampler->subpos - dt) / -dt);
+ else
+ todo = (long)((((LONG_LONG)(resampler->end - resampler->pos) << 16) - resampler->subpos - 1 + dt) / dt);
+
+ if (todo < 0)
+ todo = 0;
+ else if (todo > dst_size - done)
+ todo = dst_size - done;
+
+ done += todo;
+
+ {
+ sample_t *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 + dt * todo;
+ pos += (long)(new_subpos >> 16);
+ subpos = (long)new_subpos & 65535;
+ } else if (quality <= DUMB_RQ_ALIASING) {
+ /* Aliasing, backwards */
+ sample_t xbuf[2];
+ sample_t *x = &xbuf[0];
+ sample_t *xstart;
+ xbuf[0] = resampler->x[1];
+ xbuf[1] = resampler->x[2];
+ while (todo && x < &xbuf[2]) {
+ HEAVYASSERT(pos >= resampler->start);
+ *dst++ += MULSC(x[0], vol);
+ subpos += dt;
+ pos += subpos >> 16;
+ x -= subpos >> 16;
+ subpos &= 65535;
+ todo--;
+ }
+ x = xstart = &src[pos];
+ LOOP4(todo,
+ *dst++ += MULSC(x[2], vol);
+ subpos += dt;
+ x += subpos >> 16;
+ subpos &= 65535;
+ );
+ pos += x - xstart;
+ } else if (quality <= DUMB_RQ_LINEAR) {
+ /* Linear interpolation, backwards */
+ sample_t xbuf[3];
+ sample_t *x = &xbuf[1];
+ xbuf[0] = resampler->x[1];
+ xbuf[1] = resampler->x[2];
+ xbuf[2] = src[pos];
+ while (todo && x < &xbuf[3]) {
+ HEAVYASSERT(pos >= resampler->start);
+ *dst++ += MULSC(x[0] + MULSC(x[-1] - x[0], subpos), vol);
+ subpos += dt;
+ pos += subpos >> 16;
+ x -= subpos >> 16;
+ subpos &= 65535;
+ todo--;
+ }
+ x = &src[pos];
+ LOOP4(todo,
+ HEAVYASSERT(pos >= resampler->start);
+ *dst++ += MULSC(x[1] + MULSC(x[2] - x[1], subpos), vol);
+ subpos += dt;
+ pos += subpos >> 16;
+ x += subpos >> 16;
+ subpos &= 65535;
+ );
+ } else {
+ /* Cubic interpolation, backwards */
+ sample_t xbuf[6];
+ sample_t *x = &xbuf[3];
+ sample_t *lastx = NULL;
+ int a = 0, b = 0, c = 0;
+ xbuf[0] = resampler->x[0];
+ xbuf[1] = resampler->x[1];
+ xbuf[2] = resampler->x[2];
+ xbuf[3] = src[pos];
+ if (pos-1 >= resampler->start) xbuf[4] = src[pos-1];
+ if (pos-2 >= resampler->start) xbuf[5] = src[pos-2];
+ while (todo && x < &xbuf[6]) {
+ HEAVYASSERT(pos >= resampler->start);
+ if (lastx != x) {
+ lastx = x;
+ a = (((x[-1] - x[-2]) << 1) + (x[-1] - x[-2]) + (x[-3] - x[0])) >> 1;
+ b = (x[-2] << 1) + x[0] - ((5 * x[-1] + x[-3]) >> 1);
+ c = (x[-2] - x[0]) >> 1;
+ }
+ *dst++ += MULSC(MULSC(MULSC(MULSC(a, subpos) + b, subpos) + c, subpos) + x[-1], vol);
+ subpos += dt;
+ pos += subpos >> 16;
+ x -= subpos >> 16;
+ subpos &= 65535;
+ todo--;
+ }
+ x = &src[pos];
+ lastx = NULL;
+ LOOP4(todo,
+ HEAVYASSERT(pos >= resampler->start);
+ if (lastx != x) {
+ lastx = x;
+ a = (((x[1] - x[2]) << 1) + (x[1] - x[2]) + (x[3] - x[0])) >> 1;
+ b = (x[2] << 1) + x[0] - ((5 * x[1] + x[3]) >> 1);
+ c = (x[2] - x[0]) >> 1;
+ }
+ *dst++ += MULSC(MULSC(MULSC(MULSC(a, subpos) + b, subpos) + c, subpos) + x[1], vol);
+ subpos += dt;
+ pos += subpos >> 16;
+ x += subpos >> 16;
+ subpos &= 65535;
+ );
+ }
+ diff = diff - pos;
+ overshot = resampler->start - pos - 1;
+ if (diff >= 3) {
+ resampler->x[0] = overshot >= 3 ? 0 : src[pos+3];
+ resampler->x[1] = overshot >= 2 ? 0 : src[pos+2];
+ resampler->x[2] = overshot >= 1 ? 0 : src[pos+1];
+ } else if (diff >= 2) {
+ resampler->x[0] = resampler->x[2];
+ resampler->x[1] = overshot >= 2 ? 0 : src[pos+2];
+ resampler->x[2] = overshot >= 1 ? 0 : src[pos+1];
+ } else if (diff >= 1) {
+ resampler->x[0] = resampler->x[1];
+ resampler->x[1] = resampler->x[2];
+ resampler->x[2] = overshot >= 1 ? 0 : src[pos+1];
+ }
+ } else {
+ if (!dst) {
+ /* Silence or simulation */
+ LONG_LONG new_subpos = subpos + dt * todo;
+ pos += (long)(new_subpos >> 16);
+ subpos = (long)new_subpos & 65535;
+ } else if (dumb_resampling_quality <= DUMB_RQ_ALIASING) {
+ /* Aliasing, forwards */
+ sample_t xbuf[2];
+ sample_t *x = &xbuf[0];
+ sample_t *xstart;
+ xbuf[0] = resampler->x[1];
+ xbuf[1] = resampler->x[2];
+ while (todo && x < &xbuf[2]) {
+ HEAVYASSERT(pos < resampler->end);
+ *dst++ += MULSC(x[0], vol);
+ subpos += dt;
+ pos += subpos >> 16;
+ x += subpos >> 16;
+ subpos &= 65535;
+ todo--;
+ }
+ x = xstart = &src[pos];
+ LOOP4(todo,
+ *dst++ += MULSC(x[-2], vol);
+ subpos += dt;
+ x += subpos >> 16;
+ subpos &= 65535;
+ );
+ pos += x - xstart;
+ } else if (dumb_resampling_quality <= DUMB_RQ_LINEAR) {
+ /* Linear interpolation, forwards */
+ sample_t xbuf[3];
+ sample_t *x = &xbuf[1];
+ xbuf[0] = resampler->x[1];
+ xbuf[1] = resampler->x[2];
+ xbuf[2] = src[pos];
+ while (todo && x < &xbuf[3]) {
+ HEAVYASSERT(pos < resampler->end);
+ *dst++ += MULSC(x[-1] + MULSC(x[0] - x[-1], subpos), vol);
+ subpos += dt;
+ pos += subpos >> 16;
+ x += subpos >> 16;
+ subpos &= 65535;
+ todo--;
+ }
+ x = &src[pos];
+ LOOP4(todo,
+ HEAVYASSERT(pos < resampler->end);
+ *dst++ += MULSC(x[-2] + MULSC(x[-1] - x[-2], subpos), vol);
+ subpos += dt;
+ pos += subpos >> 16;
+ x += subpos >> 16;
+ subpos &= 65535;
+ );
+ } else {
+ /* Cubic interpolation, forwards */
+ sample_t xbuf[6];
+ sample_t *x = &xbuf[3];
+ sample_t *lastx = NULL;
+ int a = 0, b = 0, c = 0;
+ xbuf[0] = resampler->x[0];
+ xbuf[1] = resampler->x[1];
+ xbuf[2] = resampler->x[2];
+ xbuf[3] = src[pos];
+ if (pos+1 < resampler->end) xbuf[4] = src[pos+1];
+ if (pos+2 < resampler->end) xbuf[5] = src[pos+2];
+ while (todo && x < &xbuf[6]) {
+ HEAVYASSERT(pos < resampler->end);
+ if (lastx != x) {
+ lastx = x;
+ a = (((x[-2] - x[-1]) << 1) + (x[-2] - x[-1]) + (x[0] - x[-3])) >> 1;
+ b = (x[-1] << 1) + x[-3] - ((5 * x[-2] + x[0]) >> 1);
+ c = (x[-1] - x[-3]) >> 1;
+ }
+ *dst++ += MULSC(MULSC(MULSC(MULSC(a, subpos) + b, subpos) + c, subpos) + x[-2], vol);
+ subpos += dt;
+ pos += subpos >> 16;
+ x += subpos >> 16;
+ subpos &= 65535;
+ todo--;
+ }
+ x = &src[pos];
+ lastx = NULL;
+ LOOP4(todo,
+ HEAVYASSERT(pos < resampler->end);
+ if (lastx != x) {
+ lastx = x;
+ a = (((x[-2] - x[-1]) << 1) + (x[-2] - x[-1]) + (x[0] - x[-3])) >> 1;
+ b = (x[-1] << 1) + x[-3] - ((5 * x[-2] + x[0]) >> 1);
+ c = (x[-1] - x[-3]) >> 1;
+ }
+ *dst++ += MULSC(MULSC(MULSC(MULSC(a, subpos) + b, subpos) + c, subpos) + x[-2], vol);
+ subpos += dt;
+ pos += subpos >> 16;
+ x += subpos >> 16;
+ subpos &= 65535;
+ );
+ }
+ diff = pos - diff;
+ overshot = pos - resampler->end;
+ if (diff >= 3) {
+ resampler->x[0] = overshot >= 3 ? 0 : src[pos-3];
+ resampler->x[1] = overshot >= 2 ? 0 : src[pos-2];
+ resampler->x[2] = overshot >= 1 ? 0 : src[pos-1];
+ } else if (diff >= 2) {
+ resampler->x[0] = resampler->x[2];
+ resampler->x[1] = overshot >= 2 ? 0 : src[pos-2];
+ resampler->x[2] = overshot >= 1 ? 0 : src[pos-1];
+ } else if (diff >= 1) {
+ resampler->x[0] = resampler->x[1];
+ resampler->x[1] = resampler->x[2];
+ resampler->x[2] = overshot >= 1 ? 0 : src[pos-1];
+ }
+ }
+ resampler->pos = pos;
+ resampler->subpos = subpos;
+ }
+ }
+
+ return done;
+}
+
+
+
+sample_t dumb_resample_get_current_sample(DUMB_RESAMPLER *resampler, float volume)
+{
+ int vol;
+ sample_t *src;
+ long pos;
+ int subpos;
+ int quality;
+
+ if (!resampler || resampler->dir == 0) return 0;
+ ASSERT(resampler->dir == -1 || resampler->dir == 1);
+
+ if (process_pickup(resampler)) return 0;
+
+ vol = (int)floor(volume * 65536.0 + 0.5);
+ if (vol == 0) return 0;
+
+ quality = dumb_resampling_quality;
+ if (quality > resampler->max_quality) quality = resampler->max_quality;
+ else if (quality < resampler->min_quality) quality = resampler->min_quality;
+
+ src = resampler->src;
+ pos = resampler->pos;
+ subpos = resampler->subpos;
+
+ if (resampler->dir < 0) {
+ HEAVYASSERT(pos >= resampler->start);
+ if (dumb_resampling_quality <= 0) {
+ /* Aliasing, backwards */
+ return MULSC(src[pos], vol);
+ } else if (quality <= DUMB_RQ_LINEAR) {
+ /* Linear interpolation, backwards */
+ return MULSC(resampler->x[2] + MULSC(resampler->x[1] - resampler->x[2], subpos), vol);
+ } else {
+ /* Cubic interpolation, backwards */
+ sample_t *x = resampler->x;
+ int a, b, c;
+ a = (((x[2] - x[1]) << 1) + (x[2] - x[1]) + (x[0] - src[pos])) >> 1;
+ b = (x[1] << 1) + src[pos] - ((5 * x[2] + x[0]) >> 1);
+ c = (x[1] - src[pos]) >> 1;
+ return MULSC(MULSC(MULSC(MULSC(a, subpos) + b, subpos) + c, subpos) + x[2], vol);
+ }
+ } else {
+ HEAVYASSERT(pos < resampler->end);
+ if (dumb_resampling_quality <= 0) {
+ /* Aliasing */
+ return MULSC(src[pos], vol);
+ } else if (dumb_resampling_quality <= DUMB_RQ_LINEAR) {
+ /* Linear interpolation, forwards */
+ return MULSC(resampler->x[1] + MULSC(resampler->x[2] - resampler->x[1], subpos), vol);
+ } else {
+ /* Cubic interpolation, forwards */
+ sample_t *x = resampler->x;
+ int a, b, c;
+ a = (((x[1] - x[2]) << 1) + (x[1] - x[2]) + (src[pos] - x[0])) >> 1;
+ b = (x[2] << 1) + x[0] - ((5 * x[1] + src[pos]) >> 1);
+ c = (x[2] - x[0]) >> 1;
+ return MULSC(MULSC(MULSC(MULSC(a, subpos) + b, subpos) + c, subpos) + x[1], vol);
+ }
+ }
+}
+
+
+
+void dumb_end_resampler(DUMB_RESAMPLER *resampler)
+{
+ if (resampler)
+ free(resampler);
+}
+
+
+
+#if 0
+/* The following macro is used to overcome the fact that most C
+ * compilers (including gcc and MSVC) can't correctly multiply signed
+ * integers outside the range -32768 to 32767. i86 assembler versions
+ * don't need to use this method, since the processor does in fact
+ * have instructions to multiply large numbers correctly - which
+ * means using assembly language could make a significant difference
+ * to the speed.
+ *
+ * The basic method is as follows. We halve the subposition (how far
+ * we are between samples), so it never exceeds 32767. We also halve
+ * the delta, which is the amount to be added to the subposition each
+ * time. Then we unroll the loop twofold, so that we can add the lost
+ * one every other time if necessary (since the halving may have
+ * resulted in rounding down).
+ *
+ * This method doesn't incur any cumulative inaccuracies. There is a
+ * very slight loss of quality, which I challenge anyone to notice -
+ * but the position will advance at *exactly* the same rate as it
+ * would if we didn't use this method. This also means the pitch is
+ * exactly the same, which may even make a difference to trained
+ * musicians when resampling down a lot :)
+ *
+ * Each time this macro is invoked, DO_RESAMPLE(inc) must be defined
+ * to calculate the samples by the appropriate equation (linear,
+ * cubic, etc.). See the individual cases for examples of how this is
+ * done.
+ */
+#define MAKE_RESAMPLER() \
+{ \
+ if (dt & 1) { \
+ long todo2; \
+ \
+ dt >>= 1; \
+ \
+ if (src_subpos & 1) { \
+ src_subpos >>= 1; \
+ DO_RESAMPLE(1); \
+ todo--; \
+ } else \
+ src_subpos >>= 1; \
+ \
+ todo2 = todo >> 1; \
+ \
+ while (todo2) { \
+ DO_RESAMPLE(0); \
+ DO_RESAMPLE(1); \
+ todo2--; \
+ } \
+ \
+ if (todo & 1) { \
+ DO_RESAMPLE(0); \
+ src_subpos = (src_subpos << 1) | 1; \
+ } else \
+ src_subpos <<= 1; \
+ \
+ todo = 0; \
+ dt = (dt << 1) | 1; \
+ } else { \
+ long subposbit = src_subpos & 1; \
+ dt >>= 1; \
+ src_subpos >>= 1; \
+ \
+ if (todo & 1) { \
+ DO_RESAMPLE(0); \
+ } \
+ \
+ todo >>= 1; \
+ \
+ while (todo) { \
+ DO_RESAMPLE(0); \
+ DO_RESAMPLE(0); \
+ todo--; \
+ } \
+ \
+ src_subpos = (src_subpos << 1) | subposbit; \
+ dt <<= 1; \
+ } \
+}
+
+
+
+sample_t dumb_resample_get_current_sample(
+ sample_t *src, long *_src_pos, int *_src_subpos,
+ long src_start, long src_end,
+ float volume, int *_dir,
+ DUMB_RESAMPLE_PICKUP pickup, void *pickup_data
+)
+{
+ long src_pos = *_src_pos;
+ int src_subpos = *_src_subpos;
+ int dir = _dir ? *_dir : 1;
+
+ sample_t value = 0;
+
+ if (dir == 0)
+ return 0;
+
+ ASSERT(dir == 1 || dir == -1);
+
+ if (dir < 0 ? (src_pos < src_start) : (src_pos >= src_end)) {
+
+ /* If there's no pick-up function, we stop. */
+ if (!pickup) {
+ dir = 0;
+ goto end;
+ }
+
+ /* Process the pick-up. It may need invoking more than once. */
+ do {
+ dir = (*pickup)(src, &src_pos, &src_subpos, &src_start, &src_end, dir, pickup_data);
+
+ if (dir == 0)
+ goto end;
+
+ ASSERT(dir == 1 || dir == -1);
+ } while (dir < 0 ? (src_pos < src_start) : (src_pos >= src_end));
+ }
+
+ HEAVYASSERT(dir < 0 ? (src_pos >= src_start) : (src_pos < src_end));
+
+ if (dumb_resampling_quality == 0) {
+ /* Aliasing (coarse) */
+ int volume_fact = (int)(volume * 16384.0);
+ value = (src[src_pos] * volume_fact) >> 14;
+ } else if (dumb_resampling_quality <= 2) {
+ /* Linear interpolation */
+ int volume_fact = (int)(volume * 16384.0);
+ int subpos = src_subpos >> 1;
+ value = ((src[src_pos] + ((((src[src_pos + 1] - src[src_pos]) >> 1) * subpos) >> 14)) * volume_fact) >> 14;
+ } else if (dumb_resampling_quality == 3) {
+ /* Quadratic interpolation */
+ int volume_fact = (int)(volume * 16384.0);
+ int a, b;
+ sample_t *x;
+ int subpos = src_subpos >> 1;
+ x = &src[src_pos];
+ a = ((x[0] + x[2]) >> 1) - x[1];
+ b = ((x[2] - x[0]) >> 1) - (a << 1);
+ value = (((((((a * subpos) >> 15) + b) * subpos) >> 15) + x[0]) * volume_fact) >> 14;
+ } else {
+ /* Cubic interpolation */
+ int volume_fact = (int)(volume * 16384.0);
+ int a, b, c;
+ sample_t *x;
+ int subpos = src_subpos >> 1;
+ x = &src[src_pos];
+ a = (((x[1] - x[2]) << 1) + (x[1] - x[2]) + (x[3] - x[0])) >> 1;
+ b = (x[2] << 1) + x[0] - ((5 * x[1] + x[3]) >> 1);
+ c = (x[2] - x[0]) >> 1;
+ value = (((int)(((LONG_LONG)((int)(((LONG_LONG)((int)(((LONG_LONG)a * subpos) >> 15) + b) * subpos) >> 15) + c) * subpos) >> 15) + x[1]) * volume_fact) >> 14;
+ }
+
+ end:
+
+ *_src_pos = src_pos;
+ *_src_subpos = src_subpos;
+ if (_dir) *_dir = dir;
+
+ return value;
+}
+
+
+
+long dumb_resample(
+ sample_t *src, long *_src_pos, int *_src_subpos,
+ long src_start, long src_end,
+ sample_t *dst, long dst_size,
+ float volume, float delta, int *_dir,
+ DUMB_RESAMPLE_PICKUP pickup, void *pickup_data
+)
+{
+ int dt = (int)(delta * 65536.0 + 0.5);
+ long s = 0; /* Current position in the destination buffer */
+
+ long src_pos = *_src_pos;
+ int src_subpos = *_src_subpos;
+ int dir = _dir ? *_dir : 1;
+
+ int linear_average;
+
+ if (dir == 0)
+ return 0;
+
+ ASSERT(dir == 1 || dir == -1);
+
+ linear_average = dst && dumb_resampling_quality >= 2 && dt > 65536;
+
+ if (dir < 0) dt = -dt;
+
+ if (linear_average)
+ volume /= delta;
+
+ while (s < dst_size) {
+
+ long todo;
+
+ /* Process pick-ups first, just in case. */
+
+ if (linear_average) {
+
+ /* For linear average, the pick-up point could split a sum into
+ * two parts. We handle this by putting the pick-up code inside
+ * the summing loop. Note that this code is only executed when we
+ * know that a pick-up is necessary somewhere during this sum
+ * (although it is always executed once for the first sample).
+ * We use a separate loop further down when we know we won't have
+ * to do a pick-up, so the condition does not need testing inside
+ * the loop.
+ */
+
+ float sum;
+ long i;
+ int advance;
+ int x[3];
+
+ advance = src_subpos + dt;
+
+ /* Make these negative. Then they stay within the necessary
+ * range for integer multiplication, -32768 to 32767 ;)
+ */
+ x[0] = ~(src_subpos >> 1); /* = -1 - (src_subpos >> 1) */
+ x[2] = x[0] ^ 0x7FFF; /* = -32768 + (src_subpos >> 1) */
+
+ sum = (float)(-((src[src_pos] * (x+1)[dir]) >> 15));
+
+ i = src_pos + (advance >> 16);
+ src_pos += dir;
+ src_subpos = (dir >> 1) & 65535; /* changes 1,-1 to 0,65535 */
+
+ advance &= 65535;
+
+ /* i is the index of the first sample NOT to sum fully,
+ * regardless of the direction of resampling.
+ */
+
+ while (dir < 0 ? (i < src_start) : (i >= src_end)) {
+ if (dir < 0) {
+ while (src_pos >= src_start)
+ sum += src[src_pos--];
+ } else {
+ while (src_pos < src_end)
+ sum += src[src_pos++];
+ }
+
+ i -= src_pos;
+ /* i is now the number of samples left to sum fully, except
+ * it's negative if we're going backwards.
+ */
+
+ if (!pickup) {
+ dir = 0;
+ goto endsum;
+ }
+
+ dir = (*pickup)(src, &src_pos, &src_subpos, &src_start, &src_end, dir, pickup_data);
+
+ if (dir == 0)
+ goto endsum;
+
+ ASSERT(dir == 1 || dir == -1);
+
+ if ((dir ^ dt) < 0) {
+ dt = -dt;
+ advance ^= 65535;
+ i = -i;
+ }
+
+ i += src_pos;
+ /* There, i is back to normal. */
+ }
+
+ for (; src_pos != i; src_pos += dir)
+ sum += src[src_pos];
+
+ src_subpos = advance;
+
+ x[2] = src_subpos >> 1;
+ x[0] = x[2] ^ 0x7FFF; /* = 32767 - (src_subpos >> 1) */
+
+ sum += (src[src_pos] * (x+1)[dir]) >> 15;
+
+ endsum:
+
+ sum *= volume;
+ dst[s] += (int)sum;
+
+ s++;
+
+ if (dir == 0)
+ break;
+
+ } else if (dir < 0 ? (src_pos < src_start) : (src_pos >= src_end)) {
+
+ /* If there's no pick-up function, we stop. */
+ if (!pickup) {
+ dir = 0;
+ break;
+ }
+
+ /* Process the pick-up. It may need invoking more than once. */
+ do {
+ dir = (*pickup)(src, &src_pos, &src_subpos, &src_start, &src_end, dir, pickup_data);
+
+ if (dir == 0)
+ goto end;
+
+ ASSERT(dir == 1 || dir == -1);
+ } while (dir < 0 ? (src_pos < src_start) : (src_pos >= src_end));
+
+ /* Update sign of dt to match that of dir. */
+ if ((dir ^ dt) < 0)
+ dt = -dt;
+ }
+
+ /* Work out how many contiguous samples we can now render. */
+ if (dir < 0)
+ todo = (long)((((LONG_LONG)(src_pos - src_start) << 16) + src_subpos) / -dt);
+ else
+ todo = (long)((((LONG_LONG)(src_end - src_pos) << 16) - src_subpos - 1) / dt);
+
+ /* The above equations work out how many complete dt-sized
+ * intervals there are between the current position and the loop
+ * point (provided there is a little fractional extra). The linear
+ * average function needs complete intervals - but the other
+ * resamplers only read a sample from the beginning of each interval,
+ * so they can process one extra sample in their main loops (so we
+ * increment todo in a moment).
+ *
+ * The linear average function makes up the extra sample using the
+ * specialised pick-up code above.
+ *
+ * Note that our above pick-up process should have absolutely ensured
+ * that the result of this function will be nonnegative.
+ */
+
+ ASSERT(todo >= 0);
+
+ if (!linear_average)
+ todo++;
+
+ /* Of course we don't want to overrun the output buffer! */
+ if (todo > dst_size - s)
+ todo = dst_size - s;
+
+ if (!dst) {
+
+ LONG_LONG t = src_subpos + (LONG_LONG)dt * todo;
+ src_pos += (long)(t >> 16);
+ src_subpos = (int)t & 0xFFFFl;
+
+ s += todo;
+
+ } else if (linear_average) {
+
+ float sum;
+ long i;
+ int advance;
+ int x[3];
+
+ while (todo) {
+
+ advance = src_subpos + dt;
+
+ /* Make these negative. Then they stay within the necessary
+ * range for integer multiplication, -32768 to 32767 ;)
+ */
+ x[0] = ~(src_subpos >> 1); /* = -1 - (src_subpos >> 1) */
+ x[2] = x[0] ^ 0x7FFF; /* = -32768 + (src_subpos >> 1) */
+
+ sum = (float)(-((src[src_pos] * (x+1)[dir]) >> 15));
+
+ i = src_pos + (advance >> 16);
+ src_pos += dir;
+ src_subpos = (dir >> 1) & 65535; /* changes 1,-1 to 0,65535 */
+
+ advance &= 65535;
+
+ /* i is the index of the first sample NOT to sum fully,
+ * regardless of the direction of resampling.
+ */
+
+ HEAVYASSERT(dir < 0 ? (i >= src_start) : (i < src_end));
+
+ for (; src_pos != i; src_pos += dir)
+ sum += src[src_pos];
+
+ src_subpos = advance;
+
+ x[2] = src_subpos >> 1;
+ x[0] = x[2] ^ 0x7FFF; /* = 32767 - (src_subpos >> 1) */
+
+ sum += (src[src_pos] * (x+1)[dir]) >> 15;
+
+ sum *= volume;
+ dst[s] += (int)sum;
+
+ s++;
+ todo--;
+ }
+
+ } else if (dumb_resampling_quality == 0 || (dumb_resampling_quality == 1 && delta >= 1.0)) {
+
+ /* Aliasing (coarse) */
+ int volume_fact = (int)(volume * 16384.0);
+
+ do {
+ HEAVYASSERT(dir < 0 ? (src_pos >= src_start) : (src_pos < src_end));
+ dst[s] += ((src[src_pos] * volume_fact) >> 14);
+ src_subpos += dt;
+ src_pos += src_subpos >> 16;
+ src_subpos &= 0xFFFFl;
+ s++;
+ } while (--todo);
+
+ } else if (dumb_resampling_quality <= 2) {
+
+ /* Linear interpolation */
+ int volume_fact = (int)(volume * 16384.0);
+
+ #define DO_RESAMPLE(inc) \
+ { \
+ HEAVYASSERT(dir < 0 ? (src_pos >= src_start) : (src_pos < src_end)); \
+ \
+ dst[s] += (((src[src_pos] + ((((src[src_pos + 1] - src[src_pos]) >> 1) * src_subpos) >> 14)) * volume_fact) >> 14); \
+ \
+ src_subpos += dt + inc; \
+ src_pos += src_subpos >> 15; \
+ src_subpos &= 0x7FFFl; \
+ s++; \
+ }
+
+ MAKE_RESAMPLER();
+
+ #undef DO_RESAMPLE
+
+ } else if (dumb_resampling_quality == 3) {
+
+ /* Quadratic interpolation */
+
+ int volume_fact = (int)(volume * 16384.0);
+ int a = 0, b = 0;
+ sample_t *x = NULL;
+ int last_src_pos = -1;
+
+ /* AIM: no integer multiplicands must transcend the range -32768 to 32767.
+ * This limitation is imposed by most compilers, including gcc and MSVC.
+ *
+ * a = 0.5 * (s0 + s2) - s1
+ * b = -1.5 * s0 + 2 * s1 - 0.5 * s2
+ * c = s0
+ *
+ * s = (a * t + b) * t + c
+ *
+ * In fixed-point:
+ *
+ * a = ((s0 + s2) >> 1) - s1
+ * b = ((-3 * s0 - s2) >> 1) + (s1 << 1)
+ *
+ * s = (((((a * t) >> 16) + b) * t) >> 16) + s0
+ *
+ * With t halved (since t can reach 65535):
+ *
+ * s = (((((a * t) >> 15) + b) * t) >> 15) + s0
+ *
+ * a currently reaches 65536
+ * b currently reaches 131072
+ *
+ * So we must use aon2
+ *
+ * s = (((((aon2 * t) >> 14) + b) * t) >> 15) + s0
+ *
+ * ((aon2 * t) >> 14) + b is 5 times too big
+ * so we must divide by 8
+ *
+ * s = (((((aon2 * t) >> 17) + bon8) * t) >> 12) + s0
+ *
+ * aon2 = ((s0 + s2) >> 2) - (s1 >> 1)
+ * bon8 = ((-3 * s0 - s2) >> 4) + (s1 >> 2)
+ * or:
+ * bon8 = ((s2 - s0) >> 4) - (aon2 >> 1)
+ */
+
+ /* Unh4x0r3d version:
+ #define DO_RESAMPLE(inc) \
+ { \
+ HEAVYASSERT(dir < 0 ? (src_pos >= src_start) : (src_pos < src_end)); \
+ \
+ if (src_pos != last_src_pos) { \
+ last_src_pos = src_pos; \
+ x = &src[src_pos]; \
+ a = ((x[0] + x[2]) >> 2) - (x[1] >> 1); \
+ b = ((x[2] - x[0]) >> 4) - (a >> 1); \
+ } \
+ \
+ dst[s] += ((((((((a * src_subpos) >> 17) + b) * src_subpos) >> 12) + x[0]) * volume_fact) >> 14); \
+ \
+ src_subpos += dt + inc; \
+ src_pos += src_subpos >> 15; \
+ src_subpos &= 0x7FFFl; \
+ s++; \
+ }
+ */
+
+ /* H4x0r3d version: */
+ #define DO_RESAMPLE(inc) \
+ { \
+ HEAVYASSERT(dir < 0 ? (src_pos >= src_start) : (src_pos < src_end)); \
+ \
+ if (src_pos != last_src_pos) { \
+ last_src_pos = src_pos; \
+ x = &src[src_pos]; \
+ a = ((x[0] + x[2]) >> 1) - x[1]; \
+ b = ((x[2] - x[0]) >> 1) - (a << 1); \
+ } \
+ \
+ dst[s] += ((((((((a * src_subpos) >> 15) + b) * src_subpos) >> 15) + x[0]) * volume_fact) >> 14); \
+ \
+ src_subpos += dt + inc; \
+ src_pos += src_subpos >> 15; \
+ src_subpos &= 0x7FFFl; \
+ s++; \
+ }
+
+ MAKE_RESAMPLER();
+
+ #undef DO_RESAMPLE
+
+ } else {
+
+ /* Cubic interpolation */
+
+ int volume_fact = (int)(volume * 16384.0);
+ int a = 0, b = 0, c = 0;
+ sample_t *x = NULL;
+ int last_src_pos = -1;
+
+ /* AIM: never multiply integers outside the range -32768 to 32767.
+ *
+ * a = 1.5f * (x[1] - x[2]) + (x[3] - x[0]) * 0.5f;
+ * b = 2.0f * x[2] + x[0] - 2.5f * x[1] - x[3] * 0.5f;
+ * c = (x[2] - x[0]) * 0.5f;
+ *
+ * s = ((a * t + b) * t + c) * t + x[1];
+ *
+ * Fixed-point version:
+ *
+ * a = (((x[1] - x[2]) << 1) + (x[1] - x[2]) + (x[3] - x[0])) >> 1;
+ * b = (x[2] << 1) + x[0] - ((5 * x[1] + x[3]) >> 1);
+ * c = (x[2] - x[0]) >> 1;
+ *
+ * s = ((((((((a * t) >> 15) + b) * t) >> 15) + c) * t) >> 15) + x[1];
+ * (with t already halved, maximum 32767)
+ *
+ * a is in (((1+1)*2)+(1+1)+(1+1))/2 = 8 times the required range
+ * b is in (1*2)+1+((5*1+1)/2) = 6 times
+ * c is in the required range
+ *
+ * We must use aon8
+ *
+ * s = ((((((((aon8 * t) >> 12) + b) * t) >> 15) + c) * t) >> 15) + x[1];
+ *
+ * But ((aon8 * t) >> 12) is in 2^(15+15-12) = 2^18 = 8 times
+ * b is in 6 times
+ * so we divide both ((aon8 * t) >> 12) and b by 16
+ *
+ * s = ((((((((aon8 * t) >> 16) + bon16) * t) >> 11) + c) * t) >> 15) + x[1];
+ *
+ * ((... + bon16) * t) >> 11 is 16 times too big
+ * c is in the correct range
+ * we must divide both by 32
+ *
+ * s = ((((((((aon8 * t) >> 16) + bon16) * t) >> 16) + con32) * t) >> 10) + x[1];
+ *
+ * aon8 = (((x[1] - x[2]) << 1) + (x[1] - x[2]) + (x[3] - x[0])) >> 4;
+ * bon16 = ((x[2] << 2) + (x[0] << 1) - (5 * x[1] + x[3])) >> 5;
+ * con32 = (x[2] - x[0]) >> 6;
+ *
+ * A lot of accuracy is lost here. It is quite likely that some
+ * of the above would cancel anyway, so the scaling down wouldn't
+ * have to be so severe. However, I'm not in the mood to work it
+ * out now :P
+ *
+ * It may also be worth investigating whether doing this stuff
+ * in floats would be faster.
+ */
+
+ /* Unh4x0r3d version:
+ #define DO_RESAMPLE(inc) \
+ { \
+ HEAVYASSERT(dir < 0 ? (src_pos >= src_start) : (src_pos < src_end)); \
+ \
+ if (src_pos != last_src_pos) { \
+ last_src_pos = src_pos; \
+ x = &src[src_pos]; \
+ a = (((x[1] - x[2]) << 1) + (x[1] - x[2]) + (x[3] - x[0])) >> 4; \
+ b = ((x[2] << 2) + (x[0] << 1) - (5 * x[1] + x[3])) >> 5; \
+ c = (x[2] - x[0]) >> 6; \
+ } \
+ \
+ dst[s] += ((((((((((a * src_subpos) >> 16) + b) * src_subpos) >> 16) + c) * src_subpos) >> 10) + x[1]) * volume_fact) >> 14; \
+ \
+ src_subpos += dt + inc; \
+ src_pos += src_subpos >> 15; \
+ src_subpos &= 0x7FFFl; \
+ s++; \
+ }
+ */
+
+ /* H4x0r3d version: */
+ #define DO_RESAMPLE(inc) \
+ { \
+ HEAVYASSERT(dir < 0 ? (src_pos >= src_start) : (src_pos < src_end)); \
+ \
+ if (src_pos != last_src_pos) { \
+ last_src_pos = src_pos; \
+ x = &src[src_pos]; \
+ a = (((x[1] - x[2]) << 1) + (x[1] - x[2]) + (x[3] - x[0])) >> 1; \
+ b = (x[2] << 1) + x[0] - ((5 * x[1] + x[3]) >> 1); \
+ c = (x[2] - x[0]) >> 1; \
+ } \
+ \
+ dst[s] += (((int)(((LONG_LONG)((int)(((LONG_LONG)((int)(((LONG_LONG)a * src_subpos) >> 15) + b) * src_subpos) >> 15) + c) * src_subpos) >> 15) + x[1]) * volume_fact) >> 14; \
+ \
+ src_subpos += dt + inc; \
+ src_pos += src_subpos >> 15; \
+ src_subpos &= 0x7FFFl; \
+ s++; \
+ }
+
+ MAKE_RESAMPLER();
+
+ #undef DO_RESAMPLE
+
+ }
+
+ }
+
+ end:
+
+ ASSERT(s <= dst_size);
+
+ *_src_pos = src_pos;
+ *_src_subpos = src_subpos;
+ if (_dir) *_dir = dir;
+
+ return s;
+}
+#endif
diff --git a/apps/codecs/dumb/src/helpers/sampbuf.c b/apps/codecs/dumb/src/helpers/sampbuf.c
new file mode 100644
index 0000000..75510c7
--- /dev/null
+++ b/apps/codecs/dumb/src/helpers/sampbuf.c
@@ -0,0 +1,47 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * sampbuf.c - Helper for allocating sample / / \ \
+ * buffers. | < / \_
+ * | \/ /\ /
+ * By entheh. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <stdlib.h>
+#include "dumb.h"
+
+
+
+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;
+}
+
+
+
+void destroy_sample_buffer(sample_t **samples)
+{
+ if (samples) {
+ free(samples[0]);
+ free(samples);
+ }
+}
diff --git a/apps/codecs/dumb/src/helpers/silence.c b/apps/codecs/dumb/src/helpers/silence.c
new file mode 100644
index 0000000..4d5fdcf
--- /dev/null
+++ b/apps/codecs/dumb/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/apps/codecs/dumb/src/helpers/stdfile.c b/apps/codecs/dumb/src/helpers/stdfile.c
new file mode 100644
index 0000000..2f02539
--- /dev/null
+++ b/apps/codecs/dumb/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/apps/codecs/dumb/src/it/itload.c b/apps/codecs/dumb/src/it/itload.c
new file mode 100644
index 0000000..a377619
--- /dev/null
+++ b/apps/codecs/dumb/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(): 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(const char *filename)
+{
+ DUH *duh;
+ DUMBFILE *f = dumbfile_open(filename);
+
+ if (!f)
+ return NULL;
+
+ duh = dumb_read_it(f);
+
+ dumbfile_close(f);
+
+ return duh;
+}
+
diff --git a/apps/codecs/dumb/src/it/itmisc.c b/apps/codecs/dumb/src/it/itmisc.c
new file mode 100644
index 0000000..b69a64d
--- /dev/null
+++ b/apps/codecs/dumb/src/it/itmisc.c
@@ -0,0 +1,175 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/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);
+}
+
+
+
+int dumb_it_sd_get_n_orders(DUMB_IT_SIGDATA *sd)
+{
+ return sd ? sd->n_orders : 0;
+}
+
+
+
+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;
+}
diff --git a/apps/codecs/dumb/src/it/itorder.c b/apps/codecs/dumb/src/it/itorder.c
new file mode 100644
index 0000000..6959f05
--- /dev/null
+++ b/apps/codecs/dumb/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/apps/codecs/dumb/src/it/itread.c b/apps/codecs/dumb/src/it/itread.c
new file mode 100644
index 0000000..7384b30
--- /dev/null
+++ b/apps/codecs/dumb/src/it/itread.c
@@ -0,0 +1,1181 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/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;
+
+
+
+static unsigned char *sourcebuf = NULL;
+static unsigned char *sourcepos = NULL;
+static unsigned char *sourceend;
+static int rembits = 0;
+
+
+
+static int readblock(DUMBFILE *f)
+{
+ long size;
+ int c;
+
+ size = dumbfile_igetw(f);
+ if (size < 0)
+ return size;
+
+ sourcebuf = malloc(size);
+ if (!sourcebuf)
+ return -1;
+
+ c = dumbfile_getnc((char *)sourcebuf, size, f);
+ if (c < size) {
+ free(sourcebuf);
+ sourcebuf = NULL;
+ return -1;
+ }
+
+ sourcepos = sourcebuf;
+ sourceend = sourcebuf + size;
+ rembits = 8;
+ return 0;
+}
+
+
+
+static void freeblock(void)
+{
+ free(sourcebuf);
+ sourcebuf = NULL;
+}
+
+
+
+static int readbits(int bitwidth)
+{
+ int val = 0;
+ int b = 0;
+
+ if (sourcepos >= sourceend) return val;
+
+ while (bitwidth > rembits) {
+ val |= *sourcepos++ << b;
+ if (sourcepos >= sourceend) return val;
+ b += rembits;
+ bitwidth -= rembits;
+ rembits = 8;
+ }
+
+ val |= (*sourcepos & ((1 << bitwidth) - 1)) << b;
+ *sourcepos >>= bitwidth;
+ 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, sample_t *left, sample_t *right, int len, int cmwt)
+{
+ int blocklen, blockpos;
+ byte bitwidth;
+ word val;
+ char d1, d2;
+
+ memset(left, 0, len * sizeof(*left));
+ if (right) {
+ memset(right, 0, len * sizeof(*right));
+ len <<= 1;
+ }
+
+ while (len > 0) {
+ //Read a block of compressed data:
+ if (readblock(f))
+ 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);
+ //Check for bit width change:
+
+ if (bitwidth < 7) { //Method 1:
+ if (val == (1 << (bitwidth - 1))) {
+ val = (word)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 -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
+ */
+ if (right && (len & 1))
+ *right++ = (int)(signed char)(cmwt == 0x215 ? d2 : d1) << 16;
+ else
+ *left++ = (int)(signed char)(cmwt == 0x215 ? d2 : d1) << 16;
+ len--;
+ blockpos++;
+ }
+ freeblock();
+ }
+ return 0;
+}
+
+
+
+static int decompress16(DUMBFILE *f, sample_t *left, sample_t *right, int len, int cmwt)
+{
+ int blocklen, blockpos;
+ byte bitwidth;
+ long val;
+ short d1, d2;
+
+ memset(left, 0, len * sizeof(*left));
+ if (right) {
+ memset(right, 0, len * sizeof(*right));
+ len <<= 1;
+ }
+
+ while (len > 0) {
+ //Read a block of compressed data:
+ if (readblock(f))
+ 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);
+ //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 -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
+ */
+ if (right && (len & 1))
+ *right++ = (int)(signed short)(cmwt == 0x215 ? d2 : d1) << 8;
+ else
+ *left++ = (int)(signed short)(cmwt == 0x215 ? d2 : d1) << 8;
+ len--;
+ blockpos++;
+ }
+ freeblock();
+ }
+ 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);
+
+ 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;
+
+ /* Skip DOS Filename. */
+ dumbfile_skip(f, 13);
+
+ 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 bytes and Instrument Name.
+ */
+ dumbfile_skip(f, 36);
+
+ 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);
+
+#if defined(INVESTIGATE_OLD_INSTRUMENTS) && 0
+ 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);
+
+#if defined(INVESTIGATE_OLD_INSTRUMENTS) && 0
+ 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;
+
+#if defined(INVESTIGATE_OLD_INSTRUMENTS) && 0
+ fprintf(stderr, "\n");
+#endif
+
+ if (dumbfile_error(f))
+ return -1;
+
+ 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 n;
+
+ if (dumbfile_mgetl(f) != IT_INSTRUMENT_SIGNATURE)
+ return -1;
+
+ /* Skip DOS Filename. */
+ dumbfile_skip(f, 13);
+
+ 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 and Instrument Name.
+ */
+ dumbfile_skip(f, 30);
+
+ instrument->filter_cutoff = dumbfile_getc(f);
+ instrument->filter_resonance = dumbfile_getc(f);
+
+ /* Skip MIDI Channel, Program and Bank. */
+ 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;
+
+ return 0;
+}
+
+
+
+static int it_read_sample_header(IT_SAMPLE *sample, unsigned char *convert, long *offset, DUMBFILE *f)
+{
+ if (dumbfile_mgetl(f) != IT_SAMPLE_SIGNATURE)
+ return -1;
+
+ /* Skip DOS Filename. */
+ dumbfile_skip(f, 13);
+
+ sample->global_volume = dumbfile_getc(f);
+ sample->flags = dumbfile_getc(f);
+ sample->default_volume = dumbfile_getc(f);
+
+ /* Skip Sample Name. */
+ dumbfile_skip(f, 26);
+
+ *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) {
+ 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);
+ sample->vibrato_rate = dumbfile_getc(f);
+ sample->vibrato_waveform = dumbfile_getc(f);
+
+ return dumbfile_error(f);
+}
+
+
+
+static long it_read_sample_data(int cmwt, IT_SAMPLE *sample, unsigned char convert, DUMBFILE *f)
+{
+ long n;
+
+ sample->left = malloc(sample->length * sizeof(*sample->left));
+ if (!sample->left)
+ return -1;
+
+ if (sample->flags & IT_SAMPLE_STEREO) {
+ sample->right = malloc(sample->length * sizeof(*sample->right));
+ if (!sample->right)
+ return -1;
+ }
+
+ 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);
+
+/*
+//#ifndef STEREO_SAMPLES_COUNT_AS_TWO
+ ASSERT(!(sample->flags & IT_SAMPLE_STEREO));
+//#endif
+*/
+ if (sample->flags & IT_SAMPLE_16BIT)
+ decompress16(f, sample->left, sample->right, sample->length, cmwt);
+ else
+ decompress8(f, sample->left, sample->right, sample->length, cmwt);
+ } else if (sample->flags & IT_SAMPLE_STEREO) {
+ if (sample->flags & IT_SAMPLE_16BIT) {
+ if (convert & 2) {
+ for (n = 0; n < sample->length; n++) {
+ sample->left[n] = (int)(signed short)dumbfile_mgetw(f) << 8;
+ sample->right[n] = (int)(signed short)dumbfile_mgetw(f) << 8;
+ }
+ } else {
+ for (n = 0; n < sample->length; n++) {
+ sample->left[n] = (int)(signed short)dumbfile_igetw(f) << 8;
+ sample->right[n] = (int)(signed short)dumbfile_igetw(f) << 8;
+ }
+ }
+ } else {
+ for (n = 0; n < sample->length; n++) {
+ sample->left[n] = (int)(signed char)dumbfile_getc(f) << 16;
+ sample->right[n] = (int)(signed char)dumbfile_getc(f) << 16;
+ }
+ }
+ } else if (sample->flags & IT_SAMPLE_16BIT) {
+ if (convert & 2)
+ for (n = 0; n < sample->length; n++)
+ sample->left[n] = (int)(signed short)dumbfile_mgetw(f) << 8;
+ else
+ for (n = 0; n < sample->length; n++)
+ sample->left[n] = (int)(signed short)dumbfile_igetw(f) << 8;
+ } else
+ for (n = 0; n < sample->length; n++)
+ sample->left[n] = (int)(signed char)dumbfile_getc(f) << 16;
+
+ if (dumbfile_error(f))
+ return -1;
+
+ if (!(convert & 1)) {
+ /* Convert to signed. */
+ for (n = 0; n < sample->length; n++)
+ sample->left[n] ^= 0xFF800000;
+
+ if (sample->right)
+ for (n = 0; n < sample->length; n++)
+ sample->right[n] ^= 0xFF800000;
+ }
+
+ /* 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];
+ }
+
+#if defined( DETECT_DUPLICATE_CHANNELS) && 0
+ 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_INSTRUMENT 1
+#define IT_COMPONENT_PATTERN 2
+#define IT_COMPONENT_SAMPLE 3
+
+typedef struct IT_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 */
+}
+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;
+
+ IT_COMPONENT *component;
+ int n_components = 0;
+
+ unsigned char sample_convert[256];
+
+ int n;
+
+ unsigned char *buffer;
+
+ if (dumbfile_mgetl(f) != IT_SIGNATURE)
+ return NULL;
+
+ sigdata = malloc(sizeof(*sigdata));
+
+ if (!sigdata)
+ return NULL;
+
+ sigdata->order = NULL;
+ sigdata->instrument = NULL;
+ sigdata->sample = NULL;
+ sigdata->pattern = NULL;
+ sigdata->midi = NULL;
+ sigdata->checkpoint = NULL;
+
+ /* Skip song name and pattern row highlight info. */
+ dumbfile_skip(f, 28);
+
+ 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, Message Length, Message Offset and Reserved. */
+ dumbfile_skip(f, 11);
+
+ dumbfile_getnc(sigdata->channel_pan, DUMB_IT_N_CHANNELS, f);
+ dumbfile_getnc(sigdata->channel_volume, DUMB_IT_N_CHANNELS, f);
+
+ if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_instruments > 256 || 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_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].right = sigdata->sample[n].left = 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(768 * sizeof(*component));
+ if (!component) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ 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)) {
+#if 0
+ 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");
+#endif
+ }
+
+ 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;
+
+ 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_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);
+
+ 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(DUMBFILE *f)
+{
+ sigdata_t *sigdata;
+ long length;
+
+ DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+ sigdata = it_load_sigdata(f);
+
+ if (!sigdata)
+ return NULL;
+
+ length = _dumb_it_build_checkpoints(sigdata);
+
+ return make_duh(length, 1, &descptr, &sigdata);
+}
+
diff --git a/apps/codecs/dumb/src/it/itrender.c b/apps/codecs/dumb/src/it/itrender.c
new file mode 100644
index 0000000..103654f
--- /dev/null
+++ b/apps/codecs/dumb/src/it/itrender.c
@@ -0,0 +1,3512 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/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/it.h"
+
+
+
+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;
+
+ 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;
+
+ dst->channel_volume = src->channel_volume;
+
+ dst->volume = src->volume;
+ dst->pan = src->pan;
+
+ dst->note = src->note;
+
+ 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->tremolo_speed = src->tremolo_speed;
+ dst->tremolo_depth = src->tremolo_depth;
+ dst->tremolo_time = src->tremolo_time;
+
+ dst->sample_vibrato_time = src->sample_vibrato_time;
+ dst->sample_vibrato_depth = src->sample_vibrato_depth;
+
+ dst->slide = src->slide;
+ dst->delta = src->delta;
+
+ 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[0] = src->resampler[0];
+ dst->resampler[1] = src->resampler[1];
+ dst->resampler[1].pickup_data = dst->resampler[0].pickup_data = dst;
+ dst->time_lost = src->time_lost;
+
+ 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->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->note_cut_count = src->note_cut_count;
+ dst->note_delay_count = src->note_delay_count;
+ dst->note_delay_entry = src->note_delay_entry;
+
+ 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->portamento = src->portamento;
+ dst->toneporta = src->toneporta;
+ dst->destnote = src->destnote;
+
+ 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->lastI = src->lastI;
+ dst->lastJ = src->lastJ;
+ dst->lastN = src->lastN;
+ dst->lastO = src->lastO;
+ dst->high_offset = src->high_offset;
+ 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->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);
+}
+
+
+
+/* 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->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->pat_loop_row = src->pat_loop_row;
+
+ 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;
+
+ return dst;
+}
+
+
+
+static 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;
+}
+
+
+
+#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 sampfreq, int cutoff, int resonance)
+{
+ float currsample = state->currsample;
+ float prevsample = state->prevsample;
+
+ float a, b, c;
+
+ {
+ 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;
+
+ if (cr) {
+ float startstep = src[0]*a + currsample*b + prevsample*c;
+ dumb_record_click(cr, pos, (sample_t)startstep);
+ }
+
+#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)));
+ sample_t csi = (sample_t)currsample;
+ sample_t psi = (sample_t)prevsample;
+ sample_t *dst_end = dst + size;
+ while (dst < dst_end) {
+ {
+ sample_t nsi = MULSCA(*src++, ai) + MULSCA(csi, bi) + MULSCA(psi, ci);
+ psi = csi;
+ csi = nsi;
+ }
+ *dst++ += csi;
+ }
+ currsample = csi;
+ prevsample = psi;
+ }
+#else
+ {
+ 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--;
+ }
+ }
+#endif
+
+ if (cr) {
+ float endstep = *src*a + currsample*b + prevsample*c;
+ dumb_record_click(cr, pos + size, -(sample_t)endstep);
+ }
+
+ state->currsample = currsample;
+ state->prevsample = prevsample;
+}
+
+#undef LOG10
+
+
+
+static 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 0
+/** 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 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 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
+};
+
+#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->note_cut_count = 0;
+ channel->note_delay_count = 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++) {
+ IT_CHANNEL *channel = &sigrenderer->channel[i];
+ channel->volslide = 0;
+ channel->xm_volslide = 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->playing) {
+ channel->playing->vibrato_n = 0;
+ channel->playing->tremolo_speed = 0;
+ channel->playing->tremolo_depth = 0;
+ }
+ }
+}
+
+
+
+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[0].start = playing->sample->sus_loop_start;
+ playing->resampler[0].end = playing->sample->sus_loop_end;
+ if (playing->sample->flags & IT_SAMPLE_PINGPONG_SUS_LOOP)
+ playing->resampler[0].pickup = &it_pickup_pingpong_loop;
+ else
+ playing->resampler[0].pickup = &it_pickup_loop;
+ } else if (playing->sample->flags & IT_SAMPLE_LOOP) {
+ playing->resampler[0].start = playing->sample->loop_start;
+ playing->resampler[0].end = playing->sample->loop_end;
+ if (playing->sample->flags & IT_SAMPLE_PINGPONG_LOOP)
+ playing->resampler[0].pickup = &it_pickup_pingpong_loop;
+ else
+ playing->resampler[0].pickup = &it_pickup_loop;
+ } else {
+ if (playing->sample->flags & IT_SAMPLE_SUS_LOOP)
+ playing->resampler[0].start = playing->sample->sus_loop_start;
+ else
+ playing->resampler[0].start = 0;
+ playing->resampler[0].end = playing->sample->length;
+ playing->resampler[0].pickup = &it_pickup_stop_at_end;
+ }
+ playing->resampler[1].start = playing->resampler[0].start;
+ playing->resampler[1].end = playing->resampler[0].end;
+ playing->resampler[1].pickup = playing->resampler[0].pickup;
+ ASSERT(playing->resampler[0].pickup_data == playing);
+ ASSERT(playing->resampler[1].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)
+{
+ dumb_reset_resampler(&playing->resampler[0], playing->sample->left, pos, 0, 0);
+ dumb_reset_resampler(&playing->resampler[1], playing->sample->right, pos, 0, 0);
+ playing->resampler[1].pickup_data = playing->resampler[0].pickup_data = playing;
+ playing->time_lost = 0;
+ playing->flags &= ~IT_PLAYING_DEAD;
+ it_playing_update_resamplers(playing);
+}
+
+
+
+static void update_retrig(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);
+ 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);
+ 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;
+ }
+ }
+}
+
+
+
+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->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(channel);
+
+ if (playing) {
+ playing->slide += channel->portamento;
+
+ 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 update_pattern_variables(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry)
+{
+ IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel];
+
+ if (entry->mask & IT_ENTRY_EFFECT) {
+ if (entry->effect == IT_S) {
+ unsigned char effectvalue = entry->effectvalue;
+ if (effectvalue == 0)
+ effectvalue = channel->lastS;
+ channel->lastS = effectvalue;
+ switch (effectvalue >> 4) {
+ //case IT_S7:
+ case IT_S_PATTERN_LOOP:
+ {
+ unsigned char v = effectvalue & 15;
+ if (v == 0)
+ channel->pat_loop_row = sigrenderer->processrow;
+ else {
+ if (channel->pat_loop_count == 0) {
+ channel->pat_loop_count = v;
+ sigrenderer->pat_loop_row = channel->pat_loop_row;
+ } else {
+ if (--channel->pat_loop_count)
+ sigrenderer->pat_loop_row = channel->pat_loop_row;
+ else if (!(sigrenderer->sigdata->flags & IT_WAS_AN_XM))
+ channel->pat_loop_row = sigrenderer->processrow + 1;
+ }
+ }
+ }
+ break;
+ case IT_S_PATTERN_DELAY:
+ sigrenderer->rowcount = 1 + (effectvalue & 15);
+ break;
+ }
+ }
+ }
+}
+
+
+
+/* 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)))
+ 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[0].dir < 0) {
+ playing->resampler[1].pos = playing->resampler[0].pos = (playing->sample->sus_loop_end << 1) - 1 - playing->resampler[0].pos;
+ playing->resampler[1].subpos = playing->resampler[0].subpos ^= 65535;
+ playing->resampler[1].dir = playing->resampler[0].dir = 1;
+ }
+
+ playing->resampler[1].pos = playing->resampler[0].pos += playing->time_lost;
+ }
+}
+
+
+
+static void retrigger_it_envelopes(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel)
+{
+ channel->playing->volume_envelope.next_node = 0;
+ channel->playing->volume_envelope.tick = -1;
+ channel->playing->pan_envelope.next_node = 0;
+ channel->playing->pan_envelope.tick = -1;
+ channel->playing->pitch_envelope.next_node = 0;
+ channel->playing->pitch_envelope.tick = -1;
+ 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 (channel->sample)
+ if (sigdata->flags & IT_USE_INSTRUMENTS)
+ channel->playing->env_instrument = &sigdata->instrument[channel->instrument-1];
+}
+
+
+
+static void it_retrigger_note(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel)
+{
+ DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata;
+ unsigned char nna;
+ int i;
+
+ 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
+ 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
+ nna = channel->playing->instrument->new_note_action;
+#endif
+
+ switch (nna) {
+ case NNA_NOTE_CUT:
+ free(channel->playing);
+ channel->playing = NULL;
+ break;
+ case NNA_NOTE_OFF:
+ channel->playing->flags |= IT_PLAYING_BACKGROUND | IT_PLAYING_SUSTAINOFF;
+ fix_sample_looping(channel->playing);
+ it_playing_update_resamplers(channel->playing);
+ if (channel->playing->instrument)
+ if ((channel->playing->instrument->volume_envelope.flags & (IT_ENVELOPE_ON | IT_ENVELOPE_LOOP_ON)) != IT_ENVELOPE_ON)
+ channel->playing->flags |= IT_PLAYING_FADING;
+ break;
+ case NNA_NOTE_FADE:
+ channel->playing->flags |= IT_PLAYING_BACKGROUND | IT_PLAYING_FADING;
+ break;
+ }
+ }
+
+ 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;
+ }
+ }
+/** 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;
+
+ channel->playing->flags = 0;
+ 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;
+ channel->playing->channel_volume = channel->channelvolume;
+ channel->playing->note = channel->truenote;
+ 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->tremolo_speed = 0;
+ channel->playing->tremolo_depth = 0;
+ channel->playing->tremolo_time = 0;
+ channel->playing->sample_vibrato_time = 0;
+ channel->playing->sample_vibrato_depth = 0;
+ channel->playing->slide = 0;
+ channel->playing->volume_envelope.next_node = 0;
+ channel->playing->volume_envelope.tick = -1;
+ channel->playing->pan_envelope.next_node = 0;
+ channel->playing->pan_envelope.tick = -1;
+ channel->playing->pitch_envelope.next_node = 0;
+ channel->playing->pitch_envelope.tick = -1;
+ 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;
+
+ {
+ 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 (!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 = 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 */
+ 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 byte)
+{
+ if (sigrenderer->callbacks->midi)
+ if ((*sigrenderer->callbacks->midi)(sigrenderer->callbacks->midi_data, channel - sigrenderer->channel, byte))
+ return;
+
+ switch (channel->midi_state) {
+ case 4: /* Ready to receive resonance parameter */
+ if (byte < 0x80) channel->filter_resonance = byte;
+ channel->midi_state = 0;
+ break;
+ case 3: /* Ready to receive cutoff parameter */
+ if (byte < 0x80) channel->filter_cutoff = byte;
+ channel->midi_state = 0;
+ break;
+ case 2: /* Ready for byte specifying which parameter will follow */
+ if (byte == 0) /* Cutoff */
+ channel->midi_state = 3;
+ else if (byte == 1) /* Resonance */
+ channel->midi_state = 4;
+ else
+ channel->midi_state = 0;
+ break;
+ default: /* Counting initial F0 bytes */
+ switch (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;
+ }
+ }
+}
+
+
+
+/* Returns 1 if a callback caused termination of playback. */
+static int process_effects(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry)
+{
+ 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)
+ sigrenderer->tick = sigrenderer->speed = entry->effectvalue;
+ else if (sigdata->flags & IT_WAS_AN_XM) {
+ 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_JUMP_TO_ORDER: sigrenderer->processorder = entry->effectvalue - 1; sigrenderer->processrow = 0xFFFE; break;
+ case IT_BREAK_TO_ROW: sigrenderer->breakrow = entry->effectvalue; sigrenderer->processrow = 0xFFFE; 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) {
+ 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
+ channel->portamento -= v << 4;
+ }
+ }
+ break;
+ case IT_PORTAMENTO_UP:
+ {
+ unsigned char v = entry->effectvalue;
+ if (sigdata->flags & IT_WAS_AN_XM) {
+ 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
+ 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_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 <<= 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 (offset < end)
+ it_playing_reset_resamplers(playing, offset);
+ else if (sigdata->flags & IT_OLD_EFFECTS)
+ it_playing_reset_resamplers(playing, end);
+ }
+ }
+ }
+ 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
+ */
+ 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(channel);
+ } else
+ update_retrig(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);
+ 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:
+ //case IT_S_SET_GLISSANDO_CONTROL:
+ //case IT_S_FINETUNE:
+ //case IT_S_SET_VIBRATO_WAVEFORM:
+ //case IT_S_SET_TREMOLO_WAVEFORM:
+ //case IT_S_SET_PANBRELLO_WAVEFORM:
+ /* Waveforms for commands S3x, S4x and S5x:
+ * 0: Sine wave
+ * 1: Ramp down
+ * 2: Square wave
+ * 3: Random wave
+ */
+ case IT_S_FINE_PATTERN_DELAY:
+ sigrenderer->tick += effectvalue & 15;
+ break;
+ //case IT_S7:
+ case IT_S_SET_PAN:
+ channel->pan =
+ ((effectvalue & 15) << 2) |
+ ((effectvalue & 15) >> 2);
+ channel->truepan = channel->pan << IT_ENVELOPE_SHIFT;
+ break;
+ case IT_S_SET_SURROUND_SOUND:
+ if ((effectvalue & 15) == 1)
+ channel->pan = IT_SURROUND;
+ channel->truepan = channel->pan << IT_ENVELOPE_SHIFT;
+ 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)
+ 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:
+ channel->pan = (entry->effectvalue + 2) >> 2;
+ channel->truepan = channel->pan << IT_ENVELOPE_SHIFT;
+ break;
+ //case IT_PANBRELLO:
+ case IT_MIDI_MACRO:
+ {
+ 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,
+ midi->SFmacroz[channel->SFmacro] & j ?
+ entry->effectvalue : midi->SFmacro[channel->SFmacro][i]);
+ }
+ }
+ 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
+ // - 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)
+ return 1;
+ if (entry->mask & IT_ENTRY_INSTRUMENT)
+ get_default_volpan(sigdata, channel);
+ } else
+ it_retrigger_note(sigrenderer, channel);
+ }
+
+ /** WARNING: This is not ideal, since channel->playing might not get allocated owing to lack of memory... */
+ if (channel->playing &&
+ (((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 (entry->mask & IT_ENTRY_INSTRUMENT) {
+ if (sigdata->flags & IT_COMPATIBLE_GXX)
+ retrigger_it_envelopes(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)
+ if (channel->sample)
+ channel->destnote = channel->truenote;
+ 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)
+ v = channel->lastEF;
+ channel->lastEF = v;
+ }
+ if (entry->mask & IT_ENTRY_NOTE)
+ if (channel->sample)
+ channel->destnote = channel->truenote;
+ channel->toneporta = v << 4;
+ }
+ } else 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);
+ it_retrigger_note(sigrenderer, channel);
+ }
+ }
+
+ 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];
+
+ if (entry->mask & IT_ENTRY_INSTRUMENT) {
+ channel->instrument = entry->instrument;
+ instrument_to_sample(sigdata, channel);
+ if (channel->playing) {
+ /* 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;
+ if (channel->pan >= 128 && channel->pan <= 192)
+ channel->pan = channel->playing->sample->default_pan - 128;
+
+ retrigger_xm_envelopes(channel->playing);
+ }
+ }
+
+ if (entry->mask & IT_ENTRY_NOTE) {
+ if (!(entry->mask & IT_ENTRY_INSTRUMENT))
+ instrument_to_sample(sigdata, channel);
+
+ if (channel->note >= 120) {
+ if (channel->playing) {
+ if (!(sigdata->instrument[channel->instrument-1].volume_envelope.flags & IT_ENVELOPE_ON))
+ if (!(entry->mask & IT_ENTRY_INSTRUMENT))
+ channel->volume = 0;
+ channel->playing->flags |= IT_PLAYING_SUSTAINOFF | IT_PLAYING_FADING;
+ it_playing_update_resamplers(channel->playing);
+ }
+ } 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) {
+ free(channel->playing);
+ channel->playing = NULL;
+ }
+ 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);
+ }
+
+ channel->playing->flags = 0;
+ 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;
+ channel->playing->channel_volume = channel->channelvolume;
+ channel->playing->note = channel->truenote;
+ 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->tremolo_speed = 0;
+ channel->playing->tremolo_depth = 0;
+ channel->playing->tremolo_time = 0;
+ channel->playing->sample_vibrato_time = 0;
+ channel->playing->sample_vibrato_depth = 0;
+ channel->playing->slide = 0;
+ 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_NOTE | IT_ENTRY_INSTRUMENT)) == (IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT)) {
+ if (channel->playing) retrigger_xm_envelopes(channel->playing);
+ get_default_volpan(sigdata, channel);
+ channel->truepan = channel->pan << IT_ENVELOPE_SHIFT;
+ }
+
+ 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->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->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->pan = (value*64)/15;
+ channel->truepan = channel->pan << IT_ENVELOPE_SHIFT;
+ break;
+ case 0xD: /* Pan slide left */
+ // TODO
+ // channel->xm_panslide = -value;
+ break;
+ case 0xE: /* Pan slide Right */
+ // TODO
+ // channel->xm_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)
+{
+ 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);
+}
+
+
+
+static int process_entry(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry)
+{
+ IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel];
+
+ if (entry->mask & IT_ENTRY_NOTE)
+ channel->note = entry->note;
+
+ 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);
+}
+
+
+
+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->note_cut_count) {
+ channel->note_cut_count--;
+ if (channel->note_cut_count == 0) {
+ if (sigrenderer->sigdata->flags & IT_WAS_AN_XM)
+ channel->volume = 0;
+ else if (channel->playing) {
+ free(channel->playing);
+ channel->playing = NULL;
+ }
+ }
+ } else if (channel->note_delay_count) {
+ channel->note_delay_count--;
+ if (channel->note_delay_count == 0)
+ process_note_data(sigrenderer, channel->note_delay_entry);
+ /* 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)
+{
+ 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);
+}
+
+
+
+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;
+}
+
+
+
+/* This returns 1 if the envelope finishes. */
+static int update_it_envelope(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe)
+{
+ if (!(envelope->flags & IT_ENVELOPE_ON))
+ return 0;
+
+ if (pe->next_node >= envelope->n_nodes)
+ return 1;
+
+ while (pe->tick >= envelope->node_t[pe->next_node]) {
+ if ((envelope->flags & IT_ENVELOPE_LOOP_ON) && pe->next_node == envelope->loop_end) {
+ pe->next_node = envelope->loop_start;
+ pe->tick = envelope->node_t[envelope->loop_start];
+ return it_envelope_end(playing, envelope, pe);
+ }
+ if ((envelope->flags & IT_ENVELOPE_SUSTAIN_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF) && pe->next_node == envelope->sus_loop_end) {
+ pe->next_node = envelope->sus_loop_start;
+ pe->tick = envelope->node_t[envelope->sus_loop_start];
+ return it_envelope_end(playing, envelope, pe);
+ }
+
+ pe->next_node++;
+
+ if (pe->next_node >= envelope->n_nodes)
+ return 1;
+ }
+
+ pe->tick++;
+
+ return it_envelope_end(playing, envelope, pe);
+}
+
+
+
+static void update_it_envelopes(IT_PLAYING *playing)
+{
+ IT_ENVELOPE *envelope = &playing->env_instrument->volume_envelope;
+
+ if (update_it_envelope(playing, envelope, &playing->volume_envelope)) {
+ playing->flags |= IT_PLAYING_FADING;
+ if (envelope->n_nodes && envelope->node_y[envelope->n_nodes-1] == 0)
+ playing->flags |= IT_PLAYING_DEAD;
+ }
+
+ update_it_envelope(playing, &playing->env_instrument->pan_envelope, &playing->pan_envelope);
+ update_it_envelope(playing, &playing->env_instrument->pitch_envelope, &playing->pitch_envelope);
+}
+
+
+
+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];
+ }
+ }
+
+ if (xm_envelope_is_sustaining(playing, envelope, pe))
+ return;
+
+ if (pe->tick >= envelope->node_t[envelope->n_nodes-1])
+ return;
+}
+
+
+
+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 void process_playing(DUMB_IT_SIGDATA *sigdata, IT_PLAYING *playing)
+{
+ if (playing->instrument) {
+ if (sigdata->flags & IT_WAS_AN_XM)
+ update_xm_envelopes(playing);
+ else
+ update_it_envelopes(playing);
+ update_fadeout(sigdata, playing);
+ }
+
+ //Calculate final volume if required
+ //Calculate final pan if required
+
+ 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 void process_all_playing(DUMB_IT_SIGRENDERER *sigrenderer)
+{
+ DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata;
+ int i;
+
+ 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 = it_sine[playing->vibrato_time];
+ 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 (sigdata->flags & IT_LINEAR_SLIDES) {
+ int currpitch = ((playing->note - 60) << 8) + playing->slide
+ + vibrato_shift;
+
+ /* 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 / 65536.0f;
+ } else {
+ int slide = playing->slide + vibrato_shift;
+
+ playing->delta = (float)pow(DUMB_SEMITONE_BASE, 60 - playing->note);
+ /* 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;
+ continue;
+ }
+
+ playing->delta = (1.0f / 65536.0f) / playing->delta;
+ }
+
+ 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(sigdata, sigrenderer->channel[i].playing);
+ 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)) {
+ 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(sigdata, sigrenderer->playing[i]);
+ 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;
+ if (sigrenderer->pat_loop_row >= 0) {
+ int n = sigrenderer->pat_loop_row - 1;
+ sigrenderer->row = sigrenderer->processrow = n;
+ sigrenderer->pat_loop_row = -1;
+ if (n < 0)
+ sigrenderer->entry = NULL;
+ else {
+ sigrenderer->entry = sigrenderer->entry_start;
+ while (n) {
+ while (sigrenderer->entry < sigrenderer->entry_end) {
+ if (IT_IS_END_ROW(sigrenderer->entry)) {
+ sigrenderer->entry++;
+ break;
+ }
+ sigrenderer->entry++;
+ }
+ n--;
+ }
+ }
+ }
+
+ sigrenderer->processrow++;
+
+ if (sigrenderer->processrow >= sigrenderer->n_rows) {
+ IT_PATTERN *pattern;
+ int n;
+
+ sigrenderer->processrow = sigrenderer->breakrow;
+ sigrenderer->breakrow = 0;
+
+ for (;;) {
+ sigrenderer->processorder++;
+
+ if (sigrenderer->processorder >= sigdata->n_orders) {
+ sigrenderer->processorder = sigdata->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)
+ sigrenderer->processorder = -1;
+#else
+ if (n == IT_ORDER_END)
+ sigrenderer->processorder = -1;
+#endif
+ }
+
+ pattern = &sigdata->pattern[n];
+
+ 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 (sigrenderer->order >= sigrenderer->processorder) {
+ if (sigrenderer->callbacks->loop) {
+ if ((*sigrenderer->callbacks->loop)(sigrenderer->callbacks->loop_data))
+ return 1;
+ 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 {
+ sigrenderer->entry = sigrenderer->entry_start;
+ sigrenderer->row = 0;
+ }
+ }
+
+ reset_effects(sigrenderer);
+
+ {
+ IT_ENTRY *entry = sigrenderer->entry;
+
+ while (entry < sigrenderer->entry_end && !IT_IS_END_ROW(entry))
+ update_pattern_variables(sigrenderer, entry++);
+ }
+
+ {
+ IT_ENTRY *entry = sigrenderer->entry;
+
+ while (entry < sigrenderer->entry_end && !IT_IS_END_ROW(entry))
+ if (process_entry(sigrenderer, entry++))
+ 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))
+ process_effects(sigrenderer, entry++);
+ /* Don't bother checking the return value; if there
+ * was a pattern delay, there can't be a speed=0.
+ */
+ }
+
+ update_effects(sigrenderer);
+ }
+ } else {
+ speed0:
+ update_effects(sigrenderer);
+ update_tick_counts(sigrenderer);
+ }
+
+ process_all_playing(sigrenderer);
+
+ sigrenderer->time_left += TICK_TIME_DIVIDEND / sigrenderer->tempo;
+
+ return 0;
+}
+
+
+
+int dumb_it_max_to_mix = 64;
+
+
+
+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;
+
+ vol = it_sine[playing->tremolo_time];
+ vol *= playing->tremolo_depth;
+
+ vol = (playing->volume << 5) + vol;
+
+ if (vol <= 0)
+ return 0;
+
+ if (vol > 64 << 5)
+ vol = 64 << 5;
+
+ 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->env_instrument->volume_envelope.flags & IT_ENVELOPE_ON) {
+ 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)
+{
+ int pan = playing->pan;
+ if (pan <= 64 << IT_ENVELOPE_SHIFT && playing->env_instrument && (playing->env_instrument->pan_envelope.flags & IT_ENVELOPE_ON)) {
+ 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;
+}
+
+
+
+/* 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 delta, long pos, long size, sample_t **samples, int store_end_sample, int *left_to_mix)
+{
+ int pan;
+
+ long size_rendered;
+
+ if (playing->flags & IT_PLAYING_DEAD)
+ return 0;
+
+ if (*left_to_mix <= 0)
+ volume = 0;
+
+ pan = apply_pan_envelope(playing);
+
+#define RESAMPLERV(rv, resampler, dst, volume) \
+{ \
+ rv = dumb_resample(resampler, dst, size, volume, delta); \
+ if (store_end_sample) \
+ (dst)[rv] = RESAMPLE_VALUE(resampler, volume); \
+}
+
+#define RESAMPLE(resampler, dst, volume) \
+{ \
+ int i; \
+ RESAMPLERV(i, resampler, dst, volume); \
+}
+
+#define RESAMPLE_VALUE(resampler, volume) \
+ dumb_resample_get_current_sample(resampler, volume)
+
+ if (volume == 0) {
+ size_rendered = dumb_resample(&playing->resampler[0], NULL, size, 0, delta);
+ if (playing->sample->flags & IT_SAMPLE_STEREO)
+ dumb_resample(&playing->resampler[1], NULL, size, 0, delta);
+ } else {
+ if (sigrenderer->n_channels == 2) {
+ float vol = volume;
+ DUMB_RESAMPLER start = playing->resampler[0];
+ if (!IT_IS_SURROUND_SHIFTED(pan)) vol *= 2.0f - pan * (1.0f / (32 << IT_ENVELOPE_SHIFT));
+ if (sigrenderer->click_remover && sigrenderer->click_remover[0])
+ dumb_record_click(sigrenderer->click_remover[0], pos, RESAMPLE_VALUE(&playing->resampler[0], vol));
+ RESAMPLERV(size_rendered, &playing->resampler[0], samples[0] + pos, vol);
+ if (sigrenderer->click_remover && sigrenderer->click_remover[0])
+ dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -RESAMPLE_VALUE(&playing->resampler[0], vol));
+ vol = -vol;
+ if (!IT_IS_SURROUND_SHIFTED(pan)) vol += 2.0f * volume;
+ if (playing->sample->flags & IT_SAMPLE_STEREO) {
+ if (sigrenderer->click_remover && sigrenderer->click_remover[1])
+ dumb_record_click(sigrenderer->click_remover[1], pos, RESAMPLE_VALUE(&playing->resampler[1], vol));
+ RESAMPLE(&playing->resampler[1], samples[1] + pos, vol);
+ if (sigrenderer->click_remover && sigrenderer->click_remover[1])
+ dumb_record_click(sigrenderer->click_remover[1], pos + size_rendered, -RESAMPLE_VALUE(&playing->resampler[1], vol));
+ } else {
+ playing->resampler[0] = start;
+ if (sigrenderer->click_remover && sigrenderer->click_remover[1])
+ dumb_record_click(sigrenderer->click_remover[1], pos, RESAMPLE_VALUE(&playing->resampler[0], vol));
+ RESAMPLE(&playing->resampler[0], samples[1] + pos, vol);
+ if (sigrenderer->click_remover && sigrenderer->click_remover[1])
+ dumb_record_click(sigrenderer->click_remover[1], pos + size_rendered, -RESAMPLE_VALUE(&playing->resampler[0], vol));
+ }
+ } else {
+ if (playing->sample->flags & IT_SAMPLE_STEREO) {
+ float vol = 0.5f * volume;
+ if (!IT_IS_SURROUND_SHIFTED(pan)) vol *= 2.0f - pan * (1.0f / (32 << IT_ENVELOPE_SHIFT));
+ if (sigrenderer->click_remover && sigrenderer->click_remover[0]) {
+ sample_t startstep, endstep;
+ startstep = RESAMPLE_VALUE(&playing->resampler[0], vol);
+ RESAMPLE(&playing->resampler[0], samples[0] + pos, vol);
+ endstep = RESAMPLE_VALUE(&playing->resampler[0], vol);
+ if (!IT_IS_SURROUND_SHIFTED(pan)) vol = 2.0f * volume - vol;
+ startstep += RESAMPLE_VALUE(&playing->resampler[1], vol);
+ RESAMPLERV(size_rendered, &playing->resampler[1], samples[0] + pos, vol);
+ endstep += RESAMPLE_VALUE(&playing->resampler[1], vol);
+ dumb_record_click(sigrenderer->click_remover[0], pos, startstep);
+ dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -endstep);
+ } else {
+ RESAMPLE(&playing->resampler[0], samples[0] + pos, vol);
+ if (!IT_IS_SURROUND_SHIFTED(pan)) vol = 2.0f * volume - vol;
+ RESAMPLERV(size_rendered, &playing->resampler[1], samples[0] + pos, vol);
+ }
+ } else {
+ if (sigrenderer->click_remover && sigrenderer->click_remover[0])
+ dumb_record_click(sigrenderer->click_remover[0], pos, RESAMPLE_VALUE(&playing->resampler[0], volume));
+ RESAMPLERV(size_rendered, &playing->resampler[0], samples[0] + pos, volume);
+ if (sigrenderer->click_remover && sigrenderer->click_remover[0])
+ dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -RESAMPLE_VALUE(&playing->resampler[0], volume));
+ }
+ }
+ (*left_to_mix)--;
+ }
+
+ if (playing->resampler[0].dir == 0)
+ playing->flags |= IT_PLAYING_DEAD;
+
+ return size_rendered;
+}
+
+
+
+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 = it_sine[playing->sample_vibrato_time];
+
+ 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;
+
+ *delta *= (float)pow(DUMB_PITCH_BASE, sample_vibrato_shift);
+ }
+
+ if (playing->env_instrument &&
+ (playing->env_instrument->pitch_envelope.flags & IT_ENVELOPE_ON))
+ {
+ 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;
+
+ 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;
+
+ 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 (to_mix[i].volume && (playing->true_filter_cutoff != 127 << IT_ENVELOPE_SHIFT || playing->true_filter_resonance != 0)) {
+ if (!samples_to_filter) {
+ samples_to_filter = create_sample_buffer(sigrenderer->n_channels, size + 1);
+ if (!samples_to_filter) {
+ render_playing(sigrenderer, playing, 0, note_delta, pos, size, NULL, 0, &left_to_mix);
+ 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(sigrenderer, playing, to_mix[i].volume, note_delta, 0, size, samples_to_filter, 1, &left_to_mix);
+ sigrenderer->click_remover = cr;
+ it_filter(cr ? cr[0] : NULL, &playing->filter_state[0], samples[0], pos, samples_to_filter[0], size_rendered,
+ 65536.0f/delta, playing->true_filter_cutoff, playing->true_filter_resonance);
+ if (sigrenderer->n_channels == 2)
+ it_filter(cr ? cr[1] : NULL, &playing->filter_state[1], samples[1], pos, samples_to_filter[1], size_rendered,
+ 65536.0f/delta, playing->true_filter_cutoff, playing->true_filter_resonance);
+ // warning: filtering is not prevented by low left_to_mix!
+ }
+ } else {
+ it_reset_filter_state(&playing->filter_state[0]);
+ it_reset_filter_state(&playing->filter_state[1]);
+ render_playing(sigrenderer, playing, to_mix[i].volume, note_delta, pos, size, samples, 0, &left_to_mix);
+ }
+ }
+
+ 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)) {
+ 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 (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->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->note = 0;
+ channel->SFmacro = 0;
+ channel->filter_cutoff = 127;
+ channel->filter_resonance = 0;
+ channel->xm_retrig = 0;
+ channel->retrig_tick = 0;
+ channel->tremor_time = 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->lastI = 0;
+ channel->lastJ = 0;
+ channel->lastN = 0;
+ channel->lastO = 0;
+ channel->high_offset = 0;
+ channel->lastQ = 0;
+ channel->lastS = 0;
+ channel->pat_loop_row = 0;
+ channel->pat_loop_count = 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;
+ }
+
+ for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++)
+ sigrenderer->playing[i] = NULL;
+
+ sigrenderer->speed = sigdata->speed;
+
+ sigrenderer->processrow = 0;
+ sigrenderer->breakrow = 0;
+ sigrenderer->pat_loop_row = -1;
+ sigrenderer->rowcount = 1;
+
+ reset_tick_counts(sigrenderer);
+
+ sigrenderer->tick = sigrenderer->speed;
+
+ {
+ IT_PATTERN *pattern;
+ int n;
+
+ sigrenderer->processorder = startorder;
+ for (;;) {
+ 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
+ {
+ _dumb_it_end_sigrenderer(sigrenderer);
+ return NULL;
+ }
+
+ sigrenderer->processorder++;
+ if (sigrenderer->processorder >= sigdata->n_orders)
+ sigrenderer->processorder = 0;
+ if (sigrenderer->processorder == startorder) {
+ _dumb_it_end_sigrenderer(sigrenderer);
+ return NULL;
+ }
+ }
+
+ pattern = &sigdata->pattern[n];
+
+ sigrenderer->n_rows = pattern->n_rows;
+
+/** WARNING - everything pertaining to a new pattern initialised? */
+
+ sigrenderer->entry = sigrenderer->entry_start = pattern->entry;
+ sigrenderer->entry_end = sigrenderer->entry + pattern->n_entries;
+
+ sigrenderer->order = sigrenderer->processorder;
+ sigrenderer->row = 0;
+ }
+
+ reset_effects(sigrenderer);
+
+ {
+ IT_ENTRY *entry = sigrenderer->entry;
+
+ while (entry < sigrenderer->entry_end && !IT_IS_END_ROW(entry))
+ update_pattern_variables(sigrenderer, entry++);
+ }
+
+ {
+ IT_ENTRY *entry = sigrenderer->entry;
+
+ while (entry < sigrenderer->entry_end && !IT_IS_END_ROW(entry)) {
+ if (process_entry(sigrenderer, entry++)) {
+ /* Oops, that song ended quickly! */
+ _dumb_it_end_sigrenderer(sigrenderer);
+ return NULL;
+ }
+ }
+ }
+
+ if (!(sigdata->flags & IT_OLD_EFFECTS))
+ update_smooth_effects(sigrenderer);
+
+ process_all_playing(sigrenderer);
+
+ sigrenderer->time_left = TICK_TIME_DIVIDEND / sigrenderer->tempo;
+ sigrenderer->sub_time_left = 0;
+
+ return sigrenderer;
+}
+
+
+
+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 byte), void *data)
+{
+ if (sigrenderer) {
+ sigrenderer->callbacks->midi = callback;
+ sigrenderer->callbacks->midi_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;
+ 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);
+ 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;
+ }
+ }
+
+ for (;;) {
+ if (sigrenderer->time_left >= pos)
+ break;
+
+ 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;
+
+ /** WARNING - everything initialised? */
+
+ 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;
+
+ 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);
+
+ 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);
+
+ 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);
+
+ 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 byte)
+{
+ (void)data;
+ (void)channel;
+ (void)byte;
+ return 1;
+}
+
+
+
+#define IT_CHECKPOINT_INTERVAL (30 * 65536) /* Half a minute */
+
+
+
+/* Returns the length of the module, up until it first loops. */
+long _dumb_it_build_checkpoints(DUMB_IT_SIGDATA *sigdata)
+{
+ IT_CHECKPOINT *checkpoint = malloc(sizeof(*checkpoint));
+ if (!checkpoint) return 0;
+ checkpoint->time = 0;
+ checkpoint->sigrenderer = dumb_it_init_sigrenderer(sigdata, 0, 0);
+ 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;
+ 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;
+ }
+}
diff --git a/apps/codecs/dumb/src/it/itunload.c b/apps/codecs/dumb/src/it/itunload.c
new file mode 100644
index 0000000..8f282be
--- /dev/null
+++ b/apps/codecs/dumb/src/it/itunload.c
@@ -0,0 +1,71 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/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->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].left)
+ free(sigdata->sample[n].left);
+ if (sigdata->sample[n].right)
+ free(sigdata->sample[n].right);
+ }
+ 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/apps/codecs/dumb/src/it/loadmod.c b/apps/codecs/dumb/src/it/loadmod.c
new file mode 100644
index 0000000..f7bbcb5
--- /dev/null
+++ b/apps/codecs/dumb/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(): 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(const char *filename)
+{
+ DUH *duh;
+ DUMBFILE *f = dumbfile_open(filename);
+
+ if (!f)
+ return NULL;
+
+ duh = dumb_read_mod(f);
+
+ dumbfile_close(f);
+
+ return duh;
+}
diff --git a/apps/codecs/dumb/src/it/loads3m.c b/apps/codecs/dumb/src/it/loads3m.c
new file mode 100644
index 0000000..dfe6a92
--- /dev/null
+++ b/apps/codecs/dumb/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(): 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(const char *filename)
+{
+ DUH *duh;
+ DUMBFILE *f = dumbfile_open(filename);
+
+ if (!f)
+ return NULL;
+
+ duh = dumb_read_s3m(f);
+
+ dumbfile_close(f);
+
+ return duh;
+}
diff --git a/apps/codecs/dumb/src/it/loadxm.c b/apps/codecs/dumb/src/it/loadxm.c
new file mode 100644
index 0000000..9cee06d
--- /dev/null
+++ b/apps/codecs/dumb/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(): 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(const char *filename)
+{
+ DUH *duh;
+ DUMBFILE *f = dumbfile_open(filename);
+
+ if (!f)
+ return NULL;
+
+ duh = dumb_read_xm(f);
+
+ dumbfile_close(f);
+
+ return duh;
+}
diff --git a/apps/codecs/dumb/src/it/readmod.c b/apps/codecs/dumb/src/it/readmod.c
new file mode 100644
index 0000000..452c890
--- /dev/null
+++ b/apps/codecs/dumb/src/it/readmod.c
@@ -0,0 +1,594 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * readmod.c - Code to read a good old-fashioned / / \ \
+ * Amiga module from an open file. | < / \_
+ * | \/ /\ /
+ * By Ben Davis. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#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);
+
+ entry++;
+ }
+ pos += 4;
+ }
+ IT_SET_END_ROW(entry);
+ entry++;
+ }
+
+ return 0;
+}
+
+
+
+/* This function does not skip the name (22 bytes); it is assumed the caller
+ * has already done that.
+ */
+static int it_mod_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.
+*/
+ 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?
+ sample->loop_start = dumbfile_mgetw(f) << 1;
+ sample->loop_end = sample->loop_start + (dumbfile_mgetw(f) << 1);
+/**
+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 = (long)(16726.0*pow(DUMB_PITCH_BASE, 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?
+
+ return dumbfile_error(f);
+}
+
+
+
+static int it_mod_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->left = malloc(sample->length * sizeof(*sample->left));
+
+ if (!sample->left)
+ return -1;
+
+ /* Sample data are stored in "8-bit two's compliment format" (sic). */
+ for (i = 0; i < sample->length; i++)
+ sample->left[i] = (int)(signed char)dumbfile_getc(f) << 16;
+
+ /* skip truncated data */
+ dumbfile_skip(f, truncated_size);
+ // Should we be truncating it?
+
+ if (dumbfile_error(f))
+ return -1;
+
+ 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) {
+ int rv;
+ memcpy(ptr, bm->buffered + bm->ptr, left);
+ free(bm->buffered);
+ bm->buffered = NULL;
+ rv = dumbfile_getnc(ptr + left, n - left, bm->remaining);
+ return left + MAX(rv, 0);
+ }
+ 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 DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f)
+{
+ DUMB_IT_SIGDATA *sigdata;
+ int n_channels;
+ int i;
+ unsigned long fft;
+
+ f = dumbfile_buffer_mod(f, &fft);
+ if (!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_skip(f, 20)) {
+ dumbfile_close(f);
+ return NULL;
+ }
+
+ sigdata = malloc(sizeof(*sigdata));
+ if (!sigdata) {
+ dumbfile_close(f);
+ return NULL;
+ }
+
+ 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('F','L','T','4'):
+ 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;
+ }
+ }
+
+ sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
+ if (!sigdata->sample) {
+ free(sigdata);
+ dumbfile_close(f);
+ return NULL;
+ }
+
+ sigdata->order = NULL;
+ sigdata->instrument = NULL;
+ sigdata->pattern = NULL;
+ sigdata->midi = NULL;
+ sigdata->checkpoint = NULL;
+
+ for (i = 0; i < sigdata->n_samples; i++)
+ sigdata->sample[i].right = sigdata->sample[i].left = NULL;
+
+ for (i = 0; i < sigdata->n_samples; i++) {
+ if (dumbfile_skip(f, 22) ||
+ 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;
+ }
+
+ /* "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);
+ }
+
+ /* And finally, the sample data */
+ for (i = 0; i < sigdata->n_samples; i++) {
+ if (it_mod_read_sample_data(&sigdata->sample[i], f)) {
+ _dumb_it_unload_sigdata(sigdata);
+ dumbfile_close(f);
+ return NULL;
+ }
+ }
+
+ dumbfile_close(f); /* Destroy the BUFFERED_MOD DUMBFILE we were using. */
+ /* The DUMBFILE originally passed to our function is intact. */
+
+ /* 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(DUMBFILE *f)
+{
+ sigdata_t *sigdata;
+ long length;
+
+ DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+ sigdata = it_mod_load_sigdata(f);
+
+ if (!sigdata)
+ return NULL;
+
+ length = _dumb_it_build_checkpoints(sigdata);
+
+ return make_duh(length, 1, &descptr, &sigdata);
+}
diff --git a/apps/codecs/dumb/src/it/reads3m.c b/apps/codecs/dumb/src/it/reads3m.c
new file mode 100644
index 0000000..6a8b84d
--- /dev/null
+++ b/apps/codecs/dumb/src/it/reads3m.c
@@ -0,0 +1,668 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/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"
+
+
+
+/** 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, DUMBFILE *f)
+{
+ unsigned char type;
+ int flags;
+
+ type = dumbfile_getc(f);
+
+ if (type > 1) {
+ /** WARNING: no adlib support */
+ }
+
+ /* Skip DOS Filename */
+ dumbfile_skip(f, 13);
+
+ *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);
+
+ if (dumbfile_getc(f) != 0)
+ /* Sample is packed apparently (or error reading from file). We don't
+ * know how to read packed samples.
+ */
+ return -1;
+
+ flags = dumbfile_getc(f);
+
+ sample->C5_speed = dumbfile_igetl(f) << 1;
+
+ /* Skip four unused bytes, three internal variables and sample name. */
+ dumbfile_skip(f, 4+2+2+4+28);
+
+ if (type == 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;
+ if (flags & 2) sample->flags |= IT_SAMPLE_STEREO;
+ if (flags & 4) sample->flags |= IT_SAMPLE_16BIT;
+
+ 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
+ /* 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;
+
+ return dumbfile_error(f);
+}
+
+
+
+static int it_s3m_read_sample_data(IT_SAMPLE *sample, int ffi, DUMBFILE *f)
+{
+ long n;
+
+ sample->left = malloc(sample->length * sizeof(*sample->left));
+ if (!sample->left)
+ return -1;
+
+ if (sample->flags & IT_SAMPLE_STEREO) {
+ sample->right = malloc(sample->length * sizeof(*sample->right));
+ if (!sample->right)
+ return -1;
+ }
+
+ if (sample->flags & IT_SAMPLE_STEREO) {
+ if (sample->flags & IT_SAMPLE_16BIT) {
+ for (n = 0; n < sample->length; n++)
+ sample->left[n] = (int)(signed short)dumbfile_igetw(f) << 8;
+ for (n = 0; n < sample->length; n++)
+ sample->right[n] = (int)(signed short)dumbfile_igetw(f) << 8;
+ } else {
+ for (n = 0; n < sample->length; n++)
+ sample->left[n] = (int)(signed char)dumbfile_getc(f) << 16;
+ for (n = 0; n < sample->length; n++)
+ sample->right[n] = (int)(signed char)dumbfile_getc(f) << 16;
+ }
+ } else if (sample->flags & IT_SAMPLE_16BIT)
+ for (n = 0; n < sample->length; n++)
+ sample->left[n] = (int)(signed short)dumbfile_igetw(f) << 8;
+ else
+ for (n = 0; n < sample->length; n++)
+ sample->left[n] = (int)(signed char)dumbfile_getc(f) << 16;
+
+ if (dumbfile_error(f))
+ return -1;
+
+ if (ffi != 1) {
+ /* Convert to signed. */
+ for (n = 0; n < sample->length; n++)
+ sample->left[n] ^= 0xFF800000;
+
+ if (sample->right)
+ for (n = 0; n < sample->length; n++)
+ sample->right[n] ^= 0xFF800000;
+ }
+
+ return 0;
+}
+
+
+
+static int it_s3m_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer)
+{
+ 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. */
+ dumbfile_skip(f, 2);
+
+ if (dumbfile_error(f))
+ 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;
+ 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, 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;
+ }
+
+ 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++];
+ if (entry->effect != 255) {
+ entry->mask |= IT_ENTRY_EFFECT;
+ if (entry->effect == IT_BREAK_TO_ROW)
+ entry->effectvalue -= (entry->effectvalue >> 4) * 6;
+ }
+ /** 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 IT_COMPONENT_INSTRUMENT 1
+#define IT_COMPONENT_PATTERN 2
+#define IT_COMPONENT_SAMPLE 3
+
+typedef struct IT_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 */
+}
+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 DUMB_IT_SIGDATA *it_s3m_load_sigdata(DUMBFILE *f)
+{
+ DUMB_IT_SIGDATA *sigdata;
+
+ int flags, cwtv, ffi;
+ int default_pan_present;
+
+ IT_COMPONENT *component;
+ int n_components = 0;
+
+ int n;
+
+ unsigned char *buffer;
+
+ /* Skip song name. */
+ if (dumbfile_skip(f, 28)) return NULL;
+
+ if (dumbfile_getc(f) != 0x1A) return NULL;
+ if (dumbfile_getc(f) != 16) return NULL;
+
+ if (dumbfile_skip(f, 2)) return NULL;
+
+ sigdata = malloc(sizeof(*sigdata));
+
+ if (!sigdata)
+ return 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].right = sigdata->sample[n].left = 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_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX;
+
+ if (dumbfile_mgetl(f) != DUMB_ID('S','C','R','M')) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ sigdata->global_volume = dumbfile_getc(f) << 1;
+ 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;
+
+ /* 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);
+
+ /* 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 */
+ 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 = IT_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 = IT_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(IT_COMPONENT), &it_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;
+ }
+ }
+
+ /** WARNING: is this right? */
+ sigdata->pan_separation = 64;
+
+ 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++) {
+ long offset;
+ int m;
+
+ 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_PATTERN:
+ if (it_s3m_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_s3m_read_sample_header(&sigdata->sample[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_s3m_read_sample_data(&sigdata->sample[component[m].n], ffi, 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_s3m(DUMBFILE *f)
+{
+ sigdata_t *sigdata;
+ long length;
+
+ DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+ sigdata = it_s3m_load_sigdata(f);
+
+ if (!sigdata)
+ return NULL;
+
+ length = _dumb_it_build_checkpoints(sigdata);
+
+ return make_duh(length, 1, &descptr, &sigdata);
+}
diff --git a/apps/codecs/dumb/src/it/readxm.c b/apps/codecs/dumb/src/it/readxm.c
new file mode 100644
index 0000000..2aeda1f
--- /dev/null
+++ b/apps/codecs/dumb/src/it/readxm.c
@@ -0,0 +1,998 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/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_E_SET_FINETUNE todo.
+ * 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 :) */
+static const char xm_convert_vibrato[] = {
+ IT_VIBRATO_SINE,
+ IT_VIBRATO_SQUARE,
+ IT_VIBRATO_SAWTOOTH,
+ IT_VIBRATO_SAWTOOTH
+};
+
+
+
+#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 size;
+ int pos;
+ int channel;
+ int row;
+ int effect, effectvalue;
+ IT_ENTRY *entry;
+
+ /* pattern header size */
+ if (dumbfile_igetl(f) != 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;
+ }
+
+ 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;
+ }
+
+ 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);
+
+ entry++;
+ }
+
+ channel++;
+ if (channel >= n_channels) {
+ channel = 0;
+ 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) {
+ TRACE("XM error: wrong number of envelope nodes (%d)\n", envelope->n_nodes);
+ envelope->n_nodes = 0;
+ return -1;
+ }
+
+ 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);
+
+ //memset(instrument, 0, sizeof(*instrument));
+ dumbfile_skip(f, 22); /* Instrument name */
+ 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 */
+ if (dumbfile_igetl(f) != 0x28) {
+ 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)
+ 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;
+ }
+
+ 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;
+
+ 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)*64)/255 | 128; /* 0-255 */
+ relative_note_number = (signed char)dumbfile_getc(f);
+
+ dumbfile_skip(f, 1); /* reserved */
+ dumbfile_skip(f, 22); /* sample name */
+
+ if (dumbfile_error(f))
+ return -1;
+
+ sample->C5_speed = (long)(16726.0*pow(DUMB_SEMITONE_BASE, relative_note_number)*pow(DUMB_PITCH_BASE, finetune*2));
+
+ sample->flags = IT_SAMPLE_EXISTS;
+
+ 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;
+
+ 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) {
+ truncated_size = sample->length - sample->loop_end;
+ sample->length = sample->loop_end;
+ } else {
+ truncated_size = 0;
+ }
+
+ sample->left = malloc(sample->length * sizeof(*sample->left));
+ if (!sample->left)
+ return -1;
+
+ if (sample->flags & IT_SAMPLE_STEREO) {
+ sample->right = malloc(sample->length * sizeof(*sample->right));
+ if (!sample->right)
+ return -1;
+ }
+
+ /* sample data is stored as signed delta values */
+ old = 0;
+ if (sample->flags & IT_SAMPLE_16BIT) {
+ for (i = 0; i < sample->length; i++) {
+ old = sample->left[i] = (int)(signed short)(old + dumbfile_igetw(f));
+ sample->left[i] <<= 8;
+ }
+ } else {
+ for (i = 0; i < sample->length; i++) {
+ old = sample->left[i] = (int)(signed char)(old + dumbfile_getc(f));
+ sample->left[i] <<= 16;
+ }
+ }
+
+ /* 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 = 0; i < sample->length; i++) {
+ old = sample->right[i] = (int)(signed short)(old + dumbfile_igetw(f));
+ sample->right[i] <<= 8;
+ }
+ } else {
+ for (i = 0; i < sample->length; i++) {
+ old = sample->right[i] = (int)(signed char)(old + dumbfile_getc(f));
+ sample->right[i] <<= 16;
+ }
+ }
+ /* 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)
+{
+ DUMB_IT_SIGDATA *sigdata;
+ char id_text[18];
+
+ 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;
+ }
+
+ /* song name */
+ if (dumbfile_skip(f, 20))
+ return NULL;
+
+ if (dumbfile_getc(f) != 0x1A) {
+ TRACE("XM error: 0x1A not found\n");
+ return NULL;
+ }
+
+ /* tracker name */
+ if (dumbfile_skip(f, 20))
+ return NULL;
+
+ /* version number */
+ if (dumbfile_igetw(f) != 0x0104) {
+ TRACE("XM error: wrong format version\n");
+ return NULL;
+ }
+
+ /*
+ ------------------
+ --- Header ---
+ ------------------
+ */
+
+ /* header size */
+ if (dumbfile_igetl(f) != 0x0114) {
+ TRACE("XM error: unexpected header size\n");
+ return NULL;
+ }
+
+ sigdata = malloc(sizeof(*sigdata));
+ if (!sigdata)
+ return 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_patterns = dumbfile_igetw(f);
+ sigdata->n_instruments = dumbfile_igetw(f); /* max 128 */
+ flags = dumbfile_igetw(f);
+ sigdata->speed = dumbfile_igetw(f);
+ sigdata->tempo = dumbfile_igetw(f);
+
+ /* sanity checks */
+ if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_orders > 256 || sigdata->n_patterns > 256 || sigdata->n_instruments > 128 || 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, 256 - sigdata->n_orders);
+
+ if (dumbfile_error(f)) {
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ /*
+ --------------------
+ --- 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) != 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) {
+ TRACE("XM error: instrument %d\n", i+1);
+ _dumb_it_unload_sigdata(sigdata);
+ return NULL;
+ }
+
+ 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].right = sigdata->sample[j].left = 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];
+ }
+ 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;
+
+ 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 */
+
+
+
+DUH *dumb_read_xm(DUMBFILE *f)
+{
+ sigdata_t *sigdata;
+ long length;
+
+ DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+ sigdata = it_xm_load_sigdata(f);
+
+ if (!sigdata)
+ return NULL;
+
+ length = _dumb_it_build_checkpoints(sigdata);
+
+ return make_duh(length, 1, &descptr, &sigdata);
+}
diff --git a/apps/codecs/dumb/src/it/xmeffect.c b/apps/codecs/dumb/src/it/xmeffect.c
new file mode 100644
index 0000000..51995f3
--- /dev/null
+++ b/apps/codecs/dumb/src/it/xmeffect.c
@@ -0,0 +1,242 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/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)
+{
+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; /** TODO: glissando control */
+ 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);
+ 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(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:
+ 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:
+ /** WARNING: In FT2, the value seems to do something... Oh well,
+ * this is undocumented anyway!
+ */
+ entry->mask &= ~IT_ENTRY_EFFECT;
+ entry->mask |= IT_ENTRY_NOTE;
+ entry->note = IT_NOTE_OFF;
+ 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; /** TODO */
+ 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_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; /** TODO: value&4 -> don't retrig wave */
+ break;
+
+ case EBASE + XM_E_SET_TREMOLO_CONTROL:
+ effect = SBASE+IT_S_SET_TREMOLO_WAVEFORM;
+ value &= ~4; /** TODO: value&4 -> don't retrig wave */
+ 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;
+}