123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113 |
- /* $Id$
- * MegaZeux
- *
- * Copyright (C) 1996 Greg Janson
- * Copyright (C) 1998 Matthew D. Williams - dbwilli@scsn.net
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
- //TO DO:
- // Sample playing (including cache'ing and freeing up mem if needed)
- // Internal .MOD conversion
- #include "ems.h"
- #include "meminter.h"
- #include "mod.h"
- #include "bwsb.h"
- #include "data.h"
- #include "timer.h"
- #include <fcntl.h>
- #include <io.h>
- #include <dos.h>
- #include "string.h"
- #include "error.h"
- #include "boardmem.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys\stat.h>
- extern char no_ems;
- extern int rob_global_ems;
- //Error leniency, 0 is least lenient, 2 is most lenient.
- //
- //Errors in most lenient: (normal game)
- // File "MZXBLANK.FIL" not found; Music code compromised
- // Low on conventional memory; Music code compromised
- //
- //Additional errors in middle leniency: (game w/debug menu)
- // Error opening module file
- // Error loading module
- // Error loading SAM
- // Out of memory for SAM
- //
- //Additional errors in least lenient: (editor)
- // Music is off- Module is set for board but not loaded
- // Music is off- SAM cannot play
- unsigned char error_mode=2;
- GDMHeader ModHeader; /* Module header */
- int ErrorFlag;
- int file, MusChans, t1;
- unsigned int Addr=0xFFFF,IRQ=0xFF,DMA=0xFF;
- char refresh_mod_playing;
- //char mod_playing[FILENAME_SIZE];
- char sfx_chan[4]={ -1,-1,-1,-1 };//Actual IDs of SFX channels (left)
- char sfx_chan2[4]={ -1,-1,-1,-1 };//Actual IDs of SFX channels (right)
- char actual_num_sfx=0;//Actual number of different SFX channels
- char current_sfx=0;//Current sfx channel
- char mod_active=0;//1=blank mod 2=real mod
- //Data for data integrity error checks
- int SCDoing=0;
- char far *SCLoadingName;
- #define FT_GDM 1
- #define FT_4MOD 2
- #define FT_8MOD 3
- #define FT_XMOD 4
- //(unkown)
- #define FT_NONE 0
- int _file_type(int fp,char *fn) {
- char id[5];
- int tmp;
- //Tells us what kind of Music file it is! (assumes at start of file)
- read(fp,&id,4);
- id[4]=0;
- if(!str_cmp(id,"GDMþ")) return FT_GDM;
- lseek(fp,1080,SEEK_SET);
- read(fp,&id,4);
- id[4]=0;
- if(!str_cmp(id,"M.K.")) {
- //WOW??
- tmp=str_len(fn);
- if(!str_cmp(&fn[tmp-3],"WOW")) return FT_8MOD;
- else return FT_4MOD;
- }
- if((!str_cmp(id,"M!K!"))||(!str_cmp(id,"FLT4"))||
- (!str_cmp(id,"4CHN"))) return FT_4MOD;
- if((!str_cmp(id,"8CHN"))||(!str_cmp(id,"FLT8"))||
- (!str_cmp(id,"OCTA"))) return FT_8MOD;
- if((id[1]=='C')&&(id[2]=='H')&&(id[3]=='N')) return FT_XMOD;
- if((id[2]=='C')&&(id[3]=='H')) return FT_XMOD;
- return FT_NONE;
- }
- int ConvertMOD(int file,int type);
- void load_mod(char far *filename) {
- int t1,t2;
- char temp3[FILENAME_SIZE];
- SCDoing|=512;
- SCLoadingName=filename;
- if(!music_device) music_on=0;
- if (!str_cmp(filename, "*")) {
- if (refresh_mod_playing) {
- //str_cpy(temp, real_mod_playing);
- if (str_cmp(real_mod_playing, "*")) {
- str_cpy(temp3, real_mod_playing);
- load_mod(temp3);
- } else { // just so we don't eat ourselves recursively
- error("load_mod(): real_mod_playing is ``*'' -- this is a bug\n",0,8,current_pg_seg,0x0f00);
- }
- }
- str_cpy(mod_playing, "*");
- refresh_mod_playing = 0;
- return;
- }
- refresh_mod_playing = 0;
- free_sam_cache(1);//Clear entire sfx cache
- save_map_state_EMS(rob_global_ems);
- if(mod_active>0) {
- StopMusic();
- StopOutput();
- UnloadModule();
- }
- mod_active=0;
- mod_playing[0]=0;
- real_mod_playing[0]=0;
- if(!music_on) {
- //Save mod playing...
- if(filename!=NULL) {
- str_cpy(mod_playing,filename);
- str_cpy(real_mod_playing,filename);
- if(!error_mode) error("Music is off- Module is set for board but not loaded",
- 0,24,current_pg_seg,0x3201);
- }
- restore_map_state_EMS(rob_global_ems);
- SCDoing&=~512;
- return;
- }
- if(filename==NULL) goto load_empty_mod;
- retry_1:
- ErrorFlag=EmsExist()&&1;/* Enable EMS if available */
- //Turn off EMS if requested in CMD line-
- if(no_ems) ErrorFlag=0;
- file=open(filename,O_RDONLY|O_BINARY);
- if(file>-1) {
- //Check file type
- t1=_file_type(file,filename);
- if(t1==FT_NONE) {
- ErrorFlag=1;
- close(file);
- goto tsukino;
- }
- if(t1==FT_GDM) {
- LoadGDM(file,0,&ErrorFlag,&ModHeader);
- close(file);
- }
- else {
- //Mod file
- file=ConvertMOD(file,t1);
- LoadGDM(file,0,&ErrorFlag,&ModHeader);
- close(file);
- }
- }
- else {
- if(error_mode<2) error("Error opening module file",1,24,current_pg_seg,0x3701);
- goto load_empty_mod;
- }
- //Errors-
- if(ErrorFlag) {
- UnloadModule();
- //Try to free up memory
- restore_map_state_EMS(rob_global_ems);
- free_up_board_memory();
- save_map_state_EMS(rob_global_ems);
- file=open(filename,O_RDONLY|O_BINARY);
- if(file>-1) {
- //Check file type
- t1=_file_type(file,filename);
- if(t1==FT_NONE) {
- ErrorFlag=1;
- close(file);
- goto tsukino;
- }
- if(t1==FT_GDM) {
- LoadGDM(file,0,&ErrorFlag,&ModHeader);
- close(file);
- }
- else {
- //Mod file
- file=ConvertMOD(file,t1);
- LoadGDM(file,0,&ErrorFlag,&ModHeader);
- close(file);
- }
- }
- else {
- tsukino:
- if(error_mode<2) error("Error opening module file",1,24,current_pg_seg,0x3701);
- goto load_empty_mod;
- }
- if(ErrorFlag) {
- UnloadModule();
- if(error_mode<2)
- if(error("Error loading module",1,26,current_pg_seg,0x3300+ErrorFlag)==2)
- goto retry_1;
- goto load_empty_mod;
- }
- }
- //Count number of channels
- MusChans=current_sfx=actual_num_sfx=0;
- sfx_chan[0]=sfx_chan[1]=sfx_chan[2]=sfx_chan[3]=-1;
- sfx_chan2[0]=sfx_chan2[1]=sfx_chan2[2]=sfx_chan2[3]=-1;
- t2=0;//Start on left
- for(t1=0;t1<32;t1++) {
- if(ModHeader.PanMap[t1]!=0xFF) MusChans++;
- else if(actual_num_sfx<4) {
- ModHeader.PanMap[t1]=t2;
- if(t2==0) {//Left channel
- sfx_chan[actual_num_sfx]=t1;
- t2=0xF;
- }
- else {//Right channel
- sfx_chan2[actual_num_sfx++]=t1;
- t2=0;
- }
- }
- }
- if(actual_num_sfx>sfx_channels) actual_num_sfx=sfx_channels;
- MusChans+=actual_num_sfx<<1;
- if((actual_num_sfx==0)&&(sfx_channels>0)) {
- actual_num_sfx=1;
- sfx_chan[0]=30;
- sfx_chan2[0]=31;
- }
- //Activate up to 4 (8) SFX channels
- StartOutput(MusChans,0);
- StartMusic();
- str_cpy(mod_playing,filename);
- str_cpy(real_mod_playing,filename);
- mod_active=2;
- restore_map_state_EMS(rob_global_ems);
- SCDoing&=~512;
- return;
- load_empty_mod:
- retry_2:
- //Load "blank MOD"
- ErrorFlag=EmsExist()&&1;/* Enable EMS if available */
- //Turn off EMS if requested in CMD line-
- if(no_ems) ErrorFlag=0;
- file=open(mzx_blank_mod_file,O_RDONLY|O_BINARY);
- if(file>-1) {
- LoadGDM(file,0,&ErrorFlag,&ModHeader);
- close(file);
- }
- else {
- error("File \"MZXBLANK.FIL\" not found; Music code compromised",1,24,
- current_pg_seg,0x3601);
- goto load_no_mod;
- }
- //Errors-
- if(ErrorFlag) {
- UnloadModule();
- //Try to free up memory
- restore_map_state_EMS(rob_global_ems);
- free_up_board_memory();
- save_map_state_EMS(rob_global_ems);
- file=open(filename,O_RDONLY|O_BINARY);
- if(file>-1) {
- LoadGDM(file,0,&ErrorFlag,&ModHeader);
- close(file);
- }
- else {
- error("File \"MZXBLANK.FIL\" not found; Music code compromised",1,24,
- current_pg_seg,0x3601);
- goto load_no_mod;
- }
- if(ErrorFlag) {
- UnloadModule();
- if(error("Low on memory; Music code compromised",1,26,current_pg_seg,0x0600+ErrorFlag)==2)
- goto retry_2;
- goto load_no_mod;
- }
- }
- //Count number of channels
- MusChans=sfx_channels<<1;
- current_sfx=0;
- actual_num_sfx=sfx_channels;
- sfx_chan[0]=sfx_chan[1]=sfx_chan[2]=sfx_chan[3]=-1;
- sfx_chan2[0]=sfx_chan2[1]=sfx_chan2[2]=sfx_chan2[3]=-1;
- for(t1=0;t1<sfx_channels;t1++) {
- ModHeader.PanMap[(t1<<1)]=0;
- ModHeader.PanMap[(t1<<1)+1]=0xF;
- sfx_chan[t1]=(t1<<1);
- sfx_chan2[t1]=(t1<<1)+1;
- }
- //Activate up to 4 SFX channels
- StartOutput(MusChans,0);
- StartMusic();
- if(filename!=NULL) {
- str_cpy(mod_playing,filename);
- str_cpy(real_mod_playing,filename);
- }
- mod_active=1;
- restore_map_state_EMS(rob_global_ems);
- SCDoing&=~512;
- return;
- load_no_mod:
- mod_active=actual_num_sfx=0;
- if(filename!=NULL) {
- str_cpy(mod_playing,filename);
- str_cpy(real_mod_playing,filename);
- }
- restore_map_state_EMS(rob_global_ems);
- SCDoing&=~512;
- return;
- }
- void end_mod(void) {
- load_mod(NULL);
- }
- //Sample allocation info-
- //Number of samples minimum cacheable (must be min. 4)
- #define NUM_SAM_CACHE 16
- //For sample allocation into MOD memory
- SamHeader SamHead[NUM_SAM_CACHE];
- //Allocated pointers (NULL does NOT mean NOT LOADED. see SamPlayed)
- char far *SamStorage[NUM_SAM_CACHE];
- //Filenames
- char SamNames[NUM_SAM_CACHE][13];
- //Number of times played (0=NOT LOADED)
- int SamPlayed[NUM_SAM_CACHE];
- //SFX Channel currently playing on plus one (0=none) 1-4
- char SamChannel[NUM_SAM_CACHE];
- //Sam numbers stored in, in MOD memory- 250 minus cache index
- //Internal func-initialize arrays
- void _init_sam_cache(void) {
- int t1,t2;
- for(t1=0;t1<NUM_SAM_CACHE;t1++) {
- SamStorage[t1]=NULL;
- SamNames[t1][0]=0;
- SamPlayed[t1]=0;
- SamChannel[t1]=0;
- for(t2=0;t2<32;t2++) SamHead[t1].SamName[t2]=0;
- for(t2=0;t2<12;t2++) SamHead[t1].FileName[t2]=0;
- SamHead[t1].EmsHandle=0;
- SamHead[t1].LoopBegin=0;
- SamHead[t1].LoopEnd=0;
- SamHead[t1].Flags=0;
- SamHead[t1].C4Hertz=8363;
- SamHead[t1].Volume=64;
- SamHead[t1].Pan=0xFF;
- SamHead[t1].Length=0;
- SamHead[t1].Segment=0;
- }
- }
- //Internal- Deallocates one sample by cache index
- void _free_sam(char sam_num) {
- if(SamPlayed[sam_num]==0) return;
- SCDoing|=32;
- save_map_state_EMS(rob_global_ems);
- if(SamChannel[sam_num]) {
- //Stop playing
- ChannelPos(sfx_chan[SamChannel[sam_num]-1]+1,65534);
- ChannelPos(sfx_chan2[SamChannel[sam_num]-1]+1,65534);
- ChannelVol(sfx_chan[SamChannel[sam_num]-1]+1,0);
- ChannelVol(sfx_chan2[SamChannel[sam_num]-1]+1,0);
- }
- //Free actual sample from music code
- if(FreeSample(250-sam_num)&255) error("BAD !",2,4,current_pg_seg,1);
- //Free memory (auto freed by FreeSample)
- if(SamStorage[sam_num]!=NULL) SamStorage[sam_num]=NULL;
- //Set other stuff
- SamNames[sam_num][0]=0;
- SamPlayed[sam_num]=0;
- SamChannel[sam_num]=0;
- SamHead[sam_num].Length=0;
- SamHead[sam_num].Segment=0;
- SamHead[sam_num].EmsHandle=0;
- restore_map_state_EMS(rob_global_ems);
- SCDoing&=~32;
- }
- //This function will remove samples from the cache one at a time,
- //starting with least-played and ending with currently-playing.
- //Call it removes ONE sample unless CLEAR_ALL is set. Returns 1
- //if nothing was found to deallocate (IE no further fixes possible)
- char free_sam_cache(char clear_all) {
- //LeastPlayedNotPlaying, LeastPlayedCurrentlyPlaying
- int lpnp,lpcp;
- //Value of the above, IE the number of TIMES played
- int lpnpv,lpcpv;
- int t1;
- SCDoing|=clear_all?64:128;
- next:
- lpnp=lpcp=-1;
- lpnpv=lpcpv=32767;
- for(t1=0;t1<NUM_SAM_CACHE;t1++) {
- if(SamPlayed[t1]<1) continue;//Skip those not loaded
- if(SamChannel[t1]) {
- if(SamPlayed[t1]<lpcpv) {
- lpcpv=SamPlayed[t1];
- lpcp=t1;
- }
- }
- else {
- if(SamPlayed[t1]<lpnpv) {
- lpnpv=SamPlayed[t1];
- lpnp=t1;
- }
- }
- }
- //Any NOT playing?
- if(lpnp>=0) {
- //Yep, deallocate
- _free_sam(lpnp);
- if(clear_all) goto next;
- SCDoing&=~(64|128);
- return 0;
- }
- //No? Any PLAYING!?
- if(lpcp>=0) {
- //Yep, deallocate
- _free_sam(lpcp);
- if(clear_all) goto next;
- SCDoing&=~(64|128);
- return 0;
- }
- //Nope.
- SCDoing&=~(64|128);
- return 1;
- }
- //This one will return the cache index for a given sample filename.
- //If needed it will allocate it, freeing memory if necessary.
- //Returns -1 if out of memory, -2 on error loading sample
- //Increases SamPlayed.
- char _load_sam(char far *file) {
- //First, see if already loaded
- int t1,t2;
- char oldc;
- FILE *fp;
- long siz;
- SCLoadingName=file;
- SCDoing|=256;
- for(t1=0;t1<NUM_SAM_CACHE;t1++) {
- if(!str_cmp(file,SamNames[t1]))
- if(SamPlayed[t1]) break;//Found
- }
- if(t1<NUM_SAM_CACHE) {
- if(SamPlayed[t1]<32700) SamPlayed[t1]++;//No overflow!
- SCDoing&=~256;
- return t1;
- }
- //Oops, must load a new one
- //Find an empty slot
- find_empty:
- for(t1=0;t1<NUM_SAM_CACHE;t1++)
- if(SamPlayed[t1]==0) break;
- if(t1>=NUM_SAM_CACHE) {
- //Create a new slot
- free_sam_cache(0);
- goto find_empty;
- }
- //Now open file and get filesize
- fp=fopen(file,"rb");
- if(fp==NULL) return -2;
- fseek(fp,0,SEEK_END);
- siz=ftell(fp);
- siz&=~1;//Make it even through truncation
- if(siz>65500) siz=65500;//Truncate filesize
- if(siz<4) {
- //Too short
- fclose(fp);
- SCDoing&=~256;
- return -2;
- }
- //Now we work to allocate memory
- if(NULL==(SamStorage[t1]=(char far *)farmalloc(siz))) {
- //Try to free up memory
- free_up_board_memory();
- if(NULL==(SamStorage[t1]=(char far *)farmalloc(siz))) {
- //Free up other samples
- while(!free_sam_cache(0)) {
- SamStorage[t1]=(char far *)farmalloc(siz);
- if(SamStorage[t1]!=NULL) break;
- }
- //Did we not get it?
- if(SamStorage[t1]==NULL) {
- fclose(fp);
- SCDoing&=~256;
- return -1;//Out of memory
- }
- }
- }
- //Allocated. Load sample in.
- fseek(fp,0,SEEK_SET);
- fread(SamStorage[t1],siz,1,fp);
- fclose(fp);
- mem_xor(SamStorage[t1],siz,128);
- //Save name and set played to 1
- //Before saving name, truncate filename if over 12 chars
- if(str_len(file)>12) {
- error("BAD !",2,4,current_pg_seg,3);
- oldc=file[12];
- file[12]=0;
- str_cpy(SamNames[t1],file);
- file[12]=oldc;
- }
- else str_cpy(SamNames[t1],file);
- SamPlayed[t1]=1;
- //Now allocate into actual MOD memory
- save_map_state_EMS(rob_global_ems);
- SamHead[t1].Length=siz;
- SamHead[t1].EmsHandle=0;
- SamHead[t1].Segment=FP_SEG(SamStorage[t1]);
- t2=AllocSample(250-t1,&SamHead[t1]);
- restore_map_state_EMS(rob_global_ems);
- //Free memory?
- if(t2) {
- farfree(SamStorage[t1]);
- SamStorage[t1]=NULL;
- }
- //Error?
- if(t2<2) {
- SCDoing&=~256;
- return t1;
- }
- error("BAD !",2,4,current_pg_seg,2);
- SamPlayed[t1]=0;
- SamNames[t1][0]=0;
- SamHead[t1].Length=0;
- SamHead[t1].Segment=0;
- SamHead[t1].EmsHandle=0;
- SCDoing&=~256;
- return -2;
- }
- //Quick Set SamChannel array to clear all instances
- //of something being played on given channel (doesn't STOP playback)
- void _clear_cache_chan(char channel) {
- int t1;
- for(t1=0;t1<NUM_SAM_CACHE;t1++)
- if(SamChannel[t1]==channel+1) SamChannel[t1]=0;
- }
- //Old frequencies - 214 = C-3 (c-5 now), INCREASE octave = half
- //New frequencies - 16770 = C-3 (c-5 now), DECREASE octave = half
- //Equation NEW=3588780/OLD
- void play_sample(int freq,char far *file) {
- long conv;
- int sample;
- if(!mod_active) return;
- if(!music_device) return;
- if(music_device==6) return;
- if(!actual_num_sfx) return;
- if(!freq) return;
- SCDoing|=1;
- conv=(3588780L/(long)freq);
- if(conv>65535U) conv=65535U;
- sample=_load_sam(file);
- if(sample<0) {
- //Error
- if(error_mode<2) {
- switch(sample) {
- case -1://Out of memory for SAM 0x2A01
- error("Out of memory for SAM",1,24,current_pg_seg,0x2A01);
- break;
- case -2://Error loading SAM 0x3501
- error("Error loading SAM",1,24,current_pg_seg,0x3501);
- break;
- }
- }
- SCDoing&=~1;
- return;
- }
- _clear_cache_chan(current_sfx);
- SamChannel[sample]=current_sfx+1;
- save_map_state_EMS(rob_global_ems);
- PlaySample(sfx_chan[current_sfx]+1,250-sample,conv,sound_gvol<<3,0xFF);
- PlaySample(sfx_chan2[current_sfx++]+1,250-sample,conv,sound_gvol<<3,0xFF);
- restore_map_state_EMS(rob_global_ems);
- if(current_sfx>=actual_num_sfx) current_sfx=0;
- SCDoing&=~1;
- }
- void end_sample(void) {
- int t1;
- if(!mod_active) return;
- if(!music_device) return;
- if(music_device==6) return;
- if(!actual_num_sfx) return;
- SCDoing|=2;
- save_map_state_EMS(rob_global_ems);
- for(t1=0;t1<actual_num_sfx;t1++) {
- ChannelVol(sfx_chan[t1]+1,0);
- ChannelVol(sfx_chan2[t1]+1,0);
- }
- for(t1=0;t1<NUM_SAM_CACHE;t1++)
- SamChannel[t1]=0;
- restore_map_state_EMS(rob_global_ems);
- SCDoing&=~2;
- }
- void jump_mod(int order) {
- if(!music_device) return;
- if(!mod_active) return;
- SCDoing|=4;
- save_map_state_EMS(rob_global_ems);
- MusicOrder(order);
- restore_map_state_EMS(rob_global_ems);
- SCDoing&=~4;
- }
- void volume_mod(int vol) {
- if(!music_device) return;
- if(!mod_active) return;
- SCDoing|=8;
- save_map_state_EMS(rob_global_ems);
- MusicVolume((vol*music_gvol)>>5);
- restore_map_state_EMS(rob_global_ems);
- SCDoing&=~8;
- }
- void mod_exit(void) {
- if(!music_device) return;
- free_sam_cache(1);
- if(mod_active>0) {
- StopMusic();
- StopOutput();
- UnloadModule();
- }
- mod_active=0;
- FreeMSE();
- music_device=0;
- }
- void music_off(void) {
- if(!music_device) return;
- free_sam_cache(1);
- save_map_state_EMS(rob_global_ems);
- if(mod_active>0) {
- StopMusic();
- StopOutput();
- UnloadModule();
- }
- mod_active=0;
- restore_map_state_EMS(rob_global_ems);
- }
- void mod_init(void) {
- if(!music_device) return;
- //Concatenate path of base MZX directory-
- str_cat(mzx_blank_mod_file,"MZXBLANK.FIL");
- str_cat(mzx_convert_mod_file,"MZX_CMOD.FIL");
- str_cat(MSE_file,music_MSEs[music_device]);
- ErrorFlag=LoadMSE(MSE_file,0,mixing_rate,4096,&Addr,&IRQ,&DMA);
- switch(ErrorFlag) {
- case 1:
- music_device=0;
- error("Sound card I/O address detection failure",2,20,
- current_pg_seg,0x3801);
- case 2:
- music_device=0;
- error("Sound card IRQ level detection failure",2,20,
- current_pg_seg,0x3901);
- case 3:
- case 4:
- music_device=0;
- error("Sound card DMA channel detection failure",2,20,
- current_pg_seg,0x3A01+ErrorFlag-3);
- case 10:
- case 11:
- music_device=0;
- error("Error loading MSE music driver",2,20,
- current_pg_seg,0x3C01+ErrorFlag-10);
- case 12:
- music_device=0;
- error("MVSOUND.SYS must be loaded for PAS support",2,20,
- current_pg_seg,0x3D01);
- default:
- music_device=0;
- error("Error initializing sound card/music code",2,20,
- current_pg_seg,0x3B01+ErrorFlag-6);
- case 0:
- break;
- }
- end_mod();
- _init_sam_cache();
- }
- //Old frequencies - 214 = C-3 (c-5 now), INCREASE octave = half
- //New frequencies - 16770 = C-3 (c-5 now), DECREASE octave = half
- //Equation NEW=3588780/OLD
- void spot_sample(int freq,int sample) {
- long conv;
- if(!freq) return;
- if(!music_device) return;
- if(!actual_num_sfx) return;
- if(!mod_active) return;
- SCDoing|=16;
- conv=(3588780L/(long)freq);
- if(conv>65535U) conv=65535U;
- _clear_cache_chan(current_sfx);
- save_map_state_EMS(rob_global_ems);
- PlaySample(sfx_chan[current_sfx]+1,sample,conv,sound_gvol<<3,0xFF);
- PlaySample(sfx_chan2[current_sfx++]+1,sample,conv,sound_gvol<<3,0xFF);
- restore_map_state_EMS(rob_global_ems);
- if(current_sfx>=actual_num_sfx) current_sfx=0;
- SCDoing&=~16;
- }
- typedef unsigned char byte;
- typedef unsigned int word;
- typedef unsigned long dword;
- //Converts a mod
- int MODSamC4Hertz[16]={
- 8363, 8424, 8485, 8547, 8608, 8671, 8734, 8797, 7894,
- 7951, 8009, 8067, 8125, 8184, 8244, 8303 };
- GDMHeader GDMHead={//Pre-init as much data as possible
- { 'G','D','M','þ' },
- { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 13,10,26 },
- { 'G','M','F','S' },
- 1,
- 0,
- 115,
- 2,
- 5,
- { 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
- 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 },
- 64,
- 6,
- 125,
- 1,
- 0,0,
- 0,0,
- 0,0,0,
- 0,0,
- 0,0,
- 0,0 };
- SamHeader2 SamHead2[31]={//Pre-init as much data as possible :)
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 },
- { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- { 0,0,0,0,0,0,0,0,0,0,0,0 },0,0,0,0,0,0,0,255 } };
- byte far patdata[2624];//Allocate at runtime, later. (dest info for GDM)
- byte far Music[2048];//Same. (orig pattern info)
- //Periods for standard mod notes
- int PT[60]={
- 1712, 1616, 1525, 1440, 1357, 1281, 1209, 1141, 1077, 1017, 961, 907,
- 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,
- 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226,
- 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113,
- 107, 101, 95, 90, 85, 80, 76, 71, 67, 64, 60, 57 };
- //GDM note value for each of these 60 "notes"
- byte Note2GDM[60]={
- 33,34,35,36,37,38,39,40,41,42,43,44,
- 49,50,51,52,53,54,55,56,57,58,59,60,
- 65,66,67,68,69,70,71,72,73,74,75,76,
- 81,82,83,84,85,86,87,88,89,90,91,92,
- 97,98,99,100,101,102,103,104,105,106,107,108 };
- //Generic order for channel mapping
- byte DefPan[32]={ 15,0,0,15,15,0,0,15,15,0,0,15,
- 15,0,0,15,15,0,0,15,15,0,0,15,15,0,0,15,15,0,0,15 };
- //Uses DOS file handle; returns handle to new file.
- //Always closes old file. Returns -1 on error.
- int ConvertMOD(int file,int type) {
- int tmp,tmp2,sam,NOP,patpos,pos,Note,LastFit;
- byte MaxChan,Patt,Row,Channel,FX1,FX1Data,NumChan,NumSam,
- Byte1,Byte2,Byte3,GDMNote,GDMIns,Chan,Ins,EvPos;
- byte Events[20];
- //For sample copy
- byte far *buff=NULL;
- word chunk=60000,chunk2;
- long len;
- char id[5];
- int destfile=open(mzx_convert_mod_file,O_CREAT|O_TRUNC|O_RDWR|O_BINARY,
- S_IREAD|S_IWRITE);
- if(destfile==-1) {
- close(file);
- return -1;
- }
- NumSam=30;
- switch(type) {
- case FT_4MOD:
- NumChan=3;
- break;
- case FT_8MOD:
- NumChan=7;
- break;
- default:
- //Determine number of channels
- lseek(file,1080,SEEK_SET);
- read(file,id,4);
- if(id[3]=='N') NumChan=id[0]-'1';//1 through 9 channels
- else NumChan=(id[1]-'1')+((id[0]-'0')*10);//10 and up channels
- break;
- }
- lseek(destfile,sizeof(GDMHeader),SEEK_SET);
- //Header conversion
- mem_cpy(GDMHead.PanMap,DefPan,32);
- GDMHead.NOS=NumSam;
- //Sample header conversion
- lseek(file,20,SEEK_SET);
- GDMHead.SamHeadOffset=tell(destfile);
- for(sam=0;sam<=NumSam;sam++) {
- //Convert sample header #sam
- tmp2=4;
- lseek(file,22,SEEK_CUR);
- read(file,Events,8);
- asm rol word ptr Events,8
- asm rol word ptr Events+4,8
- asm rol word ptr Events+6,8
- SamHead2[sam].Length=((unsigned int *)Events)[0]<<1;
- SamHead2[sam].C4Hertz=MODSamC4Hertz[Events[2]&0xF];
- if((SamHead2[sam].Volume=Events[3])>64) SamHead2[sam].Volume=64;
- SamHead2[sam].LoopBegin=((unsigned int *)Events)[2]<<1;
- SamHead2[sam].LoopEnd=SamHead2[sam].LoopBegin+
- (((unsigned int *)Events)[3]<<1)+1;
- if((SamHead2[sam].LoopEnd-SamHead2[sam].LoopBegin)>8) tmp2|=1;
- if(SamHead2[sam].LoopEnd>SamHead2[sam].Length)
- SamHead2[sam].LoopEnd=SamHead2[sam].Length+1;
- SamHead2[sam].Flags=tmp2;
- }
- for(tmp=NumSam;tmp>=0;tmp--)
- if((SamHead2[tmp].SamName[0])||(SamHead2[tmp].Length)) break;
- GDMHead.NOS=tmp;
- for(sam=0;sam<=GDMHead.NOS;sam++)
- write(destfile,&SamHead2[sam],sizeof(SamHeader2));
- //Order conversion
- read(file,patdata,130);
- GDMHead.NOO=patdata[0]-1;
- GDMHead.OrdOffset=tell(destfile);
- NOP=0;
- for(tmp=2;tmp<130;tmp++)
- if(patdata[tmp]>NOP) NOP=patdata[tmp];
- GDMHead.NOP=NOP;
- write(destfile,&patdata[2],GDMHead.NOO+1);
- //Pattern conversion
- lseek(file,154+(NumSam+1)*30,SEEK_SET);
- GDMHead.PatOffset=tell(destfile);
- MaxChan=0;
- for(Patt=0;Patt<=GDMHead.NOP;Patt++) {
- //Convert pattern #Patt
- patpos=2;
- pos=0;
- read(file,Music,(NumChan+1)<<8);
- for(Row=0;Row<64;Row++) {
- for(Channel=0;Channel<=NumChan;Channel++) {
- GDMNote=GDMIns=0;
- Byte1=Music[pos++];
- Byte2=Music[pos++];
- Byte3=Music[pos++];
- FX1Data=Music[pos++];
- Note=((Byte1&15)<<8)+Byte2;
- Ins=(Byte3>>4)+(Byte1&0xF0);
- if((Note)||(Ins)) {
- GDMIns=Ins;
- if(GDMIns>(GDMHead.NOS+1)) GDMIns=0;
- if(Note) {
- LastFit=32767;
- for(tmp=0;tmp<60;tmp++) {
- if(abs(PT[tmp]-Note)<LastFit) {
- GDMNote=Note2GDM[tmp];
- LastFit=abs(PT[tmp]-Note);
- }
- }
- }
- }
- FX1=Byte3&15;
- switch(FX1) {
- //Effects to leave as-is-
- //1 Portamento up
- //2 Portamento down
- //4 Vibrato
- //7 Tremolo
- //9 Sample offset
- //B Jump to order
- //D Pattern break
- case 0://Arpeggio or no-command
- if(FX1Data) FX1=0x10;
- break;
- case 3://Portamento to
- if(GDMNote) GDMNote=((GDMNote-1)|128)+1;
- break;
- case 5://Portamento to+Volume slide
- if(GDMNote) GDMNote=((GDMNote-1)|128)+1;
- if(FX1Data==0) FX1=3;
- break;
- case 6://Vibrato+Volume slide
- if(FX1Data==0) FX1=4;
- break;
- case 8://Pan
- /* if(FX1Data==0xA4) {
- FX1=0x1E;
- FX1Data=1;
- }
- else {
- if(FX1Data<0x80) {
- FX1Data>>=3;
- if(FX1Data>15) FX1Data=15;
- FX1=0x1E;
- FX1Data|=0x80;
- }
- else FX1=FX1Data=0;
- }*/
- FX1=FX1Data=0;
- break;
- case 0xA://Volume Slide
- if(FX1Data==0) FX1=0;
- break;
- case 0xC://Set Volume
- if(FX1Data>64) FX1Data=64;
- break;
- case 0xF://Set Tempo or BPM
- if(FX1Data>31) FX1=0x1F;
- else if(FX1Data==0) FX1=0;
- break;
- case 0xE://Extended effects
- switch(FX1Data>>4) {
- //Effects to leave as-is-
- //1 Fineslide Up
- //2 Fineslide Down
- //3 Glissando Control
- //4 Vibrato Waveform
- //5 Set C-4 finetune
- //6 Patttern Loop
- //7 Tremolo Waveform
- //A Fine Volume up
- //B Fine Volume down
- //C Note Cut
- //E Pattern Delay
- //F Invert Loop
- case 0://0 Set filter- remove
- FX1=0;
- FX1Data=0;
- break;
- case 8://Pan Position
- FX1=0x1E;
- break;
- case 9://Retrigger
- FX1=0x12;
- FX1Data&=0xF;
- if(FX1Data==0) FX1=0;
- break;
- case 0xD://Note Delay
- if(GDMNote) GDMNote=((GDMNote-1)|128)+1;
- else FX1=FX1Data=0;
- break;
- }
- break;
- }
- if((GDMNote)||(GDMIns)||(FX1)) {
- Chan=Channel;
- if(Channel>=MaxChan) MaxChan=Channel+1;
- EvPos=1;
- if((GDMNote)||(GDMIns)) {
- Chan|=32;
- Events[EvPos++]=GDMNote;
- Events[EvPos++]=GDMIns;
- }
- if(FX1) {
- Chan|=64;
- Events[EvPos++]=FX1;
- Events[EvPos++]=FX1Data;
- }
- Events[0]=Chan;
- mem_cpy(&patdata[patpos],Events,EvPos);
- patpos+=EvPos;
- }
- }//Next channel
- patdata[patpos++]=0;
- }//Next row
- patdata[0]=patpos&255;
- patdata[1]=patpos>>8;
- write(destfile,patdata,patpos);
- }//Next pattern
- for(tmp=MaxChan;tmp<32;tmp++)
- GDMHead.PanMap[tmp]=0xFF;
- //Sample conversion
- GDMHead.SamOffset=tell(destfile);
- //Allocate for sample copy
- buff=(byte far *)farmalloc(chunk);
- if(buff==NULL) {
- chunk=farcoreleft()-64;
- if(chunk>=2625) buff=(byte far *)farmalloc(chunk);
- if((buff==NULL)||(chunk<=2624)) {
- buff=patdata;
- chunk=2624;
- }
- }
- for(tmp=0;tmp<=GDMHead.NOS;tmp++) {
- //Convert sample #tmp
- if((len=SamHead2[tmp].Length)!=0) {
- //Copy buffer by buffer
- do {
- if(len<chunk) chunk2=len;
- else chunk2=chunk;
- read(file,buff,chunk2);
- mem_xor(buff,chunk2,128);
- write(destfile,buff,chunk2);
- } while((len-=chunk2)>0);
- }
- }//Next sample
- lseek(destfile,0,SEEK_SET);
- write(destfile,&GDMHead,sizeof(GDMHead));
- //Done!
- if(chunk>2624) farfree(buff);
- close(file);
- return destfile;
- }
- void fix_global_volumes(void) {
- //Call this when music_gvol and/or sound_gvol changes
- //This resets all the volumes properly
- MusicVolume((volume*music_gvol)>>5);
- }
|