aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlec Murphy <alec@checksum.fail>2020-04-04 18:02:20 -0400
committerAlec Murphy <alec@checksum.fail>2020-04-04 18:02:20 -0400
commit48e45af06a5fc7f7d1490aae8e307bfed7ac1b6b (patch)
tree30478ee84f0a33e8a7949a699f4c6b359d84871c
parent66964416db8372a5b23e686fad3dde9243ca4328 (diff)
AdLib music support
-rw-r--r--Actor_WorkType.HC4
-rw-r--r--Dialog.HC8
-rw-r--r--Fullscreen_Image.HC2
-rw-r--r--HDAudio.HC992
-rw-r--r--Main.HC5
-rw-r--r--Map.HC4
-rw-r--r--Music.HC118
-rw-r--r--Opl.HC1168
-rw-r--r--Run.HC4
9 files changed, 2294 insertions, 11 deletions
diff --git a/Actor_WorkType.HC b/Actor_WorkType.HC
index cb671ba..43fa145 100644
--- a/Actor_WorkType.HC
+++ b/Actor_WorkType.HC
@@ -21,8 +21,8 @@ U0 actor_wt_133_boss_purple_15411(ActorData *actor)
{
word_2E21E = 1;
actor_add_new(0xf4, player_x_pos - 1, player_y_pos - 5);
- //stop_music();
- //load_music(2);
+ stop_music();
+ load_music(2);
}
if (actor->has_moved_right_flag > 0)
diff --git a/Dialog.HC b/Dialog.HC
index 3c21976..9d830df 100644
--- a/Dialog.HC
+++ b/Dialog.HC
@@ -528,7 +528,7 @@ U0 now_entering_level_n_dialog(U16 level_number)
I64 main_menu() {
set_initial_game_state();
show_one_moment_screen_flag = 0;
- //load_music(0x12);
+ load_music(0x12);
display_fullscreen_image(1);
flush_input();
I64 i, return_to_title;
@@ -575,7 +575,7 @@ I64 main_menu() {
Bt(kbd.down_bitmap, Char2ScanCode(' ')))
{
- //stop_music();
+ stop_music();
show_one_moment_screen_flag = 1;
show_monster_attack_hint = 0;
//play_sfx(0x30);
@@ -588,7 +588,7 @@ I64 main_menu() {
//restore_status = restore_savegame_dialog();
if(restore_status == 1)
{
- //stop_music();
+ stop_music();
return PLAY_GAME;
}
@@ -752,7 +752,7 @@ U0 monster_attack_hint_dialog()
U0 display_score_from_level()
{
- //stop_music();
+ stop_music();
if(num_stars_collected == 0)
{
diff --git a/Fullscreen_Image.HC b/Fullscreen_Image.HC
index 03b1abc..b9882a6 100644
--- a/Fullscreen_Image.HC
+++ b/Fullscreen_Image.HC
@@ -76,7 +76,7 @@ U0 display_fullscreen_image(U8 image_number)
if(image_number != 1 && image_number != 2)
{
- //stop_music();
+ stop_music();
}
fade_to_black_speed_3();
diff --git a/HDAudio.HC b/HDAudio.HC
new file mode 100644
index 0000000..44ac622
--- /dev/null
+++ b/HDAudio.HC
@@ -0,0 +1,992 @@
+Bool audio_sync_begin = FALSE;
+
+class AudioStream
+{
+ I64 rate;
+ I64 channels;
+ I64 bits;
+ U32 *buf;
+ I64 size;
+};
+
+U8 *Mem2MegAlloc(I64 *_pages2Meg,CBlkPool *bp=NULL)
+{/*Alloc 2Meg pages from BlkPool. Don't link to task.
+(Linking to a task means they will be freed when the task dies.)
+It might give you more than you asked for
+so a ptr to a page count is passed.
+
+Return: NULL if out of memory.
+*/
+ I64 i,j,*pte,num=*_pages2Meg;
+ CMemBlk *res=NULL,*m,*m1;
+
+ if (!bp) bp=sys_code_bp;
+ PUSHFD
+ CLI
+ while (LBts(&bp->locked_flags,BPlf_LOCKED))
+ PAUSE
+ num<<=21-MEM_PAG_BITS;
+
+ m=&bp->mem_free_2meg_lst;
+ while (TRUE) {
+ if (!(res=m->next))
+ break;
+ if (res->pags<num)
+ m=res;
+ else {
+ if (res->pags==num) {
+ m->next=res->next;
+ goto am_done;
+ } else {
+ res->pags-=num;
+ res(U8 *)+=res->pags<<MEM_PAG_BITS;
+ res->pags=num;
+ goto am_done;
+ }
+ }
+ }
+
+ m=&bp->mem_free_lst;
+ while (TRUE) {
+ if (!(res=m->next)) {
+ num=0;
+ res=NULL; //Out of memory
+ goto am_done;
+ }
+ if (res->pags<num)
+ m=res;
+ else {
+ if (res->pags==num) {
+ if (res(U8 *)&0x1FFFFF)
+ m=res;
+ else {
+ m->next=res->next;
+ goto am_done;
+ }
+ } else {
+ if (i=(res(U8 *)&0x1FFFFF)>>MEM_PAG_BITS) {
+ j=1<<(21-MEM_PAG_BITS)-i;
+ if (res->pags<num+j)
+ m=res;
+ else if (res->pags==num+j) {
+ res->pags-=num;
+ res(U8 *)+=res->pags<<MEM_PAG_BITS;
+ res->pags=num;
+ goto am_done;
+ } else {
+ m1=res;
+ res(U8 *)+=j<<MEM_PAG_BITS;
+ res->pags=num;
+ m=res(U8 *)+num<<MEM_PAG_BITS;
+ m->pags=m1->pags-num-j;
+ m1->pags=j;
+ m->next=m1->next;
+ m1->next=m;
+ m->mb_signature=MBS_UNUSED_SIGNATURE_VAL;
+ goto am_done;
+ }
+ } else {
+ m=m->next=res(U8 *)+num<<MEM_PAG_BITS;
+ m->next=res->next;
+ m->pags=res->pags-num;
+ m->mb_signature=MBS_UNUSED_SIGNATURE_VAL;
+ res->pags=num;
+ goto am_done;
+ }
+ }
+ }
+ }
+am_done:
+ i=num<<MEM_PAG_BITS;
+ bp->used_u8s+=i;
+ num>>=21-MEM_PAG_BITS;
+ *_pages2Meg=num;
+ m=res;
+ m1=m(U8 *)+i;
+ while (m<m1) {
+ pte=MemPageTable(m);
+ *pte &= ~0x18;
+ InvlPg(m);
+ m(U8 *)+=0x200000;
+ }
+ LBtr(&bp->locked_flags,BPlf_LOCKED);
+ POPFD
+ return res;
+}
+
+U8 *Mem2MegUncachedAlloc(I64 *_pages2Meg,CBlkPool *bp=NULL)
+{/*Alloc 2Meg pages from BlkPool. Don't link to task.
+(Linking to a task means they will be freed when the task dies.)
+It will be marked uncached. It might give you more than you asked for
+so a ptr to a page count is passed.
+
+Return: NULL if out of memory.
+*/
+ CMemBlk *res,*m,*m1;
+ I64 num=*_pages2Meg,*pte;
+ if (res=Mem2MegAlloc(_pages2Meg,bp)) {
+ num=*_pages2Meg;
+ m=res;
+ m1=m(U8 *)+num<<21;
+ while (m<m1) {
+ pte=MemPageTable(m);
+ *pte= *pte& ~0x18 |0x10;
+ InvlPg(m);
+ m(U8 *)+=0x200000;
+ }
+ }
+ return res;
+}
+
+CHeapCtrl *HeapCtrlBPInit(CBlkPool *bp,I64 pags)
+{//Make mem chunk into HeapCtrl and BlkPool.
+ I64 num;
+ CMemBlk *m;
+ CHeapCtrl *hc;
+ MemSet(bp,0,sizeof(CBlkPool)+sizeof(CHeapCtrl));
+ hc=HeapCtrlInit(bp(U8 *)+sizeof(CBlkPool),,bp);
+ m=(bp(U8 *)+sizeof(CBlkPool)+sizeof(CHeapCtrl)+MEM_PAG_SIZE-1)&
+ ~(MEM_PAG_SIZE-1);
+ num=(bp(U8 *)+pags<<MEM_PAG_BITS-m(U8 *))>>MEM_PAG_BITS;
+ bp->alloced_u8s=(pags-num)<<MEM_PAG_BITS;
+ BlkPoolAdd(bp,m,num);
+ return hc;
+}
+
+class CSndWaveCtrl
+{
+ I64 sample_rate,sample_bits,channels;
+ F64 freq_multiplier,amp_multiplier;
+ F64 phase,last_y,last_dydt,next_y;
+};
+
+#define WF_NULL 0
+#define WF_SQUARE 1
+#define WF_SINE 2
+#define WF_TRI 3
+#define WF_SAWTOOTH 4
+#define WF_NOISE 5
+#define WF_WAVEFORMS_NUM 6
+
+//snd devs
+#define SD_PC_SPEAKER 0
+#define SD_HD_AUDIO 1
+
+#define SND_SAMPLE_RATE 48000
+#define SND_SAMPLE_BITS 16
+#define SND_OCHANNELS 2
+#define SND_ICHANNELS 2
+#define SND_OUT_CONTAINER U32
+#define SND_IN_CONTAINER I16
+#define SND_BUF_LEN 0x400
+#define SND_BUF_TIME_mS (SND_BUF_LEN/SND_OCHANNELS*1000.0/\
+ SND_SAMPLE_RATE)
+
+F64 snd_freq=0;
+I64 snd_dev=SD_PC_SPEAKER;
+Bool snd_record=FALSE;
+F64 snd_vol=0.1;
+U0 (*fp_snd)(F64 freq,I64 waveform,F64 amp)=NULL;
+U0 (*fp_snd_record)(F64 freq,I64 waveform,F64 amp)=NULL;
+U0 (*fp_snd_fill_buf)(SND_OUT_CONTAINER *buf,I64 buf_num)=NULL;
+U0 (*fp_snd_copy_buf)(SND_IN_CONTAINER *buf,I64 buf_num)=NULL;
+
+I64 snd_obuf_num,snd_ibuf_num;
+
+#define Sf_FILLING_OUT 0
+I64 snd_flags;
+
+#define HD_1_CHAN 0
+#define HD_2_CHAN 1
+#define HD_3_CHAN 2
+#define HD_4_CHAN 3
+
+#define HD_8_BIT 0
+#define HD_16_BIT 1
+#define HD_20_BIT 2
+#define HD_24_BIT 3
+#define HD_32_BIT 4
+
+#define HD_48kHz 0
+
+#define HD_DFT_OUT_FMT (HD_2_CHAN+HD_16_BIT<<4+HD_48kHz<<8)
+#define HD_DFT_IN_FMT (HD_2_CHAN+HD_16_BIT<<4+HD_48kHz<<8)
+
+#define HD_POS_BUF_MULTIPLES 0x1000
+
+#define HD_CORB_ENTRIES 256
+#define HD_RIRB_ENTRIES 256
+#define HD_BDL_ENTRIES 256
+
+#define HD_GCTL 0x08
+#define HD_STATESTS 0x0E
+#define HD_GSTS 0x10
+#define HD_CORBLBASE 0x40
+#define HD_CORBUBASE 0x44
+#define HD_CORBWP 0x48
+#define HD_CORBRP 0x4A
+#define HD_CORBCTL 0x4C
+#define HD_CORBST 0x4D
+#define HD_RIRBLBASE 0x50
+#define HD_RIRBUBASE 0x54
+#define HD_RIRBWP 0x58
+#define HD_RIRBCTL 0x5C
+#define HD_RIRBSTS 0x5D
+
+#define STRCTL 0x00
+#define STRSTS 0x03
+#define STRLPIB 0x04
+#define STRCBL 0x08
+#define STRLVI 0x0C
+#define STRFIFOW 0x0E
+#define STRFIFOS 0x10
+#define STRFMT 0x12
+#define STRBDPL 0x18
+#define STRBDPU 0x1C
+
+#define ISTR0 0x080
+#define ISTR1 0x0A0
+#define ISTR2 0x0C0
+#define ISTR3 0x0E0
+#define OSTR0 0x100
+#define OSTR1 0x120
+#define OSTR2 0x140
+#define OSTR3 0x160
+
+#define VERB_GET_PARAM 0xF0000
+#define VERB_CONNECT_SEL_GET 0xF0100
+#define VERB_CONNECT_SEL_SET 0x70100
+#define VERB_GET_CONNECT_LST 0xF0200
+#define VERB_PROCESS_STATE_GET 0xF0300
+#define VERB_PROCESS_STATE_SET 0x70300
+#define VERB_COEFF_IDX_GET 0xD0000
+#define VERB_COEFF_IDX_SET 0x50000
+#define VERB_PROCESS_COEFF_GET 0xC0000
+#define VERB_PROCESS_COEFF_SET 0x40000
+#define VERB_AMPLIFIER_GAIN_GET 0xB0000
+#define VERB_AMPLIFIER_GAIN_SET 0x30000
+#define VERB_STREAM_FMT_GET 0xA0000
+#define VERB_STREAM_FMT_SET 0x20000
+#define VERB_DIGIT_CONVERT1_GET 0xF0D00
+#define VERB_DIGIT_CONVERT1_SET 0x70D00
+#define VERB_DIGIT_CONVERT2_GET 0xF0D00
+#define VERB_DIGIT_CONVERT2_SET 0x70E00
+#define VERB_POWER_STATE_GET 0xF0500
+#define VERB_POWER_STATE_SET 0x70500
+#define VERB_CHAN_STREAM_ID_GET 0xF0600
+#define VERB_CHAN_STREAM_ID_SET 0x70600
+#define VERB_SDI_SEL_GET 0xF0400
+#define VERB_SDI_SEL_SET 0x70400
+#define VERB_PIN_WIDGET_CTL_GET 0xF0700
+#define VERB_PIN_WIDGET_CTL_SET 0x70700
+#define VERB_UNSOL_ENABLE_GET 0xF0800
+#define VERB_UNSOL_ENABLE_SET 0x70800
+#define VERB_PIN_SENSE_GET 0xF0900
+#define VERB_PIN_SENSE_SET 0x70900
+#define VERB_EAPDBTL_ENABLE_GET 0xF0C00
+#define VERB_EAPDBTL_ENABLE_SET 0x70C00
+#define VERB_BEEP_CTL_GET 0xF0A00
+#define VERB_BEEP_CTL_SET 0x70A00
+#define VERB_GPI_CTRL0_GET 0xF1000
+#define VERB_GPI_CTRL0_SET 0x71000
+#define VERB_GPI_CTRL1_GET 0xF1100
+#define VERB_GPI_CTRL1_SET 0x71100
+#define VERB_GPI_CTRL2_GET 0xF1200
+#define VERB_GPI_CTRL2_SET 0x71200
+#define VERB_GPI_CTRL3_GET 0xF1300
+#define VERB_GPI_CTRL3_SET 0x71300
+#define VERB_GPI_CTRL4_GET 0xF1400
+#define VERB_GPI_CTRL4_SET 0x71400
+#define VERB_GPI_CTRL5_GET 0xF1500
+#define VERB_GPI_CTRL5_SET 0x71500
+#define VERB_GPI_CTRL6_GET 0xF1600
+#define VERB_GPI_CTRL6_SET 0x71600
+#define VERB_GPI_CTRL7_GET 0xF1700
+#define VERB_GPI_CTRL7_SET 0x71700
+#define VERB_GPI_CTRL8_GET 0xF1800
+#define VERB_GPI_CTRL8_SET 0x71800
+#define VERB_GPI_CTRL9_GET 0xF1900
+#define VERB_GPI_CTRL9_SET 0x71900
+#define VERB_GPI_CTRLA_GET 0xF1A00
+#define VERB_GPI_CTRLA_SET 0x71A00
+#define VERB_VOL_CTL_GET 0xF0F00
+#define VERB_VOL_CTL_SET 0x70F00
+#define VERB_SUB_SYS_ID0_GET 0xF2000
+#define VERB_SUB_SYS_ID0_SET 0x72000
+#define VERB_SUB_SYS_ID1_GET 0xF2000
+#define VERB_SUB_SYS_ID1_SET 0x72100
+#define VERB_SUB_SYS_ID2_GET 0xF2000
+#define VERB_SUB_SYS_ID2_SET 0x72200
+#define VERB_SUB_SYS_ID3_GET 0xF2000
+#define VERB_SUB_SYS_ID3_SET 0x72300
+#define VERB_CFG_DFT0_GET 0xF1C00
+#define VERB_CFG_DFT0_SET 0x71C00
+#define VERB_CFG_DFT1_GET 0xF1C00
+#define VERB_CFG_DFT1_SET 0x71D00
+#define VERB_CFG_DFT2_GET 0xF1C00
+#define VERB_CFG_DFT2_SET 0x71E00
+#define VERB_CFG_DFT3_GET 0xF1C00
+#define VERB_CFG_DFT3_SET 0x71F00
+#define VERB_STRIPE_CTL_GET 0xF2400
+#define VERB_STRIPE_CTL_SET 0x72400
+#define VERB_RST 0x7FF00
+
+//Parameters
+#define P_VENDOR_ID 0x00
+#define P_REVISION_ID 0x02
+#define P_SUBNODE_CNT 0x04
+#define P_FUN_GRP_TYPE 0x05
+#define P_AUDIO_FUN_CAP 0x08
+#define P_AUDIO_WIDGET_CAP 0x09
+#define P_SAMPLE_SIZE_RATE_CAP 0x0A
+#define P_STREAM_FMTS 0x0B
+#define P_PIN_CAP 0x0C
+#define P_INPUT_AMP_CAP 0x0D
+#define P_OUTPUT_AMP_CAP 0x12
+#define P_CONNECT_LST_LEN 0x0E
+#define P_POWER_STATES 0x0F
+#define P_PROCESSING_CAP 0x10
+#define P_GPIO_CNT 0x11
+#define P_VOL_KNOB_CAP 0x13
+
+//Function Group Types
+//00 reserved
+#define FGT_AUDIO 1
+#define FGT_VENDOR_MODEM 2
+//03-7F reserved
+//80-FF vendor function group
+
+//Audio Widget Types
+#define AWT_OUTPUT 0x0
+#define AWT_INPUT 0x1
+#define AWT_MIXER 0x2
+#define AWT_SELECTOR 0x3
+#define AWT_PIN_COMPLEX 0x4
+#define AWT_POWER_WIDGET 0x5
+#define AWT_VOL_KNOB_WIDGET 0x6
+#define AWT_BEEP_GEN_WIDGET 0x7
+#define AWT_VENDOR 0xF
+#define AWT_NODE 0x10
+DefineLstLoad("ST_AUDIO_WIDGET_TYPES",
+"Output\0Input\0Mixer\0Sellector\0Pin Complex\0"
+"Power Widget\0Vol Knob\0Beep Gen\0\0\0\0\0\0\0\0Vendor\0Node\0");
+
+class CHDBufDesc
+{
+ I32 *buf;
+ U32 len;
+ U32 ctrl;
+};
+
+#define HD_TONES 8
+
+class CHDAudioCtrl
+{
+ U8 *bar;
+ CBlkPool *bp;
+ CHeapCtrl *hc;
+ I64 cad;
+ U32 *corb;
+ I64 *rirb;
+ CHDBufDesc *ostr0_bdl,*istr0_bdl;
+ SND_OUT_CONTAINER *ostr0_buf[2],*o_tmp_buf;
+ SND_IN_CONTAINER *istr0_buf[2];
+ CTask *task;
+ I64 waveform;
+ F64 freq,amp;
+ CSndWaveCtrl *tone_swcs[HD_TONES];
+ U8 rirb_rp,corb_wp;
+ Bool audio_task_started,in_running,out_running;
+} hda;
+MemSet(&hda,0,sizeof(CHDAudioCtrl));
+
+F64 SinPhaseCont(F64 last_y,F64 last_dydt,
+ F64 current_amp,F64 phase_offset)
+{//Next sample of sin waveform.
+ F64 phase;
+ phase=last_y/current_amp;
+ if (phase>1.0) phase=1.0;
+ if (phase<-1.0) phase=-1.0;
+ if (last_dydt<0)
+ phase=pi-ASin(phase);
+ else
+ phase=ASin(phase);
+ return phase-phase_offset;
+}
+
+public CSndWaveCtrl *SndWaveCtrlNew(I64 sample_rate=8000,I64 sample_bits=24,
+ I64 channels=2,CTask *mem_task=NULL)
+{//MAlloc ctrl struct for generating waveforms.
+ CSndWaveCtrl *swc=CAlloc(sizeof(CSndWaveCtrl),mem_task);
+ swc->freq_multiplier=1.0;
+ swc->amp_multiplier=1.0;
+ swc->sample_rate=sample_rate;
+ swc->sample_bits=sample_bits;
+ swc->channels=channels;
+ swc->last_dydt=1.0;
+ return swc;
+}
+
+public U0 SndWaveCtrlDel(CSndWaveCtrl *swc)
+{//Free waveform ctrl.
+ Free(swc);
+}
+
+public U0 SndWaveAddBuf(CSndWaveCtrl *swc,U8 *buf,I64 num_samples,
+ F64 _freq,I64 _waveform=WF_SQUARE,F64 _amp=1.0,F64 _left=1.0, F64 _right=1.0)
+{//Add waveform to buffer.
+//num_samples is multiplied by channels to get buf_len.
+ //left,right range from 0.0-1.0
+ //Supports 16,24 and 32 bits
+ I64 reg i,reg j,reg k;
+ F64 a,f,amp,reg phase;
+ if (!swc) return;
+ _freq*=swc->freq_multiplier;
+ _amp*=swc->amp_multiplier;
+ if (!_freq||!_amp) {
+ swc->last_y=swc->phase=0;
+ swc->last_dydt=1.0;
+ } else {
+ phase=swc->phase;
+ i=0;
+ amp=Min(I32_MAX,I32_MAX*_amp);
+ f=2*pi/swc->sample_rate*_freq;
+ switch (_waveform) {
+ case WF_NOISE:
+ a=2.0/pi*amp;
+ break;
+ case WF_SAWTOOTH:
+ a=amp/pi;
+ break;
+ case WF_SINE:
+ phase=SinPhaseCont(swc->last_y,swc->last_dydt,amp,0.0);
+ break;
+ }
+ while (phase<0)
+ phase+=2*pi;
+ while (phase>=2*pi)
+ phase-=2*pi;
+ num_samples*=swc->channels;
+ while (i<num_samples) {
+ switch (_waveform) {
+ case WF_SQUARE:
+ if (phase>=pi)
+ j=-amp;
+ else
+ j=amp;
+ break;
+ case WF_SINE:
+ j=amp*Sin(phase);
+ break;
+ case WF_TRI:
+ if (phase>=pi) {
+ swc->last_y=swc->next_y;
+ swc->next_y=-amp*Sign(swc->last_y)+.00001;
+ phase-=pi;
+ }
+ j=(swc->last_y*(pi-phase)+swc->next_y*phase)/pi;
+ break;
+ case WF_SAWTOOTH:
+ j=a*(phase-pi);
+ break;
+ case WF_NOISE:
+ if (phase<pi) {
+ if (phase<f) {
+ swc->last_y=swc->next_y;
+ swc->next_y=a*RandI16/U16_MAX;
+ }
+ j=swc->last_y*(pi-phase)+swc->next_y*phase;
+ } else {
+ if (phase-pi<f) {
+ swc->last_y=swc->next_y;
+ swc->next_y=a*RandI16/U16_MAX;
+ }
+ j=swc->last_y*(2.0*pi-phase)+swc->next_y*(phase-pi);
+ }
+ break;
+ }
+//left channel
+ k=j*_left;
+ if (swc->sample_bits==16) {
+ k>>=16;
+ buf(I16 *)[i++]+=k;
+ } else {
+ if (swc->sample_bits==24)
+ k&=0xFFFFFF00;
+ buf(I32 *)[i++]+=k;
+ }
+//right channel
+ if (swc->channels==2) {
+ k=j*_right;
+ if (swc->sample_bits==16) {
+ k>>=16;
+ buf(I16 *)[i++]+=k;
+ } else {
+ if (swc->sample_bits==24)
+ k&=0xFFFFFF00;
+ buf(I32 *)[i++]+=k;
+ }
+ }
+ phase+=f;
+ while (phase>=2*pi)
+ phase-=2*pi;
+ }
+ if (_waveform==WF_SINE) {
+ swc->last_y=amp*Sin(phase);
+ swc->last_dydt=Cos(phase);
+ }
+ swc->phase=phase;
+ }
+}
+
+U0 HDSyncCORB()
+{
+ U16 *wp,*rp;
+ wp =hda.bar+HD_CORBWP;
+ *wp=hda.corb_wp;
+ rp =hda.bar+HD_CORBRP;
+ while (*rp&255!=hda.corb_wp)
+ Yield;
+}
+
+U0 HDWriteCORB(I64 cad,I64 nid,U32 val)
+{
+ val|=cad<<28+nid<<20;
+ hda.corb[++hda.corb_wp]=val;
+}
+
+I64 HDSyncRIRB()
+{
+ U16 *_w;
+ I64 wp,res=0;
+ _w=hda.bar+HD_RIRBWP;
+ wp=*_w;
+ while (hda.rirb_rp!=wp)
+ res=hda.rirb[++hda.rirb_rp];
+ return res;
+}
+
+I64 HDReadRIRB()
+{
+ U16 *_w;
+ I64 wp,res=0;
+ _w=hda.bar+HD_RIRBWP;
+ do {
+ Yield;
+ wp=*_w;
+ } while (wp==hda.rirb_rp);
+ res=hda.rirb[++hda.rirb_rp];
+ return res;
+}
+
+I64 HDWriteCORBSync(I64 cad,I64 nid,U32 val)
+{
+ HDSyncCORB;
+ HDSyncRIRB;
+ HDWriteCORB(cad,nid,val);
+ HDSyncCORB;
+ return HDReadRIRB;
+}
+
+Bool HDTestCORBSync(I64 cad,I64 nid,U32 val)
+{ //Checks for a response
+ U16 *_w;
+ I64 wp;
+
+ HDSyncCORB;
+ HDSyncRIRB;
+ HDWriteCORB(cad,nid,val);
+ HDSyncCORB;
+
+ Sleep(1);
+ _w=hda.bar+HD_RIRBWP;
+ wp=*_w;
+ if (wp==hda.rirb_rp)
+ return FALSE;
+ HDReadRIRB;
+ return TRUE;
+}
+
+U0 HDTraverse(I64 cad,I64 nid)
+{
+ I64 i,len,aud_cap,type;
+ HDWriteCORBSync(cad,nid,VERB_POWER_STATE_SET+0x00); //0 is on
+ HDWriteCORBSync(cad,nid,VERB_EAPDBTL_ENABLE_SET+0x02);
+ HDWriteCORBSync(cad,nid,VERB_PROCESS_STATE_SET+0x02);
+ HDWriteCORBSync(cad,nid,VERB_CONNECT_SEL_SET+0x00);
+ aud_cap=HDWriteCORBSync(cad,nid,VERB_GET_PARAM+P_SUBNODE_CNT);
+ if (aud_cap.u16[0]) {
+ for (i=aud_cap.u16[1];i<aud_cap.u16[1]+aud_cap.u16[0];i++)
+ HDTraverse(cad,i);
+ } else {
+ aud_cap=HDWriteCORBSync(cad,nid,VERB_GET_PARAM+P_AUDIO_WIDGET_CAP);
+ type=aud_cap>>20&15;
+ if (Bt(&aud_cap,8))
+ len=HDWriteCORBSync(cad,nid,VERB_GET_PARAM+P_CONNECT_LST_LEN)&127;
+ else
+ len=0;
+ HDWriteCORBSync(cad,nid,VERB_AMPLIFIER_GAIN_SET+0xF07F); //set I/O amp #0
+ for (i=1;i<len;i++)
+//Set IN amps to mute
+ HDWriteCORBSync(cad,nid,VERB_AMPLIFIER_GAIN_SET+0x7080+i<<8);
+ switch (type) {
+ case AWT_OUTPUT:
+ if (FALSE) //if disabled
+ HDWriteCORBSync(cad,nid,VERB_CHAN_STREAM_ID_SET+0x00);
+ else
+ HDWriteCORBSync(cad,nid,VERB_CHAN_STREAM_ID_SET+0x10);
+ HDWriteCORBSync(cad,nid,VERB_STREAM_FMT_SET+HD_DFT_OUT_FMT);
+ HDWriteCORBSync(cad,nid,VERB_PROCESS_STATE_SET+0x01);
+ break;
+ case AWT_INPUT:
+ if (TRUE) //if disabled
+ HDWriteCORBSync(cad,nid,VERB_CHAN_STREAM_ID_SET+0x00);
+ else
+ HDWriteCORBSync(cad,nid,VERB_CHAN_STREAM_ID_SET+0x20);
+ HDWriteCORBSync(cad,nid,VERB_STREAM_FMT_SET+HD_DFT_IN_FMT);
+ HDWriteCORBSync(cad,nid,VERB_PROCESS_STATE_SET+0x01);
+ break;
+ case AWT_PIN_COMPLEX:
+ HDWriteCORBSync(cad,nid,VERB_PIN_WIDGET_CTL_SET+0xE2);
+ break;
+ }
+ }
+}
+
+U0 HDRun(Bool in,Bool out)
+{
+ U32 *_d;
+ if (hda.bar) {
+ if (out) {
+ _d=hda.bar+OSTR0+STRCTL;
+ *_d=0x100002;
+ hda.out_running=TRUE;
+ }
+ if (in) {
+ _d=hda.bar+ISTR0+STRCTL;
+ *_d=0x200002;
+ hda.in_running=TRUE;
+ }
+ }
+}
+
+U0 HDStop(Bool in,Bool out)
+{
+ U32 *_d;
+ if (hda.bar) {
+ if (out) {
+ _d=hda.bar+OSTR0+STRCTL;
+ *_d=0;
+ hda.out_running=FALSE;
+ }
+ if (in) {
+ _d=hda.bar+ISTR0+STRCTL;
+ *_d=0;
+ hda.in_running=FALSE;
+ }
+ }
+}
+
+U0 HDSnd(F64 freq,I64 waveform=WF_SQUARE,F64 amp=1.0)
+{
+ hda.waveform=waveform;
+ hda.amp=amp;
+ hda.freq=freq;
+}
+
+U32 *hd_buf=NULL;
+I64 hd_buf_pos=0;
+I64 hd_buf_size=0;
+
+U0 HDFillBuf(SND_OUT_CONTAINER *buf,I64)
+{
+ I64 i;
+ for (i=0;i<SND_BUF_LEN;i++)
+ {
+ buf[i]=0;
+ }
+}
+
+U0 SetAudioStream(AudioStream *stream)
+{
+ hd_buf_pos=0;
+ hd_buf_size=stream->size;
+ hd_buf = stream->buf;
+}
+
+U0 HDAudioTaskEndCB()
+{
+ I64 i;
+ HDStop(FALSE,TRUE);
+ fp_snd=NULL;
+ for (i=0;i<HD_TONES;i++) {
+ SndWaveCtrlDel(hda.tone_swcs[i]);
+ hda.tone_swcs[i]=NULL;
+ }
+ Exit;
+}
+
+public U0 HDTonesInit()
+{
+ I64 i;
+ if (hda.bar) {
+ for (i=0;i<HD_TONES;i++) {
+ hda.tone_swcs[i]->freq_multiplier=1.0;
+ hda.tone_swcs[i]->amp_multiplier=0;
+ }
+ hda.tone_swcs[0]->amp_multiplier=1.0;
+ }
+}
+
+U0 HDAudioTask(I64)
+{
+//I didn't feel like messing around with PCI interrupts
+ //so this task polls every millisecond to know when to
+ //switch buffers.
+ I64 i,next_obuf_trigger=SND_BUF_LEN*sizeof(SND_OUT_CONTAINER)/2,
+ obuf_rollover=0,
+ next_ibuf_trigger=SND_BUF_LEN*sizeof(SND_IN_CONTAINER),
+ ibuf_rollover=0;
+ U32 *pos_in_obuf=hda.bar+OSTR0+STRLPIB,
+ *pos_in_ibuf=hda.bar+ISTR0+STRLPIB;
+ Fs->task_end_cb=&HDAudioTaskEndCB;
+ for (i=0;i<HD_TONES;i++)
+ hda.tone_swcs[i]=SndWaveCtrlNew;
+ HDTonesInit;
+ hda.freq=0;
+ Snd;
+ fp_snd=&HDSnd;
+ fp_snd_fill_buf=&HDFillBuf;
+ fp_snd_copy_buf=NULL;
+ snd_obuf_num=1;
+ snd_ibuf_num=1;
+ HDRun(FALSE,TRUE);
+ hda.audio_task_started=TRUE; //This flag is probably not necessary
+ while (TRUE) {
+ if (next_obuf_trigger-obuf_rollover<=*pos_in_obuf<
+ next_obuf_trigger-obuf_rollover+
+ (HD_POS_BUF_MULTIPLES-1)*SND_BUF_LEN*sizeof(SND_OUT_CONTAINER)) {
+ next_obuf_trigger+=SND_BUF_LEN*sizeof(SND_OUT_CONTAINER);
+ if (next_obuf_trigger-obuf_rollover>=
+ HD_POS_BUF_MULTIPLES*SND_BUF_LEN*sizeof(SND_OUT_CONTAINER))
+ obuf_rollover+=HD_POS_BUF_MULTIPLES*SND_BUF_LEN
+ *sizeof(SND_OUT_CONTAINER);
+ if (fp_snd_fill_buf) {
+ LBts(&snd_flags,Sf_FILLING_OUT);
+ (*fp_snd_fill_buf)(hda.ostr0_buf[snd_obuf_num&1],snd_obuf_num);
+ audio_sync_begin = TRUE;
+ if (IsMute)
+ MemSet(hda.ostr0_buf[snd_obuf_num&1],0,
+ SND_BUF_LEN*sizeof(SND_OUT_CONTAINER));
+ LBtr(&snd_flags,Sf_FILLING_OUT);
+ }
+ snd_obuf_num++;
+ }
+ if (next_ibuf_trigger-ibuf_rollover<=*pos_in_ibuf<
+ next_ibuf_trigger-ibuf_rollover+(HD_POS_BUF_MULTIPLES-1)
+ *SND_BUF_LEN*sizeof(SND_IN_CONTAINER)) {
+ next_ibuf_trigger+=SND_BUF_LEN*sizeof(SND_IN_CONTAINER);
+ if (next_ibuf_trigger-ibuf_rollover>=
+ HD_POS_BUF_MULTIPLES*SND_BUF_LEN*sizeof(SND_IN_CONTAINER))
+ ibuf_rollover+=HD_POS_BUF_MULTIPLES*SND_BUF_LEN
+ *sizeof(SND_IN_CONTAINER);
+ if (fp_snd_copy_buf)
+ (*fp_snd_copy_buf)(hda.istr0_buf[snd_obuf_num&1],snd_ibuf_num);
+ snd_ibuf_num++;
+ }
+ Sleep(1);
+ }
+}
+
+U0 HDRst()
+{
+ U32 d,*_d;
+ HDStop(TRUE,TRUE);
+ _d=hda.bar+HD_GCTL;
+ *_d=0; //rst
+ do {
+ Sleep(1);
+ d=*_d;
+ } while (d & 1);
+ *_d=1;
+ do {
+ Sleep(1);
+ d=*_d;
+ } while (!(d & 1));
+ Sleep(1);
+}
+
+public U0 HDAudioEnd(Bool rst=TRUE)
+{
+ snd_dev=SD_PC_SPEAKER;
+ if (hda.bar) {
+ Kill(hda.task);
+ hda.task=NULL;
+ if (rst)
+ HDRst;
+ Free(hda.corb);
+ Free(hda.rirb);
+ Free(hda.o_tmp_buf);
+ Free(hda.ostr0_buf[0]);
+ Free(hda.ostr0_buf[1]);
+ Free(hda.istr0_buf[0]);
+ Free(hda.istr0_buf[1]);
+ Free(hda.ostr0_bdl);
+ Free(hda.istr0_bdl);
+ Mem32DevFree(hda.bar);
+ hda.bar=NULL;
+ }
+}
+
+U0 HDAudioUncachedInit()
+{
+ I64 shared_blks=1;
+ hda.bp=Mem2MegUncachedAlloc(&shared_blks);
+ hda.hc=HeapCtrlBPInit(hda.bp,shared_blks<<12);
+}
+
+public Bool HDAudioInit(I64 hd_bus,I64 hd_dev,I64 hd_fun)
+{
+ I64 i;
+ U32 *_d;
+ U16 w,*_w;
+ U8 *_b;
+ if (hda.bar)
+ HDAudioEnd;
+ else
+ HDAudioUncachedInit;
+ if (PCIReadU16(hd_bus,hd_dev,hd_fun,0)==0x8086 &&
+ (hda.bar=PCIReadU32(hd_bus,hd_dev,hd_fun,0x10) & ~(0x1F))) {
+ PCIWriteU16(hd_bus,hd_dev,hd_fun,0x04,
+ PCIReadU16(hd_bus,hd_dev,hd_fun,0x04)|0x406);
+
+ HDRst;
+
+ hda.corb=CAllocAligned(HD_CORB_ENTRIES*sizeof(U32),128,hda.hc);
+ _d=hda.bar+HD_CORBLBASE;
+ *_d=hda.corb(I64).u32[0];
+ _d=hda.bar+HD_CORBUBASE;
+ *_d=hda.corb(I64).u32[1];
+
+ hda.rirb=CAllocAligned(HD_RIRB_ENTRIES*sizeof(I64),128,hda.hc);
+ _d=hda.bar+HD_RIRBLBASE;
+ *_d=hda.rirb(I64).u32[0];
+ _d=hda.bar+HD_RIRBUBASE;
+ *_d=hda.rirb(I64).u32[1];
+
+ _w=hda.bar+HD_CORBRP;
+ /*
+ *_w=0x8000; //Rst read ptr
+ do {
+ Yield;
+ w=*_w;
+ "%08X\n", w;
+ } while (!(w&0x8000));
+ */
+ *_w=0x0000; //Rst read ptr
+ do {
+ Yield;
+ w=*_w;
+ } while (w&0x8000);
+
+ _w=hda.bar+HD_RIRBWP;
+ *_w=0x8000; //Rst write ptr
+
+ _b=hda.bar+HD_CORBCTL;
+ *_b=0x02; //Run
+ _b=hda.bar+HD_RIRBCTL;
+ *_b=0x02; //Run
+
+ _w=hda.bar+HD_CORBWP;
+ hda.corb_wp=*_w;
+ _w=hda.bar+HD_RIRBWP;
+ hda.rirb_rp=*_w;
+
+ hda.ostr0_bdl =CAllocAligned(
+ HD_BDL_ENTRIES*sizeof(CHDBufDesc),128,hda.hc);
+ _d=hda.bar+OSTR0+STRBDPL;
+ *_d=hda.ostr0_bdl(I64).u32[0];
+ _d=hda.bar+OSTR0+STRBDPU;
+ *_d=hda.ostr0_bdl(I64).u32[1];
+ for (i=0;i<2;i++) {
+ hda.ostr0_bdl[i].buf=hda.ostr0_buf[i]=
+ CAllocAligned(
+ SND_BUF_LEN*sizeof(SND_OUT_CONTAINER),128,hda.hc);
+ hda.ostr0_bdl[i].len=SND_BUF_LEN*sizeof(SND_OUT_CONTAINER);
+ hda.ostr0_bdl[i].ctrl=1;
+ }
+
+ hda.istr0_bdl =CAllocAligned(
+ HD_BDL_ENTRIES*sizeof(CHDBufDesc),128,hda.hc);
+ _d=hda.bar+ISTR0+STRBDPL;
+ *_d=hda.istr0_bdl(I64).u32[0];
+ _d=hda.bar+ISTR0+STRBDPU;
+ *_d=hda.istr0_bdl(I64).u32[1];
+ for (i=0;i<2;i++) {
+ hda.istr0_bdl[i].buf=hda.istr0_buf[i]=CAllocAligned(
+ SND_BUF_LEN*sizeof(SND_IN_CONTAINER),128,hda.hc);
+ hda.istr0_bdl[i].len=SND_BUF_LEN*sizeof(SND_IN_CONTAINER);
+ hda.istr0_bdl[i].ctrl=1;
+ }
+
+ _w=hda.bar+HD_STATESTS;
+ w=*_w;
+ while (w) {
+ hda.cad=Bsf(w);
+ if (HDTestCORBSync(hda.cad,0,VERB_GET_PARAM+P_SUBNODE_CNT)) {
+ HDTraverse(hda.cad,0);
+
+ _d=hda.bar+OSTR0+STRLPIB;
+ *_d=0;
+ _d=hda.bar+OSTR0+STRCBL;
+ *_d=HD_POS_BUF_MULTIPLES*SND_BUF_LEN*sizeof(SND_OUT_CONTAINER);
+ _w=hda.bar+OSTR0+STRLVI;
+ *_w=1; //last valid idx
+ _w=hda.bar+OSTR0+STRFMT;
+ *_w=HD_DFT_OUT_FMT;
+
+ _d=hda.bar+ISTR0+STRLPIB;
+ *_d=0;
+ _d=hda.bar+ISTR0+STRCBL;
+ *_d=HD_POS_BUF_MULTIPLES*SND_BUF_LEN*sizeof(SND_IN_CONTAINER);
+ _w=hda.bar+ISTR0+STRLVI;
+ *_w=1; //last valid idx
+ _w=hda.bar+ISTR0+STRFMT;
+ *_w=HD_DFT_IN_FMT;
+
+ LBts(&sys_semas[SEMA_SND],0); //turn off until cfg completed
+ LBtr(&snd_flags,Sf_FILLING_OUT);
+ hda.audio_task_started=FALSE;
+ if (mp_cnt>1)
+ hda.task=Spawn(&HDAudioTask,NULL,"HD Audio",mp_cnt-1);
+ else
+ hda.task=Spawn(&HDAudioTask,NULL,"HD Audio");
+ while (!hda.audio_task_started)
+ Yield;
+ snd_dev=SD_HD_AUDIO;
+ return TRUE;
+ }
+ Btr(&w,hda.cad);
+ }
+ HDAudioEnd(FALSE);
+ } else
+ hda.bar=NULL;
+ return FALSE;
+}
+
+Bool HDAudioScan()
+{
+ I64 i=-1,j;
+ while (TRUE) {
+ j=PCIClassFind(0x040300,++i);
+ if (j<0)
+ return FALSE;
+
+ if (HDAudioInit(j.u8[2],j.u8[1],j.u8[0]))
+ return TRUE;
+ }
+}
+
+HDAudioScan;
+Kill(hda.task);
+HDAudioScan;
diff --git a/Main.HC b/Main.HC
index e0f92f0..6de42d9 100644
--- a/Main.HC
+++ b/Main.HC
@@ -17,6 +17,7 @@ I64 main(...)
Fs->draw_it = &video_draw_it;
game_init();
//audio_init();
+ music_init();
video_fill_screen_with_black();
@@ -40,7 +41,7 @@ I64 main(...)
}
game_loop();
- //stop_music();
+ stop_music();
/*
if(game_play_mode == PLAY_GAME)
{
@@ -50,7 +51,7 @@ I64 main(...)
game_play_mode = main_menu();
}
- //stop_music();
+ stop_music();
//display_exit_text();
return cleanup_and_exit();
diff --git a/Map.HC b/Map.HC
index 728cdb6..01bb9cf 100644
--- a/Map.HC
+++ b/Map.HC
@@ -297,7 +297,7 @@ U0 load_level(I64 level_number)
file_close(&map_file);
- //stop_music();
+ stop_music();
rain_flag = (level_flags & 0x20);
U16 backdrop_index = (level_flags & 0x1f);
background_x_scroll_flag = (level_flags & 0x40);
@@ -347,7 +347,7 @@ U0 load_level(I64 level_number)
status_panel_init();
//write_savegame_file('T');
- //load_music(music_index);
+ load_music(music_index);
//I Don't think this will be needed.
diff --git a/Music.HC b/Music.HC
new file mode 100644
index 0000000..46dab2a
--- /dev/null
+++ b/Music.HC
@@ -0,0 +1,118 @@
+extern U8 *load_file_in_new_buf(U8 *filename, U32 *file_size);
+
+#define MUSIC_INSTRUCTION_RATE 560 //Hz
+#define ADLIB_OP_SIZE 4
+
+//Data
+I64 music_index = -1;
+
+U8 *music_data;
+U32 music_data_length;
+
+U32 adlib_instruction_position = 0;
+U32 delay_counter = 0;
+
+U8 music_on_flag = 1;
+
+U8 music_filename_tbl[19][13] = {
+ "MCAVES.MNI",
+ "MSCARRY.MNI",
+ "MBOSS.MNI",
+ "MRUNAWAY.MNI",
+ "MCIRCUS.MNI",
+ "MTEKWRD.MNI",
+ "MEASYLEV.MNI",
+ "MROCKIT.MNI",
+ "MHAPPY.MNI",
+ "MDEVO.MNI",
+ "MDADODA.MNI",
+ "MBELLS.MNI",
+ "MDRUMS.MNI",
+ "MBANJO.MNI",
+ "MEASY2.MNI",
+ "MTECK2.MNI",
+ "MTECK3.MNI",
+ "MTECK4.MNI",
+ "MZZTOP.MNI"
+};
+
+//Get delay between adlib commands. Measured in audio samples.
+U32 get_delay(U32 instruction_num)
+{
+ return (SND_SAMPLE_RATE / MUSIC_INSTRUCTION_RATE) * (music_data[instruction_num*ADLIB_OP_SIZE+2] + (music_data[instruction_num*ADLIB_OP_SIZE+3] << 8));
+}
+
+hd_buf = MAlloc(4096);
+
+U0 music_callback(SND_OUT_CONTAINER *buf,I64)
+{
+ U8 *stream = buf;
+ I64 num_samples = SND_BUF_LEN;
+ U8 is_stereo = Cond(SND_OCHANNELS == 2, 1, 0);
+ I64 i;
+
+ for(i=num_samples;i > 0;)
+ {
+ if(delay_counter == 0)
+ {
+ adlib_write(music_data[adlib_instruction_position*ADLIB_OP_SIZE], music_data[adlib_instruction_position*ADLIB_OP_SIZE+1]);
+ delay_counter = get_delay(adlib_instruction_position);
+ adlib_instruction_position++;
+ if(adlib_instruction_position * ADLIB_OP_SIZE >= music_data_length)
+ {
+ adlib_instruction_position = 0;
+ }
+ }
+ if(delay_counter > i)
+ {
+ delay_counter -= i;
+ adlib_getsample(stream, i, is_stereo);
+ return;
+ }
+ if(delay_counter <= i)
+ {
+ i -= delay_counter;
+ adlib_getsample(stream, delay_counter, is_stereo);
+ stream += delay_counter * SND_OCHANNELS * SND_SAMPLE_BITS/8;
+ delay_counter = 0;
+ }
+ }
+}
+
+U0 play_music()
+{
+ fp_snd_fill_buf=&music_callback;
+}
+
+U0 stop_music()
+{
+ fp_snd_fill_buf=&HDFillBuf;
+}
+
+U0 load_music(U16 new_music_index)
+{
+ adlib_instruction_position = 0;
+ delay_counter = 0;
+
+ if(music_index == new_music_index)
+ {
+ play_music();
+ return;
+ }
+
+ if(music_index != -1)
+ {
+ Free(music_data);
+ }
+
+ music_index = new_music_index;
+ music_data = load_file_in_new_buf(music_filename_tbl[music_index], &music_data_length);
+
+ play_music();
+}
+
+U0 music_init()
+{
+ adlib_init(SND_SAMPLE_RATE);
+}
+ \ No newline at end of file
diff --git a/Opl.HC b/Opl.HC
new file mode 100644
index 0000000..4d32ce6
--- /dev/null
+++ b/Opl.HC
@@ -0,0 +1,1168 @@
+/*
+ * Copyright (C) 2002-2017 The DOSBox Team
+ * OPL2/OPL3 emulation library
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+/*
+ * Originally based on ADLIBEMU.C, an AdLib/OPL2 emulation library by Ken Silverman
+ * Copyright (C) 1998-2001 Ken Silverman
+ * Ken Silverman's official web site: "http://www.advsys.net/ken"
+ */
+
+/*
+ * Converted to HolyC by Alec Murphy, 2020-04-04
+ */
+
+#define NUM_CHANNELS 9
+
+#define MAXOPERATORS NUM_CHANNELS*2
+
+#define FL05 0.5
+#define FL2 2.0
+#define PI pi
+
+#define FIXEDPT 0x10000 // fixed-point calculations using 16+16
+#define FIXEDPT_LFO 0x1000000 // fixed-point calculations using 8+24
+
+#define WAVEPREC 1024 // waveform precision (10 bits)
+
+#define INTFREQU 14318180.0 / 288.0 // clocking of the chip
+
+
+#define OF_TYPE_ATT 0
+#define OF_TYPE_DEC 1
+#define OF_TYPE_REL 2
+#define OF_TYPE_SUS 3
+#define OF_TYPE_SUS_NOKEEP 4
+#define OF_TYPE_OFF 5
+
+#define ARC_CONTROL 0x00
+#define ARC_TVS_KSR_MUL 0x20
+#define ARC_KSL_OUTLEV 0x40
+#define ARC_ATTR_DECR 0x60
+#define ARC_SUSL_RELR 0x80
+#define ARC_FREQ_NUM 0xa0
+#define ARC_KON_BNUM 0xb0
+#define ARC_PERC_MODE 0xbd
+#define ARC_FEEDBACK 0xc0
+#define ARC_WAVE_SEL 0xe0
+
+#define ARC_SECONDSET 0x100 // second operator set for OPL3
+
+
+#define OP_ACT_OFF 0x00
+#define OP_ACT_NORMAL 0x01 // regular channel activated (bitmasked)
+#define OP_ACT_PERC 0x02 // percussion channel activated (bitmasked)
+
+#define BLOCKBUF_SIZE 512
+
+
+// vibrato constants
+#define VIBTAB_SIZE 8
+#define VIBFAC 70/50000 // no braces, integer mul/div
+
+// tremolo constants and table
+#define TREMTAB_SIZE 53
+#define TREM_FREQ 3.7 // tremolo at 3.7hz
+
+#define Bitu U64
+#define Bits I64
+#define Bit32u U32
+#define Bit32s I32
+#define Bit16u U16
+#define Bit16s I16
+#define Bit8u U8
+#define Bit8s I8
+#define fltype F64
+
+class op_type {
+ Bit32s cval, lastcval; // current output/last output (used for feedback)
+ Bit32u tcount, wfpos, tinc; // time (position in waveform) and time increment
+ fltype amp, step_amp; // and amplification (envelope)
+ fltype vol; // volume
+ fltype sustain_level; // sustain level
+ Bit32s mfbi; // feedback amount
+ fltype a0, a1, a2, a3; // attack rate function coefficients
+ fltype decaymul, releasemul; // decay/release rate functions
+ Bit32u op_state; // current state of operator (attack/decay/sustain/release/off)
+ Bit32u toff;
+ Bit32s freq_high; // highest three bits of the frequency, used for vibrato calculations
+ Bit16s* cur_wform; // start of selected waveform
+ Bit32u cur_wmask; // mask for selected waveform
+ Bit32u act_state; // activity state (regular, percussion)
+ Bool sus_keep; // keep sustain level when decay finished
+ Bool vibrato,tremolo; // vibrato/tremolo enable bits
+
+ // variables used to provide non-continuous envelopes
+ Bit32u generator_pos; // for non-standard sample rates we need to determine how many samples have passed
+ Bits cur_env_step; // current (standardized) sample position
+ Bits env_step_a,env_step_d,env_step_r; // number of std samples of one step (for attack/decay/release mode)
+ Bit8u step_skip_pos_a; // position of 8-cyclic step skipping (always 2^x to check against mask)
+ Bits env_step_skip_a; // bitmask that determines if a step is skipped (respective bit is zero then)
+};
+
+Bit32u generator_add; // should be a chip parameter
+
+// per-chip variables
+Bitu chip_num;
+op_type op[MAXOPERATORS];
+
+Bits int_samplerate;
+
+Bit8u status;
+Bit32u opl_index;
+Bit8u adlibreg[256]; // adlib register set
+Bit8u wave_sel[22]; // waveform selection
+
+// vibrato/tremolo increment/counter
+Bit32u vibtab_pos;
+Bit32u vibtab_add;
+Bit32u tremtab_pos;
+Bit32u tremtab_add;
+
+fltype recipsamp; // inverse of sampling rate
+Bit16s wavtable[WAVEPREC*3]; // wave form table
+
+// vibrato/tremolo tables
+Bit32s vib_table[VIBTAB_SIZE];
+Bit32s trem_table[TREMTAB_SIZE*2];
+
+Bit32s vibval_const[BLOCKBUF_SIZE];
+Bit32s tremval_const[BLOCKBUF_SIZE];
+
+// vibrato value tables (used per-operator)
+Bit32s vibval_var1[BLOCKBUF_SIZE];
+Bit32s vibval_var2[BLOCKBUF_SIZE];
+//Bit32s vibval_var3[BLOCKBUF_SIZE];
+//Bit32s vibval_var4[BLOCKBUF_SIZE];
+
+// vibrato/trmolo value table pointers
+Bit32s *vibval1, *vibval2, *vibval3, *vibval4;
+Bit32s *tremval1, *tremval2, *tremval3, *tremval4;
+
+
+// key scale level lookup table
+fltype kslmul[4] = {
+ 0.0, 0.5, 0.25, 1.0 // -> 0, 3, 1.5, 6 dB/oct
+};
+
+// frequency multiplicator lookup table
+fltype frqmul_tab[16] = {
+ 0.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15
+};
+// calculated frequency multiplication values (depend on sampling rate)
+fltype frqmul[16];
+
+// key scale levels
+Bit8u kslev[8][16];
+
+// map a channel number to the register offset of the modulator (=register base)
+Bit8u modulatorbase[9] = {
+ 0,1,2,
+ 8,9,10,
+ 16,17,18
+};
+
+// map a register base to a modulator operator number or operator number
+Bit8u regbase2modop[22] = {
+ 0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8
+};
+Bit8u regbase2op[22] = {
+ 0,1,2,9,10,11,0,0,3,4,5,12,13,14,0,0,6,7,8,15,16,17
+};
+
+// start of the waveform
+Bit32u waveform[8] = {
+ WAVEPREC,
+ WAVEPREC>>1,
+ WAVEPREC,
+ (WAVEPREC*3)>>2,
+ 0,
+ 0,
+ (WAVEPREC*5)>>2,
+ WAVEPREC<<1
+};
+
+// length of the waveform as mask
+Bit32u wavemask[8] = {
+ WAVEPREC-1,
+ WAVEPREC-1,
+ (WAVEPREC>>1)-1,
+ (WAVEPREC>>1)-1,
+ WAVEPREC-1,
+ ((WAVEPREC*3)>>2)-1,
+ WAVEPREC>>1,
+ WAVEPREC-1
+};
+
+// where the first entry resides
+Bit32u wavestart[8] = {
+ 0,
+ WAVEPREC>>1,
+ 0,
+ WAVEPREC>>2,
+ 0,
+ 0,
+ 0,
+ WAVEPREC>>3
+};
+
+// envelope generator function constants
+fltype attackconst[4] = {
+ (1/2.82624),
+ (1/2.25280),
+ (1/1.88416),
+ (1/1.59744)
+};
+fltype decrelconst[4] = {
+ (1/39.28064),
+ (1/31.41608),
+ (1/26.17344),
+ (1/22.44608)
+};
+
+U0 operator_advance(op_type* op_pt, Bit32s vib) {
+ op_pt->wfpos = op_pt->tcount; // waveform position
+
+ // advance waveform time
+ op_pt->tcount += op_pt->tinc;
+ op_pt->tcount += (op_pt->tinc)*vib/FIXEDPT;
+
+ op_pt->generator_pos += generator_add;
+}
+
+U0 operator_advance_drums(op_type* op_pt1, Bit32s vib1, op_type* op_pt2, Bit32s vib2, op_type* op_pt3, Bit32s vib3) {
+ Bit32u c1 = op_pt1->tcount/FIXEDPT;
+ Bit32u c3 = op_pt3->tcount/FIXEDPT;
+ Bit32u phasebit = Cond((((c1 & 0x88) ^ ((c1<<5) & 0x80)) | ((c3 ^ (c3<<2)) & 0x20)), 0x02, 0x00);
+
+ Bit32u noisebit = RandU64()&1;
+
+ Bit32u snare_phase_bit = ((((op_pt1->tcount/FIXEDPT) / 0x100))&1);
+
+ //Hihat
+ Bit32u inttm = (phasebit<<8) | (0x34<<(phasebit ^ (noisebit<<1)));
+ op_pt1->wfpos = inttm*FIXEDPT; // waveform position
+ // advance waveform time
+ op_pt1->tcount += op_pt1->tinc;
+ op_pt1->tcount += (op_pt1->tinc)*vib1/FIXEDPT;
+ op_pt1->generator_pos += generator_add;
+
+ //Snare
+ inttm = ((1+snare_phase_bit) ^ noisebit)<<8;
+ op_pt2->wfpos = inttm*FIXEDPT; // waveform position
+ // advance waveform time
+ op_pt2->tcount += op_pt2->tinc;
+ op_pt2->tcount += (op_pt2->tinc)*vib2/FIXEDPT;
+ op_pt2->generator_pos += generator_add;
+
+ //Cymbal
+ inttm = (1+phasebit)<<8;
+ op_pt3->wfpos = inttm*FIXEDPT; // waveform position
+ // advance waveform time
+ op_pt3->tcount += op_pt3->tinc;
+ op_pt3->tcount += (op_pt3->tinc)*vib3/FIXEDPT;
+ op_pt3->generator_pos += generator_add;
+}
+
+// output level is sustained, mode changes only when operator is turned off (->release)
+// or when the keep-sustained bit is turned off (->sustain_nokeep)
+U0 operator_output(op_type* op_pt, Bit32s modulator, Bit32s trem) {
+ if (op_pt->op_state != OF_TYPE_OFF) {
+ op_pt->lastcval = op_pt->cval;
+ Bit32u i = ((op_pt->wfpos+modulator)/FIXEDPT);
+
+ // wform: -16384 to 16383 (0x4000)
+ // trem : 32768 to 65535 (0x10000)
+ // step_amp: 0.0 to 1.0
+ // vol : 1/2^14 to 1/2^29 (/0x4000; /1../0x8000)
+
+ op_pt->cval = (op_pt->step_amp*op_pt->vol*op_pt->cur_wform[i&op_pt->cur_wmask]*trem/16.0);
+ }
+}
+
+// no action, operator is off
+U0 operator_off(op_type* op_pt) {
+}
+
+// output level is sustained, mode changes only when operator is turned off (->release)
+// or when the keep-sustained bit is turned off (->sustain_nokeep)
+U0 operator_sustain(op_type* op_pt) {
+ Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples
+ Bit32u ct;
+ for (ct=0; ct<num_steps_add; ct++) {
+ op_pt->cur_env_step++;
+ }
+ op_pt->generator_pos -= num_steps_add*FIXEDPT;
+}
+
+// operator in release mode, if output level reaches zero the operator is turned off
+U0 operator_release(op_type* op_pt) {
+ // ??? boundary?
+ if (op_pt->amp > 0.00000001) {
+ // release phase
+ op_pt->amp *= op_pt->releasemul;
+ }
+
+ Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples
+ Bit32u ct;
+ for (ct=0; ct<num_steps_add; ct++) {
+ op_pt->cur_env_step++; // sample counter
+ if ((op_pt->cur_env_step & op_pt->env_step_r)==0) {
+ if (op_pt->amp <= 0.00000001) {
+ // release phase finished, turn off this operator
+ op_pt->amp = 0.0;
+ if (op_pt->op_state == OF_TYPE_REL) {
+ op_pt->op_state = OF_TYPE_OFF;
+ }
+ }
+ op_pt->step_amp = op_pt->amp;
+ }
+ }
+ op_pt->generator_pos -= num_steps_add*FIXEDPT;
+}
+
+// operator in decay mode, if sustain level is reached the output level is either
+// kept (sustain level keep enabled) or the operator is switched into release mode
+U0 operator_decay(op_type* op_pt) {
+ if (op_pt->amp > op_pt->sustain_level) {
+ // decay phase
+ op_pt->amp *= op_pt->decaymul;
+ }
+
+ Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples
+ Bit32u ct;
+ for (ct=0; ct<num_steps_add; ct++) {
+ op_pt->cur_env_step++;
+ if ((op_pt->cur_env_step & op_pt->env_step_d)==0) {
+ if (op_pt->amp <= op_pt->sustain_level) {
+ // decay phase finished, sustain level reached
+ if (op_pt->sus_keep) {
+ // keep sustain level (until turned off)
+ op_pt->op_state = OF_TYPE_SUS;
+ op_pt->amp = op_pt->sustain_level;
+ } else {
+ // next: release phase
+ op_pt->op_state = OF_TYPE_SUS_NOKEEP;
+ }
+ }
+ op_pt->step_amp = op_pt->amp;
+ }
+ }
+ op_pt->generator_pos -= num_steps_add*FIXEDPT;
+}
+
+// operator in attack mode, if full output level is reached,
+// the operator is switched into decay mode
+U0 operator_attack(op_type* op_pt) {
+ op_pt->amp = ((op_pt->a3*op_pt->amp + op_pt->a2)*op_pt->amp + op_pt->a1)*op_pt->amp + op_pt->a0;
+
+ Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples
+ Bit32u ct;
+ for (ct=0; ct<num_steps_add; ct++) {
+ op_pt->cur_env_step++; // next sample
+ if ((op_pt->cur_env_step & op_pt->env_step_a)==0) { // check if next step already reached
+ if (op_pt->amp > 1.0) {
+ // attack phase finished, next: decay
+ op_pt->op_state = OF_TYPE_DEC;
+ op_pt->amp = 1.0;
+ op_pt->step_amp = 1.0;
+ }
+ op_pt->step_skip_pos_a <<= 1;
+ if (op_pt->step_skip_pos_a==0) op_pt->step_skip_pos_a = 1;
+ if (op_pt->step_skip_pos_a & op_pt->env_step_skip_a) { // check if required to skip next step
+ op_pt->step_amp = op_pt->amp;
+ }
+ }
+ }
+ op_pt->generator_pos -= num_steps_add*FIXEDPT;
+}
+
+Bit8u step_skip_mask[5] = {0xff, 0xfe, 0xee, 0xba, 0xaa};
+
+U0 change_attackrate(Bitu regbase, op_type* op_pt) {
+ Bits attackrate = adlibreg[ARC_ATTR_DECR+regbase]>>4;
+ if (attackrate) {
+ fltype f = (Pow(FL2,attackrate+(op_pt->toff>>2)-1)*attackconst[op_pt->toff&3]*recipsamp);
+ // attack rate coefficients
+ op_pt->a0 = (0.0377*f);
+ op_pt->a1 = (10.73*f+1);
+ op_pt->a2 = (-17.57*f);
+ op_pt->a3 = (7.42*f);
+
+ Bits step_skip = attackrate*4 + op_pt->toff;
+ Bits steps = step_skip >> 2;
+ op_pt->env_step_a = (1<<Cond(steps<=12,12-steps,0))-1;
+
+ Bits step_num = Cond((step_skip<=48),(4-(step_skip&3)),0);
+ op_pt->env_step_skip_a = step_skip_mask[step_num];
+
+ if (step_skip>=62) {
+ op_pt->a0 = (2.0); // something that triggers an immediate transition to amp:=1.0
+ op_pt->a1 = (0.0);
+ op_pt->a2 = (0.0);
+ op_pt->a3 = (0.0);
+ }
+ } else {
+ // attack disabled
+ op_pt->a0 = 0.0;
+ op_pt->a1 = 1.0;
+ op_pt->a2 = 0.0;
+ op_pt->a3 = 0.0;
+ op_pt->env_step_a = 0;
+ op_pt->env_step_skip_a = 0;
+ }
+}
+
+U0 change_decayrate(Bitu regbase, op_type* op_pt) {
+ Bits decayrate = adlibreg[ARC_ATTR_DECR+regbase]&15;
+ // decaymul should be 1.0 when decayrate==0
+ if (decayrate) {
+ fltype f = (-7.4493*decrelconst[op_pt->toff&3]*recipsamp);
+ op_pt->decaymul = (Pow(FL2,f*Pow(FL2,(decayrate+(op_pt->toff>>2)))));
+ Bits steps = (decayrate*4 + op_pt->toff) >> 2;
+ op_pt->env_step_d = (1<<Cond(steps<=12,12-steps,0))-1;
+ } else {
+ op_pt->decaymul = 1.0;
+ op_pt->env_step_d = 0;
+ }
+}
+
+U0 change_releaserate(Bitu regbase, op_type* op_pt) {
+ Bits releaserate = adlibreg[ARC_SUSL_RELR+regbase]&15;
+ // releasemul should be 1.0 when releaserate==0
+ if (releaserate) {
+ fltype f = (-7.4493*decrelconst[op_pt->toff&3]*recipsamp);
+ op_pt->releasemul = (Pow(FL2,f*Pow(FL2,(releaserate+(op_pt->toff>>2)))));
+ Bits steps = (releaserate*4 + op_pt->toff) >> 2;
+ op_pt->env_step_r = (1<<Cond(steps<=12,12-steps,0))-1;
+ } else {
+ op_pt->releasemul = 1.0;
+ op_pt->env_step_r = 0;
+ }
+}
+
+U0 change_sustainlevel(Bitu regbase, op_type* op_pt) {
+ Bits sustainlevel = adlibreg[ARC_SUSL_RELR+regbase]>>4;
+ // sustainlevel should be 0.0 when sustainlevel==15 (max)
+ if (sustainlevel<15) {
+ op_pt->sustain_level = (Pow(FL2,sustainlevel * (-FL05)));
+ } else {
+ op_pt->sustain_level = 0.0;
+ }
+}
+
+U0 change_waveform(Bitu regbase, op_type* op_pt) {
+ // waveform selection
+ op_pt->cur_wmask = wavemask[wave_sel[regbase]];
+ op_pt->cur_wform = &wavtable[waveform[wave_sel[regbase]]];
+ // (might need to be adapted to waveform type here...)
+}
+
+U0 change_keepsustain(Bitu regbase, op_type* op_pt) {
+ op_pt->sus_keep = (adlibreg[ARC_TVS_KSR_MUL+regbase]&0x20)>0;
+ if (op_pt->op_state==OF_TYPE_SUS) {
+ if (!op_pt->sus_keep) op_pt->op_state = OF_TYPE_SUS_NOKEEP;
+ } else if (op_pt->op_state==OF_TYPE_SUS_NOKEEP) {
+ if (op_pt->sus_keep) op_pt->op_state = OF_TYPE_SUS;
+ }
+}
+
+// enable/disable vibrato/tremolo LFO effects
+U0 change_vibrato(Bitu regbase, op_type* op_pt) {
+ op_pt->vibrato = (adlibreg[ARC_TVS_KSR_MUL+regbase]&0x40)!=0;
+ op_pt->tremolo = (adlibreg[ARC_TVS_KSR_MUL+regbase]&0x80)!=0;
+}
+
+// change amount of self-feedback
+U0 change_feedback(Bitu chanbase, op_type* op_pt) {
+ Bits feedback = adlibreg[ARC_FEEDBACK+chanbase]&14;
+ if (feedback) op_pt->mfbi = (Pow(FL2,((feedback>>1)+8)));
+ else op_pt->mfbi = 0;
+}
+
+U0 change_frequency(Bitu chanbase, Bitu regbase, op_type* op_pt) {
+ // frequency
+ Bit32u frn = (((adlibreg[ARC_KON_BNUM+chanbase])&3)<<8) + adlibreg[ARC_FREQ_NUM+chanbase];
+ // block number/octave
+ Bit32u oct = (((adlibreg[ARC_KON_BNUM+chanbase])>>2)&7);
+ op_pt->freq_high = ((frn>>7)&7);
+
+ // keysplit
+ Bit32u note_sel = (adlibreg[8]>>6)&1;
+ op_pt->toff = ((frn>>9)&(note_sel^1)) | ((frn>>8)&note_sel);
+ op_pt->toff += (oct<<1);
+
+ // envelope scaling (KSR)
+ if (!(adlibreg[ARC_TVS_KSR_MUL+regbase]&0x10)) op_pt->toff >>= 2;
+
+ // 20+a0+b0:
+ op_pt->tinc = ((((frn<<oct))*frqmul[adlibreg[ARC_TVS_KSR_MUL+regbase]&15]));
+ // 40+a0+b0:
+ fltype vol_in = ((adlibreg[ARC_KSL_OUTLEV+regbase]&63) +
+ kslmul[adlibreg[ARC_KSL_OUTLEV+regbase]>>6]*kslev[oct][frn>>6]);
+ op_pt->vol = (Pow(FL2,(vol_in * -0.125 - 14)));
+
+ // operator frequency changed, care about features that depend on it
+ change_attackrate(regbase,op_pt);
+ change_decayrate(regbase,op_pt);
+ change_releaserate(regbase,op_pt);
+}
+
+U0 enable_operator(Bitu regbase, op_type* op_pt, Bit32u act_type) {
+ // check if this is really an off-on transition
+ if (op_pt->act_state == OP_ACT_OFF) {
+ Bits wselbase = regbase;
+ if (wselbase>=ARC_SECONDSET) wselbase -= (ARC_SECONDSET-22); // second set starts at 22
+
+ op_pt->tcount = wavestart[wave_sel[wselbase]]*FIXEDPT;
+
+ // start with attack mode
+ op_pt->op_state = OF_TYPE_ATT;
+ op_pt->act_state |= act_type;
+ }
+}
+
+U0 disable_operator(op_type* op_pt, Bit32u act_type) {
+ // check if this is really an on-off transition
+ if (op_pt->act_state != OP_ACT_OFF) {
+ op_pt->act_state &= (~act_type);
+ if (op_pt->act_state == OP_ACT_OFF) {
+ if (op_pt->op_state != OF_TYPE_OFF) op_pt->op_state = OF_TYPE_REL;
+ }
+ }
+}
+
+U0 adlib_init(Bit32u samplerate) {
+ Bits i, j, oct;
+
+ int_samplerate = samplerate;
+
+ generator_add = (INTFREQU*FIXEDPT/int_samplerate);
+
+ MemSet(adlibreg,0,sizeof(adlibreg));
+ MemSet(op,0,sizeof(op_type)*MAXOPERATORS);
+ MemSet(wave_sel,0,sizeof(wave_sel));
+
+ for (i=0;i<MAXOPERATORS;i++) {
+ op[i].op_state = OF_TYPE_OFF;
+ op[i].act_state = OP_ACT_OFF;
+ op[i].amp = 0.0;
+ op[i].step_amp = 0.0;
+ op[i].vol = 0.0;
+ op[i].tcount = 0;
+ op[i].tinc = 0;
+ op[i].toff = 0;
+ op[i].cur_wmask = wavemask[0];
+ op[i].cur_wform = &wavtable[waveform[0]];
+ op[i].freq_high = 0;
+
+ op[i].generator_pos = 0;
+ op[i].cur_env_step = 0;
+ op[i].env_step_a = 0;
+ op[i].env_step_d = 0;
+ op[i].env_step_r = 0;
+ op[i].step_skip_pos_a = 0;
+ op[i].env_step_skip_a = 0;
+ }
+
+ recipsamp = 1.0 / int_samplerate;
+ for (i=15;i>=0;i--) {
+ frqmul[i] = (frqmul_tab[i]*INTFREQU/WAVEPREC*FIXEDPT*recipsamp);
+ }
+
+ status = 0;
+ opl_index = 0;
+
+
+ // create vibrato table
+ vib_table[0] = 8;
+ vib_table[1] = 4;
+ vib_table[2] = 0;
+ vib_table[3] = -4;
+ for (i=4; i<VIBTAB_SIZE; i++) vib_table[i] = vib_table[i-4]*-1;
+
+ // vibrato at ~6.1 ?? (opl3 docs say 6.1, opl4 docs say 6.0, y8950 docs say 6.4)
+ vibtab_add = (VIBTAB_SIZE*FIXEDPT_LFO/8192*INTFREQU/int_samplerate);
+ vibtab_pos = 0;
+
+ for (i=0; i<BLOCKBUF_SIZE; i++) vibval_const[i] = 0;
+
+
+ // create tremolo table
+ Bit32s trem_table_int[TREMTAB_SIZE];
+ for (i=0; i<14; i++) trem_table_int[i] = i-13; // upwards (13 to 26 -> -0.5/6 to 0)
+ for (i=14; i<41; i++) trem_table_int[i] = -i+14; // downwards (26 to 0 -> 0 to -1/6)
+ for (i=41; i<53; i++) trem_table_int[i] = i-40-26; // upwards (1 to 12 -> -1/6 to -0.5/6)
+
+ for (i=0; i<TREMTAB_SIZE; i++) {
+ // 0.0 .. -26/26*4.8/6 == [0.0 .. -0.8], 4/53 steps == [1 .. 0.57]
+ fltype trem_val1=((trem_table_int[i])*4.8/26.0/6.0); // 4.8db
+ fltype trem_val2=(((trem_table_int[i]/4))*1.2/6.0/6.0); // 1.2db (larger stepping)
+
+ trem_table[i] = (Pow(FL2,trem_val1)*FIXEDPT);
+ trem_table[TREMTAB_SIZE+i] = (Pow(FL2,trem_val2)*FIXEDPT);
+ }
+
+ // tremolo at 3.7hz
+ tremtab_add = (TREMTAB_SIZE * TREM_FREQ * FIXEDPT_LFO / int_samplerate);
+ tremtab_pos = 0;
+
+ for (i=0; i<BLOCKBUF_SIZE; i++) tremval_const[i] = FIXEDPT;
+
+
+ Bitu initfirstime = 0;
+ if (!initfirstime) {
+ initfirstime = 1;
+
+ // create waveform tables
+ for (i=0;i<(WAVEPREC>>1);i++) {
+ wavtable[(i<<1) +WAVEPREC] = (16384*Sin(((i<<1) )*PI*2/WAVEPREC));
+ wavtable[(i<<1)+1+WAVEPREC] = (16384*Sin(((i<<1)+1)*PI*2/WAVEPREC));
+ wavtable[i] = wavtable[(i<<1) +WAVEPREC];
+ // alternative: (zero-less)
+/* wavtable[(i<<1) +WAVEPREC] = (16384*sin(((i<<2)+1)*PI/WAVEPREC));
+ wavtable[(i<<1)+1+WAVEPREC] = (16384*sin(((i<<2)+3)*PI/WAVEPREC));
+ wavtable[i] = wavtable[(i<<1)-1+WAVEPREC]; */
+ }
+ for (i=0;i<(WAVEPREC>>3);i++) {
+ wavtable[i+(WAVEPREC<<1)] = wavtable[i+(WAVEPREC>>3)]-16384;
+ wavtable[i+((WAVEPREC*17)>>3)] = wavtable[i+(WAVEPREC>>2)]+16384;
+ }
+
+ // key scale level table verified ([table in book]*8/3)
+ kslev[7][0] = 0; kslev[7][1] = 24; kslev[7][2] = 32; kslev[7][3] = 37;
+ kslev[7][4] = 40; kslev[7][5] = 43; kslev[7][6] = 45; kslev[7][7] = 47;
+ kslev[7][8] = 48;
+ for (i=9;i<16;i++) kslev[7][i] = (i+41);
+ for (j=6;j>=0;j--) {
+ for (i=0;i<16;i++) {
+ oct = kslev[j+1][i]-8;
+ if (oct < 0) oct = 0;
+ kslev[j][i] = oct;
+ }
+ }
+ }
+
+}
+
+U0 adlib_write(Bitu idx, Bit8u val) {
+ Bit32u second_set = idx&0x100;
+ adlibreg[idx] = val;
+ I64 num;
+ Bitu base;
+ Bitu modop;
+ Bitu chanbase;
+ Bitu regbase;
+ Bits opbase;
+ Bits modbase;
+ op_type* op_ptr;
+
+ switch (idx&0xf0) {
+ case ARC_CONTROL:
+ // here we check for the second set registers, too:
+ switch (idx) {
+ case 0x02: // timer1 counter
+ case 0x03: // timer2 counter
+ break;
+ case 0x04:
+ // IRQ reset, timer mask/start
+ if (val&0x80) {
+ // clear IRQ bits in status register
+ status &= ~0x60;
+ } else {
+ status = 0;
+ }
+ break;
+ case 0x08:
+ // CSW, note select
+ break;
+ default:
+ break;
+ }
+ break;
+ case ARC_TVS_KSR_MUL:
+ case ARC_TVS_KSR_MUL+0x10: {
+ // tremolo/vibrato/sustain keeping enabled; key scale rate; frequency multiplication
+ num = idx&7;
+ base = (idx-ARC_TVS_KSR_MUL)&0xff;
+ if ((num<6) && (base<22)) {
+ modop = regbase2modop[Cond(second_set,(base+22),base)];
+ regbase = base+second_set;
+ chanbase = Cond(second_set,(modop-18+ARC_SECONDSET),modop);
+
+ // change tremolo/vibrato and sustain keeping of this operator
+ op_ptr = &op[modop+Cond((num<3), 0, 9)];
+ change_keepsustain(regbase,op_ptr);
+ change_vibrato(regbase,op_ptr);
+
+ // change frequency calculations of this operator as
+ // key scale rate and frequency multiplicator can be changed
+ change_frequency(chanbase,base,op_ptr);
+ }
+ }
+ break;
+ case ARC_KSL_OUTLEV:
+ case ARC_KSL_OUTLEV+0x10: {
+ // key scale level; output rate
+ num = idx&7;
+ base = (idx-ARC_KSL_OUTLEV)&0xff;
+ if ((num<6) && (base<22)) {
+ modop = regbase2modop[Cond(second_set,(base+22),base)];
+ chanbase = Cond(second_set,(modop-18+ARC_SECONDSET),modop);
+
+ // change frequency calculations of this operator as
+ // key scale level and output rate can be changed
+ op_ptr = &op[modop+Cond((num<3), 0, 9)];
+ change_frequency(chanbase,base,op_ptr);
+ }
+ }
+ break;
+ case ARC_ATTR_DECR:
+ case ARC_ATTR_DECR+0x10: {
+ // attack/decay rates
+ num = idx&7;
+ base = (idx-ARC_ATTR_DECR)&0xff;
+ if ((num<6) && (base<22)) {
+ regbase = base+second_set;
+
+ // change attack rate and decay rate of this operator
+ op_ptr = &op[regbase2op[Cond(second_set,(base+22),base)]];
+ change_attackrate(regbase,op_ptr);
+ change_decayrate(regbase,op_ptr);
+ }
+ }
+ break;
+ case ARC_SUSL_RELR:
+ case ARC_SUSL_RELR+0x10: {
+ // sustain level; release rate
+ num = idx&7;
+ base = (idx-ARC_SUSL_RELR)&0xff;
+ if ((num<6) && (base<22)) {
+ regbase = base+second_set;
+
+ // change sustain level and release rate of this operator
+ op_ptr = &op[regbase2op[Cond(second_set,(base+22),base)]];
+ change_releaserate(regbase,op_ptr);
+ change_sustainlevel(regbase,op_ptr);
+ }
+ }
+ break;
+ case ARC_FREQ_NUM: {
+ // 0xa0-0xa8 low8 frequency
+ base = (idx-ARC_FREQ_NUM)&0xff;
+ if (base<9) {
+ opbase = Cond(second_set,(base+18),base);
+ // regbase of modulator:
+ modbase = modulatorbase[base]+second_set;
+
+ chanbase = base+second_set;
+
+ change_frequency(chanbase,modbase,&op[opbase]);
+ change_frequency(chanbase,modbase+3,&op[opbase+9]);
+ }
+ }
+ break;
+ case ARC_KON_BNUM: {
+ if (idx == ARC_PERC_MODE) {
+ if ((val&0x30) == 0x30) { // BassDrum active
+ enable_operator(16,&op[6],OP_ACT_PERC);
+ change_frequency(6,16,&op[6]);
+ enable_operator(16+3,&op[6+9],OP_ACT_PERC);
+ change_frequency(6,16+3,&op[6+9]);
+ } else {
+ disable_operator(&op[6],OP_ACT_PERC);
+ disable_operator(&op[6+9],OP_ACT_PERC);
+ }
+ if ((val&0x28) == 0x28) { // Snare active
+ enable_operator(17+3,&op[16],OP_ACT_PERC);
+ change_frequency(7,17+3,&op[16]);
+ } else {
+ disable_operator(&op[16],OP_ACT_PERC);
+ }
+ if ((val&0x24) == 0x24) { // TomTom active
+ enable_operator(18,&op[8],OP_ACT_PERC);
+ change_frequency(8,18,&op[8]);
+ } else {
+ disable_operator(&op[8],OP_ACT_PERC);
+ }
+ if ((val&0x22) == 0x22) { // Cymbal active
+ enable_operator(18+3,&op[8+9],OP_ACT_PERC);
+ change_frequency(8,18+3,&op[8+9]);
+ } else {
+ disable_operator(&op[8+9],OP_ACT_PERC);
+ }
+ if ((val&0x21) == 0x21) { // Hihat active
+ enable_operator(17,&op[7],OP_ACT_PERC);
+ change_frequency(7,17,&op[7]);
+ } else {
+ disable_operator(&op[7],OP_ACT_PERC);
+ }
+
+ break;
+ }
+ // regular 0xb0-0xb8
+ base = (idx-ARC_KON_BNUM)&0xff;
+ if (base<9) {
+ opbase = Cond(second_set,(base+18),base);
+ // regbase of modulator:
+ modbase = modulatorbase[base]+second_set;
+
+ if (val&32) {
+ // operator switched on
+ enable_operator(modbase,&op[opbase],OP_ACT_NORMAL); // modulator (if 2op)
+ enable_operator(modbase+3,&op[opbase+9],OP_ACT_NORMAL); // carrier (if 2op)
+ } else {
+ // operator switched off
+ disable_operator(&op[opbase],OP_ACT_NORMAL);
+ disable_operator(&op[opbase+9],OP_ACT_NORMAL);
+ }
+
+ chanbase = base+second_set;
+
+ // change frequency calculations of modulator and carrier (2op) as
+ // the frequency of the channel has changed
+ change_frequency(chanbase,modbase,&op[opbase]);
+ change_frequency(chanbase,modbase+3,&op[opbase+9]);
+ }
+ }
+ break;
+ case ARC_FEEDBACK: {
+ // 0xc0-0xc8 feedback/modulation type (AM/FM)
+ base = (idx-ARC_FEEDBACK)&0xff;
+ if (base<9) {
+ opbase = Cond(second_set,(base+18),base);
+ chanbase = base+second_set;
+ change_feedback(chanbase,&op[opbase]);
+ }
+ }
+ break;
+ case ARC_WAVE_SEL:
+ case ARC_WAVE_SEL+0x10: {
+ num = idx&7;
+ base = (idx-ARC_WAVE_SEL)&0xff;
+ if ((num<6) && (base<22)) {
+ if (adlibreg[0x01]&0x20) {
+ // wave selection enabled, change waveform
+ wave_sel[base] = val&3;
+ op_ptr = &op[regbase2modop[base]+Cond((num<3), 0, 9)];
+ change_waveform(base,op_ptr);
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+Bitu adlib_reg_read(Bitu port) {
+ // opl2-detection routines require ret&6 to be 6
+ if ((port&1)==0) {
+ return status|6;
+ }
+ return 0xff;
+}
+
+U0 adlib_write_index(Bitu port, Bit8u val) {
+ opl_index = val;
+}
+
+U0 clipit16(Bit32s ival, Bit16s* outval) {
+ if (ival<32768) {
+ if (ival>-32769) {
+ *outval=ival;
+ } else {
+ *outval = -32768;
+ }
+ } else {
+ *outval = 32767;
+ }
+}
+
+U0 (*opfuncs)(op_type*)[6] = {
+ &operator_attack,
+ &operator_decay,
+ &operator_release,
+ &operator_sustain, // sustain phase (keeping level)
+ &operator_release, // sustain_nokeep phase (release-style)
+ &operator_off
+};
+
+U0 adlib_getsample(Bit8u* sndptr, Bits numsamples, Bit8u is_stereo_output) {
+ Bits i, endsamples;
+ op_type* cptr;
+
+ Bit32s outbufl[BLOCKBUF_SIZE];
+
+ // vibrato/tremolo lookup tables (global, to possibly be used by all operators)
+ Bit32s vib_lut[BLOCKBUF_SIZE];
+ Bit32s trem_lut[BLOCKBUF_SIZE];
+
+ Bits samples_to_process = numsamples;
+ Bits cursmp;
+ Bit32s chanval;
+
+ for (cursmp=0; cursmp<samples_to_process; cursmp+=endsamples) {
+ endsamples = samples_to_process-cursmp;
+ if (endsamples>BLOCKBUF_SIZE) endsamples = BLOCKBUF_SIZE;
+
+ MemSet(&outbufl,0,endsamples*sizeof(Bit32s));
+
+ // calculate vibrato/tremolo lookup tables
+ Bit32s vib_tshift = Cond(((adlibreg[ARC_PERC_MODE]&0x40)==0), 1, 0); // 14cents/7cents switching
+ for (i=0;i<endsamples;i++) {
+ // cycle through vibrato table
+ vibtab_pos += vibtab_add;
+ if (vibtab_pos/FIXEDPT_LFO>=VIBTAB_SIZE) vibtab_pos-=VIBTAB_SIZE*FIXEDPT_LFO;
+ vib_lut[i] = vib_table[vibtab_pos/FIXEDPT_LFO]>>vib_tshift; // 14cents (14/100 of a semitone) or 7cents
+
+ // cycle through tremolo table
+ tremtab_pos += tremtab_add;
+ if (tremtab_pos/FIXEDPT_LFO>=TREMTAB_SIZE) tremtab_pos-=TREMTAB_SIZE*FIXEDPT_LFO;
+ if (adlibreg[ARC_PERC_MODE]&0x80) trem_lut[i] = trem_table[tremtab_pos/FIXEDPT_LFO];
+ else trem_lut[i] = trem_table[TREMTAB_SIZE+tremtab_pos/FIXEDPT_LFO];
+ }
+
+ if (adlibreg[ARC_PERC_MODE]&0x20) {
+ //BassDrum
+ cptr = &op[6];
+ if (adlibreg[ARC_FEEDBACK+6]&1) {
+ // additive synthesis
+ if (cptr[9].op_state != OF_TYPE_OFF) {
+ if (cptr[9].vibrato) {
+ vibval1 = vibval_var1;
+ for (i=0;i<endsamples;i++)
+ vibval1[i] = ((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval1 = vibval_const;
+ if (cptr[9].tremolo) tremval1 = trem_lut; // tremolo enabled, use table
+ else tremval1 = tremval_const;
+
+ // calculate channel output
+ for (i=0;i<endsamples;i++) {
+ operator_advance(&cptr[9],vibval1[i]);
+ opfuncs[cptr[9].op_state](&cptr[9]);
+ operator_output(&cptr[9],0,tremval1[i]);
+
+ chanval = cptr[9].cval*2;
+ outbufl[i] += chanval;
+ }
+ }
+ } else {
+ // frequency modulation
+ if ((cptr[9].op_state != OF_TYPE_OFF) || (cptr[0].op_state != OF_TYPE_OFF)) {
+ if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) {
+ vibval1 = vibval_var1;
+ for (i=0;i<endsamples;i++)
+ vibval1[i] = ((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval1 = vibval_const;
+ if ((cptr[9].vibrato) && (cptr[9].op_state != OF_TYPE_OFF)) {
+ vibval2 = vibval_var2;
+ for (i=0;i<endsamples;i++)
+ vibval2[i] = ((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval2 = vibval_const;
+ if (cptr[0].tremolo) tremval1 = trem_lut; // tremolo enabled, use table
+ else tremval1 = tremval_const;
+ if (cptr[9].tremolo) tremval2 = trem_lut; // tremolo enabled, use table
+ else tremval2 = tremval_const;
+
+ // calculate channel output
+ for (i=0;i<endsamples;i++) {
+ operator_advance(&cptr[0],vibval1[i]);
+ opfuncs[cptr[0].op_state](&cptr[0]);
+ operator_output(&cptr[0],(cptr[0].lastcval+cptr[0].cval)*cptr[0].mfbi/2,tremval1[i]);
+
+ operator_advance(&cptr[9],vibval2[i]);
+ opfuncs[cptr[9].op_state](&cptr[9]);
+ operator_output(&cptr[9],cptr[0].cval*FIXEDPT,tremval2[i]);
+
+ chanval = cptr[9].cval*2;
+ outbufl[i] += chanval;
+ }
+ }
+ }
+
+ //TomTom (j=8)
+ if (op[8].op_state != OF_TYPE_OFF) {
+ cptr = &op[8];
+ if (cptr[0].vibrato) {
+ vibval3 = vibval_var1;
+ for (i=0;i<endsamples;i++)
+ vibval3[i] = ((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval3 = vibval_const;
+
+ if (cptr[0].tremolo) tremval3 = trem_lut; // tremolo enabled, use table
+ else tremval3 = tremval_const;
+
+ // calculate channel output
+ for (i=0;i<endsamples;i++) {
+ operator_advance(&cptr[0],vibval3[i]);
+ opfuncs[cptr[0].op_state](&cptr[0]); //TomTom
+ operator_output(&cptr[0],0,tremval3[i]);
+ chanval = cptr[0].cval*2;
+ outbufl[i] += chanval;
+ }
+ }
+
+ //Snare/Hihat (j=7), Cymbal (j=8)
+ if ((op[7].op_state != OF_TYPE_OFF) || (op[16].op_state != OF_TYPE_OFF) ||
+ (op[17].op_state != OF_TYPE_OFF)) {
+ cptr = &op[7];
+ if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) {
+ vibval1 = vibval_var1;
+ for (i=0;i<endsamples;i++)
+ vibval1[i] = ((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval1 = vibval_const;
+ if ((cptr[9].vibrato) && (cptr[9].op_state == OF_TYPE_OFF)) {
+ vibval2 = vibval_var2;
+ for (i=0;i<endsamples;i++)
+ vibval2[i] = ((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval2 = vibval_const;
+
+ if (cptr[0].tremolo) tremval1 = trem_lut; // tremolo enabled, use table
+ else tremval1 = tremval_const;
+ if (cptr[9].tremolo) tremval2 = trem_lut; // tremolo enabled, use table
+ else tremval2 = tremval_const;
+
+ cptr = &op[8];
+ if ((cptr[9].vibrato) && (cptr[9].op_state == OF_TYPE_OFF)) {
+ vibval4 = vibval_var2;
+ for (i=0;i<endsamples;i++)
+ vibval4[i] = ((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval4 = vibval_const;
+
+ if (cptr[9].tremolo) tremval4 = trem_lut; // tremolo enabled, use table
+ else tremval4 = tremval_const;
+
+ // calculate channel output
+ for (i=0;i<endsamples;i++) {
+ operator_advance_drums(&op[7],vibval1[i],&op[7+9],vibval2[i],&op[8+9],vibval4[i]);
+
+ opfuncs[op[7].op_state](&op[7]); //Hihat
+ operator_output(&op[7],0,tremval1[i]);
+
+ opfuncs[op[7+9].op_state](&op[7+9]); //Snare
+ operator_output(&op[7+9],0,tremval2[i]);
+
+ opfuncs[op[8+9].op_state](&op[8+9]); //Cymbal
+ operator_output(&op[8+9],0,tremval4[i]);
+
+ chanval = (op[7].cval + op[7+9].cval + op[8+9].cval)*2;
+ outbufl[i] += chanval;
+ }
+ }
+ }
+
+ Bitu max_channel = NUM_CHANNELS;
+ Bits cur_ch;
+ for (cur_ch=max_channel-1; cur_ch>=0; cur_ch--) {
+ // skip drum/percussion operators
+ if ((adlibreg[ARC_PERC_MODE]&0x20) && (cur_ch >= 6) && (cur_ch < 9)) goto cur_ch_cont;
+
+ Bitu k = cur_ch;
+ cptr = &op[cur_ch];
+
+ // check for FM/AM
+ if (adlibreg[ARC_FEEDBACK+k]&1) {
+ // 2op additive synthesis
+ if ((cptr[9].op_state == OF_TYPE_OFF) && (cptr[0].op_state == OF_TYPE_OFF)) goto cur_ch_cont;
+ if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) {
+ vibval1 = vibval_var1;
+ for (i=0;i<endsamples;i++)
+ vibval1[i] = ((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval1 = vibval_const;
+ if ((cptr[9].vibrato) && (cptr[9].op_state != OF_TYPE_OFF)) {
+ vibval2 = vibval_var2;
+ for (i=0;i<endsamples;i++)
+ vibval2[i] = ((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval2 = vibval_const;
+ if (cptr[0].tremolo) tremval1 = trem_lut; // tremolo enabled, use table
+ else tremval1 = tremval_const;
+ if (cptr[9].tremolo) tremval2 = trem_lut; // tremolo enabled, use table
+ else tremval2 = tremval_const;
+
+ // calculate channel output
+ for (i=0;i<endsamples;i++) {
+ // carrier1
+ operator_advance(&cptr[0],vibval1[i]);
+ opfuncs[cptr[0].op_state](&cptr[0]);
+ operator_output(&cptr[0],(cptr[0].lastcval+cptr[0].cval)*cptr[0].mfbi/2,tremval1[i]);
+
+ // carrier2
+ operator_advance(&cptr[9],vibval2[i]);
+ opfuncs[cptr[9].op_state](&cptr[9]);
+ operator_output(&cptr[9],0,tremval2[i]);
+
+ chanval = cptr[9].cval + cptr[0].cval;
+ outbufl[i] += chanval;
+ }
+ } else {
+
+ // 2op frequency modulation
+ if ((cptr[9].op_state == OF_TYPE_OFF) && (cptr[0].op_state == OF_TYPE_OFF)) goto cur_ch_cont;
+ if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) {
+ vibval1 = vibval_var1;
+ for (i=0;i<endsamples;i++)
+ vibval1[i] = ((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval1 = vibval_const;
+ if ((cptr[9].vibrato) && (cptr[9].op_state != OF_TYPE_OFF)) {
+ vibval2 = vibval_var2;
+ for (i=0;i<endsamples;i++)
+ vibval2[i] = ((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval2 = vibval_const;
+ if (cptr[0].tremolo) tremval1 = trem_lut; // tremolo enabled, use table
+ else tremval1 = tremval_const;
+ if (cptr[9].tremolo) tremval2 = trem_lut; // tremolo enabled, use table
+ else tremval2 = tremval_const;
+
+ // calculate channel output
+ for (i=0;i<endsamples;i++) {
+ // modulator
+ operator_advance(&cptr[0],vibval1[i]);
+ opfuncs[cptr[0].op_state](&cptr[0]);
+ operator_output(&cptr[0],(cptr[0].lastcval+cptr[0].cval)*cptr[0].mfbi/2,tremval1[i]);
+
+ // carrier
+ operator_advance(&cptr[9],vibval2[i]);
+ opfuncs[cptr[9].op_state](&cptr[9]);
+ operator_output(&cptr[9],cptr[0].cval*FIXEDPT,tremval2[i]);
+
+ chanval = cptr[9].cval;
+ outbufl[i] += chanval;
+ }
+ }
+cur_ch_cont:
+ }
+
+ // convert to 16bit samples
+ if (is_stereo_output) {
+ for (i = 0; i < endsamples; i++) {
+ clipit16(outbufl[i], sndptr);
+ sndptr +=2;
+ clipit16(outbufl[i], sndptr);
+ sndptr +=2;
+ }
+ } else {
+ for (i = 0; i < endsamples; i++) {
+ clipit16(outbufl[i], sndptr);
+ sndptr +=2;
+ }
+ }
+
+ }
+}
diff --git a/Run.HC b/Run.HC
index 9417c26..6c31561 100644
--- a/Run.HC
+++ b/Run.HC
@@ -7,6 +7,10 @@
#include "Input";
#include "Tile";
+#include "HDAudio";
+#include "Opl";
+#include "Music";
+
#include "Font";
#include "Status";