From: norly Date: Fri, 26 Apr 2019 14:26:03 +0000 (+0200) Subject: Add "MikMod for Rockbox 0.1" from 2007-06-29 X-Git-Url: https://git.enpas.org/?p=mikmod-rockbox.git;a=commitdiff_plain;h=6ca1a0f9cd1e5400701ad1ec7d09206aa75ff2f9 Add "MikMod for Rockbox 0.1" from 2007-06-29 This is an early port of MikMod to Rockbox that I did back in 2007. It was one of my early exercises in the C language. Since I didn't upstream it back then, someone else ended up porting it with a nicer UI. Fun fact: This version's PCM buffers are designed to be just about large enough to allow an iPod Video to render fm-2year/rapido.xm without pausing. --- diff --git a/apps/plugins/mikmod/Makefile b/apps/plugins/mikmod/Makefile new file mode 100644 index 0000000..45a44ce --- /dev/null +++ b/apps/plugins/mikmod/Makefile @@ -0,0 +1,154 @@ +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ +# $Id: Makefile 13515 2007-05-29 16:33:16Z nls $ +# + +INCLUDES = -I$(APPSDIR) \ + -I.. \ + -I. $(TARGET_INC) \ + -I$(FIRMDIR)/include \ + -I$(FIRMDIR)/export \ + -I$(FIRMDIR)/common \ + -I$(FIRMDIR)/drivers \ + -I$(OUTDIR) \ + -I$(BUILDDIR) \ + -I./include +CFLAGS = $(INCLUDES) $(GCCOPTS) -O2 $(TARGET) $(EXTRA_DEFINES) \ + -DTARGET_ID=$(TARGET_ID) -DMEM=${MEMORYSIZE} -DPLUGIN -DHAVE_SNPRINTF + +ifdef APPEXTRA + INCLUDES += $(patsubst %,-I$(APPSDIR)/%,$(subst :, ,$(APPEXTRA))) +endif + +LINKFILE := $(OBJDIR)/link.lds +DEPFILE = $(OBJDIR)/dep-mikmod +SRC = mikmod.c \ + loaders/load_669.c \ + loaders/load_amf.c \ + loaders/load_asy.c \ + loaders/load_dsm.c \ + loaders/load_far.c \ + loaders/load_gdm.c \ + loaders/load_imf.c \ + loaders/load_it.c \ + loaders/load_m15.c \ + loaders/load_med.c \ + loaders/load_mod.c \ + loaders/load_mtm.c \ + loaders/load_s3m.c \ + loaders/load_stm.c \ + loaders/load_stx.c \ + loaders/load_ult.c \ + loaders/load_uni.c \ + loaders/load_xm.c \ + mmio/mmalloc.c \ + mmio/mmerror.c \ + mmio/mmio.c \ + playercode/mdriver.c \ + playercode/mloader.c \ + playercode/mlreg.c \ + playercode/mlutil.c \ + playercode/mplayer.c \ + playercode/munitrk.c \ + playercode/mwav.c \ + playercode/npertab.c \ + playercode/sloader.c \ + playercode/virtch2.c \ + playercode/virtch.c \ + playercode/virtch_common.c + +SOURCES = $(SRC) +OBJS := $(SRC:%.c=$(OBJDIR)/%.o) +DIRS = . + +ifndef SIMVER +ifneq (,$(strip $(foreach tgt,RECORDER ONDIO,$(findstring $(tgt),$(TARGET))))) + LDS := archos.lds + OUTPUT = $(OUTDIR)/mikmod.ovl +else ## iRiver target + LDS := ../plugin.lds + OUTPUT = $(OUTDIR)/mikmod.rock +endif +else ## simulators + OUTPUT = $(OUTDIR)/mikmod.rock +endif + +all: $(OUTPUT) + +ifndef SIMVER +$(OBJDIR)/mikmod.elf: $(OBJS) $(LINKFILE) $(BITMAPLIBS) + $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) -o $@ $(OBJS) -L$(BUILDDIR) -lplugin -lgcc \ + $(LINKBITMAPS) -T$(LINKFILE) -Wl,-Map,$(OBJDIR)/mikmod.map + +$(OUTPUT): $(OBJDIR)/mikmod.elf + $(call PRINTS,OBJCOPY $(@F))$(OC) -O binary $< $@ +else + +ifeq ($(SIMVER), x11) +################################################### +# This is the X11 simulator version + +$(OUTPUT): $(OBJS) + $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_FLAG) $(OBJS) -L$(BUILDDIR) -lplugin $(LINKBITMAPS) -o $@ +ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN) +# 'x' must be kept or you'll have "Win32 error 5" +# $ fgrep 5 /usr/include/w32api/winerror.h | head -1 +# #define ERROR_ACCESS_DENIED 5L +else + @chmod -x $@ +endif + +else # end of x11-simulator +ifeq ($(SIMVER), sdl) +################################################### +# This is the SDL simulator version + +$(OUTPUT): $(OBJS) + $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_FLAG) $(OBJS) -L$(BUILDDIR) -lplugin $(LINKBITMAPS) -o $@ +ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN) +# 'x' must be kept or you'll have "Win32 error 5" +# $ fgrep 5 /usr/include/w32api/winerror.h | head -1 +# #define ERROR_ACCESS_DENIED 5L +else + @chmod -x $@ +endif + +else # end of sdl-simulator +################################################### +# This is the win32 simulator version +DLLTOOLFLAGS = --export-all +DLLWRAPFLAGS = -s --entry _DllMain@12 --target=i386-mingw32 -mno-cygwin + +$(OUTPUT): $(OBJS) + $(call PRINTS,DLL $(@F))$(DLLTOOL) $(DLLTOOLFLAGS) -z $(OBJDIR)/$*.def $(OBJS) + $(SILENT)$(DLLWRAP) $(DLLWRAPFLAGS) --def $(OBJDIR)/$*.def $(OBJS) \ + $(BUILDDIR)/libplugin.a $(BITMAPLIBS) -o $@ +ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN) +# 'x' must be kept or you'll have "Win32 error 5" +# $ fgrep 5 /usr/include/w32api/winerror.h | head -1 +# #define ERROR_ACCESS_DENIED 5L +else + @chmod -x $@ +endif +endif # end of win32-simulator +endif +endif # end of simulator section + + +include $(TOOLSDIR)/make.inc + +# MEMORYSIZE should be passed on to this makefile with the chosen memory size +# given in number of MB +$(LINKFILE): $(LDS) + $(call PRINTS,build $(@F))cat $< | $(CC) -DMEMORYSIZE=$(MEMORYSIZE) $(INCLUDES) $(TARGET) \ + $(DEFINES) -E -P - >$@ + +clean: + $(call PRINTS,cleaning mikmod)rm -rf $(OBJDIR)/mikmod + $(SILENT)rm -f $(OBJDIR)/mikmod.* $(DEPFILE) + +-include $(DEPFILE) diff --git a/apps/plugins/mikmod/cygwin.bat b/apps/plugins/mikmod/cygwin.bat new file mode 100755 index 0000000..6c2fe02 --- /dev/null +++ b/apps/plugins/mikmod/cygwin.bat @@ -0,0 +1,6 @@ +@echo off + +C: +chdir C:\cygwin\bin + +bash --login -i diff --git a/apps/plugins/mikmod/include/mikmod_build.h b/apps/plugins/mikmod/include/mikmod_build.h new file mode 100644 index 0000000..0f80319 --- /dev/null +++ b/apps/plugins/mikmod/include/mikmod_build.h @@ -0,0 +1,753 @@ +/* MikMod sound library + (c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS + for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: mikmod_build.h,v 1.3 2004/02/19 14:15:22 raph Exp $ + + MikMod sound library include file + +==============================================================================*/ + +#ifndef _MIKMOD_H_ +#define _MIKMOD_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ========== Compiler magic for shared libraries + */ + +#if defined WIN32 && defined _DLL +#ifdef DLL_EXPORTS +#define MIKMODAPI __declspec(dllexport) +#else +#define MIKMODAPI __declspec(dllimport) +#endif +#else +#define MIKMODAPI +#endif + +#define BOOL BOOLTYPE + +#ifdef WIN32 + #undef WIN32 +#endif + +/* + * ========== Library version + */ + +#define LIBMIKMOD_VERSION_MAJOR 3L +#define LIBMIKMOD_VERSION_MINOR 2L +#define LIBMIKMOD_REVISION 0L + +#define LIBMIKMOD_VERSION \ + ((LIBMIKMOD_VERSION_MAJOR<<16)| \ + (LIBMIKMOD_VERSION_MINOR<< 8)| \ + (LIBMIKMOD_REVISION)) + +MIKMODAPI extern long MikMod_GetVersion(void); + +/* + * ========== Platform independent-type definitions + */ + +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#endif + +#if defined(__OS2__)||defined(__EMX__) +#define INCL_DOSSEMAPHORES +#include +#else +typedef char CHAR; +#endif + + + +#if defined(__arch64__) || defined(__alpha) +/* 64 bit architectures */ + +typedef signed char SBYTE; /* 1 byte, signed */ +typedef unsigned char UBYTE; /* 1 byte, unsigned */ +typedef signed short SWORD; /* 2 bytes, signed */ +typedef unsigned short UWORD; /* 2 bytes, unsigned */ +typedef signed int SLONG; /* 4 bytes, signed */ +typedef unsigned int ULONG; /* 4 bytes, unsigned */ +typedef int BOOL; /* 0=false, <>0 true */ + +#else +/* 32 bit architectures */ + +typedef signed char SBYTE; /* 1 byte, signed */ +typedef unsigned char UBYTE; /* 1 byte, unsigned */ +typedef signed short SWORD; /* 2 bytes, signed */ +typedef unsigned short UWORD; /* 2 bytes, unsigned */ +typedef signed long SLONG; /* 4 bytes, signed */ +#if !defined(__OS2__)&&!defined(__EMX__)&&!defined(WIN32) +typedef unsigned long ULONG; /* 4 bytes, unsigned */ +typedef int BOOL; /* 0=false, <>0 true */ +#endif +#endif + +/* + * ========== Error codes + */ + +enum { + MMERR_OPENING_FILE = 1, + MMERR_OUT_OF_MEMORY, + MMERR_DYNAMIC_LINKING, + + MMERR_SAMPLE_TOO_BIG, + MMERR_OUT_OF_HANDLES, + MMERR_UNKNOWN_WAVE_TYPE, + + MMERR_LOADING_PATTERN, + MMERR_LOADING_TRACK, + MMERR_LOADING_HEADER, + MMERR_LOADING_SAMPLEINFO, + MMERR_NOT_A_MODULE, + MMERR_NOT_A_STREAM, + MMERR_MED_SYNTHSAMPLES, + MMERR_ITPACK_INVALID_DATA, + + MMERR_DETECTING_DEVICE, + MMERR_INVALID_DEVICE, + MMERR_INITIALIZING_MIXER, + MMERR_OPENING_AUDIO, + MMERR_8BIT_ONLY, + MMERR_16BIT_ONLY, + MMERR_STEREO_ONLY, + MMERR_ULAW, + MMERR_NON_BLOCK, + + MMERR_AF_AUDIO_PORT, + + MMERR_AIX_CONFIG_INIT, + MMERR_AIX_CONFIG_CONTROL, + MMERR_AIX_CONFIG_START, + + MMERR_GUS_SETTINGS, + MMERR_GUS_RESET, + MMERR_GUS_TIMER, + + MMERR_HP_SETSAMPLESIZE, + MMERR_HP_SETSPEED, + MMERR_HP_CHANNELS, + MMERR_HP_AUDIO_OUTPUT, + MMERR_HP_AUDIO_DESC, + MMERR_HP_BUFFERSIZE, + + MMERR_OSS_SETFRAGMENT, + MMERR_OSS_SETSAMPLESIZE, + MMERR_OSS_SETSTEREO, + MMERR_OSS_SETSPEED, + + MMERR_SGI_SPEED, + MMERR_SGI_16BIT, + MMERR_SGI_8BIT, + MMERR_SGI_STEREO, + MMERR_SGI_MONO, + + MMERR_SUN_INIT, + + MMERR_OS2_MIXSETUP, + MMERR_OS2_SEMAPHORE, + MMERR_OS2_TIMER, + MMERR_OS2_THREAD, + + MMERR_DS_PRIORITY, + MMERR_DS_BUFFER, + MMERR_DS_FORMAT, + MMERR_DS_NOTIFY, + MMERR_DS_EVENT, + MMERR_DS_THREAD, + MMERR_DS_UPDATE, + + MMERR_WINMM_HANDLE, + MMERR_WINMM_ALLOCATED, + MMERR_WINMM_DEVICEID, + MMERR_WINMM_FORMAT, + MMERR_WINMM_UNKNOWN, + + MMERR_MAC_SPEED, + MMERR_MAC_START, + + MMERR_OSX_UNKNOWN_DEVICE, + MMERR_OSX_BAD_PROPERTY, + MMERR_OSX_UNSUPPORTED_FORMAT, + MMERR_OSX_SET_STEREO, + MMERR_OSX_BUFFER_ALLOC, + MMERR_OSX_ADD_IO_PROC, + MMERR_OSX_DEVICE_START, + MMERR_OSX_PTHREAD, + + MMERR_DOSWSS_STARTDMA, + MMERR_DOSSB_STARTDMA, + + MMERR_MAX +}; + +/* + * ========== Error handling + */ + +typedef void (MikMod_handler)(void); +typedef MikMod_handler *MikMod_handler_t; + +MIKMODAPI extern int MikMod_errno; +MIKMODAPI extern BOOL MikMod_critical; +MIKMODAPI extern char *MikMod_strerror(int); + +MIKMODAPI extern MikMod_handler_t MikMod_RegisterErrorHandler(MikMod_handler_t); + +/* + * ========== Library initialization and core functions + */ + +struct MDRIVER; + +MIKMODAPI extern void MikMod_RegisterAllDrivers(void); + +MIKMODAPI extern CHAR* MikMod_InfoDriver(void); +MIKMODAPI extern void MikMod_RegisterDriver(struct MDRIVER*); +MIKMODAPI extern int MikMod_DriverFromAlias(CHAR*); +MIKMODAPI extern struct MDRIVER *MikMod_DriverByOrdinal(int); + +MIKMODAPI extern BOOL MikMod_Init(CHAR*); +MIKMODAPI extern void MikMod_Exit(void); +MIKMODAPI extern BOOL MikMod_Reset(CHAR*); +MIKMODAPI extern BOOL MikMod_SetNumVoices(int,int); +MIKMODAPI extern BOOL MikMod_Active(void); +MIKMODAPI extern BOOL MikMod_EnableOutput(void); +MIKMODAPI extern void MikMod_DisableOutput(void); +MIKMODAPI extern void MikMod_Update(void); + +MIKMODAPI extern BOOL MikMod_InitThreads(void); +MIKMODAPI extern void MikMod_Lock(void); +MIKMODAPI extern void MikMod_Unlock(void); + +/* + * ========== Reader, Writer + */ + +typedef struct MREADER { + BOOL (*Seek)(struct MREADER*,long,int); + long (*Tell)(struct MREADER*); + BOOL (*Read)(struct MREADER*,void*,size_t); + int (*Get)(struct MREADER*); + BOOL (*Eof)(struct MREADER*); +} MREADER; + +typedef struct MWRITER { + BOOL (*Seek)(struct MWRITER*,long,int); + long (*Tell)(struct MWRITER*); + BOOL (*Write)(struct MWRITER*,void*,size_t); + BOOL (*Put)(struct MWRITER*,int); +} MWRITER; + +/* + * ========== Samples + */ + +/* Sample playback should not be interrupted */ +#define SFX_CRITICAL 1 + +/* Sample format [loading and in-memory] flags: */ +#define SF_16BITS 0x0001 +#define SF_STEREO 0x0002 +#define SF_SIGNED 0x0004 +#define SF_BIG_ENDIAN 0x0008 +#define SF_DELTA 0x0010 +#define SF_ITPACKED 0x0020 + +#define SF_FORMATMASK 0x003F + +/* General Playback flags */ + +#define SF_LOOP 0x0100 +#define SF_BIDI 0x0200 +#define SF_REVERSE 0x0400 +#define SF_SUSTAIN 0x0800 + +#define SF_PLAYBACKMASK 0x0C00 + +/* Module-only Playback Flags */ + +#define SF_OWNPAN 0x1000 +#define SF_UST_LOOP 0x2000 + +#define SF_EXTRAPLAYBACKMASK 0x3000 + +/* Panning constants */ +#define PAN_LEFT 0 +#define PAN_HALFLEFT 64 +#define PAN_CENTER 128 +#define PAN_HALFRIGHT 192 +#define PAN_RIGHT 255 +#define PAN_SURROUND 512 /* panning value for Dolby Surround */ + +typedef struct SAMPLE { + SWORD panning; /* panning (0-255 or PAN_SURROUND) */ + ULONG speed; /* Base playing speed/frequency of note */ + UBYTE volume; /* volume 0-64 */ + UWORD inflags; /* sample format on disk */ + UWORD flags; /* sample format in memory */ + ULONG length; /* length of sample (in samples!) */ + ULONG loopstart; /* repeat position (relative to start, in samples) */ + ULONG loopend; /* repeat end */ + ULONG susbegin; /* sustain loop begin (in samples) \ Not Supported */ + ULONG susend; /* sustain loop end / Yet! */ + + /* Variables used by the module player only! (ignored for sound effects) */ + UBYTE globvol; /* global volume */ + UBYTE vibflags; /* autovibrato flag stuffs */ + UBYTE vibtype; /* Vibratos moved from INSTRUMENT to SAMPLE */ + UBYTE vibsweep; + UBYTE vibdepth; + UBYTE vibrate; + CHAR* samplename; /* name of the sample */ + + /* Values used internally only */ + UWORD avibpos; /* autovibrato pos [player use] */ + UBYTE divfactor; /* for sample scaling, maintains proper period slides */ + ULONG seekpos; /* seek position in file */ + SWORD handle; /* sample handle used by individual drivers */ +} SAMPLE; + +/* Sample functions */ + +MIKMODAPI extern SAMPLE *Sample_Load(CHAR*); +MIKMODAPI extern SAMPLE *Sample_LoadFP(int); +MIKMODAPI extern SAMPLE *Sample_LoadGeneric(MREADER*); +MIKMODAPI extern void Sample_Free(SAMPLE*); +MIKMODAPI extern SBYTE Sample_Play(SAMPLE*,ULONG,UBYTE); + +MIKMODAPI extern void Voice_SetVolume(SBYTE,UWORD); +MIKMODAPI extern UWORD Voice_GetVolume(SBYTE); +MIKMODAPI extern void Voice_SetFrequency(SBYTE,ULONG); +MIKMODAPI extern ULONG Voice_GetFrequency(SBYTE); +MIKMODAPI extern void Voice_SetPanning(SBYTE,ULONG); +MIKMODAPI extern ULONG Voice_GetPanning(SBYTE); +MIKMODAPI extern void Voice_Play(SBYTE,SAMPLE*,ULONG); +MIKMODAPI extern void Voice_Stop(SBYTE); +MIKMODAPI extern BOOL Voice_Stopped(SBYTE); +MIKMODAPI extern SLONG Voice_GetPosition(SBYTE); +MIKMODAPI extern ULONG Voice_RealVolume(SBYTE); + +/* + * ========== Internal module representation (UniMod) + */ + +/* + Instrument definition - for information only, the only field which may be + of use in user programs is the name field +*/ + +/* Instrument note count */ +#define INSTNOTES 120 + +/* Envelope point */ +typedef struct ENVPT { + SWORD pos; + SWORD val; +} ENVPT; + +/* Envelope point count */ +#define ENVPOINTS 32 + +/* Instrument structure */ +typedef struct INSTRUMENT { + CHAR* insname; + + UBYTE flags; + UWORD samplenumber[INSTNOTES]; + UBYTE samplenote[INSTNOTES]; + + UBYTE nnatype; + UBYTE dca; /* duplicate check action */ + UBYTE dct; /* duplicate check type */ + UBYTE globvol; + UWORD volfade; + SWORD panning; /* instrument-based panning var */ + + UBYTE pitpansep; /* pitch pan separation (0 to 255) */ + UBYTE pitpancenter; /* pitch pan center (0 to 119) */ + UBYTE rvolvar; /* random volume varations (0 - 100%) */ + UBYTE rpanvar; /* random panning varations (0 - 100%) */ + + /* volume envelope */ + UBYTE volflg; /* bit 0: on 1: sustain 2: loop */ + UBYTE volpts; + UBYTE volsusbeg; + UBYTE volsusend; + UBYTE volbeg; + UBYTE volend; + ENVPT volenv[ENVPOINTS]; + /* panning envelope */ + UBYTE panflg; /* bit 0: on 1: sustain 2: loop */ + UBYTE panpts; + UBYTE pansusbeg; + UBYTE pansusend; + UBYTE panbeg; + UBYTE panend; + ENVPT panenv[ENVPOINTS]; + /* pitch envelope */ + UBYTE pitflg; /* bit 0: on 1: sustain 2: loop */ + UBYTE pitpts; + UBYTE pitsusbeg; + UBYTE pitsusend; + UBYTE pitbeg; + UBYTE pitend; + ENVPT pitenv[ENVPOINTS]; +} INSTRUMENT; + +struct MP_CONTROL; +struct MP_VOICE; + +/* + Module definition +*/ + +/* maximum master channels supported */ +#define UF_MAXCHAN 64 + +/* Module flags */ +#define UF_XMPERIODS 0x0001 /* XM periods / finetuning */ +#define UF_LINEAR 0x0002 /* LINEAR periods (UF_XMPERIODS must be set) */ +#define UF_INST 0x0004 /* Instruments are used */ +#define UF_NNA 0x0008 /* IT: NNA used, set numvoices rather + than numchn */ +#define UF_S3MSLIDES 0x0010 /* uses old S3M volume slides */ +#define UF_BGSLIDES 0x0020 /* continue volume slides in the background */ +#define UF_HIGHBPM 0x0040 /* MED: can use >255 bpm */ +#define UF_NOWRAP 0x0080 /* XM-type (i.e. illogical) pattern break + semantics */ +#define UF_ARPMEM 0x0100 /* IT: need arpeggio memory */ +#define UF_FT2QUIRKS 0x0200 /* emulate some FT2 replay quirks */ +#define UF_PANNING 0x0400 /* module uses panning effects or have + non-tracker default initial panning */ + +typedef struct MODULE { + /* general module information */ + CHAR* songname; /* name of the song */ + CHAR* modtype; /* string type of module loaded */ + CHAR* comment; /* module comments */ + + UWORD flags; /* See module flags above */ + UBYTE numchn; /* number of module channels */ + UBYTE numvoices; /* max # voices used for full NNA playback */ + UWORD numpos; /* number of positions in this song */ + UWORD numpat; /* number of patterns in this song */ + UWORD numins; /* number of instruments */ + UWORD numsmp; /* number of samples */ +struct INSTRUMENT* instruments; /* all instruments */ +struct SAMPLE* samples; /* all samples */ + UBYTE realchn; /* real number of channels used */ + UBYTE totalchn; /* total number of channels used (incl NNAs) */ + + /* playback settings */ + UWORD reppos; /* restart position */ + UBYTE initspeed; /* initial song speed */ + UWORD inittempo; /* initial song tempo */ + UBYTE initvolume; /* initial global volume (0 - 128) */ + UWORD panning[UF_MAXCHAN]; /* panning positions */ + UBYTE chanvol[UF_MAXCHAN]; /* channel positions */ + UWORD bpm; /* current beats-per-minute speed */ + UWORD sngspd; /* current song speed */ + SWORD volume; /* song volume (0-128) (or user volume) */ + + BOOL extspd; /* extended speed flag (default enabled) */ + BOOL panflag; /* panning flag (default enabled) */ + BOOL wrap; /* wrap module ? (default disabled) */ + BOOL loop; /* allow module to loop ? (default enabled) */ + BOOL fadeout; /* volume fade out during last pattern */ + + UWORD patpos; /* current row number */ + SWORD sngpos; /* current song position */ + ULONG sngtime; /* current song time in 2^-10 seconds */ + + SWORD relspd; /* relative speed factor */ + + /* internal module representation */ + UWORD numtrk; /* number of tracks */ + UBYTE** tracks; /* array of numtrk pointers to tracks */ + UWORD* patterns; /* array of Patterns */ + UWORD* pattrows; /* array of number of rows for each pattern */ + UWORD* positions; /* all positions */ + + BOOL forbid; /* if true, no player update! */ + UWORD numrow; /* number of rows on current pattern */ + UWORD vbtick; /* tick counter (counts from 0 to sngspd) */ + UWORD sngremainder;/* used for song time computation */ + +struct MP_CONTROL* control; /* Effects Channel info (size pf->numchn) */ +struct MP_VOICE* voice; /* Audio Voice information (size md_numchn) */ + + UBYTE globalslide; /* global volume slide rate */ + UBYTE pat_repcrazy;/* module has just looped to position -1 */ + UWORD patbrk; /* position where to start a new pattern */ + UBYTE patdly; /* patterndelay counter (command memory) */ + UBYTE patdly2; /* patterndelay counter (real one) */ + SWORD posjmp; /* flag to indicate a jump is needed... */ + UWORD bpmlimit; /* threshold to detect bpm or speed values */ +} MODULE; + + +/* This structure is used to query current playing voices status */ +typedef struct VOICEINFO { + INSTRUMENT* i; /* Current channel instrument */ + SAMPLE* s; /* Current channel sample */ + SWORD panning; /* panning position */ + SBYTE volume; /* channel's "global" volume (0..64) */ + UWORD period; /* period to play the sample at */ + UBYTE kick; /* if true = sample has been restarted */ +} VOICEINFO; + +/* + * ========== Module loaders + */ + +struct MLOADER; + +MIKMODAPI extern CHAR* MikMod_InfoLoader(void); +MIKMODAPI extern void MikMod_RegisterAllLoaders(void); +MIKMODAPI extern void MikMod_RegisterLoader(struct MLOADER*); + +MIKMODAPI extern struct MLOADER load_669; /* 669 and Extended-669 (by Tran/Renaissance) */ +MIKMODAPI extern struct MLOADER load_amf; /* DMP Advanced Module Format (by Otto Chrons) */ +MIKMODAPI extern struct MLOADER load_asy; /* ASYLUM Music Format 1.0 */ +MIKMODAPI extern struct MLOADER load_dsm; /* DSIK internal module format */ +MIKMODAPI extern struct MLOADER load_far; /* Farandole Composer (by Daniel Potter) */ +MIKMODAPI extern struct MLOADER load_gdm; /* General DigiMusic (by Edward Schlunder) */ +MIKMODAPI extern struct MLOADER load_it; /* Impulse Tracker (by Jeffrey Lim) */ +MIKMODAPI extern struct MLOADER load_imf; /* Imago Orpheus (by Lutz Roeder) */ +MIKMODAPI extern struct MLOADER load_med; /* Amiga MED modules (by Teijo Kinnunen) */ +MIKMODAPI extern struct MLOADER load_m15; /* Soundtracker 15-instrument */ +MIKMODAPI extern struct MLOADER load_mod; /* Standard 31-instrument Module loader */ +MIKMODAPI extern struct MLOADER load_mtm; /* Multi-Tracker Module (by Renaissance) */ +MIKMODAPI extern struct MLOADER load_okt; /* Amiga Oktalyzer */ +MIKMODAPI extern struct MLOADER load_stm; /* ScreamTracker 2 (by Future Crew) */ +MIKMODAPI extern struct MLOADER load_stx; /* STMIK 0.2 (by Future Crew) */ +MIKMODAPI extern struct MLOADER load_s3m; /* ScreamTracker 3 (by Future Crew) */ +MIKMODAPI extern struct MLOADER load_ult; /* UltraTracker (by MAS) */ +MIKMODAPI extern struct MLOADER load_uni; /* MikMod and APlayer internal module format */ +MIKMODAPI extern struct MLOADER load_xm; /* FastTracker 2 (by Triton) */ + +/* + * ========== Module player + */ + +MIKMODAPI extern MODULE* Player_Load(CHAR*,int,BOOL); +MIKMODAPI extern MODULE* Player_LoadFP(int,int,BOOL); +MIKMODAPI extern MODULE* Player_LoadGeneric(MREADER*,int,BOOL); +MIKMODAPI extern CHAR* Player_LoadTitle(CHAR*); +MIKMODAPI extern CHAR* Player_LoadTitleFP(int); +MIKMODAPI extern void Player_Free(MODULE*); +MIKMODAPI extern void Player_Start(MODULE*); +MIKMODAPI extern BOOL Player_Active(void); +MIKMODAPI extern void Player_Stop(void); +MIKMODAPI extern void Player_TogglePause(void); +MIKMODAPI extern BOOL Player_Paused(void); +MIKMODAPI extern void Player_NextPosition(void); +MIKMODAPI extern void Player_PrevPosition(void); +MIKMODAPI extern void Player_SetPosition(UWORD); +MIKMODAPI extern BOOL Player_Muted(UBYTE); +MIKMODAPI extern void Player_SetVolume(SWORD); +MIKMODAPI extern MODULE* Player_GetModule(void); +MIKMODAPI extern void Player_SetSpeed(UWORD); +MIKMODAPI extern void Player_SetTempo(UWORD); +MIKMODAPI extern void Player_Unmute(SLONG,...); +MIKMODAPI extern void Player_Mute(SLONG,...); +MIKMODAPI extern void Player_ToggleMute(SLONG,...); +MIKMODAPI extern int Player_GetChannelVoice(UBYTE); +MIKMODAPI extern UWORD Player_GetChannelPeriod(UBYTE); +MIKMODAPI extern int Player_QueryVoices(UWORD numvoices, VOICEINFO *vinfo); + +typedef void (MikMod_player)(void); +typedef MikMod_player *MikMod_player_t; + +MIKMODAPI extern MikMod_player_t MikMod_RegisterPlayer(MikMod_player_t); + +#define MUTE_EXCLUSIVE 32000 +#define MUTE_INCLUSIVE 32001 + +/* + * ========== Drivers + */ + +enum { + MD_MUSIC = 0, + MD_SNDFX +}; + +enum { + MD_HARDWARE = 0, + MD_SOFTWARE +}; + +/* Mixing flags */ + +/* These ones take effect only after MikMod_Init or MikMod_Reset */ +#define DMODE_16BITS 0x0001 /* enable 16 bit output */ +#define DMODE_STEREO 0x0002 /* enable stereo output */ +#define DMODE_SOFT_SNDFX 0x0004 /* Process sound effects via software mixer */ +#define DMODE_SOFT_MUSIC 0x0008 /* Process music via software mixer */ +#define DMODE_HQMIXER 0x0010 /* Use high-quality (slower) software mixer */ +#define DMODE_FLOAT 0x0020 /* enable float output */ +/* These take effect immediately. */ +#define DMODE_SURROUND 0x0100 /* enable surround sound */ +#define DMODE_INTERP 0x0200 /* enable interpolation */ +#define DMODE_REVERSE 0x0400 /* reverse stereo */ + +struct SAMPLOAD; +typedef struct MDRIVER { +struct MDRIVER* next; + CHAR* Name; + CHAR* Version; + + UBYTE HardVoiceLimit; /* Limit of hardware mixer voices */ + UBYTE SoftVoiceLimit; /* Limit of software mixer voices */ + + CHAR *Alias; + CHAR *CmdLineHelp; + + void (*CommandLine) (CHAR*); + BOOL (*IsPresent) (void); + SWORD (*SampleLoad) (struct SAMPLOAD*,int); + void (*SampleUnload) (SWORD); + ULONG (*FreeSampleSpace) (int); + ULONG (*RealSampleLength) (int,struct SAMPLE*); + BOOL (*Init) (void); + void (*Exit) (void); + BOOL (*Reset) (void); + BOOL (*SetNumVoices) (void); + BOOL (*PlayStart) (void); + void (*PlayStop) (void); + void (*Update) (void); + void (*Pause) (void); + void (*VoiceSetVolume) (UBYTE,UWORD); + UWORD (*VoiceGetVolume) (UBYTE); + void (*VoiceSetFrequency)(UBYTE,ULONG); + ULONG (*VoiceGetFrequency)(UBYTE); + void (*VoiceSetPanning) (UBYTE,ULONG); + ULONG (*VoiceGetPanning) (UBYTE); + void (*VoicePlay) (UBYTE,SWORD,ULONG,ULONG,ULONG,ULONG,UWORD); + void (*VoiceStop) (UBYTE); + BOOL (*VoiceStopped) (UBYTE); + SLONG (*VoiceGetPosition) (UBYTE); + ULONG (*VoiceRealVolume) (UBYTE); +} MDRIVER; + +/* These variables can be changed at ANY time and results will be immediate */ +MIKMODAPI extern UBYTE md_volume; /* global sound volume (0-128) */ +MIKMODAPI extern UBYTE md_musicvolume; /* volume of song */ +MIKMODAPI extern UBYTE md_sndfxvolume; /* volume of sound effects */ +MIKMODAPI extern UBYTE md_reverb; /* 0 = none; 15 = chaos */ +MIKMODAPI extern UBYTE md_pansep; /* 0 = mono; 128 == 100% (full left/right) */ + +/* The variables below can be changed at any time, but changes will not be + implemented until MikMod_Reset is called. A call to MikMod_Reset may result + in a skip or pop in audio (depending on the soundcard driver and the settings + changed). */ +MIKMODAPI extern UWORD md_device; /* device */ +MIKMODAPI extern UWORD md_mixfreq; /* mixing frequency */ +MIKMODAPI extern UWORD md_mode; /* mode. See DMODE_? flags above */ + +/* The following variable should not be changed! */ +MIKMODAPI extern MDRIVER* md_driver; /* Current driver in use. */ + +/* Known drivers list */ + +MIKMODAPI extern struct MDRIVER drv_nos; /* no sound */ +MIKMODAPI extern struct MDRIVER drv_pipe; /* piped output */ +MIKMODAPI extern struct MDRIVER drv_raw; /* raw file disk writer [music.raw] */ +MIKMODAPI extern struct MDRIVER drv_stdout; /* output to stdout */ +MIKMODAPI extern struct MDRIVER drv_wav; /* RIFF WAVE file disk writer [music.wav] */ +MIKMODAPI extern struct MDRIVER drv_aiff; /* AIFF file disk writer [music.aiff] */ + +MIKMODAPI extern struct MDRIVER drv_ultra; /* Linux Ultrasound driver */ +MIKMODAPI extern struct MDRIVER drv_sam9407; /* Linux sam9407 driver */ + +MIKMODAPI extern struct MDRIVER drv_AF; /* Dec Alpha AudioFile */ +MIKMODAPI extern struct MDRIVER drv_aix; /* AIX audio device */ +MIKMODAPI extern struct MDRIVER drv_alsa; /* Advanced Linux Sound Architecture (ALSA) */ +MIKMODAPI extern struct MDRIVER drv_esd; /* Enlightened sound daemon (EsounD) */ +MIKMODAPI extern struct MDRIVER drv_hp; /* HP-UX audio device */ +MIKMODAPI extern struct MDRIVER drv_oss; /* OpenSound System (Linux,FreeBSD...) */ +MIKMODAPI extern struct MDRIVER drv_sgi; /* SGI audio library */ +MIKMODAPI extern struct MDRIVER drv_sun; /* Sun/NetBSD/OpenBSD audio device */ + +MIKMODAPI extern struct MDRIVER drv_dart; /* OS/2 Direct Audio RealTime */ +MIKMODAPI extern struct MDRIVER drv_os2; /* OS/2 MMPM/2 */ + +MIKMODAPI extern struct MDRIVER drv_ds; /* Win32 DirectSound driver */ +MIKMODAPI extern struct MDRIVER drv_win; /* Win32 multimedia API driver */ + +MIKMODAPI extern struct MDRIVER drv_mac; /* Macintosh Sound Manager driver */ +MIKMODAPI extern struct MDRIVER drv_osx; /* MacOS X CoreAudio Driver */ + +/*========== Virtual channel mixer interface (for user-supplied drivers only) */ + +MIKMODAPI extern BOOL VC_Init(void); +MIKMODAPI extern void VC_Exit(void); +MIKMODAPI extern BOOL VC_SetNumVoices(void); +MIKMODAPI extern ULONG VC_SampleSpace(int); +MIKMODAPI extern ULONG VC_SampleLength(int,SAMPLE*); + +MIKMODAPI extern BOOL VC_PlayStart(void); +MIKMODAPI extern void VC_PlayStop(void); + +MIKMODAPI extern SWORD VC_SampleLoad(struct SAMPLOAD*,int); +MIKMODAPI extern void VC_SampleUnload(SWORD); + +MIKMODAPI extern ULONG VC_WriteBytes(SBYTE*,ULONG); +MIKMODAPI extern ULONG VC_SilenceBytes(SBYTE*,ULONG); + +MIKMODAPI extern void VC_VoiceSetVolume(UBYTE,UWORD); +MIKMODAPI extern UWORD VC_VoiceGetVolume(UBYTE); +MIKMODAPI extern void VC_VoiceSetFrequency(UBYTE,ULONG); +MIKMODAPI extern ULONG VC_VoiceGetFrequency(UBYTE); +MIKMODAPI extern void VC_VoiceSetPanning(UBYTE,ULONG); +MIKMODAPI extern ULONG VC_VoiceGetPanning(UBYTE); +MIKMODAPI extern void VC_VoicePlay(UBYTE,SWORD,ULONG,ULONG,ULONG,ULONG,UWORD); + +MIKMODAPI extern void VC_VoiceStop(UBYTE); +MIKMODAPI extern BOOL VC_VoiceStopped(UBYTE); +MIKMODAPI extern SLONG VC_VoiceGetPosition(UBYTE); +MIKMODAPI extern ULONG VC_VoiceRealVolume(UBYTE); + +#ifdef __cplusplus +} +#endif + +#endif + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/include/mikmod_internals.h b/apps/plugins/mikmod/include/mikmod_internals.h new file mode 100644 index 0000000..53dd755 --- /dev/null +++ b/apps/plugins/mikmod/include/mikmod_internals.h @@ -0,0 +1,729 @@ +/* MikMod sound library + (c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: mikmod_internals.h,v 1.4 2004/02/18 13:29:17 raph Exp $ + + MikMod sound library internal definitions + +==============================================================================*/ + +#ifndef _MIKMOD_INTERNALS_H +#define _MIKMOD_INTERNALS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_MALLOC_H +#include +#endif +#include +#if defined(__OS2__)||defined(__EMX__)||defined(WIN32) +#define strcasecmp(s,t) stricmp(s,t) +#endif + + +#include "plugin.h" +extern struct plugin_api *rb; + +#include + +#ifdef WIN32 +#pragma warning(disable:4761) +#endif + +/*========== More type definitions */ + +/* SLONGLONG: 64bit, signed */ +typedef long long SLONGLONG; + +/*========== Error handling */ + +#define _mm_errno MikMod_errno +#define _mm_critical MikMod_critical +extern MikMod_handler_t _mm_errorhandler; + +/*========== Memory allocation */ + +extern void* _mm_malloc(size_t); +extern void* _mm_calloc(size_t,size_t); +#define _mm_free(p) do { if (p) free(p); p = NULL; } while(0) + +/*========== MT stuff */ + +#ifdef HAVE_PTHREAD +#include +#define DECLARE_MUTEX(name) \ + extern pthread_mutex_t _mm_mutex_##name +#define MUTEX_LOCK(name) \ + pthread_mutex_lock(&_mm_mutex_##name) +#define MUTEX_UNLOCK(name) \ + pthread_mutex_unlock(&_mm_mutex_##name) +#elif defined(__OS2__)||defined(__EMX__) +#define DECLARE_MUTEX(name) \ + extern HMTX _mm_mutex_##name +#define MUTEX_LOCK(name) \ + if(_mm_mutex_##name) \ + DosRequestMutexSem(_mm_mutex_##name,SEM_INDEFINITE_WAIT) +#define MUTEX_UNLOCK(name) \ + if(_mm_mutex_##name) \ + DosReleaseMutexSem(_mm_mutex_##name) +#elif defined(WIN32) +#include +#define DECLARE_MUTEX(name) \ + extern HANDLE _mm_mutex_##name +#define MUTEX_LOCK(name) \ + if(_mm_mutex_##name) \ + WaitForSingleObject(_mm_mutex_##name,INFINITE) +#define MUTEX_UNLOCK(name) \ + if(_mm_mutex_##name) \ + ReleaseMutex(_mm_mutex_##name) +#else +#define DECLARE_MUTEX(name) \ + extern void *_mm_mutex_##name +#define MUTEX_LOCK(name) +#define MUTEX_UNLOCK(name) +#endif + +DECLARE_MUTEX(lists); +DECLARE_MUTEX(vars); + +/*========== Portable file I/O */ + +extern MREADER* _mm_new_file_reader(int fp); +extern void _mm_delete_file_reader(MREADER*); + +extern MWRITER* _mm_new_file_writer(int fp); +extern void _mm_delete_file_writer(MWRITER*); + +extern BOOL _mm_FileExists(CHAR *fname); + +#define _mm_write_SBYTE(x,y) y->Put(y,(int)x) +#define _mm_write_UBYTE(x,y) y->Put(y,(int)x) + +#define _mm_read_SBYTE(x) (SBYTE)x->Get(x) +#define _mm_read_UBYTE(x) (UBYTE)x->Get(x) + +#define _mm_write_SBYTES(x,y,z) z->Write(z,(void *)x,y) +#define _mm_write_UBYTES(x,y,z) z->Write(z,(void *)x,y) +#define _mm_read_SBYTES(x,y,z) z->Read(z,(void *)x,y) +#define _mm_read_UBYTES(x,y,z) z->Read(z,(void *)x,y) + +#define _mm_fseek(x,y,z) x->Seek(x,y,z) +#define _mm_ftell(x) x->Tell(x) +#define _mm_rewind(x) _mm_fseek(x,0,SEEK_SET) + +#define _mm_eof(x) x->Eof(x) + +extern void _mm_iobase_setcur(MREADER*); +extern void _mm_iobase_revert(void); +extern int _mm_fopen(CHAR*,int); +extern int _mm_fclose(int); +extern void _mm_write_string(CHAR*,MWRITER*); +extern int _mm_read_string (CHAR*,int,MREADER*); + +extern SWORD _mm_read_M_SWORD(MREADER*); +extern SWORD _mm_read_I_SWORD(MREADER*); +extern UWORD _mm_read_M_UWORD(MREADER*); +extern UWORD _mm_read_I_UWORD(MREADER*); + +extern SLONG _mm_read_M_SLONG(MREADER*); +extern SLONG _mm_read_I_SLONG(MREADER*); +extern ULONG _mm_read_M_ULONG(MREADER*); +extern ULONG _mm_read_I_ULONG(MREADER*); + +extern int _mm_read_M_SWORDS(SWORD*,int,MREADER*); +extern int _mm_read_I_SWORDS(SWORD*,int,MREADER*); +extern int _mm_read_M_UWORDS(UWORD*,int,MREADER*); +extern int _mm_read_I_UWORDS(UWORD*,int,MREADER*); + +extern int _mm_read_M_SLONGS(SLONG*,int,MREADER*); +extern int _mm_read_I_SLONGS(SLONG*,int,MREADER*); +extern int _mm_read_M_ULONGS(ULONG*,int,MREADER*); +extern int _mm_read_I_ULONGS(ULONG*,int,MREADER*); + +extern void _mm_write_M_SWORD(SWORD,MWRITER*); +extern void _mm_write_I_SWORD(SWORD,MWRITER*); +extern void _mm_write_M_UWORD(UWORD,MWRITER*); +extern void _mm_write_I_UWORD(UWORD,MWRITER*); + +extern void _mm_write_M_SLONG(SLONG,MWRITER*); +extern void _mm_write_I_SLONG(SLONG,MWRITER*); +extern void _mm_write_M_ULONG(ULONG,MWRITER*); +extern void _mm_write_I_ULONG(ULONG,MWRITER*); + +extern void _mm_write_M_SWORDS(SWORD*,int,MWRITER*); +extern void _mm_write_I_SWORDS(SWORD*,int,MWRITER*); +extern void _mm_write_M_UWORDS(UWORD*,int,MWRITER*); +extern void _mm_write_I_UWORDS(UWORD*,int,MWRITER*); + +extern void _mm_write_M_SLONGS(SLONG*,int,MWRITER*); +extern void _mm_write_I_SLONGS(SLONG*,int,MWRITER*); +extern void _mm_write_M_ULONGS(ULONG*,int,MWRITER*); +extern void _mm_write_I_ULONGS(ULONG*,int,MWRITER*); + +/*========== Samples */ + +/* This is a handle of sorts attached to any sample registered with + SL_RegisterSample. Generally, this only need be used or changed by the + loaders and drivers of mikmod. */ +typedef struct SAMPLOAD { + struct SAMPLOAD *next; + + ULONG length; /* length of sample (in samples!) */ + ULONG loopstart; /* repeat position (relative to start, in samples) */ + ULONG loopend; /* repeat end */ + UWORD infmt,outfmt; + int scalefactor; + SAMPLE* sample; + MREADER* reader; +} SAMPLOAD; + +/*========== Sample and waves loading interface */ + +extern void SL_HalveSample(SAMPLOAD*,int); +extern void SL_Sample8to16(SAMPLOAD*); +extern void SL_Sample16to8(SAMPLOAD*); +extern void SL_SampleSigned(SAMPLOAD*); +extern void SL_SampleUnsigned(SAMPLOAD*); +extern BOOL SL_LoadSamples(void); +extern SAMPLOAD* SL_RegisterSample(SAMPLE*,int,MREADER*); +extern BOOL SL_Load(void*,SAMPLOAD*,ULONG); +extern BOOL SL_Init(SAMPLOAD*); +extern void SL_Exit(SAMPLOAD*); + +/*========== Internal module representation (UniMod) interface */ + +/* number of notes in an octave */ +#define OCTAVE 12 + +extern void UniSetRow(UBYTE*); +extern UBYTE UniGetByte(void); +extern UWORD UniGetWord(void); +extern UBYTE* UniFindRow(UBYTE*,UWORD); +extern void UniSkipOpcode(void); +extern void UniReset(void); +extern void UniWriteByte(UBYTE); +extern void UniWriteWord(UWORD); +extern void UniNewline(void); +extern UBYTE* UniDup(void); +extern BOOL UniInit(void); +extern void UniCleanup(void); +extern void UniEffect(UWORD,UWORD); +#define UniInstrument(x) UniEffect(UNI_INSTRUMENT,x) +#define UniNote(x) UniEffect(UNI_NOTE,x) +extern void UniPTEffect(UBYTE,UBYTE); +extern void UniVolEffect(UWORD,UBYTE); + +/*========== Module Commands */ + +enum { + /* Simple note */ + UNI_NOTE = 1, + /* Instrument change */ + UNI_INSTRUMENT, + /* Protracker effects */ + UNI_PTEFFECT0, /* arpeggio */ + UNI_PTEFFECT1, /* porta up */ + UNI_PTEFFECT2, /* porta down */ + UNI_PTEFFECT3, /* porta to note */ + UNI_PTEFFECT4, /* vibrato */ + UNI_PTEFFECT5, /* dual effect 3+A */ + UNI_PTEFFECT6, /* dual effect 4+A */ + UNI_PTEFFECT7, /* tremolo */ + UNI_PTEFFECT8, /* pan */ + UNI_PTEFFECT9, /* sample offset */ + UNI_PTEFFECTA, /* volume slide */ + UNI_PTEFFECTB, /* pattern jump */ + UNI_PTEFFECTC, /* set volume */ + UNI_PTEFFECTD, /* pattern break */ + UNI_PTEFFECTE, /* extended effects */ + UNI_PTEFFECTF, /* set speed */ + /* Scream Tracker effects */ + UNI_S3MEFFECTA, /* set speed */ + UNI_S3MEFFECTD, /* volume slide */ + UNI_S3MEFFECTE, /* porta down */ + UNI_S3MEFFECTF, /* porta up */ + UNI_S3MEFFECTI, /* tremor */ + UNI_S3MEFFECTQ, /* retrig */ + UNI_S3MEFFECTR, /* tremolo */ + UNI_S3MEFFECTT, /* set tempo */ + UNI_S3MEFFECTU, /* fine vibrato */ + UNI_KEYOFF, /* note off */ + /* Fast Tracker effects */ + UNI_KEYFADE, /* note fade */ + UNI_VOLEFFECTS, /* volume column effects */ + UNI_XMEFFECT4, /* vibrato */ + UNI_XMEFFECT6, /* dual effect 4+A */ + UNI_XMEFFECTA, /* volume slide */ + UNI_XMEFFECTE1, /* fine porta up */ + UNI_XMEFFECTE2, /* fine porta down */ + UNI_XMEFFECTEA, /* fine volume slide up */ + UNI_XMEFFECTEB, /* fine volume slide down */ + UNI_XMEFFECTG, /* set global volume */ + UNI_XMEFFECTH, /* global volume slide */ + UNI_XMEFFECTL, /* set envelope position */ + UNI_XMEFFECTP, /* pan slide */ + UNI_XMEFFECTX1, /* extra fine porta up */ + UNI_XMEFFECTX2, /* extra fine porta down */ + /* Impulse Tracker effects */ + UNI_ITEFFECTG, /* porta to note */ + UNI_ITEFFECTH, /* vibrato */ + UNI_ITEFFECTI, /* tremor (xy not incremented) */ + UNI_ITEFFECTM, /* set channel volume */ + UNI_ITEFFECTN, /* slide / fineslide channel volume */ + UNI_ITEFFECTP, /* slide / fineslide channel panning */ + UNI_ITEFFECTT, /* slide tempo */ + UNI_ITEFFECTU, /* fine vibrato */ + UNI_ITEFFECTW, /* slide / fineslide global volume */ + UNI_ITEFFECTY, /* panbrello */ + UNI_ITEFFECTZ, /* resonant filters */ + UNI_ITEFFECTS0, + /* UltraTracker effects */ + UNI_ULTEFFECT9, /* Sample fine offset */ + /* OctaMED effects */ + UNI_MEDSPEED, + UNI_MEDEFFECTF1, /* play note twice */ + UNI_MEDEFFECTF2, /* delay note */ + UNI_MEDEFFECTF3, /* play note three times */ + /* Oktalyzer effects */ + UNI_OKTARP, /* arpeggio */ + + UNI_LAST +}; + +extern UWORD unioperands[UNI_LAST]; + +/* IT / S3M Extended SS effects: */ +enum { + SS_GLISSANDO = 1, + SS_FINETUNE, + SS_VIBWAVE, + SS_TREMWAVE, + SS_PANWAVE, + SS_FRAMEDELAY, + SS_S7EFFECTS, + SS_PANNING, + SS_SURROUND, + SS_HIOFFSET, + SS_PATLOOP, + SS_NOTECUT, + SS_NOTEDELAY, + SS_PATDELAY +}; + +/* IT Volume column effects */ +enum { + VOL_VOLUME = 1, + VOL_PANNING, + VOL_VOLSLIDE, + VOL_PITCHSLIDEDN, + VOL_PITCHSLIDEUP, + VOL_PORTAMENTO, + VOL_VIBRATO +}; + +/* IT resonant filter information */ + +#define UF_MAXMACRO 0x10 +#define UF_MAXFILTER 0x100 + +#define FILT_CUT 0x80 +#define FILT_RESONANT 0x81 + +typedef struct FILTER { + UBYTE filter,inf; +} FILTER; + +/*========== Instruments */ + +/* Instrument format flags */ +#define IF_OWNPAN 1 +#define IF_PITCHPAN 2 + +/* Envelope flags: */ +#define EF_ON 1 +#define EF_SUSTAIN 2 +#define EF_LOOP 4 +#define EF_VOLENV 8 + +/* New Note Action Flags */ +#define NNA_CUT 0 +#define NNA_CONTINUE 1 +#define NNA_OFF 2 +#define NNA_FADE 3 + +#define NNA_MASK 3 + +#define DCT_OFF 0 +#define DCT_NOTE 1 +#define DCT_SAMPLE 2 +#define DCT_INST 3 + +#define DCA_CUT 0 +#define DCA_OFF 1 +#define DCA_FADE 2 + +#define KEY_KICK 0 +#define KEY_OFF 1 +#define KEY_FADE 2 +#define KEY_KILL (KEY_OFF|KEY_FADE) + +#define KICK_ABSENT 0 +#define KICK_NOTE 1 +#define KICK_KEYOFF 2 +#define KICK_ENV 4 + +#define AV_IT 1 /* IT vs. XM vibrato info */ + +/*========== Playing */ + +#define POS_NONE (-2) /* no loop position defined */ + +#define LAST_PATTERN (UWORD)(-1) /* special ``end of song'' pattern */ + +typedef struct ENVPR { + UBYTE flg; /* envelope flag */ + UBYTE pts; /* number of envelope points */ + UBYTE susbeg; /* envelope sustain index begin */ + UBYTE susend; /* envelope sustain index end */ + UBYTE beg; /* envelope loop begin */ + UBYTE end; /* envelope loop end */ + SWORD p; /* current envelope counter */ + UWORD a; /* envelope index a */ + UWORD b; /* envelope index b */ + ENVPT* env; /* envelope points */ +} ENVPR; + +typedef struct MP_CHANNEL { + INSTRUMENT* i; + SAMPLE* s; + UBYTE sample; /* which sample number */ + UBYTE note; /* the audible note as heard, direct rep of period */ + SWORD outvolume; /* output volume (vol + sampcol + instvol) */ + SBYTE chanvol; /* channel's "global" volume */ + UWORD fadevol; /* fading volume rate */ + SWORD panning; /* panning position */ + UBYTE kick; /* if true = sample has to be restarted */ + UBYTE kick_flag; /* kick has been true */ + UWORD period; /* period to play the sample at */ + UBYTE nna; /* New note action type + master/slave flags */ + + UBYTE volflg; /* volume envelope settings */ + UBYTE panflg; /* panning envelope settings */ + UBYTE pitflg; /* pitch envelope settings */ + + UBYTE keyoff; /* if true = fade out and stuff */ + SWORD handle; /* which sample-handle */ + UBYTE notedelay; /* (used for note delay) */ + SLONG start; /* The starting byte index in the sample */ +} MP_CHANNEL; + +typedef struct MP_CONTROL { + struct MP_CHANNEL main; + + struct MP_VOICE *slave; /* Audio Slave of current effects control channel */ + + UBYTE slavechn; /* Audio Slave of current effects control channel */ + UBYTE muted; /* if set, channel not played */ + UWORD ultoffset; /* fine sample offset memory */ + UBYTE anote; /* the note that indexes the audible */ + UBYTE oldnote; + SWORD ownper; + SWORD ownvol; + UBYTE dca; /* duplicate check action */ + UBYTE dct; /* duplicate check type */ + UBYTE* row; /* row currently playing on this channel */ + SBYTE retrig; /* retrig value (0 means don't retrig) */ + ULONG speed; /* what finetune to use */ + SWORD volume; /* amiga volume (0 t/m 64) to play the sample at */ + + SWORD tmpvolume; /* tmp volume */ + UWORD tmpperiod; /* tmp period */ + UWORD wantedperiod; /* period to slide to (with effect 3 or 5) */ + + UBYTE arpmem; /* arpeggio command memory */ + UBYTE pansspd; /* panslide speed */ + UWORD slidespeed; + UWORD portspeed; /* noteslide speed (toneportamento) */ + + UBYTE s3mtremor; /* s3m tremor (effect I) counter */ + UBYTE s3mtronof; /* s3m tremor ontime/offtime */ + UBYTE s3mvolslide; /* last used volslide */ + SBYTE sliding; + UBYTE s3mrtgspeed; /* last used retrig speed */ + UBYTE s3mrtgslide; /* last used retrig slide */ + + UBYTE glissando; /* glissando (0 means off) */ + UBYTE wavecontrol; + + SBYTE vibpos; /* current vibrato position */ + UBYTE vibspd; /* "" speed */ + UBYTE vibdepth; /* "" depth */ + + SBYTE trmpos; /* current tremolo position */ + UBYTE trmspd; /* "" speed */ + UBYTE trmdepth; /* "" depth */ + + UBYTE fslideupspd; + UBYTE fslidednspd; + UBYTE fportupspd; /* fx E1 (extra fine portamento up) data */ + UBYTE fportdnspd; /* fx E2 (extra fine portamento dn) data */ + UBYTE ffportupspd; /* fx X1 (extra fine portamento up) data */ + UBYTE ffportdnspd; /* fx X2 (extra fine portamento dn) data */ + + ULONG hioffset; /* last used high order of sample offset */ + UWORD soffset; /* last used low order of sample-offset (effect 9) */ + + UBYTE sseffect; /* last used Sxx effect */ + UBYTE ssdata; /* last used Sxx data info */ + UBYTE chanvolslide; /* last used channel volume slide */ + + UBYTE panbwave; /* current panbrello waveform */ + UBYTE panbpos; /* current panbrello position */ + SBYTE panbspd; /* "" speed */ + UBYTE panbdepth; /* "" depth */ + + UWORD newsamp; /* set to 1 upon a sample / inst change */ + UBYTE voleffect; /* Volume Column Effect Memory as used by IT */ + UBYTE voldata; /* Volume Column Data Memory */ + + SWORD pat_reppos; /* patternloop position */ + UWORD pat_repcnt; /* times to loop */ +} MP_CONTROL; + +/* Used by NNA only player (audio control. AUDTMP is used for full effects + control). */ +typedef struct MP_VOICE { + struct MP_CHANNEL main; + + ENVPR venv; + ENVPR penv; + ENVPR cenv; + + UWORD avibpos; /* autovibrato pos */ + UWORD aswppos; /* autovibrato sweep pos */ + + ULONG totalvol; /* total volume of channel (before global mixings) */ + + BOOL mflag; + SWORD masterchn; + UWORD masterperiod; + + MP_CONTROL* master; /* index of "master" effects channel */ +} MP_VOICE; + +/*========== Loaders */ + +typedef struct MLOADER { +struct MLOADER* next; + CHAR* type; + CHAR* version; + BOOL (*Init)(void); + BOOL (*Test)(void); + BOOL (*Load)(BOOL); + void (*Cleanup)(void); + CHAR* (*LoadTitle)(void); +} MLOADER; + +/* internal loader variables */ +extern MREADER* modreader; +extern UWORD finetune[16]; +extern MODULE of; /* static unimod loading space */ +extern UWORD npertab[7*OCTAVE]; /* used by the original MOD loaders */ + +extern SBYTE remap[UF_MAXCHAN]; /* for removing empty channels */ +extern UBYTE* poslookup; /* lookup table for pattern jumps after + blank pattern removal */ +extern UBYTE poslookupcnt; +extern UWORD* origpositions; + +extern BOOL filters; /* resonant filters in use */ +extern UBYTE activemacro; /* active midi macro number for Sxx */ +extern UBYTE filtermacros[UF_MAXMACRO]; /* midi macro settings */ +extern FILTER filtersettings[UF_MAXFILTER]; /* computed filter settings */ + +extern int* noteindex; + +/*========== Internal loader interface */ + +extern BOOL ReadComment(UWORD); +extern BOOL ReadLinedComment(UWORD,UWORD); +extern BOOL AllocPositions(int); +extern BOOL AllocPatterns(void); +extern BOOL AllocTracks(void); +extern BOOL AllocInstruments(void); +extern BOOL AllocSamples(void); +extern CHAR* DupStr(CHAR*,UWORD,BOOL); + +/* loader utility functions */ +extern int* AllocLinear(void); +extern void FreeLinear(void); +extern int speed_to_finetune(ULONG,int); +extern void S3MIT_ProcessCmd(UBYTE,UBYTE,unsigned int); +extern void S3MIT_CreateOrders(BOOL); + +/* flags for S3MIT_ProcessCmd */ +#define S3MIT_OLDSTYLE 1 /* behave as old scream tracker */ +#define S3MIT_IT 2 /* behave as impulse tracker */ +#define S3MIT_SCREAM 4 /* enforce scream tracker specific limits */ + +/* used to convert c4spd to linear XM periods (IT and IMF loaders). */ +extern UWORD getlinearperiod(UWORD,ULONG); +extern ULONG getfrequency(UWORD,ULONG); + +/* loader shared data */ +#define STM_NTRACKERS 3 +extern CHAR *STM_Signatures[STM_NTRACKERS]; + +/*========== Player interface */ + +extern BOOL Player_Init(MODULE*); +extern void Player_Exit(MODULE*); +extern void Player_HandleTick(void); + +/*========== Drivers */ + +/* max. number of handles a driver has to provide. (not strict) */ +#define MAXSAMPLEHANDLES 384 + +/* These variables can be changed at ANY time and results will be immediate */ +extern UWORD md_bpm; /* current song / hardware BPM rate */ + +/* Variables below can be changed via MD_SetNumVoices at any time. However, a + call to MD_SetNumVoicess while the driver is active will cause the sound to + skip slightly. */ +extern UBYTE md_numchn; /* number of song + sound effects voices */ +extern UBYTE md_sngchn; /* number of song voices */ +extern UBYTE md_sfxchn; /* number of sound effects voices */ +extern UBYTE md_hardchn; /* number of hardware mixed voices */ +extern UBYTE md_softchn; /* number of software mixed voices */ + +/* This is for use by the hardware drivers only. It points to the registered + tickhandler function. */ +extern void (*md_player)(void); + +extern SWORD MD_SampleLoad(SAMPLOAD*,int); +extern void MD_SampleUnload(SWORD); +extern ULONG MD_SampleSpace(int); +extern ULONG MD_SampleLength(int,SAMPLE*); + +/* uLaw conversion */ +extern void unsignedtoulaw(char *,int); + +/* Parameter extraction helper */ +extern CHAR *MD_GetAtom(CHAR*,CHAR*,BOOL); + +/* Internal software mixer stuff */ +extern void VC_SetupPointers(void); +extern BOOL VC1_Init(void); +extern BOOL VC2_Init(void); + +#if defined(unix) || defined(__APPLE__) && defined(__MACH__) +/* POSIX helper functions */ +extern BOOL MD_Access(CHAR *); +extern BOOL MD_DropPrivileges(void); +#endif + +/* Macro to define a missing driver, yet allowing binaries to dynamically link + with the library without missing symbol errors */ +#define MISSING(a) MDRIVER a = { NULL, NULL, NULL, 0, 0 } + +/*========== Prototypes for non-MT safe versions of some public functions */ + +extern void _mm_registerdriver(struct MDRIVER*); +extern void _mm_registerloader(struct MLOADER*); +extern BOOL MikMod_Active_internal(void); +extern void MikMod_DisableOutput_internal(void); +extern BOOL MikMod_EnableOutput_internal(void); +extern void MikMod_Exit_internal(void); +extern BOOL MikMod_SetNumVoices_internal(int,int); +extern void Player_Exit_internal(MODULE*); +extern void Player_Stop_internal(void); +extern BOOL Player_Paused_internal(void); +extern void Sample_Free_internal(SAMPLE*); +extern void Voice_Play_internal(SBYTE,SAMPLE*,ULONG); +extern void Voice_SetFrequency_internal(SBYTE,ULONG); +extern void Voice_SetPanning_internal(SBYTE,ULONG); +extern void Voice_SetVolume_internal(SBYTE,UWORD); +extern void Voice_Stop_internal(SBYTE); +extern BOOL Voice_Stopped_internal(SBYTE); + + +/* + * Static memory management + rockboxed function calls + */ +void* mikmod_malloc(size_t size); +void* mikmod_calloc(size_t nmemb, size_t size); +void* mikmod_realloc(void* ptr, size_t size); +void mikmod_free(void* ptr); +int mikmod_abs(int num); +char* mikmod_strdup(const char *srcbuf); +char* mikmod_strncat(char *dest, const char *src, size_t count); +char* mikmod_strstr(char *str, char *search); +int mikmod_toupper(int character); +int mikmod_isalnum(int character); +int mikmod_isdigit(char c); + +//#define malloc(a) mikmod_malloc(a) +//#define calloc(a,b) mikmod_calloc(a,b) +#define free(a) mikmod_free(a) +#define realloc(a,b) mikmod_realloc(a,b) +//#define abs(a) mikmod_abs(a) +#define strdup(a) mikmod_strdup(a) +#define strncat(a, b, c) mikmod_strncat(a, b, c) +#define strstr(a, b) mikmod_strstr(a, b) +#define toupper(a) mikmod_toupper(a) +#define isalnum(a) mikmod_isalnum(a) +#define isdigit(a) mikmod_isdigit(a) + + +#define strcpy(a, b) rb->strcpy(a, b) +#define strncpy(a, b, c) rb->strncpy(a, b, c) +#define strlen(a) rb->strlen(a) +#define strrchr(a, b) rb->strrchr(a, b) +#define strcmp(a, b) rb->strcmp(a, b) +#define strncmp(a, b, c) rb->strncmp(a, b, c) +#define strcasecmp(a, b) rb->strcasecmp(a, b) +#define strncasecmp(a, b, c) rb->strncasecmp(a, b, c) +#define memset(a, b, c) rb->memset(a, b, c) +#define memcpy(a, b, c) rb->memcpy(a, b, c) +#define memmove(a, b, c) rb->memmove(a, b, c) + +#define rand() rb->rand() +#define atoi(a) rb->atoi(a) +#define strchr(a, b) rb->strchr(a, b) +#define strcat(a, b) rb->strcat(a, b) +#define memchr(a, b, c) rb->memchr(a, b, c) +#define memcmp(a, b, c) rb->memcmp(a, b, c) +#define strcasestr(a, b) rb->strcasestr(a, b) +#define strtok_r(a, b, c) rb->strtok_r(a, b, c) +#define snprintf(a, b, c, args...) rb->snprintf(a, b, c, args) + + +#ifdef __cplusplus +} +#endif + +#endif + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/loaders/load_669.c b/apps/plugins/mikmod/loaders/load_669.c new file mode 100644 index 0000000..1991e75 --- /dev/null +++ b/apps/plugins/mikmod/loaders/load_669.c @@ -0,0 +1,369 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file + AUTHORS for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: load_669.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $ + + Composer 669 module loader + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Module structure */ + +/* header */ +typedef struct S69HEADER { + UBYTE marker[2]; + CHAR message[108]; + UBYTE nos; + UBYTE nopa; + UBYTE looporder; + UBYTE orders[0x80]; + UBYTE tempos[0x80]; + UBYTE breaks[0x80]; +} S69HEADER; + +/* sample information */ +typedef struct S69SAMPLE { + CHAR filename[13]; + SLONG length; + SLONG loopbeg; + SLONG loopend; +} S69SAMPLE; + +/* encoded note */ +typedef struct S69NOTE { + UBYTE a,b,c; +} S69NOTE; + +/*========== Loader variables */ + +/* current pattern */ +static S69NOTE* s69pat=NULL; +/* Module header */ +static S69HEADER* mh=NULL; + +/* file type identification */ +static CHAR* S69_Version[]={ + "Composer 669", + "Extended 669" +}; + +/*========== Loader code */ + +BOOL S69_Test(void) +{ + UBYTE buf[0x80]; + + if(!_mm_read_UBYTES(buf,2,modreader)) + return 0; + /* look for id */ + if(!memcmp(buf,"if",2) || !memcmp(buf,"JN",2)) { + int i; + + /* skip song message */ + _mm_fseek(modreader,108,SEEK_CUR); + /* sanity checks */ + if(_mm_read_UBYTE(modreader) > 64) return 0; + if(_mm_read_UBYTE(modreader) > 128) return 0; + if(_mm_read_UBYTE(modreader) > 127) return 0; + /* check order table */ + if(!_mm_read_UBYTES(buf,0x80,modreader)) return 0; + for(i=0;i<0x80;i++) + if((buf[i]>=0x80)&&(buf[i]!=0xff)) return 0; + /* check tempos table */ + if(!_mm_read_UBYTES(buf,0x80,modreader)) return 0; + for(i=0;i<0x80;i++) + if((!buf[i])||(buf[i]>32)) return 0; + /* check pattern length table */ + if(!_mm_read_UBYTES(buf,0x80,modreader)) return 0; + for(i=0;i<0x80;i++) + if(buf[i]>0x3f) return 0; + } else + return 0; + + return 1; +} + +BOOL S69_Init(void) +{ + if(!(s69pat=(S69NOTE *)_mm_malloc(64*8*sizeof(S69NOTE)))) return 0; + if(!(mh=(S69HEADER *)_mm_malloc(sizeof(S69HEADER)))) return 0; + + return 1; +} + +void S69_Cleanup(void) +{ + _mm_free(s69pat); + _mm_free(mh); +} + +static BOOL S69_LoadPatterns(void) +{ + int track,row,channel; + UBYTE note,inst,vol,effect,lastfx,lastval; + S69NOTE *cur; + int tracks=0; + + if(!AllocPatterns()) return 0; + if(!AllocTracks()) return 0; + + for(track=0;trackbreaks[track]+1; + + /* load the 669 pattern */ + cur=s69pat; + for(row=0;row<64;row++) { + for(channel=0;channel<8;channel++,cur++) { + cur->a = _mm_read_UBYTE(modreader); + cur->b = _mm_read_UBYTE(modreader); + cur->c = _mm_read_UBYTE(modreader); + } + } + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_PATTERN; + return 0; + } + + /* translate the pattern */ + for(channel=0;channel<8;channel++) { + UniReset(); + /* set pattern tempo */ + UniPTEffect(0xf,78); + UniPTEffect(0xf,mh->tempos[track]); + + lastfx=0xff,lastval=0; + + for(row=0;row<=mh->breaks[track];row++) { + int a,b,c; + + /* fetch the encoded note */ + a=s69pat[(row*8)+channel].a; + b=s69pat[(row*8)+channel].b; + c=s69pat[(row*8)+channel].c; + + /* decode it */ + note=a>>2; + inst=((a&0x3)<<4)|((b&0xf0)>>4); + vol=b&0xf; + + if (a<0xff) { + if (a<0xfe) { + UniInstrument(inst); + UniNote(note+2*OCTAVE); + lastfx=0xff; /* reset background effect memory */ + } + UniPTEffect(0xc,vol<<2); + } + + if ((c!=0xff)||(lastfx!=0xff)) { + if(c==0xff) + c=lastfx,effect=lastval; + else + effect=c&0xf; + + switch(c>>4) { + case 0: /* porta up */ + UniPTEffect(0x1,effect); + lastfx=c,lastval=effect; + break; + case 1: /* porta down */ + UniPTEffect(0x2,effect); + lastfx=c,lastval=effect; + break; + case 2: /* porta to note */ + UniPTEffect(0x3,effect); + lastfx=c,lastval=effect; + break; + case 3: /* frequency adjust */ + /* DMP converts this effect to S3M FF1. Why not ? */ + UniEffect(UNI_S3MEFFECTF,0xf0|effect); + break; + case 4: /* vibrato */ + UniPTEffect(0x4,effect); + lastfx=c,lastval=effect; + break; + case 5: /* set speed */ + if (effect) + UniPTEffect(0xf,effect); + else + if(mh->marker[0]!=0x69) { +#ifdef MIKMOD_DEBUG + fprintf(stderr,"\r669: unsupported super fast tempo at pat=%d row=%d chan=%d\n", + track,row,channel); +#endif + } + break; + } + } + UniNewline(); + } + if(!(of.tracks[tracks++]=UniDup())) return 0; + } + } + + return 1; +} + +BOOL S69_Load(BOOL curious) +{ + int i; + SAMPLE *current; + S69SAMPLE sample; + (void)curious; + + /* module header */ + _mm_read_UBYTES(mh->marker,2,modreader); + _mm_read_UBYTES(mh->message,108,modreader); + mh->nos=_mm_read_UBYTE(modreader); + mh->nopa=_mm_read_UBYTE(modreader); + mh->looporder=_mm_read_UBYTE(modreader); + _mm_read_UBYTES(mh->orders,0x80,modreader); + for(i=0;i<0x80;i++) + if ((mh->orders[i]>=0x80)&&(mh->orders[i]!=0xff)) { + _mm_errno=MMERR_NOT_A_MODULE; + return 1; + } + _mm_read_UBYTES(mh->tempos,0x80,modreader); + for(i=0;i<0x80;i++) + if ((!mh->tempos[i])||(mh->tempos[i]>32)) { + _mm_errno=MMERR_NOT_A_MODULE; + return 1; + } + _mm_read_UBYTES(mh->breaks,0x80,modreader); + for(i=0;i<0x80;i++) + if (mh->breaks[i]>0x3f) { + _mm_errno=MMERR_NOT_A_MODULE; + return 1; + } + + /* set module variables */ + of.initspeed=4; + of.inittempo=78; + of.songname=DupStr(mh->message,36,1); + of.modtype=strdup(S69_Version[memcmp(mh->marker,"JN",2)==0]); + of.numchn=8; + of.numpat=mh->nopa; + of.numins=of.numsmp=mh->nos; + of.numtrk=of.numchn*of.numpat; + of.flags=UF_XMPERIODS|UF_LINEAR; + + for(i= 35;(i>= 0)&&(mh->message[i]==' ');i--) mh->message[i]=0; + for(i=36+35;(i>=36+0)&&(mh->message[i]==' ');i--) mh->message[i]=0; + for(i=72+35;(i>=72+0)&&(mh->message[i]==' ');i--) mh->message[i]=0; + if((mh->message[0])||(mh->message[36])||(mh->message[72])) + if((of.comment=(CHAR*)_mm_malloc(3*(36+1)+1))) { + strncpy(of.comment,mh->message,36); + strcat(of.comment,"\r"); + if (mh->message[36]) strncat(of.comment,mh->message+36,36); + strcat(of.comment,"\r"); + if (mh->message[72]) strncat(of.comment,mh->message+72,36); + strcat(of.comment,"\r"); + of.comment[3*(36+1)]=0; + } + + if(!AllocPositions(0x80)) return 0; + for(i=0;i<0x80;i++) { + if(mh->orders[i]>=mh->nopa) break; + of.positions[i]=mh->orders[i]; + } + of.numpos=i; + of.reppos=mh->looporderlooporder:0; + + if(!AllocSamples()) return 0; + current=of.samples; + + for(i=0;isamplename=DupStr(sample.filename,13,1); + current->seekpos=0; + current->speed=0; + current->length=sample.length; + current->loopstart=sample.loopbeg; + current->loopend=sample.loopend; + current->flags=(sample.loopbegvolume=64; + + current++; + } + + if(!S69_LoadPatterns()) return 0; + + return 1; +} + +CHAR *S69_LoadTitle(void) +{ + CHAR s[36]; + + _mm_fseek(modreader,2,SEEK_SET); + if(!_mm_read_UBYTES(s,36,modreader)) return NULL; + + return(DupStr(s,36,1)); +} + +/*========== Loader information */ + +MIKMODAPI MLOADER load_669={ + NULL, + "669", + "669 (Composer 669, Unis 669)", + S69_Init, + S69_Test, + S69_Load, + S69_Cleanup, + S69_LoadTitle +}; + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/loaders/load_amf.c b/apps/plugins/mikmod/loaders/load_amf.c new file mode 100644 index 0000000..f53d2ee --- /dev/null +++ b/apps/plugins/mikmod/loaders/load_amf.c @@ -0,0 +1,570 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file + AUTHORS for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: load_amf.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $ + + DMP Advanced Module Format loader + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Module structure */ + +typedef struct AMFHEADER { + UBYTE id[3]; /* AMF file marker */ + UBYTE version; /* upper major, lower nibble minor version number */ + CHAR songname[32]; /* ASCIIZ songname */ + UBYTE numsamples; /* number of samples saved */ + UBYTE numorders; + UWORD numtracks; /* number of tracks saved */ + UBYTE numchannels; /* number of channels used */ + SBYTE panpos[32]; /* voice pan positions */ + UBYTE songbpm; + UBYTE songspd; +} AMFHEADER; + +typedef struct AMFSAMPLE { + UBYTE type; + CHAR samplename[32]; + CHAR filename[13]; + ULONG offset; + ULONG length; + UWORD c2spd; + UBYTE volume; + ULONG reppos; + ULONG repend; +} AMFSAMPLE; + +typedef struct AMFNOTE { + UBYTE note,instr,volume,fxcnt; + UBYTE effect[3]; + SBYTE parameter[3]; +} AMFNOTE; + +/*========== Loader variables */ + +static AMFHEADER *mh = NULL; +#define AMFTEXTLEN 22 +static CHAR AMF_Version[AMFTEXTLEN+1] = "DSMI Module Format 0.0"; +static AMFNOTE *track = NULL; + +/*========== Loader code */ + +BOOL AMF_Test(void) +{ + UBYTE id[3],ver; + + if(!_mm_read_UBYTES(id,3,modreader)) return 0; + if(memcmp(id,"AMF",3)) return 0; + + ver=_mm_read_UBYTE(modreader); + if((ver>=10)&&(ver<=14)) return 1; + return 0; +} + +BOOL AMF_Init(void) +{ + if(!(mh=(AMFHEADER*)_mm_malloc(sizeof(AMFHEADER)))) return 0; + if(!(track=(AMFNOTE*)_mm_calloc(64,sizeof(AMFNOTE)))) return 0; + + return 1; +} + +void AMF_Cleanup(void) +{ + _mm_free(mh); + _mm_free(track); +} + +static BOOL AMF_UnpackTrack(MREADER* modreader) +{ + ULONG tracksize; + UBYTE row,cmd; + SBYTE arg; + + /* empty track */ + memset(track,0,64*sizeof(AMFNOTE)); + + /* read packed track */ + if (modreader) { + tracksize=_mm_read_I_UWORD(modreader); + tracksize+=((ULONG)_mm_read_UBYTE(modreader))<<16; + if (tracksize) + while(tracksize--) { + row=_mm_read_UBYTE(modreader); + cmd=_mm_read_UBYTE(modreader); + arg=_mm_read_SBYTE(modreader); + /* unexpected end of track */ + if(!tracksize) { + if((row==0xff)&&(cmd==0xff)&&(arg==-1)) + break; + /* the last triplet should be FF FF FF, but this is not + always the case... maybe a bug in m2amf ? + else + return 0; + */ + + } + /* invalid row (probably unexpected end of row) */ + if (row>=64) + return 0; + if (cmd<0x7f) { + /* note, vol */ + track[row].note=cmd; + track[row].volume=(UBYTE)arg+1; + } else + if (cmd==0x7f) { + /* duplicate row */ + if ((arg<0)&&(row+arg>=0)) { + memcpy(track+row,track+(row+arg),sizeof(AMFNOTE)); + } + } else + if (cmd==0x80) { + /* instr */ + track[row].instr=arg+1; + } else + if (cmd==0x83) { + /* volume without note */ + track[row].volume=(UBYTE)arg+1; + } else + if (cmd==0xff) { + /* apparently, some M2AMF version fail to estimate the + size of the compressed patterns correctly, and end + up with blanks, i.e. dead triplets. Those are marked + with cmd == 0xff. Let's ignore them. */ + } else + if(track[row].fxcnt<3) { + /* effect, param */ + if(cmd>0x97) + return 0; + track[row].effect[track[row].fxcnt]=cmd&0x7f; + track[row].parameter[track[row].fxcnt]=arg; + track[row].fxcnt++; + } else + return 0; + } + } + return 1; +} + +static UBYTE* AMF_ConvertTrack(void) +{ + int row,fx4memory=0; + + /* convert track */ + UniReset(); + for (row=0;row<64;row++) { + if (track[row].instr) UniInstrument(track[row].instr-1); + if (track[row].note>OCTAVE) UniNote(track[row].note-OCTAVE); + + /* AMF effects */ + while(track[row].fxcnt--) { + SBYTE inf=track[row].parameter[track[row].fxcnt]; + + switch(track[row].effect[track[row].fxcnt]) { + case 1: /* Set speed */ + UniEffect(UNI_S3MEFFECTA,inf); + break; + case 2: /* Volume slide */ + if(inf) { + UniWriteByte(UNI_S3MEFFECTD); + if (inf>=0) + UniWriteByte((inf&0xf)<<4); + else + UniWriteByte((-inf)&0xf); + } + break; + /* effect 3, set channel volume, done in UnpackTrack */ + case 4: /* Porta up/down */ + if(inf) { + if(inf>0) { + UniEffect(UNI_S3MEFFECTE,inf); + fx4memory=UNI_S3MEFFECTE; + } else { + UniEffect(UNI_S3MEFFECTF,-inf); + fx4memory=UNI_S3MEFFECTF; + } + } else if(fx4memory) + UniEffect(fx4memory,0); + break; + /* effect 5, "Porta abs", not supported */ + case 6: /* Porta to note */ + UniEffect(UNI_ITEFFECTG,inf); + break; + case 7: /* Tremor */ + UniEffect(UNI_S3MEFFECTI,inf); + break; + case 8: /* Arpeggio */ + UniPTEffect(0x0,inf); + break; + case 9: /* Vibrato */ + UniPTEffect(0x4,inf); + break; + case 0xa: /* Porta + Volume slide */ + UniPTEffect(0x3,0); + if(inf) { + UniWriteByte(UNI_S3MEFFECTD); + if (inf>=0) + UniWriteByte((inf&0xf)<<4); + else + UniWriteByte((-inf)&0xf); + } + break; + case 0xb: /* Vibrato + Volume slide */ + UniPTEffect(0x4,0); + if(inf) { + UniWriteByte(UNI_S3MEFFECTD); + if (inf>=0) + UniWriteByte((inf&0xf)<<4); + else + UniWriteByte((-inf)&0xf); + } + break; + case 0xc: /* Pattern break (in hex) */ + UniPTEffect(0xd,inf); + break; + case 0xd: /* Pattern jump */ + UniPTEffect(0xb,inf); + break; + /* effect 0xe, "Sync", not supported */ + case 0xf: /* Retrig */ + UniEffect(UNI_S3MEFFECTQ,inf&0xf); + break; + case 0x10: /* Sample offset */ + UniPTEffect(0x9,inf); + break; + case 0x11: /* Fine volume slide */ + if(inf) { + UniWriteByte(UNI_S3MEFFECTD); + if (inf>=0) + UniWriteByte((inf&0xf)<<4|0xf); + else + UniWriteByte(0xf0|((-inf)&0xf)); + } + break; + case 0x12: /* Fine portamento */ + if(inf) { + if(inf>0) { + UniEffect(UNI_S3MEFFECTE,0xf0|(inf&0xf)); + fx4memory=UNI_S3MEFFECTE; + } else { + UniEffect(UNI_S3MEFFECTF,0xf0|((-inf)&0xf)); + fx4memory=UNI_S3MEFFECTF; + } + } else if(fx4memory) + UniEffect(fx4memory,0); + break; + case 0x13: /* Delay note */ + UniPTEffect(0xe,0xd0|(inf&0xf)); + break; + case 0x14: /* Note cut */ + UniPTEffect(0xc,0); + track[row].volume=0; + break; + case 0x15: /* Set tempo */ + UniEffect(UNI_S3MEFFECTT,inf); + break; + case 0x16: /* Extra fine portamento */ + if(inf) { + if(inf>0) { + UniEffect(UNI_S3MEFFECTE,0xe0|((inf>>2)&0xf)); + fx4memory=UNI_S3MEFFECTE; + } else { + UniEffect(UNI_S3MEFFECTF,0xe0|(((-inf)>>2)&0xf)); + fx4memory=UNI_S3MEFFECTF; + } + } else if(fx4memory) + UniEffect(fx4memory,0); + break; + case 0x17: /* Panning */ + if (inf>64) + UniEffect(UNI_ITEFFECTS0,0x91); /* surround */ + else + UniPTEffect(0x8,(inf==64)?255:(inf+64)<<1); + of.flags |= UF_PANNING; + break; + } + + } + if (track[row].volume) UniVolEffect(VOL_VOLUME,track[row].volume-1); + UniNewline(); + } + return UniDup(); +} + +BOOL AMF_Load(BOOL curious) +{ + int t,u,realtrackcnt,realsmpcnt,defaultpanning; + AMFSAMPLE s; + SAMPLE *q; + UWORD *track_remap; + ULONG samplepos; + int channel_remap[16]; + (void)curious; + + /* try to read module header */ + _mm_read_UBYTES(mh->id,3,modreader); + mh->version =_mm_read_UBYTE(modreader); + _mm_read_string(mh->songname,32,modreader); + mh->numsamples =_mm_read_UBYTE(modreader); + mh->numorders =_mm_read_UBYTE(modreader); + mh->numtracks =_mm_read_I_UWORD(modreader); + mh->numchannels =_mm_read_UBYTE(modreader); + if((!mh->numchannels)||(mh->numchannels>(mh->version>=12?32:16))) { + _mm_errno=MMERR_NOT_A_MODULE; + return 0; + } + + if(mh->version>=11) { + memset(mh->panpos,0,32); + _mm_read_SBYTES(mh->panpos,(mh->version>=13)?32:16,modreader); + } else + _mm_read_UBYTES(channel_remap,16,modreader); + + if (mh->version>=13) { + mh->songbpm=_mm_read_UBYTE(modreader); + if(mh->songbpm<32) { + _mm_errno=MMERR_NOT_A_MODULE; + return 0; + } + mh->songspd=_mm_read_UBYTE(modreader); + if(mh->songspd>32) { + _mm_errno=MMERR_NOT_A_MODULE; + return 0; + } + } else { + mh->songbpm=125; + mh->songspd=6; + } + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* set module variables */ + of.initspeed = mh->songspd; + of.inittempo = mh->songbpm; + AMF_Version[AMFTEXTLEN-3]='0'+(mh->version/10); + AMF_Version[AMFTEXTLEN-1]='0'+(mh->version%10); + of.modtype = strdup(AMF_Version); + of.numchn = mh->numchannels; + of.numtrk = mh->numorders*mh->numchannels; + if (mh->numtracks>of.numtrk) + of.numtrk=mh->numtracks; + of.numtrk++; /* add room for extra, empty track */ + of.songname = DupStr(mh->songname,32,1); + of.numpos = mh->numorders; + of.numpat = mh->numorders; + of.reppos = 0; + of.flags |= UF_S3MSLIDES; + /* XXX whenever possible, we should try to determine the original format. + Here we assume it was S3M-style wrt bpmlimit... */ + of.bpmlimit = 32; + + /* + * Play with the panning table. Although the AMF format embeds a + * panning table, if the module was a MOD or an S3M with default + * panning and didn't use any panning commands, don't flag + * UF_PANNING, to use our preferred panning table for this case. + */ + defaultpanning = 1; + for (t = 0; t < 32; t++) { + if (mh->panpos[t] > 64) { + of.panning[t] = PAN_SURROUND; + defaultpanning = 0; + } else + if (mh->panpos[t] == 64) + of.panning[t] = PAN_RIGHT; + else + of.panning[t] = (mh->panpos[t] + 64) << 1; + } + if (defaultpanning) { + for (t = 0; t < of.numchn; t++) + if (of.panning[t] == (((t + 1) & 2) ? PAN_RIGHT : PAN_LEFT)) { + defaultpanning = 0; /* not MOD canonical panning */ + break; + } + } + if (defaultpanning) + of.flags |= UF_PANNING; + + of.numins=of.numsmp=mh->numsamples; + + if(!AllocPositions(of.numpos)) return 0; + for(t=0;tversion>=14) + /* track size */ + of.pattrows[t]=_mm_read_I_UWORD(modreader); + if (mh->version>=10) + _mm_read_I_UWORDS(of.patterns+(t*of.numchn),of.numchn,modreader); + else + for(u=0;uversion>=11) { + s.reppos =_mm_read_I_ULONG(modreader); + s.repend =_mm_read_I_ULONG(modreader); + } else { + s.reppos =_mm_read_I_UWORD(modreader); + s.repend =s.length; + } + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + q->samplename = DupStr(s.samplename,32,1); + q->speed = s.c2spd; + q->volume = s.volume; + if (s.type) { + q->seekpos = s.offset; + q->length = s.length; + q->loopstart = s.reppos; + q->loopend = s.repend; + if((s.repend-s.reppos)>2) q->flags |= SF_LOOP; + } + q++; + } + + /* read track table */ + if(!(track_remap=_mm_calloc(mh->numtracks+1,sizeof(UWORD)))) + return 0; + _mm_read_I_UWORDS(track_remap+1,mh->numtracks,modreader); + if(_mm_eof(modreader)) { + free(track_remap); + _mm_errno=MMERR_LOADING_TRACK; + return 0; + } + + for(realtrackcnt=t=0;t<=mh->numtracks;t++) + if (realtrackcntnumtracks)? + track_remap[of.patterns[t]]-1:realtrackcnt; + + free(track_remap); + + /* unpack tracks */ + for(t=0;tseekpos!=t) q++; + q->seekpos=samplepos; + samplepos+=q->length; + } + + return 1; +} + +CHAR *AMF_LoadTitle(void) +{ + CHAR s[32]; + + _mm_fseek(modreader,4,SEEK_SET); + if(!_mm_read_UBYTES(s,32,modreader)) return NULL; + + return(DupStr(s,32,1)); +} + +/*========== Loader information */ + +MIKMODAPI MLOADER load_amf={ + NULL, + "AMF", + "AMF (DSMI Advanced Module Format)", + AMF_Init, + AMF_Test, + AMF_Load, + AMF_Cleanup, + AMF_LoadTitle +}; + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/loaders/load_asy.c b/apps/plugins/mikmod/loaders/load_asy.c new file mode 100644 index 0000000..6b78e31 --- /dev/null +++ b/apps/plugins/mikmod/loaders/load_asy.c @@ -0,0 +1,399 @@ +/* MikMod sound library + (c) 2004, Raphael Assenat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: load_asy.c,v 1.3 2004/01/28 01:18:22 raph Exp $ + + ASYLUM Music Format v1.0 (.amf) loader + adapted from load_mod.c by Raphael Assenat , + with the help of the AMF2MOD utility sourcecode, + written to convert crusader's amf files into 8 + channels mod file in 1995 by Mr. P / Powersource + mrp@fish.share.net, ac054@sfn.saskatoon.sk.ca + + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +//#include +#include + +#include "mikmod_internals.h" + +/*========== Module structure */ + +typedef struct MSAMPINFO { + CHAR samplename[24]; + UBYTE finetune; + UBYTE volume; + ULONG length; + ULONG reppos; + ULONG replen; +} MSAMPINFO; + +typedef struct MODULEHEADER { + CHAR songname[21]; + UBYTE num_patterns; /* number of patterns used */ + UBYTE num_orders; + UBYTE positions[256]; /* which pattern to play at pos */ + MSAMPINFO samples[64]; /* all sampleinfo */ +} MODULEHEADER; + +typedef struct MODTYPE { + CHAR id[5]; + UBYTE channels; + CHAR *name; +} MODTYPE; + +typedef struct MODNOTE { + UBYTE a, b, c, d; +} MODNOTE; + +/* This table is taken from AMF2MOD.C + * written in 1995 by Mr. P / Powersource + * mrp@fish.share.net, ac054@sfn.saskatoon.sk.ca */ +UWORD periodtable[]={6848,6464,6096,5760,5424,5120,4832,4560,4304, + 4064,3840,3628,3424,3232,3048,2880,2712,2560, + 2416,2280,2152,2032,1920,1814,1712,1616,1524, + 1440,1356,1280,1208,1140,1076,1016, 960, 907, + 856, 808, 762, 720, 678, 640, 604, 570, 538, + 508, 480, 453, 428, 404, 381, 360, 339, 320, + 302, 285, 269, 254, 240, 226, 214, 202, 190, + 180, 170, 160, 151, 143, 135, 127, 120, 113, + 107, 101, 95, 90, 85, 80, 75, 71, 67, + 63, 60, 56, 53, 50, 47, 45, 42, 40, + 37, 35, 33, 31, 30, 28}; + +/*========== Loader variables */ + +static CHAR asylum[] = "Asylum 1.0"; + +static MODULEHEADER *mh = NULL; +static MODNOTE *patbuf = NULL; +static int modtype = 0; + +/*========== Loader code */ + +static BOOL ASY_CheckType(UBYTE *id, UBYTE *numchn, CHAR **descr) +{ + if (!memcmp(id, "ASYLUM Music Format V1.0", 24)) + { + *descr = asylum; + *numchn = 8; + modtype = 1; + return 1; + } + + return 0; +} + +static BOOL ASY_Test(void) +{ + UBYTE namestring[24], numchn; + CHAR *descr; + + /* Read the magic string */ + _mm_fseek(modreader, 0, SEEK_SET); + if (!_mm_read_UBYTES(namestring, 24, modreader)) + return 0; + + /* Test if the string is what we expect */ + if (ASY_CheckType(namestring, &numchn, &descr)) + return 1; + + return 0; +} + +static BOOL ASY_Init(void) +{ + if (!(mh = (MODULEHEADER *)_mm_malloc(sizeof(MODULEHEADER)))) + return 0; + return 1; +} + +static void ASY_Cleanup(void) +{ + _mm_free(mh); + _mm_free(patbuf); +} + +static void ConvertNote(MODNOTE *n) +{ + UBYTE instrument, effect, effdat, note; + UWORD period; + UBYTE lastnote = 0; + + instrument = n->b&0x1f; + effect = n->c; + effdat = n->d; + + /* convert amf note to mod period */ + if (n->a) { + period = periodtable[n->a]; + } else { + period = 0; + } + + /* Convert the period to a note number */ + note = 0; + if (period) + { + for (note = 0; note < 7 * OCTAVE; note++) + if (period >= npertab[note]) + break; + if (note == 7 * OCTAVE) + note = 0; + else + note++; + } + + if (instrument) { + /* if instrument does not exist, note cut */ + if ((instrument > 31) || (!mh->samples[instrument - 1].length)) { + UniPTEffect(0xc, 0); + if (effect == 0xc) + effect = effdat = 0; + } else { + /* Protracker handling */ + if (!modtype) { + /* if we had a note, then change instrument...*/ + if (note) + UniInstrument(instrument - 1); + /* ...otherwise, only adjust volume... */ + else { + /* ...unless an effect was specified, + * which forces a new note to be + * played */ + if (effect || effdat) { + UniInstrument(instrument - 1); + note = lastnote; + } else + UniPTEffect(0xc, + mh->samples[instrument - + 1].volume & 0x7f); + } + } else { + /* Fasttracker handling */ + UniInstrument(instrument - 1); + if (!note) + note = lastnote; + } + } + } + if (note) { + UniNote(note + 2 * OCTAVE - 1); + lastnote = note; + } + + /* Convert pattern jump from Dec to Hex */ + if (effect == 0xd) + effdat = (((effdat & 0xf0) >> 4) * 10) + (effdat & 0xf); + + /* Volume slide, up has priority */ + if ((effect == 0xa) && (effdat & 0xf) && (effdat & 0xf0)) + effdat &= 0xf0; + + UniPTEffect(effect, effdat); +} + +static UBYTE *ConvertTrack(MODNOTE *n) +{ + int t; + + UniReset(); + for (t = 0; t < 64; t++) { + ConvertNote(n); + UniNewline(); + n += of.numchn; + } + return UniDup(); +} + +/* Loads all patterns of a modfile and converts them into the 3 byte format. */ +static BOOL ML_LoadPatterns(void) +{ + int t, s, tracks = 0; + + if (!AllocPatterns()) { + return 0; + } + if (!AllocTracks()) { + return 0; + } + + /* Allocate temporary buffer for loading and converting the patterns */ + if (!(patbuf = (MODNOTE *)_mm_calloc(64U * of.numchn, sizeof(MODNOTE)))) + return 0; + + + /* patterns start here */ + _mm_fseek(modreader, 0xA66, SEEK_SET); + for (t = 0; t < of.numpat; t++) { + /* Load the pattern into the temp buffer and convert it */ + for (s = 0; s < (64U * of.numchn); s++) { + patbuf[s].a = _mm_read_UBYTE(modreader); + patbuf[s].b = _mm_read_UBYTE(modreader); + patbuf[s].c = _mm_read_UBYTE(modreader); + patbuf[s].d = _mm_read_UBYTE(modreader); + } + for (s = 0; s < of.numchn; s++) { + if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s))) { + return 0; + } + } + } + return 1; +} + +static BOOL ASY_Load(BOOL curious) +{ + int t; + SAMPLE *q; + MSAMPINFO *s; + CHAR *descr=asylum; + ULONG seekpos; + (void)curious; + + // no title in asylum amf files :( + strcpy(mh->songname, ""); + + _mm_fseek(modreader, 0x23, SEEK_SET); + mh->num_patterns = _mm_read_UBYTE(modreader); + mh->num_orders = _mm_read_UBYTE(modreader); + + // skip unknown byte + _mm_read_UBYTE(modreader); + _mm_read_UBYTES(mh->positions, 256, modreader); + + /* read samples headers*/ + for (t = 0; t < 64; t++) { + s = &mh->samples[t]; + + _mm_fseek(modreader, 0x126 + (t*37), SEEK_SET); + + _mm_read_string(s->samplename, 22, modreader); + s->samplename[21] = 0; /* just in case */ + + s->finetune = _mm_read_UBYTE(modreader); + s->volume = _mm_read_UBYTE(modreader); + _mm_read_UBYTE(modreader); // skip unknown byte + s->length = _mm_read_I_ULONG(modreader); + s->reppos = _mm_read_I_ULONG(modreader); + s->replen = _mm_read_I_ULONG(modreader); + } + + if (_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* set module variables */ + of.initspeed = 6; + of.inittempo = 125; + of.numchn = 8; + modtype = 0; + of.songname = DupStr(mh->songname, 21, 1); + of.numpos = mh->num_orders; + of.reppos = 0; + of.numpat = mh->num_patterns; + of.numtrk = of.numpat * of.numchn; + + + /* Copy positions (orders) */ + if (!AllocPositions(of.numpos)) + return 0; + for (t = 0; t < of.numpos; t++) { + of.positions[t] = mh->positions[t]; + } + + /* Finally, init the sampleinfo structures */ + of.numins = 31; + of.numsmp = 31; + if (!AllocSamples()) + return 0; + s = mh->samples; + q = of.samples; + seekpos = 2662+(2048*(of.numpat)); + for (t = 0; t < of.numins; t++) { + /* convert the samplename */ + q->samplename = DupStr(s->samplename, 23, 1); + + /* init the sampleinfo variables */ + q->speed = finetune[s->finetune & 0xf]; + q->volume = s->volume & 0x7f; + + q->loopstart = (ULONG)s->reppos; + q->loopend = (ULONG)q->loopstart + (s->replen); + q->length = (ULONG)s->length; + + q->flags = SF_SIGNED; + + q->seekpos = seekpos; + seekpos += q->length; + + if ((s->replen) > 2) { + q->flags |= SF_LOOP; + } + + /* fix replen if repend > length */ + if (q->loopend > q->length) + q->loopend = q->length; + + s++; + q++; + } + + of.modtype = strdup(descr); + + if (!ML_LoadPatterns()) + return 0; + + return 1; +} + +static CHAR *ASY_LoadTitle(void) +{ + CHAR *s = ""; // no titles + + return (DupStr(s, 21, 1)); +} + +/*========== Loader information */ + +MLOADER load_asy = { + NULL, + "AMF", + "AMF (ASYLUM Music Format V1.0)", + ASY_Init, + ASY_Test, + ASY_Load, + ASY_Cleanup, + ASY_LoadTitle +}; + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/loaders/load_dsm.c b/apps/plugins/mikmod/loaders/load_dsm.c new file mode 100644 index 0000000..0eace5f --- /dev/null +++ b/apps/plugins/mikmod/loaders/load_dsm.c @@ -0,0 +1,365 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file + AUTHORS for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: load_dsm.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $ + + DSIK internal format (DSM) module loader + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Module structure */ + +#define DSM_MAXCHAN (16) +#define DSM_MAXORDERS (128) + +typedef struct DSMSONG { + CHAR songname[28]; + UWORD version; + UWORD flags; + ULONG reserved2; + UWORD numord; + UWORD numsmp; + UWORD numpat; + UWORD numtrk; + UBYTE globalvol; + UBYTE mastervol; + UBYTE speed; + UBYTE bpm; + UBYTE panpos[DSM_MAXCHAN]; + UBYTE orders[DSM_MAXORDERS]; +} DSMSONG; + +typedef struct DSMINST { + CHAR filename[13]; + UWORD flags; + UBYTE volume; + ULONG length; + ULONG loopstart; + ULONG loopend; + ULONG reserved1; + UWORD c2spd; + UWORD period; + CHAR samplename[28]; +} DSMINST; + +typedef struct DSMNOTE { + UBYTE note,ins,vol,cmd,inf; +} DSMNOTE; + +#define DSM_SURROUND (0xa4) + +/*========== Loader variables */ + +static CHAR* SONGID="SONG"; +static CHAR* INSTID="INST"; +static CHAR* PATTID="PATT"; + +static UBYTE blockid[4]; +static ULONG blockln; +static ULONG blocklp; +static DSMSONG* mh=NULL; +static DSMNOTE* dsmbuf=NULL; + +static CHAR DSM_Version[]="DSIK DSM-format"; + +static unsigned char DSMSIG[4+4]={'R','I','F','F','D','S','M','F'}; + +/*========== Loader code */ + +BOOL DSM_Test(void) +{ + UBYTE id[12]; + + if(!_mm_read_UBYTES(id,12,modreader)) return 0; + if(!memcmp(id,DSMSIG,4) && !memcmp(id+8,DSMSIG+4,4)) return 1; + + return 0; +} + +BOOL DSM_Init(void) +{ + if(!(dsmbuf=(DSMNOTE *)_mm_malloc(DSM_MAXCHAN*64*sizeof(DSMNOTE)))) return 0; + if(!(mh=(DSMSONG *)_mm_calloc(1,sizeof(DSMSONG)))) return 0; + return 1; +} + +void DSM_Cleanup(void) +{ + _mm_free(dsmbuf); + _mm_free(mh); +} + +static BOOL GetBlockHeader(void) +{ + /* make sure we're at the right position for reading the + next riff block, no matter how many bytes read */ + _mm_fseek(modreader, blocklp+blockln, SEEK_SET); + + while(1) { + _mm_read_UBYTES(blockid,4,modreader); + blockln=_mm_read_I_ULONG(modreader); + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + if(memcmp(blockid,SONGID,4) && memcmp(blockid,INSTID,4) && + memcmp(blockid,PATTID,4)) { +#ifdef MIKMOD_DEBUG + fprintf(stderr,"\rDSM: Skipping unknown block type %4.4s\n",blockid); +#endif + _mm_fseek(modreader, blockln, SEEK_CUR); + } else + break; + } + + blocklp = _mm_ftell(modreader); + + return 1; +} + +static BOOL DSM_ReadPattern(void) +{ + int flag,row=0; + SWORD length; + DSMNOTE *n; + + /* clear pattern data */ + memset(dsmbuf,255,DSM_MAXCHAN*64*sizeof(DSMNOTE)); + length=_mm_read_I_SWORD(modreader); + + while(row<64) { + flag=_mm_read_UBYTE(modreader); + if((_mm_eof(modreader))||(--length<0)) { + _mm_errno = MMERR_LOADING_PATTERN; + return 0; + } + + if(flag) { + n=&dsmbuf[((flag&0xf)*64)+row]; + if(flag&0x80) n->note=_mm_read_UBYTE(modreader); + if(flag&0x40) n->ins=_mm_read_UBYTE(modreader); + if(flag&0x20) n->vol=_mm_read_UBYTE(modreader); + if(flag&0x10) { + n->cmd=_mm_read_UBYTE(modreader); + n->inf=_mm_read_UBYTE(modreader); + } + } else + row++; + } + + return 1; +} + +static UBYTE *DSM_ConvertTrack(DSMNOTE *tr) +{ + int t; + UBYTE note,ins,vol,cmd,inf; + + UniReset(); + for(t=0;t<64;t++) { + note=tr[t].note; + ins=tr[t].ins; + vol=tr[t].vol; + cmd=tr[t].cmd; + inf=tr[t].inf; + + if(ins!=0 && ins!=255) UniInstrument(ins-1); + if(note!=255) UniNote(note-1); /* normal note */ + if(vol<65) UniPTEffect(0xc,vol); + + if(cmd!=255) { + if(cmd==0x8) { + if(inf==DSM_SURROUND) + UniEffect(UNI_ITEFFECTS0,0x91); + else + if(inf<=0x80) { + inf=(inf<0x80)?inf<<1:255; + UniPTEffect(cmd,inf); + } + } else + if(cmd==0xb) { + if(inf<=0x7f) UniPTEffect(cmd,inf); + } else { + /* Convert pattern jump from Dec to Hex */ + if(cmd == 0xd) + inf = (((inf&0xf0)>>4)*10)+(inf&0xf); + UniPTEffect(cmd,inf); + } + } + UniNewline(); + } + return UniDup(); +} + +BOOL DSM_Load(BOOL curious) +{ + int t; + DSMINST s; + SAMPLE *q; + int cursmp=0,curpat=0,track=0; + + blocklp=0; + blockln=12; + (void)curious; + + if(!GetBlockHeader()) return 0; + if(memcmp(blockid,SONGID,4)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + _mm_read_UBYTES(mh->songname,28,modreader); + mh->version=_mm_read_I_UWORD(modreader); + mh->flags=_mm_read_I_UWORD(modreader); + mh->reserved2=_mm_read_I_ULONG(modreader); + mh->numord=_mm_read_I_UWORD(modreader); + mh->numsmp=_mm_read_I_UWORD(modreader); + mh->numpat=_mm_read_I_UWORD(modreader); + mh->numtrk=_mm_read_I_UWORD(modreader); + mh->globalvol=_mm_read_UBYTE(modreader); + mh->mastervol=_mm_read_UBYTE(modreader); + mh->speed=_mm_read_UBYTE(modreader); + mh->bpm=_mm_read_UBYTE(modreader); + _mm_read_UBYTES(mh->panpos,DSM_MAXCHAN,modreader); + _mm_read_UBYTES(mh->orders,DSM_MAXORDERS,modreader); + + /* set module variables */ + of.initspeed=mh->speed; + of.inittempo=mh->bpm; + of.modtype=strdup(DSM_Version); + of.numchn=mh->numtrk; + of.numpat=mh->numpat; + of.numtrk=of.numchn*of.numpat; + of.songname=DupStr(mh->songname,28,1); /* make a cstr of songname */ + of.reppos=0; + of.flags |= UF_PANNING; + /* XXX whenever possible, we should try to determine the original format. + Here we assume it was S3M-style wrt bpmlimit... */ + of.bpmlimit = 32; + + for(t=0;tpanpos[t]==DSM_SURROUND?PAN_SURROUND: + mh->panpos[t]<0x80?(mh->panpos[t]<<1):255; + + if(!AllocPositions(mh->numord)) return 0; + of.numpos=0; + for(t=0;tnumord;t++) { + int order=mh->orders[t]; + if(order==255) order=LAST_PATTERN; + of.positions[of.numpos]=order; + if(mh->orders[t]<254) of.numpos++; + } + + of.numins=of.numsmp=mh->numsmp; + + if(!AllocSamples()) return 0; + if(!AllocTracks()) return 0; + if(!AllocPatterns()) return 0; + + while(cursmpsamplename=DupStr(s.samplename,28,1); + q->seekpos=_mm_ftell(modreader); + q->speed=s.c2spd; + q->length=s.length; + q->loopstart=s.loopstart; + q->loopend=s.loopend; + q->volume=s.volume; + + if(s.flags&1) q->flags|=SF_LOOP; + if(s.flags&2) q->flags|=SF_SIGNED; + /* (s.flags&4) means packed sample, + but did they really exist in dsm ?*/ + cursmp++; + } else + if(!memcmp(blockid,PATTID,4) && curpat +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Module structure */ + +typedef struct FARHEADER1 { + UBYTE id[4]; /* file magic */ + CHAR songname[40]; /* songname */ + CHAR blah[3]; /* 13,10,26 */ + UWORD headerlen; /* remaining length of header in bytes */ + UBYTE version; + UBYTE onoff[16]; + UBYTE edit1[9]; + UBYTE speed; + UBYTE panning[16]; + UBYTE edit2[4]; + UWORD stlen; +} FARHEADER1; + +typedef struct FARHEADER2 { + UBYTE orders[256]; + UBYTE numpat; + UBYTE snglen; + UBYTE loopto; + UWORD patsiz[256]; +} FARHEADER2; + +typedef struct FARSAMPLE { + CHAR samplename[32]; + ULONG length; + UBYTE finetune; + UBYTE volume; + ULONG reppos; + ULONG repend; + UBYTE type; + UBYTE loop; +} FARSAMPLE; + +typedef struct FARNOTE { + UBYTE note,ins,vol,eff; +} FARNOTE; + +/*========== Loader variables */ + +static CHAR FAR_Version[] = "Farandole"; +static FARHEADER1 *mh1 = NULL; +static FARHEADER2 *mh2 = NULL; +static FARNOTE *pat = NULL; + +static unsigned char FARSIG[4+3]={'F','A','R',0xfe,13,10,26}; + +/*========== Loader code */ + +BOOL FAR_Test(void) +{ + UBYTE id[47]; + + if(!_mm_read_UBYTES(id,47,modreader)) return 0; + if((memcmp(id,FARSIG,4))||(memcmp(id+44,FARSIG+4,3))) return 0; + return 1; +} + +BOOL FAR_Init(void) +{ + if(!(mh1 = (FARHEADER1*)_mm_malloc(sizeof(FARHEADER1)))) return 0; + if(!(mh2 = (FARHEADER2*)_mm_malloc(sizeof(FARHEADER2)))) return 0; + if(!(pat = (FARNOTE*)_mm_malloc(256*16*4*sizeof(FARNOTE)))) return 0; + + return 1; +} + +void FAR_Cleanup(void) +{ + _mm_free(mh1); + _mm_free(mh2); + _mm_free(pat); +} + +static UBYTE *FAR_ConvertTrack(FARNOTE* n,int rows) +{ + int t,vibdepth=1; + + UniReset(); + for(t=0;tnote) { + UniInstrument(n->ins); + UniNote(n->note+3*OCTAVE-1); + } + if (n->vol&0xf) UniPTEffect(0xc,(n->vol&0xf)<<2); + if (n->eff) + switch(n->eff>>4) { + case 0x3: /* porta to note */ + UniPTEffect(0x3,(n->eff&0xf)<<4); + break; + case 0x4: /* retrigger */ + UniPTEffect(0x0e, 0x90 | (n->eff & 0x0f)); + break; + case 0x5: /* set vibrato depth */ + vibdepth=n->eff&0xf; + break; + case 0x6: /* vibrato */ + UniPTEffect(0x4,((n->eff&0xf)<<4)|vibdepth); + break; + case 0x7: /* volume slide up */ + UniPTEffect(0xa,(n->eff&0xf)<<4); + break; + case 0x8: /* volume slide down */ + UniPTEffect(0xa,n->eff&0xf); + break; + case 0xb: /* panning */ + UniPTEffect(0xe,0x80|(n->eff&0xf)); + break; + case 0xf: /* set speed */ + UniPTEffect(0xf,n->eff&0xf); + break; + + /* others not yet implemented */ + default: +#ifdef MIKMOD_DEBUG + fprintf(stderr,"\rFAR: unsupported effect %02X\n",n->eff); +#endif + break; + } + + UniNewline(); + n+=16; + } + return UniDup(); +} + +BOOL FAR_Load(BOOL curious) +{ + int t,u,tracks=0; + SAMPLE *q; + FARSAMPLE s; + FARNOTE *crow; + UBYTE smap[8]; + (void)curious; + + /* try to read module header (first part) */ + _mm_read_UBYTES(mh1->id,4,modreader); + _mm_read_SBYTES(mh1->songname,40,modreader); + _mm_read_SBYTES(mh1->blah,3,modreader); + mh1->headerlen = _mm_read_I_UWORD (modreader); + mh1->version = _mm_read_UBYTE (modreader); + _mm_read_UBYTES(mh1->onoff,16,modreader); + _mm_read_UBYTES(mh1->edit1,9,modreader); + mh1->speed = _mm_read_UBYTE(modreader); + _mm_read_UBYTES(mh1->panning,16,modreader); + _mm_read_UBYTES(mh1->edit2,4,modreader); + mh1->stlen = _mm_read_I_UWORD (modreader); + + /* init modfile data */ + of.modtype = strdup(FAR_Version); + of.songname = DupStr(mh1->songname,40,1); + of.numchn = 16; + of.initspeed = mh1->speed; + of.inittempo = 80; + of.reppos = 0; + of.flags |= UF_PANNING; + for(t=0;t<16;t++) of.panning[t]=mh1->panning[t]<<4; + + /* read songtext into comment field */ + if(mh1->stlen) + if (!ReadLinedComment(mh1->stlen, 66)) return 0; + + /* try to read module header (second part) */ + _mm_read_UBYTES(mh2->orders,256,modreader); + mh2->numpat = _mm_read_UBYTE(modreader); + mh2->snglen = _mm_read_UBYTE(modreader); + mh2->loopto = _mm_read_UBYTE(modreader); + _mm_read_I_UWORDS(mh2->patsiz,256,modreader); + + of.numpos = mh2->snglen; + if(!AllocPositions(of.numpos)) return 0; + for(t=0;torders[t]==0xff) break; + of.positions[t] = mh2->orders[t]; + } + + /* count number of patterns stored in file */ + of.numpat = 0; + for(t=0;t<256;t++) + if(mh2->patsiz[t]) + if((t+1)>of.numpat) of.numpat=t+1; + of.numtrk = of.numpat*of.numchn; + + /* seek across eventual new data */ + _mm_fseek(modreader,mh1->headerlen-(869+mh1->stlen),SEEK_CUR); + + /* alloc track and pattern structures */ + if(!AllocTracks()) return 0; + if(!AllocPatterns()) return 0; + + for(t=0;tpatsiz[t]) { + rows = _mm_read_UBYTE(modreader); + tempo = _mm_read_UBYTE(modreader); + + crow = pat; + /* file often allocates 64 rows even if there are less in pattern */ + if (mh2->patsiz[t]<2+(rows*16*4)) { + _mm_errno = MMERR_LOADING_PATTERN; + return 0; + } + for(u=(mh2->patsiz[t]-2)/4;u;u--,crow++) { + crow->note = _mm_read_UBYTE(modreader); + crow->ins = _mm_read_UBYTE(modreader); + crow->vol = _mm_read_UBYTE(modreader); + crow->eff = _mm_read_UBYTE(modreader); + } + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_PATTERN; + return 0; + } + + crow=pat; + of.pattrows[t] = rows; + for(u=16;u;u--,crow++) + if(!(of.tracks[tracks++]=FAR_ConvertTrack(crow,rows))) { + _mm_errno=MMERR_LOADING_PATTERN; + return 0; + } + } else + tracks+=16; + } + + /* read sample map */ + if(!_mm_read_UBYTES(smap,8,modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* count number of samples used */ + of.numins = 0; + for(t=0;t<64;t++) + if(smap[t>>3]&(1<<(t&7))) of.numins=t+1; + of.numsmp = of.numins; + + /* alloc sample structs */ + if(!AllocSamples()) return 0; + + q = of.samples; + for(t=0;tspeed = 8363; + q->flags = SF_SIGNED; + if(smap[t>>3]&(1<<(t&7))) { + _mm_read_SBYTES(s.samplename,32,modreader); + s.length = _mm_read_I_ULONG(modreader); + s.finetune = _mm_read_UBYTE(modreader); + s.volume = _mm_read_UBYTE(modreader); + s.reppos = _mm_read_I_ULONG(modreader); + s.repend = _mm_read_I_ULONG(modreader); + s.type = _mm_read_UBYTE(modreader); + s.loop = _mm_read_UBYTE(modreader); + + q->samplename = DupStr(s.samplename,32,1); + q->length = s.length; + q->loopstart = s.reppos; + q->loopend = s.repend; + q->volume = s.volume<<2; + + if(s.type&1) q->flags|=SF_16BITS; + if(s.loop&8) q->flags|=SF_LOOP; + + q->seekpos = _mm_ftell(modreader); + _mm_fseek(modreader,q->length,SEEK_CUR); + } else + q->samplename = DupStr(NULL,0,0); + q++; + } + return 1; +} + +CHAR *FAR_LoadTitle(void) +{ + CHAR s[40]; + + _mm_fseek(modreader,4,SEEK_SET); + if(!_mm_read_UBYTES(s,40,modreader)) return NULL; + + return(DupStr(s,40,1)); +} + +/*========== Loader information */ + +MIKMODAPI MLOADER load_far={ + NULL, + "FAR", + "FAR (Farandole Composer)", + FAR_Init, + FAR_Test, + FAR_Load, + FAR_Cleanup, + FAR_LoadTitle +}; + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/loaders/load_gdm.c b/apps/plugins/mikmod/loaders/load_gdm.c new file mode 100644 index 0000000..d65c2f8 --- /dev/null +++ b/apps/plugins/mikmod/loaders/load_gdm.c @@ -0,0 +1,559 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file + AUTHORS for complete list. + + This library is free software;you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation;either version 2 of + the License,or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY;without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library;if not,write to the Free Software + Foundation,Inc.,59 Temple Place - Suite 330,Boston,MA + 02111-1307,USA. +*/ + +/*============================================================================== + + $Id: load_gdm.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $ + + General DigiMusic (GDM) module loader + +==============================================================================*/ + +/* + + Written by Kev Vance + based on the file format description written by 'MenTaLguY' + + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +typedef struct GDMNOTE { + UBYTE note; + UBYTE samp; + struct { + UBYTE effect; + UBYTE param; + } effect[4]; +} GDMNOTE; + +typedef GDMNOTE GDMTRACK[64]; + +typedef struct GDMHEADER { + CHAR id1[4]; + CHAR songname[32]; + CHAR author[32]; + CHAR eofmarker[3]; + CHAR id2[4]; + + UBYTE majorver; + UBYTE minorver; + UWORD trackerid; + UBYTE t_majorver; + UBYTE t_minorver; + UBYTE pantable[32]; + UBYTE mastervol; + UBYTE mastertempo; + UBYTE masterbpm; + UWORD flags; + + ULONG orderloc; + UBYTE ordernum; + ULONG patternloc; + UBYTE patternnum; + ULONG samhead; + ULONG samdata; + UBYTE samnum; + ULONG messageloc; + ULONG messagelen; + ULONG scrollyloc; + UWORD scrollylen; + ULONG graphicloc; + UWORD graphiclen; +} GDMHEADER; + +typedef struct GDMSAMPLE { + CHAR sampname[32]; + CHAR filename[13]; + UBYTE ems; + ULONG length; + ULONG loopbeg; + ULONG loopend; + UBYTE flags; + UWORD c4spd; + UBYTE vol; + UBYTE pan; +} GDMSAMPLE; + +static GDMHEADER *mh=NULL; /* pointer to GDM header */ +static GDMNOTE *gdmbuf=NULL; /* pointer to a complete GDM pattern */ + +CHAR GDM_Version[]="General DigiMusic 1.xx"; + +BOOL GDM_Test(void) +{ + /* test for gdm magic numbers */ + UBYTE id[4]; + + _mm_fseek(modreader,0x00,SEEK_SET); + if (!_mm_read_UBYTES(id,4,modreader)) + return 0; + if (!memcmp(id,"GDM\xfe",4)) { + _mm_fseek(modreader,71,SEEK_SET); + if (!_mm_read_UBYTES(id,4,modreader)) + return 0; + if (!memcmp(id,"GMFS",4)) + return 1; + } + return 0; +} + +BOOL GDM_Init(void) +{ + if (!(gdmbuf=(GDMNOTE*)_mm_malloc(32*64*sizeof(GDMNOTE)))) return 0; + if (!(mh=(GDMHEADER*)_mm_malloc(sizeof(GDMHEADER)))) return 0; + + return 1; +} + +void GDM_Cleanup(void) +{ + _mm_free(mh); + _mm_free(gdmbuf); +} + +BOOL GDM_ReadPattern(void) +{ + int pos,flag,ch,i,maxch; + GDMNOTE n; + UWORD length,x=0; + + /* get pattern length */ + length=_mm_read_I_UWORD(modreader)-2; + + /* clear pattern data */ + memset(gdmbuf,255,32*64*sizeof(GDMNOTE)); + pos=0; + maxch=0; + + while (xmaxch) maxch=ch; + if (!flag) { + pos++; + continue; + } + if (flag&0x60) { + if (flag&0x20) { + /* new note */ + n.note=_mm_read_UBYTE(modreader)&127; + n.samp=_mm_read_UBYTE(modreader); + x +=2; + } + if (flag&0x40) { + do { + /* effect channel set */ + i=_mm_read_UBYTE(modreader); + n.effect[i>>6].effect=i&31; + n.effect[i>>6].param=_mm_read_UBYTE(modreader); + x +=2; + } while (i&32); + } + memcpy(gdmbuf+(64U*ch)+pos,&n,sizeof(GDMNOTE)); + } + } + return 1; +} + +UBYTE *GDM_ConvertTrack(GDMNOTE*tr) +{ + int t,i=0; + UBYTE note,ins,inf; + + UniReset(); + for (t=0;t<64;t++) { + note=tr[t].note; + ins=tr[t].samp; + + if ((ins)&&(ins!=255)) + UniInstrument(ins-1); + if (note!=255) { + UniNote(((note>>4)*OCTAVE)+(note&0xf)-1); + } + for (i=0;i<4;i++) { + inf = tr[t].effect[i].param; + switch (tr[t].effect[i].effect) { + case 1: /* toneslide up */ + UniEffect(UNI_S3MEFFECTF,inf); + break; + case 2: /* toneslide down */ + UniEffect(UNI_S3MEFFECTE,inf); + break; + case 3: /* glissando to note */ + UniEffect(UNI_ITEFFECTG,inf); + break; + case 4: /* vibrato */ + UniEffect(UNI_ITEFFECTH,inf); + break; + case 5: /* portamento+volslide */ + UniEffect(UNI_ITEFFECTG,0); + UniEffect(UNI_S3MEFFECTD,inf); + break; + case 6: /* vibrato+volslide */ + UniEffect(UNI_ITEFFECTH,0); + UniEffect(UNI_S3MEFFECTD,inf); + break; + case 7: /* tremolo */ + UniEffect(UNI_S3MEFFECTR,inf); + break; + case 8: /* tremor */ + UniEffect(UNI_S3MEFFECTI,inf); + break; + case 9: /* offset */ + UniPTEffect(0x09,inf); + break; + case 0x0a: /* volslide */ + UniEffect(UNI_S3MEFFECTD,inf); + break; + case 0x0b: /* jump to order */ + UniPTEffect(0x0b,inf); + break; + case 0x0c: /* volume set */ + UniPTEffect(0x0c,inf); + break; + case 0x0d: /* pattern break */ + UniPTEffect(0x0d,inf); + break; + case 0x0e: /* extended */ + switch (inf&0xf0) { + case 0x10: /* fine portamento up */ + UniEffect(UNI_S3MEFFECTF, 0x0f|((inf<<4)&0x0f)); + break; + case 0x20: /* fine portamento down */ + UniEffect(UNI_S3MEFFECTE, 0xf0|(inf&0x0f)); + break; + case 0x30: /* glissando control */ + UniEffect(SS_GLISSANDO, inf&0x0f); + break; + case 0x40: /* vibrato waveform */ + UniEffect(SS_VIBWAVE, inf&0x0f); + break; + case 0x50: /* set c4spd */ + UniEffect(SS_FINETUNE, inf&0x0f); + break; + case 0x60: /* loop fun */ + UniEffect(UNI_ITEFFECTS0, (inf&0x0f)|0xb0); + break; + case 0x70: /* tremolo waveform */ + UniEffect(SS_TREMWAVE, inf&0x0f); + break; + case 0x80: /* extra fine porta up */ + UniEffect(UNI_S3MEFFECTF, 0x0e|((inf<<4)&0x0f)); + break; + case 0x90: /* extra fine porta down */ + UniEffect(UNI_S3MEFFECTE, 0xe0|(inf&0x0f)); + break; + case 0xa0: /* fine volslide up */ + UniEffect(UNI_S3MEFFECTD, 0x0f|((inf<<4)&0x0f)); + break; + case 0xb0: /* fine volslide down */ + UniEffect(UNI_S3MEFFECTE, 0xf0|(inf&0x0f)); + break; + case 0xc0: /* note cut */ + case 0xd0: /* note delay */ + case 0xe0: /* extend row */ + UniPTEffect(0xe,inf); + break; + } + break; + case 0x0f: /* set tempo */ + UniEffect(UNI_S3MEFFECTA,inf); + break; + case 0x10: /* arpeggio */ + UniPTEffect(0x0,inf); + break; + case 0x12: /* retrigger */ + UniEffect(UNI_S3MEFFECTQ,inf); + break; + case 0x13: /* set global volume */ + UniEffect(UNI_XMEFFECTG,inf<<1); + break; + case 0x14: /* fine vibrato */ + UniEffect(UNI_ITEFFECTU,inf); + break; + case 0x1e: /* special */ + switch (inf&0xf0) { + case 8: /* set pan position */ + if (inf >=128) + UniPTEffect(0x08,255); + else + UniPTEffect(0x08,inf<<1); + break; + } + break; + case 0x1f: /* set bpm */ + if (inf >=0x20) + UniEffect(UNI_S3MEFFECTT,inf); + break; + } + } + UniNewline(); + } + return UniDup(); +} + +BOOL GDM_Load(BOOL curious) +{ + int i,x,u,track; + SAMPLE *q; + GDMSAMPLE s; + ULONG position; + (void)curious; + + /* read header */ + _mm_read_string(mh->id1,4,modreader); + _mm_read_string(mh->songname,32,modreader); + _mm_read_string(mh->author,32,modreader); + _mm_read_string(mh->eofmarker,3,modreader); + _mm_read_string(mh->id2,4,modreader); + + mh->majorver=_mm_read_UBYTE(modreader); + mh->minorver=_mm_read_UBYTE(modreader); + mh->trackerid=_mm_read_I_UWORD(modreader); + mh->t_majorver=_mm_read_UBYTE(modreader); + mh->t_minorver=_mm_read_UBYTE(modreader); + _mm_read_UBYTES(mh->pantable,32,modreader); + mh->mastervol=_mm_read_UBYTE(modreader); + mh->mastertempo=_mm_read_UBYTE(modreader); + mh->masterbpm=_mm_read_UBYTE(modreader); + mh->flags=_mm_read_I_UWORD(modreader); + + mh->orderloc=_mm_read_I_ULONG(modreader); + mh->ordernum=_mm_read_UBYTE(modreader); + mh->patternloc=_mm_read_I_ULONG(modreader); + mh->patternnum=_mm_read_UBYTE(modreader); + mh->samhead=_mm_read_I_ULONG(modreader); + mh->samdata=_mm_read_I_ULONG(modreader); + mh->samnum=_mm_read_UBYTE(modreader); + mh->messageloc=_mm_read_I_ULONG(modreader); + mh->messagelen=_mm_read_I_ULONG(modreader); + mh->scrollyloc=_mm_read_I_ULONG(modreader); + mh->scrollylen=_mm_read_I_UWORD(modreader); + mh->graphicloc=_mm_read_I_ULONG(modreader); + mh->graphiclen=_mm_read_I_UWORD(modreader); + + /* have we ended abruptly? */ + if (_mm_eof(modreader)) { + _mm_errno=MMERR_LOADING_HEADER; + return 0; + } + + /* any orders? */ + if(mh->ordernum==255) { + _mm_errno=MMERR_LOADING_PATTERN; + return 0; + } + + /* now we fill */ + of.modtype=strdup(GDM_Version); + of.modtype[18]=mh->majorver+'0'; + of.modtype[20]=mh->minorver/10+'0'; + of.modtype[21]=mh->minorver%10+'0'; + of.songname=DupStr(mh->songname,32,0); + of.numpat=mh->patternnum+1; + of.reppos=0; + of.numins=of.numsmp=mh->samnum+1; + of.initspeed=mh->mastertempo; + of.inittempo=mh->masterbpm; + of.initvolume=mh->mastervol<<1; + of.flags|=UF_S3MSLIDES | UF_PANNING; + /* XXX whenever possible, we should try to determine the original format. + Here we assume it was S3M-style wrt bpmlimit... */ + of.bpmlimit = 32; + + /* read the order data */ + if (!AllocPositions(mh->ordernum+1)) { + _mm_errno=MMERR_OUT_OF_MEMORY; + return 0; + } + + _mm_fseek(modreader,mh->orderloc,SEEK_SET); + for (i=0;iordernum+1;i++) + of.positions[i]=_mm_read_UBYTE(modreader); + + of.numpos=0; + for (i=0;iordernum+1;i++) { + int order=of.positions[i]; + if(order==255) order=LAST_PATTERN; + of.positions[of.numpos]=order; + if (of.positions[i]<254) of.numpos++; + } + + /* have we ended abruptly yet? */ + if (_mm_eof(modreader)) { + _mm_errno=MMERR_LOADING_HEADER; + return 0; + } + + /* time to load the samples */ + if (!AllocSamples()) { + _mm_errno=MMERR_OUT_OF_MEMORY; + return 0; + } + + q=of.samples; + position=mh->samdata; + + /* seek to instrument position */ + _mm_fseek(modreader,mh->samhead,SEEK_SET); + + for (i=0;isamplename=DupStr(s.sampname,32,0); + q->speed=s.c4spd; + q->length=s.length; + q->loopstart=s.loopbeg; + q->loopend=s.loopend; + q->volume=s.vol; + q->panning=s.pan; + q->seekpos=position; + + position +=s.length; + + if (s.flags&1) + q->flags |=SF_LOOP; + if (s.flags&2) + q->flags |=SF_16BITS; + if (s.flags&16) + q->flags |=SF_STEREO; + q++; + } + + /* set the panning */ + for (i=x=0;i<32;i++) { + of.panning[i]=mh->pantable[i]; + if (!of.panning[i]) + of.panning[i]=PAN_LEFT; + else if (of.panning[i]==8) + of.panning[i]=PAN_CENTER; + else if (of.panning[i]==15) + of.panning[i]=PAN_RIGHT; + else if (of.panning[i]==16) + of.panning[i]=PAN_SURROUND; + else if (of.panning[i]==255) + of.panning[i]=128; + else + of.panning[i]<<=3; + if (mh->pantable[i]!=255) + x=i; + } + + of.numchn=x+1; + if (of.numchn<1) + of.numchn=1; /* for broken counts */ + + /* load the pattern info */ + of.numtrk=of.numpat*of.numchn; + + /* jump to patterns */ + _mm_fseek(modreader,mh->patternloc,SEEK_SET); + + if (!AllocTracks()) { + _mm_errno=MMERR_OUT_OF_MEMORY; + return 0; + } + + if (!AllocPatterns()) { + _mm_errno=MMERR_OUT_OF_MEMORY; + return 0; + } + + for (i=track=0;i +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Module structure */ + +/* module header */ +typedef struct IMFHEADER { + CHAR songname[32]; + UWORD ordnum; + UWORD patnum; + UWORD insnum; + UWORD flags; + UBYTE initspeed; + UBYTE inittempo; + UBYTE mastervol; + UBYTE mastermult; + UBYTE orders[256]; +} IMFHEADER; + +/* channel settings */ +typedef struct IMFCHANNEL { + CHAR name[12]; + UBYTE chorus; + UBYTE reverb; + UBYTE pan; + UBYTE status; +} IMFCHANNEL; + +/* instrument header */ +#define IMFNOTECNT (10*OCTAVE) +#define IMFENVCNT (16*2) +typedef struct IMFINSTHEADER { + CHAR name[32]; + UBYTE what[IMFNOTECNT]; + UWORD volenv[IMFENVCNT]; + UWORD panenv[IMFENVCNT]; + UWORD pitenv[IMFENVCNT]; + UBYTE volpts; + UBYTE volsus; + UBYTE volbeg; + UBYTE volend; + UBYTE volflg; + UBYTE panpts; + UBYTE pansus; + UBYTE panbeg; + UBYTE panend; + UBYTE panflg; + UBYTE pitpts; + UBYTE pitsus; + UBYTE pitbeg; + UBYTE pitend; + UBYTE pitflg; + UWORD volfade; + UWORD numsmp; + ULONG signature; +} IMFINSTHEADER; + +/* sample header */ +typedef struct IMFWAVHEADER { + CHAR samplename[13]; + ULONG length; + ULONG loopstart; + ULONG loopend; + ULONG samplerate; + UBYTE volume; + UBYTE pan; + UBYTE flags; +} IMFWAVHEADER; + +typedef struct IMFNOTE { + UBYTE note,ins,eff1,dat1,eff2,dat2; +} IMFNOTE; + +/*========== Loader variables */ + +static CHAR IMF_Version[]="Imago Orpheus"; + +static IMFNOTE *imfpat=NULL; +static IMFHEADER *mh=NULL; + +/*========== Loader code */ + +BOOL IMF_Test(void) +{ + UBYTE id[4]; + + _mm_fseek(modreader,0x3c,SEEK_SET); + if(!_mm_read_UBYTES(id,4,modreader)) return 0; + if(!memcmp(id,"IM10",4)) return 1; + return 0; +} + +BOOL IMF_Init(void) +{ + if(!(imfpat=(IMFNOTE*)_mm_malloc(32*256*sizeof(IMFNOTE)))) return 0; + if(!(mh=(IMFHEADER*)_mm_malloc(sizeof(IMFHEADER)))) return 0; + + return 1; +} + +void IMF_Cleanup(void) +{ + FreeLinear(); + + _mm_free(imfpat); + _mm_free(mh); +} + +static BOOL IMF_ReadPattern(SLONG size,UWORD rows) +{ + int row=0,flag,ch; + IMFNOTE *n,dummy; + + /* clear pattern data */ + memset(imfpat,255,32*256*sizeof(IMFNOTE)); + + while((size>0)&&(rownote=_mm_read_UBYTE(modreader); + if(n->note>=0xa0) n->note=0xa0; /* note off */ + n->ins =_mm_read_UBYTE(modreader); + size-=2; + } + if(flag&64) { + size-=2; + n->eff2=_mm_read_UBYTE(modreader); + n->dat2=_mm_read_UBYTE(modreader); + } + if(flag&128) { + n->eff1=_mm_read_UBYTE(modreader); + n->dat1=_mm_read_UBYTE(modreader); + size-=2; + } + } else row++; + } + if((size)||(row!=rows)) { + _mm_errno=MMERR_LOADING_PATTERN; + return 0; + } + return 1; +} + +static void IMF_ProcessCmd(UBYTE eff,UBYTE inf) +{ + if((eff)&&(eff!=255)) + switch (eff) { + case 0x01: /* set tempo */ + UniEffect(UNI_S3MEFFECTA,inf); + break; + case 0x02: /* set BPM */ + if(inf>=0x20) UniEffect(UNI_S3MEFFECTT,inf); + break; + case 0x03: /* tone portamento */ + UniEffect(UNI_ITEFFECTG,inf); + break; + case 0x04: /* porta + volslide */ + UniEffect(UNI_ITEFFECTG,inf); + UniEffect(UNI_S3MEFFECTD,0); + break; + case 0x05: /* vibrato */ + UniEffect(UNI_XMEFFECT4,inf); + break; + case 0x06: /* vibrato + volslide */ + UniEffect(UNI_XMEFFECT6,inf); + break; + case 0x07: /* fine vibrato */ + UniEffect(UNI_ITEFFECTU,inf); + break; + case 0x08: /* tremolo */ + UniEffect(UNI_S3MEFFECTR,inf); + break; + case 0x09: /* arpeggio */ + UniPTEffect(0x0,inf); + break; + case 0x0a: /* panning */ + UniPTEffect(0x8,(inf>=128)?255:(inf<<1)); + break; + case 0x0b: /* pan slide */ + UniEffect(UNI_XMEFFECTP,inf); + break; + case 0x0c: /* set channel volume */ + if(inf<=64) UniPTEffect(0xc,inf); + break; + case 0x0d: /* volume slide */ + UniEffect(UNI_S3MEFFECTD,inf); + break; + case 0x0e: /* fine volume slide */ + if(inf) { + if(inf>>4) + UniEffect(UNI_S3MEFFECTD,0x0f|inf); + else + UniEffect(UNI_S3MEFFECTD,0xf0|inf); + } else + UniEffect(UNI_S3MEFFECTD,0); + break; + case 0x0f: /* set finetune */ + UniPTEffect(0xe,0x50|(inf>>4)); + break; +#ifdef MIKMOD_DEBUG + case 0x10: /* note slide up */ + case 0x11: /* not slide down */ + fprintf(stderr,"\rIMF effect 0x10/0x11 (note slide)" + " not implemented (eff=%2X inf=%2X)\n",eff,inf); + break; +#endif + case 0x12: /* slide up */ + UniEffect(UNI_S3MEFFECTF,inf); + break; + case 0x13: /* slide down */ + UniEffect(UNI_S3MEFFECTE,inf); + break; + case 0x14: /* fine slide up */ + if (inf) { + if (inf<0x40) + UniEffect(UNI_S3MEFFECTF,0xe0|(inf>>2)); + else + UniEffect(UNI_S3MEFFECTF,0xf0|(inf>>4)); + } else + UniEffect(UNI_S3MEFFECTF,0); + break; + case 0x15: /* fine slide down */ + if (inf) { + if (inf<0x40) + UniEffect(UNI_S3MEFFECTE,0xe0|(inf>>2)); + else + UniEffect(UNI_S3MEFFECTE,0xf0|(inf>>4)); + } else + UniEffect(UNI_S3MEFFECTE,0); + break; + /* 0x16 set filter cutoff (awe32) */ + /* 0x17 filter side + resonance (awe32) */ + case 0x18: /* sample offset */ + UniPTEffect(0x9,inf); + break; +#ifdef MIKMOD_DEBUG + case 0x19: /* set fine sample offset */ + fprintf(stderr,"\rIMF effect 0x19 (fine sample offset)" + " not implemented (inf=%2X)\n",inf); + break; +#endif + case 0x1a: /* keyoff */ + UniWriteByte(UNI_KEYOFF); + break; + case 0x1b: /* retrig */ + UniEffect(UNI_S3MEFFECTQ,inf); + break; + case 0x1c: /* tremor */ + UniEffect(UNI_S3MEFFECTI,inf); + break; + case 0x1d: /* position jump */ + UniPTEffect(0xb,inf); + break; + case 0x1e: /* pattern break */ + UniPTEffect(0xd,(inf>>4)*10+(inf&0xf)); + break; + case 0x1f: /* set master volume */ + if(inf<=64) UniEffect(UNI_XMEFFECTG,inf<<1); + break; + case 0x20: /* master volume slide */ + UniEffect(UNI_XMEFFECTH,inf); + break; + case 0x21: /* extended effects */ + switch(inf>>4) { + case 0x1: /* set filter */ + case 0x5: /* vibrato waveform */ + case 0x8: /* tremolo waveform */ + UniPTEffect(0xe,inf-0x10); + break; + case 0xa: /* pattern loop */ + UniPTEffect(0xe,0x60|(inf&0xf)); + break; + case 0xb: /* pattern delay */ + UniPTEffect(0xe,0xe0|(inf&0xf)); + break; + case 0x3: /* glissando */ + case 0xc: /* note cut */ + case 0xd: /* note delay */ + case 0xf: /* invert loop */ + UniPTEffect(0xe,inf); + break; + case 0xe: /* ignore envelope */ + UniEffect(UNI_ITEFFECTS0, 0x77); /* vol */ + UniEffect(UNI_ITEFFECTS0, 0x79); /* pan */ + UniEffect(UNI_ITEFFECTS0, 0x7b); /* pit */ + break; + } + break; + /* 0x22 chorus (awe32) */ + /* 0x23 reverb (awe32) */ + } +} + +static UBYTE* IMF_ConvertTrack(IMFNOTE* tr,UWORD rows) +{ + int t; + UBYTE note,ins; + + UniReset(); + for(t=0;t>4)*OCTAVE)+(note&0xf)); + } + + IMF_ProcessCmd(tr[t].eff1,tr[t].dat1); + IMF_ProcessCmd(tr[t].eff2,tr[t].dat2); + UniNewline(); + } + return UniDup(); +} + +BOOL IMF_Load(BOOL curious) +{ +#define IMF_SMPINCR 64 + int t,u,track=0,oldnumsmp; + IMFCHANNEL channels[32]; + INSTRUMENT *d; + SAMPLE *q; + IMFWAVHEADER *wh=NULL,*s=NULL; + ULONG *nextwav=NULL; + UWORD wavcnt=0; + UBYTE id[4]; + (void)curious; + + /* try to read the module header */ + _mm_read_string(mh->songname,32,modreader); + mh->ordnum=_mm_read_I_UWORD(modreader); + mh->patnum=_mm_read_I_UWORD(modreader); + mh->insnum=_mm_read_I_UWORD(modreader); + mh->flags =_mm_read_I_UWORD(modreader); + _mm_fseek(modreader,8,SEEK_CUR); + mh->initspeed =_mm_read_UBYTE(modreader); + mh->inittempo =_mm_read_UBYTE(modreader); + mh->mastervol =_mm_read_UBYTE(modreader); + mh->mastermult=_mm_read_UBYTE(modreader); + _mm_fseek(modreader,64,SEEK_SET); + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* set module variables */ + of.songname=DupStr(mh->songname,31,1); + of.modtype=strdup(IMF_Version); + of.numpat=mh->patnum; + of.numins=mh->insnum; + of.reppos=0; + of.initspeed=mh->initspeed; + of.inittempo=mh->inittempo; + of.initvolume=mh->mastervol<<1; + of.flags |= UF_INST | UF_ARPMEM | UF_PANNING; + if(mh->flags&1) of.flags |= UF_LINEAR; + of.bpmlimit=32; + + /* read channel information */ + of.numchn=0; + memset(remap,-1,32*sizeof(UBYTE)); + for(t=0;t<32;t++) { + _mm_read_string(channels[t].name,12,modreader); + channels[t].chorus=_mm_read_UBYTE(modreader); + channels[t].reverb=_mm_read_UBYTE(modreader); + channels[t].pan =_mm_read_UBYTE(modreader); + channels[t].status=_mm_read_UBYTE(modreader); + } + /* bug in Imago Orpheus ? If only channel 1 is enabled, in fact we have to + enable 16 channels */ + if(!channels[0].status) { + for(t=1;t<16;t++) if(channels[t].status!=1) break; + if(t==16) for(t=1;t<16;t++) channels[t].status=0; + } + for(t=0;t<32;t++) { + if(channels[t].status!=2) + remap[t]=of.numchn++; + else + remap[t]=-1; + } + for(t=0;t<32;t++) + if(remap[t]!=-1) { + of.panning[remap[t]]=channels[t].pan; + of.chanvol[remap[t]]=channels[t].status?0:64; + } + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* read order list */ + _mm_read_UBYTES(mh->orders,256,modreader); + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + of.numpos=0; + for(t=0;tordnum;t++) + if(mh->orders[t]!=0xff) of.numpos++; + if(!AllocPositions(of.numpos)) return 0; + for(t=u=0;tordnum;t++) + if(mh->orders[t]!=0xff) of.positions[u++]=mh->orders[t]; + + /* load pattern info */ + of.numtrk=of.numpat*of.numchn; + if(!AllocTracks()) return 0; + if(!AllocPatterns()) return 0; + + for(t=0;t256)||(size<4)) { + _mm_errno=MMERR_LOADING_PATTERN; + return 0; + } + + of.pattrows[t]=rows; + if(!IMF_ReadPattern(size-4,rows)) return 0; + for(u=0;usamplenumber,0xff,INSTNOTES*sizeof(UWORD)); + + /* read instrument header */ + _mm_read_string(ih.name,32,modreader); + d->insname=DupStr(ih.name,31,1); + _mm_read_UBYTES(ih.what,IMFNOTECNT,modreader); + _mm_fseek(modreader,8,SEEK_CUR); + _mm_read_I_UWORDS(ih.volenv,IMFENVCNT,modreader); + _mm_read_I_UWORDS(ih.panenv,IMFENVCNT,modreader); + _mm_read_I_UWORDS(ih.pitenv,IMFENVCNT,modreader); + +#if defined __STDC__ || defined _MSC_VER || defined MPW_C +#define IMF_FinishLoadingEnvelope(name) \ + ih. name##pts=_mm_read_UBYTE(modreader); \ + ih. name##sus=_mm_read_UBYTE(modreader); \ + ih. name##beg=_mm_read_UBYTE(modreader); \ + ih. name##end=_mm_read_UBYTE(modreader); \ + ih. name##flg=_mm_read_UBYTE(modreader); \ + _mm_read_UBYTE(modreader); \ + _mm_read_UBYTE(modreader); \ + _mm_read_UBYTE(modreader) +#else +#define IMF_FinishLoadingEnvelope(name) \ + ih. name/**/pts=_mm_read_UBYTE(modreader); \ + ih. name/**/sus=_mm_read_UBYTE(modreader); \ + ih. name/**/beg=_mm_read_UBYTE(modreader); \ + ih. name/**/end=_mm_read_UBYTE(modreader); \ + ih. name/**/flg=_mm_read_UBYTE(modreader); \ + _mm_read_UBYTE(modreader); \ + _mm_read_UBYTE(modreader); \ + _mm_read_UBYTE(modreader) +#endif + + IMF_FinishLoadingEnvelope(vol); + IMF_FinishLoadingEnvelope(pan); + IMF_FinishLoadingEnvelope(pit); + + ih.volfade=_mm_read_I_UWORD(modreader); + ih.numsmp =_mm_read_I_UWORD(modreader); + + _mm_read_UBYTES(id,4,modreader); + /* Looks like Imago Orpheus forgets the signature for empty + instruments following a multi-sample instrument... */ + if(memcmp(id,"II10",4) && + (oldnumsmp && memcmp(id,"\x0\x0\x0\x0",4))) { + if(nextwav) free(nextwav); + if(wh) free(wh); + _mm_errno=MMERR_LOADING_SAMPLEINFO; + return 0; + } + oldnumsmp=ih.numsmp; + + if((ih.numsmp>16)||(ih.volpts>IMFENVCNT/2)||(ih.panpts>IMFENVCNT/2)|| + (ih.pitpts>IMFENVCNT/2)||(_mm_eof(modreader))) { + if(nextwav) free(nextwav); + if(wh) free(wh); + _mm_errno=MMERR_LOADING_SAMPLEINFO; + return 0; + } + + for(u=0;usamplenumber[u]=ih.what[u]>ih.numsmp?0xffff:ih.what[u]+of.numsmp; + d->volfade=ih.volfade; + +#if defined __STDC__ || defined _MSC_VER || defined MPW_C +#define IMF_ProcessEnvelope(name) \ + for (u = 0; u < (IMFENVCNT >> 1); u++) { \ + d-> name##env[u].pos = ih. name##env[u << 1]; \ + d-> name##env[u].val = ih. name##env[(u << 1)+ 1]; \ + } \ + if (ih. name##flg&1) d-> name##flg|=EF_ON; \ + if (ih. name##flg&2) d-> name##flg|=EF_SUSTAIN; \ + if (ih. name##flg&4) d-> name##flg|=EF_LOOP; \ + d-> name##susbeg=d-> name##susend=ih. name##sus; \ + d-> name##beg=ih. name##beg; \ + d-> name##end=ih. name##end; \ + d-> name##pts=ih. name##pts; \ + \ + if ((d-> name##flg&EF_ON)&&(d-> name##pts<2)) \ + d-> name##flg&=~EF_ON +#else +#define IMF_ProcessEnvelope(name) \ + for (u = 0; u < (IMFENVCNT >> 1); u++) { \ + d-> name/**/env[u].pos = ih. name/**/env[u << 1]; \ + d-> name/**/env[u].val = ih. name/**/env[(u << 1)+ 1]; \ + } \ + if (ih. name/**/flg&1) d-> name/**/flg|=EF_ON; \ + if (ih. name/**/flg&2) d-> name/**/flg|=EF_SUSTAIN; \ + if (ih. name/**/flg&4) d-> name/**/flg|=EF_LOOP; \ + d-> name/**/susbeg=d-> name/**/susend=ih. name/**/sus; \ + d-> name/**/beg=ih. name/**/beg; \ + d-> name/**/end=ih. name/**/end; \ + d-> name/**/pts=ih. name/**/pts; \ + \ + if ((d-> name/**/flg&EF_ON)&&(d-> name/**/pts<2)) \ + d-> name/**/flg&=~EF_ON +#endif + + IMF_ProcessEnvelope(vol); + IMF_ProcessEnvelope(pan); + IMF_ProcessEnvelope(pit); +#undef IMF_ProcessEnvelope + + if(ih.pitflg&1) { + d->pitflg&=~EF_ON; +#ifdef MIKMOD_DEBUG + fprintf(stderr, "\rFilter envelopes not supported yet\n"); +#endif + } + + /* gather sample information */ + for(u=0;usamplename,13,modreader); + _mm_read_UBYTE(modreader);_mm_read_UBYTE(modreader);_mm_read_UBYTE(modreader); + s->length =_mm_read_I_ULONG(modreader); + s->loopstart =_mm_read_I_ULONG(modreader); + s->loopend =_mm_read_I_ULONG(modreader); + s->samplerate=_mm_read_I_ULONG(modreader); + s->volume =_mm_read_UBYTE(modreader)&0x7f; + s->pan =_mm_read_UBYTE(modreader); + _mm_fseek(modreader,14,SEEK_CUR); + s->flags =_mm_read_UBYTE(modreader); + _mm_fseek(modreader,11,SEEK_CUR); + _mm_read_UBYTES(id,4,modreader); + if(((memcmp(id,"IS10",4))&&(memcmp(id,"IW10",4)))|| + (_mm_eof(modreader))) { + free(nextwav);free(wh); + _mm_errno=MMERR_LOADING_SAMPLEINFO; + return 0; + } + nextwav[of.numsmp+u]=_mm_ftell(modreader); + _mm_fseek(modreader,s->length,SEEK_CUR); + } + + of.numsmp+=ih.numsmp; + d++; + } + + /* sanity check */ + if(!of.numsmp) { + if(nextwav) free(nextwav); + if(wh) free(wh); + _mm_errno=MMERR_LOADING_SAMPLEINFO; + return 0; + } + + /* load samples */ + if(!AllocSamples()) { + free(nextwav);free(wh); + return 0; + } + if(!AllocLinear()) { + free(nextwav);free(wh); + return 0; + } + q=of.samples; + s=wh; + for(u=0;usamplename=DupStr(s->samplename,12,1); + q->length =s->length; + q->loopstart=s->loopstart; + q->loopend =s->loopend; + q->volume =s->volume; + q->speed =s->samplerate; + if(of.flags&UF_LINEAR) + q->speed=speed_to_finetune(s->samplerate<<1,u); + q->panning =s->pan; + q->seekpos =nextwav[u]; + + q->flags|=SF_SIGNED; + if(s->flags&0x1) q->flags|=SF_LOOP; + if(s->flags&0x2) q->flags|=SF_BIDI; + if(s->flags&0x8) q->flags|=SF_OWNPAN; + if(s->flags&0x4) { + q->flags|=SF_16BITS; + q->length >>=1; + q->loopstart>>=1; + q->loopend >>=1; + } + } + + d=of.instruments; + s=wh; + for(u=0;usamplenumber[t]>=of.numsmp) + d->samplenote[t]=255; + else if (of.flags&UF_LINEAR) { + int note=(int)d->samplenote[u]+noteindex[d->samplenumber[u]]; + d->samplenote[u]=(note<0)?0:(note>255?255:note); + } else + d->samplenote[t]=t; + } + } + + free(wh);free(nextwav); + return 1; +} + +CHAR *IMF_LoadTitle(void) +{ + CHAR s[31]; + + _mm_fseek(modreader,0,SEEK_SET); + if(!_mm_read_UBYTES(s,31,modreader)) return NULL; + + return(DupStr(s,31,1)); +} + +/*========== Loader information */ + +MIKMODAPI MLOADER load_imf={ + NULL, + "IMF", + "IMF (Imago Orpheus)", + IMF_Init, + IMF_Test, + IMF_Load, + IMF_Cleanup, + IMF_LoadTitle +}; + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/loaders/load_it.c b/apps/plugins/mikmod/loaders/load_it.c new file mode 100644 index 0000000..d115af5 --- /dev/null +++ b/apps/plugins/mikmod/loaders/load_it.c @@ -0,0 +1,1008 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file + AUTHORS for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: load_it.c,v 1.2 2004/02/06 19:29:03 raph Exp $ + + Impulse tracker (IT) module loader + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +//#include +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +extern int toupper(int); +#endif + +/*========== Module structure */ + +/* header */ +typedef struct ITHEADER { + CHAR songname[26]; + UBYTE blank01[2]; + UWORD ordnum; + UWORD insnum; + UWORD smpnum; + UWORD patnum; + UWORD cwt; /* Created with tracker (y.xx = 0x0yxx) */ + UWORD cmwt; /* Compatible with tracker ver > than val. */ + UWORD flags; + UWORD special; /* bit 0 set = song message attached */ + UBYTE globvol; + UBYTE mixvol; /* mixing volume [ignored] */ + UBYTE initspeed; + UBYTE inittempo; + UBYTE pansep; /* panning separation between channels */ + UBYTE zerobyte; + UWORD msglength; + ULONG msgoffset; + UBYTE blank02[4]; + UBYTE pantable[64]; + UBYTE voltable[64]; +} ITHEADER; + +/* sample information */ +typedef struct ITSAMPLE { + CHAR filename[12]; + UBYTE zerobyte; + UBYTE globvol; + UBYTE flag; + UBYTE volume; + UBYTE panning; + CHAR sampname[28]; + UWORD convert; /* sample conversion flag */ + ULONG length; + ULONG loopbeg; + ULONG loopend; + ULONG c5spd; + ULONG susbegin; + ULONG susend; + ULONG sampoffset; + UBYTE vibspeed; + UBYTE vibdepth; + UBYTE vibrate; + UBYTE vibwave; /* 0=sine, 1=rampdown, 2=square, 3=random (speed ignored) */ +} ITSAMPLE; + +/* instrument information */ + +#define ITENVCNT 25 +#define ITNOTECNT 120 +typedef struct ITINSTHEADER { + ULONG size; /* (dword) Instrument size */ + CHAR filename[12]; /* (char) Instrument filename */ + UBYTE zerobyte; /* (byte) Instrument type (always 0) */ + UBYTE volflg; + UBYTE volpts; + UBYTE volbeg; /* (byte) Volume loop start (node) */ + UBYTE volend; /* (byte) Volume loop end (node) */ + UBYTE volsusbeg; /* (byte) Volume sustain begin (node) */ + UBYTE volsusend; /* (byte) Volume Sustain end (node) */ + UBYTE panflg; + UBYTE panpts; + UBYTE panbeg; /* (byte) channel loop start (node) */ + UBYTE panend; /* (byte) channel loop end (node) */ + UBYTE pansusbeg; /* (byte) channel sustain begin (node) */ + UBYTE pansusend; /* (byte) channel Sustain end (node) */ + UBYTE pitflg; + UBYTE pitpts; + UBYTE pitbeg; /* (byte) pitch loop start (node) */ + UBYTE pitend; /* (byte) pitch loop end (node) */ + UBYTE pitsusbeg; /* (byte) pitch sustain begin (node) */ + UBYTE pitsusend; /* (byte) pitch Sustain end (node) */ + UWORD blank; + UBYTE globvol; + UBYTE chanpan; + UWORD fadeout; /* Envelope end / NNA volume fadeout */ + UBYTE dnc; /* Duplicate note check */ + UBYTE dca; /* Duplicate check action */ + UBYTE dct; /* Duplicate check type */ + UBYTE nna; /* New Note Action [0,1,2,3] */ + UWORD trkvers; /* tracker version used to save [files only] */ + UBYTE ppsep; /* Pitch-pan Separation */ + UBYTE ppcenter; /* Pitch-pan Center */ + UBYTE rvolvar; /* random volume varations */ + UBYTE rpanvar; /* random panning varations */ + UWORD numsmp; /* Number of samples in instrument [files only] */ + CHAR name[26]; /* Instrument name */ + UBYTE blank01[6]; + UWORD samptable[ITNOTECNT];/* sample for each note [note / samp pairs] */ + UBYTE volenv[200]; /* volume envelope (IT 1.x stuff) */ + UBYTE oldvoltick[ITENVCNT];/* volume tick position (IT 1.x stuff) */ + UBYTE volnode[ITENVCNT]; /* amplitude of volume nodes */ + UWORD voltick[ITENVCNT]; /* tick value of volume nodes */ + SBYTE pannode[ITENVCNT]; /* panenv - node points */ + UWORD pantick[ITENVCNT]; /* tick value of panning nodes */ + SBYTE pitnode[ITENVCNT]; /* pitchenv - node points */ + UWORD pittick[ITENVCNT]; /* tick value of pitch nodes */ +} ITINSTHEADER; + +/* unpacked note */ + +typedef struct ITNOTE { + UBYTE note,ins,volpan,cmd,inf; +} ITNOTE; + +/*========== Loader data */ + +static ULONG *paraptr=NULL; /* parapointer array (see IT docs) */ +static ITHEADER *mh=NULL; +static ITNOTE *itpat=NULL; /* allocate to space for one full pattern */ +static UBYTE *mask=NULL; /* arrays allocated to 64 elements and used for */ +static ITNOTE *last=NULL; /* uncompressing IT's pattern information */ +static int numtrk=0; +static unsigned int old_effect; /* if set, use S3M old-effects stuffs */ + +static CHAR* IT_Version[]={ + "ImpulseTracker . ", + "Compressed ImpulseTracker . ", + "ImpulseTracker 2.14p3", + "Compressed ImpulseTracker 2.14p3", + "ImpulseTracker 2.14p4", + "Compressed ImpulseTracker 2.14p4", +}; + +/* table for porta-to-note command within volume/panning column */ +static UBYTE portatable[10]= {0,1,4,8,16,32,64,96,128,255}; + +/*========== Loader code */ + +BOOL IT_Test(void) +{ + UBYTE id[4]; + + if(!_mm_read_UBYTES(id,4,modreader)) return 0; + if(!memcmp(id,"IMPM",4)) return 1; + return 0; +} + +BOOL IT_Init(void) +{ + if(!(mh=(ITHEADER*)_mm_malloc(sizeof(ITHEADER)))) return 0; + if(!(poslookup=(UBYTE*)_mm_malloc(256*sizeof(UBYTE)))) return 0; + if(!(itpat=(ITNOTE*)_mm_malloc(200*64*sizeof(ITNOTE)))) return 0; + if(!(mask=(UBYTE*)_mm_malloc(64*sizeof(UBYTE)))) return 0; + if(!(last=(ITNOTE*)_mm_malloc(64*sizeof(ITNOTE)))) return 0; + + return 1; +} + +void IT_Cleanup(void) +{ + FreeLinear(); + + _mm_free(mh); + _mm_free(poslookup); + _mm_free(itpat); + _mm_free(mask); + _mm_free(last); + _mm_free(paraptr); + _mm_free(origpositions); +} + +/* Because so many IT files have 64 channels as the set number used, but really + only use far less (usually from 8 to 24 still), I had to make this function, + which determines the number of channels that are actually USED by a pattern. + + NOTE: You must first seek to the file location of the pattern before calling + this procedure. + + Returns 1 on error +*/ +static BOOL IT_GetNumChannels(UWORD patrows) +{ + int row=0,flag,ch; + + do { + if((flag=_mm_read_UBYTE(modreader))==EOF) { + _mm_errno=MMERR_LOADING_PATTERN; + return 1; + } + if(!flag) + row++; + else { + ch=(flag-1)&63; + remap[ch]=0; + if(flag & 128) mask[ch]=_mm_read_UBYTE(modreader); + if(mask[ch]&1) _mm_read_UBYTE(modreader); + if(mask[ch]&2) _mm_read_UBYTE(modreader); + if(mask[ch]&4) _mm_read_UBYTE(modreader); + if(mask[ch]&8) { _mm_read_UBYTE(modreader);_mm_read_UBYTE(modreader); } + } + } while(rownote=n->note=_mm_read_UBYTE(modreader))==255) + l->note=n->note=253; + if(mask[ch]&2) + l->ins=n->ins=_mm_read_UBYTE(modreader); + if(mask[ch]&4) + l->volpan=n->volpan=_mm_read_UBYTE(modreader); + if(mask[ch]&8) { + l->cmd=n->cmd=_mm_read_UBYTE(modreader); + l->inf=n->inf=_mm_read_UBYTE(modreader); + } + if(mask[ch]&16) + n->note=l->note; + if(mask[ch]&32) + n->ins=l->ins; + if(mask[ch]&64) + n->volpan=l->volpan; + if(mask[ch]&128) { + n->cmd=l->cmd; + n->inf=l->inf; + } + } + } while(rowsongname,26,modreader); + _mm_read_UBYTES(mh->blank01,2,modreader); + mh->ordnum =_mm_read_I_UWORD(modreader); + mh->insnum =_mm_read_I_UWORD(modreader); + mh->smpnum =_mm_read_I_UWORD(modreader); + mh->patnum =_mm_read_I_UWORD(modreader); + mh->cwt =_mm_read_I_UWORD(modreader); + mh->cmwt =_mm_read_I_UWORD(modreader); + mh->flags =_mm_read_I_UWORD(modreader); + mh->special =_mm_read_I_UWORD(modreader); + mh->globvol =_mm_read_UBYTE(modreader); + mh->mixvol =_mm_read_UBYTE(modreader); + mh->initspeed =_mm_read_UBYTE(modreader); + mh->inittempo =_mm_read_UBYTE(modreader); + mh->pansep =_mm_read_UBYTE(modreader); + mh->zerobyte =_mm_read_UBYTE(modreader); + mh->msglength =_mm_read_I_UWORD(modreader); + mh->msgoffset =_mm_read_I_ULONG(modreader); + _mm_read_UBYTES(mh->blank02,4,modreader); + _mm_read_UBYTES(mh->pantable,64,modreader); + _mm_read_UBYTES(mh->voltable,64,modreader); + + if(_mm_eof(modreader)) { + _mm_errno=MMERR_LOADING_HEADER; + return 0; + } + + /* set module variables */ + of.songname = DupStr(mh->songname,26,0); /* make a cstr of songname */ + of.reppos = 0; + of.numpat = mh->patnum; + of.numins = mh->insnum; + of.numsmp = mh->smpnum; + of.initspeed = mh->initspeed; + of.inittempo = mh->inittempo; + of.initvolume = mh->globvol; + of.flags |= UF_BGSLIDES | UF_ARPMEM; + if (!(mh->flags & 1)) + of.flags |= UF_PANNING; + of.bpmlimit=32; + + if(mh->songname[25]) { + of.numvoices=1+mh->songname[25]; +#ifdef MIKMOD_DEBUG + fprintf(stderr,"Embedded IT limitation to %d voices\n",of.numvoices); +#endif + } + + /* set the module type */ + /* 2.17 : IT 2.14p4 */ + /* 2.16 : IT 2.14p3 with resonant filters */ + /* 2.15 : IT 2.14p3 (improved compression) */ + if((mh->cwt<=0x219)&&(mh->cwt>=0x217)) + of.modtype=strdup(IT_Version[mh->cmwt<0x214?4:5]); + else if (mh->cwt>=0x215) + of.modtype=strdup(IT_Version[mh->cmwt<0x214?2:3]); + else { + of.modtype = strdup(IT_Version[mh->cmwt<0x214?0:1]); + of.modtype[mh->cmwt<0x214?15:26] = (mh->cwt>>8)+'0'; + of.modtype[mh->cmwt<0x214?17:28] = ((mh->cwt>>4)&0xf)+'0'; + of.modtype[mh->cmwt<0x214?18:29] = ((mh->cwt)&0xf)+'0'; + } + + if(mh->flags&8) + of.flags |= UF_XMPERIODS | UF_LINEAR; + + if((mh->cwt>=0x106)&&(mh->flags&16)) + old_effect=S3MIT_OLDSTYLE; + else + old_effect=0; + + /* set panning positions */ + if (mh->flags & 1) + for(t=0;t<64;t++) { + mh->pantable[t]&=0x7f; + if(mh->pantable[t]<64) + of.panning[t]=mh->pantable[t]<<2; + else if(mh->pantable[t]==64) + of.panning[t]=255; + else if(mh->pantable[t]==100) + of.panning[t]=PAN_SURROUND; + else if(mh->pantable[t]==127) + of.panning[t]=PAN_CENTER; + else { + _mm_errno=MMERR_LOADING_HEADER; + return 0; + } + } + else + for(t=0;t<64;t++) + of.panning[t]=PAN_CENTER; + + /* set channel volumes */ + memcpy(of.chanvol,mh->voltable,64); + + /* read the order data */ + if(!AllocPositions(mh->ordnum)) return 0; + if(!(origpositions=_mm_calloc(mh->ordnum,sizeof(UWORD)))) return 0; + + for(t=0;tordnum;t++) { + origpositions[t]=_mm_read_UBYTE(modreader); + if((origpositions[t]>mh->patnum)&&(origpositions[t]<254)) + origpositions[t]=255; + } + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + poslookupcnt=mh->ordnum; + S3MIT_CreateOrders(curious); + + if(!(paraptr=(ULONG*)_mm_malloc((mh->insnum+mh->smpnum+of.numpat)* + sizeof(ULONG)))) return 0; + + /* read the instrument, sample, and pattern parapointers */ + _mm_read_I_ULONGS(paraptr,mh->insnum+mh->smpnum+of.numpat,modreader); + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* Check for and load midi information for resonant filters */ + if(mh->cmwt>=0x216) { + if(mh->special&8) { + IT_LoadMidiConfiguration(modreader); + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + } else + IT_LoadMidiConfiguration(NULL); + filters=1; + } + + /* Check for and load song comment */ + if((mh->special&1)&&(mh->cwt>=0x104)&&(mh->msglength)) { + _mm_fseek(modreader,(long)(mh->msgoffset),SEEK_SET); + if(!ReadComment(mh->msglength)) return 0; + } + + if(!(mh->flags&4)) of.numins=of.numsmp; + if(!AllocSamples()) return 0; + + if(!AllocLinear()) return 0; + + /* Load all samples */ + q = of.samples; + for(t=0;tsmpnum;t++) { + ITSAMPLE s; + + /* seek to sample position */ + _mm_fseek(modreader,(long)(paraptr[mh->insnum+t]+4),SEEK_SET); + + /* load sample info */ + _mm_read_string(s.filename,12,modreader); + s.zerobyte = _mm_read_UBYTE(modreader); + s.globvol = _mm_read_UBYTE(modreader); + s.flag = _mm_read_UBYTE(modreader); + s.volume = _mm_read_UBYTE(modreader); + _mm_read_string(s.sampname,26,modreader); + s.convert = _mm_read_UBYTE(modreader); + s.panning = _mm_read_UBYTE(modreader); + s.length = _mm_read_I_ULONG(modreader); + s.loopbeg = _mm_read_I_ULONG(modreader); + s.loopend = _mm_read_I_ULONG(modreader); + s.c5spd = _mm_read_I_ULONG(modreader); + s.susbegin = _mm_read_I_ULONG(modreader); + s.susend = _mm_read_I_ULONG(modreader); + s.sampoffset = _mm_read_I_ULONG(modreader); + s.vibspeed = _mm_read_UBYTE(modreader); + s.vibdepth = _mm_read_UBYTE(modreader); + s.vibrate = _mm_read_UBYTE(modreader); + s.vibwave = _mm_read_UBYTE(modreader); + + /* Generate an error if c5spd is > 512k, or samplelength > 256 megs + (nothing would EVER be that high) */ + + if(_mm_eof(modreader)||(s.c5spd>0x7ffffL)||(s.length>0xfffffffUL)) { + _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + /* Reality check for sample loop information */ + if((s.flag&16)&& + ((s.loopbeg>0xfffffffUL)||(s.loopend>0xfffffffUL))) { + _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + q->samplename = DupStr(s.sampname,26,0); + q->speed = s.c5spd / 2; + q->panning = ((s.panning&127)==64)?255:(s.panning&127)<<2; + q->length = s.length; + q->loopstart = s.loopbeg; + q->loopend = s.loopend; + q->volume = s.volume; + q->globvol = s.globvol; + q->seekpos = s.sampoffset; + + /* Convert speed to XM linear finetune */ + if(of.flags&UF_LINEAR) + q->speed=speed_to_finetune(s.c5spd,t); + + if(s.panning&128) q->flags|=SF_OWNPAN; + + if(s.vibrate) { + q->vibflags |= AV_IT; + q->vibtype = s.vibwave; + q->vibsweep = s.vibrate * 2; + q->vibdepth = s.vibdepth; + q->vibrate = s.vibspeed; + } + + if(s.flag&2) q->flags|=SF_16BITS; + if((s.flag&8)&&(mh->cwt>=0x214)) { + q->flags|=SF_ITPACKED; + compressed=1; + } + if(s.flag&16) q->flags|=SF_LOOP; + if(s.flag&64) q->flags|=SF_BIDI; + + if(mh->cwt>=0x200) { + if(s.convert&1) q->flags|=SF_SIGNED; + if(s.convert&4) q->flags|=SF_DELTA; + } + q++; + } + + /* Load instruments if instrument mode flag enabled */ + if(mh->flags&4) { + if(!AllocInstruments()) return 0; + d=of.instruments; + of.flags|=UF_NNA|UF_INST; + + for(t=0;tinsnum;t++) { + ITINSTHEADER ih; + + /* seek to instrument position */ + _mm_fseek(modreader,paraptr[t]+4,SEEK_SET); + + /* load instrument info */ + _mm_read_string(ih.filename,12,modreader); + ih.zerobyte = _mm_read_UBYTE(modreader); + if(mh->cwt<0x200) { + /* load IT 1.xx inst header */ + ih.volflg = _mm_read_UBYTE(modreader); + ih.volbeg = _mm_read_UBYTE(modreader); + ih.volend = _mm_read_UBYTE(modreader); + ih.volsusbeg = _mm_read_UBYTE(modreader); + ih.volsusend = _mm_read_UBYTE(modreader); + _mm_read_I_UWORD(modreader); + ih.fadeout = _mm_read_I_UWORD(modreader); + ih.nna = _mm_read_UBYTE(modreader); + ih.dnc = _mm_read_UBYTE(modreader); + } else { + /* Read IT200+ header */ + ih.nna = _mm_read_UBYTE(modreader); + ih.dct = _mm_read_UBYTE(modreader); + ih.dca = _mm_read_UBYTE(modreader); + ih.fadeout = _mm_read_I_UWORD(modreader); + ih.ppsep = _mm_read_UBYTE(modreader); + ih.ppcenter = _mm_read_UBYTE(modreader); + ih.globvol = _mm_read_UBYTE(modreader); + ih.chanpan = _mm_read_UBYTE(modreader); + ih.rvolvar = _mm_read_UBYTE(modreader); + ih.rpanvar = _mm_read_UBYTE(modreader); + } + + ih.trkvers = _mm_read_I_UWORD(modreader); + ih.numsmp = _mm_read_UBYTE(modreader); + _mm_read_UBYTE(modreader); + _mm_read_string(ih.name,26,modreader); + _mm_read_UBYTES(ih.blank01,6,modreader); + _mm_read_I_UWORDS(ih.samptable,ITNOTECNT,modreader); + if(mh->cwt<0x200) { + /* load IT 1xx volume envelope */ + _mm_read_UBYTES(ih.volenv,200,modreader); + for(lp=0;lpvolflg|=EF_VOLENV; + d->insname = DupStr(ih.name,26,0); + d->nnatype = ih.nna & NNA_MASK; + + if(mh->cwt<0x200) { + d->volfade=ih.fadeout<< 6; + if(ih.dnc) { + d->dct=DCT_NOTE; + d->dca=DCA_CUT; + } + + if(ih.volflg&1) d->volflg|=EF_ON; + if(ih.volflg&2) d->volflg|=EF_LOOP; + if(ih.volflg&4) d->volflg|=EF_SUSTAIN; + + /* XM conversion of IT envelope Array */ + d->volbeg = ih.volbeg; + d->volend = ih.volend; + d->volsusbeg = ih.volsusbeg; + d->volsusend = ih.volsusend; + + if(ih.volflg&1) { + for(u=0;uvolpts]!=0xff) { + d->volenv[d->volpts].val=(ih.volnode[d->volpts]<<2); + d->volenv[d->volpts].pos=ih.oldvoltick[d->volpts]; + d->volpts++; + } else + break; + } + } else { + d->panning=((ih.chanpan&127)==64)?255:(ih.chanpan&127)<<2; + if(!(ih.chanpan&128)) d->flags|=IF_OWNPAN; + + if(!(ih.ppsep & 128)) { + d->pitpansep=ih.ppsep<<2; + d->pitpancenter=ih.ppcenter; + d->flags|=IF_PITCHPAN; + } + d->globvol=ih.globvol>>1; + d->volfade=ih.fadeout<<5; + d->dct =ih.dct; + d->dca =ih.dca; + + if(mh->cwt>=0x204) { + d->rvolvar = ih.rvolvar; + d->rpanvar = ih.rpanvar; + } + +#if defined __STDC__ || defined _MSC_VER || defined MPW_C +#define IT_ProcessEnvelope(name) \ + if(ih. name##flg&1) d-> name##flg|=EF_ON; \ + if(ih. name##flg&2) d-> name##flg|=EF_LOOP; \ + if(ih. name##flg&4) d-> name##flg|=EF_SUSTAIN; \ + d-> name##pts=ih. name##pts; \ + d-> name##beg=ih. name##beg; \ + d-> name##end=ih. name##end; \ + d-> name##susbeg=ih. name##susbeg; \ + d-> name##susend=ih. name##susend; \ + \ + for(u=0;u name##env[u].pos=ih. name##tick[u]; \ + \ + if((d-> name##flg&EF_ON)&&(d-> name##pts<2)) \ + d-> name##flg&=~EF_ON +#else +#define IT_ProcessEnvelope(name) \ + if(ih. name/**/flg&1) d-> name/**/flg|=EF_ON; \ + if(ih. name/**/flg&2) d-> name/**/flg|=EF_LOOP; \ + if(ih. name/**/flg&4) d-> name/**/flg|=EF_SUSTAIN; \ + d-> name/**/pts=ih. name/**/pts; \ + d-> name/**/beg=ih. name/**/beg; \ + d-> name/**/end=ih. name/**/end; \ + d-> name/**/susbeg=ih. name/**/susbeg; \ + d-> name/**/susend=ih. name/**/susend; \ + \ + for(u=0;u name/**/env[u].pos=ih. name/**/tick[u]; \ + \ + if((d-> name/**/flg&EF_ON)&&(d-> name/**/pts<2)) \ + d-> name/**/flg&=~EF_ON +#endif + + IT_ProcessEnvelope(vol); + for(u=0;uvolenv[u].val=(ih.volnode[u]<<2); + + IT_ProcessEnvelope(pan); + for(u=0;upanenv[u].val= + ih.pannode[u]==32?255:(ih.pannode[u]+32)<<2; + + IT_ProcessEnvelope(pit); + for(u=0;upitenv[u].val=ih.pitnode[u]+32; +#undef IT_ProcessEnvelope + + if(ih.pitflg&0x80) { + /* filter envelopes not supported yet */ + d->pitflg&=~EF_ON; + ih.pitpts=ih.pitbeg=ih.pitend=0; +#ifdef MIKMOD_DEBUG + { + static int warn=0; + + if(!warn) + fprintf(stderr, "\rFilter envelopes not supported yet\n"); + warn=1; + } +#endif + } + } + + for(u=0;usamplenote[u]=(ih.samptable[u]&255); + d->samplenumber[u]= + (ih.samptable[u]>>8)?((ih.samptable[u]>>8)-1):0xffff; + if(d->samplenumber[u]>=of.numsmp) + d->samplenote[u]=255; + else if (of.flags&UF_LINEAR) { + int note=(int)d->samplenote[u]+noteindex[d->samplenumber[u]]; + d->samplenote[u]=(note<0)?0:(note>255?255:note); + } + } + + d++; + } + } else if(of.flags & UF_LINEAR) { + if(!AllocInstruments()) return 0; + d=of.instruments; + of.flags|=UF_INST; + + for(t=0;tsmpnum;t++,d++) + for(u=0;usamplenumber[u]>=of.numsmp) + d->samplenote[u]=255; + else { + int note=(int)d->samplenote[u]+noteindex[d->samplenumber[u]]; + d->samplenote[u]=(note<0)?0:(note>255?255:note); + } + } + } + + /* Figure out how many channels this song actually uses */ + of.numchn=0; + memset(remap,-1,UF_MAXCHAN*sizeof(UBYTE)); + for(t=0;tinsnum+mh->smpnum+t]) { /* 0 -> empty 64 row pattern */ + _mm_fseek(modreader,((long)paraptr[mh->insnum+mh->smpnum+t]),SEEK_SET); + _mm_read_I_UWORD(modreader); + /* read pattern length (# of rows) + Impulse Tracker never creates patterns with less than 32 rows, + but some other trackers do, so we only check for more than 256 + rows */ + packlen=_mm_read_I_UWORD(modreader); + if(packlen>256) { + _mm_errno=MMERR_LOADING_PATTERN; + return 0; + } + _mm_read_I_ULONG(modreader); + if(IT_GetNumChannels(packlen)) return 0; + } + } + + /* give each of them a different number */ + for(t=0;tinsnum+mh->smpnum+t]) { /* 0 -> empty 64 row pattern */ + of.pattrows[t]=64; + for(u=0;uinsnum+mh->smpnum+t]),SEEK_SET); + packlen=_mm_read_I_UWORD(modreader); + of.pattrows[t]=_mm_read_I_UWORD(modreader); + _mm_read_I_ULONG(modreader); + if(!IT_ReadPattern(of.pattrows[t])) return 0; + } + } + + return 1; +} + +CHAR *IT_LoadTitle(void) +{ + CHAR s[26]; + + _mm_fseek(modreader,4,SEEK_SET); + if(!_mm_read_UBYTES(s,26,modreader)) return NULL; + + return(DupStr(s,26,0)); +} + +/*========== Loader information */ + +MIKMODAPI MLOADER load_it={ + NULL, + "IT", + "IT (Impulse Tracker)", + IT_Init, + IT_Test, + IT_Load, + IT_Cleanup, + IT_LoadTitle +}; + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/loaders/load_m15.c b/apps/plugins/mikmod/loaders/load_m15.c new file mode 100644 index 0000000..6adae0e --- /dev/null +++ b/apps/plugins/mikmod/loaders/load_m15.c @@ -0,0 +1,505 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file + AUTHORS for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: load_m15.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $ + + 15 instrument MOD loader + Also supports Ultimate Sound Tracker (old M15 format) + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +//#include +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Module Structure */ + +typedef struct MSAMPINFO { + CHAR samplename[23]; /* 22 in module, 23 in memory */ + UWORD length; + UBYTE finetune; + UBYTE volume; + UWORD reppos; + UWORD replen; +} MSAMPINFO; + +typedef struct MODULEHEADER { + CHAR songname[21]; /* the songname.., 20 in module, 21 in memory */ + MSAMPINFO samples[15]; /* all sampleinfo */ + UBYTE songlength; /* number of patterns used */ + UBYTE magic1; /* should be 127 */ + UBYTE positions[128]; /* which pattern to play at pos */ +} MODULEHEADER; + +typedef struct MODNOTE { + UBYTE a,b,c,d; +} MODNOTE; + +/*========== Loader variables */ + +static MODULEHEADER *mh = NULL; +static MODNOTE *patbuf = NULL; +static BOOL ust_loader = 0; /* if TRUE, load as an ust module. */ + +/* known file formats which can confuse the loader */ +#define REJECT 2 +static char *signatures[REJECT]={ + "CAKEWALK", /* cakewalk midi files */ + "SZDD" /* Microsoft compressed files */ +}; +static int siglen[REJECT]={8,4}; + +/*========== Loader code */ + +static BOOL LoadModuleHeader(MODULEHEADER *mh) +{ + int t,u; + + _mm_read_string(mh->songname,20,modreader); + mh->songname[20]=0; /* just in case */ + + /* sanity check : title should contain printable characters and a bunch + of null chars */ + for(t=0;t<20;t++) + if((mh->songname[t])&&(mh->songname[t]<32)) return 0; + for(t=0;(mh->songname[t])&&(t<20);t++); + if(t<20) for(;t<20;t++) if(mh->songname[t]) return 0; + + for(t=0;t<15;t++) { + MSAMPINFO *s=&mh->samples[t]; + + _mm_read_string(s->samplename,22,modreader); + s->samplename[22]=0; /* just in case */ + s->length =_mm_read_M_UWORD(modreader); + s->finetune =_mm_read_UBYTE(modreader); + s->volume =_mm_read_UBYTE(modreader); + s->reppos =_mm_read_M_UWORD(modreader); + s->replen =_mm_read_M_UWORD(modreader); + + /* sanity check : sample title should contain printable characters and + a bunch of null chars */ + for(u=0;u<20;u++) + if((s->samplename[u])&&(s->samplename[u]samplename[u])&&(u<20);u++); + if(u<20) for(;u<20;u++) if(s->samplename[u]) return 0; + + /* sanity check : finetune values */ + if(s->finetune>>4) return 0; + } + + mh->songlength =_mm_read_UBYTE(modreader); + mh->magic1 =_mm_read_UBYTE(modreader); /* should be 127 */ + + /* sanity check : no more than 128 positions, restart position in range */ + if((!mh->songlength)||(mh->songlength>128)) return 0; + /* values encountered so far are 0x6a and 0x78 */ + if(((mh->magic1&0xf8)!=0x78)&&(mh->magic1!=0x6a)&&(mh->magic1>mh->songlength)) return 0; + + _mm_read_UBYTES(mh->positions,128,modreader); + + /* sanity check : pattern range is 0..63 */ + for(t=0;t<128;t++) + if(mh->positions[t]>63) return 0; + + return(!_mm_eof(modreader)); +} + +/* Checks the patterns in the modfile for UST / 15-inst indications. + For example, if an effect 3xx is found, it is assumed that the song + is 15-inst. If a 1xx effect has dat greater than 0x20, it is UST. + + Returns: 0 indecisive; 1 = UST; 2 = 15-inst */ +static int CheckPatternType(int numpat) +{ + int t; + UBYTE eff, dat; + + for(t=0;t0x1f) return 1; + if(dat<0x3) return 2; + break; + case 2: + if(dat>0x1f) return 1; + return 2; + case 3: + if (dat) return 2; + break; + default: + return 2; + } + } + return 0; +} + +static BOOL M15_Test(void) +{ + int t, numpat; + MODULEHEADER mh; + + ust_loader = 0; + if(!LoadModuleHeader(&mh)) return 0; + + /* reject other file types */ + for(t=0;t127) return 0; + if((!mh.songlength)||(mh.songlength>mh.magic1)) return 0; + + for(t=0;t<15;t++) { + /* all finetunes should be zero */ + if(mh.samples[t].finetune) return 0; + + /* all volumes should be <= 64 */ + if(mh.samples[t].volume>64) return 0; + + /* all instrument names should begin with s, st-, or a number */ + if((mh.samples[t].samplename[0]=='s')|| + (mh.samples[t].samplename[0]=='S')) { + if((memcmp(mh.samples[t].samplename,"st-",3)) && + (memcmp(mh.samples[t].samplename,"ST-",3)) && + (*mh.samples[t].samplename)) + ust_loader = 1; + } else + if(!isdigit((int)mh.samples[t].samplename[0])) + ust_loader = 1; + + if(mh.samples[t].length>4999||mh.samples[t].reppos>9999) { + ust_loader = 0; + if(mh.samples[t].length>32768) return 0; + } + + /* if loop information is incorrect as words, but correct as bytes, + this is likely to be an ust-style module */ + if((mh.samples[t].reppos+mh.samples[t].replen>mh.samples[t].length)&& + (mh.samples[t].reppos+mh.samples[t].replen<(mh.samples[t].length<<1))){ + ust_loader = 1; + return 1; + } + + if(!ust_loader) return 1; + } + + for(numpat=0,t=0;tnumpat) + numpat = mh.positions[t]; + numpat++; + switch(CheckPatternType(numpat)) { + case 0: /* indecisive, so check more clues... */ + break; + case 1: + ust_loader = 1; + break; + case 2: + ust_loader = 0; + break; + } + return 1; +} + +static BOOL M15_Init(void) +{ + if(!(mh=(MODULEHEADER*)_mm_malloc(sizeof(MODULEHEADER)))) return 0; + return 1; +} + +static void M15_Cleanup(void) +{ + _mm_free(mh); + _mm_free(patbuf); +} + +/* +Old (amiga) noteinfo: + + _____byte 1_____ byte2_ _____byte 3_____ byte4_ +/ \ / \ / \ / \ +0000 0000-00000000 0000 0000-00000000 + +Upper four 12 bits for Lower four Effect command. +bits of sam- note period. bits of sam- +ple number. ple number. +*/ + +static UBYTE M15_ConvertNote(MODNOTE* n, UBYTE lasteffect) +{ + UBYTE instrument,effect,effdat,note; + UWORD period; + UBYTE lastnote=0; + + /* decode the 4 bytes that make up a single note */ + instrument = n->c>>4; + period = (((UWORD)n->a&0xf)<<8)+n->b; + effect = n->c&0xf; + effdat = n->d; + + /* Convert the period to a note number */ + note=0; + if(period) { + for(note=0;note<7*OCTAVE;note++) + if(period>=npertab[note]) break; + if(note==7*OCTAVE) note=0; + else note++; + } + + if(instrument) { + /* if instrument does not exist, note cut */ + if((instrument>15)||(!mh->samples[instrument-1].length)) { + UniPTEffect(0xc,0); + if(effect==0xc) effect=effdat=0; + } else { + /* if we had a note, then change instrument... */ + if(note) + UniInstrument(instrument-1); + /* ...otherwise, only adjust volume... */ + else { + /* ...unless an effect was specified, which forces a new note + to be played */ + if(effect||effdat) { + UniInstrument(instrument-1); + note=lastnote; + } else + UniPTEffect(0xc,mh->samples[instrument-1].volume&0x7f); + } + } + } + if(note) { + UniNote(note+2*OCTAVE-1); + lastnote=note; + } + + /* Convert pattern jump from Dec to Hex */ + if(effect == 0xd) + effdat=(((effdat&0xf0)>>4)*10)+(effdat&0xf); + + /* Volume slide, up has priority */ + if((effect==0xa)&&(effdat&0xf)&&(effdat&0xf0)) + effdat&=0xf0; + + /* Handle ``heavy'' volumes correctly */ + if ((effect == 0xc) && (effdat > 0x40)) + effdat = 0x40; + + if(ust_loader) { + switch(effect) { + case 0: + case 3: + break; + case 1: + UniPTEffect(0,effdat); + break; + case 2: + if(effdat&0xf) UniPTEffect(1,effdat&0xf); + else if(effdat>>2) UniPTEffect(2,effdat>>2); + break; + default: + UniPTEffect(effect,effdat); + break; + } + } else { + /* An isolated 100, 200 or 300 effect should be ignored (no + "standalone" porta memory in mod files). However, a sequence + such as 1XX, 100, 100, 100 is fine. */ + if ((!effdat) && ((effect == 1)||(effect == 2)||(effect ==3)) && + (lasteffect < 0x10) && (effect != lasteffect)) + effect = 0; + + UniPTEffect(effect,effdat); + } + if (effect == 8) + of.flags |= UF_PANNING; + + return effect; +} + +static UBYTE *M15_ConvertTrack(MODNOTE* n) +{ + int t; + UBYTE lasteffect = 0x10; /* non existant effect */ + + UniReset(); + for(t=0;t<64;t++) { + lasteffect = M15_ConvertNote(n,lasteffect); + UniNewline(); + n+=4; + } + return UniDup(); +} + +/* Loads all patterns of a modfile and converts them into the 3 byte format. */ +static BOOL M15_LoadPatterns(void) +{ + int t,s,tracks=0; + + if(!AllocPatterns()) return 0; + if(!AllocTracks()) return 0; + + /* Allocate temporary buffer for loading and converting the patterns */ + if(!(patbuf=(MODNOTE*)_mm_calloc(64U*4,sizeof(MODNOTE)))) return 0; + + for(t=0;tsongname,21,1); + of.numpos = mh->songlength; + of.reppos = 0; + + /* Count the number of patterns */ + of.numpat = 0; + for(t=0;tpositions[t]>of.numpat) + of.numpat=mh->positions[t]; + /* since some old modules embed extra patterns, we have to check the + whole list to get the samples' file offsets right - however we can find + garbage here, so check carefully */ + scan=1; + for(t=of.numpos;t<128;t++) + if(mh->positions[t]>=0x80) scan=0; + if (scan) + for(t=of.numpos;t<128;t++) { + if(mh->positions[t]>of.numpat) + of.numpat=mh->positions[t]; + if((curious)&&(mh->positions[t])) of.numpos=t+1; + } + of.numpat++; + of.numtrk = of.numpat*of.numchn; + + if(!AllocPositions(of.numpos)) return 0; + for(t=0;tpositions[t]; + + /* Finally, init the sampleinfo structures */ + of.numins=of.numsmp=15; + if(!AllocSamples()) return 0; + + s = mh->samples; + q = of.samples; + + for(t=0;tsamplename = DupStr(s->samplename,23,1); + + /* init the sampleinfo variables and convert the size pointers */ + q->speed = finetune[s->finetune&0xf]; + q->volume = s->volume; + if(ust_loader) + q->loopstart = s->reppos; + else + q->loopstart = s->reppos<<1; + q->loopend = q->loopstart+(s->replen<<1); + q->length = s->length<<1; + + q->flags = SF_SIGNED; + if(ust_loader) q->flags |= SF_UST_LOOP; + if(s->replen>2) q->flags |= SF_LOOP; + + s++; + q++; + } + + if(!M15_LoadPatterns()) return 0; + ust_loader = 0; + + return 1; +} + +static CHAR *M15_LoadTitle(void) +{ + CHAR s[21]; + + _mm_fseek(modreader,0,SEEK_SET); + if(!_mm_read_UBYTES(s,20,modreader)) return NULL; + s[20]=0; /* just in case */ + return(DupStr(s,21,1)); +} + +/*========== Loader information */ + +MIKMODAPI MLOADER load_m15={ + NULL, + "15-instrument module", + "MOD (15 instrument)", + M15_Init, + M15_Test, + M15_Load, + M15_Cleanup, + M15_LoadTitle +}; + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/loaders/load_med.c b/apps/plugins/mikmod/loaders/load_med.c new file mode 100644 index 0000000..3078dd2 --- /dev/null +++ b/apps/plugins/mikmod/loaders/load_med.c @@ -0,0 +1,719 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file + AUTHORS for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: load_med.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $ + + Amiga MED module loader + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Module information */ + +typedef struct MEDHEADER { + ULONG id; + ULONG modlen; + ULONG MEDSONGP; /* struct MEDSONG *song; */ + UWORD psecnum; /* for the player routine, MMD2 only */ + UWORD pseq; /* " " " " */ + ULONG MEDBlockPP; /* struct MEDBlock **blockarr; */ + ULONG reserved1; + ULONG MEDINSTHEADERPP; /* struct MEDINSTHEADER **smplarr; */ + ULONG reserved2; + ULONG MEDEXPP; /* struct MEDEXP *expdata; */ + ULONG reserved3; + UWORD pstate; /* some data for the player routine */ + UWORD pblock; + UWORD pline; + UWORD pseqnum; + SWORD actplayline; + UBYTE counter; + UBYTE extra_songs; /* number of songs - 1 */ +} MEDHEADER; + +typedef struct MEDSAMPLE { + UWORD rep, replen; /* offs: 0(s), 2(s) */ + UBYTE midich; /* offs: 4(s) */ + UBYTE midipreset; /* offs: 5(s) */ + UBYTE svol; /* offs: 6(s) */ + SBYTE strans; /* offs: 7(s) */ +} MEDSAMPLE; + +typedef struct MEDSONG { + MEDSAMPLE sample[63]; /* 63 * 8 bytes = 504 bytes */ + UWORD numblocks; /* offs: 504 */ + UWORD songlen; /* offs: 506 */ + UBYTE playseq[256]; /* offs: 508 */ + UWORD deftempo; /* offs: 764 */ + SBYTE playtransp; /* offs: 766 */ + UBYTE flags; /* offs: 767 */ + UBYTE flags2; /* offs: 768 */ + UBYTE tempo2; /* offs: 769 */ + UBYTE trkvol[16]; /* offs: 770 */ + UBYTE mastervol; /* offs: 786 */ + UBYTE numsamples; /* offs: 787 */ +} MEDSONG; + +typedef struct MEDEXP { + ULONG nextmod; /* pointer to next module */ + ULONG exp_smp; /* pointer to MEDINSTEXT array */ + UWORD s_ext_entries; + UWORD s_ext_entrsz; + ULONG annotxt; /* pointer to annotation text */ + ULONG annolen; + ULONG iinfo; /* pointer to MEDINSTINFO array */ + UWORD i_ext_entries; + UWORD i_ext_entrsz; + ULONG jumpmask; + ULONG rgbtable; + ULONG channelsplit; + ULONG n_info; + ULONG songname; /* pointer to songname */ + ULONG songnamelen; + ULONG dumps; + ULONG reserved2[7]; +} MEDEXP; + +typedef struct MMD0NOTE { + UBYTE a, b, c; +} MMD0NOTE; + +typedef struct MMD1NOTE { + UBYTE a, b, c, d; +} MMD1NOTE; + +typedef struct MEDINSTHEADER { + ULONG length; + SWORD type; + /* Followed by actual data */ +} MEDINSTHEADER; + +typedef struct MEDINSTEXT { + UBYTE hold; + UBYTE decay; + UBYTE suppress_midi_off; + SBYTE finetune; +} MEDINSTEXT; + +typedef struct MEDINSTINFO { + UBYTE name[40]; +} MEDINSTINFO; + +/*========== Loader variables */ + +#define MMD0_string 0x4D4D4430 +#define MMD1_string 0x4D4D4431 + +static MEDHEADER *mh = NULL; +static MEDSONG *ms = NULL; +static MEDEXP *me = NULL; +static ULONG *ba = NULL; +static MMD0NOTE *mmd0pat = NULL; +static MMD1NOTE *mmd1pat = NULL; + +static BOOL decimalvolumes; +static BOOL bpmtempos; + +#define d0note(row,col) mmd0pat[((row)*(UWORD)of.numchn)+(col)] +#define d1note(row,col) mmd1pat[((row)*(UWORD)of.numchn)+(col)] + +static CHAR MED_Version[] = "OctaMED (MMDx)"; + +/*========== Loader code */ + +BOOL MED_Test(void) +{ + UBYTE id[4]; + + if (!_mm_read_UBYTES(id, 4, modreader)) + return 0; + if ((!memcmp(id, "MMD0", 4)) || (!memcmp(id, "MMD1", 4))) + return 1; + return 0; +} + +BOOL MED_Init(void) +{ + if (!(me = (MEDEXP *)_mm_malloc(sizeof(MEDEXP)))) + return 0; + if (!(mh = (MEDHEADER *)_mm_malloc(sizeof(MEDHEADER)))) + return 0; + if (!(ms = (MEDSONG *)_mm_malloc(sizeof(MEDSONG)))) + return 0; + return 1; +} + +void MED_Cleanup(void) +{ + _mm_free(me); + _mm_free(mh); + _mm_free(ms); + _mm_free(ba); + _mm_free(mmd0pat); + _mm_free(mmd1pat); +} + +static void EffectCvt(UBYTE eff, UBYTE dat) +{ + switch (eff) { + /* 0x0 0x1 0x2 0x3 0x4 PT effects */ + case 0x5: /* PT vibrato with speed/depth nibbles swapped */ + UniPTEffect(0x4, (dat >> 4) | ((dat & 0xf) << 4)); + break; + /* 0x6 0x7 not used */ + case 0x6: + case 0x7: + break; + case 0x8: /* midi hold/decay */ + break; + case 0x9: + if (bpmtempos) { + if (!dat) + dat = of.initspeed; + UniEffect(UNI_S3MEFFECTA, dat); + } else { + if (dat <= 0x20) { + if (!dat) + dat = of.initspeed; + else + dat /= 4; + UniPTEffect(0xf, dat); + } else + UniEffect(UNI_MEDSPEED, ((UWORD)dat * 125) / (33 * 4)); + } + break; + /* 0xa 0xb PT effects */ + case 0xc: + if (decimalvolumes) + dat = (dat >> 4) * 10 + (dat & 0xf); + UniPTEffect(0xc, dat); + break; + case 0xd: /* same as PT volslide */ + UniPTEffect(0xa, dat); + break; + case 0xe: /* synth jmp - midi */ + break; + case 0xf: + switch (dat) { + case 0: /* patternbreak */ + UniPTEffect(0xd, 0); + break; + case 0xf1: /* play note twice */ + UniWriteByte(UNI_MEDEFFECTF1); + break; + case 0xf2: /* delay note */ + UniWriteByte(UNI_MEDEFFECTF2); + break; + case 0xf3: /* play note three times */ + UniWriteByte(UNI_MEDEFFECTF3); + break; + case 0xfe: /* stop playing */ + UniPTEffect(0xb, of.numpat); + break; + case 0xff: /* note cut */ + UniPTEffect(0xc, 0); + break; + default: + if (dat <= 10) + UniPTEffect(0xf, dat); + else if (dat <= 240) { + if (bpmtempos) + UniPTEffect(0xf, (dat < 32) ? 32 : dat); + else + UniEffect(UNI_MEDSPEED, ((UWORD)dat * 125) / 33); + } + } + break; + default: /* all normal PT effects are handled here */ + UniPTEffect(eff, dat); + break; + } +} + +static UBYTE *MED_Convert1(int count, int col) +{ + int t; + UBYTE inst, note, eff, dat; + MMD1NOTE *n; + + UniReset(); + for (t = 0; t < count; t++) { + n = &d1note(t, col); + + note = n->a & 0x7f; + inst = n->b & 0x3f; + eff = n->c & 0xf; + dat = n->d; + + if (inst) + UniInstrument(inst - 1); + if (note) + UniNote(note + 3 * OCTAVE - 1); + EffectCvt(eff, dat); + UniNewline(); + } + return UniDup(); +} + +static UBYTE *MED_Convert0(int count, int col) +{ + int t; + UBYTE a, b, inst, note, eff, dat; + MMD0NOTE *n; + + UniReset(); + for (t = 0; t < count; t++) { + n = &d0note(t, col); + a = n->a; + b = n->b; + + note = a & 0x3f; + a >>= 6; + a = ((a & 1) << 1) | (a >> 1); + inst = (b >> 4) | (a << 4); + eff = b & 0xf; + dat = n->c; + + if (inst) + UniInstrument(inst - 1); + if (note) + UniNote(note + 3 * OCTAVE - 1); + EffectCvt(eff, dat); + UniNewline(); + } + return UniDup(); +} + +static BOOL LoadMEDPatterns(void) +{ + int t, row, col; + UWORD numtracks, numlines, maxlines = 0, track = 0; + MMD0NOTE *mmdp; + + /* first, scan patterns to see how many channels are used */ + for (t = 0; t < of.numpat; t++) { + _mm_fseek(modreader, ba[t], SEEK_SET); + numtracks = _mm_read_UBYTE(modreader); + numlines = _mm_read_UBYTE(modreader); + + if (numtracks > of.numchn) + of.numchn = numtracks; + if (numlines > maxlines) + maxlines = numlines; + } + + of.numtrk = of.numpat * of.numchn; + if (!AllocTracks()) + return 0; + if (!AllocPatterns()) + return 0; + + if (! + (mmd0pat = + (MMD0NOTE *)_mm_calloc(of.numchn * (maxlines + 1), + sizeof(MMD0NOTE)))) return 0; + + /* second read: read and convert patterns */ + for (t = 0; t < of.numpat; t++) { + _mm_fseek(modreader, ba[t], SEEK_SET); + numtracks = _mm_read_UBYTE(modreader); + numlines = _mm_read_UBYTE(modreader); + + of.pattrows[t] = ++numlines; + memset(mmdp = mmd0pat, 0, of.numchn * maxlines * sizeof(MMD0NOTE)); + for (row = numlines; row; row--) { + for (col = numtracks; col; col--, mmdp++) { + mmdp->a = _mm_read_UBYTE(modreader); + mmdp->b = _mm_read_UBYTE(modreader); + mmdp->c = _mm_read_UBYTE(modreader); + } + } + + for (col = 0; col < of.numchn; col++) + of.tracks[track++] = MED_Convert0(numlines, col); + } + return 1; +} + +static BOOL LoadMMD1Patterns(void) +{ + int t, row, col; + UWORD numtracks, numlines, maxlines = 0, track = 0; + MMD1NOTE *mmdp; + + /* first, scan patterns to see how many channels are used */ + for (t = 0; t < of.numpat; t++) { + _mm_fseek(modreader, ba[t], SEEK_SET); + numtracks = _mm_read_M_UWORD(modreader); + numlines = _mm_read_M_UWORD(modreader); + if (numtracks > of.numchn) + of.numchn = numtracks; + if (numlines > maxlines) + maxlines = numlines; + } + + of.numtrk = of.numpat * of.numchn; + if (!AllocTracks()) + return 0; + if (!AllocPatterns()) + return 0; + + if (! + (mmd1pat = + (MMD1NOTE *)_mm_calloc(of.numchn * (maxlines + 1), + sizeof(MMD1NOTE)))) return 0; + + /* second read: really read and convert patterns */ + for (t = 0; t < of.numpat; t++) { + _mm_fseek(modreader, ba[t], SEEK_SET); + numtracks = _mm_read_M_UWORD(modreader); + numlines = _mm_read_M_UWORD(modreader); + + _mm_fseek(modreader, sizeof(ULONG), SEEK_CUR); + of.pattrows[t] = ++numlines; + memset(mmdp = mmd1pat, 0, of.numchn * maxlines * sizeof(MMD1NOTE)); + + for (row = numlines; row; row--) { + for (col = numtracks; col; col--, mmdp++) { + mmdp->a = _mm_read_UBYTE(modreader); + mmdp->b = _mm_read_UBYTE(modreader); + mmdp->c = _mm_read_UBYTE(modreader); + mmdp->d = _mm_read_UBYTE(modreader); + } + } + + for (col = 0; col < of.numchn; col++) + of.tracks[track++] = MED_Convert1(numlines, col); + } + return 1; +} + +BOOL MED_Load(BOOL curious) +{ + int t; + ULONG sa[64]; + MEDINSTHEADER s; + SAMPLE *q; + MEDSAMPLE *mss; + + /* try to read module header */ + mh->id = _mm_read_M_ULONG(modreader); + mh->modlen = _mm_read_M_ULONG(modreader); + mh->MEDSONGP = _mm_read_M_ULONG(modreader); + mh->psecnum = _mm_read_M_UWORD(modreader); + mh->pseq = _mm_read_M_UWORD(modreader); + mh->MEDBlockPP = _mm_read_M_ULONG(modreader); + mh->reserved1 = _mm_read_M_ULONG(modreader); + mh->MEDINSTHEADERPP = _mm_read_M_ULONG(modreader); + mh->reserved2 = _mm_read_M_ULONG(modreader); + mh->MEDEXPP = _mm_read_M_ULONG(modreader); + mh->reserved3 = _mm_read_M_ULONG(modreader); + mh->pstate = _mm_read_M_UWORD(modreader); + mh->pblock = _mm_read_M_UWORD(modreader); + mh->pline = _mm_read_M_UWORD(modreader); + mh->pseqnum = _mm_read_M_UWORD(modreader); + mh->actplayline = _mm_read_M_SWORD(modreader); + mh->counter = _mm_read_UBYTE(modreader); + mh->extra_songs = _mm_read_UBYTE(modreader); + + /* Seek to MEDSONG struct */ + _mm_fseek(modreader, mh->MEDSONGP, SEEK_SET); + + /* Load the MED Song Header */ + mss = ms->sample; /* load the sample data first */ + for (t = 63; t; t--, mss++) { + mss->rep = _mm_read_M_UWORD(modreader); + mss->replen = _mm_read_M_UWORD(modreader); + mss->midich = _mm_read_UBYTE(modreader); + mss->midipreset = _mm_read_UBYTE(modreader); + mss->svol = _mm_read_UBYTE(modreader); + mss->strans = _mm_read_SBYTE(modreader); + } + + ms->numblocks = _mm_read_M_UWORD(modreader); + ms->songlen = _mm_read_M_UWORD(modreader); + _mm_read_UBYTES(ms->playseq, 256, modreader); + ms->deftempo = _mm_read_M_UWORD(modreader); + ms->playtransp = _mm_read_SBYTE(modreader); + ms->flags = _mm_read_UBYTE(modreader); + ms->flags2 = _mm_read_UBYTE(modreader); + ms->tempo2 = _mm_read_UBYTE(modreader); + _mm_read_UBYTES(ms->trkvol, 16, modreader); + ms->mastervol = _mm_read_UBYTE(modreader); + ms->numsamples = _mm_read_UBYTE(modreader); + + /* check for a bad header */ + if (_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* load extension structure */ + if (mh->MEDEXPP) { + _mm_fseek(modreader, mh->MEDEXPP, SEEK_SET); + me->nextmod = _mm_read_M_ULONG(modreader); + me->exp_smp = _mm_read_M_ULONG(modreader); + me->s_ext_entries = _mm_read_M_UWORD(modreader); + me->s_ext_entrsz = _mm_read_M_UWORD(modreader); + me->annotxt = _mm_read_M_ULONG(modreader); + me->annolen = _mm_read_M_ULONG(modreader); + me->iinfo = _mm_read_M_ULONG(modreader); + me->i_ext_entries = _mm_read_M_UWORD(modreader); + me->i_ext_entrsz = _mm_read_M_UWORD(modreader); + me->jumpmask = _mm_read_M_ULONG(modreader); + me->rgbtable = _mm_read_M_ULONG(modreader); + me->channelsplit = _mm_read_M_ULONG(modreader); + me->n_info = _mm_read_M_ULONG(modreader); + me->songname = _mm_read_M_ULONG(modreader); + me->songnamelen = _mm_read_M_ULONG(modreader); + me->dumps = _mm_read_M_ULONG(modreader); + } + + /* seek to and read the samplepointer array */ + _mm_fseek(modreader, mh->MEDINSTHEADERPP, SEEK_SET); + if (!_mm_read_M_ULONGS(sa, ms->numsamples, modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* alloc and read the blockpointer array */ + if (!(ba = (ULONG *)_mm_calloc(ms->numblocks, sizeof(ULONG)))) + return 0; + _mm_fseek(modreader, mh->MEDBlockPP, SEEK_SET); + if (!_mm_read_M_ULONGS(ba, ms->numblocks, modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* copy song positions */ + if (!AllocPositions(ms->songlen)) + return 0; + for (t = 0; t < ms->songlen; t++) + of.positions[t] = ms->playseq[t]; + + decimalvolumes = (ms->flags & 0x10) ? 0 : 1; + bpmtempos = (ms->flags2 & 0x20) ? 1 : 0; + + if (bpmtempos) { + int bpmlen = (ms->flags2 & 0x1f) + 1; + of.initspeed = ms->tempo2; + of.inittempo = ms->deftempo * bpmlen / 4; + + if (bpmlen != 4) { + /* Let's do some math : compute GCD of BPM beat length and speed */ + int a, b; + + a = bpmlen; + b = ms->tempo2; + + if (a > b) { + t = b; + b = a; + a = t; + } + while ((a != b) && (a)) { + t = a; + a = b - a; + b = t; + if (a > b) { + t = b; + b = a; + a = t; + } + } + + of.initspeed /= b; + of.inittempo = ms->deftempo * bpmlen / (4 * b); + } + } else { + of.initspeed = ms->tempo2; + of.inittempo = ms->deftempo ? ((UWORD)ms->deftempo * 125) / 33 : 128; + if ((ms->deftempo <= 10) && (ms->deftempo)) + of.inittempo = (of.inittempo * 33) / 6; + of.flags |= UF_HIGHBPM; + } + MED_Version[12] = mh->id; + of.modtype = strdup(MED_Version); + of.numchn = 0; /* will be counted later */ + of.numpat = ms->numblocks; + of.numpos = ms->songlen; + of.numins = ms->numsamples; + of.numsmp = of.numins; + of.reppos = 0; + if ((mh->MEDEXPP) && (me->songname) && (me->songnamelen)) { + char *name; + + _mm_fseek(modreader, me->songname, SEEK_SET); + name = _mm_malloc(me->songnamelen); + _mm_read_UBYTES(name, me->songnamelen, modreader); + of.songname = DupStr(name, me->songnamelen, 1); + free(name); + } else + of.songname = DupStr(NULL, 0, 0); + if ((mh->MEDEXPP) && (me->annotxt) && (me->annolen)) { + _mm_fseek(modreader, me->annotxt, SEEK_SET); + ReadComment(me->annolen); + } + + if (!AllocSamples()) + return 0; + q = of.samples; + for (t = 0; t < of.numins; t++) { + q->flags = SF_SIGNED; + q->volume = 64; + if (sa[t]) { + _mm_fseek(modreader, sa[t], SEEK_SET); + s.length = _mm_read_M_ULONG(modreader); + s.type = _mm_read_M_SWORD(modreader); + + if (s.type) { +#ifdef MIKMOD_DEBUG + fprintf(stderr, "\rNon-sample instruments not supported in MED loader yet\n"); +#endif + if (!curious) { + _mm_errno = MMERR_MED_SYNTHSAMPLES; + return 0; + } + s.length = 0; + } + + if (_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + q->length = s.length; + q->seekpos = _mm_ftell(modreader); + q->loopstart = ms->sample[t].rep << 1; + q->loopend = q->loopstart + (ms->sample[t].replen << 1); + + if (ms->sample[t].replen > 1) + q->flags |= SF_LOOP; + + /* don't load sample if length>='MMD0'... + such kluges make libmikmod's code unique !!! */ + if (q->length >= MMD0_string) + q->length = 0; + } else + q->length = 0; + + if ((mh->MEDEXPP) && (me->exp_smp) && + (t < me->s_ext_entries) && (me->s_ext_entrsz >= 4)) { + MEDINSTEXT ie; + + _mm_fseek(modreader, me->exp_smp + t * me->s_ext_entrsz, + SEEK_SET); + ie.hold = _mm_read_UBYTE(modreader); + ie.decay = _mm_read_UBYTE(modreader); + ie.suppress_midi_off = _mm_read_UBYTE(modreader); + ie.finetune = _mm_read_SBYTE(modreader); + + q->speed = finetune[ie.finetune & 0xf]; + } else + q->speed = 8363; + + if ((mh->MEDEXPP) && (me->iinfo) && + (t < me->i_ext_entries) && (me->i_ext_entrsz >= 40)) { + MEDINSTINFO ii; + + _mm_fseek(modreader, me->iinfo + t * me->i_ext_entrsz, SEEK_SET); + _mm_read_UBYTES(ii.name, 40, modreader); + q->samplename = DupStr((char*)ii.name, 40, 1); + } else + q->samplename = NULL; + + q++; + } + + if (mh->id == MMD0_string) { + if (!LoadMEDPatterns()) { + _mm_errno = MMERR_LOADING_PATTERN; + return 0; + } + } else if (mh->id == MMD1_string) { + if (!LoadMMD1Patterns()) { + _mm_errno = MMERR_LOADING_PATTERN; + return 0; + } + } else { + _mm_errno = MMERR_NOT_A_MODULE; + return 0; + } + return 1; +} + +CHAR *MED_LoadTitle(void) +{ + ULONG posit, namelen; + CHAR *name, *retvalue = NULL; + + _mm_fseek(modreader, 0x20, SEEK_SET); + posit = _mm_read_M_ULONG(modreader); + + if (posit) { + _mm_fseek(modreader, posit + 0x2C, SEEK_SET); + posit = _mm_read_M_ULONG(modreader); + namelen = _mm_read_M_ULONG(modreader); + + _mm_fseek(modreader, posit, SEEK_SET); + name = _mm_malloc(namelen); + _mm_read_UBYTES(name, namelen, modreader); + retvalue = DupStr(name, namelen, 1); + free(name); + } + + return retvalue; +} + +/*========== Loader information */ + +MIKMODAPI MLOADER load_med = { + NULL, + "MED", + "MED (OctaMED)", + MED_Init, + MED_Test, + MED_Load, + MED_Cleanup, + MED_LoadTitle +}; + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/loaders/load_mod.c b/apps/plugins/mikmod/loaders/load_mod.c new file mode 100644 index 0000000..5166234 --- /dev/null +++ b/apps/plugins/mikmod/loaders/load_mod.c @@ -0,0 +1,512 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file + AUTHORS for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: load_mod.c,v 1.2 2004/01/21 13:33:11 raph Exp $ + + Generic MOD loader (Protracker, StarTracker, FastTracker, etc) + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +//#include +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Module structure */ + +typedef struct MSAMPINFO { + CHAR samplename[23]; /* 22 in module, 23 in memory */ + UWORD length; + UBYTE finetune; + UBYTE volume; + UWORD reppos; + UWORD replen; +} MSAMPINFO; + +typedef struct MODULEHEADER { + CHAR songname[21]; /* the songname.. 20 in module, 21 in memory */ + MSAMPINFO samples[31]; /* all sampleinfo */ + UBYTE songlength; /* number of patterns used */ + UBYTE magic1; /* should be 127 */ + UBYTE positions[128]; /* which pattern to play at pos */ + UBYTE magic2[4]; /* string "M.K." or "FLT4" or "FLT8" */ +} MODULEHEADER; + +typedef struct MODTYPE { + CHAR id[5]; + UBYTE channels; + CHAR *name; +} MODTYPE; + +typedef struct MODNOTE { + UBYTE a, b, c, d; +} MODNOTE; + +/*========== Loader variables */ + +#define MODULEHEADERSIZE 0x438 + +static CHAR protracker[] = "Protracker"; +static CHAR startrekker[] = "Startrekker"; +static CHAR fasttracker[] = "Fasttracker"; +static CHAR oktalyser[] = "Oktalyser"; +static CHAR oktalyzer[] = "Oktalyzer"; +static CHAR taketracker[] = "TakeTracker"; +static CHAR orpheus[] = "Imago Orpheus (MOD format)"; + +static MODULEHEADER *mh = NULL; +static MODNOTE *patbuf = NULL; +static int modtype, trekker; + +/*========== Loader code */ + +/* given the module ID, determine the number of channels and the tracker + description ; also alters modtype */ +static BOOL MOD_CheckType(UBYTE *id, UBYTE *numchn, CHAR **descr) +{ + modtype = trekker = 0; + + /* Protracker and variants */ + if ((!memcmp(id, "M.K.", 4)) || (!memcmp(id, "M!K!", 4))) { + *descr = protracker; + modtype = 0; + *numchn = 4; + return 1; + } + + /* Star Tracker */ + if (((!memcmp(id, "FLT", 3)) || (!memcmp(id, "EXO", 3))) && + (isdigit(id[3]))) { + *descr = startrekker; + modtype = trekker = 1; + *numchn = id[3] - '0'; + if (*numchn == 4 || *numchn == 8) + return 1; +#ifdef MIKMOD_DEBUG + else + fprintf(stderr, "\rUnknown FLT%d module type\n", *numchn); +#endif + return 0; + } + + /* Oktalyzer (Amiga) */ + if (!memcmp(id, "OKTA", 4)) { + *descr = oktalyzer; + modtype = 1; + *numchn = 8; + return 1; + } + + /* Oktalyser (Atari) */ + if (!memcmp(id, "CD81", 4)) { + *descr = oktalyser; + modtype = 1; + *numchn = 8; + return 1; + } + + /* Fasttracker */ + if ((!memcmp(id + 1, "CHN", 3)) && (isdigit(id[0]))) { + *descr = fasttracker; + modtype = 1; + *numchn = id[0] - '0'; + return 1; + } + /* Fasttracker or Taketracker */ + if (((!memcmp(id + 2, "CH", 2)) || (!memcmp(id + 2, "CN", 2))) + && (isdigit(id[0])) && (isdigit(id[1]))) { + if (id[3] == 'H') { + *descr = fasttracker; + modtype = 2; /* this can also be Imago Orpheus */ + } else { + *descr = taketracker; + modtype = 1; + } + *numchn = (id[0] - '0') * 10 + (id[1] - '0'); + return 1; + } + + return 0; +} + +static BOOL MOD_Test(void) +{ + UBYTE id[4], numchn; + CHAR *descr; + + _mm_fseek(modreader, MODULEHEADERSIZE, SEEK_SET); + if (!_mm_read_UBYTES(id, 4, modreader)) + return 0; + + if (MOD_CheckType(id, &numchn, &descr)) + return 1; + + return 0; +} + +static BOOL MOD_Init(void) +{ + if (!(mh = (MODULEHEADER *)_mm_malloc(sizeof(MODULEHEADER)))) + return 0; + return 1; +} + +static void MOD_Cleanup(void) +{ + _mm_free(mh); + _mm_free(patbuf); +} + +/* +Old (amiga) noteinfo: + +_____byte 1_____ byte2_ _____byte 3_____ byte4_ +/ \ / \ / \ / \ +0000 0000-00000000 0000 0000-00000000 + +Upper four 12 bits for Lower four Effect command. +bits of sam- note period. bits of sam- +ple number. ple number. + +*/ + +static UBYTE ConvertNote(MODNOTE *n, UBYTE lasteffect) +{ + UBYTE instrument, effect, effdat, note; + UWORD period; + UBYTE lastnote = 0; + + /* extract the various information from the 4 bytes that make up a note */ + instrument = (n->a & 0x10) | (n->c >> 4); + period = (((UWORD)n->a & 0xf) << 8) + n->b; + effect = n->c & 0xf; + effdat = n->d; + + /* Convert the period to a note number */ + note = 0; + if (period) { + for (note = 0; note < 7 * OCTAVE; note++) + if (period >= npertab[note]) + break; + if (note == 7 * OCTAVE) + note = 0; + else + note++; + } + + if (instrument) { + /* if instrument does not exist, note cut */ + if ((instrument > 31) || (!mh->samples[instrument - 1].length)) { + UniPTEffect(0xc, 0); + if (effect == 0xc) + effect = effdat = 0; + } else { + /* Protracker handling */ + if (!modtype) { + /* if we had a note, then change instrument... */ + if (note) + UniInstrument(instrument - 1); + /* ...otherwise, only adjust volume... */ + else { + /* ...unless an effect was specified, which forces a new + note to be played */ + if (effect || effdat) { + UniInstrument(instrument - 1); + note = lastnote; + } else + UniPTEffect(0xc, + mh->samples[instrument - + 1].volume & 0x7f); + } + } else { + /* Fasttracker handling */ + UniInstrument(instrument - 1); + if (!note) + note = lastnote; + } + } + } + if (note) { + UniNote(note + 2 * OCTAVE - 1); + lastnote = note; + } + + /* Convert pattern jump from Dec to Hex */ + if (effect == 0xd) + effdat = (((effdat & 0xf0) >> 4) * 10) + (effdat & 0xf); + + /* Volume slide, up has priority */ + if ((effect == 0xa) && (effdat & 0xf) && (effdat & 0xf0)) + effdat &= 0xf0; + + /* Handle ``heavy'' volumes correctly */ + if ((effect == 0xc) && (effdat > 0x40)) + effdat = 0x40; + + /* An isolated 100, 200 or 300 effect should be ignored (no + "standalone" porta memory in mod files). However, a sequence such + as 1XX, 100, 100, 100 is fine. */ + if ((!effdat) && ((effect == 1)||(effect == 2)||(effect ==3)) && + (lasteffect < 0x10) && (effect != lasteffect)) + effect = 0; + + UniPTEffect(effect, effdat); + if (effect == 8) + of.flags |= UF_PANNING; + + return effect; +} + +static UBYTE *ConvertTrack(MODNOTE *n, int numchn) +{ + int t; + UBYTE lasteffect = 0x10; /* non existant effect */ + + UniReset(); + for (t = 0; t < 64; t++) { + lasteffect = ConvertNote(n,lasteffect); + UniNewline(); + n += numchn; + } + return UniDup(); +} + +/* Loads all patterns of a modfile and converts them into the 3 byte format. */ +static BOOL ML_LoadPatterns(void) +{ + int t, s, tracks = 0; + + if (!AllocPatterns()) + return 0; + if (!AllocTracks()) + return 0; + + /* Allocate temporary buffer for loading and converting the patterns */ + if (!(patbuf = (MODNOTE *)_mm_calloc(64U * of.numchn, sizeof(MODNOTE)))) + return 0; + + if (trekker && of.numchn == 8) { + /* Startrekker module dual pattern */ + for (t = 0; t < of.numpat; t++) { + for (s = 0; s < (64U * 4); s++) { + patbuf[s].a = _mm_read_UBYTE(modreader); + patbuf[s].b = _mm_read_UBYTE(modreader); + patbuf[s].c = _mm_read_UBYTE(modreader); + patbuf[s].d = _mm_read_UBYTE(modreader); + } + for (s = 0; s < 4; s++) + if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, 4))) + return 0; + for (s = 0; s < (64U * 4); s++) { + patbuf[s].a = _mm_read_UBYTE(modreader); + patbuf[s].b = _mm_read_UBYTE(modreader); + patbuf[s].c = _mm_read_UBYTE(modreader); + patbuf[s].d = _mm_read_UBYTE(modreader); + } + for (s = 0; s < 4; s++) + if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, 4))) + return 0; + } + } else { + /* Generic module pattern */ + for (t = 0; t < of.numpat; t++) { + /* Load the pattern into the temp buffer and convert it */ + for (s = 0; s < (64U * of.numchn); s++) { + patbuf[s].a = _mm_read_UBYTE(modreader); + patbuf[s].b = _mm_read_UBYTE(modreader); + patbuf[s].c = _mm_read_UBYTE(modreader); + patbuf[s].d = _mm_read_UBYTE(modreader); + } + for (s = 0; s < of.numchn; s++) + if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, of.numchn))) + return 0; + } + } + return 1; +} + +static BOOL MOD_Load(BOOL curious) +{ + int t, scan; + SAMPLE *q; + MSAMPINFO *s; + CHAR *descr; + + /* try to read module header */ + _mm_read_string((CHAR *)mh->songname, 20, modreader); + mh->songname[20] = 0; /* just in case */ + + for (t = 0; t < 31; t++) { + s = &mh->samples[t]; + _mm_read_string(s->samplename, 22, modreader); + s->samplename[22] = 0; /* just in case */ + s->length = _mm_read_M_UWORD(modreader); + s->finetune = _mm_read_UBYTE(modreader); + s->volume = _mm_read_UBYTE(modreader); + s->reppos = _mm_read_M_UWORD(modreader); + s->replen = _mm_read_M_UWORD(modreader); + } + + mh->songlength = _mm_read_UBYTE(modreader); + + /* this fixes mods which declare more than 128 positions. + * eg: beatwave.mod */ + if (mh->songlength > 128) { mh->songlength = 128; } + + mh->magic1 = _mm_read_UBYTE(modreader); + _mm_read_UBYTES(mh->positions, 128, modreader); + _mm_read_UBYTES(mh->magic2, 4, modreader); + + if (_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* set module variables */ + of.initspeed = 6; + of.inittempo = 125; + if (!(MOD_CheckType(mh->magic2, &of.numchn, &descr))) { + _mm_errno = MMERR_NOT_A_MODULE; + return 0; + } + if (trekker && of.numchn == 8) + for (t = 0; t < 128; t++) + /* if module pretends to be FLT8, yet the order table + contains odd numbers, chances are it's a lying FLT4... */ + if (mh->positions[t] & 1) { + of.numchn = 4; + break; + } + if (trekker && of.numchn == 8) + for (t = 0; t < 128; t++) + mh->positions[t] >>= 1; + + of.songname = DupStr(mh->songname, 21, 1); + of.numpos = mh->songlength; + of.reppos = 0; + + /* Count the number of patterns */ + of.numpat = 0; + for (t = 0; t < of.numpos; t++) + if (mh->positions[t] > of.numpat) + of.numpat = mh->positions[t]; + + /* since some old modules embed extra patterns, we have to check the + whole list to get the samples' file offsets right - however we can find + garbage here, so check carefully */ + scan = 1; + for (t = of.numpos; t < 128; t++) + if (mh->positions[t] >= 0x80) + scan = 0; + if (scan) + for (t = of.numpos; t < 128; t++) { + if (mh->positions[t] > of.numpat) + of.numpat = mh->positions[t]; + if ((curious) && (mh->positions[t])) + of.numpos = t + 1; + } + of.numpat++; + of.numtrk = of.numpat * of.numchn; + + if (!AllocPositions(of.numpos)) + return 0; + for (t = 0; t < of.numpos; t++) + of.positions[t] = mh->positions[t]; + + /* Finally, init the sampleinfo structures */ + of.numins = of.numsmp = 31; + if (!AllocSamples()) + return 0; + s = mh->samples; + q = of.samples; + for (t = 0; t < of.numins; t++) { + /* convert the samplename */ + q->samplename = DupStr(s->samplename, 23, 1); + /* init the sampleinfo variables and convert the size pointers */ + q->speed = finetune[s->finetune & 0xf]; + q->volume = s->volume & 0x7f; + q->loopstart = (ULONG)s->reppos << 1; + q->loopend = q->loopstart + ((ULONG)s->replen << 1); + q->length = (ULONG)s->length << 1; + q->flags = SF_SIGNED; + /* Imago Orpheus creates MODs with 16 bit samples, check */ + if ((modtype == 2) && (s->volume & 0x80)) { + q->flags |= SF_16BITS; + descr = orpheus; + } + if (s->replen > 2) + q->flags |= SF_LOOP; + + s++; + q++; + } + + of.modtype = strdup(descr); + + if (!ML_LoadPatterns()) + return 0; + + return 1; +} + +static CHAR *MOD_LoadTitle(void) +{ + CHAR s[21]; + + _mm_fseek(modreader, 0, SEEK_SET); + if (!_mm_read_UBYTES(s, 20, modreader)) + return NULL; + s[20] = 0; /* just in case */ + + return (DupStr(s, 21, 1)); +} + +/*========== Loader information */ + +MIKMODAPI MLOADER load_mod = { + NULL, + "Standard module", + "MOD (31 instruments)", + MOD_Init, + MOD_Test, + MOD_Load, + MOD_Cleanup, + MOD_LoadTitle +}; + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/loaders/load_mtm.c b/apps/plugins/mikmod/loaders/load_mtm.c new file mode 100644 index 0000000..3da7b28 --- /dev/null +++ b/apps/plugins/mikmod/loaders/load_mtm.c @@ -0,0 +1,286 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file + AUTHORS for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: load_mtm.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $ + + MTM module loader + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Module structure */ + +typedef struct MTMHEADER { + UBYTE id[3]; /* MTM file marker */ + UBYTE version; /* upper major, lower nibble minor version number */ + CHAR songname[20]; /* ASCIIZ songname */ + UWORD numtracks; /* number of tracks saved */ + UBYTE lastpattern; /* last pattern number saved */ + UBYTE lastorder; /* last order number to play (songlength-1) */ + UWORD commentsize; /* length of comment field */ + UBYTE numsamples; /* number of samples saved */ + UBYTE attribute; /* attribute byte (unused) */ + UBYTE beatspertrack; + UBYTE numchannels; /* number of channels used */ + UBYTE panpos[32]; /* voice pan positions */ +} MTMHEADER; + +typedef struct MTMSAMPLE { + CHAR samplename[22]; + ULONG length; + ULONG reppos; + ULONG repend; + UBYTE finetune; + UBYTE volume; + UBYTE attribute; +} MTMSAMPLE; + +typedef struct MTMNOTE { + UBYTE a,b,c; +} MTMNOTE; + +/*========== Loader variables */ + +static MTMHEADER *mh = NULL; +static MTMNOTE *mtmtrk = NULL; +static UWORD pat[32]; + +static CHAR MTM_Version[] = "MTM"; + +/*========== Loader code */ + +BOOL MTM_Test(void) +{ + UBYTE id[3]; + + if(!_mm_read_UBYTES(id,3,modreader)) return 0; + if(!memcmp(id,"MTM",3)) return 1; + return 0; +} + +BOOL MTM_Init(void) +{ + if(!(mtmtrk=(MTMNOTE*)_mm_calloc(64,sizeof(MTMNOTE)))) return 0; + if(!(mh=(MTMHEADER*)_mm_malloc(sizeof(MTMHEADER)))) return 0; + + return 1; +} + +void MTM_Cleanup(void) +{ + _mm_free(mtmtrk); + _mm_free(mh); +} + +static UBYTE* MTM_Convert(void) +{ + int t; + UBYTE a,b,inst,note,eff,dat; + + UniReset(); + for(t=0;t<64;t++) { + a=mtmtrk[t].a; + b=mtmtrk[t].b; + inst=((a&0x3)<<4)|(b>>4); + note=a>>2; + eff=b&0xf; + dat=mtmtrk[t].c; + + if(inst) UniInstrument(inst-1); + if(note) UniNote(note+2*OCTAVE); + + /* MTM bug workaround : when the effect is volslide, slide-up *always* + overrides slide-down. */ + if(eff==0xa && (dat&0xf0)) dat&=0xf0; + + /* Convert pattern jump from Dec to Hex */ + if(eff==0xd) + dat=(((dat&0xf0)>>4)*10)+(dat&0xf); + UniPTEffect(eff,dat); + UniNewline(); + } + return UniDup(); +} + +BOOL MTM_Load(BOOL curious) +{ + int t,u; + MTMSAMPLE s; + SAMPLE *q; + (void)curious; + + /* try to read module header */ + _mm_read_UBYTES(mh->id,3,modreader); + mh->version =_mm_read_UBYTE(modreader); + _mm_read_string(mh->songname,20,modreader); + mh->numtracks =_mm_read_I_UWORD(modreader); + mh->lastpattern =_mm_read_UBYTE(modreader); + mh->lastorder =_mm_read_UBYTE(modreader); + mh->commentsize =_mm_read_I_UWORD(modreader); + mh->numsamples =_mm_read_UBYTE(modreader); + mh->attribute =_mm_read_UBYTE(modreader); + mh->beatspertrack=_mm_read_UBYTE(modreader); + mh->numchannels =_mm_read_UBYTE(modreader); + _mm_read_UBYTES(mh->panpos,32,modreader); + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* set module variables */ + of.initspeed = 6; + of.inittempo = 125; + of.modtype = strdup(MTM_Version); + of.numchn = mh->numchannels; + of.numtrk = mh->numtracks+1; /* get number of channels */ + of.songname = DupStr(mh->songname,20,1); /* make a cstr of songname */ + of.numpos = mh->lastorder+1; /* copy the songlength */ + of.numpat = mh->lastpattern+1; + of.reppos = 0; + of.flags |= UF_PANNING; + for(t=0;t<32;t++) of.panning[t]=mh->panpos[t]<< 4; + of.numins=of.numsmp=mh->numsamples; + + if(!AllocSamples()) return 0; + q=of.samples; + for(t=0;tsamplename = DupStr(s.samplename,22,1); + q->seekpos = 0; + q->speed = finetune[s.finetune]; + q->length = s.length; + q->loopstart = s.reppos; + q->loopend = s.repend; + q->volume = s.volume; + if((s.repend-s.reppos)>2) q->flags |= SF_LOOP; + + if(s.attribute&1) { + /* If the sample is 16-bits, convert the length and replen + byte-values into sample-values */ + q->flags|=SF_16BITS; + q->length>>=1; + q->loopstart>>=1; + q->loopend>>=1; + } + q++; + } + + if(!AllocPositions(of.numpos)) return 0; + for(t=0;tcommentsize) + if(!ReadLinedComment(mh->commentsize, 40)) return 0; + + return 1; +} + +CHAR *MTM_LoadTitle(void) +{ + CHAR s[20]; + + _mm_fseek(modreader,4,SEEK_SET); + if(!_mm_read_UBYTES(s,20,modreader)) return NULL; + + return(DupStr(s,20,1)); +} + +/*========== Loader information */ + +MIKMODAPI MLOADER load_mtm={ + NULL, + "MTM", + "MTM (MultiTracker Module editor)", + MTM_Init, + MTM_Test, + MTM_Load, + MTM_Cleanup, + MTM_LoadTitle +}; + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/loaders/load_okt.c b/apps/plugins/mikmod/loaders/load_okt.c new file mode 100644 index 0000000..e99837d --- /dev/null +++ b/apps/plugins/mikmod/loaders/load_okt.c @@ -0,0 +1,461 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file + AUTHORS for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: load_okt.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $ + + Oktalyzer (OKT) module loader + +==============================================================================*/ + +/* + Written by UFO + based on the file description compiled by Harald Zappe + + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Module blocks */ + +/* sample information */ +typedef struct OKTSAMPLE { + CHAR sampname[20]; + ULONG len; + UWORD loopbeg; + UWORD looplen; + UBYTE volume; +} OKTSAMPLE; + +typedef struct OKTNOTE { + UBYTE note, ins, eff, dat; +} OKTNOTE; + +/*========== Loader variables */ + +static OKTNOTE *okttrk = NULL; + +/*========== Loader code */ + +BOOL OKT_Test(void) +{ + CHAR id[8]; + + if (!_mm_read_UBYTES(id, 8, modreader)) + return 0; + if (!memcmp(id, "OKTASONG", 8)) + return 1; + + return 0; +} + +/* Pattern analysis routine. + Effects not implemented (yet) : (in decimal) + 11 Arpeggio 4: Change note every 50Hz tick between N,H,N,L + 12 Arpeggio 5: Change note every 50Hz tick between H,H,N + N = normal note being played in this channel (1-36) + L = normal note number minus upper four bits of 'data'. + H = normal note number plus lower four bits of 'data'. + 13 Decrease note number by 'data' once per tick. + 17 Increase note number by 'data' once per tick. + 21 Decrease note number by 'data' once per line. + 30 Increase note number by 'data' once per line. +*/ +static UBYTE *OKT_ConvertTrack(UBYTE patrows) +{ + int t; + UBYTE ins, note, eff, dat; + + UniReset(); + for (t = 0; t < patrows; t++) { + note = okttrk[t].note; + ins = okttrk[t].ins; + eff = okttrk[t].eff; + dat = okttrk[t].dat; + + if (note) { + UniNote(note + 3 * OCTAVE - 1); + UniInstrument(ins); + } + + if (eff) + switch (eff) { + case 1: /* Porta Up */ + UniPTEffect(0x1, dat); + break; + case 2: /* Portamento Down */ + UniPTEffect(0x2, dat); + break; + /* case 9: what is this? */ + case 10: /* Arpeggio 3 */ + case 11: /* Arpeggio 4 */ + case 12: /* Arpeggio 5 */ + UniWriteByte(UNI_OKTARP); + UniWriteByte(eff + 3 - 10); + UniWriteByte(dat); + break; + case 15: /* Amiga filter toggle, ignored */ + break; + case 25: /* Pattern Jump */ + dat = (dat >> 4) * 10 + (dat & 0x0f); + UniPTEffect(0xb, dat); + break; + case 27: /* Release - similar to Keyoff */ + UniWriteByte(UNI_KEYOFF); + break; + case 28: /* Set Tempo */ + UniPTEffect(0xf, dat & 0x0f); + break; + case 31: /* volume Control */ + if (dat <= 0x40) + UniPTEffect(0xc, dat); + else if (dat <= 0x50) + UniEffect(UNI_XMEFFECTA, (dat - 0x40)); /* fast fade out */ + else if (dat <= 0x60) + UniEffect(UNI_XMEFFECTA, (dat - 0x50) << 4); /* fast fade in */ + else if (dat <= 0x70) + UniEffect(UNI_XMEFFECTEB, (dat - 0x60)); /* slow fade out */ + else if (dat <= 0x80) + UniEffect(UNI_XMEFFECTEA, (dat - 0x70)); /* slow fade in */ + break; +#ifdef MIKMOD_DEBUG + default: + fprintf(stderr, "\rUnimplemented effect (%02d,%02x)\n", + eff, dat); +#endif + } + + UniNewline(); + } + return UniDup(); +} + +/* Read "channel modes" i.e. channel number and panning information */ +static void OKT_doCMOD(void) +{ + /* amiga channel panning table */ + UBYTE amigapan[4] = { 0x00, 0xff, 0xff, 0x00 }; + int t; + + of.numchn = 0; + of.flags |= UF_PANNING; + + for (t = 0; t < 4; t++) + if (_mm_read_M_UWORD(modreader)) { + /* two channels tied to the same Amiga hardware voice */ + of.panning[of.numchn++] = amigapan[t]; + of.panning[of.numchn++] = amigapan[t]; + } else + /* one channel tied to the Amiga hardware voice */ + of.panning[of.numchn++] = amigapan[t]; +} + +/* Read sample information */ +static BOOL OKT_doSAMP(int len) +{ + int t; + SAMPLE *q; + OKTSAMPLE s; + + of.numins = of.numsmp = (len / 0x20); + if (!AllocSamples()) + return 0; + + for (t = 0, q = of.samples; t < of.numins; t++, q++) { + _mm_read_UBYTES(s.sampname, 20, modreader); + s.len = _mm_read_M_ULONG(modreader); + s.loopbeg = _mm_read_M_UWORD(modreader) * 2; + s.looplen = _mm_read_M_UWORD(modreader) * 2; + _mm_read_UBYTE(modreader); + s.volume = _mm_read_UBYTE(modreader); + _mm_read_M_UWORD(modreader); + + if (_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + if (!s.len) + q->seekpos = q->length = q->loopstart = q->loopend = q->flags = 0; + else { + s.len--; + /* sanity checks */ + if (s.loopbeg > s.len) + s.loopbeg = s.len; + if (s.loopbeg + s.looplen > s.len) + s.looplen = s.len - s.loopbeg; + if (s.looplen < 2) + s.looplen = 0; + + q->length = s.len; + q->loopstart = s.loopbeg; + q->loopend = s.looplen + q->loopstart; + q->volume = s.volume; + q->flags = SF_SIGNED; + + if (s.looplen) + q->flags |= SF_LOOP; + } + q->samplename = DupStr(s.sampname, 20, 1); + q->speed = 8287; + } + return 1; +} + +/* Read speed information */ +static void OKT_doSPEE(void) +{ + int tempo = _mm_read_M_UWORD(modreader); + + of.initspeed = tempo; +} + +/* Read song length information */ +static void OKT_doSLEN(void) +{ + of.numpat = _mm_read_M_UWORD(modreader); +} + +/* Read pattern length information */ +static void OKT_doPLEN(void) +{ + of.numpos = _mm_read_M_UWORD(modreader); +} + +/* Read order table */ +static BOOL OKT_doPATT(void) +{ + int t; + + if (!of.numpos || !AllocPositions(of.numpos)) + return 0; + + for (t = 0; t < 128; t++) + if (t < of.numpos) + of.positions[t] = _mm_read_UBYTE(modreader); + else + break; + + return 1; +} + +static BOOL OKT_doPBOD(int patnum) +{ + char *patbuf; + int rows, i; + int u; + + if (!patnum) { + of.numtrk = of.numpat * of.numchn; + + if (!AllocTracks() || !AllocPatterns()) + return 0; + } + + /* Read pattern */ + of.pattrows[patnum] = rows = _mm_read_M_UWORD(modreader); + + if (!(okttrk = (OKTNOTE *) _mm_calloc(rows, sizeof(OKTNOTE))) || + !(patbuf = (char *)_mm_calloc(rows * of.numchn, sizeof(OKTNOTE)))) + return 0; + _mm_read_UBYTES(patbuf, rows * of.numchn * sizeof(OKTNOTE), modreader); + if (_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_PATTERN; + return 0; + } + + for (i = 0; i < of.numchn; i++) { + for (u = 0; u < rows; u++) { + okttrk[u].note = patbuf[(u * of.numchn + i) * sizeof(OKTNOTE)]; + okttrk[u].ins = patbuf[(u * of.numchn + i) * sizeof(OKTNOTE) + 1]; + okttrk[u].eff = patbuf[(u * of.numchn + i) * sizeof(OKTNOTE) + 2]; + okttrk[u].dat = patbuf[(u * of.numchn + i) * sizeof(OKTNOTE) + 3]; + } + + if (!(of.tracks[patnum * of.numchn + i] = OKT_ConvertTrack(rows))) + return 0; + } + _mm_free(patbuf); + _mm_free(okttrk); + return 1; +} + +static void OKT_doSBOD(int insnum) +{ + of.samples[insnum].seekpos = _mm_ftell(modreader); +} + +BOOL OKT_Load(BOOL curious) +{ + UBYTE id[4]; + ULONG len; + ULONG fp; + BOOL seen_cmod = 0, seen_samp = 0, seen_slen = 0, seen_plen = 0, seen_patt + = 0, seen_spee = 0; + int patnum = 0, insnum = 0; + (void)curious; + + /* skip OKTALYZER header */ + _mm_fseek(modreader, 8, SEEK_SET); + of.songname = strdup(""); + + of.modtype = strdup("Amiga Oktalyzer"); + of.numpos = of.reppos = 0; + + /* default values */ + of.initspeed = 6; + of.inittempo = 125; + + while (1) { + /* read block header */ + _mm_read_UBYTES(id, 4, modreader); + len = _mm_read_M_ULONG(modreader); + + if (_mm_eof(modreader)) + break; + fp = _mm_ftell(modreader); + + if (!memcmp(id, "CMOD", 4)) { + /*if (!seen_cmod) { + OKT_doCMOD(); // gcc for ipod stucks here, saying it is a call to memcpy... + seen_cmod = 1; + } else {*/ + _mm_errno = MMERR_LOADING_HEADER; + return 0; + /* } */ + } else if (!memcmp(id, "SAMP", 4)) { + if (!seen_samp && OKT_doSAMP(len)) + seen_samp = 1; + else { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + } else if (!memcmp(id, "SPEE", 4)) { + if (!seen_spee) { + OKT_doSPEE(); + seen_spee = 1; + } else { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + } else if (!memcmp(id, "SLEN", 4)) { + if (!seen_slen) { + OKT_doSLEN(); + seen_slen = 1; + } else { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + } else if (!memcmp(id, "PLEN", 4)) { + if (!seen_plen) { + OKT_doPLEN(); + seen_plen = 1; + } else { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + } else if (!memcmp(id, "PATT", 4)) { + if (!seen_plen) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + if (!seen_patt && OKT_doPATT()) + seen_patt = 1; + else { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + } else if (!memcmp(id,"PBOD", 4)) { + /* need to know numpat and numchn */ + if (!seen_slen || !seen_cmod || (patnum >= of.numpat)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + if (!OKT_doPBOD(patnum++)) { + _mm_errno = MMERR_LOADING_PATTERN; + return 0; + } + } else if (!memcmp(id,"SBOD",4)) { + /* need to know numsmp */ + if (!seen_samp) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + while ((insnum < of.numins) && !of.samples[insnum].length) + insnum++; + if (insnum >= of.numins) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + OKT_doSBOD(insnum++); + } + + /* goto next block start position */ + _mm_fseek(modreader, fp + len, SEEK_SET); + } + + if (!seen_cmod || !seen_samp || !seen_patt || + !seen_slen || !seen_plen || (patnum != of.numpat)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + return 1; +} + +CHAR *OKT_LoadTitle(void) +{ + return strdup(""); +} + +/*========== Loader information */ + +MIKMODAPI MLOADER load_okt = { + NULL, + "OKT", + "OKT (Amiga Oktalyzer)", + NULL, + OKT_Test, + OKT_Load, + NULL, + OKT_LoadTitle +}; + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/loaders/load_s3m.c b/apps/plugins/mikmod/loaders/load_s3m.c new file mode 100644 index 0000000..11f691b --- /dev/null +++ b/apps/plugins/mikmod/loaders/load_s3m.c @@ -0,0 +1,470 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file + AUTHORS for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: load_s3m.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $ + + Screamtracker (S3M) module loader + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Module structure */ + +/* header */ +typedef struct S3MHEADER { + CHAR songname[28]; + UBYTE t1a; + UBYTE type; + UBYTE unused1[2]; + UWORD ordnum; + UWORD insnum; + UWORD patnum; + UWORD flags; + UWORD tracker; + UWORD fileformat; + CHAR scrm[4]; + UBYTE mastervol; + UBYTE initspeed; + UBYTE inittempo; + UBYTE mastermult; + UBYTE ultraclick; + UBYTE pantable; + UBYTE unused2[8]; + UWORD special; + UBYTE channels[32]; +} S3MHEADER; + +/* sample information */ +typedef struct S3MSAMPLE { + UBYTE type; + CHAR filename[12]; + UBYTE memsegh; + UWORD memsegl; + ULONG length; + ULONG loopbeg; + ULONG loopend; + UBYTE volume; + UBYTE dsk; + UBYTE pack; + UBYTE flags; + ULONG c2spd; + UBYTE unused[12]; + CHAR sampname[28]; + CHAR scrs[4]; +} S3MSAMPLE; + +typedef struct S3MNOTE { + UBYTE note,ins,vol,cmd,inf; +} S3MNOTE; + +/*========== Loader variables */ + +static S3MNOTE *s3mbuf = NULL; /* pointer to a complete S3M pattern */ +static S3MHEADER *mh = NULL; +static UWORD *paraptr = NULL; /* parapointer array (see S3M docs) */ +static unsigned int tracker; /* tracker id */ + +/* tracker identifiers */ +#define NUMTRACKERS 4 +static CHAR* S3M_Version[] = { + "Screamtracker x.xx", + "Imago Orpheus x.xx (S3M format)", + "Impulse Tracker x.xx (S3M format)", + "Unknown tracker x.xx (S3M format)", + "Impulse Tracker 2.14p3 (S3M format)", + "Impulse Tracker 2.14p4 (S3M format)" +}; +/* version number position in above array */ +static int numeric[NUMTRACKERS]={14,14,16,16}; + +/*========== Loader code */ + +BOOL S3M_Test(void) +{ + UBYTE id[4]; + + _mm_fseek(modreader,0x2c,SEEK_SET); + if(!_mm_read_UBYTES(id,4,modreader)) return 0; + if(!memcmp(id,"SCRM",4)) return 1; + return 0; +} + +BOOL S3M_Init(void) +{ + if(!(s3mbuf=(S3MNOTE*)_mm_malloc(32*64*sizeof(S3MNOTE)))) return 0; + if(!(mh=(S3MHEADER*)_mm_malloc(sizeof(S3MHEADER)))) return 0; + if(!(poslookup=(UBYTE*)_mm_malloc(sizeof(UBYTE)*256))) return 0; + memset(poslookup,-1,256); + + return 1; +} + +void S3M_Cleanup(void) +{ + _mm_free(s3mbuf); + _mm_free(paraptr); + _mm_free(poslookup); + _mm_free(mh); + _mm_free(origpositions); +} + +/* Because so many s3m files have 16 channels as the set number used, but really + only use far less (usually 8 to 12 still), I had to make this function, which + determines the number of channels that are actually USED by a pattern. + + For every channel that's used, it sets the appropriate array entry of the + global variable 'remap' + + NOTE: You must first seek to the file location of the pattern before calling + this procedure. + + Returns 1 on fail. */ +static BOOL S3M_GetNumChannels(void) +{ + int row=0,flag,ch; + + while(row<64) { + flag=_mm_read_UBYTE(modreader); + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_PATTERN; + return 1; + } + + if(flag) { + ch=flag&31; + if(mh->channels[ch]<32) remap[ch] = 0; + if(flag&32) {_mm_read_UBYTE(modreader);_mm_read_UBYTE(modreader);} + if(flag&64) _mm_read_UBYTE(modreader); + if(flag&128){_mm_read_UBYTE(modreader);_mm_read_UBYTE(modreader);} + } else row++; + } + return 0; +} + +static BOOL S3M_ReadPattern(void) +{ + int row=0,flag,ch; + S3MNOTE *n,dummy; + + /* clear pattern data */ + memset(s3mbuf,255,32*64*sizeof(S3MNOTE)); + + while(row<64) { + flag=_mm_read_UBYTE(modreader); + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_PATTERN; + return 0; + } + + if(flag) { + ch=remap[flag&31]; + + if(ch!=-1) + n=&s3mbuf[(64U*ch)+row]; + else + n=&dummy; + + if(flag&32) { + n->note=_mm_read_UBYTE(modreader); + n->ins=_mm_read_UBYTE(modreader); + } + if(flag&64) { + n->vol=_mm_read_UBYTE(modreader); + if (n->vol>64) n->vol=64; + } + if(flag&128) { + n->cmd=_mm_read_UBYTE(modreader); + n->inf=_mm_read_UBYTE(modreader); + } + } else row++; + } + return 1; +} + +static UBYTE* S3M_ConvertTrack(S3MNOTE* tr) +{ + int t; + + UniReset(); + for(t=0;t<64;t++) { + UBYTE note,ins,vol; + + note=tr[t].note; + ins=tr[t].ins; + vol=tr[t].vol; + + if((ins)&&(ins!=255)) UniInstrument(ins-1); + if(note!=255) { + if(note==254) { + UniPTEffect(0xc,0); /* note cut command */ + vol=255; + } else + UniNote(((note>>4)*OCTAVE)+(note&0xf)); /* normal note */ + } + if(vol<255) UniPTEffect(0xc,vol); + + S3MIT_ProcessCmd(tr[t].cmd,tr[t].inf, + tracker == 1 ? S3MIT_OLDSTYLE | S3MIT_SCREAM : S3MIT_OLDSTYLE); + UniNewline(); + } + return UniDup(); +} + +BOOL S3M_Load(BOOL curious) +{ + int t,u,track = 0; + SAMPLE *q; + UBYTE pan[32]; + + /* try to read module header */ + _mm_read_string(mh->songname,28,modreader); + mh->t1a =_mm_read_UBYTE(modreader); + mh->type =_mm_read_UBYTE(modreader); + _mm_read_UBYTES(mh->unused1,2,modreader); + mh->ordnum =_mm_read_I_UWORD(modreader); + mh->insnum =_mm_read_I_UWORD(modreader); + mh->patnum =_mm_read_I_UWORD(modreader); + mh->flags =_mm_read_I_UWORD(modreader); + mh->tracker =_mm_read_I_UWORD(modreader); + mh->fileformat =_mm_read_I_UWORD(modreader); + _mm_read_string(mh->scrm,4,modreader); + mh->mastervol =_mm_read_UBYTE(modreader); + mh->initspeed =_mm_read_UBYTE(modreader); + mh->inittempo =_mm_read_UBYTE(modreader); + mh->mastermult =_mm_read_UBYTE(modreader); + mh->ultraclick =_mm_read_UBYTE(modreader); + mh->pantable =_mm_read_UBYTE(modreader); + _mm_read_UBYTES(mh->unused2,8,modreader); + mh->special =_mm_read_I_UWORD(modreader); + _mm_read_UBYTES(mh->channels,32,modreader); + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* then we can decide the module type */ + tracker=mh->tracker>>12; + if((!tracker)||(tracker>=NUMTRACKERS)) + tracker=NUMTRACKERS-1; /* unknown tracker */ + else { + if(mh->tracker>=0x3217) + tracker=NUMTRACKERS+1; /* IT 2.14p4 */ + else if(mh->tracker>=0x3216) + tracker=NUMTRACKERS; /* IT 2.14p3 */ + else tracker--; + } + of.modtype = strdup(S3M_Version[tracker]); + if(trackertracker>>8) &0xf)+'0'; + of.modtype[numeric[tracker]+2] = ((mh->tracker>>4)&0xf)+'0'; + of.modtype[numeric[tracker]+3] = ((mh->tracker)&0xf)+'0'; + } + /* set module variables */ + of.songname = DupStr(mh->songname,28,0); + of.numpat = mh->patnum; + of.reppos = 0; + of.numins = of.numsmp = mh->insnum; + of.initspeed = mh->initspeed; + of.inittempo = mh->inittempo; + of.initvolume = mh->mastervol<<1; + of.flags |= UF_ARPMEM | UF_PANNING; + if((mh->tracker==0x1300)||(mh->flags&64)) + of.flags|=UF_S3MSLIDES; + of.bpmlimit = 32; + + /* read the order data */ + if(!AllocPositions(mh->ordnum)) return 0; + if(!(origpositions=_mm_calloc(mh->ordnum,sizeof(UWORD)))) return 0; + + for(t=0;tordnum;t++) { + origpositions[t]=_mm_read_UBYTE(modreader); + if((origpositions[t]>=mh->patnum)&&(origpositions[t]<254)) + origpositions[t]=255/*mh->patnum-1*/; + } + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + poslookupcnt=mh->ordnum; + S3MIT_CreateOrders(curious); + + if(!(paraptr=(UWORD*)_mm_malloc((of.numins+of.numpat)*sizeof(UWORD)))) + return 0; + + /* read the instrument+pattern parapointers */ + _mm_read_I_UWORDS(paraptr,of.numins+of.numpat,modreader); + + if(mh->pantable==252) { + /* read the panning table (ST 3.2 addition. See below for further + portions of channel panning [past reampper]). */ + _mm_read_UBYTES(pan,32,modreader); + } + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* load samples */ + if(!AllocSamples()) return 0; + q = of.samples; + for(t=0;t 64000) + s.length = 64000; + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + q->samplename = DupStr(s.sampname,28,0); + q->speed = s.c2spd; + q->length = s.length; + q->loopstart = s.loopbeg; + q->loopend = s.loopend; + q->volume = s.volume; + q->seekpos = (((long)s.memsegh)<<16|s.memsegl)<<4; + + if(s.flags&1) q->flags |= SF_LOOP; + if(s.flags&4) q->flags |= SF_16BITS; + if(mh->fileformat==1) q->flags |= SF_SIGNED; + + /* don't load sample if it doesn't have the SCRS tag */ + if(memcmp(s.scrs,"SCRS",4)) q->length = 0; + + q++; + } + + /* determine the number of channels actually used. */ + of.numchn = 0; + memset(remap,-1,32*sizeof(UBYTE)); + for(t=0;tchannels[t]<32)&&(remap[t]!=-1)) { + if(mh->channels[t]<8) + of.panning[remap[t]]=0x30; + else + of.panning[remap[t]]=0xc0; + } + if(mh->pantable==252) + /* set panning positions according to panning table (new for st3.2) */ + for(t=0;t<32;t++) + if((pan[t]&0x20)&&(mh->channels[t]<32)&&(remap[t]!=-1)) + of.panning[remap[t]]=(pan[t]&0xf)<<4; + + /* load pattern info */ + of.numtrk=of.numpat*of.numchn; + if(!AllocTracks()) return 0; + if(!AllocPatterns()) return 0; + + for(t=0;t +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Module structure */ + +/* sample information */ +typedef struct STMSAMPLE { + CHAR filename[12]; + UBYTE unused; /* 0x00 */ + UBYTE instdisk; /* Instrument disk */ + UWORD reserved; + UWORD length; /* Sample length */ + UWORD loopbeg; /* Loop start point */ + UWORD loopend; /* Loop end point */ + UBYTE volume; /* Volume */ + UBYTE reserved2; + UWORD c2spd; /* Good old c2spd */ + ULONG reserved3; + UWORD isa; +} STMSAMPLE; + +/* header */ +typedef struct STMHEADER { + CHAR songname[20]; + CHAR trackername[8]; /* !Scream! for ST 2.xx */ + UBYTE unused; /* 0x1A */ + UBYTE filetype; /* 1=song, 2=module */ + UBYTE ver_major; + UBYTE ver_minor; + UBYTE inittempo; /* initspeed= stm inittempo>>4 */ + UBYTE numpat; /* number of patterns */ + UBYTE globalvol; + UBYTE reserved[13]; + STMSAMPLE sample[31]; /* STM sample data */ + UBYTE patorder[128]; /* Docs say 64 - actually 128 */ +} STMHEADER; + +typedef struct STMNOTE { + UBYTE note,insvol,volcmd,cmdinf; +} STMNOTE; + +/*========== Loader variables */ + +static STMNOTE *stmbuf = NULL; +static STMHEADER *mh = NULL; + +/* tracker identifiers */ +static CHAR* STM_Version[STM_NTRACKERS] = { + "Screamtracker 2", + "Converted by MOD2STM (STM format)", + "Wuzamod (STM format)" +}; + +/*========== Loader code */ + +BOOL STM_Test(void) +{ + UBYTE str[44]; + int t; + + _mm_fseek(modreader,20,SEEK_SET); + _mm_read_UBYTES(str,44,modreader); + if(str[9]!=2) return 0; /* STM Module = filetype 2 */ + + /* Prevent false positives for S3M files */ + if(!memcmp(str+40,"SCRM",4)) + return 0; + + for (t=0;tnote; + ins = n->insvol>>3; + vol = (n->insvol&7)+((n->volcmd&0x70)>>1); + cmd = n->volcmd&15; + inf = n->cmdinf; + + if((ins)&&(ins<32)) UniInstrument(ins-1); + + /* special values of [SBYTE0] are handled here + we have no idea if these strange values will ever be encountered. + but it appears as those stms sound correct. */ + if((note==254)||(note==252)) { + UniPTEffect(0xc,0); /* note cut */ + n->volcmd|=0x80; + } else + /* if note < 251, then all three bytes are stored in the file */ + if(note<251) UniNote((((note>>4)+2)*OCTAVE)+(note&0xf)); + + if((!(n->volcmd&0x80))&&(vol<65)) UniPTEffect(0xc,vol); + if(cmd!=255) + switch(cmd) { + case 1: /* Axx set speed to xx */ + UniPTEffect(0xf,inf>>4); + break; + case 2: /* Bxx position jump */ + UniPTEffect(0xb,inf); + break; + case 3: /* Cxx patternbreak to row xx */ + UniPTEffect(0xd,(((inf&0xf0)>>4)*10)+(inf&0xf)); + break; + case 4: /* Dxy volumeslide */ + UniEffect(UNI_S3MEFFECTD,inf); + break; + case 5: /* Exy toneslide down */ + UniEffect(UNI_S3MEFFECTE,inf); + break; + case 6: /* Fxy toneslide up */ + UniEffect(UNI_S3MEFFECTF,inf); + break; + case 7: /* Gxx Tone portamento,speed xx */ + UniPTEffect(0x3,inf); + break; + case 8: /* Hxy vibrato */ + UniPTEffect(0x4,inf); + break; + case 9: /* Ixy tremor, ontime x, offtime y */ + UniEffect(UNI_S3MEFFECTI,inf); + break; + case 0: /* protracker arpeggio */ + if(!inf) break; + /* fall through */ + case 0xa: /* Jxy arpeggio */ + UniPTEffect(0x0,inf); + break; + case 0xb: /* Kxy Dual command H00 & Dxy */ + UniPTEffect(0x4,0); + UniEffect(UNI_S3MEFFECTD,inf); + break; + case 0xc: /* Lxy Dual command G00 & Dxy */ + UniPTEffect(0x3,0); + UniEffect(UNI_S3MEFFECTD,inf); + break; + /* Support all these above, since ST2 can LOAD these values but can + actually only play up to J - and J is only half-way implemented + in ST2 */ + case 0x18: /* Xxx amiga panning command 8xx */ + UniPTEffect(0x8,inf); + of.flags |= UF_PANNING; + break; + } +} + +static UBYTE *STM_ConvertTrack(STMNOTE *n) +{ + int t; + + UniReset(); + for(t=0;t<64;t++) { + STM_ConvertNote(n); + UniNewline(); + n+=of.numchn; + } + return UniDup(); +} + +static BOOL STM_LoadPatterns(void) +{ + int t,s,tracks=0; + + if(!AllocPatterns()) return 0; + if(!AllocTracks()) return 0; + + /* Allocate temporary buffer for loading and converting the patterns */ + for(t=0;tsongname,20,modreader); + _mm_read_string(mh->trackername,8,modreader); + mh->unused =_mm_read_UBYTE(modreader); + mh->filetype =_mm_read_UBYTE(modreader); + mh->ver_major =_mm_read_UBYTE(modreader); + mh->ver_minor =_mm_read_UBYTE(modreader); + mh->inittempo =_mm_read_UBYTE(modreader); + if(!mh->inittempo) { + _mm_errno=MMERR_NOT_A_MODULE; + return 0; + } + mh->numpat =_mm_read_UBYTE(modreader); + mh->globalvol =_mm_read_UBYTE(modreader); + _mm_read_UBYTES(mh->reserved,13,modreader); + + for(t=0;t<31;t++) { + STMSAMPLE *s=&mh->sample[t]; /* STM sample data */ + + _mm_read_string(s->filename,12,modreader); + s->unused =_mm_read_UBYTE(modreader); + s->instdisk =_mm_read_UBYTE(modreader); + s->reserved =_mm_read_I_UWORD(modreader); + s->length =_mm_read_I_UWORD(modreader); + s->loopbeg =_mm_read_I_UWORD(modreader); + s->loopend =_mm_read_I_UWORD(modreader); + s->volume =_mm_read_UBYTE(modreader); + s->reserved2=_mm_read_UBYTE(modreader); + s->c2spd =_mm_read_I_UWORD(modreader); + s->reserved3=_mm_read_I_ULONG(modreader); + s->isa =_mm_read_I_UWORD(modreader); + } + _mm_read_UBYTES(mh->patorder,128,modreader); + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* set module variables */ + for(t=0;ttrackername,STM_Signatures[t],8)) break; + of.modtype = strdup(STM_Version[t]); + of.songname = DupStr(mh->songname,20,1); /* make a cstr of songname */ + of.numpat = mh->numpat; + of.inittempo = 125; /* mh->inittempo+0x1c; */ + of.initspeed = mh->inittempo>>4; + of.numchn = 4; /* get number of channels */ + of.reppos = 0; + of.flags |= UF_S3MSLIDES; + of.bpmlimit = 32; + + t=0; + if(!AllocPositions(0x80)) return 0; + /* 99 terminates the patorder list */ + while((mh->patorder[t]<=99)&&(mh->patorder[t]numpat)) { + of.positions[t]=mh->patorder[t]; + t++; + } + if(mh->patorder[t]<=99) t++; + of.numpos=t; + of.numtrk=of.numpat*of.numchn; + of.numins=of.numsmp=31; + + if(!AllocSamples()) return 0; + if(!STM_LoadPatterns()) return 0; + MikMod_ISA=_mm_ftell(modreader); + MikMod_ISA=(MikMod_ISA+15)&0xfffffff0; /* normalize */ + + for(q=of.samples,t=0;tsamplename = DupStr(mh->sample[t].filename,12,1); + q->speed = (mh->sample[t].c2spd * 8363) / 8448; + q->volume = mh->sample[t].volume; + q->length = mh->sample[t].length; + if (/*(!mh->sample[t].volume)||*/(q->length==1)) q->length=0; + q->loopstart = mh->sample[t].loopbeg; + q->loopend = mh->sample[t].loopend; + q->seekpos = MikMod_ISA; + + MikMod_ISA+=q->length; + MikMod_ISA=(MikMod_ISA+15)&0xfffffff0; /* normalize */ + + /* contrary to the STM specs, sample data is signed */ + q->flags = SF_SIGNED; + + if(q->loopend && q->loopend != 0xffff) + q->flags|=SF_LOOP; + } + return 1; +} + +CHAR *STM_LoadTitle(void) +{ + CHAR s[20]; + + _mm_fseek(modreader,0,SEEK_SET); + if(!_mm_read_UBYTES(s,20,modreader)) return NULL; + + return(DupStr(s,20,1)); +} + +/*========== Loader information */ + +MIKMODAPI MLOADER load_stm={ + NULL, + "STM", + "STM (Scream Tracker)", + STM_Init, + STM_Test, + STM_Load, + STM_Cleanup, + STM_LoadTitle +}; + + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/loaders/load_stx.c b/apps/plugins/mikmod/loaders/load_stx.c new file mode 100644 index 0000000..66e67f0 --- /dev/null +++ b/apps/plugins/mikmod/loaders/load_stx.c @@ -0,0 +1,439 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file + AUTHORS for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: load_stx.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $ + + STMIK 0.2 (STX) module loader + +==============================================================================*/ + +/* + + Written by Claudio Matsuoka + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Module structure */ + +/* header */ +typedef struct STXHEADER { + CHAR songname[20]; + CHAR trackername[8]; + UWORD patsize; + UWORD unknown1; + UWORD patptr; + UWORD insptr; + UWORD chnptr; /* not sure */ + UWORD unknown2; + UWORD unknown3; + UBYTE mastermult; + UBYTE initspeed; + UWORD unknown4; + UWORD unknown5; + UWORD patnum; + UWORD insnum; + UWORD ordnum; + UWORD unknown6; + UWORD unknown7; + UWORD unknown8; + CHAR scrm[4]; +} STXHEADER; + +/* sample information */ +typedef struct STXSAMPLE { + UBYTE type; + CHAR filename[12]; + UBYTE memsegh; + UWORD memsegl; + ULONG length; + ULONG loopbeg; + ULONG loopend; + UBYTE volume; + UBYTE dsk; + UBYTE pack; + UBYTE flags; + ULONG c2spd; + UBYTE unused[12]; + CHAR sampname[28]; + CHAR scrs[4]; +} STXSAMPLE; + +typedef struct STXNOTE { + UBYTE note,ins,vol,cmd,inf; +} STXNOTE; + +/*========== Loader variables */ + +static STXNOTE *stxbuf = NULL; /* pointer to a complete STX pattern */ +static STXHEADER *mh = NULL; +static UWORD *paraptr = NULL; /* parapointer array (see STX docs) */ + +/*========== Loader code */ + +static BOOL STX_Test(void) +{ + UBYTE id[8]; + int t; + + _mm_fseek(modreader,0x3C,SEEK_SET); + if(!_mm_read_UBYTES(id,4,modreader)) return 0; + if(memcmp(id,"SCRM",4)) return 0; + + _mm_fseek(modreader,0x14,SEEK_SET); + if(!_mm_read_UBYTES(id,8,modreader)) return 0; + + for(t=0;t=0)&&(ch<4)) + n=&stxbuf[(64U*ch)+row]; + else + n=&dummy; + + if(flag&32) { + n->note=_mm_read_UBYTE(modreader); + n->ins=_mm_read_UBYTE(modreader); + } + if(flag&64) { + n->vol=_mm_read_UBYTE(modreader); + if(n->vol>64) n->vol=64; + } + if(flag&128) { + n->cmd=_mm_read_UBYTE(modreader); + n->inf=_mm_read_UBYTE(modreader); + } + } else row++; + } + return 1; +} + +static UBYTE* STX_ConvertTrack(STXNOTE* tr) +{ + int t; + + UniReset(); + for(t=0;t<64;t++) { + UBYTE note,ins,vol,cmd,inf; + + note=tr[t].note; + ins=tr[t].ins; + vol=tr[t].vol; + cmd=tr[t].cmd; + inf=tr[t].inf; + + if((ins)&&(ins!=255)) UniInstrument(ins-1); + if((note)&&(note!=255)) { + if(note==254) { + UniPTEffect(0xc,0); /* note cut command */ + vol=255; + } else UniNote(24+((note>>4)*OCTAVE)+(note&0xf)); /* normal note */ + } + + if(vol<255) UniPTEffect(0xc,vol); + + if(cmd<255) switch(cmd) { + case 1: /* Axx set speed to xx */ + UniPTEffect(0xf,inf>>4); + break; + case 2: /* Bxx position jump */ + UniPTEffect(0xb,inf); + break; + case 3: /* Cxx patternbreak to row xx */ + UniPTEffect(0xd,(((inf&0xf0)>>4)*10)+(inf&0xf)); + break; + case 4: /* Dxy volumeslide */ + UniEffect(UNI_S3MEFFECTD,inf); + break; + case 5: /* Exy toneslide down */ + UniEffect(UNI_S3MEFFECTE,inf); + break; + case 6: /* Fxy toneslide up */ + UniEffect(UNI_S3MEFFECTF,inf); + break; + case 7: /* Gxx Tone portamento,speed xx */ + UniPTEffect(0x3,inf); + break; + case 8: /* Hxy vibrato */ + UniPTEffect(0x4,inf); + break; + case 9: /* Ixy tremor, ontime x, offtime y */ + UniEffect(UNI_S3MEFFECTI,inf); + break; + case 0: /* protracker arpeggio */ + if(!inf) break; + /* fall through */ + case 0xa: /* Jxy arpeggio */ + UniPTEffect(0x0,inf); + break; + case 0xb: /* Kxy Dual command H00 & Dxy */ + UniPTEffect(0x4,0); + UniEffect(UNI_S3MEFFECTD,inf); + break; + case 0xc: /* Lxy Dual command G00 & Dxy */ + UniPTEffect(0x3,0); + UniEffect(UNI_S3MEFFECTD,inf); + break; + /* Support all these above, since ST2 can LOAD these values but can + actually only play up to J - and J is only half-way implemented + in ST2 */ + case 0x18: /* Xxx amiga panning command 8xx */ + UniPTEffect(0x8,inf); + of.flags |= UF_PANNING; + break; + } + UniNewline(); + } + return UniDup(); +} + +static BOOL STX_Load(BOOL curious) +{ + int t,u,track = 0; + int version = 0; + SAMPLE *q; + + /* try to read module header */ + _mm_read_string(mh->songname,20,modreader); + _mm_read_string(mh->trackername,8,modreader); + mh->patsize =_mm_read_I_UWORD(modreader); + mh->unknown1 =_mm_read_I_UWORD(modreader); + mh->patptr =_mm_read_I_UWORD(modreader); + mh->insptr =_mm_read_I_UWORD(modreader); + mh->chnptr =_mm_read_I_UWORD(modreader); + mh->unknown2 =_mm_read_I_UWORD(modreader); + mh->unknown3 =_mm_read_I_UWORD(modreader); + mh->mastermult =_mm_read_UBYTE(modreader); + mh->initspeed =_mm_read_UBYTE(modreader)>>4; + mh->unknown4 =_mm_read_I_UWORD(modreader); + mh->unknown5 =_mm_read_I_UWORD(modreader); + mh->patnum =_mm_read_I_UWORD(modreader); + mh->insnum =_mm_read_I_UWORD(modreader); + mh->ordnum =_mm_read_I_UWORD(modreader); + mh->unknown6 =_mm_read_I_UWORD(modreader); + mh->unknown7 =_mm_read_I_UWORD(modreader); + mh->unknown8 =_mm_read_I_UWORD(modreader); + _mm_read_string(mh->scrm,4,modreader); + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* set module variables */ + of.songname = DupStr(mh->songname,20,1); + of.numpat = mh->patnum; + of.reppos = 0; + of.numins = of.numsmp = mh->insnum; + of.initspeed = mh->initspeed; + of.inittempo = 125; + of.numchn = 4; + of.flags |= UF_S3MSLIDES; + of.bpmlimit = 32; + + if(!(paraptr=(UWORD*)_mm_malloc((of.numins+of.numpat)*sizeof(UWORD)))) + return 0; + + /* read the instrument+pattern parapointers */ + _mm_fseek(modreader,mh->insptr<<4,SEEK_SET); + _mm_read_I_UWORDS(paraptr,of.numins,modreader); + _mm_fseek(modreader,mh->patptr<<4,SEEK_SET); + _mm_read_I_UWORDS(paraptr+of.numins,of.numpat,modreader); + + /* check module version */ + _mm_fseek(modreader,paraptr[of.numins]<<4,SEEK_SET); + version=_mm_read_I_UWORD(modreader); + if(version==mh->patsize) { + version = 0x10; + of.modtype = strdup("STMIK 0.2 (STM2STX 1.0)"); + } else { + version = 0x11; + of.modtype = strdup("STMIK 0.2 (STM2STX 1.1)"); + } + + /* read the order data */ + _mm_fseek(modreader,(mh->chnptr<<4)+32,SEEK_SET); + if(!AllocPositions(mh->ordnum)) return 0; + for(t=0;tordnum;t++) { + of.positions[t]=_mm_read_UBYTE(modreader); + _mm_fseek(modreader,4,SEEK_CUR); + } + + of.numpos=0;poslookupcnt=mh->ordnum; + for(t=0;tordnum;t++) { + int order=of.positions[t]; + if(order==255) order=LAST_PATTERN; + of.positions[of.numpos]=order; + poslookup[t]=of.numpos; /* bug fix for freaky S3Ms */ + if(of.positions[t]<254) of.numpos++; + else + /* special end of song pattern */ + if((order==LAST_PATTERN)&&(!curious)) break; + } + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* load samples */ + if(!AllocSamples()) return 0; + for(q=of.samples,t=0;tsamplename = DupStr(s.sampname,28,1); + q->speed = (s.c2spd * 8363) / 8448; + q->length = s.length; + q->loopstart = s.loopbeg; + q->loopend = s.loopend; + q->volume = s.volume; + q->seekpos = (((long)s.memsegh)<<16|s.memsegl)<<4; + q->flags |= SF_SIGNED; + + if(s.flags&1) q->flags |= SF_LOOP; + if(s.flags&4) q->flags |= SF_16BITS; + } + + /* load pattern info */ + of.numtrk=of.numpat*of.numchn; + if(!AllocTracks()) return 0; + if(!AllocPatterns()) return 0; + + for(t=0;t +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Module structure */ + +/* header */ +typedef struct ULTHEADER { + CHAR id[16]; + CHAR songtitle[32]; + UBYTE reserved; +} ULTHEADER; + +/* sample information */ +typedef struct ULTSAMPLE { + CHAR samplename[32]; + CHAR dosname[12]; + SLONG loopstart; + SLONG loopend; + SLONG sizestart; + SLONG sizeend; + UBYTE volume; + UBYTE flags; + UWORD speed; + SWORD finetune; +} ULTSAMPLE; + +typedef struct ULTEVENT { + UBYTE note,sample,eff,dat1,dat2; +} ULTEVENT; + +/*========== Loader variables */ + +#define ULTS_16BITS 4 +#define ULTS_LOOP 8 +#define ULTS_REVERSE 16 + +#define ULT_VERSION_LEN 18 +static CHAR ULT_Version[ULT_VERSION_LEN]="Ultra Tracker v1.x"; + +static ULTEVENT ev; + +/*========== Loader code */ + +BOOL ULT_Test(void) +{ + CHAR id[16]; + + if(!_mm_read_string(id,15,modreader)) return 0; + if(strncmp(id,"MAS_UTrack_V00",14)) return 0; + if((id[14]<'1')||(id[14]>'4')) return 0; + return 1; +} + +BOOL ULT_Init(void) +{ + return 1; +} + +void ULT_Cleanup(void) +{ +} + +static UBYTE ReadUltEvent(ULTEVENT* event) +{ + UBYTE flag,rep=1; + + flag = _mm_read_UBYTE(modreader); + if(flag==0xfc) { + rep = _mm_read_UBYTE(modreader); + event->note =_mm_read_UBYTE(modreader); + } else + event->note = flag; + + event->sample =_mm_read_UBYTE(modreader); + event->eff =_mm_read_UBYTE(modreader); + event->dat1 =_mm_read_UBYTE(modreader); + event->dat2 =_mm_read_UBYTE(modreader); + + return rep; +} + +BOOL ULT_Load(BOOL curious) +{ + int t,u,tracks=0; + SAMPLE *q; + ULTSAMPLE s; + ULTHEADER mh; + UBYTE nos,noc,nopa; + (void)curious; + + /* try to read module header */ + _mm_read_string(mh.id,15,modreader); + _mm_read_string(mh.songtitle,32,modreader); + mh.reserved=_mm_read_UBYTE(modreader); + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + ULT_Version[ULT_VERSION_LEN-1]='3'+(mh.id[14]-'1'); + of.modtype = DupStr(ULT_Version,ULT_VERSION_LEN,1); + of.initspeed = 6; + of.inittempo = 125; + of.reppos = 0; + + /* read songtext */ + if ((mh.id[14]>'1')&&(mh.reserved)) + if(!ReadLinedComment(mh.reserved * 32, 32)) return 0; + + nos=_mm_read_UBYTE(modreader); + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + of.songname=DupStr(mh.songtitle,32,1); + of.numins=of.numsmp=nos; + + if(!AllocSamples()) return 0; + q = of.samples; + for(t=0;t='4')?_mm_read_I_UWORD(modreader):8363; + s.finetune =_mm_read_I_SWORD(modreader); + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + q->samplename=DupStr(s.samplename,32,1); + /* The correct formula for the coefficient would be + pow(2,(double)s.finetume/OCTAVE/32768), but to avoid floating point + here, we'll use a first order approximation here. + 1/567290 == Ln(2)/OCTAVE/32768 */ + q->speed=s.speed+s.speed*(((SLONG)s.speed*(SLONG)s.finetune)/567290); + q->length = s.sizeend-s.sizestart; + q->volume = s.volume>>2; + q->loopstart = s.loopstart; + q->loopend = s.loopend; + q->flags = SF_SIGNED; + if(s.flags&ULTS_LOOP) q->flags|=SF_LOOP; + if(s.flags&ULTS_16BITS) { + s.sizeend+=(s.sizeend-s.sizestart); + s.sizestart<<=1; + q->flags|=SF_16BITS; + q->loopstart>>=1; + q->loopend>>=1; + } + q++; + } + + if(!AllocPositions(256)) return 0; + for(t=0;t<256;t++) + of.positions[t]=_mm_read_UBYTE(modreader); + for(t=0;t<256;t++) + if(of.positions[t]==255) { + of.positions[t]=LAST_PATTERN; + break; + } + of.numpos=t; + + noc=_mm_read_UBYTE(modreader); + nopa=_mm_read_UBYTE(modreader); + + of.numchn=++noc; + of.numpat=++nopa; + of.numtrk=of.numchn*of.numpat; + if(!AllocTracks()) return 0; + if(!AllocPatterns()) return 0; + for(u=0;u='3') { + for(t=0;t>4; + switch(eff) { + case 0x3: /* tone portamento */ + UniEffect(UNI_ITEFFECTG,ev.dat2); + break; + case 0x5: + break; + case 0x9: /* sample offset */ + offset=(ev.dat2<<8)|((ev.eff&0xf)==9?ev.dat1:0); + UniEffect(UNI_ULTEFFECT9,offset); + break; + case 0xb: /* panning */ + UniPTEffect(8,ev.dat2*0xf); + of.flags |= UF_PANNING; + break; + case 0xc: /* volume */ + UniPTEffect(eff,ev.dat2>>2); + break; + default: + UniPTEffect(eff,ev.dat2); + break; + } + + /* second effect */ + eff=ev.eff&0xf; + switch(eff) { + case 0x3: /* tone portamento */ + UniEffect(UNI_ITEFFECTG,ev.dat1); + break; + case 0x5: + break; + case 0x9: /* sample offset */ + if((ev.eff>>4)!=9) + UniEffect(UNI_ULTEFFECT9,((UWORD)ev.dat1)<<8); + break; + case 0xb: /* panning */ + UniPTEffect(8,ev.dat1*0xf); + of.flags |= UF_PANNING; + break; + case 0xc: /* volume */ + UniPTEffect(eff,ev.dat1>>2); + break; + default: + UniPTEffect(eff,ev.dat1); + break; + } + + UniNewline(); + row++; + } + } + if(!(of.tracks[t]=UniDup())) return 0; + } + return 1; +} + +CHAR *ULT_LoadTitle(void) +{ + CHAR s[32]; + + _mm_fseek(modreader,15,SEEK_SET); + if(!_mm_read_UBYTES(s,32,modreader)) return NULL; + + return(DupStr(s,32,1)); +} + +/*========== Loader information */ + +MIKMODAPI MLOADER load_ult={ + NULL, + "ULT", + "ULT (UltraTracker)", + ULT_Init, + ULT_Test, + ULT_Load, + ULT_Cleanup, + ULT_LoadTitle +}; + + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/loaders/load_uni.c b/apps/plugins/mikmod/loaders/load_uni.c new file mode 100644 index 0000000..ae33671 --- /dev/null +++ b/apps/plugins/mikmod/loaders/load_uni.c @@ -0,0 +1,718 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file + AUTHORS for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: load_uni.c,v 1.2 2004/02/06 19:29:03 raph Exp $ + + UNIMOD (libmikmod's and APlayer's internal module format) loader + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Module structure */ + +typedef struct UNIHEADER { + CHAR id[4]; + UBYTE numchn; + UWORD numpos; + UWORD reppos; + UWORD numpat; + UWORD numtrk; + UWORD numins; + UWORD numsmp; + UBYTE initspeed; + UBYTE inittempo; + UBYTE initvolume; + UWORD flags; + UBYTE numvoices; + UWORD bpmlimit; + + UBYTE positions[256]; + UBYTE panning[32]; +} UNIHEADER; + +typedef struct UNISMP05 { + UWORD c2spd; + UWORD transpose; + UBYTE volume; + UBYTE panning; + ULONG length; + ULONG loopstart; + ULONG loopend; + UWORD flags; + CHAR* samplename; + UBYTE vibtype; + UBYTE vibsweep; + UBYTE vibdepth; + UBYTE vibrate; +} UNISMP05; + +/*========== Loader variables */ + +static UWORD universion; +static UNIHEADER mh; + +#define UNI_SMPINCR 64 +static UNISMP05 *wh=NULL,*s=NULL; + +/*========== Loader code */ + +static char* readstring(void) +{ + char *s=NULL; + UWORD len; + + len=_mm_read_I_UWORD(modreader); + if(len) { + s=_mm_malloc(len+1); + _mm_read_UBYTES(s,len,modreader); + s[len]=0; + } + return s; +} + +BOOL UNI_Test(void) +{ + char id[6]; + + if(!_mm_read_UBYTES(id,6,modreader)) return 0; + + /* UNIMod created by MikCvt */ + if(!(memcmp(id,"UN0",3))) { + if((id[3]>='4')&&(id[3]<='6')) return 1; + } + /* UNIMod created by APlayer */ + if(!(memcmp(id,"APUN\01",5))) { + if((id[5]>=1)&&(id[5]<=6)) return 1; + } + return 0; +} + +BOOL UNI_Init(void) +{ + return 1; +} + +void UNI_Cleanup(void) +{ + _mm_free(wh); + s=NULL; +} + +static UBYTE* readtrack(void) +{ + UBYTE *t; + UWORD len; + int cur=0,chunk; + + if(universion>=6) + len=_mm_read_M_UWORD(modreader); + else + len=_mm_read_I_UWORD(modreader); + + if(!len) return NULL; + if(!(t=_mm_malloc(len))) return NULL; + _mm_read_UBYTES(t,len,modreader); + + /* Check if the track is correct */ + while(1) { + chunk=t[cur++]; + if(!chunk) break; + chunk=(chunk&0x1f)-1; + while(chunk>0) { + int opcode,oplen; + + if(cur>=len) { + free(t); + return NULL; + } + opcode=t[cur]; + + /* Remap opcodes */ + if (universion <= 5) { + if (opcode > 29) { + free(t); + return NULL; + } + switch (opcode) { + /* UNI_NOTE .. UNI_S3MEFFECTQ are the same */ + case 25: + opcode = UNI_S3MEFFECTT; + break; + case 26: + opcode = UNI_XMEFFECTA; + break; + case 27: + opcode = UNI_XMEFFECTG; + break; + case 28: + opcode = UNI_XMEFFECTH; + break; + case 29: + opcode = UNI_XMEFFECTP; + break; + } + } else { + /* APlayer < 1.05 does not have XMEFFECT6 */ + if (opcode >= UNI_XMEFFECT6 && universion < 0x105) + opcode++; + /* APlayer < 1.03 does not have ITEFFECTT */ + if (opcode >= UNI_ITEFFECTT && universion < 0x103) + opcode++; + /* APlayer < 1.02 does not have ITEFFECTZ */ + if (opcode >= UNI_ITEFFECTZ && universion < 0x102) + opcode++; + } + + if((!opcode)||(opcode>=UNI_LAST)) { + free(t); + return NULL; + } + t[cur]=opcode; + oplen=unioperands[opcode]+1; + cur+=oplen; + chunk-=oplen; + } + if((chunk<0)||(cur>=len)) { + free(t); + return NULL; + } + } + return t; +} + +static BOOL loadsmp6(void) +{ + int t; + SAMPLE *s; + + s=of.samples; + for(t=0;tflags=0; + if(flags&0x0004) s->flags|=SF_STEREO; + if(flags&0x0002) s->flags|=SF_SIGNED; + if(flags&0x0001) s->flags|=SF_16BITS; + /* convert flags */ + if(universion>=0x104) { + if(flags&0x2000) s->flags|=SF_UST_LOOP; + if(flags&0x1000) s->flags|=SF_OWNPAN; + if(flags&0x0800) s->flags|=SF_SUSTAIN; + if(flags&0x0400) s->flags|=SF_REVERSE; + if(flags&0x0200) s->flags|=SF_BIDI; + if(flags&0x0100) s->flags|=SF_LOOP; + if(flags&0x0020) s->flags|=SF_ITPACKED; + if(flags&0x0010) s->flags|=SF_DELTA; + if(flags&0x0008) s->flags|=SF_BIG_ENDIAN; + } else if(universion>=0x102) { + if(flags&0x0800) s->flags|=SF_UST_LOOP; + if(flags&0x0400) s->flags|=SF_OWNPAN; + if(flags&0x0200) s->flags|=SF_SUSTAIN; + if(flags&0x0100) s->flags|=SF_REVERSE; + if(flags&0x0080) s->flags|=SF_BIDI; + if(flags&0x0040) s->flags|=SF_LOOP; + if(flags&0x0020) s->flags|=SF_ITPACKED; + if(flags&0x0010) s->flags|=SF_DELTA; + if(flags&0x0008) s->flags|=SF_BIG_ENDIAN; + } else { + if(flags&0x400) s->flags|=SF_UST_LOOP; + if(flags&0x200) s->flags|=SF_OWNPAN; + if(flags&0x100) s->flags|=SF_REVERSE; + if(flags&0x080) s->flags|=SF_SUSTAIN; + if(flags&0x040) s->flags|=SF_BIDI; + if(flags&0x020) s->flags|=SF_LOOP; + if(flags&0x010) s->flags|=SF_BIG_ENDIAN; + if(flags&0x008) s->flags|=SF_DELTA; + } + + s->speed = _mm_read_M_ULONG(modreader); + s->volume = _mm_read_UBYTE(modreader); + s->panning = _mm_read_M_UWORD(modreader); + s->length = _mm_read_M_ULONG(modreader); + s->loopstart = _mm_read_M_ULONG(modreader); + s->loopend = _mm_read_M_ULONG(modreader); + s->susbegin = _mm_read_M_ULONG(modreader); + s->susend = _mm_read_M_ULONG(modreader); + s->globvol = _mm_read_UBYTE(modreader); + s->vibflags = _mm_read_UBYTE(modreader); + s->vibtype = _mm_read_UBYTE(modreader); + s->vibsweep = _mm_read_UBYTE(modreader); + s->vibdepth = _mm_read_UBYTE(modreader); + s->vibrate = _mm_read_UBYTE(modreader); + + s->samplename=readstring(); + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + } + return 1; +} + +static BOOL loadinstr6(void) +{ + int t,w; + INSTRUMENT *i; + + i=of.instruments; + for(t=0;tflags = _mm_read_UBYTE(modreader); + i->nnatype = _mm_read_UBYTE(modreader); + i->dca = _mm_read_UBYTE(modreader); + i->dct = _mm_read_UBYTE(modreader); + i->globvol = _mm_read_UBYTE(modreader); + i->panning = _mm_read_M_UWORD(modreader); + i->pitpansep = _mm_read_UBYTE(modreader); + i->pitpancenter = _mm_read_UBYTE(modreader); + i->rvolvar = _mm_read_UBYTE(modreader); + i->rpanvar = _mm_read_UBYTE(modreader); + i->volfade = _mm_read_M_UWORD(modreader); + +#if defined __STDC__ || defined _MSC_VER || defined MPW_C +#define UNI_LoadEnvelope6(name) \ + i-> name##flg=_mm_read_UBYTE(modreader); \ + i-> name##pts=_mm_read_UBYTE(modreader); \ + i-> name##susbeg=_mm_read_UBYTE(modreader); \ + i-> name##susend=_mm_read_UBYTE(modreader); \ + i-> name##beg=_mm_read_UBYTE(modreader); \ + i-> name##end=_mm_read_UBYTE(modreader); \ + for(w=0;w<(universion>=0x100?32:i-> name##pts);w++) { \ + i-> name##env[w].pos=_mm_read_M_SWORD(modreader); \ + i-> name##env[w].val=_mm_read_M_SWORD(modreader); \ + } +#else +#define UNI_LoadEnvelope6(name) \ + i-> name/**/flg=_mm_read_UBYTE(modreader); \ + i-> name/**/pts=_mm_read_UBYTE(modreader); \ + i-> name/**/susbeg=_mm_read_UBYTE(modreader); \ + i-> name/**/susend=_mm_read_UBYTE(modreader); \ + i-> name/**/beg=_mm_read_UBYTE(modreader); \ + i-> name/**/end=_mm_read_UBYTE(modreader); \ + for (w=0;w<(universion>=0x100?32:i-> name/**/pts);w++) { \ + i-> name/**/env[w].pos=_mm_read_M_SWORD(modreader); \ + i-> name/**/env[w].val=_mm_read_M_SWORD(modreader); \ + } +#endif + + UNI_LoadEnvelope6(vol); + UNI_LoadEnvelope6(pan); + UNI_LoadEnvelope6(pit); +#undef UNI_LoadEnvelope6 + + if(universion>=0x103) + _mm_read_M_UWORDS(i->samplenumber,120,modreader); + else + for(w=0;w<120;w++) + i->samplenumber[w]=_mm_read_UBYTE(modreader); + _mm_read_UBYTES(i->samplenote,120,modreader); + + i->insname=readstring(); + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + } + return 1; +} + +static BOOL loadinstr5(void) +{ + INSTRUMENT *i; + int t; + UWORD wavcnt=0; + UBYTE vibtype,vibsweep,vibdepth,vibrate; + + i=of.instruments; + for(of.numsmp=t=0;tsamplenumber,0xff,INSTNOTES*sizeof(UWORD)); + for(u=0;u<96;u++) + i->samplenumber[u]=of.numsmp+_mm_read_UBYTE(modreader); + +#if defined __STDC__ || defined _MSC_VER || defined MPW_C +#define UNI_LoadEnvelope5(name) \ + i-> name##flg=_mm_read_UBYTE(modreader); \ + i-> name##pts=_mm_read_UBYTE(modreader); \ + i-> name##susbeg=_mm_read_UBYTE(modreader); \ + i-> name##susend=i-> name##susbeg; \ + i-> name##beg=_mm_read_UBYTE(modreader); \ + i-> name##end=_mm_read_UBYTE(modreader); \ + for(u=0;u<12;u++) { \ + i-> name##env[u].pos=_mm_read_I_SWORD(modreader); \ + i-> name##env[u].val=_mm_read_I_SWORD(modreader); \ + } +#else +#define UNI_LoadEnvelope5(name) \ + i-> name/**/flg=_mm_read_UBYTE(modreader); \ + i-> name/**/pts=_mm_read_UBYTE(modreader); \ + i-> name/**/susbeg=_mm_read_UBYTE(modreader); \ + i-> name/**/susend=i-> name/**/susbeg; \ + i-> name/**/beg=_mm_read_UBYTE(modreader); \ + i-> name/**/end=_mm_read_UBYTE(modreader); \ + for(u=0;u<12;u++) { \ + i-> name/**/env[u].pos=_mm_read_I_SWORD(modreader); \ + i-> name/**/env[u].val=_mm_read_I_SWORD(modreader); \ + } +#endif + + UNI_LoadEnvelope5(vol); + UNI_LoadEnvelope5(pan); +#undef UNI_LoadEnvelope5 + + vibtype =_mm_read_UBYTE(modreader); + vibsweep =_mm_read_UBYTE(modreader); + vibdepth =_mm_read_UBYTE(modreader); + vibrate =_mm_read_UBYTE(modreader); + + i->volfade=_mm_read_I_UWORD(modreader); + i->insname=readstring(); + + for(u=0;uc2spd =_mm_read_I_UWORD(modreader); + s->transpose=_mm_read_SBYTE(modreader); + s->volume =_mm_read_UBYTE(modreader); + s->panning =_mm_read_UBYTE(modreader); + s->length =_mm_read_I_ULONG(modreader); + s->loopstart=_mm_read_I_ULONG(modreader); + s->loopend =_mm_read_I_ULONG(modreader); + s->flags =_mm_read_I_UWORD(modreader); + s->samplename=readstring(); + + s->vibtype =vibtype; + s->vibsweep=vibsweep; + s->vibdepth=vibdepth; + s->vibrate =vibrate; + + if(_mm_eof(modreader)) { + free(wh);wh=NULL; + _mm_errno=MMERR_LOADING_SAMPLEINFO; + return 0; + } + } + } + + /* sanity check */ + if(!of.numsmp) { + if(wh) { free(wh);wh=NULL; } + _mm_errno=MMERR_LOADING_SAMPLEINFO; + return 0; + } + return 1; +} + +static BOOL loadsmp5(void) +{ + int t,u; + SAMPLE *q; + INSTRUMENT *d; + + q=of.samples;s=wh; + for(u=0;usamplename=s->samplename; + + q->length =s->length; + q->loopstart=s->loopstart; + q->loopend =s->loopend; + q->volume =s->volume; + q->speed =s->c2spd; + q->panning =s->panning; + q->vibtype =s->vibtype; + q->vibsweep =s->vibsweep; + q->vibdepth =s->vibdepth; + q->vibrate =s->vibrate; + + /* convert flags */ + q->flags=0; + if(s->flags&128) q->flags|=SF_REVERSE; + if(s->flags& 64) q->flags|=SF_SUSTAIN; + if(s->flags& 32) q->flags|=SF_BIDI; + if(s->flags& 16) q->flags|=SF_LOOP; + if(s->flags& 8) q->flags|=SF_BIG_ENDIAN; + if(s->flags& 4) q->flags|=SF_DELTA; + if(s->flags& 2) q->flags|=SF_SIGNED; + if(s->flags& 1) q->flags|=SF_16BITS; + } + + d=of.instruments;s=wh; + for(u=0;usamplenote[t]=(d->samplenumber[t]>=of.numsmp)? + 255:(t+s[d->samplenumber[t]].transpose); + + free(wh);wh=NULL; + + return 1; +} + +BOOL UNI_Load(BOOL curious) +{ + int t; + char *modtype,*oldtype=NULL; + INSTRUMENT *d; + SAMPLE *q; + (void)curious; + + /* read module header */ + _mm_read_UBYTES(mh.id,4,modreader); + if(mh.id[3]!='N') + universion=mh.id[3]-'0'; + else + universion=0x100; + + if(universion>=6) { + if (universion==6) + _mm_read_UBYTE(modreader); + else + universion=_mm_read_M_UWORD(modreader); + + mh.flags =_mm_read_M_UWORD(modreader); + mh.numchn =_mm_read_UBYTE(modreader); + mh.numvoices =_mm_read_UBYTE(modreader); + mh.numpos =_mm_read_M_UWORD(modreader); + mh.numpat =_mm_read_M_UWORD(modreader); + mh.numtrk =_mm_read_M_UWORD(modreader); + mh.numins =_mm_read_M_UWORD(modreader); + mh.numsmp =_mm_read_M_UWORD(modreader); + mh.reppos =_mm_read_M_UWORD(modreader); + mh.initspeed =_mm_read_UBYTE(modreader); + mh.inittempo =_mm_read_UBYTE(modreader); + mh.initvolume=_mm_read_UBYTE(modreader); + /* I expect this to show up soon in APlayer 1.06 format */ + if (universion >= 0x106) + mh.bpmlimit=_mm_read_M_UWORD(modreader); + else + mh.bpmlimit=32; + + mh.flags &= UF_XMPERIODS | UF_LINEAR | UF_INST | UF_NNA; + mh.flags |= UF_PANNING; + } else { + mh.numchn =_mm_read_UBYTE(modreader); + mh.numpos =_mm_read_I_UWORD(modreader); + mh.reppos =(universion==5)?_mm_read_I_UWORD(modreader):0; + mh.numpat =_mm_read_I_UWORD(modreader); + mh.numtrk =_mm_read_I_UWORD(modreader); + mh.numins =_mm_read_I_UWORD(modreader); + mh.initspeed =_mm_read_UBYTE(modreader); + mh.inittempo =_mm_read_UBYTE(modreader); + _mm_read_UBYTES(mh.positions,256,modreader); + _mm_read_UBYTES(mh.panning,32,modreader); + mh.flags =_mm_read_UBYTE(modreader); + mh.bpmlimit =32; + + mh.flags &= UF_XMPERIODS | UF_LINEAR; + mh.flags |= UF_INST | UF_NOWRAP | UF_PANNING; + } + + /* set module parameters */ + of.flags =mh.flags; + of.numchn =mh.numchn; + of.numpos =mh.numpos; + of.numpat =mh.numpat; + of.numtrk =mh.numtrk; + of.numins =mh.numins; + of.reppos =mh.reppos; + of.initspeed =mh.initspeed; + of.inittempo =mh.inittempo; + if(mh.bpmlimit) + of.bpmlimit=mh.bpmlimit; + else + /* be bug-compatible with older releases */ + of.bpmlimit=32; + + of.songname=readstring(); + if(universion<0x102) + oldtype=readstring(); + if(oldtype) { + int len=strlen(oldtype)+20; + if(!(modtype=_mm_malloc(len))) return 0; +#ifdef HAVE_SNPRINTF + snprintf(modtype,len,"%s (was %s)",(universion>=0x100)?"APlayer":"MikCvt2",oldtype); +#else + sprintf(modtype,"%s (was %s)",(universion>=0x100)?"APlayer":"MikCvt2",oldtype); +#endif + } else { + if(!(modtype=_mm_malloc(10))) return 0; +#ifdef HAVE_SNPRINTF + snprintf(modtype,10,"%s",(universion>=0x100)?"APlayer":"MikCvt3"); +#else + sprintf(modtype,"%s",(universion>=0x100)?"APlayer":"MikCvt3"); +#endif + } + of.modtype=strdup(modtype); + free(modtype);free(oldtype); + of.comment=readstring(); + + if(universion>=6) { + of.numvoices=mh.numvoices; + of.initvolume=mh.initvolume; + } + + if(_mm_eof(modreader)) { + _mm_errno=MMERR_LOADING_HEADER; + return 0; + } + + /* positions */ + if(!AllocPositions(of.numpos)) return 0; + if(universion>=6) { + if(universion>=0x100) + _mm_read_M_UWORDS(of.positions,of.numpos,modreader); + else + for(t=0;t256)||(mh.numchn>32)) { + _mm_errno=MMERR_LOADING_HEADER; + return 0; + } + for(t=0;t=6) { + of.numsmp=mh.numsmp; + if(!AllocSamples()) return 0; + if(!loadsmp6()) return 0; + + if(of.flags&UF_INST) { + if(!AllocInstruments()) return 0; + if(!loadinstr6()) return 0; + } + } else { + if(!AllocInstruments()) return 0; + if(!loadinstr5()) return 0; + if(!AllocSamples()) { + if(wh) { free(wh);wh=NULL; } + return 0; + } + if(!loadsmp5()) return 0; + + /* check if the original file had no instruments */ + if(of.numsmp==of.numins) { + for(t=0,d=of.instruments;tvolpts)||(d->panpts)||(d->globvol!=64)) break; + for(u=0;u<96;u++) + if((d->samplenumber[u]!=t)||(d->samplenote[u]!=u)) break; + if(u!=96) break; + } + if(t==of.numins) { + of.flags&=~UF_INST; + of.flags&=~UF_NOWRAP; + for(t=0,d=of.instruments,q=of.samples;tsamplename=d->insname; + d->insname=NULL; + } + } + } + } + + /* patterns */ + if(!AllocPatterns()) return 0; + if(universion>=6) { + _mm_read_M_UWORDS(of.pattrows,of.numpat,modreader); + _mm_read_M_UWORDS(of.patterns,of.numpat*of.numchn,modreader); + } else { + _mm_read_I_UWORDS(of.pattrows,of.numpat,modreader); + _mm_read_I_UWORDS(of.patterns,of.numpat*of.numchn,modreader); + } + + /* tracks */ + if(!AllocTracks()) return 0; + for(t=0;t +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Module structure */ + +typedef struct XMHEADER { + CHAR id[17]; /* ID text: 'Extended module: ' */ + CHAR songname[21]; /* Module name */ + CHAR trackername[20]; /* Tracker name */ + UWORD version; /* Version number */ + ULONG headersize; /* Header size */ + UWORD songlength; /* Song length (in patten order table) */ + UWORD restart; /* Restart position */ + UWORD numchn; /* Number of channels (2,4,6,8,10,...,32) */ + UWORD numpat; /* Number of patterns (max 256) */ + UWORD numins; /* Number of instruments (max 128) */ + UWORD flags; + UWORD tempo; /* Default tempo */ + UWORD bpm; /* Default BPM */ + UBYTE orders[256]; /* Pattern order table */ +} XMHEADER; + +typedef struct XMINSTHEADER { + ULONG size; /* Instrument size */ + CHAR name[22]; /* Instrument name */ + UBYTE type; /* Instrument type (always 0) */ + UWORD numsmp; /* Number of samples in instrument */ + ULONG ssize; +} XMINSTHEADER; + +#define XMENVCNT (12*2) +#define XMNOTECNT (8*OCTAVE) +typedef struct XMPATCHHEADER { + UBYTE what[XMNOTECNT]; /* Sample number for all notes */ + UWORD volenv[XMENVCNT]; /* Points for volume envelope */ + UWORD panenv[XMENVCNT]; /* Points for panning envelope */ + UBYTE volpts; /* Number of volume points */ + UBYTE panpts; /* Number of panning points */ + UBYTE volsus; /* Volume sustain point */ + UBYTE volbeg; /* Volume loop start point */ + UBYTE volend; /* Volume loop end point */ + UBYTE pansus; /* Panning sustain point */ + UBYTE panbeg; /* Panning loop start point */ + UBYTE panend; /* Panning loop end point */ + UBYTE volflg; /* Volume type: bit 0: On; 1: Sustain; 2: Loop */ + UBYTE panflg; /* Panning type: bit 0: On; 1: Sustain; 2: Loop */ + UBYTE vibflg; /* Vibrato type */ + UBYTE vibsweep; /* Vibrato sweep */ + UBYTE vibdepth; /* Vibrato depth */ + UBYTE vibrate; /* Vibrato rate */ + UWORD volfade; /* Volume fadeout */ +} XMPATCHHEADER; + +typedef struct XMWAVHEADER { + ULONG length; /* Sample length */ + ULONG loopstart; /* Sample loop start */ + ULONG looplength; /* Sample loop length */ + UBYTE volume; /* Volume */ + SBYTE finetune; /* Finetune (signed byte -128..+127) */ + UBYTE type; /* Loop type */ + UBYTE panning; /* Panning (0-255) */ + SBYTE relnote; /* Relative note number (signed byte) */ + UBYTE reserved; + CHAR samplename[22]; /* Sample name */ + UBYTE vibtype; /* Vibrato type */ + UBYTE vibsweep; /* Vibrato sweep */ + UBYTE vibdepth; /* Vibrato depth */ + UBYTE vibrate; /* Vibrato rate */ +} XMWAVHEADER; + +typedef struct XMPATHEADER { + ULONG size; /* Pattern header length */ + UBYTE packing; /* Packing type (always 0) */ + UWORD numrows; /* Number of rows in pattern (1..256) */ + SWORD packsize; /* Packed patterndata size */ +} XMPATHEADER; + +typedef struct XMNOTE { + UBYTE note,ins,vol,eff,dat; +} XMNOTE; + +/*========== Loader variables */ + +static XMNOTE *xmpat=NULL; +static XMHEADER *mh=NULL; + +/* increment unit for sample array reallocation */ +#define XM_SMPINCR 64 +static ULONG *nextwav=NULL; +static XMWAVHEADER *wh=NULL,*s=NULL; + +/*========== Loader code */ + +BOOL XM_Test(void) +{ + UBYTE id[38]; + + if(!_mm_read_UBYTES(id,38,modreader)) return 0; + if(memcmp(id,"Extended Module: ",17)) return 0; + if(id[37]==0x1a) return 1; + return 0; +} + +BOOL XM_Init(void) +{ + if(!(mh=(XMHEADER *)_mm_malloc(sizeof(XMHEADER)))) return 0; + return 1; +} + +void XM_Cleanup(void) +{ + _mm_free(mh); +} + +static int XM_ReadNote(XMNOTE* n) +{ + UBYTE cmp,result=1; + + memset(n,0,sizeof(XMNOTE)); + cmp=_mm_read_UBYTE(modreader); + + if(cmp&0x80) { + if(cmp&1) { result++;n->note = _mm_read_UBYTE(modreader); } + if(cmp&2) { result++;n->ins = _mm_read_UBYTE(modreader); } + if(cmp&4) { result++;n->vol = _mm_read_UBYTE(modreader); } + if(cmp&8) { result++;n->eff = _mm_read_UBYTE(modreader); } + if(cmp&16) { result++;n->dat = _mm_read_UBYTE(modreader); } + } else { + n->note = cmp; + n->ins = _mm_read_UBYTE(modreader); + n->vol = _mm_read_UBYTE(modreader); + n->eff = _mm_read_UBYTE(modreader); + n->dat = _mm_read_UBYTE(modreader); + result += 4; + } + return result; +} + +static UBYTE* XM_Convert(XMNOTE* xmtrack,UWORD rows) +{ + int t; + UBYTE note,ins,vol,eff,dat; + + UniReset(); + for(t=0;tnote; + ins = xmtrack->ins; + vol = xmtrack->vol; + eff = xmtrack->eff; + dat = xmtrack->dat; + + if(note) { + if(note>XMNOTECNT) + UniEffect(UNI_KEYFADE,0); + else + UniNote(note-1); + } + if(ins) UniInstrument(ins-1); + + switch(vol>>4) { + case 0x6: /* volslide down */ + if(vol&0xf) UniEffect(UNI_XMEFFECTA,vol&0xf); + break; + case 0x7: /* volslide up */ + if(vol&0xf) UniEffect(UNI_XMEFFECTA,vol<<4); + break; + + /* volume-row fine volume slide is compatible with protracker + EBx and EAx effects i.e. a zero nibble means DO NOT SLIDE, as + opposed to 'take the last sliding value'. */ + case 0x8: /* finevol down */ + UniPTEffect(0xe,0xb0|(vol&0xf)); + break; + case 0x9: /* finevol up */ + UniPTEffect(0xe,0xa0|(vol&0xf)); + break; + case 0xa: /* set vibrato speed */ + UniEffect(UNI_XMEFFECT4,vol<<4); + break; + case 0xb: /* vibrato */ + UniEffect(UNI_XMEFFECT4,vol&0xf); + break; + case 0xc: /* set panning */ + UniPTEffect(0x8,vol<<4); + break; + case 0xd: /* panning slide left (only slide when data not zero) */ + if(vol&0xf) UniEffect(UNI_XMEFFECTP,vol&0xf); + break; + case 0xe: /* panning slide right (only slide when data not zero) */ + if(vol&0xf) UniEffect(UNI_XMEFFECTP,vol<<4); + break; + case 0xf: /* tone porta */ + UniPTEffect(0x3,vol<<4); + break; + default: + if((vol>=0x10)&&(vol<=0x50)) + UniPTEffect(0xc,vol-0x10); + } + + switch(eff) { + case 0x4: + UniEffect(UNI_XMEFFECT4,dat); + break; + case 0x6: + UniEffect(UNI_XMEFFECT6,dat); + break; + case 0xa: + UniEffect(UNI_XMEFFECTA,dat); + break; + case 0xe: /* Extended effects */ + switch(dat>>4) { + case 0x1: /* XM fine porta up */ + UniEffect(UNI_XMEFFECTE1,dat&0xf); + break; + case 0x2: /* XM fine porta down */ + UniEffect(UNI_XMEFFECTE2,dat&0xf); + break; + case 0xa: /* XM fine volume up */ + UniEffect(UNI_XMEFFECTEA,dat&0xf); + break; + case 0xb: /* XM fine volume down */ + UniEffect(UNI_XMEFFECTEB,dat&0xf); + break; + default: + UniPTEffect(eff,dat); + } + break; + case 'G'-55: /* G - set global volume */ + UniEffect(UNI_XMEFFECTG,dat>64?128:dat<<1); + break; + case 'H'-55: /* H - global volume slide */ + UniEffect(UNI_XMEFFECTH,dat); + break; + case 'K'-55: /* K - keyOff and KeyFade */ + UniEffect(UNI_KEYFADE,dat); + break; + case 'L'-55: /* L - set envelope position */ + UniEffect(UNI_XMEFFECTL,dat); + break; + case 'P'-55: /* P - panning slide */ + UniEffect(UNI_XMEFFECTP,dat); + break; + case 'R'-55: /* R - multi retrig note */ + UniEffect(UNI_S3MEFFECTQ,dat); + break; + case 'T'-55: /* T - Tremor */ + UniEffect(UNI_S3MEFFECTI,dat); + break; + case 'X'-55: + switch(dat>>4) { + case 1: /* X1 - Extra Fine Porta up */ + UniEffect(UNI_XMEFFECTX1,dat&0xf); + break; + case 2: /* X2 - Extra Fine Porta down */ + UniEffect(UNI_XMEFFECTX2,dat&0xf); + break; + } + break; + default: + if(eff<=0xf) { + /* the pattern jump destination is written in decimal, + but it seems some poor tracker software writes them + in hexadecimal... (sigh) */ + if (eff==0xd) + /* don't change anything if we're sure it's in hexa */ + if ((((dat&0xf0)>>4)<=9)&&((dat&0xf)<=9)) + /* otherwise, convert from dec to hex */ + dat=(((dat&0xf0)>>4)*10)+(dat&0xf); + UniPTEffect(eff,dat); + } + break; + } + UniNewline(); + xmtrack++; + } + return UniDup(); +} + +static BOOL LoadPatterns(BOOL dummypat) +{ + int t,u,v,numtrk; + + if(!AllocTracks()) return 0; + if(!AllocPatterns()) return 0; + + numtrk=0; + for(t=0;tnumpat;t++) { + XMPATHEADER ph; + + ph.size =_mm_read_I_ULONG(modreader); + if (ph.size<(mh->version==0x0102?8:9)) { + _mm_errno=MMERR_LOADING_PATTERN; + return 0; + } + ph.packing =_mm_read_UBYTE(modreader); + if(ph.packing) { + _mm_errno=MMERR_LOADING_PATTERN; + return 0; + } + if(mh->version==0x0102) + ph.numrows =_mm_read_UBYTE(modreader)+1; + else + ph.numrows =_mm_read_I_UWORD(modreader); + ph.packsize =_mm_read_I_UWORD(modreader); + + ph.size-=(mh->version==0x0102?8:9); + if(ph.size) + _mm_fseek(modreader,ph.size,SEEK_CUR); + + of.pattrows[t]=ph.numrows; + + if(ph.numrows) { + if(!(xmpat=(XMNOTE*)_mm_calloc(ph.numrows*of.numchn,sizeof(XMNOTE)))) + return 0; + + /* when packsize is 0, don't try to load a pattern.. it's empty. */ + if(ph.packsize) + for(u=0;upos; + + for (u = 1; u < pts; u++, prev++, cur++) { + if (cur->pos < prev->pos) { + if (cur->pos < 0x100) { + if (cur->pos > old) /* same hex century */ + tmp = cur->pos + (prev->pos - old); + else + tmp = cur->pos | ((prev->pos + 0x100) & 0xff00); + old = cur->pos; + cur->pos = tmp; +#ifdef MIKMOD_DEBUG + fprintf(stderr, "\rbroken envelope position(%d/%d), %d %d -> %d\n", + u, pts, prev->pos, old, cur->pos); +#endif + } else { +#ifdef MIKMOD_DEBUG + /* different brokenness style... fix unknown */ + fprintf(stderr, "\rbroken envelope position(%d/%d), %d %d\n", + u, pts, old, cur->pos); +#endif + old = cur->pos; + } + } else + old = cur->pos; + } +} + +static BOOL LoadInstruments(void) +{ + int t,u; + INSTRUMENT *d; + ULONG next=0; + UWORD wavcnt=0; + + if(!AllocInstruments()) return 0; + d=of.instruments; + for(t=0;tsamplenumber,0xff,INSTNOTES*sizeof(UWORD)); + + /* read instrument header */ + headend = _mm_ftell(modreader); + ih.size = _mm_read_I_ULONG(modreader); + headend += ih.size; + _mm_read_string(ih.name, 22, modreader); + ih.type = _mm_read_UBYTE(modreader); + ih.numsmp = _mm_read_I_UWORD(modreader); + + d->insname = DupStr(ih.name,22,1); + + if((SWORD)ih.size>29) { + ih.ssize = _mm_read_I_ULONG(modreader); + if(((SWORD)ih.numsmp>0)&&(ih.numsmp<=XMNOTECNT)) { + XMPATCHHEADER pth; + int p; + + _mm_read_UBYTES (pth.what,XMNOTECNT,modreader); + _mm_read_I_UWORDS (pth.volenv, XMENVCNT, modreader); + _mm_read_I_UWORDS (pth.panenv, XMENVCNT, modreader); + pth.volpts = _mm_read_UBYTE(modreader); + pth.panpts = _mm_read_UBYTE(modreader); + pth.volsus = _mm_read_UBYTE(modreader); + pth.volbeg = _mm_read_UBYTE(modreader); + pth.volend = _mm_read_UBYTE(modreader); + pth.pansus = _mm_read_UBYTE(modreader); + pth.panbeg = _mm_read_UBYTE(modreader); + pth.panend = _mm_read_UBYTE(modreader); + pth.volflg = _mm_read_UBYTE(modreader); + pth.panflg = _mm_read_UBYTE(modreader); + pth.vibflg = _mm_read_UBYTE(modreader); + pth.vibsweep = _mm_read_UBYTE(modreader); + pth.vibdepth = _mm_read_UBYTE(modreader); + pth.vibrate = _mm_read_UBYTE(modreader); + pth.volfade = _mm_read_I_UWORD(modreader); + + /* read the remainder of the header + (2 bytes for 1.03, 22 for 1.04) */ + for(u=headend-_mm_ftell(modreader);u;u--) _mm_read_UBYTE(modreader); + + /* we can't trust the envelope point count here, as some + modules have incorrect values (K_OSPACE.XM reports 32 volume + points, for example). */ + if(pth.volpts>XMENVCNT/2) pth.volpts=XMENVCNT/2; + if(pth.panpts>XMENVCNT/2) pth.panpts=XMENVCNT/2; + + if((_mm_eof(modreader))||(pth.volpts>XMENVCNT/2)||(pth.panpts>XMENVCNT/2)) { + if(nextwav) { free(nextwav);nextwav=NULL; } + if(wh) { free(wh);wh=NULL; } + _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + for(u=0;usamplenumber[u]=pth.what[u]+of.numsmp; + d->volfade = pth.volfade; + +#if defined __STDC__ || defined _MSC_VER || defined MPW_C +#define XM_ProcessEnvelope(name) \ + for (u = 0; u < (XMENVCNT >> 1); u++) { \ + d-> name##env[u].pos = pth. name##env[u << 1]; \ + d-> name##env[u].val = pth. name##env[(u << 1)+ 1]; \ + } \ + if (pth. name##flg&1) d-> name##flg|=EF_ON; \ + if (pth. name##flg&2) d-> name##flg|=EF_SUSTAIN; \ + if (pth. name##flg&4) d-> name##flg|=EF_LOOP; \ + d-> name##susbeg=d-> name##susend=pth. name##sus; \ + d-> name##beg=pth. name##beg; \ + d-> name##end=pth. name##end; \ + d-> name##pts=pth. name##pts; \ + \ + /* scale envelope */ \ + for (p=0;p name##env[p].val<<=2; \ + \ + if ((d-> name##flg&EF_ON)&&(d-> name##pts<2)) \ + d-> name##flg&=~EF_ON +#else +#define XM_ProcessEnvelope(name) \ + for (u = 0; u < (XMENVCNT >> 1); u++) { \ + d-> name/**/env[u].pos = pth. name/**/env[u << 1]; \ + d-> name/**/env[u].val = pth. name/**/env[(u << 1)+ 1]; \ + } \ + if (pth. name/**/flg&1) d-> name/**/flg|=EF_ON; \ + if (pth. name/**/flg&2) d-> name/**/flg|=EF_SUSTAIN; \ + if (pth. name/**/flg&4) d-> name/**/flg|=EF_LOOP; \ + d-> name/**/susbeg=d-> name/**/susend= \ + pth. name/**/sus; \ + d-> name/**/beg=pth. name/**/beg; \ + d-> name/**/end=pth. name/**/end; \ + d-> name/**/pts=pth. name/**/pts; \ + \ + /* scale envelope */ \ + for (p=0;p name/**/env[p].val<<=2; \ + \ + if ((d-> name/**/flg&EF_ON)&&(d-> name/**/pts<2)) \ + d-> name/**/flg&=~EF_ON +#endif + + XM_ProcessEnvelope(vol); + XM_ProcessEnvelope(pan); +#undef XM_ProcessEnvelope + + if (d->volflg & EF_ON) + FixEnvelope(d->volenv, d->volpts); + if (d->panflg & EF_ON) + FixEnvelope(d->panenv, d->panpts); + + /* Samples are stored outside the instrument struct now, so we + have to load them all into a temp area, count the of.numsmp + along the way and then do an AllocSamples() and move + everything over */ + if(mh->version>0x0103) next = 0; + for(u=0;ulength =_mm_read_I_ULONG (modreader); + s->loopstart =_mm_read_I_ULONG (modreader); + s->looplength =_mm_read_I_ULONG (modreader); + s->volume =_mm_read_UBYTE (modreader); + s->finetune =_mm_read_SBYTE (modreader); + s->type =_mm_read_UBYTE (modreader); + s->panning =_mm_read_UBYTE (modreader); + s->relnote =_mm_read_SBYTE (modreader); + s->vibtype = pth.vibflg; + s->vibsweep = pth.vibsweep; + s->vibdepth = pth.vibdepth*4; + s->vibrate = pth.vibrate; + s->reserved =_mm_read_UBYTE (modreader); + _mm_read_string(s->samplename, 22, modreader); + + nextwav[of.numsmp+u]=next; + next+=s->length; + + if(_mm_eof(modreader)) { + free(nextwav);free(wh); + nextwav=NULL;wh=NULL; + _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + } + + if(mh->version>0x0103) { + for(u=0;uid,17,modreader); + _mm_read_string(mh->songname,21,modreader); + _mm_read_string(mh->trackername,20,modreader); + mh->version =_mm_read_I_UWORD(modreader); + if((mh->version<0x102)||(mh->version>0x104)) { + _mm_errno=MMERR_NOT_A_MODULE; + return 0; + } + mh->headersize =_mm_read_I_ULONG(modreader); + mh->songlength =_mm_read_I_UWORD(modreader); + mh->restart =_mm_read_I_UWORD(modreader); + mh->numchn =_mm_read_I_UWORD(modreader); + mh->numpat =_mm_read_I_UWORD(modreader); + mh->numins =_mm_read_I_UWORD(modreader); + mh->flags =_mm_read_I_UWORD(modreader); + mh->tempo =_mm_read_I_UWORD(modreader); + mh->bpm =_mm_read_I_UWORD(modreader); + if(!mh->bpm) { + _mm_errno=MMERR_NOT_A_MODULE; + return 0; + } + _mm_read_UBYTES(mh->orders,256,modreader); + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* set module variables */ + of.initspeed = mh->tempo; + of.inittempo = mh->bpm; + strncpy(tracker,mh->trackername,20);tracker[20]=0; + for(t=20;(tracker[t]<=' ')&&(t>=0);t--) tracker[t]=0; + + /* some modules have the tracker name empty */ + if (!tracker[0]) + strcpy(tracker,"Unknown tracker"); + +#ifdef HAVE_SNPRINTF + snprintf(modtype,60,"%s (XM format %d.%02d)", + tracker,mh->version>>8,mh->version&0xff); +#else + sprintf(modtype,"%s (XM format %d.%02d)", + tracker,mh->version>>8,mh->version&0xff); +#endif + of.modtype = strdup(modtype); + of.numchn = mh->numchn; + of.numpat = mh->numpat; + of.numtrk = (UWORD)of.numpat*of.numchn; /* get number of channels */ + of.songname = DupStr(mh->songname,20,1); + of.numpos = mh->songlength; /* copy the songlength */ + of.reppos = mh->restartsonglength?mh->restart:0; + of.numins = mh->numins; + of.flags |= UF_XMPERIODS | UF_INST | UF_NOWRAP | UF_FT2QUIRKS | + UF_PANNING; + if(mh->flags&1) of.flags |= UF_LINEAR; + of.bpmlimit = 32; + + memset(of.chanvol,64,of.numchn); /* store channel volumes */ + + if(!AllocPositions(of.numpos+1)) return 0; + for(t=0;torders[t]; + + /* We have to check for any pattern numbers in the order list greater than + the number of patterns total. If one or more is found, we set it equal to + the pattern total and make a dummy pattern to workaround the problem */ + for(t=0;t=of.numpat) { + of.positions[t]=of.numpat; + dummypat=1; + } + } + if(dummypat) { + of.numpat++;of.numtrk+=of.numchn; + } + + if(mh->version<0x0104) { + if(!LoadInstruments()) return 0; + if(!LoadPatterns(dummypat)) return 0; + for(t=0;tsamplename = DupStr(s->samplename,22,1); + q->length = s->length; + q->loopstart = s->loopstart; + q->loopend = s->loopstart+s->looplength; + q->volume = s->volume; + q->speed = s->finetune+128; + q->panning = s->panning; + q->seekpos = nextwav[u]; + q->vibtype = s->vibtype; + q->vibsweep = s->vibsweep; + q->vibdepth = s->vibdepth; + q->vibrate = s->vibrate; + + if(s->type & 0x10) { + q->length >>= 1; + q->loopstart >>= 1; + q->loopend >>= 1; + } + + q->flags|=SF_OWNPAN|SF_DELTA|SF_SIGNED; + if(s->type&0x3) q->flags|=SF_LOOP; + if(s->type&0x2) q->flags|=SF_BIDI; + if(s->type&0x10) q->flags|=SF_16BITS; + } + + d=of.instruments; + s=wh; + for(u=0;usamplenumber[t]>=of.numsmp) + d->samplenote[t]=255; + else { + int note=t+s[d->samplenumber[t]].relnote; + d->samplenote[t]=(note<0)?0:note; + } + } + + free(wh);free(nextwav); + wh=NULL;nextwav=NULL; + return 1; +} + +CHAR *XM_LoadTitle(void) +{ + CHAR s[21]; + + _mm_fseek(modreader,17,SEEK_SET); + if(!_mm_read_UBYTES(s,21,modreader)) return NULL; + + return(DupStr(s,21,1)); +} + +/*========== Loader information */ + +MIKMODAPI MLOADER load_xm={ + NULL, + "XM", + "XM (FastTracker 2)", + XM_Init, + XM_Test, + XM_Load, + XM_Cleanup, + XM_LoadTitle +}; + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/mikmod.c b/apps/plugins/mikmod/mikmod.c new file mode 100644 index 0000000..3ec91bd --- /dev/null +++ b/apps/plugins/mikmod/mikmod.c @@ -0,0 +1,348 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "plugin.h" +#include "mikmod_build.h" + +PLUGIN_HEADER + +struct plugin_api *rb; + +MODULE *module; + +#define MAXPCMBUFFERS 16 // Do NOT set this to a value lower than 2 or playback will break +#define PCMBUFFERSIZE 65536 + +char *pcmbufs[MAXPCMBUFFERS]; +int bufferlen[MAXPCMBUFFERS]; +int buffercount = 0; + +int curbuff = 0; +int lastbuff = 0; + +size_t buffrendered = 0; +size_t buffplayed = 0; +size_t buff_last = ~0; + +int vol, minvol, maxvol; +int lastpower = 0, lastvol = 0; +int lastbutton = 0; + +char sbuf[32]; + +int mallocbufsize; +char *mbuf; + + +void mikmod_prepare_malloc(char *buff, int bufsize); +long mikmod_get_malloc_usage(void); +void* mikmod_malloc(size_t size); + + + +int FindLastOccurrenceChar(char *string, char chr) +{ + int i = 0; + //int LastOcc = -1; + int LastOcc = 0; + + while (string[i] != 0) + { + if (string[i] == chr) + { + LastOcc = i; + } + + i++; + } + + return LastOcc; +} + + +void get_more(unsigned char **start, size_t *size) + { + if (curbuff >= (buffercount - 1)) + { + curbuff = 0; + } + else + { + curbuff++; + } + *start = pcmbufs[curbuff]; + *size = bufferlen[curbuff]; + buffplayed++; + } + + +void checkbutton(void) +{ + lastbutton = rb->button_get(false); + if (lastbutton & BUTTON_MENU) + { + return; + } + + switch (lastbutton) + { + case BUTTON_SCROLL_FWD: + case (BUTTON_SCROLL_FWD | BUTTON_REPEAT): + vol = rb->global_settings->volume; + + if (vol < maxvol) + { + vol++; + rb->sound_set(SOUND_VOLUME, vol); + rb->global_settings->volume = vol; + } + break; + + case BUTTON_SCROLL_BACK: + case (BUTTON_SCROLL_BACK | BUTTON_REPEAT): + vol = rb->global_settings->volume; + + if (vol > minvol) + { + vol--; + rb->sound_set(SOUND_VOLUME, vol); + rb->global_settings->volume = vol; + } + break; + + case (BUTTON_PLAY | BUTTON_REL): + +#ifdef HAVE_ADJUSTABLE_CPU_FREQ + rb->cpu_boost(false); // Just in case we got called from inside the render loop +#endif + rb->pcm_play_pause(false); + do /* code from mpegplayer, *very* simple unpause loop */ + { + lastbutton = rb->button_get(true); + if (lastbutton == BUTTON_MENU) + { + return; + } + } while (lastbutton != (BUTTON_PLAY | BUTTON_REL)); + rb->pcm_play_pause(true); + break; + + default: + if(rb->default_event_handler(lastbutton) == SYS_USB_CONNECTED) + { + return; + } + } /* switch(button) */ + + if ((rb->global_settings->volume != lastvol) || (rb->battery_level() != lastpower)) + { + lastpower = rb->battery_level(); + lastvol = rb->global_settings->volume; + + rb->snprintf(sbuf, sizeof(sbuf) - 1, "%d dB ", rb->global_settings->volume); + rb->lcd_puts(5, 3, sbuf); + rb->snprintf(sbuf, sizeof(sbuf) - 1, "%d %% ", rb->battery_level()); + rb->lcd_puts(5, 4, sbuf); + + rb->lcd_update(); + } + lastbutton = 0; +} + + +void mainloop(void) +{ + int i; + + minvol = rb->sound_min(SOUND_VOLUME); + maxvol = rb->sound_max(SOUND_VOLUME); + + while (buffplayed <= buff_last && !lastbutton) + { + while (curbuff != lastbuff && !lastbutton) // Render loop, fills up all buffers except the one being played + { + +#ifdef HAVE_ADJUSTABLE_CPU_FREQ + rb->cpu_boost(true); +#endif + + checkbutton(); + + if (Player_Active()) + { + bufferlen[lastbuff] = VC_WriteBytes(pcmbufs[lastbuff], PCMBUFFERSIZE); + buffrendered++; + } + else + { + // No more data to render (songend) - clear the next buffer so get_more() can return it a last time + for (i = 0; i < PCMBUFFERSIZE; i++) + (pcmbufs[lastbuff])[i] = 0; + bufferlen[lastbuff] = PCMBUFFERSIZE; + buff_last = buffrendered; + } + + if (lastbuff >= (buffercount - 1)) + lastbuff = 0; + else + lastbuff++; + + checkbutton(); + rb->reset_poweroff_timer(); + +#ifdef HAVE_ADJUSTABLE_CPU_FREQ + rb->cpu_boost(false); +#endif + + } + + checkbutton(); + + rb->yield(); + + } /* while */ + +} /* mainloop */ + + + + +enum plugin_status plugin_start(struct plugin_api *api, void *parameter) +{ + bool talk_menu; + int i; + + rb = api; + + /* Have to shut up voice menus or it will mess up our waveform playback */ + talk_menu = rb->global_settings->talk_menu; + rb->global_settings->talk_menu = false; + + rb->audio_stop(); + rb->pcm_play_stop(); + rb->pcm_set_frequency(44100); + + // Initialize internal semi-dynamic memory + mbuf = rb->plugin_get_audio_buffer(&mallocbufsize); + mikmod_prepare_malloc(mbuf, mallocbufsize); + + + // Allocate PCM output buffers + for (i = 0; i < MAXPCMBUFFERS; i++) + { + if ((pcmbufs[i] = mikmod_malloc(PCMBUFFERSIZE)) == NULL) + { + break; + } + } + buffercount = i; + //lastbuff = buffercount - 1; + // Prefill buffers ! + + + // General output... + rb->lcd_clear_display(); + rb->lcd_puts(0, 0, " MikMod for Rockbox 0.1"); + rb->lcd_puts(0, 1, "========================"); + + rb->lcd_puts(0, 3, "Vol: -"); + rb->lcd_puts(0, 4, "Pwr: -"); + rb->lcd_puts(0, 5, "---------"); + rb->lcd_puts(0, 6, "File:"); + rb->lcd_puts(7, 6, &((char*)parameter)[FindLastOccurrenceChar(parameter, '/') + 1]); + + rb->lcd_update(); + + //initialize the library + MikMod_RegisterAllLoaders(); + md_mode = DMODE_SOFT_MUSIC | DMODE_16BITS | DMODE_STEREO | DMODE_INTERP | DMODE_SOFT_SNDFX; + + if (!MikMod_Init("")) + { + #ifdef HAVE_ADJUSTABLE_CPU_FREQ + rb->cpu_boost(true); + #endif + //load module + module = Player_Load(parameter, 32, 1); + + #ifdef HAVE_ADJUSTABLE_CPU_FREQ + rb->cpu_boost(false); + #endif + + if (module) + { + Player_Start(module); +#ifndef SIMULATOR + rb->ata_sleep(); +#endif + + rb->snprintf(sbuf, sizeof(sbuf) - 1, "Memory: %d / %d", (int)(mikmod_get_malloc_usage() - (buffercount * PCMBUFFERSIZE)), mallocbufsize); + rb->lcd_puts(0, 16, sbuf); + + rb->lcd_puts(0, 7, "Type:"); + rb->lcd_puts(7, 7, module->modtype); + + rb->lcd_puts(0, 9, "Title:"); + rb->lcd_puts_scroll(7, 9, module->songname); + + rb->lcd_update(); + + // Only prefill the first buffer so we get a quick start... + bufferlen[0] = VC_WriteBytes(pcmbufs[0], PCMBUFFERSIZE); + + curbuff = buffercount - 1; + lastbuff = 1; // Fixes skip during first buffers played + + rb->pcm_play_data(get_more, NULL, 0); + + mainloop(); + + } + else /* module */ + { + rb->splash(HZ*2, "Could not load module, reason:"); + rb->splash(HZ*2, MikMod_strerror(MikMod_errno)); + } + } + else /* MikMod_Init */ + { + rb->splash(HZ*2, "Could not initialize sound, reason:"); + rb->splash(HZ*2, MikMod_strerror(MikMod_errno)); + } + + + #ifdef HAVE_ADJUSTABLE_CPU_FREQ + rb->cpu_boost(false); + #endif + + rb->pcm_play_stop(); + + Player_Stop(); + Player_Free(module); + MikMod_Exit(); + + + //restore default - user of apis is responsible for restoring + // default state - normally playback at 44100Hz + rb->pcm_set_frequency(HW_SAMPR_DEFAULT); + rb->global_settings->talk_menu = talk_menu; + +#ifdef HAVE_ADJUSTABLE_CPU_FREQ + rb->cpu_boost(false); +#endif + + return PLUGIN_OK; +} diff --git a/apps/plugins/mikmod/mmio/mmalloc.c b/apps/plugins/mikmod/mmio/mmalloc.c new file mode 100644 index 0000000..7f7fdbf --- /dev/null +++ b/apps/plugins/mikmod/mmio/mmalloc.c @@ -0,0 +1,259 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: mmalloc.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $ + + Dynamic memory routines + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mikmod_internals.h" + + +size_t mem_offset = 0; +char *mallocbuf = NULL; +size_t mallocbuflen = 0; + + +size_t mikmod_get_mbuffsize(size_t *buff) +{ + return buff[-1]; +} + + +void mikmod_prepare_malloc(char *buff, int bufsize) +{ + mallocbuf = buff; + mallocbuflen = bufsize; + mem_offset = 0; +} + +long mikmod_get_malloc_usage(void) +{ + return mem_offset; +} + +int mikmod_abs(int num) +{ + if (num < 0) + return (num * -1); + else + return (num); +} + +void* mikmod_memset(char *buf, int val, size_t count) +{ + size_t i; + + for (i = 0; i < count; i++) + { + buf[i] = val; + } + return buf; +} + +/* 'Poor man's malloc' taken from rockbox codeclib.c */ +void* mikmod_malloc(size_t size) +{ + void* x; + + if (!mallocbuf) + return NULL; + if (mem_offset + (long)size + 4 > mallocbuflen) + return NULL; + + mem_offset += 4; + + x = &mallocbuf[mem_offset]; + ((size_t*)x)[-1] = size; + + mem_offset += (size + 3) & ~3; /* Keep memory 32-bit aligned */ + + + memset(x, 0, size); + + return(x); +} + +void* mikmod_calloc(size_t nmemb, size_t size) +{ + void *x; + x = mikmod_malloc(nmemb * size); + if (x == NULL) + return NULL; + //memset(x, 0, nmemb*size); /* already done in mikmod_malloc() */ + return(x); +} + +void mikmod_free(void* ptr) { + (void)ptr; +} + +void* mikmod_realloc(void* ptr, size_t size) +{ + void* x; + //(void)ptr; + x = mikmod_malloc(size); + if (ptr != NULL) + { + if (mikmod_get_mbuffsize(ptr) < size) + { + memcpy(x, ptr, mikmod_get_mbuffsize(ptr)); + } + else + { + memcpy(x, ptr, size); + } + } + return(x); +} + +unsigned int mikmod_strlen(const char *string) +{ + unsigned int i; + for (i = 0; string[i] != 0; i++); + return i; +} + +char* mikmod_strdup(const char *srcbuf) +{ + char *newbuf; + unsigned int i, len; + + len = mikmod_strlen(srcbuf); + newbuf = mikmod_malloc(len + 1); + + if (newbuf) + { + for (i = 0; i <= len; i++) + newbuf[i] = srcbuf[i]; + } + return newbuf; +} + +char* mikmod_strncat(char *dest, const char *src, size_t count) +{ + size_t i, j; + j = mikmod_strlen(dest); + + for(i = 0; i < count; i++) + { + if (src[i] == 0) + break; + dest[i + j] = src[i]; + } + return dest; +} + +int mikmod_memcmp(const char *buf1, const char *buf2, size_t count) +{ + size_t i; + + for(i = 0; i < count; i++) + { + if (buf1[i] > buf2[i]) + return 1; + if (buf1[i] < buf2[i]) + return -1; + } + return 0; +} + +char* mikmod_strstr(char *str, char *search) +{ + size_t i, j, k; + + if (!mikmod_strlen(search)) + return str; + + j = mikmod_strlen(str); + k = mikmod_strlen(search); + + for (i = 0; i < (j - k); i++) + { + if (!mikmod_memcmp(str, search, k)) + return &str[i]; + } + return NULL; +} + +int mikmod_toupper(int character) +{ + if ((character > 96) && (character < 123)) + return (character - 32); + else + return character; +} + +int mikmod_isalnum(int character) +{ + if ((character > 96) && (character < 123)) + return character; + if ((character > 64) && (character < 91)) + return character; + if ((character > 47) && (character < 58)) + return character; + return 0; +} + +int mikmod_isdigit(char c) +{ + return (c >= '0') && (c <= '9'); +} + + +/**************************************************** + * 'Original' MikMod code goes here + * + ****************************************************/ + +/* Same as malloc, but sets error variable _mm_error when fails */ +void* _mm_malloc(size_t size) +{ + void *d; + + //if(!(d=mikmod_malloc(size))) { + if(!(d=mikmod_malloc(size))) { + _mm_errno = MMERR_OUT_OF_MEMORY; + if(_mm_errorhandler) _mm_errorhandler(); + } + memset(d, 0, size); + return d; +} + +/* Same as calloc, but sets error variable _mm_error when fails */ +void* _mm_calloc(size_t nitems,size_t size) +{ + void *d; + + if(!(d=mikmod_calloc(nitems,size))) { + _mm_errno = MMERR_OUT_OF_MEMORY; + if(_mm_errorhandler) _mm_errorhandler(); + } + return d; +} + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/mmio/mmerror.c b/apps/plugins/mikmod/mmio/mmerror.c new file mode 100644 index 0000000..94d0122 --- /dev/null +++ b/apps/plugins/mikmod/mmio/mmerror.c @@ -0,0 +1,197 @@ +/* MikMod sound library + (c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: mmerror.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $ + + Error handling functions. + Register an error handler with _mm_RegisterErrorHandler() and you're all set. + +==============================================================================*/ + +/* + + The global variables _mm_errno, and _mm_critical are set before the error + handler in called. See below for the values of these variables. + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mikmod_internals.h" + +CHAR *_mm_errmsg[MMERR_MAX+1] = +{ +/* No error */ + + "No error", + +/* Generic errors */ + + "Could not open requested file", + "Out of memory", + "Dynamic linking failed", + +/* Sample errors */ + + "Out of memory to load sample", + "Out of sample handles to load sample", + "Sample format not recognized", + +/* Module errors */ + + "Failure loading module pattern", + "Failure loading module track", + "Failure loading module header", + "Failure loading sampleinfo", + "Module format not recognized", + "Module sample format not recognized", + "Synthsounds not supported in MED files", + "Compressed sample is invalid", + +/* Driver errors: */ + + "Sound device not detected", + "Device number out of range", + "Software mixer failure", + "Could not open sound device", + "This driver supports 8 bit linear output only", + "This driver supports 16 bit linear output only", + "This driver supports stereo output only", + "This driver supports uLaw output (8 bit mono, 8 kHz) only", + "Unable to set non-blocking mode for audio device", + +/* AudioFile driver errors */ + + "Cannot find suitable AudioFile audio port", + +/* AIX driver errors */ + + "Configuration (init step) of audio device failed", + "Configuration (control step) of audio device failed", + "Configuration (start step) of audio device failed", + +/* ALSA driver errors */ + +/* EsounD driver errors */ + +/* Ultrasound driver errors */ + + "Ultrasound driver only works in 16 bit stereo 44 KHz", + "Ultrasound card could not be reset", + "Could not start Ultrasound timer", + +/* HP driver errors */ + + "Unable to select 16bit-linear sample format", + "Could not select requested sample-rate", + "Could not select requested number of channels", + "Unable to select audio output", + "Unable to get audio description", + "Could not set transmission buffer size", + +/* Open Sound System driver errors */ + + "Could not set fragment size", + "Could not set sample size", + "Could not set mono/stereo setting", + "Could not set sample rate", + +/* SGI driver errors */ + + "Unsupported sample rate", + "Hardware does not support 16 bit sound", + "Hardware does not support 8 bit sound", + "Hardware does not support stereo sound", + "Hardware does not support mono sound", + +/* Sun driver errors */ + + "Sound device initialization failed", + +/* OS/2 drivers errors */ + + "Could not set mixing parameters", + "Could not create playback semaphores", + "Could not create playback timer", + "Could not create playback thread", + +/* DirectSound driver errors */ + + "Could not set playback priority", + "Could not create playback buffers", + "Could not set playback format", + "Could not register callback", + "Could not register event", + "Could not create playback thread", + "Could not initialize playback thread", + +/* Windows Multimedia API driver errors */ + + "Invalid device handle", + "The resource is already allocated", + "Invalid device identifier", + "Unsupported output format", + "Unknown error", + +/* Macintosh driver errors */ + + "Unsupported sample rate", + "Could not start playback", + +/* Invalid error */ + + "Invalid error code" +}; + +MIKMODAPI char *MikMod_strerror(int code) +{ + if ((code<0)||(code>MMERR_MAX)) code=MMERR_MAX+1; + return _mm_errmsg[code]; +} + +/* User installed error callback */ +MikMod_handler_t _mm_errorhandler = NULL; +MIKMODAPI int _mm_errno = 0; +MIKMODAPI BOOL _mm_critical = 0; + +MikMod_handler_t _mm_registererrorhandler(MikMod_handler_t proc) +{ + MikMod_handler_t oldproc=_mm_errorhandler; + + _mm_errorhandler = proc; + return oldproc; +} + +MIKMODAPI MikMod_handler_t MikMod_RegisterErrorHandler(MikMod_handler_t proc) +{ + MikMod_handler_t result; + + MUTEX_LOCK(vars); + result=_mm_registererrorhandler(proc); + MUTEX_UNLOCK(vars); + + return result; +} + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/mmio/mmio.c b/apps/plugins/mikmod/mmio/mmio.c new file mode 100644 index 0000000..50269fb --- /dev/null +++ b/apps/plugins/mikmod/mmio/mmio.c @@ -0,0 +1,427 @@ +/* MikMod sound library + (c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: mmio.c,v 1.2 2004/02/06 19:29:05 raph Exp $ + + Portable file I/O routines + +==============================================================================*/ + +/* + + The way this module works: + + - _mm_fopen will call the errorhandler [see mmerror.c] in addition to + setting _mm_errno on exit. + - _mm_iobase is for internal use. It is used by Player_LoadFP to + ensure that it works properly with wad files. + - _mm_read_I_* and _mm_read_M_* differ : the first is for reading data + written by a little endian (intel) machine, and the second is for reading + big endian (Mac, RISC, Alpha) machine data. + - _mm_write functions work the same as the _mm_read functions. + - _mm_read_string is for reading binary strings. It is basically the same + as an fread of bytes. + +*/ + +/* FIXME + the _mm_iobase variable ought to be MREADER-specific. It will eventually + become a private field of the MREADER structure, but this will require a + soname version bump. + + In the meantime, the drawback is that if you use the xxx_LoadFP functions, + you can't have several MREADER objects with different iobase values. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include + +#include "mikmod_internals.h" + +#define COPY_BUFSIZE 1024 + +static long _mm_iobase=0,temp_iobase=0; + + +int _mm_fopen(CHAR* fname,int attrib) +{ + int fp; + + fp=rb->open(fname,attrib); + if(fp == -1) { + fp = 0; + _mm_errno = MMERR_OPENING_FILE; + if(_mm_errorhandler) _mm_errorhandler(); + } + return fp; +} + +BOOL _mm_FileExists(CHAR* fname) +{ + int fp; + + if(!(fp=rb->open(fname, O_RDONLY))) return 0; + rb->close(fp); + + return 1; +} + +int _mm_fclose(int fp) +{ + return rb->close(fp); +} + +/* Sets the current file-position as the new _mm_iobase */ +void _mm_iobase_setcur(MREADER* reader) +{ + temp_iobase=_mm_iobase; /* store old value in case of revert */ + _mm_iobase=reader->Tell(reader); +} + +/* Reverts to the last known _mm_iobase value. */ +void _mm_iobase_revert(void) +{ + _mm_iobase=temp_iobase; +} + +/*========== File Reader */ + +typedef struct MFILEREADER { + MREADER core; + int file; + int waseof; +} MFILEREADER; + +static BOOL _mm_FileReader_Eof(MREADER* reader) +{ + /* + if ((lseek(((MFILEREADER*)reader)->file, 0, SEEK_CUR)-_mm_iobase) < filesize(((MFILEREADER*)reader)->file)) + return 0; + else + return 1; + */ + return ((MFILEREADER*)reader)->waseof; +} + +static BOOL _mm_FileReader_Read(MREADER* reader,void* ptr,size_t size) +{ + int tempb; + tempb = rb->read(((MFILEREADER*)reader)->file, ptr, size); + //printf("(**) READ: %i\n", tempb); + /* + if (tempb != size) + { + printf("Read: count: %i newpos: %i return: %i\n", size, lseek(((MFILEREADER*)reader)->file-_mm_iobase, 0, SEEK_CUR), tempb); + } + */ + if ((tempb < 1) && size > 0) + { + tempb = 0; + ((MFILEREADER*)reader)->waseof = 1; + } + else + { + ((MFILEREADER*)reader)->waseof = 0; + } + return tempb; +} + +static int _mm_FileReader_Get(MREADER* reader) +{ + char buf; + if ((rb->read(((MFILEREADER*)reader)->file, &buf, 1) > 0) == 1) + { + ((MFILEREADER*)reader)->waseof = 0; + //printf("Get: newpos: %i _mm_iobase: %i\n", lseek(((MFILEREADER*)reader)->file-_mm_iobase, 0, SEEK_CUR), _mm_iobase); + return buf; + } + else + { + ((MFILEREADER*)reader)->waseof = 1; + //printf("Get: newpos: %i _mm_iobase: %i\n", lseek(((MFILEREADER*)reader)->file-_mm_iobase, 0, SEEK_CUR), _mm_iobase); + return EOF; + } +} + +static BOOL _mm_FileReader_Seek(MREADER* reader,long offset,int whence) +{ + long tempb; + + tempb = rb->lseek(((MFILEREADER*)reader)->file, + (whence==SEEK_SET)?offset+_mm_iobase:offset,whence); + ((MFILEREADER*)reader)->waseof = 0; + //printf("(**) SEEK: %i\n", tempb); + if (tempb < 0) + { + //printf("Seek: count: %i newpos: %i return: %i\n", offset, lseek(((MFILEREADER*)reader)->file-_mm_iobase, 0, SEEK_CUR), tempb); + return 1; + } + else + { + return 0; + } +} + +static long _mm_FileReader_Tell(MREADER* reader) +{ + return rb->lseek(((MFILEREADER*)reader)->file, 0, SEEK_CUR)-_mm_iobase; +} + +MREADER *_mm_new_file_reader(int fp) +{ + MFILEREADER* reader=(MFILEREADER*)_mm_malloc(sizeof(MFILEREADER)); + if (reader) { + reader->core.Eof =&_mm_FileReader_Eof; + reader->core.Read=&_mm_FileReader_Read; + reader->core.Get =&_mm_FileReader_Get; + reader->core.Seek=&_mm_FileReader_Seek; + reader->core.Tell=&_mm_FileReader_Tell; + reader->file=fp; + reader->waseof = 0; + } + return (MREADER*)reader; +} + +void _mm_delete_file_reader (MREADER* reader) +{ + if(reader) free(reader); +} + +/*========== File Writer */ + +typedef struct MFILEWRITER { + MWRITER core; + int file; +} MFILEWRITER; + +static BOOL _mm_FileWriter_Seek(MWRITER* writer,long offset,int whence) +{ + return rb->lseek(((MFILEWRITER*)writer)->file,offset,whence); +} + +static long _mm_FileWriter_Tell(MWRITER* writer) +{ + return rb->lseek(((MFILEREADER*)writer)->file, 0, SEEK_CUR); +} + +static BOOL _mm_FileWriter_Write(MWRITER* writer,void* ptr,size_t size) +{ + return (rb->write(((MFILEWRITER*)writer)->file, ptr, size)==size); +} + +static BOOL _mm_FileWriter_Put(MWRITER* writer,int value) +{ + char buf = value; + if (rb->write(((MFILEWRITER*)writer)->file, &buf, 1)) + return 0; + else + return -1; +} + +MWRITER *_mm_new_file_writer(int fp) +{ + MFILEWRITER* writer=(MFILEWRITER*)_mm_malloc(sizeof(MFILEWRITER)); + if (writer) { + writer->core.Seek =&_mm_FileWriter_Seek; + writer->core.Tell =&_mm_FileWriter_Tell; + writer->core.Write=&_mm_FileWriter_Write; + writer->core.Put =&_mm_FileWriter_Put; + writer->file=fp; + } + return (MWRITER*) writer; +} + +void _mm_delete_file_writer (MWRITER* writer) +{ + if(writer) free (writer); +} + +/*========== Write functions */ + +void _mm_write_string(CHAR* data,MWRITER* writer) +{ + if(data) + _mm_write_UBYTES(data,strlen(data),writer); +} + +void _mm_write_M_UWORD(UWORD data,MWRITER* writer) +{ + _mm_write_UBYTE(data>>8,writer); + _mm_write_UBYTE(data&0xff,writer); +} + +void _mm_write_I_UWORD(UWORD data,MWRITER* writer) +{ + _mm_write_UBYTE(data&0xff,writer); + _mm_write_UBYTE(data>>8,writer); +} + +void _mm_write_M_ULONG(ULONG data,MWRITER* writer) +{ + _mm_write_M_UWORD((UWORD)(data>>16),writer); + _mm_write_M_UWORD((UWORD)(data&0xffff),writer); +} + +void _mm_write_I_ULONG(ULONG data,MWRITER* writer) +{ + _mm_write_I_UWORD((UWORD)(data&0xffff),writer); + _mm_write_I_UWORD((UWORD)(data>>16),writer); +} + +void _mm_write_M_SWORD(SWORD data,MWRITER* writer) +{ + _mm_write_M_UWORD((UWORD)data,writer); +} + +void _mm_write_I_SWORD(SWORD data,MWRITER* writer) +{ + _mm_write_I_UWORD((UWORD)data,writer); +} + +void _mm_write_M_SLONG(SLONG data,MWRITER* writer) +{ + _mm_write_M_ULONG((ULONG)data,writer); +} + +void _mm_write_I_SLONG(SLONG data,MWRITER* writer) +{ + _mm_write_I_ULONG((ULONG)data,writer); +} + +#if defined __STDC__ || defined _MSC_VER || defined MPW_C +#define DEFINE_MULTIPLE_WRITE_FUNCTION(type_name,type) \ +void _mm_write_##type_name##S (type *buffer,int number,MWRITER* writer) \ +{ \ + while(number-->0) \ + _mm_write_##type_name(*(buffer++),writer); \ +} +#else +#define DEFINE_MULTIPLE_WRITE_FUNCTION(type_name,type) \ +void _mm_write_/**/type_name/**/S (type *buffer,int number,MWRITER* writer) \ +{ \ + while(number-->0) \ + _mm_write_/**/type_name(*(buffer++),writer); \ +} +#endif + +DEFINE_MULTIPLE_WRITE_FUNCTION(M_SWORD,SWORD) +DEFINE_MULTIPLE_WRITE_FUNCTION(M_UWORD,UWORD) +DEFINE_MULTIPLE_WRITE_FUNCTION(I_SWORD,SWORD) +DEFINE_MULTIPLE_WRITE_FUNCTION(I_UWORD,UWORD) + +DEFINE_MULTIPLE_WRITE_FUNCTION(M_SLONG,SLONG) +DEFINE_MULTIPLE_WRITE_FUNCTION(M_ULONG,ULONG) +DEFINE_MULTIPLE_WRITE_FUNCTION(I_SLONG,SLONG) +DEFINE_MULTIPLE_WRITE_FUNCTION(I_ULONG,ULONG) + +/*========== Read functions */ + +int _mm_read_string(CHAR* buffer,int number,MREADER* reader) +{ + return reader->Read(reader,buffer,number); +} + +UWORD _mm_read_M_UWORD(MREADER* reader) +{ + UWORD result=((UWORD)_mm_read_UBYTE(reader))<<8; + result|=_mm_read_UBYTE(reader); + return result; +} + +UWORD _mm_read_I_UWORD(MREADER* reader) +{ + UWORD result=_mm_read_UBYTE(reader); + result|=((UWORD)_mm_read_UBYTE(reader))<<8; + return result; +} + +ULONG _mm_read_M_ULONG(MREADER* reader) +{ + ULONG result=((ULONG)_mm_read_M_UWORD(reader))<<16; + result|=_mm_read_M_UWORD(reader); + return result; +} + +ULONG _mm_read_I_ULONG(MREADER* reader) +{ + ULONG result=_mm_read_I_UWORD(reader); + result|=((ULONG)_mm_read_I_UWORD(reader))<<16; + return result; +} + +SWORD _mm_read_M_SWORD(MREADER* reader) +{ + return((SWORD)_mm_read_M_UWORD(reader)); +} + +SWORD _mm_read_I_SWORD(MREADER* reader) +{ + return((SWORD)_mm_read_I_UWORD(reader)); +} + +SLONG _mm_read_M_SLONG(MREADER* reader) +{ + return((SLONG)_mm_read_M_ULONG(reader)); +} + +SLONG _mm_read_I_SLONG(MREADER* reader) +{ + return((SLONG)_mm_read_I_ULONG(reader)); +} + +#if defined __STDC__ || defined _MSC_VER || defined MPW_C +#define DEFINE_MULTIPLE_READ_FUNCTION(type_name,type) \ +int _mm_read_##type_name##S (type *buffer,int number,MREADER* reader) \ +{ \ + while(number-->0) \ + *(buffer++)=_mm_read_##type_name(reader); \ + return !reader->Eof(reader); \ +} +#else +#define DEFINE_MULTIPLE_READ_FUNCTION(type_name,type) \ +int _mm_read_/**/type_name/**/S (type *buffer,int number,MREADER* reader) \ +{ \ + while(number-->0) \ + *(buffer++)=_mm_read_/**/type_name(reader); \ + return !reader->Eof(reader); \ +} +#endif + +DEFINE_MULTIPLE_READ_FUNCTION(M_SWORD,SWORD) +DEFINE_MULTIPLE_READ_FUNCTION(M_UWORD,UWORD) +DEFINE_MULTIPLE_READ_FUNCTION(I_SWORD,SWORD) +DEFINE_MULTIPLE_READ_FUNCTION(I_UWORD,UWORD) + +DEFINE_MULTIPLE_READ_FUNCTION(M_SLONG,SLONG) +DEFINE_MULTIPLE_READ_FUNCTION(M_ULONG,ULONG) +DEFINE_MULTIPLE_READ_FUNCTION(I_SLONG,SLONG) +DEFINE_MULTIPLE_READ_FUNCTION(I_ULONG,ULONG) + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/mmio/old_mmalloc.c b/apps/plugins/mikmod/mmio/old_mmalloc.c new file mode 100644 index 0000000..ea9924e --- /dev/null +++ b/apps/plugins/mikmod/mmio/old_mmalloc.c @@ -0,0 +1,290 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: mmalloc.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $ + + Dynamic memory routines + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mikmod_internals.h" + + +size_t mem_offset = 0; +char *mallocbuf = NULL; +size_t mallocbuflen = 0; + +#define OTMAX 8192 +size_t offsettable[OTMAX]; +int otcounter = 0; + +size_t mikmod_get_mbuffsize(void *buff) +{ + int i; + + for (i = 0; i < otcounter; i++) + { + if (offsettable[i] == (size_t)buff - (size_t)mallocbuf) + { + if (i < (otcounter - 1)) + { + return (size_t)(offsettable[i + 1] - offsettable[i]); + } + else // i == (otcounter - 1) + { + return mem_offset - offsettable[i]; + } + } + } + + return 0; // i >= otcounter +} + + +void mikmod_prepare_malloc(char *buff, int bufsize) +{ + mallocbuf = buff; + mallocbuflen = bufsize; + mem_offset = 0; + otcounter = 0; +} + +long mikmod_get_malloc_usage(void) +{ + return mem_offset; +} + +int mikmod_abs(int num) +{ + if (num < 0) + return (num * -1); + else + return (num); +} + +void* mikmod_memset(char *buf, int val, size_t count) +{ + size_t i; + + for (i = 0; i < count; i++) + { + buf[i] = val; + } + return buf; +} + +/* 'Poor man's malloc' taken from rockbox codeclib.c */ +void* mikmod_malloc(size_t size) +{ + void* x; + + if (!mallocbuf) + return NULL; + if (mem_offset + (long)size > mallocbuflen) + return NULL; + + x = &mallocbuf[mem_offset]; + + + if (otcounter < OTMAX) + { + offsettable[otcounter] = mem_offset; + otcounter++; + } + else + { + if (otcounter == OTMAX) + { + rb->splash(HZ * 2, "Offset Table FULL !\n"); + } + } + mem_offset += (size + 3) & ~3; /* Keep memory 32-bit aligned */ + + + memset(x, 0, size); + + return(x); +} + +void* mikmod_calloc(size_t nmemb, size_t size) +{ + void *x; + x = mikmod_malloc(nmemb * size); + if (x == NULL) + return NULL; + //memset(x, 0, nmemb*size); + return(x); +} + +void mikmod_free(void* ptr) { + (void)ptr; +} + +void* mikmod_realloc(void* ptr, size_t size) +{ + void* x; + //(void)ptr; + x = mikmod_malloc(size); + if (ptr != NULL) + { + if (mikmod_get_mbuffsize(ptr)) + { + memcpy(x, ptr, mikmod_get_mbuffsize(ptr)); + } + else + { + memcpy(x, ptr, size); //FIXME: size has to be the size of the original buffer..... + } + } + return(x); +} + +unsigned int mikmod_strlen(const char *string) +{ + unsigned int i; + for (i = 0; string[i] != 0; i++); + return i; +} + +char* mikmod_strdup(const char *srcbuf) +{ + char *newbuf; + unsigned int i, len; + + len = mikmod_strlen(srcbuf); + newbuf = mikmod_malloc(len + 1); + + if (newbuf) + { + for (i = 0; i <= len; i++) + newbuf[i] = srcbuf[i]; + } + return newbuf; +} + +char* mikmod_strncat(char *dest, const char *src, size_t count) +{ + size_t i, j; + j = mikmod_strlen(dest); + + for(i = 0; i < count; i++) + { + if (src[i] == 0) + break; + dest[i + j] = src[i]; + } + return dest; +} + +int mikmod_memcmp(const char *buf1, const char *buf2, size_t count) +{ + size_t i; + + for(i = 0; i < count; i++) + { + if (buf1[i] > buf2[i]) + return 1; + if (buf1[i] < buf2[i]) + return -1; + } + return 0; +} + +char* mikmod_strstr(char *str, char *search) +{ + size_t i, j, k; + + if (!mikmod_strlen(search)) + return str; + + j = mikmod_strlen(str); + k = mikmod_strlen(search); + + for (i = 0; i < (j - k); i++) + { + if (!mikmod_memcmp(str, search, k)) + return &str[i]; + } + return NULL; +} + +int mikmod_toupper(int character) +{ + if ((character > 96) && (character < 123)) + return (character - 32); + else + return character; +} + +int mikmod_isalnum(int character) +{ + if ((character > 96) && (character < 123)) + return character; + if ((character > 64) && (character < 91)) + return character; + if ((character > 47) && (character < 58)) + return character; + return 0; +} + +int mikmod_isdigit(char c) +{ + return (c >= '0') && (c <= '9'); +} + + +/**************************************************** + * 'Original' MikMod code goes here + * + ****************************************************/ + +/* Same as malloc, but sets error variable _mm_error when fails */ +void* _mm_malloc(size_t size) +{ + void *d; + + //if(!(d=mikmod_malloc(size))) { + if(!(d=mikmod_malloc(size))) { + _mm_errno = MMERR_OUT_OF_MEMORY; + if(_mm_errorhandler) _mm_errorhandler(); + } + memset(d, 0, size); + return d; +} + +/* Same as calloc, but sets error variable _mm_error when fails */ +void* _mm_calloc(size_t nitems,size_t size) +{ + void *d; + + if(!(d=mikmod_calloc(nitems,size))) { + _mm_errno = MMERR_OUT_OF_MEMORY; + if(_mm_errorhandler) _mm_errorhandler(); + } + return d; +} + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/playercode/mdriver.c b/apps/plugins/mikmod/playercode/mdriver.c new file mode 100644 index 0000000..247423a --- /dev/null +++ b/apps/plugins/mikmod/playercode/mdriver.c @@ -0,0 +1,961 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS + for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: mdriver.c,v 1.3 2004/02/18 13:29:19 raph Exp $ + + These routines are used to access the available soundcard drivers. + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#if defined unix || (defined __APPLE__ && defined __MACH__) +#include +#include +#endif + +#include +#ifdef HAVE_STRINGS_H +#include +#endif + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +static MDRIVER *firstdriver=NULL; + +extern MODULE *pf; /* modfile being played */ + +/* Initial global settings */ +MIKMODAPI UWORD md_device = 0; /* autodetect */ +MIKMODAPI UWORD md_mixfreq = 44100; +MIKMODAPI UWORD md_mode = DMODE_STEREO | DMODE_16BITS | + DMODE_SURROUND |DMODE_SOFT_MUSIC | + DMODE_SOFT_SNDFX; +MIKMODAPI UBYTE md_pansep = 128; /* 128 == 100% (full left/right) */ +MIKMODAPI UBYTE md_reverb = 0; /* no reverb */ +MIKMODAPI UBYTE md_volume = 128; /* global sound volume (0-128) */ +MIKMODAPI UBYTE md_musicvolume = 128; /* volume of song */ +MIKMODAPI UBYTE md_sndfxvolume = 128; /* volume of sound effects */ + UWORD md_bpm = 125; /* tempo */ + +/* Do not modify the numchn variables yourself! use MD_SetVoices() */ + UBYTE md_numchn=0,md_sngchn=0,md_sfxchn=0; + UBYTE md_hardchn=0,md_softchn=0; + + void (*md_player)(void) = Player_HandleTick; +static BOOL isplaying=0, initialized = 0; +static UBYTE *sfxinfo; +static int sfxpool; + +static SAMPLE **md_sample = NULL; + +/* Previous driver in use * +static SWORD olddevice = -1; +*/ +/* Limits the number of hardware voices to the specified amount. + This function should only be used by the low-level drivers. */ +static void LimitHardVoices(int limit) +{ + int t=0; + + if (!(md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>limit)) md_sfxchn=limit; + if (!(md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>limit)) md_sngchn=limit; + + if (!(md_mode & DMODE_SOFT_SNDFX)) + md_hardchn=md_sfxchn; + else + md_hardchn=0; + + if (!(md_mode & DMODE_SOFT_MUSIC)) md_hardchn += md_sngchn; + + while (md_hardchn>limit) { + if (++t & 1) { + if (!(md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>4)) md_sfxchn--; + } else { + if (!(md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>8)) md_sngchn--; + } + + if (!(md_mode & DMODE_SOFT_SNDFX)) + md_hardchn=md_sfxchn; + else + md_hardchn=0; + + if (!(md_mode & DMODE_SOFT_MUSIC)) + md_hardchn+=md_sngchn; + } + md_numchn=md_hardchn+md_softchn; +} + +/* Limits the number of hardware voices to the specified amount. + This function should only be used by the low-level drivers. */ +static void LimitSoftVoices(int limit) +{ + int t=0; + + if ((md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>limit)) md_sfxchn=limit; + if ((md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>limit)) md_sngchn=limit; + + if (md_mode & DMODE_SOFT_SNDFX) + md_softchn=md_sfxchn; + else + md_softchn=0; + + if (md_mode & DMODE_SOFT_MUSIC) md_softchn+=md_sngchn; + + while (md_softchn>limit) { + if (++t & 1) { + if ((md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>4)) md_sfxchn--; + } else { + if ((md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>8)) md_sngchn--; + } + + if (!(md_mode & DMODE_SOFT_SNDFX)) + md_softchn=md_sfxchn; + else + md_softchn=0; + + if (!(md_mode & DMODE_SOFT_MUSIC)) + md_softchn+=md_sngchn; + } + md_numchn=md_hardchn+md_softchn; +} + +/* Note: 'type' indicates whether the returned value should be for music or for + sound effects. */ +ULONG MD_SampleSpace(int type) +{ + if(type==MD_MUSIC) + type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE; + else if(type==MD_SNDFX) + type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE; + + return VC_SampleSpace(type); +} + +ULONG MD_SampleLength(int type,SAMPLE* s) +{ + if(type==MD_MUSIC) + type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE; + else + if(type==MD_SNDFX) + type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE; + + return VC_SampleLength(type,s); +} +/* +MIKMODAPI CHAR* MikMod_InfoDriver(void) +{ + int t,len=0; + MDRIVER *l; + CHAR *list=NULL; + + MUTEX_LOCK(lists); + /* compute size of buffer * + for(l=firstdriver;l;l=l->next) + len+=4+(l->next?1:0)+strlen(l->Version); + + if(len) + if((list=_mm_malloc(len*sizeof(CHAR)))) { + list[0]=0; + /* list all registered device drivers : * + for(t=1,l=firstdriver;l;l=l->next,t++) + sprintf(list,(l->next)?"%s%2d %s\n":"%s%2d %s", + list,t,l->Version); + } + MUTEX_UNLOCK(lists); + return list; +} +*/ +void _mm_registerdriver(struct MDRIVER* drv) +{ + MDRIVER *cruise = firstdriver; + + /* don't register a MISSING() driver */ + if ((drv->Name) && (drv->Version)) { + if (cruise) { + while (cruise->next) cruise = cruise->next; + cruise->next = drv; + } else + firstdriver = drv; + } +} + +MIKMODAPI void MikMod_RegisterDriver(struct MDRIVER* drv) +{ + /* if we try to register an invalid driver, or an already registered driver, + ignore this attempt */ + if ((!drv)||(drv->next)||(!drv->Name)) + return; + + MUTEX_LOCK(lists); + _mm_registerdriver(drv); + MUTEX_UNLOCK(lists); +} + +MIKMODAPI int MikMod_DriverFromAlias(CHAR *alias) +{ + int rank=1; + MDRIVER *cruise; + + MUTEX_LOCK(lists); + cruise=firstdriver; + while(cruise) { + if (cruise->Alias) { + if (!(strcasecmp(alias,cruise->Alias))) break; + rank++; + } + cruise=cruise->next; + } + if(!cruise) rank=0; + MUTEX_UNLOCK(lists); + + return rank; +} + +MIKMODAPI MDRIVER *MikMod_DriverByOrdinal(int ordinal) +{ + MDRIVER *cruise; + + /* Allow only driver ordinals > 0 */ + if (!ordinal) + return 0; + + MUTEX_LOCK(lists); + cruise = firstdriver; + while (cruise && --ordinal) + cruise = cruise->next; + MUTEX_UNLOCK(lists); + return cruise; +} + +SWORD MD_SampleLoad(SAMPLOAD* s, int type) +{ + SWORD result; + + if(type==MD_MUSIC) + type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE; + else if(type==MD_SNDFX) + type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE; + + SL_Init(s); + result=VC_SampleLoad(s,type); + SL_Exit(s); + + return result; +} + +void MD_SampleUnload(SWORD handle) +{ + VC_SampleUnload(handle); +} + +MIKMODAPI MikMod_player_t MikMod_RegisterPlayer(MikMod_player_t player) +{ + MikMod_player_t result; + + MUTEX_LOCK(vars); + result=md_player; + md_player=player; + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI void MikMod_Update(void) +{ + MUTEX_LOCK(vars); + /* FIXME - if I'm broken... + if(isplaying) + { + if((!pf)||(!pf->forbid)) + { + md_driver->Update(); + } + else + { + if (md_driver->Pause) + md_driver->Pause(); + } + } + */ + MUTEX_UNLOCK(vars); +} + +void Voice_SetVolume_internal(SBYTE voice,UWORD vol) +{ + ULONG tmp; + + if((voice<0)||(voice>=md_numchn)) return; + + /* range checks */ + if(md_musicvolume>128) md_musicvolume=128; + if(md_sndfxvolume>128) md_sndfxvolume=128; + if(md_volume>128) md_volume=128; + + tmp=(ULONG)vol*(ULONG)md_volume* + ((voice=0)&&(voice=md_numchn)) return; + if((md_sample[voice])&&(md_sample[voice]->divfactor)) + frq/=md_sample[voice]->divfactor; + VC_VoiceSetFrequency(voice,frq); +} + +MIKMODAPI void Voice_SetFrequency(SBYTE voice,ULONG frq) +{ + MUTEX_LOCK(vars); + Voice_SetFrequency_internal(voice,frq); + MUTEX_UNLOCK(vars); +} + +MIKMODAPI ULONG Voice_GetFrequency(SBYTE voice) +{ + ULONG result=0; + + MUTEX_LOCK(vars); + if((voice>=0)&&(voice=md_numchn)) return; + if(pan!=PAN_SURROUND) { + if(md_pansep>128) md_pansep=128; + if(md_mode & DMODE_REVERSE) pan=255-pan; + pan = (((SWORD)(pan-128)*md_pansep)/128)+128; + } + VC_VoiceSetPanning(voice, pan); +} + +MIKMODAPI void Voice_SetPanning(SBYTE voice,ULONG pan) +{ +#ifdef MIKMOD_DEBUG + if((pan!=PAN_SURROUND)&&((pan<0)||(pan>255))) + fprintf(stderr,"\rVoice_SetPanning called with pan=%ld\n",(long)pan); +#endif + + MUTEX_LOCK(vars); + Voice_SetPanning_internal(voice,pan); + MUTEX_UNLOCK(vars); +} + +MIKMODAPI ULONG Voice_GetPanning(SBYTE voice) +{ + ULONG result=PAN_CENTER; + + MUTEX_LOCK(vars); + if((voice>=0)&&(voice=md_numchn)) return; + + md_sample[voice]=s; + repend=s->loopend; + + if(s->flags&SF_LOOP) + /* repend can't be bigger than size */ + if(repend>s->length) repend=s->length; + + VC_VoicePlay(voice,s->handle,start,s->length,s->loopstart,repend,s->flags); +} + +MIKMODAPI void Voice_Play(SBYTE voice,SAMPLE* s,ULONG start) +{ + if(start>s->length) return; + + MUTEX_LOCK(vars); + Voice_Play_internal(voice,s,start); + MUTEX_UNLOCK(vars); +} + +void Voice_Stop_internal(SBYTE voice) +{ + if((voice<0)||(voice>=md_numchn)) return; + if(voice>=md_sngchn) + /* It is a sound effects channel, so flag the voice as non-critical! */ + sfxinfo[voice-md_sngchn]=0; + VC_VoiceStop(voice); +} + +MIKMODAPI void Voice_Stop(SBYTE voice) +{ + MUTEX_LOCK(vars); + Voice_Stop_internal(voice); + MUTEX_UNLOCK(vars); +} + +BOOL Voice_Stopped_internal(SBYTE voice) +{ + if((voice<0)||(voice>=md_numchn)) return 0; + return(VC_VoiceStopped(voice)); +} + +MIKMODAPI BOOL Voice_Stopped(SBYTE voice) +{ + BOOL result; + + MUTEX_LOCK(vars); + result=Voice_Stopped_internal(voice); + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI SLONG Voice_GetPosition(SBYTE voice) +{ + SLONG result=0; + + MUTEX_LOCK(vars); + if((voice>=0)&&(voice=0)&&(voicenext,t++) + if(md_driver->IsPresent()) break; + + if(!md_driver) { + _mm_errno = MMERR_DETECTING_DEVICE; + if(_mm_errorhandler) _mm_errorhandler(); + md_driver = &drv_nos; + return 1; + } + + md_device = t; + } else { + /* if n>0, use that driver * + for(t=1,md_driver=firstdriver;(md_driver)&&(t!=md_device);md_driver=md_driver->next) + t++; + + if(!md_driver) { + _mm_errno = MMERR_INVALID_DEVICE; + if(_mm_errorhandler) _mm_errorhandler(); + md_driver = &drv_nos; + return 1; + } + + /* arguments here might be necessary for the presence check to succeed * + if(cmdline&&(md_driver->CommandLine)) + md_driver->CommandLine(cmdline); + + if(!md_driver->IsPresent()) { + _mm_errno = MMERR_DETECTING_DEVICE; + if(_mm_errorhandler) _mm_errorhandler(); + md_driver = &drv_nos; + return 1; + } + } + + olddevice = md_device; +*/ + md_mode |= DMODE_SOFT_MUSIC | DMODE_SOFT_SNDFX; + + if(VC_Init()) { + MikMod_Exit_internal(); + if(_mm_errorhandler) _mm_errorhandler(); + return 1; + } + + initialized=1; + _mm_critical=0; + + return 0; +} + +MIKMODAPI BOOL MikMod_Init(CHAR *cmdline) +{ + BOOL result; + + MUTEX_LOCK(vars); + MUTEX_LOCK(lists); + result=_mm_init(cmdline); + MUTEX_UNLOCK(lists); + MUTEX_UNLOCK(vars); + + return result; +} + +void MikMod_Exit_internal(void) +{ + MikMod_DisableOutput_internal(); + VC_Exit(); + md_numchn = md_sfxchn = md_sngchn = 0; + //md_driver = &drv_nos; + + if(sfxinfo) free(sfxinfo); + if(md_sample) free(md_sample); + md_sample = NULL; + sfxinfo = NULL; + + initialized = 0; +} + +MIKMODAPI void MikMod_Exit(void) +{ + MUTEX_LOCK(vars); + MUTEX_LOCK(lists); + MikMod_Exit_internal(); + MUTEX_UNLOCK(lists); + MUTEX_UNLOCK(vars); +} + +/* Reset the driver using the new global variable settings. + If the driver has not been initialized, it will be now. */ +static BOOL _mm_reset(CHAR *cmdline) +{ + BOOL wasplaying = 0; + + if(!initialized) return _mm_init(cmdline); + + if (isplaying) { + wasplaying = 1; + VC_PlayStop(); + } +/* + if((!md_driver->Reset)||(md_device != olddevice)) { + /* md_driver->Reset was NULL, or md_device was changed, so do a full + reset of the driver. * + md_driver->Exit(); + if(_mm_init(cmdline)) { + MikMod_Exit_internal(); + if(_mm_errno) + if(_mm_errorhandler) _mm_errorhandler(); + return 1; + } + } else { + if(md_driver->Reset()) { + MikMod_Exit_internal(); + if(_mm_errno) + if(_mm_errorhandler) _mm_errorhandler(); + return 1; + } + } +*/ + if (wasplaying) VC_PlayStart(); + return 0; +} + +MIKMODAPI BOOL MikMod_Reset(CHAR *cmdline) +{ + BOOL result; + + MUTEX_LOCK(vars); + MUTEX_LOCK(lists); + result=_mm_reset(cmdline); + MUTEX_UNLOCK(lists); + MUTEX_UNLOCK(vars); + + return result; +} + +/* If either parameter is -1, the current set value will be retained. */ +BOOL MikMod_SetNumVoices_internal(int music, int sfx) +{ + BOOL resume = 0; + int t, oldchn = 0; + + if((!music)&&(!sfx)) return 1; + _mm_critical = 1; + if(isplaying) { + MikMod_DisableOutput_internal(); + oldchn = md_numchn; + resume = 1; + } + + if(sfxinfo) free(sfxinfo); + if(md_sample) free(md_sample); + md_sample = NULL; + sfxinfo = NULL; + + if(music!=-1) md_sngchn = music; + if(sfx!=-1) md_sfxchn = sfx; + md_numchn = md_sngchn + md_sfxchn; +/* + LimitHardVoices(md_driver->HardVoiceLimit); + LimitSoftVoices(md_driver->SoftVoiceLimit); +*/ + LimitHardVoices(0); + LimitSoftVoices(255); + + if(VC_SetNumVoices()) { + MikMod_Exit_internal(); + if(_mm_errno) + if(_mm_errorhandler!=NULL) _mm_errorhandler(); + md_numchn = md_softchn = md_hardchn = md_sfxchn = md_sngchn = 0; + return 1; + } + + if(md_sngchn+md_sfxchn) + md_sample=(SAMPLE**)_mm_calloc(md_sngchn+md_sfxchn,sizeof(SAMPLE*)); + if(md_sfxchn) + sfxinfo = (UBYTE *)_mm_calloc(md_sfxchn,sizeof(UBYTE)); + + /* make sure the player doesn't start with garbage */ + for(t=oldchn;tvolume>64) s->volume = 64; + + /* check the first location after sfxpool */ + do { + if(sfxinfo[sfxpool]&SFX_CRITICAL) { + if(VC_VoiceStopped(c=sfxpool+md_sngchn)) { + sfxinfo[sfxpool]=flags; + Voice_Play_internal(c,s,start); + VC_VoiceSetVolume(c,s->volume<<2); + Voice_SetPanning_internal(c,s->panning); + VC_VoiceSetFrequency(c,s->speed); + sfxpool++; + if(sfxpool>=md_sfxchn) sfxpool=0; + return c; + } + } else { + sfxinfo[sfxpool]=flags; + Voice_Play_internal(c=sfxpool+md_sngchn,s,start); + VC_VoiceSetVolume(c,s->volume<<2); + Voice_SetPanning_internal(c,s->panning); + VC_VoiceSetFrequency(c,s->speed); + sfxpool++; + if(sfxpool>=md_sfxchn) sfxpool=0; + return c; + } + + sfxpool++; + if(sfxpool>=md_sfxchn) sfxpool = 0; + } while(sfxpool!=orig); + + return -1; +} + +MIKMODAPI SBYTE Sample_Play(SAMPLE *s,ULONG start,UBYTE flags) +{ + SBYTE result; + + MUTEX_LOCK(vars); + result=Sample_Play_internal(s,start,flags); + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI long MikMod_GetVersion(void) +{ + return LIBMIKMOD_VERSION; +} + +/*========== MT-safe stuff */ + +#ifdef HAVE_PTHREAD +#define INIT_MUTEX(name) \ + pthread_mutex_t _mm_mutex_##name=PTHREAD_MUTEX_INITIALIZER +#elif defined(__OS2__)||defined(__EMX__) +#define INIT_MUTEX(name) \ + HMTX _mm_mutex_##name +#elif defined(WIN32) +#define INIT_MUTEX(name) \ + HANDLE _mm_mutex_##name +#else +#define INIT_MUTEX(name) \ + void *_mm_mutex_##name = NULL +#endif + +INIT_MUTEX(vars); +INIT_MUTEX(lists); + +MIKMODAPI BOOL MikMod_InitThreads(void) +{ + static int firstcall=1; + static int result=0; + + if (firstcall) { + firstcall=0; +#ifdef HAVE_PTHREAD + result=1; +#elif defined(__OS2__)||defined(__EMX__) + if(DosCreateMutexSem((PSZ)NULL,&_mm_mutex_lists,0,0) || + DosCreateMutexSem((PSZ)NULL,&_mm_mutex_vars,0,0)) { + _mm_mutex_lists=_mm_mutex_vars=(HMTX)NULL; + result=0; + } else + result=1; +#elif defined(WIN32) + if((!(_mm_mutex_lists=CreateMutex(NULL,FALSE,"libmikmod(lists)")))|| + (!(_mm_mutex_vars=CreateMutex(NULL,FALSE,"libmikmod(vars)")))) + result=0; + else + result=1; +#endif + } + return result; +} + +MIKMODAPI void MikMod_Unlock(void) +{ + MUTEX_UNLOCK(lists); + MUTEX_UNLOCK(vars); +} + +MIKMODAPI void MikMod_Lock(void) +{ + MUTEX_LOCK(vars); + MUTEX_LOCK(lists); +} + +/*========== Parameter extraction helper */ + +CHAR *MD_GetAtom(CHAR *atomname,CHAR *cmdline,BOOL implicit) +{ + CHAR *ret=NULL; + + if(cmdline) { + CHAR *buf=strstr(cmdline,atomname); + + if((buf)&&((buf==cmdline)||(*(buf-1)==','))) { + CHAR *ptr=buf+strlen(atomname); + + if(*ptr=='=') { + for(buf=++ptr;(*ptr)&&((*ptr)!=',');ptr++); + ret=_mm_malloc((1+ptr-buf)*sizeof(CHAR)); + if(ret) + strncpy(ret,buf,ptr-buf); + } else if((*ptr==',')||(!*ptr)) { + if(implicit) { + ret=_mm_malloc((1+ptr-buf)*sizeof(CHAR)); + if(ret) + strncpy(ret,buf,ptr-buf); + } + } + } + } + return ret; +} + +#if defined unix || (defined __APPLE__ && defined __MACH__) + +/*========== Posix helper functions */ + +/* Check if the file is a regular or nonexistant file (or a link to a such a + file), and that, should the calling program be setuid, the access rights are + reasonable. Returns 1 if it is safe to rewrite the file, 0 otherwise. + The goal is to prevent a setuid root libmikmod application from overriding + files like /etc/passwd with digital sound... */ +BOOL MD_Access(CHAR *filename) +{ + struct stat buf; + + if(!stat(filename,&buf)) { + /* not a regular file ? */ + if(!S_ISREG(buf.st_mode)) return 0; + /* more than one hard link to the file ? */ + if(buf.st_nlink>1) return 0; + /* check access rights with the real user and group id */ + if(getuid()==buf.st_uid) { + if(!(buf.st_mode&S_IWUSR)) return 0; + } else if(getgid()==buf.st_gid) { + if(!(buf.st_mode&S_IWGRP)) return 0; + } else + if(!(buf.st_mode&S_IWOTH)) return 0; + } + + return 1; +} + +/* Drop all root privileges we might have */ +BOOL MD_DropPrivileges(void) +{ + if(!geteuid()) { + if(getuid()) { + /* we are setuid root -> drop setuid to become the real user */ + if(setuid(getuid())) return 1; + } else { + /* we are run as root -> drop all and become user 'nobody' */ + struct passwd *nobody; + int uid; + + if(!(nobody=getpwnam("nobody"))) return 1; /* no such user ? */ + uid=nobody->pw_uid; + if (!uid) /* user 'nobody' has root privileges ? weird... */ + return 1; + if (setuid(uid)) return 1; + } + } + return 0; +} + +#endif + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/playercode/mloader.c b/apps/plugins/mikmod/playercode/mloader.c new file mode 100644 index 0000000..75504eb --- /dev/null +++ b/apps/plugins/mikmod/playercode/mloader.c @@ -0,0 +1,564 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file + AUTHORS for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: mloader.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $ + + These routines are used to access the available module loaders + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + + MREADER *modreader; + MODULE of; + +static MLOADER *firstloader=NULL; + +UWORD finetune[16]={ + 8363,8413,8463,8529,8581,8651,8723,8757, + 7895,7941,7985,8046,8107,8169,8232,8280 +}; +/* +MIKMODAPI CHAR* MikMod_InfoLoader(void) +{ + int len=0; + MLOADER *l; + CHAR *list=NULL; + + MUTEX_LOCK(lists); + /* compute size of buffer * + for(l=firstloader;l;l=l->next) len+=1+(l->next?1:0)+strlen(l->version); + + if(len) + if((list=_mm_malloc(len*sizeof(CHAR)))) { + list[0]=0; + /* list all registered module loders * + for(l=firstloader;l;l=l->next) + sprintf(list,(l->next)?"%s%s\n":"%s%s",list,l->version); + }* + MUTEX_UNLOCK(lists); + return list; +} +*/ +void _mm_registerloader(MLOADER* ldr) +{ + MLOADER *cruise=firstloader; + + if(cruise) { + while(cruise->next) cruise = cruise->next; + cruise->next=ldr; + } else + firstloader=ldr; +} + +MIKMODAPI void MikMod_RegisterLoader(struct MLOADER* ldr) +{ + /* if we try to register an invalid loader, or an already registered loader, + ignore this attempt */ + if ((!ldr)||(ldr->next)) + return; + + MUTEX_LOCK(lists); + _mm_registerloader(ldr); + MUTEX_UNLOCK(lists); +} + +BOOL ReadComment(UWORD len) +{ + if(len) { + int i; + + if(!(of.comment=(CHAR*)_mm_malloc(len+1))) return 0; + _mm_read_UBYTES(of.comment,len,modreader); + + /* translate IT linefeeds */ + for(i=0;i=0)&&(line[i]==' ');i--) line[i]=0; + for(i=0;ilines) { + if(!(of.comment=(CHAR*)_mm_malloc(total+1))) { + free(storage); + free(tempcomment); + return 0; + } + + /* convert message */ + for(line=tempcomment,t=0;tlength) SL_RegisterSample(s,MD_MUSIC,modreader); + + return 1; +} + +/* Creates a CSTR out of a character buffer of 'len' bytes, but strips any + terminating non-printing characters like 0, spaces etc. */ +CHAR *DupStr(CHAR* s,UWORD len,BOOL strict) +{ + UWORD t; + CHAR *d=NULL; + + /* Scan for last printing char in buffer [includes high ascii up to 254] */ + while(len) { + if(s[len-1]>0x20) break; + len--; + } + + /* Scan forward for possible NULL character */ + if(strict) { + for(t=0;thandle>=0) + MD_SampleUnload(s->handle); + if(s->samplename) free(s->samplename); +} + +static void ML_XFreeInstrument(INSTRUMENT *i) +{ + if(i->insname) free(i->insname); +} + +static void ML_FreeEx(MODULE *mf) +{ + UWORD t; + + if(mf->songname) free(mf->songname); + if(mf->comment) free(mf->comment); + + if(mf->modtype) free(mf->modtype); + if(mf->positions) free(mf->positions); + if(mf->patterns) free(mf->patterns); + if(mf->pattrows) free(mf->pattrows); + + if(mf->tracks) { + for(t=0;tnumtrk;t++) + if(mf->tracks[t]) free(mf->tracks[t]); + free(mf->tracks); + } + if(mf->instruments) { + for(t=0;tnumins;t++) + ML_XFreeInstrument(&mf->instruments[t]); + free(mf->instruments); + } + if(mf->samples) { + for(t=0;tnumsmp;t++) + if(mf->samples[t].length) ML_XFreeSample(&mf->samples[t]); + free(mf->samples); + } + memset(mf,0,sizeof(MODULE)); + if(mf!=&of) free(mf); +} + +static MODULE *ML_AllocUniMod(void) +{ + MODULE *mf; + + return (mf=_mm_malloc(sizeof(MODULE))); +} + +void Player_Free_internal(MODULE *mf) +{ + if(mf) { + Player_Exit_internal(mf); + ML_FreeEx(mf); + } +} + +MIKMODAPI void Player_Free(MODULE *mf) +{ + MUTEX_LOCK(vars); + Player_Free_internal(mf); + MUTEX_UNLOCK(vars); +} + +static CHAR* Player_LoadTitle_internal(MREADER *reader) +{ + MLOADER *l; + + modreader=reader; + _mm_errno = 0; + _mm_critical = 0; + _mm_iobase_setcur(modreader); + + /* Try to find a loader that recognizes the module */ + for(l=firstloader;l;l=l->next) { + _mm_rewind(modreader); + if(l->Test()) break; + } + + if(!l) { + _mm_errno = MMERR_NOT_A_MODULE; + if(_mm_errorhandler) _mm_errorhandler(); + return NULL; + } + + return l->LoadTitle(); +} + +MIKMODAPI CHAR* Player_LoadTitleFP(int fp) +{ + CHAR* result=NULL; + MREADER* reader; + + if(fp && (reader=_mm_new_file_reader(fp))) { + MUTEX_LOCK(lists); + result=Player_LoadTitle_internal(reader); + MUTEX_UNLOCK(lists); + _mm_delete_file_reader(reader); + } + return result; +} + +MIKMODAPI CHAR* Player_LoadTitle(CHAR* filename) +{ + CHAR* result=NULL; + int fp; + MREADER* reader; + + if((fp=_mm_fopen(filename,O_RDONLY))) { + if((reader=_mm_new_file_reader(fp))) { + MUTEX_LOCK(lists); + result=Player_LoadTitle_internal(reader); + MUTEX_UNLOCK(lists); + _mm_delete_file_reader(reader); + } + _mm_fclose(fp); + } + return result; +} + +/* Loads a module given an reader */ +MODULE* Player_LoadGeneric_internal(MREADER *reader,int maxchan,BOOL curious) +{ + int t; + MLOADER *l; + BOOL ok; + MODULE *mf; + + modreader = reader; + _mm_errno = 0; + _mm_critical = 0; + _mm_iobase_setcur(modreader); + + /* Try to find a loader that recognizes the module */ + for(l=firstloader;l;l=l->next) { + _mm_rewind(modreader); + if(l->Test()) break; + } + + if(!l) { + _mm_errno = MMERR_NOT_A_MODULE; + if(_mm_errorhandler) _mm_errorhandler(); + _mm_rewind(modreader);_mm_iobase_revert(); + return NULL; + } + + /* init unitrk routines */ + if(!UniInit()) { + if(_mm_errorhandler) _mm_errorhandler(); + _mm_rewind(modreader);_mm_iobase_revert(); + return NULL; + } + + /* init the module structure with vanilla settings */ + memset(&of,0,sizeof(MODULE)); + of.bpmlimit = 33; + of.initvolume = 128; + for (t = 0; t < UF_MAXCHAN; t++) of.chanvol[t] = 64; + for (t = 0; t < UF_MAXCHAN; t++) + of.panning[t] = ((t + 1) & 2) ? PAN_RIGHT : PAN_LEFT; + + /* init module loader and load the header / patterns */ + if (!l->Init || l->Init()) { + _mm_rewind(modreader); + ok = l->Load(curious); + /* propagate inflags=flags for in-module samples */ + for (t = 0; t < of.numsmp; t++) + if (of.samples[t].inflags == 0) + of.samples[t].inflags = of.samples[t].flags; + } else + ok = 0; + + /* free loader and unitrk allocations */ + if (l->Cleanup) l->Cleanup(); + UniCleanup(); + + if(!ok) { + ML_FreeEx(&of); + if(_mm_errorhandler) _mm_errorhandler(); + _mm_rewind(modreader);_mm_iobase_revert(); + return NULL; + } + + if(!ML_LoadSamples()) { + ML_FreeEx(&of); + if(_mm_errorhandler) _mm_errorhandler(); + _mm_rewind(modreader);_mm_iobase_revert(); + return NULL; + } + + if(!(mf=ML_AllocUniMod())) { + ML_FreeEx(&of); + _mm_rewind(modreader);_mm_iobase_revert(); + if(_mm_errorhandler) _mm_errorhandler(); + return NULL; + } + + /* If the module doesn't have any specific panning, create a + MOD-like panning, with the channels half-separated. */ + if (!(of.flags & UF_PANNING)) + for (t = 0; t < of.numchn; t++) + of.panning[t] = ((t + 1) & 2) ? PAN_HALFRIGHT : PAN_HALFLEFT; + + /* Copy the static MODULE contents into the dynamic MODULE struct. */ + memcpy(mf,&of,sizeof(MODULE)); + + if(maxchan>0) { + if(!(mf->flags&UF_NNA)&&(mf->numchnnumchn; + else + if((mf->numvoices)&&(mf->numvoicesnumvoices; + + if(maxchannumchn) mf->flags |= UF_NNA; + + if(MikMod_SetNumVoices_internal(maxchan,-1)) { + _mm_iobase_revert(); + Player_Free(mf); + return NULL; + } + } + if(SL_LoadSamples()) { + _mm_iobase_revert(); + Player_Free_internal(mf); + return NULL; + } + if(Player_Init(mf)) { + _mm_iobase_revert(); + Player_Free_internal(mf); + mf=NULL; + } + _mm_iobase_revert(); + return mf; +} + +MIKMODAPI MODULE* Player_LoadGeneric(MREADER *reader,int maxchan,BOOL curious) +{ + MODULE* result; + + MUTEX_LOCK(vars); + MUTEX_LOCK(lists); + result=Player_LoadGeneric_internal(reader,maxchan,curious); + MUTEX_UNLOCK(lists); + MUTEX_UNLOCK(vars); + + return result; +} + +/* Loads a module given a file pointer. + File is loaded from the current file seek position. */ +MIKMODAPI MODULE* Player_LoadFP(int fp,int maxchan,BOOL curious) +{ + MODULE* result=NULL; + struct MREADER* reader=_mm_new_file_reader (fp); + + if (reader) { + result=Player_LoadGeneric(reader,maxchan,curious); + _mm_delete_file_reader(reader); + } + return result; +} + +/* Open a module via its filename. The loader will initialize the specified + song-player 'player'. */ +MIKMODAPI MODULE* Player_Load(CHAR* filename,int maxchan,BOOL curious) +{ + int fp; + MODULE *mf=NULL; + + if((fp=_mm_fopen(filename,O_RDONLY))) { + mf=Player_LoadFP(fp,maxchan,curious); + _mm_fclose(fp); + } + return mf; +} + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/playercode/mlreg.c b/apps/plugins/mikmod/playercode/mlreg.c new file mode 100644 index 0000000..575f17b --- /dev/null +++ b/apps/plugins/mikmod/playercode/mlreg.c @@ -0,0 +1,65 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: mlreg.c,v 1.2 2004/01/28 00:49:56 raph Exp $ + + Routine for registering all loaders in libmikmod for the current platform. + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mikmod_internals.h" + +void MikMod_RegisterAllLoaders_internal(void) +{ + _mm_registerloader(&load_669); + _mm_registerloader(&load_amf); + _mm_registerloader(&load_asy); + _mm_registerloader(&load_dsm); + _mm_registerloader(&load_far); + _mm_registerloader(&load_gdm); + _mm_registerloader(&load_it); + _mm_registerloader(&load_imf); + _mm_registerloader(&load_mod); + _mm_registerloader(&load_med); + _mm_registerloader(&load_mtm); +/* _mm_registerloader(&load_okt); */ + _mm_registerloader(&load_s3m); + _mm_registerloader(&load_stm); + _mm_registerloader(&load_stx); + _mm_registerloader(&load_ult); + _mm_registerloader(&load_uni); + _mm_registerloader(&load_xm); + + _mm_registerloader(&load_m15); +} + +void MikMod_RegisterAllLoaders(void) +{ + MUTEX_LOCK(lists); + MikMod_RegisterAllLoaders_internal(); + MUTEX_UNLOCK(lists); +} +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/playercode/mlutil.c b/apps/plugins/mikmod/playercode/mlutil.c new file mode 100644 index 0000000..0746a13 --- /dev/null +++ b/apps/plugins/mikmod/playercode/mlutil.c @@ -0,0 +1,336 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS + for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: mlutil.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $ + + Utility functions for the module loader + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Shared tracker identifiers */ + +CHAR *STM_Signatures[STM_NTRACKERS] = { + "!Scream!", + "BMOD2STM", + "WUZAMOD!" +}; + +CHAR *STM_Version[STM_NTRACKERS] = { + "Screamtracker 2", + "Converted by MOD2STM (STM format)", + "Wuzamod (STM format)" +}; + +/*========== Shared loader variables */ + +SBYTE remap[UF_MAXCHAN]; /* for removing empty channels */ +UBYTE* poslookup=NULL; /* lookup table for pattern jumps after blank + pattern removal */ +UBYTE poslookupcnt; +UWORD* origpositions=NULL; + +BOOL filters; /* resonant filters in use */ +UBYTE activemacro; /* active midi macro number for Sxx,xx<80h */ +UBYTE filtermacros[UF_MAXMACRO]; /* midi macro settings */ +FILTER filtersettings[UF_MAXFILTER]; /* computed filter settings */ + +/*========== Linear periods stuff */ + +int* noteindex=NULL; /* remap value for linear period modules */ +static int noteindexcount=0; + +int *AllocLinear(void) +{ + if(of.numsmp>noteindexcount) { + noteindexcount=of.numsmp; + noteindex=realloc(noteindex,noteindexcount*sizeof(int)); + } + return noteindex; +} + +void FreeLinear(void) +{ + if(noteindex) { + free(noteindex); + noteindex=NULL; + } + noteindexcount=0; +} + +int speed_to_finetune(ULONG speed,int sample) +{ + int ctmp=0,tmp,note=1,finetune=0; + + speed>>=1; + while((tmp=getfrequency(of.flags,getlinearperiod(note<<1,0)))speed) + tmp=getfrequency(of.flags,getlinearperiod(note<<1,--finetune)); + else { + note--; + while(ctmp>4; + + /* process S3M / IT specific command structure */ + + if(cmd!=255) { + switch(cmd) { + case 1: /* Axx set speed to xx */ + UniEffect(UNI_S3MEFFECTA,inf); + break; + case 2: /* Bxx position jump */ + if (inf>4)*10+(inf&0xf)); + else + UniPTEffect(0xd,inf); + break; + case 4: /* Dxy volumeslide */ + UniEffect(UNI_S3MEFFECTD,inf); + break; + case 5: /* Exy toneslide down */ + UniEffect(UNI_S3MEFFECTE,inf); + break; + case 6: /* Fxy toneslide up */ + UniEffect(UNI_S3MEFFECTF,inf); + break; + case 7: /* Gxx Tone portamento, speed xx */ + if (flags & S3MIT_OLDSTYLE) + UniPTEffect(0x3,inf); + else + UniEffect(UNI_ITEFFECTG,inf); + break; + case 8: /* Hxy vibrato */ + if (flags & S3MIT_OLDSTYLE) + UniPTEffect(0x4,inf); + else + UniEffect(UNI_ITEFFECTH,inf); + break; + case 9: /* Ixy tremor, ontime x, offtime y */ + if (flags & S3MIT_OLDSTYLE) + UniEffect(UNI_S3MEFFECTI,inf); + else + UniEffect(UNI_ITEFFECTI,inf); + break; + case 0xa: /* Jxy arpeggio */ + UniPTEffect(0x0,inf); + break; + case 0xb: /* Kxy Dual command H00 & Dxy */ + if (flags & S3MIT_OLDSTYLE) + UniPTEffect(0x4,0); + else + UniEffect(UNI_ITEFFECTH,0); + UniEffect(UNI_S3MEFFECTD,inf); + break; + case 0xc: /* Lxy Dual command G00 & Dxy */ + if (flags & S3MIT_OLDSTYLE) + UniPTEffect(0x3,0); + else + UniEffect(UNI_ITEFFECTG,0); + UniEffect(UNI_S3MEFFECTD,inf); + break; + case 0xd: /* Mxx Set Channel Volume */ + UniEffect(UNI_ITEFFECTM,inf); + break; + case 0xe: /* Nxy Slide Channel Volume */ + UniEffect(UNI_ITEFFECTN,inf); + break; + case 0xf: /* Oxx set sampleoffset xx00h */ + UniPTEffect(0x9,inf); + break; + case 0x10: /* Pxy Slide Panning Commands */ + UniEffect(UNI_ITEFFECTP,inf); + break; + case 0x11: /* Qxy Retrig (+volumeslide) */ + UniWriteByte(UNI_S3MEFFECTQ); + if(inf && !lo && !(flags & S3MIT_OLDSTYLE)) + UniWriteByte(1); + else + UniWriteByte(inf); + break; + case 0x12: /* Rxy tremolo speed x, depth y */ + UniEffect(UNI_S3MEFFECTR,inf); + break; + case 0x13: /* Sxx special commands */ + if (inf>=0xf0) { + /* change resonant filter settings if necessary */ + if((filters)&&((inf&0xf)!=activemacro)) { + activemacro=inf&0xf; + for(inf=0;inf<0x80;inf++) + filtersettings[inf].filter=filtermacros[activemacro]; + } + } else { + /* Scream Tracker does not have samples larger than + 64 Kb, thus doesn't need the SAx effect */ + if ((flags & S3MIT_SCREAM) && ((inf & 0xf0) == 0xa0)) + break; + + UniEffect(UNI_ITEFFECTS0,inf); + } + break; + case 0x14: /* Txx tempo */ + if(inf>=0x20) + UniEffect(UNI_S3MEFFECTT,inf); + else { + if(!(flags & S3MIT_OLDSTYLE)) + /* IT Tempo slide */ + UniEffect(UNI_ITEFFECTT,inf); + } + break; + case 0x15: /* Uxy Fine Vibrato speed x, depth y */ + if(flags & S3MIT_OLDSTYLE) + UniEffect(UNI_S3MEFFECTU,inf); + else + UniEffect(UNI_ITEFFECTU,inf); + break; + case 0x16: /* Vxx Set Global Volume */ + UniEffect(UNI_XMEFFECTG,inf); + break; + case 0x17: /* Wxy Global Volume Slide */ + UniEffect(UNI_ITEFFECTW,inf); + break; + case 0x18: /* Xxx amiga command 8xx */ + if(flags & S3MIT_OLDSTYLE) { + if(inf>128) + UniEffect(UNI_ITEFFECTS0,0x91); /* surround */ + else + UniPTEffect(0x8,(inf==128)?255:(inf<<1)); + } else + UniPTEffect(0x8,inf); + break; + case 0x19: /* Yxy Panbrello speed x, depth y */ + UniEffect(UNI_ITEFFECTY,inf); + break; + case 0x1a: /* Zxx midi/resonant filters */ + if(filtersettings[inf].filter) { + UniWriteByte(UNI_ITEFFECTZ); + UniWriteByte(filtersettings[inf].filter); + UniWriteByte(filtersettings[inf].inf); + } + break; + } + } +} + +/*========== Unitrk stuff */ + +/* Generic effect writing routine */ +void UniEffect(UWORD eff,UWORD dat) +{ + if((!eff)||(eff>=UNI_LAST)) return; + + UniWriteByte(eff); + if(unioperands[eff]==2) + UniWriteWord(dat); + else + UniWriteByte(dat); +} + +/* Appends UNI_PTEFFECTX opcode to the unitrk stream. */ +void UniPTEffect(UBYTE eff, UBYTE dat) +{ +#ifdef MIKMOD_DEBUG + if (eff>=0x10) + fprintf(stderr,"UniPTEffect called with incorrect eff value %d\n",eff); + else +#endif + if((eff)||(dat)||(of.flags&UF_ARPMEM)) UniEffect(UNI_PTEFFECT0+eff,dat); +} + +/* Appends UNI_VOLEFFECT + effect/dat to unistream. */ +void UniVolEffect(UWORD eff,UBYTE dat) +{ + if((eff)||(dat)) { /* don't write empty effect */ + UniWriteByte(UNI_VOLEFFECTS); + UniWriteByte(eff);UniWriteByte(dat); + } +} + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/playercode/mplayer.c b/apps/plugins/mikmod/playercode/mplayer.c new file mode 100644 index 0000000..fa38a80 --- /dev/null +++ b/apps/plugins/mikmod/playercode/mplayer.c @@ -0,0 +1,3546 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS + for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: mplayer.c,v 1.5 2004/01/31 22:40:22 raph Exp $ + + The Protracker Player Driver + + The protracker driver supports all base Protracker 3.x commands and features. + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#ifdef SRANDOM_IN_MATH_H +#include +#else +#include +#endif + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +extern long int random(void); +#endif + +/* The currently playing module */ +/* This variable should better be static, but it would break the ABI, so this + will wait */ +/*static*/ MODULE *pf = NULL; + +#define HIGH_OCTAVE 2 /* number of above-range octaves */ + +static UWORD oldperiods[OCTAVE*2]={ + 0x6b00, 0x6800, 0x6500, 0x6220, 0x5f50, 0x5c80, + 0x5a00, 0x5740, 0x54d0, 0x5260, 0x5010, 0x4dc0, + 0x4b90, 0x4960, 0x4750, 0x4540, 0x4350, 0x4160, + 0x3f90, 0x3dc0, 0x3c10, 0x3a40, 0x38b0, 0x3700 +}; + +static UBYTE VibratoTable[32]={ + 0, 24, 49, 74, 97,120,141,161,180,197,212,224,235,244,250,253, + 255,253,250,244,235,224,212,197,180,161,141,120, 97, 74, 49, 24 +}; + +static UBYTE avibtab[128]={ + 0, 1, 3, 4, 6, 7, 9,10,12,14,15,17,18,20,21,23, + 24,25,27,28,30,31,32,34,35,36,38,39,40,41,42,44, + 45,46,47,48,49,50,51,52,53,54,54,55,56,57,57,58, + 59,59,60,60,61,61,62,62,62,63,63,63,63,63,63,63, + 64,63,63,63,63,63,63,63,62,62,62,61,61,60,60,59, + 59,58,57,57,56,55,54,54,53,52,51,50,49,48,47,46, + 45,44,42,41,40,39,38,36,35,34,32,31,30,28,27,25, + 24,23,21,20,18,17,15,14,12,10, 9, 7, 6, 4, 3, 1 +}; + +/* Triton's linear periods to frequency translation table (for XM modules) */ +static ULONG lintab[768]={ + 535232,534749,534266,533784,533303,532822,532341,531861, + 531381,530902,530423,529944,529466,528988,528511,528034, + 527558,527082,526607,526131,525657,525183,524709,524236, + 523763,523290,522818,522346,521875,521404,520934,520464, + 519994,519525,519057,518588,518121,517653,517186,516720, + 516253,515788,515322,514858,514393,513929,513465,513002, + 512539,512077,511615,511154,510692,510232,509771,509312, + 508852,508393,507934,507476,507018,506561,506104,505647, + 505191,504735,504280,503825,503371,502917,502463,502010, + 501557,501104,500652,500201,499749,499298,498848,498398, + 497948,497499,497050,496602,496154,495706,495259,494812, + 494366,493920,493474,493029,492585,492140,491696,491253, + 490809,490367,489924,489482,489041,488600,488159,487718, + 487278,486839,486400,485961,485522,485084,484647,484210, + 483773,483336,482900,482465,482029,481595,481160,480726, + 480292,479859,479426,478994,478562,478130,477699,477268, + 476837,476407,475977,475548,475119,474690,474262,473834, + 473407,472979,472553,472126,471701,471275,470850,470425, + 470001,469577,469153,468730,468307,467884,467462,467041, + 466619,466198,465778,465358,464938,464518,464099,463681, + 463262,462844,462427,462010,461593,461177,460760,460345, + 459930,459515,459100,458686,458272,457859,457446,457033, + 456621,456209,455797,455386,454975,454565,454155,453745, + 453336,452927,452518,452110,451702,451294,450887,450481, + 450074,449668,449262,448857,448452,448048,447644,447240, + 446836,446433,446030,445628,445226,444824,444423,444022, + 443622,443221,442821,442422,442023,441624,441226,440828, + 440430,440033,439636,439239,438843,438447,438051,437656, + 437261,436867,436473,436079,435686,435293,434900,434508, + 434116,433724,433333,432942,432551,432161,431771,431382, + 430992,430604,430215,429827,429439,429052,428665,428278, + 427892,427506,427120,426735,426350,425965,425581,425197, + 424813,424430,424047,423665,423283,422901,422519,422138, + 421757,421377,420997,420617,420237,419858,419479,419101, + 418723,418345,417968,417591,417214,416838,416462,416086, + 415711,415336,414961,414586,414212,413839,413465,413092, + 412720,412347,411975,411604,411232,410862,410491,410121, + 409751,409381,409012,408643,408274,407906,407538,407170, + 406803,406436,406069,405703,405337,404971,404606,404241, + 403876,403512,403148,402784,402421,402058,401695,401333, + 400970,400609,400247,399886,399525,399165,398805,398445, + 398086,397727,397368,397009,396651,396293,395936,395579, + 395222,394865,394509,394153,393798,393442,393087,392733, + 392378,392024,391671,391317,390964,390612,390259,389907, + 389556,389204,388853,388502,388152,387802,387452,387102, + 386753,386404,386056,385707,385359,385012,384664,384317, + 383971,383624,383278,382932,382587,382242,381897,381552, + 381208,380864,380521,380177,379834,379492,379149,378807, + 378466,378124,377783,377442,377102,376762,376422,376082, + 375743,375404,375065,374727,374389,374051,373714,373377, + 373040,372703,372367,372031,371695,371360,371025,370690, + 370356,370022,369688,369355,369021,368688,368356,368023, + 367691,367360,367028,366697,366366,366036,365706,365376, + 365046,364717,364388,364059,363731,363403,363075,362747, + 362420,362093,361766,361440,361114,360788,360463,360137, + 359813,359488,359164,358840,358516,358193,357869,357547, + 357224,356902,356580,356258,355937,355616,355295,354974, + 354654,354334,354014,353695,353376,353057,352739,352420, + 352103,351785,351468,351150,350834,350517,350201,349885, + 349569,349254,348939,348624,348310,347995,347682,347368, + 347055,346741,346429,346116,345804,345492,345180,344869, + 344558,344247,343936,343626,343316,343006,342697,342388, + 342079,341770,341462,341154,340846,340539,340231,339924, + 339618,339311,339005,338700,338394,338089,337784,337479, + 337175,336870,336566,336263,335959,335656,335354,335051, + 334749,334447,334145,333844,333542,333242,332941,332641, + 332341,332041,331741,331442,331143,330844,330546,330247, + 329950,329652,329355,329057,328761,328464,328168,327872, + 327576,327280,326985,326690,326395,326101,325807,325513, + 325219,324926,324633,324340,324047,323755,323463,323171, + 322879,322588,322297,322006,321716,321426,321136,320846, + 320557,320267,319978,319690,319401,319113,318825,318538, + 318250,317963,317676,317390,317103,316817,316532,316246, + 315961,315676,315391,315106,314822,314538,314254,313971, + 313688,313405,313122,312839,312557,312275,311994,311712, + 311431,311150,310869,310589,310309,310029,309749,309470, + 309190,308911,308633,308354,308076,307798,307521,307243, + 306966,306689,306412,306136,305860,305584,305308,305033, + 304758,304483,304208,303934,303659,303385,303112,302838, + 302565,302292,302019,301747,301475,301203,300931,300660, + 300388,300117,299847,299576,299306,299036,298766,298497, + 298227,297958,297689,297421,297153,296884,296617,296349, + 296082,295815,295548,295281,295015,294749,294483,294217, + 293952,293686,293421,293157,292892,292628,292364,292100, + 291837,291574,291311,291048,290785,290523,290261,289999, + 289737,289476,289215,288954,288693,288433,288173,287913, + 287653,287393,287134,286875,286616,286358,286099,285841, + 285583,285326,285068,284811,284554,284298,284041,283785, + 283529,283273,283017,282762,282507,282252,281998,281743, + 281489,281235,280981,280728,280475,280222,279969,279716, + 279464,279212,278960,278708,278457,278206,277955,277704, + 277453,277203,276953,276703,276453,276204,275955,275706, + 275457,275209,274960,274712,274465,274217,273970,273722, + 273476,273229,272982,272736,272490,272244,271999,271753, + 271508,271263,271018,270774,270530,270286,270042,269798, + 269555,269312,269069,268826,268583,268341,268099,267857 +}; + +#define LOGFAC 2*16 +static UWORD logtab[104]={ + LOGFAC*907,LOGFAC*900,LOGFAC*894,LOGFAC*887, + LOGFAC*881,LOGFAC*875,LOGFAC*868,LOGFAC*862, + LOGFAC*856,LOGFAC*850,LOGFAC*844,LOGFAC*838, + LOGFAC*832,LOGFAC*826,LOGFAC*820,LOGFAC*814, + LOGFAC*808,LOGFAC*802,LOGFAC*796,LOGFAC*791, + LOGFAC*785,LOGFAC*779,LOGFAC*774,LOGFAC*768, + LOGFAC*762,LOGFAC*757,LOGFAC*752,LOGFAC*746, + LOGFAC*741,LOGFAC*736,LOGFAC*730,LOGFAC*725, + LOGFAC*720,LOGFAC*715,LOGFAC*709,LOGFAC*704, + LOGFAC*699,LOGFAC*694,LOGFAC*689,LOGFAC*684, + LOGFAC*678,LOGFAC*675,LOGFAC*670,LOGFAC*665, + LOGFAC*660,LOGFAC*655,LOGFAC*651,LOGFAC*646, + LOGFAC*640,LOGFAC*636,LOGFAC*632,LOGFAC*628, + LOGFAC*623,LOGFAC*619,LOGFAC*614,LOGFAC*610, + LOGFAC*604,LOGFAC*601,LOGFAC*597,LOGFAC*592, + LOGFAC*588,LOGFAC*584,LOGFAC*580,LOGFAC*575, + LOGFAC*570,LOGFAC*567,LOGFAC*563,LOGFAC*559, + LOGFAC*555,LOGFAC*551,LOGFAC*547,LOGFAC*543, + LOGFAC*538,LOGFAC*535,LOGFAC*532,LOGFAC*528, + LOGFAC*524,LOGFAC*520,LOGFAC*516,LOGFAC*513, + LOGFAC*508,LOGFAC*505,LOGFAC*502,LOGFAC*498, + LOGFAC*494,LOGFAC*491,LOGFAC*487,LOGFAC*484, + LOGFAC*480,LOGFAC*477,LOGFAC*474,LOGFAC*470, + LOGFAC*467,LOGFAC*463,LOGFAC*460,LOGFAC*457, + LOGFAC*453,LOGFAC*450,LOGFAC*447,LOGFAC*443, + LOGFAC*440,LOGFAC*437,LOGFAC*434,LOGFAC*431 +}; + +static SBYTE PanbrelloTable[256]={ + 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23, + 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, + 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60, + 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, + 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26, + 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2, + 0,- 2,- 3,- 5,- 6,- 8,- 9,-11,-12,-14,-16,-17,-19,-20,-22,-23, + -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44, + -45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59, + -59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64, + -64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60, + -59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46, + -45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26, + -24,-23,-22,-20,-19,-17,-16,-14,-12,-11,- 9,- 8,- 6,- 5,- 3,- 2 +}; + +/* returns a random value between 0 and ceil-1, ceil must be a power of two */ +static int getrandom(int ceil) +{ +#ifdef HAVE_SRANDOM + return random()&(ceil-1); +#else + return (rand()*ceil)/(RAND_MAX+1.0); + //return ceil - 1; +#endif +} + +/* New Note Action Scoring System : + -------------------------------- + 1) total-volume (fadevol, chanvol, volume) is the main scorer. + 2) a looping sample is a bonus x2 + 3) a foreground channel is a bonus x4 + 4) an active envelope with keyoff is a handicap -x2 +*/ +static int MP_FindEmptyChannel(MODULE *mod) +{ + MP_VOICE *a; + ULONG t,k,tvol,pp; + + for (t=0;tvoice[t].main.kick==KICK_ABSENT)|| + (mod->voice[t].main.kick==KICK_ENV))&& + Voice_Stopped_internal(t)) + return t; + + tvol=0xffffffUL;t=-1;a=mod->voice; + for (k=0;kmain.s) + return k; + + if ((a->main.kick==KICK_ABSENT)||(a->main.kick==KICK_ENV)) { + pp=a->totalvol<<((a->main.s->flags&SF_LOOP)?1:0); + if ((a->master)&&(a==a->master->slave)) + pp<<=2; + + if (pp8000*7) return -1; + return t; +} + +static SWORD Interpolate(SWORD p,SWORD p1,SWORD p2,SWORD v1,SWORD v2) +{ + if ((p1==p2)||(p==p1)) return v1; + return v1+((SLONG)((p-p1)*(v2-v1))/(p2-p1)); +} + +UWORD getlinearperiod(UWORD note,ULONG fine) +{ + UWORD t; + + t=((20L+2*HIGH_OCTAVE)*OCTAVE+2-note)*32L-(fine>>1); + return t; +} + +static UWORD getlogperiod(UWORD note,ULONG fine) +{ + UWORD n,o; + UWORD p1,p2; + ULONG i; + + n=note%(2*OCTAVE); + o=note/(2*OCTAVE); + i=(n<<2)+(fine>>4); /* n*8 + fine/16 */ + + p1=logtab[i]; + p2=logtab[i+1]; + + return (Interpolate(fine>>4,0,15,p1,p2)>>o); +} + +static UWORD getoldperiod(UWORD note,ULONG speed) +{ + UWORD n,o; + + /* This happens sometimes on badly converted AMF, and old MOD */ + if (!speed) { +#ifdef MIKMOD_DEBUG + fprintf(stderr,"\rmplayer: getoldperiod() called with note=%d, speed=0 !\n",note); +#endif + return 4242; /* <- prevent divide overflow.. (42 hehe) */ + } + + n=note%(2*OCTAVE); + o=note/(2*OCTAVE); + return ((8363L*(ULONG)oldperiods[n])>>o)/speed; +} + +static UWORD GetPeriod(UWORD flags, UWORD note, ULONG speed) +{ + if (flags & UF_XMPERIODS) { + if (flags & UF_LINEAR) + return getlinearperiod(note, speed); + else + return getlogperiod(note, speed); + } else + return getoldperiod(note, speed); +} + +static SWORD InterpolateEnv(SWORD p,ENVPT *a,ENVPT *b) +{ + return (Interpolate(p,a->pos,b->pos,a->val,b->val)); +} + +static SWORD DoPan(SWORD envpan,SWORD pan) +{ + int newpan; + + newpan=pan+(((envpan-PAN_CENTER)*(128-abs(pan-PAN_CENTER)))/128); + + return (newpanPAN_RIGHT?PAN_RIGHT:newpan); +} + +static SWORD StartEnvelope(ENVPR *t,UBYTE flg,UBYTE pts,UBYTE susbeg,UBYTE susend,UBYTE beg,UBYTE end,ENVPT *p,UBYTE keyoff) +{ + t->flg=flg; + t->pts=pts; + t->susbeg=susbeg; + t->susend=susend; + t->beg=beg; + t->end=end; + t->env=p; + t->p=0; + t->a=0; + t->b=((t->flg&EF_SUSTAIN)&&(!(keyoff&KEY_OFF)))?0:1; + + /* Imago Orpheus sometimes stores an extra initial point in the envelope */ + if ((t->pts>=2)&&(t->env[0].pos==t->env[1].pos)) { + t->a++;t->b++; + } + + /* Fit in the envelope, still */ + if (t->a >= t->pts) + t->a = t->pts - 1; + if (t->b >= t->pts) + t->b = t->pts-1; + + return t->env[t->a].val; +} + +/* This procedure processes all envelope types, include volume, pitch, and + panning. Envelopes are defined by a set of points, each with a magnitude + [relating either to volume, panning position, or pitch modifier] and a tick + position. + + Envelopes work in the following manner: + + (a) Each tick the envelope is moved a point further in its progression. For + an accurate progression, magnitudes between two envelope points are + interpolated. + + (b) When progression reaches a defined point on the envelope, values are + shifted to interpolate between this point and the next, and checks for + loops or envelope end are done. + + Misc: + Sustain loops are loops that are only active as long as the keyoff flag is + clear. When a volume envelope terminates, so does the current fadeout. +*/ +static SWORD ProcessEnvelope(MP_VOICE *aout, ENVPR *t, SWORD v) +{ + if (t->flg & EF_ON) { + UBYTE a, b; /* actual points in the envelope */ + UWORD p; /* the 'tick counter' - real point being played */ + + a = t->a; + b = t->b; + p = t->p; + + /* + * Sustain loop on one point (XM type). + * Not processed if KEYOFF. + * Don't move and don't interpolate when the point is reached + */ + if ((t->flg & EF_SUSTAIN) && t->susbeg == t->susend && + (!(aout->main.keyoff & KEY_OFF) && p == t->env[t->susbeg].pos)) { + v = t->env[t->susbeg].val; + } else { + /* + * All following situations will require interpolation between + * two envelope points. + */ + + /* + * Sustain loop between two points (IT type). + * Not processed if KEYOFF. + */ + /* if we were on a loop point, loop now */ + if ((t->flg & EF_SUSTAIN) && !(aout->main.keyoff & KEY_OFF) && + a >= t->susend) { + a = t->susbeg; + b = (t->susbeg==t->susend)?a:a+1; + p = t->env[a].pos; + v = t->env[a].val; + } else + /* + * Regular loop. + * Be sure to correctly handle single point loops. + */ + if ((t->flg & EF_LOOP) && a >= t->end) { + a = t->beg; + b = t->beg == t->end ? a : a + 1; + p = t->env[a].pos; + v = t->env[a].val; + } else + /* + * Non looping situations. + */ + if (a != b) + v = InterpolateEnv(p, &t->env[a], &t->env[b]); + else + v = t->env[a].val; + + /* + * Start to fade if the volume envelope is finished. + */ + if (p >= t->env[t->pts - 1].pos) { + if (t->flg & EF_VOLENV) { + aout->main.keyoff |= KEY_FADE; + if (!v) + aout->main.fadevol = 0; + } + } else { + p++; + /* did pointer reach point b? */ + if (p >= t->env[b].pos) + a = b++; /* shift points a and b */ + } + t->a = a; + t->b = b; + t->p = p; + } + } + return v; +} + +/* XM linear period to frequency conversion */ +ULONG getfrequency(UWORD flags,ULONG period) +{ + if (flags & UF_LINEAR) { + SLONG shift = ((SLONG)period / 768) - HIGH_OCTAVE; + + if (shift >= 0) + return lintab[period % 768] >> shift; + else + return lintab[period % 768] << (-shift); + } else + return (8363L*1712L)/(period?period:1); +} + +/*========== Protracker effects */ + +static void DoArpeggio(UWORD tick, UWORD flags, MP_CONTROL *a, UBYTE style) +{ + UBYTE note=a->main.note; + + if (a->arpmem) { + switch (style) { + case 0: /* mod style: N, N+x, N+y */ + switch (tick % 3) { + /* case 0: unchanged */ + case 1: + note += (a->arpmem >> 4); + break; + case 2: + note += (a->arpmem & 0xf); + break; + } + break; + case 3: /* okt arpeggio 3: N-x, N, N+y */ + switch (tick % 3) { + case 0: + note -= (a->arpmem >> 4); + break; + /* case 1: unchanged */ + case 2: + note += (a->arpmem & 0xf); + break; + } + break; + case 4: /* okt arpeggio 4: N, N+y, N, N-x */ + switch (tick % 4) { + /* case 0, case 2: unchanged */ + case 1: + note += (a->arpmem & 0xf); + break; + case 3: + note -= (a->arpmem >> 4); + break; + } + break; + case 5: /* okt arpeggio 5: N-x, N+y, N, and nothing at tick 0 */ + if (!tick) + break; + switch (tick % 3) { + /* case 0: unchanged */ + case 1: + note -= (a->arpmem >> 4); + break; + case 2: + note += (a->arpmem & 0xf); + break; + } + break; + } + a->main.period = GetPeriod(flags, (UWORD)note << 1, a->speed); + a->ownper = 1; + } +} + +static int DoPTEffect0(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)mod; + (void)channel; + + dat = UniGetByte(); + if (!tick) { + if (!dat && (flags & UF_ARPMEM)) + dat=a->arpmem; + else + a->arpmem=dat; + } + if (a->main.period) + DoArpeggio(tick, flags, a, 0); + + return 0; +} + +static int DoPTEffect1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)flags; + (void)mod; + (void)channel; + + dat = UniGetByte(); + if (!tick && dat) + a->slidespeed = (UWORD)dat << 2; + if (a->main.period) + if (tick) + a->tmpperiod -= a->slidespeed; + + return 0; +} + +static int DoPTEffect2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)flags; + (void)mod; + (void)channel; + + dat = UniGetByte(); + if (!tick && dat) + a->slidespeed = (UWORD)dat << 2; + if (a->main.period) + if (tick) + a->tmpperiod += a->slidespeed; + + return 0; +} + +static void DoToneSlide(UWORD tick, MP_CONTROL *a) +{ + if (!a->main.fadevol) + a->main.kick = (a->main.kick == KICK_NOTE)? KICK_NOTE : KICK_KEYOFF; + else + a->main.kick = (a->main.kick == KICK_NOTE)? KICK_ENV : KICK_ABSENT; + + if (tick != 0) { + int dist; + + /* We have to slide a->main.period towards a->wantedperiod, so compute + the difference between those two values */ + dist=a->main.period-a->wantedperiod; + + /* if they are equal or if portamentospeed is too big ...*/ + if (dist == 0 || a->portspeed > abs(dist)) + /* ...make tmpperiod equal tperiod */ + a->tmpperiod=a->main.period=a->wantedperiod; + else if (dist>0) { + a->tmpperiod-=a->portspeed; + a->main.period-=a->portspeed; /* dist>0, slide up */ + } else { + a->tmpperiod+=a->portspeed; + a->main.period+=a->portspeed; /* dist<0, slide down */ + } + } else + a->tmpperiod=a->main.period; + a->ownper = 1; +} + +static int DoPTEffect3(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)flags; + (void)mod; + (void)channel; + + dat=UniGetByte(); + if ((!tick)&&(dat)) a->portspeed=(UWORD)dat<<2; + if (a->main.period) + DoToneSlide(tick, a); + + return 0; +} + +static void DoVibrato(UWORD tick, MP_CONTROL *a) +{ + UBYTE q; + UWORD temp = 0; /* silence warning */ + + if (!tick) + return; + + q=(a->vibpos>>2)&0x1f; + + switch (a->wavecontrol&3) { + case 0: /* sine */ + temp=VibratoTable[q]; + break; + case 1: /* ramp down */ + q<<=3; + if (a->vibpos<0) q=255-q; + temp=q; + break; + case 2: /* square wave */ + temp=255; + break; + case 3: /* random wave */ + temp=getrandom(256); + break; + } + + temp*=a->vibdepth; + temp>>=7;temp<<=2; + + if (a->vibpos>=0) + a->main.period=a->tmpperiod+temp; + else + a->main.period=a->tmpperiod-temp; + a->ownper = 1; + + if (tick != 0) + a->vibpos+=a->vibspd; +} + +static int DoPTEffect4(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)flags; + (void)mod; + (void)channel; + + dat=UniGetByte(); + if (!tick) { + if (dat&0x0f) a->vibdepth=dat&0xf; + if (dat&0xf0) a->vibspd=(dat&0xf0)>>2; + } + if (a->main.period) + DoVibrato(tick, a); + + return 0; +} + +static void DoVolSlide(MP_CONTROL *a, UBYTE dat) +{ + if (dat&0xf) { + a->tmpvolume-=(dat&0x0f); + if (a->tmpvolume<0) + a->tmpvolume=0; + } else { + a->tmpvolume+=(dat>>4); + if (a->tmpvolume>64) + a->tmpvolume=64; + } +} + +static int DoPTEffect5(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)flags; + (void)mod; + (void)channel; + + dat=UniGetByte(); + if (a->main.period) + DoToneSlide(tick, a); + + if (tick) + DoVolSlide(a, dat); + + return 0; +} + +/* DoPTEffect6 after DoPTEffectA */ + +static int DoPTEffect7(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + UBYTE q; + UWORD temp = 0; /* silence warning */ + (void)flags; + (void)mod; + (void)channel; + + dat=UniGetByte(); + if (!tick) { + if (dat&0x0f) a->trmdepth=dat&0xf; + if (dat&0xf0) a->trmspd=(dat&0xf0)>>2; + } + if (a->main.period) { + q=(a->trmpos>>2)&0x1f; + + switch ((a->wavecontrol>>4)&3) { + case 0: /* sine */ + temp=VibratoTable[q]; + break; + case 1: /* ramp down */ + q<<=3; + if (a->trmpos<0) q=255-q; + temp=q; + break; + case 2: /* square wave */ + temp=255; + break; + case 3: /* random wave */ + temp=getrandom(256); + break; + } + temp*=a->trmdepth; + temp>>=6; + + if (a->trmpos>=0) { + a->volume=a->tmpvolume+temp; + if (a->volume>64) a->volume=64; + } else { + a->volume=a->tmpvolume-temp; + if (a->volume<0) a->volume=0; + } + a->ownvol = 1; + + if (tick) + a->trmpos+=a->trmspd; + } + + return 0; +} + +static int DoPTEffect8(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)flags; + (void)tick; + + dat = UniGetByte(); + if (mod->panflag) + a->main.panning = mod->panning[channel] = dat; + + return 0; +} + +static int DoPTEffect9(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)flags; + (void)mod; + (void)channel; + + dat=UniGetByte(); + if (!tick) { + if (dat) a->soffset=(UWORD)dat<<8; + a->main.start=a->hioffset|a->soffset; + + if ((a->main.s)&&(a->main.start>a->main.s->length)) + a->main.start=a->main.s->flags&(SF_LOOP|SF_BIDI)? + a->main.s->loopstart:a->main.s->length; + } + + return 0; +} + +static int DoPTEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)flags; + (void)mod; + (void)channel; + + dat=UniGetByte(); + if (tick) + DoVolSlide(a, dat); + + return 0; +} + +static int DoPTEffect6(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + if (a->main.period) + DoVibrato(tick, a); + DoPTEffectA(tick, flags, a, mod, channel); + + return 0; +} + +static int DoPTEffectB(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)a; + (void)channel; + + dat=UniGetByte(); + + if (tick || mod->patdly2) + return 0; + + /* Vincent Voois uses a nasty trick in "Universal Bolero" */ + if (dat == mod->sngpos && mod->patbrk == mod->patpos) + return 0; + + if (!mod->loop && !mod->patbrk && + (dat < mod->sngpos || + (mod->sngpos == (mod->numpos - 1) && !mod->patbrk) || + (dat == mod->sngpos && (flags & UF_NOWRAP)) + )) { + /* if we don't loop, better not to skip the end of the + pattern, after all... so: + mod->patbrk=0; */ + mod->posjmp=3; + } else { + /* if we were fading, adjust... */ + if (mod->sngpos == (mod->numpos-1)) + mod->volume=mod->initvolume>128?128:mod->initvolume; + mod->sngpos=dat; + mod->posjmp=2; + mod->patpos=0; + } + + return 0; +} + +static int DoPTEffectC(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)flags; + (void)mod; + (void)channel; + + dat=UniGetByte(); + if (tick) return 0; + if (dat==(UBYTE)-1) a->anote=dat=0; /* note cut */ + else if (dat>64) dat=64; + a->tmpvolume=dat; + + return 0; +} + +static int DoPTEffectD(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)a; + (void)channel; + + dat=UniGetByte(); + if ((tick)||(mod->patdly2)) return 0; + if ((mod->positions[mod->sngpos]!=LAST_PATTERN)&& + (dat>mod->pattrows[mod->positions[mod->sngpos]])) + dat=mod->pattrows[mod->positions[mod->sngpos]]; + mod->patbrk=dat; + if (!mod->posjmp) { + /* don't ask me to explain this code - it makes + backwards.s3m and children.xm (heretic's version) play + correctly, among others. Take that for granted, or write + the page of comments yourself... you might need some + aspirin - Miod */ + if ((mod->sngpos==mod->numpos-1)&&(dat)&&((mod->loop)|| + (mod->positions[mod->sngpos]==(mod->numpat-1) + && !(flags&UF_NOWRAP)))) { + mod->sngpos=0; + mod->posjmp=2; + } else + mod->posjmp=3; + } + + return 0; +} + +static void DoEEffects(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, + SWORD channel, UBYTE dat) +{ + UBYTE nib = dat & 0xf; + + switch (dat>>4) { + case 0x0: /* hardware filter toggle, not supported */ + break; + case 0x1: /* fineslide up */ + if (a->main.period) + if (!tick) + a->tmpperiod-=(nib<<2); + break; + case 0x2: /* fineslide dn */ + if (a->main.period) + if (!tick) + a->tmpperiod+=(nib<<2); + break; + case 0x3: /* glissando ctrl */ + a->glissando=nib; + break; + case 0x4: /* set vibrato waveform */ + a->wavecontrol&=0xf0; + a->wavecontrol|=nib; + break; + case 0x5: /* set finetune */ + if (a->main.period) { + if (flags&UF_XMPERIODS) + a->speed=nib+128; + else + a->speed=finetune[nib]; + a->tmpperiod=GetPeriod(flags, (UWORD)a->main.note<<1,a->speed); + } + break; + case 0x6: /* set patternloop */ + if (tick) + break; + if (nib) { /* set reppos or repcnt ? */ + /* set repcnt, so check if repcnt already is set, which means we + are already looping */ + if (a->pat_repcnt) + a->pat_repcnt--; /* already looping, decrease counter */ + else { +#if 0 + /* this would make walker.xm, shipped with Xsoundtracker, + play correctly, but it's better to remain compatible + with FT2 */ + if ((!(flags&UF_NOWRAP))||(a->pat_reppos!=POS_NONE)) +#endif + a->pat_repcnt=nib; /* not yet looping, so set repcnt */ + } + + if (a->pat_repcnt) { /* jump to reppos if repcnt>0 */ + if (a->pat_reppos==POS_NONE) + a->pat_reppos=mod->patpos-1; + if (a->pat_reppos==-1) { + mod->pat_repcrazy=1; + mod->patpos=0; + } else + mod->patpos=a->pat_reppos; + } else a->pat_reppos=POS_NONE; + } else + a->pat_reppos=mod->patpos-1; /* set reppos - can be (-1) */ + break; + case 0x7: /* set tremolo waveform */ + a->wavecontrol&=0x0f; + a->wavecontrol|=nib<<4; + break; + case 0x8: /* set panning */ + if (mod->panflag) { + if (nib<=8) nib<<=4; + else nib*=17; + a->main.panning=mod->panning[channel]=nib; + } + break; + case 0x9: /* retrig note */ + /* do not retrigger on tick 0, until we are emulating FT2 and effect + data is zero */ + if (!tick && !((flags & UF_FT2QUIRKS) && (!nib))) + break; + /* only retrigger if data nibble > 0, or if tick 0 (FT2 compat) */ + if (nib || !tick) { + if (!a->retrig) { + /* when retrig counter reaches 0, reset counter and restart + the sample */ + if (a->main.period) a->main.kick=KICK_NOTE; + a->retrig=nib; + } + a->retrig--; /* countdown */ + } + break; + case 0xa: /* fine volume slide up */ + if (tick) + break; + a->tmpvolume+=nib; + if (a->tmpvolume>64) a->tmpvolume=64; + break; + case 0xb: /* fine volume slide dn */ + if (tick) + break; + a->tmpvolume-=nib; + if (a->tmpvolume<0)a->tmpvolume=0; + break; + case 0xc: /* cut note */ + /* When tick reaches the cut-note value, turn the volume to + zero (just like on the amiga) */ + if (tick>=nib) + a->tmpvolume=0; /* just turn the volume down */ + break; + case 0xd: /* note delay */ + /* delay the start of the sample until tick==nib */ + if (!tick) + a->main.notedelay=nib; + else if (a->main.notedelay) + a->main.notedelay--; + break; + case 0xe: /* pattern delay */ + if (!tick) + if (!mod->patdly2) + mod->patdly=nib+1; /* only once, when tick=0 */ + break; + case 0xf: /* invert loop, not supported */ + break; + } +} + +static int DoPTEffectE(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + DoEEffects(tick, flags, a, mod, channel, UniGetByte()); + + return 0; +} + +static int DoPTEffectF(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)flags; + (void)a; + (void)channel; + + dat=UniGetByte(); + if (tick||mod->patdly2) return 0; + if (mod->extspd&&(dat>=mod->bpmlimit)) + mod->bpm=dat; + else + if (dat) { + mod->sngspd=(dat>=mod->bpmlimit)?mod->bpmlimit-1:dat; + mod->vbtick=0; + } + + return 0; +} + +/*========== Scream Tracker effects */ + +static int DoS3MEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE speed; + (void)flags; + (void)a; + (void)channel; + + speed = UniGetByte(); + + if (tick || mod->patdly2) + return 0; + + if (speed > 128) + speed -= 128; + if (speed) { + mod->sngspd = speed; + mod->vbtick = 0; + } + + return 0; +} + +static void DoS3MVolSlide(UWORD tick, UWORD flags, MP_CONTROL *a, UBYTE inf) +{ + UBYTE lo, hi; + + if (inf) + a->s3mvolslide=inf; + else + inf=a->s3mvolslide; + + lo=inf&0xf; + hi=inf>>4; + + if (!lo) { + if ((tick)||(flags&UF_S3MSLIDES)) a->tmpvolume+=hi; + } else + if (!hi) { + if ((tick)||(flags&UF_S3MSLIDES)) a->tmpvolume-=lo; + } else + if (lo==0xf) { + if (!tick) a->tmpvolume+=(hi?hi:0xf); + } else + if (hi==0xf) { + if (!tick) a->tmpvolume-=(lo?lo:0xf); + } else + return; + + if (a->tmpvolume<0) + a->tmpvolume=0; + else if (a->tmpvolume>64) + a->tmpvolume=64; +} + +static int DoS3MEffectD(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + (void)mod; + (void)channel; + + DoS3MVolSlide(tick, flags, a, UniGetByte()); + + return 1; +} + +static void DoS3MSlideDn(UWORD tick, MP_CONTROL *a, UBYTE inf) +{ + UBYTE hi,lo; + + if (inf) + a->slidespeed=inf; + else + inf=a->slidespeed; + + hi=inf>>4; + lo=inf&0xf; + + if (hi==0xf) { + if (!tick) a->tmpperiod+=(UWORD)lo<<2; + } else + if (hi==0xe) { + if (!tick) a->tmpperiod+=lo; + } else { + if (tick) a->tmpperiod+=(UWORD)inf<<2; + } +} + +static int DoS3MEffectE(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)flags; + (void)mod; + (void)channel; + + dat=UniGetByte(); + if (a->main.period) + DoS3MSlideDn(tick, a,dat); + + return 0; +} + +static void DoS3MSlideUp(UWORD tick, MP_CONTROL *a, UBYTE inf) +{ + UBYTE hi,lo; + + if (inf) a->slidespeed=inf; + else inf=a->slidespeed; + + hi=inf>>4; + lo=inf&0xf; + + if (hi==0xf) { + if (!tick) a->tmpperiod-=(UWORD)lo<<2; + } else + if (hi==0xe) { + if (!tick) a->tmpperiod-=lo; + } else { + if (tick) a->tmpperiod-=(UWORD)inf<<2; + } +} + +static int DoS3MEffectF(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)flags; + (void)mod; + (void)channel; + + dat=UniGetByte(); + if (a->main.period) + DoS3MSlideUp(tick, a,dat); + + return 0; +} + +static int DoS3MEffectI(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE inf, on, off; + (void)flags; + (void)mod; + (void)channel; + + inf = UniGetByte(); + if (inf) + a->s3mtronof = inf; + else { + inf = a->s3mtronof; + if (!inf) + return 0; + } + + if (!tick) + return 0; + + on=(inf>>4)+1; + off=(inf&0xf)+1; + a->s3mtremor%=(on+off); + a->volume=(a->s3mtremortmpvolume:0; + a->ownvol=1; + a->s3mtremor++; + + return 0; +} + +static int DoS3MEffectQ(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE inf; + (void)mod; + (void)channel; + + inf = UniGetByte(); + if (a->main.period) { + if (inf) { + a->s3mrtgslide=inf>>4; + a->s3mrtgspeed=inf&0xf; + } + + /* only retrigger if low nibble > 0 */ + if (a->s3mrtgspeed>0) { + if (!a->retrig) { + /* when retrig counter reaches 0, reset counter and restart the + sample */ + if (a->main.kick!=KICK_NOTE) a->main.kick=KICK_KEYOFF; + a->retrig=a->s3mrtgspeed; + + if ((tick)||(flags&UF_S3MSLIDES)) { + switch (a->s3mrtgslide) { + case 1: + case 2: + case 3: + case 4: + case 5: + a->tmpvolume-=(1<<(a->s3mrtgslide-1)); + break; + case 6: + a->tmpvolume=(2*a->tmpvolume)/3; + break; + case 7: + a->tmpvolume>>=1; + break; + case 9: + case 0xa: + case 0xb: + case 0xc: + case 0xd: + a->tmpvolume+=(1<<(a->s3mrtgslide-9)); + break; + case 0xe: + a->tmpvolume=(3*a->tmpvolume)>>1; + break; + case 0xf: + a->tmpvolume=a->tmpvolume<<1; + break; + } + if (a->tmpvolume<0) + a->tmpvolume=0; + else if (a->tmpvolume>64) + a->tmpvolume=64; + } + } + a->retrig--; /* countdown */ + } + } + + return 0; +} + +static int DoS3MEffectR(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat, q; + UWORD temp=0; /* silence warning */ + (void)flags; + (void)mod; + (void)channel; + + dat = UniGetByte(); + if (!tick) { + if (dat&0x0f) a->trmdepth=dat&0xf; + if (dat&0xf0) a->trmspd=(dat&0xf0)>>2; + } + + q=(a->trmpos>>2)&0x1f; + + switch ((a->wavecontrol>>4)&3) { + case 0: /* sine */ + temp=VibratoTable[q]; + break; + case 1: /* ramp down */ + q<<=3; + if (a->trmpos<0) q=255-q; + temp=q; + break; + case 2: /* square wave */ + temp=255; + break; + case 3: /* random */ + temp=getrandom(256); + break; + } + + temp*=a->trmdepth; + temp>>=7; + + if (a->trmpos>=0) { + a->volume=a->tmpvolume+temp; + if (a->volume>64) a->volume=64; + } else { + a->volume=a->tmpvolume-temp; + if (a->volume<0) a->volume=0; + } + a->ownvol = 1; + + if (tick) + a->trmpos+=a->trmspd; + + return 0; +} + +static int DoS3MEffectT(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE tempo; + (void)flags; + (void)a; + (void)channel; + + tempo = UniGetByte(); + + if (tick || mod->patdly2) + return 0; + + mod->bpm = (tempo < 32) ? 32 : tempo; + + return 0; +} + +static int DoS3MEffectU(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat, q; + UWORD temp = 0; /* silence warning */ + (void)flags; + (void)mod; + (void)channel; + + dat = UniGetByte(); + if (!tick) { + if (dat&0x0f) a->vibdepth=dat&0xf; + if (dat&0xf0) a->vibspd=(dat&0xf0)>>2; + } else + if (a->main.period) { + q=(a->vibpos>>2)&0x1f; + + switch (a->wavecontrol&3) { + case 0: /* sine */ + temp=VibratoTable[q]; + break; + case 1: /* ramp down */ + q<<=3; + if (a->vibpos<0) q=255-q; + temp=q; + break; + case 2: /* square wave */ + temp=255; + break; + case 3: /* random */ + temp=getrandom(256); + break; + } + + temp*=a->vibdepth; + temp>>=8; + + if (a->vibpos>=0) + a->main.period=a->tmpperiod+temp; + else + a->main.period=a->tmpperiod-temp; + a->ownper = 1; + + a->vibpos+=a->vibspd; + } + + return 0; +} + +/*========== Envelope helpers */ + +static int DoKeyOff(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + (void)tick; + (void)flags; + (void)mod; + (void)channel; + + a->main.keyoff|=KEY_OFF; + if ((!(a->main.volflg&EF_ON))||(a->main.volflg&EF_LOOP)) + a->main.keyoff=KEY_KILL; + + return 0; +} + +static int DoKeyFade(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)flags; + (void)channel; + + dat=UniGetByte(); + if ((tick>=dat)||(tick==mod->sngspd-1)) { + a->main.keyoff=KEY_KILL; + if (!(a->main.volflg&EF_ON)) + a->main.fadevol=0; + } + + return 0; +} + +/*========== Fast Tracker effects */ + +/* DoXMEffect6 after DoXMEffectA */ + +static int DoXMEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE inf, lo, hi; + (void)flags; + (void)mod; + (void)channel; + + inf = UniGetByte(); + if (inf) + a->s3mvolslide = inf; + else + inf = a->s3mvolslide; + + if (tick) { + lo=inf&0xf; + hi=inf>>4; + + if (!hi) { + a->tmpvolume-=lo; + if (a->tmpvolume<0) a->tmpvolume=0; + } else { + a->tmpvolume+=hi; + if (a->tmpvolume>64) a->tmpvolume=64; + } + } + + return 0; +} + +static int DoXMEffect6(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + if (a->main.period) + DoVibrato(tick, a); + + return DoXMEffectA(tick, flags, a, mod, channel); +} + +static int DoXMEffectE1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)flags; + (void)mod; + (void)channel; + + dat=UniGetByte(); + if (!tick) { + if (dat) a->fportupspd=dat; + if (a->main.period) + a->tmpperiod-=(a->fportupspd<<2); + } + + return 0; +} + +static int DoXMEffectE2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)flags; + (void)mod; + (void)channel; + + dat=UniGetByte(); + if (!tick) { + if (dat) a->fportdnspd=dat; + if (a->main.period) + a->tmpperiod+=(a->fportdnspd<<2); + } + + return 0; +} + +static int DoXMEffectEA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)flags; + (void)mod; + (void)channel; + + dat=UniGetByte(); + if (!tick) + if (dat) a->fslideupspd=dat; + a->tmpvolume+=a->fslideupspd; + if (a->tmpvolume>64) a->tmpvolume=64; + + return 0; +} + +static int DoXMEffectEB(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)flags; + (void)mod; + (void)channel; + + dat=UniGetByte(); + if (!tick) + if (dat) a->fslidednspd=dat; + a->tmpvolume-=a->fslidednspd; + if (a->tmpvolume<0) a->tmpvolume=0; + + return 0; +} + +static int DoXMEffectG(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + (void)tick; + (void)flags; + (void)a; + (void)channel; + + mod->volume=UniGetByte()<<1; + if (mod->volume>128) mod->volume=128; + + return 0; +} + +static int DoXMEffectH(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE inf; + (void)flags; + (void)a; + (void)channel; + + inf = UniGetByte(); + + if (tick) { + if (inf) mod->globalslide=inf; + else inf=mod->globalslide; + if (inf & 0xf0) inf&=0xf0; + mod->volume=mod->volume+((inf>>4)-(inf&0xf))*2; + + if (mod->volume<0) + mod->volume=0; + else if (mod->volume>128) + mod->volume=128; + } + + return 0; +} + +static int DoXMEffectL(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)flags; + (void)mod; + (void)channel; + + dat=UniGetByte(); + if ((!tick)&&(a->main.i)) { + UWORD points; + INSTRUMENT *i=a->main.i; + MP_VOICE *aout; + + if ((aout=a->slave)) { + if (aout->venv.env) { + points=i->volenv[i->volpts-1].pos; + aout->venv.p=aout->venv.env[(dat>points)?points:dat].pos; + } + if (aout->penv.env) { + points=i->panenv[i->panpts-1].pos; + aout->penv.p=aout->penv.env[(dat>points)?points:dat].pos; + } + } + } + + return 0; +} + +static int DoXMEffectP(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE inf, lo, hi; + SWORD pan; + (void)flags; + (void)channel; + + inf = UniGetByte(); + if (!mod->panflag) + return 0; + + if (inf) + a->pansspd = inf; + else + inf =a->pansspd; + + if (tick) { + lo=inf&0xf; + hi=inf>>4; + + /* slide right has absolute priority */ + if (hi) + lo = 0; + + pan=((a->main.panning==PAN_SURROUND)?PAN_CENTER:a->main.panning)+hi-lo; + a->main.panning=(panPAN_RIGHT?PAN_RIGHT:pan); + } + + return 0; +} + +static int DoXMEffectX1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)flags; + (void)mod; + (void)channel; + + dat = UniGetByte(); + if (dat) + a->ffportupspd = dat; + else + dat = a->ffportupspd; + + if (a->main.period) + if (!tick) { + a->main.period-=dat; + a->tmpperiod-=dat; + a->ownper = 1; + } + + return 0; +} + +static int DoXMEffectX2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + (void)flags; + (void)mod; + (void)channel; + + dat = UniGetByte(); + if (dat) + a->ffportdnspd=dat; + else + dat = a->ffportdnspd; + + if (a->main.period) + if (!tick) { + a->main.period+=dat; + a->tmpperiod+=dat; + a->ownper = 1; + } + + return 0; +} + +/*========== Impulse Tracker effects */ + +static void DoITToneSlide(UWORD tick, MP_CONTROL *a, UBYTE dat) +{ + if (dat) + a->portspeed = dat; + + /* if we don't come from another note, ignore the slide and play the note + as is */ + if (!a->oldnote || !a->main.period) + return; + + if ((!tick)&&(a->newsamp)){ + a->main.kick=KICK_NOTE; + a->main.start=-1; + } else + a->main.kick=(a->main.kick==KICK_NOTE)?KICK_ENV:KICK_ABSENT; + + if (tick) { + int dist; + + /* We have to slide a->main.period towards a->wantedperiod, compute the + difference between those two values */ + dist=a->main.period-a->wantedperiod; + + /* if they are equal or if portamentospeed is too big... */ + if ((!dist)||((a->portspeed<<2)>abs(dist))) + /* ... make tmpperiod equal tperiod */ + a->tmpperiod=a->main.period=a->wantedperiod; + else + if (dist>0) { + a->tmpperiod-=a->portspeed<<2; + a->main.period-=a->portspeed<<2; /* dist>0 slide up */ + } else { + a->tmpperiod+=a->portspeed<<2; + a->main.period+=a->portspeed<<2; /* dist<0 slide down */ + } + } else + a->tmpperiod=a->main.period; + a->ownper=1; +} + +static int DoITEffectG(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + (void)flags; + (void)mod; + (void)channel; + + DoITToneSlide(tick, a, UniGetByte()); + + return 0; +} + +static void DoITVibrato(UWORD tick, MP_CONTROL *a, UBYTE dat) +{ + UBYTE q; + UWORD temp=0; + + if (!tick) { + if (dat&0x0f) a->vibdepth=dat&0xf; + if (dat&0xf0) a->vibspd=(dat&0xf0)>>2; + } + if (!a->main.period) + return; + + q=(a->vibpos>>2)&0x1f; + + switch (a->wavecontrol&3) { + case 0: /* sine */ + temp=VibratoTable[q]; + break; + case 1: /* square wave */ + temp=255; + break; + case 2: /* ramp down */ + q<<=3; + if (a->vibpos<0) q=255-q; + temp=q; + break; + case 3: /* random */ + temp=getrandom(256); + break; + } + + temp*=a->vibdepth; + temp>>=8; + temp<<=2; + + if (a->vibpos>=0) + a->main.period=a->tmpperiod+temp; + else + a->main.period=a->tmpperiod-temp; + a->ownper=1; + + a->vibpos+=a->vibspd; +} + +static int DoITEffectH(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + (void)flags; + (void)mod; + (void)channel; + + DoITVibrato(tick, a, UniGetByte()); + + return 0; +} + +static int DoITEffectI(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE inf, on, off; + (void)tick; + (void)flags; + (void)mod; + (void)channel; + + inf = UniGetByte(); + if (inf) + a->s3mtronof = inf; + else { + inf = a->s3mtronof; + if (!inf) + return 0; + } + + on=(inf>>4); + off=(inf&0xf); + + a->s3mtremor%=(on+off); + a->volume=(a->s3mtremortmpvolume:0; + a->ownvol = 1; + a->s3mtremor++; + + return 0; +} + +static int DoITEffectM(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + (void)tick; + (void)flags; + (void)mod; + (void)channel; + + a->main.chanvol=UniGetByte(); + if (a->main.chanvol>64) + a->main.chanvol=64; + else if (a->main.chanvol<0) + a->main.chanvol=0; + + return 0; +} + +static int DoITEffectN(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE inf, lo, hi; + (void)flags; + (void)mod; + (void)channel; + + inf = UniGetByte(); + + if (inf) + a->chanvolslide = inf; + else + inf = a->chanvolslide; + + lo=inf&0xf; + hi=inf>>4; + + if (!hi) + a->main.chanvol-=lo; + else + if (!lo) { + a->main.chanvol+=hi; + } else + if (hi==0xf) { + if (!tick) a->main.chanvol-=lo; + } else + if (lo==0xf) { + if (!tick) a->main.chanvol+=hi; + } + + if (a->main.chanvol<0) + a->main.chanvol=0; + else if (a->main.chanvol>64) + a->main.chanvol=64; + + return 0; +} + +static int DoITEffectP(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE inf, lo, hi; + SWORD pan; + (void)flags; + (void)channel; + + inf = UniGetByte(); + if (inf) + a->pansspd = inf; + else + inf = a->pansspd; + + if (!mod->panflag) + return 0; + + lo=inf&0xf; + hi=inf>>4; + + pan=(a->main.panning==PAN_SURROUND)?PAN_CENTER:a->main.panning; + + if (!hi) + pan+=lo<<2; + else + if (!lo) { + pan-=hi<<2; + } else + if (hi==0xf) { + if (!tick) pan+=lo<<2; + } else + if (lo==0xf) { + if (!tick) pan-=hi<<2; + } + a->main.panning= + (panPAN_RIGHT?PAN_RIGHT:pan); + + return 0; +} + +static int DoITEffectT(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE tempo; + SWORD temp; + (void)tick; + (void)flags; + (void)a; + (void)channel; + + tempo = UniGetByte(); + + if (mod->patdly2) + return 0; + + temp = mod->bpm; + if (tempo & 0x10) + temp += (tempo & 0x0f); + else + temp -= tempo; + + mod->bpm=(temp>255)?255:(temp<1?1:temp); + + return 0; +} + +static int DoITEffectU(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat, q; + UWORD temp = 0; /* silence warning */ + (void)flags; + (void)mod; + (void)channel; + + dat = UniGetByte(); + if (!tick) { + if (dat&0x0f) a->vibdepth=dat&0xf; + if (dat&0xf0) a->vibspd=(dat&0xf0)>>2; + } + if (a->main.period) { + q=(a->vibpos>>2)&0x1f; + + switch (a->wavecontrol&3) { + case 0: /* sine */ + temp=VibratoTable[q]; + break; + case 1: /* square wave */ + temp=255; + break; + case 2: /* ramp down */ + q<<=3; + if (a->vibpos<0) q=255-q; + temp=q; + break; + case 3: /* random */ + temp=getrandom(256); + break; + } + + temp*=a->vibdepth; + temp>>=8; + + if (a->vibpos>=0) + a->main.period=a->tmpperiod+temp; + else + a->main.period=a->tmpperiod-temp; + a->ownper = 1; + + a->vibpos+=a->vibspd; + } + + return 0; +} + +static int DoITEffectW(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE inf, lo, hi; + (void)flags; + (void)a; + (void)channel; + + inf = UniGetByte(); + + if (inf) + mod->globalslide = inf; + else + inf = mod->globalslide; + + lo=inf&0xf; + hi=inf>>4; + + if (!lo) { + if (tick) mod->volume+=hi; + } else + if (!hi) { + if (tick) mod->volume-=lo; + } else + if (lo==0xf) { + if (!tick) mod->volume+=hi; + } else + if (hi==0xf) { + if (!tick) mod->volume-=lo; + } + + if (mod->volume<0) + mod->volume=0; + else if (mod->volume>128) + mod->volume=128; + + return 0; +} + +static int DoITEffectY(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat, q; + SLONG temp = 0; /* silence warning */ + (void)flags; + + + dat=UniGetByte(); + if (!tick) { + if (dat&0x0f) a->panbdepth=(dat&0xf); + if (dat&0xf0) a->panbspd=(dat&0xf0)>>4; + } + if (mod->panflag) { + q=a->panbpos; + + switch (a->panbwave) { + case 0: /* sine */ + temp=PanbrelloTable[q]; + break; + case 1: /* square wave */ + temp=(q<0x80)?64:0; + break; + case 2: /* ramp down */ + q<<=3; + temp=q; + break; + case 3: /* random */ + temp=getrandom(256); + break; + } + + temp*=a->panbdepth; + temp=(temp/8)+mod->panning[channel]; + + a->main.panning= + (tempPAN_RIGHT?PAN_RIGHT:temp); + a->panbpos+=a->panbspd; + + } + + return 0; +} + +static void DoNNAEffects(MODULE *, MP_CONTROL *, UBYTE); + +/* Impulse/Scream Tracker Sxx effects. + All Sxx effects share the same memory space. */ +static int DoITEffectS0(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat, inf, c; + + dat = UniGetByte(); + inf=dat&0xf; + c=dat>>4; + + if (!dat) { + c=a->sseffect; + inf=a->ssdata; + } else { + a->sseffect=c; + a->ssdata=inf; + } + + switch (c) { + case SS_GLISSANDO: /* S1x set glissando voice */ + DoEEffects(tick, flags, a, mod, channel, 0x30|inf); + break; + case SS_FINETUNE: /* S2x set finetune */ + DoEEffects(tick, flags, a, mod, channel, 0x50|inf); + break; + case SS_VIBWAVE: /* S3x set vibrato waveform */ + DoEEffects(tick, flags, a, mod, channel, 0x40|inf); + break; + case SS_TREMWAVE: /* S4x set tremolo waveform */ + DoEEffects(tick, flags, a, mod, channel, 0x70|inf); + break; + case SS_PANWAVE: /* S5x panbrello */ + a->panbwave=inf; + break; + case SS_FRAMEDELAY: /* S6x delay x number of frames (patdly) */ + DoEEffects(tick, flags, a, mod, channel, 0xe0|inf); + break; + case SS_S7EFFECTS: /* S7x instrument / NNA commands */ + DoNNAEffects(mod, a, inf); + break; + case SS_PANNING: /* S8x set panning position */ + DoEEffects(tick, flags, a, mod, channel, 0x80 | inf); + break; + case SS_SURROUND: /* S9x set surround sound */ + if (mod->panflag) + a->main.panning = mod->panning[channel] = PAN_SURROUND; + break; + case SS_HIOFFSET: /* SAy set high order sample offset yxx00h */ + if (!tick) { + a->hioffset=inf<<16; + a->main.start=a->hioffset|a->soffset; + + if ((a->main.s)&&(a->main.start>a->main.s->length)) + a->main.start=a->main.s->flags&(SF_LOOP|SF_BIDI)? + a->main.s->loopstart:a->main.s->length; + } + break; + case SS_PATLOOP: /* SBx pattern loop */ + DoEEffects(tick, flags, a, mod, channel, 0x60|inf); + break; + case SS_NOTECUT: /* SCx notecut */ + if (!inf) inf = 1; + DoEEffects(tick, flags, a, mod, channel, 0xC0|inf); + break; + case SS_NOTEDELAY: /* SDx notedelay */ + DoEEffects(tick, flags, a, mod, channel, 0xD0|inf); + break; + case SS_PATDELAY: /* SEx patterndelay */ + DoEEffects(tick, flags, a, mod, channel, 0xE0|inf); + break; + } + + return 0; +} + +/*========== Impulse Tracker Volume/Pan Column effects */ + +/* + * All volume/pan column effects share the same memory space. + */ + +static int DoVolEffects(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE c, inf; + (void)channel; + + c = UniGetByte(); + inf = UniGetByte(); + + if ((!c)&&(!inf)) { + c=a->voleffect; + inf=a->voldata; + } else { + a->voleffect=c; + a->voldata=inf; + } + + if (c) + switch (c) { + case VOL_VOLUME: + if (tick) break; + if (inf>64) inf=64; + a->tmpvolume=inf; + break; + case VOL_PANNING: + if (mod->panflag) + a->main.panning=inf; + break; + case VOL_VOLSLIDE: + DoS3MVolSlide(tick, flags, a, inf); + return 1; + case VOL_PITCHSLIDEDN: + if (a->main.period) + DoS3MSlideDn(tick, a, inf); + break; + case VOL_PITCHSLIDEUP: + if (a->main.period) + DoS3MSlideUp(tick, a, inf); + break; + case VOL_PORTAMENTO: + DoITToneSlide(tick, a, inf); + break; + case VOL_VIBRATO: + DoITVibrato(tick, a, inf); + break; + } + + return 0; +} + +/*========== UltraTracker effects */ + +static int DoULTEffect9(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UWORD offset=UniGetWord(); + (void)tick; + (void)flags; + (void)mod; + (void)channel; + + if (offset) + a->ultoffset=offset; + + a->main.start=a->ultoffset<<2; + if ((a->main.s)&&(a->main.start>a->main.s->length)) + a->main.start=a->main.s->flags&(SF_LOOP|SF_BIDI)? + a->main.s->loopstart:a->main.s->length; + + return 0; +} + +/*========== OctaMED effects */ + +static int DoMEDSpeed(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UWORD speed=UniGetWord(); + (void)tick; + (void)flags; + (void)a; + (void)channel; + + mod->bpm=speed; + + return 0; +} + +static int DoMEDEffectF1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + DoEEffects(tick, flags, a, mod, channel, 0x90|(mod->sngspd/2)); + + return 0; +} + +static int DoMEDEffectF2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + DoEEffects(tick, flags, a, mod, channel, 0xd0|(mod->sngspd/2)); + + return 0; +} + +static int DoMEDEffectF3(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + DoEEffects(tick, flags, a, mod, channel, 0x90|(mod->sngspd/3)); + + return 0; +} + +/*========== Oktalyzer effects */ + +static int DoOktArp(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat, dat2; + (void)mod; + (void)channel; + + dat2 = UniGetByte(); /* arpeggio style */ + dat = UniGetByte(); + if (!tick) { + if (!dat && (flags & UF_ARPMEM)) + dat=a->arpmem; + else + a->arpmem=dat; + } + if (a->main.period) + DoArpeggio(tick, flags, a, dat2); + + return 0; +} + +/*========== General player functions */ + +static int DoNothing(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UniSkipOpcode(); + (void)tick; + (void)flags; + (void)a; + (void)mod; + (void)channel; + + return 0; +} + +typedef int (*effect_func) (UWORD, UWORD, MP_CONTROL *, MODULE *, SWORD); + +static effect_func effects[UNI_LAST] = { + DoNothing, /* 0 */ + DoNothing, /* UNI_NOTE */ + DoNothing, /* UNI_INSTRUMENT */ + DoPTEffect0, /* UNI_PTEFFECT0 */ + DoPTEffect1, /* UNI_PTEFFECT1 */ + DoPTEffect2, /* UNI_PTEFFECT2 */ + DoPTEffect3, /* UNI_PTEFFECT3 */ + DoPTEffect4, /* UNI_PTEFFECT4 */ + DoPTEffect5, /* UNI_PTEFFECT5 */ + DoPTEffect6, /* UNI_PTEFFECT6 */ + DoPTEffect7, /* UNI_PTEFFECT7 */ + DoPTEffect8, /* UNI_PTEFFECT8 */ + DoPTEffect9, /* UNI_PTEFFECT9 */ + DoPTEffectA, /* UNI_PTEFFECTA */ + DoPTEffectB, /* UNI_PTEFFECTB */ + DoPTEffectC, /* UNI_PTEFFECTC */ + DoPTEffectD, /* UNI_PTEFFECTD */ + DoPTEffectE, /* UNI_PTEFFECTE */ + DoPTEffectF, /* UNI_PTEFFECTF */ + DoS3MEffectA, /* UNI_S3MEFFECTA */ + DoS3MEffectD, /* UNI_S3MEFFECTD */ + DoS3MEffectE, /* UNI_S3MEFFECTE */ + DoS3MEffectF, /* UNI_S3MEFFECTF */ + DoS3MEffectI, /* UNI_S3MEFFECTI */ + DoS3MEffectQ, /* UNI_S3MEFFECTQ */ + DoS3MEffectR, /* UNI_S3MEFFECTR */ + DoS3MEffectT, /* UNI_S3MEFFECTT */ + DoS3MEffectU, /* UNI_S3MEFFECTU */ + DoKeyOff, /* UNI_KEYOFF */ + DoKeyFade, /* UNI_KEYFADE */ + DoVolEffects, /* UNI_VOLEFFECTS */ + DoPTEffect4, /* UNI_XMEFFECT4 */ + DoXMEffect6, /* UNI_XMEFFECT6 */ + DoXMEffectA, /* UNI_XMEFFECTA */ + DoXMEffectE1, /* UNI_XMEFFECTE1 */ + DoXMEffectE2, /* UNI_XMEFFECTE2 */ + DoXMEffectEA, /* UNI_XMEFFECTEA */ + DoXMEffectEB, /* UNI_XMEFFECTEB */ + DoXMEffectG, /* UNI_XMEFFECTG */ + DoXMEffectH, /* UNI_XMEFFECTH */ + DoXMEffectL, /* UNI_XMEFFECTL */ + DoXMEffectP, /* UNI_XMEFFECTP */ + DoXMEffectX1, /* UNI_XMEFFECTX1 */ + DoXMEffectX2, /* UNI_XMEFFECTX2 */ + DoITEffectG, /* UNI_ITEFFECTG */ + DoITEffectH, /* UNI_ITEFFECTH */ + DoITEffectI, /* UNI_ITEFFECTI */ + DoITEffectM, /* UNI_ITEFFECTM */ + DoITEffectN, /* UNI_ITEFFECTN */ + DoITEffectP, /* UNI_ITEFFECTP */ + DoITEffectT, /* UNI_ITEFFECTT */ + DoITEffectU, /* UNI_ITEFFECTU */ + DoITEffectW, /* UNI_ITEFFECTW */ + DoITEffectY, /* UNI_ITEFFECTY */ + DoNothing, /* UNI_ITEFFECTZ */ + DoITEffectS0, /* UNI_ITEFFECTS0 */ + DoULTEffect9, /* UNI_ULTEFFECT9 */ + DoMEDSpeed, /* UNI_MEDSPEED */ + DoMEDEffectF1, /* UNI_MEDEFFECTF1 */ + DoMEDEffectF2, /* UNI_MEDEFFECTF2 */ + DoMEDEffectF3, /* UNI_MEDEFFECTF3 */ + DoOktArp, /* UNI_OKTARP */ +}; + +static int pt_playeffects(MODULE *mod, SWORD channel, MP_CONTROL *a) +{ + UWORD tick = mod->vbtick; + UWORD flags = mod->flags; + UBYTE c; + int explicitslides = 0; + effect_func f; + + while((c=UniGetByte())) { + f = effects[c]; + if (f != DoNothing) + a->sliding = 0; + explicitslides |= f(tick, flags, a, mod, channel); + } + return explicitslides; +} + +static void DoNNAEffects(MODULE *mod, MP_CONTROL *a, UBYTE dat) +{ + int t; + MP_VOICE *aout; + + dat&=0xf; + aout=(a->slave)?a->slave:NULL; + + switch (dat) { + case 0x0: /* past note cut */ + for (t=0;tvoice[t].master==a) + mod->voice[t].main.fadevol=0; + break; + case 0x1: /* past note off */ + for (t=0;tvoice[t].master==a) { + mod->voice[t].main.keyoff|=KEY_OFF; + if ((!(mod->voice[t].venv.flg & EF_ON))|| + (mod->voice[t].venv.flg & EF_LOOP)) + mod->voice[t].main.keyoff=KEY_KILL; + } + break; + case 0x2: /* past note fade */ + for (t=0;tvoice[t].master==a) + mod->voice[t].main.keyoff|=KEY_FADE; + break; + case 0x3: /* set NNA note cut */ + a->main.nna=(a->main.nna&~NNA_MASK)|NNA_CUT; + break; + case 0x4: /* set NNA note continue */ + a->main.nna=(a->main.nna&~NNA_MASK)|NNA_CONTINUE; + break; + case 0x5: /* set NNA note off */ + a->main.nna=(a->main.nna&~NNA_MASK)|NNA_OFF; + break; + case 0x6: /* set NNA note fade */ + a->main.nna=(a->main.nna&~NNA_MASK)|NNA_FADE; + break; + case 0x7: /* disable volume envelope */ + if (aout) + aout->main.volflg&=~EF_ON; + break; + case 0x8: /* enable volume envelope */ + if (aout) + aout->main.volflg|=EF_ON; + break; + case 0x9: /* disable panning envelope */ + if (aout) + aout->main.panflg&=~EF_ON; + break; + case 0xa: /* enable panning envelope */ + if (aout) + aout->main.panflg|=EF_ON; + break; + case 0xb: /* disable pitch envelope */ + if (aout) + aout->main.pitflg&=~EF_ON; + break; + case 0xc: /* enable pitch envelope */ + if (aout) + aout->main.pitflg|=EF_ON; + break; + } +} + +void pt_UpdateVoices(MODULE *mod, int max_volume) +{ + SWORD envpan,envvol,envpit,channel; + UWORD playperiod; + SLONG vibval,vibdpt; + ULONG tmpvol; + + MP_VOICE *aout; + INSTRUMENT *i; + SAMPLE *s; + + mod->totalchn=mod->realchn=0; + for (channel=0;channelvoice[channel]; + i=aout->main.i; + s=aout->main.s; + + if (!s || !s->length) continue; + + if (aout->main.period<40) + aout->main.period=40; + else if (aout->main.period>50000) + aout->main.period=50000; + + if ((aout->main.kick==KICK_NOTE)||(aout->main.kick==KICK_KEYOFF)) { + Voice_Play_internal(channel,s,(aout->main.start==-1)? + ((s->flags&SF_UST_LOOP)?s->loopstart:0):aout->main.start); + aout->main.fadevol=32768; + aout->aswppos=0; + } + + envvol = 256; + envpan = PAN_CENTER; + envpit = 32; + if (i && ((aout->main.kick==KICK_NOTE)||(aout->main.kick==KICK_ENV))) { + if (aout->main.volflg & EF_ON) + envvol = StartEnvelope(&aout->venv,aout->main.volflg, + i->volpts,i->volsusbeg,i->volsusend, + i->volbeg,i->volend,i->volenv,aout->main.keyoff); + if (aout->main.panflg & EF_ON) + envpan = StartEnvelope(&aout->penv,aout->main.panflg, + i->panpts,i->pansusbeg,i->pansusend, + i->panbeg,i->panend,i->panenv,aout->main.keyoff); + if (aout->main.pitflg & EF_ON) + envpit = StartEnvelope(&aout->cenv,aout->main.pitflg, + i->pitpts,i->pitsusbeg,i->pitsusend, + i->pitbeg,i->pitend,i->pitenv,aout->main.keyoff); + + if (aout->cenv.flg & EF_ON) + aout->masterperiod=GetPeriod(mod->flags, + (UWORD)aout->main.note<<1, aout->master->speed); + } else { + if (aout->main.volflg & EF_ON) + envvol = ProcessEnvelope(aout,&aout->venv,256); + if (aout->main.panflg & EF_ON) + envpan = ProcessEnvelope(aout,&aout->penv,PAN_CENTER); + if (aout->main.pitflg & EF_ON) + envpit = ProcessEnvelope(aout,&aout->cenv,32); + } + if (aout->main.kick == KICK_NOTE) { + aout->main.kick_flag = 1; + } + aout->main.kick=KICK_ABSENT; + + tmpvol = aout->main.fadevol; /* max 32768 */ + tmpvol *= aout->main.chanvol; /* * max 64 */ + tmpvol *= aout->main.outvolume; /* * max 256 */ + tmpvol /= (256 * 64); /* tmpvol is max 32768 again */ + aout->totalvol = tmpvol >> 2; /* used to determine samplevolume */ + tmpvol *= envvol; /* * max 256 */ + tmpvol *= mod->volume; /* * max 128 */ + tmpvol /= (128 * 256 * 128); + + /* fade out */ + if (mod->sngpos>=mod->numpos) + tmpvol=0; + else + tmpvol=(tmpvol*max_volume)/128; + + if ((aout->masterchn!=-1)&& mod->control[aout->masterchn].muted) + Voice_SetVolume_internal(channel,0); + else { + Voice_SetVolume_internal(channel,tmpvol); + if ((tmpvol)&&(aout->master)&&(aout->master->slave==aout)) + mod->realchn++; + mod->totalchn++; + } + + if (aout->main.panning==PAN_SURROUND) + Voice_SetPanning_internal(channel,PAN_SURROUND); + else + if ((mod->panflag)&&(aout->penv.flg & EF_ON)) + Voice_SetPanning_internal(channel, + DoPan(envpan,aout->main.panning)); + else + Voice_SetPanning_internal(channel,aout->main.panning); + + if (aout->main.period && s->vibdepth) + switch (s->vibtype) { + case 0: + vibval=avibtab[s->avibpos&127]; + if (aout->avibpos & 0x80) vibval=-vibval; + break; + case 1: + vibval=64; + if (aout->avibpos & 0x80) vibval=-vibval; + break; + case 2: + vibval=63-(((aout->avibpos+128)&255)>>1); + break; + default: + vibval=(((aout->avibpos+128)&255)>>1)-64; + break; + } + else + vibval=0; + + if (s->vibflags & AV_IT) { + if ((aout->aswppos>>8)vibdepth) { + aout->aswppos += s->vibsweep; + vibdpt=aout->aswppos; + } else + vibdpt=s->vibdepth<<8; + vibval=(vibval*vibdpt)>>16; + if (aout->mflag) { + if (!(mod->flags&UF_LINEAR)) vibval>>=1; + aout->main.period-=vibval; + } + } else { + /* do XM style auto-vibrato */ + if (!(aout->main.keyoff & KEY_OFF)) { + if (aout->aswpposvibsweep) { + vibdpt=(aout->aswppos*s->vibdepth)/s->vibsweep; + aout->aswppos++; + } else + vibdpt=s->vibdepth; + } else { + /* keyoff -> depth becomes 0 if final depth wasn't reached or + stays at final level if depth WAS reached */ + if (aout->aswppos>=s->vibsweep) + vibdpt=s->vibdepth; + else + vibdpt=0; + } + vibval=(vibval*vibdpt)>>8; + aout->main.period-=vibval; + } + + /* update vibrato position */ + aout->avibpos=(aout->avibpos+s->vibrate)&0xff; + + /* process pitch envelope */ + playperiod=aout->main.period; + + if ((aout->main.pitflg&EF_ON)&&(envpit!=32)) { + long p1; + + envpit-=32; + if ((aout->main.note<<1)+envpit<=0) envpit=-(aout->main.note<<1); + + p1=GetPeriod(mod->flags, ((UWORD)aout->main.note<<1)+envpit, + aout->master->speed)-aout->masterperiod; + if (p1>0) { + if ((UWORD)(playperiod+p1)<=playperiod) { + p1=0; + aout->main.keyoff|=KEY_OFF; + } + } else if (p1<0) { + if ((UWORD)(playperiod+p1)>=playperiod) { + p1=0; + aout->main.keyoff|=KEY_OFF; + } + } + playperiod+=p1; + } + + if (!aout->main.fadevol) { /* check for a dead note (fadevol=0) */ + Voice_Stop_internal(channel); + mod->totalchn--; + if ((tmpvol)&&(aout->master)&&(aout->master->slave==aout)) + mod->realchn--; + } else { + Voice_SetFrequency_internal(channel, + getfrequency(mod->flags,playperiod)); + + /* if keyfade, start substracting fadeoutspeed from fadevol: */ + if ((i)&&(aout->main.keyoff&KEY_FADE)) { + if (aout->main.fadevol>=i->volfade) + aout->main.fadevol-=i->volfade; + else + aout->main.fadevol=0; + } + } + + md_bpm=mod->bpm+mod->relspd; + if (md_bpm<32) + md_bpm=32; + else if ((!(mod->flags&UF_HIGHBPM)) && md_bpm>255) + md_bpm=255; + } +} + +/* Handles new notes or instruments */ +void pt_Notes(MODULE *mod) +{ + SWORD channel; + MP_CONTROL *a; + UBYTE c,inst; + int tr,funky; /* funky is set to indicate note or instrument change */ + + for (channel=0;channelnumchn;channel++) { + a=&mod->control[channel]; + + if (mod->sngpos>=mod->numpos) { + tr=mod->numtrk; + mod->numrow=0; + } else { + tr=mod->patterns[(mod->positions[mod->sngpos]*mod->numchn)+channel]; + mod->numrow=mod->pattrows[mod->positions[mod->sngpos]]; + } + + a->row=(trnumtrk)?UniFindRow(mod->tracks[tr],mod->patpos):NULL; + a->newsamp=0; + if (!mod->vbtick) a->main.notedelay=0; + + if (!a->row) continue; + UniSetRow(a->row); + funky=0; + + while((c=UniGetByte())) + switch (c) { + case UNI_NOTE: + funky|=1; + a->oldnote=a->anote,a->anote=UniGetByte(); + a->main.kick =KICK_NOTE; + a->main.start=-1; + a->sliding=0; + + /* retrig tremolo and vibrato waves ? */ + if (!(a->wavecontrol & 0x80)) a->trmpos=0; + if (!(a->wavecontrol & 0x08)) a->vibpos=0; + if (!a->panbwave) a->panbpos=0; + break; + case UNI_INSTRUMENT: + inst=UniGetByte(); + if (inst>=mod->numins) break; /* safety valve */ + funky|=2; + a->main.i=(mod->flags & UF_INST)?&mod->instruments[inst]:NULL; + a->retrig=0; + a->s3mtremor=0; + a->ultoffset=0; + a->main.sample=inst; + break; + default: + UniSkipOpcode(); + break; + } + + if (funky) { + INSTRUMENT *i; + SAMPLE *s; + + if ((i=a->main.i)) { + if (i->samplenumber[a->anote] >= mod->numsmp) continue; + s=&mod->samples[i->samplenumber[a->anote]]; + a->main.note=i->samplenote[a->anote]; + } else { + a->main.note=a->anote; + s=&mod->samples[a->main.sample]; + } + + if (a->main.s!=s) { + a->main.s=s; + a->newsamp=a->main.period; + } + + /* channel or instrument determined panning ? */ + a->main.panning=mod->panning[channel]; + if (s->flags & SF_OWNPAN) + a->main.panning=s->panning; + else if ((i)&&(i->flags & IF_OWNPAN)) + a->main.panning=i->panning; + + a->main.handle=s->handle; + a->speed=s->speed; + + if (i) { + if ((mod->panflag)&&(i->flags & IF_PITCHPAN) + &&(a->main.panning!=PAN_SURROUND)){ + a->main.panning+= + ((a->anote-i->pitpancenter)*i->pitpansep)/8; + if (a->main.panningmain.panning=PAN_LEFT; + else if (a->main.panning>PAN_RIGHT) + a->main.panning=PAN_RIGHT; + } + a->main.pitflg=i->pitflg; + a->main.volflg=i->volflg; + a->main.panflg=i->panflg; + a->main.nna=i->nnatype; + a->dca=i->dca; + a->dct=i->dct; + } else { + a->main.pitflg=a->main.volflg=a->main.panflg=0; + a->main.nna=a->dca=0; + a->dct=DCT_OFF; + } + + if (funky&2) /* instrument change */ { + /* IT random volume variations: 0:8 bit fixed, and one bit for + sign. */ + a->volume=a->tmpvolume=s->volume; + if ((s)&&(i)) { + if (i->rvolvar) { + a->volume=a->tmpvolume=s->volume+ + ((s->volume*((SLONG)i->rvolvar*(SLONG)getrandom(512) + ))/25600); + if (a->volume<0) + a->volume=a->tmpvolume=0; + else if (a->volume>64) + a->volume=a->tmpvolume=64; + } + if ((mod->panflag)&&(a->main.panning!=PAN_SURROUND)) { + a->main.panning+=((a->main.panning*((SLONG)i->rpanvar* + (SLONG)getrandom(512)))/25600); + if (a->main.panningmain.panning=PAN_LEFT; + else if (a->main.panning>PAN_RIGHT) + a->main.panning=PAN_RIGHT; + } + } + } + + a->wantedperiod=a->tmpperiod= + GetPeriod(mod->flags, (UWORD)a->main.note<<1,a->speed); + a->main.keyoff=KEY_KICK; + } + } +} + +/* Handles effects */ +void pt_EffectsPass1(MODULE *mod) +{ + SWORD channel; + MP_CONTROL *a; + MP_VOICE *aout; + int explicitslides; + + for (channel=0;channelnumchn;channel++) { + a=&mod->control[channel]; + + if ((aout=a->slave)) { + a->main.fadevol=aout->main.fadevol; + a->main.period=aout->main.period; + if (a->main.kick==KICK_KEYOFF) + a->main.keyoff=aout->main.keyoff; + } + + if (!a->row) continue; + UniSetRow(a->row); + + a->ownper=a->ownvol=0; + explicitslides = pt_playeffects(mod, channel, a); + + /* continue volume slide if necessary for XM and IT */ + if (mod->flags&UF_BGSLIDES) { + if (!explicitslides && a->sliding) + DoS3MVolSlide(mod->vbtick, mod->flags, a, 0); + else if (a->tmpvolume) + a->sliding = explicitslides; + } + + if (!a->ownper) + a->main.period=a->tmpperiod; + if (!a->ownvol) + a->volume=a->tmpvolume; + + if (a->main.s) { + if (a->main.i) + a->main.outvolume= + (a->volume*a->main.s->globvol*a->main.i->globvol)>>10; + else + a->main.outvolume=(a->volume*a->main.s->globvol)>>4; + if (a->main.outvolume>256) + a->main.outvolume=256; + else if (a->main.outvolume<0) + a->main.outvolume=0; + } + } +} + +/* NNA management */ +void pt_NNA(MODULE *mod) +{ + SWORD channel; + MP_CONTROL *a; + + for (channel=0;channelnumchn;channel++) { + a=&mod->control[channel]; + + if (a->main.kick==KICK_NOTE) { + BOOL kill=0; + + if (a->slave) { + MP_VOICE *aout; + + aout=a->slave; + if (aout->main.nna & NNA_MASK) { + /* Make sure the old MP_VOICE channel knows it has no + master now ! */ + a->slave=NULL; + /* assume the channel is taken by NNA */ + aout->mflag=0; + + switch (aout->main.nna) { + case NNA_CONTINUE: /* continue note, do nothing */ + break; + case NNA_OFF: /* note off */ + aout->main.keyoff|=KEY_OFF; + if ((!(aout->main.volflg & EF_ON))|| + (aout->main.volflg & EF_LOOP)) + aout->main.keyoff=KEY_KILL; + break; + case NNA_FADE: + aout->main.keyoff |= KEY_FADE; + break; + } + } + } + + if (a->dct!=DCT_OFF) { + int t; + + for (t=0;tvoice[t].masterchn==channel)&& + (a->main.sample==mod->voice[t].main.sample)) { + kill=0; + switch (a->dct) { + case DCT_NOTE: + if (a->main.note==mod->voice[t].main.note) + kill=1; + break; + case DCT_SAMPLE: + if (a->main.handle==mod->voice[t].main.handle) + kill=1; + break; + case DCT_INST: + kill=1; + break; + } + if (kill) + switch (a->dca) { + case DCA_CUT: + mod->voice[t].main.fadevol=0; + break; + case DCA_OFF: + mod->voice[t].main.keyoff|=KEY_OFF; + if ((!(mod->voice[t].main.volflg&EF_ON))|| + (mod->voice[t].main.volflg&EF_LOOP)) + mod->voice[t].main.keyoff=KEY_KILL; + break; + case DCA_FADE: + mod->voice[t].main.keyoff|=KEY_FADE; + break; + } + } + } + } /* if (a->main.kick==KICK_NOTE) */ + } +} + +/* Setup module and NNA voices */ +void pt_SetupVoices(MODULE *mod) +{ + SWORD channel; + MP_CONTROL *a; + MP_VOICE *aout; + + for (channel=0;channelnumchn;channel++) { + a=&mod->control[channel]; + + if (a->main.notedelay) continue; + if (a->main.kick==KICK_NOTE) { + /* if no channel was cut above, find an empty or quiet channel + here */ + if (mod->flags&UF_NNA) { + if (!a->slave) { + int newchn; + + if ((newchn=MP_FindEmptyChannel(mod))!=-1) + a->slave=&mod->voice[a->slavechn=newchn]; + } + } else + a->slave=&mod->voice[a->slavechn=channel]; + + /* assign parts of MP_VOICE only done for a KICK_NOTE */ + if ((aout=a->slave)) { + if (aout->mflag && aout->master) aout->master->slave=NULL; + aout->master=a; + a->slave=aout; + aout->masterchn=channel; + aout->mflag=1; + } + } else + aout=a->slave; + + if (aout) + aout->main=a->main; + a->main.kick=KICK_ABSENT; + } +} + +/* second effect pass */ +void pt_EffectsPass2(MODULE *mod) +{ + SWORD channel; + MP_CONTROL *a; + UBYTE c; + + for (channel=0;channelnumchn;channel++) { + a=&mod->control[channel]; + + if (!a->row) continue; + UniSetRow(a->row); + + while((c=UniGetByte())) + if (c==UNI_ITEFFECTS0) { + c=UniGetByte(); + if ((c>>4)==SS_S7EFFECTS) + DoNNAEffects(mod, a, c&0xf); + } else + UniSkipOpcode(); + } +} + +void Player_HandleTick(void) +{ + SWORD channel; + int max_volume; + +#if 0 + /* don't handle the very first ticks, this allows the other hardware to + settle down so we don't loose any starting notes */ + if (isfirst) { + isfirst--; + return; + } +#endif + + if ((!pf)||(pf->forbid)||(pf->sngpos>=pf->numpos)) return; + + /* update time counter (sngtime is in milliseconds (in fact 2^-10)) */ + pf->sngremainder+=(1<<9)*5; /* thus 2.5*(1<<10), since fps=0.4xtempo */ + pf->sngtime+=pf->sngremainder/pf->bpm; + pf->sngremainder%=pf->bpm; + + if (++pf->vbtick>=pf->sngspd) { + if (pf->pat_repcrazy) + pf->pat_repcrazy=0; /* play 2 times row 0 */ + else + pf->patpos++; + pf->vbtick=0; + + /* process pattern-delay. pf->patdly2 is the counter and pf->patdly is + the command memory. */ + if (pf->patdly) + pf->patdly2=pf->patdly,pf->patdly=0; + if (pf->patdly2) { + /* patterndelay active */ + if (--pf->patdly2) + /* so turn back pf->patpos by 1 */ + if (pf->patpos) pf->patpos--; + } + + /* do we have to get a new patternpointer ? (when pf->patpos reaches the + pattern size, or when a patternbreak is active) */ + if (((pf->patpos>=pf->numrow)&&(pf->numrow>0))&&(!pf->posjmp)) + pf->posjmp=3; + + if (pf->posjmp) { + pf->patpos=pf->numrow?(pf->patbrk%pf->numrow):0; + pf->pat_repcrazy=0; + pf->sngpos+=(pf->posjmp-2); + for (channel=0;channelnumchn;channel++) + pf->control[channel].pat_reppos=-1; + + pf->patbrk=pf->posjmp=0; + /* handle the "---" (end of song) pattern since it can occur + *inside* the module in some formats */ + if ((pf->sngpos>=pf->numpos)|| + (pf->positions[pf->sngpos]==LAST_PATTERN)) { + if (!pf->wrap) return; + if (!(pf->sngpos=pf->reppos)) { + pf->volume=pf->initvolume>128?128:pf->initvolume; + if(pf->initspeed!=0) + pf->sngspd=pf->initspeed<32?pf->initspeed:32; + else + pf->sngspd=6; + pf->bpm=pf->inittempo<32?32:pf->inittempo; + } + } + if (pf->sngpos<0) pf->sngpos=pf->numpos-1; + } + + if (!pf->patdly2) + pt_Notes(pf); + } + + /* Fade global volume if enabled and we're playing the last pattern */ + if (((pf->sngpos==pf->numpos-1)|| + (pf->positions[pf->sngpos+1]==LAST_PATTERN))&& + (pf->fadeout)) + max_volume=pf->numrow?((pf->numrow-pf->patpos)*128)/pf->numrow:0; + else + max_volume=128; + + pt_EffectsPass1(pf); + if (pf->flags&UF_NNA) + pt_NNA(pf); + pt_SetupVoices(pf); + pt_EffectsPass2(pf); + + /* now set up the actual hardware channel playback information */ + pt_UpdateVoices(pf, max_volume); +} + +static void Player_Init_internal(MODULE* mod) +{ + int t; + + for (t=0;tnumchn;t++) { + mod->control[t].main.chanvol=mod->chanvol[t]; + mod->control[t].main.panning=mod->panning[t]; + } + + mod->sngtime=0; + mod->sngremainder=0; + + mod->pat_repcrazy=0; + mod->sngpos=0; + if(mod->initspeed!=0) + mod->sngspd=mod->initspeed<32?mod->initspeed:32; + else + mod->sngspd=6; + mod->volume=mod->initvolume>128?128:mod->initvolume; + + mod->vbtick=mod->sngspd; + mod->patdly=0; + mod->patdly2=0; + mod->bpm=mod->inittempo<32?32:mod->inittempo; + mod->realchn=0; + + mod->patpos=0; + mod->posjmp=2; /* make sure the player fetches the first note */ + mod->numrow=-1; + mod->patbrk=0; +} + +BOOL Player_Init(MODULE* mod) +{ + mod->extspd=1; + mod->panflag=1; + mod->wrap=0; + mod->loop=1; + mod->fadeout=0; + + mod->relspd=0; + + /* make sure the player doesn't start with garbage */ + if (!(mod->control=(MP_CONTROL*)_mm_calloc(mod->numchn,sizeof(MP_CONTROL)))) + return 1; + if (!(mod->voice=(MP_VOICE*)_mm_calloc(md_sngchn,sizeof(MP_VOICE)))) + return 1; + + Player_Init_internal(mod); + return 0; +} + +void Player_Exit_internal(MODULE* mod) +{ + if (!mod) + return; + + /* Stop playback if necessary */ + if (mod==pf) { + Player_Stop_internal(); + pf=NULL; + } + + if (mod->control) + free(mod->control); + if (mod->voice) + free(mod->voice); + mod->control=NULL; + mod->voice=NULL; +} + +void Player_Exit(MODULE* mod) +{ + MUTEX_LOCK(vars); + Player_Exit_internal(mod); + MUTEX_UNLOCK(vars); +} + +MIKMODAPI void Player_SetVolume(SWORD volume) +{ + MUTEX_LOCK(vars); + if (pf) + pf->volume=(volume<0)?0:(volume>128)?128:volume; + MUTEX_UNLOCK(vars); +} + +MIKMODAPI MODULE* Player_GetModule(void) +{ + MODULE* result; + + MUTEX_LOCK(vars); + result=pf; + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI void Player_Start(MODULE *mod) +{ + int t; + + if (!mod) + return; + + if (!MikMod_Active()) + MikMod_EnableOutput(); + + mod->forbid=0; + + MUTEX_LOCK(vars); + if (pf!=mod) { + /* new song is being started, so completely stop out the old one. */ + if (pf) pf->forbid=1; + for (t=0;tforbid=1; + pf=NULL; +} + +MIKMODAPI void Player_Stop(void) +{ + MUTEX_LOCK(vars); + Player_Stop_internal(); + MUTEX_UNLOCK(vars); +} + +MIKMODAPI BOOL Player_Active(void) +{ + BOOL result=0; + + MUTEX_LOCK(vars); + if (pf) + result=(!(pf->sngpos>=pf->numpos)); + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI void Player_NextPosition(void) +{ + MUTEX_LOCK(vars); + if (pf) { + int t; + + pf->forbid=1; + pf->posjmp=3; + pf->patbrk=0; + pf->vbtick=pf->sngspd; + + for (t=0;tvoice[t].main.i=NULL; + pf->voice[t].main.s=NULL; + } + for (t=0;tnumchn;t++) { + pf->control[t].main.i=NULL; + pf->control[t].main.s=NULL; + } + pf->forbid=0; + } + MUTEX_UNLOCK(vars); +} + +MIKMODAPI void Player_PrevPosition(void) +{ + MUTEX_LOCK(vars); + if (pf) { + int t; + + pf->forbid=1; + pf->posjmp=1; + pf->patbrk=0; + pf->vbtick=pf->sngspd; + + for (t=0;tvoice[t].main.i=NULL; + pf->voice[t].main.s=NULL; + } + for (t=0;tnumchn;t++) { + pf->control[t].main.i=NULL; + pf->control[t].main.s=NULL; + } + pf->forbid=0; + } + MUTEX_UNLOCK(vars); +} + +MIKMODAPI void Player_SetPosition(UWORD pos) +{ + MUTEX_LOCK(vars); + if (pf) { + int t; + + pf->forbid=1; + if (pos>=pf->numpos) pos=pf->numpos; + pf->posjmp=2; + pf->patbrk=0; + pf->sngpos=pos; + pf->vbtick=pf->sngspd; + + for (t=0;tvoice[t].main.i=NULL; + pf->voice[t].main.s=NULL; + } + for (t=0;tnumchn;t++) { + pf->control[t].main.i=NULL; + pf->control[t].main.s=NULL; + } + pf->forbid=0; + + if (!pos) + Player_Init_internal(pf); + } + MUTEX_UNLOCK(vars); +} + +static void Player_Unmute_internal(SLONG arg1,va_list ap) +{ + SLONG t,arg2,arg3=0; + + if (pf) { + switch (arg1) { + case MUTE_INCLUSIVE: + if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| + (arg2>arg3)||(arg3>=pf->numchn)) + return; + for (;arg2numchn && arg2<=arg3;arg2++) + pf->control[arg2].muted=0; + break; + case MUTE_EXCLUSIVE: + if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| + (arg2>arg3)||(arg3>=pf->numchn)) + return; + for (t=0;tnumchn;t++) { + if ((t>=arg2) && (t<=arg3)) + continue; + pf->control[t].muted=0; + } + break; + default: + if (arg1numchn) pf->control[arg1].muted=0; + break; + } + } +} + +MIKMODAPI void Player_Unmute(SLONG arg1, ...) +{ + va_list args; + + va_start(args,arg1); + MUTEX_LOCK(vars); + Player_Unmute_internal(arg1,args); + MUTEX_UNLOCK(vars); + va_end(args); +} + +static void Player_Mute_internal(SLONG arg1,va_list ap) +{ + SLONG t,arg2,arg3=0; + + if (pf) { + switch (arg1) { + case MUTE_INCLUSIVE: + if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| + (arg2>arg3)||(arg3>=pf->numchn)) + return; + for (;arg2numchn && arg2<=arg3;arg2++) + pf->control[arg2].muted=1; + break; + case MUTE_EXCLUSIVE: + if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| + (arg2>arg3)||(arg3>=pf->numchn)) + return; + for (t=0;tnumchn;t++) { + if ((t>=arg2) && (t<=arg3)) + continue; + pf->control[t].muted=1; + } + break; + default: + if (arg1numchn) + pf->control[arg1].muted=1; + break; + } + } +} + +MIKMODAPI void Player_Mute(SLONG arg1,...) +{ + va_list args; + + va_start(args,arg1); + MUTEX_LOCK(vars); + Player_Mute_internal(arg1,args); + MUTEX_UNLOCK(vars); + va_end(args); +} + +static void Player_ToggleMute_internal(SLONG arg1,va_list ap) +{ + SLONG arg2,arg3=0; + ULONG t; + + if (pf) { + switch (arg1) { + case MUTE_INCLUSIVE: + if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| + (arg2>arg3)||(arg3>=pf->numchn)) + return; + for (;arg2numchn && arg2<=arg3;arg2++) + pf->control[arg2].muted=1-pf->control[arg2].muted; + break; + case MUTE_EXCLUSIVE: + if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| + (arg2>arg3)||(arg3>=pf->numchn)) + return; + for (t=0;tnumchn;t++) { + if ((t>=arg2) && (t<=arg3)) + continue; + pf->control[t].muted=1-pf->control[t].muted; + } + break; + default: + if (arg1numchn) + pf->control[arg1].muted=1-pf->control[arg1].muted; + break; + } + } +} + +MIKMODAPI void Player_ToggleMute(SLONG arg1,...) +{ + va_list args; + + va_start(args,arg1); + MUTEX_LOCK(vars); + Player_ToggleMute_internal(arg1,args); + MUTEX_UNLOCK(vars); + va_end(args); +} + +MIKMODAPI BOOL Player_Muted(UBYTE chan) +{ + BOOL result=1; + + MUTEX_LOCK(vars); + if (pf) + result=(channumchn)?pf->control[chan].muted:1; + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI int Player_GetChannelVoice(UBYTE chan) +{ + int result=0; + + MUTEX_LOCK(vars); + if (pf) + result=(channumchn)?pf->control[chan].slavechn:-1; + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI UWORD Player_GetChannelPeriod(UBYTE chan) +{ + UWORD result=0; + + MUTEX_LOCK(vars); + if (pf) + result=(channumchn)?pf->control[chan].main.period:0; + MUTEX_UNLOCK(vars); + + return result; +} + +BOOL Player_Paused_internal(void) +{ + return pf?pf->forbid:1; +} + +MIKMODAPI BOOL Player_Paused(void) +{ + BOOL result; + + MUTEX_LOCK(vars); + result=Player_Paused_internal(); + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI void Player_TogglePause(void) +{ + MUTEX_LOCK(vars); + if (pf) + pf->forbid=1-pf->forbid; + MUTEX_UNLOCK(vars); +} + +MIKMODAPI void Player_SetSpeed(UWORD speed) +{ + MUTEX_LOCK(vars); + if (pf) + pf->sngspd=speed?(speed<32?speed:32):1; + MUTEX_UNLOCK(vars); +} + +MIKMODAPI void Player_SetTempo(UWORD tempo) +{ + if (tempo<32) tempo=32; + MUTEX_LOCK(vars); + if (pf) { + if ((!(pf->flags&UF_HIGHBPM))&&(tempo>255)) tempo=255; + pf->bpm=tempo; + } + MUTEX_UNLOCK(vars); +} + +MIKMODAPI int Player_QueryVoices(UWORD numvoices, VOICEINFO *vinfo) +{ + int i; + + if (numvoices > md_sngchn) + numvoices = md_sngchn; + + MUTEX_LOCK(vars); + if (pf) + for (i = 0; i < md_sngchn; i++) { + vinfo [i].i = pf->voice[i].main.i; + vinfo [i].s = pf->voice[i].main.s; + vinfo [i].panning = pf->voice [i].main.panning; + vinfo [i].volume = pf->voice [i].main.chanvol; + vinfo [i].period = pf->voice [i].main.period; + vinfo [i].kick = pf->voice [i].main.kick_flag; + pf->voice [i].main.kick_flag = 0; + } + MUTEX_UNLOCK(vars); + + return numvoices; +} + + + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/playercode/munitrk.c b/apps/plugins/mikmod/playercode/munitrk.c new file mode 100644 index 0000000..e8c3a44 --- /dev/null +++ b/apps/plugins/mikmod/playercode/munitrk.c @@ -0,0 +1,303 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS + for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: munitrk.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $ + + All routines dealing with the manipulation of UNITRK streams + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "mikmod_internals.h" + +/* Unibuffer chunk size */ +#define BUFPAGE 128 + +UWORD unioperands[UNI_LAST]={ + 0, /* not used */ + 1, /* UNI_NOTE */ + 1, /* UNI_INSTRUMENT */ + 1, /* UNI_PTEFFECT0 */ + 1, /* UNI_PTEFFECT1 */ + 1, /* UNI_PTEFFECT2 */ + 1, /* UNI_PTEFFECT3 */ + 1, /* UNI_PTEFFECT4 */ + 1, /* UNI_PTEFFECT5 */ + 1, /* UNI_PTEFFECT6 */ + 1, /* UNI_PTEFFECT7 */ + 1, /* UNI_PTEFFECT8 */ + 1, /* UNI_PTEFFECT9 */ + 1, /* UNI_PTEFFECTA */ + 1, /* UNI_PTEFFECTB */ + 1, /* UNI_PTEFFECTC */ + 1, /* UNI_PTEFFECTD */ + 1, /* UNI_PTEFFECTE */ + 1, /* UNI_PTEFFECTF */ + 1, /* UNI_S3MEFFECTA */ + 1, /* UNI_S3MEFFECTD */ + 1, /* UNI_S3MEFFECTE */ + 1, /* UNI_S3MEFFECTF */ + 1, /* UNI_S3MEFFECTI */ + 1, /* UNI_S3MEFFECTQ */ + 1, /* UNI_S3MEFFECTR */ + 1, /* UNI_S3MEFFECTT */ + 1, /* UNI_S3MEFFECTU */ + 0, /* UNI_KEYOFF */ + 1, /* UNI_KEYFADE */ + 2, /* UNI_VOLEFFECTS */ + 1, /* UNI_XMEFFECT4 */ + 1, /* UNI_XMEFFECT6 */ + 1, /* UNI_XMEFFECTA */ + 1, /* UNI_XMEFFECTE1 */ + 1, /* UNI_XMEFFECTE2 */ + 1, /* UNI_XMEFFECTEA */ + 1, /* UNI_XMEFFECTEB */ + 1, /* UNI_XMEFFECTG */ + 1, /* UNI_XMEFFECTH */ + 1, /* UNI_XMEFFECTL */ + 1, /* UNI_XMEFFECTP */ + 1, /* UNI_XMEFFECTX1 */ + 1, /* UNI_XMEFFECTX2 */ + 1, /* UNI_ITEFFECTG */ + 1, /* UNI_ITEFFECTH */ + 1, /* UNI_ITEFFECTI */ + 1, /* UNI_ITEFFECTM */ + 1, /* UNI_ITEFFECTN */ + 1, /* UNI_ITEFFECTP */ + 1, /* UNI_ITEFFECTT */ + 1, /* UNI_ITEFFECTU */ + 1, /* UNI_ITEFFECTW */ + 1, /* UNI_ITEFFECTY */ + 2, /* UNI_ITEFFECTZ */ + 1, /* UNI_ITEFFECTS0 */ + 2, /* UNI_ULTEFFECT9 */ + 2, /* UNI_MEDSPEED */ + 0, /* UNI_MEDEFFECTF1 */ + 0, /* UNI_MEDEFFECTF2 */ + 0, /* UNI_MEDEFFECTF3 */ + 2, /* UNI_OKTARP */ +}; + +/* Sparse description of the internal module format + ------------------------------------------------ + + A UNITRK stream is an array of bytes representing a single track of a pattern. +It's made up of 'repeat/length' bytes, opcodes and operands (sort of a assembly +language): + +rrrlllll +[REP/LEN][OPCODE][OPERAND][OPCODE][OPERAND] [REP/LEN][OPCODE][OPERAND].. +^ ^ ^ +|-------ROWS 0 - 0+REP of a track---------| |-------ROWS xx - xx+REP of a track... + + The rep/len byte contains the number of bytes in the current row, _including_ +the length byte itself (So the LENGTH byte of row 0 in the previous example +would have a value of 5). This makes it easy to search through a stream for a +particular row. A track is concluded by a 0-value length byte. + + The upper 3 bits of the rep/len byte contain the number of times -1 this row +is repeated for this track. (so a value of 7 means this row is repeated 8 times) + + Opcodes can range from 1 to 255 but currently only opcodes 1 to 62 are being +used. Each opcode can have a different number of operands. You can find the +number of operands to a particular opcode by using the opcode as an index into +the 'unioperands' table. + +*/ + +/*========== Reading routines */ + +static UBYTE *rowstart; /* startadress of a row */ +static UBYTE *rowend; /* endaddress of a row (exclusive) */ +static UBYTE *rowpc; /* current unimod(tm) programcounter */ + +static UBYTE lastbyte; /* for UniSkipOpcode() */ + +void UniSetRow(UBYTE* t) +{ + rowstart = t; + rowpc = rowstart; + rowend = t?rowstart+(*(rowpc++)&0x1f):t; +} + +UBYTE UniGetByte(void) +{ + return lastbyte = (rowpc end of track.. */ + l = (c>>5)+1; /* extract repeat value */ + if(l>row) break; /* reached wanted row? -> return pointer */ + row -= l; /* haven't reached row yet.. update row */ + t += c&0x1f; /* point t to the next row */ + } + return t; +} + +/*========== Writing routines */ + +static UBYTE *unibuf; /* pointer to the temporary unitrk buffer */ +static UWORD unimax; /* buffer size */ + +static UWORD unipc; /* buffer cursor */ +static UWORD unitt; /* current row index */ +static UWORD lastp; /* previous row index */ + +/* Resets index-pointers to create a new track. */ +void UniReset(void) +{ + unitt = 0; /* reset index to rep/len byte */ + unipc = 1; /* first opcode will be written to index 1 */ + lastp = 0; /* no previous row yet */ + unibuf[0] = 0; /* clear rep/len byte */ +} + +/* Expands the buffer */ +static BOOL UniExpand(int wanted) +{ + if ((unipc+wanted)>=unimax) { + UBYTE *newbuf; + + /* Expand the buffer by BUFPAGE bytes */ + newbuf=(UBYTE*)realloc(unibuf,(unimax+BUFPAGE)*sizeof(UBYTE)); + + /* Check if realloc succeeded */ + if(newbuf) { + unibuf = newbuf; + unimax+=BUFPAGE; + return 1; + } else + return 0; + } + return 1; +} + +/* Appends one byte of data to the current row of a track. */ +void UniWriteByte(UBYTE data) +{ + if (UniExpand(1)) + /* write byte to current position and update */ + unibuf[unipc++]=data; +} + +void UniWriteWord(UWORD data) +{ + if (UniExpand(2)) { + unibuf[unipc++]=data>>8; + unibuf[unipc++]=data&0xff; + } +} + +static BOOL MyCmp(UBYTE* a,UBYTE* b,UWORD l) +{ + UWORD t; + + for(t=0;t>5)+1; /* repeat of previous row */ + l = (unibuf[lastp]&0x1f); /* length of previous row */ + + len = unipc-unitt; /* length of current row */ + + /* Now, check if the previous and the current row are identical.. when they + are, just increase the repeat field of the previous row */ + if(n<8 && len==l && MyCmp(&unibuf[lastp+1],&unibuf[unitt+1],len-1)) { + unibuf[lastp]+=0x20; + unipc = unitt+1; + } else { + if (UniExpand(unitt-unipc)) { + /* current and previous row aren't equal... update the pointers */ + unibuf[unitt] = len; + lastp = unitt; + unitt = unipc++; + } + } +} + +/* Terminates the current unitrk stream and returns a pointer to a copy of the + stream. */ +UBYTE* UniDup(void) +{ + UBYTE *d; + + if (!UniExpand(unitt-unipc)) return NULL; + unibuf[unitt] = 0; + + if(!(d=(UBYTE *)_mm_malloc(unipc))) return NULL; + memcpy(d,unibuf,unipc); + + return d; +} + +BOOL UniInit(void) +{ + unimax = BUFPAGE; + + if(!(unibuf=(UBYTE*)_mm_malloc(unimax*sizeof(UBYTE)))) return 0; + return 1; +} + +void UniCleanup(void) +{ + if(unibuf) free(unibuf); + unibuf = NULL; +} + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/playercode/mwav.c b/apps/plugins/mikmod/playercode/mwav.c new file mode 100644 index 0000000..f054208 --- /dev/null +++ b/apps/plugins/mikmod/playercode/mwav.c @@ -0,0 +1,198 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS + for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: mwav.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $ + + WAV sample loader + +==============================================================================*/ + +/* + FIXME: Stereo .WAV files are not yet supported as samples. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +typedef struct WAV { + CHAR rID[4]; + ULONG rLen; + CHAR wID[4]; + CHAR fID[4]; + ULONG fLen; + UWORD wFormatTag; + UWORD nChannels; + ULONG nSamplesPerSec; + ULONG nAvgBytesPerSec; + UWORD nBlockAlign; + UWORD nFormatSpecific; +} WAV; + +SAMPLE* Sample_LoadGeneric_internal(MREADER* reader) +{ + SAMPLE *si=NULL; + WAV wh; + BOOL have_fmt=0; + + /* read wav header */ + _mm_read_string(wh.rID,4,reader); + wh.rLen = _mm_read_I_ULONG(reader); + _mm_read_string(wh.wID,4,reader); + + /* check for correct header */ + if(_mm_eof(reader)|| memcmp(wh.rID,"RIFF",4) || memcmp(wh.wID,"WAVE",4)) { + _mm_errno = MMERR_UNKNOWN_WAVE_TYPE; + return NULL; + } + + /* scan all RIFF blocks until we find the sample data */ + for(;;) { + CHAR dID[4]; + ULONG len,start; + + _mm_read_string(dID,4,reader); + len = _mm_read_I_ULONG(reader); + /* truncated file ? */ + if (_mm_eof(reader)) { + _mm_errno=MMERR_UNKNOWN_WAVE_TYPE; + return NULL; + } + start = _mm_ftell(reader); + + /* sample format block + should be present only once and before a data block */ + if(!memcmp(dID,"fmt ",4)) { + wh.wFormatTag = _mm_read_I_UWORD(reader); + wh.nChannels = _mm_read_I_UWORD(reader); + wh.nSamplesPerSec = _mm_read_I_ULONG(reader); + wh.nAvgBytesPerSec = _mm_read_I_ULONG(reader); + wh.nBlockAlign = _mm_read_I_UWORD(reader); + wh.nFormatSpecific = _mm_read_I_UWORD(reader); + +#ifdef MIKMOD_DEBUG + fprintf(stderr,"\rwavloader : wFormatTag=%04x blockalign=%04x nFormatSpc=%04x\n", + wh.wFormatTag,wh.nBlockAlign,wh.nFormatSpecific); +#endif + + if((have_fmt)||(wh.nChannels>1)) { + _mm_errno=MMERR_UNKNOWN_WAVE_TYPE; + return NULL; + } + have_fmt=1; + } else + /* sample data block + should be present only once and after a format block */ + if(!memcmp(dID,"data",4)) { + if(!have_fmt) { + _mm_errno=MMERR_UNKNOWN_WAVE_TYPE; + return NULL; + } + if(!(si=(SAMPLE*)_mm_malloc(sizeof(SAMPLE)))) return NULL; + si->speed = wh.nSamplesPerSec/wh.nChannels; + si->volume = 64; + si->length = len; + if(wh.nBlockAlign == 2) { + si->flags = SF_16BITS | SF_SIGNED; + si->length >>= 1; + } + si->inflags = si->flags; + SL_RegisterSample(si,MD_SNDFX,reader); + SL_LoadSamples(); + + /* skip any other remaining blocks - so in case of repeated sample + fragments, we'll return the first anyway instead of an error */ + break; + } + /* onto next block */ + _mm_fseek(reader,start+len,SEEK_SET); + if (_mm_eof(reader)) + break; + } + + return si; +} + +MIKMODAPI SAMPLE* Sample_LoadGeneric(MREADER* reader) +{ + SAMPLE* result; + + MUTEX_LOCK(vars); + result=Sample_LoadGeneric_internal(reader); + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI SAMPLE* Sample_LoadFP(int fp) +{ + SAMPLE* result=NULL; + MREADER* reader; + + if((reader=_mm_new_file_reader(fp))) { + result=Sample_LoadGeneric(reader); + _mm_delete_file_reader(reader); + } + return result; +} + +MIKMODAPI SAMPLE* Sample_Load(CHAR* filename) +{ + int fp; + SAMPLE *si=NULL; + + if(!(md_mode & DMODE_SOFT_SNDFX)) return NULL; + if((fp=_mm_fopen(filename,O_RDONLY))) { + si = Sample_LoadFP(fp); + _mm_fclose(fp); + } + return si; +} + +MIKMODAPI void Sample_Free(SAMPLE* si) +{ + if(si) { + MD_SampleUnload(si->handle); + free(si); + } +} + +void Sample_Free_internal(SAMPLE *si) +{ + MUTEX_LOCK(vars); + Sample_Free(si); + MUTEX_UNLOCK(vars); +} + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/playercode/npertab.c b/apps/plugins/mikmod/playercode/npertab.c new file mode 100644 index 0000000..ca02611 --- /dev/null +++ b/apps/plugins/mikmod/playercode/npertab.c @@ -0,0 +1,48 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: npertab.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $ + + MOD format period table. Used by both the MOD and M15 (15-inst mod) Loaders. + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mikmod_internals.h" + +UWORD npertab[7 * OCTAVE] = { + /* Octaves 6 -> 0 */ + /* C C# D D# E F F# G G# A A# B */ + 0x6b0,0x650,0x5f4,0x5a0,0x54c,0x500,0x4b8,0x474,0x434,0x3f8,0x3c0,0x38a, + 0x358,0x328,0x2fa,0x2d0,0x2a6,0x280,0x25c,0x23a,0x21a,0x1fc,0x1e0,0x1c5, + 0x1ac,0x194,0x17d,0x168,0x153,0x140,0x12e,0x11d,0x10d,0x0fe,0x0f0,0x0e2, + 0x0d6,0x0ca,0x0be,0x0b4,0x0aa,0x0a0,0x097,0x08f,0x087,0x07f,0x078,0x071, + 0x06b,0x065,0x05f,0x05a,0x055,0x050,0x04b,0x047,0x043,0x03f,0x03c,0x038, + 0x035,0x032,0x02f,0x02d,0x02a,0x028,0x025,0x023,0x021,0x01f,0x01e,0x01c, + 0x01b,0x019,0x018,0x016,0x015,0x014,0x013,0x012,0x011,0x010,0x00f,0x00e +}; + +/* ex:set ts=4: */ + diff --git a/apps/plugins/mikmod/playercode/sloader.c b/apps/plugins/mikmod/playercode/sloader.c new file mode 100644 index 0000000..390e94e --- /dev/null +++ b/apps/plugins/mikmod/playercode/sloader.c @@ -0,0 +1,519 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS + for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: sloader.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $ + + Routines for loading samples. The sample loader utilizes the routines + provided by the "registered" sample loader. + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "mikmod_internals.h" + +static int sl_rlength; +static SWORD sl_old; +static SWORD *sl_buffer=NULL; +static SAMPLOAD *musiclist=NULL,*sndfxlist=NULL; + +/* size of the loader buffer in words */ +#define SLBUFSIZE 2048 + +/* IT-Compressed status structure */ +typedef struct ITPACK { + UWORD bits; /* current number of bits */ + UWORD bufbits; /* bits in buffer */ + SWORD last; /* last output */ + UBYTE buf; /* bit buffer */ +} ITPACK; + +BOOL SL_Init(SAMPLOAD* s) +{ + if(!sl_buffer) + if(!(sl_buffer=_mm_malloc(SLBUFSIZE*sizeof(SWORD)))) return 0; + + sl_rlength = s->length; + if(s->infmt & SF_16BITS) sl_rlength>>=1; + sl_old = 0; + + return 1; +} + +void SL_Exit(SAMPLOAD *s) +{ + if(sl_rlength>0) _mm_fseek(s->reader,sl_rlength,SEEK_CUR); + if(sl_buffer) { + free(sl_buffer); + sl_buffer=NULL; + } +} + +/* unpack a 8bit IT packed sample */ +static BOOL read_itcompr8(ITPACK* status,MREADER *reader,SWORD *sl_buffer,UWORD count,UWORD* incnt) +{ + SWORD *dest=sl_buffer,*end=sl_buffer+count; + UWORD x,y,needbits,havebits,new_count=0; + UWORD bits = status->bits; + UWORD bufbits = status->bufbits; + SBYTE last = status->last; + UBYTE buf = status->buf; + + while (dest>=y; + bufbits-=y; + needbits-=y; + havebits+=y; + } + if (new_count) { + new_count = 0; + if (++x >= bits) + x++; + bits = x; + continue; + } + if (bits<7) { + if (x==(1<<(bits-1))) { + new_count = 1; + continue; + } + } + else if (bits<9) { + y = (0xff >> (9-bits)) - 4; + if ((x>y)&&(x<=y+8)) { + if ((x-=y)>=bits) + x++; + bits = x; + continue; + } + } + else if (bits<10) { + if (x>=0x100) { + bits=x-0x100+1; + continue; + } + } else { + /* error in compressed data... */ + _mm_errno=MMERR_ITPACK_INVALID_DATA; + return 0; + } + + if (bits<8) /* extend sign */ + x = ((SBYTE)(x <<(8-bits))) >> (8-bits); + *(dest++)= (last+=x) << 8; /* convert to 16 bit */ + } + status->bits = bits; + status->bufbits = bufbits; + status->last = last; + status->buf = buf; + return dest-sl_buffer; +} + +/* unpack a 16bit IT packed sample */ +static BOOL read_itcompr16(ITPACK *status,MREADER *reader,SWORD *sl_buffer,UWORD count,UWORD* incnt) +{ + SWORD *dest=sl_buffer,*end=sl_buffer+count; + SLONG x,y,needbits,havebits,new_count=0; + UWORD bits = status->bits; + UWORD bufbits = status->bufbits; + SWORD last = status->last; + UBYTE buf = status->buf; + + while (dest>=y; + bufbits-=y; + needbits-=y; + havebits+=y; + } + if (new_count) { + new_count = 0; + if (++x >= bits) + x++; + bits = x; + continue; + } + if (bits<7) { + if (x==(1<<(bits-1))) { + new_count=1; + continue; + } + } + else if (bits<17) { + y=(0xffff>>(17-bits))-8; + if ((x>y)&&(x<=y+16)) { + if ((x-=y)>=bits) + x++; + bits = x; + continue; + } + } + else if (bits<18) { + if (x>=0x10000) { + bits=x-0x10000+1; + continue; + } + } else { + /* error in compressed data... */ + _mm_errno=MMERR_ITPACK_INVALID_DATA; + return 0; + } + + if (bits<16) /* extend sign */ + x = ((SWORD)(x<<(16-bits)))>>(16-bits); + *(dest++)=(last+=x); + } + status->bits = bits; + status->bufbits = bufbits; + status->last = last; + status->buf = buf; + return dest-sl_buffer; +} + +static BOOL SL_LoadInternal(void* buffer,UWORD infmt,UWORD outfmt,int scalefactor,ULONG length,MREADER* reader,BOOL dither) +{ + SBYTE *bptr = (SBYTE*)buffer; + SWORD *wptr = (SWORD*)buffer; + int stodo,t,u; + + int result,c_block=0; /* compression bytes until next block */ + ITPACK status; + UWORD incnt; + + while(length) { + stodo=(lengthRead(reader,sl_buffer,sizeof(SBYTE)*stodo); + src = (SBYTE*)sl_buffer; + dest = sl_buffer; + src += stodo;dest += stodo; + + for(t=0;t>1; + length-=2; + } + stodo = idx; + } + } + + if(outfmt & SF_16BITS) { + for(t=0;t>8; + } + } + return 0; +} + +BOOL SL_Load(void* buffer,SAMPLOAD *smp,ULONG length) +{ + return SL_LoadInternal(buffer,smp->infmt,smp->outfmt,smp->scalefactor, + length,smp->reader,0); +} + +/* Registers a sample for loading when SL_LoadSamples() is called. */ +SAMPLOAD* SL_RegisterSample(SAMPLE* s,int type,MREADER* reader) +{ + SAMPLOAD *news,**samplist,*cruise; + + if(type==MD_MUSIC) { + samplist = &musiclist; + cruise = musiclist; + } else + if (type==MD_SNDFX) { + samplist = &sndfxlist; + cruise = sndfxlist; + } else + return NULL; + + /* Allocate and add structure to the END of the list */ + if(!(news=(SAMPLOAD*)_mm_malloc(sizeof(SAMPLOAD)))) return NULL; + + if(cruise) { + while(cruise->next) cruise=cruise->next; + cruise->next = news; + } else + *samplist = news; + + news->infmt = s->flags & SF_FORMATMASK; + news->outfmt = news->infmt; + news->reader = reader; + news->sample = s; + news->length = s->length; + news->loopstart = s->loopstart; + news->loopend = s->loopend; + + return news; +} + +static void FreeSampleList(SAMPLOAD* s) +{ + SAMPLOAD *old; + + while(s) { + old = s; + s = s->next; + free(old); + } +} + +/* Returns the total amount of memory required by the samplelist queue. */ +static ULONG SampleTotal(SAMPLOAD* samplist,int type) +{ + int total = 0; + + while(samplist) { + samplist->sample->flags= + (samplist->sample->flags&~SF_FORMATMASK)|samplist->outfmt; + total += MD_SampleLength(type,samplist->sample); + samplist=samplist->next; + } + + return total; +} + +static ULONG RealSpeed(SAMPLOAD *s) +{ + return(s->sample->speed/(s->scalefactor?s->scalefactor:1)); +} + +static BOOL DitherSamples(SAMPLOAD* samplist,int type) +{ + SAMPLOAD *c2smp=NULL; + ULONG maxsize, speed; + SAMPLOAD *s; + + if(!samplist) return 0; + + if((maxsize=MD_SampleSpace(type)*1024)) + while(SampleTotal(samplist,type)>maxsize) { + /* First Pass - check for any 16 bit samples */ + s = samplist; + while(s) { + if(s->outfmt & SF_16BITS) { + SL_Sample16to8(s); + break; + } + s=s->next; + } + /* Second pass (if no 16bits found above) is to take the sample with + the highest speed and dither it by half. */ + if(!s) { + s = samplist; + speed = 0; + while(s) { + if((s->sample->length) && (RealSpeed(s)>speed)) { + speed=RealSpeed(s); + c2smp=s; + } + s=s->next; + } + if (c2smp) + SL_HalveSample(c2smp,2); + } + } + + /* Samples dithered, now load them ! */ + s = samplist; + while(s) { + /* sample has to be loaded ? -> increase number of samples, allocate + memory and load sample. */ + if(s->sample->length) { + if(s->sample->seekpos) + _mm_fseek(s->reader, s->sample->seekpos, SEEK_SET); + + /* Call the sample load routine of the driver module. It has to + return a 'handle' (>=0) that identifies the sample. */ + s->sample->handle = MD_SampleLoad(s, type); + s->sample->flags = (s->sample->flags & ~SF_FORMATMASK) | s->outfmt; + if(s->sample->handle<0) { + FreeSampleList(samplist); + if(_mm_errorhandler) _mm_errorhandler(); + return 1; + } + } + s = s->next; + } + + FreeSampleList(samplist); + return 0; +} + +BOOL SL_LoadSamples(void) +{ + BOOL ok; + + _mm_critical = 0; + + if((!musiclist)&&(!sndfxlist)) return 0; + ok=DitherSamples(musiclist,MD_MUSIC)||DitherSamples(sndfxlist,MD_SNDFX); + musiclist=sndfxlist=NULL; + + return ok; +} + +void SL_Sample16to8(SAMPLOAD* s) +{ + s->outfmt &= ~SF_16BITS; + s->sample->flags = (s->sample->flags&~SF_FORMATMASK) | s->outfmt; +} + +void SL_Sample8to16(SAMPLOAD* s) +{ + s->outfmt |= SF_16BITS; + s->sample->flags = (s->sample->flags&~SF_FORMATMASK) | s->outfmt; +} + +void SL_SampleSigned(SAMPLOAD* s) +{ + s->outfmt |= SF_SIGNED; + s->sample->flags = (s->sample->flags&~SF_FORMATMASK) | s->outfmt; +} + +void SL_SampleUnsigned(SAMPLOAD* s) +{ + s->outfmt &= ~SF_SIGNED; + s->sample->flags = (s->sample->flags&~SF_FORMATMASK) | s->outfmt; +} + +void SL_HalveSample(SAMPLOAD* s,int factor) +{ + s->scalefactor=factor>0?factor:2; + + s->sample->divfactor = s->scalefactor; + s->sample->length = s->length / s->scalefactor; + s->sample->loopstart = s->loopstart / s->scalefactor; + s->sample->loopend = s->loopend / s->scalefactor; +} + + +/* ex:set ts=4: */ diff --git a/apps/plugins/mikmod/playercode/virtch.c b/apps/plugins/mikmod/playercode/virtch.c new file mode 100644 index 0000000..a7b49d8 --- /dev/null +++ b/apps/plugins/mikmod/playercode/virtch.c @@ -0,0 +1,968 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file + AUTHORS for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: virtch.c,v 1.2 2004/02/13 13:31:54 raph Exp $ + + Sample mixing routines, using a 32 bits mixing buffer. + +==============================================================================*/ + +/* + + Optional features include: + (a) 4-step reverb (for 16 bit output only) + (b) Interpolation of sample data during mixing + (c) Dolby Surround Sound +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +/* + Constant definitions + ==================== + + BITSHIFT + Controls the maximum volume of the sound output. All data is shifted + right by BITSHIFT after being mixed. Higher values result in quieter + sound and less chance of distortion. + + REVERBERATION + Controls the duration of the reverb. Larger values represent a shorter + reverb loop. Smaller values extend the reverb but can result in more of + an echo-ish sound. + +*/ + +#define BITSHIFT 9 +#define REVERBERATION 110000L + +#define FRACBITS 11 +#define FRACMASK ((1L< sample has to be restarted */ + UBYTE active; /* =1 -> sample is playing */ + UWORD flags; /* 16/8 bits looping/one-shot */ + SWORD handle; /* identifies the sample */ + ULONG start; /* start index */ + ULONG size; /* samplesize */ + ULONG reppos; /* loop start */ + ULONG repend; /* loop end */ + ULONG frq; /* current frequency */ + int vol; /* current volume */ + int pan; /* current panning position */ + + int rampvol; + int lvolsel,rvolsel; /* Volume factor in range 0-255 */ + int oldlvol,oldrvol; + + SLONGLONG current; /* current index in the sample */ + SLONGLONG increment; /* increment value */ +} VINFO; + +static SWORD **Samples; +static VINFO *vinf=NULL,*vnf; +static long tickleft,samplesthatfit,vc_memory=0; +static int vc_softchn; +static SLONGLONG idxsize,idxlpos,idxlend; +static SLONG *vc_tickbuf=NULL; +static UWORD vc_mode; + +/* Reverb control variables */ + +static int RVc1, RVc2, RVc3, RVc4, RVc5, RVc6, RVc7, RVc8; +static ULONG RVRindex; + +/* For Mono or Left Channel */ +static SLONG *RVbufL1=NULL,*RVbufL2=NULL,*RVbufL3=NULL,*RVbufL4=NULL, + *RVbufL5=NULL,*RVbufL6=NULL,*RVbufL7=NULL,*RVbufL8=NULL; + +/* For Stereo only (Right Channel) */ +static SLONG *RVbufR1=NULL,*RVbufR2=NULL,*RVbufR3=NULL,*RVbufR4=NULL, + *RVbufR5=NULL,*RVbufR6=NULL,*RVbufR7=NULL,*RVbufR8=NULL; + +#ifdef NATIVE_64BIT_INT +#define NATIVE SLONGLONG +#else +#define NATIVE SLONG +#endif + +/*========== 32 bit sample mixers - only for 32 bit platforms */ +#ifndef NATIVE_64BIT_INT + +static SLONG Mix32MonoNormal(SWORD* srce,SLONG* dest,SLONG index,SLONG increment,SLONG todo) +{ + SWORD sample; + SLONG lvolsel = vnf->lvolsel; + + while(todo--) { + sample = srce[index >> FRACBITS]; + index += increment; + + *dest++ += lvolsel * sample; + } + return index; +} + +static SLONG Mix32StereoNormal(SWORD* srce,SLONG* dest,SLONG index,SLONG increment,SLONG todo) +{ + SWORD sample; + SLONG lvolsel = vnf->lvolsel; + SLONG rvolsel = vnf->rvolsel; + + while(todo--) { + sample=srce[index >> FRACBITS]; + index += increment; + + *dest++ += lvolsel * sample; + *dest++ += rvolsel * sample; + } + return index; +} + +static SLONG Mix32SurroundNormal(SWORD* srce,SLONG* dest,SLONG index,SLONG increment,SLONG todo) +{ + SWORD sample; + SLONG lvolsel = vnf->lvolsel; + SLONG rvolsel = vnf->rvolsel; + + if (lvolsel>=rvolsel) { + while(todo--) { + sample = srce[index >> FRACBITS]; + index += increment; + + *dest++ += lvolsel*sample; + *dest++ -= lvolsel*sample; + } + } else { + while(todo--) { + sample = srce[index >> FRACBITS]; + index += increment; + + *dest++ -= rvolsel*sample; + *dest++ += rvolsel*sample; + } + } + return index; +} + +static SLONG Mix32MonoInterp(SWORD* srce,SLONG* dest,SLONG index,SLONG increment,SLONG todo) +{ + SLONG sample; + SLONG lvolsel = vnf->lvolsel; + SLONG rampvol = vnf->rampvol; + + if (rampvol) { + SLONG oldlvol = vnf->oldlvol - lvolsel; + while(todo--) { + sample=(SLONG)srce[index>>FRACBITS]+ + ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS]) + *(index&FRACMASK)>>FRACBITS); + index += increment; + + *dest++ += ((lvolsel << CLICK_SHIFT) + oldlvol * rampvol) + * sample >> CLICK_SHIFT; + if (!--rampvol) + break; + } + vnf->rampvol = rampvol; + if (todo < 0) + return index; + } + + while(todo--) { + sample=(SLONG)srce[index>>FRACBITS]+ + ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS]) + *(index&FRACMASK)>>FRACBITS); + index += increment; + + *dest++ += lvolsel * sample; + } + return index; +} + +static SLONG Mix32StereoInterp(SWORD* srce,SLONG* dest,SLONG index,SLONG increment,SLONG todo) +{ + SLONG sample; + SLONG lvolsel = vnf->lvolsel; + SLONG rvolsel = vnf->rvolsel; + SLONG rampvol = vnf->rampvol; + + if (rampvol) { + SLONG oldlvol = vnf->oldlvol - lvolsel; + SLONG oldrvol = vnf->oldrvol - rvolsel; + while(todo--) { + sample=(SLONG)srce[index>>FRACBITS]+ + ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS]) + *(index&FRACMASK)>>FRACBITS); + index += increment; + + *dest++ += ((lvolsel << CLICK_SHIFT) + oldlvol * rampvol) + * sample >> CLICK_SHIFT; + *dest++ += ((rvolsel << CLICK_SHIFT) + oldrvol * rampvol) + * sample >> CLICK_SHIFT; + if (!--rampvol) + break; + } + vnf->rampvol = rampvol; + if (todo < 0) + return index; + } + + while(todo--) { + sample=(SLONG)srce[index>>FRACBITS]+ + ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS]) + *(index&FRACMASK)>>FRACBITS); + index += increment; + + *dest++ += lvolsel * sample; + *dest++ += rvolsel * sample; + } + return index; +} + +static SLONG Mix32SurroundInterp(SWORD* srce,SLONG* dest,SLONG index,SLONG increment,SLONG todo) +{ + SLONG sample; + SLONG lvolsel = vnf->lvolsel; + SLONG rvolsel = vnf->rvolsel; + SLONG rampvol = vnf->rampvol; + SLONG oldvol, vol; + + if (lvolsel >= rvolsel) { + vol = lvolsel; + oldvol = vnf->oldlvol; + } else { + vol = rvolsel; + oldvol = vnf->oldrvol; + } + + if (rampvol) { + oldvol -= vol; + while(todo--) { + sample=(SLONG)srce[index>>FRACBITS]+ + ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS]) + *(index&FRACMASK)>>FRACBITS); + index += increment; + + sample=((vol << CLICK_SHIFT) + oldvol * rampvol) + * sample >> CLICK_SHIFT; + *dest++ += sample; + *dest++ -= sample; + + if (!--rampvol) + break; + } + vnf->rampvol = rampvol; + if (todo < 0) + return index; + } + + while(todo--) { + sample=(SLONG)srce[index>>FRACBITS]+ + ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS]) + *(index&FRACMASK)>>FRACBITS); + index += increment; + + *dest++ += vol*sample; + *dest++ -= vol*sample; + } + return index; +} +#endif + +/*========== 64 bit sample mixers - all platforms */ + +static SLONGLONG MixMonoNormal(SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,SLONG todo) +{ + SWORD sample; + SLONG lvolsel = vnf->lvolsel; + + while(todo--) { + sample = srce[index >> FRACBITS]; + index += increment; + + *dest++ += lvolsel * sample; + } + return index; +} + +static SLONGLONG MixStereoNormal(SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,SLONG todo) +{ + SWORD sample; + SLONG lvolsel = vnf->lvolsel; + SLONG rvolsel = vnf->rvolsel; + + while(todo--) { + sample=srce[index >> FRACBITS]; + index += increment; + + *dest++ += lvolsel * sample; + *dest++ += rvolsel * sample; + } + return index; +} + +static SLONGLONG MixSurroundNormal(SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,SLONG todo) +{ + SWORD sample; + SLONG lvolsel = vnf->lvolsel; + SLONG rvolsel = vnf->rvolsel; + + if(vnf->lvolsel>=vnf->rvolsel) { + while(todo--) { + sample = srce[index >> FRACBITS]; + index += increment; + + *dest++ += lvolsel*sample; + *dest++ -= lvolsel*sample; + } + } else { + while(todo--) { + sample = srce[index >> FRACBITS]; + index += increment; + + *dest++ -= rvolsel*sample; + *dest++ += rvolsel*sample; + } + } + return index; +} + +static SLONGLONG MixMonoInterp(SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,SLONG todo) +{ + SLONG sample; + SLONG lvolsel = vnf->lvolsel; + SLONG rampvol = vnf->rampvol; + + if (rampvol) { + SLONG oldlvol = vnf->oldlvol - lvolsel; + while(todo--) { + sample=(SLONG)srce[index>>FRACBITS]+ + ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS]) + *(index&FRACMASK)>>FRACBITS); + index += increment; + + *dest++ += ((lvolsel << CLICK_SHIFT) + oldlvol * rampvol) + * sample >> CLICK_SHIFT; + if (!--rampvol) + break; + } + vnf->rampvol = rampvol; + if (todo < 0) + return index; + } + + while(todo--) { + sample=(SLONG)srce[index>>FRACBITS]+ + ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS]) + *(index&FRACMASK)>>FRACBITS); + index += increment; + + *dest++ += lvolsel * sample; + } + return index; +} + +static SLONGLONG MixStereoInterp(SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,SLONG todo) +{ + SLONG sample; + SLONG lvolsel = vnf->lvolsel; + SLONG rvolsel = vnf->rvolsel; + SLONG rampvol = vnf->rampvol; + + if (rampvol) { + SLONG oldlvol = vnf->oldlvol - lvolsel; + SLONG oldrvol = vnf->oldrvol - rvolsel; + while(todo--) { + sample=(SLONG)srce[index>>FRACBITS]+ + ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS]) + *(index&FRACMASK)>>FRACBITS); + index += increment; + + *dest++ +=((lvolsel << CLICK_SHIFT) + oldlvol * rampvol) + * sample >> CLICK_SHIFT; + *dest++ +=((rvolsel << CLICK_SHIFT) + oldrvol * rampvol) + * sample >> CLICK_SHIFT; + if (!--rampvol) + break; + } + vnf->rampvol = rampvol; + if (todo < 0) + return index; + } + + while(todo--) { + sample=(SLONG)srce[index>>FRACBITS]+ + ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS]) + *(index&FRACMASK)>>FRACBITS); + index += increment; + + *dest++ += lvolsel * sample; + *dest++ += rvolsel * sample; + } + return index; +} + +static SLONGLONG MixSurroundInterp(SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,SLONG todo) +{ + SLONG sample; + SLONG lvolsel = vnf->lvolsel; + SLONG rvolsel = vnf->rvolsel; + SLONG rampvol = vnf->rampvol; + SLONG oldvol, vol; + + if (lvolsel >= rvolsel) { + vol = lvolsel; + oldvol = vnf->oldlvol; + } else { + vol = rvolsel; + oldvol = vnf->oldrvol; + } + + if (rampvol) { + oldvol -= vol; + while(todo--) { + sample=(SLONG)srce[index>>FRACBITS]+ + ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS]) + *(index&FRACMASK)>>FRACBITS); + index += increment; + + sample=((vol << CLICK_SHIFT) + oldvol * rampvol) + * sample >> CLICK_SHIFT; + *dest++ += sample; + *dest++ -= sample; + if (!--rampvol) + break; + } + vnf->rampvol = rampvol; + if (todo < 0) + return index; + } + + while(todo--) { + sample=(SLONG)srce[index>>FRACBITS]+ + ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS]) + *(index&FRACMASK)>>FRACBITS); + index += increment; + + *dest++ += vol*sample; + *dest++ -= vol*sample; + } + return index; +} + +static void (*MixReverb)(SLONG* srce,NATIVE count); + +/* Reverb macros */ +#define COMPUTE_LOC(n) loc##n = RVRindex % RVc##n +#define COMPUTE_LECHO(n) RVbufL##n [loc##n ]=speedup+((ReverbPct*RVbufL##n [loc##n ])>>7) +#define COMPUTE_RECHO(n) RVbufR##n [loc##n ]=speedup+((ReverbPct*RVbufR##n [loc##n ])>>7) + +static void MixReverb_Normal(SLONG* srce,NATIVE count) +{ + unsigned int speedup; + int ReverbPct; + unsigned int loc1,loc2,loc3,loc4; + unsigned int loc5,loc6,loc7,loc8; + + ReverbPct=58+(md_reverb<<2); + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + while(count--) { + /* Compute the left channel echo buffers */ + speedup = *srce >> 3; + + COMPUTE_LECHO(1); COMPUTE_LECHO(2); COMPUTE_LECHO(3); COMPUTE_LECHO(4); + COMPUTE_LECHO(5); COMPUTE_LECHO(6); COMPUTE_LECHO(7); COMPUTE_LECHO(8); + + /* Prepare to compute actual finalized data */ + RVRindex++; + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + /* left channel */ + *srce++ +=RVbufL1[loc1]-RVbufL2[loc2]+RVbufL3[loc3]-RVbufL4[loc4]+ + RVbufL5[loc5]-RVbufL6[loc6]+RVbufL7[loc7]-RVbufL8[loc8]; + } +} + +static void MixReverb_Stereo(SLONG* srce,NATIVE count) +{ + unsigned int speedup; + int ReverbPct; + unsigned int loc1, loc2, loc3, loc4; + unsigned int loc5, loc6, loc7, loc8; + + ReverbPct = 92+(md_reverb<<1); + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + while(count--) { + /* Compute the left channel echo buffers */ + speedup = *srce >> 3; + + COMPUTE_LECHO(1); COMPUTE_LECHO(2); COMPUTE_LECHO(3); COMPUTE_LECHO(4); + COMPUTE_LECHO(5); COMPUTE_LECHO(6); COMPUTE_LECHO(7); COMPUTE_LECHO(8); + + /* Compute the right channel echo buffers */ + speedup = srce[1] >> 3; + + COMPUTE_RECHO(1); COMPUTE_RECHO(2); COMPUTE_RECHO(3); COMPUTE_RECHO(4); + COMPUTE_RECHO(5); COMPUTE_RECHO(6); COMPUTE_RECHO(7); COMPUTE_RECHO(8); + + /* Prepare to compute actual finalized data */ + RVRindex++; + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + /* left channel then right channel */ + *srce++ +=RVbufL1[loc1]-RVbufL2[loc2]+RVbufL3[loc3]-RVbufL4[loc4]+ + RVbufL5[loc5]-RVbufL6[loc6]+RVbufL7[loc7]-RVbufL8[loc8]; + + *srce++ +=RVbufR1[loc1]-RVbufR2[loc2]+RVbufR3[loc3]-RVbufR4[loc4]+ + RVbufR5[loc5]-RVbufR6[loc6]+RVbufR7[loc7]-RVbufR8[loc8]; + } +} + +/* Mixing macros */ +#define EXTRACT_SAMPLE_FP(var,size) var=(*srce++>>(BITSHIFT-size)) * ((1.0f / 32768.0f) / (1 << size)) +#define CHECK_SAMPLE_FP(var,bound) var=(var>bound)?bound:(var<-bound)?-bound:var +#define PUT_SAMPLE_FP(var) *dste++=var + +static void Mix32ToFP(float* dste,SLONG* srce,NATIVE count) +{ + float x1,x2,x3,x4; + int remain; + + #define FP_SHIFT 4 + + remain=count&3; + for(count>>=2;count;count--) { + EXTRACT_SAMPLE_FP(x1,FP_SHIFT); EXTRACT_SAMPLE_FP(x2,FP_SHIFT); + EXTRACT_SAMPLE_FP(x3,FP_SHIFT); EXTRACT_SAMPLE_FP(x4,FP_SHIFT); + + CHECK_SAMPLE_FP(x1,1.0f); CHECK_SAMPLE_FP(x2,1.0f); + CHECK_SAMPLE_FP(x3,1.0f); CHECK_SAMPLE_FP(x4,1.0f); + + PUT_SAMPLE_FP(x1); PUT_SAMPLE_FP(x2); + PUT_SAMPLE_FP(x3); PUT_SAMPLE_FP(x4); + } + while(remain--) { + EXTRACT_SAMPLE_FP(x1,FP_SHIFT); + CHECK_SAMPLE_FP(x1,1.0f); + PUT_SAMPLE_FP(x1); + } +} + + +/* Mixing macros */ +#define EXTRACT_SAMPLE(var,size) var=*srce++>>(BITSHIFT+16-size) +#define CHECK_SAMPLE(var,bound) var=(var>=bound)?bound-1:(var<-bound)?-bound:var +#define PUT_SAMPLE(var) *dste++=var + +static void Mix32To16(SWORD* dste,SLONG* srce,NATIVE count) +{ + SLONG x1,x2,x3,x4; + int remain; + + remain=count&3; + for(count>>=2;count;count--) { + EXTRACT_SAMPLE(x1,16); EXTRACT_SAMPLE(x2,16); + EXTRACT_SAMPLE(x3,16); EXTRACT_SAMPLE(x4,16); + + CHECK_SAMPLE(x1,32768); CHECK_SAMPLE(x2,32768); + CHECK_SAMPLE(x3,32768); CHECK_SAMPLE(x4,32768); + + PUT_SAMPLE(x1); PUT_SAMPLE(x2); PUT_SAMPLE(x3); PUT_SAMPLE(x4); + } + while(remain--) { + EXTRACT_SAMPLE(x1,16); + CHECK_SAMPLE(x1,32768); + PUT_SAMPLE(x1); + } +} + +static void Mix32To8(SBYTE* dste,SLONG* srce,NATIVE count) +{ + SWORD x1,x2,x3,x4; + int remain; + + remain=count&3; + for(count>>=2;count;count--) { + EXTRACT_SAMPLE(x1,8); EXTRACT_SAMPLE(x2,8); + EXTRACT_SAMPLE(x3,8); EXTRACT_SAMPLE(x4,8); + + CHECK_SAMPLE(x1,128); CHECK_SAMPLE(x2,128); + CHECK_SAMPLE(x3,128); CHECK_SAMPLE(x4,128); + + PUT_SAMPLE(x1+128); PUT_SAMPLE(x2+128); + PUT_SAMPLE(x3+128); PUT_SAMPLE(x4+128); + } + while(remain--) { + EXTRACT_SAMPLE(x1,8); + CHECK_SAMPLE(x1,128); + PUT_SAMPLE(x1+128); + } +} + +static void AddChannel(SLONG* ptr,NATIVE todo) +{ + SLONGLONG end,done; + SWORD *s; + + if(!(s=Samples[vnf->handle])) { + vnf->current = vnf->active = 0; + return; + } + + /* update the 'current' index so the sample loops, or stops playing if it + reached the end of the sample */ + while(todo>0) { + SLONGLONG endpos; + + if(vnf->flags & SF_REVERSE) { + /* The sample is playing in reverse */ + if((vnf->flags&SF_LOOP)&&(vnf->currentflags & SF_BIDI) { + /* sample is doing bidirectional loops, so 'bounce' the + current index against the idxlpos */ + vnf->current = idxlpos+(idxlpos-vnf->current); + vnf->flags &= ~SF_REVERSE; + vnf->increment = -vnf->increment; + } else + /* normal backwards looping, so set the current position to + loopend index */ + vnf->current=idxlend-(idxlpos-vnf->current); + } else { + /* the sample is not looping, so check if it reached index 0 */ + if(vnf->current < 0) { + /* playing index reached 0, so stop playing this sample */ + vnf->current = vnf->active = 0; + break; + } + } + } else { + /* The sample is playing forward */ + if((vnf->flags & SF_LOOP) && + (vnf->current >= idxlend)) { + /* the sample is looping, check the loopend index */ + if(vnf->flags & SF_BIDI) { + /* sample is doing bidirectional loops, so 'bounce' the + current index against the idxlend */ + vnf->flags |= SF_REVERSE; + vnf->increment = -vnf->increment; + vnf->current = idxlend-(vnf->current-idxlend); + } else + /* normal backwards looping, so set the current position + to loopend index */ + vnf->current=idxlpos+(vnf->current-idxlend); + } else { + /* sample is not looping, so check if it reached the last + position */ + if(vnf->current >= idxsize) { + /* yes, so stop playing this sample */ + vnf->current = vnf->active = 0; + break; + } + } + } + + end=(vnf->flags&SF_REVERSE)?(vnf->flags&SF_LOOP)?idxlpos:0: + (vnf->flags&SF_LOOP)?idxlend:idxsize; + + /* if the sample is not blocked... */ + if((end==vnf->current)||(!vnf->increment)) + done=0; + else { + done=MIN((end-vnf->current)/vnf->increment+1,todo); + if(done<0) done=0; + } + + if(!done) { + vnf->active = 0; + break; + } + + endpos=vnf->current+done*vnf->increment; + + if(vnf->vol) { +#ifndef NATIVE_64BIT_INT + /* use the 32 bit mixers as often as we can (they're much faster) */ + if((vnf->current<0x7fffffff)&&(endpos<0x7fffffff)) { + if((md_mode & DMODE_INTERP)) { + if(vc_mode & DMODE_STEREO) { + if((vnf->pan==PAN_SURROUND)&&(md_mode&DMODE_SURROUND)) + vnf->current=Mix32SurroundInterp + (s,ptr,vnf->current,vnf->increment,done); + else + vnf->current=Mix32StereoInterp + (s,ptr,vnf->current,vnf->increment,done); + } else + vnf->current=Mix32MonoInterp + (s,ptr,vnf->current,vnf->increment,done); + } else if(vc_mode & DMODE_STEREO) { + if((vnf->pan==PAN_SURROUND)&&(md_mode&DMODE_SURROUND)) + vnf->current=Mix32SurroundNormal + (s,ptr,vnf->current,vnf->increment,done); + else + vnf->current=Mix32StereoNormal + (s,ptr,vnf->current,vnf->increment,done); + } else + vnf->current=Mix32MonoNormal + (s,ptr,vnf->current,vnf->increment,done); + } else +#endif + { + if((md_mode & DMODE_INTERP)) { + if(vc_mode & DMODE_STEREO) { + if((vnf->pan==PAN_SURROUND)&&(md_mode&DMODE_SURROUND)) + vnf->current=MixSurroundInterp + (s,ptr,vnf->current,vnf->increment,done); + else + vnf->current=MixStereoInterp + (s,ptr,vnf->current,vnf->increment,done); + } else + vnf->current=MixMonoInterp + (s,ptr,vnf->current,vnf->increment,done); + } else if(vc_mode & DMODE_STEREO) { + if((vnf->pan==PAN_SURROUND)&&(md_mode&DMODE_SURROUND)) + vnf->current=MixSurroundNormal + (s,ptr,vnf->current,vnf->increment,done); + else + vnf->current=MixStereoNormal + (s,ptr,vnf->current,vnf->increment,done); + } else + vnf->current=MixMonoNormal + (s,ptr,vnf->current,vnf->increment,done); + } + } else + /* update sample position */ + vnf->current=endpos; + + todo-=done; + ptr +=(vc_mode & DMODE_STEREO)?(done<<1):done; + } +} + +#define _IN_VIRTCH_ +#include "virtch_common.c" +#undef _IN_VIRTCH_ + +void VC1_WriteSamples(SBYTE* buf,ULONG todo) +{ + int left,portion=0,count; + SBYTE *buffer; + int t, pan, vol; + + while(todo) { + if(!tickleft) { + if(vc_mode & DMODE_SOFT_MUSIC) md_player(); + tickleft=(md_mixfreq*125L)/(md_bpm*50L); + } + left = MIN(tickleft, todo); + buffer = buf; + tickleft -= left; + todo -= left; + buf += samples2bytes(left); + + while(left) { + portion = MIN(left, samplesthatfit); + count = (vc_mode & DMODE_STEREO)?(portion<<1):portion; + memset(vc_tickbuf, 0, count<<2); + for(t=0;tkick) { + vnf->current=((SLONGLONG)vnf->start)<kick =0; + vnf->active =1; + } + + if(!vnf->frq) vnf->active = 0; + + if(vnf->active) { + vnf->increment=((SLONGLONG)(vnf->frq<flags&SF_REVERSE) vnf->increment=-vnf->increment; + vol = vnf->vol; pan = vnf->pan; + + vnf->oldlvol=vnf->lvolsel;vnf->oldrvol=vnf->rvolsel; + if(vc_mode & DMODE_STEREO) { + if(pan != PAN_SURROUND) { + vnf->lvolsel=(vol*(PAN_RIGHT-pan))>>8; + vnf->rvolsel=(vol*pan)>>8; + } else + vnf->lvolsel=vnf->rvolsel=vol/2; + } else + vnf->lvolsel=vol; + + idxsize = (vnf->size)? ((SLONGLONG)vnf->size << FRACBITS)-1 : 0; + idxlend = (vnf->repend)? ((SLONGLONG)vnf->repend << FRACBITS)-1 : 0; + idxlpos = (SLONGLONG)vnf->reppos << FRACBITS; + AddChannel(vc_tickbuf, portion); + } + } + + if(md_reverb) { + if(md_reverb>15) md_reverb=15; + MixReverb(vc_tickbuf, portion); + } + + if(vc_mode & DMODE_FLOAT) + Mix32ToFP((float*) buffer, vc_tickbuf, count); + else if(vc_mode & DMODE_16BITS) + Mix32To16((SWORD*) buffer, vc_tickbuf, count); + else + Mix32To8((SBYTE*) buffer, vc_tickbuf, count); + + buffer += samples2bytes(portion); + left -= portion; + } + } +} + +BOOL VC1_Init(void) +{ + VC_SetupPointers(); + + if (md_mode&DMODE_HQMIXER) + return VC2_Init(); + + if(!(Samples=(SWORD**)_mm_calloc(MAXSAMPLEHANDLES,sizeof(SWORD*)))) { + _mm_errno = MMERR_INITIALIZING_MIXER; + return 1; + } + if(!vc_tickbuf) + if(!(vc_tickbuf=(SLONG*)_mm_malloc((TICKLSIZE+32)*sizeof(SLONG)))) { + _mm_errno = MMERR_INITIALIZING_MIXER; + return 1; + } + + MixReverb=(md_mode&DMODE_STEREO)?MixReverb_Stereo:MixReverb_Normal; + vc_mode = md_mode; + return 0; +} + +BOOL VC1_PlayStart(void) +{ + samplesthatfit=TICKLSIZE; + if(vc_mode & DMODE_STEREO) samplesthatfit >>= 1; + tickleft = 0; + + RVc1 = (5000L * md_mixfreq) / REVERBERATION; + RVc2 = (5078L * md_mixfreq) / REVERBERATION; + RVc3 = (5313L * md_mixfreq) / REVERBERATION; + RVc4 = (5703L * md_mixfreq) / REVERBERATION; + RVc5 = (6250L * md_mixfreq) / REVERBERATION; + RVc6 = (6953L * md_mixfreq) / REVERBERATION; + RVc7 = (7813L * md_mixfreq) / REVERBERATION; + RVc8 = (8828L * md_mixfreq) / REVERBERATION; + + if(!(RVbufL1=(SLONG*)_mm_calloc((RVc1+1),sizeof(SLONG)))) return 1; + if(!(RVbufL2=(SLONG*)_mm_calloc((RVc2+1),sizeof(SLONG)))) return 1; + if(!(RVbufL3=(SLONG*)_mm_calloc((RVc3+1),sizeof(SLONG)))) return 1; + if(!(RVbufL4=(SLONG*)_mm_calloc((RVc4+1),sizeof(SLONG)))) return 1; + if(!(RVbufL5=(SLONG*)_mm_calloc((RVc5+1),sizeof(SLONG)))) return 1; + if(!(RVbufL6=(SLONG*)_mm_calloc((RVc6+1),sizeof(SLONG)))) return 1; + if(!(RVbufL7=(SLONG*)_mm_calloc((RVc7+1),sizeof(SLONG)))) return 1; + if(!(RVbufL8=(SLONG*)_mm_calloc((RVc8+1),sizeof(SLONG)))) return 1; + + if(!(RVbufR1=(SLONG*)_mm_calloc((RVc1+1),sizeof(SLONG)))) return 1; + if(!(RVbufR2=(SLONG*)_mm_calloc((RVc2+1),sizeof(SLONG)))) return 1; + if(!(RVbufR3=(SLONG*)_mm_calloc((RVc3+1),sizeof(SLONG)))) return 1; + if(!(RVbufR4=(SLONG*)_mm_calloc((RVc4+1),sizeof(SLONG)))) return 1; + if(!(RVbufR5=(SLONG*)_mm_calloc((RVc5+1),sizeof(SLONG)))) return 1; + if(!(RVbufR6=(SLONG*)_mm_calloc((RVc6+1),sizeof(SLONG)))) return 1; + if(!(RVbufR7=(SLONG*)_mm_calloc((RVc7+1),sizeof(SLONG)))) return 1; + if(!(RVbufR8=(SLONG*)_mm_calloc((RVc8+1),sizeof(SLONG)))) return 1; + + RVRindex = 0; + return 0; +} + +void VC1_PlayStop(void) +{ + if(RVbufL1) free(RVbufL1); + if(RVbufL2) free(RVbufL2); + if(RVbufL3) free(RVbufL3); + if(RVbufL4) free(RVbufL4); + if(RVbufL5) free(RVbufL5); + if(RVbufL6) free(RVbufL6); + if(RVbufL7) free(RVbufL7); + if(RVbufL8) free(RVbufL8); + RVbufL1=RVbufL2=RVbufL3=RVbufL4=RVbufL5=RVbufL6=RVbufL7=RVbufL8=NULL; + if(RVbufR1) free(RVbufR1); + if(RVbufR2) free(RVbufR2); + if(RVbufR3) free(RVbufR3); + if(RVbufR4) free(RVbufR4); + if(RVbufR5) free(RVbufR5); + if(RVbufR6) free(RVbufR6); + if(RVbufR7) free(RVbufR7); + if(RVbufR8) free(RVbufR8); + RVbufR1=RVbufR2=RVbufR3=RVbufR4=RVbufR5=RVbufR6=RVbufR7=RVbufR8=NULL; +} + +BOOL VC1_SetNumVoices(void) +{ + int t; + + if(!(vc_softchn=md_softchn)) return 0; + + if(vinf) free(vinf); + if(!(vinf= _mm_calloc(sizeof(VINFO),vc_softchn))) return 1; + + for(t=0;t +#endif +#include + +#include "mikmod_internals.h" + +/* + Constant Definitions + ==================== + + MAXVOL_FACTOR (was BITSHIFT in virtch.c) + Controls the maximum volume of the output data. All mixed data is + divided by this number after mixing, so larger numbers result in + quieter mixing. Smaller numbers will increase the likeliness of + distortion on loud modules. + + REVERBERATION + Larger numbers result in shorter reverb duration. Longer reverb + durations can cause unwanted static and make the reverb sound more + like a crappy echo. + + SAMPLING_SHIFT + Specified the shift multiplier which controls by how much the mixing + rate is multiplied while mixing. Higher values can improve quality by + smoothing the sound and reducing pops and clicks. Note, this is a shift + value, so a value of 2 becomes a mixing-rate multiplier of 4, and a + value of 3 = 8, etc. + + FRACBITS + The number of bits per integer devoted to the fractional part of the + number. Generally, this number should not be changed for any reason. + + !!! IMPORTANT !!! All values below MUST ALWAYS be greater than 0 + +*/ + +#define MAXVOL_FACTOR (1<<9) +#define REVERBERATION 11000L + +#define SAMPLING_SHIFT 2 +#define SAMPLING_FACTOR (1UL< sample has to be restarted */ + UBYTE active; /* =1 -> sample is playing */ + UWORD flags; /* 16/8 bits looping/one-shot */ + SWORD handle; /* identifies the sample */ + ULONG start; /* start index */ + ULONG size; /* samplesize */ + ULONG reppos; /* loop start */ + ULONG repend; /* loop end */ + ULONG frq; /* current frequency */ + int vol; /* current volume */ + int pan; /* current panning position */ + + int click; + int rampvol; + SLONG lastvalL,lastvalR; + int lvolsel,rvolsel; /* Volume factor in range 0-255 */ + int oldlvol,oldrvol; + + SLONGLONG current; /* current index in the sample */ + SLONGLONG increment; /* increment value */ +} VINFO; + +static SWORD **Samples; +static VINFO *vinf=NULL,*vnf; +static long tickleft,samplesthatfit,vc_memory=0; +static int vc_softchn; +static SLONGLONG idxsize,idxlpos,idxlend; +static SLONG *vc_tickbuf=NULL; +static UWORD vc_mode; + +/* Reverb control variables */ + +static int RVc1, RVc2, RVc3, RVc4, RVc5, RVc6, RVc7, RVc8; +static ULONG RVRindex; + +/* For Mono or Left Channel */ +static SLONG *RVbufL1=NULL,*RVbufL2=NULL,*RVbufL3=NULL,*RVbufL4=NULL, + *RVbufL5=NULL,*RVbufL6=NULL,*RVbufL7=NULL,*RVbufL8=NULL; + +/* For Stereo only (Right Channel) */ +static SLONG *RVbufR1=NULL,*RVbufR2=NULL,*RVbufR3=NULL,*RVbufR4=NULL, + *RVbufR5=NULL,*RVbufR6=NULL,*RVbufR7=NULL,*RVbufR8=NULL; + +#ifdef NATIVE_64BIT_INT +#define NATIVE SLONGLONG +#else +#define NATIVE SLONG +#endif + +/*========== 32 bit sample mixers - only for 32 bit platforms */ +#ifndef NATIVE_64BIT_INT + +static SLONG Mix32MonoNormal(SWORD* srce,SLONG* dest,SLONG index,SLONG increment,SLONG todo) +{ + SWORD sample=0; + SLONG i,f; + + while(todo--) { + i=index>>FRACBITS,f=index&FRACMASK; + sample=(((SLONG)(srce[i]*(FRACMASK+1L-f)) + + ((SLONG)srce[i+1]*f)) >> FRACBITS); + index+=increment; + + if(vnf->rampvol) { + *dest++ += (long)( + ( ( (SLONG)(vnf->oldlvol*vnf->rampvol) + + (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) ) * + (SLONG)sample ) >> CLICK_SHIFT ); + vnf->rampvol--; + } else + if(vnf->click) { + *dest++ += (long)( + ( ( ((SLONG)vnf->lvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONG)sample ) + + (vnf->lastvalL*vnf->click) ) >> CLICK_SHIFT ); + vnf->click--; + } else + *dest++ +=vnf->lvolsel*sample; + } + vnf->lastvalL=vnf->lvolsel * sample; + + return index; +} + +static SLONG Mix32StereoNormal(SWORD* srce,SLONG* dest,SLONG index,SLONG increment,ULONG todo) +{ + SWORD sample=0; + SLONG i,f; + + while(todo--) { + i=index>>FRACBITS,f=index&FRACMASK; + sample=((((SLONG)srce[i]*(FRACMASK+1L-f)) + + ((SLONG)srce[i+1] * f)) >> FRACBITS); + index += increment; + + if(vnf->rampvol) { + *dest++ += (long)( + ( ( ((SLONG)vnf->oldlvol*vnf->rampvol) + + (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) + ) * (SLONG)sample ) >> CLICK_SHIFT ); + *dest++ += (long)( + ( ( ((SLONG)vnf->oldrvol*vnf->rampvol) + + (vnf->rvolsel*(CLICK_BUFFER-vnf->rampvol)) + ) * (SLONG)sample ) >> CLICK_SHIFT ); + vnf->rampvol--; + } else + if(vnf->click) { + *dest++ += (long)( + ( ( (SLONG)(vnf->lvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONG)sample ) + (vnf->lastvalL * vnf->click) ) + >> CLICK_SHIFT ); + *dest++ += (long)( + ( ( ((SLONG)vnf->rvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONG)sample ) + (vnf->lastvalR * vnf->click) ) + >> CLICK_SHIFT ); + vnf->click--; + } else { + *dest++ +=vnf->lvolsel*sample; + *dest++ +=vnf->rvolsel*sample; + } + } + vnf->lastvalL=vnf->lvolsel*sample; + vnf->lastvalR=vnf->rvolsel*sample; + + return index; +} + +static SLONG Mix32StereoSurround(SWORD* srce,SLONG* dest,SLONG index,SLONG increment,ULONG todo) +{ + SWORD sample=0; + long whoop; + SLONG i, f; + + while(todo--) { + i=index>>FRACBITS,f=index&FRACMASK; + sample=((((SLONG)srce[i]*(FRACMASK+1L-f)) + + ((SLONG)srce[i+1]*f)) >> FRACBITS); + index+=increment; + + if(vnf->rampvol) { + whoop=(long)( + ( ( (SLONG)(vnf->oldlvol*vnf->rampvol) + + (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) ) * + (SLONG)sample) >> CLICK_SHIFT ); + *dest++ +=whoop; + *dest++ -=whoop; + vnf->rampvol--; + } else + if(vnf->click) { + whoop = (long)( + ( ( ((SLONG)vnf->lvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONG)sample) + + (vnf->lastvalL * vnf->click) ) >> CLICK_SHIFT ); + *dest++ +=whoop; + *dest++ -=whoop; + vnf->click--; + } else { + *dest++ +=vnf->lvolsel*sample; + *dest++ -=vnf->lvolsel*sample; + } + } + vnf->lastvalL=vnf->lvolsel*sample; + vnf->lastvalR=vnf->lvolsel*sample; + + return index; +} +#endif + +/*========== 64 bit mixers */ + +static SLONGLONG MixMonoNormal(SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,SLONG todo) +{ + SWORD sample=0; + SLONGLONG i,f; + + while(todo--) { + i=index>>FRACBITS,f=index&FRACMASK; + sample=(((SLONGLONG)(srce[i]*(FRACMASK+1L-f)) + + ((SLONGLONG)srce[i+1]*f)) >> FRACBITS); + index+=increment; + + if(vnf->rampvol) { + *dest++ += (long)( + ( ( (SLONGLONG)(vnf->oldlvol*vnf->rampvol) + + (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) ) * + (SLONGLONG)sample ) >> CLICK_SHIFT ); + vnf->rampvol--; + } else + if(vnf->click) { + *dest++ += (long)( + ( ( ((SLONGLONG)vnf->lvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONGLONG)sample ) + + (vnf->lastvalL*vnf->click) ) >> CLICK_SHIFT ); + vnf->click--; + } else + *dest++ +=vnf->lvolsel*sample; + } + vnf->lastvalL=vnf->lvolsel * sample; + + return index; +} + +static SLONGLONG MixStereoNormal(SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,ULONG todo) +{ + SWORD sample=0; + SLONGLONG i,f; + + while(todo--) { + i=index>>FRACBITS,f=index&FRACMASK; + sample=((((SLONGLONG)srce[i]*(FRACMASK+1L-f)) + + ((SLONGLONG)srce[i+1] * f)) >> FRACBITS); + index += increment; + + if(vnf->rampvol) { + *dest++ += (long)( + ( ( ((SLONGLONG)vnf->oldlvol*vnf->rampvol) + + (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) + ) * (SLONGLONG)sample ) >> CLICK_SHIFT ); + *dest++ += (long)( + ( ( ((SLONGLONG)vnf->oldrvol*vnf->rampvol) + + (vnf->rvolsel*(CLICK_BUFFER-vnf->rampvol)) + ) * (SLONGLONG)sample ) >> CLICK_SHIFT ); + vnf->rampvol--; + } else + if(vnf->click) { + *dest++ += (long)( + ( ( (SLONGLONG)(vnf->lvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONGLONG)sample ) + (vnf->lastvalL * vnf->click) ) + >> CLICK_SHIFT ); + *dest++ += (long)( + ( ( ((SLONGLONG)vnf->rvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONGLONG)sample ) + (vnf->lastvalR * vnf->click) ) + >> CLICK_SHIFT ); + vnf->click--; + } else { + *dest++ +=vnf->lvolsel*sample; + *dest++ +=vnf->rvolsel*sample; + } + } + vnf->lastvalL=vnf->lvolsel*sample; + vnf->lastvalR=vnf->rvolsel*sample; + + return index; +} + +static SLONGLONG MixStereoSurround(SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,ULONG todo) +{ + SWORD sample=0; + long whoop; + SLONGLONG i, f; + + while(todo--) { + i=index>>FRACBITS,f=index&FRACMASK; + sample=((((SLONGLONG)srce[i]*(FRACMASK+1L-f)) + + ((SLONGLONG)srce[i+1]*f)) >> FRACBITS); + index+=increment; + + if(vnf->rampvol) { + whoop=(long)( + ( ( (SLONGLONG)(vnf->oldlvol*vnf->rampvol) + + (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) ) * + (SLONGLONG)sample) >> CLICK_SHIFT ); + *dest++ +=whoop; + *dest++ -=whoop; + vnf->rampvol--; + } else + if(vnf->click) { + whoop = (long)( + ( ( ((SLONGLONG)vnf->lvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONGLONG)sample) + + (vnf->lastvalL * vnf->click) ) >> CLICK_SHIFT ); + *dest++ +=whoop; + *dest++ -=whoop; + vnf->click--; + } else { + *dest++ +=vnf->lvolsel*sample; + *dest++ -=vnf->lvolsel*sample; + } + } + vnf->lastvalL=vnf->lvolsel*sample; + vnf->lastvalR=vnf->lvolsel*sample; + + return index; +} + +static void(*Mix32toFP)(float* dste,SLONG* srce,NATIVE count); +static void(*Mix32to16)(SWORD* dste,SLONG* srce,NATIVE count); +static void(*Mix32to8)(SBYTE* dste,SLONG* srce,NATIVE count); +static void(*MixReverb)(SLONG* srce,NATIVE count); + +/* Reverb macros */ +#define COMPUTE_LOC(n) loc##n = RVRindex % RVc##n +#define COMPUTE_LECHO(n) RVbufL##n [loc##n ]=speedup+((ReverbPct*RVbufL##n [loc##n ])>>7) +#define COMPUTE_RECHO(n) RVbufR##n [loc##n ]=speedup+((ReverbPct*RVbufR##n [loc##n ])>>7) + +static void MixReverb_Normal(SLONG* srce,NATIVE count) +{ + NATIVE speedup; + int ReverbPct; + unsigned int loc1,loc2,loc3,loc4,loc5,loc6,loc7,loc8; + + ReverbPct=58+(md_reverb*4); + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + while(count--) { + /* Compute the left channel echo buffers */ + speedup = *srce >> 3; + + COMPUTE_LECHO(1); COMPUTE_LECHO(2); COMPUTE_LECHO(3); COMPUTE_LECHO(4); + COMPUTE_LECHO(5); COMPUTE_LECHO(6); COMPUTE_LECHO(7); COMPUTE_LECHO(8); + + /* Prepare to compute actual finalized data */ + RVRindex++; + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + /* left channel */ + *srce++ +=RVbufL1[loc1]-RVbufL2[loc2]+RVbufL3[loc3]-RVbufL4[loc4]+ + RVbufL5[loc5]-RVbufL6[loc6]+RVbufL7[loc7]-RVbufL8[loc8]; + } +} + +static void MixReverb_Stereo(SLONG *srce,NATIVE count) +{ + NATIVE speedup; + int ReverbPct; + unsigned int loc1,loc2,loc3,loc4,loc5,loc6,loc7,loc8; + + ReverbPct=58+(md_reverb*4); + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + while(count--) { + /* Compute the left channel echo buffers */ + speedup = *srce >> 3; + + COMPUTE_LECHO(1); COMPUTE_LECHO(2); COMPUTE_LECHO(3); COMPUTE_LECHO(4); + COMPUTE_LECHO(5); COMPUTE_LECHO(6); COMPUTE_LECHO(7); COMPUTE_LECHO(8); + + /* Compute the right channel echo buffers */ + speedup = srce[1] >> 3; + + COMPUTE_RECHO(1); COMPUTE_RECHO(2); COMPUTE_RECHO(3); COMPUTE_RECHO(4); + COMPUTE_RECHO(5); COMPUTE_RECHO(6); COMPUTE_RECHO(7); COMPUTE_RECHO(8); + + /* Prepare to compute actual finalized data */ + RVRindex++; + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + /* left channel */ + *srce++ +=RVbufL1[loc1]-RVbufL2[loc2]+RVbufL3[loc3]-RVbufL4[loc4]+ + RVbufL5[loc5]-RVbufL6[loc6]+RVbufL7[loc7]-RVbufL8[loc8]; + + /* right channel */ + *srce++ +=RVbufR1[loc1]-RVbufR2[loc2]+RVbufR3[loc3]-RVbufR4[loc4]+ + RVbufR5[loc5]-RVbufR6[loc6]+RVbufR7[loc7]-RVbufR8[loc8]; + } +} + +/* Mixing macros */ +#define EXTRACT_SAMPLE_FP(var,attenuation) var=*srce++*((1.0f / 32768.0f) / (MAXVOL_FACTOR*attenuation)) +#define CHECK_SAMPLE_FP(var,bound) var=(var>bound)?bound:(var<-bound)?-bound:var + +static void Mix32ToFP_Normal(float* dste,SLONG* srce,NATIVE count) +{ + float x1,x2,tmpx; + int i; + + for(count/=SAMPLING_FACTOR;count;count--) { + tmpx=0.0f; + + for(i=SAMPLING_FACTOR/2;i;i--) { + EXTRACT_SAMPLE_FP(x1,1.0f); EXTRACT_SAMPLE_FP(x2,1.0f); + + CHECK_SAMPLE_FP(x1,1.0f); CHECK_SAMPLE_FP(x2,1.0f); + + tmpx+=x1+x2; + } + *dste++ =tmpx*(1.0f/SAMPLING_FACTOR); + } +} + +static void Mix32ToFP_Stereo(float* dste,SLONG* srce,NATIVE count) +{ + float x1,x2,x3,x4,tmpx,tmpy; + int i; + + for(count/=SAMPLING_FACTOR;count;count--) { + tmpx=tmpy=0.0f; + + for(i=SAMPLING_FACTOR/2;i;i--) { + EXTRACT_SAMPLE_FP(x1,1.0f); EXTRACT_SAMPLE_FP(x2,1.0f); + EXTRACT_SAMPLE_FP(x3,1.0f); EXTRACT_SAMPLE_FP(x4,1.0f); + + CHECK_SAMPLE_FP(x1,1.0f); CHECK_SAMPLE_FP(x2,1.0f); + CHECK_SAMPLE_FP(x3,1.0f); CHECK_SAMPLE_FP(x4,1.0f); + + tmpx+=x1+x3; + tmpy+=x2+x4; + } + *dste++ =tmpx*(1.0f/SAMPLING_FACTOR); + *dste++ =tmpy*(1.0f/SAMPLING_FACTOR); + } +} + +/* Mixing macros */ +#define EXTRACT_SAMPLE(var,attenuation) var=*srce++/(MAXVOL_FACTOR*attenuation) +#define CHECK_SAMPLE(var,bound) var=(var>=bound)?bound-1:(var<-bound)?-bound:var + +static void Mix32To16_Normal(SWORD* dste,SLONG* srce,NATIVE count) +{ + NATIVE x1,x2,tmpx; + int i; + + for(count/=SAMPLING_FACTOR;count;count--) { + tmpx=0; + + for(i=SAMPLING_FACTOR/2;i;i--) { + EXTRACT_SAMPLE(x1,1); EXTRACT_SAMPLE(x2,1); + + CHECK_SAMPLE(x1,32768); CHECK_SAMPLE(x2,32768); + + tmpx+=x1+x2; + } + *dste++ =tmpx/SAMPLING_FACTOR; + } +} + +static void Mix32To16_Stereo(SWORD* dste,SLONG* srce,NATIVE count) +{ + NATIVE x1,x2,x3,x4,tmpx,tmpy; + int i; + + for(count/=SAMPLING_FACTOR;count;count--) { + tmpx=tmpy=0; + + for(i=SAMPLING_FACTOR/2;i;i--) { + EXTRACT_SAMPLE(x1,1); EXTRACT_SAMPLE(x2,1); + EXTRACT_SAMPLE(x3,1); EXTRACT_SAMPLE(x4,1); + + CHECK_SAMPLE(x1,32768); CHECK_SAMPLE(x2,32768); + CHECK_SAMPLE(x3,32768); CHECK_SAMPLE(x4,32768); + + tmpx+=x1+x3; + tmpy+=x2+x4; + } + *dste++ =tmpx/SAMPLING_FACTOR; + *dste++ =tmpy/SAMPLING_FACTOR; + } +} + +static void Mix32To8_Normal(SBYTE* dste,SLONG* srce,NATIVE count) +{ + NATIVE x1,x2,tmpx; + int i; + + for(count/=SAMPLING_FACTOR;count;count--) { + tmpx = 0; + + for(i=SAMPLING_FACTOR/2;i;i--) { + EXTRACT_SAMPLE(x1,256); EXTRACT_SAMPLE(x2,256); + + CHECK_SAMPLE(x1,128); CHECK_SAMPLE(x2,128); + + tmpx+=x1+x2; + } + *dste++ =(tmpx/SAMPLING_FACTOR)+128; + } +} + +static void Mix32To8_Stereo(SBYTE* dste,SLONG* srce,NATIVE count) +{ + NATIVE x1,x2,x3,x4,tmpx,tmpy; + int i; + + for(count/=SAMPLING_FACTOR;count;count--) { + tmpx=tmpy=0; + + for(i=SAMPLING_FACTOR/2;i;i--) { + EXTRACT_SAMPLE(x1,256); EXTRACT_SAMPLE(x2,256); + EXTRACT_SAMPLE(x3,256); EXTRACT_SAMPLE(x4,256); + + CHECK_SAMPLE(x1,128); CHECK_SAMPLE(x2,128); + CHECK_SAMPLE(x3,128); CHECK_SAMPLE(x4,128); + + tmpx+=x1+x3; + tmpy+=x2+x4; + } + *dste++ =(tmpx/SAMPLING_FACTOR)+128; + *dste++ =(tmpy/SAMPLING_FACTOR)+128; + } +} + +static void AddChannel(SLONG* ptr,NATIVE todo) +{ + SLONGLONG end,done; + SWORD *s; + + if(!(s=Samples[vnf->handle])) { + vnf->current = vnf->active = 0; + vnf->lastvalL = vnf->lastvalR = 0; + return; + } + + /* update the 'current' index so the sample loops, or stops playing if it + reached the end of the sample */ + while(todo>0) { + SLONGLONG endpos; + + if(vnf->flags & SF_REVERSE) { + /* The sample is playing in reverse */ + if((vnf->flags&SF_LOOP)&&(vnf->currentflags & SF_BIDI) { + /* sample is doing bidirectional loops, so 'bounce' the + current index against the idxlpos */ + vnf->current = idxlpos+(idxlpos-vnf->current); + vnf->flags &= ~SF_REVERSE; + vnf->increment = -vnf->increment; + } else + /* normal backwards looping, so set the current position to + loopend index */ + vnf->current=idxlend-(idxlpos-vnf->current); + } else { + /* the sample is not looping, so check if it reached index 0 */ + if(vnf->current < 0) { + /* playing index reached 0, so stop playing this sample */ + vnf->current = vnf->active = 0; + break; + } + } + } else { + /* The sample is playing forward */ + if((vnf->flags & SF_LOOP) && + (vnf->current >= idxlend)) { + /* the sample is looping, check the loopend index */ + if(vnf->flags & SF_BIDI) { + /* sample is doing bidirectional loops, so 'bounce' the + current index against the idxlend */ + vnf->flags |= SF_REVERSE; + vnf->increment = -vnf->increment; + vnf->current = idxlend-(vnf->current-idxlend); + } else + /* normal backwards looping, so set the current position + to loopend index */ + vnf->current=idxlpos+(vnf->current-idxlend); + } else { + /* sample is not looping, so check if it reached the last + position */ + if(vnf->current >= idxsize) { + /* yes, so stop playing this sample */ + vnf->current = vnf->active = 0; + break; + } + } + } + + end=(vnf->flags&SF_REVERSE)?(vnf->flags&SF_LOOP)?idxlpos:0: + (vnf->flags&SF_LOOP)?idxlend:idxsize; + + /* if the sample is not blocked... */ + if((end==vnf->current)||(!vnf->increment)) + done=0; + else { + done=MIN((end-vnf->current)/vnf->increment+1,todo); + if(done<0) done=0; + } + + if(!done) { + vnf->active = 0; + break; + } + + endpos=vnf->current+done*vnf->increment; + + if(vnf->vol || vnf->rampvol) { +#ifndef NATIVE_64BIT_INT + /* use the 32 bit mixers as often as we can (they're much faster) */ + if((vnf->current<0x7fffffff)&&(endpos<0x7fffffff)) { + if(vc_mode & DMODE_STEREO) { + if((vnf->pan==PAN_SURROUND)&&(vc_mode&DMODE_SURROUND)) + vnf->current=Mix32StereoSurround + (s,ptr,vnf->current,vnf->increment,done); + else + vnf->current=Mix32StereoNormal + (s,ptr,vnf->current,vnf->increment,done); + } else + vnf->current=Mix32MonoNormal + (s,ptr,vnf->current,vnf->increment,done); + } else +#endif + { + if(vc_mode & DMODE_STEREO) { + if((vnf->pan==PAN_SURROUND)&&(vc_mode&DMODE_SURROUND)) + vnf->current=MixStereoSurround + (s,ptr,vnf->current,vnf->increment,done); + else + vnf->current=MixStereoNormal + (s,ptr,vnf->current,vnf->increment,done); + } else + vnf->current=MixMonoNormal + (s,ptr,vnf->current,vnf->increment,done); + } + } else { + vnf->lastvalL = vnf->lastvalR = 0; + /* update sample position */ + vnf->current=endpos; + } + + todo -= done; + ptr +=(vc_mode & DMODE_STEREO)?(done<<1):done; + } +} + +#define _IN_VIRTCH_ + +#define VC1_SilenceBytes VC2_SilenceBytes +#define VC1_WriteSamples VC2_WriteSamples +#define VC1_WriteBytes VC2_WriteBytes +#define VC1_Exit VC2_Exit +#define VC1_VoiceSetVolume VC2_VoiceSetVolume +#define VC1_VoiceGetVolume VC2_VoiceGetVolume +#define VC1_VoiceSetPanning VC2_VoiceSetPanning +#define VC1_VoiceGetPanning VC2_VoiceGetPanning +#define VC1_VoiceSetFrequency VC2_VoiceSetFrequency +#define VC1_VoiceGetFrequency VC2_VoiceGetFrequency +#define VC1_VoicePlay VC2_VoicePlay +#define VC1_VoiceStop VC2_VoiceStop +#define VC1_VoiceStopped VC2_VoiceStopped +#define VC1_VoiceGetPosition VC2_VoiceGetPosition +#define VC1_SampleUnload VC2_SampleUnload +#define VC1_SampleLoad VC2_SampleLoad +#define VC1_SampleSpace VC2_SampleSpace +#define VC1_SampleLength VC2_SampleLength +#define VC1_VoiceRealVolume VC2_VoiceRealVolume + +#include "virtch_common.c" +#undef _IN_VIRTCH_ + +void VC2_WriteSamples(SBYTE* buf,ULONG todo) +{ + int left,portion=0; + SBYTE *buffer; + int t,pan,vol; + + todo*=SAMPLING_FACTOR; + + while(todo) { + if(!tickleft) { + if(vc_mode & DMODE_SOFT_MUSIC) md_player(); + tickleft=(md_mixfreq*125L*SAMPLING_FACTOR)/(md_bpm*50L); + tickleft&=~(SAMPLING_FACTOR-1); + } + left = MIN(tickleft, todo); + buffer = buf; + tickleft -= left; + todo -= left; + buf += samples2bytes(left)/SAMPLING_FACTOR; + + while(left) { + portion = MIN(left, samplesthatfit); + memset(vc_tickbuf,0,portion<<((vc_mode&DMODE_STEREO)?3:2)); + for(t=0;tkick) { + vnf->current=((SLONGLONG)(vnf->start))<kick = 0; + vnf->active = 1; + vnf->click = CLICK_BUFFER; + vnf->rampvol = 0; + } + + if(!vnf->frq) vnf->active = 0; + + if(vnf->active) { + vnf->increment=((SLONGLONG)(vnf->frq)<<(FRACBITS-SAMPLING_SHIFT)) + /md_mixfreq; + if(vnf->flags&SF_REVERSE) vnf->increment=-vnf->increment; + vol = vnf->vol; pan = vnf->pan; + + vnf->oldlvol=vnf->lvolsel;vnf->oldrvol=vnf->rvolsel; + if(vc_mode & DMODE_STEREO) { + if(pan!=PAN_SURROUND) { + vnf->lvolsel=(vol*(PAN_RIGHT-pan))>>8; + vnf->rvolsel=(vol*pan)>>8; + } else { + vnf->lvolsel=vnf->rvolsel=(vol * 256L) / 480; + } + } else + vnf->lvolsel=vol; + + idxsize=(vnf->size)?((SLONGLONG)(vnf->size)<repend)?((SLONGLONG)(vnf->repend)<reppos)<15) md_reverb=15; + MixReverb(vc_tickbuf,portion); + } + + if(vc_mode & DMODE_FLOAT) + Mix32toFP((float*)buffer,vc_tickbuf,portion); + else if(vc_mode & DMODE_16BITS) + Mix32to16((SWORD*)buffer,vc_tickbuf,portion); + else + Mix32to8((SBYTE*)buffer,vc_tickbuf,portion); + + buffer += samples2bytes(portion) / SAMPLING_FACTOR; + left -= portion; + } + } +} + +BOOL VC2_Init(void) +{ + VC_SetupPointers(); + + if (!(md_mode&DMODE_HQMIXER)) + return VC1_Init(); + + if(!(Samples=(SWORD**)_mm_calloc(MAXSAMPLEHANDLES,sizeof(SWORD*)))) { + _mm_errno = MMERR_INITIALIZING_MIXER; + return 1; + } + if(!vc_tickbuf) + if(!(vc_tickbuf=(SLONG*)_mm_malloc((TICKLSIZE+32)*sizeof(SLONG)))) { + _mm_errno = MMERR_INITIALIZING_MIXER; + return 1; + } + + if(md_mode & DMODE_STEREO) { + Mix32toFP = Mix32ToFP_Stereo; + Mix32to16 = Mix32To16_Stereo; + Mix32to8 = Mix32To8_Stereo; + MixReverb = MixReverb_Stereo; + } else { + Mix32toFP = Mix32ToFP_Normal; + Mix32to16 = Mix32To16_Normal; + Mix32to8 = Mix32To8_Normal; + MixReverb = MixReverb_Normal; + } + md_mode |= DMODE_INTERP; + vc_mode = md_mode; + return 0; +} + +BOOL VC2_PlayStart(void) +{ + md_mode|=DMODE_INTERP; + + samplesthatfit = TICKLSIZE; + if(vc_mode & DMODE_STEREO) samplesthatfit >>= 1; + tickleft = 0; + + RVc1 = (5000L * md_mixfreq) / (REVERBERATION * 10); + RVc2 = (5078L * md_mixfreq) / (REVERBERATION * 10); + RVc3 = (5313L * md_mixfreq) / (REVERBERATION * 10); + RVc4 = (5703L * md_mixfreq) / (REVERBERATION * 10); + RVc5 = (6250L * md_mixfreq) / (REVERBERATION * 10); + RVc6 = (6953L * md_mixfreq) / (REVERBERATION * 10); + RVc7 = (7813L * md_mixfreq) / (REVERBERATION * 10); + RVc8 = (8828L * md_mixfreq) / (REVERBERATION * 10); + + if(!(RVbufL1=(SLONG*)_mm_calloc((RVc1+1),sizeof(SLONG)))) return 1; + if(!(RVbufL2=(SLONG*)_mm_calloc((RVc2+1),sizeof(SLONG)))) return 1; + if(!(RVbufL3=(SLONG*)_mm_calloc((RVc3+1),sizeof(SLONG)))) return 1; + if(!(RVbufL4=(SLONG*)_mm_calloc((RVc4+1),sizeof(SLONG)))) return 1; + if(!(RVbufL5=(SLONG*)_mm_calloc((RVc5+1),sizeof(SLONG)))) return 1; + if(!(RVbufL6=(SLONG*)_mm_calloc((RVc6+1),sizeof(SLONG)))) return 1; + if(!(RVbufL7=(SLONG*)_mm_calloc((RVc7+1),sizeof(SLONG)))) return 1; + if(!(RVbufL8=(SLONG*)_mm_calloc((RVc8+1),sizeof(SLONG)))) return 1; + + if(!(RVbufR1=(SLONG*)_mm_calloc((RVc1+1),sizeof(SLONG)))) return 1; + if(!(RVbufR2=(SLONG*)_mm_calloc((RVc2+1),sizeof(SLONG)))) return 1; + if(!(RVbufR3=(SLONG*)_mm_calloc((RVc3+1),sizeof(SLONG)))) return 1; + if(!(RVbufR4=(SLONG*)_mm_calloc((RVc4+1),sizeof(SLONG)))) return 1; + if(!(RVbufR5=(SLONG*)_mm_calloc((RVc5+1),sizeof(SLONG)))) return 1; + if(!(RVbufR6=(SLONG*)_mm_calloc((RVc6+1),sizeof(SLONG)))) return 1; + if(!(RVbufR7=(SLONG*)_mm_calloc((RVc7+1),sizeof(SLONG)))) return 1; + if(!(RVbufR8=(SLONG*)_mm_calloc((RVc8+1),sizeof(SLONG)))) return 1; + + RVRindex = 0; + return 0; +} + +void VC2_PlayStop(void) +{ + if(RVbufL1) free(RVbufL1); + if(RVbufL2) free(RVbufL2); + if(RVbufL3) free(RVbufL3); + if(RVbufL4) free(RVbufL4); + if(RVbufL5) free(RVbufL5); + if(RVbufL6) free(RVbufL6); + if(RVbufL7) free(RVbufL7); + if(RVbufL8) free(RVbufL8); + if(RVbufR1) free(RVbufR1); + if(RVbufR2) free(RVbufR2); + if(RVbufR3) free(RVbufR3); + if(RVbufR4) free(RVbufR4); + if(RVbufR5) free(RVbufR5); + if(RVbufR6) free(RVbufR6); + if(RVbufR7) free(RVbufR7); + if(RVbufR8) free(RVbufR8); + + RVbufL1=RVbufL2=RVbufL3=RVbufL4=RVbufL5=RVbufL6=RVbufL7=RVbufL8=NULL; + RVbufR1=RVbufR2=RVbufR3=RVbufR4=RVbufR5=RVbufR6=RVbufR7=RVbufR8=NULL; +} + +BOOL VC2_SetNumVoices(void) +{ + int t; + + md_mode|=DMODE_INTERP; + + if(!(vc_softchn=md_softchn)) return 0; + + if(vinf) free(vinf); + if(!(vinf=_mm_calloc(sizeof(VINFO),vc_softchn))) return 1; + + for(t=0;t>= 2; + else if(vc_mode & DMODE_16BITS) bytes >>= 1; + if(vc_mode & DMODE_STEREO) bytes >>= 1; + return bytes; +} + +/* Fill the buffer with 'todo' bytes of silence (it depends on the mixing mode + how the buffer is filled) */ +ULONG VC1_SilenceBytes(SBYTE* buf,ULONG todo) +{ + todo=samples2bytes(bytes2samples(todo)); + + /* clear the buffer to zero (16 bits signed) or 0x80 (8 bits unsigned) */ + if(vc_mode & DMODE_FLOAT) + memset(buf,0,todo); + else if(vc_mode & DMODE_16BITS) + memset(buf,0,todo); + else + memset(buf,0x80,todo); + + return todo; +} + +void VC1_WriteSamples(SBYTE*,ULONG); + +/* Writes 'todo' mixed SBYTES (!!) to 'buf'. It returns the number of SBYTES + actually written to 'buf' (which is rounded to number of samples that fit + into 'todo' bytes). */ +ULONG VC1_WriteBytes(SBYTE* buf,ULONG todo) +{ + if(!vc_softchn) + return VC1_SilenceBytes(buf,todo); + + todo = bytes2samples(todo); + VC1_WriteSamples(buf,todo); + + return samples2bytes(todo); +} + +void VC1_Exit(void) +{ + if(vc_tickbuf) free(vc_tickbuf); + if(vinf) free(vinf); + if(Samples) free(Samples); + + vc_tickbuf = NULL; + vinf = NULL; + Samples = NULL; + + VC_SetupPointers(); +} + +UWORD VC1_VoiceGetVolume(UBYTE voice) +{ + return vinf[voice].vol; +} + +ULONG VC1_VoiceGetPanning(UBYTE voice) +{ + return vinf[voice].pan; +} + +void VC1_VoiceSetFrequency(UBYTE voice,ULONG frq) +{ + vinf[voice].frq=frq; +} + +ULONG VC1_VoiceGetFrequency(UBYTE voice) +{ + return vinf[voice].frq; +} + +void VC1_VoicePlay(UBYTE voice,SWORD handle,ULONG start,ULONG size,ULONG reppos,ULONG repend,UWORD flags) +{ + vinf[voice].flags = flags; + vinf[voice].handle = handle; + vinf[voice].start = start; + vinf[voice].size = size; + vinf[voice].reppos = reppos; + vinf[voice].repend = repend; + vinf[voice].kick = 1; +} + +void VC1_VoiceStop(UBYTE voice) +{ + vinf[voice].active = 0; +} + +BOOL VC1_VoiceStopped(UBYTE voice) +{ + return(vinf[voice].active==0); +} + +SLONG VC1_VoiceGetPosition(UBYTE voice) +{ + return(vinf[voice].current>>FRACBITS); +} + +void VC1_VoiceSetVolume(UBYTE voice,UWORD vol) +{ + /* protect against clicks if volume variation is too high */ + if(abs((int)vinf[voice].vol-(int)vol)>32) + vinf[voice].rampvol=CLICK_BUFFER; + vinf[voice].vol=vol; +} + +void VC1_VoiceSetPanning(UBYTE voice,ULONG pan) +{ + /* protect against clicks if panning variation is too high */ + if(abs((int)vinf[voice].pan-(int)pan)>48) + vinf[voice].rampvol=CLICK_BUFFER; + vinf[voice].pan=pan; +} + +/*========== External mixer interface */ + +void VC1_SampleUnload(SWORD handle) +{ + if (handlesample; + int handle; + ULONG t, length,loopstart,loopend; + + if(type==MD_HARDWARE) return -1; + + /* Find empty slot to put sample address in */ + for(handle=0;handleloopend > s->length) + s->loopend = s->length; + if (s->loopstart >= s->loopend) + s->flags &= ~SF_LOOP; + + length = s->length; + loopstart = s->loopstart; + loopend = s->loopend; + + SL_SampleSigned(sload); + SL_Sample8to16(sload); + + if(!(Samples[handle]=(SWORD*)_mm_malloc((length+20)<<1))) { + _mm_errno = MMERR_SAMPLE_TOO_BIG; + return -1; + } + + /* read sample into buffer */ + if (SL_Load(Samples[handle],sload,length)) + return -1; + + /* Unclick sample */ + if(s->flags & SF_LOOP) { + if(s->flags & SF_BIDI) + for(t=0;t<16;t++) + Samples[handle][loopend+t]=Samples[handle][(loopend-t)-1]; + else + for(t=0;t<16;t++) + Samples[handle][loopend+t]=Samples[handle][t+loopstart]; + } else + for(t=0;t<16;t++) + Samples[handle][t+length]=0; + + return handle; +} + +ULONG VC1_SampleSpace(int type) +{ + (void)type; + return vc_memory; +} + +ULONG VC1_SampleLength(int type,SAMPLE* s) +{ + (void)type; + if (!s) return 0; + + return (s->length*((s->flags&SF_16BITS)?2:1))+16; +} + +ULONG VC1_VoiceRealVolume(UBYTE voice) +{ + ULONG i,s,size; + int k,j; + SWORD *smp; + SLONG t; + + t = vinf[voice].current>>FRACBITS; + if(!vinf[voice].active) return 0; + + s = vinf[voice].handle; + size = vinf[voice].size; + + i=64; t-=64; k=0; j=0; + if(i>size) i = size; + if(t<0) t = 0; + if(t+i > size) t = size-i; + + i &= ~1; /* make sure it's EVEN. */ + + smp = &Samples[s][t]; + for(;i;i--,smp++) { + if(k<*smp) k = *smp; + if(j>*smp) j = *smp; + } + return abs(k-j); +} + +#endif + +#endif + +/* ex:set ts=4: */