forked from len0rd/rockbox
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
This commit is contained in:
parent
7e7662bb71
commit
27be5bc728
67 changed files with 18488 additions and 1 deletions
|
@ -16,7 +16,7 @@ ifdef APPEXTRA
|
|||
INCLUDES += -I$(APPSDIR)/$(APPEXTRA)
|
||||
endif
|
||||
|
||||
.PHONY: libmad liba52 libFLAC libTremor libwavpack
|
||||
.PHONY: libmad liba52 libFLAC libTremor libwavpack dumb
|
||||
|
||||
OUTPUT = $(SOFTWARECODECS)
|
||||
|
||||
|
@ -50,6 +50,11 @@ libwavpack:
|
|||
@mkdir -p $(OBJDIR)/libwavpack
|
||||
@$(MAKE) -C libwavpack TARGET=$(TARGET) DEBUG=$(DEBUG) OBJDIR=$(OBJDIR)/libwavpack VERSION=$(VERSION) EXTRA_DEFINES="$(EXTRA_DEFINES)" MEM=${MEMORYSIZE} OUTPUT=$(OBJDIR)/libwavpack.a
|
||||
|
||||
dumb:
|
||||
@echo "MAKE in dumb"
|
||||
@mkdir -p $(OBJDIR)/dumb
|
||||
@$(MAKE) -C dumb TARGET=$(TARGET) DEBUG=$(DEBUG) OBJDIR=$(OBJDIR)/dumb VERSION=$(VERSION) EXTRA_DEFINES="$(EXTRA_DEFINES)" MEM=${MEMORYSIZE} LIBDIR=$(OBJDIR) OFLAGS="$(CFLAGS)"
|
||||
|
||||
clean:
|
||||
@echo "cleaning codecs"
|
||||
@rm -fr $(OBJDIR)/libmad $(OBJDIR)/liba52 $(OBJDIR)/libFLAC $(OBJDIR)/Tremor $(OBJDIR)/libwavpack
|
||||
|
|
311
apps/codecs/dumb/Makefile
Normal file
311
apps/codecs/dumb/Makefile
Normal file
|
@ -0,0 +1,311 @@
|
|||
# Main Makefile for DUMB.
|
||||
|
||||
# In theory, this Makefile can be used without modifications on DOS, Windows,
|
||||
# Linux, BeOS and Mac OS X. Caveats are as follows:
|
||||
|
||||
# - For DOS and Windows users, COMSPEC (or ComSpec) must be set to point to
|
||||
# command.com or cmd.exe. If they point to a Unix-style shell, this
|
||||
# Makefile will die horribly.
|
||||
|
||||
# - Users of other platforms must NOT set COMSPEC or ComSpec. They must be
|
||||
# undefined.
|
||||
|
||||
# Commands are as follows:
|
||||
|
||||
# make - Build the library (does make config for you first time).
|
||||
# make install - Install the library and examples into the system.
|
||||
# make uninstall - Remove the above.
|
||||
# make config - Do or redo the configuration.
|
||||
# make clean - Delete all object files; examples and libraries remain.
|
||||
# make veryclean - Delete examples and libraries too.
|
||||
|
||||
# TODO: consider whether to delete config.txt and/or dumbask(.exe)
|
||||
|
||||
|
||||
.PHONY: all install uninstall clean veryclean config config-if-necessary
|
||||
|
||||
PHONY_TARGETS := core allegro core-examples allegro-examples core-headers allegro-headers
|
||||
|
||||
.PHONY: $(PHONY_TARGETS)
|
||||
.PHONY: $(PHONY_TARGETS:%=install-%)
|
||||
.PHONY: $(PHONY_TARGETS:%=uninstall-%)
|
||||
|
||||
|
||||
COMMA := ,
|
||||
|
||||
#CC := gcc
|
||||
#AR := ar
|
||||
|
||||
|
||||
# Configuration.
|
||||
# The configuration is done by an MS-DOS batch file if COMSPEC is set.
|
||||
# Otherwise it is done by a Unix shell script. A file called 'config.txt',
|
||||
# containing variables that control the build process, is created, and
|
||||
# included by this Makefile.
|
||||
|
||||
|
||||
ifeq "$(COMSPEC)" ""
|
||||
ifdef ComSpec
|
||||
COMSPEC := $(ComSpec)
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
-include make/config.txt
|
||||
|
||||
|
||||
ifeq "$(OSTYPE)" "beos"
|
||||
|
||||
INCLUDE_INSTALL_PATH := /boot/develop/headers
|
||||
LIB_INSTALL_PATH := /boot/develop/lib/x86
|
||||
BIN_INSTALL_PATH := /boot/home/config/bin
|
||||
# DEFAULT_PREFIX is not set, so config.sh will not prompt for PREFIX.
|
||||
LINK_MATH :=
|
||||
|
||||
else
|
||||
|
||||
ifdef PREFIX
|
||||
DEFAULT_PREFIX := $(PREFIX)
|
||||
else
|
||||
DEFAULT_PREFIX := /usr/local
|
||||
endif
|
||||
export DEFAULT_PREFIX
|
||||
INCLUDE_INSTALL_PATH := $(PREFIX)/include
|
||||
LIB_INSTALL_PATH := $(PREFIX)/lib
|
||||
BIN_INSTALL_PATH := $(PREFIX)/bin
|
||||
|
||||
endif
|
||||
|
||||
|
||||
all: config-if-necessary
|
||||
@$(MAKE) --no-print-directory $(ALL_TARGETS)
|
||||
$(call ECHO,DUMB has been built. Run $(APOST)make install$(APOST) to install it.)
|
||||
|
||||
install: config-if-necessary
|
||||
@$(MAKE) --no-print-directory $(ALL_TARGETS:%=install-%)
|
||||
$(call ECHO,DUMB has been installed.)
|
||||
$(call ECHO,See readme.txt for details on the example programs.)
|
||||
$(call ECHO,When you$(APOST)re ready to start using DUMB$(COMMA) see docs/howto.txt.)
|
||||
$(call ECHO,Enjoy!)
|
||||
|
||||
uninstall: config-if-necessary
|
||||
@$(MAKE) --no-print-directory $(ALL_TARGETS:%=uninstall-%)
|
||||
$(call ECHO,DUMB has been uninstalled.)
|
||||
|
||||
|
||||
ifdef COMSPEC
|
||||
# Assume DOS or Windows.
|
||||
SHELL := $(COMSPEC)
|
||||
CONFIG_COMMAND := make\config.bat
|
||||
DUMBASK_EXE := make/dumbask.exe
|
||||
else
|
||||
# Assume a Unix-compatible system.
|
||||
CONFIG_COMMAND := make/config.sh
|
||||
DUMBASK_EXE := make/dumbask
|
||||
endif
|
||||
|
||||
# This will always configure.
|
||||
config: $(DUMBASK_EXE)
|
||||
$(CONFIG_COMMAND)
|
||||
|
||||
# This will only configure if the configuration file is absent. We don't use
|
||||
# config.txt as the target name, because Make then runs the config initially,
|
||||
# and again when it sees the 'config' target, so an initial 'make config'
|
||||
# causes the configuration to be done twice.
|
||||
ifeq "$(wildcard make/config.txt)" ""
|
||||
config-if-necessary: config
|
||||
else
|
||||
config-if-necessary:
|
||||
endif
|
||||
|
||||
$(DUMBASK_EXE): make/dumbask.c
|
||||
$(CC) $< -o $@
|
||||
|
||||
|
||||
ifdef PLATFORM
|
||||
|
||||
|
||||
# Build.
|
||||
|
||||
|
||||
CORE_MODULES := \
|
||||
core/atexit.c \
|
||||
core/duhlen.c \
|
||||
core/dumbfile.c \
|
||||
core/loadduh.c \
|
||||
core/makeduh.c \
|
||||
core/rawsig.c \
|
||||
core/readduh.c \
|
||||
core/register.c \
|
||||
core/rendduh.c \
|
||||
core/rendsig.c \
|
||||
core/unload.c \
|
||||
helpers/clickrem.c \
|
||||
helpers/memfile.c \
|
||||
helpers/resample.c \
|
||||
helpers/sampbuf.c \
|
||||
helpers/silence.c \
|
||||
it/itload.c \
|
||||
it/itread.c \
|
||||
it/itrender.c \
|
||||
it/itunload.c \
|
||||
it/loads3m.c \
|
||||
it/reads3m.c \
|
||||
it/loadxm.c \
|
||||
it/readxm.c \
|
||||
it/loadmod.c \
|
||||
it/readmod.c \
|
||||
it/xmeffect.c \
|
||||
it/itorder.c \
|
||||
it/itmisc.c
|
||||
# helpers/stdfile.c
|
||||
|
||||
ALLEGRO_MODULES := \
|
||||
allegro/alplay.c \
|
||||
allegro/datduh.c \
|
||||
allegro/datit.c \
|
||||
allegro/datxm.c \
|
||||
allegro/dats3m.c \
|
||||
allegro/datmod.c \
|
||||
allegro/datunld.c \
|
||||
allegro/packfile.c
|
||||
|
||||
CORE_EXAMPLES := examples/dumbout.c
|
||||
ALLEGRO_EXAMPLES := examples/dumbplay.c
|
||||
|
||||
CORE_HEADERS := include/dumb.h
|
||||
ALLEGRO_HEADERS := include/aldumb.h
|
||||
|
||||
|
||||
LIBDIR := lib/$(PLATFORM)
|
||||
OBJDIR_BASE := obj/$(PLATFORM)
|
||||
|
||||
|
||||
WFLAGS := -Wall -W -Wwrite-strings -Wstrict-prototypes -Wmissing-declarations -DDUMB_DECLARE_DEPRECATED
|
||||
WFLAGS_ALLEGRO := -Wno-missing-declarations
|
||||
OFLAGS := -O2 -ffast-math -fomit-frame-pointer
|
||||
DBGFLAGS := -DDEBUGMODE=1 -g3
|
||||
|
||||
CFLAGS_RELEASE := -Iinclude $(WFLAGS) $(OFLAGS)
|
||||
CFLAGS_DEBUG := -Iinclude $(WFLAGS) $(DBGFLAGS)
|
||||
|
||||
LDFLAGS := -s
|
||||
|
||||
|
||||
CORE_EXAMPLES_OBJ := $(addprefix examples/, $(notdir $(patsubst %.c, %.o, $(CORE_EXAMPLES))))
|
||||
ALLEGRO_EXAMPLES_OBJ := $(addprefix examples/, $(notdir $(patsubst %.c, %.o, $(ALLEGRO_EXAMPLES))))
|
||||
|
||||
CORE_EXAMPLES_EXE := $(addprefix examples/, $(notdir $(patsubst %.c, %$(EXE_SUFFIX), $(CORE_EXAMPLES))))
|
||||
ALLEGRO_EXAMPLES_EXE := $(addprefix examples/, $(notdir $(patsubst %.c, %$(EXE_SUFFIX), $(ALLEGRO_EXAMPLES))))
|
||||
|
||||
|
||||
CORE_LIB_FILE_RELEASE := $(LIBDIR)/libdumb.a
|
||||
ALLEGRO_LIB_FILE_RELEASE := $(LIBDIR)/libaldmb.a
|
||||
|
||||
CORE_LIB_FILE_DEBUG := $(LIBDIR)/libdumbd.a
|
||||
ALLEGRO_LIB_FILE_DEBUG := $(LIBDIR)/libaldmd.a
|
||||
|
||||
|
||||
core: $(CORE_LIB_FILE_RELEASE) $(CORE_LIB_FILE_DEBUG)
|
||||
allegro: $(ALLEGRO_LIB_FILE_RELEASE) $(ALLEGRO_LIB_FILE_DEBUG)
|
||||
|
||||
core-examples: $(CORE_EXAMPLES_EXE)
|
||||
allegro-examples: $(ALLEGRO_EXAMPLES_EXE)
|
||||
|
||||
core-headers:
|
||||
|
||||
allegro-headers:
|
||||
|
||||
install-core: core
|
||||
$(call COPY,$(CORE_LIB_FILE_RELEASE),$(LIB_INSTALL_PATH))
|
||||
$(call COPY,$(CORE_LIB_FILE_DEBUG),$(LIB_INSTALL_PATH))
|
||||
|
||||
install-allegro: allegro
|
||||
$(call COPY,$(ALLEGRO_LIB_FILE_RELEASE),$(LIB_INSTALL_PATH))
|
||||
$(call COPY,$(ALLEGRO_LIB_FILE_DEBUG),$(LIB_INSTALL_PATH))
|
||||
|
||||
ifeq "$(COMSPEC)" ""
|
||||
install-core-examples: core-examples
|
||||
$(call COPY,$(CORE_EXAMPLES_EXE),$(BIN_INSTALL_PATH))
|
||||
|
||||
install-allegro-examples: allegro-examples
|
||||
$(call COPY,$(ALLEGRO_EXAMPLES_EXE),$(BIN_INSTALL_PATH))
|
||||
else
|
||||
# Don't install the examples on a Windows system.
|
||||
install-core-examples:
|
||||
install-allegro-examples:
|
||||
endif
|
||||
|
||||
install-core-headers:
|
||||
$(call COPY,$(CORE_HEADERS),$(INCLUDE_INSTALL_PATH))
|
||||
|
||||
install-allegro-headers:
|
||||
$(call COPY,$(ALLEGRO_HEADERS),$(INCLUDE_INSTALL_PATH))
|
||||
|
||||
|
||||
uninstall-core:
|
||||
$(call DELETE,$(LIB_INSTALL_PATH)/$(notdir $(CORE_LIB_FILE_RELEASE)))
|
||||
$(call DELETE,$(LIB_INSTALL_PATH)/$(notdir $(CORE_LIB_FILE_DEBUG)))
|
||||
|
||||
uninstall-allegro:
|
||||
$(call DELETE,$(LIB_INSTALL_PATH)/$(notdir $(ALLEGRO_LIB_FILE_RELEASE)))
|
||||
$(call DELETE,$(LIB_INSTALL_PATH)/$(notdir $(ALLEGRO_LIB_FILE_DEBUG)))
|
||||
|
||||
ifeq "$COMSPEC" ""
|
||||
uninstall-core-examples:
|
||||
$(call DELETE,$(patsubst %,$(BIN_INSTALL_PATH)/%,$(notdir $(CORE_EXAMPLES_EXE))))
|
||||
|
||||
uninstall-allegro-examples:
|
||||
$(call DELETE,$(patsubst %,$(BIN_INSTALL_PATH)/%,$(notdir $(ALLEGRO_EXAMPLES_EXE))))
|
||||
else
|
||||
# The examples wouldn't have been installed on a Windows system.
|
||||
uninstall-core-examples:
|
||||
uninstall-allegro-examples:
|
||||
endif
|
||||
|
||||
uninstall-core-headers:
|
||||
$(call DELETE,$(patsubst %,$(INCLUDE_INSTALL_PATH)/%,$(notdir $(CORE_HEADERS))))
|
||||
|
||||
uninstall-allegro-headers:
|
||||
$(call DELETE,$(patsubst %,$(INCLUDE_INSTALL_PATH)/%,$(notdir $(ALLEGRO_HEADERS))))
|
||||
|
||||
|
||||
OBJDIR := $(OBJDIR_BASE)/release
|
||||
CFLAGS := $(CFLAGS_RELEASE)
|
||||
CORE_LIB_FILE := $(LIBDIR)/libdumb.a
|
||||
ALLEGRO_LIB_FILE := $(LIBDIR)/libaldmb.a
|
||||
include make/Makefile.inc
|
||||
|
||||
OBJDIR := $(OBJDIR_BASE)/debug
|
||||
CFLAGS := $(CFLAGS_DEBUG)
|
||||
CORE_LIB_FILE := $(LIBDIR)/libdumbd.a
|
||||
ALLEGRO_LIB_FILE := $(LIBDIR)/libaldmd.a
|
||||
include make/Makefile.inc
|
||||
|
||||
|
||||
$(CORE_EXAMPLES_EXE): examples/%$(EXE_SUFFIX): examples/%.o $(CORE_LIB_FILE_RELEASE)
|
||||
$(CC) $^ -o $@ $(LDFLAGS) $(LINK_MATH)
|
||||
|
||||
$(ALLEGRO_EXAMPLES_EXE): examples/%$(EXE_SUFFIX): examples/%.o $(ALLEGRO_LIB_FILE_RELEASE) $(CORE_LIB_FILE_RELEASE)
|
||||
$(CC) $^ -o $@ $(LDFLAGS) $(LINK_ALLEGRO)
|
||||
|
||||
$(CORE_EXAMPLES_OBJ): examples/%.o: examples/%.c include/dumb.h
|
||||
$(CC) -c $< -o $@ $(CFLAGS_RELEASE)
|
||||
|
||||
$(ALLEGRO_EXAMPLES_OBJ): examples/%.o: examples/%.c include/dumb.h include/aldumb.h
|
||||
$(CC) -c $< -o $@ $(CFLAGS_RELEASE) -Wno-missing-declarations
|
||||
|
||||
|
||||
clean:
|
||||
$(call DELETE,$(call FIX,$(OBJDIR_BASE)/release/*.o))
|
||||
$(call DELETE,$(call FIX,$(OBJDIR_BASE)/debug/*.o))
|
||||
$(call DELETE,$(call FIX,examples/*.o))
|
||||
|
||||
veryclean: clean
|
||||
$(call DELETE,$(call FIX,$(CORE_LIB_FILE)))
|
||||
$(call DELETE,$(call FIX,$(ALLEGRO_LIB_FILE)))
|
||||
$(call DELETE,$(call FIX,$(CORE_EXAMPLES_EXE)))
|
||||
$(call DELETE,$(call FIX,$(ALLEGRO_EXAMPLES_EXE)))
|
||||
|
||||
|
||||
endif # ifdef PLATFORM
|
281
apps/codecs/dumb/docs/deprec.txt
Normal file
281
apps/codecs/dumb/docs/deprec.txt
Normal file
|
@ -0,0 +1,281 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* deprec.txt - Deprecated functions, why they / / \ \
|
||||
* were deprecated, and what to do | < / \_
|
||||
* instead. | \/ /\ /
|
||||
* \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
|
||||
**********************************************
|
||||
*** How the functions have been deprecated ***
|
||||
**********************************************
|
||||
|
||||
|
||||
GCC 3.1 and later provide a very useful attribute. The following:
|
||||
|
||||
__attribute__((__deprecated__))
|
||||
|
||||
when written alongside a function prototype, variable declaration or type
|
||||
definition, will result in a warning from GCC if any such part of the API
|
||||
is used. The warning will even tell you where the declaration is, and I
|
||||
have inserted comments by all the deprecated declarations, telling you
|
||||
what to do.
|
||||
|
||||
Unfortunately, GCC 2.x and 3.0.x and MSVC do not have any means to
|
||||
deprecate things. The approach I have taken with these compilers is to
|
||||
avoid prototyping the declared functions. This means you will get
|
||||
warnings and errors, and they won't be very helpful. If your program
|
||||
compiles, you may get strange crashes when you run it, since the compiler
|
||||
needs the declarations in order to make sure function calls are carried
|
||||
out correctly.
|
||||
|
||||
If you would like the deprecated parts of the API to be declared, you can
|
||||
compile with the -DDUMB_DECLARE_DEPRECATED switch for GCC, or the
|
||||
-D"DUMB_DECLARE_DEPRECATED" switch for MSVC. This will be accepted by
|
||||
GCC 3.x but is unnecessary. Use this switch with other people's projects
|
||||
if necessary, but please make the effort to update your own projects to
|
||||
use the new API, as the deprecated parts may be removed in the future.
|
||||
|
||||
The rest of this file explains why some parts of the API were deprecated,
|
||||
and how to adapt your code.
|
||||
|
||||
|
||||
**************************************
|
||||
*** What happened to DUH_RENDERER? ***
|
||||
**************************************
|
||||
|
||||
|
||||
The DUH_RENDERER struct was designed for rendering audio to an end-user
|
||||
format - 8-bit or 16-bit, signed or unsigned, with stereo samples
|
||||
interleaved. In order for it to do this, it was built on top of the
|
||||
hitherto undocumented DUH_SIGRENDERER struct, which rendered audio in
|
||||
DUMB's internal 32-bit signed format with channels (left/right) stored
|
||||
separately. The DUH_RENDERER struct contained a pointer to a
|
||||
DUH_SIGRENDERER struct, along with some other data like the position and
|
||||
number of channels.
|
||||
|
||||
There were then some developments in the API. The DUH_SIGRENDERER struct
|
||||
also stored the position and the number of channels, so I decided to write
|
||||
functions for returning these. Suddenly there was no need to store them in
|
||||
the DUH_RENDERER struct. Before long, the DUH_RENDERER struct contained
|
||||
nothing but a pointer to a DUH_SIGRENDERER.
|
||||
|
||||
I decided it would be a good idea to unify the structs. After all, there
|
||||
really is no difference between the data stored in each, and it would be
|
||||
easy to make duh_render(DUH_RENDERER *dr, ...) and
|
||||
duh_render_signal(DUH_SIGRENDERER *sr, ...) work on the same type of
|
||||
struct. (Note that duh_render_signal() is now deprecated too; see the next
|
||||
section.) It took some deliberation, but I decided I didn't want functions
|
||||
to be #defined (it prevents you from using these names for member
|
||||
functions in C++ classes), and that meant they had to be defined
|
||||
somewhere. Defining redundant functions is a source of bloat, inefficiency
|
||||
and general inelegance. After weighing things up, I decided it was better
|
||||
to deprecate the redundant functions and have people begin to use the more
|
||||
efficient versions, and eventually the redundant functions will be able to
|
||||
be removed.
|
||||
|
||||
So why did I choose to keep the more complicated name, DUH_SIGRENDERER?
|
||||
The reason has to do with what DUMB will become in the future. Signals are
|
||||
an inherent part of the DUH struct and how .duh files will be constructed.
|
||||
It will be possible to have multiple signals in a single DUH struct, and
|
||||
you will be able to choose which one you want to play (this is the 'sig'
|
||||
parameter passed to duh_start_sigrenderer()). But don't hold your breath;
|
||||
we still have a long way to go before .duh files will start to appear...
|
||||
|
||||
|
||||
typedef DUH_SIGRENDERER DUH_RENDERER;
|
||||
|
||||
Wherever you are using DUH_RENDERER in your program, simply replace it
|
||||
with DUH_SIGRENDERER. An automated (case-sensitive!) search and replace
|
||||
operation should get this done.
|
||||
|
||||
|
||||
DUH_RENDERER *duh_start_renderer(DUH *duh, int n_channels, long pos);
|
||||
|
||||
Use duh_start_sigrenderer() instead. It takes an extra parameter, 'sig',
|
||||
which comes after 'duh' and before 'n_channels'; pass 0 for this. So an
|
||||
example would be, replace:
|
||||
|
||||
sr = duh_start_renderer(duh, 2, 0);
|
||||
|
||||
with:
|
||||
|
||||
sr = duh_start_sigrenderer(duh, 0, 2, 0);
|
||||
|
||||
|
||||
int duh_renderer_get_n_channels(DUH_RENDERER *dr);
|
||||
long duh_renderer_get_position(DUH_RENDERER *dr);
|
||||
void duh_end_renderer(DUH_RENDERER *dr);
|
||||
|
||||
These are easy enough to fix; all you have to do is replace 'renderer'
|
||||
with 'sigrenderer'. So the new functions are:
|
||||
|
||||
int duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer);
|
||||
long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer);
|
||||
void duh_end_sigrenderer(DUH_SIGRENDERER *sigrenderer);
|
||||
|
||||
|
||||
Note that duh_render() has NOT been deprecated. It now uses DUH_SIGRENDERER
|
||||
instead of DUH_RENDERER, but its functionality is unchanged. You do not have
|
||||
to change calls to this function in any way.
|
||||
|
||||
|
||||
DUH_RENDERER *duh_renderer_encapsulate_sigrenderer(DUH_SIGRENDERER *sr);
|
||||
DUH_SIGRENDERER *duh_renderer_get_sigrenderer(DUH_RENDERER *dr);
|
||||
DUH_SIGRENDERER *duh_renderer_decompose_to_sigrenderer(DUH_RENDERER *dr);
|
||||
|
||||
These functions did not exist in the last release of DUMB, so you are
|
||||
probably not using them, but they are included here for completeness. All
|
||||
you have to do here is unwrap the function, since the structs have been
|
||||
unified. So, for instance, replace:
|
||||
|
||||
duh_renderer_encapsulate_sigrenderer(my_sigrenderer)
|
||||
|
||||
with:
|
||||
|
||||
my_sigrenderer
|
||||
|
||||
Simple!
|
||||
|
||||
|
||||
AL_DUH_PLAYER *al_duh_encapsulate_renderer(DUH_RENDERER *dr,
|
||||
float volume, long bufsize, int freq);
|
||||
DUH_RENDERER *al_duh_get_renderer(AL_DUH_PLAYER *dp);
|
||||
DUH_RENDERER *al_duh_decompose_to_renderer(AL_DUH_PLAYER *dp);
|
||||
|
||||
Again, these functions were not in the last release, so you probably
|
||||
aren't using them. Nevertheless, the fix is simple as always: simply
|
||||
replace 'renderer' with 'sigrenderer'. So the new functions are:
|
||||
|
||||
AL_DUH_PLAYER *al_duh_encapsulate_sigrenderer(DUH_SIGRENDERER *sr,
|
||||
float volume, long bufsize, int freq);
|
||||
DUH_SIGRENDERER *al_duh_get_sigrenderer(AL_DUH_PLAYER *dp);
|
||||
DUH_SIGRENDERER *al_duh_decompose_to_sigrenderer(AL_DUH_PLAYER *dp);
|
||||
|
||||
|
||||
*********************
|
||||
*** Miscellaneous ***
|
||||
*********************
|
||||
|
||||
|
||||
long duh_render_signal(DUH_SIGRENDERER *sigrenderer,
|
||||
float volume, float delta,
|
||||
long size, sample_t **samples);
|
||||
|
||||
This function used to return samples in DUMB's internal format. This
|
||||
format consisted of 32-bit integers whose 'normal range' was -0x8000 to
|
||||
0x7FFF (any samples outside this range would have to be clipped when sent
|
||||
to the sound card).
|
||||
|
||||
DUMB's internal format has changed. DUMB still uses 32-bit integers, but
|
||||
now the normal range is -0x800000 to 0x7FFFFF. The lowest eight bits are
|
||||
discarded at the final stage by duh_render() when you ask for 16-bit
|
||||
output. A new function, duh_sigrenderer_get_samples(), will return samples
|
||||
in DUMB's new internal format. It takes exactly the same parameters, so
|
||||
all you have to do to the call itself is change the name; however, you
|
||||
will most likely have to change your code to account for the new
|
||||
normalised range.
|
||||
|
||||
duh_render_signal() will still be able to give you the samples in DUMB's
|
||||
old internal format, but it is inefficient. You should change your code as
|
||||
soon as possible.
|
||||
|
||||
|
||||
typedef void (*DUH_SIGRENDERER_CALLBACK)(void *data, sample_t **samples,
|
||||
int n_channels, long length);
|
||||
|
||||
void duh_sigrenderer_set_callback(DUH_SIGRENDERER *sigrenderer,
|
||||
DUH_SIGRENDERER_CALLBACK callback, void *data);
|
||||
|
||||
This callback was intended to allow you to analyse the output. It was by
|
||||
no means intended to let you modify the output. For this reason, the names
|
||||
have been changed to DUH_SIGRENDERER_ANALYSER_CALLBACK and
|
||||
duh_sigrenderer_set_analyser_callback, and the 'samples' parameter to your
|
||||
callback should now be specified as follows:
|
||||
|
||||
const sample_t *const *samples
|
||||
|
||||
The first 'const' indicates that you must not modify the samples. The
|
||||
second indicates that you must not modify the pointers to each channel.
|
||||
|
||||
There is a second reason why this change was necessary, and it is the one
|
||||
described further up for duh_render_signal()'s entry: the format in which
|
||||
the samples themselves are stored has changed. They are 256 times as
|
||||
large, with a normal range from -0x800000 to 0x7FFFFF. You will most
|
||||
likely need to change your code to account for this.
|
||||
|
||||
If you try to call the old function, it will print a message to stderr
|
||||
directing you to this file, and it will not install the callback. You
|
||||
shouldn't be able to get this far without a compiler warning (or, if you
|
||||
don't have GCC 3.1 or later, some compiler errors).
|
||||
|
||||
If you wanted to use this callback to apply a DSP effect, don't worry;
|
||||
there is a better way of doing this. It is undocumented, so contact me
|
||||
and I shall try to help. Contact details are at the bottom of this file.
|
||||
|
||||
For reference, here are the new definitions:
|
||||
|
||||
typedef void (*DUH_SIGRENDERER_ANALYSER_CALLBACK)(void *data,
|
||||
const sample_t *const *samples, int n_channels, long length);
|
||||
|
||||
void duh_sigrenderer_set_analyser_callback(DUH_SIGRENDERER *sigrenderer,
|
||||
DUH_SIGRENDERER_ANALYSER_CALLBACK callback, void *data);
|
||||
|
||||
|
||||
int dumb_resampling_quality;
|
||||
|
||||
This variable has changed meaning. It used to hold a value from 0 to 4,
|
||||
whose meaning was as follows:
|
||||
|
||||
0 - aliasing
|
||||
1,2 - linear interpolation
|
||||
3 - quadratic interpolation
|
||||
4 - cubic interpolation
|
||||
|
||||
0,1 - always use a straightforward interpolation algorithm
|
||||
2,3,4 - when decimating (increasing the pitch), use a linear average
|
||||
algorithm designed to reduce frequencies that would otherwise
|
||||
reflect off the Nyquist
|
||||
|
||||
Now the variable only holds values from 0 to 2, and these values have
|
||||
preprocessor constants associated with them. The somewhat inappropriate
|
||||
quadratic interpolation has been removed. The linear average algorithm has
|
||||
also been removed, and may or may not come back; there are probably more
|
||||
efficient ways of achieving the same effect, which I shall be
|
||||
investigating in the future.
|
||||
|
||||
This change will have hardly any noticeable effect on existing programs.
|
||||
Levels 2, 3 and 4 used considerably more processor time because of the
|
||||
linear average algorithm. Likewise, Level 2 in the new scheme (cubic) uses
|
||||
considerably more processor time than Levels 1 and 0, and Levels 3 and 4
|
||||
will behave identically to Level 2.
|
||||
|
||||
|
||||
******************
|
||||
*** Conclusion ***
|
||||
******************
|
||||
|
||||
|
||||
"I conclude that... DUMB is the bestest music player in the world because...
|
||||
Complete this sentence in fifteen words or fewer... D'OH!"
|
||||
|
||||
The preceding conclusion formerly appeared in dumb.txt, and is deprecated
|
||||
because it's lame.
|
||||
|
||||
|
||||
Ben Davis
|
||||
entheh@users.sf.net
|
||||
IRC EFnet #dumb
|
||||
See readme.txt for details on using IRC.
|
1699
apps/codecs/dumb/docs/dumb.txt
Normal file
1699
apps/codecs/dumb/docs/dumb.txt
Normal file
File diff suppressed because it is too large
Load diff
263
apps/codecs/dumb/docs/faq.txt
Normal file
263
apps/codecs/dumb/docs/faq.txt
Normal file
|
@ -0,0 +1,263 @@
|
|||
TO DO: add question regarding set_close_button_callback vs set_window_close_hook
|
||||
|
||||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* faq.txt - Frequently Asked Questions. / / \ \
|
||||
* | < / \_
|
||||
* This file covers some of the common problems | \/ /\ /
|
||||
* and misconceptions people have with DUMB. If \_ / > /
|
||||
* your problem is not covered here, please | \ / /
|
||||
* contact me. I'll do my best to help - but | ' /
|
||||
* don't be offended if I just direct you to the \__/
|
||||
* manual!
|
||||
*/
|
||||
|
||||
|
||||
*****************************************************************************
|
||||
* I get a lot of strange warnings and errors when I compile my projects *
|
||||
* with this release of DUMB. They work with older versions! What happened? *
|
||||
*****************************************************************************
|
||||
|
||||
Some parts of DUMB's API have been deprecated. See docs/deprec.txt for
|
||||
full details, including an explanation as to why your compiler warnings
|
||||
and errors are so unfriendly, and information on how to fix each warning
|
||||
or error.
|
||||
|
||||
|
||||
*****************************************************************************
|
||||
* When I try to compile DUMB with Allegro, it complains that it cannot find *
|
||||
* 'internal/alconfig.h'! What's wrong? *
|
||||
*****************************************************************************
|
||||
|
||||
In Allegro 4.0.1, and quite likely some other versions of Allegro, the
|
||||
msvcmake batch file does not install Allegro properly. I believe this was
|
||||
fixed in Allegro 4.0.2, but don't take my word for it. Some include files
|
||||
are neglected, including alconfig.h. The fix is quite easy; you need to
|
||||
copy all of Allegro's include files to your compiler's directory. The
|
||||
following should do this for you (alter it accordingly depending on where
|
||||
MSVC and Allegro are installed):
|
||||
|
||||
cd\progra~1\msvc\include
|
||||
xcopy/s \allegro\include\*.*
|
||||
|
||||
You can safely tell it to overwrite all files.
|
||||
|
||||
|
||||
*****************************************************************************
|
||||
* When I build a project that uses DUMB, I get an error that it doesn't *
|
||||
* find -laldmbd! What's wrong? *
|
||||
*****************************************************************************
|
||||
|
||||
See the notes for DUMB v0.8 in release.txt; the existence of libaldmbd.a
|
||||
in DUMB v0.7 was due to a mistake in the makefiles. It should be
|
||||
libaldmd.a, in order to maintain DOS compatibility. All subsequent
|
||||
releases get it right, but you will have to change your project files to
|
||||
allow for the change. If this is someone else's project, please let them
|
||||
know that it needs changing.
|
||||
|
||||
|
||||
*****************************************************************************
|
||||
* When I build a project that uses DUMB, I get some linker errors about *
|
||||
* _free, _malloc, etc. already being defined in LIBC.lib! What's wrong? *
|
||||
*****************************************************************************
|
||||
|
||||
MSVC offers three different implementations of the standard libraries.
|
||||
When you link statically with a library, you have to use the same
|
||||
implementation that the library uses. You need the multithreaded DLL
|
||||
implementation, which you can select by passing /MD when you compile (not
|
||||
when you link). See howto.txt for details.
|
||||
|
||||
|
||||
*****************************************************************************
|
||||
* I created an IT file with Impulse Tracker, but DUMB won't play it! Why? *
|
||||
*****************************************************************************
|
||||
|
||||
You probably created some patterns but didn't give any information on the
|
||||
order in which they should be played. Impulse Tracker will also fail to
|
||||
play your music if you press F5. Press F11 and you will have an
|
||||
opportunity to create an order list, required for playback.
|
||||
|
||||
|
||||
*****************************************************************************
|
||||
* I created an IT file with ModPlug Tracker and I have it fading out at the *
|
||||
* end. Why won't it loop when I play it with DUMB? *
|
||||
*****************************************************************************
|
||||
|
||||
It loops at zero volume. This is what Impulse Tracker itself does. Fix the
|
||||
IT file by setting the global volume explicitly (Vxx in the effects
|
||||
column), either at the start, or right at the end before looping. Also see
|
||||
the next two questions.
|
||||
|
||||
|
||||
*****************************************************************************
|
||||
* My module plays too loud and distorts badly with DUMB! What can I do? *
|
||||
*****************************************************************************
|
||||
|
||||
This problem is most often caused by ModPlug Tracker, which has a complete
|
||||
lack of regard for the playback volume of the original tracker. See the
|
||||
next question for DUMB's official position with regard to ModPlug Tracker.
|
||||
If you wrote your module with ModPlug Tracker, please try loading it with
|
||||
the original tracker and see if it distorts there too. If it does, reduce
|
||||
the volume. If not, then it's a problem with DUMB; please let me know.
|
||||
|
||||
If for whatever reason you cannot modify the module file itself, you can
|
||||
make it sound better by reducing the volume passed to al_start_duh().
|
||||
|
||||
|
||||
*****************************************************************************
|
||||
* I created a music module with ModPlug Tracker, and DUMB doesn't play it *
|
||||
* right! *
|
||||
*****************************************************************************
|
||||
|
||||
DUMB cannot and will not support ModPlug Tracker. Please see
|
||||
docs/modplug.txt for details. The original trackers, which DUMB is
|
||||
designed to mimic as closely as possible, are listed in readme.txt.
|
||||
If you find DUMB plays your module differently from the original tracker,
|
||||
then please contact me.
|
||||
|
||||
|
||||
*****************************************************************************
|
||||
* My program crashes as soon as I try to load anything with DUMB! *
|
||||
*****************************************************************************
|
||||
|
||||
Please take my advice and use the debugging build of DUMB, not the
|
||||
optimised build. Then you'll probably find it aborts instead of crashing.
|
||||
In this case you probably forgot to register a DUMBFILE system; this is
|
||||
necessary for loading stand-alone files, though not for loading Allegro
|
||||
datafiles with embedded music. Follow the instructions in docs/howto.txt
|
||||
carefully and you shouldn't have this problem.
|
||||
|
||||
If DUMB crashes with a specific music module, please let me know.
|
||||
|
||||
|
||||
*****************************************************************************
|
||||
* I want to use the stdio file access functions to load stand-alone music *
|
||||
* files, but I also want to load datafiles containing music files. The docs *
|
||||
* say I shouldn't call both dumb_register_stdfiles() and *
|
||||
* dumb_register_packfiles(). What shall I do? *
|
||||
*****************************************************************************
|
||||
|
||||
When you register a DUMBFILE system, it only applies to files opened with
|
||||
dumbfile_open(), i.e. separate files. When a file is embedded in a
|
||||
datafile, dumbfile_open_ex() is used to read it, enabling it to use
|
||||
PACKFILEs regardless of which DUMBFILE system is registered. In short, you
|
||||
do not need to call dumb_register_packfiles() in order to load datafiles
|
||||
with embedded music. See the section on "Sequential File Input" in
|
||||
docs/dumb.txt if you're interested in how all this works.
|
||||
|
||||
|
||||
*****************************************************************************
|
||||
* I want to read a specific object in a datafile using Allegro's *
|
||||
* "demo.dat#MY_MUSIC" syntax. Why won't it work? *
|
||||
*****************************************************************************
|
||||
|
||||
Did you call dumb_register_packfiles(), or did you call
|
||||
dumb_register_stdfiles()? It will only work if you use the former.
|
||||
|
||||
|
||||
*****************************************************************************
|
||||
* My program runs, but no music plays! What am I doing wrong? *
|
||||
*****************************************************************************
|
||||
|
||||
There are a number of possible causes for this. The most likely reason is
|
||||
that you aren't calling al_poll_duh(); see docs/howto.txt for further
|
||||
information.
|
||||
|
||||
Other possible causes are as follows:
|
||||
|
||||
- The speakers are turned down (duh)
|
||||
- The volume of some system mixer is turned down
|
||||
- Another program is using the sound card (not a problem for most modern
|
||||
systems)
|
||||
- You didn't initialise Allegro's sound system; see install_sound() in
|
||||
Allegro's docs
|
||||
- Allegro's drivers don't work on your system and chosen platform
|
||||
|
||||
In order to narrow down the cause, consider the following:
|
||||
|
||||
- Do you get any other sound from your program?
|
||||
- Do other Allegro+DUMB programs generate sound?
|
||||
- Do other Allegro programs generate sound?
|
||||
- Do other non-Allegro programs generate sound?
|
||||
- Does your program fail only on a specific platform (e.g. DOS but not
|
||||
Windows)?
|
||||
|
||||
This problem is highly system-specific; please try hard to solve it by
|
||||
yourself before contacting me. However, if you think this problem could
|
||||
affect other people, please let me know what the problem is and how you
|
||||
fixed it, if you did. Be as specific as possible.
|
||||
|
||||
|
||||
*****************************************************************************
|
||||
* The music stutters! What can I do? *
|
||||
*****************************************************************************
|
||||
|
||||
If you have an older computer, it may not be able to cope with the load.
|
||||
Try reducing quality options; look up dumb_resampling_quality and
|
||||
dumb_it_max_to_mix in docs/dumb.txt, and consider changing the frequency
|
||||
you pass to al_start_duh().
|
||||
|
||||
Stuttering may not be caused by excessive load. To find out, try
|
||||
increasing the buffer size passed to al_start_duh(). Beware of making it
|
||||
too big though; older systems will freeze periodically if it's too big,
|
||||
because they render larger chunks less frequently. The timing of callbacks
|
||||
will also be less accurate, if you are using those.
|
||||
|
||||
If you're using the 'dumbplay' example, you can control these parameters
|
||||
by editing dumb.ini.
|
||||
|
||||
|
||||
*****************************************************************************
|
||||
* Why does DUMB use so much processor time compared with other players? *
|
||||
*****************************************************************************
|
||||
|
||||
This should be less so in this release than in previous releases; the
|
||||
resampling and filtering algorithms have been optimised.
|
||||
|
||||
By default, DUMB uses the most expensive resampling quality option. I've
|
||||
found on an AthlonXP 1800+ and on a Pentium 233 that it typically uses
|
||||
about twice as much processor time as the least expensive option.
|
||||
|
||||
Try setting dumb_resampling_quality to DUMB_RQ_ALIASING or DUMB_RQ_LINEAR.
|
||||
See dumb.txt for more information. If you're using the example programs,
|
||||
you can control this variable by editing dumb.ini.
|
||||
|
||||
DUMB uses 32-bit ints for mixing. Some players use 16-bit ints, and are
|
||||
therefore marginally faster (not much!) and lower quality. So you can't
|
||||
expect DUMB to beat these players. Furthermore, DUMB is currently written
|
||||
entirely in C. GCC does an impressive job on the C code, but that's not to
|
||||
say some custom-written assembly language couldn't beat it ...
|
||||
|
||||
|
||||
*****************************************************************************
|
||||
* Why does DUMB generate so much background noise? *
|
||||
*****************************************************************************
|
||||
|
||||
You're probably using the DOS build on a system with bad Sound Blaster
|
||||
compatibility (most Windows XP systems fall in this category). This would
|
||||
mean DUMB could only access an 8-bit driver. The Windows build will almost
|
||||
certainly give better results. Your DOS binary will still give good
|
||||
results on systems with better compatibility (like my Windows 98 system).
|
||||
|
||||
|
||||
*****************************************************************************
|
||||
* I e-mailed you and you replied with "RTFM"! What does that mean? *
|
||||
*****************************************************************************
|
||||
|
||||
Read The Manual. If it's a specific problem, I'll probably be kind and
|
||||
tell you where to look in the manual. However, if I get the impression you
|
||||
haven't even looked for a solution in the manual, expect no mercy ...
|
||||
|
||||
|
||||
Ben Davis
|
||||
entheh@users.sf.net
|
||||
IRC EFnet #dumb
|
||||
See readme.txt for details on using IRC.
|
113
apps/codecs/dumb/docs/fnptr.txt
Normal file
113
apps/codecs/dumb/docs/fnptr.txt
Normal file
|
@ -0,0 +1,113 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* fnptr.txt - Function pointer explanation. / / \ \
|
||||
* | < / \_
|
||||
* | \/ /\ /
|
||||
* \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
|
||||
C allows you to create and use function pointers. A function pointer is a
|
||||
variable that points to a function, and you can use it to call that function.
|
||||
Why is this useful?
|
||||
|
||||
Function pointers can be passed as parameters. As an example, here's a
|
||||
function from Allegro:
|
||||
|
||||
void create_light_table(COLOR_MAP *table, const PALETTE pal, int r, g, b,
|
||||
void (*callback)(int pos));
|
||||
|
||||
Don't worry about the syntax just yet, but the last parameter, 'callback', is
|
||||
a pointer to a function that takes an int parameter. create_light_table() can
|
||||
take some time to complete its work, and you may want to display a progress
|
||||
indicator. So you write a function to draw the progress indicator, and then,
|
||||
for 'callback', you specify a pointer to your function. This will enable
|
||||
create_light_table() to call your function at intervals during its
|
||||
processing. (If you don't want to use the callback, you can pass NULL, but
|
||||
this only works because create_light_table() checks actively for NULL. You
|
||||
can't always specify NULL when you want nothing to happen.)
|
||||
|
||||
There are many other uses. In addition to using function pointers as
|
||||
parameters, Allegro has some global function pointers you can set to point to
|
||||
your functions. Function pointers can also be used in structs, and this is
|
||||
where DUMB makes the most use of them.
|
||||
|
||||
So how are they used?
|
||||
|
||||
void bar(void) { ... } /* Here's a function */
|
||||
void (*foo)(void) = &bar; /* Take a pointer */
|
||||
(*foo)(); /* Call the function */
|
||||
|
||||
char *baz(float a) { ... } /* Here's another function */
|
||||
char *(*foobarbaz)(float a) = &baz; /* Take a pointer */
|
||||
char *rv = (*foobarbaz)(0.1); /* Call the function */
|
||||
|
||||
In both these cases, note how the statement for calling the pointed-to
|
||||
function (third line) resembles the definition of the function pointer
|
||||
(second line). This is true of any variable in C, and can lead to some truly
|
||||
obfuscated definitions if you are that way inclined. Such definitions can be
|
||||
clarified with typedefs, but before you use those, it is important you
|
||||
understand how the above statements work. I speak from experience: function
|
||||
pointer notation looks random and scary, until you understand why it's the
|
||||
way it is; then it makes perfect sense.
|
||||
|
||||
(It is actually permissible to omit the & when taking a pointer and to write
|
||||
e.g. foobarbaz(0.1) instead of (*foobarbaz)(0.1). However, I recommend not
|
||||
doing this, since the syntax for using the pointer no longer resembles the
|
||||
definition. Writing e.g. (*foobarbaz)(0.1) also makes a clear distinction
|
||||
between function pointer calls and ordinary function calls, which makes code
|
||||
more readable.)
|
||||
|
||||
Note that function pointers have the return value and parameter list
|
||||
specified. A function pointer can only point to a function with a matching
|
||||
return value and matching parameters. (You can break this rule by casting the
|
||||
pointer explicitly, but there is no situation where doing so is portable to
|
||||
all computers, and I strongly advise against it unless you're writing system
|
||||
code. If you're not sure whether you're writing system code or not, then
|
||||
you're not.)
|
||||
|
||||
The parameter names need not match (although the types must). If you wish to
|
||||
rename a parameter in your function, you do not have to change the function
|
||||
pointer accordingly. In fact, when you define a function pointer, you don't
|
||||
even have to specify the names of parameters if you don't want to. I normally
|
||||
do so for clarity.
|
||||
|
||||
It is possible to typedef a function pointer. In order to typedef a function
|
||||
pointer, you start by declaring the pointer as a variable:
|
||||
|
||||
void (*myfunc)(void);
|
||||
|
||||
Then you write 'typedef' before it and replace the variable name, which is
|
||||
myfunc, with the type name (this rule can be applied to any variable when you
|
||||
want to use typedef):
|
||||
|
||||
typedef void (*MYTYPE)(void);
|
||||
|
||||
Now 'MYTYPE' represents a pointer to a function with no parameters and no
|
||||
return value. The following two lines are completely equivalent:
|
||||
|
||||
MYTYPE myfunc;
|
||||
void (*myfunc)(void);
|
||||
|
||||
Note that we use MYTYPE without an asterisk (*), since it is already a
|
||||
pointer.
|
||||
|
||||
That's it. If you feel anything should be explained better here, or if you
|
||||
feel something should be added, please don't hesitate to let me know!
|
||||
|
||||
|
||||
Ben Davis
|
||||
entheh@users.sf.net
|
||||
IRC EFnet #dumb
|
||||
See readme.txt for details on using IRC.
|
845
apps/codecs/dumb/docs/howto.txt
Normal file
845
apps/codecs/dumb/docs/howto.txt
Normal file
|
@ -0,0 +1,845 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* howto.txt - How To Use DUMB. / / \ \
|
||||
* | < / \_
|
||||
* See readme.txt for general information on | \/ /\ /
|
||||
* DUMB and how to set it up. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
|
||||
********************
|
||||
*** Introduction ***
|
||||
********************
|
||||
|
||||
|
||||
Welcome to the DUMB How-To! It is assumed here that you have already set DUMB
|
||||
up on your system, with or without Allegro. If not, please see readme.txt.
|
||||
|
||||
|
||||
*********************************
|
||||
*** Adding music to your game ***
|
||||
*********************************
|
||||
|
||||
|
||||
These instructions will help you add a piece of music to your game, assuming
|
||||
your music is stored in a stand-alone IT, XM, S3M or MOD file. If you wish to
|
||||
use a different method (such as putting the music file in an Allegro
|
||||
datafile), please follow these instructions first, test your program, and
|
||||
then follow the instructions further down for adapting your code.
|
||||
|
||||
|
||||
1. You need to include DUMB's header file. If you have Allegro, add the
|
||||
following line to the top of your source file (or at the top of each file
|
||||
where you wish to use DUMB):
|
||||
|
||||
#include <aldumb.h>
|
||||
|
||||
If you do not have Allegro or do not wish to use it, use dumb.h instead.
|
||||
|
||||
|
||||
2. You need to link with DUMB's library file or files. If you are compiling
|
||||
with GCC from a command line on any platform, you need to add the
|
||||
following to the command line:
|
||||
|
||||
If you are using Allegro: -laldmd -ldumbd
|
||||
If you are not using Allegro: -ldumbd
|
||||
|
||||
If you are using MSVC from the command line:
|
||||
|
||||
If you are using Allegro: /link aldmd.lib dumbd.lib
|
||||
If you are not using Allegro: /link dumbd.lib
|
||||
|
||||
With MSVC, you must also add /MD to the command line when compiling (not
|
||||
when linking).
|
||||
|
||||
Note that -laldmd or aldmd.lib must PRECEDE alleg.lib, -lalleg_s,
|
||||
`allegro-config --libs`, or whatever you are already using to link with
|
||||
Allegro. For MSVC users, the /MD flag selects the multithreaded DLL
|
||||
implementation of the standard libraries; since DUMB is statically linked,
|
||||
you have to use the same library DUMB uses. You would also need this flag
|
||||
to link statically with Allegro; if you already have it, there's no need
|
||||
to put it twice.
|
||||
|
||||
(If anyone would like to contribute instructions for doing the above using
|
||||
MSVC's IDE, please contact me. Contact details are at the end of this
|
||||
file.)
|
||||
|
||||
If you are using RHIDE, go to Options -> Libraries. You will need to type
|
||||
'aldmd' and 'dumbd' in two boxes, making sure 'aldmd' comes above whatever
|
||||
you are using to link with Allegro (or just put 'dumbd' if you are not
|
||||
using Allegro). Make sure the box next to each of these libraries is
|
||||
checked.
|
||||
|
||||
The above are the debugging libraries. It is VERY HIGHLY RECOMMENDED that
|
||||
you use the debugging libraries at first. The reason is as follows.
|
||||
Although DUMB is supposedly robust against corrupt music files and things
|
||||
like lack of memory, it will NOT tolerate programmer error. If you write
|
||||
faulty code, DUMB will probably crash rather than returning an error code
|
||||
for you. However, the debugging libraries will abort in many cases,
|
||||
enabling you to find out what the cause is.
|
||||
|
||||
Once your program is up and running reliably, you can replace 'aldmd' with
|
||||
'aldmb' and 'dumbd' with 'dumb'. Don't forget to do this, or DUMB will be
|
||||
a lot slower than it should be!
|
||||
|
||||
|
||||
3. As you use DUMB, it may claim system resources (memory in particular). You
|
||||
will need to arrange for these resources to be freed at the end. Doing so
|
||||
is very easy. Simply write the following line at the top of your main
|
||||
function, but below allegro_init() if you are using Allegro:
|
||||
|
||||
atexit(&dumb_exit);
|
||||
|
||||
This arranges for the function dumb_exit() to be called when your program
|
||||
exits; you do not need to call dumb_exit() yourself. This method is
|
||||
preferable to calling dumb_exit() manually, as it will free resources even
|
||||
if your program aborts unexpectedly.
|
||||
|
||||
If you are happy with this, please skip ahead to Step 4. If you are
|
||||
interested in alternative methods, read on, but read on carefully.
|
||||
|
||||
In fact it mostly doesn't matter where you put the above atexit() line,
|
||||
provided it gets called only once, and before you do anything with DUMB.
|
||||
If you are using DUMB with Allegro, it is recommended that you write the
|
||||
functions in this order:
|
||||
|
||||
allegro_init();
|
||||
atexit(&dumb_exit);
|
||||
|
||||
And then you must NOT call allegro_exit() yourself (because it has to be
|
||||
called after dumb_exit()). Alternatively, if you prefer not to use
|
||||
atexit() (or you cannot), you will have to do the following before
|
||||
exiting:
|
||||
|
||||
dumb_exit();
|
||||
allegro_exit();
|
||||
|
||||
|
||||
4. DUMB does not automatically do any of its own file input. You have to tell
|
||||
it how to read files. Don't worry, it's easy. Simply call the following
|
||||
function near the beginning of your program, after your atexit() call:
|
||||
|
||||
dumb_register_stdfiles();
|
||||
|
||||
This tells DUMB to use ordinary stdio FILE structs for reading and writing
|
||||
files. If you are using Allegro and would rather DUMB used PACKFILEs, call
|
||||
the following function INSTEAD:
|
||||
|
||||
dumb_register_packfiles();
|
||||
|
||||
In the latter case, DUMB will be affected by any password you set with
|
||||
packfile_password() in the same way that other PACKFILEs are.
|
||||
|
||||
Note that the procedure for loading datafiles with embedded music is
|
||||
independent of these two functions; even if you will be loading datafiles,
|
||||
you can use either of these functions. If you are loading datafiles, your
|
||||
executable might be slightly smaller if you use dumb_register_packfiles().
|
||||
On the other hand, dumb_register_stdfiles() will probably be faster. If
|
||||
you are only ever going to load datafiles and never stand-alone files, you
|
||||
can actually leave this step out; but I would recommend you put this in,
|
||||
test your code with a stand-alone file, then follow the instructions in
|
||||
the next section in order to adapt your code to use the datafile (you will
|
||||
be reminded that you can remove the function call).
|
||||
|
||||
|
||||
5. If you are using Allegro, you'll have to initialise Allegro's sound
|
||||
system. In most cases the following line will do the job:
|
||||
|
||||
install_sound(DIGI_AUTODETECT, MIDI_NONE, NULL);
|
||||
|
||||
You may like to initialise a MIDI driver though; see Allegro's docs for
|
||||
details. Put this line after allegro_init().
|
||||
|
||||
|
||||
6. All pieces of music are stored in memory in DUH structs. To handle these,
|
||||
you must define pointers to them. Such pointers look like this:
|
||||
|
||||
DUH *myduh;
|
||||
|
||||
You can of course replace 'myduh' with anything you like. If you are
|
||||
unfamiliar with pointers, please see ptr.txt. It is very important that
|
||||
you understand these if you wish to use DUMB correctly.
|
||||
|
||||
You do not have direct access to the contents of a DUH struct, so do not
|
||||
try. DUMB's functions provide everything you need; if you disagree, please
|
||||
let me know and I shall see what I can do. Contact details are at the end
|
||||
of this file.
|
||||
|
||||
Given the above definition, you can load a piece of music using one of the
|
||||
following lines, depending on what file format you want to load:
|
||||
|
||||
myduh = dumb_load_it("a_one.it");
|
||||
myduh = dumb_load_xm("a_two.xm");
|
||||
myduh = dumb_load_s3m("a_one_two.s3m");
|
||||
myduh = dumb_load_mod("three_four.mod");
|
||||
|
||||
Obviously you can use relative or absolute paths as normal. You should
|
||||
always use forward slash (/), not backslash (\), when coding in C and
|
||||
similar languages.
|
||||
|
||||
Every piece of music you load must be unloaded when you've finished with
|
||||
it. When you type the above line in, it is good practice to type the
|
||||
following line in at the same time, but put it at the end of the program:
|
||||
|
||||
unload_duh(myduh);
|
||||
|
||||
You will now be able to use the DUH struct anywhere in between the two
|
||||
lines you just added. There is no need to check the return value; if the
|
||||
DUH failed to load for one reason or another (this could be due to lack of
|
||||
memory as well as the file not being there), then DUMB will do nothing -
|
||||
safely.
|
||||
|
||||
|
||||
7. From this step onwards, it will be assumed you're using Allegro. If not,
|
||||
please read these steps anyway, and then see the section entitled
|
||||
"Rendering music into a buffer". You will have to write your own playback
|
||||
code using whatever sound output system is available. Alternatively you
|
||||
may like to write data to a file (especially if you have a file that
|
||||
consumes a lot of processor time), but beware that any streaming audio
|
||||
format is likely to be substantially larger than the module file you
|
||||
generate it from, and formats like MP3 will be lower quality. You might
|
||||
not be able to hear the difference between the MP3 and the original, but
|
||||
many people can and don't like it, so please consider them. I'm one of
|
||||
them. If you really want to use a lossy compression format, I highly
|
||||
recommend Ogg Vorbis:
|
||||
|
||||
http://www.vorbis.com/
|
||||
|
||||
But I digress.
|
||||
|
||||
In order to play the DUH you loaded, you need to define a pointer to an
|
||||
AL_DUH_PLAYER struct:
|
||||
|
||||
AL_DUH_PLAYER *dp;
|
||||
|
||||
Two of the functions you will need are prototyped as follows:
|
||||
|
||||
AL_DUH_PLAYER *al_start_duh(DUH *duh, int n_channels, long pos,
|
||||
float volume, long bufsize, int freq);
|
||||
|
||||
void al_stop_duh(AL_DUH_PLAYER *dp);
|
||||
|
||||
As you can see, al_start_duh() returns a pointer to an AL_DUH_PLAYER
|
||||
struct when you call it. You then pass this pointer to all the other
|
||||
functions. Again, if it is a NULL pointer for whatever reason (usually
|
||||
lack of memory), DUMB will safely do nothing. When you call al_stop_duh(),
|
||||
the pointer becomes invalid and you should not use it again; if there's
|
||||
any risk of the pointer being used again, it is wise to set it to NULL at
|
||||
this point. You can reassign the variable with a new call to
|
||||
al_start_duh() of course.
|
||||
|
||||
Set 'n_channels' to 1 or 2 for mono or stereo respectively. Note that this
|
||||
parameter has nothing to do with the number of samples that can play at
|
||||
once in a music module. Set 'pos' to 0 to play from the beginning; each
|
||||
time you add 65536, you will have advanced one second into the piece. As a
|
||||
general rule, set the volume to 1.0f and adjust it later if the music is
|
||||
too loud or too quiet - but see Allegro's set_volume_per_voice() function
|
||||
first.
|
||||
|
||||
'bufsize' can generally be set to 4096. If your music stutters, try
|
||||
increasing it; if your game freezes periodically, try reducing it. Find a
|
||||
happy medium. Set 'freq' to 48000 for the best quality, though 44100 will
|
||||
do in most cases. 22050 will be fine for a lot of music, though 11025 may
|
||||
sound muffled. You can choose any other value, higher, lower or in
|
||||
between. If your music stutters, and increasing 'bufsize' doesn't fix it,
|
||||
try reducing this value.
|
||||
|
||||
Once you have put in a call to al_start_duh(), it is good practice to
|
||||
insert the call to al_stop_duh() at the same time. You must call
|
||||
al_stop_duh() before the DUH is unloaded (unload_duh(), Step 6 above).
|
||||
|
||||
Don't get impetuous, your program is not ready yet! Proceed to Step 8.
|
||||
|
||||
|
||||
8. DUMB does not play music in the background for you; if you were expecting
|
||||
it to do so, please see the explanation at the end of this step. For your
|
||||
music to be played, you have to call another function at regular
|
||||
intervals. Here is its prototype:
|
||||
|
||||
int al_poll_duh(AL_DUH_PLAYER *dp);
|
||||
|
||||
Do NOT call this function from inside a timer function unless you really
|
||||
know what you are doing. The reasons why this is bad are explained
|
||||
further down. You should call it from your main program.
|
||||
|
||||
Simply writing the following line will be sufficient in general, if you
|
||||
have a variable 'dp' that points to your AL_DUH_PLAYER struct.
|
||||
|
||||
al_poll_duh(dp);
|
||||
|
||||
As a general rule, calling this once for each logic update will do the
|
||||
trick. If, however, you are executing time-consuming algorithms such as
|
||||
software 3D rendering, you may wish to insert calls to this function in
|
||||
the middle of those algorithms. You cannot call this function too often
|
||||
(within reason); if it has nothing to do it will return immediately.
|
||||
|
||||
Exactly how often you need to call the function depends on the values for
|
||||
'bufsize' and 'freq' that you passed to al_start_duh():
|
||||
|
||||
n = freq / bufsize;
|
||||
|
||||
You have to call al_poll_duh() at least n times a second. Do not hesitate
|
||||
to call it more often for safety; if the sound stutters, you may need to
|
||||
do just that. (Or you may need to increase the buffer size or reduce the
|
||||
quality settings; the only way to find out is to try.)
|
||||
|
||||
For now, don't worry about al_poll_duh()'s return value. As soon as you
|
||||
need it, it will be explained.
|
||||
|
||||
If you are happy, please skip to Step 9. If you were expecting DUMB to
|
||||
play your music in the background, please read on.
|
||||
|
||||
The natural way to play music in the background on most operating systems
|
||||
nowadays is to use threads. DOS was not built with multithreading in mind,
|
||||
and its system operations (notably disk access) assume they will only be
|
||||
used from a single thread.
|
||||
|
||||
Interrupts are the next best thing to threads. A DOS hardware interrupt
|
||||
could be triggered at any moment, and a handler function will be called.
|
||||
This is how Allegro's timer functions work. Unfortunately, what you can do
|
||||
inside an interrupt handler is very limited. For one thing, all code and
|
||||
data used by the handler must be locked in memory; if not, it could get
|
||||
written to disk (virtual memory). If the main program was accessing the
|
||||
disk when it got interrupted, the system would then die a horrible death.
|
||||
This precludes the possibility of allocating extra memory inside the
|
||||
handler, and DUMB does a lot of that in al_poll_duh().
|
||||
|
||||
Given DUMB's architecture, which cannot change for reasons which will
|
||||
become apparent in future versions, this renders it impossible to come up
|
||||
with a portable solution for making DUMB play music in the background.
|
||||
Having said that, if you wish to write your own wrapper for al_poll_duh()
|
||||
and use it in a thread, there is nothing stopping you. If you do do this,
|
||||
you will have to be very careful when stopping the music; see the
|
||||
description of al_poll_duh() in dumb.txt for more information.
|
||||
|
||||
So why not kill DOS? It is all too common a practice among programmers to
|
||||
quote the phrase, "DOS is as dead as the dodo." Despite being a decidedly
|
||||
derisible demonstation of the dreary device of alliteration, it shows a
|
||||
distinct lack of experience. Many embedded systems still use DOS because
|
||||
it provides hardware access capabilities and real-time possibilities
|
||||
unparalleled by any current multitasking operating system. For an argument
|
||||
closer to home, I used to use RHIDE for DOS before I switched to Linux,
|
||||
and I have not found a single Freeware Windows IDE that measures up to
|
||||
RHIDE. I'm sure many people are in the same boat, and really appreciate
|
||||
DUMB's DOS port.
|
||||
|
||||
We will not be removing DOS support from DUMB. Any blind suggestions to do
|
||||
so will be met with fiery flames. You have been warned.
|
||||
|
||||
|
||||
9. Test your program!
|
||||
|
||||
If you have trouble, check through the above steps to make sure you didn't
|
||||
miss one out. Refer to faq.txt to see if your problem is addressed there.
|
||||
If you still have trouble, contact me; details are at the end of this
|
||||
file.
|
||||
|
||||
|
||||
**********************************
|
||||
*** Controlling music playback ***
|
||||
**********************************
|
||||
|
||||
|
||||
Here I describe some common operations you may wish to perform. The method
|
||||
for doing so will seem a bit strange sometimes, as will the names of the
|
||||
structs. However, there is a reason behind everything. If you would like to
|
||||
do more exotic things, or better understand some of the methods used here,
|
||||
then see dumb.txt, which covers everything from the ground up.
|
||||
|
||||
|
||||
To control playback quality:
|
||||
|
||||
#define DUMB_RQ_ALIASING
|
||||
#define DUMB_RQ_LINEAR
|
||||
#define DUMB_RQ_CUBIC
|
||||
#define DUMB_RQ_N_LEVELS
|
||||
extern int dumb_resampling_quality;
|
||||
extern int dumb_it_max_to_mix;
|
||||
|
||||
Please note that dumb_resampling_quality has changed in DUMB v0.9.2. See
|
||||
deprec.txt for more details on the change.
|
||||
|
||||
dumb_resampling_quality can be set to any of the DUMB_RQ_* constants
|
||||
(except DUMB_RQ_N_LEVELS; see below). Resampling is the term given to the
|
||||
process of adjusting a sample's pitch (in this context).
|
||||
dumb_resampling_quality defaults to DUMB_RQ_CUBIC, which sounds nice but
|
||||
takes a lot of processor power. Try reducing it if you have an older
|
||||
computer or if you are trying to mix an insane number of samples (or
|
||||
both!). See dumb.txt for details on what the different values actually do.
|
||||
|
||||
If you wish to give this option to your user, you can use
|
||||
DUMB_RQ_N_LEVELS. All the values from 0 to DUMB_RQ_N_LEVELS - 1 will be
|
||||
valid resampling levels. If a value outside this range is chosen, it is
|
||||
not the end of the world; DUMB will behave as if you had chosen the value
|
||||
at whichever extreme you went beyond.
|
||||
|
||||
dumb_it_max_to_mix, defaulting to 64, is the maximum number of samples
|
||||
DUMB will ever mix together when playing an IT, XM, S3M or MOD file.
|
||||
Unlike many other music systems, DUMB will still keep track of all samples
|
||||
(up to a fixed maximum of 256 of them, roughly speaking), and then will
|
||||
just render as many of them as this variable permits, starting with the
|
||||
loudest ones. When samples are cut or come back in, the exact timings will
|
||||
not generally be predictable - but nor will they be important.
|
||||
|
||||
dumb_it_max_to_mix applies to each currently playing module file
|
||||
independently. So if you set it to 64, but render two modules
|
||||
simultaneously, DUMB could end up mixing up to 128 samples.
|
||||
|
||||
|
||||
To pause and resume playback, set the volume, get the current playback
|
||||
position, or get the length of time a DUH will play for before either looping
|
||||
or freezing (effect F00 in XM and MOD files, which means no new notes will be
|
||||
played but any existing notes will continue):
|
||||
|
||||
void al_pause_duh(AL_DUH_PLAYER *dp);
|
||||
void al_resume_duh(AL_DUH_PLAYER *dp);
|
||||
void al_duh_set_volume(AL_DUH_PLAYER *dp, float volume);
|
||||
long al_duh_get_position(AL_DUH_PLAYER *dp);
|
||||
|
||||
long duh_get_length(DUH *duh);
|
||||
|
||||
These functions are pretty self-explanatory. The volume passed to
|
||||
al_duh_set_volume() and the position returned by al_duh_get_position() are
|
||||
in the same units as those you passed to al_start_duh(). The length
|
||||
returned by duh_get_length() is in the same units as the aforementioned
|
||||
position; see dumb.txt for more information on this function. Be careful
|
||||
with al_duh_get_position(); it will return a position slightly ahead of
|
||||
what you can hear, because the system has to keep ahead slightly to avoid
|
||||
stuttering.
|
||||
|
||||
|
||||
To prevent the music from looping and/or freezing:
|
||||
|
||||
DUH_SIGRENDERER *al_duh_get_sigrenderer(AL_DUH_PLAYER *dp);
|
||||
DUMB_IT_SIGRENDERER *duh_get_it_sigrenderer(DUH_SIGRENDERER *sigrenderer);
|
||||
|
||||
void dumb_it_set_loop_callback(DUMB_IT_SIGRENDERER *sigrenderer,
|
||||
int (*callback)(void *data), void *data);
|
||||
void dumb_it_set_xm_speed_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer,
|
||||
int (*callback)(void *data), void *data);
|
||||
|
||||
int dumb_it_callback_terminate(void *data);
|
||||
|
||||
If you are unfamiliar with function pointers, please see fnptr.txt.
|
||||
|
||||
Note that these functions apply to IT, XM, S3M and MOD files - not just to
|
||||
IT files. This holds true throughout DUMB, for all functions with "it" in
|
||||
the name. The xm_speed_zero event can only occur with XM and MOD files.
|
||||
|
||||
The first two functions will return a pointer to a struct contained by the
|
||||
struct you pass. This system is necessary to ensure that these operations
|
||||
are possible when not using Allegro. Typically you would write the
|
||||
following code:
|
||||
|
||||
{
|
||||
DUH_SIGRENDERER *sr = al_duh_get_sigrenderer(dp);
|
||||
DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(sigrenderer);
|
||||
dumb_it_set_loop_callback(itsr, &dumb_it_callback_terminate, NULL);
|
||||
dumb_it_set_xm_speed_zero_callback
|
||||
(itsr, &dumb_it_callback_terminate, NULL);
|
||||
}
|
||||
|
||||
Once you have done this, the return value of al_poll_duh() becomes
|
||||
significant. It will be 0 as long as the music is playing. When the music
|
||||
stops, al_poll_duh() will return nonzero. You can call al_stop_duh() and
|
||||
do something else as soon as you wish, but calling al_poll_duh() some more
|
||||
will not do any harm.
|
||||
|
||||
al_poll_duh() will also return 1 if the music could not be loaded, or if
|
||||
memory was short when trying to play it, or if it was a quirky music file
|
||||
with no music in it (technically one with an empty order list). This
|
||||
happens regardless of whether or not you execute the above code to disable
|
||||
looping. Normally you shouldn't need to worry about this.
|
||||
|
||||
To undo the above and make DUMB loop or freeze again, pass NULL instead of
|
||||
&dumb_it_callback_terminate. If you would like to fade on looping, or loop
|
||||
a finite number of times, or display a message when looping, or whatever,
|
||||
you will have to write your own callback function. In this case, please
|
||||
see dumb.txt.
|
||||
|
||||
Note that the above code can safely be applied for a DUH that doesn't
|
||||
contain a music module but contains some other kind of music.
|
||||
duh_get_it_sigrenderer() will return NULL, and the code will do nothing.
|
||||
|
||||
|
||||
To analyse the audio as it's generated:
|
||||
|
||||
typedef int sample_t;
|
||||
|
||||
typedef void (*DUH_SIGRENDERER_ANALYSER_CALLBACK)(void *data,
|
||||
const sample_t *const *samples, int n_channels, long length);
|
||||
|
||||
void duh_sigrenderer_set_analyser_callback(DUH_SIGRENDERER *sigrenderer,
|
||||
DUH_SIGRENDERER_ANALYSER_CALLBACK callback, void *data);
|
||||
|
||||
If the above confuses you, see fnptr.txt. These functions, along with
|
||||
al_duh_get_sigrenderer() from the last section, enable you to register a
|
||||
callback function. Every time some samples are generated, they will be
|
||||
passed to this function. This enables you to display an oscilloscope or
|
||||
spectrum analyser, for example.
|
||||
|
||||
Beware: your callback function may occasionally be called with
|
||||
samples == NULL. This means the main program has decided to skip through
|
||||
the music without generating any data. You should handle this case
|
||||
elegantly, typically by returning immediately, but you may wish to make a
|
||||
note of the fact that the music is being skipped, for whatever reason.
|
||||
|
||||
Beware again: if the main program ever calls duh_sigrenderer_get_samples()
|
||||
on a buffer that isn't all silence, this callback function will be passed
|
||||
the existing buffer after mixing, and thus it will include the original
|
||||
data. This will not be an issue if you stick to duh_render(), which always
|
||||
starts with a buffer filled with silence.
|
||||
|
||||
The samples array is two-dimensional. Refer to it as follows:
|
||||
|
||||
samples[channel_number][sample_position]
|
||||
|
||||
where 0 <= channel_number < n_channels,
|
||||
and 0 <= sample_position < length.
|
||||
|
||||
In addition you can pass any 'data' pointer you like to
|
||||
duh_sigrenderer_set_analyser_callback(), and this pointer will be relayed
|
||||
to your callback function each time.
|
||||
|
||||
To remove the callback function, pass NULL to
|
||||
duh_sigrenderer_set_analyser_callback().
|
||||
|
||||
|
||||
Everything below this point assumes some knowledge of how a music module is
|
||||
constructed. If you do not have this knowledge, talk to whoever is writing
|
||||
music for you, or download a tracking program and play with it (see
|
||||
readme.txt).
|
||||
|
||||
|
||||
To start playing an IT, XM, S3M or MOD from an arbitrary order number (the
|
||||
default being 0, the beginning of the song), use the following:
|
||||
|
||||
DUH_SIGRENDERER *dumb_it_start_at_order
|
||||
(DUH *duh, int n_channels, int startorder);
|
||||
AL_DUH_PLAYER *al_duh_encapsulate_sigrenderer
|
||||
(DUH_SIGRENDERER *sigrenderer, float volume, long bufsize, int freq);
|
||||
|
||||
The usage of these functions is as follows:
|
||||
|
||||
{
|
||||
DUH_SIGRENDERER *sr = dumb_it_start_at_order
|
||||
(duh, n_channels, startorder);
|
||||
dp = al_duh_encapsulate_sigrenderer(sr, volume, bufsize, freq);
|
||||
}
|
||||
|
||||
Replace 'dp' with whatever your AL_DUH_PLAYER pointer is. You also need
|
||||
to insert suitable values for n_channels, startorder, volume, bufsize and
|
||||
freq. These have the same meaning as those passed to al_start_duh().
|
||||
|
||||
WARNING: after passing a pointer to an "encapsulate" function, do not use
|
||||
that pointer again. (More specifically, do not use it again if
|
||||
the function returns NULL, because the function will have
|
||||
destroyed the pointer if this happens, to help prevent memory
|
||||
leaks.) There will be a "get" function with which you can obtain
|
||||
the original pointer if it is still valid, or NULL otherwise.
|
||||
|
||||
The above functions will fail (safely) if you try to use them with a DUH
|
||||
that contains a different type of music.
|
||||
|
||||
Notice that there is no 'pos' parameter. If you would like to skip through
|
||||
the music, you can use this function:
|
||||
|
||||
long duh_sigrenderer_get_samples(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
float volume, float delta,
|
||||
long size, sample_t **samples
|
||||
);
|
||||
|
||||
Pass 0 for volume and NULL for samples, and this function will skip
|
||||
through the music nice and quickly. So insert the following between the
|
||||
two above statements:
|
||||
|
||||
duh_sigrenderer_get_samples(sr, 0, 65536.0f / freq, pos, NULL);
|
||||
|
||||
Substitute for 'freq' and 'pos'. An explanation of the 'delta' parameter
|
||||
can be found further down in this file.
|
||||
|
||||
Finally, note that duh_get_length() is only meaningful when you start
|
||||
playing music from order 0.
|
||||
|
||||
|
||||
If an IT file contains Zxx effects, DUMB will generate MIDI messages, which
|
||||
will control the low-pass resonant filters unless the IT file actively
|
||||
specifies something else. In rare cases this may not be what the Zxx effects
|
||||
were intended to do; if this is the case, you can block the MIDI messages as
|
||||
follows. Note that this does NOT mean filters are disabled; if an instrument
|
||||
specifies initial cut-off and resonance values, or has a filter envelope,
|
||||
then filters will be applied. It only makes sense to use this procedure at
|
||||
the beginning of playback.
|
||||
|
||||
void dumb_it_set_midi_callback(DUMB_IT_SIGRENDERER *sigrenderer,
|
||||
int (*callback)(void *data, int channel, unsigned char byte),
|
||||
void *data);
|
||||
|
||||
int dumb_it_callback_midi_block(void *data, int channel,
|
||||
unsigned char byte);
|
||||
|
||||
Using some functions described in the previous section, we arrive at the
|
||||
following code:
|
||||
|
||||
{
|
||||
DUH_SIGRENDERER *sr = al_duh_get_sigrenderer(dp);
|
||||
DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(sigrenderer);
|
||||
dumb_it_set_midi_callback(itsr, &dumb_it_callback_midi_block, NULL);
|
||||
}
|
||||
|
||||
DUMB offers no way of disabling filters completely. Disabling filters is not
|
||||
recommended as a means to reduce processor usage, as it will completely
|
||||
damage any piece of music that uses the filters. If you want lower processor
|
||||
consumption, use a piece of music that does not use filters.
|
||||
|
||||
|
||||
Finally, DUMB offers a myriad of functions for querying and adjusting
|
||||
module playback. Those beginning with "dumb_it_sd" operate on the
|
||||
DUMB_IT_SIGDATA struct, which represents the piece of music before it starts
|
||||
to play. Those beginning with "dumb_it_sr" operate on the DUMB_IT_SIGRENDERER
|
||||
struct, which represents a currently playing instance of the music. Note that
|
||||
duh_get_length(), described above, becomes meaningless after some of these
|
||||
functions are used.
|
||||
|
||||
The method for getting a DUMB_IT_SIGRENDERER struct has already been given,
|
||||
but the function prototypes are repeated here for convenience:
|
||||
|
||||
DUH_SIGRENDERER *al_duh_get_sigrenderer(AL_DUH_PLAYER *dp);
|
||||
DUMB_IT_SIGRENDERER *duh_get_it_sigrenderer(DUH_SIGRENDERER *sigrenderer);
|
||||
|
||||
Getting a DUMB_IT_SIGDATA struct is simpler:
|
||||
|
||||
DUMB_IT_SIGDATA *duh_get_it_sigdata(DUH *duh);
|
||||
|
||||
For a list of dumb_it_sd_*() and dumb_it_sr_*() functions, please see
|
||||
dumb.txt. These functions are new, and may not provide exactly what you need;
|
||||
if not, please let me know.
|
||||
|
||||
|
||||
**************************************************
|
||||
*** Embedding music files in Allegro datafiles ***
|
||||
**************************************************
|
||||
|
||||
|
||||
In this section it is assumed you are already reasonably familiar with how
|
||||
Allegro datafiles are used. If not, please refer to Allegro's documentation.
|
||||
At the time of writing, the documentation you need is off the beaten track,
|
||||
so to speak, in allegro/tools/grabber.txt.
|
||||
|
||||
To add a piece of music to a datafile, you need to create an object of type
|
||||
"IT ", "XM ", "S3M " or "MOD " (note the spaces used as padding, although
|
||||
you do not need to type these into the grabber). Then grab the piece of music
|
||||
in. The grabber will treat it as a binary object. Save the datafile as usual.
|
||||
|
||||
|
||||
To use a piece of music you added to the datafile, follow these steps:
|
||||
|
||||
|
||||
1. Before loading the datafile, call one or more of these functions,
|
||||
depending on which music format or formats you'd like to support:
|
||||
|
||||
dumb_register_dat_it(DUMB_DAT_IT);
|
||||
dumb_register_dat_xm(DUMB_DAT_XM);
|
||||
dumb_register_dat_s3m(DUMB_DAT_S3M);
|
||||
dumb_register_dat_mod(DUMB_DAT_MOD);
|
||||
|
||||
Remember, do not call multiple functions unless you want to support
|
||||
multiple formats. Calling more functions will add unused code to your
|
||||
executable.
|
||||
|
||||
It is important that you make call these before loading the datafile,
|
||||
since they tell Allegro how to load the respective files straight from
|
||||
datafiles in the future. They will not help Allegro interpret any module
|
||||
files that have already been loaded as binary objects (but if you really
|
||||
need to interpret a module that has been loaded in this fashion, have a
|
||||
look at dumbfile_open_memory() in dumb.txt).
|
||||
|
||||
If for whatever reason your music objects are identified by a different
|
||||
type in the datafile, you can tell DUMB what that type is by changing the
|
||||
parameter to the registration function above. Use Allegro's DAT_ID()
|
||||
macro, e.g. DAT_ID('B','L','A','H'). This is not really recommended
|
||||
though, since it would prevent a hypothetical grabber plug-in from being
|
||||
able to play your music files. Use the above types if possible.
|
||||
|
||||
|
||||
2. Whenever you need a pointer to a DUH struct, simply use the 'dat' field.
|
||||
Do this in the same way you would for a pointer to a BITMAP struct or
|
||||
anything else. If it makes you feel more comfortable, you can extract the
|
||||
pointer in advance:
|
||||
|
||||
DATAFILE *dat = load_datafile("smurf.dat");
|
||||
if (!dat) abort(); /* There are much nicer ways of handling failure! */
|
||||
DUH *myduh = (DUH *)dat[GAME_MUSIC].dat;
|
||||
|
||||
Note that the explicit (DUH *) cast is only necessary for C++, not for C.
|
||||
However, it does no harm.
|
||||
|
||||
Be sure that you do NOT call unload_duh() for anything stored in the
|
||||
datafile. These DUHs will be freed when you call unload_datafile(), and
|
||||
freeing them twice is practically guaranteed to crash your program.
|
||||
|
||||
|
||||
3. If you only ever load music as part of a datafile, and you never load any
|
||||
stand-alone music files, you do not need to register a file input system
|
||||
for DUMB to use. If you followed the instructions for the first section
|
||||
you will have one of these two lines in your program:
|
||||
|
||||
dumb_register_stdfiles();
|
||||
dumb_register_packfiles();
|
||||
|
||||
You can safely delete this line - but only if you never load any
|
||||
stand-alone music files. The debugging library will bale you out if you
|
||||
delete it when you shouldn't; the optimised library won't.
|
||||
|
||||
|
||||
*************************************
|
||||
*** Rendering music into a buffer ***
|
||||
*************************************
|
||||
|
||||
|
||||
NOTE: much of the API formerly described in this section has been deprecated,
|
||||
and you will need to alter your code. See deprec.txt for details. If
|
||||
you are reading this section for the first time, you can ignore this
|
||||
note.
|
||||
|
||||
Rendering to a buffer is similar to playing using an AL_DUH_PLAYER. However,
|
||||
you must use a DUH_SIGRENDERER struct instead. Here are the functions:
|
||||
|
||||
DUH_SIGRENDERER *duh_start_sigrenderer
|
||||
(DUH *duh, int sig, int n_channels, long pos);
|
||||
|
||||
int duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer);
|
||||
long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer);
|
||||
|
||||
long duh_sigrenderer_get_samples(DUH_SIGRENDERER *sigrenderer,
|
||||
float volume, float delta, long size, sample_t **samples);
|
||||
|
||||
long duh_render(DUH_SIGRENDERER *sigrenderer,
|
||||
int bits, int unsign, float volume, float delta, long size, void *sptr);
|
||||
|
||||
void duh_end_sigrenderer(DUH_SIGRENDERER *sigrenderer);
|
||||
|
||||
The parameters to duh_start_sigrenderer() have the same meanings as those to
|
||||
al_start_duh(). However, note that the volume is not set at this stage. You
|
||||
pass the desired volume each time you want to render a block. The 'sig'
|
||||
parameter should be set to 0 for now.
|
||||
|
||||
Notice that there are two rendering functions. duh_sigrenderer_get_samples()
|
||||
will generate samples in the internal 32-bit format, with a normal range from
|
||||
-0x800000 to 0x7FFFFF and with each channel in a separate array; duh_render()
|
||||
will convert to 8 or 16 bits, signed or unsigned, with stereo samples
|
||||
interleaved, left first.
|
||||
|
||||
When you call duh_render(), pass 8 or 16 for 'bits'. If you pass 8, 'sptr' is
|
||||
expected to be an array of chars. If you pass 16, 'sptr' is expected to be an
|
||||
array of shorts. Endianness therefore depends on the platform, and you should
|
||||
not try to interpret 16-bit wave data as an array of chars (unless you're
|
||||
writing highly system-specific code anyway). Because DUMB renders internally
|
||||
with 32 bits, there is no significant speed increase in rendering an 8-bit
|
||||
stream.
|
||||
|
||||
If you are rendering in stereo, make sure your 'sptr' array is twice as big!
|
||||
|
||||
If you set 'unsign' to a nonzero value, then the samples generated will be
|
||||
centred on 0x80 or 0x8000, suitably stored in an array of unsigned chars or
|
||||
unsigned shorts. If 'unsign' is zero, the samples will be centred on 0,
|
||||
suitably stored in an array of signed chars or signed shorts. Note that 8-bit
|
||||
WAV files are unsigned while 16-bit WAV files are signed. This convention was
|
||||
used by the SoundBlaster 16 when receiving samples to be sent to the
|
||||
speakers. If you wish to write 16-bit sample data to a WAV file, don't use
|
||||
fwrite(); instead, take the shorts one at a time, split them up into chars as
|
||||
follows, and write the chars to the file.
|
||||
|
||||
short sptr[n];
|
||||
char lsb = (char)sptr[n];
|
||||
char msb = (char)(sptr[n] >> 8);
|
||||
|
||||
For a 16-bit WAV file, write the LSB (less significant byte) first.
|
||||
|
||||
The following applies equally to duh_render() and
|
||||
duh_sigrenderer_get_samples(), except where otherwise stated.
|
||||
|
||||
If you set 'delta' to 1.0f, the sound generated will be suitable for playback
|
||||
at 65536 Hz. Increasing 'delta' causes the wave to speed up, given a constant
|
||||
sampling rate for playback. Supposing you want to vary the playback sampling
|
||||
rate but keep the pitch constant, here's the equation for 'delta':
|
||||
|
||||
delta = 65536.0f / sampling_rate;
|
||||
|
||||
'size' is the number of samples you want rendered. For duh_render(), they
|
||||
will be rendered into an array which you pass as 'sptr'. Note that stereo
|
||||
samples count as one; so if you set n_channels to 2, your array must contain
|
||||
(2 * size) elements.
|
||||
|
||||
For duh_sigrenderer_get_samples() you will have to use the following
|
||||
functions:
|
||||
|
||||
sample_t **create_sample_buffer(int n_channels, long length);
|
||||
void destroy_sample_buffer(sample_t **samples);
|
||||
|
||||
void dumb_silence(sample_t *samples, long length);
|
||||
|
||||
create_sample_buffer() allocates the channels sequentially in memory, so the
|
||||
following technique is valid:
|
||||
|
||||
sample_t **samples = create_sample_buffer(n_channels, length);
|
||||
dumb_silence(samples[0], n_channels * length);
|
||||
|
||||
It is necessary to fill the buffer with silence like this because
|
||||
duh_sigrenderer_get_samples() mixes what it renders with the existing
|
||||
contents of the buffer.
|
||||
|
||||
The return values from duh_render() and duh_sigrenderer_get_samples() tell
|
||||
you how many samples were actually generated. In most cases, this will be the
|
||||
same as the 'size' parameter. However, if you reach the end of the DUH (which
|
||||
will happen if you disable looping or freezing as described further up), this
|
||||
function will return less. When that happens, you can assume the stream has
|
||||
finished. In the case of duh_render(), the remainder of the array will not
|
||||
have been initialised, so you either have to initialise it yourself or avoid
|
||||
using it.
|
||||
|
||||
If for whatever reason duh_start_sigrenderer() returns NULL, then
|
||||
duh_render() and duh_sigrenderer_get_samples() will generate exactly 0
|
||||
samples, duh_sigrenderer_get_n_channels() will return 0,
|
||||
duh_sigrenderer_get_position() will return -1, and duh_end_sigrenderer() will
|
||||
safely do nothing.
|
||||
|
||||
|
||||
*********************
|
||||
*** Miscellaneous ***
|
||||
*********************
|
||||
|
||||
|
||||
Please see dumb.txt for an API reference and for information on thread safety
|
||||
with DUMB. The API reference has been stripped down, since some functions and
|
||||
variables are subject to change. If something does not appear in dumb.txt,
|
||||
please do not use it.
|
||||
|
||||
|
||||
******************
|
||||
*** Conclusion ***
|
||||
******************
|
||||
|
||||
|
||||
If you have any difficulties, or if you use DUMB successfully, please don't
|
||||
hesitate to contact me (see below).
|
||||
|
||||
Enjoy!
|
||||
|
||||
|
||||
Ben Davis
|
||||
entheh@users.sf.net
|
||||
IRC EFnet #dumb
|
||||
See readme.txt for details on using IRC.
|
137
apps/codecs/dumb/docs/modplug.txt
Normal file
137
apps/codecs/dumb/docs/modplug.txt
Normal file
|
@ -0,0 +1,137 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* modplug.txt - Our official position regarding / / \ \
|
||||
* compatibility with ModPlug | < / \_
|
||||
* Tracker. | \/ /\ /
|
||||
* \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
|
||||
********************
|
||||
*** Introduction ***
|
||||
********************
|
||||
|
||||
ModPlug Tracker is a very popular tracker for Windows. Its popularity is due
|
||||
to the intuitive interface and its many advanced features. The author has
|
||||
done a good job with this piece of software, but sadly in doing so he has
|
||||
desecrated the IT file format.
|
||||
|
||||
I am not against ModPlug Tracker being used to write music modules. As
|
||||
already stated, it has some very advanced and convenient features; I use it
|
||||
myself. However, I believe its users should be aware of the entire situation
|
||||
before using it for any serious work.
|
||||
|
||||
ModPlug Tracker - http://www.modplug.com/
|
||||
|
||||
|
||||
*************************
|
||||
*** Incompatibilities ***
|
||||
*************************
|
||||
|
||||
There are a few situations in which ModPlug Tracker misinterprets the
|
||||
original module formats. I shall list the five I am most aware of, from least
|
||||
to most annoying:
|
||||
|
||||
5. Create a multisample instrument, for example a piano. Play a low note.
|
||||
Then go up the scale, but in the pattern data, make sure the instrument
|
||||
column is blank; put in only the notes. Play this with ModPlug Tracker,
|
||||
and play it with Impulse Tracker. Impulse Tracker changes sample as you go
|
||||
up the scale; ModPlug Tracker does not.
|
||||
|
||||
4. Arpeggio and Retrigger Note effects behave badly when combined with
|
||||
Portamento, which can appear in the volume column. While Retrigger Note
|
||||
isn't too bad, Arpeggio sounds completely wrong. Try it and see what
|
||||
happens. Then repeat the experiment in Impulse Tracker.
|
||||
|
||||
3. The filter algorithm is incorrect, in more ways than one. When Jeffrey Lim
|
||||
programmed the low-pass resonant filters into Impulse Tracker, he used a
|
||||
standard filter algorithm with a slight modification to achieve greater
|
||||
resonance. ModPlug Tracker does not incorporate this modification.
|
||||
Furthermore, ModPlug Tracker uses integer arithmetic with nowhere near
|
||||
enough precision; the wave output is really poor in some cases. I don't
|
||||
doubt it damages the acoustic properties of the filters in subtle ways.
|
||||
|
||||
2. When looping, ModPlug Tracker resets all variables. The original trackers
|
||||
do not do this.
|
||||
|
||||
1. Worst of all, ModPlug Tracker has no regard for playback volume, and
|
||||
generally has a much lower output level than the original trackers.
|
||||
|
||||
Cases 3, 2 and 1 lead people to write IT files that play badly in the
|
||||
original trackers. If some of these problems could be fixed, I'd be all for
|
||||
it - but these problems have been reported to the author and he had no
|
||||
motivation to fix them. ModPlug Tracker has been around long enough that
|
||||
fixing 3, 2 and 1 would be detrimental to too many people's music.
|
||||
|
||||
|
||||
******************
|
||||
*** Extensions ***
|
||||
******************
|
||||
|
||||
Worse than the incompatibilities are the extensions ModPlug Tracker makes,
|
||||
mostly to the IT format. DUMB currently supports one of these extensions,
|
||||
namely stereo samples, but supporting the others is not high on my list of
|
||||
priorities.
|
||||
|
||||
Other extensions ModPlug Tracker has provided mostly take the form of extra
|
||||
effects. For instance, S98 and S99 can be used to enable or disable reverb. I
|
||||
believe the latest versions of ModPlug Tracker offer alternative types of
|
||||
filter, such as high-pass and band-pass. As soon as an IT file uses any of
|
||||
these features, it will play incorrectly with Impulse Tracker.
|
||||
|
||||
By far the most evil extension provided by ModPlug Tracker is the effect
|
||||
plug-ins. These enable IT files to use VST effects. I recently downloaded an
|
||||
IT file that uses some effects from a collection named "DirectX Media Audio
|
||||
Effects". When can we expect these effects to be ported to Linux?
|
||||
|
||||
|
||||
******************
|
||||
*** Conclusion ***
|
||||
******************
|
||||
|
||||
ModPlug Tracker is trying to be two things at once. It wants to be an editor
|
||||
for the existing formats, but at the same time it wants to be proprietary,
|
||||
with all its own features and extensions. Unfortunately it is succeeding;
|
||||
there are many IT files out there that only play right in ModPlug Tracker. In
|
||||
my opinion, ModPlug Tracker should have come out with its own file format, in
|
||||
which all these extensions would have found a home.
|
||||
|
||||
If you are going to use ModPlug Tracker's extensions, I recommend you
|
||||
ultimately convert your music to a streamed format such as Ogg Vorbis. (If
|
||||
you were thinking of using MP3, then don't - consider using Ogg Vorbis
|
||||
instead.) If you release IT files that use ModPlug Tracker's extensions,
|
||||
please state prominently that the files are designed to be played with
|
||||
ModPlug Tracker. Finally, don't ask me to support ModPlug Tracker's
|
||||
extensions; ModPlug Tracker's playback code is available for use in your
|
||||
games, so use that instead.
|
||||
|
||||
Ogg Vorbis - http://www.vorbis.com/
|
||||
|
||||
Despite all the above problems, don't forget that ModPlug Tracker does have a
|
||||
lot of very useful features for editing files. These include a function for
|
||||
removing unused patterns, samples and instruments, drag-and-drop sample and
|
||||
instrument ripping, drop-down menus for selecting the effects by name without
|
||||
having to memorise the codes or refer to help, and lots of other nice things.
|
||||
I do recommend it as an editor, provided you make sure you are aware of the
|
||||
situation and do not use ModPlug Tracker's extensions or incompatibilities
|
||||
inadvertently.
|
||||
|
||||
Oh, and by the way, save your final version with Impulse Tracker. Then the
|
||||
samples will be compressed for you!
|
||||
|
||||
|
||||
Ben Davis
|
||||
entheh@users.sf.net
|
||||
IRC EFnet #dumb
|
||||
See readme.txt for details on using IRC.
|
129
apps/codecs/dumb/docs/ptr.txt
Normal file
129
apps/codecs/dumb/docs/ptr.txt
Normal file
|
@ -0,0 +1,129 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* ptr.txt - Pointer explanation. / / \ \
|
||||
* | < / \_
|
||||
* | \/ /\ /
|
||||
* \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
|
||||
A pointer is a small variable (often the same size as an int BUT NOT ALWAYS)
|
||||
that holds the address of something in memory. You create a pointer by adding
|
||||
a * to a variable, as follows:
|
||||
|
||||
int x, *y;
|
||||
|
||||
x = 5;
|
||||
y = &x;
|
||||
|
||||
The & means 'address of', so &x gives us a pointer to x. We are storing it in
|
||||
y.
|
||||
|
||||
(*y)++;
|
||||
|
||||
The * here means 'value at'. It's known as the 'dereferencing' operator. When
|
||||
written before a pointer, as it is here, it allows you to treat the value
|
||||
like a normal variable. In this case we are incrementing the value. If we
|
||||
look at x, we'll find that it now contains 6, not 5.
|
||||
|
||||
y++;
|
||||
|
||||
Here we are incrementing the pointer itself. This is useful for traversing
|
||||
through an array, but in this particular example it is not much use.
|
||||
|
||||
*y++;
|
||||
|
||||
Beware; this will increment the pointer, not the value stored there. It will
|
||||
return the value stored at the pointer (before incrementing the pointer), so
|
||||
you can use this in a bigger expression. This is why we needed brackets in
|
||||
the first example.
|
||||
|
||||
Note that you will not need these three examples when working with DUMB; they
|
||||
are simply to help illustrate the idea of pointers.
|
||||
|
||||
Also be aware that when defining pointers you attach the * to the variable,
|
||||
not to the type. The following example will create a pointer and an int, not
|
||||
two pointers:
|
||||
|
||||
int *a, b;
|
||||
|
||||
That is why I believe it's a good idea to put a space before the * and not
|
||||
after it, although programmers are divided on this.
|
||||
|
||||
y = 0;
|
||||
y = NULL;
|
||||
|
||||
These two statements are equivalent. 0, or NULL, is a special value that is
|
||||
guaranteed to have a different value from any valid pointer. This is most
|
||||
often used to indicate that something doesn't point anywhere. DUMB's
|
||||
functions may return it on occasion. However, in simple usage of DUMB, you
|
||||
will not actually need to check for it.
|
||||
|
||||
Some of DUMB's functions return pointers to structs. (A struct is an
|
||||
aggregration of other variables, such as ints, pointers, or other structs.
|
||||
You can generally treat a struct as a single unit.) Here's an example of such
|
||||
a function:
|
||||
|
||||
DUH *dumb_load_it(const char *filename);
|
||||
|
||||
You do not know what the DUH struct actually contains; dumb.h and aldumb.h
|
||||
only give the compiler enough information to deal with pointers to them. DUMB
|
||||
will take charge of everything that happens inside a DUH struct.
|
||||
|
||||
The above function will create a DUH struct for you. First it allocates
|
||||
the memory it needs, then it fills the struct with data, then it returns a
|
||||
pointer. This DUH struct will contain the data necessary to play an IT file.
|
||||
You can define a suitable variable and store the pointer in it as follows:
|
||||
|
||||
DUH *duh = dumb_load_it("music.it");
|
||||
|
||||
Or this can be split up:
|
||||
|
||||
DUH *duh;
|
||||
duh = dumb_load_it("music.it");
|
||||
|
||||
In order to use this DUH struct later, you must pass its pointer to other
|
||||
functions. To pass the pointer to a function, simply write 'duh' for the
|
||||
appropriate parameter.
|
||||
|
||||
When you've finished with a DUH struct (this applies equally to the other
|
||||
structs DUMB deals with), you must pass it to an appropriate function for
|
||||
freeing up the memory:
|
||||
|
||||
unload_duh(duh);
|
||||
|
||||
After you've done this, the memory will no longer be allocated, and the
|
||||
pointer will have no meaning. You may wish to set it to NULL at this point
|
||||
for safety. Alternatively just be sure not to use the present value of the
|
||||
pointer any more. You can of course assign a new value to the pointer, e.g.
|
||||
by calling dumb_load_it() again.
|
||||
|
||||
Note the following:
|
||||
|
||||
DUH *duh2 = duh;
|
||||
|
||||
This only duplicates the pointer, not the DUH itself. You still only have one
|
||||
copy of the DUH. There is no way of duplicating a DUH, short of loading it
|
||||
twice. This is not a problem, because DUMB can play it 'twice at the same
|
||||
time' anyway.
|
||||
|
||||
That should be all you need to know about pointers in order to use DUMB. If
|
||||
there's anything you feel should be explained better here, or anything else
|
||||
that should be added, please don't hesitate to let me know!
|
||||
|
||||
|
||||
Ben Davis
|
||||
entheh@users.sf.net
|
||||
IRC EFnet #dumb
|
||||
See readme.txt for details on using IRC.
|
44
apps/codecs/dumb/examples/dumb.ini
Normal file
44
apps/codecs/dumb/examples/dumb.ini
Normal file
|
@ -0,0 +1,44 @@
|
|||
# Please edit this file to control the playback quality for 'dumbplay'. Note
|
||||
# that this does not affect DUMB when you use it in your own programs; you
|
||||
# need to borrow some code from the example program in order to get that to
|
||||
# happen.
|
||||
|
||||
# dumb_resampling_quality can be 0 for aliasing, 1 for linear interpolation
|
||||
# or 2 for cubic interpolation. See docs/dumb.txt for details on what these
|
||||
# terms mean.
|
||||
|
||||
# dumb_it_max_to_mix is the maximum number of samples DUMB will render at a
|
||||
# time. See docs/dumb.txt for a more detailed description.
|
||||
|
||||
# Increase buffer_size to combat stuttering.
|
||||
|
||||
# The music module will be rendered at the sampling frequency specified by
|
||||
# sound_freq. This variable is also used by Allegro for initialising the
|
||||
# sound hardware.
|
||||
|
||||
# buffer_size and sound_freq are passed directly to al_start_duh(). See this
|
||||
# function's description in docs/dumb.txt for information about how to use
|
||||
# these variables.
|
||||
|
||||
# You can ignore the quality variable. Allegro uses it when relaying the
|
||||
# audio stream to the sound card. Only a masochist would set it lower than 2;
|
||||
# if your computer is powerful enough to run DUMB, it is powerful enough to
|
||||
# use this setting with Allegro.
|
||||
|
||||
# For best results, choose a value for sound_freq that your sound card can do
|
||||
# exactly. See Allegro's docs, "Standard config variables", for details. If
|
||||
# you do not choose an exact value, Allegro will round it to the nearest
|
||||
# value it can do; then when DUMB plays the stream at a sampling frequency of
|
||||
# sound_freq, Allegro will have to resample it. Allegro's 'quality = 2'
|
||||
# setting is only comparable with DUMB's 'dumb_resampling_quality = 1'
|
||||
# setting. Therefore, in order to appreciate DUMB's cubic resampler fully,
|
||||
# you will need to make sure Allegro doesn't do any resampling, by choosing
|
||||
# an exact value for sound_freq.
|
||||
|
||||
[sound]
|
||||
dumb_resampling_quality = 2
|
||||
dumb_it_max_to_mix = 256
|
||||
buffer_size = 4096
|
||||
sound_freq = 44100
|
||||
|
||||
quality = 2
|
335
apps/codecs/dumb/examples/dumbout.c
Normal file
335
apps/codecs/dumb/examples/dumbout.c
Normal file
|
@ -0,0 +1,335 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* dumbout.c - Utility to stream music to a file. / / \ \
|
||||
* | < / \_
|
||||
* By entheh. | \/ /\ /
|
||||
* \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <dumb.h>
|
||||
|
||||
|
||||
union {
|
||||
short s16[8192];
|
||||
char s8[16384];
|
||||
} buffer;
|
||||
|
||||
|
||||
int main(int argc, const char *const *argv) /* I'm const-crazy! */
|
||||
{
|
||||
DUH *duh;
|
||||
DUH_SIGRENDERER *sr;
|
||||
|
||||
const char *fn = NULL;
|
||||
const char *fn_out = NULL;
|
||||
FILE *outf;
|
||||
|
||||
int depth = 16;
|
||||
int bigendian = 0;
|
||||
int unsign = 0;
|
||||
int freq = 44100;
|
||||
int n_channels = 2;
|
||||
float volume = 1.0f;
|
||||
float delay = 0.0f;
|
||||
float delta;
|
||||
int bufsize;
|
||||
clock_t start, end;
|
||||
|
||||
int i = 1;
|
||||
|
||||
LONG_LONG length;
|
||||
LONG_LONG done;
|
||||
int dots;
|
||||
|
||||
while (i < argc) {
|
||||
const char *arg = argv[i++];
|
||||
if (*arg != '-') {
|
||||
if (fn) {
|
||||
fprintf(stderr,
|
||||
"Cannot specify multiple filenames!\n"
|
||||
"Second filename found: \"%s\"\n", arg);
|
||||
return 1;
|
||||
}
|
||||
fn = arg;
|
||||
continue;
|
||||
}
|
||||
arg++;
|
||||
while (*arg) {
|
||||
char *endptr;
|
||||
switch (*arg++) {
|
||||
case 'o':
|
||||
case 'O':
|
||||
if (i >= argc) {
|
||||
fprintf(stderr, "Out of arguments; output filename expected!\n");
|
||||
return 1;
|
||||
}
|
||||
fn_out = argv[i++];
|
||||
break;
|
||||
case 'd':
|
||||
case 'D':
|
||||
if (i >= argc) {
|
||||
fprintf(stderr, "Out of arguments; delay expected!\n");
|
||||
return 1;
|
||||
}
|
||||
delay = (float)strtod(argv[i++], &endptr);
|
||||
if (*endptr != 0 || delay < 0.0f || delay > 64.0f) {
|
||||
fprintf(stderr, "Invalid delay!\n");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
case 'V':
|
||||
if (i >= argc) {
|
||||
fprintf(stderr, "Out of arguments; volume expected!\n");
|
||||
return 1;
|
||||
}
|
||||
volume = (float)strtod(argv[i++], &endptr);
|
||||
if (*endptr != 0 || volume < -8.0f || volume > 8.0f) {
|
||||
fprintf(stderr, "Invalid volume!\n");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
case 'S':
|
||||
if (i >= argc) {
|
||||
fprintf(stderr, "Out of arguments; sampling rate expected!\n");
|
||||
return 1;
|
||||
}
|
||||
freq = strtol(argv[i++], &endptr, 10);
|
||||
if (*endptr != 0 || freq < 1 || freq > 960000) {
|
||||
fprintf(stderr, "Invalid sampling rate!\n");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case '8':
|
||||
depth = 8;
|
||||
break;
|
||||
case 'b':
|
||||
case 'B':
|
||||
bigendian = 1;
|
||||
break;
|
||||
case 'm':
|
||||
case 'M':
|
||||
n_channels = 1;
|
||||
break;
|
||||
case 'u':
|
||||
case 'U':
|
||||
unsign = 1;
|
||||
break;
|
||||
case 'r':
|
||||
case 'R':
|
||||
if (i >= argc) {
|
||||
fprintf(stderr, "Out of arguments; resampling quality expected!\n");
|
||||
return 1;
|
||||
}
|
||||
dumb_resampling_quality = strtol(argv[i++], &endptr, 10);
|
||||
if (*endptr != 0 || dumb_resampling_quality < 0 || dumb_resampling_quality > 2) {
|
||||
fprintf(stderr, "Invalid resampling quality!\n");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Invalid switch - '%c'!\n", isprint(arg[-1]) ? arg[-1] : '?');
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!fn) {
|
||||
fprintf(stderr,
|
||||
"Usage: dumbout [options] module [more-options]\n"
|
||||
"\n"
|
||||
"The module can be any IT, XM, S3M or MOD file. It will be rendered to a .pcm\n"
|
||||
"file of the same name, unless you specify otherwise with the -o option.\n"
|
||||
"\n"
|
||||
"The valid options are:\n"
|
||||
"-o <file> specify the output filename (defaults to the input filename with\n"
|
||||
" the extension replaced with .pcm); use - to write to standard\n"
|
||||
" output or . to write nowhere (useful for measuring DUMB's\n"
|
||||
" performance, and DOS and Windows don't have /dev/null!)\n"
|
||||
"-d <delay> set the initial delay, in seconds (default 0.0)\n"
|
||||
"-v <volume> adjust the volume (default 1.0)\n"
|
||||
"-s <freq> set the sampling rate in Hz (default 44100)\n"
|
||||
"-8 generate 8-bit instead of 16-bit\n"
|
||||
"-b generate big-endian data instead of little-endian (meaningless when\n"
|
||||
" using -8)\n"
|
||||
"-m generate mono output instead of stereo left/right pairs\n"
|
||||
"-u generated unsigned output instead of signed\n"
|
||||
"-r <value> specify the resampling quality to use\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
atexit(&dumb_exit);
|
||||
dumb_register_stdfiles();
|
||||
|
||||
dumb_it_max_to_mix = 256;
|
||||
|
||||
duh = load_duh(fn);
|
||||
if (!duh) {
|
||||
duh = dumb_load_it(fn);
|
||||
if (!duh) {
|
||||
duh = dumb_load_xm(fn);
|
||||
if (!duh) {
|
||||
duh = dumb_load_s3m(fn);
|
||||
if (!duh) {
|
||||
duh = dumb_load_mod(fn);
|
||||
if (!duh) {
|
||||
fprintf(stderr, "Unable to open %s!\n", fn);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sr = duh_start_sigrenderer(duh, 0, n_channels, 0);
|
||||
if (!sr) {
|
||||
unload_duh(duh);
|
||||
fprintf(stderr, "Unable to play file!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (fn_out) {
|
||||
if (fn_out[0] == '-' && fn_out[1] == 0)
|
||||
outf = stdout;
|
||||
else if (fn_out[0] == '.' && fn_out[1] == 0)
|
||||
outf = NULL;
|
||||
else {
|
||||
outf = fopen(fn_out, "wb");
|
||||
if (!outf) {
|
||||
fprintf(stderr, "Unable to open %s for writing!\n", fn_out);
|
||||
duh_end_sigrenderer(sr);
|
||||
unload_duh(duh);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
char *extptr = NULL, *p;
|
||||
char *fn_out = malloc(strlen(fn)+5);
|
||||
if (!fn_out) {
|
||||
fprintf(stderr, "Out of memory!\n");
|
||||
duh_end_sigrenderer(sr);
|
||||
unload_duh(duh);
|
||||
return 1;
|
||||
}
|
||||
strcpy(fn_out, fn);
|
||||
for (p = fn_out; *p; p++)
|
||||
if (*p == '.') extptr = p;
|
||||
if (!extptr) extptr = p;
|
||||
strcpy(extptr, ".pcm");
|
||||
outf = fopen(fn_out, "wb");
|
||||
if (!outf) {
|
||||
fprintf(stderr, "Unable to open %s for writing!\n", fn_out);
|
||||
free(fn_out);
|
||||
duh_end_sigrenderer(sr);
|
||||
unload_duh(duh);
|
||||
return 1;
|
||||
}
|
||||
free(fn_out);
|
||||
}
|
||||
|
||||
{
|
||||
DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(sr);
|
||||
dumb_it_set_loop_callback(itsr, &dumb_it_callback_terminate, NULL);
|
||||
dumb_it_set_xm_speed_zero_callback(itsr, &dumb_it_callback_terminate, NULL);
|
||||
}
|
||||
|
||||
length = (LONG_LONG)duh_get_length(duh) * freq >> 16;
|
||||
done = 0;
|
||||
dots = 0;
|
||||
delta = 65536.0f / freq;
|
||||
bufsize = depth == 16 ? 8192 : 16384;
|
||||
bufsize /= n_channels;
|
||||
|
||||
{
|
||||
long l = (long)floor(delay * freq + 0.5f);
|
||||
l *= n_channels * (depth >> 3);
|
||||
if (l) {
|
||||
if (unsign) {
|
||||
if (depth == 16) {
|
||||
if (bigendian) {
|
||||
for (i = 0; i < 8192; i++) {
|
||||
buffer.s8[i*2] = 0x80;
|
||||
buffer.s8[i*2+1] = 0x00;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < 8192; i++) {
|
||||
buffer.s8[i*2] = 0x00;
|
||||
buffer.s8[i*2+1] = 0x80;
|
||||
}
|
||||
}
|
||||
} else
|
||||
memset(buffer.s8, 0x80, 16384);
|
||||
} else
|
||||
memset(buffer.s8, 0, 16384);
|
||||
while (l >= 16384) {
|
||||
if (outf) fwrite(buffer.s8, 1, 16384, outf);
|
||||
l -= 16384;
|
||||
}
|
||||
if (l && outf) fwrite(buffer.s8, 1, l, outf);
|
||||
}
|
||||
}
|
||||
|
||||
start = clock();
|
||||
|
||||
fprintf(stderr, "................................................................\n");
|
||||
for (;;) {
|
||||
int l = duh_render(sr, depth, unsign, volume, delta, bufsize, &buffer);
|
||||
if (depth == 16) {
|
||||
if (bigendian) {
|
||||
for (i = 0; i < l * n_channels; i++) {
|
||||
short val = buffer.s16[i];
|
||||
buffer.s8[i*2] = val >> 8;
|
||||
buffer.s8[i*2+1] = val;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < l * n_channels; i++) {
|
||||
short val = buffer.s16[i];
|
||||
buffer.s8[i*2] = val;
|
||||
buffer.s8[i*2+1] = val >> 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (outf) fwrite(buffer.s8, 1, l * n_channels * (depth >> 3), outf);
|
||||
if (l < bufsize) break;
|
||||
done += l;
|
||||
l = done * 64 / length;
|
||||
while (dots < 64 && l > dots) {
|
||||
fprintf(stderr, "|");
|
||||
dots++;
|
||||
}
|
||||
}
|
||||
|
||||
while (64 > dots) {
|
||||
fprintf(stderr, "|");
|
||||
dots++;
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
end = clock();
|
||||
|
||||
duh_end_sigrenderer(sr);
|
||||
unload_duh(duh);
|
||||
if (outf && outf != stdout) fclose(outf);
|
||||
|
||||
fprintf(stderr, "Elapsed time: %f seconds\n", (end - start) / (float)CLOCKS_PER_SEC);
|
||||
|
||||
return 0;
|
||||
}
|
238
apps/codecs/dumb/examples/dumbplay.c
Normal file
238
apps/codecs/dumb/examples/dumbplay.c
Normal file
|
@ -0,0 +1,238 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* dumbplay.c - Not-so-simple program to play / / \ \
|
||||
* music. It used to be simpler! | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* IMPORTANT NOTE: This file is not very friendly. | ' /
|
||||
* I strongly recommend AGAINST using it as a \__/
|
||||
* reference for writing your own code. If you would
|
||||
* like to write a program that uses DUMB, or add DUMB to an existing
|
||||
* project, please use docs/howto.txt. It will help you a lot more than this
|
||||
* file can. (If you have difficulty reading documentation, you are lacking
|
||||
* an important coding skill, and now is as good a time as any to learn.)
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <allegro.h>
|
||||
|
||||
#ifndef ALLEGRO_DOS
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
/* Note that your own programs should use <aldumb.h> not "aldumb.h". <> tells
|
||||
* the compiler to look in the compiler's default header directory, which is
|
||||
* where DUMB should be installed before you use it (make install does this).
|
||||
* Use "" when it is your own header file. This example uses "" because DUMB
|
||||
* might not have been installed yet when the makefile builds it.
|
||||
*/
|
||||
#include "aldumb.h"
|
||||
|
||||
|
||||
|
||||
#ifndef ALLEGRO_DOS
|
||||
static volatile int closed = 0;
|
||||
static void closehook(void) { closed = 1; }
|
||||
#else
|
||||
#define closed 0
|
||||
#endif
|
||||
|
||||
#ifdef ALLEGRO_WINDOWS
|
||||
#define GFX_DUMB_MODE GFX_GDI
|
||||
#include <winalleg.h>
|
||||
#define YIELD() Sleep(1)
|
||||
#else
|
||||
#define GFX_DUMB_MODE GFX_AUTODETECT_WINDOWED
|
||||
#ifdef ALLEGRO_UNIX
|
||||
#include <sys/time.h>
|
||||
static void YIELD(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 1;
|
||||
select(0, NULL, NULL, NULL, &tv);
|
||||
}
|
||||
#else
|
||||
#define YIELD() yield_timeslice()
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef ALLEGRO_DOS
|
||||
static int loop_callback(void *data)
|
||||
{
|
||||
(void)data;
|
||||
printf("Music has looped.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xm_speed_zero_callback(void *data)
|
||||
{
|
||||
(void)data;
|
||||
printf("Music has stopped.\n");
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int gfx_half_width;
|
||||
|
||||
static int loop_callback(void *data)
|
||||
{
|
||||
(void)data;
|
||||
if (gfx_half_width) {
|
||||
acquire_screen();
|
||||
textout_centre(screen, font, "Music has looped.", gfx_half_width, 36, 10);
|
||||
release_screen();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xm_speed_zero_callback(void *data)
|
||||
{
|
||||
(void)data;
|
||||
if (gfx_half_width) {
|
||||
text_mode(0); /* In case this is overwriting "Music has looped." */
|
||||
acquire_screen();
|
||||
textout_centre(screen, font, "Music has stopped.", gfx_half_width, 36, 10);
|
||||
release_screen();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
static void usage(const char *exename)
|
||||
{
|
||||
allegro_message(
|
||||
#ifdef ALLEGRO_WINDOWS
|
||||
"Usage:\n"
|
||||
" At the command line: %s file\n"
|
||||
" In Windows Explorer: drag a file on to this program's icon.\n"
|
||||
#else
|
||||
"Usage: %s file\n"
|
||||
#endif
|
||||
"This will play the music file specified.\n"
|
||||
"File formats supported: IT XM S3M MOD.\n"
|
||||
"You can control playback quality by editing dumb.ini.\n", exename);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(int argc, const char *const *argv) /* I'm const-crazy! */
|
||||
{
|
||||
DUH *duh;
|
||||
AL_DUH_PLAYER *dp;
|
||||
|
||||
if (allegro_init())
|
||||
return 1;
|
||||
|
||||
if (argc != 2)
|
||||
usage(argv[0]);
|
||||
|
||||
set_config_file("dumb.ini");
|
||||
|
||||
if (install_keyboard()) {
|
||||
allegro_message("Failed to initialise keyboard driver!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
set_volume_per_voice(0);
|
||||
|
||||
if (install_sound(DIGI_AUTODETECT, MIDI_NONE, NULL)) {
|
||||
allegro_message("Failed to initialise sound driver!\n%s\n", allegro_error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
atexit(&dumb_exit);
|
||||
|
||||
dumb_register_packfiles();
|
||||
|
||||
duh = dumb_load_it(argv[1]);
|
||||
if (!duh) {
|
||||
duh = dumb_load_xm(argv[1]);
|
||||
if (!duh) {
|
||||
duh = dumb_load_s3m(argv[1]);
|
||||
if (!duh) {
|
||||
duh = dumb_load_mod(argv[1]);
|
||||
if (!duh) {
|
||||
allegro_message("Failed to load %s!\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dumb_resampling_quality = get_config_int("sound", "dumb_resampling_quality", 4);
|
||||
dumb_it_max_to_mix = get_config_int("sound", "dumb_it_max_to_mix", 128);
|
||||
|
||||
#ifndef ALLEGRO_DOS
|
||||
{
|
||||
const char *fn = get_filename(argv[1]);
|
||||
gfx_half_width = strlen(fn);
|
||||
if (gfx_half_width < 22) gfx_half_width = 22;
|
||||
gfx_half_width = (gfx_half_width + 2) * 4;
|
||||
|
||||
/* set_window_title() is not const-correct (yet). */
|
||||
set_window_title((char *)"DUMB Music Player");
|
||||
|
||||
if (set_gfx_mode(GFX_DUMB_MODE, gfx_half_width*2, 80, 0, 0) == 0) {
|
||||
acquire_screen();
|
||||
textout_centre(screen, font, fn, gfx_half_width, 20, 14);
|
||||
textout_centre(screen, font, "Press any key to exit.", gfx_half_width, 52, 11);
|
||||
release_screen();
|
||||
} else
|
||||
gfx_half_width = 0;
|
||||
}
|
||||
|
||||
#if ALLEGRO_VERSION*10000 + ALLEGRO_SUB_VERSION*100 + ALLEGRO_WIP_VERSION >= 40105
|
||||
set_close_button_callback(&closehook);
|
||||
#else
|
||||
set_window_close_hook(&closehook);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
set_display_switch_mode(SWITCH_BACKGROUND);
|
||||
|
||||
dp = al_start_duh(duh, 2, 0, 1.0f,
|
||||
get_config_int("sound", "buffer_size", 4096),
|
||||
get_config_int("sound", "sound_freq", 44100));
|
||||
|
||||
{
|
||||
DUH_SIGRENDERER *sr = al_duh_get_sigrenderer(dp);
|
||||
DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(sr);
|
||||
dumb_it_set_loop_callback(itsr, &loop_callback, NULL);
|
||||
dumb_it_set_xm_speed_zero_callback(itsr, &xm_speed_zero_callback, NULL);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (keypressed()) {
|
||||
readkey();
|
||||
break;
|
||||
}
|
||||
|
||||
if (al_poll_duh(dp) || closed)
|
||||
break;
|
||||
|
||||
YIELD();
|
||||
}
|
||||
|
||||
al_stop_duh(dp);
|
||||
|
||||
unload_duh(duh);
|
||||
|
||||
return 0;
|
||||
}
|
||||
END_OF_MAIN();
|
93
apps/codecs/dumb/include/aldumb.h
Normal file
93
apps/codecs/dumb/include/aldumb.h
Normal file
|
@ -0,0 +1,93 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* aldumb.h - The user header file for DUMB with / / \ \
|
||||
* Allegro. | < / \_
|
||||
* | \/ /\ /
|
||||
* Include this file if you wish to use DUMB \_ / > /
|
||||
* with Allegro. It will include dumb.h for you, | \ / /
|
||||
* and provide extra functionality such as audio | ' /
|
||||
* stream and datafile integration. \__/
|
||||
*/
|
||||
|
||||
#ifndef ALDUMB_H
|
||||
#define ALDUMB_H
|
||||
|
||||
|
||||
#include <allegro.h>
|
||||
|
||||
#include "dumb.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* Packfile Support */
|
||||
|
||||
void dumb_register_packfiles(void);
|
||||
|
||||
DUMBFILE *dumbfile_open_packfile(PACKFILE *p);
|
||||
DUMBFILE *dumbfile_from_packfile(PACKFILE *p);
|
||||
|
||||
|
||||
/* Datafile Registration Functions */
|
||||
|
||||
#define DUMB_DAT_DUH DAT_ID('D','U','H',' ')
|
||||
#define DUMB_DAT_IT DAT_ID('I','T',' ',' ')
|
||||
#define DUMB_DAT_XM DAT_ID('X','M',' ',' ')
|
||||
#define DUMB_DAT_S3M DAT_ID('S','3','M',' ')
|
||||
#define DUMB_DAT_MOD DAT_ID('M','O','D',' ')
|
||||
|
||||
void dumb_register_dat_duh(long type);
|
||||
void dumb_register_dat_it(long type);
|
||||
void dumb_register_dat_xm(long type);
|
||||
void dumb_register_dat_s3m(long type);
|
||||
void dumb_register_dat_mod(long type);
|
||||
|
||||
|
||||
/* DUH Playing Functions */
|
||||
|
||||
typedef struct AL_DUH_PLAYER AL_DUH_PLAYER;
|
||||
|
||||
AL_DUH_PLAYER *al_start_duh(DUH *duh, int n_channels, long pos, float volume, long bufsize, int freq);
|
||||
void al_stop_duh(AL_DUH_PLAYER *dp);
|
||||
void al_pause_duh(AL_DUH_PLAYER *dp);
|
||||
void al_resume_duh(AL_DUH_PLAYER *dp);
|
||||
void al_duh_set_priority(AL_DUH_PLAYER *dp, int priority);
|
||||
void al_duh_set_volume(AL_DUH_PLAYER *dp, float volume);
|
||||
int al_poll_duh(AL_DUH_PLAYER *dp);
|
||||
long al_duh_get_position(AL_DUH_PLAYER *dp);
|
||||
|
||||
AL_DUH_PLAYER *al_duh_encapsulate_sigrenderer(DUH_SIGRENDERER *sigrenderer, float volume, long bufsize, int freq);
|
||||
DUH_SIGRENDERER *al_duh_get_sigrenderer(AL_DUH_PLAYER *dp);
|
||||
|
||||
/* IMPORTANT: This function will return NULL if the music has ended. */
|
||||
DUH_SIGRENDERER *al_duh_decompose_to_sigrenderer(AL_DUH_PLAYER *dp);
|
||||
|
||||
#ifdef DUMB_DECLARE_DEPRECATED
|
||||
|
||||
AL_DUH_PLAYER *al_duh_encapsulate_renderer(DUH_SIGRENDERER *dr, float volume, long bufsize, int freq) DUMB_DEPRECATED;
|
||||
DUH_SIGRENDERER *al_duh_get_renderer(AL_DUH_PLAYER *dp) DUMB_DEPRECATED;
|
||||
DUH_SIGRENDERER *al_duh_decompose_to_renderer(AL_DUH_PLAYER *dp) DUMB_DEPRECATED;
|
||||
/* Replace 'renderer' with 'sigrenderer' in each case where you called one of
|
||||
* these functions.
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* ALDUMB_H */
|
563
apps/codecs/dumb/include/dumb.h
Normal file
563
apps/codecs/dumb/include/dumb.h
Normal file
|
@ -0,0 +1,563 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* dumb.h - The user header file for DUMB. / / \ \
|
||||
* | < / \_
|
||||
* Include this file in any of your files in | \/ /\ /
|
||||
* which you wish to use the DUMB functions \_ / > /
|
||||
* and variables. | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#ifndef DUMB_H
|
||||
#define DUMB_H
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#define DUMB_MAJOR_VERSION 0
|
||||
#define DUMB_MINOR_VERSION 9
|
||||
#define DUMB_REVISION_VERSION 2
|
||||
|
||||
#define DUMB_VERSION (DUMB_MAJOR_VERSION*10000 + DUMB_MINOR_VERSION*100 + DUMB_REVISION_VERSION)
|
||||
|
||||
#define DUMB_VERSION_STR "0.9.2"
|
||||
|
||||
#define DUMB_NAME "DUMB v"DUMB_VERSION_STR
|
||||
|
||||
#define DUMB_YEAR 2003
|
||||
#define DUMB_MONTH 4
|
||||
#define DUMB_DAY 2
|
||||
|
||||
#define DUMB_YEAR_STR2 "03"
|
||||
#define DUMB_YEAR_STR4 "2003"
|
||||
#define DUMB_MONTH_STR1 "4"
|
||||
#define DUMB_DAY_STR1 "2"
|
||||
|
||||
#if DUMB_MONTH < 10
|
||||
#define DUMB_MONTH_STR2 "0"DUMB_MONTH_STR1
|
||||
#else
|
||||
#define DUMB_MONTH_STR2 DUMB_MONTH_STR1
|
||||
#endif
|
||||
|
||||
#if DUMB_DAY < 10
|
||||
#define DUMB_DAY_STR2 "0"DUMB_DAY_STR1
|
||||
#else
|
||||
#define DUMB_DAY_STR2 DUMB_DAY_STR1
|
||||
#endif
|
||||
|
||||
|
||||
/* WARNING: The month and day were inadvertently swapped in the v0.8 release.
|
||||
* Please do not compare this constant against any date in 2002. In
|
||||
* any case, DUMB_VERSION is probably more useful for this purpose.
|
||||
*/
|
||||
#define DUMB_DATE (DUMB_YEAR*10000 + DUMB_MONTH*100 + DUMB_DAY)
|
||||
|
||||
#define DUMB_DATE_STR DUMB_DAY_STR1"."DUMB_MONTH_STR1"."DUMB_YEAR_STR4
|
||||
|
||||
|
||||
#undef MIN
|
||||
#undef MAX
|
||||
#undef MID
|
||||
|
||||
#define MIN(x,y) (((x) < (y)) ? (x) : (y))
|
||||
#define MAX(x,y) (((x) > (y)) ? (x) : (y))
|
||||
#define MID(x,y,z) MAX((x), MIN((y), (z)))
|
||||
|
||||
#undef ABS
|
||||
#define ABS(x) (((x) >= 0) ? (x) : (-(x)))
|
||||
|
||||
|
||||
#ifdef DEBUGMODE
|
||||
|
||||
#ifndef ASSERT
|
||||
#include <assert.h>
|
||||
#define ASSERT(n) assert(n)
|
||||
#endif
|
||||
#ifndef TRACE
|
||||
// it would be nice if this did actually trace ...
|
||||
#define TRACE 1 ? (void)0 : (void)printf
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifndef ASSERT
|
||||
#define ASSERT(n)
|
||||
#endif
|
||||
#ifndef TRACE
|
||||
#define TRACE 1 ? (void)0 : (void)printf
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#define DUMB_ID(a,b,c,d) (((unsigned int)(a) << 24) | \
|
||||
((unsigned int)(b) << 16) | \
|
||||
((unsigned int)(c) << 8) | \
|
||||
((unsigned int)(d) ))
|
||||
|
||||
|
||||
|
||||
#ifndef LONG_LONG
|
||||
#ifdef __GNUC__
|
||||
#define LONG_LONG long long
|
||||
#elif defined _MSC_VER
|
||||
#define LONG_LONG __int64
|
||||
#else
|
||||
#error 64-bit integer type unknown
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if __GNUC__ * 100 + __GNUC_MINOR__ >= 301 /* GCC 3.1+ */
|
||||
#ifndef DUMB_DECLARE_DEPRECATED
|
||||
#define DUMB_DECLARE_DEPRECATED
|
||||
#endif
|
||||
#define DUMB_DEPRECATED __attribute__((__deprecated__))
|
||||
#else
|
||||
#define DUMB_DEPRECATED
|
||||
#endif
|
||||
|
||||
|
||||
/* Basic Sample Type. Normal range is -0x800000 to 0x7FFFFF. */
|
||||
|
||||
typedef int sample_t;
|
||||
|
||||
|
||||
/* Library Clean-up Management */
|
||||
|
||||
int dumb_atexit(void (*proc)(void));
|
||||
|
||||
void dumb_exit(void);
|
||||
|
||||
|
||||
/* File Input Functions */
|
||||
|
||||
typedef struct DUMBFILE_SYSTEM
|
||||
{
|
||||
void *(*open)(const char *filename);
|
||||
int (*skip)(void *f, long n);
|
||||
int (*getc)(void *f);
|
||||
long (*getnc)(char *ptr, long n, void *f);
|
||||
void (*close)(void *f);
|
||||
}
|
||||
DUMBFILE_SYSTEM;
|
||||
|
||||
typedef struct DUMBFILE DUMBFILE;
|
||||
|
||||
void register_dumbfile_system(DUMBFILE_SYSTEM *dfs);
|
||||
|
||||
DUMBFILE *dumbfile_open(const char *filename);
|
||||
DUMBFILE *dumbfile_open_ex(void *file, DUMBFILE_SYSTEM *dfs);
|
||||
|
||||
long dumbfile_pos(DUMBFILE *f);
|
||||
int dumbfile_skip(DUMBFILE *f, long n);
|
||||
|
||||
int dumbfile_getc(DUMBFILE *f);
|
||||
|
||||
int dumbfile_igetw(DUMBFILE *f);
|
||||
int dumbfile_mgetw(DUMBFILE *f);
|
||||
|
||||
long dumbfile_igetl(DUMBFILE *f);
|
||||
long dumbfile_mgetl(DUMBFILE *f);
|
||||
|
||||
unsigned long dumbfile_cgetul(DUMBFILE *f);
|
||||
signed long dumbfile_cgetsl(DUMBFILE *f);
|
||||
|
||||
long dumbfile_getnc(char *ptr, long n, DUMBFILE *f);
|
||||
|
||||
int dumbfile_error(DUMBFILE *f);
|
||||
int dumbfile_close(DUMBFILE *f);
|
||||
|
||||
|
||||
/* stdio File Input Module */
|
||||
|
||||
/*void dumb_register_stdfiles(void);
|
||||
|
||||
DUMBFILE *dumbfile_open_stdfile(int fd);*/
|
||||
|
||||
|
||||
/* Memory File Input Module */
|
||||
|
||||
DUMBFILE *dumbfile_open_memory(const char *data, long size);
|
||||
|
||||
|
||||
/* DUH Management */
|
||||
|
||||
typedef struct DUH DUH;
|
||||
|
||||
#define DUH_SIGNATURE DUMB_ID('D','U','H','!')
|
||||
|
||||
void unload_duh(DUH *duh);
|
||||
|
||||
DUH *load_duh(const char *filename);
|
||||
DUH *read_duh(DUMBFILE *f);
|
||||
|
||||
long duh_get_length(DUH *duh);
|
||||
|
||||
|
||||
/* Signal Rendering Functions */
|
||||
|
||||
typedef struct DUH_SIGRENDERER DUH_SIGRENDERER;
|
||||
|
||||
DUH_SIGRENDERER *duh_start_sigrenderer(DUH *duh, int sig, int n_channels, long pos);
|
||||
|
||||
#ifdef DUMB_DECLARE_DEPRECATED
|
||||
typedef void (*DUH_SIGRENDERER_CALLBACK)(void *data, sample_t **samples, int n_channels, long length);
|
||||
/* This is deprecated, but is not marked as such because GCC tends to
|
||||
* complain spuriously when the typedef is used later. See comments below.
|
||||
*/
|
||||
|
||||
void duh_sigrenderer_set_callback(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
DUH_SIGRENDERER_CALLBACK callback, void *data
|
||||
) DUMB_DEPRECATED;
|
||||
/* The 'callback' argument's type has changed for const-correctness. See the
|
||||
* DUH_SIGRENDERER_CALLBACK definition just above. Also note that the samples
|
||||
* in the buffer are now 256 times as large; the normal range is -0x800000 to
|
||||
* 0x7FFFFF. The function has been renamed partly because its functionality
|
||||
* has changed slightly and partly so that its name is more meaningful. The
|
||||
* new one is duh_sigrenderer_set_analyser_callback(), and the typedef for
|
||||
* the function pointer has also changed, from DUH_SIGRENDERER_CALLBACK to
|
||||
* DUH_SIGRENDERER_ANALYSER_CALLBACK. (If you wanted to use this callback to
|
||||
* apply a DSP effect, don't worry; there is a better way of doing this. It
|
||||
* is undocumented, so contact me and I shall try to help. Contact details
|
||||
* are in readme.txt.)
|
||||
*/
|
||||
#endif
|
||||
|
||||
typedef void (*DUH_SIGRENDERER_ANALYSER_CALLBACK)(void *data, const sample_t *const *samples, int n_channels, long length);
|
||||
|
||||
void duh_sigrenderer_set_analyser_callback(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
DUH_SIGRENDERER_ANALYSER_CALLBACK callback, void *data
|
||||
);
|
||||
|
||||
int duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer);
|
||||
long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer);
|
||||
|
||||
void duh_sigrenderer_set_sigparam(DUH_SIGRENDERER *sigrenderer, unsigned char id, long value);
|
||||
|
||||
long duh_sigrenderer_get_samples(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
float volume, float delta,
|
||||
long size, sample_t **samples
|
||||
);
|
||||
|
||||
void duh_sigrenderer_get_current_sample(DUH_SIGRENDERER *sigrenderer, float volume, sample_t *samples);
|
||||
|
||||
void duh_end_sigrenderer(DUH_SIGRENDERER *sigrenderer);
|
||||
|
||||
|
||||
/* DUH Rendering Functions */
|
||||
|
||||
long duh_render(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
int bits, int unsign,
|
||||
float volume, float delta,
|
||||
long size, void *sptr
|
||||
);
|
||||
|
||||
#ifdef DUMB_DECLARE_DEPRECATED
|
||||
|
||||
long duh_render_signal(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
float volume, float delta,
|
||||
long size, sample_t **samples
|
||||
) DUMB_DEPRECATED;
|
||||
/* Please use duh_sigrenderer_get_samples(). Arguments and functionality are
|
||||
* identical.
|
||||
*/
|
||||
|
||||
typedef DUH_SIGRENDERER DUH_RENDERER DUMB_DEPRECATED;
|
||||
/* Please use DUH_SIGRENDERER instead of DUH_RENDERER. */
|
||||
|
||||
DUH_SIGRENDERER *duh_start_renderer(DUH *duh, int n_channels, long pos) DUMB_DEPRECATED;
|
||||
/* Please use duh_start_sigrenderer() instead. Pass 0 for 'sig'. */
|
||||
|
||||
int duh_renderer_get_n_channels(DUH_SIGRENDERER *dr) DUMB_DEPRECATED;
|
||||
long duh_renderer_get_position(DUH_SIGRENDERER *dr) DUMB_DEPRECATED;
|
||||
/* Please use the duh_sigrenderer_*() equivalents of these two functions. */
|
||||
|
||||
void duh_end_renderer(DUH_SIGRENDERER *dr) DUMB_DEPRECATED;
|
||||
/* Please use duh_end_sigrenderer() instead. */
|
||||
|
||||
DUH_SIGRENDERER *duh_renderer_encapsulate_sigrenderer(DUH_SIGRENDERER *sigrenderer) DUMB_DEPRECATED;
|
||||
DUH_SIGRENDERER *duh_renderer_get_sigrenderer(DUH_SIGRENDERER *dr) DUMB_DEPRECATED;
|
||||
DUH_SIGRENDERER *duh_renderer_decompose_to_sigrenderer(DUH_SIGRENDERER *dr) DUMB_DEPRECATED;
|
||||
/* These functions have become no-ops that just return the parameter.
|
||||
* So, for instance, replace
|
||||
* duh_renderer_encapsulate_sigrenderer(my_sigrenderer)
|
||||
* with
|
||||
* my_sigrenderer
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* Impulse Tracker Support */
|
||||
|
||||
extern int dumb_it_max_to_mix;
|
||||
|
||||
typedef struct DUMB_IT_SIGDATA DUMB_IT_SIGDATA;
|
||||
typedef struct DUMB_IT_SIGRENDERER DUMB_IT_SIGRENDERER;
|
||||
|
||||
DUMB_IT_SIGDATA *duh_get_it_sigdata(DUH *duh);
|
||||
DUH_SIGRENDERER *duh_encapsulate_it_sigrenderer(DUMB_IT_SIGRENDERER *it_sigrenderer, int n_channels, long pos);
|
||||
DUMB_IT_SIGRENDERER *duh_get_it_sigrenderer(DUH_SIGRENDERER *sigrenderer);
|
||||
|
||||
DUH_SIGRENDERER *dumb_it_start_at_order(DUH *duh, int n_channels, int startorder);
|
||||
|
||||
void dumb_it_set_loop_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data);
|
||||
void dumb_it_set_xm_speed_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data);
|
||||
void dumb_it_set_midi_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data, int channel, unsigned char byte), void *data);
|
||||
|
||||
int dumb_it_callback_terminate(void *data);
|
||||
int dumb_it_callback_midi_block(void *data, int channel, unsigned char byte);
|
||||
|
||||
DUH *dumb_load_it(const char *filename);
|
||||
DUH *dumb_load_xm(const char *filename);
|
||||
DUH *dumb_load_s3m(const char *filename);
|
||||
DUH *dumb_load_mod(const char *filename);
|
||||
|
||||
DUH *dumb_read_it(DUMBFILE *f);
|
||||
DUH *dumb_read_xm(DUMBFILE *f);
|
||||
DUH *dumb_read_s3m(DUMBFILE *f);
|
||||
DUH *dumb_read_mod(DUMBFILE *f);
|
||||
|
||||
int dumb_it_sd_get_n_orders(DUMB_IT_SIGDATA *sd);
|
||||
|
||||
int dumb_it_sd_get_initial_global_volume(DUMB_IT_SIGDATA *sd);
|
||||
void dumb_it_sd_set_initial_global_volume(DUMB_IT_SIGDATA *sd, int gv);
|
||||
|
||||
int dumb_it_sd_get_mixing_volume(DUMB_IT_SIGDATA *sd);
|
||||
void dumb_it_sd_set_mixing_volume(DUMB_IT_SIGDATA *sd, int mv);
|
||||
|
||||
int dumb_it_sd_get_initial_speed(DUMB_IT_SIGDATA *sd);
|
||||
void dumb_it_sd_set_initial_speed(DUMB_IT_SIGDATA *sd, int speed);
|
||||
|
||||
int dumb_it_sd_get_initial_tempo(DUMB_IT_SIGDATA *sd);
|
||||
void dumb_it_sd_set_initial_tempo(DUMB_IT_SIGDATA *sd, int tempo);
|
||||
|
||||
int dumb_it_sd_get_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel);
|
||||
void dumb_it_sd_set_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel, int volume);
|
||||
|
||||
int dumb_it_sr_get_current_order(DUMB_IT_SIGRENDERER *sr);
|
||||
int dumb_it_sr_get_current_row(DUMB_IT_SIGRENDERER *sr);
|
||||
|
||||
int dumb_it_sr_get_global_volume(DUMB_IT_SIGRENDERER *sr);
|
||||
void dumb_it_sr_set_global_volume(DUMB_IT_SIGRENDERER *sr, int gv);
|
||||
|
||||
int dumb_it_sr_get_tempo(DUMB_IT_SIGRENDERER *sr);
|
||||
void dumb_it_sr_set_tempo(DUMB_IT_SIGRENDERER *sr, int tempo);
|
||||
|
||||
int dumb_it_sr_get_speed(DUMB_IT_SIGRENDERER *sr);
|
||||
void dumb_it_sr_set_speed(DUMB_IT_SIGRENDERER *sr, int speed);
|
||||
|
||||
#define DUMB_IT_N_CHANNELS 64
|
||||
#define DUMB_IT_N_NNA_CHANNELS 192
|
||||
#define DUMB_IT_TOTAL_CHANNELS (DUMB_IT_N_CHANNELS + DUMB_IT_N_NNA_CHANNELS)
|
||||
|
||||
/* Channels passed to any of these functions are 0-based */
|
||||
int dumb_it_sr_get_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel);
|
||||
void dumb_it_sr_set_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel, int volume);
|
||||
|
||||
typedef struct DUMB_IT_CHANNEL_STATE DUMB_IT_CHANNEL_STATE;
|
||||
|
||||
struct DUMB_IT_CHANNEL_STATE
|
||||
{
|
||||
int channel; /* 0-based; meaningful for NNA channels */
|
||||
int sample; /* 1-based; 0 if nothing playing, then other fields undef */
|
||||
int freq; /* in Hz */
|
||||
float volume; /* 1.0 maximum; affected by ALL factors, inc. mixing vol */
|
||||
unsigned char pan; /* 0-64, 100 for surround */
|
||||
signed char subpan; /* use (pan + subpan/256.0f) or ((pan<<8)+subpan) */
|
||||
unsigned char filter_cutoff; /* 0-127 cutoff=127 AND resonance=0 */
|
||||
unsigned char filter_subcutoff; /* 0-255 -> no filters (subcutoff */
|
||||
unsigned char filter_resonance; /* 0-127 always 0 in this case) */
|
||||
/* subcutoff only changes from zero if filter envelopes are in use. The
|
||||
* calculation (filter_cutoff + filter_subcutoff/256.0f) gives a more
|
||||
* accurate filter cutoff measurement as a float. It would often be more
|
||||
* useful to use a scaled int such as ((cutoff<<8) + subcutoff).
|
||||
*/
|
||||
};
|
||||
|
||||
/* 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);
|
||||
|
||||
|
||||
/* Signal Design Helper Values */
|
||||
|
||||
/* Use pow(DUMB_SEMITONE_BASE, n) to get the 'delta' value to transpose up by
|
||||
* n semitones. To transpose down, use negative n.
|
||||
*/
|
||||
#define DUMB_SEMITONE_BASE 1.059463094359295309843105314939748495817
|
||||
|
||||
/* Use pow(DUMB_QUARTERTONE_BASE, n) to get the 'delta' value to transpose up
|
||||
* by n quartertones. To transpose down, use negative n.
|
||||
*/
|
||||
#define DUMB_QUARTERTONE_BASE 1.029302236643492074463779317738953977823
|
||||
|
||||
/* Use pow(DUMB_PITCH_BASE, n) to get the 'delta' value to transpose up by n
|
||||
* units. In this case, 256 units represent one semitone; 3072 units
|
||||
* represent one octave. These units are used by the sequence signal (SEQU).
|
||||
*/
|
||||
#define DUMB_PITCH_BASE 1.000225659305069791926712241547647863626
|
||||
|
||||
|
||||
/* Signal Design Function Types */
|
||||
|
||||
typedef void sigdata_t;
|
||||
typedef void sigrenderer_t;
|
||||
|
||||
typedef sigdata_t *(*DUH_LOAD_SIGDATA)(DUH *duh, DUMBFILE *file);
|
||||
|
||||
typedef sigrenderer_t *(*DUH_START_SIGRENDERER)(
|
||||
DUH *duh,
|
||||
sigdata_t *sigdata,
|
||||
int n_channels,
|
||||
long pos
|
||||
);
|
||||
|
||||
typedef void (*DUH_SIGRENDERER_SET_SIGPARAM)(
|
||||
sigrenderer_t *sigrenderer,
|
||||
unsigned char id, long value
|
||||
);
|
||||
|
||||
typedef long (*DUH_SIGRENDERER_GET_SAMPLES)(
|
||||
sigrenderer_t *sigrenderer,
|
||||
float volume, float delta,
|
||||
long size, sample_t **samples
|
||||
);
|
||||
|
||||
typedef void (*DUH_SIGRENDERER_GET_CURRENT_SAMPLE)(
|
||||
sigrenderer_t *sigrenderer,
|
||||
float volume,
|
||||
sample_t *samples
|
||||
);
|
||||
|
||||
typedef void (*DUH_END_SIGRENDERER)(sigrenderer_t *sigrenderer);
|
||||
|
||||
typedef void (*DUH_UNLOAD_SIGDATA)(sigdata_t *sigdata);
|
||||
|
||||
|
||||
/* Signal Design Function Registration */
|
||||
|
||||
typedef struct DUH_SIGTYPE_DESC
|
||||
{
|
||||
long type;
|
||||
DUH_LOAD_SIGDATA load_sigdata;
|
||||
DUH_START_SIGRENDERER start_sigrenderer;
|
||||
DUH_SIGRENDERER_SET_SIGPARAM sigrenderer_set_sigparam;
|
||||
DUH_SIGRENDERER_GET_SAMPLES sigrenderer_get_samples;
|
||||
DUH_SIGRENDERER_GET_CURRENT_SAMPLE sigrenderer_get_current_sample;
|
||||
DUH_END_SIGRENDERER end_sigrenderer;
|
||||
DUH_UNLOAD_SIGDATA unload_sigdata;
|
||||
}
|
||||
DUH_SIGTYPE_DESC;
|
||||
|
||||
void dumb_register_sigtype(DUH_SIGTYPE_DESC *desc);
|
||||
|
||||
|
||||
// Decide where to put these functions; new heading?
|
||||
|
||||
sigdata_t *duh_get_raw_sigdata(DUH *duh, int sig, long type);
|
||||
|
||||
DUH_SIGRENDERER *duh_encapsulate_raw_sigrenderer(sigrenderer_t *vsigrenderer, DUH_SIGTYPE_DESC *desc, int n_channels, long pos);
|
||||
sigrenderer_t *duh_get_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, long type);
|
||||
|
||||
|
||||
/* Sample Buffer Allocation Helpers */
|
||||
|
||||
sample_t **create_sample_buffer(int n_channels, long length);
|
||||
void destroy_sample_buffer(sample_t **samples);
|
||||
|
||||
|
||||
/* Silencing Helper */
|
||||
|
||||
void dumb_silence(sample_t *samples, long length);
|
||||
|
||||
|
||||
/* Click Removal Helpers */
|
||||
|
||||
typedef struct DUMB_CLICK_REMOVER DUMB_CLICK_REMOVER;
|
||||
|
||||
DUMB_CLICK_REMOVER *dumb_create_click_remover(void);
|
||||
void dumb_record_click(DUMB_CLICK_REMOVER *cr, long pos, sample_t step);
|
||||
void dumb_remove_clicks(DUMB_CLICK_REMOVER *cr, sample_t *samples, long length, float halflife);
|
||||
sample_t dumb_click_remover_get_offset(DUMB_CLICK_REMOVER *cr);
|
||||
void dumb_destroy_click_remover(DUMB_CLICK_REMOVER *cr);
|
||||
|
||||
DUMB_CLICK_REMOVER **dumb_create_click_remover_array(int n);
|
||||
void dumb_record_click_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step);
|
||||
void dumb_record_click_negative_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step);
|
||||
void dumb_remove_clicks_array(int n, DUMB_CLICK_REMOVER **cr, sample_t **samples, long length, float halflife);
|
||||
void dumb_click_remover_get_offset_array(int n, DUMB_CLICK_REMOVER **cr, sample_t *offset);
|
||||
void dumb_destroy_click_remover_array(int n, DUMB_CLICK_REMOVER **cr);
|
||||
|
||||
|
||||
/* Resampling Helpers */
|
||||
|
||||
#define DUMB_RQ_ALIASING 0
|
||||
#define DUMB_RQ_LINEAR 1
|
||||
#define DUMB_RQ_CUBIC 2
|
||||
#define DUMB_RQ_N_LEVELS 3
|
||||
extern int dumb_resampling_quality;
|
||||
|
||||
typedef struct DUMB_RESAMPLER DUMB_RESAMPLER;
|
||||
|
||||
typedef void (*DUMB_RESAMPLE_PICKUP)(DUMB_RESAMPLER *resampler, void *data);
|
||||
|
||||
struct DUMB_RESAMPLER
|
||||
{
|
||||
sample_t *src;
|
||||
long pos;
|
||||
int subpos;
|
||||
long start, end;
|
||||
int dir;
|
||||
DUMB_RESAMPLE_PICKUP pickup;
|
||||
void *pickup_data;
|
||||
int min_quality;
|
||||
int max_quality;
|
||||
/* Everything below this point is internal: do not use. */
|
||||
sample_t x[3];
|
||||
int overshot;
|
||||
};
|
||||
|
||||
void dumb_reset_resampler(DUMB_RESAMPLER *resampler, sample_t *src, long pos, long start, long end);
|
||||
DUMB_RESAMPLER *dumb_start_resampler(sample_t *src, long pos, long start, long end);
|
||||
long dumb_resample(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume, float delta);
|
||||
sample_t dumb_resample_get_current_sample(DUMB_RESAMPLER *resampler, float volume);
|
||||
void dumb_end_resampler(DUMB_RESAMPLER *resampler);
|
||||
|
||||
|
||||
/* DUH Construction */
|
||||
|
||||
DUH *make_duh(
|
||||
long length,
|
||||
int n_signals,
|
||||
DUH_SIGTYPE_DESC *desc[],
|
||||
sigdata_t *sigdata[]
|
||||
);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* DUMB_H */
|
27
apps/codecs/dumb/include/internal/aldumb.h
Normal file
27
apps/codecs/dumb/include/internal/aldumb.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* internal/aldumb.h - The internal header file / / \ \
|
||||
* for DUMB with Allegro. | < / \_
|
||||
* | \/ /\ /
|
||||
* \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#ifndef INTERNAL_ALDUMB_H
|
||||
#define INTERNAL_ALDUMB_H
|
||||
|
||||
|
||||
void _dat_unload_duh(void *duh);
|
||||
|
||||
|
||||
#endif /* INTERNAL_DUMB_H */
|
58
apps/codecs/dumb/include/internal/dumb.h
Normal file
58
apps/codecs/dumb/include/internal/dumb.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* internal/dumb.h - DUMB's internal declarations. / / \ \
|
||||
* | < / \_
|
||||
* This header file provides access to the | \/ /\ /
|
||||
* internal structure of DUMB, and is liable \_ / > /
|
||||
* to change, mutate or cease to exist at any | \ / /
|
||||
* moment. Include it at your own peril. | ' /
|
||||
* \__/
|
||||
* ...
|
||||
*
|
||||
* I mean it, people. You don't need access to anything in this file. If you
|
||||
* disagree, contact the authors. In the unlikely event that you make a good
|
||||
* case, we'll add what you need to dumb.h. Thanking you kindly.
|
||||
*/
|
||||
|
||||
#ifndef INTERNAL_DUMB_H
|
||||
#define INTERNAL_DUMB_H
|
||||
|
||||
|
||||
typedef struct DUH_SIGTYPE_DESC_LINK
|
||||
{
|
||||
struct DUH_SIGTYPE_DESC_LINK *next;
|
||||
DUH_SIGTYPE_DESC *desc;
|
||||
}
|
||||
DUH_SIGTYPE_DESC_LINK;
|
||||
|
||||
|
||||
typedef struct DUH_SIGNAL
|
||||
{
|
||||
sigdata_t *sigdata;
|
||||
DUH_SIGTYPE_DESC *desc;
|
||||
}
|
||||
DUH_SIGNAL;
|
||||
|
||||
|
||||
struct DUH
|
||||
{
|
||||
long length;
|
||||
|
||||
int n_signals;
|
||||
DUH_SIGNAL **signal;
|
||||
};
|
||||
|
||||
|
||||
DUH_SIGTYPE_DESC *_dumb_get_sigtype_desc(long type);
|
||||
|
||||
|
||||
#endif /* INTERNAL_DUMB_H */
|
||||
|
710
apps/codecs/dumb/include/internal/it.h
Normal file
710
apps/codecs/dumb/include/internal/it.h
Normal file
|
@ -0,0 +1,710 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* internal/it.h - Internal stuff for IT playback / / \ \
|
||||
* and MOD/XM/S3M conversion. | < / \_
|
||||
* | \/ /\ /
|
||||
* \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#ifndef INTERNAL_IT_H
|
||||
#define INTERNAL_IT_H
|
||||
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
|
||||
/** TO DO: THINK ABOUT THE FOLLOWING:
|
||||
|
||||
sigdata->flags & IT_COMPATIBLE_GXX
|
||||
|
||||
Bit 5: On = Link Effect G's memory with Effect E/F. Also
|
||||
Gxx with an instrument present will cause the
|
||||
envelopes to be retriggered. If you change a
|
||||
sample on a row with Gxx, it'll adjust the
|
||||
frequency of the current note according to:
|
||||
|
||||
NewFrequency = OldFrequency * NewC5 / OldC5;
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* These #defines are TEMPORARY. They are used to write alternative code to
|
||||
* handle ambiguities in the format specification. The correct code in each
|
||||
* case will be determined most likely by experimentation.
|
||||
*/
|
||||
#define STEREO_SAMPLES_COUNT_AS_TWO
|
||||
#define INVALID_ORDERS_END_SONG
|
||||
#define INVALID_NOTES_CAUSE_NOTE_CUT
|
||||
#define SUSTAIN_LOOP_OVERRIDES_NORMAL_LOOP
|
||||
#define VOLUME_OUT_OF_RANGE_SETS_MAXIMUM
|
||||
|
||||
|
||||
|
||||
#define SIGTYPE_IT DUMB_ID('I', 'T', ' ', ' ')
|
||||
|
||||
#define IT_SIGNATURE DUMB_ID('I', 'M', 'P', 'M')
|
||||
#define IT_INSTRUMENT_SIGNATURE DUMB_ID('I', 'M', 'P', 'I')
|
||||
#define IT_SAMPLE_SIGNATURE DUMB_ID('I', 'M', 'P', 'S')
|
||||
|
||||
|
||||
|
||||
/* 1 minute per 4 rows, each row 6 ticks; this is divided by the tempo to get
|
||||
* the interval between ticks.
|
||||
*/
|
||||
#define TICK_TIME_DIVIDEND ((65536 * 60) / (4 * 6))
|
||||
|
||||
|
||||
|
||||
/* I'm not going to try to explain this, because I didn't derive it very
|
||||
* formally ;)
|
||||
*/
|
||||
/* #define AMIGA_DIVISOR ((float)(4.0 * 14317056.0)) */
|
||||
/* I believe the following one to be more accurate. */
|
||||
#define AMIGA_DIVISOR ((float)(8.0 * 7159090.5))
|
||||
|
||||
|
||||
|
||||
typedef struct IT_MIDI IT_MIDI;
|
||||
typedef struct IT_FILTER_STATE IT_FILTER_STATE;
|
||||
typedef struct IT_ENVELOPE IT_ENVELOPE;
|
||||
typedef struct IT_INSTRUMENT IT_INSTRUMENT;
|
||||
typedef struct IT_SAMPLE IT_SAMPLE;
|
||||
typedef struct IT_ENTRY IT_ENTRY;
|
||||
typedef struct IT_PATTERN IT_PATTERN;
|
||||
typedef struct IT_PLAYING_ENVELOPE IT_PLAYING_ENVELOPE;
|
||||
typedef struct IT_PLAYING IT_PLAYING;
|
||||
typedef struct IT_CHANNEL IT_CHANNEL;
|
||||
typedef struct IT_CHECKPOINT IT_CHECKPOINT;
|
||||
typedef struct IT_CALLBACKS IT_CALLBACKS;
|
||||
|
||||
|
||||
|
||||
struct IT_MIDI
|
||||
{
|
||||
unsigned char SFmacro[16][16]; // read these from 0x120
|
||||
unsigned char SFmacrolen[16];
|
||||
unsigned short SFmacroz[16]; /* Bitfield; bit 0 set = z in first position */
|
||||
unsigned char Zmacro[128][16]; // read these from 0x320
|
||||
unsigned char Zmacrolen[128];
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct IT_FILTER_STATE
|
||||
{
|
||||
float currsample, prevsample;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define IT_ENVELOPE_ON 1
|
||||
#define IT_ENVELOPE_LOOP_ON 2
|
||||
#define IT_ENVELOPE_SUSTAIN_LOOP 4
|
||||
#define IT_ENVELOPE_PITCH_IS_FILTER 128
|
||||
|
||||
struct IT_ENVELOPE
|
||||
{
|
||||
unsigned char flags;
|
||||
unsigned char n_nodes;
|
||||
unsigned char loop_start;
|
||||
unsigned char loop_end;
|
||||
unsigned char sus_loop_start;
|
||||
unsigned char sus_loop_end;
|
||||
signed char node_y[25];
|
||||
unsigned short node_t[25];
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define NNA_NOTE_CUT 0
|
||||
#define NNA_NOTE_CONTINUE 1
|
||||
#define NNA_NOTE_OFF 2
|
||||
#define NNA_NOTE_FADE 3
|
||||
|
||||
#define DCT_OFF 0
|
||||
#define DCT_NOTE 1
|
||||
#define DCT_SAMPLE 2
|
||||
#define DCT_INSTRUMENT 3
|
||||
|
||||
#define DCA_NOTE_CUT 0
|
||||
#define DCA_NOTE_OFF 1
|
||||
#define DCA_NOTE_FADE 2
|
||||
|
||||
struct IT_INSTRUMENT
|
||||
{
|
||||
int fadeout;
|
||||
|
||||
IT_ENVELOPE volume_envelope;
|
||||
IT_ENVELOPE pan_envelope;
|
||||
IT_ENVELOPE pitch_envelope;
|
||||
|
||||
unsigned char new_note_action;
|
||||
unsigned char dup_check_type;
|
||||
unsigned char dup_check_action;
|
||||
unsigned char pp_separation;
|
||||
unsigned char pp_centre;
|
||||
unsigned char global_volume;
|
||||
unsigned char default_pan;
|
||||
unsigned char random_volume;
|
||||
unsigned char random_pan;
|
||||
|
||||
unsigned char filter_cutoff;
|
||||
unsigned char filter_resonance;
|
||||
|
||||
unsigned char map_note[120];
|
||||
unsigned short map_sample[120];
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define IT_SAMPLE_EXISTS 1
|
||||
#define IT_SAMPLE_16BIT 2
|
||||
#define IT_SAMPLE_STEREO 4
|
||||
#define IT_SAMPLE_LOOP 16
|
||||
#define IT_SAMPLE_SUS_LOOP 32
|
||||
#define IT_SAMPLE_PINGPONG_LOOP 64
|
||||
#define IT_SAMPLE_PINGPONG_SUS_LOOP 128
|
||||
|
||||
#define IT_VIBRATO_SINE 0
|
||||
#define IT_VIBRATO_SAWTOOTH 1 /* Ramp down */
|
||||
#define IT_VIBRATO_SQUARE 2
|
||||
#define IT_VIBRATO_RANDOM 3
|
||||
|
||||
struct IT_SAMPLE
|
||||
{
|
||||
unsigned char flags;
|
||||
unsigned char global_volume;
|
||||
unsigned char default_volume;
|
||||
unsigned char default_pan;
|
||||
|
||||
long length;
|
||||
long loop_start;
|
||||
long loop_end;
|
||||
long C5_speed;
|
||||
long sus_loop_start;
|
||||
long sus_loop_end;
|
||||
|
||||
unsigned char vibrato_speed;
|
||||
unsigned char vibrato_depth;
|
||||
unsigned char vibrato_rate;
|
||||
unsigned char vibrato_waveform;
|
||||
|
||||
sample_t *left;
|
||||
sample_t *right;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define IT_ENTRY_NOTE 1
|
||||
#define IT_ENTRY_INSTRUMENT 2
|
||||
#define IT_ENTRY_VOLPAN 4
|
||||
#define IT_ENTRY_EFFECT 8
|
||||
|
||||
#define IT_SET_END_ROW(entry) ((entry)->channel = 255)
|
||||
#define IT_IS_END_ROW(entry) ((entry)->channel >= DUMB_IT_N_CHANNELS)
|
||||
|
||||
#define IT_NOTE_OFF 255
|
||||
#define IT_NOTE_CUT 254
|
||||
|
||||
#define IT_ENVELOPE_SHIFT 8
|
||||
|
||||
#define IT_SURROUND 100
|
||||
#define IT_IS_SURROUND(pan) ((pan) > 64)
|
||||
#define IT_IS_SURROUND_SHIFTED(pan) ((pan) > 64 << IT_ENVELOPE_SHIFT)
|
||||
|
||||
#define IT_SET_SPEED 1
|
||||
#define IT_JUMP_TO_ORDER 2
|
||||
#define IT_BREAK_TO_ROW 3
|
||||
#define IT_VOLUME_SLIDE 4
|
||||
#define IT_PORTAMENTO_DOWN 5
|
||||
#define IT_PORTAMENTO_UP 6
|
||||
#define IT_TONE_PORTAMENTO 7
|
||||
#define IT_VIBRATO 8
|
||||
#define IT_TREMOR 9
|
||||
#define IT_ARPEGGIO 10
|
||||
#define IT_VOLSLIDE_VIBRATO 11
|
||||
#define IT_VOLSLIDE_TONEPORTA 12
|
||||
#define IT_SET_CHANNEL_VOLUME 13
|
||||
#define IT_CHANNEL_VOLUME_SLIDE 14
|
||||
#define IT_SET_SAMPLE_OFFSET 15
|
||||
#define IT_PANNING_SLIDE 16
|
||||
#define IT_RETRIGGER_NOTE 17
|
||||
#define IT_TREMOLO 18
|
||||
#define IT_S 19
|
||||
#define IT_SET_SONG_TEMPO 20
|
||||
#define IT_FINE_VIBRATO 21
|
||||
#define IT_SET_GLOBAL_VOLUME 22
|
||||
#define IT_GLOBAL_VOLUME_SLIDE 23
|
||||
#define IT_SET_PANNING 24
|
||||
#define IT_PANBRELLO 25
|
||||
#define IT_MIDI_MACRO 26 //see MIDI.TXT
|
||||
|
||||
/* Some effects needed for XM compatibility */
|
||||
#define IT_XM_PORTAMENTO_DOWN 27
|
||||
#define IT_XM_PORTAMENTO_UP 28
|
||||
#define IT_XM_FINE_VOLSLIDE_DOWN 29
|
||||
#define IT_XM_FINE_VOLSLIDE_UP 30
|
||||
#define IT_XM_RETRIGGER_NOTE 31
|
||||
|
||||
#define IT_N_EFFECTS 32
|
||||
|
||||
/* These represent the top nibble of the command value. */
|
||||
#define IT_S_SET_FILTER 0 /* Greyed out in IT... */
|
||||
#define IT_S_SET_GLISSANDO_CONTROL 1 /* Greyed out in IT... */
|
||||
#define IT_S_FINETUNE 2 /* Greyed out in IT... */
|
||||
#define IT_S_SET_VIBRATO_WAVEFORM 3
|
||||
#define IT_S_SET_TREMOLO_WAVEFORM 4
|
||||
#define IT_S_SET_PANBRELLO_WAVEFORM 5
|
||||
#define IT_S_FINE_PATTERN_DELAY 6
|
||||
#define IT_S7 7
|
||||
#define IT_S_SET_PAN 8
|
||||
#define IT_S_SET_SURROUND_SOUND 9
|
||||
#define IT_S_SET_HIGH_OFFSET 10
|
||||
#define IT_S_PATTERN_LOOP 11
|
||||
#define IT_S_DELAYED_NOTE_CUT 12
|
||||
#define IT_S_NOTE_DELAY 13
|
||||
#define IT_S_PATTERN_DELAY 14
|
||||
#define IT_S_SET_MIDI_MACRO 15
|
||||
|
||||
/*
|
||||
S0x Set filter
|
||||
S1x Set glissando control
|
||||
S2x Set finetune
|
||||
|
||||
|
||||
S3x Set vibrato waveform to type x
|
||||
S4x Set tremelo waveform to type x
|
||||
S5x Set panbrello waveform to type x
|
||||
Waveforms for commands S3x, S4x and S5x:
|
||||
0: Sine wave
|
||||
1: Ramp down
|
||||
2: Square wave
|
||||
3: Random wave
|
||||
S6x Pattern delay for x ticks
|
||||
S70 Past note cut
|
||||
S71 Past note off
|
||||
S72 Past note fade
|
||||
S73 Set NNA to note cut
|
||||
S74 Set NNA to continue
|
||||
S75 Set NNA to note off
|
||||
S76 Set NNA to note fade
|
||||
S77 Turn off volume envelope
|
||||
S78 Turn on volume envelope
|
||||
S79 Turn off panning envelope
|
||||
S7A Turn on panning envelope
|
||||
S7B Turn off pitch envelope
|
||||
S7C Turn on pitch envelope
|
||||
S8x Set panning position
|
||||
S91 Set surround sound
|
||||
SAy Set high value of sample offset yxx00h
|
||||
SB0 Set loopback point
|
||||
SBx Loop x times to loopback point
|
||||
SCx Note cut after x ticks
|
||||
SDx Note delay for x ticks
|
||||
SEx Pattern delay for x rows
|
||||
SFx Set parameterised MIDI Macro
|
||||
*/
|
||||
|
||||
struct IT_ENTRY
|
||||
{
|
||||
unsigned char channel; /* End of row if channel >= DUMB_IT_N_CHANNELS */
|
||||
unsigned char mask;
|
||||
unsigned char note;
|
||||
unsigned char instrument;
|
||||
unsigned char volpan;
|
||||
unsigned char effect;
|
||||
unsigned char effectvalue;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct IT_PATTERN
|
||||
{
|
||||
int n_rows;
|
||||
int n_entries;
|
||||
IT_ENTRY *entry;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define IT_STEREO 1
|
||||
#define IT_USE_INSTRUMENTS 4
|
||||
#define IT_LINEAR_SLIDES 8 /* If not set, use Amiga slides */
|
||||
#define IT_OLD_EFFECTS 16
|
||||
#define IT_COMPATIBLE_GXX 32
|
||||
|
||||
/* Make sure IT_WAS_AN_XM and IT_WAS_A_MOD aren't set accidentally */
|
||||
#define IT_REAL_FLAGS 63
|
||||
|
||||
#define IT_WAS_AN_XM 64 /* Set for both XMs and MODs */
|
||||
#define IT_WAS_A_MOD 128
|
||||
|
||||
#define IT_ORDER_END 255
|
||||
#define IT_ORDER_SKIP 254
|
||||
|
||||
struct DUMB_IT_SIGDATA
|
||||
{
|
||||
int n_orders;
|
||||
int n_instruments;
|
||||
int n_samples;
|
||||
int n_patterns;
|
||||
|
||||
int flags;
|
||||
|
||||
int global_volume;
|
||||
int mixing_volume;
|
||||
int speed;
|
||||
int tempo;
|
||||
int pan_separation;
|
||||
|
||||
unsigned char channel_pan[DUMB_IT_N_CHANNELS];
|
||||
unsigned char channel_volume[DUMB_IT_N_CHANNELS];
|
||||
|
||||
unsigned char *order;
|
||||
unsigned char restart_position; /* for XM compatiblity */
|
||||
|
||||
IT_INSTRUMENT *instrument;
|
||||
IT_SAMPLE *sample;
|
||||
IT_PATTERN *pattern;
|
||||
|
||||
IT_MIDI *midi;
|
||||
|
||||
IT_CHECKPOINT *checkpoint;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct IT_PLAYING_ENVELOPE
|
||||
{
|
||||
int next_node;
|
||||
int tick;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define IT_PLAYING_BACKGROUND 1
|
||||
#define IT_PLAYING_SUSTAINOFF 2
|
||||
#define IT_PLAYING_FADING 4
|
||||
#define IT_PLAYING_DEAD 8
|
||||
|
||||
struct IT_PLAYING
|
||||
{
|
||||
int flags;
|
||||
|
||||
IT_CHANNEL *channel;
|
||||
IT_SAMPLE *sample;
|
||||
IT_INSTRUMENT *instrument;
|
||||
IT_INSTRUMENT *env_instrument;
|
||||
|
||||
unsigned short sampnum;
|
||||
unsigned char instnum;
|
||||
|
||||
unsigned char channel_volume;
|
||||
|
||||
unsigned char volume;
|
||||
unsigned short pan;
|
||||
|
||||
unsigned char note;
|
||||
|
||||
unsigned char filter_cutoff;
|
||||
unsigned char filter_resonance;
|
||||
|
||||
unsigned short true_filter_cutoff; /* These incorporate the filter envelope, and will not */
|
||||
unsigned char true_filter_resonance; /* be changed if they would be set to 127<<8 and 0. */
|
||||
|
||||
unsigned char vibrato_speed;
|
||||
unsigned char vibrato_depth;
|
||||
unsigned char vibrato_n; /* May be specified twice: volpan & effect. */
|
||||
unsigned char vibrato_time;
|
||||
|
||||
unsigned char tremolo_speed;
|
||||
unsigned char tremolo_depth;
|
||||
unsigned char tremolo_time;
|
||||
|
||||
unsigned char sample_vibrato_time;
|
||||
int sample_vibrato_depth; /* Starts at rate?0:depth, increases by rate */
|
||||
|
||||
int slide;
|
||||
float delta;
|
||||
|
||||
IT_PLAYING_ENVELOPE volume_envelope;
|
||||
IT_PLAYING_ENVELOPE pan_envelope;
|
||||
IT_PLAYING_ENVELOPE pitch_envelope;
|
||||
|
||||
int fadeoutcount;
|
||||
|
||||
IT_FILTER_STATE filter_state[2]; /* Left and right */
|
||||
|
||||
DUMB_RESAMPLER resampler[2];
|
||||
|
||||
/* time_lost is used to emulate Impulse Tracker's sample looping
|
||||
* characteristics. When time_lost is added to pos, the result represents
|
||||
* the position in the theoretical version of the sample where all loops
|
||||
* have been expanded. If this is stored, the resampling helpers will
|
||||
* safely convert it for use with new loop boundaries. The situation is
|
||||
* slightly more complicated if dir == -1 when the change takes place; we
|
||||
* must reflect pos off the loop end point and set dir to 1 before
|
||||
* proceeding.
|
||||
*/
|
||||
long time_lost;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define IT_CHANNEL_MUTED 1
|
||||
|
||||
struct IT_CHANNEL
|
||||
{
|
||||
int flags;
|
||||
|
||||
unsigned char volume;
|
||||
signed char volslide;
|
||||
signed char xm_volslide;
|
||||
|
||||
/* xm_volslide is used for volume slides done in the volume column in an
|
||||
* XM file, since it seems the volume column slide is applied first,
|
||||
* followed by clamping, followed by the effects column slide. IT does
|
||||
* not exhibit this behaviour, so xm_volslide is maintained at zero.
|
||||
*/
|
||||
|
||||
unsigned char pan;
|
||||
unsigned short truepan;
|
||||
|
||||
unsigned char channelvolume;
|
||||
signed char channelvolslide;
|
||||
|
||||
unsigned char instrument;
|
||||
unsigned char note;
|
||||
|
||||
unsigned char SFmacro;
|
||||
|
||||
unsigned char filter_cutoff;
|
||||
unsigned char filter_resonance;
|
||||
|
||||
unsigned char note_cut_count;
|
||||
unsigned char note_delay_count;
|
||||
IT_ENTRY *note_delay_entry;
|
||||
|
||||
int arpeggio;
|
||||
unsigned char retrig;
|
||||
unsigned char xm_retrig;
|
||||
int retrig_tick;
|
||||
|
||||
unsigned char tremor;
|
||||
unsigned char tremor_time; /* Bit 6 set if note on; bit 7 set if tremor active. */
|
||||
|
||||
int portamento;
|
||||
int toneporta;
|
||||
unsigned char destnote;
|
||||
|
||||
/** WARNING - for neatness, should one or both of these be in the IT_PLAYING struct? */
|
||||
unsigned short sample;
|
||||
unsigned char truenote;
|
||||
|
||||
unsigned char midi_state;
|
||||
|
||||
signed char lastvolslide;
|
||||
unsigned char lastDKL;
|
||||
unsigned char lastEF; /* Doubles as last portamento up for XM files */
|
||||
unsigned char lastG;
|
||||
unsigned char lastHspeed;
|
||||
unsigned char lastHdepth;
|
||||
unsigned char lastRspeed;
|
||||
unsigned char lastRdepth;
|
||||
unsigned char lastI;
|
||||
unsigned char lastJ; /* Doubles as last portamento down for XM files */
|
||||
unsigned char lastN;
|
||||
unsigned char lastO;
|
||||
unsigned char high_offset;
|
||||
unsigned char lastQ;
|
||||
unsigned char lastS;
|
||||
unsigned char pat_loop_row;
|
||||
unsigned char pat_loop_count;
|
||||
unsigned char lastW;
|
||||
|
||||
unsigned char xm_lastE1;
|
||||
unsigned char xm_lastE2;
|
||||
unsigned char xm_lastEA;
|
||||
unsigned char xm_lastEB;
|
||||
unsigned char xm_lastX1;
|
||||
unsigned char xm_lastX2;
|
||||
|
||||
IT_PLAYING *playing;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct DUMB_IT_SIGRENDERER
|
||||
{
|
||||
DUMB_IT_SIGDATA *sigdata;
|
||||
|
||||
int n_channels;
|
||||
|
||||
unsigned char globalvolume;
|
||||
signed char globalvolslide;
|
||||
|
||||
unsigned char tempo;
|
||||
signed char temposlide;
|
||||
|
||||
IT_CHANNEL channel[DUMB_IT_N_CHANNELS];
|
||||
|
||||
IT_PLAYING *playing[DUMB_IT_N_NNA_CHANNELS];
|
||||
|
||||
int tick;
|
||||
int speed;
|
||||
int rowcount;
|
||||
|
||||
int order; /* Set to -1 if the song is terminated by a callback. */
|
||||
int row;
|
||||
int processorder;
|
||||
int processrow;
|
||||
int breakrow;
|
||||
int pat_loop_row;
|
||||
|
||||
int n_rows;
|
||||
|
||||
IT_ENTRY *entry_start;
|
||||
IT_ENTRY *entry;
|
||||
IT_ENTRY *entry_end;
|
||||
|
||||
long time_left; /* Time before the next tick is processed */
|
||||
int sub_time_left;
|
||||
|
||||
DUMB_CLICK_REMOVER **click_remover;
|
||||
|
||||
IT_CALLBACKS *callbacks;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct IT_CHECKPOINT
|
||||
{
|
||||
IT_CHECKPOINT *next;
|
||||
long time;
|
||||
DUMB_IT_SIGRENDERER *sigrenderer;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct IT_CALLBACKS
|
||||
{
|
||||
int (*loop)(void *data);
|
||||
void *loop_data;
|
||||
/* Return 1 to prevent looping; the music will terminate abruptly. If you
|
||||
* want to make the music stop but allow samples to fade (beware, as they
|
||||
* might not fade at all!), use dumb_it_sr_set_speed() and set the speed
|
||||
* to 0. Note that xm_speed_zero() will not be called if you set the
|
||||
* speed manually, and also that this will work for IT and S3M files even
|
||||
* though the music can't stop in this way by itself.
|
||||
*/
|
||||
|
||||
int (*xm_speed_zero)(void *data);
|
||||
void *xm_speed_zero_data;
|
||||
/* Return 1 to terminate the mod, without letting samples fade. */
|
||||
|
||||
int (*midi)(void *data, int channel, unsigned char byte);
|
||||
void *midi_data;
|
||||
/* Return 1 to prevent DUMB from subsequently interpreting the MIDI bytes
|
||||
* itself. In other words, return 1 if the Zxx macros in an IT file are
|
||||
* controlling filters and shouldn't be.
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
|
||||
void _dumb_it_end_sigrenderer(sigrenderer_t *sigrenderer);
|
||||
void _dumb_it_unload_sigdata(sigdata_t *vsigdata);
|
||||
|
||||
extern DUH_SIGTYPE_DESC _dumb_sigtype_it;
|
||||
|
||||
|
||||
|
||||
long _dumb_it_build_checkpoints(DUMB_IT_SIGDATA *sigdata);
|
||||
|
||||
|
||||
|
||||
#define XM_APPREGIO 0
|
||||
#define XM_PORTAMENTO_UP 1
|
||||
#define XM_PORTAMENTO_DOWN 2
|
||||
#define XM_TONE_PORTAMENTO 3
|
||||
#define XM_VIBRATO 4
|
||||
#define XM_VOLSLIDE_TONEPORTA 5
|
||||
#define XM_VOLSLIDE_VIBRATO 6
|
||||
#define XM_TREMOLO 7
|
||||
#define XM_SET_PANNING 8
|
||||
#define XM_SAMPLE_OFFSET 9
|
||||
#define XM_VOLUME_SLIDE 10 /* A */
|
||||
#define XM_POSITION_JUMP 11 /* B */
|
||||
#define XM_SET_CHANNEL_VOLUME 12 /* C */
|
||||
#define XM_PATTERN_BREAK 13 /* D */
|
||||
#define XM_E 14 /* E */
|
||||
#define XM_SET_TEMPO_BPM 15 /* F */
|
||||
#define XM_SET_GLOBAL_VOLUME 16 /* G */
|
||||
#define XM_GLOBAL_VOLUME_SLIDE 17 /* H */
|
||||
#define XM_KEY_OFF 20 /* K (undocumented) */
|
||||
#define XM_SET_ENVELOPE_POSITION 21 /* L */
|
||||
#define XM_PANNING_SLIDE 25 /* P */
|
||||
#define XM_MULTI_RETRIG 27 /* R */
|
||||
#define XM_TREMOR 29 /* T */
|
||||
#define XM_X 33 /* X */
|
||||
#define XM_N_EFFECTS (10+26)
|
||||
|
||||
#define XM_E_SET_FILTER 0x0
|
||||
#define XM_E_FINE_PORTA_UP 0x1
|
||||
#define XM_E_FINE_PORTA_DOWN 0x2
|
||||
#define XM_E_SET_GLISSANDO_CONTROL 0x3
|
||||
#define XM_E_SET_VIBRATO_CONTROL 0x4
|
||||
#define XM_E_SET_FINETUNE 0x5
|
||||
#define XM_E_SET_LOOP 0x6
|
||||
#define XM_E_SET_TREMOLO_CONTROL 0x7
|
||||
#define XM_E_RETRIG_NOTE 0x9
|
||||
#define XM_E_FINE_VOLSLIDE_UP 0xA
|
||||
#define XM_E_FINE_VOLSLIDE_DOWN 0xB
|
||||
#define XM_E_NOTE_CUT 0xC
|
||||
#define XM_E_NOTE_DELAY 0xD
|
||||
#define XM_E_PATTERN_DELAY 0xE
|
||||
|
||||
#define XM_X_EXTRAFINE_PORTA_UP 1
|
||||
#define XM_X_EXTRAFINE_PORTA_DOWN 2
|
||||
|
||||
/* To make my life a bit simpler during conversion, effect E:xy is converted
|
||||
* to effect number EBASE+x:y. The same applies to effect X, and IT's S. That
|
||||
* way, these effects can be manipulated like regular effects.
|
||||
*/
|
||||
#define EBASE (XM_N_EFFECTS)
|
||||
#define XBASE (EBASE+16)
|
||||
#define SBASE (IT_N_EFFECTS)
|
||||
|
||||
#define EFFECT_VALUE(x, y) (((x)<<4)|(y))
|
||||
#define HIGH(v) ((v)>>4)
|
||||
#define LOW(v) ((v)&0x0F)
|
||||
#define SET_HIGH(v, x) v = (((x)<<4)|((v)&0x0F))
|
||||
#define SET_LOW(v, y) v = (((v)&0xF0)|(y))
|
||||
#define BCD_TO_NORMAL(v) (HIGH(v)*10+LOW(v))
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
unsigned char **_dumb_malloc2(int w, int h);
|
||||
void _dumb_free2(unsigned char **line);
|
||||
#endif
|
||||
|
||||
void _dumb_it_xm_convert_effect(int effect, int value, IT_ENTRY *entry);
|
||||
int _dumb_it_fix_invalid_orders(DUMB_IT_SIGDATA *sigdata);
|
||||
|
||||
|
||||
|
||||
#endif /* INTERNAL_IT_H */
|
54
apps/codecs/dumb/licence.txt
Normal file
54
apps/codecs/dumb/licence.txt
Normal file
|
@ -0,0 +1,54 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* licence.txt - Conditions for use of DUMB. / / \ \
|
||||
* | < / \_
|
||||
* If you do not agree to these terms, please | \/ /\ /
|
||||
* do not use DUMB. \_ / > /
|
||||
* | \ / /
|
||||
* Information in [brackets] is provided to aid | ' /
|
||||
* interpretation of the licence. \__/
|
||||
*/
|
||||
|
||||
|
||||
Dynamic Universal Music Bibliotheque
|
||||
|
||||
Copyright (C) 2001-2003 Ben Davis, Robert J Ohannessian and Julien Cugniere
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty.
|
||||
In no event shall the authors be held liable for any damages arising from the
|
||||
use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not claim
|
||||
that you wrote the original software. If you use this software in a
|
||||
product, you are requested to acknowledge its use in the product
|
||||
documentation, along with details on where to get an unmodified version of
|
||||
this software, but this is not a strict requirement.
|
||||
|
||||
[Note that the above point asks for a link to DUMB, not just a mention.
|
||||
Googling for DUMB doesn't help much! The URL is "http://dumb.sf.net/".]
|
||||
|
||||
[The only reason why the link is not strictly required is that such a
|
||||
requirement prevents DUMB from being used in projects with certain other
|
||||
licences, notably the GPL. See http://www.gnu.org/philosophy/bsd.html .]
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed from or altered in any source distribution.
|
||||
|
||||
4. If you are using the Program in someone else's bedroom at any Monday
|
||||
3:05 PM, you are not allowed to modify the Program for ten minutes. [This
|
||||
clause provided by Inphernic; every licence should contain at least one
|
||||
clause, the reasoning behind which is far from obvious.]
|
34
apps/codecs/dumb/make/Makefile.inc
Normal file
34
apps/codecs/dumb/make/Makefile.inc
Normal file
|
@ -0,0 +1,34 @@
|
|||
# This file contains the main rules for compiling the library. It is included
|
||||
# twice with different values for CFLAGS and OBJDIR, so the optimised and
|
||||
# debugging libraries are both built.
|
||||
|
||||
CORE_OBJECTS := $(addprefix $(OBJDIR)/, $(notdir $(patsubst %.c, %.o, $(CORE_MODULES))))
|
||||
ALLEGRO_OBJECTS := $(addprefix $(OBJDIR)/, $(notdir $(patsubst %.c, %.o, $(ALLEGRO_MODULES))))
|
||||
|
||||
|
||||
# Pass the current value of CFLAGS through to the commands. Or, more
|
||||
# accurately, create a local copy of the current CFLAGS variable. This is
|
||||
# necessary because Make doesn't expand variables in commands until they are
|
||||
# executed.
|
||||
$(CORE_LIB_FILE): CFLAGS := $(CFLAGS)
|
||||
$(ALLEGRO_LIB_FILE): CFLAGS := $(CFLAGS)
|
||||
|
||||
|
||||
$(OBJDIR)/%.o: src/core/%.c include/dumb.h include/internal/dumb.h
|
||||
$(CC) -c -o $@ $< $(CFLAGS)
|
||||
|
||||
$(OBJDIR)/%.o: src/helpers/%.c include/dumb.h
|
||||
$(CC) -c -o $@ $< $(CFLAGS)
|
||||
|
||||
$(OBJDIR)/%.o: src/it/%.c include/dumb.h include/internal/it.h
|
||||
$(CC) -c -o $@ $< $(CFLAGS)
|
||||
|
||||
$(OBJDIR)/%.o: src/allegro/%.c include/aldumb.h include/dumb.h \
|
||||
include/internal/aldumb.h include/internal/dumb.h
|
||||
$(CC) -c -o $@ $< $(CFLAGS) $(WFLAGS_ALLEGRO)
|
||||
|
||||
$(CORE_LIB_FILE): $(CORE_OBJECTS)
|
||||
$(AR) rs $@ $^
|
||||
|
||||
$(ALLEGRO_LIB_FILE): $(ALLEGRO_OBJECTS)
|
||||
$(AR) rs $@ $^
|
33
apps/codecs/dumb/make/config.bat
Normal file
33
apps/codecs/dumb/make/config.bat
Normal file
|
@ -0,0 +1,33 @@
|
|||
@ECHO OFF
|
||||
|
||||
REM This file does an interactive configuration for users of DOS and Windows.
|
||||
REM It creates a config.txt file for inclusion in the Makefile. This batch
|
||||
REM file should be run indirectly through the 'make config' target (or the
|
||||
REM 'make' target the first time).
|
||||
|
||||
IF EXIST make\dumbask.exe GOTO dumbaskok
|
||||
ECHO You should not be running this directly! Use 'make' or 'make config'.
|
||||
GOTO end
|
||||
:dumbaskok
|
||||
|
||||
make\dumbask.exe "Would you like to compile DUMB for DJGPP or MinGW (D/M)? " DM
|
||||
IF ERRORLEVEL 1 GOTO mingw
|
||||
ECHO include make/djgpp.inc>make\config.tmp
|
||||
GOTO djgpp
|
||||
:mingw
|
||||
ECHO include make/mingw.inc>make\config.tmp
|
||||
:djgpp
|
||||
|
||||
ECHO ALL_TARGETS := core core-examples core-headers>>make\config.tmp
|
||||
|
||||
make\dumbask.exe "Would you like support for Allegro (Y/N)? "
|
||||
IF NOT ERRORLEVEL 1 ECHO ALL_TARGETS += allegro allegro-examples allegro-headers>>make\config.tmp
|
||||
|
||||
IF EXIST make\config.txt DEL make\config.txt
|
||||
REN make\config.tmp config.txt
|
||||
|
||||
ECHO Configuration complete.
|
||||
ECHO Run 'make config' to change it in the future.
|
||||
PAUSE
|
||||
|
||||
:end
|
35
apps/codecs/dumb/make/config.sh
Executable file
35
apps/codecs/dumb/make/config.sh
Executable file
|
@ -0,0 +1,35 @@
|
|||
#!/bin/sh
|
||||
|
||||
# This file does an interactive configuration for users of Unix-like systems.
|
||||
# It creates a config.txt file for inclusion in the Makefile. This script
|
||||
# should be run indirectly through the 'make config' target (or the 'make'
|
||||
# target the first time).
|
||||
|
||||
if [ ! -e make/dumbask ]; then
|
||||
echo "You should not be running this directly! Use 'make' or 'make config'."
|
||||
exit
|
||||
fi
|
||||
|
||||
echo 'include make/unix.inc' > make/config.tmp
|
||||
|
||||
echo 'ALL_TARGETS := core core-examples core-headers' >> make/config.tmp
|
||||
|
||||
if make/dumbask 'Would you like support for Allegro (Y/N)? ' YN; then
|
||||
echo 'ALL_TARGETS += allegro allegro-examples allegro-headers' >> make/config.tmp
|
||||
fi
|
||||
|
||||
|
||||
if [ ! -z $DEFAULT_PREFIX ]; then
|
||||
echo "Please specify an installation prefix (default $DEFAULT_PREFIX)."
|
||||
echo -n '> '
|
||||
read PREFIX
|
||||
if [ -z $PREFIX ]; then PREFIX=$DEFAULT_PREFIX; fi
|
||||
echo "PREFIX := $PREFIX" >> make/config.tmp
|
||||
fi
|
||||
|
||||
mv -f make/config.tmp make/config.txt
|
||||
|
||||
echo 'Configuration complete.'
|
||||
echo "Run 'make config' to change it in the future."
|
||||
echo -n 'Press Enter to continue ... '
|
||||
read
|
3
apps/codecs/dumb/make/config.txt
Normal file
3
apps/codecs/dumb/make/config.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
include make/unix.inc
|
||||
ALL_TARGETS := core
|
||||
PREFIX := /usr
|
28
apps/codecs/dumb/make/djgpp.inc
Normal file
28
apps/codecs/dumb/make/djgpp.inc
Normal file
|
@ -0,0 +1,28 @@
|
|||
# This file contains DJGPP-specific definitions. It will be included by the
|
||||
# main Makefile when you compile with DJGPP.
|
||||
|
||||
PLATFORM := djgpp
|
||||
|
||||
APOST := \'
|
||||
|
||||
# Macro for replacing / with \ where necessary. Usage: $(call FIX,path)
|
||||
FIX = $(subst /,\,$(subst /*,\\\*,$(1)))
|
||||
|
||||
ECHO = @$(COMSPEC) /C ECHO $(1)
|
||||
# Note: the following two macros only work for single files!
|
||||
DELETE = $(COMSPEC) /C DEL $(call FIX,$(1))
|
||||
COPY = $(COMSPEC) /C COPY $(call FIX,$(1)) $(call FIX,$(2))
|
||||
|
||||
EXE_SUFFIX := .exe
|
||||
|
||||
LINK_MATH :=
|
||||
LINK_ALLEGRO := -lalleg
|
||||
|
||||
ifndef DJDIR
|
||||
.PHONY: error
|
||||
error:
|
||||
$(call ECHO,Your DJDIR environment variable is not set!)
|
||||
$(call ECHO,Please refer to DJGPP's documentation and install it properly.)
|
||||
endif
|
||||
|
||||
PREFIX := $(DJDIR)
|
BIN
apps/codecs/dumb/make/dumbask
Executable file
BIN
apps/codecs/dumb/make/dumbask
Executable file
Binary file not shown.
30
apps/codecs/dumb/make/dumbask.c
Normal file
30
apps/codecs/dumb/make/dumbask.c
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
int main(int argc, const char *const argv[])
|
||||
{
|
||||
const char *message = argv[1];
|
||||
const char *options;
|
||||
|
||||
if (!message) {
|
||||
fprintf(stderr,
|
||||
"dumbask: asks the user a question.\n"
|
||||
"Specify a message as the first argument (quoted!).\n"
|
||||
"You may optionally specify the choices as the second argument.\n"
|
||||
"Default choices are YN. Exit code is 0 for first, 1 for second, etc.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
options = argv[2] ? : "YN"; /* I _had_ to use a GNU Extension _somewhere_! */
|
||||
|
||||
printf("%s", argv[1]);
|
||||
|
||||
for (;;) {
|
||||
char c = toupper(getchar());
|
||||
int i;
|
||||
for (i = 0; options[i]; i++)
|
||||
if (c == toupper(options[i]))
|
||||
return i;
|
||||
}
|
||||
}
|
28
apps/codecs/dumb/make/mingw.inc
Normal file
28
apps/codecs/dumb/make/mingw.inc
Normal file
|
@ -0,0 +1,28 @@
|
|||
# This file contains MinGW-specific definitions. It will be included by the
|
||||
# main Makefile when you compile with MinGW.
|
||||
|
||||
PLATFORM := mingw
|
||||
|
||||
APOST := \'
|
||||
|
||||
# Macro for replacing / with \ where necessary. Usage: $(call FIX,path)
|
||||
FIX = $(subst /,\,$(subst /*,\\\*,$(1)))
|
||||
|
||||
ECHO = @$(COMSPEC) /C ECHO $(1)
|
||||
# Note: the following two macros only work for single files!
|
||||
DELETE = $(COMSPEC) /C DEL $(call FIX,$(1))
|
||||
COPY = $(COMSPEC) /C COPY $(call FIX,$(1)) $(call FIX,$(2))
|
||||
|
||||
EXE_SUFFIX := .exe
|
||||
|
||||
LINK_MATH :=
|
||||
LINK_ALLEGRO := -lalleg
|
||||
|
||||
ifndef MINGDIR
|
||||
.PHONY: error
|
||||
error:
|
||||
$(call ECHO,Your MINGDIR environment variable is not set!)
|
||||
$(call ECHO,Please set it to point to the directory containing your MinGW installation.)
|
||||
endif
|
||||
|
||||
PREFIX := $(MINGDIR)
|
20
apps/codecs/dumb/make/unix.inc
Normal file
20
apps/codecs/dumb/make/unix.inc
Normal file
|
@ -0,0 +1,20 @@
|
|||
# This file contains definitions suitable for Unix-compatible systems. It will
|
||||
# be included by the main Makefile when you compile on such a system.
|
||||
|
||||
PLATFORM := unix
|
||||
|
||||
APOST := \'
|
||||
|
||||
# Macro that on DOS and Windows would replace / with \. Usage: $(call FIX,path)
|
||||
FIX = $(1)
|
||||
|
||||
ECHO = @echo $(1)
|
||||
DELETE = rm -f $(1)
|
||||
COPY = cp $(1) $(2)
|
||||
|
||||
EXE_SUFFIX :=
|
||||
|
||||
LINK_MATH := -lm
|
||||
LINK_ALLEGRO := `allegro-config --libs`
|
||||
|
||||
# PREFIX is set by config.sh.
|
421
apps/codecs/dumb/readme.txt
Normal file
421
apps/codecs/dumb/readme.txt
Normal file
|
@ -0,0 +1,421 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* readme.txt - General information on DUMB. / / \ \
|
||||
* | < / \_
|
||||
* | \/ /\ /
|
||||
* \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
|
||||
********************
|
||||
*** Introduction ***
|
||||
********************
|
||||
|
||||
|
||||
Thank you for downloading DUMB! You should have the following documentation:
|
||||
|
||||
readme.txt - This file
|
||||
licence.txt - Conditions for the use of this software
|
||||
release.txt - Release notes and changes for this and past releases
|
||||
docs/
|
||||
howto.txt - Step-by-step instructions on adding DUMB to your project
|
||||
faq.txt - Frequently asked questions and answers to them
|
||||
dumb.txt - DUMB library reference
|
||||
deprec.txt - Information about deprecated parts of the API
|
||||
ptr.txt - Quick introduction to pointers for those who need it
|
||||
fnptr.txt - Explanation of function pointers for those who need it
|
||||
modplug.txt - Our official position regarding ModPlug Tracker
|
||||
|
||||
This file will help you get DUMB set up. If you have not yet done so, please
|
||||
read licence.txt and release.txt before proceeding. After you've got DUMB set
|
||||
up, please refer to the files in the docs/ directory at your convenience. I
|
||||
recommend you start with howto.txt.
|
||||
|
||||
|
||||
****************
|
||||
*** Features ***
|
||||
****************
|
||||
|
||||
|
||||
Here is the statutory feature list:
|
||||
|
||||
- Freeware
|
||||
|
||||
- Supports playback of IT, XM, S3M and MOD files
|
||||
|
||||
- Faithful to the original trackers, especially IT; if it plays your module
|
||||
wrongly, please tell me so I can fix the bug! (But please don't complain
|
||||
about differences between DUMB and ModPlug Tracker; see docs/modplug.txt)
|
||||
|
||||
- Accurate support for low-pass resonant filters for IT files
|
||||
|
||||
- Very accurate timing and pitching; completely deterministic playback
|
||||
|
||||
- Click removal
|
||||
|
||||
- Facility to embed music files in other files (e.g. Allegro datafiles)
|
||||
|
||||
- Three resampling quality settings: aliasing, linear interpolation and cubic
|
||||
interpolation
|
||||
|
||||
- Number of samples playing at once can be limited to reduce processor usage,
|
||||
but samples will come back in when other louder ones stop
|
||||
|
||||
- All notes will be present and correct even if you start a piece of music in
|
||||
the middle
|
||||
|
||||
- Fast seeking to any point before the music first loops (seeking time
|
||||
increases beyond this point)
|
||||
|
||||
- Audio generated can be used in any way; DUMB does not necessarily send it
|
||||
straight to a sound output system
|
||||
|
||||
- Makefile provided for DJGPP, MinGW, Linux, BeOS and Mac OS X; project file
|
||||
provided for MSVC 6 (please contact me if you'd like to submit or request
|
||||
support for a new platform; the code itself should port anywhere that has a
|
||||
32-bit C compiler)
|
||||
|
||||
- Can be used with Allegro, can be used without (if you'd like to help make
|
||||
DUMB more approachable to people who aren't using Allegro, please contact
|
||||
me)
|
||||
|
||||
|
||||
*********************
|
||||
*** What you need ***
|
||||
*********************
|
||||
|
||||
|
||||
To use DUMB, you need a 32-bit C compiler (GCC and MSVC are fine). If you
|
||||
have Allegro, DUMB can integrate with its audio streams and datafiles, making
|
||||
your life easier. If you do not wish to use Allegro, you will have to do some
|
||||
work to get music playing back. The 'dumbplay' example program requires
|
||||
Allegro.
|
||||
|
||||
Allegro - http://alleg.sf.net/
|
||||
|
||||
Neil Walker has kindly uploaded some DUMB binaries at
|
||||
http://retrospec.sgn.net/allegro/ . They may not always be up to date, so you
|
||||
should try to compile it yourself first.
|
||||
|
||||
|
||||
**********************************************
|
||||
*** How to set DUMB up with DJGPP or MinGW ***
|
||||
**********************************************
|
||||
|
||||
|
||||
You should have got the .zip version. If for some reason you got the .tar.gz
|
||||
version instead, you may have to convert make/config.bat to DOS text file
|
||||
format. WinZip does this automatically by default. Otherwise, loading it into
|
||||
MS EDIT and saving it again should do the trick. You will have to do the same
|
||||
for any files you want to view in Windows Notepad. If you have problems, just
|
||||
go and download the .zip instead.
|
||||
|
||||
Make sure you preserved the directory structure when you extracted DUMB from
|
||||
the archive. Most unzipping programs will do this by default, but pkunzip
|
||||
requires you to pass -d. If not, please delete DUMB and extract it again
|
||||
properly.
|
||||
|
||||
If you are using Windows, open an MS-DOS Prompt or a Windows Command Line.
|
||||
Change to the directory into which you unzipped DUMB.
|
||||
|
||||
Type the following:
|
||||
|
||||
make
|
||||
|
||||
DUMB will ask you whether you wish to compile for DJGPP or MinGW. Then it
|
||||
will ask you whether you want support for Allegro. (You have to have made and
|
||||
installed Allegro's optimised library for this to work.) Finally, it will
|
||||
compile optimised and debugging builds of DUMB, along with the example
|
||||
programs. When it has finished, run the following to install the libraries:
|
||||
|
||||
make install
|
||||
|
||||
All done! If you ever need the configuration again (e.g. if you compiled for
|
||||
DJGPP before and you want to compile for MinGW now), run the following:
|
||||
|
||||
make config
|
||||
|
||||
See the comments in the makefile for other targets.
|
||||
|
||||
Note: the makefile will only work properly if you have COMSPEC or ComSpec set
|
||||
to point to command.com or cmd.exe. If you set it to point to a Unix-style
|
||||
shell, the makefile won't work.
|
||||
|
||||
Please let me know if you have any trouble.
|
||||
|
||||
Scroll down for information on the example programs. Refer to docs/howto.txt
|
||||
when you are ready to start programming with DUMB. If you use DUMB in a game,
|
||||
let me know - I might decide to place a link to your game on DUMB's website!
|
||||
|
||||
|
||||
******************************************************
|
||||
*** How to set DUMB up with Microsoft Visual C++ 6 ***
|
||||
******************************************************
|
||||
|
||||
|
||||
You should have got the .zip version. If for some reason you got the .tar.gz
|
||||
version instead, you may have to convert some files to DOS text file format.
|
||||
WinZip does this automatically by default. Otherwise, loading such files into
|
||||
MS EDIT and saving them again should do the trick. You will have to do this
|
||||
for any files you want to view in Windows Notepad. If you have problems, just
|
||||
go and download the .zip instead.
|
||||
|
||||
Make sure you preserved the directory structure when you extracted DUMB from
|
||||
the archive. Most unzipping programs will do this by default, but pkunzip
|
||||
requires you to pass -d. If not, please delete DUMB and extract it again
|
||||
properly.
|
||||
|
||||
DUMB now comes with a project file for Microsoft Visual C++ 6. To add DUMB to
|
||||
your project:
|
||||
|
||||
1. Open your project in VC++.
|
||||
2. Select Project|Insert Project into Workspace...
|
||||
3. Navigate to the dumb\vc6 directory, and select dumb.dsp.
|
||||
4. Select Build|Set Active Configuration..., and reselect one of your
|
||||
project's configurations.
|
||||
5. Select Project|Dependencies... and ensure your project is dependent on
|
||||
DUMB.
|
||||
6. Select Project|Settings..., Settings for: All Configurations, C/C++ tab,
|
||||
Preprocessor category. Add the DUMB include directory to the Additional
|
||||
Include Directories box.
|
||||
7. Ensure that for all the projects in the workspace (or more likely just all
|
||||
the projects in a particular dependency chain) the run-time libraries are
|
||||
the same. That's in Project|Settings, C/C++ tab, Code generation category,
|
||||
Use run-time library dropdown. The settings for Release and Debug are
|
||||
separate, so you'll have to change them one at a time. Exactly which run-
|
||||
time library you use will depend on what you need; it doesn't appear that
|
||||
DUMB has any particular requirements, so set it to whatever you're using
|
||||
now.
|
||||
|
||||
Good thing you only have to do all that once ...
|
||||
|
||||
If you have the Intel compiler installed, it will - well, should - be used to
|
||||
compile DUMB. The only setting I added is /QxiM. This allows the compiler to
|
||||
use PPro and MMX instructions, and so when compiling with Intel the resultant
|
||||
EXE will require a Pentium II or greater. I don't think this is unreasonable.
|
||||
After all, it is 2003 :)
|
||||
|
||||
If you don't have the Intel compiler, VC will compile DUMB as normal.
|
||||
|
||||
This project file and these instructions were provided by Tom Seddon (I hope
|
||||
I got his name right; I had to guess it from his e-mail address!). They are
|
||||
untested by me. If you have problems, check the download page at
|
||||
http://dumb.sf.net/ to see if they are addressed; failing that, direct
|
||||
queries to me and I'll try to figure them out.
|
||||
|
||||
When you are ready to start using DUMB, refer to docs/howto.txt. If you use
|
||||
DUMB in a game, let me know - I might decide to place a link to your game on
|
||||
DUMB's website!
|
||||
|
||||
|
||||
********************************************************************
|
||||
*** How to set DUMB up on Linux, BeOS and possibly even Mac OS X ***
|
||||
********************************************************************
|
||||
|
||||
|
||||
You should have got the .tar.gz version. If for some reason you got the .zip
|
||||
version instead, you may have to use dtou on some or all of the text files.
|
||||
If you have problems, just go and download the .tar.gz instead.
|
||||
|
||||
First, run the following command as a normal user:
|
||||
|
||||
make
|
||||
|
||||
You will be asked whether you want Allegro support. Then, unless you are on
|
||||
BeOS, you will be asked where you'd like DUMB to install its headers,
|
||||
libraries and examples (which will go in the include/, lib/ and bin/
|
||||
subdirectories of the prefix you specify). BeOS has fixed locations for these
|
||||
files. Once you have specified these pieces of information, the optimised and
|
||||
debugging builds of DUMB will be compiled, along with the examples. When it
|
||||
has finished, you can install them with:
|
||||
|
||||
make install
|
||||
|
||||
You may need to be root for this to work. It depends on the prefix you chose.
|
||||
|
||||
Note: the makefile will only work if COMSPEC and ComSpec are both undefined.
|
||||
If either of these is defined, the makefile will try to build for a Windows
|
||||
system, and will fail.
|
||||
|
||||
Please let me know if you have any trouble.
|
||||
|
||||
Information on the example programs is just below. Refer to docs/howto.txt
|
||||
when you are ready to start programming with DUMB. If you use DUMB in a game,
|
||||
let me know - I might decide to place a link to your game on DUMB's website!
|
||||
|
||||
|
||||
****************************
|
||||
*** The example programs ***
|
||||
****************************
|
||||
|
||||
|
||||
Two example programs are provided. On DOS and Windows, you can find them in
|
||||
the examples subdirectory. On other systems they will be installed system-
|
||||
wide.
|
||||
|
||||
dumbplay
|
||||
This program will only be built if you have Allegro. Pass it the filename
|
||||
of an IT, XM, S3M or MOD file, and it will play it. It's not a polished
|
||||
player with real-time threading or anything - so don't complain about it
|
||||
stuttering while you use other programs - but it does show DUMB's fidelity
|
||||
nicely. You can control the playback quality by editing dumb.ini, which
|
||||
must be in the current working directory. (This is a flaw for systems
|
||||
where the program is installed system-wide, but it is non-fatal.) Have a
|
||||
look at the examples/dumb.ini file for further information.
|
||||
|
||||
dumbout
|
||||
This program does not need Allegro. You can use it to stream an IT, XM,
|
||||
S3M or MOD file to raw PCM. This can be used as input to an encoder like
|
||||
oggenc (with appropriate command-line options), or it can be sent to a
|
||||
.pcm file which can be read by any respectable waveform editor. No .wav
|
||||
support yet, sorry. This program is also convenient for timing DUMB.
|
||||
Compare the time it takes to render a module with the module's playing
|
||||
time! dumbout doesn't try to read any configuration file; the options are
|
||||
set on the command line.
|
||||
|
||||
|
||||
*********************************************
|
||||
*** Downloading music or writing your own ***
|
||||
*********************************************
|
||||
|
||||
|
||||
If you would like to compose your own music modules, then first I must offer
|
||||
a word of warning: not everyone is capable of composing music. Do not assume
|
||||
you will be able to learn the art. By all means have a go; if you can learn
|
||||
to play tunes on the computer keyboard, you're well on the way to being a
|
||||
composer!
|
||||
|
||||
The best programs for the job are the trackers that pioneered the file
|
||||
formats:
|
||||
|
||||
Impulse Tracker - IT files - http://www.noisemusic.org/it/
|
||||
Fast Tracker II - XM files - http://www.gwinternet.com/music/ft2/
|
||||
Scream Tracker 3 - S3M files -
|
||||
http://www.united-trackers.org/resources/software/screamtracker.htm
|
||||
|
||||
MOD files come from the Amiga; I do not know what PC tracker to recommend for
|
||||
editing these. If you know of one, let me know! In the meantime, I would
|
||||
recommend using a more advanced file format. However, don't convert your
|
||||
existing MODs just for the sake of it.
|
||||
|
||||
Note that Fast Tracker II is Shareware. It arguably offers the best
|
||||
interface, but the IT file format is more powerful and better defined.
|
||||
Impulse Tracker and Scream Tracker 3 are Freeware. DUMB is likely to be at
|
||||
its best with IT files.
|
||||
|
||||
These editors are DOS programs. Users of DOS-incapable operating systems may
|
||||
like to try ModPlug Tracker, but should read docs/modplug.txt before using it
|
||||
for any serious work. If you use a different operating system, or if you know
|
||||
of any module editors for Windows that are more faithful to the original
|
||||
trackers' playback, please give me some links so I can put them here!
|
||||
|
||||
ModPlug Tracker - http://www.modplug.com/
|
||||
|
||||
BEWARE OF WINAMP! Although it's excellent for MP3s, it is notorious for being
|
||||
one of the worst module players in existence; very few modules play correctly
|
||||
with it. There are plug-ins available to improve Winamp's module support, for
|
||||
example WSP.
|
||||
|
||||
Winamp - http://www.winamp.com/
|
||||
WSP - http://www.spytech.cz/index.php?sec=demo
|
||||
|
||||
Samples and instruments are the building blocks of music modules. You can
|
||||
download samples at:
|
||||
|
||||
http://www.tump.net/
|
||||
|
||||
If you would like to download module files composed by other people, check
|
||||
the following sites:
|
||||
|
||||
http://www.modarchive.com/
|
||||
http://www.scene.org/
|
||||
http://www.tump.net/
|
||||
http://www.homemusic.cc/main.php
|
||||
http://www.modplug.com/
|
||||
|
||||
Once again, if you know of more sites where samples or module files are
|
||||
available for download, please let me know.
|
||||
|
||||
If you wish to use someone's music in your game, please respect the
|
||||
composer's wishes. In general, you should ask the composer. Music that has
|
||||
been placed in the Public Domain can be used by anyone for anything, but it
|
||||
wouldn't do any harm to ask anyway if you know who the author is. In most
|
||||
cases the author will be thrilled, so don't hesitate!
|
||||
|
||||
A note about converting modules from one format to another: don't do it,
|
||||
unless you are a musician and are prepared to go through the file and make
|
||||
sure everything sounds the way it should! The module formats are all slightly
|
||||
different, and converting from one format to another will usually do some
|
||||
damage.
|
||||
|
||||
Instead, it is recommended that you allow DUMB to interpret the original file
|
||||
as it sees fit. DUMB may make mistakes (it does a lot of conversion on
|
||||
loading), but future versions of DUMB will be able to rectify these mistakes.
|
||||
On the other hand, if you convert the file, the damage is permanent.
|
||||
|
||||
|
||||
***********************
|
||||
*** Contact details ***
|
||||
***********************
|
||||
|
||||
|
||||
If you have trouble with DUMB, or want to contact me for any other reason, my
|
||||
e-mail address is given below. However, I may be able to help more if you
|
||||
come on to IRC EFnet #dumb.
|
||||
|
||||
IRC stands for Internet Relay Chat, and is a type of chat network. Several
|
||||
such networks exist, and EFnet is a popular one. In order to connect to an
|
||||
IRC network, you first need an IRC client. Here are some:
|
||||
|
||||
http://www.xchat.org/
|
||||
http://www.visualirc.net/beta.php
|
||||
http://www.mirc.com/
|
||||
|
||||
Getting on to IRC can be a steep cliff, but it is not insurmountable, and
|
||||
it's well worth it. Once you have set up the client software, you need to
|
||||
connect to a server. Here is a list of EFnet servers I have had success with.
|
||||
Type "/server" (without quotes), then a space, then the name of a server.
|
||||
|
||||
irc.homelien.no
|
||||
irc.webgiro.se
|
||||
efnet.vuurwerk.nl
|
||||
efnet.demon.co.uk
|
||||
irc.isdnet.fr
|
||||
irc.prison.net
|
||||
|
||||
If these servers do not work, visit http://efnet.org/ircdb/servers.php for a
|
||||
huge list of other EFnet servers to try.
|
||||
|
||||
Once you're connected, type the following:
|
||||
|
||||
/join #dumb
|
||||
|
||||
A window will appear, and you can ask your question. It should be clear
|
||||
what's going on from this point onwards. I am 'entheh'. Note that unlike many
|
||||
other nerds I am not always at my computer, so if I don't answer your
|
||||
question, don't take it personally! I will usually be able to read your
|
||||
question when I come back.
|
||||
|
||||
|
||||
******************
|
||||
*** Conclusion ***
|
||||
******************
|
||||
|
||||
|
||||
This is the conclusion.
|
||||
|
||||
|
||||
Ben Davis
|
||||
entheh@users.sf.net
|
||||
IRC EFnet #dumb
|
406
apps/codecs/dumb/release.txt
Normal file
406
apps/codecs/dumb/release.txt
Normal file
|
@ -0,0 +1,406 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* release.txt - Release notes for DUMB. / / \ \
|
||||
* | < / \_
|
||||
* | \/ /\ /
|
||||
* \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
|
||||
******************************************
|
||||
*** DUMB v0.9.2, released 2 April 2003 ***
|
||||
******************************************
|
||||
|
||||
Yes, there really has been a release. This is not a day-late April fools'
|
||||
joke.
|
||||
|
||||
DUMB's full name has changed! The old "Dedicated Universal Music
|
||||
Bastardisation" was rather silly, and not much more than a forced attempt at
|
||||
finding words beginning with D, U, M and B. I spent weeks and weeks browsing
|
||||
dictionaries and hopelessly asking others for bright ideas, until the
|
||||
brilliant Chris "Kitty Cat" Robinson came up with "Dynamic". I decided to
|
||||
keep the U as Universal, since a DUH struct can hold digital music in any
|
||||
format. Now all that remained was the B, but it didn't take me long to come
|
||||
up with Bibliotheque, which, despite looking French, is indeed considered an
|
||||
English word by Oxford English Dictionary Online, to which my university has
|
||||
a subscription. So there you have it - the name now makes sense.
|
||||
|
||||
The two most significant additions to the project would have to be the new
|
||||
thread safety (with an important restriction, detailed in docs/dumb.txt), and
|
||||
the new build system. The silly 'makeall' and 'makecore' scripts are gone. If
|
||||
you are a GCC user, all you need do now is run 'make' and 'make install', as
|
||||
for other projects. You don't even have to run a 'fix' script any more! There
|
||||
are some caveats, which are covered in readme.txt. If you use Microsoft
|
||||
Visual C++ 6, you no longer need to obtain GCC and GNU Make - there is a
|
||||
project file just for you.
|
||||
|
||||
Huge thanks go to Steve Terry for testing on Windows XP - about five times -
|
||||
and to lillo for testing on BeOS and Mac OS X. Thanks also to X-G for testing
|
||||
on a Windows system that has consistently posed problems for DUMB's old
|
||||
makefiles.
|
||||
|
||||
There was a bug whereby al_poll_duh() would sometimes cause the music to
|
||||
resume playing if you called it after al_pause_duh(). Whether this was DUMB's
|
||||
fault for misusing Allegro's API, or a bug in Allegro, is unclear, but this
|
||||
release makes it work.
|
||||
|
||||
In one of my projects, I found that my AL_DUH_PLAYER stopped playing when
|
||||
there were lots of other sound effects. In order to fix this, I programmed
|
||||
DUMB to set the priority of the stream's voice to 255, the maximum. I also
|
||||
added al_duh_set_priority(), so you can set the priority yourself if you need
|
||||
to.
|
||||
|
||||
The resampling code has undergone a transformation. The bad news is that the
|
||||
linear average code is no longer in use. The good news is that where DUMB's
|
||||
resamplers used to require three extra samples' worth of memory to be
|
||||
allocated and initialised, it now copes with just the sample data. And it
|
||||
does a very good job at bouncing off loop points and otherwise hurtling
|
||||
around the sample. The resampling code is considerably more complicated, but
|
||||
the code that uses the resamplers is considerably simpler - and if you
|
||||
noticed a slight click in some bidirectionally looping samples, you'll be
|
||||
pleased to know that that click is gone!
|
||||
|
||||
I have also devoted some effort to optimisation. It seemed hopeless for a
|
||||
while, but then I actually figured out a way of making it faster AND more
|
||||
accurate at the same time! DUMB is now quite a bit faster than it was, and it
|
||||
mixes not with 16-bit precision, but with 24-bit precision. (It used 32-bit
|
||||
integers all along, but the difference is that it now makes use of 256 times
|
||||
as much of the integer's range.)
|
||||
|
||||
There have been the usual improvements to playback. The last release occurred
|
||||
rather too soon after I had fixed the XM effect memories; EAx and EBx, fine
|
||||
volume ramps, had been neglected. These are now handled properly.
|
||||
|
||||
In previous versions of DUMB, muted channels in IT were actually played with
|
||||
surround sound panning (where the right-hand channel is inverted). This has
|
||||
been fixed, so muted channels will really be muted now.
|
||||
|
||||
There were also some subtle problems with the way DUMB handled New Note
|
||||
Actions for IT files. It turned out that, in all releases of DUMB so far,
|
||||
pitch, filter and panning envelopes and sample vibrato were not being
|
||||
processed for any note that was forced into the background by a new note on
|
||||
the same channel! This only affected IT files. Not only has this been fixed,
|
||||
but envelope interpolation is much more accurate. Long trailing envelope-
|
||||
driven fade-outs sound a lot better now!
|
||||
|
||||
Since panning and filter envelopes are more precise, extra fields have been
|
||||
added to the DUMB_IT_CHANNEL_STATE struct, used by
|
||||
dumb_it_sr_get_channel_state(). These fields hold the 'decimal' parts of the
|
||||
pan and filter cut-off. See dumb.txt for details.
|
||||
|
||||
Mxx (set channel volume) now correctly only modifies the last note played on
|
||||
the channel, not any previous notes that have been forced into the background
|
||||
by New Note Actions, and filter effect processing is now closer to what
|
||||
Impulse Tracker does.
|
||||
|
||||
The XM loader was slightly flawed and could crash on files containing samples
|
||||
with out-of-range loop points. One such file was given to me. This has been
|
||||
fixed.
|
||||
|
||||
Finally, the legal stuff. Julien Cugniere has been added to the list of
|
||||
copyright owners. He deserves it, for all the work he did on the XM support!
|
||||
And the licence has been changed. You are no longer required to include a
|
||||
link to DUMB in a project that uses DUMB; the reasons for this relaxation are
|
||||
explained in licence.txt. However, the request is still there ...
|
||||
|
||||
As usual, enjoy!
|
||||
|
||||
|
||||
**********************************************
|
||||
*** DUMB v0.9.1, released 19 December 2002 ***
|
||||
**********************************************
|
||||
|
||||
Hi again! Lots to say this time, so I shall cut right to the chase.
|
||||
|
||||
DUMB now supports Impulse Tracker's low-pass resonant filters! Huge thanks go
|
||||
to Jeffrey Lim, author of Impulse Tracker, for giving me what information he
|
||||
still had regarding the algorithm; to cut a long story short, modifying
|
||||
ModPlug Tracker's source code (which is in the Public Domain) led to an
|
||||
algorithm whose output matched Impulse Tracker's perfectly.
|
||||
|
||||
Please note that ModPlug Tracker's filters as they stand do not match Impulse
|
||||
Tracker's, and I have no interest in supporting ModPlug Tracker's variant
|
||||
(especially not the integer rounding problems). Please see docs/modplug.txt,
|
||||
new in this release, for details.
|
||||
|
||||
Thanks also go to Fatso Huuskonen for motivating me to add filter support,
|
||||
and providing me with several great IT files to test it with!
|
||||
|
||||
The other important feature added for this release is click removal. Up until
|
||||
now, DUMB has generated clicks when cutting notes, starting samples in the
|
||||
middle, and so on. This version of DUMB will remove any such clicks. Note
|
||||
that DUMB does not use volume ramps to accomplish this; the algorithm will
|
||||
not take the bite out of the music!
|
||||
|
||||
In other news, DUMB now supports sample vibrato for IT files, and instrument
|
||||
vibrato for XM files. A slight bug in New Note Action handling for IT files
|
||||
has been fixed; Note Fade will not break the sustain loops of the sample and
|
||||
envelope, as it did before. Tremor handling (Ixy) had a strange bug in it,
|
||||
which has been fixed.
|
||||
|
||||
Support for XM files has been greatly enhanced. The XM envelope handling new
|
||||
in the last release contained a huge bug, resulting in notes seeming not to
|
||||
stop when they should; this has been fixed. Some XM files crashed DUMB, while
|
||||
others failed to load; these problems have been solved. Effect memories now
|
||||
work properly for XM and MOD files, to the best of my knowledge. Some other
|
||||
differences between IT and XM have been accounted for, most notably the
|
||||
Retrigger Note effects, Rxy and E9x.
|
||||
|
||||
DUMB's sound quality and accuracy are not the only areas that have been
|
||||
enhanced. The API has been expanded, at last. You can now detect when a
|
||||
module loops, or make it play through just once. You can ask DUMB to inform
|
||||
you every time it generates some samples; this is useful for visualisation.
|
||||
For IT files, you can intercept the MIDI messages generated by Zxx macros,
|
||||
enabling you to synchronise your game with the music to some extent. (There
|
||||
is no such method for XM, S3M or MOD files yet; sorry. Also note that the
|
||||
function will be called before you actually hear the sound; I cannot improve
|
||||
this until DUMB has its own sound drivers, which won't be for a while.) You
|
||||
can query the current order and row. Finally, operations like changing the
|
||||
speed and tempo are now possible, and you can query the playback state on
|
||||
each channel.
|
||||
|
||||
Some parts of DUMB's API have been deprecated. Simple programs that use
|
||||
Allegro will be unaffected, but if you get some compiler warnings or errors,
|
||||
please review docs/deprec.txt. This file explains why those parts of the API
|
||||
were deprecated, and tells you how to adapt your code; the changes you need
|
||||
to make are straightforward. Sorry for the inconvenience.
|
||||
|
||||
For various reasons, I have made DUMB's makefiles use different compiler
|
||||
flags depending on your GCC version (unless you are using MSVC). There is no
|
||||
elegant way of getting the makefiles to detect when GCC is upgraded. If you
|
||||
upgrade GCC, you should execute 'make clean' in order to make DUMB detect the
|
||||
GCC version again. Otherwise you may get some annoying error messages. (It is
|
||||
wise to do this in any case, so that all the object files are built with the
|
||||
same GCC version.)
|
||||
|
||||
DUMB's example players have been unified into a single player called
|
||||
'dumbplay'. The player has been enhanced to display messages when the music
|
||||
loops, and when XM and MOD files freeze (effect F00; more information on this
|
||||
in docs/howto.txt).
|
||||
|
||||
Finally, as noted on DUMB's website, the release notes from the last release
|
||||
were inaccurate. It has been verified that DUMBOGG v0.5 does still work with
|
||||
that release, and still works with this release. The esoteric DUMBOGG v0.6
|
||||
has not been created yet, since DUMBOGG v0.5 still works.
|
||||
|
||||
Please scroll down and read through the indented paragraphs in the notes for
|
||||
the last release; they are relevant for this release too.
|
||||
|
||||
That's all folks! Until next time.
|
||||
|
||||
|
||||
*******************************************
|
||||
*** DUMB v0.9, released 16 October 2002 ***
|
||||
*******************************************
|
||||
|
||||
MOD support is here! DUMB now supports all four of the common module formats.
|
||||
As usual, there have also been some improvements to the way modules are
|
||||
played back. Most notably, handling of tone portamento in IT files has been
|
||||
improved a lot, and XM envelopes are now processed correctly.
|
||||
|
||||
The other major change is that DUMB now does a dummy run through each module
|
||||
on loading. It stores the playback state at thirty-second intervals. It stops
|
||||
when the module first loops, and then stores the playback time. This results
|
||||
in a slightly longer load time and a greater memory overhead, but seeking is
|
||||
faster (to any point before the module first loops) and the length is
|
||||
calculated! duh_get_length() will return this and is now documented in
|
||||
docs/howto.txt and docs/dumb.txt.
|
||||
|
||||
DUMB's build process has been changed to use 'mingw' wherever it used
|
||||
'mingw32' before; some directories have been renamed, and the 'fix' command
|
||||
you had to run for MinGW has been changed from 'fix mingw32' to 'fix mingw'.
|
||||
|
||||
Last time, I directed you to scroll down and read the notes from a past
|
||||
release, but ignore this point, and that point applies to something else, and
|
||||
so on. Did anyone do so? Well, if you're reading this at all, you probably
|
||||
did. Nevertheless, this time I shall be much less confusing and restate any
|
||||
relevant information. So the least you can do is read it!
|
||||
|
||||
- If your program ever aborts with exit code 37 while loading an IT file,
|
||||
PLEASE LET ME KNOW! The IT file in question has a stereo compressed sample
|
||||
in it, and the format is unspecified for this case (Impulse Tracker itself
|
||||
doesn't use stereo samples at all). I will need the IT file in question,
|
||||
and any information you can give me about how the IT file was created (e.g.
|
||||
what program). (If you don't get to see an exit code, let me know anyway.)
|
||||
|
||||
- If your program ever outputs a line resembling "Inst 01 Env: 0,64 8,32
|
||||
15,48" to stderr while loading an IT file, PLEASE LET ME KNOW! You have an
|
||||
old IT file (saved by an Impulse Tracker version older than 2.00), and
|
||||
support for such files is STILL untested.
|
||||
|
||||
- Not all parts of DUMB's API are documented yet. You will find some
|
||||
functions in dumb.h which are not listed in docs/dumb.txt; the reason is
|
||||
that these functions still need work and will probably change. If you
|
||||
really, really want to use them, talk to me first (IRC EFnet #dumb is a
|
||||
good place for this; see readme.txt for details on using IRC). I intend to
|
||||
finalise and document the whole of DUMB's API for Version 1.0.
|
||||
|
||||
There have been some changes to the naming conventions in DUMB's undocumented
|
||||
API. DUMBOGG v0.5 will not work with this and subsequent releases of DUMB;
|
||||
please upgrade to DUMBOGG v0.6. These changes should not break anything in
|
||||
your own code, since you didn't use those parts of the API, did you ;)
|
||||
|
||||
There is still a great deal of work to be done before DUMB's API can be
|
||||
finalised, and thus it will be a while before DUMB v1.0 comes out. It should
|
||||
be worth the wait. In the meantime, there will be 0.9.x releases with
|
||||
additional functionality, improved playback, and possibly support for some
|
||||
extra file formats.
|
||||
|
||||
Finally I should like to offer an apology; there is a strong possibility that
|
||||
some of DUMB's official API will change in the near future. There will not be
|
||||
any drastic changes, and the corresponding changes to your source code will
|
||||
be simple enough. If I didn't make these changes, DUMB's API would start to
|
||||
become limited, or messy, or both, so it's for the better. I apologise in
|
||||
advance for this.
|
||||
|
||||
Now scroll down and read the notes for the first r... oh wait, we already did
|
||||
that. I guess that's it then. You can stop reading now.
|
||||
|
||||
Right after you've read this.
|
||||
|
||||
And this.
|
||||
|
||||
Off you go.
|
||||
|
||||
Bye.
|
||||
|
||||
|
||||
********************************************
|
||||
*** DUMB v0.8.1, released 11 August 2002 ***
|
||||
********************************************
|
||||
|
||||
This is a minor release that fixes a few bugs. One of these bugs, however,
|
||||
was pretty serious. dumb_register_dat_xm() was never coded! It was prototyped
|
||||
in aldumb.h, so code would compile, but there would be an unresolved symbol
|
||||
at the linking stage. This has been fixed.
|
||||
|
||||
Platforms other than Unix did not have a working 'make veryclean' target;
|
||||
this has been fixed. In addition, the makefiles now use 'xcopy' instead of
|
||||
'copy', since on some systems GNU Make seems to have trouble calling commands
|
||||
built in to the shell.
|
||||
|
||||
Contrary to the errata that was on the DUMB website, the makeall.sh and
|
||||
makecore.sh scripts actually DID install in /usr. This has now been
|
||||
corrected, and regardless of whether you use these scripts or call make
|
||||
directly, the files will now be installed to /usr/local by default.
|
||||
|
||||
The XM loader used to treat stereo samples as mono samples with the data for
|
||||
the right channel positioned after the data for the left channel. This
|
||||
generally resulted in an unwanted echo effect. This has been fixed.
|
||||
|
||||
When playing XM files, specifying an invalid instrument would cause an old
|
||||
note on that channel to come back (roughly speaking). Fast Tracker 2 does not
|
||||
exhibit this behaviour. This has been fixed.
|
||||
|
||||
The GCC makefiles used -mpentium, which is deprecated in gcc 3.x. This was
|
||||
generating warnings, and has now been fixed.
|
||||
|
||||
In XM files, the length of a sample is stored in bytes. DUMB was assuming
|
||||
that the length of a 16-bit sample would be even. I had two XM files where
|
||||
this was not the case, and DUMB was unable to load them. This has been fixed.
|
||||
|
||||
In order to accommodate the extra part of the version number,
|
||||
DUMB_REVISION_VERSION has been added. DUMB_VERSION has also been added in
|
||||
order to facilitate checking if the version of DUMB installed is sufficient.
|
||||
See docs/dumb.txt for details.
|
||||
|
||||
As a last-minute fix, the XM "Break to row" effect is now loaded properly. It
|
||||
was necessary to convert from binary-coded decimal to hexadecimal (those who
|
||||
have experience with Fast Tracker 2 will know what I mean). In short, this
|
||||
means the effect will now work properly when breaking to row 10 or greater.
|
||||
|
||||
DUMB v0.8 had faulty release date constants; DUMB_MONTH and DUMB_DAY were
|
||||
swapped! For this reason, DUMB_DATE should not be compared against any date
|
||||
in 2002. This note has been added to docs/dumb.txt and also to dumb.h.
|
||||
|
||||
Please scroll to the end and read the release notes for the first version,
|
||||
DUMB v0.7. Most of them apply equally to this release. However, the
|
||||
non-portable code was rewritten for DUMB v0.8, so that point does not apply.
|
||||
The point about length not being calculated also applies to XM files.
|
||||
|
||||
Enjoy :)
|
||||
|
||||
|
||||
****************************************
|
||||
*** DUMB v0.8, released 14 June 2002 ***
|
||||
****************************************
|
||||
|
||||
Welcome to the second release of DUMB!
|
||||
|
||||
In addition to these notes, please read below the release notes for the
|
||||
previous version, DUMB v0.7. Most of them apply equally to this release.
|
||||
However, the non-portable code has been rewritten; DUMB should now port to
|
||||
big-endian platforms.
|
||||
|
||||
The main improvement in this release of DUMB is the support for XM files.
|
||||
Enormous thanks go to Julien Cugniere for working on this while I had to
|
||||
revise for my exams!
|
||||
|
||||
There was a mistake in the makefiles in the last release. The debugging
|
||||
Allegro interface library was mistakenly named libaldmbd.a instead of
|
||||
libaldmd.a, meaning you had to compile with -laldmbd, contrary to what the
|
||||
docs said. Apologies to everyone who lost sleep trying to work out what was
|
||||
wrong! The reason for using libaldmd.a is to maintain compatibility with
|
||||
plain DOS, where filenames are limited to eight characters (plus a three-
|
||||
letter extension). The makefiles have now been changed to match the
|
||||
information in the docs, so you may have to alter your project files
|
||||
accordingly.
|
||||
|
||||
The example programs were faulty, and crashed on Windows if they were unable
|
||||
to load the file. It was also difficult to work out how to exit them (you had
|
||||
to click the taskbar button that didn't have a window, then press a key).
|
||||
They have been improved in both these respects.
|
||||
|
||||
I have now added a docs/faq.txt file (Frequently Asked Questions), which is
|
||||
based on problems and misconceptions people have had with the first release.
|
||||
Please refer to it before contacting me with problems.
|
||||
|
||||
Thanks to networm for touching up the Unix makefile and writing the
|
||||
instructions on using it.
|
||||
|
||||
Incidentally, today (Friday 14 June) is the Robinson College May Ball at
|
||||
Cambridge Uni. God knows why it's called a May Ball if it's in June. I'm not
|
||||
going myself (72 GBP, and I'd have to wear a suit, ugh), but with all the
|
||||
noise outside I shall enjoy pumping up the speakers tonight!
|
||||
|
||||
|
||||
****************************************
|
||||
*** DUMB v0.7, released 2 March 2002 ***
|
||||
****************************************
|
||||
|
||||
This is the first release of DUMB, and parts of the library are not
|
||||
crystallised. Don't let this put you off! Provided you don't try to use any
|
||||
features that aren't documented in docs/dumb.txt, the library should be rock
|
||||
solid and you should be able to upgrade more or less without problems.
|
||||
|
||||
Here are some notes on this release:
|
||||
|
||||
- There is some non-portable code in this release of DUMB. It is likely that
|
||||
the library will fail to load IT files with compressed samples on
|
||||
big-endian machines such as the Apple Macintosh.
|
||||
|
||||
- If your program ever aborts with exit code 37 while loading an IT file,
|
||||
PLEASE LET ME KNOW! The IT file in question has a stereo compressed sample
|
||||
in it, and the format is unspecified for this case (Impulse Tracker itself
|
||||
doesn't use stereo samples at all). I will need the IT file in question,
|
||||
and any information you can give me about how the IT file was created (e.g.
|
||||
what program). (If you don't get to see an exit code, let me know anyway.)
|
||||
|
||||
- If your program ever outputs a line resembling "Inst 01 Env: 0,64 8,32
|
||||
15,48" to stderr while loading an IT file, PLEASE LET ME KNOW! You have an
|
||||
old IT file (saved by an Impulse Tracker version older than 2.00), and
|
||||
support for such files is untested.
|
||||
|
||||
- The length of IT and S3M files is not currently calculated. It is just set
|
||||
to ten minutes.
|
270
apps/codecs/dumb/src/allegro/alplay.c
Normal file
270
apps/codecs/dumb/src/allegro/alplay.c
Normal file
|
@ -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);
|
||||
}
|
60
apps/codecs/dumb/src/allegro/datduh.c
Normal file
60
apps/codecs/dumb/src/allegro/datduh.c
Normal file
|
@ -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
|
||||
);
|
||||
}
|
62
apps/codecs/dumb/src/allegro/datit.c
Normal file
62
apps/codecs/dumb/src/allegro/datit.c
Normal file
|
@ -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
|
||||
);
|
||||
}
|
||||
|
61
apps/codecs/dumb/src/allegro/datmod.c
Normal file
61
apps/codecs/dumb/src/allegro/datmod.c
Normal file
|
@ -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
|
||||
);
|
||||
}
|
61
apps/codecs/dumb/src/allegro/dats3m.c
Normal file
61
apps/codecs/dumb/src/allegro/dats3m.c
Normal file
|
@ -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
|
||||
);
|
||||
}
|
31
apps/codecs/dumb/src/allegro/datunld.c
Normal file
31
apps/codecs/dumb/src/allegro/datunld.c
Normal file
|
@ -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);
|
||||
}
|
||||
|
62
apps/codecs/dumb/src/allegro/datxm.c
Normal file
62
apps/codecs/dumb/src/allegro/datxm.c
Normal file
|
@ -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
|
||||
);
|
||||
}
|
||||
|
98
apps/codecs/dumb/src/allegro/packfile.c
Normal file
98
apps/codecs/dumb/src/allegro/packfile.c
Normal file
|
@ -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;
|
||||
}
|
71
apps/codecs/dumb/src/core/atexit.c
Normal file
71
apps/codecs/dumb/src/core/atexit.c
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
34
apps/codecs/dumb/src/core/duhlen.c
Normal file
34
apps/codecs/dumb/src/core/duhlen.c
Normal file
|
@ -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;
|
||||
}
|
401
apps/codecs/dumb/src/core/dumbfile.c
Normal file
401
apps/codecs/dumb/src/core/dumbfile.c
Normal file
|
@ -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;
|
||||
}
|
42
apps/codecs/dumb/src/core/loadduh.c
Normal file
42
apps/codecs/dumb/src/core/loadduh.c
Normal file
|
@ -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;
|
||||
}
|
92
apps/codecs/dumb/src/core/makeduh.c
Normal file
92
apps/codecs/dumb/src/core/makeduh.c
Normal file
|
@ -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;
|
||||
}
|
44
apps/codecs/dumb/src/core/rawsig.c
Normal file
44
apps/codecs/dumb/src/core/rawsig.c
Normal file
|
@ -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;
|
||||
}
|
107
apps/codecs/dumb/src/core/readduh.c
Normal file
107
apps/codecs/dumb/src/core/readduh.c
Normal file
|
@ -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;
|
||||
}
|
104
apps/codecs/dumb/src/core/register.c
Normal file
104
apps/codecs/dumb/src/core/register.c
Normal file
|
@ -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;
|
||||
}
|
202
apps/codecs/dumb/src/core/rendduh.c
Normal file
202
apps/codecs/dumb/src/core/rendduh.c
Normal file
|
@ -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;
|
||||
}
|
299
apps/codecs/dumb/src/core/rendsig.c
Normal file
299
apps/codecs/dumb/src/core/rendsig.c
Normal file
|
@ -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
|
58
apps/codecs/dumb/src/core/unload.c
Normal file
58
apps/codecs/dumb/src/core/unload.c
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
270
apps/codecs/dumb/src/helpers/clickrem.c
Normal file
270
apps/codecs/dumb/src/helpers/clickrem.c
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
96
apps/codecs/dumb/src/helpers/memfile.c
Normal file
96
apps/codecs/dumb/src/helpers/memfile.c
Normal file
|
@ -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);
|
||||
}
|
1177
apps/codecs/dumb/src/helpers/resample.c
Normal file
1177
apps/codecs/dumb/src/helpers/resample.c
Normal file
File diff suppressed because it is too large
Load diff
47
apps/codecs/dumb/src/helpers/sampbuf.c
Normal file
47
apps/codecs/dumb/src/helpers/sampbuf.c
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
29
apps/codecs/dumb/src/helpers/silence.c
Normal file
29
apps/codecs/dumb/src/helpers/silence.c
Normal file
|
@ -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));
|
||||
}
|
||||
|
93
apps/codecs/dumb/src/helpers/stdfile.c
Normal file
93
apps/codecs/dumb/src/helpers/stdfile.c
Normal file
|
@ -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;
|
||||
}
|
43
apps/codecs/dumb/src/it/itload.c
Normal file
43
apps/codecs/dumb/src/it/itload.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
175
apps/codecs/dumb/src/it/itmisc.c
Normal file
175
apps/codecs/dumb/src/it/itmisc.c
Normal file
|
@ -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;
|
||||
}
|
63
apps/codecs/dumb/src/it/itorder.c
Normal file
63
apps/codecs/dumb/src/it/itorder.c
Normal file
|
@ -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;
|
||||
}
|
1181
apps/codecs/dumb/src/it/itread.c
Normal file
1181
apps/codecs/dumb/src/it/itread.c
Normal file
File diff suppressed because it is too large
Load diff
3512
apps/codecs/dumb/src/it/itrender.c
Normal file
3512
apps/codecs/dumb/src/it/itrender.c
Normal file
File diff suppressed because it is too large
Load diff
71
apps/codecs/dumb/src/it/itunload.c
Normal file
71
apps/codecs/dumb/src/it/itunload.c
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
42
apps/codecs/dumb/src/it/loadmod.c
Normal file
42
apps/codecs/dumb/src/it/loadmod.c
Normal file
|
@ -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;
|
||||
}
|
42
apps/codecs/dumb/src/it/loads3m.c
Normal file
42
apps/codecs/dumb/src/it/loads3m.c
Normal file
|
@ -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;
|
||||
}
|
42
apps/codecs/dumb/src/it/loadxm.c
Normal file
42
apps/codecs/dumb/src/it/loadxm.c
Normal file
|
@ -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;
|
||||
}
|
594
apps/codecs/dumb/src/it/readmod.c
Normal file
594
apps/codecs/dumb/src/it/readmod.c
Normal file
|
@ -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);
|
||||
}
|
668
apps/codecs/dumb/src/it/reads3m.c
Normal file
668
apps/codecs/dumb/src/it/reads3m.c
Normal file
|
@ -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);
|
||||
}
|
998
apps/codecs/dumb/src/it/readxm.c
Normal file
998
apps/codecs/dumb/src/it/readxm.c
Normal file
|
@ -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);
|
||||
}
|
242
apps/codecs/dumb/src/it/xmeffect.c
Normal file
242
apps/codecs/dumb/src/it/xmeffect.c
Normal file
|
@ -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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue