{$A+,B+,D-,E-,F-,G+,I-,L-,N-,O-,P-,Q-,R-,S-,T-,V-,X+,Y-}
{AdnMod 0.2 by Beta/Adrenalin.
GUS only
Thanks to:
flap / Capacala for sending me "some" info
Mark Feldham for PCGPE
Mark Dixon for his GUS669 source
Thunder for excellent info about MODs
Tran & Joshua C. Jensen for releasing ultradox
Greets:
Wihannes / Nordic vision
Solar / Hysteria
Psyko / Acidface software
TOP4.ZIP
All users of Metropoli & Starport
}
unit modunit;
interface
uses dos;
const
maxchn = 8; {max # of channels in mod. Lower this, if you run out
of memory}
amp_vol : byte = 14; {amplifying volume. Increasing by one doubles
the volume}
def_pan : byte = 4; {default panning. 0-7}
max_per = 1000; {Max & min period for Amiga limits}
min_per = 20; {not implemented anymore coz of extra octaves}
Base : word = $200; {GUS address}
mod_error : word = 0;
{0 = no error
1 = wrong number of channels
2 = load error
3 = out of pattern memory
255 = other error}
per_table : array[0..15,1..48 ] of word = (
(856,808,762,720,678,640,604,570,538,508,480,453,
428,404,381,360,339,320,302,285,269,254,240,226,
214,202,190,180,170,160,151,143,135,127,120,113,
107,101,95,90,85,80,75,71,67,63,60,56),
(850,802,757,715,674,637,601,567,535,505,477,450,{ : C-1 to B-1 Finetune +1}
425,401,379,357,337,318,300,284,268,253,239,225, { : C-2 to B-2 Finetune +1}
213,201,189,179,169,159,150,142,134,126,119,113, { : C-3 to B-3 Finetune +1}
106,100,94,89,84,79,75,71,67,83,59,56), { : C-4 to B-4 Finetune +1}
(844,796,752,709,670,632,597,563,532,502,474,447,{ : C-1 to B-1 Finetune +2}
422,398,376,355,335,316,298,282,266,251,237,224, { : C-2 to B-2 Finetune +2}
211,199,188,177,167,158,149,141,133,125,118,112, { : C-3 to B-3 Finetune +2}
105, 99, 94, 88, 83, 79, 74, 70, 66, 62, 59, 56),{ : C-4 to B-4 Finetune +2}
(838,791,746,704,665,628,592,559,528,498,470,444,{ : C-1 to B-1 Finetune +3}
419,395,373,352,332,314,296,280,264,249,235,222, { : C-2 to B-2 Finetune +3}
209,198,187,176,166,157,148,140,132,125,118,111, { : C-3 to B-3 Finetune +3}
104, 99, 93, 88, 83, 78, 74, 70, 66, 62, 59, 55),{ : C-4 to B-4 Finetune +3}
(832,785,741,699,660,623,588,555,524,495,467,441,{ : C-1 to B-1 Finetune +4}
416,392,370,350,330,312,294,278,262,247,233,220, { : C-2 to B-2 Finetune +4}
208,196,185,175,165,156,147,139,131,124,117,110, { : C-3 to B-3 Finetune +4}
104, 98, 92, 87, 82, 78, 73, 69, 65, 62, 58, 55), {; C-4 to B-4 Finetune +4}
(826,779,736,694,655,619,584,551,520,491,463,437,{ : C-1 to B-1 Finetune +5}
413,390,368,347,328,309,292,276,260,245,232,219, { : C-2 to B-2 Finetune +5}
206,195,184,174,164,155,146,138,130,123,116,109, { : C-3 to B-3 Finetune +5}
103, 97, 92, 87, 82, 77, 73, 69, 65, 61, 58, 54),{ ; C-4 to B-4 Finetune +5}
(820,774,730,689,651,614,580,547,516,487,460,434,{ : C-1 to B-1 Finetune +6}
410,387,365,345,325,307,290,274,258,244,230,217, { : C-2 to B-2 Finetune +6}
205,193,183,172,163,154,145,137,129,122,115,109, { : C-3 to B-3 Finetune +6}
102, 96, 91, 86, 81, 77, 72, 68, 64, 61, 57, 54),{ : C-4 to B-4 Finetune +6}
(814,768,725,684,646,610,575,543,513,484,457,431,{ : C-1 to B-1 Finetune +7}
407,384,363,342,323,305,288,272,256,242,228,216, { : C-2 to B-2 Finetune +7}
204,192,181,171,161,152,144,136,128,121,114,108, { : C-3 to B-3 Finetune +7}
102, 96, 90, 85, 80, 76, 72, 68, 64, 60, 57, 54),{ : C-4 to B-4 Finetune +7}
(907,856,808,762,720,678,640,604,570,538,504,480,{ : C-1 to B-1 Finetune -8 }
453,428,404,381,360,339,320,302,285,269,254,240, { : C-2 to B-2 Finetune -8 }
226,214,202,190,180,170,160,151,143,135,127,120, { : C-3 to B-3 Finetune -8 }
113,107,101, 95, 90, 85, 80, 75, 71, 67, 63, 60),{ : C-4 to B-4 Finetune -8}
(900,850,802,757,715,675,636,601,567,535,505,477,{ : C-1 to B-1 Finetune -7 }
450,425,401,379,357,337,318,300,284,268,253,238, { : C-2 to B-2 Finetune -7 }
225,212,200,189,179,169,159,150,142,134,126,119, { : C-3 to B-3 Finetune -7 }
112,106,100, 94, 89, 84, 79, 75, 71, 67, 63, 59),{ : C-4 to B-4 Finetune -7}
(894,844,796,752,709,670,632,597,563,532,502,474,{ : C-1 to B-1 Finetune -6 }
447,422,398,376,355,335,316,298,282,266,251,237, { : C-2 to B-2 Finetune -6 }
223,211,199,188,177,167,158,149,141,133,125,118, { : C-3 to B-3 Finetune -6 }
111,105, 99, 94, 88, 83, 79, 74, 70, 66, 62, 59),{ : C-4 to B-4 Finetune -6}
(887,838,791,746,704,665,628,592,559,528,498,470,{ : C-1 to B-1 Finetune -5 }
444,419,395,373,352,332,314,296,280,264,249,235, { : C-2 to B-2 Finetune -5 }
222,209,198,187,176,166,157,148,140,132,125,118, { : C-3 to B-3 Finetune -5 }
111,104, 99, 93, 88, 83, 78, 74, 70, 66, 62, 59),{ : C-4 to B-4 Finetune -5}
(881,832,785,741,699,660,623,588,555,524,494,467,{ : C-1 to B-1 Finetune -4 }
441,416,392,370,350,330,312,294,278,262,247,233, { : C-2 to B-2 Finetune -4 }
220,208,196,185,175,165,156,147,139,131,123,117, { : C-3 to B-3 Finetune -4 }
110,104, 98, 92, 87, 82, 78, 73, 69, 65, 61, 58),{ C-4 to H-4 Finetune -4}
(875,826,779,736,694,655,619,584,551,520,491,463,{ : C-1 to B-1 Finetune -3 }
437,413,390,368,347,338,309,292,276,260,245,232, { : C-2 to B-2 Finetune -3 }
219,206,195,184,174,164,155,146,138,130,123,116, { : C-3 to B-3 Finetune -3 }
109,103, 97, 92, 87, 82, 77, 73, 69, 65, 61, 58),{ C-4 to H-4 Finetune -3}
(868,820,774,730,689,651,614,580,547,516,487,460,{ : C-1 to B-1 Finetune -2 }
434,410,387,365,345,325,307,290,274,258,244,230, { : C-2 to B-2 Finetune -2 }
217,205,193,183,172,163,154,145,137,129,122,115, { : C-3 to B-3 Finetune -2 }
108,102, 96, 91, 86, 81, 77, 72, 68, 64, 61, 57),{ C-4 to H-4 Finetune -2}
(862,814,768,725,684,646,610,575,543,513,484,457,{ : C-1 to B-1 Finetune -1 }
431,407,384,363,342,323,305,288,272,256,242,228, { : C-2 to B-2 Finetune -1 }
216,203,192,181,171,161,152,144,136,128,121,114, { : C-3 to B-3 Finetune -1}
108,101, 96, 90, 85, 80, 76, 72, 68, 64, 60, 57));{ C-4 to H-4 Finetune -1}
gusvol : array[0..64] of word = {volume table}
(0,1750,2503,2701,2741,2781,2944,2964,2981,
3000,3017,3034,3052,3070,3207,3215,3224,
3232,3240,3248,3256,3263,3271,3279,3287,
3294,3303,3310,3317,3325,3458,3462,3466,
3469,3473,3478,3481,3484,3489,3492,3495,
3499,3502,3506,3509,3513,3517,3520,3524,
3528,3532,3534,3538,3543,3545,3549,3552,
3556,3558,3563,3565,3570,3573,3577,3580);
vib_tbl : array[0..2,0..63] of shortint =
((0,6,12,19,24,30,36,41,45,49,53,56,59,61,63,64,
64,64,63,61,59,56,53,49,45,41,36,30,24,19,12,6,
0,-6,-12,-19,-24,-30,-36,-41,-45,-49,-53,-56,-59,-61,-63,-64,
-64,-64,-63,-61,-59,-56,-53,-49,-45,-41,-36,-30,-24,-19,-12,-6),
(-63,-61,-59,-57,-55,-53,-51,-49,-47,-45,-43,-41,-39,-37,-35,-33,
-31,-29,-27,-25,-23,-21,-19,-17,-15,-13,-11,-9,-7,-5,-3,-1,
1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,
33,35,37,39,41,43,45,47,49,51,53,55,57,59,61,63),
(-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64));
type
t_memarray = array[0..2000] of word;
t_channel = record
Vol : byte; {current volume 0-64}
note : byte; {current note 1(C-1) to 48(B-4)}
Per,dper : word; {period & dest. period for tone portamentos}
Sample : byte; {current sample}
Pan : byte; {panning}
fx,fxdata : byte;
fx_sl2,fx_vib : byte; {slide to & vibrato fx-data}
vib_wave : byte; {vibrato waveform}
vib_cnt : byte; {vibrato counter}
trig_cnt : byte; {retrig counter}
arp1,arp2, {arpeggio params}
arp_cnt : byte; {arpeggio counter}
start_fx : byte; {tick to start do_fx for channel}
on : byte; {0 = channel is muted}
bar : byte; {volume bar}
hit : byte;
no_fx : byte {1 = do not get new fx}
end;
t_sample = record
Name : array[1..40] of char;
Addr : longint; {address in GUS mem}
Length : word;
LoopStart,
LoopEnd : word;
ftune : byte;
Volume : byte;
end;
t_note = record
per : word;
note,
sample,
fx,
fxdata : byte;
end;
t_row = array[0..maxchn-1] of t_note;
t_pattern = array[0..63] of t_row;
p_pattern = ^t_pattern;
mod_header = record
name : string[20];
Length : byte;
tag : array[0..3] of char; {M.K.}
chns : byte; {4..12}
samples : byte; {15 / 31}
end;
var
gus_addr : array[0..32] of longint;
periods : array[0..1100] of word;
channels : array[0..maxchn-1] of t_channel;
samples : array[0..32] of t_sample;
patterns : array[0..128] of p_pattern;
orders : array[0..255] of byte; {order list}
max_ptn : word; {# patterns in mod}
cur_ptn,cur_row,cur_tick : byte;
new_ptn,new_row,jump : byte; {used in jumps}
speed,nspeed,tempo : byte;
vblank : boolean; {true = do not use bpm tempos}
header : mod_header;
top_addr : longint; {Next free address in GUS mem}
time_counter : longint; {For syncing with demos. Increments
every 1/18.2 seconds}
time_counter2 : longint; {Increments every tick}
vrt_flag : byte; {if 1 then vertical retrace happened}
Procedure GUSDelay;
Function VoicePos( V : Byte) : Longint;
Function GUSPeek(Loc : Longint) : Byte;
Procedure GUSPoke(Loc : Longint; B : Byte);
Function GUSProbe(adr : word) : Boolean;
Procedure GUSFind;
Function GUSFindMem : Longint;
Procedure GUSSetFreq( V : Byte; hz : Word);
Procedure GUSVoiceControl( V, B : Byte);
Procedure GUSSetBalance( V, Bal : Byte);
Procedure GUSSetVolume( V : Byte; Vol : Word);
Procedure GUSSetLoopMode( V : Byte);
Procedure GUSStopVoice( V : Byte);
Procedure GUSPlayVoice( V, Mode : Byte;VBegin, VStart, VEnd : Longint);
procedure gusrelvoice(v : byte);
procedure GusSetOfs(v : byte;vbegin : longint);
Procedure GUSReset;
procedure gusdeinit;
procedure updatenotes;
procedure start_playing;
procedure stop_playing;
procedure set_timer(ticks : word);
procedure init_mod;
procedure free_mod;
procedure load_mod(s : string;debug : boolean);
implementation
var
oldint : procedure;
int_tick,o_int_tick : word;
int_rate : word;
gus_bank : longint;
misc_buf : array[0..5000] of byte; {buffer used while loading mod}
misc_buf2 : ^t_memarray; {points to misc_buf}
{$i gus.inc}
{$s-}
procedure get_notes;
var
chn : byte;
ptn : byte;
org_sam,sam,note : byte;
st_ofs : longint;
per,dper,vol,freq : word;
_fx,_fxdata : byte;
mute: byte;
_ptn : p_pattern;
procedure prefx;
var
w : word;
_efxdata : byte;
begin
case _fx of
9 : begin
w := _fxdata*$100;
st_ofs := w;
channels[chn].no_fx := 1;
channels[chn].fx := _fx;
channels[chn].fxdata := _fxdata;
end;
$c : begin
if _fxdata > 64 then _fxdata := 64;
vol := _fxdata;
end;
$e : begin
_efxdata := _fxdata and 15;
case _fxdata shr 4 of
4 : begin
channels[chn].fx := _fx;
channels[chn].fxdata := _fxdata;
if _efxdata and 3 < 3 then channels[chn].vib_wave := _efxdata
else channels[chn].vib_wave := 0 or (_efxdata and 4);
end;
$c : if _efxdata and 15 = 0 then begin
mute := 1;
gusstopvoice(chn+1);
end;
$d : if _efxdata > 0 then mute := 2
else mute := 0;
end;
end;
end;
end;
begin
ptn := orders[cur_ptn];
for chn := 0 to header.chns-1 do begin
if channels[chn].fx = 0 then begin
sam := channels[chn].sample;
per := per_table[samples[sam].ftune,
channels[chn].note];
gussetfreq(chn+1,periods[per]);
end;
channels[chn].hit := 0;
if ((patterns[ptn]^[cur_row,chn].per > 0) or
(patterns[ptn]^[cur_row,chn].sample > 0)) then begin
mute := 1;
vol := channels[chn].vol;
per := channels[chn].per;
note := channels[chn].note;
freq := periods[channels[chn].per];
_fx := patterns[ptn]^[cur_row,chn].fx;
_fxdata := patterns[ptn]^[cur_row,chn].fxdata;
org_sam := patterns[ptn]^[cur_row,chn].sample;
channels[chn].start_fx := 0;
channels[chn].trig_cnt := 0;
if org_sam = 0 then begin
sam := channels[chn].sample;
end
else begin
sam := org_sam;
end;
if (_fx = 3) or (_fx = 5) then begin {port to/port to&vol slide}
mute := 1; {dont restart sample}
if patterns[ptn]^[cur_row,chn].note > 0 then begin
note := patterns[ptn]^[cur_row,chn].note;
dper := per_table[samples[sam].ftune,note];
if dper > max_per then dper := max_per;
if dper < min_per then dper := min_per;
channels[chn].dper := dper;
end;
end
else if patterns[ptn]^[cur_row,chn].per > 0 then begin
if patterns[ptn]^[cur_row,chn].note > 0 then begin
note := patterns[ptn]^[cur_row,chn].note;
per := per_table[samples[sam].ftune,note];
end
else if patterns[ptn]^[cur_row,chn].per > 0 then
per := patterns[ptn]^[cur_row,chn].per;
if per > max_per then per := max_per;
if per < min_per then per := min_per;
channels[chn].dper := per;
channels[chn].per := per;
freq := periods[per];
mute := 0;
end;
if org_sam > 0 then begin {should I reset volume}
vol := samples[sam].volume;
if channels[chn].sample <> org_sam then mute := 0;
end;
if samples[sam].length > 0 then st_ofs := 2;
{coz first 2 bytes = amiga loopinfo, discard them}
channels[chn].no_fx := 0;
prefx;
channels[chn].vol := vol;
channels[chn].note := note;
if channels[chn].vib_wave and 4 = 0 then channels[chn].vib_cnt := 0;
channels[chn].sample := sam;
channels[chn].bar := channels[chn].vol;
vol := gusvol[vol]*amp_vol;
if channels[chn].on = 0 then mute := 1;
if mute = 0 then begin
channels[chn].hit := 1;
gussetbalance(chn+1,channels[chn].pan);
if (samples[sam].loopend > 2) then
gusplayall(chn+1,8,gus_addr[sam]+st_ofs,
gus_addr[sam]+samples[sam].loopstart,
gus_addr[sam]+samples[sam].loopend,freq,vol)
else gusplayall(chn+1,0,gus_addr[sam]+st_ofs,
gus_addr[sam]+st_ofs,
gus_addr[sam]+samples[sam].length,freq,vol);
end
else if (channels[chn].on = 1) and (mute=1) then gussetvolume(chn+1,vol);
end;
end;
end;
procedure get_fx;
var
chn,ptn : byte;
_fx,_fxdata : byte;
_efx,_efxdata : byte;
per : word;
b : byte;
w : word;
begin
ptn := orders[cur_ptn];
new_ptn := cur_ptn;
new_row := cur_row;
jump := 0;
for chn := 0 to header.chns-1 do
if channels[chn].no_fx = 0 then begin
channels[chn].start_fx := 0;
channels[chn].fx := 255;
_fx := patterns[ptn]^[cur_row,chn].fx;
_fxdata := patterns[ptn]^[cur_row,chn].fxdata;
case _fx of
0 : if _fxdata > 0 then begin {Arpeggio}
channels[chn].fx := _fx;
channels[chn].fxdata := _fxdata;
channels[chn].arp1 := _fxdata shr 4;
channels[chn].arp2 := _fxdata and 15;
channels[chn].arp_cnt := 0;
end;
1 : begin {port up}
channels[chn].fx := _fx;
channels[chn].fxdata := _fxdata;
channels[chn].start_fx := 2;
end;
2 : begin {port down}
channels[chn].fx := _fx;
channels[chn].fxdata := _fxdata;
channels[chn].start_fx := 2;
end;
3 : begin {port to}
channels[chn].fx := _fx;
if _fxdata > 0 then begin
channels[chn].fxdata := _fxdata;
channels[chn].fx_sl2 := _fxdata;
end
else channels[chn].fxdata := channels[chn].fx_sl2;
channels[chn].start_fx := 2;
end;
4 : begin {vibrato}
channels[chn].fx := _fx;
b := _fxdata and 15;
if b = 0 then b := channels[chn].fx_vib and 15;
w := b;
b := _fxdata shr 4;
if b = 0 then b := channels[chn].fx_vib shr 4;
w := w or (b shl 4);
b := w;
channels[chn].fxdata := b;
channels[chn].fx_vib := b;
end;
5 : begin {port to & vol slide}
channels[chn].fx := _fx;
if _fxdata and 15 > 0 then
_fxdata := _fxdata and 15; {if both ways, then slide down}
channels[chn].fxdata := _fxdata;
end;
6 : begin {Vibrato & vol slide}
channels[chn].fx := _fx;
channels[chn].fxdata := _fxdata;
end;
7 : begin {Tremolo}
channels[chn].fx := _fx;
channels[chn].fxdata := _fxdata;
end;
8 : begin {Set panning}
channels[chn].fx := _fx;
channels[chn].fxdata := _fxdata;
end;
9 : begin {set sample offset}
channels[chn].fx := _fx;
channels[chn].fxdata := _fxdata;
w := _fxdata * 256;
b := channels[chn].sample;
if channels[chn].on = 1 then gussetofs(chn+1,gus_addr[b]+w);
end;
$a : begin {volume slide}
channels[chn].fx := _fx;
if _fxdata and 15 > 0 then
_fxdata := _fxdata and 15; {if both ways, then slide up}
channels[chn].fxdata := _fxdata;
channels[chn].start_fx := 2;
end;
$b : begin {position jump}
if _fxdata < max_ptn then begin
new_ptn := _fxdata;
new_row := 0;
jump := 1;
end;
end;
$c : begin {Set volume}
if _fxdata > 64 then _fxdata := 64;
channels[chn].fx := _fx;
channels[chn].fxdata := _fxdata;
channels[chn].vol := _fxdata;
channels[chn].bar := _fxdata;
w := gusvol[_fxdata {* main_vol}]*amp_vol;
if channels[chn].on = 1 then gussetvolume(chn+1,w);
end;
$d : begin {break pattern}
new_ptn := cur_ptn;
inc(new_ptn);
new_row := ((_fxdata and $f0) shr 4)*10+_fxdata and 15;
jump := 1;
end;
$e : begin {extended effect}
channels[chn].fx := _fx;
channels[chn].fxdata := _fxdata;
_efx := _fxdata shr 4;
_efxdata := _fxdata and 15;
case _efx of
1 : begin
per := channels[chn].per;
inc(per,_efxdata);
if per > max_per then per := max_per;
channels[chn].per := per;
w := periods[channels[chn].per];
gussetfreq(chn+1,w);
end;
2 : begin
per := channels[chn].per;
dec(per,_efxdata);
if per < min_per then per := min_per;
channels[chn].per := per;
w := periods[channels[chn].per];
gussetfreq(chn+1,w);
end;
4 : begin {set vibrato waveform}
channels[chn].vib_wave := _efxdata;
end;
5 : begin {set finetune}
samples[channels[chn].sample].ftune := _efxdata;
end;
8 : begin {set mtm-pan}
channels[chn].pan := _efxdata;
gussetbalance(chn+1,_efxdata);
end;
9 : if _efxdata > 0 then begin {retrigger}
channels[chn].fx := _fx;
channels[chn].fxdata := _fxdata;
channels[chn].trig_cnt := _efxdata;
end;
$a : begin {fine vol slide up}
b := channels[chn].vol;
inc(b,_efxdata);
if b > 64 then b := 64;
channels[chn].vol := b;
w := gusvol[b{*main_vol}]*amp_vol;
if channels[chn].on = 1 then gussetvolume(chn+1,w);
channels[chn].bar := b;
end;
$b : begin {fine vol slide down}
b := channels[chn].vol;
dec(b,_efxdata);
if b > 128 then b := 0;
channels[chn].vol := b;
w := gusvol[b{*main_vol}]*amp_vol;
if channels[chn].on = 1 then gussetvolume(chn+1,w);
channels[chn].bar := b;
end;
$c : begin {cut note}
channels[chn].fx := _fx;
channels[chn].fxdata := _fxdata;
end;
$d : if _efxdata > 0 then begin
channels[chn].fx := _fx;
channels[chn].fxdata := _fxdata;
channels[chn].start_fx := 1+_efxdata;
end
else channels[chn].fx := 255;
end;
end;
$f : begin {set speed}
channels[chn].fx := _fx;
channels[chn].fxdata := _fxdata;
if (_fxdata <= 32) or vblank then begin {SPEED not tempo}
nspeed := _fxdata;
speed := _fxdata;
end
else begin
tempo := _fxdata;
if tempo < 50 then tempo := 50;
int_rate := 1193182 div (tempo*4 div 10);
set_timer(int_rate);
end;
end
else begin
channels[chn].fx := 255;
channels[chn].fxdata := 0;
end;
end;
end
else channels[chn].no_fx := 0;
end;
procedure do_fx;
var
chn : byte;
_fx,_fxdata : byte;
_efx,_efxdata : byte;
per : word;
b : byte;
s : shortint;
w : word;
begin
for chn := 0 to header.chns-1 do if channels[chn].on = 1 then begin
if channels[chn].start_fx > 0 then dec(channels[chn].start_fx);
_fx := channels[chn].fx;
_fxdata := channels[chn].fxdata;
if (channels[chn].on = 1) and (channels[chn].start_fx = 0)
then case _fx of
0 : with channels[chn] do begin
case channels[chn].arp_cnt mod 3 of
0 : gussetfreq(chn+1,
periods[per_table[samples[sample].ftune,note]]);
1 : gussetfreq(chn+1,
periods[per_table[samples[sample].ftune,note+arp1]]);
2 : gussetfreq(chn+1,
periods[per_table[samples[sample].ftune,note+arp2]]);
end;
inc(arp_cnt);
end;
1 : begin {port up}
per := channels[chn].per;
dec(per,_fxdata);
if per < min_per then per := min_per;
channels[chn].per := per;
gussetfreq(chn+1,periods[per]);
end;
2 : begin {port down}
per := channels[chn].per;
inc(per,_fxdata);
if per > max_per then per := max_per;
channels[chn].per := per;
gussetfreq(chn+1,periods[per]);
end;
3 : begin {Port to}
if channels[chn].per < channels[chn].dper then begin
w := channels[chn].dper;
per := channels[chn].per;
inc(per,channels[chn].fx_sl2);
if per > w then per := w;
if per > max_per then per := max_per;
if per < min_per then per := min_per;
channels[chn].per := per;
gussetfreq(chn+1,periods[per]);
end
else begin
w := channels[chn].dper;
per := channels[chn].per;
if per-channels[chn].fx_sl2 > per then per := min_per
else dec(per,channels[chn].fx_sl2);
if per < w then per := w;
if per < min_per then per := min_per;
if per > max_per then per := max_per;
channels[chn].per := per;
gussetfreq(chn+1,periods[per]);
end;
end;
4 : begin
_fxdata := channels[chn].fx_vib;
b := _fxdata and 15;
s := vib_tbl[channels[chn].vib_wave,channels[chn].vib_cnt];
s := (s * b) div 64;
w := channels[chn].per+s;
if w > max_per then w := max_per;
if w < min_per then w := min_per;
b := _fxdata shr 4;
gussetfreq(chn+1,periods[w]);
inc(channels[chn].vib_cnt,b);
if channels[chn].vib_cnt > 63 then
channels[chn].vib_cnt := channels[chn].vib_cnt - 64;
end;
5 : begin
{volume slide}
if _fxdata and 15 > 0 then begin {slide down}
b := channels[chn].vol;
if b-_fxdata >= 0 then dec(b,_fxdata)
else b := 0;
if b > 128 then b := 0;
channels[chn].vol := b;
channels[chn].bar := b;
w := gusvol[b {* main_vol}]*amp_vol;
gussetvolume(chn+1,w);
end
else begin {slide up}
b := channels[chn].vol;
inc(b,_fxdata shr 4);
if b > 64 then b := 64;
channels[chn].vol := b;
channels[chn].bar := b;
w := gusvol[b {* main_vol}]*amp_vol;
gussetvolume(chn+1,w);
end;
_fxdata := channels[chn].fx_sl2;
if channels[chn].per < channels[chn].dper then begin {port to}
w := channels[chn].dper;
per := channels[chn].per;
inc(per,_fxdata);
if per > w then per := w;
if per > max_per then per := max_per;
if per < min_per then per := min_per;
channels[chn].per := per;
gussetfreq(chn+1,periods[per]);
end
else begin
w := channels[chn].dper;
per := channels[chn].per;
if per-_fxdata > per then per := min_per
else dec(per,_fxdata);
if per < w then per := w;
if per < min_per then per := min_per;
if per > max_per then per := max_per;
channels[chn].per := per;
gussetfreq(chn+1,periods[per]);
end;
end;
6 : begin
begin
b := channels[chn].fx_vib and 15;
s := vib_tbl[channels[chn].vib_wave,channels[chn].vib_cnt];
s := (s * b) div 64;
w := channels[chn].per+s;
if w > max_per then w := max_per;
if w < min_per then w := min_per;
b := channels[chn].fx_vib shr 4;
gussetfreq(chn+1,periods[w]);
inc(channels[chn].vib_cnt,b);
if channels[chn].vib_cnt > 63 then
channels[chn].vib_cnt := channels[chn].vib_cnt - 64;
end;
{volume slide}
if _fxdata and 15 > 0 then begin {slide down}
b := channels[chn].vol;
if b-_fxdata >= 0 then dec(b,_fxdata)
else b := 0;
if b > 128 then b := 0;
channels[chn].vol := b;
channels[chn].bar := b;
w := gusvol[b {* main_vol}]*amp_vol;
gussetvolume(chn+1,w);
end
else begin {slide up}
b := channels[chn].vol;
inc(b,_fxdata shr 4);
if b > 64 then b := 64;
channels[chn].vol := b;
channels[chn].bar := b;
w := gusvol[b {* main_vol}]*amp_vol;
gussetvolume(chn+1,w);
end;
end;
$a : begin {volume slide}
if _fxdata and 15 > 0 then begin {slide down}
b := channels[chn].vol;
if b < (_fxdata and 15) then b := 0
else dec(b,_fxdata and 15);
if b > 64 then b := 0;
channels[chn].vol := b;
channels[chn].bar := b;
w := gusvol[b]*amp_vol;
gussetvolume(chn+1,w);
end
else begin {slide up}
b := channels[chn].vol;
inc(b,_fxdata shr 4);
if b > 64 then b := 64;
channels[chn].vol := b;
channels[chn].bar := b;
w := gusvol[b {* main_vol}]*amp_vol;
gussetvolume(chn+1,w);
end;
end;
$e : begin
_efx := _fxdata shr 4;
_efxdata := _fxdata and 15;
case _efx of
9 : begin
b := channels[chn].sample;
dec(channels[chn].trig_cnt);
if channels[chn].trig_cnt = 0 then begin
gussetofs(chn+1,gus_addr[b]+2);
channels[chn].trig_cnt := _efxdata;
end;
end;
$c : if _efxdata = 0 then begin
gussetvolume(chn+1,0);
end
else begin
dec(_efxdata);
b := _fxdata;
b := b and $f0;
b := b or _efxdata;
channels[chn].fxdata := b;
end;
$d : begin
w := channels[chn].sample;
if channels[chn].on = 1 then begin
channels[chn].hit := 1;
gussetbalance(chn+1,channels[chn].pan);
if (samples[w].loopend > 2) then
gusplayall(chn+1,8,gus_addr[w]+2,
gus_addr[w]+samples[w].loopstart,
gus_addr[w]+samples[w].loopend,
periods[channels[chn].per],
gusvol[channels[chn].vol]*amp_vol)
else gusplayall(chn+1,0,gus_addr[w]+2,
gus_addr[w],
gus_addr[w]+samples[w].length+1,
periods[channels[chn].per],
gusvol[channels[chn].vol]*amp_vol);
end;
end;
end;
end;
end;
end;
end;
procedure updatenotes;
var
n : integer;
begin
if cur_ptn > header.length-1 then new_ptn := 0;
cur_ptn := new_ptn;
cur_row := new_row;
if (cur_tick >= speed) and (speed > 0) then begin
speed := nspeed;
cur_tick := 0;
if jump = 0 then inc(cur_row);
if cur_row > 63 then begin
inc(cur_ptn);
cur_row := 0;
if cur_ptn > header.length-1 then begin
new_ptn := 0;
cur_ptn := 0;
end;
end;
end;
for n := 0 to maxchn-1 do begin
if channels[n].bar > 1 then dec(channels[n].bar,2)
else channels[n].bar := 0;
end;
new_ptn := cur_ptn;
new_row := cur_row;
if speed > 0 then begin
inc(cur_tick);
if cur_tick = 1 then begin
get_notes;
if port[$3da] and 8 = 8 then vrt_flag := 1;
get_fx;
end;
if port[$3da] and 8 = 8 then vrt_flag := 1;
do_fx;
if port[$3da] and 8 = 8 then vrt_flag := 1;
end;
end;
procedure modint; interrupt;
{This happens bpm*4/10 times per second (50hz if vblank).}
begin
asm sti end;
if port[$3da] and 8 = 8 then vrt_flag := 1;
inc(time_counter2);
updatenotes;
o_int_tick := int_tick;
int_tick := int_tick + int_rate;
if o_int_tick > int_tick then begin
inc(time_counter);
asm
cli
pushf
call oldint
end;
end
else
asm
mov al,20h
out 20h,al {send EOI}
end;
end;
{$s+}
procedure load_MOD(s : string;debug : boolean);
var
f : file;
procedure set_up_modheader;
var
chn : integer;
begin
header.samples := 31;
header.name[0] := #20;
move(misc_buf[0],header.name[1],20);
header.tag := ' ';
move(misc_buf[1080],header.tag,4);
chn := maxchn;
if header.tag = 'M.K.' then chn := 4
else if header.tag = 'M!K!' then chn := 4
else if header.tag = '6CHN' then chn := 6
else if header.tag = '8CHN' then chn := 8
else if header.tag = '12CH' then chn := 12
else begin
header.samples := 15;
chn := 4;
end;
if chn > maxchn then begin
mod_error := 1;
exit;
end;
if header.samples = 15 then begin
move(misc_buf[472],orders[0],128);
seek(f,600);
header.length := misc_buf[470];
header.chns := 4;
end else begin
header.length := misc_buf[950];
move(misc_buf[952],orders[0],128);
if debug then writeln('Tag: ',header.tag);
end;
header.chns := chn;
end;
procedure mod_sample_info;
var
n : integer;
maxi : integer;
begin
for n := 0 to 31 do begin
fillchar(samples[n].name,22,0);
samples[n].length := 0;
samples[n].ftune := 0;
samples[n].volume := 0;
samples[n].loopstart := 0;
samples[n].loopend := 0;
end;
for n := 1 to header.samples do begin
move(misc_buf[(n-1)*30+20],samples[n].name[1],22);
samples[n].name[23] := #0;
samples[n].length := 2*swap(misc_buf2^[(n-1)*15+21]); {n*30+42}
samples[n].ftune := misc_buf[(n-1)*30+44];
samples[n].volume := misc_buf[(n-1)*30+45];
samples[n].loopstart := 2*swap(misc_buf2^[(n-1)*15+23]); {n*30+46}
samples[n].loopend := 2*swap(misc_buf2^[(n-1)*15+24]); {n*30+48}
if samples[n].loopend < 3 then begin
samples[n].loopend := 0;
samples[n].loopstart := 0;
end;
inc(samples[n].loopend,samples[n].loopstart);
if samples[n].loopend > samples[n].length then
samples[n].loopend := samples[n].length;
end;
end;
procedure read_ptn(n : word);
var
row,note : integer;
w,w2,i : word;
b : byte;
mchn : byte;
begin
mchn := header.chns;
blockread(f,misc_buf,256*mchn);
for row := 0 to 63 do
for note := 0 to mchn-1 do begin
w := misc_buf2^[row*(2*mchn)+note*2];
w2 := misc_buf2^[row*(2*mchn)+note*2+1];
asm
mov cx,w
and cl,15
xchg cl,ch
and cx,0fffh
mov i,cx
end;
patterns[n]^[row,note].per := i;
asm
mov al,byte ptr w2
shr al,4
mov ah,byte ptr w
and ah,11110000b
or al,ah
xor ah,ah
mov i,ax
end;
patterns[n]^[row][note].sample := i;
patterns[n]^[row][note].fx := lo(w2) and 15;
patterns[n]^[row][note].fxdata := hi(w2);
i := patterns[n]^[row,note].per;
w := 0;
b := 0;
while b = 0 do begin
inc(w);
if (w > 48) or (i = per_table[0,w]) then b := 1;
end;
if w <= 48 then patterns[n]^[row,note].note := w
else patterns[n]^[row,note].note := 0;
end;
end;
procedure load_patterns;
var
num_ptn : longint;
n : word;
m_ptn : integer;
begin
if debug then write('Loading patterns');
num_ptn := 0;
for n := 0 to 127 do if orders[n] > num_ptn then begin
if orders[n] > 127 then begin
mod_error := 2;
exit;
end else num_ptn := orders[n];
end;
max_ptn := num_ptn+1;
for n := 0 to max_ptn-1 do begin
if maxavail < 256*header.chns then begin
mod_error := 3; {if error then release memory allocated}
if n >= max_ptn-2 then for n := 0 to max_ptn-2 do dispose(patterns[n]);
exit;
end;
if debug then write('.');
new(patterns[n]);
read_ptn(n);
end;
if debug then writeln;
end;
procedure load2gus(len : word);
var
{n : word;
addlo,addhi : word;}
l : longint;
begin
l := top_addr;
asm
mov di,len
mov si,offset misc_buf
@@1:
{AddLo := L AND $FFFF;
AddHi := longint(L and $ff0000) shr 16;}
mov ax,word ptr l
mov cx,ax {cx=addlo}
mov ax,word ptr l+2
and ax,0ffh
mov bx,ax {bx=addhi}
mov dx,command {Port [command] := $43;}
mov al,43h
out dx,al
mov dx,data_low {Portw[data_low] := AddLo;}
mov ax,cx
out dx,ax
mov dx,command {Port [command] := $44;}
mov al,44h
out dx,al
mov dx,data_high
mov ax,bx
out dx,ax {Port [data_high] := AddHi;}
add word ptr l,1 {inc(l,1);}
adc word ptr l+2,0
mov dx,dram_io {Port [dram_io] := misc_buf[n];}
outsb
dec di
jnz @@1
end;
inc(top_addr,len);
end;
procedure load_sample(num : word);
const
block = 4096;
var
w : word;
fl,l : word;
len : longint;
begin
if debug then write('.');
guspoke(top_addr,0);
inc(top_addr);
guspoke(top_addr,0);
inc(top_addr);
len := samples[num].length+top_addr;
if (len > gus_bank+$40000) and (top_addr < gus_bank+$40000) then begin
gus_bank := gus_bank+$40000;
top_addr := gus_bank;
end;
samples[num].addr := top_addr;
gus_addr[num] := top_addr;
if samples[num].length < 1 then exit;
{blockread(f,w,2);} {read amiga repeat bytes}
fl := (samples[num].length) div block;
l := (samples[num].length) mod block;
if fl > 0 then for w := 1 to fl do begin
blockread(f,misc_buf,block);
load2gus(block); {load in 4kb blocks}
end;
if l > 0 then begin
blockread(f,misc_buf,l);
load2gus(l); {load remainder}
end;
if samples[num].loopend > 2 then begin
guspoke(top_addr,guspeek(gus_addr[num]+samples[num].loopstart));
guspoke(gus_addr[num]+samples[num].loopend+1,
guspeek(gus_addr[num]+samples[num].loopstart));
inc(top_addr);
end;
end;
var
i : integer;
l : longint;
begin
gus_bank := 0;
assign(f,s);
{$i-}
reset(f,1);
blockread(f,misc_buf,1084); {read module header}
i := ioresult;
if i <> 0 then begin
mod_error := 2;
exit;
end;
set_up_modheader;
if mod_error <> 0 then exit;
mod_sample_info;
load_patterns;
if mod_error <> 0 then exit;
if debug then write('Loading samples');
for i := 0 to 31 do load_sample(i);
if debug then writeln;
close(f);
{$i+}
end;
procedure free_mod;
var
n,i : word;
begin
for n := max_ptn-1 downto 0 do dispose(patterns[n]);
top_addr := 16;
for n := 0 to 31 do with samples[n] do begin
addr := 0;
for i := 0 to sizeof(name) do name[i] := #0;
length := 0;
loopstart := 0;
loopend := 0;
ftune := 0;
volume := 0;
end;
gus_bank := 0;
end;
procedure init_mod;
var
n,i : integer;
l : longint;
begin
vrt_flag := 0;
misc_buf2 := @misc_buf;
for n := 10 to 1050 do begin
{gusperiod:=586580935 div (amigaperiod * (divisor div 100 shl 4))}
{divisor = 44100}
{l := 7093789 div (n*2);
l := l div 40;}
l := n;
l := 586580935 div (l * 7056);
periods[n] := l;
{hz = 7093789.2/(per*2)}
end;
for n := 0 to 255 do orders[n] := 0;
for n := 0 to maxchn-1 do begin
channels[n].vol := 64;
channels[n].per := 0;
channels[n].dper := 0;
channels[n].sample := 0;
channels[n].pan := 7; {middle}
channels[n].note := 0;
end;
for n := 0 to 31 do with samples[n] do begin
addr := 0;
for i := 0 to sizeof(name) do name[i] := #0;
length := 0;
loopstart := 0;
loopend := 0;
ftune := 0;
volume := 0;
end;
for n := 0 to 128 do patterns[n] := nil;
for n := 1 to 14 do gussetvolume(n,0);
for n := 1 to 14 do gusstopvoice(n);
cur_ptn := 0;
cur_row := 0;
new_ptn := 0;
new_row := 0;
cur_tick := 0;
for n := 0 to 31 do guspoke(n,0);
top_addr := 16;
gus_bank := 0;
vblank := false;
getintvec(8,@oldint);
end;
procedure set_timer(ticks : word);
begin
asm cli end;
port[$43] := $36;
port[$40] := lo(ticks);
port[$40] := hi(ticks);
asm sti end;
end;
procedure stop_playing;
var
n : integer;
begin
int_rate := 65535;
set_timer(65535);
setintvec(8,@oldint);
for n := 1 to maxchn do GusStopVoice(n);
end;
procedure start_playing;
var
n : integer;
begin
for n := 0 to maxchn-1 do begin
channels[n].vol := 0;
channels[n].per := 428;
channels[n].sample := 0;
channels[n].pan := 7; {middle}
channels[n].on := 1;
channels[n].dper := 428;
channels[n].bar := 0;
channels[n].fx := 255;
channels[n].fxdata := 0;
channels[n].fx_sl2 := 0;
channels[n].fx_vib := 0;
channels[n].vib_cnt := 0;
channels[n].vib_wave := 0;
channels[n].note := 0;
channels[n].hit := 0;
channels[n].no_fx := 0;
channels[n].start_fx := 0;
channels[n].arp1 := 0;
channels[n].arp2 := 0;
channels[n].arp_cnt := 0;
end;
speed := 6;
nspeed := 6;
tempo := 125;
channels[0].pan := 7-def_pan;
channels[1].pan := 7+def_pan;
channels[2].pan := 7+def_pan;
channels[3].pan := 7-def_pan;
if maxchn > 4 then for n := 4 to maxchn-1 do
channels[n].pan := channels[n-4].pan;
if maxchn > 8 then for n := 8 to maxchn-1 do
channels[n].pan := channels[n-8].pan;
jump := 0;
int_tick := 0;
cur_ptn := 0;
cur_row := 0;
new_ptn := 0;
new_row := 0;
cur_tick := 0;
time_counter := 0;
time_counter2 := 0;
asm cli end;
setintvec(8,@modint);
int_rate := 1193182 div 50;
set_timer(int_rate);
asm sti end;
end;
begin
end.