TriOS/lib/adm-hss.spin

738 lines
23 KiB
Plaintext

''*****************************
''* Hydra Sound System v1.2 *
''* (C)2007 Andrew Arsenault *
''*****************************
''http://www.andrewarsenault.com/hss/
''e-mail: ym2413a@yahoo.com
''
'' Cogs used: 2
'' HUB-RAM: ~2.7k
'' Please visit the website for the latest version, documentation, examples and media files.
'' Thank you! --Ym2413a
'' 25.01.2009 Anpassungen für ein komfortableres Interface zur Visualisierung und Steuerung
CON
#0, iEndFlag 'Repeat oder Ende wurde erreicht
iRowFlag 'Flag das Songzeile fertig ist
iEngineC 'Patternzähler
iBeatC 'Beatzähler
iRepeat 'zähler für loops
#5, iChannel
#5, iChannel1
#10, iChannel2
#15, iChannel3
#16, iChannel4
#0, iNote
iOktave
iVolume
iEffekt
iInstrument
VAR
'Interface
word intreg[5 * 5]
'Sound Engine Stack
long hsnd_stack[18]
long cog1, cog2
'WavSynth Parameters
long snd_regs[48] 'Regs for Sound Hardware (8x6)+5dpcm
long dpcm_regs[5]
'DPCM Command Variables
word dpcmreg_ptr
'Global Hmus Player Vars
word tempo
word song_pc
word song_div
word song_ptr
word chfre[4]
byte chfx[4]
byte chvol[4]
byte hmus_state
byte hmvol
byte fxphs
'Sound FX Variables
word runlen[2]
word envamp[2]
word sfx_ptr[2]
byte envphs[2]
byte fmcnt[2], fmfreq[2]
byte loadsfx[2]
CON
'' Hss Master Control
PUB start: okay
okay := cog1 := cognew(@entry, @snd_regs)
okay := cog2 := cognew(hsound, @hsnd_stack)
PUB stop
cogstop(cog2)
cogstop(cog1)
PUB peek(addrptr) : var1
var1 := LONG[@snd_regs][addrptr]
PUB intread(index): wert 'interface: auslesen eines interfaceregisters
wert := intreg[index]
CON
'' Hydra Music Commands
PUB hmus_load(songptr) | z
hmvol := 15
song_div := 0
song_ptr := songptr
song_pc := WORD[songptr][8]
tempo := WORD[songptr][12]
repeat z from 0 to 3
chfx[z] := 0
repeat z from 0 to 5*5 'interface: playerinterface alle werte löschen
intreg[z] := 0
PUB hmus_play
hmus_state := 1
PUB hmus_stop | z
hmus_state := 0
repeat z from 0 to 3
chvol[z] := 0
PUB hmus_pause
hmus_state := 0
PUB hmus_tempo(var1)
tempo := var1
PUB get_hmus_tempo : var1
var1 := tempo
PUB hmus_vol(var1)
hmvol := var1 <# 15 #> 0
PUB get_hmus_vol : var1
var1 := hmvol
CON
'' FXsynth Commands
PUB sfx_play(chan, soundptr)
if(chan == 1)
sfx_ptr[0] := soundptr
loadsfx[0] := 0
if(chan == 2)
sfx_ptr[1] := soundptr
loadsfx[1] := 0
PUB sfx_stop(chan)
if(chan == 1)
sfx_ptr[0] := 0
if(chan == 2)
sfx_ptr[1] := 0
PUB sfx_keyoff(chan)
if(chan == 1)
envphs[0] := 3
if(chan == 2)
envphs[1] := 3
CON
'' Hydra DPCM Commands
PUB dpcm_play(soundptr)
dpcmreg_ptr := soundptr
PUB dpcm_stop
dpcmreg_ptr := 1
CON
''*****************************
''* Hss Sound Engine *
''*****************************
PRI Hsound
repeat
'Update Music Engine
UpdateMus(song_ptr, Hmus_state) 'Update Music Player
'volume/frequenzwerte werden in die soundregister geschrieben
VolumeInterpol 'Delay and Interpolate Volume to Remove Pops and Clicks.
'Update DPCM Engine
if(dpcmreg_ptr)
DpcmUpdate 'Update the DPCM registers
'Update SoundFX Engine
'FX channel A
FXSynth(0,32)
'FX channel B
FXSynth(1, 40)
PRI VolumeInterpol | z, channelmul, musvar, freqval
fxphs += 5
'Volume Interpolation
repeat z from 0 to 3 step 1
channelmul := 4+(8*z)
musvar := (chvol[z]*(hmvol+1))&$F0
snd_regs[channelmul] := (snd_regs[channelmul] & 15)+musvar
'Freq Interpolation
channelmul -= 1 'Jump down a REG to Freq
musvar := chfre[z]<<16
if(chfx[z] == 0) 'None
snd_regs[channelmul] := musvar
elseif(chfx[z] < 3) 'Vibrato (light/hard)
if(fxphs < 128)
snd_regs[channelmul] := musvar+(chfre[z]<<(7+chfx[z]))
else
snd_regs[channelmul] := musvar-(chfre[z]<<(7+chfx[z]))
elseif(chfx[z] == 3) 'Tremolo
if(fxphs < 128)
snd_regs[channelmul] := musvar
else
snd_regs[channelmul] := musvar<<1
else 'Portamento
freqval := snd_regs[channelmul]>>16
if(freqval & $F000 == chfre[z] & $F000)
snd_regs[channelmul] := musvar
elseif(freqval < chfre[z])
snd_regs[channelmul] := snd_regs[channelmul]+(chfx[z]<<22)
else
snd_regs[channelmul] := snd_regs[channelmul]-(chfx[z]<<22)
PRI UpdateMus(songptr, state) | i, channel, channelmul, scrdat, freq, freqoct, flag
if(state == 0)
return ''Song is not playing.
song_div++ 'zeitfaktor; wird erhöht bis...
if(song_div => tempo) 'Tempo Divider 'schwellwert erreicht, dann nächster beat
song_div := 0
flag := 0
intreg[iBeatC] := intreg[iBeatC] + 1 'interface: beatconter erhöhen
intreg[iRowFlag] := 0 'interface: Kennung das Zeile bearbeitet wird
repeat i from 5 to 5*5 'interface: channelwerte löschen
intreg[i] := 0
repeat 'Score Decoder and Processor
scrdat := BYTE[song_ptr][song_pc] 'song_pc ist zeiger auf wert in MusicDat
channel := scrdat & 3 'untere zwei bit enthalten die kanalnummer
channelmul := channel<<3 'jedem channel sind 8 registerwerte zugeordent
intreg[iEngineC] := song_pc 'interface: enginecounter setzen
song_pc++ 'zeiger auf nächsten wert setzen
''Base Commands
if(scrdat == 0) 'End Row 'nächste trackerzeile
intreg[iRowFlag] := 1 'interface: Zeile fertig bearbeitet
quit
if(scrdat == 1) 'Repeat Song 'wiederholt ab MusicLoop (MusicDat ist also die einleitung)
song_pc := WORD[songptr][9]
intreg[iRepeat] := intreg[iRepeat] + 1 'interface: flag das songende erreicht wurde
quit
if(scrdat == 2) 'End Song 'status wird auf 0 gesetzt
intreg[iEndFlag] := 1 'interface: flag das songende erreicht wurde
hmus_stop
quit
if(scrdat == 3) 'Set Flag
flag := 1
next
if((scrdat & $3C) == $20) 'Patch HI Note 'oktave erhöhen und veränderung zu "Change Note"
flag := 2
scrdat := scrdat>>3
scrdat += 64+channel
if(scrdat & 4) 'Change Note
freq := scrdat>>3 'note Bit3 bis Bit7 (32 Noten)
freqoct := freq/12
freq -= freqoct*12
case flag
1 : freqoct += 2
2 : freqoct += 6
other : freqoct += 4
flag := 0
snd_regs[4+channelmul] := snd_regs[4+channelmul] & $FE
intreg[(channel*iChannel)+iChannel+iNote] := freq + 1 'interface: note setzen (0 ist erste note!)
intreg[(channel*iChannel)+iChannel+iOktave] := freqoct 'interface: oktave setzen
'frequenz aus tabelle holen
'je nach oktave wird nach rechts verschoben (/2)
chfre[channel] := NoteFreqs[freq]>>(6-freqoct)
snd_regs[4+channelmul] := (snd_regs[4+channelmul] & $FE)+1
next 'Repeat To Next Datum
if(scrdat & 8) 'Change Evelope / Channel Effect
if(flag)
intreg[(channel*iChannel)+iChannel+iEffekt] := scrdat>>4 + 1 'interface: effektwert setzen
chfx[channel] := scrdat>>4
flag := 0
else
intreg[(channel*iChannel)+iChannel+iVolume] := scrdat>>4 'interface: volume setzen
chvol[channel] := scrdat>>4
next 'Repeat To Next Datum
if(scrdat & 16) 'Change Instrument
freq := (scrdat & $E0)>>3
freq += flag<<5
flag := 0
intreg[(channel*iChannel)+iChannel+iInstrument] := freq>>2 + 1 'interface: instrument setzen
snd_regs[0+channelmul] := songptr+WORD[songptr+32][freq] 'zeiger auf neues instrumentensample
snd_regs[1+channelmul] := WORD[songptr+32][freq+1] 'ende des samples
snd_regs[2+channelmul] := WORD[songptr+32][freq+2] 'loop
snd_regs[4+channelmul] := WORD[songptr+32][freq+3] & $0F 'flags?
next 'Repeat To Next Datum
if(scrdat & 64) 'Detune
chfre[channel] := chfre[channel]+(chfre[channel]>>8)
PRI DpcmUpdate
if(dpcmreg_ptr > 15) 'Play Sample.
dpcm_regs[2] := 65535 'End sample if one was playing
dpcm_regs[0] := dpcmreg_ptr+8
dpcm_regs[4] := 128
dpcm_regs[3] := LONG[dpcmreg_ptr][1] 'Get sampling rate
dpcm_regs[1] := WORD[dpcmreg_ptr][1] 'Get length
dpcm_regs[2] := 0 'Reset play counter
elseif(dpcmreg_ptr == 1) 'Stop Sample
dpcm_regs[2] := 65535 'End sample
dpcm_regs[4] := 128
dpcmreg_ptr := 0
PRI FXSynth(SoundVars, ChannelFX) | TimeCnt, SoundFX, Modwav, FMwav, AMwav
TimeCnt := Cnt
SoundFX := sfx_ptr[SoundVars]
if(loadsfx[SoundVars] == 0)
'Setup OSC WaveForm
case BYTE[SoundFX][0]
$00: 'Sine
snd_regs[ChannelFX] := @SineTable
snd_regs[1+ChannelFX] := 64
$01: 'Fast Sine
snd_regs[ChannelFX] := @FastSine
snd_regs[1+ChannelFX] := 32
$02: 'Sawtooth
snd_regs[ChannelFX] := @Sawtooth
snd_regs[1+ChannelFX] := 64
$03: 'Square
snd_regs[ChannelFX] := @SqrTable
snd_regs[1+ChannelFX] := 32
$04: 'Fast Square
snd_regs[ChannelFX] := @FastSqr
snd_regs[1+ChannelFX] := 8
$05: 'Buzz
snd_regs[ChannelFX] := @NoteFreqs
snd_regs[1+ChannelFX] := 24
$06: 'Noise
snd_regs[ChannelFX] := $F002
snd_regs[1+ChannelFX] := 3000
snd_regs[2+ChannelFX] := 0
snd_regs[4+ChannelFX] := $01
loadsfx[SoundVars] := 1
runlen[SoundVars] := 0
fmcnt[SoundVars] := 0
fmfreq[SoundVars] := 0
envamp[SoundVars] := 0
envphs[SoundVars] := 0
''Modulation Code
fmfreq[SoundVars]++
if(fmfreq[SoundVars] => BYTE[SoundFX][4])
fmfreq[SoundVars] := 0
fmcnt[SoundVars]++
fmcnt[SoundVars] := fmcnt[SoundVars] & $3F
case BYTE[SoundFX][5]
$00:
Modwav := BYTE[@SineTable][fmcnt[SoundVars]]
$01:
Modwav := BYTE[@FastSine][fmcnt[SoundVars] & 31]
$02:
Modwav := fmcnt[SoundVars]<<2
$03:
Modwav := !fmcnt[SoundVars]<<2
$04:
if(fmcnt[SoundVars] & 8)
Modwav := $ff
else
Modwav := $00
$05:
Modwav := BYTE[$F002][fmcnt[SoundVars]]
$FF:
Modwav := BYTE[SoundFX+12][fmcnt[SoundVars] & 15]
fmwav := Modwav/(BYTE[SoundFX][6]) 'FM amount
amwav := 256-(Modwav/(BYTE[SoundFX][7])) 'AM amount
amwav := (BYTE[SoundFX][3]*amwav)>>8
''Envelope Generator
if(envphs[SoundVars] == 0) 'Attack
envamp[SoundVars] += BYTE[SoundFX][8]
if(envamp[SoundVars] > 8191)
envamp[SoundVars] := 8191
envphs[SoundVars] := 1
if(BYTE[SoundFX][8] == $ff)
envamp[SoundVars] := 8191
if(envphs[SoundVars] == 1) 'Decay
envamp[SoundVars] -= BYTE[SoundFX][9]
if(envamp[SoundVars] & $8000)
envphs[SoundVars] := 2
if(envamp[SoundVars] =< (BYTE[SoundFX][10]<<5))
envphs[SoundVars] := 2
if(envphs[SoundVars] == 2) 'Sustain
envamp[SoundVars] := (BYTE[SoundFX][10]<<5)
if(envphs[SoundVars] == 3) 'Release
envamp[SoundVars] -= BYTE[SoundFX][11]
if(envamp[SoundVars] & $8000)
envamp[SoundVars] := 4
amwav := ((envamp[SoundVars]>>9)*(amwav+1))>>4
''Run Length and Outputing
if(SoundFX > 15)
runlen[SoundVars]++
snd_regs[3+ChannelFX] := (BYTE[SoundFX][2]+fmwav)<<24 'Update Frequency
snd_regs[4+ChannelFX] := (amwav<<4)+(snd_regs[4+ChannelFX] & $0F) 'Update Amplitude
else
snd_regs[4+ChannelFX] := $00 'Mute
if(BYTE[SoundFX][1] == $ff) '$ff = never stop
runlen[SoundVars] := 0
if(runlen[SoundVars] > (BYTE[SoundFX][1]<<5)) 'Duration KeyOff
envphs[SoundVars] := 3
WaitCnt(TimeCnt + 52_000) ''Delay for Synth Engine Update.
DAT
SineTable byte $80, $8c, $98, $a5, $b0, $bc, $c6, $d0
byte $da, $e2, $ea, $f0, $f5, $fa, $fd, $fe
byte $ff, $fe, $fd, $fa, $f5, $f0, $ea, $e2
byte $da, $d0, $c6, $bc, $b0, $a5, $98, $8c
byte $80, $73, $67, $5a, $4f, $43, $39, $2f
byte $25, $1d, $15, $0f, $0a, $05, $02, $01
byte $00, $01, $02, $05, $0a, $0f, $15, $1d
byte $25, $2f, $39, $43, $4f, $5a, $67, $73
Sawtooth byte $ff, $fb, $f7, $f3, $ef, $eb, $e7, $e3
byte $df, $db, $d7, $d3, $cf, $cb, $c7, $c3
byte $bf, $bb, $b7, $b3, $af, $ab, $a7, $a3
byte $9f, $9b, $97, $93, $8f, $8b, $87, $83
byte $80, $7c, $78, $74, $70, $6c, $68, $64
byte $60, $5c, $58, $54, $50, $4c, $48, $44
byte $40, $3c, $38, $34, $30, $2c, $28, $24
byte $20, $1c, $18, $14, $10, $0c, $08, $04
FastSine byte $80, $98, $b0, $c6, $da, $ea, $f5, $fd
byte $ff, $fd, $f5, $ea, $da, $c6, $b0, $98
byte $80, $67, $4f, $39, $25, $15, $0a, $02
byte $00, $02, $0a, $15, $25, $39, $4f, $67
SqrTable byte $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff
byte $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff
byte $00, $00, $00, $00, $00, $00, $00, $00
byte $00, $00, $00, $00, $00, $00, $00, $00
FastSqr byte $ff, $ff, $ff, $ff, $00, $00, $00, $00
'Note LookupTable.
NoteFreqs word $85F3, $8DEA, $965B, $9F4B, $A8C4, $B2CD, $BD6F, $C8B3, $D4A2, $E147, $EEAC, $FCDE 'Top Octave Lookup
CON
''*****************************
''* WaveTable Synth v1.2 *
''* DPCM Synth v1.1 *
''* (C)2006 Andrew Arsenault *
''*****************************
DAT
org
entry mov dira,Port_Pins 'Setup output pins
mov ctra,Right_ctra 'Setup Right Audio Channel
mov ctrb,Left_ctra 'Setup Left Audio Channel
mov ChlA_wave,#256 'Set channel signals.
mov ChlA_offset,#0 'Set channel's offset.
mov ChlA_counter,#0
mov Time,#10
add Time,cnt 'Prepare for asm type WAITCNT loop.
'MAIN LOOP
update waitcnt Time,Timing_delay 'Wait for CNT = D, then add S into D
'Transfer Sound Registers
mov addrregs,par
mov y,NumberOfChannels
'Fetch Channel's Registers
transferchl rdlong ChlAp_sampptr,addrregs
add addrregs,#4
rdlong ChlAp_sampend,addrregs
add addrregs,#4
rdlong Ch1Ap_samplpp,addrregs
add addrregs,#4
rdlong Ch1Ap_freq,addrregs
add addrregs,#4
rdlong ChlAp_keyon,addrregs
'Fetch Channel's Static Variables
add addrregs,#8
rdlong ChlA_offset,addrregs
add addrregs,#4
rdlong ChlA_counter,addrregs
'Run Synth Engine on Channel
call #wvsynth
'Store Channel's Static Variables (Tucked Center X move to Wave)
wrlong ChlA_counter,addrregs
sub addrregs,#4
sub x,#256
wrlong ChlA_offset,addrregs
sub addrregs,#4
mov ChlA_wave,x 'Doesn't Waste anything doing this.
wrlong ChlA_wave,addrregs
add addrregs,#12
'Loop Until All Channel's Are Done.
djnz y,#transferchl
'Run DPCM Engine
call #dpcm
'Mix Channels Together
mov addrregs,par
mov ChlA_wave,#0
add addrregs,#5*4
mov y,NumberOfChannels
mixchls rdlong x,addrregs
add ChlA_wave,x
add addrregs,#8*4
djnz y,#mixchls
mov x,DPCM_wave 'Add DPCM
shl x,#2
add ChlA_wave,x
shl ChlA_wave,#20 'Convert 12bit singal into a 32bit one.
'Update output Channels then repeat again.
mov frqa,ChlA_wave
mov frqb,ChlA_wave
jmp #update
'-------------------------Dpcm Engine-------------------------'
dpcm mov addrregs,par
add addrregs,#192
rdlong DPCM_address,addrregs 'Start Address
add addrregs,#4
rdlong DPCM_runlen,addrregs 'File Lenght
add addrregs,#4
rdlong DPCM_offset,addrregs 'File Offset
add addrregs,#4
rdlong DPCM_freq,addrregs 'Playback Speed
add addrregs,#4
rdlong DPCM_wave,addrregs 'Waveform Amp
'Check for if keyon/length is set.
cmp DPCM_offset,DPCM_runlen wc
if_ae jmp #mute_dpcm 'End of file
'Freq Timer/Divider and Increase sampling offset
add DPCM_counter,DPCM_freq wc
if_nc jmp #done_dpcm
'Decode DPCM
add DPCM_address,DPCM_offset
rdbyte x,DPCM_address 'Fetch Datum
mov DPCM_delta,x
shr DPCM_delta,#6
mov y,#1
shl y,DPCM_delta
mov DPCM_delta,y
mov y,#1
shl y,DPCM_phs
test x,y wc
if_c add DPCM_wave,DPCM_delta
if_nc sub DPCM_wave,DPCM_delta
add DPCM_phs,#1
cmp DPCM_phs,#6 wc
if_b jmp #done_dpcm
mov DPCM_phs,#0
add DPCM_offset,#1
jmp #done_dpcm
mute_dpcm mov DPCM_wave, #128
done_dpcm mov addrregs,par
add addrregs,#200
wrlong DPCM_offset,addrregs 'File Offset
add addrregs,#8
wrlong DPCM_wave,addrregs 'Wave
dpcm_ret ret
'-----------------------Dpcm Engine End-----------------------'
'-------------------------Sound Engine-------------------------'
'Freq Timer/Divider and Increase sampling offset
wvsynth add ChlA_counter,Ch1Ap_freq wc
if_c add ChlA_offset,#1
'Reset sample position and lock at zero if Keyoff.
test ChlAp_keyon,#%0001 wc
if_nc mov ChlA_offset,#0
'Reset(loop) if needed
cmp ChlA_offset,ChlAp_sampend wc
if_ae mov ChlA_offset,Ch1Ap_samplpp
'Check BitRate and Set Offset
mov x,ChlA_offset
test ChlAp_keyon,#%0010 wc
if_c shr x,#1
'Fetch WaveTable
mov ChlA_wave,ChlAp_sampptr
add ChlA_wave,x
rdbyte ChlA_wave,ChlA_wave
'Check BitRate and Skip if 8bit
test ChlAp_keyon,#%0010 wc
if_nc jmp #skip_4bitsam
'Convert 4bit to 8bit
test ChlA_offset,#%0001 wc
if_c shr ChlA_wave,#4
if_nc and ChlA_wave,#%00001111
mov x,ChlA_wave
shl ChlA_wave,#4
add ChlA_wave,x
'Center Amplitude and mute if Keyoff.
skip_4bitsam test ChlAp_keyon,#%0001 wc
if_nc mov ChlA_wave,#128
'Volume Multiply
mov x,#0
test ChlAp_keyon,#%10000000 wc
if_c add x,ChlA_wave
if_nc add x,#128
shr ChlA_wave,#1
test ChlAp_keyon,#%01000000 wc
if_c add x,ChlA_wave
if_nc add x,#64
add x,#64
shr ChlA_wave,#1
test ChlAp_keyon,#%00100000 wc
if_c add x,ChlA_wave
if_nc add x,#32
add x,#96
shr ChlA_wave,#1
test ChlAp_keyon,#%00010000 wc
if_c add x,ChlA_wave
if_nc add x,#16
add x,#112
'Return Audio as X.
wvsynth_ret ret
'-----------------------Sound Engine End-----------------------'
Port_Pins long %00000000_00000000_00000011_00000000
'- CTR PLL -------- BPIN --- APIN
Right_ctra long %0_00110_000_00000000_000000_000_001000
Left_ctra long %0_00110_000_00000000_000000_000_001001
Timing_delay long 2500 'Sampling Rate = 32,000.00hz
NumberOfChannels long 6
Time res 1
addrregs res 1
x res 1
y res 1
'WaveTable Synth Accumulators
ChlA_wave res 1
ChlA_offset res 1
ChlA_counter res 1
ChlAp_sampptr res 1
ChlAp_sampend res 1
Ch1Ap_samplpp res 1
Ch1Ap_freq res 1
ChlAp_keyon res 1
'DPCM Accumulators
DPCM_wave res 1
DPCM_address res 1
DPCM_offset res 1
DPCM_counter res 1
DPCM_freq res 1
DPCM_runlen res 1
DPCM_phs res 1
DPCM_delta res 1