# HG changeset patch # User darius # Date 893316013 0 # Node ID 437e8455d86273aed975ab7dbbbb19710a2add92 # Parent d5cb2cfc8ecab20c59b5e92ab1c1c68a9229c39c General tidy up.. Rename makefile.gcc to Makefile diff -r d5cb2cfc8eca -r 437e8455d862 playercode/drv_wav_v2.c --- a/playercode/drv_wav_v2.c Fri Jan 23 16:05:11 1998 +0000 +++ b/playercode/drv_wav_v2.c Thu Apr 23 07:20:13 1998 +0000 @@ -1,116 +1,116 @@ - -#include "mikmod.h" - -#ifdef __GNUC__ -#include -#else -#include -#endif -#include -#include - -#define WAVBUFFERSIZE 65536 - -static FILE *wavout; - -static SBYTE *WAV_DMABUF; -static ULONG dumpsize; - -static BOOL WAV_IsThere(void) -{ - return 1; -} - - -static BOOL WAV_Init(void) -{ - if(NULL == (wavout = fopen("music.wav", "wb"))) return 1; - if(NULL == (WAV_DMABUF = _mm_malloc(WAVBUFFERSIZE))) return 1; - - md_mode |= DMODE_SOFT_MUSIC | DMODE_SOFT_SNDFX; - - if(VC2_Init()) return 1; - - _mm_write_string("RIFF WAVEfmt ",wavout); - _mm_write_I_ULONG(16,wavout); // length of this RIFF block crap - - _mm_write_I_UWORD(1, wavout); // microsoft format type - _mm_write_I_UWORD((md_mode & DMODE_STEREO) ? 2 : 1, wavout); - _mm_write_I_ULONG(md_mixfreq, wavout); - _mm_write_I_ULONG(md_mixfreq * ((md_mode & DMODE_STEREO) ? 2 : 1) * - ((md_mode & DMODE_16BITS) ? 2 : 1), wavout); - - _mm_write_I_UWORD(((md_mode & DMODE_16BITS) ? 2 : 1) * - ((md_mode & DMODE_STEREO) ? 2 : 1), wavout); // block alignment (8/16 bit) - - _mm_write_I_UWORD((md_mode & DMODE_16BITS) ? 16 : 8,wavout); - - _mm_write_string("data",wavout); - - dumpsize = 0; - - return 0; -} - - -static void WAV_Exit(void) -{ - VC2_Exit(); - - // write in the actual sizes now - - if(wavout!=NULL) - { _mm_fseek(wavout,4,SEEK_SET); - _mm_write_I_ULONG(dumpsize + 32, wavout); - _mm_fseek(wavout,40,SEEK_SET); - _mm_write_I_ULONG(dumpsize, wavout); - - fclose(wavout); - - if(WAV_DMABUF != NULL) free(WAV_DMABUF); - } -} - - -static void WAV_Update(void) -{ - VC2_WriteBytes(WAV_DMABUF, WAVBUFFERSIZE); - fwrite(WAV_DMABUF, 1, WAVBUFFERSIZE, wavout); - dumpsize += WAVBUFFERSIZE; -} - - -static BOOL WAV_Reset(void) -{ - return 0; -} - - -MDRIVER drv_wav = -{ NULL, - "music.wav file", - "WAV [music.wav] file output driver v1.0", - 0,255, - WAV_IsThere, - VC2_SampleLoad, - VC2_SampleUnload, - VC2_SampleSpace, - VC2_SampleLength, - WAV_Init, - WAV_Exit, - WAV_Reset, - VC2_SetNumVoices, - VC2_PlayStart, - VC2_PlayStop, - WAV_Update, - VC2_VoiceSetVolume, - VC2_VoiceSetFrequency, - VC2_VoiceSetPanning, - VC2_VoicePlay, - VC2_VoiceStop, - VC2_VoiceStopped, - VC2_VoiceReleaseSustain, - VC2_VoiceGetPosition, - NULL -}; - + +#include "mikmod.h" + +#ifdef __GNUC__ +#include +#else +#include +#endif +#include +#include + +#define WAVBUFFERSIZE 65536 + +static FILE *wavout; + +static SBYTE *WAV_DMABUF; +static ULONG dumpsize; + +static BOOL WAV_IsThere(void) +{ + return 1; +} + + +static BOOL WAV_Init(void) +{ + if(NULL == (wavout = fopen("music.wav", "wb"))) return 1; + if(NULL == (WAV_DMABUF = _mm_malloc(WAVBUFFERSIZE))) return 1; + + md_mode |= DMODE_SOFT_MUSIC | DMODE_SOFT_SNDFX; + + if(VC2_Init()) return 1; + + _mm_write_string("RIFF WAVEfmt ",wavout); + _mm_write_I_ULONG(16,wavout); // length of this RIFF block crap + + _mm_write_I_UWORD(1, wavout); // microsoft format type + _mm_write_I_UWORD((md_mode & DMODE_STEREO) ? 2 : 1, wavout); + _mm_write_I_ULONG(md_mixfreq, wavout); + _mm_write_I_ULONG(md_mixfreq * ((md_mode & DMODE_STEREO) ? 2 : 1) * + ((md_mode & DMODE_16BITS) ? 2 : 1), wavout); + + _mm_write_I_UWORD(((md_mode & DMODE_16BITS) ? 2 : 1) * + ((md_mode & DMODE_STEREO) ? 2 : 1), wavout); // block alignment (8/16 bit) + + _mm_write_I_UWORD((md_mode & DMODE_16BITS) ? 16 : 8,wavout); + + _mm_write_string("data",wavout); + + dumpsize = 0; + + return 0; +} + + +static void WAV_Exit(void) +{ + VC2_Exit(); + + // write in the actual sizes now + + if(wavout!=NULL) + { _mm_fseek(wavout,4,SEEK_SET); + _mm_write_I_ULONG(dumpsize + 32, wavout); + _mm_fseek(wavout,40,SEEK_SET); + _mm_write_I_ULONG(dumpsize, wavout); + + fclose(wavout); + + if(WAV_DMABUF != NULL) free(WAV_DMABUF); + } +} + + +static void WAV_Update(void) +{ + VC2_WriteBytes(WAV_DMABUF, WAVBUFFERSIZE); + fwrite(WAV_DMABUF, 1, WAVBUFFERSIZE, wavout); + dumpsize += WAVBUFFERSIZE; +} + + +static BOOL WAV_Reset(void) +{ + return 0; +} + + +MDRIVER drv_wav = +{ NULL, + "music.wav file", + "WAV [music.wav] file output driver v1.0", + 0,255, + WAV_IsThere, + VC2_SampleLoad, + VC2_SampleUnload, + VC2_SampleSpace, + VC2_SampleLength, + WAV_Init, + WAV_Exit, + WAV_Reset, + VC2_SetNumVoices, + VC2_PlayStart, + VC2_PlayStop, + WAV_Update, + VC2_VoiceSetVolume, + VC2_VoiceSetFrequency, + VC2_VoiceSetPanning, + VC2_VoicePlay, + VC2_VoiceStop, + VC2_VoiceStopped, + VC2_VoiceReleaseSustain, + VC2_VoiceGetPosition, + NULL +}; + diff -r d5cb2cfc8eca -r 437e8455d862 playercode/makefile.gcc --- a/playercode/makefile.gcc Fri Jan 23 16:05:11 1998 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -# MikMod Library Makefile for DJGPP (*NOT* Linux) -# Divine Entertainment Game Programming Pack - - -#################### -### User Options ### -#################### - -# Use the C or C++ compiler -CC = gcc -Librarian = ar -Compiler_Options = -O2 -I. -I../include -g - -Lib_file = ../lib/mikmod.a -Lib_file2 = ..\lib\mikmod.a - -LoaderObjs = mloader.o mlreg.o npertab.o sloader.o load_uni.o \ - load_mod.o load_m15.o load_mtm.o load_s3m.o load_stm.o load_669.o \ - load_far.o load_dsm.o load_med.o load_xm.o load_ult.o load_it.o \ - s3m_it.o - -DriverObjs = mdriver.o mdreg.o drv_nos.o drv_raw.o dos_drv/mdma.o \ - dos_drv/mirq.o dos_drv/gus.o dos_drv/gusdrv1.o dos_drv/gusdrv2.o \ - dos_drv/sb.o dos_drv/sb16.o dos_drv/sbpro.o dos_drv/sndscape.o \ - dos_drv/pas16.o dos_drv/awe32.o dos_drv/wss.o - -DriverObjs2 = mdriver.o mdreg.o drv_nos.o drv_raw.o mdma.o \ - mirq.o gus.o gus_drv1.o gus_mix.o sb.o sb16.o sbpro.o sndscape.o \ - pas16.o awe32.o wss.o - -PlayerObjs = mplayer.o - -#################### -## Makefile rules ## -#################### - -all: $(Lib_file) - -$(Lib_file): stream.o virtch.o resample.o munitrk.o \ - $(LoaderObjs) $(DriverObjs) $(PlayerObjs) - del $(Lib_file2) - $(Librarian) r $(Lib_file) stream.o virtch.o resample.o munitrk.o \ - $(LoaderObjs) $(DriverObjs2) $(PlayerObjs) - -%.o: %.c - $(CC) -c $(Compiler_Options) $< - -# capital S is very important!!! -%.o: %.S - $(CC) -c $(Compiler_Options) $< - -clean: - del *.o - del dos_drv\*.o - del $(Lib_file2) diff -r d5cb2cfc8eca -r 437e8455d862 playercode/mdriver.c --- a/playercode/mdriver.c Fri Jan 23 16:05:11 1998 +0000 +++ b/playercode/mdriver.c Thu Apr 23 07:20:13 1998 +0000 @@ -1,556 +1,626 @@ -/* - -Name: MDRIVER.C - -Description: -These routines are used to access the available soundcard drivers. - -Portability: -All systems - all compilers - -*/ - -#include "mikmod.h" - -MDRIVER *firstdriver = NULL, *md_driver = &drv_nos; - -UWORD md_device = 0; -UWORD md_mixfreq = 44100; -UWORD md_mode = DMODE_STEREO | DMODE_16BITS | DMODE_SURROUND; -UWORD md_dmabufsize = 50; -UBYTE md_pansep = 128; // 128 == 100% (full left/right) - -UBYTE md_reverb = 6; // Reverb - -UBYTE md_volume = 96; // Global sound volume (0-128) -UBYTE md_musicvolume = 128; // volume of song -UBYTE md_sndfxvolume = 128; // volume of sound effects - -UBYTE md_bpm = 125; - - -// 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; - -// Backup variables. This way, the end programmer can fiddle with the -// main globals without mikmod blowing up. - -static UWORD idevice, imixfreq, imode, idmabufsize; - - -static void LimitHardVoices(int limit) - -// Limits the number of hardware voices to the specified amount. -// This function should only be used by the low-level drivers. - -{ - 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; -} - - -static void LimitSoftVoices(int limit) - -// Limits the number of hardware voices to the specified amount. -// This function should only be used by the low-level drivers. - -{ - 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 md_driver->FreeSampleSpace(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 md_driver->RealSampleLength(type, s); -} - - -UWORD MD_SetDMA(int secs) - -// Converts the given number of 1/10th seconds into the number of bytes of -// audio that a sample # 1/10th seconds long would require at the current md_* -// settings. - -{ - ULONG result; - - result = (md_mixfreq * ((md_mode & DMODE_STEREO) ? 2 : 1) * - ((md_mode & DMODE_16BITS) ? 2 : 1) * secs) * 10; - - if(result > 32000) result = 32000; - return(md_dmabufsize = (result & ~3)); // round it off to an 8 byte boundry -} - - -void MD_InfoDriver(void) -{ - int t; - MDRIVER *l; - - // list all registered devicedrivers: - for(t=1,l=firstdriver; l!=NULL; l=l->next, t++) - printf("%d. %s\n",t,l->Version); -} - - -void MD_RegisterDriver(MDRIVER *drv) -{ - MDRIVER *cruise = firstdriver; - - if(cruise!=NULL) - { while(cruise->next!=NULL) cruise = cruise->next; - cruise->next = drv; - } else - firstdriver = drv; -} - - -SWORD MD_SampleLoad(SAMPLOAD *s, int type, FILE *fp) -// type - sample type .. MD_MUSIC or MD_SNDFX -{ - 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 = md_driver->SampleLoad(s, type, fp); - SL_Exit(s); - - return result; -} - - -void MD_SampleUnLoad(SWORD handle) -{ - md_driver->SampleUnLoad(handle); -} - - -void MD_SetBPM(UBYTE bpm) -{ - md_bpm = bpm; -} - - -void MikMod_RegisterPlayer(void (*player)(void)) -{ - md_player = player; -} - - -void MikMod_Update(void) -{ - if(isplaying) md_driver->Update(); -} - - -void Voice_SetVolume(int voice, UWORD vol) -{ - ULONG tmp; - - if((voice<0) || (voice>=md_numchn)) return; - tmp = (ULONG)vol * (ULONG)md_volume * ((voice < md_sngchn) ? (ULONG)md_musicvolume : (ULONG)md_sndfxvolume); - md_driver->VoiceSetVolume(voice,tmp/16384UL); -} - - -void Voice_SetFrequency(int voice, ULONG frq) -{ - if((voice < 0) || (voice >= md_numchn)) return; - if(md_sample[voice]!=NULL && md_sample[voice]->divfactor!=0) frq/=md_sample[voice]->divfactor; - md_driver->VoiceSetFrequency(voice, frq); -} - - -void Voice_SetPanning(int voice, ULONG pan) -{ - if((voice < 0) || (voice >= md_numchn)) return; - if(pan!=PAN_SURROUND) - { if(md_mode & DMODE_REVERSE) pan = 255-pan; - pan = (((SWORD)(pan-128)*md_pansep) / 128)+128; - } - md_driver->VoiceSetPanning(voice, pan); -} - - -void Voice_Play(int voice, SAMPLE *s, ULONG start) -{ - ULONG repend; - - if((voice < 0) || (voice >= md_numchn) || (start >= s->length)) return; - - md_sample[voice] = s; - repend = s->loopend; - - if(s->flags&SF_LOOP) - if(repend > s->length) repend = s->length; // repend can't be bigger than size - - md_driver->VoicePlay(voice,s->handle,start,s->length,s->loopstart,repend,s->flags); -} - - -void Voice_Stop(int 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; - } - - md_driver->VoiceStop(voice); -} - - -BOOL Voice_Stopped(int voice) -{ - if((voice < 0) || (voice >= md_numchn)) return 0; - return(md_driver->VoiceStopped(voice)); -} - - -SLONG Voice_GetPosition(int voice) -{ - if((voice < 0) || (voice >= md_numchn)) return 0; - return(md_driver->VoiceGetPosition(voice)); -} - - -ULONG Voice_RealVolume(int voice) -{ - if((voice < 0) || (voice >= md_numchn)) return 0; - return(md_driver->VoiceRealVolume(voice)); -} - - -// ================================ -// Functions prefixed with MikMod -// ================================ - -BOOL MikMod_Init(void) -{ - UWORD t; - - _mm_critical = 1; - - // if md_device==0, try to find a device number - - if(md_device==0) - { for(t=1,md_driver=firstdriver; md_driver!=NULL; md_driver=md_driver->next, t++) - { if(md_driver->IsPresent()) break; - } - - if(md_driver==NULL) - { _mm_errno = MMERR_DETECTING_DEVICE; - if(_mm_errorhandler!=NULL) _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!=NULL) && (t!=md_device); md_driver=md_driver->next, t++); - - if(md_driver==NULL) - { _mm_errno = MMERR_INVALID_DEVICE; - if(_mm_errorhandler!=NULL) _mm_errorhandler(); - md_driver = &drv_nos; - return 1; - } - - if(!md_driver->IsPresent()) - { _mm_errno = MMERR_DETECTING_DEVICE; - if(_mm_errorhandler!=NULL) _mm_errorhandler(); - md_driver = &drv_nos; - return 1; - } - } - - if(md_driver->Init()) - { MikMod_Exit(); - if(_mm_errorhandler!=NULL) _mm_errorhandler(); - return 1; - } - - idevice = md_device; imode = md_mode; - imixfreq = md_mixfreq; idmabufsize = md_dmabufsize; - initialized = 1; - _mm_critical = 0; - - return 0; -} - - -void MikMod_Exit(void) -{ - MikMod_DisableOutput(); - md_driver->Exit(); - md_numchn = md_sfxchn = md_sngchn = 0; - md_driver = &drv_nos; - initialized = 0; -} - - -BOOL MikMod_Reset(void) - -// Reset the driver using the new global variable settings. -// If the driver has not been initialized, it will be now. - -{ - if(!initialized) return MikMod_Init(); - if((md_driver->Reset == NULL) || (md_device != idevice)) - { // md_driver->Reset was NULL, or md_device was changed, - // so do a full reset of the driver. - - if(isplaying) md_driver->PlayStop(); - md_driver->Exit(); - if(MikMod_Init()) - { MikMod_Exit(); - if(_mm_errorhandler!=NULL) _mm_errorhandler(); - return 1; - } - if(isplaying) md_driver->PlayStart(); - } else - { if(md_driver->Reset()) - { MikMod_Exit(); - if(_mm_errorhandler!=NULL) _mm_errorhandler(); - return 1; - } - } - - return 0; -} - - -BOOL MikMod_SetNumVoices(int music, int sfx) - -// If either parameter is -1, the current set value will be retained. - -{ - BOOL resume = 0; - int t, oldchn = 0; - - if((music==0) && (sfx==0)) return 0; - - _mm_critical = 1; - - if(isplaying) - { MikMod_DisableOutput(); - oldchn = md_numchn; - resume = 1; - } - - if(sfxinfo!=NULL) free(sfxinfo); - if(md_sample!=NULL) free(md_sample); - md_sample = NULL; - sfxinfo = NULL; - - /*md_softchn = md_hardchn = 0; - - if(md_mode & DMODE_SOFT_SNDFX) - md_softchn = sfx; else md_hardchn = sfx; - - if(md_mode & DMODE_SOFT_MUSIC) - md_softchn += music; else md_hardchn += music; - */ - - 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); - - if(md_driver->SetNumVoices()) - { MikMod_Exit(); - md_numchn = md_softchn = md_hardchn = md_sfxchn = md_sngchn = 0; - if(_mm_errorhandler!=NULL) _mm_errorhandler(); - 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; tPlayStart()) return 1; - isplaying = 1; - } - _mm_critical = 0; - return 0; -} - - -void MikMod_DisableOutput(void) -{ - // safety valve, prevents calling playStop when playstart - // hasn't been called: - - if(isplaying && md_driver!=NULL) - { isplaying = 0; - md_driver->PlayStop(); - } -} - - -BOOL MikMod_Active(void) -{ - return isplaying; -} - - -int MikMod_PlaySample(SAMPLE *s, ULONG start, UBYTE flags) - -// Plays a sound effects sample. Picks a voice from the number of voices -// allocated for use as sound effects (loops through voices, skipping all -// active criticals). -// -// Returns the voice that the sound is being played on. - -{ - int orig = sfxpool; // for cases where all channels are critical - int c; - - if(md_sfxchn==0) return -1; - if(s->volume > 64) s->volume = 64; - - // check the first location after sfxpool - do - { if(sfxinfo[sfxpool] & SFX_CRITICAL) - { if(md_driver->VoiceStopped(c=sfxpool+md_sngchn)) - { sfxinfo[sfxpool] = flags; - Voice_Play(c, s, start); - md_driver->VoiceSetVolume(c,s->volume<<2); - md_driver->VoiceSetPanning(c,s->panning); - md_driver->VoiceSetFrequency(c,s->speed); - sfxpool++; - if(sfxpool >= md_sfxchn) sfxpool = 0; - return c; - } - } else - { sfxinfo[sfxpool] = flags; - Voice_Play(c=sfxpool+md_sngchn, s, start); - md_driver->VoiceSetVolume(c,s->volume<<2); - md_driver->VoiceSetPanning(c,s->panning); - md_driver->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; -} - +/* + * Name: MDRIVER.C + * + * Description: These routines are used to access the available soundcard + * drivers. + * + * Portability: All systems - all compilers + * + */ + +#include "mikmod.h" + +MDRIVER *firstdriver = NULL, *md_driver = &drv_nos; + +UWORD md_device = 0; +UWORD md_mixfreq = 44100; +UWORD md_mode = DMODE_STEREO | DMODE_16BITS | DMODE_SURROUND; +UWORD md_dmabufsize = 50; +UBYTE md_pansep = 128; /* 128 == 100% (full left/right) */ + +UBYTE md_reverb = 6; /* Reverb */ + +UBYTE md_volume = 96; /* Global sound volume (0-128) */ +UBYTE md_musicvolume = 128; /* volume of song */ +UBYTE md_sndfxvolume = 128; /* volume of sound effects */ + +UBYTE md_bpm = 125; + + +/* 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; + +/* + * Backup variables. This way, the end programmer can fiddle with the main + * globals without mikmod blowing up. + */ + +static UWORD idevice, imixfreq, imode, idmabufsize; + +/* + * 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 md_driver->FreeSampleSpace(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 md_driver->RealSampleLength(type, s); +} + + +/* + * Converts the given number of 1/10th seconds into the number of bytes of + * audio that a sample # 1/10th seconds long would require at the current md_* + * settings. + */ +UWORD +MD_SetDMA(int secs) +{ + ULONG result; + + result = (md_mixfreq * ((md_mode & DMODE_STEREO) ? 2 : 1) * + ((md_mode & DMODE_16BITS) ? 2 : 1) * secs) * 10; + + if (result > 32000) + result = 32000; + return (md_dmabufsize = (result & ~3)); /* round it off to an 8 byte + * boundry */ +} + + +void +MD_InfoDriver(void) +{ + int t; + MDRIVER *l; + + /* list all registered devicedrivers: */ + for (t = 1, l = firstdriver; l != NULL; l = l->next, t++) + printf("%d. %s\n", t, l->Version); +} + + +void +MD_RegisterDriver(MDRIVER * drv) +{ + MDRIVER *cruise = firstdriver; + + if (cruise != NULL) { + while (cruise->next != NULL) + cruise = cruise->next; + cruise->next = drv; + } else + firstdriver = drv; +} + + +/* type - sample type .. MD_MUSIC or MD_SNDFX */ +SWORD +MD_SampleLoad(SAMPLOAD * s, int type, FILE * fp) +{ + 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 = md_driver->SampleLoad(s, type, fp); + SL_Exit(s); + + return result; +} + + +void +MD_SampleUnLoad(SWORD handle) +{ + md_driver->SampleUnLoad(handle); +} + + +void +MD_SetBPM(UBYTE bpm) +{ + md_bpm = bpm; +} + + +void +MikMod_RegisterPlayer(void (*player) (void)) +{ + md_player = player; +} + + +void +MikMod_Update(void) +{ + if (isplaying) + md_driver->Update(); +} + + +void +Voice_SetVolume(int voice, UWORD vol) +{ + ULONG tmp; + + if ((voice < 0) || (voice >= md_numchn)) + return; + tmp = (ULONG) vol *(ULONG) md_volume * + ((voice < md_sngchn) ? (ULONG) md_musicvolume : (ULONG) md_sndfxvolume); + md_driver->VoiceSetVolume(voice, tmp / 16384 UL); +} + + +void +Voice_SetFrequency(int voice, ULONG frq) +{ + if ((voice < 0) || (voice >= md_numchn)) + return; + if (md_sample[voice] != NULL && md_sample[voice]->divfactor != 0) + frq /= md_sample[voice]->divfactor; + md_driver->VoiceSetFrequency(voice, frq); +} + + +void +Voice_SetPanning(int voice, ULONG pan) +{ + if ((voice < 0) || (voice >= md_numchn)) + return; + if (pan != PAN_SURROUND) { + if (md_mode & DMODE_REVERSE) + pan = 255 - pan; + pan = (((SWORD) (pan - 128) * md_pansep) / 128) + 128; + } + md_driver->VoiceSetPanning(voice, pan); +} + + +void +Voice_Play(int voice, SAMPLE * s, ULONG start) +{ + ULONG repend; + + if ((voice < 0) || (voice >= md_numchn) || (start >= s->length)) + return; + + md_sample[voice] = s; + repend = s->loopend; + + if (s->flags & SF_LOOP) + if (repend > s->length) + repend = s->length; /* repend can't be bigger than size */ + + md_driver->VoicePlay(voice, s->handle, start, s->length, s->loopstart, repend, s->flags); +} + + +void +Voice_Stop(int 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; + } + md_driver->VoiceStop(voice); +} + + +BOOL +Voice_Stopped(int voice) +{ + if ((voice < 0) || (voice >= md_numchn)) + return 0; + return (md_driver->VoiceStopped(voice)); +} + + +SLONG +Voice_GetPosition(int voice) +{ + if ((voice < 0) || (voice >= md_numchn)) + return 0; + return (md_driver->VoiceGetPosition(voice)); +} + + +ULONG +Voice_RealVolume(int voice) +{ + if ((voice < 0) || (voice >= md_numchn)) + return 0; + return (md_driver->VoiceRealVolume(voice)); +} + + +/* + * Functions prefixed with MikMod + */ +BOOL +MikMod_Init(void) +{ + UWORD t; + + _mm_critical = 1; + + /* if md_device==0, try to find a device number */ + + if (md_device == 0) { + for (t = 1, md_driver = firstdriver; md_driver != NULL; md_driver = md_driver->next, t++) { + if (md_driver->IsPresent()) + break; + } + + if (md_driver == NULL) { + _mm_errno = MMERR_DETECTING_DEVICE; + if (_mm_errorhandler != NULL) + _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 != NULL) && (t != md_device); + md_driver = md_driver->next, t++) + ; /* Nothing */ + + if (md_driver == NULL) { + _mm_errno = MMERR_INVALID_DEVICE; + if (_mm_errorhandler != NULL) + _mm_errorhandler(); + md_driver = &drv_nos; + return 1; + } + if (!md_driver->IsPresent()) { + _mm_errno = MMERR_DETECTING_DEVICE; + if (_mm_errorhandler != NULL) + _mm_errorhandler(); + md_driver = &drv_nos; + return 1; + } + } + + if (md_driver->Init()) { + MikMod_Exit(); + if (_mm_errorhandler != NULL) + _mm_errorhandler(); + return 1; + } + idevice = md_device; + imode = md_mode; + imixfreq = md_mixfreq; + idmabufsize = md_dmabufsize; + initialized = 1; + _mm_critical = 0; + + return 0; +} + + +void +MikMod_Exit(void) +{ + MikMod_DisableOutput(); + md_driver->Exit(); + md_numchn = md_sfxchn = md_sngchn = 0; + md_driver = &drv_nos; + initialized = 0; +} + + +/* + * Reset the driver using the new global variable settings. If the driver has + * not been initialized, it will be now. + */ +BOOL +MikMod_Reset(void) +{ + if (!initialized) + return MikMod_Init(); + if ((md_driver->Reset == NULL) || + (md_device != idevice)) { /* md_driver->Reset was + * NULL, or md_device + * was changed, so do a + * full reset of the + * driver. */ + if (isplaying) + md_driver->PlayStop(); + md_driver->Exit(); + if (MikMod_Init()) { + MikMod_Exit(); + if (_mm_errorhandler != NULL) + _mm_errorhandler(); + return 1; + } + if (isplaying) + md_driver->PlayStart(); + } else { + if (md_driver->Reset()) { + MikMod_Exit(); + if (_mm_errorhandler != NULL) + _mm_errorhandler(); + return 1; + } + } + + return 0; +} + + +/* If either parameter is -1, the current set value will be retained. */ +BOOL +MikMod_SetNumVoices(int music, int sfx) +{ + BOOL resume = 0; + int t, oldchn = 0; + + if ((music == 0) && (sfx == 0)) + return 0; + + _mm_critical = 1; + + if (isplaying) { + MikMod_DisableOutput(); + oldchn = md_numchn; + resume = 1; + } + if (sfxinfo != NULL) + free(sfxinfo); + if (md_sample != NULL) + free(md_sample); + md_sample = NULL; + sfxinfo = NULL; + + /* + * md_softchn = md_hardchn = 0; + * + * if(md_mode & DMODE_SOFT_SNDFX) md_softchn = sfx; else md_hardchn = sfx; + * + * if(md_mode & DMODE_SOFT_MUSIC) md_softchn += music; else md_hardchn += + * music; + */ + + 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); + + if (md_driver->SetNumVoices()) { + MikMod_Exit(); + md_numchn = md_softchn = md_hardchn = md_sfxchn = md_sngchn = 0; + if (_mm_errorhandler != NULL) + _mm_errorhandler(); + 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; t < md_numchn; t++) + Voice_Stop(t); + + sfxpool = 0; + + if (resume) + MikMod_EnableOutput(); + _mm_critical = 0; + + return 0; +} + + +BOOL +MikMod_EnableOutput(void) +{ + /* + * safety valve, prevents entering playstart twice: + */ + _mm_critical = 1; + if (!isplaying) { + if (md_driver->PlayStart()) + return 1; + isplaying = 1; + } + _mm_critical = 0; + return 0; +} + + +void +MikMod_DisableOutput(void) +{ + /* + * safety valve, prevents calling playStop when playstart hasn't been + * called: + */ + + if (isplaying && md_driver != NULL) { + isplaying = 0; + md_driver->PlayStop(); + } +} + + +BOOL +MikMod_Active(void) +{ + return isplaying; +} + + +/* + * Plays a sound effects sample. Picks a voice from the number of voices + * allocated for use as sound effects (loops through voices, skipping all + * active criticals). + * + * Returns the voice that the sound is being played on. + */ +int +MikMod_PlaySample(SAMPLE * s, ULONG start, UBYTE flags) +{ + int orig = sfxpool; /* for cases where all channels are + * critical */ + int c; + + if (md_sfxchn == 0) + return -1; + if (s->volume > 64) + s->volume = 64; + + /* check the first location after sfxpool */ + do { + if (sfxinfo[sfxpool] & SFX_CRITICAL) { + if (md_driver->VoiceStopped(c = sfxpool + md_sngchn)) { + sfxinfo[sfxpool] = flags; + Voice_Play(c, s, start); + md_driver->VoiceSetVolume(c, s->volume << 2); + md_driver->VoiceSetPanning(c, s->panning); + md_driver->VoiceSetFrequency(c, s->speed); + sfxpool++; + if (sfxpool >= md_sfxchn) + sfxpool = 0; + return c; + } + } else { + sfxinfo[sfxpool] = flags; + Voice_Play(c = sfxpool + md_sngchn, s, start); + md_driver->VoiceSetVolume(c, s->volume << 2); + md_driver->VoiceSetPanning(c, s->panning); + md_driver->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; +} diff -r d5cb2cfc8eca -r 437e8455d862 playercode/mplayer.c --- a/playercode/mplayer.c Fri Jan 23 16:05:11 1998 +0000 +++ b/playercode/mplayer.c Thu Apr 23 07:20:13 1998 +0000 @@ -1,2759 +1,3047 @@ -/* - --> The Protracker Player Driver - -> Part of the SPLAYER pack for MikMod 3.0 - - The protracker driver supports all base Protracker 3.x commands and fea- - tures. -*/ - -#include -#include -#include "mikmod.h" - - -static void DoNNAEffects(UBYTE dat); - -// Set forbid to 1 when you want to modify any of the pf->sngpos, pf->patpos etc. -// variables and clear it when you're done. This prevents getting strange -// results due to intermediate interrupts. - -static UNIMOD *pf; // <- this modfile is being played -static SWORD mp_channel; // channel it's working on -static MP_CONTROL *a; // current AUDTMP it's working on -static int isfirst; - -static MP_VOICE aout_dummy; - -static UWORD mytab[12] = -{ 1712*16, 1616*16, 1524*16, 1440*16, 1356*16, 1280*16, - 1208*16, 1140*16, 1076*16, 1016*16, 960*16, 907*16 -}; - - -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 -// ** Fast Tracker 2 [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 -}; - - -/* 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 forground channel is a bonus x4 - 4) an active envelope with keyoff is a handicap -x2 -*/ -static int MP_FindEmptyChannel(int curchan) // returns mp_control index of free channel -{ - MP_VOICE *a; - ULONG t,k,tvol,p,pp; - - /*for(t=md_sngchn; t; t--, audpool++) - { if(audpool == md_sngchn) audpool = 0; - if(!(pf->voice[audpool].kick) && Voice_Stopped(audpool)) - { audpool++; - return audpool-1; - } - }*/ - - for(t=0; tvoice[t].kick) && Voice_Stopped(t)) - { return t; - } - } - - tvol = 0xffffffUL; t = 0; p = 0; a = pf->voice; - for(k=0; kkick) - { pp = a->totalvol << ((a->s->flags & SF_LOOP) ? 1 : 0); - if((a->master!=NULL) && (a==a->master->slave)) - pp <<= 2; - - /*if(a->volflg & EF_ON) - { if(a->volflg & (EF_SUSTAIN | EF_LOOP)) - { if(a->keyoff & KEY_OFF) - { pp >>= 1; - if(a->venv.env[a->venv.end].val < 32) pp>>=1; - } else - pp <<= 1; - } else pp <<= 1; - }*/ - - if(pp < tvol) - { tvol = pp; - t = k; - } - } - } - - if(tvol>8000*7) return -1; //mp_channel; - - return t; -} - - -static SWORD Interpolate(SWORD p, SWORD p1, SWORD p2, SWORD v1, SWORD v2) -{ - SWORD dp,dv,di; - - if(p1==p2) return v1; - - dv = v2-v1; - dp = p2-p1; - di = p-p1; - - return v1 + ((SLONG)(di*dv) / dp); -} - - -UWORD getlinearperiod(UBYTE note, ULONG fine) -{ - return((10L*12*16*4)-((ULONG)note*16*4)-(fine/2)+64); -} - - -static UWORD getlogperiod(UBYTE note,ULONG fine) -{ - UBYTE n,o; - UWORD p1,p2; - ULONG i; - - n = note%12; - o = note/12; - i = (n<<3) + (fine>>4); // n*8 + fine/16 - - p1 = logtab[i]; - p2 = logtab[i+1]; - - return(Interpolate(fine/16,0,15,p1,p2)>>o); -} - - -static UWORD getoldperiod(UBYTE note, ULONG speed) -{ - UBYTE n, o; - ULONG period; - - if(!speed) return 4242; // <- prevent divide overflow.. (42 eheh) - - n = note % 12; - o = note / 12; - period = ((8363l*(ULONG)mytab[n]) >> o ) / speed; - return period; -} - - -static UWORD GetPeriod(UBYTE note, ULONG speed) -{ - if(pf->flags & UF_XMPERIODS) - return (pf->flags & UF_LINEAR) ? getlinearperiod(note,speed) : getlogperiod(note,speed); - - 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) -{ - return(pan + (((envpan-128)*(128-abs(pan-128)))/128)); -} - - -static void 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; -} - - -static SWORD ProcessEnvelope(ENVPR *t, SWORD v, UBYTE keyoff) - -// 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, panniong 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. -// 1. 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. - -{ - 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; - - // compute the current envelope value between points a and b - - if(a == b) - v = t->env[a].val; - else - v = InterpolateEnv(p, &t->env[a], &t->env[b]); - - p++; - - // pointer reached point b? - - if(p >= t->env[b].pos) - { a = b++; // shift points a and b - - // Check for loops, sustain loops, or end of envelope. - - if((t->flg & EF_SUSTAIN) && !(keyoff & KEY_OFF) && (b > t->susend)) - { a = t->susbeg; - if(t->susbeg == t->susend) b = a; else b = a + 1; - p = t->env[a].pos; - } else if((t->flg & EF_LOOP) && (b > t->end)) - { a = t->beg; - if(t->beg == t->end) b = a; else b = a + 1; - p = t->env[a].pos; - } else - { if(b >= t->pts) - { if((t->flg & EF_VOLENV) && (mp_channel != -1)) - { pf->voice[mp_channel].keyoff |= KEY_FADE; - if(v==0) - pf->voice[mp_channel].fadevol = 0; - } - b--; p--; - } - } - } - t->a = a; - t->b = b; - t->p = p; - } - return v; -} - - -ULONG getfrequency(UBYTE flags, ULONG period) - -// XM linear period to frequency conversion - -{ - ULONG result; - - if(flags & UF_LINEAR) - result = lintab[period % 768] >> (period / 768); - else - result = (8363L*1712L) / period; - - return result; -} - - -static void DoEEffects(UBYTE dat) -{ - UBYTE nib; - - nib = dat & 0xf; - - switch(dat>>4) - { case 0x0: // filter toggle, not supported - break; - - case 0x1: // fineslide up - if(!pf->vbtick) a->tmpperiod-=(nib<<2); - break; - - case 0x2: // fineslide dn - if(!pf->vbtick) 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 -// a->speed=finetune[nib]; -// a->tmpperiod=GetPeriod(a->note,pf->samples[a->sample].transpose,a->speed); - break; - - case 0x6: // set patternloop - if(pf->vbtick) break; - // hmm.. this one is a real kludge. But now it - // works - if(nib) // set reppos or repcnt ? - { // set repcnt, so check if repcnt already is set, - // which means we are already looping - - if(pf->pat_repcnt > 0) - pf->pat_repcnt--; // already looping, decrease counter - else - pf->pat_repcnt = nib; // not yet looping, so set repcnt - - if(pf->pat_repcnt) // jump to reppos if repcnt>0 - pf->patpos = pf->pat_reppos; - } else - { pf->pat_reppos = pf->patpos-1; // set reppos - } - break; - - - case 0x7: // set tremolo waveform - a->wavecontrol &= 0x0f; - a->wavecontrol |= nib << 4; - break; - - case 0x8: // set panning - if(pf->panflag) - { if(nib<=8) nib*=16; else nib*=17; - a->panning = nib; - pf->panning[mp_channel] = nib; - } - break; - - case 0x9: // retrig note - // only retrigger if - // data nibble > 0 - - if(nib > 0) - { if(a->retrig==0) - { // when retrig counter reaches 0, - // reset counter and restart the sample - a->kick = 1; - a->retrig = nib; - } - a->retrig--; // countdown - } - break; - - case 0xa: // fine volume slide up - if(pf->vbtick) break; - - a->tmpvolume += nib; - if(a->tmpvolume > 64) a->tmpvolume = 64; - break; - - case 0xb: // fine volume slide dn - if(pf->vbtick) break; - - a->tmpvolume -= nib; - if(a->tmpvolume < 0) a->tmpvolume = 0; - break; - - case 0xc: // cut note - // When pf->vbtick reaches the cut-note value, - // turn the volume to zero ( Just like - // on the amiga) - if(pf->vbtick>=nib) - a->tmpvolume = 0; // just turn the volume down - break; - - case 0xd: // note delay - // delay the start of the - // sample until pf->vbtick==nib - if(pf->vbtick==nib) - { //a->kick = 1; - a->notedelay = 0; - } else if(pf->vbtick==0) - { //a->kick = 0; - a->notedelay = 1; - } - break; - - case 0xe: // pattern delay - if(pf->vbtick) break; - if(!pf->patdly2) pf->patdly = nib+1; // only once (when pf->vbtick=0) - break; - - case 0xf: // invert loop, not supported - break; - } -} - - -static void DoVibrato(void) -{ - UBYTE q; - UWORD temp; - - 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: // Evil random wave - temp = rand() & 255; - break; - } - - temp*=a->vibdepth; - temp>>=7; - temp<<=2; - - if(a->vibpos>=0) - a->period = a->tmpperiod+temp; - else - a->period = a->tmpperiod-temp; - - if(pf->vbtick) a->vibpos+=a->vibspd; // do not update when pf->vbtick==0 -} - - -static void DoTremolo(void) -{ - UBYTE q; - UWORD temp; - - 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: // Evil random wave - temp = rand() & 255; - 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; - } - - if(pf->vbtick) a->trmpos+=a->trmspd; // do not update when pf->vbtick==0 -} - - -static void DoVolSlide(UBYTE dat) -{ - if(!pf->vbtick) return; // do not update when pf->vbtick==0 - - a->tmpvolume += dat >> 4; // volume slide - a->tmpvolume -= dat & 0xf; - if(a->tmpvolume < 0) a->tmpvolume = 0; - if(a->tmpvolume > 64) a->tmpvolume = 64; -} - - -static void DoToneSlide(void) -{ - int dist; - - if(a->period==0) return; - - if(!pf->vbtick) - { a->tmpperiod = a->period; - return; - } - - // We have to slide a->period towards a->wantedperiod, so - // compute the difference between those two values - - dist = a->period-a->wantedperiod; - - if( dist==0 || a->portspeed>abs(dist) ) // if they are equal or if portamentospeed is too big - a->period = a->wantedperiod; // make tmpperiod equal tperiod - else if(dist>0) // dist>0 ? - a->period-=a->portspeed; // then slide up - else - a->period+=a->portspeed; // dist<0 -> slide down - - a->tmpperiod = a->period; -} - - -static void DoPTEffect0(UBYTE dat) -{ - UBYTE note; - - note = a->note; - - if(dat!=0) - { switch(pf->vbtick%3) - { case 1: - note+=(dat>>4); break; - case 2: - note+=(dat&0xf); break; - } - a->period = GetPeriod(note,a->speed); - a->ownper = 1; - } -} - - -// ----------------------------------------- -// --> ScreamTreacker 3 Specific Effects <-- -// ----------------------------------------- - -static void DoS3MVolSlide(UBYTE inf) -{ - UBYTE lo, hi; - - if(inf) a->s3mvolslide = inf; - - inf = a->s3mvolslide; - lo = inf & 0xf; - hi = inf >> 4; - - if(hi==0) a->tmpvolume -= lo; - else if(lo==0) a->tmpvolume += hi; - else if(hi==0xf) - { if(!pf->vbtick) a->tmpvolume -= lo; - } else if(lo==0xf) - { if(!pf->vbtick) a->tmpvolume += hi; - } - if(a->tmpvolume < 0) a->tmpvolume = 0; - if(a->tmpvolume > 64) a->tmpvolume = 64; -} - - -static void DoS3MSlideDn(UBYTE inf) -{ - UBYTE hi,lo; - - if(inf!=0) a->slidespeed = inf; - else inf = a->slidespeed; - - hi = inf>>4; - lo = inf&0xf; - - if(hi==0xf) - { if(!pf->vbtick) a->tmpperiod+=(UWORD)lo<<2; - } else if(hi==0xe) - { if(!pf->vbtick) a->tmpperiod+=lo; - } else - { if(pf->vbtick) a->tmpperiod+=(UWORD)inf<<2; - } -} - - -static void DoS3MSlideUp(UBYTE inf) -{ - UBYTE hi,lo; - - if(inf!=0) a->slidespeed = inf; - else inf = a->slidespeed; - - hi = inf>>4; - lo = inf&0xf; - - if(hi==0xf) - { if(!pf->vbtick) a->tmpperiod-=(UWORD)lo<<2; - } else if(hi==0xe) - { if(!pf->vbtick) a->tmpperiod-=lo; - } else - { if(pf->vbtick) a->tmpperiod-=(UWORD)inf<<2; - } -} - - -static void DoS3MTremor(UBYTE inf) -{ - UBYTE on,off; - - if(inf!=0) a->s3mtronof = inf; - else inf = a->s3mtronof; - - if(!pf->vbtick) return; - - on = (inf>>4)+1; - off = (inf&0xf)+1; - - a->s3mtremor %= (on+off); - a->volume = (a->s3mtremor < on ) ? a->tmpvolume : 0; - a->s3mtremor++; -} - - -static void DoS3MRetrig(UBYTE inf) -{ - UBYTE hi,lo; - - hi = inf >> 4; - lo = inf & 0xf; - - if(inf) - { a->s3mrtgslide = hi; - a->s3mrtgspeed = lo; - } - - // only retrigger if - // lo nibble > 0 - - if(a->s3mrtgspeed > 0) - { if(a->retrig == 0) - { // when retrig counter reaches 0, - // reset counter and restart the sample - - if(!a->kick) a->kick = 2; - a->retrig = a->s3mrtgspeed; - - if(pf->vbtick) // don't slide on first retrig - { 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 = 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)/2; - break; - - case 0xf: - a->tmpvolume=a->tmpvolume<<1; - break; - } - if(a->tmpvolume<0) a->tmpvolume = 0; - if(a->tmpvolume>64) a->tmpvolume = 64; - } - } - a->retrig--; // countdown - } -} - - -static void DoS3MSpeed(UBYTE speed) -{ - if(pf->vbtick || pf->patdly2) return; - - if(speed) - { pf->sngspd = speed; - pf->vbtick = 0; - } -} - - -static void DoS3MTempo(UBYTE tempo) -{ - if(pf->vbtick || pf->patdly2) return; - pf->bpm = tempo; -} - - -static void DoS3MFineVibrato(void) -{ - UBYTE q; - UWORD temp; - - 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: // evil random - temp = rand() & 255; // (range 0 to 255) - } - - temp*=a->vibdepth; - temp>>=8; - - if(a->vibpos>=0) - a->period = a->tmpperiod+temp; - else - a->period = a->tmpperiod-temp; - - a->vibpos += a->vibspd; -} - - -static void DoS3MTremolo(void) -{ - UBYTE q; - UWORD temp; - - 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: // evil random - temp = rand() & 255; // (range 0 to 255) - } - - 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; - } - - if(pf->vbtick) a->trmpos += a->trmspd; // do not update when pf->vbtick==0 -} - - -// -------------------------------------- -// --> FastTracker 2 Specific Effects <-- -// -------------------------------------- - -static void DoXMVolSlide(UBYTE inf) -{ - UBYTE lo,hi; - - if(inf) - a->s3mvolslide = inf; - - inf = a->s3mvolslide; - if(!pf->vbtick) return; - - lo = inf&0xf; - hi = inf>>4; - - if(hi==0) - a->tmpvolume-=lo; - else - a->tmpvolume+=hi; - - if(a->tmpvolume<0) a->tmpvolume=0; - else if(a->tmpvolume>64) a->tmpvolume=64; -} - - -static void DoXMGlobalSlide(UBYTE inf) -{ - if(pf->vbtick) - { if(inf) pf->globalslide=inf; else inf=pf->globalslide; - if(inf & 0xf0) inf &= 0xf0; - pf->volume = pf->volume + ((inf >> 4) - (inf & 0xf))*2; - - if(pf->volume<0) pf->volume = 0; - else if(pf->volume>128) pf->volume = 128; - } -} - - -static void DoXMPanSlide(UBYTE inf) -{ - UBYTE lo,hi; - SWORD pan; - - - if(inf!=0) a->pansspd = inf; - else inf = a->pansspd; - - if(!pf->vbtick) return; - - lo = inf & 0xf; - hi = inf >> 4; - - // slide right has absolute priority: - - if(hi) lo = 0; - - pan = (a->panning == PAN_SURROUND) ? 128 : a->panning; - - pan -= lo; - pan += hi; - - if(pan < 0) pan = 0; - if(pan > 255) pan = 255; - - a->panning = pan; -} - - -static void DoXMExtraFineSlideUp(UBYTE inf) -{ - if(!pf->vbtick) - { if(inf) a->ffportupspd = inf; else inf = a->ffportupspd; - a->period -= inf; - } - a->tmpperiod = a->period; -} - - -static void DoXMExtraFineSlideDown(UBYTE inf) -{ - if(!pf->vbtick) - { if(inf) a->ffportdnspd = inf; else inf = a->ffportdnspd; - a->period += inf; - } - a->tmpperiod = a->period; -} - - -// --------------------------------------- -// --> ImpulseTracker Player Functions <-- -// --------------------------------------- - -static void DoITChanVolSlide(UBYTE inf) -{ - UBYTE lo, hi; - - if(inf) a->chanvolslide = inf; - inf = a->chanvolslide; - - lo = inf&0xf; - hi = inf>>4; - - if(hi==0) - { a->chanvol-=lo; - } else if(lo==0) - { a->chanvol+=hi; - } else if(hi==0xf) - { if(!pf->vbtick) a->chanvol-=lo; - } else if(lo==0xf) - { if(!pf->vbtick) a->chanvol+=hi; - } - - if(a->chanvol<0) a->chanvol = 0; - if(a->chanvol>64) a->chanvol = 64; -} - - -static void DoITGlobalSlide(UBYTE inf) -{ - UBYTE lo,hi; - - if(inf) pf->globalslide = inf; - inf = pf->globalslide; - - lo = inf&0xf; - hi = inf>>4; - - if(lo==0) - { pf->volume += hi; - } else if(hi==0) - { pf->volume -= lo; - } else if(lo==0xf) - { if(!pf->vbtick) pf->volume += hi; - } else if(hi==0xf) - { if(!pf->vbtick) pf->volume -= lo; - } - - if(pf->volume < 0) pf->volume = 0; - if(pf->volume > 128) pf->volume = 128; -} - - -static void DoITPanSlide(UBYTE inf) -{ - UBYTE lo,hi; - SWORD pan; - - if(inf) a->pansspd = inf; - inf = a->pansspd; - - lo = inf & 0xf; - hi = inf >> 4; - - pan = (a->panning == PAN_SURROUND) ? 128 : a->panning; - - if(hi==0) - { pan += lo << 2; - } else if(lo==0) - { pan -= hi << 2; - } else if(hi==0xf) - { if(!pf->vbtick) pan += lo << 2; - } else if(lo==0xf) - { if(!pf->vbtick) pan -= hi << 2; - } - if(pan > 255) pan = 255; - if(pan < 0) pan = 0; - a->panning = /*pf->panning[mp_channel] =*/ pan; -} - - -static void DoITVibrato(void) -{ - UBYTE q; - UWORD temp; - - 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: // evil random - temp = rand() & 255; // (range 0 to 255) - break; - } - - temp*=a->vibdepth; - temp>>=8; - temp<<=2; - - if(a->vibpos>=0) - a->period = a->tmpperiod+temp; - else - a->period = a->tmpperiod-temp; - - a->vibpos+=a->vibspd; -} - - -static void DoITFineVibrato(void) -{ - UBYTE q; - UWORD temp; - - 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: // evil random - temp = rand() & 255; // (range 0 to 255) - break; - } - - temp*=a->vibdepth; - temp>>=8; - - if(a->vibpos>=0) - a->period = a->tmpperiod+temp; - else - a->period = a->tmpperiod-temp; - - a->vibpos+=a->vibspd; -} - - -static void DoITTremor(UBYTE inf) -{ - UBYTE on,off; - - if(inf!=0) a->s3mtronof = inf; - else inf = a->s3mtronof; - - if(!pf->vbtick) return; - - on=(inf>>4); - off=(inf&0xf); - - a->s3mtremor%=(on+off); - a->volume = (a->s3mtremor < on ) ? a->tmpvolume : 0; - a->s3mtremor++; -} - - -static void DoITPanbrello(void) -{ - UBYTE q; - static SLONG temp; - - q = a->panbpos; - - switch(a->panbwave) - { case 0: // sine - temp = PanbrelloTable[q]; - break; - - // only sinewave is correctly supported right now - - case 1: // ramp down - q<<=3; - temp = q; - break; - - case 2: // square wave - temp = 64; - break; - - case 3: // evil random - if(a->panbpos >= a->panbspd) - { a->panbpos = 0; - temp = rand() & 255; - } - } - - temp*=a->panbdepth; - temp/=8; - - a->panning = pf->panning[mp_channel] + temp; - a->panbpos += a->panbspd; -} - - -static void DoITToneSlide(void) -{ - int dist; - - if(a->period == 0) return; - - if(!pf->vbtick) - { a->tmpperiod = a->period; - return; - } - - // We have to slide a->period towards a->wantedperiod, - // compute the difference between those two values - - dist = a->period - a->wantedperiod; - - if( (dist == 0) || // if they are equal - ((a->slidespeed<<2) > abs(dist)) ) // or if portamentospeed is too big - { a->period = a->wantedperiod; // make tmpperiod equal tperiod - } else if(dist > 0) // dist>0 ? - { a->period -= a->slidespeed << 2; // then slide up - } else - { a->period += a->slidespeed << 2; // dist<0 -> slide down - } - a->tmpperiod = a->period; -} - - -static void DoSSEffects(UBYTE dat) - -// Impulse/Scream Tracker Sxx effects. -// All Sxx effects share the same memory space. - -{ - UBYTE inf,c; - - inf = dat&0xf; - c = dat>>4; - - if(dat==0) - { c = a->sseffect; - inf = a->ssdata; - } else - { a->sseffect = c; - a->ssdata = inf; - } - - switch(c) - { case SS_GLISSANDO: // S1x set glissando voice - DoEEffects(0x30|inf); - break; - - case SS_FINETUNE: // S2x set finetune - DoEEffects(0x50|inf); - break; - - case SS_VIBWAVE: // S3x set vibrato waveform - DoEEffects(0x40|inf); - break; - - case SS_TREMWAVE: // S4x set tremolo waveform - DoEEffects(0x70|inf); - break; - - case SS_PANWAVE: // The Satanic Panbrello waveform - a->panbwave = (UniGetByte()); - break; - - case SS_FRAMEDELAY: // S6x Delay x number of frames (patdly) - DoEEffects(0xe0|inf); - break; - - case SS_S7EFFECTS: // S7x Instrument / NNA commands - DoNNAEffects(UniGetByte()); - break; - - case SS_PANNING: // S8x set panning position - DoEEffects(0x80 | inf); - break; - - case SS_SURROUND: // S9x Set Surround Sound - a->panning = pf->panning[mp_channel] = PAN_SURROUND; - break; - - case SS_HIOFFSET: // SAy Set high order sample offset yxx00h - a->hioffset |= UniGetByte() << 16; - break; - - case SS_PATLOOP: // SBx pattern loop - DoEEffects(0x60|inf); - break; - - case SS_NOTECUT: // SCx notecut - DoEEffects(0xC0|inf); - break; - - case SS_NOTEDELAY: // SDx notedelay - DoEEffects(0xD0|inf); - break; - - case SS_PATDELAY: // SEx patterndelay - DoEEffects(0xE0|inf); - break; - } -} - - -static void DoVolEffects(UBYTE c) - -// Impulse Tracker Volume/Pan Column effects. -// All volume/pan column effects share the same memory space. - -{ - UBYTE inf; - - inf = UniGetByte(); - - if(c==0 && inf==0) - { c = a->voleffect; - inf = a->voldata; - } else - { a->voleffect = c; - a->voldata = inf; - } - - switch(c) - { case 0: break; // do nothing - case VOL_VOLUME: - if(pf->vbtick) break; - if(inf>64) inf = 64; - a->tmpvolume = inf; - break; - - case VOL_PANNING: - if(pf->panflag) - { a->panning = inf; - pf->panning[mp_channel] = inf; - } - break; - - case VOL_VOLSLIDE: - DoS3MVolSlide(inf); - break; - - case VOL_PITCHSLIDEDN: - DoS3MSlideDn(UniGetByte()); - break; - - case VOL_PITCHSLIDEUP: - DoS3MSlideUp(UniGetByte()); - break; - - case VOL_PORTAMENTO: - if(inf != 0) a->slidespeed = inf; - - if(a->period != 0) - { if(!(pf->vbtick==pf->sngspd-1) && (a->newsamp)) - { a->kick = 1; - a->start = -1; - //a->period *= a->speed * a->newsamp; - } else - a->kick = 0; - - DoITToneSlide(); - a->ownper = 1; - } - break; - - case VOL_VIBRATO: - if(inf & 0x0f) a->vibdepth = inf & 0xf; - if(inf & 0xf0) a->vibspd = (inf & 0xf0) >> 2; - DoITVibrato(); - a->ownper = 1; - break; - } -} - - - -// -------------------------------- -// --> General Player Functions <-- -// -------------------------------- - -static void pt_playeffects(void) -{ - UBYTE dat,c; - - while(c = UniGetByte()) - switch(c) - { case UNI_NOTE: - case UNI_INSTRUMENT: - UniSkipOpcode(c); - break; - - case UNI_PTEFFECT0: - DoPTEffect0(UniGetByte()); - break; - - case UNI_PTEFFECT1: - dat = UniGetByte(); - if(dat != 0) a->slidespeed = (UWORD)dat << 2; - if(pf->vbtick) a->tmpperiod -= a->slidespeed; - break; - - case UNI_PTEFFECT2: - dat = UniGetByte(); - if(dat != 0) a->slidespeed = (UWORD)dat << 2; - if(pf->vbtick) a->tmpperiod += a->slidespeed; - break; - - case UNI_PTEFFECT3: - dat = UniGetByte(); - - if(dat!=0) - { a->portspeed = dat; - a->portspeed <<= 2; - } - - if(a->period != 0) - { a->kick = 0; // temp XM fix - DoToneSlide(); - a->ownper = 1; - } - break; - - case UNI_PTEFFECT4: - dat = UniGetByte(); - if(dat & 0x0f) a->vibdepth = dat & 0xf; - if(dat & 0xf0) a->vibspd = (dat & 0xf0) >> 2; - DoVibrato(); - a->ownper = 1; - break; - - case UNI_PTEFFECT5: - dat = UniGetByte(); - a->kick = 0; - DoToneSlide(); - DoVolSlide(dat); - a->ownper = 1; - break; - - case UNI_PTEFFECT6: - dat = UniGetByte(); - DoVibrato(); - DoVolSlide(dat); - a->ownper = 1; - break; - - case UNI_PTEFFECT7: - dat = UniGetByte(); - if(dat & 0x0f) a->trmdepth = dat & 0xf; - if(dat & 0xf0) a->trmspd = (dat & 0xf0) >> 2; - DoTremolo(); - a->ownvol = 1; - break; - - case UNI_PTEFFECT8: - dat = UniGetByte(); - if(pf->panflag) - { a->panning = dat; - pf->panning[mp_channel] = dat; - } - break; - - case UNI_PTEFFECT9: - dat = UniGetByte(); - if(dat) a->soffset = (UWORD)dat << 8; - a->start = a->hioffset | a->soffset; - if((a->s != NULL) && (a->start > a->s->length)) a->start = a->s->loopstart; - break; - - case UNI_PTEFFECTA: - DoVolSlide(UniGetByte()); - break; - - case UNI_PTEFFECTB: - dat = UniGetByte(); - if(pf->patdly2) break; - pf->patbrk = 0; - pf->sngpos = dat-1; - pf->posjmp = 3; - break; - - case UNI_PTEFFECTC: - dat = UniGetByte(); - if(pf->vbtick) break; - if(dat>64) dat = 64; - a->tmpvolume = dat; - break; - - case UNI_PTEFFECTD: - dat = UniGetByte(); - if(pf->patdly2) break; - pf->patbrk = dat; - if(pf->patbrk>pf->pattrows[mp_channel]) - pf->patbrk = pf->pattrows[mp_channel]; - pf->posjmp = 3; - break; - - case UNI_PTEFFECTE: - DoEEffects(UniGetByte()); - break; - - case UNI_PTEFFECTF: - dat = UniGetByte(); - - if(pf->vbtick || pf->patdly2) break; - - if(pf->extspd && (dat >= 0x20)) - pf->bpm = dat; - else - { if(dat) - { pf->sngspd = dat; - pf->vbtick = 0; - } - } - break; - - case UNI_S3MEFFECTA: - DoS3MSpeed(UniGetByte()); - break; - - case UNI_S3MEFFECTD: - DoS3MVolSlide(UniGetByte()); - break; - - case UNI_S3MEFFECTE: - DoS3MSlideDn(UniGetByte()); - break; - - case UNI_S3MEFFECTF: - DoS3MSlideUp(UniGetByte()); - break; - - case UNI_S3MEFFECTI: - DoS3MTremor(UniGetByte()); - a->ownvol = 1; - break; - - case UNI_S3MEFFECTQ: - DoS3MRetrig(UniGetByte()); - break; - - case UNI_S3MEFFECTR: - dat = UniGetByte(); - if(dat & 0x0f) a->trmdepth = dat & 0xf; - if(dat & 0xf0) a->trmspd = (dat & 0xf0) >> 2; - DoS3MTremolo(); - a->ownvol = 1; - break; - - case UNI_S3MEFFECTT: - DoS3MTempo(UniGetByte()); - break; - - case UNI_S3MEFFECTU: - dat = UniGetByte(); - if(dat & 0x0f) a->vibdepth = dat & 0xf; - if(dat & 0xf0) a->vibspd = (dat & 0xf0) >> 2; - DoS3MFineVibrato(); - a->ownper = 1; - break; - - case UNI_KEYOFF: - a->keyoff |= KEY_OFF; - if(a->i != NULL) - { if(!(a->i->volflg & EF_ON) || (a->i->volflg & EF_LOOP)) - a->keyoff = KEY_KILL; - } - break; - - case UNI_KEYFADE: - if(pf->vbtick >= UniGetByte()) - { a->keyoff = KEY_KILL; - if((a->i != NULL) && !(a->i->volflg & EF_ON)) - a->fadevol = 0; - } - break; - - case UNI_VOLEFFECTS: - DoVolEffects(UniGetByte()); - break; - - case UNI_XMEFFECT4: - dat = UniGetByte(); - if(pf->vbtick) - { if(dat & 0x0f) a->vibdepth = dat & 0xf; - if(dat & 0xf0) a->vibspd = (dat & 0xf0) >> 2; - } - DoVibrato(); - a->ownper = 1; - break; - - case UNI_XMEFFECTA: - DoXMVolSlide(UniGetByte()); - break; - - case UNI_XMEFFECTE1: // xm fineslide up - dat = UniGetByte(); - if(!pf->vbtick) - { if(dat) a->fportupspd = dat; else dat = a->fportupspd; - a->tmpperiod -= (dat << 2); - } - break; - - case UNI_XMEFFECTE2: // xm fineslide dn - dat = UniGetByte(); - if(!pf->vbtick) - { if(dat) a->fportdnspd = dat; else dat = a->fportdnspd; - a->tmpperiod += (dat<<2); - } - break; - - case UNI_XMEFFECTEA: // fine volume slide up - dat = UniGetByte(); - if(pf->vbtick) break; - if(dat) a->fslideupspd = dat; else dat = a->fslideupspd; - a->tmpvolume+=dat; - if(a->tmpvolume>64) a->tmpvolume = 64; - break; - - case UNI_XMEFFECTEB: // fine volume slide dn - dat = UniGetByte(); - if(pf->vbtick) break; - if(dat) a->fslidednspd = dat; else dat = a->fslidednspd; - a->tmpvolume-=dat; - if(a->tmpvolume<0) a->tmpvolume = 0; - break; - - case UNI_XMEFFECTG: - pf->volume = UniGetByte(); - break; - - case UNI_XMEFFECTH: - DoXMGlobalSlide(UniGetByte()); - break; - - case UNI_XMEFFECTL: - dat = UniGetByte(); - if(!pf->vbtick && a->i!=NULL) - { UWORD points; - INSTRUMENT *i = a->i; - MP_VOICE *aout; - - if((aout = a->slave) != NULL) - { points = i->volenv[i->volpts-1].pos; - aout->venv.p = aout->venv.env[(dat>points) ? points : dat].pos; - - points = i->panenv[i->panpts-1].pos; - aout->penv.p = aout->penv.env[(dat>points) ? points : dat].pos; - } - } - break; - - case UNI_XMEFFECTP: - DoXMPanSlide(UniGetByte()); - break; - - case UNI_XMEFFECTX1: - DoXMExtraFineSlideUp(UniGetByte()); - a->ownper = 1; - break; - - case UNI_XMEFFECTX2: - DoXMExtraFineSlideDown(UniGetByte()); - a->ownper = 1; - break; - - case UNI_ITEFFECTG: - dat = UniGetByte(); - if(dat != 0) a->slidespeed = dat; - - if(a->period != 0) - { if((pf->vbtick < 1) && (a->newsamp)) - { a->kick = 1; - a->start = -1; - //a->period *= a->speed * a->newsamp; - } else - a->kick = 0; - - DoITToneSlide(); - a->ownper = 1; - } - break; - - case UNI_ITEFFECTH: // it vibrato - dat = UniGetByte(); - if(dat & 0x0f) a->vibdepth = dat & 0xf; - if(dat & 0xf0) a->vibspd = (dat & 0xf0) >> 2; - DoITVibrato(); - a->ownper = 1; - break; - - case UNI_ITEFFECTI: // it tremor - DoITTremor(UniGetByte()); - a->ownvol = 1; - break; - - case UNI_ITEFFECTM: - a->chanvol = UniGetByte(); - if(a->chanvol > 64) a->chanvol = 64; - else if(a->chanvol < 0) a->chanvol = 0; - break; - - case UNI_ITEFFECTN: // Slide / Fineslide Channel Volume - DoITChanVolSlide(UniGetByte()); - break; - - case UNI_ITEFFECTP: // slide / fineslide channel panning - DoITPanSlide(UniGetByte()); - break; - - case UNI_ITEFFECTU: // fine vibrato - dat = UniGetByte(); - if(dat & 0x0f) a->vibdepth = dat & 0xf; - if(dat & 0xf0) a->vibspd = (dat & 0xf0) >> 2; - DoITFineVibrato(); - a->ownper = 1; - break; - - case UNI_ITEFFECTW: // Slide / Fineslide Global volume - DoITGlobalSlide(UniGetByte()); - break; - - case UNI_ITEFFECTY: // The Satanic Panbrello - dat = UniGetByte(); - if(dat & 0x0f) a->panbdepth = (dat & 0xf); - if(dat & 0xf0) a->panbspd = (dat & 0xf0) >> 4; - DoITPanbrello(); - break; - - case UNI_ITEFFECTS0: - DoSSEffects(UniGetByte()); - break; - - default: - UniSkipOpcode(c); - break; - } -} - - -static void DoNNAEffects(UBYTE dat) -{ - int t; - MP_VOICE *aout; - - dat &= 0xf; - aout = (a->slave==NULL) ? &aout_dummy : a->slave; - - switch(dat) - { case 0x0: // Past note cut - for(t=0; tvoice[t].master == a) - pf->voice[t].fadevol = 0; - break; - - case 0x1: // Past note off - for(t=0; tvoice[t].master == a) - { pf->voice[t].keyoff |= KEY_OFF; - if(!(pf->voice[t].venv.flg & EF_ON)) - pf->voice[t].keyoff = KEY_KILL; - } - break; - - case 0x2: // Past note fade - for(t=0; tvoice[t].master == a) - pf->voice[t].keyoff |= KEY_FADE; - break; - - case 0x3: // set NNA note cut - a->nna = (a->nna & ~0x3f) | NNA_CUT; - break; - - case 0x4: // set NNA note continue - a->nna = (a->nna & ~0x3f) | NNA_CONTINUE; - break; - - case 0x5: // set NNA note off - a->nna = (a->nna & ~0x3f) | NNA_OFF; - break; - - case 0x6: // set NNA note fade - a->nna = (a->nna & ~0x3f) | NNA_FADE; - break; - - case 0x7: // disable volume envelope - aout->volflg &= ~EF_ON; - break; - - case 0x8: // enable volume envelope - aout->volflg |= EF_ON; - break; - - case 0x9: // disable panning envelope - aout->panflg &= ~EF_ON; - break; - - case 0xa: // enable panning envelope - aout->panflg |= EF_ON; - break; - - case 0xb: // disable pitch envelope - aout->pitflg &= ~EF_ON; - break; - - case 0xc: // enable pitch envelope - aout->pitflg |= EF_ON; - break; - } -} - - -void Player_HandleTick(void) -{ - MP_VOICE *aout; // current audout (slave of audtmp) it's working on - int t, tr, t2, k; - ULONG tmpvol, period; - UBYTE c; - BOOL funky; - SAMPLE *s; - INSTRUMENT *i; - - if(isfirst) - { // don't handle the very first ticks, this allows the - // other hardware to settle down so we don't loose any - // starting notes - isfirst--; - return; - } - - if(pf==NULL || pf->forbid) return; - - if(++pf->vbtick >= pf->sngspd) - { 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) pf->patpos--; // so turn back pf->patpos by 1 - } - - // Do we have to get a new patternpointer ? - // (when pf->patpos reaches 64 or when - // a patternbreak is active) - - if(pf->patpos == pf->numrow) pf->posjmp = 3; - - if(pf->posjmp) - { pf->patpos = pf->patbrk; - pf->sngpos+=(pf->posjmp-2); - pf->patbrk = pf->posjmp = 0; - if(pf->sngpos>=pf->numpos) - { if(!pf->loop) return; - if((pf->sngpos = pf->reppos) == 0) - { pf->volume = pf->initvolume; - pf->sngspd = pf->initspeed; - pf->bpm = pf->inittempo; - } - } - if(pf->sngpos<0) pf->sngpos = pf->numpos-1; - } - - if(!pf->patdly2) - { for(t=0; tnumchn; t++) - { UBYTE inst; - - tr = pf->patterns[(pf->positions[pf->sngpos]*pf->numchn)+t]; - pf->numrow = pf->pattrows[pf->positions[pf->sngpos]]; - - mp_channel = t; - a = &pf->control[t]; - a->row = (tr < pf->numtrk) ? UniFindRow(pf->tracks[tr],pf->patpos) : NULL; - a->newsamp = 0; - - if(a->row==NULL) continue; - UniSetRow(a->row); - funky = 0; // Funky is set to indicate note or inst change - - while(c = UniGetByte()) - { switch(c) - { case UNI_NOTE: - funky |= 1; - a->anote = UniGetByte(); - a->kick = 1; - a->start = -1; - - // 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: - funky |= 2; - inst = UniGetByte(); - if(inst >= pf->numins) break; // <- safety valve - - a->i = (pf->flags & UF_INST) ? &pf->instruments[inst] : NULL; - a->retrig = 0; - a->s3mtremor = 0; - a->sample = inst; - break; - - default: - UniSkipOpcode(c); - break; - } - } - - if(funky) - { i = a->i; - if(i != NULL) - { if(i->samplenumber[a->anote] >= pf->numsmp) continue; - s = &pf->samples[i->samplenumber[a->anote]]; - a->note = i->samplenote[a->anote]; - } else - { a->note = a->anote; - s = &pf->samples[a->sample]; - } - - if(a->s != s) - { a->s = s; - a->newsamp = a->period; - } - - // channel or instrument determined panning ? - - a->panning = pf->panning[t]; - if(s->flags & SF_OWNPAN) - a->panning = s->panning; - else if((i != NULL) && (i->flags & IF_OWNPAN)) - a->panning = i->panning; - - a->handle = s->handle; - a->speed = s->speed; - - if(i != NULL) - { if(i->flags & IF_PITCHPAN) - a->panning += ((a->anote-i->pitpancenter) * i->pitpansep) / 8; - a->pitflg = i->pitflg; - a->volflg = i->volflg; - a->panflg = i->panflg; - a->nna = i->nnatype; - a->dca = i->dca; - a->dct = i->dct; - } else - { a->pitflg = 0; - a->volflg = 0; - a->panflg = 0; - a->nna = 0; - a->dca = 0; - a->dct = 0; - } - - if(funky & 2) - { // IT's random volume variations: 0:8 bit fixed, and one bit for sign. - a->volume = s->volume; - a->tmpvolume = s->volume; - if((s != NULL) && (i != NULL)) - { a->volume = a->tmpvolume = s->volume + ((s->volume * ((SLONG)i->rvolvar * (SLONG)((rand() & 511)-255))) / 25600); - if(a->panning != PAN_SURROUND) a->panning += ((a->panning * ((SLONG)i->rpanvar * (SLONG)((rand() & 511)-255))) / 25600); - } - } - - period = GetPeriod(a->note, a->speed); - a->wantedperiod = period; - a->tmpperiod = period; - a->keyoff = KEY_KICK; - } - } - } - } - - // Update effects - - for(t=0; tnumchn; t++) - { mp_channel = t; - a = &pf->control[t]; - - if((aout = a->slave) != NULL) - { a->fadevol = aout->fadevol; - a->period = aout->period; - if(a->kick != 1) a->keyoff = aout->keyoff; - } - - if(a->row == NULL) continue; - UniSetRow(a->row); - - a->ownper = a->ownvol = 0; - pt_playeffects(); - if(!a->ownper) a->period = a->tmpperiod; - if(!a->ownvol) a->volume = a->tmpvolume; - - if(a->s != NULL) - { if(a->i != NULL) - a->outvolume = (a->volume * a->s->globvol * a->i->globvol) / 1024; // max val: 256 - else - a->outvolume = (a->volume * a->s->globvol) / 16; // max val: 256 - if(a->outvolume > 256) a->volume = 256; - } - } - - - a = pf->control; - if(pf->flags & UF_NNA) - { for(t=0; tnumchn; t++, a++) - { if(a->kick == 1) - { if(a->slave != NULL) - { aout = a->slave; - if(aout->nna & 0x3f) - { // oh boy, we have to do an NNA - // 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->nna) - { case NNA_CONTINUE: - break; // continue note, do nothing - - case NNA_OFF: // note off - aout->keyoff |= KEY_OFF; - if(!(aout->volflg & EF_ON) || (aout->volflg & EF_LOOP)) - aout->keyoff = KEY_KILL; - break; - - case NNA_FADE: - aout->keyoff |= KEY_FADE; - break; - } - } - } - - k = 0; - if(a->dct != DCT_OFF) - { for(t2=0; t2voice[t2].masterchn == t) && (a->sample == pf->voice[t2].sample)) - { switch(a->dct) - { case DCT_NOTE: - if(a->note == pf->voice[t2].note) - k = 1; - break; - - case DCT_SAMPLE: - if(a->handle == pf->voice[t2].handle) - k = 1; - break; - - case DCT_INST: - k = 1; - break; - } - - if(k==1) - { k = 0; - switch(a->dca) - { case DCA_CUT : - pf->voice[t2].fadevol = 0; - a->slave = &pf->voice[a->slavechn=t2]; - break; - - case DCA_OFF : - //a->slave = &pf->voice[newchn]; - pf->voice[t2].keyoff |= KEY_OFF; - if(!(pf->voice[t2].volflg & EF_ON) || (pf->voice[t2].volflg & EF_LOOP)) - pf->voice[t2].keyoff = KEY_KILL; - break; - - case DCA_FADE: - //a->slave = &pf->voice[newchn]; - pf->voice[t2].keyoff |= KEY_FADE; - break; - } - } - } - } - } // DCT checking - } // if a->kick - } // for loop - } - - a = pf->control; - for(t=0; tnumchn; t++, a++) - { int newchn; - - if(a->notedelay) continue; - - if(a->kick == 1) - { // If no channel was cut above, find an empty or quiet channel here - if(pf->flags & UF_NNA) - { if(a->slave==NULL) - { if((newchn = MP_FindEmptyChannel(t)) != -1) - a->slave = &pf->voice[a->slavechn=newchn]; - } - } else - a->slave = &pf->voice[a->slavechn=t]; - - // Assign parts of MP_VOICE only done for a KICK! - - if((aout = a->slave) != NULL) - { if(aout->mflag && (aout->master!=NULL)) aout->master->slave = NULL; - a->slave = aout; - aout->master = a; - aout->masterchn = t; - aout->mflag = 1; - } - } else - { aout = a->slave; - } - - if(aout != NULL) - { aout->kick = a->kick; - aout->i = a->i; - aout->s = a->s; - aout->sample = a->sample; - aout->handle = a->handle; - aout->period = a->period; - aout->panning = a->panning; - aout->chanvol = a->chanvol; - aout->fadevol = a->fadevol; - aout->start = a->start; - aout->volflg = a->volflg; - aout->panflg = a->panflg; - aout->pitflg = a->pitflg; - aout->volume = a->outvolume; - aout->keyoff = a->keyoff; - aout->note = a->note; - aout->nna = a->nna; - } - a->kick = 0; - - } - - // Now set up the actual hardware channel playback information - - for(t=0; tvoice[mp_channel = t]; - i = aout->i; - s = aout->s; - - if(s==NULL) continue; - - if(aout->period < 40) aout->period = 40; - if(aout->period > 50000) aout->period = 50000; - - if(aout->kick) - { Voice_Play(t,s,(aout->start == -1) ? ((s->flags & SF_UST_LOOP) ? s->loopstart : 0) : aout->start); - //aout->keyoff = KEY_KICK; - aout->fadevol = 32768; - aout->aswppos = 0; - - if((i != NULL) && (aout->kick != 2)) - { StartEnvelope(&aout->venv, aout->volflg, i->volpts, i->volsusbeg, i->volsusend, i->volbeg, i->volend, i->volenv, aout->keyoff); - StartEnvelope(&aout->penv, aout->panflg, i->panpts, i->pansusbeg, i->pansusend, i->panbeg, i->panend, i->panenv, aout->keyoff); - StartEnvelope(&aout->cenv, aout->pitflg, i->pitpts, i->pitsusbeg, i->pitsusend, i->pitbeg, i->pitend, i->pitenv, aout->keyoff); - } - aout->kick = 0; - } - - if(i != NULL) - { envvol = ProcessEnvelope(&aout->venv,256,aout->keyoff); - envpan = ProcessEnvelope(&aout->penv,128,aout->keyoff); - envpit = ProcessEnvelope(&aout->cenv,32,aout->keyoff); - } - - tmpvol = aout->fadevol; // max 32768 - tmpvol *= aout->chanvol; // * max 64 - tmpvol *= aout->volume; // * max 256 - tmpvol /= 16384L; // tmpvol is max 32768 - aout->totalvol = tmpvol>>2; // totalvolume used to determine samplevolume - tmpvol *= envvol; // * max 256 - tmpvol *= pf->volume; // * max 128 - tmpvol /= 4194304UL; - - if((aout->masterchn != -1) && pf->control[aout->masterchn].muted) // Channel Muting Line - Voice_SetVolume(t,0); - else - Voice_SetVolume(t,tmpvol); - - - if(aout->panning == PAN_SURROUND) - Voice_SetPanning(t, PAN_SURROUND); - else - { if(aout->penv.flg & EF_ON) - Voice_SetPanning(t,DoPan(envpan,aout->panning)); - else - Voice_SetPanning(t,aout->panning); - } - - if(aout->period && s->vibdepth) - { switch(s->vibtype) - { case 0: - vibval = avibtab[s->avibpos & 127]; - if(s->avibpos & 0x80) vibval=-vibval; - break; - - case 1: - vibval = 64; - if(s->avibpos & 0x80) vibval=-vibval; - break; - - case 2: - vibval = 63-(((s->avibpos + 128) & 255) >> 1); - break; - - case 3: - vibval = (((s->avibpos + 128) & 255) >> 1) - 64; - break; - } - } - - if(s->vibflags & AV_IT) - { if((aout->aswppos >> 8) < s->vibdepth) - { aout->aswppos += s->vibsweep; - vibdpt = aout->aswppos; - } else - vibdpt = s->vibdepth << 8; - vibval = (vibval*vibdpt) >> 16; - if(aout->mflag) - { if(!(pf->flags & UF_LINEAR)) vibval>>=1; - aout->period -= vibval; - } - } else // do XM style auto-vibrato - { if(!(aout->keyoff & KEY_OFF)) - { if(aout->aswppos < s->vibsweep) - { vibdpt = (aout->aswppos*s->vibdepth) / s->vibsweep; - aout->aswppos++; - } else - vibdpt = s->vibdepth; - } else - { // key-off -> 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->period -= vibval; - } - - // update vibrato position - s->avibpos = (s->avibpos+s->vibrate) & 0xff; - - if(aout->cenv.flg & EF_ON) - { envpit = envpit-32; - aout->period -= envpit; - } - - Voice_SetFrequency(t,getfrequency(pf->flags,aout->period)); - - if(aout->fadevol == 0) // check for a dead note (fadevol = 0) - Voice_Stop(t); - else - { // if keyfade, start substracting - // fadeoutspeed from fadevol: - - if((i != NULL) && (aout->keyoff & KEY_FADE)) - { if(aout->fadevol >= i->volfade) - aout->fadevol -= i->volfade; - else - aout->fadevol = 0; - } - } - - MD_SetBPM(pf->bpm); - } -} - - -BOOL Player_Init(UNIMOD *mf) -{ - int t; - - mf->extspd = 1; - mf->panflag = 1; - mf->loop = 0; - - mf->pat_reppos = 0; - mf->pat_repcnt = 0; - mf->sngpos = 0; - mf->sngspd = mf->initspeed; - mf->volume = mf->initvolume; - - mf->vbtick = mf->sngspd; - mf->patdly = 0; - mf->patdly2 = 0; - mf->bpm = mf->inittempo; - - mf->patpos = 0; - mf->posjmp = 2; // <- make sure the player fetches the first note - mf->patbrk = 0; - - // Make sure the player doesn't start with garbage: - - if((mf->control=(MP_CONTROL *)_mm_calloc(mf->numchn,sizeof(MP_CONTROL)))==NULL) return 1; - if((mf->voice=(MP_VOICE *)_mm_calloc(md_sngchn,sizeof(MP_VOICE)))==NULL) return 1; - - for(t=0; tnumchn; t++) - { mf->control[t].chanvol = mf->chanvol[t]; - mf->control[t].panning = mf->panning[t]; - } - - return 0; -} - - -void Player_Exit(UNIMOD *mf) -{ - if(mf==NULL) return; - if(mf==pf) - { Player_Stop(); - pf = NULL; - } - if(mf->control!=NULL) free(mf->control); - if(mf->voice!=NULL) free(mf->voice); - mf->control = NULL; - mf->voice = NULL; - -} - - -void Player_SetVolume(int volume) -{ - if(pf==NULL) return; - - if(volume > 128) volume = 128; - if(volume < 0) volume = 0; - - pf->volume = volume; -} - - -UNIMOD *Player_GetUnimod(void) -{ - return pf; -} - - -void Player_Start(UNIMOD *mf) -{ - int t; - - if(!MikMod_Active()) - { isfirst = 2; - MikMod_EnableOutput(); - } - - if(mf==NULL) return; - - mf->forbid = 0; - if(pf != mf) - { // new song is being started, so completely stop out the old one. - if(pf!=NULL) pf->forbid = 1; - for(t=0; tforbid = 1; - pf = NULL; -} - - -BOOL MP_Playing(UNIMOD *mf) -{ - if((mf==NULL) || (mf!=pf)) return 0; - return(!(mf->sngpos>=mf->numpos)); -} - - -BOOL Player_Active(void) -{ - if(pf==NULL) return 0; - return(!(pf->sngpos>=pf->numpos)); -} - - -void MP_NextPosition(UNIMOD *mf) -{ - int t; - - if(mf==NULL) return; - mf->forbid = 1; - mf->posjmp = 3; - mf->patbrk = 0; - mf->vbtick = mf->sngspd; - - for(t=0; tvoice[t].i = NULL; - mf->voice[t].s = NULL; - } - - for(t=0; tnumchn; t++) - { mf->control[t].i = NULL; - mf->control[t].s = NULL; - } - mf->forbid = 0; -} - - -void Player_NextPosition(void) -{ - MP_NextPosition(pf); -} - - -void MP_PrevPosition(UNIMOD *mf) -{ - int t; - - if(mf==NULL) return; - mf->forbid = 1; - mf->posjmp = 1; - mf->patbrk = 0; - mf->vbtick = mf->sngspd; - - for(t=0; tvoice[t].i = NULL; - mf->voice[t].s = NULL; - } - - for(t=0; tnumchn; t++) - { mf->control[t].i = NULL; - mf->control[t].s = NULL; - } - - mf->forbid = 0; -} - - -void Player_PrevPosition(void) -{ - MP_PrevPosition(pf); -} - - -void MP_SetPosition(UNIMOD *mf, UWORD pos) -{ - int t; - - if(mf==NULL) return; - mf->forbid = 1; - if(pos>=mf->numpos) pos = mf->numpos; - mf->posjmp = 2; - mf->patbrk = 0; - mf->sngpos = pos; - mf->vbtick = mf->sngspd; - - for(t=0; tvoice[t].i = NULL; - mf->voice[t].s = NULL; - } - - for(t=0; tnumchn; t++) - { mf->control[t].i = NULL; - mf->control[t].s = NULL; - } - - mf->forbid = 0; -} - - -void Player_SetPosition(UWORD pos) -{ - MP_SetPosition(pf,pos); -} - - -void MP_Unmute(UNIMOD *mf, SLONG arg1, ...) -{ - va_list ap; - SLONG t, arg2, arg3; - - va_start(ap,arg1); - - if(mf != NULL) - { switch(arg1) - { case MUTE_INCLUSIVE: - if((!(arg2=va_arg(ap,SLONG))) && (!(arg3=va_arg(ap,SLONG))) || (arg2 > arg3) || (arg3 >= mf->numchn)) - { va_end(ap); - return; - } - for(;arg2numchn && arg2<=arg3;arg2++) - mf->control[arg2].muted = 0; - break; - - case MUTE_EXCLUSIVE: - if((!(arg2=va_arg(ap,SLONG))) && (!(arg3=va_arg(ap,SLONG))) || (arg2 > arg3) || (arg3 >= mf->numchn)) - { va_end(ap); - return; - } - for(t=0;tnumchn;t++) - { if ((t>=arg2) && (t<=arg3)) continue; - mf->control[t].muted = 0; - } - break; - - default: - if(arg1numchn) mf->control[arg1].muted = 0; - break; - } - } - va_end(ap); - - return; -} - - -void Player_Unmute(SLONG arg1, ...) -{ - va_list argptr; - MP_Unmute(pf,arg1, argptr); - va_end(argptr); -} - - -void MP_Mute(UNIMOD *mf, SLONG arg1, ...) -{ - va_list ap; - SLONG t, arg2, arg3; - - va_start(ap,arg1); - - if(mf != NULL) - { switch (arg1) - { case MUTE_INCLUSIVE: - if ((!(arg2=va_arg(ap,SLONG))) && (!(arg3=va_arg(ap,SLONG))) || (arg2 > arg3) || (arg3 >= mf->numchn)) - { va_end(ap); - return; - } - for(;arg2numchn && arg2<=arg3;arg2++) - mf->control[arg2].muted = 1; - break; - - case MUTE_EXCLUSIVE: - if ((!(arg2=va_arg(ap,SLONG))) && (!(arg3=va_arg(ap,SLONG))) || (arg2 > arg3) || (arg3 >= mf->numchn)) - { va_end(ap); - return; - } - for (t=0; tnumchn; t++) - { if ((t>=arg2) && (t<=arg3)) continue; - mf->control[t].muted = 1; - } - break; - - default: - if(arg1numchn) - mf->control[arg1].muted = 1; - break; - } - } - va_end(ap); - - return; -} - - -void Player_Mute(SLONG arg1, ...) -{ - va_list argptr; - MP_Mute(pf,arg1, argptr); - va_end(argptr); -} - - -void MP_ToggleMute(UNIMOD *mf, SLONG arg1, ...) -{ - va_list ap; - SLONG arg2, arg3; - ULONG t; - - va_start(ap,arg1); - - if(mf != NULL) - { switch (arg1) - { case MUTE_INCLUSIVE: - if ((!(arg2=va_arg(ap,SLONG))) && (!(arg3=va_arg(ap,SLONG))) || (arg2 > arg3) || (arg3 >= mf->numchn)) - { va_end(ap); - return; - } - for(; arg2numchn && arg2<=arg3; arg2++) - mf->control[arg2].muted = (mf->control[arg2].muted) ? 0 : 1; - - break; - - case MUTE_EXCLUSIVE: - if ((!(arg2=va_arg(ap,SLONG))) && (!(arg3=va_arg(ap,SLONG))) || (arg2 > arg3) || (arg3 >= mf->numchn)) - { va_end(ap); - return; - } - for (t=0;tnumchn;t++) - { if((t>=arg2) && (t<=arg3)) continue; - mf->control[t].muted = (mf->control[t].muted) ? 0 : 1; - } - break; - - default: - if(arg1numchn) - mf->control[arg1].muted = (mf->control[arg1].muted) ? 0 : 1; - break; - } - } - va_end(ap); - - return; -} - - -void Player_ToggleMute(SLONG arg1, ...) -{ - va_list argptr; - MP_ToggleMute(pf,arg1, argptr); - va_end(argptr); -} - - -BOOL MP_Muted(UNIMOD *mf, int chan) -{ - if(mf==NULL) return 1; - return (channumchn) ? mf->control[chan].muted : 1; -} - - -BOOL Player_Muted(int chan) -{ - if(pf==NULL) return 1; - return (channumchn) ? pf->control[chan].muted : 1; -} - - -int MP_GetChannelVoice(UNIMOD *mf, int chan) -{ - if(mf==NULL) return 0; - return mf->control[chan].slavechn; -} - - -int Player_GetChannelVoice(int chan) -{ - if(pf==NULL) return 0; - return pf->control[chan].slavechn; -} - - -void Player_TogglePause(void) -{ - if(pf==NULL) return; - if(pf->forbid == 1) - pf->forbid = 0; - else - pf->forbid = 1; -} - - -// --> The following procedures were taken from UNITRK because they -// -> are ProTracker format specific. - -void UniInstrument(UBYTE ins) -// Appends UNI_INSTRUMENT opcode to the unitrk stream. -{ - UniWrite(UNI_INSTRUMENT); - UniWrite(ins); -} - -void UniNote(UBYTE note) -// Appends UNI_NOTE opcode to the unitrk stream. -{ - UniWrite(UNI_NOTE); - UniWrite(note); -} - -void UniPTEffect(UBYTE eff, UBYTE dat) -// Appends UNI_PTEFFECTX opcode to the unitrk stream. -{ - if(eff!=0 || dat!=0) // don't write empty effect - { UniWrite(UNI_PTEFFECT0+eff); - UniWrite(dat); - } -} - -void UniVolEffect(UWORD eff, UBYTE dat) -// Appends UNI_VOLEFFECT + effect/dat to unistream. -{ - if(eff!=0 || dat!=0) // don't write empty effect - { UniWrite(UNI_VOLEFFECTS); - UniWrite(eff); UniWrite(dat); - } -} - +/* + * --> The Protracker Player Driver -> Part of the SPLAYER pack for MikMod + * 3.0 + * + * The protracker driver supports all base Protracker 3.x commands and fea- + * tures. + */ + +#include +#include +#include "mikmod.h" + + +static void DoNNAEffects(UBYTE dat); + +/* + * Set forbid to 1 when you want to modify any of the pf->sngpos, pf->patpos + * etc. variables and clear it when you're done. This prevents getting + * strange results due to intermediate interrupts. + */ + +static UNIMOD *pf; /* <- this modfile is being played */ +static SWORD mp_channel; /* channel it's working on */ +static MP_CONTROL *a; /* current AUDTMP it's working on */ +static int isfirst; + +static MP_VOICE aout_dummy; + +static UWORD mytab[12] = +{ + 1712 * 16, 1616 * 16, 1524 * 16, 1440 * 16, 1356 * 16, 1280 * 16, + 1208 * 16, 1140 * 16, 1076 * 16, 1016 * 16, 960 * 16, 907 * 16 +}; + +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 * Fast + * Tracker 2 [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 +}; + + +/* + * 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 forground channel is a bonus x4 4) an active + * envelope with keyoff is a handicap -x2 + */ + +static int +MP_FindEmptyChannel(int curchan) +{ /* returns mp_control index of free channel */ + MP_VOICE *a; + ULONG t, k, tvol, p, pp; + + /* + * for(t=md_sngchn; t; t--, audpool++) { if(audpool == md_sngchn) + * audpool = 0; if(!(pf->voice[audpool].kick) && Voice_Stopped(audpool)) + * { audpool++; return audpool-1; } } + */ + + for (t = 0; t < md_sngchn; t++) { + if (!(pf->voice[t].kick) && Voice_Stopped(t)) { + return t; + } + } + + tvol = 0xffffff UL; + t = 0; + p = 0; + a = pf->voice; + for (k = 0; k < md_sngchn; k++, a++) { + if (!a->kick) { + pp = a->totalvol << ((a->s->flags & SF_LOOP) ? 1 : 0); + if ((a->master != NULL) && (a == a->master->slave)) + pp <<= 2; + + /* + * if(a->volflg & EF_ON) { if(a->volflg & (EF_SUSTAIN | + * EF_LOOP)) { if(a->keyoff & KEY_OFF) { pp >>= 1; + * if(a->venv.env[a->venv.end].val < 32) pp>>=1; } else pp <<= 1; + * } else pp <<= 1; } + */ + + if (pp < tvol) { + tvol = pp; + t = k; + } + } + } + + if (tvol > (8000 * 7)) + return -1; /* mp_channel; */ + + return t; +} + + +static SWORD +Interpolate(SWORD p, SWORD p1, SWORD p2, SWORD v1, SWORD v2) +{ + SWORD dp, dv, di; + + if (p1 == p2) + return v1; + + dv = v2 - v1; + dp = p2 - p1; + di = p - p1; + + return v1 + ((SLONG) (di * dv) / dp); +} + + +UWORD +getlinearperiod(UBYTE note, ULONG fine) +{ + return ((10L * 12 * 16 * 4) - ((ULONG) note * 16 * 4) - (fine / 2) + 64); +} + + +static UWORD +getlogperiod(UBYTE note, ULONG fine) +{ + UBYTE n, o; + UWORD p1, p2; + ULONG i; + + n = note % 12; + o = note / 12; + i = (n << 3) + (fine >> 4); /* n*8 + fine/16 */ + + p1 = logtab[i]; + p2 = logtab[i + 1]; + + return (Interpolate(fine / 16, 0, 15, p1, p2) >> o); +} + + +static UWORD +getoldperiod(UBYTE note, ULONG speed) +{ + UBYTE n, o; + ULONG period; + + if (!speed) + return 4242; /* <- prevent divide overflow.. (42 eheh) */ + + n = note % 12; + o = note / 12; + period = ((8363l * (ULONG) mytab[n]) >> o) / speed; + return period; +} + + +static UWORD +GetPeriod(UBYTE note, ULONG speed) +{ + if (pf->flags & UF_XMPERIODS) { + return (pf->flags & UF_LINEAR) ? + getlinearperiod(note, speed) : getlogperiod(note, speed); + } + + 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) +{ + return (pan + (((envpan - 128) * (128 - abs(pan - 128))) / 128)); +} + + +static void +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; +} + + +/* + * 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, panniong 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. 1. + * 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(ENVPR * t, SWORD v, UBYTE keyoff) +{ + 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; + + /* compute the current envelope value between points a and b */ + + if (a == b) + v = t->env[a].val; + else + v = InterpolateEnv(p, &t->env[a], &t->env[b]); + + p++; + + /* pointer reached point b? */ + + if (p >= t->env[b].pos) { + a = b++; /* shift points a and b */ + + /* Check for loops, sustain loops, or end of envelope. */ + + if ((t->flg & EF_SUSTAIN) && !(keyoff & KEY_OFF) && (b > t->susend)) { + a = t->susbeg; + if (t->susbeg == t->susend) + b = a; + else + b = a + 1; + p = t->env[a].pos; + } else if ((t->flg & EF_LOOP) && (b > t->end)) { + a = t->beg; + if (t->beg == t->end) + b = a; + else + b = a + 1; + p = t->env[a].pos; + } else { + if (b >= t->pts) { + if ((t->flg & EF_VOLENV) && (mp_channel != -1)) { + pf->voice[mp_channel].keyoff |= KEY_FADE; + if (v == 0) + pf->voice[mp_channel].fadevol = 0; + } + b--; + p--; + } + } + } + t->a = a; + t->b = b; + t->p = p; + } + return v; +} + + +/* XM linear period to frequency conversion */ +ULONG +getfrequency(UBYTE flags, ULONG period) +{ + ULONG result; + + if (flags & UF_LINEAR) + result = lintab[period % 768] >> (period / 768); + else + result = (8363L * 1712L) / period; + + return result; +} + + +static void +DoEEffects(UBYTE dat) +{ + UBYTE nib; + + nib = dat & 0xf; + + switch (dat >> 4) { + case 0x0: /* filter toggle, not supported */ + break; + + case 0x1: /* fineslide up */ + if (!pf->vbtick) + a->tmpperiod -= (nib << 2); + break; + + case 0x2: /* fineslide dn */ + if (!pf->vbtick) + 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 */ + /* a->speed=finetune[nib]; */ + /* + * + * a->tmpperiod=GetPeriod(a->note,pf->samples[a->sample].transpose,a-> + * speed); + */ + break; + + case 0x6: /* set patternloop */ + if (pf->vbtick) + break; + /* + * hmm.. this one is a real kludge. But now it works + */ + if (nib) { /* set reppos or repcnt ? set repcnt, so + * check if repcnt already is set, which + * means we are already looping */ + + if (pf->pat_repcnt > 0) + pf->pat_repcnt--; /* already looping, decrease counter */ + else + pf->pat_repcnt = nib; /* not yet looping, so set repcnt */ + + if (pf->pat_repcnt) /* jump to reppos if repcnt>0 */ + pf->patpos = pf->pat_reppos; + } else { + pf->pat_reppos = pf->patpos - 1; /* set reppos */ + } + break; + + case 0x7: /* set tremolo waveform */ + a->wavecontrol &= 0x0f; + a->wavecontrol |= nib << 4; + break; + + case 0x8: /* set panning */ + if (pf->panflag) { + if (nib <= 8) + nib *= 16; + else + nib *= 17; + a->panning = nib; + pf->panning[mp_channel] = nib; + } + break; + + case 0x9: /* retrig note */ + /* + * only retrigger if data nibble > 0 + */ + + if (nib > 0) { + if (a->retrig == 0) { /* when retrig counter reaches 0, + * reset counter and restart the + * sample */ + a->kick = 1; + a->retrig = nib; + } + a->retrig--; /* countdown */ + } + break; + + case 0xa: /* fine volume slide up */ + if (pf->vbtick) + break; + + a->tmpvolume += nib; + if (a->tmpvolume > 64) + a->tmpvolume = 64; + break; + + case 0xb: /* fine volume slide dn */ + if (pf->vbtick) + break; + + a->tmpvolume -= nib; + if (a->tmpvolume < 0) + a->tmpvolume = 0; + break; + + case 0xc: /* cut note */ + /* + * When pf->vbtick reaches the cut-note value, turn the volume to + * zero ( Just like on the amiga) + */ + if (pf->vbtick >= nib) + a->tmpvolume = 0; /* just turn the volume down */ + break; + + case 0xd: /* note delay */ + /* + * delay the start of the sample until pf->vbtick==nib + */ + if (pf->vbtick == nib) {/* a->kick = 1; */ + a->notedelay = 0; + } else if (pf->vbtick == 0) { /* a->kick = 0; */ + a->notedelay = 1; + } + break; + + case 0xe: /* pattern delay */ + if (pf->vbtick) + break; + if (!pf->patdly2) + pf->patdly = nib + 1; /* only once (when pf->vbtick = 0) */ + break; + + case 0xf: /* invert loop, not supported */ + break; + } +} + + +static void +DoVibrato(void) +{ + UBYTE q; + UWORD temp; + + 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: /* Evil random wave */ + temp = rand() & 255; + break; + } + + temp *= a->vibdepth; + temp >>= 7; + temp <<= 2; + + if (a->vibpos >= 0) + a->period = a->tmpperiod + temp; + else + a->period = a->tmpperiod - temp; + + if (pf->vbtick) + a->vibpos += a->vibspd; /* do not update when pf->vbtick==0 */ +} + + +static void +DoTremolo(void) +{ + UBYTE q; + UWORD temp; + + 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: /* Evil random wave */ + temp = rand() & 255; + 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; + } + + if (pf->vbtick) + a->trmpos += a->trmspd; /* do not update when pf->vbtick == 0 */ +} + + +static void +DoVolSlide(UBYTE dat) +{ + if (!pf->vbtick) + return; /* do not update when pf->vbtick == 0 */ + + a->tmpvolume += dat >> 4; /* volume slide */ + a->tmpvolume -= dat & 0xf; + if (a->tmpvolume < 0) + a->tmpvolume = 0; + if (a->tmpvolume > 64) + a->tmpvolume = 64; +} + + +static void +DoToneSlide(void) +{ + int dist; + + if (a->period == 0) + return; + + if (!pf->vbtick) { + a->tmpperiod = a->period; + return; + } + /* + * We have to slide a->period towards a->wantedperiod, so compute the + * difference between those two values + */ + + dist = a->period - a->wantedperiod; + + if (dist == 0 || a->portspeed > abs(dist)) /* if they are equal or if + * portamentospeed is too big */ + a->period = a->wantedperiod; /* make tmpperiod equal tperiod */ + else if (dist > 0) /* dist > 0 ? */ + a->period -= a->portspeed; /* then slide up */ + else + a->period += a->portspeed; /* dist < 0 -> slide down */ + + a->tmpperiod = a->period; +} + + +static void +DoPTEffect0(UBYTE dat) +{ + UBYTE note; + + note = a->note; + + if (dat != 0) { + switch (pf->vbtick % 3) { + case 1: + note += (dat >> 4); + break; + case 2: + note += (dat & 0xf); + break; + } + a->period = GetPeriod(note, a->speed); + a->ownper = 1; + } +} + + +/* + * ----------------------------------------- --> ScreamTreacker 3 Specific + * Effects <-- ----------------------------------------- + */ +static void +DoS3MVolSlide(UBYTE inf) +{ + UBYTE lo, hi; + + if (inf) + a->s3mvolslide = inf; + + inf = a->s3mvolslide; + lo = inf & 0xf; + hi = inf >> 4; + + if (hi == 0) + a->tmpvolume -= lo; + else if (lo == 0) + a->tmpvolume += hi; + else if (hi == 0xf) { + if (!pf->vbtick) + a->tmpvolume -= lo; + } else if (lo == 0xf) { + if (!pf->vbtick) + a->tmpvolume += hi; + } + if (a->tmpvolume < 0) + a->tmpvolume = 0; + if (a->tmpvolume > 64) + a->tmpvolume = 64; +} + + +static void +DoS3MSlideDn(UBYTE inf) +{ + UBYTE hi, lo; + + if (inf != 0) + a->slidespeed = inf; + else + inf = a->slidespeed; + + hi = inf >> 4; + lo = inf & 0xf; + + if (hi == 0xf) { + if (!pf->vbtick) + a->tmpperiod += (UWORD) lo << 2; + } else if (hi == 0xe) { + if (!pf->vbtick) + a->tmpperiod += lo; + } else { + if (pf->vbtick) + a->tmpperiod += (UWORD) inf << 2; + } +} + + +static void +DoS3MSlideUp(UBYTE inf) +{ + UBYTE hi, lo; + + if (inf != 0) + a->slidespeed = inf; + else + inf = a->slidespeed; + + hi = inf >> 4; + lo = inf & 0xf; + + if (hi == 0xf) { + if (!pf->vbtick) + a->tmpperiod -= (UWORD) lo << 2; + } else if (hi == 0xe) { + if (!pf->vbtick) + a->tmpperiod -= lo; + } else { + if (pf->vbtick) + a->tmpperiod -= (UWORD) inf << 2; + } +} + + +static void +DoS3MTremor(UBYTE inf) +{ + UBYTE on, off; + + if (inf != 0) + a->s3mtronof = inf; + else + inf = a->s3mtronof; + + if (!pf->vbtick) + return; + + on = (inf >> 4) + 1; + off = (inf & 0xf) + 1; + + a->s3mtremor %= (on + off); + a->volume = (a->s3mtremor < on) ? a->tmpvolume : 0; + a->s3mtremor++; +} + + +static void +DoS3MRetrig(UBYTE inf) +{ + UBYTE hi, lo; + + hi = inf >> 4; + lo = inf & 0xf; + + if (inf) { + a->s3mrtgslide = hi; + a->s3mrtgspeed = lo; + } + /* + * only retrigger if lo nibble > 0 + */ + + if (a->s3mrtgspeed > 0) { + if (a->retrig == 0) { /* when retrig counter reaches 0, reset + * counter and restart the sample */ + + if (!a->kick) + a->kick = 2; + a->retrig = a->s3mrtgspeed; + + if (pf->vbtick) { /* don't slide on first retrig */ + 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 = 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) / 2; + break; + + case 0xf: + a->tmpvolume = a->tmpvolume << 1; + break; + } + if (a->tmpvolume < 0) + a->tmpvolume = 0; + if (a->tmpvolume > 64) + a->tmpvolume = 64; + } + } + a->retrig--; /* countdown */ + } +} + + +static void +DoS3MSpeed(UBYTE speed) +{ + if (pf->vbtick || pf->patdly2) + return; + + if (speed) { + pf->sngspd = speed; + pf->vbtick = 0; + } +} + + +static void +DoS3MTempo(UBYTE tempo) +{ + if (pf->vbtick || pf->patdly2) + return; + pf->bpm = tempo; +} + + +static void +DoS3MFineVibrato(void) +{ + UBYTE q; + UWORD temp; + + 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: /* evil random */ + temp = rand() & 255; /* (range 0 to 255) */ + } + + temp *= a->vibdepth; + temp >>= 8; + + if (a->vibpos >= 0) + a->period = a->tmpperiod + temp; + else + a->period = a->tmpperiod - temp; + + a->vibpos += a->vibspd; +} + + +static void +DoS3MTremolo(void) +{ + UBYTE q; + UWORD temp; + + 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: /* evil random */ + temp = rand() & 255; /* (range 0 to 255) */ + } + + 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; + } + + if (pf->vbtick) + a->trmpos += a->trmspd; /* do not update when pf->vbtick == 0 */ +} + + +/* + * -------------------------------------- --> FastTracker 2 Specific Effects + * <-- -------------------------------------- + */ +static void +DoXMVolSlide(UBYTE inf) +{ + UBYTE lo, hi; + + if (inf) + a->s3mvolslide = inf; + + inf = a->s3mvolslide; + if (!pf->vbtick) + return; + + lo = inf & 0xf; + hi = inf >> 4; + + if (hi == 0) + a->tmpvolume -= lo; + else + a->tmpvolume += hi; + + if (a->tmpvolume < 0) + a->tmpvolume = 0; + else if (a->tmpvolume > 64) + a->tmpvolume = 64; +} + + +static void +DoXMGlobalSlide(UBYTE inf) +{ + if (pf->vbtick) { + if (inf) + pf->globalslide = inf; + else + inf = pf->globalslide; + if (inf & 0xf0) + inf &= 0xf0; + pf->volume = pf->volume + ((inf >> 4) - (inf & 0xf)) * 2; + + if (pf->volume < 0) + pf->volume = 0; + else if (pf->volume > 128) + pf->volume = 128; + } +} + + +static void +DoXMPanSlide(UBYTE inf) +{ + UBYTE lo, hi; + SWORD pan; + + + if (inf != 0) + a->pansspd = inf; + else + inf = a->pansspd; + + if (!pf->vbtick) + return; + + lo = inf & 0xf; + hi = inf >> 4; + + /* slide right has absolute priority: */ + + if (hi) + lo = 0; + + pan = (a->panning == PAN_SURROUND) ? 128 : a->panning; + + pan -= lo; + pan += hi; + + if (pan < 0) + pan = 0; + if (pan > 255) + pan = 255; + + a->panning = pan; +} + + +static void +DoXMExtraFineSlideUp(UBYTE inf) +{ + if (!pf->vbtick) { + if (inf) + a->ffportupspd = inf; + else + inf = a->ffportupspd; + a->period -= inf; + } + a->tmpperiod = a->period; +} + + +static void +DoXMExtraFineSlideDown(UBYTE inf) +{ + if (!pf->vbtick) { + if (inf) + a->ffportdnspd = inf; + else + inf = a->ffportdnspd; + a->period += inf; + } + a->tmpperiod = a->period; +} + + +/* + * --------------------------------------- --> ImpulseTracker Player + * Functions <-- --------------------------------------- + */ +static void +DoITChanVolSlide(UBYTE inf) +{ + UBYTE lo, hi; + + if (inf) + a->chanvolslide = inf; + inf = a->chanvolslide; + + lo = inf & 0xf; + hi = inf >> 4; + + if (hi == 0) { + a->chanvol -= lo; + } else if (lo == 0) { + a->chanvol += hi; + } else if (hi == 0xf) { + if (!pf->vbtick) + a->chanvol -= lo; + } else if (lo == 0xf) { + if (!pf->vbtick) + a->chanvol += hi; + } + if (a->chanvol < 0) + a->chanvol = 0; + if (a->chanvol > 64) + a->chanvol = 64; +} + + +static void +DoITGlobalSlide(UBYTE inf) +{ + UBYTE lo, hi; + + if (inf) + pf->globalslide = inf; + inf = pf->globalslide; + + lo = inf & 0xf; + hi = inf >> 4; + + if (lo == 0) { + pf->volume += hi; + } else if (hi == 0) { + pf->volume -= lo; + } else if (lo == 0xf) { + if (!pf->vbtick) + pf->volume += hi; + } else if (hi == 0xf) { + if (!pf->vbtick) + pf->volume -= lo; + } + if (pf->volume < 0) + pf->volume = 0; + if (pf->volume > 128) + pf->volume = 128; +} + + +static void +DoITPanSlide(UBYTE inf) +{ + UBYTE lo, hi; + SWORD pan; + + if (inf) + a->pansspd = inf; + inf = a->pansspd; + + lo = inf & 0xf; + hi = inf >> 4; + + pan = (a->panning == PAN_SURROUND) ? 128 : a->panning; + + if (hi == 0) { + pan += lo << 2; + } else if (lo == 0) { + pan -= hi << 2; + } else if (hi == 0xf) { + if (!pf->vbtick) + pan += lo << 2; + } else if (lo == 0xf) { + if (!pf->vbtick) + pan -= hi << 2; + } + if (pan > 255) + pan = 255; + if (pan < 0) + pan = 0; + a->panning = /* pf->panning[mp_channel] = */ pan; +} + + +static void +DoITVibrato(void) +{ + UBYTE q; + UWORD temp; + + 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: /* evil random */ + temp = rand() & 255; /* (range 0 to 255) */ + break; + } + + temp *= a->vibdepth; + temp >>= 8; + temp <<= 2; + + if (a->vibpos >= 0) + a->period = a->tmpperiod + temp; + else + a->period = a->tmpperiod - temp; + + a->vibpos += a->vibspd; +} + + +static void +DoITFineVibrato(void) +{ + UBYTE q; + UWORD temp; + + 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: /* evil random */ + temp = rand() & 255; /* (range 0 to 255) */ + break; + } + + temp *= a->vibdepth; + temp >>= 8; + + if (a->vibpos >= 0) + a->period = a->tmpperiod + temp; + else + a->period = a->tmpperiod - temp; + + a->vibpos += a->vibspd; +} + + +static void +DoITTremor(UBYTE inf) +{ + UBYTE on, off; + + if (inf != 0) + a->s3mtronof = inf; + else + inf = a->s3mtronof; + + if (!pf->vbtick) + return; + + on = (inf >> 4); + off = (inf & 0xf); + + a->s3mtremor %= (on + off); + a->volume = (a->s3mtremor < on) ? a->tmpvolume : 0; + a->s3mtremor++; +} + + +static void +DoITPanbrello(void) +{ + UBYTE q; + static SLONG temp; + + q = a->panbpos; + + switch (a->panbwave) { + case 0: /* sine */ + temp = PanbrelloTable[q]; + break; + + /* only sinewave is correctly supported right now */ + + case 1: /* ramp down */ + q <<= 3; + temp = q; + break; + + case 2: /* square wave */ + temp = 64; + break; + + case 3: /* evil random */ + if (a->panbpos >= a->panbspd) { + a->panbpos = 0; + temp = rand() & 255; + } + } + + temp *= a->panbdepth; + temp /= 8; + + a->panning = pf->panning[mp_channel] + temp; + a->panbpos += a->panbspd; +} + + +static void +DoITToneSlide(void) +{ + int dist; + + if (a->period == 0) + return; + + if (!pf->vbtick) { + a->tmpperiod = a->period; + return; + } + /* + * We have to slide a->period towards a->wantedperiod, compute the + * difference between those two values + */ + + dist = a->period - a->wantedperiod; + + if ((dist == 0) || /* if they are equal */ + ((a->slidespeed << 2) > abs(dist))) { /* or if portamentospeed is + * too big */ + a->period = a->wantedperiod; /* make tmpperiod equal tperiod */ + } else if (dist > 0) { /* dist > 0 ? */ + a->period -= a->slidespeed << 2; /* then slide up */ + } else { + a->period += a->slidespeed << 2; /* dist<0 -> slide down */ + } + a->tmpperiod = a->period; +} + + +/* + * Impulse/Scream Tracker Sxx effects. All Sxx effects share the same memory + * space. + */ +static void +DoSSEffects(UBYTE dat) +{ + UBYTE inf, c; + + inf = dat & 0xf; + c = dat >> 4; + + if (dat == 0) { + c = a->sseffect; + inf = a->ssdata; + } else { + a->sseffect = c; + a->ssdata = inf; + } + + switch (c) { + case SS_GLISSANDO: /* S1x set glissando voice */ + DoEEffects(0x30 | inf); + break; + + case SS_FINETUNE: /* S2x set finetune */ + DoEEffects(0x50 | inf); + break; + + case SS_VIBWAVE: /* S3x set vibrato waveform */ + DoEEffects(0x40 | inf); + break; + + case SS_TREMWAVE: /* S4x set tremolo waveform */ + DoEEffects(0x70 | inf); + break; + + case SS_PANWAVE: /* The Satanic Panbrello waveform */ + a->panbwave = (UniGetByte()); + break; + + case SS_FRAMEDELAY: /* S6x Delay x number of frames (patdly) */ + DoEEffects(0xe0 | inf); + break; + + case SS_S7EFFECTS: /* S7x Instrument / NNA commands */ + DoNNAEffects(UniGetByte()); + break; + + case SS_PANNING: /* S8x set panning position */ + DoEEffects(0x80 | inf); + break; + + case SS_SURROUND: /* S9x Set Surround Sound */ + a->panning = pf->panning[mp_channel] = PAN_SURROUND; + break; + + case SS_HIOFFSET: /* SAy Set high order sample offset yxx00h */ + a->hioffset |= UniGetByte() << 16; + break; + + case SS_PATLOOP: /* SBx pattern loop */ + DoEEffects(0x60 | inf); + break; + + case SS_NOTECUT: /* SCx notecut */ + DoEEffects(0xC0 | inf); + break; + + case SS_NOTEDELAY: /* SDx notedelay */ + DoEEffects(0xD0 | inf); + break; + + case SS_PATDELAY: /* SEx patterndelay */ + DoEEffects(0xE0 | inf); + break; + } +} + + +/* + * Impulse Tracker Volume/Pan Column effects. All volume/pan column effects + * share the same memory space. + */ +static void +DoVolEffects(UBYTE c) +{ + UBYTE inf; + + inf = UniGetByte(); + + if (c == 0 && inf == 0) { + c = a->voleffect; + inf = a->voldata; + } else { + a->voleffect = c; + a->voldata = inf; + } + + switch (c) { + case 0: + break; /* do nothing */ + case VOL_VOLUME: + if (pf->vbtick) + break; + if (inf > 64) + inf = 64; + a->tmpvolume = inf; + break; + + case VOL_PANNING: + if (pf->panflag) { + a->panning = inf; + pf->panning[mp_channel] = inf; + } + break; + + case VOL_VOLSLIDE: + DoS3MVolSlide(inf); + break; + + case VOL_PITCHSLIDEDN: + DoS3MSlideDn(UniGetByte()); + break; + + case VOL_PITCHSLIDEUP: + DoS3MSlideUp(UniGetByte()); + break; + + case VOL_PORTAMENTO: + if (inf != 0) + a->slidespeed = inf; + + if (a->period != 0) { + if (!(pf->vbtick == pf->sngspd - 1) && (a->newsamp)) { + a->kick = 1; + a->start = -1; + /* a->period *= a->speed * a->newsamp; */ + } else + a->kick = 0; + + DoITToneSlide(); + a->ownper = 1; + } + break; + + case VOL_VIBRATO: + if (inf & 0x0f) + a->vibdepth = inf & 0xf; + if (inf & 0xf0) + a->vibspd = (inf & 0xf0) >> 2; + DoITVibrato(); + a->ownper = 1; + break; + } +} + + + +/* + * -------------------------------- --> General Player Functions <-- + * -------------------------------- + */ +static void +pt_playeffects(void) +{ + UBYTE dat, c; + + while (c = UniGetByte()) + switch (c) { + case UNI_NOTE: + case UNI_INSTRUMENT: + UniSkipOpcode(c); + break; + + case UNI_PTEFFECT0: + DoPTEffect0(UniGetByte()); + break; + + case UNI_PTEFFECT1: + dat = UniGetByte(); + if (dat != 0) + a->slidespeed = (UWORD) dat << 2; + if (pf->vbtick) + a->tmpperiod -= a->slidespeed; + break; + + case UNI_PTEFFECT2: + dat = UniGetByte(); + if (dat != 0) + a->slidespeed = (UWORD) dat << 2; + if (pf->vbtick) + a->tmpperiod += a->slidespeed; + break; + + case UNI_PTEFFECT3: + dat = UniGetByte(); + + if (dat != 0) { + a->portspeed = dat; + a->portspeed <<= 2; + } + if (a->period != 0) { + a->kick = 0; /* temp XM fix */ + DoToneSlide(); + a->ownper = 1; + } + break; + + case UNI_PTEFFECT4: + dat = UniGetByte(); + if (dat & 0x0f) + a->vibdepth = dat & 0xf; + if (dat & 0xf0) + a->vibspd = (dat & 0xf0) >> 2; + DoVibrato(); + a->ownper = 1; + break; + + case UNI_PTEFFECT5: + dat = UniGetByte(); + a->kick = 0; + DoToneSlide(); + DoVolSlide(dat); + a->ownper = 1; + break; + + case UNI_PTEFFECT6: + dat = UniGetByte(); + DoVibrato(); + DoVolSlide(dat); + a->ownper = 1; + break; + + case UNI_PTEFFECT7: + dat = UniGetByte(); + if (dat & 0x0f) + a->trmdepth = dat & 0xf; + if (dat & 0xf0) + a->trmspd = (dat & 0xf0) >> 2; + DoTremolo(); + a->ownvol = 1; + break; + + case UNI_PTEFFECT8: + dat = UniGetByte(); + if (pf->panflag) { + a->panning = dat; + pf->panning[mp_channel] = dat; + } + break; + + case UNI_PTEFFECT9: + dat = UniGetByte(); + if (dat) + a->soffset = (UWORD) dat << 8; + a->start = a->hioffset | a->soffset; + if ((a->s != NULL) && (a->start > a->s->length)) + a->start = a->s->loopstart; + break; + + case UNI_PTEFFECTA: + DoVolSlide(UniGetByte()); + break; + + case UNI_PTEFFECTB: + dat = UniGetByte(); + if (pf->patdly2) + break; + pf->patbrk = 0; + pf->sngpos = dat - 1; + pf->posjmp = 3; + break; + + case UNI_PTEFFECTC: + dat = UniGetByte(); + if (pf->vbtick) + break; + if (dat > 64) + dat = 64; + a->tmpvolume = dat; + break; + + case UNI_PTEFFECTD: + dat = UniGetByte(); + if (pf->patdly2) + break; + pf->patbrk = dat; + if (pf->patbrk > pf->pattrows[mp_channel]) + pf->patbrk = pf->pattrows[mp_channel]; + pf->posjmp = 3; + break; + + case UNI_PTEFFECTE: + DoEEffects(UniGetByte()); + break; + + case UNI_PTEFFECTF: + dat = UniGetByte(); + + if (pf->vbtick || pf->patdly2) + break; + + if (pf->extspd && (dat >= 0x20)) + pf->bpm = dat; + else { + if (dat) { + pf->sngspd = dat; + pf->vbtick = 0; + } + } + break; + + case UNI_S3MEFFECTA: + DoS3MSpeed(UniGetByte()); + break; + + case UNI_S3MEFFECTD: + DoS3MVolSlide(UniGetByte()); + break; + + case UNI_S3MEFFECTE: + DoS3MSlideDn(UniGetByte()); + break; + + case UNI_S3MEFFECTF: + DoS3MSlideUp(UniGetByte()); + break; + + case UNI_S3MEFFECTI: + DoS3MTremor(UniGetByte()); + a->ownvol = 1; + break; + + case UNI_S3MEFFECTQ: + DoS3MRetrig(UniGetByte()); + break; + + case UNI_S3MEFFECTR: + dat = UniGetByte(); + if (dat & 0x0f) + a->trmdepth = dat & 0xf; + if (dat & 0xf0) + a->trmspd = (dat & 0xf0) >> 2; + DoS3MTremolo(); + a->ownvol = 1; + break; + + case UNI_S3MEFFECTT: + DoS3MTempo(UniGetByte()); + break; + + case UNI_S3MEFFECTU: + dat = UniGetByte(); + if (dat & 0x0f) + a->vibdepth = dat & 0xf; + if (dat & 0xf0) + a->vibspd = (dat & 0xf0) >> 2; + DoS3MFineVibrato(); + a->ownper = 1; + break; + + case UNI_KEYOFF: + a->keyoff |= KEY_OFF; + if (a->i != NULL) { + if (!(a->i->volflg & EF_ON) || (a->i->volflg & EF_LOOP)) + a->keyoff = KEY_KILL; + } + break; + + case UNI_KEYFADE: + if (pf->vbtick >= UniGetByte()) { + a->keyoff = KEY_KILL; + if ((a->i != NULL) && !(a->i->volflg & EF_ON)) + a->fadevol = 0; + } + break; + + case UNI_VOLEFFECTS: + DoVolEffects(UniGetByte()); + break; + + case UNI_XMEFFECT4: + dat = UniGetByte(); + if (pf->vbtick) { + if (dat & 0x0f) + a->vibdepth = dat & 0xf; + if (dat & 0xf0) + a->vibspd = (dat & 0xf0) >> 2; + } + DoVibrato(); + a->ownper = 1; + break; + + case UNI_XMEFFECTA: + DoXMVolSlide(UniGetByte()); + break; + + case UNI_XMEFFECTE1: /* xm fineslide up */ + dat = UniGetByte(); + if (!pf->vbtick) { + if (dat) + a->fportupspd = dat; + else + dat = a->fportupspd; + a->tmpperiod -= (dat << 2); + } + break; + + case UNI_XMEFFECTE2: /* xm fineslide dn */ + dat = UniGetByte(); + if (!pf->vbtick) { + if (dat) + a->fportdnspd = dat; + else + dat = a->fportdnspd; + a->tmpperiod += (dat << 2); + } + break; + + case UNI_XMEFFECTEA: /* fine volume slide up */ + dat = UniGetByte(); + if (pf->vbtick) + break; + if (dat) + a->fslideupspd = dat; + else + dat = a->fslideupspd; + a->tmpvolume += dat; + if (a->tmpvolume > 64) + a->tmpvolume = 64; + break; + + case UNI_XMEFFECTEB: /* fine volume slide dn */ + dat = UniGetByte(); + if (pf->vbtick) + break; + if (dat) + a->fslidednspd = dat; + else + dat = a->fslidednspd; + a->tmpvolume -= dat; + if (a->tmpvolume < 0) + a->tmpvolume = 0; + break; + + case UNI_XMEFFECTG: + pf->volume = UniGetByte(); + break; + + case UNI_XMEFFECTH: + DoXMGlobalSlide(UniGetByte()); + break; + + case UNI_XMEFFECTL: + dat = UniGetByte(); + if (!pf->vbtick && a->i != NULL) { + UWORD points; + INSTRUMENT *i = a->i; + MP_VOICE *aout; + + if ((aout = a->slave) != NULL) { + points = i->volenv[i->volpts - 1].pos; + aout->venv.p = aout->venv.env[(dat > points) ? points : dat].pos; + + points = i->panenv[i->panpts - 1].pos; + aout->penv.p = aout->penv.env[(dat > points) ? points : dat].pos; + } + } + break; + + case UNI_XMEFFECTP: + DoXMPanSlide(UniGetByte()); + break; + + case UNI_XMEFFECTX1: + DoXMExtraFineSlideUp(UniGetByte()); + a->ownper = 1; + break; + + case UNI_XMEFFECTX2: + DoXMExtraFineSlideDown(UniGetByte()); + a->ownper = 1; + break; + + case UNI_ITEFFECTG: + dat = UniGetByte(); + if (dat != 0) + a->slidespeed = dat; + + if (a->period != 0) { + if ((pf->vbtick < 1) && (a->newsamp)) { + a->kick = 1; + a->start = -1; + /* a->period *= a->speed * a->newsamp; */ + } else + a->kick = 0; + + DoITToneSlide(); + a->ownper = 1; + } + break; + + case UNI_ITEFFECTH: /* it vibrato */ + dat = UniGetByte(); + if (dat & 0x0f) + a->vibdepth = dat & 0xf; + if (dat & 0xf0) + a->vibspd = (dat & 0xf0) >> 2; + DoITVibrato(); + a->ownper = 1; + break; + + case UNI_ITEFFECTI: /* it tremor */ + DoITTremor(UniGetByte()); + a->ownvol = 1; + break; + + case UNI_ITEFFECTM: + a->chanvol = UniGetByte(); + if (a->chanvol > 64) + a->chanvol = 64; + else if (a->chanvol < 0) + a->chanvol = 0; + break; + + case UNI_ITEFFECTN: /* Slide / Fineslide Channel Volume */ + DoITChanVolSlide(UniGetByte()); + break; + + case UNI_ITEFFECTP: /* slide / fineslide channel panning */ + DoITPanSlide(UniGetByte()); + break; + + case UNI_ITEFFECTU: /* fine vibrato */ + dat = UniGetByte(); + if (dat & 0x0f) + a->vibdepth = dat & 0xf; + if (dat & 0xf0) + a->vibspd = (dat & 0xf0) >> 2; + DoITFineVibrato(); + a->ownper = 1; + break; + + case UNI_ITEFFECTW: /* Slide / Fineslide Global volume */ + DoITGlobalSlide(UniGetByte()); + break; + + case UNI_ITEFFECTY: /* The Satanic Panbrello */ + dat = UniGetByte(); + if (dat & 0x0f) + a->panbdepth = (dat & 0xf); + if (dat & 0xf0) + a->panbspd = (dat & 0xf0) >> 4; + DoITPanbrello(); + break; + + case UNI_ITEFFECTS0: + DoSSEffects(UniGetByte()); + break; + + default: + UniSkipOpcode(c); + break; + } +} + + +static void +DoNNAEffects(UBYTE dat) +{ + int t; + MP_VOICE *aout; + + dat &= 0xf; + aout = (a->slave == NULL) ? &aout_dummy : a->slave; + + switch (dat) { + case 0x0: /* Past note cut */ + for (t = 0; t < md_sngchn; t++) + if (pf->voice[t].master == a) + pf->voice[t].fadevol = 0; + break; + + case 0x1: /* Past note off */ + for (t = 0; t < md_sngchn; t++) + if (pf->voice[t].master == a) { + pf->voice[t].keyoff |= KEY_OFF; + if (!(pf->voice[t].venv.flg & EF_ON)) + pf->voice[t].keyoff = KEY_KILL; + } + break; + + case 0x2: /* Past note fade */ + for (t = 0; t < md_sngchn; t++) + if (pf->voice[t].master == a) + pf->voice[t].keyoff |= KEY_FADE; + break; + + case 0x3: /* set NNA note cut */ + a->nna = (a->nna & ~0x3f) | NNA_CUT; + break; + + case 0x4: /* set NNA note continue */ + a->nna = (a->nna & ~0x3f) | NNA_CONTINUE; + break; + + case 0x5: /* set NNA note off */ + a->nna = (a->nna & ~0x3f) | NNA_OFF; + break; + + case 0x6: /* set NNA note fade */ + a->nna = (a->nna & ~0x3f) | NNA_FADE; + break; + + case 0x7: /* disable volume envelope */ + aout->volflg &= ~EF_ON; + break; + + case 0x8: /* enable volume envelope */ + aout->volflg |= EF_ON; + break; + + case 0x9: /* disable panning envelope */ + aout->panflg &= ~EF_ON; + break; + + case 0xa: /* enable panning envelope */ + aout->panflg |= EF_ON; + break; + + case 0xb: /* disable pitch envelope */ + aout->pitflg &= ~EF_ON; + break; + + case 0xc: /* enable pitch envelope */ + aout->pitflg |= EF_ON; + break; + } +} + + +void +Player_HandleTick(void) +{ + MP_VOICE *aout; /* current audout (slave of audtmp) it's + * working on */ + int t, tr, t2, k; + ULONG tmpvol, period; + UBYTE c; + BOOL funky; + SAMPLE *s; + INSTRUMENT *i; + + if (isfirst) { /* don't handle the very first ticks, this + * allows the other hardware to settle down + * so we don't loose any starting notes */ + isfirst--; + return; + } + if (pf == NULL || pf->forbid) + return; + + if (++pf->vbtick >= pf->sngspd) { + 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) + pf->patpos--; /* so turn back pf->patpos by 1 */ + } + /* + * Do we have to get a new patternpointer ? (when pf->patpos reaches + * 64 or when a patternbreak is active) + */ + + if (pf->patpos == pf->numrow) + pf->posjmp = 3; + + if (pf->posjmp) { + pf->patpos = pf->patbrk; + pf->sngpos += (pf->posjmp - 2); + pf->patbrk = pf->posjmp = 0; + if (pf->sngpos >= pf->numpos) { + if (!pf->loop) + return; + if ((pf->sngpos = pf->reppos) == 0) { + pf->volume = pf->initvolume; + pf->sngspd = pf->initspeed; + pf->bpm = pf->inittempo; + } + } + if (pf->sngpos < 0) + pf->sngpos = pf->numpos - 1; + } + if (!pf->patdly2) { + for (t = 0; t < pf->numchn; t++) { + UBYTE inst; + + tr = pf->patterns[(pf->positions[pf->sngpos] * pf->numchn) + t]; + pf->numrow = pf->pattrows[pf->positions[pf->sngpos]]; + + mp_channel = t; + a = &pf->control[t]; + a->row = (tr < pf->numtrk) ? UniFindRow(pf->tracks[tr], pf->patpos) : NULL; + a->newsamp = 0; + + if (a->row == NULL) + continue; + UniSetRow(a->row); + funky = 0; /* Funky is set to indicate note or inst + * change */ + + while (c = UniGetByte()) { + switch (c) { + case UNI_NOTE: + funky |= 1; + a->anote = UniGetByte(); + a->kick = 1; + a->start = -1; + + /* 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: + funky |= 2; + inst = UniGetByte(); + if (inst >= pf->numins) + break; /* <- safety valve */ + + a->i = (pf->flags & UF_INST) ? &pf->instruments[inst] : NULL; + a->retrig = 0; + a->s3mtremor = 0; + a->sample = inst; + break; + + default: + UniSkipOpcode(c); + break; + } + } + + if (funky) { + i = a->i; + if (i != NULL) { + if (i->samplenumber[a->anote] >= pf->numsmp) + continue; + s = &pf->samples[i->samplenumber[a->anote]]; + a->note = i->samplenote[a->anote]; + } else { + a->note = a->anote; + s = &pf->samples[a->sample]; + } + + if (a->s != s) { + a->s = s; + a->newsamp = a->period; + } + /* channel or instrument determined panning ? */ + + a->panning = pf->panning[t]; + if (s->flags & SF_OWNPAN) + a->panning = s->panning; + else if ((i != NULL) && (i->flags & IF_OWNPAN)) + a->panning = i->panning; + + a->handle = s->handle; + a->speed = s->speed; + + if (i != NULL) { + if (i->flags & IF_PITCHPAN) + a->panning += ((a->anote - i->pitpancenter) * i->pitpansep) / 8; + a->pitflg = i->pitflg; + a->volflg = i->volflg; + a->panflg = i->panflg; + a->nna = i->nnatype; + a->dca = i->dca; + a->dct = i->dct; + } else { + a->pitflg = 0; + a->volflg = 0; + a->panflg = 0; + a->nna = 0; + a->dca = 0; + a->dct = 0; + } + + if (funky & 2) { /* IT's random volume variations: 0:8 + * bit fixed, and one bit for sign. */ + a->volume = s->volume; + a->tmpvolume = s->volume; + if ((s != NULL) && (i != NULL)) { + a->volume = a->tmpvolume = s->volume + ((s->volume * ((SLONG) i->rvolvar * (SLONG) ((rand() & 511) - 255))) / 25600); + if (a->panning != PAN_SURROUND) + a->panning += ((a->panning * ((SLONG) i->rpanvar * (SLONG) ((rand() & 511) - 255))) / 25600); + } + } + period = GetPeriod(a->note, a->speed); + a->wantedperiod = period; + a->tmpperiod = period; + a->keyoff = KEY_KICK; + } + } + } + } + /* Update effects */ + + for (t = 0; t < pf->numchn; t++) { + mp_channel = t; + a = &pf->control[t]; + + if ((aout = a->slave) != NULL) { + a->fadevol = aout->fadevol; + a->period = aout->period; + if (a->kick != 1) + a->keyoff = aout->keyoff; + } + if (a->row == NULL) + continue; + UniSetRow(a->row); + + a->ownper = a->ownvol = 0; + pt_playeffects(); + if (!a->ownper) + a->period = a->tmpperiod; + if (!a->ownvol) + a->volume = a->tmpvolume; + + if (a->s != NULL) { + if (a->i != NULL) + a->outvolume = (a->volume * a->s->globvol * a->i->globvol) / 1024; /* max val: 256 */ + else + a->outvolume = (a->volume * a->s->globvol) / 16; /* max val: 256 */ + if (a->outvolume > 256) + a->volume = 256; + } + } + + + a = pf->control; + if (pf->flags & UF_NNA) { + for (t = 0; t < pf->numchn; t++, a++) { + if (a->kick == 1) { + if (a->slave != NULL) { + aout = a->slave; + if (aout->nna & 0x3f) { /* oh boy, we have to do an + * NNA 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->nna) { + case NNA_CONTINUE: + break; /* continue note, do nothing */ + + case NNA_OFF: /* note off */ + aout->keyoff |= KEY_OFF; + if (!(aout->volflg & EF_ON) || (aout->volflg & EF_LOOP)) + aout->keyoff = KEY_KILL; + break; + + case NNA_FADE: + aout->keyoff |= KEY_FADE; + break; + } + } + } + k = 0; + if (a->dct != DCT_OFF) { + for (t2 = 0; t2 < md_sngchn; t2++) { + if (!(Voice_Stopped(t2)) && (pf->voice[t2].masterchn == t) && (a->sample == pf->voice[t2].sample)) { + switch (a->dct) { + case DCT_NOTE: + if (a->note == pf->voice[t2].note) + k = 1; + break; + + case DCT_SAMPLE: + if (a->handle == pf->voice[t2].handle) + k = 1; + break; + + case DCT_INST: + k = 1; + break; + } + + if (k == 1) { + k = 0; + switch (a->dca) { + case DCA_CUT: + pf->voice[t2].fadevol = 0; + a->slave = &pf->voice[a->slavechn = t2]; + break; + + case DCA_OFF: + /* a->slave = &pf->voice[newchn]; */ + pf->voice[t2].keyoff |= KEY_OFF; + if (!(pf->voice[t2].volflg & EF_ON) || (pf->voice[t2].volflg & EF_LOOP)) + pf->voice[t2].keyoff = KEY_KILL; + break; + + case DCA_FADE: + /* a->slave = &pf->voice[newchn]; */ + pf->voice[t2].keyoff |= KEY_FADE; + break; + } + } + } + } + } /* DCT checking */ + } /* if a->kick */ + } /* for loop */ + } + a = pf->control; + for (t = 0; t < pf->numchn; t++, a++) { + int newchn; + + if (a->notedelay) + continue; + + if (a->kick == 1) { /* If no channel was cut above, find an empty + * or quiet channel here */ + if (pf->flags & UF_NNA) { + if (a->slave == NULL) { + if ((newchn = MP_FindEmptyChannel(t)) != -1) + a->slave = &pf->voice[a->slavechn = newchn]; + } + } else + a->slave = &pf->voice[a->slavechn = t]; + + /* Assign parts of MP_VOICE only done for a KICK! */ + + if ((aout = a->slave) != NULL) { + if (aout->mflag && (aout->master != NULL)) + aout->master->slave = NULL; + a->slave = aout; + aout->master = a; + aout->masterchn = t; + aout->mflag = 1; + } + } else { + aout = a->slave; + } + + if (aout != NULL) { + aout->kick = a->kick; + aout->i = a->i; + aout->s = a->s; + aout->sample = a->sample; + aout->handle = a->handle; + aout->period = a->period; + aout->panning = a->panning; + aout->chanvol = a->chanvol; + aout->fadevol = a->fadevol; + aout->start = a->start; + aout->volflg = a->volflg; + aout->panflg = a->panflg; + aout->pitflg = a->pitflg; + aout->volume = a->outvolume; + aout->keyoff = a->keyoff; + aout->note = a->note; + aout->nna = a->nna; + } + a->kick = 0; + + } + + /* Now set up the actual hardware channel playback information */ + + for (t = 0; t < md_sngchn; t++) { + SWORD envpan, envvol = 256, envpit = 0; + SLONG vibval, vibdpt; + + aout = &pf->voice[mp_channel = t]; + i = aout->i; + s = aout->s; + + if (s == NULL) + continue; + + if (aout->period < 40) + aout->period = 40; + if (aout->period > 50000) + aout->period = 50000; + + if (aout->kick) { + Voice_Play(t, s, (aout->start == -1) ? ((s->flags & SF_UST_LOOP) ? s->loopstart : 0) : aout->start); + /* aout->keyoff = KEY_KICK; */ + aout->fadevol = 32768; + aout->aswppos = 0; + + if ((i != NULL) && (aout->kick != 2)) { + StartEnvelope(&aout->venv, aout->volflg, i->volpts, i->volsusbeg, i->volsusend, i->volbeg, i->volend, i->volenv, aout->keyoff); + StartEnvelope(&aout->penv, aout->panflg, i->panpts, i->pansusbeg, i->pansusend, i->panbeg, i->panend, i->panenv, aout->keyoff); + StartEnvelope(&aout->cenv, aout->pitflg, i->pitpts, i->pitsusbeg, i->pitsusend, i->pitbeg, i->pitend, i->pitenv, aout->keyoff); + } + aout->kick = 0; + } + if (i != NULL) { + envvol = ProcessEnvelope(&aout->venv, 256, aout->keyoff); + envpan = ProcessEnvelope(&aout->penv, 128, aout->keyoff); + envpit = ProcessEnvelope(&aout->cenv, 32, aout->keyoff); + } + tmpvol = aout->fadevol; /* max 32768 */ + tmpvol *= aout->chanvol;/* * max 64 */ + tmpvol *= aout->volume; /* * max 256 */ + tmpvol /= 16384L; /* tmpvol is max 32768 */ + aout->totalvol = tmpvol >> 2; /* totalvolume used to determine + * samplevolume */ + tmpvol *= envvol; /* * max 256 */ + tmpvol *= pf->volume; /* * max 128 */ + tmpvol /= 4194304 UL; + + if ((aout->masterchn != -1) && pf->control[aout->masterchn].muted) /* Channel Muting Line */ + Voice_SetVolume(t, 0); + else + Voice_SetVolume(t, tmpvol); + + + if (aout->panning == PAN_SURROUND) + Voice_SetPanning(t, PAN_SURROUND); + else { + if (aout->penv.flg & EF_ON) + Voice_SetPanning(t, DoPan(envpan, aout->panning)); + else + Voice_SetPanning(t, aout->panning); + } + + if (aout->period && s->vibdepth) { + switch (s->vibtype) { + case 0: + vibval = avibtab[s->avibpos & 127]; + if (s->avibpos & 0x80) + vibval = -vibval; + break; + + case 1: + vibval = 64; + if (s->avibpos & 0x80) + vibval = -vibval; + break; + + case 2: + vibval = 63 - (((s->avibpos + 128) & 255) >> 1); + break; + + case 3: + vibval = (((s->avibpos + 128) & 255) >> 1) - 64; + break; + } + } + if (s->vibflags & AV_IT) { + if ((aout->aswppos >> 8) < s->vibdepth) { + aout->aswppos += s->vibsweep; + vibdpt = aout->aswppos; + } else + vibdpt = s->vibdepth << 8; + vibval = (vibval * vibdpt) >> 16; + if (aout->mflag) { + if (!(pf->flags & UF_LINEAR)) + vibval >>= 1; + aout->period -= vibval; + } + } else { /* do XM style auto-vibrato */ + if (!(aout->keyoff & KEY_OFF)) { + if (aout->aswppos < s->vibsweep) { + vibdpt = (aout->aswppos * s->vibdepth) / s->vibsweep; + aout->aswppos++; + } else + vibdpt = s->vibdepth; + } else { + /* + * key-off -> 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->period -= vibval; + } + + /* update vibrato position */ + s->avibpos = (s->avibpos + s->vibrate) & 0xff; + + if (aout->cenv.flg & EF_ON) { + envpit = envpit - 32; + aout->period -= envpit; + } + Voice_SetFrequency(t, getfrequency(pf->flags, aout->period)); + + if (aout->fadevol == 0) /* check for a dead note (fadevol = 0) */ + Voice_Stop(t); + else { /* if keyfade, start substracting + * fadeoutspeed from fadevol: */ + + if ((i != NULL) && (aout->keyoff & KEY_FADE)) { + if (aout->fadevol >= i->volfade) + aout->fadevol -= i->volfade; + else + aout->fadevol = 0; + } + } + + MD_SetBPM(pf->bpm); + } +} + + +BOOL +Player_Init(UNIMOD * mf) +{ + int t; + + mf->extspd = 1; + mf->panflag = 1; + mf->loop = 0; + + mf->pat_reppos = 0; + mf->pat_repcnt = 0; + mf->sngpos = 0; + mf->sngspd = mf->initspeed; + mf->volume = mf->initvolume; + + mf->vbtick = mf->sngspd; + mf->patdly = 0; + mf->patdly2 = 0; + mf->bpm = mf->inittempo; + + mf->patpos = 0; + mf->posjmp = 2; /* <- make sure the player fetches the first + * note */ + mf->patbrk = 0; + + /* Make sure the player doesn't start with garbage: */ + + if ((mf->control = (MP_CONTROL *) _mm_calloc(mf->numchn, sizeof(MP_CONTROL))) == NULL) + return 1; + if ((mf->voice = (MP_VOICE *) _mm_calloc(md_sngchn, sizeof(MP_VOICE))) == NULL) + return 1; + + for (t = 0; t < mf->numchn; t++) { + mf->control[t].chanvol = mf->chanvol[t]; + mf->control[t].panning = mf->panning[t]; + } + + return 0; +} + + +void +Player_Exit(UNIMOD * mf) +{ + if (mf == NULL) + return; + if (mf == pf) { + Player_Stop(); + pf = NULL; + } + if (mf->control != NULL) + free(mf->control); + if (mf->voice != NULL) + free(mf->voice); + mf->control = NULL; + mf->voice = NULL; + +} + + +void +Player_SetVolume(int volume) +{ + if (pf == NULL) + return; + + if (volume > 128) + volume = 128; + if (volume < 0) + volume = 0; + + pf->volume = volume; +} + + +UNIMOD * +Player_GetUnimod(void) +{ + return pf; +} + + +void +Player_Start(UNIMOD * mf) +{ + int t; + + if (!MikMod_Active()) { + isfirst = 2; + MikMod_EnableOutput(); + } + if (mf == NULL) + return; + + mf->forbid = 0; + if (pf != mf) { /* new song is being started, so completely + * stop out the old one. */ + if (pf != NULL) + pf->forbid = 1; + for (t = 0; t < md_sngchn; t++) + Voice_Stop(t); + } + pf = mf; +} + + +void +Player_Stop(void) +{ + if (md_sfxchn == 0) + MikMod_DisableOutput(); + if (pf != NULL) + pf->forbid = 1; + pf = NULL; +} + + +BOOL +MP_Playing(UNIMOD * mf) +{ + if ((mf == NULL) || (mf != pf)) + return 0; + return (!(mf->sngpos >= mf->numpos)); +} + + +BOOL +Player_Active(void) +{ + if (pf == NULL) + return 0; + return (!(pf->sngpos >= pf->numpos)); +} + + +void +MP_NextPosition(UNIMOD * mf) +{ + int t; + + if (mf == NULL) + return; + mf->forbid = 1; + mf->posjmp = 3; + mf->patbrk = 0; + mf->vbtick = mf->sngspd; + + for (t = 0; t < md_sngchn; t++) { + Voice_Stop(t); + mf->voice[t].i = NULL; + mf->voice[t].s = NULL; + } + + for (t = 0; t < mf->numchn; t++) { + mf->control[t].i = NULL; + mf->control[t].s = NULL; + } + mf->forbid = 0; +} + + +void +Player_NextPosition(void) +{ + MP_NextPosition(pf); +} + + +void +MP_PrevPosition(UNIMOD * mf) +{ + int t; + + if (mf == NULL) + return; + mf->forbid = 1; + mf->posjmp = 1; + mf->patbrk = 0; + mf->vbtick = mf->sngspd; + + for (t = 0; t < md_sngchn; t++) { + Voice_Stop(t); + mf->voice[t].i = NULL; + mf->voice[t].s = NULL; + } + + for (t = 0; t < mf->numchn; t++) { + mf->control[t].i = NULL; + mf->control[t].s = NULL; + } + + mf->forbid = 0; +} + + +void +Player_PrevPosition(void) +{ + MP_PrevPosition(pf); +} + + +void +MP_SetPosition(UNIMOD * mf, UWORD pos) +{ + int t; + + if (mf == NULL) + return; + mf->forbid = 1; + if (pos >= mf->numpos) + pos = mf->numpos; + mf->posjmp = 2; + mf->patbrk = 0; + mf->sngpos = pos; + mf->vbtick = mf->sngspd; + + for (t = 0; t < md_sngchn; t++) { + Voice_Stop(t); + mf->voice[t].i = NULL; + mf->voice[t].s = NULL; + } + + for (t = 0; t < mf->numchn; t++) { + mf->control[t].i = NULL; + mf->control[t].s = NULL; + } + + mf->forbid = 0; +} + + +void +Player_SetPosition(UWORD pos) +{ + MP_SetPosition(pf, pos); +} + + +void +MP_Unmute(UNIMOD * mf, SLONG arg1,...) +{ + va_list ap; + SLONG t, arg2, arg3; + + va_start(ap, arg1); + + if (mf != NULL) { + switch (arg1) { + case MUTE_INCLUSIVE: + if ((!(arg2 = va_arg(ap, SLONG))) && (!(arg3 = va_arg(ap, SLONG))) || + (arg2 > arg3) || (arg3 >= mf->numchn)) { + va_end(ap); + return; + } + for (; arg2 < mf->numchn && arg2 <= arg3; arg2++) + mf->control[arg2].muted = 0; + break; + + case MUTE_EXCLUSIVE: + if ((!(arg2 = va_arg(ap, SLONG))) && (!(arg3 = va_arg(ap, SLONG))) || + (arg2 > arg3) || (arg3 >= mf->numchn)) { + va_end(ap); + return; + } + for (t = 0; t < mf->numchn; t++) { + if ((t >= arg2) && (t <= arg3)) + continue; + mf->control[t].muted = 0; + } + break; + + default: + if (arg1 < mf->numchn) + mf->control[arg1].muted = 0; + break; + } + } + va_end(ap); + + return; +} + + +void +Player_Unmute(SLONG arg1,...) +{ + va_list argptr; + MP_Unmute(pf, arg1, argptr); + va_end(argptr); +} + + +void +MP_Mute(UNIMOD * mf, SLONG arg1,...) +{ + va_list ap; + SLONG t, arg2, arg3; + + va_start(ap, arg1); + + if (mf != NULL) { + switch (arg1) { + case MUTE_INCLUSIVE: + if ((!(arg2 = va_arg(ap, SLONG))) && (!(arg3 = va_arg(ap, SLONG))) || + (arg2 > arg3) || (arg3 >= mf->numchn)) { + va_end(ap); + return; + } + for (; arg2 < mf->numchn && arg2 <= arg3; arg2++) + mf->control[arg2].muted = 1; + break; + + case MUTE_EXCLUSIVE: + if ((!(arg2 = va_arg(ap, SLONG))) && (!(arg3 = va_arg(ap, SLONG))) || + (arg2 > arg3) || (arg3 >= mf->numchn)) { + va_end(ap); + return; + } + for (t = 0; t < mf->numchn; t++) { + if ((t >= arg2) && (t <= arg3)) + continue; + mf->control[t].muted = 1; + } + break; + + default: + if (arg1 < mf->numchn) + mf->control[arg1].muted = 1; + break; + } + } + va_end(ap); + + return; +} + + +void +Player_Mute(SLONG arg1,...) +{ + va_list argptr; + MP_Mute(pf, arg1, argptr); + va_end(argptr); +} + + +void +MP_ToggleMute(UNIMOD * mf, SLONG arg1,...) +{ + va_list ap; + SLONG arg2, arg3; + ULONG t; + + va_start(ap, arg1); + + if (mf != NULL) { + switch (arg1) { + case MUTE_INCLUSIVE: + if ((!(arg2 = va_arg(ap, SLONG))) && (!(arg3 = va_arg(ap, SLONG))) || + (arg2 > arg3) || (arg3 >= mf->numchn)) { + va_end(ap); + return; + } + for (; arg2 < mf->numchn && arg2 <= arg3; arg2++) + mf->control[arg2].muted = (mf->control[arg2].muted) ? 0 : 1; + + break; + + case MUTE_EXCLUSIVE: + if ((!(arg2 = va_arg(ap, SLONG))) && (!(arg3 = va_arg(ap, SLONG))) || + (arg2 > arg3) || (arg3 >= mf->numchn)) { + va_end(ap); + return; + } + for (t = 0; t < mf->numchn; t++) { + if ((t >= arg2) && (t <= arg3)) + continue; + mf->control[t].muted = (mf->control[t].muted) ? 0 : 1; + } + break; + + default: + if (arg1 < mf->numchn) + mf->control[arg1].muted = (mf->control[arg1].muted) ? 0 : 1; + break; + } + } + va_end(ap); + + return; +} + + +void +Player_ToggleMute(SLONG arg1,...) +{ + va_list argptr; + MP_ToggleMute(pf, arg1, argptr); + va_end(argptr); +} + + +BOOL +MP_Muted(UNIMOD * mf, int chan) +{ + if (mf == NULL) + return 1; + return (chan < mf->numchn) ? mf->control[chan].muted : 1; +} + + +BOOL +Player_Muted(int chan) +{ + if (pf == NULL) + return 1; + return (chan < pf->numchn) ? pf->control[chan].muted : 1; +} + + +int +MP_GetChannelVoice(UNIMOD * mf, int chan) +{ + if (mf == NULL) + return 0; + return mf->control[chan].slavechn; +} + + +int +Player_GetChannelVoice(int chan) +{ + if (pf == NULL) + return 0; + return pf->control[chan].slavechn; +} + + +void +Player_TogglePause(void) +{ + if (pf == NULL) + return; + if (pf->forbid == 1) + pf->forbid = 0; + else + pf->forbid = 1; +} + + +/* + * --> The following procedures were taken from UNITRK because they -> are + * ProTracker format specific. + */ + +/* Appends UNI_INSTRUMENT opcode to the unitrk stream. */ +void +UniInstrument(UBYTE ins) +{ + UniWrite(UNI_INSTRUMENT); + UniWrite(ins); +} + +/* Appends UNI_NOTE opcode to the unitrk stream. */ +void +UniNote(UBYTE note) +{ + UniWrite(UNI_NOTE); + UniWrite(note); +} + +/* Appends UNI_PTEFFECTX opcode to the unitrk stream. */ +void +UniPTEffect(UBYTE eff, UBYTE dat) +{ + if (eff != 0 || dat != 0) { /* don't write empty effect */ + UniWrite(UNI_PTEFFECT0 + eff); + UniWrite(dat); + } +} + +/* Appends UNI_VOLEFFECT + effect/dat to unistream. */ +void +UniVolEffect(UWORD eff, UBYTE dat) +{ + if (eff != 0 || dat != 0) { /* don't write empty effect */ + UniWrite(UNI_VOLEFFECTS); + UniWrite(eff); + UniWrite(dat); + } +}