-- cgit v1.2.3 From 2a64882493c381b2ce9840ddd259aa5abb71a06a Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Wed, 4 Mar 2009 21:16:07 +0000 Subject: Add fmopl files from ScummVM. Subversion-branch: /branches/opl-branch Subversion-revision: 1445 --- opl/fmopl.c | 1191 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ opl/fmopl.h | 173 +++++++++ 2 files changed, 1364 insertions(+) create mode 100644 opl/fmopl.c create mode 100644 opl/fmopl.h diff --git a/opl/fmopl.c b/opl/fmopl.c new file mode 100644 index 00000000..5fda6e44 --- /dev/null +++ b/opl/fmopl.c @@ -0,0 +1,1191 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: http://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/sound/fmopl.cpp $ + * $Id: fmopl.cpp 38211 2009-02-15 10:07:50Z sev $ + * + * LGPL licensed version of MAMEs fmopl (V0.37a modified) by + * Tatsuyuki Satoh. Included from LGPL'ed AdPlug. + */ + +#include +#include +#include +#include +#include + +#include "sound/fmopl.h" + +#if defined (_WIN32_WCE) || defined (__SYMBIAN32__) || defined(PALMOS_MODE) || defined(__GP32__) || defined(GP2X) || defined (__MAEMO__) || defined(__DS__) || defined (__MINT__) +#include "common/config-manager.h" +#endif + +/* -------------------- preliminary define section --------------------- */ +/* attack/decay rate time rate */ +#define OPL_ARRATE 141280 /* RATE 4 = 2826.24ms @ 3.6MHz */ +#define OPL_DRRATE 1956000 /* RATE 4 = 39280.64ms @ 3.6MHz */ + +#define FREQ_BITS 24 /* frequency turn */ + +/* counter bits = 20 , octerve 7 */ +#define FREQ_RATE (1<<(FREQ_BITS-20)) +#define TL_BITS (FREQ_BITS+2) + +/* final output shift , limit minimum and maximum */ +#define OPL_OUTSB (TL_BITS+3-16) /* OPL output final shift 16bit */ +#define OPL_MAXOUT (0x7fff<status |= flag; + if(!(OPL->status & 0x80)) { + if(OPL->status & OPL->statusmask) { /* IRQ on */ + OPL->status |= 0x80; + /* callback user interrupt handler (IRQ is OFF to ON) */ + if(OPL->IRQHandler) + (OPL->IRQHandler)(OPL->IRQParam,1); + } + } +} + +/* status reset and IRQ handling */ +inline void OPL_STATUS_RESET(FM_OPL *OPL, int flag) { + /* reset status flag */ + OPL->status &= ~flag; + if((OPL->status & 0x80)) { + if (!(OPL->status & OPL->statusmask)) { + OPL->status &= 0x7f; + /* callback user interrupt handler (IRQ is ON to OFF) */ + if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0); + } + } +} + +/* IRQ mask set */ +inline void OPL_STATUSMASK_SET(FM_OPL *OPL, int flag) { + OPL->statusmask = flag; + /* IRQ handling check */ + OPL_STATUS_SET(OPL,0); + OPL_STATUS_RESET(OPL,0); +} + +/* ----- key on ----- */ +inline void OPL_KEYON(OPL_SLOT *SLOT) { + /* sin wave restart */ + SLOT->Cnt = 0; + /* set attack */ + SLOT->evm = ENV_MOD_AR; + SLOT->evs = SLOT->evsa; + SLOT->evc = EG_AST; + SLOT->eve = EG_AED; +} + +/* ----- key off ----- */ +inline void OPL_KEYOFF(OPL_SLOT *SLOT) { + if( SLOT->evm > ENV_MOD_RR) { + /* set envelope counter from envleope output */ + + // WORKAROUND: The Kyra engine does something very strange when + // starting a new song. For each channel: + // + // * The release rate is set to "fastest". + // * Any note is keyed off. + // * A very low-frequency note is keyed on. + // + // Usually, what happens next is that the real notes is keyed + // on immediately, in which case there's no problem. + // + // However, if the note is again keyed off (because the channel + // begins on a rest rather than a note), the envelope counter + // was moved from the very lowest point on the attack curve to + // the very highest point on the release curve. + // + // Again, this might not be a problem, if the release rate is + // still set to "fastest". But in many cases, it had already + // been increased. And, possibly because of inaccuracies in the + // envelope generator, that would cause the note to "fade out" + // for quite a long time. + // + // What we really need is a way to find the correct starting + // point for the envelope counter, and that may be what the + // commented-out line below is meant to do. For now, simply + // handle the pathological case. + + if (SLOT->evm == ENV_MOD_AR && SLOT->evc == EG_AST) + SLOT->evc = EG_DED; + else if( !(SLOT->evc & EG_DST) ) + //SLOT->evc = (ENV_CURVE[SLOT->evc>>ENV_BITS]<evc = EG_DST; + SLOT->eve = EG_DED; + SLOT->evs = SLOT->evsr; + SLOT->evm = ENV_MOD_RR; + } +} + +/* ---------- calcrate Envelope Generator & Phase Generator ---------- */ + +/* return : envelope output */ +inline uint OPL_CALC_SLOT(OPL_SLOT *SLOT) { + /* calcrate envelope generator */ + if((SLOT->evc += SLOT->evs) >= SLOT->eve) { + switch( SLOT->evm ) { + case ENV_MOD_AR: /* ATTACK -> DECAY1 */ + /* next DR */ + SLOT->evm = ENV_MOD_DR; + SLOT->evc = EG_DST; + SLOT->eve = SLOT->SL; + SLOT->evs = SLOT->evsd; + break; + case ENV_MOD_DR: /* DECAY -> SL or RR */ + SLOT->evc = SLOT->SL; + SLOT->eve = EG_DED; + if(SLOT->eg_typ) { + SLOT->evs = 0; + } else { + SLOT->evm = ENV_MOD_RR; + SLOT->evs = SLOT->evsr; + } + break; + case ENV_MOD_RR: /* RR -> OFF */ + SLOT->evc = EG_OFF; + SLOT->eve = EG_OFF + 1; + SLOT->evs = 0; + break; + } + } + /* calcrate envelope */ + return SLOT->TLL + ENV_CURVE[SLOT->evc>>ENV_BITS] + (SLOT->ams ? ams : 0); +} + +/* set algorythm connection */ +static void set_algorythm(OPL_CH *CH) { + int *carrier = &outd[0]; + CH->connect1 = CH->CON ? carrier : &feedback2; + CH->connect2 = carrier; +} + +/* ---------- frequency counter for operater update ---------- */ +inline void CALC_FCSLOT(OPL_CH *CH, OPL_SLOT *SLOT) { + int ksr; + + /* frequency step counter */ + SLOT->Incr = CH->fc * SLOT->mul; + ksr = CH->kcode >> SLOT->KSR; + + if( SLOT->ksr != ksr ) { + SLOT->ksr = ksr; + /* attack , decay rate recalcration */ + SLOT->evsa = SLOT->AR[ksr]; + SLOT->evsd = SLOT->DR[ksr]; + SLOT->evsr = SLOT->RR[ksr]; + } + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); +} + +/* set multi,am,vib,EG-TYP,KSR,mul */ +inline void set_mul(FM_OPL *OPL, int slot, int v) { + OPL_CH *CH = &OPL->P_CH[slot>>1]; + OPL_SLOT *SLOT = &CH->SLOT[slot & 1]; + + SLOT->mul = MUL_TABLE[v & 0x0f]; + SLOT->KSR = (v & 0x10) ? 0 : 2; + SLOT->eg_typ = (v & 0x20) >> 5; + SLOT->vib = (v & 0x40); + SLOT->ams = (v & 0x80); + CALC_FCSLOT(CH, SLOT); +} + +/* set ksl & tl */ +inline void set_ksl_tl(FM_OPL *OPL, int slot, int v) { + OPL_CH *CH = &OPL->P_CH[slot>>1]; + OPL_SLOT *SLOT = &CH->SLOT[slot & 1]; + int ksl = v >> 6; /* 0 / 1.5 / 3 / 6 db/OCT */ + + SLOT->ksl = ksl ? 3-ksl : 31; + SLOT->TL = (int)((v & 0x3f) * (0.75 / EG_STEP)); /* 0.75db step */ + + if(!(OPL->mode & 0x80)) { /* not CSM latch total level */ + SLOT->TLL = SLOT->TL + (CH->ksl_base >> SLOT->ksl); + } +} + +/* set attack rate & decay rate */ +inline void set_ar_dr(FM_OPL *OPL, int slot, int v) { + OPL_CH *CH = &OPL->P_CH[slot>>1]; + OPL_SLOT *SLOT = &CH->SLOT[slot & 1]; + int ar = v >> 4; + int dr = v & 0x0f; + + SLOT->AR = ar ? &OPL->AR_TABLE[ar << 2] : RATE_0; + SLOT->evsa = SLOT->AR[SLOT->ksr]; + if(SLOT->evm == ENV_MOD_AR) + SLOT->evs = SLOT->evsa; + + SLOT->DR = dr ? &OPL->DR_TABLE[dr<<2] : RATE_0; + SLOT->evsd = SLOT->DR[SLOT->ksr]; + if(SLOT->evm == ENV_MOD_DR) + SLOT->evs = SLOT->evsd; +} + +/* set sustain level & release rate */ +inline void set_sl_rr(FM_OPL *OPL, int slot, int v) { + OPL_CH *CH = &OPL->P_CH[slot>>1]; + OPL_SLOT *SLOT = &CH->SLOT[slot & 1]; + int sl = v >> 4; + int rr = v & 0x0f; + + SLOT->SL = SL_TABLE[sl]; + if(SLOT->evm == ENV_MOD_DR) + SLOT->eve = SLOT->SL; + SLOT->RR = &OPL->DR_TABLE[rr<<2]; + SLOT->evsr = SLOT->RR[SLOT->ksr]; + if(SLOT->evm == ENV_MOD_RR) + SLOT->evs = SLOT->evsr; +} + +/* operator output calcrator */ + +#define OP_OUT(slot,env,con) slot->wavetable[((slot->Cnt + con)>>(24-SIN_ENT_SHIFT)) & (SIN_ENT-1)][env] +/* ---------- calcrate one of channel ---------- */ +inline void OPL_CALC_CH(OPL_CH *CH) { + uint env_out; + OPL_SLOT *SLOT; + + feedback2 = 0; + /* SLOT 1 */ + SLOT = &CH->SLOT[SLOT1]; + env_out=OPL_CALC_SLOT(SLOT); + if(env_out < (uint)(EG_ENT - 1)) { + /* PG */ + if(SLOT->vib) + SLOT->Cnt += (SLOT->Incr * vib) >> VIB_RATE_SHIFT; + else + SLOT->Cnt += SLOT->Incr; + /* connection */ + if(CH->FB) { + int feedback1 = (CH->op1_out[0] + CH->op1_out[1]) >> CH->FB; + CH->op1_out[1] = CH->op1_out[0]; + *CH->connect1 += CH->op1_out[0] = OP_OUT(SLOT, env_out, feedback1); + } else { + *CH->connect1 += OP_OUT(SLOT, env_out, 0); + } + } else { + CH->op1_out[1] = CH->op1_out[0]; + CH->op1_out[0] = 0; + } + /* SLOT 2 */ + SLOT = &CH->SLOT[SLOT2]; + env_out=OPL_CALC_SLOT(SLOT); + if(env_out < (uint)(EG_ENT - 1)) { + /* PG */ + if(SLOT->vib) + SLOT->Cnt += (SLOT->Incr * vib) >> VIB_RATE_SHIFT; + else + SLOT->Cnt += SLOT->Incr; + /* connection */ + outd[0] += OP_OUT(SLOT, env_out, feedback2); + } +} + +/* ---------- calcrate rythm block ---------- */ +#define WHITE_NOISE_db 6.0 +inline void OPL_CALC_RH(FM_OPL *OPL, OPL_CH *CH) { + uint env_tam, env_sd, env_top, env_hh; + // This code used to do int(OPL->rnd.getRandomBit() * (WHITE_NOISE_db / EG_STEP)), + // but EG_STEP = 96.0/EG_ENT, and WHITE_NOISE_db=6.0. So, that's equivalent to + // int(OPL->rnd.getRandomBit() * EG_ENT/16). We know that EG_ENT is 4096, or 1024, + // or 128, so we can safely avoid any FP ops. + int whitenoise = OPL->rnd.getRandomBit() * (EG_ENT>>4); + + int tone8; + + OPL_SLOT *SLOT; + int env_out; + + /* BD : same as FM serial mode and output level is large */ + feedback2 = 0; + /* SLOT 1 */ + SLOT = &CH[6].SLOT[SLOT1]; + env_out = OPL_CALC_SLOT(SLOT); + if(env_out < EG_ENT-1) { + /* PG */ + if(SLOT->vib) + SLOT->Cnt += (SLOT->Incr * vib) >> VIB_RATE_SHIFT; + else + SLOT->Cnt += SLOT->Incr; + /* connection */ + if(CH[6].FB) { + int feedback1 = (CH[6].op1_out[0] + CH[6].op1_out[1]) >> CH[6].FB; + CH[6].op1_out[1] = CH[6].op1_out[0]; + feedback2 = CH[6].op1_out[0] = OP_OUT(SLOT, env_out, feedback1); + } + else { + feedback2 = OP_OUT(SLOT, env_out, 0); + } + } else { + feedback2 = 0; + CH[6].op1_out[1] = CH[6].op1_out[0]; + CH[6].op1_out[0] = 0; + } + /* SLOT 2 */ + SLOT = &CH[6].SLOT[SLOT2]; + env_out = OPL_CALC_SLOT(SLOT); + if(env_out < EG_ENT-1) { + /* PG */ + if(SLOT->vib) + SLOT->Cnt += (SLOT->Incr * vib) >> VIB_RATE_SHIFT; + else + SLOT->Cnt += SLOT->Incr; + /* connection */ + outd[0] += OP_OUT(SLOT, env_out, feedback2) * 2; + } + + // SD (17) = mul14[fnum7] + white noise + // TAM (15) = mul15[fnum8] + // TOP (18) = fnum6(mul18[fnum8]+whitenoise) + // HH (14) = fnum7(mul18[fnum8]+whitenoise) + white noise + env_sd = OPL_CALC_SLOT(SLOT7_2) + whitenoise; + env_tam =OPL_CALC_SLOT(SLOT8_1); + env_top = OPL_CALC_SLOT(SLOT8_2); + env_hh = OPL_CALC_SLOT(SLOT7_1) + whitenoise; + + /* PG */ + if(SLOT7_1->vib) + SLOT7_1->Cnt += (SLOT7_1->Incr * vib) >> (VIB_RATE_SHIFT-1); + else + SLOT7_1->Cnt += 2 * SLOT7_1->Incr; + if(SLOT7_2->vib) + SLOT7_2->Cnt += (CH[7].fc * vib) >> (VIB_RATE_SHIFT-3); + else + SLOT7_2->Cnt += (CH[7].fc * 8); + if(SLOT8_1->vib) + SLOT8_1->Cnt += (SLOT8_1->Incr * vib) >> VIB_RATE_SHIFT; + else + SLOT8_1->Cnt += SLOT8_1->Incr; + if(SLOT8_2->vib) + SLOT8_2->Cnt += ((CH[8].fc * 3) * vib) >> (VIB_RATE_SHIFT-4); + else + SLOT8_2->Cnt += (CH[8].fc * 48); + + tone8 = OP_OUT(SLOT8_2,whitenoise,0 ); + + /* SD */ + if(env_sd < (uint)(EG_ENT - 1)) + outd[0] += OP_OUT(SLOT7_1, env_sd, 0) * 8; + /* TAM */ + if(env_tam < (uint)(EG_ENT - 1)) + outd[0] += OP_OUT(SLOT8_1, env_tam, 0) * 2; + /* TOP-CY */ + if(env_top < (uint)(EG_ENT - 1)) + outd[0] += OP_OUT(SLOT7_2, env_top, tone8) * 2; + /* HH */ + if(env_hh < (uint)(EG_ENT-1)) + outd[0] += OP_OUT(SLOT7_2, env_hh, tone8) * 2; +} + +/* ----------- initialize time tabls ----------- */ +static void init_timetables(FM_OPL *OPL, int ARRATE, int DRRATE) { + int i; + double rate; + + /* make attack rate & decay rate tables */ + for (i = 0; i < 4; i++) + OPL->AR_TABLE[i] = OPL->DR_TABLE[i] = 0; + for (i = 4; i <= 60; i++) { + rate = OPL->freqbase; /* frequency rate */ + if(i < 60) + rate *= 1.0 + (i & 3) * 0.25; /* b0-1 : x1 , x1.25 , x1.5 , x1.75 */ + rate *= 1 << ((i >> 2) - 1); /* b2-5 : shift bit */ + rate *= (double)(EG_ENT << ENV_BITS); + OPL->AR_TABLE[i] = (int)(rate / ARRATE); + OPL->DR_TABLE[i] = (int)(rate / DRRATE); + } + for (i = 60; i < 76; i++) { + OPL->AR_TABLE[i] = EG_AED-1; + OPL->DR_TABLE[i] = OPL->DR_TABLE[60]; + } +} + +/* ---------- generic table initialize ---------- */ +static int OPLOpenTable(void) { + int s,t; + double rate; + int i,j; + double pom; + +#ifdef __DS__ + DS::fastRamReset(); + + TL_TABLE = (int *) DS::fastRamAlloc(TL_MAX * 2 * sizeof(int *)); + SIN_TABLE = (int **) DS::fastRamAlloc(SIN_ENT * 4 * sizeof(int *)); +#else + + /* allocate dynamic tables */ + if((TL_TABLE = (int *)malloc(TL_MAX * 2 * sizeof(int))) == NULL) + return 0; + + if((SIN_TABLE = (int **)malloc(SIN_ENT * 4 * sizeof(int *))) == NULL) { + free(TL_TABLE); + return 0; + } +#endif + + if((AMS_TABLE = (int *)malloc(AMS_ENT * 2 * sizeof(int))) == NULL) { + free(TL_TABLE); + free(SIN_TABLE); + return 0; + } + + if((VIB_TABLE = (int *)malloc(VIB_ENT * 2 * sizeof(int))) == NULL) { + free(TL_TABLE); + free(SIN_TABLE); + free(AMS_TABLE); + return 0; + } + /* make total level table */ + for (t = 0; t < EG_ENT - 1 ; t++) { + rate = ((1 << TL_BITS) - 1) / pow(10.0, EG_STEP * t / 20); /* dB -> voltage */ + TL_TABLE[ t] = (int)rate; + TL_TABLE[TL_MAX + t] = -TL_TABLE[t]; + } + /* fill volume off area */ + for (t = EG_ENT - 1; t < TL_MAX; t++) { + TL_TABLE[t] = TL_TABLE[TL_MAX + t] = 0; + } + + /* make sinwave table (total level offet) */ + /* degree 0 = degree 180 = off */ + SIN_TABLE[0] = SIN_TABLE[SIN_ENT /2 ] = &TL_TABLE[EG_ENT - 1]; + for (s = 1;s <= SIN_ENT / 4; s++) { + pom = sin(2 * PI * s / SIN_ENT); /* sin */ + pom = 20 * log10(1 / pom); /* decibel */ + j = int(pom / EG_STEP); /* TL_TABLE steps */ + + /* degree 0 - 90 , degree 180 - 90 : plus section */ + SIN_TABLE[ s] = SIN_TABLE[SIN_ENT / 2 - s] = &TL_TABLE[j]; + /* degree 180 - 270 , degree 360 - 270 : minus section */ + SIN_TABLE[SIN_ENT / 2 + s] = SIN_TABLE[SIN_ENT - s] = &TL_TABLE[TL_MAX + j]; + } + for (s = 0;s < SIN_ENT; s++) { + SIN_TABLE[SIN_ENT * 1 + s] = s < (SIN_ENT / 2) ? SIN_TABLE[s] : &TL_TABLE[EG_ENT]; + SIN_TABLE[SIN_ENT * 2 + s] = SIN_TABLE[s % (SIN_ENT / 2)]; + SIN_TABLE[SIN_ENT * 3 + s] = (s / (SIN_ENT / 4)) & 1 ? &TL_TABLE[EG_ENT] : SIN_TABLE[SIN_ENT * 2 + s]; + } + + + ENV_CURVE = (int *)malloc(sizeof(int) * (2*EG_ENT+1)); + + /* envelope counter -> envelope output table */ + for (i=0; i < EG_ENT; i++) { + /* ATTACK curve */ + pom = pow(((double)(EG_ENT - 1 - i) / EG_ENT), 8) * EG_ENT; + /* if( pom >= EG_ENT ) pom = EG_ENT-1; */ + ENV_CURVE[i] = (int)pom; + /* DECAY ,RELEASE curve */ + ENV_CURVE[(EG_DST >> ENV_BITS) + i]= i; + } + /* off */ + ENV_CURVE[EG_OFF >> ENV_BITS]= EG_ENT - 1; + /* make LFO ams table */ + for (i=0; i < AMS_ENT; i++) { + pom = (1.0 + sin(2 * PI * i / AMS_ENT)) / 2; /* sin */ + AMS_TABLE[i] = (int)((1.0 / EG_STEP) * pom); /* 1dB */ + AMS_TABLE[AMS_ENT + i] = (int)((4.8 / EG_STEP) * pom); /* 4.8dB */ + } + /* make LFO vibrate table */ + for (i=0; i < VIB_ENT; i++) { + /* 100cent = 1seminote = 6% ?? */ + pom = (double)VIB_RATE * 0.06 * sin(2 * PI * i / VIB_ENT); /* +-100sect step */ + VIB_TABLE[i] = (int)(VIB_RATE + (pom * 0.07)); /* +- 7cent */ + VIB_TABLE[VIB_ENT + i] = (int)(VIB_RATE + (pom * 0.14)); /* +-14cent */ + } + return 1; +} + +static void OPLCloseTable(void) { + free(TL_TABLE); + free(SIN_TABLE); + free(AMS_TABLE); + free(VIB_TABLE); + free(ENV_CURVE); +} + +/* CSM Key Controll */ +inline void CSMKeyControll(OPL_CH *CH) { + OPL_SLOT *slot1 = &CH->SLOT[SLOT1]; + OPL_SLOT *slot2 = &CH->SLOT[SLOT2]; + /* all key off */ + OPL_KEYOFF(slot1); + OPL_KEYOFF(slot2); + /* total level latch */ + slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl); + slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl); + /* key on */ + CH->op1_out[0] = CH->op1_out[1] = 0; + OPL_KEYON(slot1); + OPL_KEYON(slot2); +} + +/* ---------- opl initialize ---------- */ +static void OPL_initalize(FM_OPL *OPL) { + int fn; + + /* frequency base */ + OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / OPL->rate) / 72 : 0; + /* Timer base time */ + OPL->TimerBase = 1.0/((double)OPL->clock / 72.0 ); + /* make time tables */ + init_timetables(OPL, OPL_ARRATE, OPL_DRRATE); + /* make fnumber -> increment counter table */ + for( fn=0; fn < 1024; fn++) { + OPL->FN_TABLE[fn] = (uint)(OPL->freqbase * fn * FREQ_RATE * (1<<7) / 2); + } + /* LFO freq.table */ + OPL->amsIncr = (int)(OPL->rate ? (double)AMS_ENT * (1 << AMS_SHIFT) / OPL->rate * 3.7 * ((double)OPL->clock/3600000) : 0); + OPL->vibIncr = (int)(OPL->rate ? (double)VIB_ENT * (1 << VIB_SHIFT) / OPL->rate * 6.4 * ((double)OPL->clock/3600000) : 0); +} + +/* ---------- write a OPL registers ---------- */ +void OPLWriteReg(FM_OPL *OPL, int r, int v) { + OPL_CH *CH; + int slot; + uint block_fnum; + + switch(r & 0xe0) { + case 0x00: /* 00-1f:controll */ + switch(r & 0x1f) { + case 0x01: + /* wave selector enable */ + if(OPL->type&OPL_TYPE_WAVESEL) { + OPL->wavesel = v & 0x20; + if(!OPL->wavesel) { + /* preset compatible mode */ + int c; + for(c=0; cmax_ch; c++) { + OPL->P_CH[c].SLOT[SLOT1].wavetable = &SIN_TABLE[0]; + OPL->P_CH[c].SLOT[SLOT2].wavetable = &SIN_TABLE[0]; + } + } + } + return; + case 0x02: /* Timer 1 */ + OPL->T[0] = (256-v) * 4; + break; + case 0x03: /* Timer 2 */ + OPL->T[1] = (256-v) * 16; + return; + case 0x04: /* IRQ clear / mask and Timer enable */ + if(v & 0x80) { /* IRQ flag clear */ + OPL_STATUS_RESET(OPL, 0x7f); + } else { /* set IRQ mask ,timer enable*/ + uint8 st1 = v & 1; + uint8 st2 = (v >> 1) & 1; + /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */ + OPL_STATUS_RESET(OPL, v & 0x78); + OPL_STATUSMASK_SET(OPL,((~v) & 0x78) | 0x01); + /* timer 2 */ + if(OPL->st[1] != st2) { + double interval = st2 ? (double)OPL->T[1] * OPL->TimerBase : 0.0; + OPL->st[1] = st2; + if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam + 1, interval); + } + /* timer 1 */ + if(OPL->st[0] != st1) { + double interval = st1 ? (double)OPL->T[0] * OPL->TimerBase : 0.0; + OPL->st[0] = st1; + if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam + 0, interval); + } + } + return; + } + break; + case 0x20: /* am,vib,ksr,eg type,mul */ + slot = slot_array[r&0x1f]; + if(slot == -1) + return; + set_mul(OPL,slot,v); + return; + case 0x40: + slot = slot_array[r&0x1f]; + if(slot == -1) + return; + set_ksl_tl(OPL,slot,v); + return; + case 0x60: + slot = slot_array[r&0x1f]; + if(slot == -1) + return; + set_ar_dr(OPL,slot,v); + return; + case 0x80: + slot = slot_array[r&0x1f]; + if(slot == -1) + return; + set_sl_rr(OPL,slot,v); + return; + case 0xa0: + switch(r) { + case 0xbd: + /* amsep,vibdep,r,bd,sd,tom,tc,hh */ + { + uint8 rkey = OPL->rythm ^ v; + OPL->ams_table = &AMS_TABLE[v & 0x80 ? AMS_ENT : 0]; + OPL->vib_table = &VIB_TABLE[v & 0x40 ? VIB_ENT : 0]; + OPL->rythm = v & 0x3f; + if(OPL->rythm & 0x20) { + /* BD key on/off */ + if(rkey & 0x10) { + if(v & 0x10) { + OPL->P_CH[6].op1_out[0] = OPL->P_CH[6].op1_out[1] = 0; + OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT1]); + OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT2]); + } else { + OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1]); + OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2]); + } + } + /* SD key on/off */ + if(rkey & 0x08) { + if(v & 0x08) + OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT2]); + else + OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2]); + }/* TAM key on/off */ + if(rkey & 0x04) { + if(v & 0x04) + OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT1]); + else + OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1]); + } + /* TOP-CY key on/off */ + if(rkey & 0x02) { + if(v & 0x02) + OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT2]); + else + OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2]); + } + /* HH key on/off */ + if(rkey & 0x01) { + if(v & 0x01) + OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT1]); + else + OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1]); + } + } + } + return; + + default: + break; + } + /* keyon,block,fnum */ + if((r & 0x0f) > 8) + return; + CH = &OPL->P_CH[r & 0x0f]; + if(!(r&0x10)) { /* a0-a8 */ + block_fnum = (CH->block_fnum & 0x1f00) | v; + } else { /* b0-b8 */ + int keyon = (v >> 5) & 1; + block_fnum = ((v & 0x1f) << 8) | (CH->block_fnum & 0xff); + if(CH->keyon != keyon) { + if((CH->keyon=keyon)) { + CH->op1_out[0] = CH->op1_out[1] = 0; + OPL_KEYON(&CH->SLOT[SLOT1]); + OPL_KEYON(&CH->SLOT[SLOT2]); + } else { + OPL_KEYOFF(&CH->SLOT[SLOT1]); + OPL_KEYOFF(&CH->SLOT[SLOT2]); + } + } + } + /* update */ + if(CH->block_fnum != block_fnum) { + int blockRv = 7 - (block_fnum >> 10); + int fnum = block_fnum & 0x3ff; + CH->block_fnum = block_fnum; + CH->ksl_base = KSL_TABLE[block_fnum >> 6]; + CH->fc = OPL->FN_TABLE[fnum] >> blockRv; + CH->kcode = CH->block_fnum >> 9; + if((OPL->mode & 0x40) && CH->block_fnum & 0x100) + CH->kcode |=1; + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + } + return; + case 0xc0: + /* FB,C */ + if((r & 0x0f) > 8) + return; + CH = &OPL->P_CH[r&0x0f]; + { + int feedback = (v >> 1) & 7; + CH->FB = feedback ? (8 + 1) - feedback : 0; + CH->CON = v & 1; + set_algorythm(CH); + } + return; + case 0xe0: /* wave type */ + slot = slot_array[r & 0x1f]; + if(slot == -1) + return; + CH = &OPL->P_CH[slot>>1]; + if(OPL->wavesel) { + CH->SLOT[slot&1].wavetable = &SIN_TABLE[(v & 0x03) * SIN_ENT]; + } + return; + } +} + +/* lock/unlock for common table */ +static int OPL_LockTable(void) { + num_lock++; + if(num_lock>1) + return 0; + /* first time */ + cur_chip = NULL; + /* allocate total level table (128kb space) */ + if(!OPLOpenTable()) { + num_lock--; + return -1; + } + return 0; +} + +static void OPL_UnLockTable(void) { + if(num_lock) + num_lock--; + if(num_lock) + return; + /* last time */ + cur_chip = NULL; + OPLCloseTable(); +} + +/*******************************************************************************/ +/* YM3812 local section */ +/*******************************************************************************/ + +/* ---------- update one of chip ----------- */ +void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length, int interleave) { + int i; + int data; + int16 *buf = buffer; + uint amsCnt = OPL->amsCnt; + uint vibCnt = OPL->vibCnt; + uint8 rythm = OPL->rythm & 0x20; + OPL_CH *CH, *R_CH; + + + if((void *)OPL != cur_chip) { + cur_chip = (void *)OPL; + /* channel pointers */ + S_CH = OPL->P_CH; + E_CH = &S_CH[9]; + /* rythm slot */ + SLOT7_1 = &S_CH[7].SLOT[SLOT1]; + SLOT7_2 = &S_CH[7].SLOT[SLOT2]; + SLOT8_1 = &S_CH[8].SLOT[SLOT1]; + SLOT8_2 = &S_CH[8].SLOT[SLOT2]; + /* LFO state */ + amsIncr = OPL->amsIncr; + vibIncr = OPL->vibIncr; + ams_table = OPL->ams_table; + vib_table = OPL->vib_table; + } + R_CH = rythm ? &S_CH[6] : E_CH; + for(i = 0; i < length; i++) { + /* channel A channel B channel C */ + /* LFO */ + ams = ams_table[(amsCnt += amsIncr) >> AMS_SHIFT]; + vib = vib_table[(vibCnt += vibIncr) >> VIB_SHIFT]; + outd[0] = 0; + /* FM part */ + for(CH=S_CH; CH < R_CH; CH++) + OPL_CALC_CH(CH); + /* Rythn part */ + if(rythm) + OPL_CALC_RH(OPL, S_CH); + /* limit check */ + data = CLIP(outd[0], OPL_MINOUT, OPL_MAXOUT); + /* store to sound buffer */ + buf[i << interleave] = data >> OPL_OUTSB; + } + + OPL->amsCnt = amsCnt; + OPL->vibCnt = vibCnt; +} + +/* ---------- reset a chip ---------- */ +void OPLResetChip(FM_OPL *OPL) { + int c,s; + int i; + + /* reset chip */ + OPL->mode = 0; /* normal mode */ + OPL_STATUS_RESET(OPL, 0x7f); + /* reset with register write */ + OPLWriteReg(OPL, 0x01,0); /* wabesel disable */ + OPLWriteReg(OPL, 0x02,0); /* Timer1 */ + OPLWriteReg(OPL, 0x03,0); /* Timer2 */ + OPLWriteReg(OPL, 0x04,0); /* IRQ mask clear */ + for(i = 0xff; i >= 0x20; i--) + OPLWriteReg(OPL,i,0); + /* reset OPerator parameter */ + for(c = 0; c < OPL->max_ch ;c++ ) { + OPL_CH *CH = &OPL->P_CH[c]; + /* OPL->P_CH[c].PAN = OPN_CENTER; */ + for(s = 0; s < 2; s++ ) { + /* wave table */ + CH->SLOT[s].wavetable = &SIN_TABLE[0]; + /* CH->SLOT[s].evm = ENV_MOD_RR; */ + CH->SLOT[s].evc = EG_OFF; + CH->SLOT[s].eve = EG_OFF + 1; + CH->SLOT[s].evs = 0; + } + } +} + +/* ---------- Create a virtual YM3812 ---------- */ +/* 'rate' is sampling rate and 'bufsiz' is the size of the */ +FM_OPL *OPLCreate(int type, int clock, int rate) { + char *ptr; + FM_OPL *OPL; + int state_size; + int max_ch = 9; /* normaly 9 channels */ + + if( OPL_LockTable() == -1) + return NULL; + /* allocate OPL state space */ + state_size = sizeof(FM_OPL); + state_size += sizeof(OPL_CH) * max_ch; + + /* allocate memory block */ + ptr = (char *)calloc(state_size, 1); + if(ptr == NULL) + return NULL; + + /* clear */ + memset(ptr, 0, state_size); + OPL = (FM_OPL *)ptr; ptr += sizeof(FM_OPL); + OPL->P_CH = (OPL_CH *)ptr; ptr += sizeof(OPL_CH) * max_ch; + + /* set channel state pointer */ + OPL->type = type; + OPL->clock = clock; + OPL->rate = rate; + OPL->max_ch = max_ch; + + /* init grobal tables */ + OPL_initalize(OPL); + + /* reset chip */ + OPLResetChip(OPL); + return OPL; +} + +/* ---------- Destroy one of vietual YM3812 ---------- */ +void OPLDestroy(FM_OPL *OPL) { + OPL_UnLockTable(); + free(OPL); +} + +/* ---------- Option handlers ---------- */ +void OPLSetTimerHandler(FM_OPL *OPL, OPL_TIMERHANDLER TimerHandler,int channelOffset) { + OPL->TimerHandler = TimerHandler; + OPL->TimerParam = channelOffset; +} + +void OPLSetIRQHandler(FM_OPL *OPL, OPL_IRQHANDLER IRQHandler, int param) { + OPL->IRQHandler = IRQHandler; + OPL->IRQParam = param; +} + +void OPLSetUpdateHandler(FM_OPL *OPL, OPL_UPDATEHANDLER UpdateHandler,int param) { + OPL->UpdateHandler = UpdateHandler; + OPL->UpdateParam = param; +} + +/* ---------- YM3812 I/O interface ---------- */ +int OPLWrite(FM_OPL *OPL,int a,int v) { + if(!(a & 1)) { /* address port */ + OPL->address = v & 0xff; + } else { /* data port */ + if(OPL->UpdateHandler) + OPL->UpdateHandler(OPL->UpdateParam,0); + OPLWriteReg(OPL, OPL->address,v); + } + return OPL->status >> 7; +} + +unsigned char OPLRead(FM_OPL *OPL,int a) { + if(!(a & 1)) { /* status port */ + return OPL->status & (OPL->statusmask | 0x80); + } + /* data port */ + switch(OPL->address) { + case 0x05: /* KeyBoard IN */ + warning("OPL:read unmapped KEYBOARD port\n"); + return 0; + case 0x19: /* I/O DATA */ + warning("OPL:read unmapped I/O port\n"); + return 0; + case 0x1a: /* PCM-DATA */ + return 0; + default: + break; + } + return 0; +} + +int OPLTimerOver(FM_OPL *OPL, int c) { + if(c) { /* Timer B */ + OPL_STATUS_SET(OPL, 0x20); + } else { /* Timer A */ + OPL_STATUS_SET(OPL, 0x40); + /* CSM mode key,TL controll */ + if(OPL->mode & 0x80) { /* CSM mode total level latch and auto key on */ + int ch; + if(OPL->UpdateHandler) + OPL->UpdateHandler(OPL->UpdateParam,0); + for(ch = 0; ch < 9; ch++) + CSMKeyControll(&OPL->P_CH[ch]); + } + } + /* reload timer */ + if (OPL->TimerHandler) + (OPL->TimerHandler)(OPL->TimerParam + c, (double)OPL->T[c] * OPL->TimerBase); + return OPL->status >> 7; +} + +FM_OPL *makeAdlibOPL(int rate) { + // We need to emulate one YM3812 chip + int env_bits = FMOPL_ENV_BITS_HQ; + int eg_ent = FMOPL_EG_ENT_HQ; +#if defined (_WIN32_WCE) || defined(__SYMBIAN32__) || defined(PALMOS_MODE) || defined(__GP32__) || defined (GP2X) || defined(__MAEMO__) || defined(__DS__) || defined (__MINT__) + if (ConfMan.hasKey("FM_high_quality") && ConfMan.getBool("FM_high_quality")) { + env_bits = FMOPL_ENV_BITS_HQ; + eg_ent = FMOPL_EG_ENT_HQ; + } else if (ConfMan.hasKey("FM_medium_quality") && ConfMan.getBool("FM_medium_quality")) { + env_bits = FMOPL_ENV_BITS_MQ; + eg_ent = FMOPL_EG_ENT_MQ; + } else { + env_bits = FMOPL_ENV_BITS_LQ; + eg_ent = FMOPL_EG_ENT_LQ; + } +#endif + + OPLBuildTables(env_bits, eg_ent); + return OPLCreate(OPL_TYPE_YM3812, 3579545, rate); +} diff --git a/opl/fmopl.h b/opl/fmopl.h new file mode 100644 index 00000000..1c5e5e9f --- /dev/null +++ b/opl/fmopl.h @@ -0,0 +1,173 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: http://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/sound/fmopl.h $ + * $Id: fmopl.h 38211 2009-02-15 10:07:50Z sev $ + * + * LGPL licensed version of MAMEs fmopl (V0.37a modified) by + * Tatsuyuki Satoh. Included from LGPL'ed AdPlug. + */ + + +#ifndef SOUND_FMOPL_H +#define SOUND_FMOPL_H + +#include "common/scummsys.h" +#include "common/util.h" + +enum { + FMOPL_ENV_BITS_HQ = 16, + FMOPL_ENV_BITS_MQ = 8, + FMOPL_ENV_BITS_LQ = 8, + FMOPL_EG_ENT_HQ = 4096, + FMOPL_EG_ENT_MQ = 1024, + FMOPL_EG_ENT_LQ = 128 +}; + + +typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec); +typedef void (*OPL_IRQHANDLER)(int param,int irq); +typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us); + +#define OPL_TYPE_WAVESEL 0x01 /* waveform select */ + +/* Saving is necessary for member of the 'R' mark for suspend/resume */ +/* ---------- OPL one of slot ---------- */ +typedef struct fm_opl_slot { + int TL; /* total level :TL << 8 */ + int TLL; /* adjusted now TL */ + uint8 KSR; /* key scale rate :(shift down bit) */ + int *AR; /* attack rate :&AR_TABLE[AR<<2] */ + int *DR; /* decay rate :&DR_TABLE[DR<<2] */ + int SL; /* sustain level :SL_TABLE[SL] */ + int *RR; /* release rate :&DR_TABLE[RR<<2] */ + uint8 ksl; /* keyscale level :(shift down bits) */ + uint8 ksr; /* key scale rate :kcode>>KSR */ + uint mul; /* multiple :ML_TABLE[ML] */ + uint Cnt; /* frequency count */ + uint Incr; /* frequency step */ + + /* envelope generator state */ + uint8 eg_typ;/* envelope type flag */ + uint8 evm; /* envelope phase */ + int evc; /* envelope counter */ + int eve; /* envelope counter end point */ + int evs; /* envelope counter step */ + int evsa; /* envelope step for AR :AR[ksr] */ + int evsd; /* envelope step for DR :DR[ksr] */ + int evsr; /* envelope step for RR :RR[ksr] */ + + /* LFO */ + uint8 ams; /* ams flag */ + uint8 vib; /* vibrate flag */ + /* wave selector */ + int **wavetable; +} OPL_SLOT; + +/* ---------- OPL one of channel ---------- */ +typedef struct fm_opl_channel { + OPL_SLOT SLOT[2]; + uint8 CON; /* connection type */ + uint8 FB; /* feed back :(shift down bit)*/ + int *connect1; /* slot1 output pointer */ + int *connect2; /* slot2 output pointer */ + int op1_out[2]; /* slot1 output for selfeedback */ + + /* phase generator state */ + uint block_fnum; /* block+fnum */ + uint8 kcode; /* key code : KeyScaleCode */ + uint fc; /* Freq. Increment base */ + uint ksl_base; /* KeyScaleLevel Base step */ + uint8 keyon; /* key on/off flag */ +} OPL_CH; + +/* OPL state */ +typedef struct fm_opl_f { + uint8 type; /* chip type */ + int clock; /* master clock (Hz) */ + int rate; /* sampling rate (Hz) */ + double freqbase; /* frequency base */ + double TimerBase; /* Timer base time (==sampling time) */ + uint8 address; /* address register */ + uint8 status; /* status flag */ + uint8 statusmask; /* status mask */ + uint mode; /* Reg.08 : CSM , notesel,etc. */ + + /* Timer */ + int T[2]; /* timer counter */ + uint8 st[2]; /* timer enable */ + + /* FM channel slots */ + OPL_CH *P_CH; /* pointer of CH */ + int max_ch; /* maximum channel */ + + /* Rythm sention */ + uint8 rythm; /* Rythm mode , key flag */ + + /* time tables */ + int AR_TABLE[76]; /* atttack rate tables */ + int DR_TABLE[76]; /* decay rate tables */ + uint FN_TABLE[1024];/* fnumber -> increment counter */ + + /* LFO */ + int *ams_table; + int *vib_table; + int amsCnt; + int amsIncr; + int vibCnt; + int vibIncr; + + /* wave selector enable flag */ + uint8 wavesel; + + /* external event callback handler */ + OPL_TIMERHANDLER TimerHandler; /* TIMER handler */ + int TimerParam; /* TIMER parameter */ + OPL_IRQHANDLER IRQHandler; /* IRQ handler */ + int IRQParam; /* IRQ parameter */ + OPL_UPDATEHANDLER UpdateHandler; /* stream update handler */ + int UpdateParam; /* stream update parameter */ + + Common::RandomSource rnd; +} FM_OPL; + +/* ---------- Generic interface section ---------- */ +#define OPL_TYPE_YM3526 (0) +#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL) + +void OPLBuildTables(int ENV_BITS_PARAM, int EG_ENT_PARAM); + +FM_OPL *OPLCreate(int type, int clock, int rate); +void OPLDestroy(FM_OPL *OPL); +void OPLSetTimerHandler(FM_OPL *OPL, OPL_TIMERHANDLER TimerHandler, int channelOffset); +void OPLSetIRQHandler(FM_OPL *OPL, OPL_IRQHANDLER IRQHandler, int param); +void OPLSetUpdateHandler(FM_OPL *OPL, OPL_UPDATEHANDLER UpdateHandler, int param); + +void OPLResetChip(FM_OPL *OPL); +int OPLWrite(FM_OPL *OPL, int a, int v); +unsigned char OPLRead(FM_OPL *OPL, int a); +int OPLTimerOver(FM_OPL *OPL, int c); +void OPLWriteReg(FM_OPL *OPL, int r, int v); +void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length, int interleave = 0); + +// Factory method +FM_OPL *makeAdlibOPL(int rate); + +#endif -- cgit v1.2.3 From 4422bbf9815ec42402d0235845a8fd33afd792b2 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Wed, 4 Mar 2009 21:18:18 +0000 Subject: Fix up fmopl code to remove ScummVM dependencies and compile as C. Add to build. Subversion-branch: /branches/opl-branch Subversion-revision: 1446 --- Makefile.am | 2 +- configure.in | 1 + opl/Makefile.am | 8 +++++ opl/fmopl.c | 106 +++++++++++++++++++------------------------------------- opl/fmopl.h | 70 +++++++++++++++++-------------------- 5 files changed, 77 insertions(+), 110 deletions(-) create mode 100644 opl/Makefile.am diff --git a/Makefile.am b/Makefile.am index 40de5828..4f678149 100644 --- a/Makefile.am +++ b/Makefile.am @@ -44,7 +44,7 @@ EXTRA_DIST= \ MAINTAINERCLEANFILES = $(AUX_DIST_GEN) docdir=$(prefix)/share/doc/@PACKAGE@ -SUBDIRS=textscreen pcsound src man setup +SUBDIRS=textscreen opl pcsound src man setup if HAVE_PYTHON diff --git a/configure.in b/configure.in index 15ef200a..24f0bc97 100644 --- a/configure.in +++ b/configure.in @@ -91,6 +91,7 @@ textscreen/examples/Makefile setup/Makefile man/Makefile src/Makefile +opl/Makefile pcsound/Makefile src/resource.rc src/doom-screensaver.desktop diff --git a/opl/Makefile.am b/opl/Makefile.am new file mode 100644 index 00000000..8fd2303e --- /dev/null +++ b/opl/Makefile.am @@ -0,0 +1,8 @@ + +AM_CFLAGS=@SDLMIXER_CFLAGS@ + +noinst_LIBRARIES=libopl.a + +libopl_a_SOURCES = \ + fmopl.c + diff --git a/opl/fmopl.c b/opl/fmopl.c index 5fda6e44..6713ecc1 100644 --- a/opl/fmopl.c +++ b/opl/fmopl.c @@ -1,4 +1,4 @@ -/* ScummVM - Graphic Adventure Engine +/* This file is derived from fmopl.cpp from ScummVM. * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT @@ -18,9 +18,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * $URL: http://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/sound/fmopl.cpp $ - * $Id: fmopl.cpp 38211 2009-02-15 10:07:50Z sev $ - * * LGPL licensed version of MAMEs fmopl (V0.37a modified) by * Tatsuyuki Satoh. Included from LGPL'ed AdPlug. */ @@ -31,11 +28,13 @@ #include #include -#include "sound/fmopl.h" +#include "fmopl.h" + +#define PI 3.1415926539 -#if defined (_WIN32_WCE) || defined (__SYMBIAN32__) || defined(PALMOS_MODE) || defined(__GP32__) || defined(GP2X) || defined (__MAEMO__) || defined(__DS__) || defined (__MINT__) -#include "common/config-manager.h" -#endif +#define CLIP(value, min, max) \ + ( (value) < (min) ? (min) : \ + (value) > (max) ? (max) : (value) ) /* -------------------- preliminary define section --------------------- */ /* attack/decay rate time rate */ @@ -57,12 +56,7 @@ /* sinwave entries */ /* used static memory = SIN_ENT * 4 (byte) */ -#ifdef __DS__ -#include "dsmain.h" -#define SIN_ENT_SHIFT 8 -#else #define SIN_ENT_SHIFT 11 -#endif #define SIN_ENT (1<status |= flag; if(!(OPL->status & 0x80)) { @@ -261,7 +256,7 @@ inline void OPL_STATUS_SET(FM_OPL *OPL, int flag) { } /* status reset and IRQ handling */ -inline void OPL_STATUS_RESET(FM_OPL *OPL, int flag) { +static inline void OPL_STATUS_RESET(FM_OPL *OPL, int flag) { /* reset status flag */ OPL->status &= ~flag; if((OPL->status & 0x80)) { @@ -274,7 +269,7 @@ inline void OPL_STATUS_RESET(FM_OPL *OPL, int flag) { } /* IRQ mask set */ -inline void OPL_STATUSMASK_SET(FM_OPL *OPL, int flag) { +static inline void OPL_STATUSMASK_SET(FM_OPL *OPL, int flag) { OPL->statusmask = flag; /* IRQ handling check */ OPL_STATUS_SET(OPL,0); @@ -282,7 +277,7 @@ inline void OPL_STATUSMASK_SET(FM_OPL *OPL, int flag) { } /* ----- key on ----- */ -inline void OPL_KEYON(OPL_SLOT *SLOT) { +static inline void OPL_KEYON(OPL_SLOT *SLOT) { /* sin wave restart */ SLOT->Cnt = 0; /* set attack */ @@ -293,7 +288,7 @@ inline void OPL_KEYON(OPL_SLOT *SLOT) { } /* ----- key off ----- */ -inline void OPL_KEYOFF(OPL_SLOT *SLOT) { +static inline void OPL_KEYOFF(OPL_SLOT *SLOT) { if( SLOT->evm > ENV_MOD_RR) { /* set envelope counter from envleope output */ @@ -337,7 +332,7 @@ inline void OPL_KEYOFF(OPL_SLOT *SLOT) { /* ---------- calcrate Envelope Generator & Phase Generator ---------- */ /* return : envelope output */ -inline uint OPL_CALC_SLOT(OPL_SLOT *SLOT) { +static inline uint OPL_CALC_SLOT(OPL_SLOT *SLOT) { /* calcrate envelope generator */ if((SLOT->evc += SLOT->evs) >= SLOT->eve) { switch( SLOT->evm ) { @@ -377,7 +372,7 @@ static void set_algorythm(OPL_CH *CH) { } /* ---------- frequency counter for operater update ---------- */ -inline void CALC_FCSLOT(OPL_CH *CH, OPL_SLOT *SLOT) { +static inline void CALC_FCSLOT(OPL_CH *CH, OPL_SLOT *SLOT) { int ksr; /* frequency step counter */ @@ -395,7 +390,7 @@ inline void CALC_FCSLOT(OPL_CH *CH, OPL_SLOT *SLOT) { } /* set multi,am,vib,EG-TYP,KSR,mul */ -inline void set_mul(FM_OPL *OPL, int slot, int v) { +static inline void set_mul(FM_OPL *OPL, int slot, int v) { OPL_CH *CH = &OPL->P_CH[slot>>1]; OPL_SLOT *SLOT = &CH->SLOT[slot & 1]; @@ -408,7 +403,7 @@ inline void set_mul(FM_OPL *OPL, int slot, int v) { } /* set ksl & tl */ -inline void set_ksl_tl(FM_OPL *OPL, int slot, int v) { +static inline void set_ksl_tl(FM_OPL *OPL, int slot, int v) { OPL_CH *CH = &OPL->P_CH[slot>>1]; OPL_SLOT *SLOT = &CH->SLOT[slot & 1]; int ksl = v >> 6; /* 0 / 1.5 / 3 / 6 db/OCT */ @@ -422,7 +417,7 @@ inline void set_ksl_tl(FM_OPL *OPL, int slot, int v) { } /* set attack rate & decay rate */ -inline void set_ar_dr(FM_OPL *OPL, int slot, int v) { +static inline void set_ar_dr(FM_OPL *OPL, int slot, int v) { OPL_CH *CH = &OPL->P_CH[slot>>1]; OPL_SLOT *SLOT = &CH->SLOT[slot & 1]; int ar = v >> 4; @@ -440,7 +435,7 @@ inline void set_ar_dr(FM_OPL *OPL, int slot, int v) { } /* set sustain level & release rate */ -inline void set_sl_rr(FM_OPL *OPL, int slot, int v) { +static inline void set_sl_rr(FM_OPL *OPL, int slot, int v) { OPL_CH *CH = &OPL->P_CH[slot>>1]; OPL_SLOT *SLOT = &CH->SLOT[slot & 1]; int sl = v >> 4; @@ -459,7 +454,7 @@ inline void set_sl_rr(FM_OPL *OPL, int slot, int v) { #define OP_OUT(slot,env,con) slot->wavetable[((slot->Cnt + con)>>(24-SIN_ENT_SHIFT)) & (SIN_ENT-1)][env] /* ---------- calcrate one of channel ---------- */ -inline void OPL_CALC_CH(OPL_CH *CH) { +static inline void OPL_CALC_CH(OPL_CH *CH) { uint env_out; OPL_SLOT *SLOT; @@ -501,13 +496,13 @@ inline void OPL_CALC_CH(OPL_CH *CH) { /* ---------- calcrate rythm block ---------- */ #define WHITE_NOISE_db 6.0 -inline void OPL_CALC_RH(FM_OPL *OPL, OPL_CH *CH) { +static inline void OPL_CALC_RH(FM_OPL *OPL, OPL_CH *CH) { uint env_tam, env_sd, env_top, env_hh; // This code used to do int(OPL->rnd.getRandomBit() * (WHITE_NOISE_db / EG_STEP)), // but EG_STEP = 96.0/EG_ENT, and WHITE_NOISE_db=6.0. So, that's equivalent to // int(OPL->rnd.getRandomBit() * EG_ENT/16). We know that EG_ENT is 4096, or 1024, // or 128, so we can safely avoid any FP ops. - int whitenoise = OPL->rnd.getRandomBit() * (EG_ENT>>4); + int whitenoise = (rand() & 1) * (EG_ENT>>4); int tone8; @@ -625,13 +620,6 @@ static int OPLOpenTable(void) { int i,j; double pom; -#ifdef __DS__ - DS::fastRamReset(); - - TL_TABLE = (int *) DS::fastRamAlloc(TL_MAX * 2 * sizeof(int *)); - SIN_TABLE = (int **) DS::fastRamAlloc(SIN_ENT * 4 * sizeof(int *)); -#else - /* allocate dynamic tables */ if((TL_TABLE = (int *)malloc(TL_MAX * 2 * sizeof(int))) == NULL) return 0; @@ -640,7 +628,6 @@ static int OPLOpenTable(void) { free(TL_TABLE); return 0; } -#endif if((AMS_TABLE = (int *)malloc(AMS_ENT * 2 * sizeof(int))) == NULL) { free(TL_TABLE); @@ -671,7 +658,7 @@ static int OPLOpenTable(void) { for (s = 1;s <= SIN_ENT / 4; s++) { pom = sin(2 * PI * s / SIN_ENT); /* sin */ pom = 20 * log10(1 / pom); /* decibel */ - j = int(pom / EG_STEP); /* TL_TABLE steps */ + j = (int) (pom / EG_STEP); /* TL_TABLE steps */ /* degree 0 - 90 , degree 180 - 90 : plus section */ SIN_TABLE[ s] = SIN_TABLE[SIN_ENT / 2 - s] = &TL_TABLE[j]; @@ -723,7 +710,7 @@ static void OPLCloseTable(void) { } /* CSM Key Controll */ -inline void CSMKeyControll(OPL_CH *CH) { +static inline void CSMKeyControll(OPL_CH *CH) { OPL_SLOT *slot1 = &CH->SLOT[SLOT1]; OPL_SLOT *slot2 = &CH->SLOT[SLOT2]; /* all key off */ @@ -790,8 +777,8 @@ void OPLWriteReg(FM_OPL *OPL, int r, int v) { if(v & 0x80) { /* IRQ flag clear */ OPL_STATUS_RESET(OPL, 0x7f); } else { /* set IRQ mask ,timer enable*/ - uint8 st1 = v & 1; - uint8 st2 = (v >> 1) & 1; + uint8_t st1 = v & 1; + uint8_t st2 = (v >> 1) & 1; /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */ OPL_STATUS_RESET(OPL, v & 0x78); OPL_STATUSMASK_SET(OPL,((~v) & 0x78) | 0x01); @@ -840,7 +827,7 @@ void OPLWriteReg(FM_OPL *OPL, int r, int v) { case 0xbd: /* amsep,vibdep,r,bd,sd,tom,tc,hh */ { - uint8 rkey = OPL->rythm ^ v; + uint8_t rkey = OPL->rythm ^ v; OPL->ams_table = &AMS_TABLE[v & 0x80 ? AMS_ENT : 0]; OPL->vib_table = &VIB_TABLE[v & 0x40 ? VIB_ENT : 0]; OPL->rythm = v & 0x3f; @@ -978,13 +965,13 @@ static void OPL_UnLockTable(void) { /*******************************************************************************/ /* ---------- update one of chip ----------- */ -void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length, int interleave) { +void YM3812UpdateOne(FM_OPL *OPL, int16_t *buffer, int length, int interleave) { int i; int data; - int16 *buf = buffer; + int16_t *buf = buffer; uint amsCnt = OPL->amsCnt; uint vibCnt = OPL->vibCnt; - uint8 rythm = OPL->rythm & 0x20; + uint8_t rythm = OPL->rythm & 0x20; OPL_CH *CH, *R_CH; @@ -1133,19 +1120,7 @@ unsigned char OPLRead(FM_OPL *OPL,int a) { if(!(a & 1)) { /* status port */ return OPL->status & (OPL->statusmask | 0x80); } - /* data port */ - switch(OPL->address) { - case 0x05: /* KeyBoard IN */ - warning("OPL:read unmapped KEYBOARD port\n"); - return 0; - case 0x19: /* I/O DATA */ - warning("OPL:read unmapped I/O port\n"); - return 0; - case 0x1a: /* PCM-DATA */ - return 0; - default: - break; - } + return 0; } @@ -1173,19 +1148,8 @@ FM_OPL *makeAdlibOPL(int rate) { // We need to emulate one YM3812 chip int env_bits = FMOPL_ENV_BITS_HQ; int eg_ent = FMOPL_EG_ENT_HQ; -#if defined (_WIN32_WCE) || defined(__SYMBIAN32__) || defined(PALMOS_MODE) || defined(__GP32__) || defined (GP2X) || defined(__MAEMO__) || defined(__DS__) || defined (__MINT__) - if (ConfMan.hasKey("FM_high_quality") && ConfMan.getBool("FM_high_quality")) { - env_bits = FMOPL_ENV_BITS_HQ; - eg_ent = FMOPL_EG_ENT_HQ; - } else if (ConfMan.hasKey("FM_medium_quality") && ConfMan.getBool("FM_medium_quality")) { - env_bits = FMOPL_ENV_BITS_MQ; - eg_ent = FMOPL_EG_ENT_MQ; - } else { - env_bits = FMOPL_ENV_BITS_LQ; - eg_ent = FMOPL_EG_ENT_LQ; - } -#endif OPLBuildTables(env_bits, eg_ent); return OPLCreate(OPL_TYPE_YM3812, 3579545, rate); } + diff --git a/opl/fmopl.h b/opl/fmopl.h index 1c5e5e9f..2bbe8363 100644 --- a/opl/fmopl.h +++ b/opl/fmopl.h @@ -1,4 +1,4 @@ -/* ScummVM - Graphic Adventure Engine +/* This file is derived from fmopl.h from ScummVM. * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT @@ -18,19 +18,15 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * $URL: http://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/sound/fmopl.h $ - * $Id: fmopl.h 38211 2009-02-15 10:07:50Z sev $ - * * LGPL licensed version of MAMEs fmopl (V0.37a modified) by * Tatsuyuki Satoh. Included from LGPL'ed AdPlug. */ -#ifndef SOUND_FMOPL_H -#define SOUND_FMOPL_H +#ifndef OPL_FMOPL_H +#define OPL_FMOPL_H -#include "common/scummsys.h" -#include "common/util.h" +#include "inttypes.h" enum { FMOPL_ENV_BITS_HQ = 16, @@ -41,7 +37,6 @@ enum { FMOPL_EG_ENT_LQ = 128 }; - typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec); typedef void (*OPL_IRQHANDLER)(int param,int irq); typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us); @@ -53,20 +48,20 @@ typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us); typedef struct fm_opl_slot { int TL; /* total level :TL << 8 */ int TLL; /* adjusted now TL */ - uint8 KSR; /* key scale rate :(shift down bit) */ + uint8_t KSR; /* key scale rate :(shift down bit) */ int *AR; /* attack rate :&AR_TABLE[AR<<2] */ int *DR; /* decay rate :&DR_TABLE[DR<<2] */ int SL; /* sustain level :SL_TABLE[SL] */ int *RR; /* release rate :&DR_TABLE[RR<<2] */ - uint8 ksl; /* keyscale level :(shift down bits) */ - uint8 ksr; /* key scale rate :kcode>>KSR */ - uint mul; /* multiple :ML_TABLE[ML] */ - uint Cnt; /* frequency count */ - uint Incr; /* frequency step */ + uint8_t ksl; /* keyscale level :(shift down bits) */ + uint8_t ksr; /* key scale rate :kcode>>KSR */ + unsigned int mul; /* multiple :ML_TABLE[ML] */ + unsigned int Cnt; /* frequency count */ + unsigned int Incr; /* frequency step */ /* envelope generator state */ - uint8 eg_typ;/* envelope type flag */ - uint8 evm; /* envelope phase */ + uint8_t eg_typ;/* envelope type flag */ + uint8_t evm; /* envelope phase */ int evc; /* envelope counter */ int eve; /* envelope counter end point */ int evs; /* envelope counter step */ @@ -75,8 +70,8 @@ typedef struct fm_opl_slot { int evsr; /* envelope step for RR :RR[ksr] */ /* LFO */ - uint8 ams; /* ams flag */ - uint8 vib; /* vibrate flag */ + uint8_t ams; /* ams flag */ + uint8_t vib; /* vibrate flag */ /* wave selector */ int **wavetable; } OPL_SLOT; @@ -84,47 +79,47 @@ typedef struct fm_opl_slot { /* ---------- OPL one of channel ---------- */ typedef struct fm_opl_channel { OPL_SLOT SLOT[2]; - uint8 CON; /* connection type */ - uint8 FB; /* feed back :(shift down bit)*/ + uint8_t CON; /* connection type */ + uint8_t FB; /* feed back :(shift down bit)*/ int *connect1; /* slot1 output pointer */ int *connect2; /* slot2 output pointer */ int op1_out[2]; /* slot1 output for selfeedback */ /* phase generator state */ - uint block_fnum; /* block+fnum */ - uint8 kcode; /* key code : KeyScaleCode */ - uint fc; /* Freq. Increment base */ - uint ksl_base; /* KeyScaleLevel Base step */ - uint8 keyon; /* key on/off flag */ + unsigned int block_fnum; /* block+fnum */ + uint8_t kcode; /* key code : KeyScaleCode */ + unsigned int fc; /* Freq. Increment base */ + unsigned int ksl_base; /* KeyScaleLevel Base step */ + uint8_t keyon; /* key on/off flag */ } OPL_CH; /* OPL state */ typedef struct fm_opl_f { - uint8 type; /* chip type */ + uint8_t type; /* chip type */ int clock; /* master clock (Hz) */ int rate; /* sampling rate (Hz) */ double freqbase; /* frequency base */ double TimerBase; /* Timer base time (==sampling time) */ - uint8 address; /* address register */ - uint8 status; /* status flag */ - uint8 statusmask; /* status mask */ - uint mode; /* Reg.08 : CSM , notesel,etc. */ + uint8_t address; /* address register */ + uint8_t status; /* status flag */ + uint8_t statusmask; /* status mask */ + unsigned int mode; /* Reg.08 : CSM , notesel,etc. */ /* Timer */ int T[2]; /* timer counter */ - uint8 st[2]; /* timer enable */ + uint8_t st[2]; /* timer enable */ /* FM channel slots */ OPL_CH *P_CH; /* pointer of CH */ int max_ch; /* maximum channel */ /* Rythm sention */ - uint8 rythm; /* Rythm mode , key flag */ + uint8_t rythm; /* Rythm mode , key flag */ /* time tables */ int AR_TABLE[76]; /* atttack rate tables */ int DR_TABLE[76]; /* decay rate tables */ - uint FN_TABLE[1024];/* fnumber -> increment counter */ + unsigned int FN_TABLE[1024];/* fnumber -> increment counter */ /* LFO */ int *ams_table; @@ -135,7 +130,7 @@ typedef struct fm_opl_f { int vibIncr; /* wave selector enable flag */ - uint8 wavesel; + uint8_t wavesel; /* external event callback handler */ OPL_TIMERHANDLER TimerHandler; /* TIMER handler */ @@ -144,8 +139,6 @@ typedef struct fm_opl_f { int IRQParam; /* IRQ parameter */ OPL_UPDATEHANDLER UpdateHandler; /* stream update handler */ int UpdateParam; /* stream update parameter */ - - Common::RandomSource rnd; } FM_OPL; /* ---------- Generic interface section ---------- */ @@ -165,9 +158,10 @@ int OPLWrite(FM_OPL *OPL, int a, int v); unsigned char OPLRead(FM_OPL *OPL, int a); int OPLTimerOver(FM_OPL *OPL, int c); void OPLWriteReg(FM_OPL *OPL, int r, int v); -void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length, int interleave = 0); +void YM3812UpdateOne(FM_OPL *OPL, int16_t *buffer, int length, int interleave); // Factory method FM_OPL *makeAdlibOPL(int rate); #endif + -- cgit v1.2.3 From 997d2ee86ef77422ce7ddd08859bd5a4aef66145 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Wed, 4 Mar 2009 22:07:27 +0000 Subject: Add initial stub for OPL backend. Subversion-branch: /branches/opl-branch Subversion-revision: 1447 --- src/Makefile.am | 2 + src/i_oplmusic.c | 214 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/i_sdlmusic.c | 2 - src/s_sound.c | 2 + 4 files changed, 218 insertions(+), 2 deletions(-) create mode 100644 src/i_oplmusic.c diff --git a/src/Makefile.am b/src/Makefile.am index 63ddc98f..66909dd1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -155,6 +155,7 @@ FEATURE_SOUND_SOURCE_FILES = \ i_pcsound.c \ i_sdlsound.c \ i_sdlmusic.c \ +i_oplmusic.c \ mus2mid.c mus2mid.h SOURCE_FILES = $(MAIN_SOURCE_FILES) \ @@ -172,6 +173,7 @@ endif chocolate_doom_LDADD = \ ../textscreen/libtextscreen.a \ ../pcsound/libpcsound.a \ + ../opl/libopl.a \ @LDFLAGS@ \ @SDLMIXER_LIBS@ \ @SDLNET_LIBS@ diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c new file mode 100644 index 00000000..ee909193 --- /dev/null +++ b/src/i_oplmusic.c @@ -0,0 +1,214 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 1993-1996 Id Software, Inc. +// Copyright(C) 2005 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// System interface for music. +// +//----------------------------------------------------------------------------- + + +#include +#include + +#include "doomdef.h" +#include "memio.h" +#include "mus2mid.h" + +#include "deh_main.h" +#include "m_misc.h" +#include "s_sound.h" +#include "w_wad.h" +#include "z_zone.h" + +#define MAXMIDLENGTH (96 * 1024) + +static boolean music_initialised = false; + +//static boolean musicpaused = false; +static int current_music_volume; + +// Shutdown music + +static void I_OPL_ShutdownMusic(void) +{ +} + +// Initialise music subsystem + +static boolean I_OPL_InitMusic(void) +{ + music_initialised = true; + + return true; +} + +// Set music volume (0 - 127) + +static void I_OPL_SetMusicVolume(int volume) +{ + // Internal state variable. + current_music_volume = volume; +} + +// Start playing a mid + +static void I_OPL_PlaySong(void *handle, int looping) +{ + if (!music_initialised) + { + return; + } +} + +static void I_OPL_PauseSong(void) +{ + if (!music_initialised) + { + return; + } +} + +static void I_OPL_ResumeSong(void) +{ + if (!music_initialised) + { + return; + } +} + +static void I_OPL_StopSong(void) +{ + if (!music_initialised) + { + return; + } +} + +static void I_OPL_UnRegisterSong(void *handle) +{ + if (!music_initialised) + { + return; + } +} + +// Determine whether memory block is a .mid file + +static boolean IsMid(byte *mem, int len) +{ + return len > 4 && !memcmp(mem, "MThd", 4); +} + +static boolean ConvertMus(byte *musdata, int len, char *filename) +{ + MEMFILE *instream; + MEMFILE *outstream; + void *outbuf; + size_t outbuf_len; + int result; + + instream = mem_fopen_read(musdata, len); + outstream = mem_fopen_write(); + + result = mus2mid(instream, outstream); + + if (result == 0) + { + mem_get_buf(outstream, &outbuf, &outbuf_len); + + M_WriteFile(filename, outbuf, outbuf_len); + } + + mem_fclose(instream); + mem_fclose(outstream); + + return result; +} + +static void *I_OPL_RegisterSong(void *data, int len) +{ + char *filename; + + if (!music_initialised) + { + return NULL; + } + + // MUS files begin with "MUS" + // Reject anything which doesnt have this signature + + filename = M_TempFile("doom.mid"); + + if (IsMid(data, len) && len < MAXMIDLENGTH) + { + M_WriteFile(filename, data, len); + } + else + { + // Assume a MUS file and try to convert + + ConvertMus(data, len, filename); + } + + // .... + + // remove file now + + remove(filename); + + Z_Free(filename); + + return NULL; +} + +// Is the song playing? +static boolean I_OPL_MusicIsPlaying(void) +{ + if (!music_initialised) + { + return false; + } + + return false; +} + +static snddevice_t music_opl_devices[] = +{ + SNDDEVICE_ADLIB, + SNDDEVICE_SB, +}; + +music_module_t music_opl_module = +{ + music_opl_devices, + arrlen(music_opl_devices), + I_OPL_InitMusic, + I_OPL_ShutdownMusic, + I_OPL_SetMusicVolume, + I_OPL_PauseSong, + I_OPL_ResumeSong, + I_OPL_RegisterSong, + I_OPL_UnRegisterSong, + I_OPL_PlaySong, + I_OPL_StopSong, + I_OPL_MusicIsPlaying, +}; + diff --git a/src/i_sdlmusic.c b/src/i_sdlmusic.c index 313e2a58..c1ab340b 100644 --- a/src/i_sdlmusic.c +++ b/src/i_sdlmusic.c @@ -324,8 +324,6 @@ static boolean I_SDL_MusicIsPlaying(void) static snddevice_t music_sdl_devices[] = { - SNDDEVICE_ADLIB, - SNDDEVICE_SB, SNDDEVICE_PAS, SNDDEVICE_GUS, SNDDEVICE_WAVEBLASTER, diff --git a/src/s_sound.c b/src/s_sound.c index 70fa75f3..f038e9cd 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -136,6 +136,7 @@ int snd_sfxdevice = SNDDEVICE_SB; extern sound_module_t sound_sdl_module; extern sound_module_t sound_pcsound_module; extern music_module_t music_sdl_module; +extern music_module_t music_opl_module; // Compiled-in sound modules: @@ -154,6 +155,7 @@ static music_module_t *music_modules[] = { #ifdef FEATURE_SOUND &music_sdl_module, + &music_opl_module, #endif NULL, }; -- cgit v1.2.3 From 6f41d1d24f4fccba4aa77be15c60a0b173b2dfe6 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Mon, 9 Mar 2009 23:59:43 +0000 Subject: Make global variables static. Replace uint with uint32_t. Subversion-branch: /branches/opl-branch Subversion-revision: 1454 --- opl/fmopl.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/opl/fmopl.c b/opl/fmopl.c index 6713ecc1..1671244e 100644 --- a/opl/fmopl.c +++ b/opl/fmopl.c @@ -61,16 +61,16 @@ /* output level entries (envelope,sinwave) */ /* envelope counter lower bits */ -int ENV_BITS; +static int ENV_BITS; /* envelope output entries */ -int EG_ENT; +static int EG_ENT; /* used dynamic memory = EG_ENT*4*4(byte)or EG_ENT*6*4(byte) */ /* used static memory = EG_ENT*4 (byte) */ -int EG_OFF; /* OFF */ -int EG_DED; -int EG_DST; /* DECAY START */ -int EG_AED; +static int EG_OFF; /* OFF */ +static int EG_DED; +static int EG_DST; /* DECAY START */ +static int EG_AED; #define EG_AST 0 /* ATTACK START */ #define EG_STEP (96.0/EG_ENT) /* OPL is 0.1875 dB step */ @@ -103,7 +103,7 @@ static const int slot_array[32] = { -1,-1,-1,-1,-1,-1,-1,-1 }; -static uint KSL_TABLE[8 * 16]; +static uint32_t KSL_TABLE[8 * 16]; static const double KSL_TABLE_SEED[8 * 16] = { /* OCT 0 */ @@ -153,7 +153,7 @@ static const double KSL_TABLE_SEED[8 * 16] = { static int SL_TABLE[16]; -static const uint SL_TABLE_SEED[16] = { +static const uint32_t SL_TABLE_SEED[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 31 }; @@ -179,7 +179,7 @@ static int *ENV_CURVE; /* multiple table */ #define ML(a) (int)(a * 2) -static const uint MUL_TABLE[16]= { +static const uint32_t MUL_TABLE[16]= { /* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 */ ML(0.50), ML(1.00), ML(2.00), ML(3.00), ML(4.00), ML(5.00), ML(6.00), ML(7.00), ML(8.00), ML(9.00), ML(10.00), ML(10.00),ML(12.00),ML(12.00),ML(15.00),ML(15.00) @@ -215,7 +215,7 @@ static int feedback2; /* connect for SLOT 2 */ /* --------------------- rebuild tables ------------------- */ #define ARRAYSIZE(x) (sizeof(x) / sizeof(*x)) -#define SC_KSL(mydb) ((uint) (mydb / (EG_STEP / 2))) +#define SC_KSL(mydb) ((uint32_t) (mydb / (EG_STEP / 2))) #define SC_SL(db) (int)(db * ((3 / EG_STEP) * (1 << ENV_BITS))) + EG_DST void OPLBuildTables(int ENV_BITS_PARAM, int EG_ENT_PARAM) { @@ -332,7 +332,7 @@ static inline void OPL_KEYOFF(OPL_SLOT *SLOT) { /* ---------- calcrate Envelope Generator & Phase Generator ---------- */ /* return : envelope output */ -static inline uint OPL_CALC_SLOT(OPL_SLOT *SLOT) { +static inline uint32_t OPL_CALC_SLOT(OPL_SLOT *SLOT) { /* calcrate envelope generator */ if((SLOT->evc += SLOT->evs) >= SLOT->eve) { switch( SLOT->evm ) { @@ -455,14 +455,14 @@ static inline void set_sl_rr(FM_OPL *OPL, int slot, int v) { #define OP_OUT(slot,env,con) slot->wavetable[((slot->Cnt + con)>>(24-SIN_ENT_SHIFT)) & (SIN_ENT-1)][env] /* ---------- calcrate one of channel ---------- */ static inline void OPL_CALC_CH(OPL_CH *CH) { - uint env_out; + uint32_t env_out; OPL_SLOT *SLOT; feedback2 = 0; /* SLOT 1 */ SLOT = &CH->SLOT[SLOT1]; env_out=OPL_CALC_SLOT(SLOT); - if(env_out < (uint)(EG_ENT - 1)) { + if(env_out < (uint32_t)(EG_ENT - 1)) { /* PG */ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr * vib) >> VIB_RATE_SHIFT; @@ -483,7 +483,7 @@ static inline void OPL_CALC_CH(OPL_CH *CH) { /* SLOT 2 */ SLOT = &CH->SLOT[SLOT2]; env_out=OPL_CALC_SLOT(SLOT); - if(env_out < (uint)(EG_ENT - 1)) { + if(env_out < (uint32_t)(EG_ENT - 1)) { /* PG */ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr * vib) >> VIB_RATE_SHIFT; @@ -497,7 +497,7 @@ static inline void OPL_CALC_CH(OPL_CH *CH) { /* ---------- calcrate rythm block ---------- */ #define WHITE_NOISE_db 6.0 static inline void OPL_CALC_RH(FM_OPL *OPL, OPL_CH *CH) { - uint env_tam, env_sd, env_top, env_hh; + uint32_t env_tam, env_sd, env_top, env_hh; // This code used to do int(OPL->rnd.getRandomBit() * (WHITE_NOISE_db / EG_STEP)), // but EG_STEP = 96.0/EG_ENT, and WHITE_NOISE_db=6.0. So, that's equivalent to // int(OPL->rnd.getRandomBit() * EG_ENT/16). We know that EG_ENT is 4096, or 1024, @@ -577,16 +577,16 @@ static inline void OPL_CALC_RH(FM_OPL *OPL, OPL_CH *CH) { tone8 = OP_OUT(SLOT8_2,whitenoise,0 ); /* SD */ - if(env_sd < (uint)(EG_ENT - 1)) + if(env_sd < (uint32_t)(EG_ENT - 1)) outd[0] += OP_OUT(SLOT7_1, env_sd, 0) * 8; /* TAM */ - if(env_tam < (uint)(EG_ENT - 1)) + if(env_tam < (uint32_t)(EG_ENT - 1)) outd[0] += OP_OUT(SLOT8_1, env_tam, 0) * 2; /* TOP-CY */ - if(env_top < (uint)(EG_ENT - 1)) + if(env_top < (uint32_t)(EG_ENT - 1)) outd[0] += OP_OUT(SLOT7_2, env_top, tone8) * 2; /* HH */ - if(env_hh < (uint)(EG_ENT-1)) + if(env_hh < (uint32_t)(EG_ENT-1)) outd[0] += OP_OUT(SLOT7_2, env_hh, tone8) * 2; } @@ -737,7 +737,7 @@ static void OPL_initalize(FM_OPL *OPL) { init_timetables(OPL, OPL_ARRATE, OPL_DRRATE); /* make fnumber -> increment counter table */ for( fn=0; fn < 1024; fn++) { - OPL->FN_TABLE[fn] = (uint)(OPL->freqbase * fn * FREQ_RATE * (1<<7) / 2); + OPL->FN_TABLE[fn] = (uint32_t)(OPL->freqbase * fn * FREQ_RATE * (1<<7) / 2); } /* LFO freq.table */ OPL->amsIncr = (int)(OPL->rate ? (double)AMS_ENT * (1 << AMS_SHIFT) / OPL->rate * 3.7 * ((double)OPL->clock/3600000) : 0); @@ -748,7 +748,7 @@ static void OPL_initalize(FM_OPL *OPL) { void OPLWriteReg(FM_OPL *OPL, int r, int v) { OPL_CH *CH; int slot; - uint block_fnum; + uint32_t block_fnum; switch(r & 0xe0) { case 0x00: /* 00-1f:controll */ @@ -969,8 +969,8 @@ void YM3812UpdateOne(FM_OPL *OPL, int16_t *buffer, int length, int interleave) { int i; int data; int16_t *buf = buffer; - uint amsCnt = OPL->amsCnt; - uint vibCnt = OPL->vibCnt; + uint32_t amsCnt = OPL->amsCnt; + uint32_t vibCnt = OPL->vibCnt; uint8_t rythm = OPL->rythm & 0x20; OPL_CH *CH, *R_CH; -- cgit v1.2.3 From 9f8ab35852dfd68eb6cc92ab84b79a90995dc027 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Tue, 10 Mar 2009 00:02:41 +0000 Subject: Add opl library main header and stub functions. Subversion-branch: /branches/opl-branch Subversion-revision: 1455 --- opl/Makefile.am | 1 + opl/opl.c | 49 ++++++++++++++++++++++++++++++++++++++ opl/opl.h | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 opl/opl.c create mode 100644 opl/opl.h diff --git a/opl/Makefile.am b/opl/Makefile.am index 8fd2303e..65f8b2de 100644 --- a/opl/Makefile.am +++ b/opl/Makefile.am @@ -4,5 +4,6 @@ AM_CFLAGS=@SDLMIXER_CFLAGS@ noinst_LIBRARIES=libopl.a libopl_a_SOURCES = \ + opl.c opl.h \ fmopl.c diff --git a/opl/opl.c b/opl/opl.c new file mode 100644 index 00000000..d3dfc23b --- /dev/null +++ b/opl/opl.c @@ -0,0 +1,49 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// OPL interface. +// +//----------------------------------------------------------------------------- + +#include "opl.h" + +int OPL_Init(unsigned int port_base) +{ + // TODO + return 1; +} + +void OPL_Shutdown(void) +{ + // TODO +} + +void OPL_WritePort(opl_port_t port, unsigned int value) +{ + // TODO +} + +unsigned int OPL_ReadPort(opl_port_t port) +{ + // TODO + return 0; +} + diff --git a/opl/opl.h b/opl/opl.h new file mode 100644 index 00000000..62b507f7 --- /dev/null +++ b/opl/opl.h @@ -0,0 +1,73 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// OPL interface. +// +//----------------------------------------------------------------------------- + + +#ifndef OPL_OPL_H +#define OPL_OPL_H + +typedef enum +{ + OPL_REGISTER_PORT = 0, + OPL_DATA_PORT = 1 +} opl_port_t; + +#define OPL_NUM_OPERATORS 21 +#define OPL_NUM_VOICES 9 + +#define OPL_REG_TIMER1 0x02 +#define OPL_REG_TIMER2 0x03 +#define OPL_REG_TIMER_CTRL 0x04 +#define OPL_REG_FM_MODE 0x08 + +// Operator registers (21 of each): + +#define OPL_REGS_TREMOLO 0x20 +#define OPL_REGS_LEVEL 0x40 +#define OPL_REGS_ATTACK 0x60 +#define OPL_REGS_SUSTAIN 0x80 + +// Voice registers (9 of each): + +#define OPL_REGS_FREQ_1 0xA0 +#define OPL_REGS_FREQ_2 0xB0 + +// Initialise the OPL subsystem. + +int OPL_Init(unsigned int port_base); + +// Shut down the OPL subsystem. + +void OPL_Shutdown(void); + +// Write to one of the OPL I/O ports: + +void OPL_WritePort(opl_port_t port, unsigned int value); + +// Read from one of the OPL I/O ports: + +unsigned int OPL_ReadPort(opl_port_t port); + +#endif + -- cgit v1.2.3 From 3efee3d5e2b662df97aabc4c8fd275b60b6f08f4 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Tue, 10 Mar 2009 00:03:54 +0000 Subject: Add initial GENMIDI lump loading, OPL detection. Subversion-branch: /branches/opl-branch Subversion-revision: 1456 --- src/Makefile.am | 2 +- src/i_oplmusic.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/m_config.c | 2 +- 3 files changed, 172 insertions(+), 3 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 66909dd1..406d0af8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,7 +3,7 @@ gamesdir = $(prefix)/games games_PROGRAMS = chocolate-doom chocolate-server -AM_CFLAGS = -I../textscreen -I../pcsound @SDLMIXER_CFLAGS@ @SDLNET_CFLAGS@ +AM_CFLAGS = -I../opl -I../textscreen -I../pcsound @SDLMIXER_CFLAGS@ @SDLNET_CFLAGS@ DEDSERV_FILES=\ d_dedicated.c \ diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index ee909193..98170284 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -27,6 +27,7 @@ #include #include +#include #include "doomdef.h" #include "memio.h" @@ -38,23 +39,191 @@ #include "w_wad.h" #include "z_zone.h" +#include "opl.h" + #define MAXMIDLENGTH (96 * 1024) +#define GENMIDI_NUM_INSTRS 128 + +#define GENMIDI_HEADER "#OPL_II#" +#define GENMIDI_FLAG_FIXED 0x0000 /* fixed pitch */ +#define GENMIDI_FLAG_2VOICE 0x0002 /* double voice (OPL3) */ + +typedef struct +{ + byte tremolo; + byte attack; + byte sustain; + byte waveform; + byte scale; + byte level; +} PACKEDATTR genmidi_op_t; + +typedef struct +{ + genmidi_op_t modulator; + byte feedback; + genmidi_op_t carrier; + byte unused; + byte base_note_offset; +} PACKEDATTR genmidi_voice_t; + +typedef struct +{ + unsigned short flags; + byte fine_tuning; + byte fixed_note; + + genmidi_voice_t opl2_voice; + genmidi_voice_t opl3_voice; +} PACKEDATTR genmidi_instr_t; static boolean music_initialised = false; //static boolean musicpaused = false; static int current_music_volume; +static genmidi_instr_t *main_instrs; +static genmidi_instr_t *percussion_instrs; + +// Configuration file variable, containing the port number for the +// adlib chip. + +int snd_mport = 0x388; + +static unsigned int GetStatus(void) +{ + return OPL_ReadPort(OPL_REGISTER_PORT); +} + +// Write an OPL register value + +static void WriteRegister(int reg, int value) +{ + int i; + + OPL_WritePort(OPL_REGISTER_PORT, reg); + + // For timing, read the register port six times after writing the + // register number to cause the appropriate delay + + for (i=0; i<6; ++i) + { + GetStatus(); + } + + OPL_WritePort(OPL_DATA_PORT, value); + + // Read the register port 25 times after writing the value to + // cause the appropriate delay + + for (i=0; i<25; ++i) + { + GetStatus(); + } +} + +// Detect the presence of an OPL chip + +static boolean DetectOPL(void) +{ + int result1, result2; + + // Reset both timers: + WriteRegister(OPL_REG_TIMER_CTRL, 0x60); + + // Enable interrupts: + WriteRegister(OPL_REG_TIMER_CTRL, 0x80); + + // Read status + result1 = GetStatus(); + + // Set timer: + WriteRegister(OPL_REG_TIMER1, 0xff); + + // Start timer 1: + WriteRegister(OPL_REG_TIMER_CTRL, 0x21); + + // Wait for 80 microseconds + + // Read status + result2 = GetStatus(); + + // Reset both timers: + WriteRegister(OPL_REG_TIMER_CTRL, 0x60); + + // Enable interrupts: + WriteRegister(OPL_REG_TIMER_CTRL, 0x80); + + return (result1 & 0xe0) == 0x00 + && (result2 & 0xe0) == 0xc0; +} + + +// Load instrument table from GENMIDI lump: + +static boolean LoadInstrumentTable(void) +{ + byte *lump; + + lump = W_CacheLumpName("GENMIDI", PU_STATIC); + + // Check header + + if (strncmp((char *) lump, GENMIDI_HEADER, strlen(GENMIDI_HEADER)) != 0) + { + W_ReleaseLumpName("GENMIDI"); + + return false; + } + + main_instrs = (genmidi_instr_t *) (lump + strlen(GENMIDI_HEADER)); + percussion_instrs = main_instrs + GENMIDI_NUM_INSTRS; + + return true; +} + // Shutdown music static void I_OPL_ShutdownMusic(void) { + if (music_initialised) + { + OPL_Shutdown(); + + // Release GENMIDI lump + + W_ReleaseLumpName("GENMIDI"); + + music_initialised = false; + } } // Initialise music subsystem static boolean I_OPL_InitMusic(void) -{ +{ + if (!OPL_Init(snd_mport)) + { + return false; + } + + // Doom does the detection sequence twice, for some reason: + + if (!DetectOPL() || !DetectOPL()) + { + printf("Dude. The Adlib isn't responding.\n"); + OPL_Shutdown(); + return false; + } + + // Load instruments from GENMIDI lump: + + if (!LoadInstrumentTable()) + { + OPL_Shutdown(); + return false; + } + music_initialised = true; return true; diff --git a/src/m_config.c b/src/m_config.c index 4066cb3c..c54b3774 100644 --- a/src/m_config.c +++ b/src/m_config.c @@ -124,6 +124,7 @@ extern int vanilla_demo_limit; extern int snd_musicdevice; extern int snd_sfxdevice; extern int snd_samplerate; +extern int snd_mport; // controls whether to use libsamplerate for sample rate conversions @@ -136,7 +137,6 @@ extern int use_libsamplerate; static int snd_sbport = 0; static int snd_sbirq = 0; static int snd_sbdma = 0; -static int snd_mport = 0; typedef enum { -- cgit v1.2.3 From ca7f823d48bb78eda9048750909cdb315836125c Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Tue, 10 Mar 2009 21:21:16 +0000 Subject: Initialise OPL registers on startup, initialise voices. Subversion-branch: /branches/opl-branch Subversion-revision: 1457 --- opl/opl.h | 3 + src/i_oplmusic.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) diff --git a/opl/opl.h b/opl/opl.h index 62b507f7..352e6696 100644 --- a/opl/opl.h +++ b/opl/opl.h @@ -36,6 +36,7 @@ typedef enum #define OPL_NUM_OPERATORS 21 #define OPL_NUM_VOICES 9 +#define OPL_REG_WAVEFORM_ENABLE 0x01 #define OPL_REG_TIMER1 0x02 #define OPL_REG_TIMER2 0x03 #define OPL_REG_TIMER_CTRL 0x04 @@ -47,11 +48,13 @@ typedef enum #define OPL_REGS_LEVEL 0x40 #define OPL_REGS_ATTACK 0x60 #define OPL_REGS_SUSTAIN 0x80 +#define OPL_REGS_WAVEFORM 0xE0 // Voice registers (9 of each): #define OPL_REGS_FREQ_1 0xA0 #define OPL_REGS_FREQ_2 0xB0 +#define OPL_REGS_FEEDBACK 0xC0 // Initialise the OPL subsystem. diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 98170284..5b3ae041 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -77,14 +77,45 @@ typedef struct genmidi_voice_t opl3_voice; } PACKEDATTR genmidi_instr_t; +typedef struct opl_voice_s opl_voice_t; + +struct opl_voice_s +{ + // Index of this voice: + int index; + + // The operators used by this voice: + int op1, op2; + + // Currently-loaded instrument data + genmidi_instr_t *current_instr; + + // Next in freelist + opl_voice_t *next; +}; + +// Operators used by the different voices. + +static const int voice_operators[2][OPL_NUM_VOICES] = { + { 0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12 }, + { 0x03, 0x04, 0x05, 0x0b, 0x0c, 0x0d, 0x13, 0x14, 0x15 } +}; + static boolean music_initialised = false; //static boolean musicpaused = false; static int current_music_volume; +// GENMIDI lump instrument data: + static genmidi_instr_t *main_instrs; static genmidi_instr_t *percussion_instrs; +// Voices: + +static opl_voice_t voices[OPL_NUM_VOICES]; +static opl_voice_t *voice_free_list; + // Configuration file variable, containing the port number for the // adlib chip. @@ -158,6 +189,47 @@ static boolean DetectOPL(void) && (result2 & 0xe0) == 0xc0; } +// Initialise registers on startup + +static void InitRegisters(void) +{ + int r; + + // Initialise level registers + + for (r=OPL_REGS_LEVEL; r < OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r) + { + WriteRegister(r, 0x3f); + } + + // Initialise other registers + // These two loops write to registers that actually don't exist, + // but this is what Doom does ... + + for (r=OPL_REGS_ATTACK; r < OPL_REGS_WAVEFORM + OPL_NUM_OPERATORS; ++r) + { + WriteRegister(r, 0x00); + } + + // More registers ... + + for (r=0; r < OPL_REGS_LEVEL; ++r) + { + WriteRegister(r, 0x00); + } + + // Re-initialise the low registers: + + // Reset both timers and enable interrupts: + WriteRegister(OPL_REG_TIMER_CTRL, 0x60); + WriteRegister(OPL_REG_TIMER_CTRL, 0x80); + + // "Allow FM chips to control the waveform of each operator": + WriteRegister(OPL_REG_WAVEFORM_ENABLE, 0x20); + + // Keyboard split point on (?) + WriteRegister(OPL_REG_FM_MODE, 0x40); +} // Load instrument table from GENMIDI lump: @@ -182,6 +254,109 @@ static boolean LoadInstrumentTable(void) return true; } +// Get the next available voice from the freelist. + +static opl_voice_t *GetFreeVoice(void) +{ + opl_voice_t *result; + + // None available? + + if (voice_free_list == NULL) + { + return NULL; + } + + result = voice_free_list; + voice_free_list = voice_free_list->next; + + return result; +} + +// Release a voice back to the freelist. + +static void ReleaseVoice(opl_voice_t *voice) +{ + opl_voice_t **rover; + + // Search to the end of the freelist (This is how Doom behaves!) + + rover = &voice_free_list; + + while (*rover != NULL) + { + rover = &(*rover)->next; + } + + *rover = voice; +} + +// Load data to the specified operator + +static void LoadOperatorData(int operator, genmidi_op_t *data, + boolean max_level) +{ + int level; + + // The scale and level fields must be combined for the level register. + // For the carrier wave we always set the maximum level. + + level = (data->scale & 0xc0) | (data->level & 0x3f); + + if (max_level) + { + level |= 0x3f; + } + + WriteRegister(OPL_REGS_LEVEL + operator, level); + WriteRegister(OPL_REGS_TREMOLO + operator, data->tremolo); + WriteRegister(OPL_REGS_ATTACK + operator, data->attack); + WriteRegister(OPL_REGS_SUSTAIN + operator, data->sustain); + WriteRegister(OPL_REGS_WAVEFORM + operator, data->waveform); +} + +// Set the instrument for a particular voice. + +static void SetVoiceInstrument(opl_voice_t *voice, genmidi_voice_t *data) +{ + // Doom loads the second operator first, then the first. + + LoadOperatorData(voice->op2, &data->carrier, true); + LoadOperatorData(voice->op1, &data->modulator, false); + + // Set feedback register that control the connection between the + // two operators. Turn on bits in the upper nybble; I think this + // is for OPL3, where it turns on channel A/B. + + WriteRegister(OPL_REGS_FEEDBACK + voice->index, + data->feedback | 0x30); +} + +// Initialise the voice table and freelist + +static void InitVoices(void) +{ + int i; + + // Start with an empty free list. + + voice_free_list = NULL; + + // Initialise each voice. + + for (i=0; i + #include "opl.h" +#include "opl_internal.h" + +#ifdef HAVE_IOPERM +extern opl_driver_t opl_linux_driver; +#endif + +static opl_driver_t *drivers[] = +{ +#ifdef HAVE_IOPERM + &opl_linux_driver, +#endif + NULL +}; + +static opl_driver_t *driver = NULL; int OPL_Init(unsigned int port_base) { - // TODO - return 1; + int i; + + // Try drivers until we find a working one: + + for (i=0; drivers[i] != NULL; ++i) + { + if (drivers[i]->init_func(port_base)) + { + driver = drivers[i]; + return 1; + } + } + + return 0; } void OPL_Shutdown(void) { - // TODO + if (driver != NULL) + { + driver->shutdown_func(); + driver = NULL; + } } void OPL_WritePort(opl_port_t port, unsigned int value) { - // TODO + if (driver != NULL) + { + driver->write_port_func(port, value); + } } unsigned int OPL_ReadPort(opl_port_t port) { - // TODO - return 0; + if (driver != NULL) + { + return driver->read_port_func(port); + } + else + { + return 0; + } } diff --git a/opl/opl_internal.h b/opl/opl_internal.h new file mode 100644 index 00000000..964f55cc --- /dev/null +++ b/opl/opl_internal.h @@ -0,0 +1,96 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// OPL internal interface. +// +//----------------------------------------------------------------------------- + + +#ifndef OPL_INTERNAL_H +#define OPL_INTERNAL_H + +#include "opl.h" + +typedef int (*opl_init_func)(unsigned int port_base); +typedef void (*opl_shutdown_func)(void); +typedef unsigned int (*opl_read_port_func)(opl_port_t port); +typedef void (*opl_write_port_func)(opl_port_t port, unsigned int value); + +typedef struct +{ + char *name; + + opl_init_func init_func; + opl_shutdown_func shutdown_func; + opl_read_port_func read_port_func; + opl_write_port_func write_port_func; +} opl_driver_t; + +#endif /* #ifndef OPL_INTERNAL_H */ + +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// OPL internal interface. +// +//----------------------------------------------------------------------------- + + +#ifndef OPL_INTERNAL_H +#define OPL_INTERNAL_H + +#include "opl.h" + +typedef int (*opl_init_func)(unsigned int port_base); +typedef void (*opl_shutdown_func)(void); +typedef unsigned int (*opl_read_port_func)(opl_port_t port); +typedef void (*opl_write_port_func)(opl_port_t port, unsigned int value); + +typedef struct +{ + char *name; + + opl_init_func init_func; + opl_shutdown_func shutdown_func; + opl_read_port_func read_port_func; + opl_write_port_func write_port_func; +} opl_driver_t; + +#endif /* #ifndef OPL_INTERNAL_H */ + diff --git a/opl/opl_linux.c b/opl/opl_linux.c new file mode 100644 index 00000000..438b091a --- /dev/null +++ b/opl/opl_linux.c @@ -0,0 +1,79 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// OPL Linux interface. +// +//----------------------------------------------------------------------------- + +#include "config.h" + +#ifdef HAVE_IOPERM + +#include +#include + +#include "opl.h" +#include "opl_internal.h" + +static unsigned int opl_port_base; + +static void OPL_Linux_Init(unsigned int port_base) +{ + // Try to get permissions: + + if (ioperm(port_base, 2, 1) < 0) + { + return 0; + } + + opl_port_base = port_base; + + return 1; +} + +static void OPL_Linux_Shutdown(void) +{ + // Release permissions + + ioperm(opl_port_base, 2, 0); +} + +static unsigned int OPL_Linux_PortRead(opl_port_t port) +{ + return inb(opl_port_base + port); +} + +static void OPL_Linux_PortWrite(opl_port_t port, unsigned int value) +{ + outb(opl_port_base + port, value); +} + +opl_driver_t opl_linux_driver = +{ + "Linux", + OPL_Linux_Init, + OPL_Linux_Shutdown, + OPL_Linux_PortRead, + OPL_Linux_PortWrite +}; + +#endif /* #ifdef HAVE_IOPERM */ + -- cgit v1.2.3 From 02e3e618472e4afa9a58950a9853aa0356b59504 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Tue, 10 Mar 2009 21:57:06 +0000 Subject: Set svn:ignore property. Subversion-branch: /branches/opl-branch Subversion-revision: 1459 --- opl/.gitignore | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 opl/.gitignore diff --git a/opl/.gitignore b/opl/.gitignore new file mode 100644 index 00000000..e64379dc --- /dev/null +++ b/opl/.gitignore @@ -0,0 +1,5 @@ +Makefile.in +Makefile +.deps +libopl.a +*.rc -- cgit v1.2.3 From c423d770e9a05e762a2382e342ddd0bb7e6f3996 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Tue, 10 Mar 2009 22:19:29 +0000 Subject: Fix outb() call, display error message if unable to gain port permissions. Subversion-branch: /branches/opl-branch Subversion-revision: 1460 --- opl/opl_linux.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/opl/opl_linux.c b/opl/opl_linux.c index 438b091a..d1d1e466 100644 --- a/opl/opl_linux.c +++ b/opl/opl_linux.c @@ -27,6 +27,9 @@ #ifdef HAVE_IOPERM +#include +#include +#include #include #include @@ -35,12 +38,14 @@ static unsigned int opl_port_base; -static void OPL_Linux_Init(unsigned int port_base) +static int OPL_Linux_Init(unsigned int port_base) { // Try to get permissions: if (ioperm(port_base, 2, 1) < 0) { + fprintf(stderr, "Failed to get I/O port permissions for 0x%x: %s\n", + port_base, strerror(errno)); return 0; } @@ -63,7 +68,7 @@ static unsigned int OPL_Linux_PortRead(opl_port_t port) static void OPL_Linux_PortWrite(opl_port_t port, unsigned int value) { - outb(opl_port_base + port, value); + outb(value, opl_port_base + port); } opl_driver_t opl_linux_driver = -- cgit v1.2.3 From b796c64d34847434d053aeacca5b6ec10ace2450 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Tue, 10 Mar 2009 22:20:32 +0000 Subject: Add delay to allow OPL detection to work. Subversion-branch: /branches/opl-branch Subversion-revision: 1461 --- src/i_oplmusic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 5b3ae041..7ead7f02 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -175,6 +175,7 @@ static boolean DetectOPL(void) WriteRegister(OPL_REG_TIMER_CTRL, 0x21); // Wait for 80 microseconds + SDL_Delay(1); // Read status result2 = GetStatus(); -- cgit v1.2.3 From 962d6dcf12e740c26c3be035884b310e74947d97 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Wed, 11 Mar 2009 20:20:05 +0000 Subject: Debug trace code for register writes. Subversion-branch: /branches/opl-branch Subversion-revision: 1462 --- opl/opl.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/opl/opl.c b/opl/opl.c index 28609223..9afdd980 100644 --- a/opl/opl.c +++ b/opl/opl.c @@ -30,6 +30,8 @@ #include "opl.h" #include "opl_internal.h" +#define OPL_DEBUG_TRACE + #ifdef HAVE_IOPERM extern opl_driver_t opl_linux_driver; #endif @@ -75,6 +77,9 @@ void OPL_WritePort(opl_port_t port, unsigned int value) { if (driver != NULL) { +#ifdef OPL_DEBUG_TRACE + printf("OPL_write: %i, %x\n", port, value); +#endif driver->write_port_func(port, value); } } @@ -83,7 +88,15 @@ unsigned int OPL_ReadPort(opl_port_t port) { if (driver != NULL) { - return driver->read_port_func(port); + unsigned int result; + + result = driver->read_port_func(port); + +#ifdef OPL_DEBUG_TRACE + printf("OPL_read: %i -> %x\n", port, result); +#endif + + return result; } else { -- cgit v1.2.3 From 99420c03a1cb10ea69207f2ed89f5fd1ec892bee Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Wed, 11 Mar 2009 21:28:28 +0000 Subject: More accurate initialisation; set registers to match what Doom does. Subversion-branch: /branches/opl-branch Subversion-revision: 1463 --- src/i_oplmusic.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 7ead7f02..baf17bd6 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -147,7 +147,7 @@ static void WriteRegister(int reg, int value) // Read the register port 25 times after writing the value to // cause the appropriate delay - for (i=0; i<25; ++i) + for (i=0; i<24; ++i) { GetStatus(); } @@ -158,6 +158,7 @@ static void WriteRegister(int reg, int value) static boolean DetectOPL(void) { int result1, result2; + int i; // Reset both timers: WriteRegister(OPL_REG_TIMER_CTRL, 0x60); @@ -175,7 +176,12 @@ static boolean DetectOPL(void) WriteRegister(OPL_REG_TIMER_CTRL, 0x21); // Wait for 80 microseconds - SDL_Delay(1); + // This is how Doom does it: + + for (i=0; i<200; ++i) + { + GetStatus(); + } // Read status result2 = GetStatus(); @@ -198,7 +204,7 @@ static void InitRegisters(void) // Initialise level registers - for (r=OPL_REGS_LEVEL; r < OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r) + for (r=OPL_REGS_LEVEL; r <= OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r) { WriteRegister(r, 0x3f); } @@ -206,15 +212,16 @@ static void InitRegisters(void) // Initialise other registers // These two loops write to registers that actually don't exist, // but this is what Doom does ... + // Similarly, the <= is also intenational. - for (r=OPL_REGS_ATTACK; r < OPL_REGS_WAVEFORM + OPL_NUM_OPERATORS; ++r) + for (r=OPL_REGS_ATTACK; r <= OPL_REGS_WAVEFORM + OPL_NUM_OPERATORS; ++r) { WriteRegister(r, 0x00); } // More registers ... - for (r=0; r < OPL_REGS_LEVEL; ++r) + for (r=1; r < OPL_REGS_LEVEL; ++r) { WriteRegister(r, 0x00); } @@ -364,6 +371,9 @@ static void I_OPL_ShutdownMusic(void) { if (music_initialised) { +#ifdef TEST + InitRegisters(); +#endif OPL_Shutdown(); // Release GENMIDI lump @@ -403,6 +413,22 @@ static boolean I_OPL_InitMusic(void) InitRegisters(); InitVoices(); +#ifdef TEST + { + opl_voice_t *voice; + + voice = GetFreeVoice(); + SetVoiceInstrument(voice, &main_instrs[34].opl2_voice); + + // Set level: + WriteRegister(OPL_REGS_LEVEL + voice->op2, 0x94); + + // Note on: + WriteRegister(OPL_REGS_FREQ_1 + voice->index, 0x65); + WriteRegister(OPL_REGS_FREQ_2 + voice->index, 0x2b); + } +#endif + music_initialised = true; return true; -- cgit v1.2.3 From 90fd85b32dd85884fb9a9cf0a7063c4509147d40 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Thu, 12 Mar 2009 18:50:15 +0000 Subject: Read from register port when doing register writes during startup phase; afterwards, read from the data port. Subversion-branch: /branches/opl-branch Subversion-revision: 1464 --- src/i_oplmusic.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index baf17bd6..0543be2e 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -116,6 +116,13 @@ static genmidi_instr_t *percussion_instrs; static opl_voice_t voices[OPL_NUM_VOICES]; static opl_voice_t *voice_free_list; +// In the initialisation stage, register writes are spaced by reading +// from the register port (0). After initialisation, spacing is +// peformed by reading from the data port instead. I have no idea +// why. + +static boolean init_stage_reg_writes = false; + // Configuration file variable, containing the port number for the // adlib chip. @@ -139,7 +146,18 @@ static void WriteRegister(int reg, int value) for (i=0; i<6; ++i) { - GetStatus(); + // An oddity of the Doom OPL code: at startup initialisation, + // the spacing here is performed by reading from the register + // port; after initialisation, the data port is read, instead. + + if (init_stage_reg_writes) + { + OPL_ReadPort(OPL_REGISTER_PORT); + } + else + { + OPL_ReadPort(OPL_DATA_PORT); + } } OPL_WritePort(OPL_DATA_PORT, value); @@ -393,6 +411,8 @@ static boolean I_OPL_InitMusic(void) return false; } + init_stage_reg_writes = true; + // Doom does the detection sequence twice, for some reason: if (!DetectOPL() || !DetectOPL()) @@ -413,6 +433,11 @@ static boolean I_OPL_InitMusic(void) InitRegisters(); InitVoices(); + // Now that initialisation has finished, switch the + // register writing mode: + + init_stage_reg_writes = false; + #ifdef TEST { opl_voice_t *voice; -- cgit v1.2.3 From 543b9bdc85dfeb84ed855084595407d6bf49d8f6 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Thu, 12 Mar 2009 18:52:12 +0000 Subject: Make base_note_offset a 16-bit integer rather than an 8-bit integer. Subversion-branch: /branches/opl-branch Subversion-revision: 1465 --- src/i_oplmusic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 0543be2e..f50c3322 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -64,7 +64,7 @@ typedef struct byte feedback; genmidi_op_t carrier; byte unused; - byte base_note_offset; + short base_note_offset; } PACKEDATTR genmidi_voice_t; typedef struct -- cgit v1.2.3 From b96cdbe82fa6cb51ac91072ad86e084d739fd9be Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 28 Mar 2009 00:24:50 +0000 Subject: Initial MIDI file parsing code. Subversion-branch: /branches/opl-branch Subversion-revision: 1489 --- src/Makefile.am | 1 + src/midifile.c | 287 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/midifile.h | 135 ++++++++++++++++++++++++++ 3 files changed, 423 insertions(+) create mode 100644 src/midifile.c create mode 100644 src/midifile.h diff --git a/src/Makefile.am b/src/Makefile.am index 406d0af8..92095b5c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -156,6 +156,7 @@ i_pcsound.c \ i_sdlsound.c \ i_sdlmusic.c \ i_oplmusic.c \ +midifile.c midifile.h \ mus2mid.c mus2mid.h SOURCE_FILES = $(MAIN_SOURCE_FILES) \ diff --git a/src/midifile.c b/src/midifile.c new file mode 100644 index 00000000..e74cf17f --- /dev/null +++ b/src/midifile.c @@ -0,0 +1,287 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// Reading of MIDI files. +// +//----------------------------------------------------------------------------- + +#include +#include +#include + +#include "doomtype.h" +#include "i_swap.h" +#include "midifile.h" + +#define HEADER_CHUNK_ID "MThd" +#define TRACK_CHUNK_ID "MTrk" + +typedef struct +{ + byte chunk_id[4]; + unsigned int chunk_size; +} chunk_header_t; + +typedef struct +{ + chunk_header_t chunk_header; + unsigned short format_type; + unsigned short num_tracks; + unsigned int time_division; +} midi_header_t; + +struct midi_file_s +{ + FILE *stream; + midi_header_t header; + unsigned int data_len; +}; + +static boolean CheckChunkHeader(chunk_header_t *chunk, + char *expected_id) +{ + boolean result; + + result = (strcmp((char *) chunk->chunk_id, expected_id) == 0); + + if (!result) + { + fprintf(stderr, "CheckChunkHeader: Expected '%s' chunk header!\n", + expected_id); + } + + return result; +} + +// Read and check the header chunk. + +static boolean ReadHeaderChunk(midi_file_t *file) +{ + size_t bytes_read; + + bytes_read = fread(&file->header, sizeof(midi_header_t), 1, file->stream); + + if (bytes_read < sizeof(midi_header_t)) + { + return false; + } + + if (!CheckChunkHeader(&file->header.chunk_header, HEADER_CHUNK_ID) + || LONG(file->header.chunk_header.chunk_size) != 6) + { + fprintf(stderr, "ReadHeaderChunk: Invalid MIDI chunk header!\n"); + return false; + } + + if (SHORT(file->header.format_type) != 0 + || SHORT(file->header.num_tracks) != 1) + { + fprintf(stderr, "ReadHeaderChunk: Only single track, " + "type 0 MIDI files supported!\n"); + return false; + } + + return true; +} + +// Read and check the track chunk header + +static boolean ReadTrackChunk(midi_file_t *file) +{ + size_t bytes_read; + chunk_header_t chunk_header; + + bytes_read = fread(&chunk_header, sizeof(chunk_header_t), 1, file->stream); + + if (bytes_read < sizeof(chunk_header)) + { + return false; + } + + if (!CheckChunkHeader(&chunk_header, TRACK_CHUNK_ID)) + { + return false; + } + + file->data_len = LONG(chunk_header.chunk_size); + + return true; +} + +midi_file_t *MIDI_OpenFile(char *filename) +{ + midi_file_t *file; + + file = malloc(sizeof(midi_file_t)); + + if (file == NULL) + { + return NULL; + } + + // Open file + + file->stream = fopen(filename, "rb"); + + if (file->stream == NULL) + { + fprintf(stderr, "Failed to open '%s'\n", filename); + free(file); + return NULL; + } + + // Read MIDI file header + + if (!ReadHeaderChunk(file)) + { + fclose(file->stream); + free(file); + return NULL; + } + + // Read track header + + if (!ReadTrackChunk(file)) + { + fclose(file->stream); + free(file); + return NULL; + } + + return file; +} + +void MIDI_CloseFile(midi_file_t *file) +{ + fclose(file->stream); + free(file); +} + +// Read a MIDI channel event. +// two_param indicates that the event type takes two parameters +// (three byte) otherwise it is single parameter (two byte) + +static boolean ReadChannelEvent(midi_file_t *file, midi_event_t *event, + int event_type, boolean two_param) +{ + int c; + + // Set basics: + + event->event_type = event_type >> 4; + event->data.channel.channel = event_type & 0xf; + + // Read parameters: + + c = fgetc(file->stream); + + if (c == EOF) + { + return false; + } + + event->data.channel.param1 = c; + + // Second parameter: + + if (two_param) + { + c = fgetc(file->stream); + + if (c == EOF) + { + return false; + } + + event->data.channel.param2 = c; + } + + return true; +} + +// Read sysex event: + +static boolean ReadSysExEvent(midi_file_t *file, midi_event_t *event, + int event_type) +{ + // TODO + return false; +} + +// Read meta event: + +static boolean ReadMetaEvent(midi_file_t *file, midi_event_t *event) +{ + // TODO + return false; +} + +boolean MIDI_ReadEvent(midi_file_t *file, midi_event_t *event) +{ + int event_type; + + event_type = fgetc(file->stream); + + if (event_type == EOF) + { + return false; + } + + // Check event type: + + switch (event_type >> 4) + { + // Two parameter channel events: + + case MIDI_EVENT_NOTE_OFF: + case MIDI_EVENT_NOTE_ON: + case MIDI_EVENT_AFTERTOUCH: + case MIDI_EVENT_CONTROLLER: + case MIDI_EVENT_PITCH_BEND: + return ReadChannelEvent(file, event, event_type, true); + + // Single parameter channel events: + + case MIDI_EVENT_PROGRAM_CHANGE: + case MIDI_EVENT_CHAN_AFTERTOUCH: + return ReadChannelEvent(file, event, event_type, false); + + // Other event types: + + case 0xf: + if (event_type == MIDI_EVENT_SYSEX + || event_type == MIDI_EVENT_SYSEX_SPLIT) + { + return ReadSysExEvent(file, event, event_type); + } + else if (event_type == MIDI_EVENT_META) + { + return ReadMetaEvent(file, event); + } + + // Fall-through deliberate - + + default: + fprintf(stderr, "Unknown MIDI event type: 0x%x\n", event_type); + return false; + } +} + diff --git a/src/midifile.h b/src/midifile.h new file mode 100644 index 00000000..d7df8b86 --- /dev/null +++ b/src/midifile.h @@ -0,0 +1,135 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// MIDI file parsing. +// +//----------------------------------------------------------------------------- + +#ifndef MIDIFILE_H +#define MIDIFILE_H + +typedef struct midi_file_s midi_file_t; + +typedef enum +{ + MIDI_EVENT_NOTE_OFF = 0x8, + MIDI_EVENT_NOTE_ON = 0x9, + MIDI_EVENT_AFTERTOUCH = 0xa, + MIDI_EVENT_CONTROLLER = 0xb, + MIDI_EVENT_PROGRAM_CHANGE = 0xc, + MIDI_EVENT_CHAN_AFTERTOUCH = 0xd, + MIDI_EVENT_PITCH_BEND = 0xe, + + MIDI_EVENT_SYSEX = 0xf0, + MIDI_EVENT_SYSEX_SPLIT = 0xf7, + MIDI_EVENT_META = 0xff, +} midi_event_type_t; + +typedef enum +{ + MIDI_CONTROLLER_BANK_SELECT = 0x0, + MIDI_CONTROLLER_MODULATION = 0x1, + MIDI_CONTROLLER_BREATH_CONTROL = 0x2, + MIDI_CONTROLLER_FOOT_CONTROL = 0x3, + MIDI_CONTROLLER_PORTAMENTO = 0x4, + MIDI_CONTROLLER_DATA_ENTRY = 0x5, +} midi_controller_t; + +typedef enum +{ + MIDI_META_SEQUENCE_NUMBER = 0x0, + + MIDI_META_TEXT = 0x1, + MIDI_META_COPYRIGHT = 0x2, + MIDI_META_TRACK_NAME = 0x3, + MIDI_META_INSTR_NAME = 0x4, + MIDI_META_LYRICS = 0x5, + MIDI_META_MARKER = 0x6, + MIDI_META_CUE_POINT = 0x7, + + MIDI_META_CHANNEL_PREFIX = 0x8, + MIDI_META_END_OF_TRACK = 0x9, + MIDI_META_SET_TEMPO = 0xa, + MIDI_META_SMPTE_OFFSET = 0xb, + MIDI_META_TIME_SIGNATURE = 0xc, + MIDI_META_KEY_SIGNATURE = 0xd, + MIDI_META_SEQUENCER_SPECIFIC = 0xe, +} midi_meta_event_type_t; + +typedef struct +{ + // Meta event type: + + unsigned int type; + + // Length: + + unsigned int length; + + // Meta event data: + + byte *data; +} midi_meta_event_data_t; + +typedef struct +{ + // Length: + + unsigned int length; + + // Event data: + + byte *data; +} midi_sysex_event_data_t; + +typedef struct +{ + // The channel number to which this applies: + + unsigned int channel; + + // Extra parameters: + + unsigned int param1; + unsigned int param2; +} midi_channel_event_data_t; + +typedef struct +{ + // Time between the previous event and this event. + unsigned int delta_time; + + // Type of event: + midi_event_type_t event_type; + + union + { + midi_channel_event_data_t channel; + midi_meta_event_data_t meta; + midi_sysex_event_data_t sysex; + } data; +} midi_event_t; + +midi_file_t *MIDI_OpenFile(char *filename); +void MIDI_CloseFile(midi_file_t *file); + +#endif /* #ifndef MIDIFILE_H */ + -- cgit v1.2.3 From 2b64dfc84981dbbd9de1a0d9cb11995bf0ba9379 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 29 Mar 2009 22:39:25 +0000 Subject: Parse SysEx, meta events Subversion-branch: /branches/opl-branch Subversion-revision: 1490 --- src/midifile.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 180 insertions(+), 16 deletions(-) diff --git a/src/midifile.c b/src/midifile.c index e74cf17f..1b8a8786 100644 --- a/src/midifile.c +++ b/src/midifile.c @@ -53,6 +53,10 @@ struct midi_file_s FILE *stream; midi_header_t header; unsigned int data_len; + + // Data buffer used to store data read for SysEx or meta events: + byte *buffer; + unsigned int buffer_size; }; static boolean CheckChunkHeader(chunk_header_t *chunk, @@ -137,6 +141,9 @@ midi_file_t *MIDI_OpenFile(char *filename) return NULL; } + file->buffer = NULL; + file->buffer_size = 0; + // Open file file->stream = fopen(filename, "rb"); @@ -172,9 +179,116 @@ midi_file_t *MIDI_OpenFile(char *filename) void MIDI_CloseFile(midi_file_t *file) { fclose(file->stream); + free(file->buffer); free(file); } +// Read a single byte. Returns false on error. + +static boolean ReadByte(midi_file_t *file, byte *result) +{ + int c; + + c = fgetc(file->stream); + + if (c == EOF) + { + return false; + } + else + { + *result = (byte) c; + + return true; + } +} + +// Read a variable-length value. + +static boolean ReadVariableLength(midi_file_t *file, unsigned int *result) +{ + int i; + byte b; + + *result = 0; + + for (i=0; i<4; ++i) + { + if (!ReadByte(file, &b)) + { + fprintf(stderr, "Error while reading variable-length value\n"); + return false; + } + + // Insert the bottom seven bits from this byte. + + *result <<= 7; + *result |= b & 0x7f; + + // If the top bit is not set, this is the end. + + if ((b & 0x80) == 0) + { + return true; + } + } + + fprintf(stderr, "Variable-length value too long: maximum of four bytes!\n");; + return false; +} + +// Expand the size of the buffer used for SysEx/Meta events: + +static boolean ExpandBuffer(midi_file_t *file, unsigned int new_size) +{ + byte *new_buffer; + + if (file->buffer_size < new_size) + { + // Reallocate to a larger size: + + new_buffer = realloc(file->buffer, new_size); + + if (new_buffer == NULL) + { + fprintf(stderr, "ExpandBuffer: Failed to expand buffer to %u " + "bytes\n", new_size); + return false; + } + + file->buffer = new_buffer; + file->buffer_size = new_size; + } + + return true; +} + +// Read a byte sequence into the data buffer. + +static boolean ReadByteSequence(midi_file_t *file, unsigned int num_bytes) +{ + unsigned int i; + + // Check that we have enough space: + + if (!ExpandBuffer(file, num_bytes)) + { + return false; + } + + for (i=0; ibuffer[i])) + { + fprintf(stderr, "ReadByteSequence: Error while reading byte %u\n", + i); + return false; + } + } + + return true; +} + // Read a MIDI channel event. // two_param indicates that the event type takes two parameters // (three byte) otherwise it is single parameter (two byte) @@ -182,7 +296,7 @@ void MIDI_CloseFile(midi_file_t *file) static boolean ReadChannelEvent(midi_file_t *file, midi_event_t *event, int event_type, boolean two_param) { - int c; + byte b; // Set basics: @@ -191,27 +305,23 @@ static boolean ReadChannelEvent(midi_file_t *file, midi_event_t *event, // Read parameters: - c = fgetc(file->stream); - - if (c == EOF) + if (!ReadByte(file, &b)) { return false; } - event->data.channel.param1 = c; + event->data.channel.param1 = b; // Second parameter: if (two_param) { - c = fgetc(file->stream); - - if (c == EOF) + if (!ReadByte(file, &b)) { return false; } - event->data.channel.param2 = c; + event->data.channel.param2 = b; } return true; @@ -222,26 +332,79 @@ static boolean ReadChannelEvent(midi_file_t *file, midi_event_t *event, static boolean ReadSysExEvent(midi_file_t *file, midi_event_t *event, int event_type) { - // TODO - return false; + event->event_type = event_type; + + if (!ReadVariableLength(file, &event->data.sysex.length)) + { + fprintf(stderr, "ReadSysExEvent: Failed to read length of " + "SysEx block\n"); + return false; + } + + // Read the byte sequence: + + if (!ReadByteSequence(file, event->data.sysex.length)) + { + fprintf(stderr, "ReadSysExEvent: Failed while reading SysEx event\n"); + return false; + } + + event->data.sysex.data = file->buffer; + + return true; } // Read meta event: static boolean ReadMetaEvent(midi_file_t *file, midi_event_t *event) { - // TODO + byte b; + + // Read meta event type: + + if (!ReadByte(file, &b)) + { + fprintf(stderr, "ReadMetaEvent: Failed to read meta event type\n"); + return false; + } + + event->data.meta.type = b; + + // Read length of meta event data: + + if (!ReadVariableLength(file, &event->data.meta.length)) + { + fprintf(stderr, "ReadSysExEvent: Failed to read length of " + "SysEx block\n"); + return false; + } + + // Read the byte sequence: + + if (!ReadByteSequence(file, event->data.meta.length)) + { + fprintf(stderr, "ReadSysExEvent: Failed while reading SysEx event\n"); + return false; + } + + event->data.meta.data = file->buffer; + return false; } boolean MIDI_ReadEvent(midi_file_t *file, midi_event_t *event) { - int event_type; + byte event_type; - event_type = fgetc(file->stream); + if (!ReadVariableLength(file, &event->delta_time)) + { + fprintf(stderr, "MIDI_ReadEvent: Failed to read event timestamp\n"); + return false; + } - if (event_type == EOF) + if (!ReadByte(file, &event_type)) { + fprintf(stderr, "MIDI_ReadEvent: Failed to read event type\n"); return false; } @@ -277,7 +440,8 @@ boolean MIDI_ReadEvent(midi_file_t *file, midi_event_t *event) return ReadMetaEvent(file, event); } - // Fall-through deliberate - + // --- Fall-through deliberate --- + // Other 0xfx event types are unknown default: fprintf(stderr, "Unknown MIDI event type: 0x%x\n", event_type); -- cgit v1.2.3 From d9f55dd56cc2a21dc6be97e9a2512d9bd8f05286 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 29 Mar 2009 22:40:44 +0000 Subject: Fix compile warning. Subversion-branch: /branches/opl-branch Subversion-revision: 1491 --- opl/opl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/opl/opl.c b/opl/opl.c index 9afdd980..8f75241b 100644 --- a/opl/opl.c +++ b/opl/opl.c @@ -25,6 +25,7 @@ #include "config.h" +#include #include #include "opl.h" -- cgit v1.2.3 From c4f640be999e80351f1c89c893a0d5ce39c20cd9 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 29 Mar 2009 22:55:55 +0000 Subject: Clean up error messages, minor bugs etc. Subversion-branch: /branches/opl-branch Subversion-revision: 1492 --- src/midifile.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/midifile.c b/src/midifile.c index 1b8a8786..ed60958d 100644 --- a/src/midifile.c +++ b/src/midifile.c @@ -33,6 +33,7 @@ #define HEADER_CHUNK_ID "MThd" #define TRACK_CHUNK_ID "MTrk" +#define MAX_BUFFER_SIZE 0x10000 typedef struct { @@ -68,7 +69,7 @@ static boolean CheckChunkHeader(chunk_header_t *chunk, if (!result) { - fprintf(stderr, "CheckChunkHeader: Expected '%s' chunk header!\n", + fprintf(stderr, "CheckChunkHeader: Expected '%s' chunk header\n", expected_id); } @@ -150,7 +151,7 @@ midi_file_t *MIDI_OpenFile(char *filename) if (file->stream == NULL) { - fprintf(stderr, "Failed to open '%s'\n", filename); + fprintf(stderr, "MIDI_OpenFile: Failed to open '%s'\n", filename); free(file); return NULL; } @@ -193,6 +194,7 @@ static boolean ReadByte(midi_file_t *file, byte *result) if (c == EOF) { + fprintf(stderr, "ReadByte: Unexpected end of file\n"); return false; } else @@ -216,7 +218,8 @@ static boolean ReadVariableLength(midi_file_t *file, unsigned int *result) { if (!ReadByte(file, &b)) { - fprintf(stderr, "Error while reading variable-length value\n"); + fprintf(stderr, "ReadVariableLength: Error while reading " + "variable-length value\n"); return false; } @@ -233,7 +236,8 @@ static boolean ReadVariableLength(midi_file_t *file, unsigned int *result) } } - fprintf(stderr, "Variable-length value too long: maximum of four bytes!\n");; + fprintf(stderr, "ReadVariableLength: Variable-length value too " + "long: maximum of four bytes\n"); return false; } @@ -243,6 +247,13 @@ static boolean ExpandBuffer(midi_file_t *file, unsigned int new_size) { byte *new_buffer; + if (new_size > MAX_BUFFER_SIZE) + { + fprintf(stderr, "ExpandBuffer: Tried to expand buffer to %u bytes\n", + new_size); + return false; + } + if (file->buffer_size < new_size) { // Reallocate to a larger size: @@ -294,7 +305,7 @@ static boolean ReadByteSequence(midi_file_t *file, unsigned int num_bytes) // (three byte) otherwise it is single parameter (two byte) static boolean ReadChannelEvent(midi_file_t *file, midi_event_t *event, - int event_type, boolean two_param) + byte event_type, boolean two_param) { byte b; @@ -307,6 +318,8 @@ static boolean ReadChannelEvent(midi_file_t *file, midi_event_t *event, if (!ReadByte(file, &b)) { + fprintf(stderr, "ReadChannelEvent: Error while reading channel " + "event parameters\n"); return false; } @@ -318,6 +331,8 @@ static boolean ReadChannelEvent(midi_file_t *file, midi_event_t *event, { if (!ReadByte(file, &b)) { + fprintf(stderr, "ReadChannelEvent: Error while reading channel " + "event parameters\n"); return false; } @@ -389,7 +404,7 @@ static boolean ReadMetaEvent(midi_file_t *file, midi_event_t *event) event->data.meta.data = file->buffer; - return false; + return true; } boolean MIDI_ReadEvent(midi_file_t *file, midi_event_t *event) -- cgit v1.2.3 From 9b192c44f932542ccd835936d593c43ce3e9e9e3 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Fri, 3 Apr 2009 19:58:08 +0000 Subject: Fix up MIDI reading code; add test code. Subversion-branch: /branches/opl-branch Subversion-revision: 1494 --- src/Makefile.am | 3 ++ src/midifile.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++--------- src/midifile.h | 15 ++++---- 3 files changed, 98 insertions(+), 24 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 92095b5c..3874c35f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -195,3 +195,6 @@ icon.c : ../data/doom.ico endif +midiread : midifile.c + $(CC) -DTEST $(CFLAGS) @LDFLAGS@ $^ -o $@ + diff --git a/src/midifile.c b/src/midifile.c index ed60958d..e4273c12 100644 --- a/src/midifile.c +++ b/src/midifile.c @@ -27,6 +27,7 @@ #include #include +#include "doomdef.h" #include "doomtype.h" #include "i_swap.h" #include "midifile.h" @@ -39,15 +40,15 @@ typedef struct { byte chunk_id[4]; unsigned int chunk_size; -} chunk_header_t; +} PACKEDATTR chunk_header_t; typedef struct { chunk_header_t chunk_header; unsigned short format_type; unsigned short num_tracks; - unsigned int time_division; -} midi_header_t; + unsigned short time_division; +} PACKEDATTR midi_header_t; struct midi_file_s { @@ -65,12 +66,15 @@ static boolean CheckChunkHeader(chunk_header_t *chunk, { boolean result; - result = (strcmp((char *) chunk->chunk_id, expected_id) == 0); + result = (memcmp((char *) chunk->chunk_id, expected_id, 4) == 0); if (!result) { - fprintf(stderr, "CheckChunkHeader: Expected '%s' chunk header\n", - expected_id); + fprintf(stderr, "CheckChunkHeader: Expected '%s' chunk header, " + "got '%c%c%c%c'\n", + expected_id, + chunk->chunk_id[0], chunk->chunk_id[1], + chunk->chunk_id[2], chunk->chunk_id[3]); } return result; @@ -80,24 +84,26 @@ static boolean CheckChunkHeader(chunk_header_t *chunk, static boolean ReadHeaderChunk(midi_file_t *file) { - size_t bytes_read; + size_t records_read; - bytes_read = fread(&file->header, sizeof(midi_header_t), 1, file->stream); + records_read = fread(&file->header, sizeof(midi_header_t), 1, file->stream); - if (bytes_read < sizeof(midi_header_t)) + if (records_read < 1) { return false; } if (!CheckChunkHeader(&file->header.chunk_header, HEADER_CHUNK_ID) - || LONG(file->header.chunk_header.chunk_size) != 6) + || SDL_SwapBE32(file->header.chunk_header.chunk_size) != 6) { - fprintf(stderr, "ReadHeaderChunk: Invalid MIDI chunk header!\n"); + fprintf(stderr, "ReadHeaderChunk: Invalid MIDI chunk header! " + "chunk_size=%i\n", + SDL_SwapBE32(file->header.chunk_header.chunk_size)); return false; } - if (SHORT(file->header.format_type) != 0 - || SHORT(file->header.num_tracks) != 1) + if (SDL_SwapBE16(file->header.format_type) != 0 + || SDL_SwapBE16(file->header.num_tracks) != 1) { fprintf(stderr, "ReadHeaderChunk: Only single track, " "type 0 MIDI files supported!\n"); @@ -111,12 +117,12 @@ static boolean ReadHeaderChunk(midi_file_t *file) static boolean ReadTrackChunk(midi_file_t *file) { - size_t bytes_read; + size_t records_read; chunk_header_t chunk_header; - bytes_read = fread(&chunk_header, sizeof(chunk_header_t), 1, file->stream); + records_read = fread(&chunk_header, sizeof(chunk_header_t), 1, file->stream); - if (bytes_read < sizeof(chunk_header)) + if (records_read < 1) { return false; } @@ -126,7 +132,7 @@ static boolean ReadTrackChunk(midi_file_t *file) return false; } - file->data_len = LONG(chunk_header.chunk_size); + file->data_len = SDL_SwapBE32(chunk_header.chunk_size); return true; } @@ -375,6 +381,8 @@ static boolean ReadMetaEvent(midi_file_t *file, midi_event_t *event) { byte b; + event->event_type = MIDI_EVENT_META; + // Read meta event type: if (!ReadByte(file, &b)) @@ -464,3 +472,65 @@ boolean MIDI_ReadEvent(midi_file_t *file, midi_event_t *event) } } +#ifdef TEST + +int main(int argc, char *argv[]) +{ + midi_file_t *file; + midi_event_t event; + + if (argc < 2) + { + printf("Usage: %s \n", argv[0]); + exit(1); + } + + file = MIDI_OpenFile(argv[1]); + + if (file == NULL) + { + fprintf(stderr, "Failed to open %s\n", argv[1]); + exit(1); + } + + while (MIDI_ReadEvent(file, &event)) + { + printf("Event type: %i\n", event.event_type); + + switch(event.event_type) + { + case MIDI_EVENT_NOTE_OFF: + case MIDI_EVENT_NOTE_ON: + case MIDI_EVENT_AFTERTOUCH: + case MIDI_EVENT_CONTROLLER: + case MIDI_EVENT_PROGRAM_CHANGE: + case MIDI_EVENT_CHAN_AFTERTOUCH: + case MIDI_EVENT_PITCH_BEND: + printf("\tChannel: %i\n", event.data.channel.channel); + printf("\tParameter 1: %i\n", event.data.channel.param1); + printf("\tParameter 2: %i\n", event.data.channel.param2); + break; + + case MIDI_EVENT_SYSEX: + case MIDI_EVENT_SYSEX_SPLIT: + printf("\tLength: %i\n", event.data.sysex.length); + break; + + case MIDI_EVENT_META: + printf("\tMeta type: %i\n", event.data.meta.type); + printf("\tLength: %i\n", event.data.meta.length); + break; + } + + if (event.event_type == MIDI_EVENT_META + && event.data.meta.type == MIDI_META_END_OF_TRACK) + { + break; + } + } + + return 0; +} + +#endif + diff --git a/src/midifile.h b/src/midifile.h index d7df8b86..7928fdda 100644 --- a/src/midifile.h +++ b/src/midifile.h @@ -65,13 +65,14 @@ typedef enum MIDI_META_MARKER = 0x6, MIDI_META_CUE_POINT = 0x7, - MIDI_META_CHANNEL_PREFIX = 0x8, - MIDI_META_END_OF_TRACK = 0x9, - MIDI_META_SET_TEMPO = 0xa, - MIDI_META_SMPTE_OFFSET = 0xb, - MIDI_META_TIME_SIGNATURE = 0xc, - MIDI_META_KEY_SIGNATURE = 0xd, - MIDI_META_SEQUENCER_SPECIFIC = 0xe, + MIDI_META_CHANNEL_PREFIX = 0x20, + MIDI_META_END_OF_TRACK = 0x2f, + + MIDI_META_SET_TEMPO = 0x51, + MIDI_META_SMPTE_OFFSET = 0x54, + MIDI_META_TIME_SIGNATURE = 0x58, + MIDI_META_KEY_SIGNATURE = 0x59, + MIDI_META_SEQUENCER_SPECIFIC = 0x7f, } midi_meta_event_type_t; typedef struct -- cgit v1.2.3 From b2d78c296a5a2f9cf37bf8bd3dafb5c8ba166812 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Fri, 3 Apr 2009 20:07:08 +0000 Subject: Show MIDI event types with string identifiers. Subversion-branch: /branches/opl-branch Subversion-revision: 1495 --- src/midifile.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/midifile.c b/src/midifile.c index e4273c12..ef2ceb88 100644 --- a/src/midifile.c +++ b/src/midifile.c @@ -474,6 +474,36 @@ boolean MIDI_ReadEvent(midi_file_t *file, midi_event_t *event) #ifdef TEST +static char *MIDI_EventTypeToString(midi_event_type_t event_type) +{ + switch (event_type) + { + case MIDI_EVENT_NOTE_OFF: + return "MIDI_EVENT_NOTE_OFF"; + case MIDI_EVENT_NOTE_ON: + return "MIDI_EVENT_NOTE_ON"; + case MIDI_EVENT_AFTERTOUCH: + return "MIDI_EVENT_AFTERTOUCH"; + case MIDI_EVENT_CONTROLLER: + return "MIDI_EVENT_CONTROLLER"; + case MIDI_EVENT_PROGRAM_CHANGE: + return "MIDI_EVENT_PROGRAM_CHANGE"; + case MIDI_EVENT_CHAN_AFTERTOUCH: + return "MIDI_EVENT_CHAN_AFTERTOUCH"; + case MIDI_EVENT_PITCH_BEND: + return "MIDI_EVENT_PITCH_BEND"; + case MIDI_EVENT_SYSEX: + return "MIDI_EVENT_SYSEX"; + case MIDI_EVENT_SYSEX_SPLIT: + return "MIDI_EVENT_SYSEX_SPLIT"; + case MIDI_EVENT_META: + return "MIDI_EVENT_META"; + + default: + return "(unknown)"; + } +} + int main(int argc, char *argv[]) { midi_file_t *file; @@ -495,7 +525,9 @@ int main(int argc, char *argv[]) while (MIDI_ReadEvent(file, &event)) { - printf("Event type: %i\n", event.event_type); + printf("Event type: %s (%i)\n", + MIDI_EventTypeToString(event.event_type), + event.event_type); switch(event.event_type) { -- cgit v1.2.3 From 3dd7e1db9bf26e1e50bc13f6b176e54d63f2da85 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Mon, 13 Apr 2009 22:23:05 +0000 Subject: Extend MIDI file code to support reading multi-track files. Subversion-branch: /branches/opl-branch Subversion-revision: 1498 --- src/midifile.c | 562 +++++++++++++++++++++++++++++++++++---------------------- src/midifile.h | 2 +- 2 files changed, 351 insertions(+), 213 deletions(-) diff --git a/src/midifile.c b/src/midifile.c index ef2ceb88..cca0189e 100644 --- a/src/midifile.c +++ b/src/midifile.c @@ -50,17 +50,33 @@ typedef struct unsigned short time_division; } PACKEDATTR midi_header_t; +typedef struct +{ + // Length in bytes: + + unsigned int data_len; + + // Events in this track: + + midi_event_t *events; + int num_events; +} PACKEDATTR midi_track_t; + struct midi_file_s { - FILE *stream; midi_header_t header; - unsigned int data_len; + + // All tracks in this file: + midi_track_t *tracks; + unsigned int num_tracks; // Data buffer used to store data read for SysEx or meta events: byte *buffer; unsigned int buffer_size; }; +// Check the header of a chunk: + static boolean CheckChunkHeader(chunk_header_t *chunk, char *expected_id) { @@ -80,123 +96,13 @@ static boolean CheckChunkHeader(chunk_header_t *chunk, return result; } -// Read and check the header chunk. - -static boolean ReadHeaderChunk(midi_file_t *file) -{ - size_t records_read; - - records_read = fread(&file->header, sizeof(midi_header_t), 1, file->stream); - - if (records_read < 1) - { - return false; - } - - if (!CheckChunkHeader(&file->header.chunk_header, HEADER_CHUNK_ID) - || SDL_SwapBE32(file->header.chunk_header.chunk_size) != 6) - { - fprintf(stderr, "ReadHeaderChunk: Invalid MIDI chunk header! " - "chunk_size=%i\n", - SDL_SwapBE32(file->header.chunk_header.chunk_size)); - return false; - } - - if (SDL_SwapBE16(file->header.format_type) != 0 - || SDL_SwapBE16(file->header.num_tracks) != 1) - { - fprintf(stderr, "ReadHeaderChunk: Only single track, " - "type 0 MIDI files supported!\n"); - return false; - } - - return true; -} - -// Read and check the track chunk header - -static boolean ReadTrackChunk(midi_file_t *file) -{ - size_t records_read; - chunk_header_t chunk_header; - - records_read = fread(&chunk_header, sizeof(chunk_header_t), 1, file->stream); - - if (records_read < 1) - { - return false; - } - - if (!CheckChunkHeader(&chunk_header, TRACK_CHUNK_ID)) - { - return false; - } - - file->data_len = SDL_SwapBE32(chunk_header.chunk_size); - - return true; -} - -midi_file_t *MIDI_OpenFile(char *filename) -{ - midi_file_t *file; - - file = malloc(sizeof(midi_file_t)); - - if (file == NULL) - { - return NULL; - } - - file->buffer = NULL; - file->buffer_size = 0; - - // Open file - - file->stream = fopen(filename, "rb"); - - if (file->stream == NULL) - { - fprintf(stderr, "MIDI_OpenFile: Failed to open '%s'\n", filename); - free(file); - return NULL; - } - - // Read MIDI file header - - if (!ReadHeaderChunk(file)) - { - fclose(file->stream); - free(file); - return NULL; - } - - // Read track header - - if (!ReadTrackChunk(file)) - { - fclose(file->stream); - free(file); - return NULL; - } - - return file; -} - -void MIDI_CloseFile(midi_file_t *file) -{ - fclose(file->stream); - free(file->buffer); - free(file); -} - // Read a single byte. Returns false on error. -static boolean ReadByte(midi_file_t *file, byte *result) +static boolean ReadByte(byte *result, FILE *stream) { int c; - c = fgetc(file->stream); + c = fgetc(stream); if (c == EOF) { @@ -213,7 +119,7 @@ static boolean ReadByte(midi_file_t *file, byte *result) // Read a variable-length value. -static boolean ReadVariableLength(midi_file_t *file, unsigned int *result) +static boolean ReadVariableLength(unsigned int *result, FILE *stream) { int i; byte b; @@ -222,7 +128,7 @@ static boolean ReadVariableLength(midi_file_t *file, unsigned int *result) for (i=0; i<4; ++i) { - if (!ReadByte(file, &b)) + if (!ReadByte(&b, stream)) { fprintf(stderr, "ReadVariableLength: Error while reading " "variable-length value\n"); @@ -247,71 +153,46 @@ static boolean ReadVariableLength(midi_file_t *file, unsigned int *result) return false; } -// Expand the size of the buffer used for SysEx/Meta events: - -static boolean ExpandBuffer(midi_file_t *file, unsigned int new_size) -{ - byte *new_buffer; - - if (new_size > MAX_BUFFER_SIZE) - { - fprintf(stderr, "ExpandBuffer: Tried to expand buffer to %u bytes\n", - new_size); - return false; - } - - if (file->buffer_size < new_size) - { - // Reallocate to a larger size: - - new_buffer = realloc(file->buffer, new_size); - - if (new_buffer == NULL) - { - fprintf(stderr, "ExpandBuffer: Failed to expand buffer to %u " - "bytes\n", new_size); - return false; - } - - file->buffer = new_buffer; - file->buffer_size = new_size; - } - - return true; -} - // Read a byte sequence into the data buffer. -static boolean ReadByteSequence(midi_file_t *file, unsigned int num_bytes) +static void *ReadByteSequence(unsigned int num_bytes, FILE *stream) { unsigned int i; + byte *result; + + // Allocate a buffer: - // Check that we have enough space: + result = malloc(num_bytes); - if (!ExpandBuffer(file, num_bytes)) + if (result == NULL) { - return false; + fprintf(stderr, "ReadByteSequence: Failed to allocate buffer\n"); + return NULL; } + // Read the data: + for (i=0; ibuffer[i])) + if (!ReadByte(&result[i], stream)) { fprintf(stderr, "ReadByteSequence: Error while reading byte %u\n", i); - return false; + free(result); + return NULL; } } - return true; + return result; } // Read a MIDI channel event. // two_param indicates that the event type takes two parameters // (three byte) otherwise it is single parameter (two byte) -static boolean ReadChannelEvent(midi_file_t *file, midi_event_t *event, - byte event_type, boolean two_param) +static boolean ReadChannelEvent(midi_event_t *event, + byte event_type, boolean two_param, + FILE *stream) { byte b; @@ -322,7 +203,7 @@ static boolean ReadChannelEvent(midi_file_t *file, midi_event_t *event, // Read parameters: - if (!ReadByte(file, &b)) + if (!ReadByte(&b, stream)) { fprintf(stderr, "ReadChannelEvent: Error while reading channel " "event parameters\n"); @@ -335,7 +216,7 @@ static boolean ReadChannelEvent(midi_file_t *file, midi_event_t *event, if (two_param) { - if (!ReadByte(file, &b)) + if (!ReadByte(&b, stream)) { fprintf(stderr, "ReadChannelEvent: Error while reading channel " "event parameters\n"); @@ -350,12 +231,12 @@ static boolean ReadChannelEvent(midi_file_t *file, midi_event_t *event, // Read sysex event: -static boolean ReadSysExEvent(midi_file_t *file, midi_event_t *event, - int event_type) +static boolean ReadSysExEvent(midi_event_t *event, int event_type, + FILE *stream) { event->event_type = event_type; - if (!ReadVariableLength(file, &event->data.sysex.length)) + if (!ReadVariableLength(&event->data.sysex.length, stream)) { fprintf(stderr, "ReadSysExEvent: Failed to read length of " "SysEx block\n"); @@ -364,20 +245,20 @@ static boolean ReadSysExEvent(midi_file_t *file, midi_event_t *event, // Read the byte sequence: - if (!ReadByteSequence(file, event->data.sysex.length)) + event->data.sysex.data = ReadByteSequence(event->data.sysex.length, stream); + + if (event->data.sysex.data == NULL) { fprintf(stderr, "ReadSysExEvent: Failed while reading SysEx event\n"); return false; } - event->data.sysex.data = file->buffer; - return true; } // Read meta event: -static boolean ReadMetaEvent(midi_file_t *file, midi_event_t *event) +static boolean ReadMetaEvent(midi_event_t *event, FILE *stream) { byte b; @@ -385,7 +266,7 @@ static boolean ReadMetaEvent(midi_file_t *file, midi_event_t *event) // Read meta event type: - if (!ReadByte(file, &b)) + if (!ReadByte(&b, stream)) { fprintf(stderr, "ReadMetaEvent: Failed to read meta event type\n"); return false; @@ -395,7 +276,7 @@ static boolean ReadMetaEvent(midi_file_t *file, midi_event_t *event) // Read length of meta event data: - if (!ReadVariableLength(file, &event->data.meta.length)) + if (!ReadVariableLength(&event->data.meta.length, stream)) { fprintf(stderr, "ReadSysExEvent: Failed to read length of " "SysEx block\n"); @@ -404,30 +285,30 @@ static boolean ReadMetaEvent(midi_file_t *file, midi_event_t *event) // Read the byte sequence: - if (!ReadByteSequence(file, event->data.meta.length)) + event->data.meta.data = ReadByteSequence(event->data.meta.length, stream); + + if (event->data.meta.data == NULL) { fprintf(stderr, "ReadSysExEvent: Failed while reading SysEx event\n"); return false; } - event->data.meta.data = file->buffer; - return true; } -boolean MIDI_ReadEvent(midi_file_t *file, midi_event_t *event) +static boolean ReadEvent(midi_event_t *event, FILE *stream) { byte event_type; - if (!ReadVariableLength(file, &event->delta_time)) + if (!ReadVariableLength(&event->delta_time, stream)) { - fprintf(stderr, "MIDI_ReadEvent: Failed to read event timestamp\n"); + fprintf(stderr, "ReadEvent: Failed to read event timestamp\n"); return false; } - if (!ReadByte(file, &event_type)) + if (!ReadByte(&event_type, stream)) { - fprintf(stderr, "MIDI_ReadEvent: Failed to read event type\n"); + fprintf(stderr, "ReadEvent: Failed to read event type\n"); return false; } @@ -442,13 +323,13 @@ boolean MIDI_ReadEvent(midi_file_t *file, midi_event_t *event) case MIDI_EVENT_AFTERTOUCH: case MIDI_EVENT_CONTROLLER: case MIDI_EVENT_PITCH_BEND: - return ReadChannelEvent(file, event, event_type, true); + return ReadChannelEvent(event, event_type, true, stream); // Single parameter channel events: case MIDI_EVENT_PROGRAM_CHANGE: case MIDI_EVENT_CHAN_AFTERTOUCH: - return ReadChannelEvent(file, event, event_type, false); + return ReadChannelEvent(event, event_type, false, stream); // Other event types: @@ -456,11 +337,11 @@ boolean MIDI_ReadEvent(midi_file_t *file, midi_event_t *event) if (event_type == MIDI_EVENT_SYSEX || event_type == MIDI_EVENT_SYSEX_SPLIT) { - return ReadSysExEvent(file, event, event_type); + return ReadSysExEvent(event, event_type, stream); } else if (event_type == MIDI_EVENT_META) { - return ReadMetaEvent(file, event); + return ReadMetaEvent(event, stream); } // --- Fall-through deliberate --- @@ -472,6 +353,254 @@ boolean MIDI_ReadEvent(midi_file_t *file, midi_event_t *event) } } +// Free an event: + +static void FreeEvent(midi_event_t *event) +{ + // Some event types have dynamically allocated buffers assigned + // to them that must be freed. + + switch (event->event_type) + { + case MIDI_EVENT_SYSEX: + case MIDI_EVENT_SYSEX_SPLIT: + free(event->data.sysex.data); + break; + + case MIDI_EVENT_META: + free(event->data.meta.data); + break; + + default: + // Nothing to do. + break; + } +} + +// Read and check the track chunk header + +static boolean ReadTrackHeader(midi_track_t *track, FILE *stream) +{ + size_t records_read; + chunk_header_t chunk_header; + + records_read = fread(&chunk_header, sizeof(chunk_header_t), 1, stream); + + if (records_read < 1) + { + return false; + } + + if (!CheckChunkHeader(&chunk_header, TRACK_CHUNK_ID)) + { + return false; + } + + track->data_len = SDL_SwapBE32(chunk_header.chunk_size); + + return true; +} + +static boolean ReadTrack(midi_track_t *track, FILE *stream) +{ + midi_event_t *new_events; + midi_event_t *event; + + track->num_events = 0; + track->events = NULL; + + // Read the header: + + if (!ReadTrackHeader(track, stream)) + { + return false; + } + + // Then the events: + + for (;;) + { + // Resize the track slightly larger to hold another event: + + new_events = realloc(track->events, + sizeof(midi_event_t) * (track->num_events + 1)); + + if (new_events == NULL) + { + return false; + } + + track->events = new_events; + + // Read the next event: + + event = &track->events[track->num_events]; + if (!ReadEvent(event, stream)) + { + return false; + } + + ++track->num_events; + + // End of track? + + if (event->event_type == MIDI_EVENT_META + && event->data.meta.type == MIDI_META_END_OF_TRACK) + { + break; + } + } + + return true; +} + +// Free a track: + +static void FreeTrack(midi_track_t *track) +{ + unsigned int i; + + for (i=0; inum_events; ++i) + { + FreeEvent(&track->events[i]); + } + + free(track->events); +} + +static boolean ReadAllTracks(midi_file_t *file, FILE *stream) +{ + unsigned int i; + + // Allocate list of tracks and read each track: + + file->tracks = malloc(sizeof(midi_track_t) * file->num_tracks); + + if (file->tracks == NULL) + { + return false; + } + + memset(file->tracks, 0, sizeof(midi_track_t) * file->num_tracks); + + // Read each track: + + for (i=0; inum_tracks; ++i) + { + if (!ReadTrack(&file->tracks[i], stream)) + { + return false; + } + } + + return true; +} + +// Read and check the header chunk. + +static boolean ReadFileHeader(midi_file_t *file, FILE *stream) +{ + size_t records_read; + unsigned int format_type; + + records_read = fread(&file->header, sizeof(midi_header_t), 1, stream); + + if (records_read < 1) + { + return false; + } + + if (!CheckChunkHeader(&file->header.chunk_header, HEADER_CHUNK_ID) + || SDL_SwapBE32(file->header.chunk_header.chunk_size) != 6) + { + fprintf(stderr, "ReadFileHeader: Invalid MIDI chunk header! " + "chunk_size=%i\n", + SDL_SwapBE32(file->header.chunk_header.chunk_size)); + return false; + } + + format_type = SDL_SwapBE16(file->header.format_type); + file->num_tracks = SDL_SwapBE16(file->header.num_tracks); + + if ((format_type != 0 && format_type != 1) + || file->num_tracks < 1) + { + fprintf(stderr, "ReadFileHeader: Only type 0/1 " + "MIDI files supported!\n"); + return false; + } + + return true; +} + +void MIDI_FreeFile(midi_file_t *file) +{ + int i; + + if (file->tracks != NULL) + { + for (i=0; inum_tracks; ++i) + { + FreeTrack(&file->tracks[i]); + } + + free(file->tracks); + } + + free(file); +} + +midi_file_t *MIDI_OpenFile(char *filename) +{ + midi_file_t *file; + FILE *stream; + + file = malloc(sizeof(midi_file_t)); + + if (file == NULL) + { + return NULL; + } + + file->tracks = NULL; + file->num_tracks = 0; + file->buffer = NULL; + file->buffer_size = 0; + + // Open file + + stream = fopen(filename, "rb"); + + if (stream == NULL) + { + fprintf(stderr, "MIDI_OpenFile: Failed to open '%s'\n", filename); + MIDI_FreeFile(file); + return NULL; + } + + // Read MIDI file header + + if (!ReadFileHeader(file, stream)) + { + fclose(stream); + MIDI_FreeFile(file); + return NULL; + } + + // Read all tracks: + + if (!ReadAllTracks(file, stream)) + { + fclose(stream); + MIDI_FreeFile(file); + return NULL; + } + + fclose(stream); + + return file; +} + #ifdef TEST static char *MIDI_EventTypeToString(midi_event_type_t event_type) @@ -504,32 +633,20 @@ static char *MIDI_EventTypeToString(midi_event_type_t event_type) } } -int main(int argc, char *argv[]) +void PrintTrack(midi_track_t *track) { - midi_file_t *file; - midi_event_t event; - - if (argc < 2) - { - printf("Usage: %s \n", argv[0]); - exit(1); - } - - file = MIDI_OpenFile(argv[1]); + midi_event_t *event; + unsigned int i; - if (file == NULL) + for (i=0; inum_events; ++i) { - fprintf(stderr, "Failed to open %s\n", argv[1]); - exit(1); - } + event = &track->events[i]; - while (MIDI_ReadEvent(file, &event)) - { printf("Event type: %s (%i)\n", - MIDI_EventTypeToString(event.event_type), - event.event_type); + MIDI_EventTypeToString(event->event_type), + event->event_type); - switch(event.event_type) + switch(event->event_type) { case MIDI_EVENT_NOTE_OFF: case MIDI_EVENT_NOTE_ON: @@ -538,27 +655,48 @@ int main(int argc, char *argv[]) case MIDI_EVENT_PROGRAM_CHANGE: case MIDI_EVENT_CHAN_AFTERTOUCH: case MIDI_EVENT_PITCH_BEND: - printf("\tChannel: %i\n", event.data.channel.channel); - printf("\tParameter 1: %i\n", event.data.channel.param1); - printf("\tParameter 2: %i\n", event.data.channel.param2); + printf("\tChannel: %i\n", event->data.channel.channel); + printf("\tParameter 1: %i\n", event->data.channel.param1); + printf("\tParameter 2: %i\n", event->data.channel.param2); break; case MIDI_EVENT_SYSEX: case MIDI_EVENT_SYSEX_SPLIT: - printf("\tLength: %i\n", event.data.sysex.length); + printf("\tLength: %i\n", event->data.sysex.length); break; case MIDI_EVENT_META: - printf("\tMeta type: %i\n", event.data.meta.type); - printf("\tLength: %i\n", event.data.meta.length); + printf("\tMeta type: %i\n", event->data.meta.type); + printf("\tLength: %i\n", event->data.meta.length); break; } + } +} - if (event.event_type == MIDI_EVENT_META - && event.data.meta.type == MIDI_META_END_OF_TRACK) - { - break; - } +int main(int argc, char *argv[]) +{ + midi_file_t *file; + unsigned int i; + + if (argc < 2) + { + printf("Usage: %s \n", argv[0]); + exit(1); + } + + file = MIDI_OpenFile(argv[1]); + + if (file == NULL) + { + fprintf(stderr, "Failed to open %s\n", argv[1]); + exit(1); + } + + for (i=0; inum_tracks; ++i) + { + printf("\n== Track %i ==\n\n", i); + + PrintTrack(&file->tracks[i]); } return 0; diff --git a/src/midifile.h b/src/midifile.h index 7928fdda..16f911e7 100644 --- a/src/midifile.h +++ b/src/midifile.h @@ -130,7 +130,7 @@ typedef struct } midi_event_t; midi_file_t *MIDI_OpenFile(char *filename); -void MIDI_CloseFile(midi_file_t *file); +void MIDI_FreeFile(midi_file_t *file); #endif /* #ifndef MIDIFILE_H */ -- cgit v1.2.3 From d8ada5c41da3eec5ef163d9efac9cb5dbdc2cb6b Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Wed, 15 Apr 2009 19:00:25 +0000 Subject: Parse MIDI events that reuse the event type from the previous event. Subversion-branch: /branches/opl-branch Subversion-revision: 1499 --- src/midifile.c | 70 +++++++++++++++++++++++++++++++++++++++++----------------- src/midifile.h | 14 ++++++------ 2 files changed, 57 insertions(+), 27 deletions(-) diff --git a/src/midifile.c b/src/midifile.c index cca0189e..1be6ec75 100644 --- a/src/midifile.c +++ b/src/midifile.c @@ -198,8 +198,8 @@ static boolean ReadChannelEvent(midi_event_t *event, // Set basics: - event->event_type = event_type >> 4; - event->data.channel.channel = event_type & 0xf; + event->event_type = event_type & 0xf0; + event->data.channel.channel = event_type & 0x0f; // Read parameters: @@ -296,7 +296,8 @@ static boolean ReadMetaEvent(midi_event_t *event, FILE *stream) return true; } -static boolean ReadEvent(midi_event_t *event, FILE *stream) +static boolean ReadEvent(midi_event_t *event, unsigned int *last_event_type, + FILE *stream) { byte event_type; @@ -312,9 +313,29 @@ static boolean ReadEvent(midi_event_t *event, FILE *stream) return false; } + // All event types have their top bit set. Therefore, if + // the top bit is not set, it is because we are using the "same + // as previous event type" shortcut to save a byte. Skip back + // a byte so that we read this byte again. + + if ((event_type & 0x80) == 0) + { + event_type = *last_event_type; + + if (fseek(stream, -1, SEEK_CUR) < 0) + { + fprintf(stderr, "ReadEvent: Unable to seek in stream\n"); + return false; + } + } + else + { + *last_event_type = event_type; + } + // Check event type: - switch (event_type >> 4) + switch (event_type & 0xf0) { // Two parameter channel events: @@ -331,26 +352,27 @@ static boolean ReadEvent(midi_event_t *event, FILE *stream) case MIDI_EVENT_CHAN_AFTERTOUCH: return ReadChannelEvent(event, event_type, false, stream); - // Other event types: + default: + break; + } + + // Specific value? - case 0xf: - if (event_type == MIDI_EVENT_SYSEX - || event_type == MIDI_EVENT_SYSEX_SPLIT) - { - return ReadSysExEvent(event, event_type, stream); - } - else if (event_type == MIDI_EVENT_META) - { - return ReadMetaEvent(event, stream); - } + switch (event_type) + { + case MIDI_EVENT_SYSEX: + case MIDI_EVENT_SYSEX_SPLIT: + return ReadSysExEvent(event, event_type, stream); - // --- Fall-through deliberate --- - // Other 0xfx event types are unknown + case MIDI_EVENT_META: + return ReadMetaEvent(event, stream); default: - fprintf(stderr, "Unknown MIDI event type: 0x%x\n", event_type); - return false; + break; } + + fprintf(stderr, "ReadEvent: Unknown MIDI event type: 0x%x\n", event_type); + return false; } // Free an event: @@ -405,6 +427,7 @@ static boolean ReadTrack(midi_track_t *track, FILE *stream) { midi_event_t *new_events; midi_event_t *event; + unsigned int last_event_type; track->num_events = 0; track->events = NULL; @@ -418,6 +441,8 @@ static boolean ReadTrack(midi_track_t *track, FILE *stream) // Then the events: + last_event_type = 0; + for (;;) { // Resize the track slightly larger to hold another event: @@ -435,7 +460,7 @@ static boolean ReadTrack(midi_track_t *track, FILE *stream) // Read the next event: event = &track->events[track->num_events]; - if (!ReadEvent(event, stream)) + if (!ReadEvent(event, &last_event_type, stream)) { return false; } @@ -642,6 +667,11 @@ void PrintTrack(midi_track_t *track) { event = &track->events[i]; + if (event->delta_time > 0) + { + printf("Delay: %i ticks\n", event->delta_time); + } + printf("Event type: %s (%i)\n", MIDI_EventTypeToString(event->event_type), event->event_type); diff --git a/src/midifile.h b/src/midifile.h index 16f911e7..490b0171 100644 --- a/src/midifile.h +++ b/src/midifile.h @@ -30,13 +30,13 @@ typedef struct midi_file_s midi_file_t; typedef enum { - MIDI_EVENT_NOTE_OFF = 0x8, - MIDI_EVENT_NOTE_ON = 0x9, - MIDI_EVENT_AFTERTOUCH = 0xa, - MIDI_EVENT_CONTROLLER = 0xb, - MIDI_EVENT_PROGRAM_CHANGE = 0xc, - MIDI_EVENT_CHAN_AFTERTOUCH = 0xd, - MIDI_EVENT_PITCH_BEND = 0xe, + MIDI_EVENT_NOTE_OFF = 0x80, + MIDI_EVENT_NOTE_ON = 0x90, + MIDI_EVENT_AFTERTOUCH = 0xa0, + MIDI_EVENT_CONTROLLER = 0xb0, + MIDI_EVENT_PROGRAM_CHANGE = 0xc0, + MIDI_EVENT_CHAN_AFTERTOUCH = 0xd0, + MIDI_EVENT_PITCH_BEND = 0xe0, MIDI_EVENT_SYSEX = 0xf0, MIDI_EVENT_SYSEX_SPLIT = 0xf7, -- cgit v1.2.3 From 1d92cd1477d44245c151c1f39b15c356019d8359 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Wed, 27 May 2009 18:00:35 +0000 Subject: Oops. Subversion-branch: /branches/opl-branch Subversion-revision: 1533 --- opl/opl_internal.h | 48 ------------------------------------------------ 1 file changed, 48 deletions(-) diff --git a/opl/opl_internal.h b/opl/opl_internal.h index 964f55cc..7c1e8854 100644 --- a/opl/opl_internal.h +++ b/opl/opl_internal.h @@ -46,51 +46,3 @@ typedef struct #endif /* #ifndef OPL_INTERNAL_H */ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// Copyright(C) 2009 Simon Howard -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -// 02111-1307, USA. -// -// DESCRIPTION: -// OPL internal interface. -// -//----------------------------------------------------------------------------- - - -#ifndef OPL_INTERNAL_H -#define OPL_INTERNAL_H - -#include "opl.h" - -typedef int (*opl_init_func)(unsigned int port_base); -typedef void (*opl_shutdown_func)(void); -typedef unsigned int (*opl_read_port_func)(opl_port_t port); -typedef void (*opl_write_port_func)(opl_port_t port, unsigned int value); - -typedef struct -{ - char *name; - - opl_init_func init_func; - opl_shutdown_func shutdown_func; - opl_read_port_func read_port_func; - opl_write_port_func write_port_func; -} opl_driver_t; - -#endif /* #ifndef OPL_INTERNAL_H */ - -- cgit v1.2.3 From a4695f20fbaba6d4fc351edb78825328cd95f49f Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Wed, 27 May 2009 18:01:00 +0000 Subject: Add initial SDL driver. Subversion-branch: /branches/opl-branch Subversion-revision: 1534 --- opl/Makefile.am | 1 + opl/opl_sdl.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 opl/opl_sdl.c diff --git a/opl/Makefile.am b/opl/Makefile.am index e1dbe42c..d2870fc1 100644 --- a/opl/Makefile.am +++ b/opl/Makefile.am @@ -7,5 +7,6 @@ libopl_a_SOURCES = \ opl_internal.h \ opl.c opl.h \ opl_linux.c \ + opl_sdl.c \ fmopl.c fmopl.h diff --git a/opl/opl_sdl.c b/opl/opl_sdl.c new file mode 100644 index 00000000..a0d9d74e --- /dev/null +++ b/opl/opl_sdl.c @@ -0,0 +1,172 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// OPL SDL interface. +// +//----------------------------------------------------------------------------- + +#include "config.h" + +#include +#include +#include + +#include "SDL.h" +#include "SDL_mixer.h" + +#include "fmopl.h" + +#include "opl.h" +#include "opl_internal.h" + +// TODO: +#define opl_sample_rate 22050 + +static FM_OPL *opl_emulator = NULL; +static int sdl_was_initialised = 0; +static int mixing_freq, mixing_channels; +static Uint16 mixing_format; + +static int SDLIsInitialised(void) +{ + int freq, channels; + Uint16 format; + + return Mix_QuerySpec(&freq, &format, &channels); +} + +// Callback function to fill a new sound buffer: + +static void OPL_Mix_Callback(void *udata, Uint8 *stream, int len) +{ +} + +static void OPL_SDL_Shutdown(void) +{ + if (sdl_was_initialised) + { + Mix_CloseAudio(); + SDL_QuitSubSystem(SDL_INIT_AUDIO); + sdl_was_initialised = 0; + } + + if (opl_emulator != NULL) + { + OPLDestroy(opl_emulator); + opl_emulator = NULL; + } +} + +static int OPL_SDL_Init(unsigned int port_base) +{ + // Check if SDL_mixer has been opened already + // If not, we must initialise it now + + if (!SDLIsInitialised()) + { + if (SDL_Init(SDL_INIT_AUDIO) < 0) + { + fprintf(stderr, "Unable to set up sound.\n"); + return 0; + } + + if (Mix_OpenAudio(opl_sample_rate, AUDIO_S16SYS, 2, 1024) < 0) + { + fprintf(stderr, "Error initialising SDL_mixer: %s\n", Mix_GetError()); + + SDL_QuitSubSystem(SDL_INIT_AUDIO); + return 0; + } + + SDL_PauseAudio(0); + + // When this module shuts down, it has the responsibility to + // shut down SDL. + + sdl_was_initialised = 1; + } + else + { + sdl_was_initialised = 0; + } + + // Get the mixer frequency, format and number of channels. + + Mix_QuerySpec(&mixing_freq, &mixing_format, &mixing_channels); + + // Only supports AUDIO_S16SYS + + if (mixing_format != AUDIO_S16SYS || mixing_channels != 2) + { + fprintf(stderr, + "OPL_SDL only supports native signed 16-bit LSB, " + "stereo format!\n"); + + OPL_SDL_Shutdown(); + return 0; + } + + // Create the emulator structure: + + opl_emulator = makeAdlibOPL(mixing_freq); + + if (opl_emulator == NULL) + { + fprintf(stderr, "Failed to initialise software OPL emulator!\n"); + OPL_SDL_Shutdown(); + return 0; + } + + // TODO: This should be music callback? or-? + Mix_SetPostMix(OPL_Mix_Callback, NULL); + + return 1; +} + +static unsigned int OPL_SDL_PortRead(opl_port_t port) +{ + if (opl_emulator != NULL) + { + return OPLRead(opl_emulator, port); + } + else + { + return 0; + } +} + +static void OPL_SDL_PortWrite(opl_port_t port, unsigned int value) +{ + if (opl_emulator != NULL) + { + OPLWrite(opl_emulator, port, value); + } +} + +opl_driver_t opl_sdl_driver = +{ + "SDL", + OPL_SDL_Init, + OPL_SDL_Shutdown, + OPL_SDL_PortRead, + OPL_SDL_PortWrite +}; + -- cgit v1.2.3 From 223879d264009f4678803651f885214f84606cb7 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Thu, 28 May 2009 18:26:13 +0000 Subject: Add droplay example program from /research, adapted to work with OPL library. Subversion-branch: /branches/opl-branch Subversion-revision: 1535 --- configure.in | 3 +- opl/Makefile.am | 2 + opl/examples/Makefile.am | 8 +++ opl/examples/droplay.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 opl/examples/Makefile.am create mode 100644 opl/examples/droplay.c diff --git a/configure.in b/configure.in index 00dadb4e..3ee2e59e 100644 --- a/configure.in +++ b/configure.in @@ -90,8 +90,9 @@ textscreen/Makefile textscreen/examples/Makefile setup/Makefile man/Makefile -src/Makefile opl/Makefile +opl/examples/Makefile +src/Makefile pcsound/Makefile src/resource.rc src/doom-screensaver.desktop diff --git a/opl/Makefile.am b/opl/Makefile.am index d2870fc1..d4149773 100644 --- a/opl/Makefile.am +++ b/opl/Makefile.am @@ -1,6 +1,8 @@ AM_CFLAGS=@SDLMIXER_CFLAGS@ +SUBDIRS = . examples + noinst_LIBRARIES=libopl.a libopl_a_SOURCES = \ diff --git a/opl/examples/Makefile.am b/opl/examples/Makefile.am new file mode 100644 index 00000000..3dc07d46 --- /dev/null +++ b/opl/examples/Makefile.am @@ -0,0 +1,8 @@ + +AM_CFLAGS = -I.. + +noinst_PROGRAMS=droplay + +droplay_LDADD = ../libopl.a @LDFLAGS@ @SDL_LIBS@ +droplay_SOURCES = droplay.c + diff --git a/opl/examples/droplay.c b/opl/examples/droplay.c new file mode 100644 index 00000000..7cd595e9 --- /dev/null +++ b/opl/examples/droplay.c @@ -0,0 +1,177 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// Demonstration program for OPL library to play back DRO +// format files. +// +//----------------------------------------------------------------------------- + + +#include +#include + +#include "SDL.h" + +#include "opl.h" + +#define HEADER_STRING "DBRAWOPL" +#define ADLIB_PORT 0x388 + +void WriteReg(unsigned int reg, unsigned int val) +{ + int i; + + OPL_WritePort(OPL_REGISTER_PORT, reg); + + for (i=0; i<6; ++i) + { + OPL_ReadPort(OPL_REGISTER_PORT); + } + + OPL_WritePort(OPL_DATA_PORT, val); + + for (i=0; i<35; ++i) + { + OPL_ReadPort(OPL_REGISTER_PORT); + } +} + +void ClearAllRegs(void) +{ + int i; + + for (i=0; i<=0xff; ++i) + { + WriteReg(i, 0x00); + } +} + +// Detect an OPL chip. + +int DetectOPL(void) +{ + WriteReg(OPL_REG_TIMER_CTRL, 0x60); + WriteReg(OPL_REG_TIMER_CTRL, 0x80); + int val1 = OPL_ReadPort(OPL_REGISTER_PORT) & 0xe0; + WriteReg(OPL_REG_TIMER1, 0xff); + WriteReg(OPL_REG_TIMER_CTRL, 0x21); + SDL_Delay(50); + int val2 = OPL_ReadPort(OPL_REGISTER_PORT) & 0xe0; + WriteReg(OPL_REG_TIMER_CTRL, 0x60); + WriteReg(OPL_REG_TIMER_CTRL, 0x80); + + return val1 != 0 || val2 != 0xc0; +} + +void Init(void) +{ + if (SDL_Init(SDL_INIT_TIMER) < 0) + { + fprintf(stderr, "Unable to initialise SDL timer\n"); + exit(-1); + } + + if (!OPL_Init(ADLIB_PORT)) + { + fprintf(stderr, "Unable to initialise OPL layer\n"); + exit(-1); + } + + if (!DetectOPL()) + { + fprintf(stderr, "Adlib not detected\n"); + exit(-1); + } +} + +void Shutdown(void) +{ + OPL_Shutdown(); +} + +void PlayFile(char *filename) +{ + FILE *stream; + char buf[8]; + + stream = fopen(filename, "rb"); + + if (fread(buf, 1, 8, stream) < 8) + { + fprintf(stderr, "failed to read raw OPL header\n"); + exit(-1); + } + + if (strncmp(buf, HEADER_STRING, 8) != 0) + { + fprintf(stderr, "Raw OPL header not found\n"); + exit(-1); + } + + fseek(stream, 28, SEEK_SET); + + while (!feof(stream)) + { + int reg, val; + + reg = fgetc(stream); + val = fgetc(stream); + + // Delay? + + if (reg == 0x00) + { + SDL_Delay(val); + } + else if (reg == 0x01) + { + val |= (fgetc(stream) << 8); + SDL_Delay(val); + } + else + { + WriteReg(reg, val); + } + } + + fclose(stream); +} + +int main(int argc, char *argv[]) +{ + if (argc < 2) + { + printf("Usage: %s \n", argv[0]); + exit(-1); + } + + Init(); + ClearAllRegs(); + SDL_Delay(1000); + + PlayFile(argv[1]); + + ClearAllRegs(); + Shutdown(); + + return 0; +} + -- cgit v1.2.3 From b695843cc549e3de4845e340c611efb0ae98866c Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Thu, 28 May 2009 18:34:05 +0000 Subject: Fix OPL detect. Subversion-branch: /branches/opl-branch Subversion-revision: 1536 --- opl/examples/droplay.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opl/examples/droplay.c b/opl/examples/droplay.c index 7cd595e9..af1a59d9 100644 --- a/opl/examples/droplay.c +++ b/opl/examples/droplay.c @@ -78,7 +78,7 @@ int DetectOPL(void) WriteReg(OPL_REG_TIMER_CTRL, 0x60); WriteReg(OPL_REG_TIMER_CTRL, 0x80); - return val1 != 0 || val2 != 0xc0; + return val1 == 0 && val2 == 0xc0; } void Init(void) -- cgit v1.2.3 From 6e4f6ab9626d81e4106d3ccc974a76d832fdff13 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Thu, 28 May 2009 18:37:31 +0000 Subject: Set channel bits for OPL3 so that OPL2 traces will play back properly. Subversion-branch: /branches/opl-branch Subversion-revision: 1537 --- opl/examples/droplay.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/opl/examples/droplay.c b/opl/examples/droplay.c index af1a59d9..5f09fe11 100644 --- a/opl/examples/droplay.c +++ b/opl/examples/droplay.c @@ -39,6 +39,15 @@ void WriteReg(unsigned int reg, unsigned int val) { int i; + // This was recorded from an OPL2, but we are probably playing + // back on an OPL3, so we need to enable the original OPL2 + // channels. Doom does this already, but other games don't. + + if ((reg & 0xf0) == OPL_REGS_FEEDBACK) + { + val |= 0x30; + } + OPL_WritePort(OPL_REGISTER_PORT, reg); for (i=0; i<6; ++i) -- cgit v1.2.3 From 98ee23f4268dbb1395aa0b2cbfad9f53d1092b33 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 30 May 2009 23:24:11 +0000 Subject: Add initial callback/timer API. Subversion-branch: /branches/opl-branch Subversion-revision: 1538 --- opl/Makefile.am | 2 + opl/examples/.gitignore | 5 ++ opl/examples/droplay.c | 94 +++++++++++++++++------ opl/opl.c | 24 ++++++ opl/opl.h | 16 ++++ opl/opl_internal.h | 8 ++ opl/opl_linux.c | 18 ++++- opl/opl_queue.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++ opl/opl_queue.h | 44 +++++++++++ opl/opl_sdl.c | 25 +++++- opl/opl_timer.c | 197 ++++++++++++++++++++++++++++++++++++++++++++++++ opl/opl_timer.h | 40 ++++++++++ 12 files changed, 641 insertions(+), 24 deletions(-) create mode 100644 opl/examples/.gitignore create mode 100644 opl/opl_queue.c create mode 100644 opl/opl_queue.h create mode 100644 opl/opl_timer.c create mode 100644 opl/opl_timer.h diff --git a/opl/Makefile.am b/opl/Makefile.am index d4149773..d48b491b 100644 --- a/opl/Makefile.am +++ b/opl/Makefile.am @@ -10,5 +10,7 @@ libopl_a_SOURCES = \ opl.c opl.h \ opl_linux.c \ opl_sdl.c \ + opl_queue.c opl_queue.h \ + opl_timer.c opl_timer.h \ fmopl.c fmopl.h diff --git a/opl/examples/.gitignore b/opl/examples/.gitignore new file mode 100644 index 00000000..49bb1af8 --- /dev/null +++ b/opl/examples/.gitignore @@ -0,0 +1,5 @@ +Makefile.in +Makefile +.deps +droplay + diff --git a/opl/examples/droplay.c b/opl/examples/droplay.c index 5f09fe11..89cf6862 100644 --- a/opl/examples/droplay.c +++ b/opl/examples/droplay.c @@ -116,44 +116,51 @@ void Shutdown(void) OPL_Shutdown(); } -void PlayFile(char *filename) +struct timer_data { - FILE *stream; - char buf[8]; - - stream = fopen(filename, "rb"); + int running; + FILE *fstream; +}; - if (fread(buf, 1, 8, stream) < 8) - { - fprintf(stderr, "failed to read raw OPL header\n"); - exit(-1); - } +void TimerCallback(void *data) +{ + struct timer_data *timer_data = data; + int delay; - if (strncmp(buf, HEADER_STRING, 8) != 0) + if (!timer_data->running) { - fprintf(stderr, "Raw OPL header not found\n"); - exit(-1); + return; } - fseek(stream, 28, SEEK_SET); + // Read data until we must make a delay. - while (!feof(stream)) + for (;;) { int reg, val; - reg = fgetc(stream); - val = fgetc(stream); + // End of file? - // Delay? + if (feof(timer_data->fstream)) + { + timer_data->running = 0; + return; + } + + reg = fgetc(timer_data->fstream); + val = fgetc(timer_data->fstream); + + // Register value of 0 or 1 indicates a delay. if (reg == 0x00) { - SDL_Delay(val); + delay = val; + break; } else if (reg == 0x01) { - val |= (fgetc(stream) << 8); - SDL_Delay(val); + val |= (fgetc(timer_data->fstream) << 8); + delay = val; + break; } else { @@ -161,7 +168,50 @@ void PlayFile(char *filename) } } - fclose(stream); + // Schedule the next timer callback. + + OPL_SetCallback(delay, TimerCallback, timer_data); +} + +void PlayFile(char *filename) +{ + struct timer_data timer_data; + int running; + char buf[8]; + + timer_data.fstream = fopen(filename, "rb"); + + if (fread(buf, 1, 8, timer_data.fstream) < 8) + { + fprintf(stderr, "failed to read raw OPL header\n"); + exit(-1); + } + + if (strncmp(buf, HEADER_STRING, 8) != 0) + { + fprintf(stderr, "Raw OPL header not found\n"); + exit(-1); + } + + fseek(timer_data.fstream, 28, SEEK_SET); + timer_data.running = 1; + + // Start callback loop sequence. + + OPL_SetCallback(0, TimerCallback, &timer_data); + + // Sleep until the playback finishes. + + do + { + OPL_Lock(); + running = timer_data.running; + OPL_Unlock(); + + SDL_Delay(100); + } while (running); + + fclose(timer_data.fstream); } int main(int argc, char *argv[]) diff --git a/opl/opl.c b/opl/opl.c index 8f75241b..6c2d9c4f 100644 --- a/opl/opl.c +++ b/opl/opl.c @@ -105,3 +105,27 @@ unsigned int OPL_ReadPort(opl_port_t port) } } +void OPL_SetCallback(unsigned int ms, opl_callback_t callback, void *data) +{ + if (driver != NULL) + { + driver->set_callback_func(ms, callback, data); + } +} + +void OPL_Lock(void) +{ + if (driver != NULL) + { + driver->lock_func(); + } +} + +void OPL_Unlock(void) +{ + if (driver != NULL) + { + driver->unlock_func(); + } +} + diff --git a/opl/opl.h b/opl/opl.h index 352e6696..515950b1 100644 --- a/opl/opl.h +++ b/opl/opl.h @@ -27,6 +27,8 @@ #ifndef OPL_OPL_H #define OPL_OPL_H +typedef void (*opl_callback_t)(void *data); + typedef enum { OPL_REGISTER_PORT = 0, @@ -72,5 +74,19 @@ void OPL_WritePort(opl_port_t port, unsigned int value); unsigned int OPL_ReadPort(opl_port_t port); +// Set a timer callback. After the specified number of milliseconds +// have elapsed, the callback will be invoked. + +void OPL_SetCallback(unsigned int ms, opl_callback_t callback, void *data); + +// Begin critical section, during which, OPL callbacks will not be +// invoked. + +void OPL_Lock(void); + +// End critical section. + +void OPL_Unlock(void); + #endif diff --git a/opl/opl_internal.h b/opl/opl_internal.h index 7c1e8854..cd125122 100644 --- a/opl/opl_internal.h +++ b/opl/opl_internal.h @@ -33,6 +33,11 @@ typedef int (*opl_init_func)(unsigned int port_base); typedef void (*opl_shutdown_func)(void); typedef unsigned int (*opl_read_port_func)(opl_port_t port); typedef void (*opl_write_port_func)(opl_port_t port, unsigned int value); +typedef void (*opl_set_callback_func)(unsigned int ms, + opl_callback_t callback, + void *data); +typedef void (*opl_lock_func)(void); +typedef void (*opl_unlock_func)(void); typedef struct { @@ -42,6 +47,9 @@ typedef struct opl_shutdown_func shutdown_func; opl_read_port_func read_port_func; opl_write_port_func write_port_func; + opl_set_callback_func set_callback_func; + opl_lock_func lock_func; + opl_unlock_func unlock_func; } opl_driver_t; #endif /* #ifndef OPL_INTERNAL_H */ diff --git a/opl/opl_linux.c b/opl/opl_linux.c index d1d1e466..4a10337f 100644 --- a/opl/opl_linux.c +++ b/opl/opl_linux.c @@ -35,6 +35,7 @@ #include "opl.h" #include "opl_internal.h" +#include "opl_timer.h" static unsigned int opl_port_base; @@ -51,11 +52,23 @@ static int OPL_Linux_Init(unsigned int port_base) opl_port_base = port_base; + // Start callback thread + + if (!OPL_Timer_StartThread()) + { + ioperm(port_base, 2, 0); + return 0; + } + return 1; } static void OPL_Linux_Shutdown(void) { + // Stop callback thread + + OPL_Timer_StopThread(); + // Release permissions ioperm(opl_port_base, 2, 0); @@ -77,7 +90,10 @@ opl_driver_t opl_linux_driver = OPL_Linux_Init, OPL_Linux_Shutdown, OPL_Linux_PortRead, - OPL_Linux_PortWrite + OPL_Linux_PortWrite, + OPL_Timer_SetCallback, + OPL_Timer_Lock, + OPL_Timer_Unlock }; #endif /* #ifdef HAVE_IOPERM */ diff --git a/opl/opl_queue.c b/opl/opl_queue.c new file mode 100644 index 00000000..6639eee5 --- /dev/null +++ b/opl/opl_queue.c @@ -0,0 +1,192 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// Queue of waiting callbacks, stored in a binary min heap, so that we +// can always get the first callback. +// +//----------------------------------------------------------------------------- + +#include +#include +#include + +#include "opl_queue.h" + +#define MAX_OPL_QUEUE 64 + +typedef struct +{ + opl_callback_t callback; + void *data; + unsigned int time; +} opl_queue_entry_t; + +struct opl_callback_queue_s +{ + opl_queue_entry_t entries[MAX_OPL_QUEUE]; + unsigned int num_entries; +}; + +opl_callback_queue_t *OPL_Queue_Create(void) +{ + opl_callback_queue_t *queue; + + queue = malloc(sizeof(opl_callback_queue_t)); + queue->num_entries = 0; + + return queue; +} + +void OPL_Queue_Destroy(opl_callback_queue_t *queue) +{ + free(queue); +} + +int OPL_Queue_IsEmpty(opl_callback_queue_t *queue) +{ + return queue->num_entries == 0; +} + +void OPL_Queue_Push(opl_callback_queue_t *queue, + opl_callback_t callback, void *data, + unsigned int time) +{ + int entry_id; + + if (queue->num_entries >= MAX_OPL_QUEUE) + { + fprintf(stderr, "OPL_Queue_Push: Exceeded maximum callbacks\n"); + return; + } + + // Add to last queue entry. + + entry_id = queue->num_entries; + ++queue->num_entries; + + // Shift existing entries down in the heap. + + while (entry_id > 0 && time < queue->entries[entry_id / 2].time) + { + memcpy(&queue->entries[entry_id], + &queue->entries[entry_id / 2], + sizeof(opl_queue_entry_t)); + + entry_id /= 2; + } + + // Insert new callback data. + + queue->entries[entry_id].callback = callback; + queue->entries[entry_id].data = data; + queue->entries[entry_id].time = time; +} + +int OPL_Queue_Pop(opl_callback_queue_t *queue, + opl_callback_t *callback, void **data) +{ + opl_queue_entry_t *entry; + int child1, child2; + int i, next_i; + + // Empty? + + if (queue->num_entries <= 0) + { + return 0; + } + + // Store the result: + + *callback = queue->entries[0].callback; + *data = queue->entries[0].data; + + // Decrease the heap size, and keep pointer to the last entry in + // the heap, which must now be percolated down from the top. + + --queue->num_entries; + entry = &queue->entries[queue->num_entries]; + + // Percolate down. + + i = 0; + + for (;;) + { + child1 = i * 2 + 1; + child2 = i * 2 + 2; + + if (child1 < queue->num_entries + && queue->entries[child1].time < entry->time) + { + // Left child is less than entry. + // Use the minimum of left and right children. + + if (child2 < queue->num_entries + && queue->entries[child2].time < queue->entries[child1].time) + { + next_i = child2; + } + else + { + next_i = child1; + } + } + else if (child2 < queue->num_entries + && queue->entries[child2].time < entry->time) + { + // Right child is less than entry. Go down the right side. + + next_i = child2; + } + else + { + // Finished percolating. + break; + } + + // Percolate the next value up and advance. + + memcpy(&queue->entries[i], + &queue->entries[next_i], + sizeof(opl_queue_entry_t)); + i = next_i; + } + + // Store the old last-entry at its new position. + + memcpy(&queue->entries[i], entry, sizeof(opl_queue_entry_t)); + + return 1; +} + +unsigned int OPL_Queue_Peek(opl_callback_queue_t *queue) +{ + if (queue->num_entries > 0) + { + return queue->entries[0].time; + } + else + { + return 0; + } +} + diff --git a/opl/opl_queue.h b/opl/opl_queue.h new file mode 100644 index 00000000..6ead0010 --- /dev/null +++ b/opl/opl_queue.h @@ -0,0 +1,44 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// OPL callback queue. +// +//----------------------------------------------------------------------------- + +#ifndef OPL_QUEUE_H +#define OPL_QUEUE_H + +#include "opl.h" + +typedef struct opl_callback_queue_s opl_callback_queue_t; + +opl_callback_queue_t *OPL_Queue_Create(void); +int OPL_Queue_IsEmpty(opl_callback_queue_t *queue); +void OPL_Queue_Destroy(opl_callback_queue_t *queue); +void OPL_Queue_Push(opl_callback_queue_t *queue, + opl_callback_t callback, void *data, + unsigned int time); +int OPL_Queue_Pop(opl_callback_queue_t *queue, + opl_callback_t *callback, void **data); +unsigned int OPL_Queue_Peek(opl_callback_queue_t *queue); + +#endif /* #ifndef OPL_QUEUE_H */ + diff --git a/opl/opl_sdl.c b/opl/opl_sdl.c index a0d9d74e..2b8f5174 100644 --- a/opl/opl_sdl.c +++ b/opl/opl_sdl.c @@ -37,9 +37,12 @@ #include "opl.h" #include "opl_internal.h" +#include "opl_queue.h" + // TODO: #define opl_sample_rate 22050 +static opl_callback_queue_t *callback_queue; static FM_OPL *opl_emulator = NULL; static int sdl_was_initialised = 0; static int mixing_freq, mixing_channels; @@ -65,6 +68,7 @@ static void OPL_SDL_Shutdown(void) { Mix_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); + OPL_Queue_Destroy(callback_queue); sdl_was_initialised = 0; } @@ -82,6 +86,8 @@ static int OPL_SDL_Init(unsigned int port_base) if (!SDLIsInitialised()) { + callback_queue = OPL_Queue_Create(); + if (SDL_Init(SDL_INIT_AUDIO) < 0) { fprintf(stderr, "Unable to set up sound.\n"); @@ -161,12 +167,29 @@ static void OPL_SDL_PortWrite(opl_port_t port, unsigned int value) } } +static void OPL_SDL_SetCallback(unsigned int ms, + opl_callback_t callback, + void *data) +{ +} + +static void OPL_SDL_Lock(void) +{ +} + +static void OPL_SDL_Unlock(void) +{ +} + opl_driver_t opl_sdl_driver = { "SDL", OPL_SDL_Init, OPL_SDL_Shutdown, OPL_SDL_PortRead, - OPL_SDL_PortWrite + OPL_SDL_PortWrite, + OPL_SDL_SetCallback, + OPL_SDL_Lock, + OPL_SDL_Unlock }; diff --git a/opl/opl_timer.c b/opl/opl_timer.c new file mode 100644 index 00000000..519dbbd9 --- /dev/null +++ b/opl/opl_timer.c @@ -0,0 +1,197 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// OPL timer thread. +// Once started using OPL_Timer_StartThread, the thread sleeps, +// waking up to invoke callbacks set using OPL_Timer_SetCallback. +// +//----------------------------------------------------------------------------- + +#include "SDL.h" + +#include "opl_timer.h" +#include "opl_queue.h" + +typedef enum +{ + THREAD_STATE_STOPPED, + THREAD_STATE_RUNNING, + THREAD_STATE_STOPPING, +} thread_state_t; + +static SDL_Thread *timer_thread = NULL; +static thread_state_t timer_thread_state; +static int current_time; + +// Queue of callbacks waiting to be invoked. +// The callback queue mutex is held while the callback queue structure +// or current_time is being accessed. + +static opl_callback_queue_t *callback_queue; +static SDL_mutex *callback_queue_mutex; + +// The timer mutex is held while timer callback functions are being +// invoked, so that the calling code can prevent clashes. + +static SDL_mutex *timer_mutex; + +// Returns true if there is a callback at the head of the queue ready +// to be invoked. Otherwise, next_time is set to the time when the +// timer thread must wake up again to check. + +static int CallbackWaiting(unsigned int *next_time) +{ + // If there are no queued callbacks, sleep for 50ms at a time + // until a callback is added. + + if (OPL_Queue_IsEmpty(callback_queue)) + { + *next_time = current_time + 50; + return 0; + } + + // Read the time of the first callback in the queue. + // If the time for the callback has not yet arrived, + // we must sleep until the callback time. + + *next_time = OPL_Queue_Peek(callback_queue); + + return *next_time <= current_time; +} + +static unsigned int GetNextTime(void) +{ + opl_callback_t callback; + void *callback_data; + unsigned int next_time; + int have_callback; + + // Keep running through callbacks until there are none ready to + // run. When we run out of callbacks, next_time will be set. + + do + { + SDL_LockMutex(callback_queue_mutex); + + // Check if the callback at the head of the list is ready to + // be invoked. If so, pop from the head of the queue. + + have_callback = CallbackWaiting(&next_time); + + if (have_callback) + { + OPL_Queue_Pop(callback_queue, &callback, &callback_data); + } + + SDL_UnlockMutex(callback_queue_mutex); + + // Now invoke the callback, if we have one. + // The timer mutex is held while the callback is invoked. + + if (have_callback) + { + SDL_LockMutex(timer_mutex); + callback(callback_data); + SDL_UnlockMutex(timer_mutex); + } + } while (have_callback); + + return next_time; +} + +static int ThreadFunction(void *unused) +{ + unsigned int next_time; + unsigned int now; + + // Keep running until OPL_Timer_StopThread is called. + + while (timer_thread_state == THREAD_STATE_RUNNING) + { + // Get the next time that we must sleep until, and + // wait until that time. + + next_time = GetNextTime(); + now = SDL_GetTicks(); + + if (next_time > now) + { + SDL_Delay(next_time - now); + } + + // Update the current time. + + SDL_LockMutex(callback_queue_mutex); + current_time = next_time; + SDL_UnlockMutex(callback_queue_mutex); + } + + timer_thread_state = THREAD_STATE_STOPPED; + + return 0; +} + +int OPL_Timer_StartThread(void) +{ + timer_thread_state = THREAD_STATE_RUNNING; + timer_thread = SDL_CreateThread(ThreadFunction, NULL); + timer_mutex = SDL_CreateMutex(); + + callback_queue = OPL_Queue_Create(); + callback_queue_mutex = SDL_CreateMutex(); + current_time = SDL_GetTicks(); + + if (timer_thread == NULL) + { + timer_thread_state = THREAD_STATE_STOPPED; + return 0; + } + + return 1; +} + +void OPL_Timer_StopThread(void) +{ + timer_thread_state = THREAD_STATE_STOPPING; + + while (timer_thread_state != THREAD_STATE_STOPPED) + { + SDL_Delay(1); + } +} + +void OPL_Timer_SetCallback(unsigned int ms, opl_callback_t callback, void *data) +{ + SDL_LockMutex(callback_queue_mutex); + OPL_Queue_Push(callback_queue, callback, data, current_time + ms); + SDL_UnlockMutex(callback_queue_mutex); +} + +void OPL_Timer_Lock(void) +{ + SDL_LockMutex(timer_mutex); +} + +void OPL_Timer_Unlock(void) +{ + SDL_UnlockMutex(timer_mutex); +} + diff --git a/opl/opl_timer.h b/opl/opl_timer.h new file mode 100644 index 00000000..26255df5 --- /dev/null +++ b/opl/opl_timer.h @@ -0,0 +1,40 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// OPL timer thread. +// +//----------------------------------------------------------------------------- + +#ifndef OPL_TIMER_H +#define OPL_TIMER_H + +#include "opl.h" + +int OPL_Timer_StartThread(void); +void OPL_Timer_StopThread(void); +void OPL_Timer_SetCallback(unsigned int ms, + opl_callback_t callback, + void *data); +void OPL_Timer_Lock(void); +void OPL_Timer_Unlock(void); + +#endif /* #ifndef OPL_TIMER_H */ + -- cgit v1.2.3 From a09487cb98273aee3ee950965b49f59d3fc3554f Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 31 May 2009 23:20:17 +0000 Subject: Fix OPL callback queue. Subversion-branch: /branches/opl-branch Subversion-revision: 1539 --- opl/opl_queue.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/opl/opl_queue.c b/opl/opl_queue.c index 6639eee5..fe5f1ef8 100644 --- a/opl/opl_queue.c +++ b/opl/opl_queue.c @@ -70,6 +70,7 @@ void OPL_Queue_Push(opl_callback_queue_t *queue, unsigned int time) { int entry_id; + int parent_id; if (queue->num_entries >= MAX_OPL_QUEUE) { @@ -84,13 +85,26 @@ void OPL_Queue_Push(opl_callback_queue_t *queue, // Shift existing entries down in the heap. - while (entry_id > 0 && time < queue->entries[entry_id / 2].time) + while (entry_id > 0) { + parent_id = (entry_id - 1) / 2; + + // Is the heap condition satisfied? + + if (time >= queue->entries[parent_id].time) + { + break; + } + + // Move the existing entry down in the heap. + memcpy(&queue->entries[entry_id], - &queue->entries[entry_id / 2], + &queue->entries[parent_id], sizeof(opl_queue_entry_t)); - entry_id /= 2; + // Advance to the parent. + + entry_id = parent_id; } // Insert new callback data. @@ -190,3 +204,72 @@ unsigned int OPL_Queue_Peek(opl_callback_queue_t *queue) } } +#ifdef TEST + +#include + +static void PrintQueueNode(opl_callback_queue_t *queue, int node, int depth) +{ + int i; + + if (node >= queue->num_entries) + { + return; + } + + for (i=0; ientries[node].time); + + PrintQueueNode(queue, node * 2 + 1, depth + 1); + PrintQueueNode(queue, node * 2 + 2, depth + 1); +} + +static void PrintQueue(opl_callback_queue_t *queue) +{ + PrintQueueNode(queue, 0, 0); +} + +int main() +{ + opl_callback_queue_t *queue; + int iteration; + + queue = OPL_Queue_Create(); + + for (iteration=0; iteration<5000; ++iteration) + { + opl_callback_t callback; + void *data; + unsigned int time; + unsigned int newtime; + int i; + + for (i=0; i= time); + time = newtime; + } + + assert(OPL_Queue_IsEmpty(queue)); + assert(!OPL_Queue_Pop(queue, &callback, &data)); + } +} + +#endif + -- cgit v1.2.3 From 40ca9e8297ae8638c638f33b6ef5393ee88c7056 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Tue, 2 Jun 2009 00:36:52 +0000 Subject: Fix crash due to timer thread starting before resources allocated. Subversion-branch: /branches/opl-branch Subversion-revision: 1540 --- opl/opl_timer.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/opl/opl_timer.c b/opl/opl_timer.c index 519dbbd9..62ffbd37 100644 --- a/opl/opl_timer.c +++ b/opl/opl_timer.c @@ -149,19 +149,34 @@ static int ThreadFunction(void *unused) return 0; } -int OPL_Timer_StartThread(void) +static void InitResources(void) { - timer_thread_state = THREAD_STATE_RUNNING; - timer_thread = SDL_CreateThread(ThreadFunction, NULL); - timer_mutex = SDL_CreateMutex(); - callback_queue = OPL_Queue_Create(); + timer_mutex = SDL_CreateMutex(); callback_queue_mutex = SDL_CreateMutex(); +} + +static void FreeResources(void) +{ + OPL_Queue_Destroy(callback_queue); + SDL_DestroyMutex(callback_queue_mutex); + SDL_DestroyMutex(timer_mutex); +} + +int OPL_Timer_StartThread(void) +{ + InitResources(); + + timer_thread_state = THREAD_STATE_RUNNING; current_time = SDL_GetTicks(); + timer_thread = SDL_CreateThread(ThreadFunction, NULL); + if (timer_thread == NULL) { timer_thread_state = THREAD_STATE_STOPPED; + FreeResources(); + return 0; } @@ -176,6 +191,8 @@ void OPL_Timer_StopThread(void) { SDL_Delay(1); } + + FreeResources(); } void OPL_Timer_SetCallback(unsigned int ms, opl_callback_t callback, void *data) -- cgit v1.2.3 From b09cb29b9f573501206bd31e51f3014ec79d5f95 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Tue, 2 Jun 2009 01:08:20 +0000 Subject: Disable debug output. Subversion-branch: /branches/opl-branch Subversion-revision: 1541 --- opl/opl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opl/opl.c b/opl/opl.c index 6c2d9c4f..5fddf205 100644 --- a/opl/opl.c +++ b/opl/opl.c @@ -31,7 +31,7 @@ #include "opl.h" #include "opl_internal.h" -#define OPL_DEBUG_TRACE +//#define OPL_DEBUG_TRACE #ifdef HAVE_IOPERM extern opl_driver_t opl_linux_driver; -- cgit v1.2.3 From 37db69b86bc5052901e250578e9c3920886d79ff Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 11 Jul 2009 12:32:08 +0000 Subject: Remove entry in bug list about OPL music. Subversion-branch: /branches/opl-branch Subversion-revision: 1618 --- BUGS | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/BUGS b/BUGS index 5dd8a531..ee6bf09a 100644 --- a/BUGS +++ b/BUGS @@ -6,21 +6,6 @@ effects are cut off at the wrong distance. This needs further investigation. -* Music plays back differently. - - Vanilla Doom was typically played with a SoundBlaster (or compatible) - card. It programmed the registers for the OPL music chip directly - in order to emulate the various General MIDI instruments. However, - Chocolate Doom uses the OS's native MIDI playback interfaces to play - MIDI sound. As the OPL is programmed differently, the music sounds - different to the original, even when using an original SoundBlaster - card. - - This can be worked around in the future: OPL emulation code exists that - simulates an OPL chip in software. Furthermore, depending on the OS, - it may be possible to program the OPL directly in order to get the - same sound. - * A small number of Doom bugs are almost impossible to emulate. An example of this can be seen in Ledmeister's "Blackbug" demo which -- cgit v1.2.3 From e33a4961331301b1e3a5c65d148050fa33c4c594 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Fri, 28 Aug 2009 18:04:04 +0000 Subject: Working SDL OPL driver. Subversion-branch: /branches/opl-branch Subversion-revision: 1632 --- opl/examples/Makefile.am | 2 +- opl/examples/droplay.c | 10 ++- opl/opl.c | 58 +++++++++++++++ opl/opl.h | 4 ++ opl/opl_sdl.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 249 insertions(+), 7 deletions(-) diff --git a/opl/examples/Makefile.am b/opl/examples/Makefile.am index 3dc07d46..7c2c7c8a 100644 --- a/opl/examples/Makefile.am +++ b/opl/examples/Makefile.am @@ -3,6 +3,6 @@ AM_CFLAGS = -I.. noinst_PROGRAMS=droplay -droplay_LDADD = ../libopl.a @LDFLAGS@ @SDL_LIBS@ +droplay_LDADD = ../libopl.a @LDFLAGS@ @SDL_LIBS@ @SDLMIXER_LIBS@ droplay_SOURCES = droplay.c diff --git a/opl/examples/droplay.c b/opl/examples/droplay.c index 89cf6862..5158fbcd 100644 --- a/opl/examples/droplay.c +++ b/opl/examples/droplay.c @@ -77,16 +77,20 @@ void ClearAllRegs(void) int DetectOPL(void) { + int val1, val2; + WriteReg(OPL_REG_TIMER_CTRL, 0x60); WriteReg(OPL_REG_TIMER_CTRL, 0x80); - int val1 = OPL_ReadPort(OPL_REGISTER_PORT) & 0xe0; + val1 = OPL_ReadPort(OPL_REGISTER_PORT) & 0xe0; WriteReg(OPL_REG_TIMER1, 0xff); WriteReg(OPL_REG_TIMER_CTRL, 0x21); - SDL_Delay(50); - int val2 = OPL_ReadPort(OPL_REGISTER_PORT) & 0xe0; + OPL_Delay(50); + val2 = OPL_ReadPort(OPL_REGISTER_PORT) & 0xe0; WriteReg(OPL_REG_TIMER_CTRL, 0x60); WriteReg(OPL_REG_TIMER_CTRL, 0x80); +// Temporary hack for SDL driver. +return 1; return val1 == 0 && val2 == 0xc0; } diff --git a/opl/opl.c b/opl/opl.c index 5fddf205..e53b5d6e 100644 --- a/opl/opl.c +++ b/opl/opl.c @@ -28,6 +28,8 @@ #include #include +#include "SDL.h" + #include "opl.h" #include "opl_internal.h" @@ -36,12 +38,14 @@ #ifdef HAVE_IOPERM extern opl_driver_t opl_linux_driver; #endif +extern opl_driver_t opl_sdl_driver; static opl_driver_t *drivers[] = { #ifdef HAVE_IOPERM &opl_linux_driver, #endif + &opl_sdl_driver, NULL }; @@ -129,3 +133,57 @@ void OPL_Unlock(void) } } +typedef struct +{ + int finished; + + SDL_mutex *mutex; + SDL_cond *cond; +} delay_data_t; + +static void DelayCallback(void *_delay_data) +{ + delay_data_t *delay_data = _delay_data; + + SDL_LockMutex(delay_data->mutex); + delay_data->finished = 1; + SDL_UnlockMutex(delay_data->mutex); + + SDL_CondSignal(delay_data->cond); +} + +void OPL_Delay(unsigned int ms) +{ + delay_data_t delay_data; + + if (driver == NULL) + { + return; + } + + // Create a callback that will signal this thread after the + // specified time. + + delay_data.finished = 0; + delay_data.mutex = SDL_CreateMutex(); + delay_data.cond = SDL_CreateCond(); + + OPL_SetCallback(ms, DelayCallback, &delay_data); + + // Wait until the callback is invoked. + + SDL_LockMutex(delay_data.mutex); + + while (!delay_data.finished) + { + SDL_CondWait(delay_data.cond, delay_data.mutex); + } + + SDL_UnlockMutex(delay_data.mutex); + + // Clean up. + + SDL_DestroyMutex(delay_data.mutex); + SDL_DestroyCond(delay_data.cond); +} + diff --git a/opl/opl.h b/opl/opl.h index 515950b1..3413e3ab 100644 --- a/opl/opl.h +++ b/opl/opl.h @@ -88,5 +88,9 @@ void OPL_Lock(void); void OPL_Unlock(void); +// Block until the specified number of milliseconds have elapsed. + +void OPL_Delay(unsigned int ms); + #endif diff --git a/opl/opl_sdl.c b/opl/opl_sdl.c index 2b8f5174..849a10b0 100644 --- a/opl/opl_sdl.c +++ b/opl/opl_sdl.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "SDL.h" #include "SDL_mixer.h" @@ -42,8 +43,33 @@ // TODO: #define opl_sample_rate 22050 +// When the callback mutex is locked using OPL_Lock, callback functions +// are not invoked. + +static SDL_mutex *callback_mutex = NULL; + +// Queue of callbacks waiting to be invoked. + static opl_callback_queue_t *callback_queue; + +// Mutex used to control access to the callback queue. + +static SDL_mutex *callback_queue_mutex = NULL; + +// Current time, in number of samples since startup: + +static int current_time; + +// OPL software emulator structure. + static FM_OPL *opl_emulator = NULL; + +// Temporary mixing buffer used by the mixing callback. + +static int16_t *mix_buffer = NULL; + +// SDL parameters. + static int sdl_was_initialised = 0; static int mixing_freq, mixing_channels; static Uint16 mixing_format; @@ -56,10 +82,131 @@ static int SDLIsInitialised(void) return Mix_QuerySpec(&freq, &format, &channels); } +// Advance time by the specified number of samples, invoking any +// callback functions as appropriate. + +static void AdvanceTime(unsigned int nsamples) +{ + opl_callback_t callback; + void *callback_data; + + SDL_LockMutex(callback_queue_mutex); + + // Advance time. + + current_time += nsamples; + + // Are there callbacks to invoke now? Keep invoking them + // until there are none more left. + + while (!OPL_Queue_IsEmpty(callback_queue) + && current_time >= OPL_Queue_Peek(callback_queue)) + { + // Pop the callback from the queue to invoke it. + + if (!OPL_Queue_Pop(callback_queue, &callback, &callback_data)) + { + break; + } + + // The mutex stuff here is a bit complicated. We must + // hold callback_mutex when we invoke the callback (so that + // the control thread can use OPL_Lock() to prevent callbacks + // from being invoked), but we must not be holding + // callback_queue_mutex, as the callback must be able to + // call OPL_SetCallback to schedule new callbacks. + + SDL_UnlockMutex(callback_queue_mutex); + + SDL_LockMutex(callback_mutex); + callback(callback_data); + SDL_UnlockMutex(callback_mutex); + + SDL_LockMutex(callback_queue_mutex); + } + + SDL_UnlockMutex(callback_queue_mutex); +} + +// Call the OPL emulator code to fill the specified buffer. + +static void FillBuffer(int16_t *buffer, unsigned int nsamples) +{ + unsigned int i; + + // This seems like a reasonable assumption. mix_buffer is + // 1 second long, which should always be much longer than the + // SDL mix buffer. + + assert(nsamples < mixing_freq); + + YM3812UpdateOne(opl_emulator, mix_buffer, nsamples, 0); + + // Mix into the destination buffer, doubling up into stereo. + + for (i=0; i buffer_len - filled) + { + nsamples = buffer_len - filled; + } + } + + SDL_UnlockMutex(callback_queue_mutex); + + // Add emulator output to buffer. + + FillBuffer(buffer + filled * 2, nsamples); + filled += nsamples; + + // Invoke callbacks for this point in time. + + AdvanceTime(nsamples); + } } static void OPL_SDL_Shutdown(void) @@ -69,6 +216,7 @@ static void OPL_SDL_Shutdown(void) Mix_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); OPL_Queue_Destroy(callback_queue); + free(mix_buffer); sdl_was_initialised = 0; } @@ -77,6 +225,18 @@ static void OPL_SDL_Shutdown(void) OPLDestroy(opl_emulator); opl_emulator = NULL; } + + if (callback_mutex != NULL) + { + SDL_DestroyMutex(callback_mutex); + callback_mutex = NULL; + } + + if (callback_queue_mutex != NULL) + { + SDL_DestroyMutex(callback_queue_mutex); + callback_queue_mutex = NULL; + } } static int OPL_SDL_Init(unsigned int port_base) @@ -86,8 +246,6 @@ static int OPL_SDL_Init(unsigned int port_base) if (!SDLIsInitialised()) { - callback_queue = OPL_Queue_Create(); - if (SDL_Init(SDL_INIT_AUDIO) < 0) { fprintf(stderr, "Unable to set up sound.\n"); @@ -114,6 +272,11 @@ static int OPL_SDL_Init(unsigned int port_base) sdl_was_initialised = 0; } + // Queue structure of callbacks to invoke. + + callback_queue = OPL_Queue_Create(); + current_time = 0; + // Get the mixer frequency, format and number of channels. Mix_QuerySpec(&mixing_freq, &mixing_format, &mixing_channels); @@ -130,6 +293,10 @@ static int OPL_SDL_Init(unsigned int port_base) return 0; } + // Mix buffer: + + mix_buffer = malloc(mixing_freq * 2); + // Create the emulator structure: opl_emulator = makeAdlibOPL(mixing_freq); @@ -141,6 +308,9 @@ static int OPL_SDL_Init(unsigned int port_base) return 0; } + callback_mutex = SDL_CreateMutex(); + callback_queue_mutex = SDL_CreateMutex(); + // TODO: This should be music callback? or-? Mix_SetPostMix(OPL_Mix_Callback, NULL); @@ -171,14 +341,20 @@ static void OPL_SDL_SetCallback(unsigned int ms, opl_callback_t callback, void *data) { + SDL_LockMutex(callback_queue_mutex); + OPL_Queue_Push(callback_queue, callback, data, + current_time + (ms * mixing_freq) / 1000); + SDL_UnlockMutex(callback_queue_mutex); } static void OPL_SDL_Lock(void) { + SDL_LockMutex(callback_mutex); } static void OPL_SDL_Unlock(void) { + SDL_UnlockMutex(callback_mutex); } opl_driver_t opl_sdl_driver = -- cgit v1.2.3 From ca065a06caac9ba5fab3eb8b1f49d529755506db Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 29 Aug 2009 20:08:21 +0000 Subject: Set timer callback for OPL emulator so that the adlib detection routine works. Subversion-branch: /branches/opl-branch Subversion-revision: 1633 --- opl/examples/droplay.c | 2 -- opl/opl_sdl.c | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/opl/examples/droplay.c b/opl/examples/droplay.c index 5158fbcd..b1656815 100644 --- a/opl/examples/droplay.c +++ b/opl/examples/droplay.c @@ -89,8 +89,6 @@ int DetectOPL(void) WriteReg(OPL_REG_TIMER_CTRL, 0x60); WriteReg(OPL_REG_TIMER_CTRL, 0x80); -// Temporary hack for SDL driver. -return 1; return val1 == 0 && val2 == 0xc0; } diff --git a/opl/opl_sdl.c b/opl/opl_sdl.c index 849a10b0..42fe3347 100644 --- a/opl/opl_sdl.c +++ b/opl/opl_sdl.c @@ -239,6 +239,29 @@ static void OPL_SDL_Shutdown(void) } } +// Callback when a timer expires. + +static void TimerOver(void *data) +{ + int channel = (int) data; + + OPLTimerOver(opl_emulator, channel); +} + +// Callback invoked when the emulator code wants to set a timer. + +static void TimerHandler(int channel, double interval_seconds) +{ + unsigned int interval_samples; + + interval_samples = (int) (interval_seconds * mixing_freq); + + SDL_LockMutex(callback_queue_mutex); + OPL_Queue_Push(callback_queue, TimerOver, (void *) channel, + current_time + interval_samples); + SDL_UnlockMutex(callback_queue_mutex); +} + static int OPL_SDL_Init(unsigned int port_base) { // Check if SDL_mixer has been opened already @@ -308,6 +331,8 @@ static int OPL_SDL_Init(unsigned int port_base) return 0; } + OPLSetTimerHandler(opl_emulator, TimerHandler, 0); + callback_mutex = SDL_CreateMutex(); callback_queue_mutex = SDL_CreateMutex(); -- cgit v1.2.3 From e7262d2a8859501152ef22bc7cb9dcc26b05c402 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 29 Aug 2009 20:12:49 +0000 Subject: Fix crash when specifying an invalid filename. Subversion-branch: /branches/opl-branch Subversion-revision: 1634 --- opl/examples/droplay.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/opl/examples/droplay.c b/opl/examples/droplay.c index b1656815..4c2fc2f8 100644 --- a/opl/examples/droplay.c +++ b/opl/examples/droplay.c @@ -183,6 +183,12 @@ void PlayFile(char *filename) timer_data.fstream = fopen(filename, "rb"); + if (timer_data.fstream == NULL) + { + fprintf(stderr, "Failed to open %s\n", filename); + exit(-1); + } + if (fread(buf, 1, 8, timer_data.fstream) < 8) { fprintf(stderr, "failed to read raw OPL header\n"); @@ -226,7 +232,6 @@ int main(int argc, char *argv[]) Init(); ClearAllRegs(); - SDL_Delay(1000); PlayFile(argv[1]); -- cgit v1.2.3 From 6cae0f05cfedce28038f9411f03e6b1415250c33 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 29 Aug 2009 21:03:24 +0000 Subject: Don't crash if OPL is shutdown after SDL was initialised. Subversion-branch: /branches/opl-branch Subversion-revision: 1635 --- opl/opl_sdl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/opl/opl_sdl.c b/opl/opl_sdl.c index 42fe3347..b9d83385 100644 --- a/opl/opl_sdl.c +++ b/opl/opl_sdl.c @@ -211,6 +211,8 @@ static void OPL_Mix_Callback(void *udata, static void OPL_SDL_Shutdown(void) { + Mix_SetPostMix(NULL, NULL); + if (sdl_was_initialised) { Mix_CloseAudio(); -- cgit v1.2.3 From 324c1d8776054394d30ea987f84c1ba2f1b1ff6f Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 29 Aug 2009 21:05:36 +0000 Subject: Rename MIDI_OpenFile to MIDI_LoadFile, remove unneeded structure packing. Subversion-branch: /branches/opl-branch Subversion-revision: 1636 --- src/midifile.c | 4 ++-- src/midifile.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/midifile.c b/src/midifile.c index 1be6ec75..6eb9e7db 100644 --- a/src/midifile.c +++ b/src/midifile.c @@ -60,7 +60,7 @@ typedef struct midi_event_t *events; int num_events; -} PACKEDATTR midi_track_t; +} midi_track_t; struct midi_file_s { @@ -575,7 +575,7 @@ void MIDI_FreeFile(midi_file_t *file) free(file); } -midi_file_t *MIDI_OpenFile(char *filename) +midi_file_t *MIDI_LoadFile(char *filename) { midi_file_t *file; FILE *stream; diff --git a/src/midifile.h b/src/midifile.h index 490b0171..f9acea35 100644 --- a/src/midifile.h +++ b/src/midifile.h @@ -129,7 +129,7 @@ typedef struct } data; } midi_event_t; -midi_file_t *MIDI_OpenFile(char *filename); +midi_file_t *MIDI_LoadFile(char *filename); void MIDI_FreeFile(midi_file_t *file); #endif /* #ifndef MIDIFILE_H */ -- cgit v1.2.3 From 9cc843c60027cb9365f3eaae3028343769d17a26 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 29 Aug 2009 21:22:32 +0000 Subject: Load MIDI file. Subversion-branch: /branches/opl-branch Subversion-revision: 1637 --- src/i_oplmusic.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index f50c3322..c444dfd8 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -40,6 +40,7 @@ #include "z_zone.h" #include "opl.h" +#include "midifile.h" #define MAXMIDLENGTH (96 * 1024) #define GENMIDI_NUM_INSTRS 128 @@ -507,6 +508,11 @@ static void I_OPL_UnRegisterSong(void *handle) { return; } + + if (handle != NULL) + { + MIDI_FreeFile(handle); + } } // Determine whether memory block is a .mid file @@ -544,6 +550,7 @@ static boolean ConvertMus(byte *musdata, int len, char *filename) static void *I_OPL_RegisterSong(void *data, int len) { + midi_file_t *result; char *filename; if (!music_initialised) @@ -567,7 +574,12 @@ static void *I_OPL_RegisterSong(void *data, int len) ConvertMus(data, len, filename); } - // .... + result = MIDI_LoadFile(filename); + + if (result == NULL) + { + fprintf(stderr, "I_OPL_RegisterSong: Failed to load MID.\n"); + } // remove file now @@ -575,7 +587,7 @@ static void *I_OPL_RegisterSong(void *data, int len) Z_Free(filename); - return NULL; + return result; } // Is the song playing? -- cgit v1.2.3 From a10180a460f6425cd308719584aa58ab4fcb63fb Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 29 Aug 2009 21:26:43 +0000 Subject: Use OPL_Delay to wait 1ms for timer to expire when doing OPL detect. Subversion-branch: /branches/opl-branch Subversion-revision: 1638 --- opl/examples/droplay.c | 7 ++++++- src/i_oplmusic.c | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/opl/examples/droplay.c b/opl/examples/droplay.c index 4c2fc2f8..d53a427b 100644 --- a/opl/examples/droplay.c +++ b/opl/examples/droplay.c @@ -81,11 +81,16 @@ int DetectOPL(void) WriteReg(OPL_REG_TIMER_CTRL, 0x60); WriteReg(OPL_REG_TIMER_CTRL, 0x80); + val1 = OPL_ReadPort(OPL_REGISTER_PORT) & 0xe0; + WriteReg(OPL_REG_TIMER1, 0xff); WriteReg(OPL_REG_TIMER_CTRL, 0x21); - OPL_Delay(50); + + OPL_Delay(1); + val2 = OPL_ReadPort(OPL_REGISTER_PORT) & 0xe0; + WriteReg(OPL_REG_TIMER_CTRL, 0x60); WriteReg(OPL_REG_TIMER_CTRL, 0x80); diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index c444dfd8..0e1b69a3 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -202,6 +202,8 @@ static boolean DetectOPL(void) GetStatus(); } + OPL_Delay(1); + // Read status result2 = GetStatus(); -- cgit v1.2.3 From f2f36117c889c19b643058a0af33070baf4b48be Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 29 Aug 2009 21:30:26 +0000 Subject: MIDI_OpenFile -> MIDI_LoadFile. Subversion-branch: /branches/opl-branch Subversion-revision: 1639 --- src/midifile.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/midifile.c b/src/midifile.c index 6eb9e7db..61e06463 100644 --- a/src/midifile.c +++ b/src/midifile.c @@ -598,7 +598,7 @@ midi_file_t *MIDI_LoadFile(char *filename) if (stream == NULL) { - fprintf(stderr, "MIDI_OpenFile: Failed to open '%s'\n", filename); + fprintf(stderr, "MIDI_LoadFile: Failed to open '%s'\n", filename); MIDI_FreeFile(file); return NULL; } @@ -714,7 +714,7 @@ int main(int argc, char *argv[]) exit(1); } - file = MIDI_OpenFile(argv[1]); + file = MIDI_LoadFile(argv[1]); if (file == NULL) { -- cgit v1.2.3 From f6ce7dfea99cf32beb2afc8e7b02fb5f19f7544f Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 30 Aug 2009 01:56:33 +0000 Subject: Make some noise. Subversion-branch: /branches/opl-branch Subversion-revision: 1640 --- src/i_oplmusic.c | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 0e1b69a3..2c0efa92 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -42,6 +42,8 @@ #include "opl.h" #include "midifile.h" +#define TEST + #define MAXMIDLENGTH (96 * 1024) #define GENMIDI_NUM_INSTRS 128 @@ -405,6 +407,30 @@ static void I_OPL_ShutdownMusic(void) } } +#ifdef TEST +static void TestCallback(void *arg) +{ + opl_voice_t *voice = arg; + int note; + int wait_time; + + // Set level: + WriteRegister(OPL_REGS_LEVEL + voice->op2, 0); + + // Note off: + + WriteRegister(OPL_REGS_FREQ_2 + voice->index, 0x00); + // Note on: + + note = (rand() % (0x2ae - 0x16b)) + 0x16b; + WriteRegister(OPL_REGS_FREQ_1 + voice->index, note & 0xff); + WriteRegister(OPL_REGS_FREQ_2 + voice->index, 0x30 + (note >> 8)); + + wait_time = (rand() % 700) + 50; + OPL_SetCallback(wait_time, TestCallback, arg); +} +#endif + // Initialise music subsystem static boolean I_OPL_InitMusic(void) @@ -443,17 +469,19 @@ static boolean I_OPL_InitMusic(void) #ifdef TEST { + int i; opl_voice_t *voice; + int instr_num; - voice = GetFreeVoice(); - SetVoiceInstrument(voice, &main_instrs[34].opl2_voice); + for (i=0; i<3; ++i) + { + voice = GetFreeVoice(); + instr_num = rand() % 100; - // Set level: - WriteRegister(OPL_REGS_LEVEL + voice->op2, 0x94); + SetVoiceInstrument(voice, &main_instrs[instr_num].opl2_voice); - // Note on: - WriteRegister(OPL_REGS_FREQ_1 + voice->index, 0x65); - WriteRegister(OPL_REGS_FREQ_2 + voice->index, 0x2b); + OPL_SetCallback(0, TestCallback, voice); + } } #endif -- cgit v1.2.3 From 4d13a1330013cfcf70c8bc13f6183ea404bbdbe4 Mon Sep 17 00:00:00 2001 From: Russell Rice Date: Sun, 30 Aug 2009 04:31:50 +0000 Subject: - Add OPL to codeblocks project Subversion-branch: /branches/opl-branch Subversion-revision: 1641 --- codeblocks/game.cbp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/codeblocks/game.cbp b/codeblocks/game.cbp index bf1c249a..ab6350eb 100644 --- a/codeblocks/game.cbp +++ b/codeblocks/game.cbp @@ -46,6 +46,7 @@ + @@ -55,6 +56,29 @@ + + + + + + + + + + + + + + + + + @@ -165,6 +189,9 @@ + + @@ -235,6 +262,10 @@ + + + -- cgit v1.2.3 From 076adeb0aa24e9bdc677435ddb6e444db58d5436 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 30 Aug 2009 12:07:04 +0000 Subject: Add method to clear all existing callbacks. Subversion-branch: /branches/opl-branch Subversion-revision: 1642 --- opl/opl.c | 8 ++++++++ opl/opl.h | 4 ++++ opl/opl_internal.h | 2 ++ opl/opl_linux.c | 1 + opl/opl_queue.c | 5 +++++ opl/opl_queue.h | 1 + opl/opl_sdl.c | 8 ++++++++ opl/opl_timer.c | 7 +++++++ opl/opl_timer.h | 1 + 9 files changed, 37 insertions(+) diff --git a/opl/opl.c b/opl/opl.c index e53b5d6e..6e75c951 100644 --- a/opl/opl.c +++ b/opl/opl.c @@ -117,6 +117,14 @@ void OPL_SetCallback(unsigned int ms, opl_callback_t callback, void *data) } } +void OPL_ClearCallbacks(void) +{ + if (driver != NULL) + { + driver->clear_callbacks_func(); + } +} + void OPL_Lock(void) { if (driver != NULL) diff --git a/opl/opl.h b/opl/opl.h index 3413e3ab..a0998404 100644 --- a/opl/opl.h +++ b/opl/opl.h @@ -79,6 +79,10 @@ unsigned int OPL_ReadPort(opl_port_t port); void OPL_SetCallback(unsigned int ms, opl_callback_t callback, void *data); +// Clear all OPL callbacks that have been set. + +void OPL_ClearCallbacks(void); + // Begin critical section, during which, OPL callbacks will not be // invoked. diff --git a/opl/opl_internal.h b/opl/opl_internal.h index cd125122..384b96f8 100644 --- a/opl/opl_internal.h +++ b/opl/opl_internal.h @@ -36,6 +36,7 @@ typedef void (*opl_write_port_func)(opl_port_t port, unsigned int value); typedef void (*opl_set_callback_func)(unsigned int ms, opl_callback_t callback, void *data); +typedef void (*opl_clear_callbacks_func)(void); typedef void (*opl_lock_func)(void); typedef void (*opl_unlock_func)(void); @@ -48,6 +49,7 @@ typedef struct opl_read_port_func read_port_func; opl_write_port_func write_port_func; opl_set_callback_func set_callback_func; + opl_clear_callbacks_func clear_callbacks_func; opl_lock_func lock_func; opl_unlock_func unlock_func; } opl_driver_t; diff --git a/opl/opl_linux.c b/opl/opl_linux.c index 4a10337f..8a61dbf7 100644 --- a/opl/opl_linux.c +++ b/opl/opl_linux.c @@ -92,6 +92,7 @@ opl_driver_t opl_linux_driver = OPL_Linux_PortRead, OPL_Linux_PortWrite, OPL_Timer_SetCallback, + OPL_Timer_ClearCallbacks, OPL_Timer_Lock, OPL_Timer_Unlock }; diff --git a/opl/opl_queue.c b/opl/opl_queue.c index fe5f1ef8..f9d4c377 100644 --- a/opl/opl_queue.c +++ b/opl/opl_queue.c @@ -65,6 +65,11 @@ int OPL_Queue_IsEmpty(opl_callback_queue_t *queue) return queue->num_entries == 0; } +void OPL_Queue_Clear(opl_callback_queue_t *queue) +{ + queue->num_entries = 0; +} + void OPL_Queue_Push(opl_callback_queue_t *queue, opl_callback_t callback, void *data, unsigned int time) diff --git a/opl/opl_queue.h b/opl/opl_queue.h index 6ead0010..2447702b 100644 --- a/opl/opl_queue.h +++ b/opl/opl_queue.h @@ -32,6 +32,7 @@ typedef struct opl_callback_queue_s opl_callback_queue_t; opl_callback_queue_t *OPL_Queue_Create(void); int OPL_Queue_IsEmpty(opl_callback_queue_t *queue); +void OPL_Queue_Clear(opl_callback_queue_t *queue); void OPL_Queue_Destroy(opl_callback_queue_t *queue); void OPL_Queue_Push(opl_callback_queue_t *queue, opl_callback_t callback, void *data, diff --git a/opl/opl_sdl.c b/opl/opl_sdl.c index b9d83385..ffbe6820 100644 --- a/opl/opl_sdl.c +++ b/opl/opl_sdl.c @@ -374,6 +374,13 @@ static void OPL_SDL_SetCallback(unsigned int ms, SDL_UnlockMutex(callback_queue_mutex); } +static void OPL_SDL_ClearCallbacks(void) +{ + SDL_LockMutex(callback_queue_mutex); + OPL_Queue_Clear(callback_queue); + SDL_UnlockMutex(callback_queue_mutex); +} + static void OPL_SDL_Lock(void) { SDL_LockMutex(callback_mutex); @@ -392,6 +399,7 @@ opl_driver_t opl_sdl_driver = OPL_SDL_PortRead, OPL_SDL_PortWrite, OPL_SDL_SetCallback, + OPL_SDL_ClearCallbacks, OPL_SDL_Lock, OPL_SDL_Unlock }; diff --git a/opl/opl_timer.c b/opl/opl_timer.c index 62ffbd37..e254a5e2 100644 --- a/opl/opl_timer.c +++ b/opl/opl_timer.c @@ -202,6 +202,13 @@ void OPL_Timer_SetCallback(unsigned int ms, opl_callback_t callback, void *data) SDL_UnlockMutex(callback_queue_mutex); } +void OPL_Timer_ClearCallbacks(void) +{ + SDL_LockMutex(callback_queue_mutex); + OPL_Queue_Clear(callback_queue); + SDL_UnlockMutex(callback_queue_mutex); +} + void OPL_Timer_Lock(void) { SDL_LockMutex(timer_mutex); diff --git a/opl/opl_timer.h b/opl/opl_timer.h index 26255df5..e8657a90 100644 --- a/opl/opl_timer.h +++ b/opl/opl_timer.h @@ -33,6 +33,7 @@ void OPL_Timer_StopThread(void); void OPL_Timer_SetCallback(unsigned int ms, opl_callback_t callback, void *data); +void OPL_Timer_ClearCallbacks(void); void OPL_Timer_Lock(void); void OPL_Timer_Unlock(void); -- cgit v1.2.3 From ca23c95db8053b82fb817241e5de83a881f931e5 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 30 Aug 2009 21:34:12 +0000 Subject: Initial/basic MIDI track playback. Subversion-branch: /branches/opl-branch Subversion-revision: 1643 --- src/i_oplmusic.c | 328 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/midifile.c | 69 ++++++++++++ src/midifile.h | 28 +++++ 3 files changed, 423 insertions(+), 2 deletions(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 2c0efa92..3d9a2649 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -42,7 +42,7 @@ #include "opl.h" #include "midifile.h" -#define TEST +//#define TEST #define MAXMIDLENGTH (96 * 1024) #define GENMIDI_NUM_INSTRS 128 @@ -80,6 +80,37 @@ typedef struct genmidi_voice_t opl3_voice; } PACKEDATTR genmidi_instr_t; +// Data associated with a channel of a track that is currently playing. + +typedef struct +{ + // The instrument currently used for this track. + + genmidi_instr_t *instrument; + + // Volume level + + int volume; +} opl_channel_data_t; + +// Data associated with a track that is currently playing. + +typedef struct +{ + // Data for each channel. + + opl_channel_data_t channels[MIDI_CHANNELS_PER_TRACK]; + + // Track iterator used to read new events. + + midi_track_iter_t *iter; + + // Tempo control variables + + unsigned int ticks_per_beat; + unsigned int us_per_beat; +} opl_track_data_t; + typedef struct opl_voice_s opl_voice_t; struct opl_voice_s @@ -93,6 +124,15 @@ struct opl_voice_s // Currently-loaded instrument data genmidi_instr_t *current_instr; + // The channel currently using this voice. + opl_channel_data_t *channel; + + // The note that this voice is playing. + unsigned int note; + + // The frequency value being used. + unsigned int freq; + // Next in freelist opl_voice_t *next; }; @@ -104,6 +144,22 @@ static const int voice_operators[2][OPL_NUM_VOICES] = { { 0x03, 0x04, 0x05, 0x0b, 0x0c, 0x0d, 0x13, 0x14, 0x15 } }; +// Frequency values to use for each note. + +static const unsigned int note_frequencies[] = { + + // These frequencies are only used for the first seven + // MIDI note values: + + 0x158, 0x16d, 0x183, 0x19a, 0x1b2, 0x1cc, 0x1e7, + + // These frequencies are used repeatedly, cycling around + // for each octave: + + 0x204, 0x223, 0x244, 0x266, 0x28b, 0x2b1, + 0x2da, 0x306, 0x334, 0x365, 0x398, 0x3cf, +}; + static boolean music_initialised = false; //static boolean musicpaused = false; @@ -119,6 +175,10 @@ static genmidi_instr_t *percussion_instrs; static opl_voice_t voices[OPL_NUM_VOICES]; static opl_voice_t *voice_free_list; +// Track data for playing tracks: + +static opl_track_data_t *tracks; + // In the initialisation stage, register writes are spaced by reading // from the register port (0). After initialisation, spacing is // peformed by reading from the data port instead. I have no idea @@ -310,6 +370,9 @@ static void ReleaseVoice(opl_voice_t *voice) { opl_voice_t **rover; + voice->channel = NULL; + voice->note = 0; + // Search to the end of the freelist (This is how Doom behaves!) rover = &voice_free_list; @@ -320,6 +383,7 @@ static void ReleaseVoice(opl_voice_t *voice) } *rover = voice; + voice->next = NULL; } // Load data to the specified operator @@ -498,14 +562,274 @@ static void I_OPL_SetMusicVolume(int volume) current_music_volume = volume; } +static opl_voice_t *FindVoiceForNote(opl_channel_data_t *channel, int note) +{ + unsigned int i; + + for (i=0; idata.channel.channel, + event->data.channel.param1, + event->data.channel.param2); + + channel = &track->channels[event->data.channel.channel]; + + // Find the voice being used to play the note. + + voice = FindVoiceForNote(channel, event->data.channel.param1); + + if (voice == NULL) + { + return; + } + + // Note off. + + WriteRegister(OPL_REGS_FREQ_2 + voice->index, voice->freq >> 8); + + // Finished with this voice now. + + ReleaseVoice(voice); +} + +// Given a MIDI note number, get the corresponding OPL +// frequency value to use. + +static unsigned int FrequencyForNote(unsigned int note) +{ + unsigned int octave; + unsigned int key_num; + + // The first seven frequencies in the frequencies array are used + // only for the first seven MIDI notes. After this, the frequency + // value loops around the same twelve notes, increasing the + // octave. + + if (note < 7) + { + return note_frequencies[note]; + } + else + { + octave = (note - 7) / 12; + key_num = (note - 7) % 12; + + return note_frequencies[key_num + 7] | (octave << 10); + } +} + +static void NoteOnEvent(opl_track_data_t *track, midi_event_t *event) +{ + opl_voice_t *voice; + opl_channel_data_t *channel; + + printf("note on: channel %i, %i, %i\n", + event->data.channel.channel, + event->data.channel.param1, + event->data.channel.param2); + + // The channel. + + channel = &track->channels[event->data.channel.channel]; + + // Find a voice to use for this new note. + + voice = GetFreeVoice(); + + if (voice == NULL) + { + return; + } + + // Program the voice with the instrument data: + + SetVoiceInstrument(voice, &channel->instrument->opl2_voice); + + // TODO: Set the volume level. + + WriteRegister(OPL_REGS_LEVEL + voice->op2, 0); + + // Play the note. + + voice->channel = channel; + voice->note = event->data.channel.param1; + + // Write the frequency value to turn the note on. + + voice->freq = FrequencyForNote(voice->note); + + WriteRegister(OPL_REGS_FREQ_1 + voice->index, voice->freq & 0xff); + WriteRegister(OPL_REGS_FREQ_2 + voice->index, (voice->freq >> 8) | 0x20); +} + +static void ProgramChangeEvent(opl_track_data_t *track, midi_event_t *event) +{ + int channel; + int instrument; + + // Set the instrument used on this channel. + + channel = event->data.channel.channel; + instrument = event->data.channel.param1; + track->channels[channel].instrument = &main_instrs[instrument]; + + // TODO: Look through existing voices that are turned on on this + // channel, and change the instrument. +} + +static void ControllerEvent(opl_track_data_t *track, midi_event_t *event) +{ + printf("change controller: channel %i, %i, %i\n", + event->data.channel.channel, + event->data.channel.param1, + event->data.channel.param2); + + // TODO: Volume, pan. +} + +// Process a MIDI event from a track. + +static void ProcessEvent(opl_track_data_t *track, midi_event_t *event) +{ + switch (event->event_type) + { + case MIDI_EVENT_NOTE_OFF: + NoteOffEvent(track, event); + break; + + case MIDI_EVENT_NOTE_ON: + NoteOnEvent(track, event); + break; + + case MIDI_EVENT_CONTROLLER: + ControllerEvent(track, event); + break; + + case MIDI_EVENT_PROGRAM_CHANGE: + ProgramChangeEvent(track, event); + break; + + default: + fprintf(stderr, "Unknown MIDI event type %i\n", event->event_type); + break; + } +} + +static void ScheduleTrack(opl_track_data_t *track); + +// Callback function invoked when another event needs to be read from +// a track. + +static void TrackTimerCallback(void *arg) +{ + opl_track_data_t *track = arg; + midi_event_t *event; + + // Get the next event and process it. + + if (!MIDI_GetNextEvent(track->iter, &event)) + { + return; + } + + ProcessEvent(track, event); + + // Reschedule the callback for the next event in the track. + + ScheduleTrack(track); +} + +static void ScheduleTrack(opl_track_data_t *track) +{ + unsigned int nticks; + unsigned int us; + static int total = 0; + + // Get the number of microseconds until the next event. + + nticks = MIDI_GetDeltaTime(track->iter); + us = (nticks * track->us_per_beat) / track->ticks_per_beat; + total += us; + + // Set a timer to be invoked when the next event is + // ready to play. + + OPL_SetCallback(us / 1000, TrackTimerCallback, track); +} + +// Initialise a channel. + +static void InitChannel(opl_track_data_t *track, opl_channel_data_t *channel) +{ + // TODO: Work out sensible defaults? + + channel->instrument = &main_instrs[0]; + channel->volume = 127; +} + +// Start a MIDI track playing: + +static void StartTrack(midi_file_t *file, unsigned int track_num) +{ + opl_track_data_t *track; + unsigned int i; + + track = &tracks[track_num]; + track->iter = MIDI_IterateTrack(file, track_num); + track->ticks_per_beat = MIDI_GetFileTimeDivision(file); + + // Default is 120 bpm. + // TODO: this is wrong + + track->us_per_beat = 500 * 1000 * 200; + + for (i=0; ichannels[i]); + } + + // Schedule the first event. + + ScheduleTrack(track); +} + // Start playing a mid static void I_OPL_PlaySong(void *handle, int looping) { - if (!music_initialised) + midi_file_t *file; + unsigned int i; + + if (!music_initialised || handle == NULL) { return; } + + file = handle; + + // Allocate track data. + + tracks = malloc(MIDI_NumTracks(file) * sizeof(opl_track_data_t)); + + for (i=0; i #include #include +#include #include "doomdef.h" #include "doomtype.h" @@ -62,6 +63,12 @@ typedef struct int num_events; } midi_track_t; +struct midi_track_iter_s +{ + midi_track_t *track; + unsigned int position; +}; + struct midi_file_s { midi_header_t header; @@ -626,6 +633,68 @@ midi_file_t *MIDI_LoadFile(char *filename) return file; } +// Get the number of tracks in a MIDI file. + +unsigned int MIDI_NumTracks(midi_file_t *file) +{ + return file->num_tracks; +} + +// Start iterating over the events in a track. + +midi_track_iter_t *MIDI_IterateTrack(midi_file_t *file, unsigned int track) +{ + midi_track_iter_t *iter; + + assert(track < file->num_tracks); + + iter = malloc(sizeof(*iter)); + iter->track = &file->tracks[track]; + iter->position = 0; + + return iter; +} + +// Get the time until the next MIDI event in a track. + +unsigned int MIDI_GetDeltaTime(midi_track_iter_t *iter) +{ + if (iter->position < iter->track->num_events) + { + midi_event_t *next_event; + + next_event = &iter->track->events[iter->position]; + + return next_event->delta_time; + } + else + { + return 0; + } +} + +// Get a pointer to the next MIDI event. + +int MIDI_GetNextEvent(midi_track_iter_t *iter, midi_event_t **event) +{ + if (iter->position < iter->track->num_events) + { + *event = &iter->track->events[iter->position]; + ++iter->position; + + return 1; + } + else + { + return 0; + } +} + +unsigned int MIDI_GetFileTimeDivision(midi_file_t *file) +{ + return file->header.time_division; +} + #ifdef TEST static char *MIDI_EventTypeToString(midi_event_type_t event_type) diff --git a/src/midifile.h b/src/midifile.h index f9acea35..a1ab4976 100644 --- a/src/midifile.h +++ b/src/midifile.h @@ -27,6 +27,9 @@ #define MIDIFILE_H typedef struct midi_file_s midi_file_t; +typedef struct midi_track_iter_s midi_track_iter_t; + +#define MIDI_CHANNELS_PER_TRACK 16 typedef enum { @@ -129,8 +132,33 @@ typedef struct } data; } midi_event_t; +// Load a MIDI file. + midi_file_t *MIDI_LoadFile(char *filename); + +// Free a MIDI file. + void MIDI_FreeFile(midi_file_t *file); +// Get the time division value from the MIDI header. + +unsigned int MIDI_GetFileTimeDivision(midi_file_t *file); + +// Get the number of tracks in a MIDI file. + +unsigned int MIDI_NumTracks(midi_file_t *file); + +// Start iterating over the events in a track. + +midi_track_iter_t *MIDI_IterateTrack(midi_file_t *file, unsigned int track_num); + +// Get the time until the next MIDI event in a track. + +unsigned int MIDI_GetDeltaTime(midi_track_iter_t *iter); + +// Get a pointer to the next MIDI event. + +int MIDI_GetNextEvent(midi_track_iter_t *iter, midi_event_t **event); + #endif /* #ifndef MIDIFILE_H */ -- cgit v1.2.3 From 1d045ed19c58abff063ac69db24a2e73d62ef530 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 30 Aug 2009 22:03:20 +0000 Subject: Initial, broken, volume level setting. Subversion-branch: /branches/opl-branch Subversion-revision: 1644 --- src/i_oplmusic.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- src/midifile.h | 3 +++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 3d9a2649..7a619c5d 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -160,6 +160,27 @@ static const unsigned int note_frequencies[] = { 0x2da, 0x306, 0x334, 0x365, 0x398, 0x3cf, }; +// Mapping from MIDI volume level to OPL level value. + +static const unsigned int volume_mapping_table[] = { + 0x3f, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x3a, + 0x39, 0x38, 0x37, 0x37, 0x36, 0x35, 0x34, 0x34, + 0x33, 0x32, 0x32, 0x31, 0x30, 0x2f, 0x2f, 0x2e, + 0x2d, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, + 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x22, 0x21, + 0x20, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1c, + 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x18, 0x18, 0x17, + 0x17, 0x16, 0x16, 0x16, 0x16, 0x15, 0x15, 0x14, + 0x14, 0x13, 0x13, 0x12, 0x12, 0x12, 0x11, 0x11, + 0x10, 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, + 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0b, + 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, + 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, + 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x04, + 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, +}; + static boolean music_initialised = false; //static boolean musicpaused = false; @@ -662,7 +683,10 @@ static void NoteOnEvent(opl_track_data_t *track, midi_event_t *event) // TODO: Set the volume level. - WriteRegister(OPL_REGS_LEVEL + voice->op2, 0); + WriteRegister(OPL_REGS_LEVEL + voice->op2, + volume_mapping_table[channel->volume]); + + printf("volume = %i\n", channel->volume); // Play the note. @@ -694,12 +718,32 @@ static void ProgramChangeEvent(opl_track_data_t *track, midi_event_t *event) static void ControllerEvent(opl_track_data_t *track, midi_event_t *event) { + unsigned int controller; + unsigned int param; + opl_channel_data_t *channel; + printf("change controller: channel %i, %i, %i\n", event->data.channel.channel, event->data.channel.param1, event->data.channel.param2); - // TODO: Volume, pan. + channel = &track->channels[event->data.channel.channel]; + controller = event->data.channel.param1; + param = event->data.channel.param2; + + switch (controller) + { + case MIDI_CONTROLLER_MAIN_VOLUME: + channel->volume = param; + break; + + case MIDI_CONTROLLER_PAN: + break; + + default: + fprintf(stderr, "Unknown MIDI controller type: %i\n", controller); + break; + } } // Process a MIDI event from a track. diff --git a/src/midifile.h b/src/midifile.h index a1ab4976..faef549c 100644 --- a/src/midifile.h +++ b/src/midifile.h @@ -54,6 +54,9 @@ typedef enum MIDI_CONTROLLER_FOOT_CONTROL = 0x3, MIDI_CONTROLLER_PORTAMENTO = 0x4, MIDI_CONTROLLER_DATA_ENTRY = 0x5, + + MIDI_CONTROLLER_MAIN_VOLUME = 0x7, + MIDI_CONTROLLER_PAN = 0xa } midi_controller_t; typedef enum -- cgit v1.2.3 From 61b2e3d2b8b6b2b6ddf8b92842ac4bcf47945160 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 30 Aug 2009 22:26:30 +0000 Subject: Set the right instrument for percussion notes. Subversion-branch: /branches/opl-branch Subversion-revision: 1645 --- src/i_oplmusic.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 7a619c5d..d844fd7b 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -656,8 +656,10 @@ static unsigned int FrequencyForNote(unsigned int note) static void NoteOnEvent(opl_track_data_t *track, midi_event_t *event) { + genmidi_instr_t *instrument; opl_voice_t *voice; opl_channel_data_t *channel; + unsigned int note; printf("note on: channel %i, %i, %i\n", event->data.channel.channel, @@ -667,6 +669,23 @@ static void NoteOnEvent(opl_track_data_t *track, midi_event_t *event) // The channel. channel = &track->channels[event->data.channel.channel]; + note = event->data.channel.param1; + + // Percussion channel (10) is treated differently to normal notes. + + if (event->data.channel.channel == 9) + { + if (note < 35 || note > 81) + { + return; + } + + instrument = &percussion_instrs[note - 35]; + } + else + { + instrument = channel->instrument; + } // Find a voice to use for this new note. @@ -679,19 +698,17 @@ static void NoteOnEvent(opl_track_data_t *track, midi_event_t *event) // Program the voice with the instrument data: - SetVoiceInstrument(voice, &channel->instrument->opl2_voice); + SetVoiceInstrument(voice, &instrument->opl2_voice); // TODO: Set the volume level. WriteRegister(OPL_REGS_LEVEL + voice->op2, volume_mapping_table[channel->volume]); - printf("volume = %i\n", channel->volume); - // Play the note. voice->channel = channel; - voice->note = event->data.channel.param1; + voice->note = note; // Write the frequency value to turn the note on. @@ -840,7 +857,7 @@ static void StartTrack(midi_file_t *file, unsigned int track_num) // Default is 120 bpm. // TODO: this is wrong - track->us_per_beat = 500 * 1000 * 200; + track->us_per_beat = 500 * 1000 * 260; for (i=0; idata.channel.channel, @@ -670,6 +671,7 @@ static void NoteOnEvent(opl_track_data_t *track, midi_event_t *event) channel = &track->channels[event->data.channel.channel]; note = event->data.channel.param1; + volume = event->data.channel.param2; // Percussion channel (10) is treated differently to normal notes. @@ -703,7 +705,7 @@ static void NoteOnEvent(opl_track_data_t *track, midi_event_t *event) // TODO: Set the volume level. WriteRegister(OPL_REGS_LEVEL + voice->op2, - volume_mapping_table[channel->volume]); + volume_mapping_table[volume]); // Play the note. -- cgit v1.2.3 From ab14960e3a24208709270fb11476f0bbb629b145 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 30 Aug 2009 23:04:54 +0000 Subject: Make I_OPL_StopSong work. Subversion-branch: /branches/opl-branch Subversion-revision: 1647 --- src/i_oplmusic.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index db7e0ffc..eff3f942 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -598,6 +598,11 @@ static opl_voice_t *FindVoiceForNote(opl_channel_data_t *channel, int note) return NULL; } +static void VoiceNoteOff(opl_voice_t *voice) +{ + WriteRegister(OPL_REGS_FREQ_2 + voice->index, voice->freq >> 8); +} + static void NoteOffEvent(opl_track_data_t *track, midi_event_t *event) { opl_voice_t *voice; @@ -619,9 +624,7 @@ static void NoteOffEvent(opl_track_data_t *track, midi_event_t *event) return; } - // Note off. - - WriteRegister(OPL_REGS_FREQ_2 + voice->index, voice->freq >> 8); + VoiceNoteOff(voice); // Finished with this voice now. @@ -913,10 +916,27 @@ static void I_OPL_ResumeSong(void) static void I_OPL_StopSong(void) { + unsigned int i; + if (!music_initialised) { return; } + + // Stop all playback. + + OPL_ClearCallbacks(); + + // Free all voices. + + for (i=0; iiter); - us = (nticks * track->us_per_beat) / track->ticks_per_beat; - total += us; + ms = (nticks * track->ms_per_beat) / track->ticks_per_beat; + total += ms; // Set a timer to be invoked when the next event is // ready to play. - OPL_SetCallback(us / 1000, TrackTimerCallback, track); + OPL_SetCallback(ms, TrackTimerCallback, track); } // Initialise a channel. @@ -862,7 +862,7 @@ static void StartTrack(midi_file_t *file, unsigned int track_num) // Default is 120 bpm. // TODO: this is wrong - track->us_per_beat = 500 * 1000 * 260; + track->ms_per_beat = 500 * 260; for (i=0; ichannel = channel; + voice->note = note; + // Program the voice with the instrument data: SetVoiceInstrument(voice, &instrument->opl2_voice); @@ -710,14 +713,16 @@ static void NoteOnEvent(opl_track_data_t *track, midi_event_t *event) WriteRegister(OPL_REGS_LEVEL + voice->op2, volume_mapping_table[volume]); - // Play the note. + // Fixed pitch? - voice->channel = channel; - voice->note = note; + if ((instrument->flags & GENMIDI_FLAG_FIXED) != 0) + { + note = instrument->fixed_note; + } // Write the frequency value to turn the note on. - voice->freq = FrequencyForNote(voice->note); + voice->freq = FrequencyForNote(note); WriteRegister(OPL_REGS_FREQ_1 + voice->index, voice->freq & 0xff); WriteRegister(OPL_REGS_FREQ_2 + voice->index, (voice->freq >> 8) | 0x20); -- cgit v1.2.3 From a43e60ee11f11fa8140b7b22da70f06b598fc49d Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Mon, 31 Aug 2009 17:32:22 +0000 Subject: Make channel volume work. Subversion-branch: /branches/opl-branch Subversion-revision: 1650 --- src/i_oplmusic.c | 108 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 86 insertions(+), 22 deletions(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 8a416f57..1d482595 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -133,6 +133,12 @@ struct opl_voice_s // The frequency value being used. unsigned int freq; + // The volume of the note being played on this channel. + unsigned int note_volume; + + // The current volume that has been set for this channel. + unsigned int volume; + // Next in freelist opl_voice_t *next; }; @@ -163,22 +169,22 @@ static const unsigned int note_frequencies[] = { // Mapping from MIDI volume level to OPL level value. static const unsigned int volume_mapping_table[] = { - 0x3f, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x3a, - 0x39, 0x38, 0x37, 0x37, 0x36, 0x35, 0x34, 0x34, - 0x33, 0x32, 0x32, 0x31, 0x30, 0x2f, 0x2f, 0x2e, - 0x2d, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, - 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x22, 0x21, - 0x20, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1c, - 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x18, 0x18, 0x17, - 0x17, 0x16, 0x16, 0x16, 0x16, 0x15, 0x15, 0x14, - 0x14, 0x13, 0x13, 0x12, 0x12, 0x12, 0x11, 0x11, - 0x10, 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, - 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0b, - 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, - 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, - 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x04, - 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0, 1, 3, 5, 6, 8, 10, 11, + 13, 14, 16, 17, 19, 20, 22, 23, + 25, 26, 27, 29, 30, 32, 33, 34, + 36, 37, 39, 41, 43, 45, 47, 49, + 50, 52, 54, 55, 57, 59, 60, 61, + 63, 64, 66, 67, 68, 69, 71, 72, + 73, 74, 75, 76, 77, 79, 80, 81, + 82, 83, 84, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 92, 93, 94, 95, + 96, 96, 97, 98, 99, 99, 100, 101, + 101, 102, 103, 103, 104, 105, 105, 106, + 107, 107, 108, 109, 109, 110, 110, 111, + 112, 112, 113, 113, 114, 114, 115, 115, + 116, 117, 117, 118, 118, 119, 119, 120, + 120, 121, 121, 122, 122, 123, 123, 123, + 124, 124, 125, 125, 126, 126, 127, 127 }; static boolean music_initialised = false; @@ -433,8 +439,13 @@ static void LoadOperatorData(int operator, genmidi_op_t *data, // Set the instrument for a particular voice. -static void SetVoiceInstrument(opl_voice_t *voice, genmidi_voice_t *data) +static void SetVoiceInstrument(opl_voice_t *voice, genmidi_instr_t *instr) { + genmidi_voice_t *data; + + voice->current_instr = instr; + data = &instr->opl2_voice; + // Doom loads the second operator first, then the first. LoadOperatorData(voice->op2, &data->carrier, true); @@ -446,6 +457,42 @@ static void SetVoiceInstrument(opl_voice_t *voice, genmidi_voice_t *data) WriteRegister(OPL_REGS_FEEDBACK + voice->index, data->feedback | 0x30); + + // Hack to force a volume update. + + voice->volume = 999; +} + +static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume) +{ + unsigned int full_volume; + unsigned int instr_volume; + unsigned int reg_volume; + + voice->note_volume = volume; + + // Multiply note volume and channel volume to get the actual volume. + + full_volume = (voice->note_volume * voice->channel->volume) / 127; + + // The volume of each instrument can be controlled via GENMIDI: + + instr_volume = 0x3f - voice->current_instr->opl2_voice.carrier.level; + + // The volume value to use in the register: + + reg_volume = ((instr_volume * volume_mapping_table[full_volume]) / 128); + reg_volume = (0x3f - reg_volume) + | voice->current_instr->opl2_voice.carrier.scale; + + // Update the register, if necessary: + + if (voice->volume != reg_volume) + { + voice->volume = reg_volume; + + WriteRegister(OPL_REGS_LEVEL + voice->op2, reg_volume); + } } // Initialise the voice table and freelist @@ -698,6 +745,7 @@ static void NoteOnEvent(opl_track_data_t *track, midi_event_t *event) if (voice == NULL) { + printf("\tno free voice\n"); return; } @@ -706,12 +754,11 @@ static void NoteOnEvent(opl_track_data_t *track, midi_event_t *event) // Program the voice with the instrument data: - SetVoiceInstrument(voice, &instrument->opl2_voice); + SetVoiceInstrument(voice, instrument); - // TODO: Set the volume level. + // Set the volume level. - WriteRegister(OPL_REGS_LEVEL + voice->op2, - volume_mapping_table[volume]); + SetVoiceVolume(voice, volume); // Fixed pitch? @@ -743,6 +790,23 @@ static void ProgramChangeEvent(opl_track_data_t *track, midi_event_t *event) // channel, and change the instrument. } +static void SetChannelVolume(opl_channel_data_t *channel, unsigned int volume) +{ + unsigned int i; + + channel->volume = volume; + + // Update all voices that this channel is using. + + for (i=0; ivolume = param; + SetChannelVolume(channel, param); break; case MIDI_CONTROLLER_PAN: -- cgit v1.2.3 From a26925cf8f27eb38b3266629a3bc259f098b1e19 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Mon, 31 Aug 2009 18:18:51 +0000 Subject: Set the volume on both operators for instruments that use non-modulating voice mode. Subversion-branch: /branches/opl-branch Subversion-revision: 1651 --- src/i_oplmusic.c | 70 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 19 deletions(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 1d482595..51a609d9 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -137,7 +137,8 @@ struct opl_voice_s unsigned int note_volume; // The current volume that has been set for this channel. - unsigned int volume; + unsigned int carrier_volume; + unsigned int modulator_volume; // Next in freelist opl_voice_t *next; @@ -442,14 +443,22 @@ static void LoadOperatorData(int operator, genmidi_op_t *data, static void SetVoiceInstrument(opl_voice_t *voice, genmidi_instr_t *instr) { genmidi_voice_t *data; + unsigned int modulating; voice->current_instr = instr; data = &instr->opl2_voice; + // Are we usind modulated feedback mode? + + modulating = (data->feedback & 0x01) == 0; + // Doom loads the second operator first, then the first. + // The carrier is set to minimum volume until the voice volume + // is set in SetVoiceVolume (below). If we are not using + // modulating mode, we must set both to minimum volume. LoadOperatorData(voice->op2, &data->carrier, true); - LoadOperatorData(voice->op1, &data->modulator, false); + LoadOperatorData(voice->op1, &data->modulator, !modulating); // Set feedback register that control the connection between the // two operators. Turn on bits in the upper nybble; I think this @@ -460,38 +469,61 @@ static void SetVoiceInstrument(opl_voice_t *voice, genmidi_instr_t *instr) // Hack to force a volume update. - voice->volume = 999; + voice->carrier_volume = 999; + voice->modulator_volume = 999; } -static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume) +// Calculate the volume level to use for a given operator. + +static void SetOperatorVolume(genmidi_op_t *op, unsigned int volume, + unsigned int opnum, + unsigned int *current_volume) { - unsigned int full_volume; - unsigned int instr_volume; + unsigned int op_volume; unsigned int reg_volume; - voice->note_volume = volume; - - // Multiply note volume and channel volume to get the actual volume. - - full_volume = (voice->note_volume * voice->channel->volume) / 127; - // The volume of each instrument can be controlled via GENMIDI: - instr_volume = 0x3f - voice->current_instr->opl2_voice.carrier.level; + op_volume = 0x3f - op->level; // The volume value to use in the register: - reg_volume = ((instr_volume * volume_mapping_table[full_volume]) / 128); - reg_volume = (0x3f - reg_volume) - | voice->current_instr->opl2_voice.carrier.scale; + reg_volume = ((op_volume * volume_mapping_table[volume]) / 128); + reg_volume = (0x3f - reg_volume) | op->scale; // Update the register, if necessary: - if (voice->volume != reg_volume) + if (*current_volume != reg_volume) { - voice->volume = reg_volume; + *current_volume = reg_volume; + + WriteRegister(OPL_REGS_LEVEL + opnum, reg_volume); + } +} - WriteRegister(OPL_REGS_LEVEL + voice->op2, reg_volume); +static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume) +{ + genmidi_voice_t *opl_voice; + unsigned int full_volume; + + voice->note_volume = volume; + + opl_voice = &voice->current_instr->opl2_voice; + + // Multiply note volume and channel volume to get the actual volume. + + full_volume = (voice->note_volume * voice->channel->volume) / 127; + + SetOperatorVolume(&opl_voice->carrier, full_volume, + voice->op2, &voice->carrier_volume); + + // If we are using non-modulated feedback mode, we must set the + // volume for both voices. + + if ((opl_voice->feedback & 0x01) != 0) + { + SetOperatorVolume(&opl_voice->modulator, full_volume, + voice->op1, &voice->modulator_volume); } } -- cgit v1.2.3 From fe96ae292d2b0e67020d77493ae366360cd93ec8 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Mon, 31 Aug 2009 18:33:36 +0000 Subject: Dont program an instrument if it is already set. Subversion-branch: /branches/opl-branch Subversion-revision: 1652 --- src/i_oplmusic.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 51a609d9..ab64505c 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -445,6 +445,13 @@ static void SetVoiceInstrument(opl_voice_t *voice, genmidi_instr_t *instr) genmidi_voice_t *data; unsigned int modulating; + // Instrument already set for this channel? + + if (voice->current_instr == instr) + { + return; + } + voice->current_instr = instr; data = &instr->opl2_voice; -- cgit v1.2.3 From eb2291030ae0f1e005a6014193fdfeaa796a913a Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Mon, 31 Aug 2009 22:50:28 +0000 Subject: Perform volume mapping on note and channel volumes before multiplying them. This gives voice volume values that are almost identical to Doom's. Subversion-branch: /branches/opl-branch Subversion-revision: 1653 --- src/i_oplmusic.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index ab64505c..74a76b00 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -495,7 +495,7 @@ static void SetOperatorVolume(genmidi_op_t *op, unsigned int volume, // The volume value to use in the register: - reg_volume = ((op_volume * volume_mapping_table[volume]) / 128); + reg_volume = (op_volume * volume) / 128; reg_volume = (0x3f - reg_volume) | op->scale; // Update the register, if necessary: @@ -519,7 +519,8 @@ static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume) // Multiply note volume and channel volume to get the actual volume. - full_volume = (voice->note_volume * voice->channel->volume) / 127; + full_volume = (volume_mapping_table[voice->note_volume] + * volume_mapping_table[voice->channel->volume]) / 127; SetOperatorVolume(&opl_voice->carrier, full_volume, voice->op2, &voice->carrier_volume); -- cgit v1.2.3 From 556f7291ea0199144794166af2757aa7ad832a7a Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Tue, 1 Sep 2009 18:17:11 +0000 Subject: Loop songs (when appropriate) Subversion-branch: /branches/opl-branch Subversion-revision: 1654 --- src/i_oplmusic.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- src/midifile.c | 10 ++++++ src/midifile.h | 8 +++++ 3 files changed, 108 insertions(+), 3 deletions(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 74a76b00..1ac502f5 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -206,6 +206,9 @@ static opl_voice_t *voice_free_list; // Track data for playing tracks: static opl_track_data_t *tracks; +static unsigned int num_tracks; +static unsigned int running_tracks = 0; +static boolean song_looping; // In the initialisation stage, register writes are spaced by reading // from the register port (0). After initialisation, spacing is @@ -868,11 +871,40 @@ static void ControllerEvent(opl_track_data_t *track, midi_event_t *event) SetChannelVolume(channel, param); break; - case MIDI_CONTROLLER_PAN: + default: + fprintf(stderr, "Unknown MIDI controller type: %i\n", controller); + break; + } +} + +// Process a meta event. + +static void MetaEvent(opl_track_data_t *track, midi_event_t *event) +{ + switch (event->data.meta.type) + { + // Things we can just ignore. + + case MIDI_META_SEQUENCE_NUMBER: + case MIDI_META_TEXT: + case MIDI_META_COPYRIGHT: + case MIDI_META_TRACK_NAME: + case MIDI_META_INSTR_NAME: + case MIDI_META_LYRICS: + case MIDI_META_MARKER: + case MIDI_META_CUE_POINT: + case MIDI_META_SEQUENCER_SPECIFIC: + break; + + // End of track - actually handled when we run out of events + // in the track, see below. + + case MIDI_META_END_OF_TRACK: break; default: - fprintf(stderr, "Unknown MIDI controller type: %i\n", controller); + fprintf(stderr, "Unknown MIDI meta event type: %i\n", + event->data.meta.type); break; } } @@ -899,6 +931,16 @@ static void ProcessEvent(opl_track_data_t *track, midi_event_t *event) ProgramChangeEvent(track, event); break; + case MIDI_EVENT_META: + MetaEvent(track, event); + break; + + // SysEx events can be ignored. + + case MIDI_EVENT_SYSEX: + case MIDI_EVENT_SYSEX_SPLIT: + break; + default: fprintf(stderr, "Unknown MIDI event type %i\n", event->event_type); break; @@ -907,6 +949,21 @@ static void ProcessEvent(opl_track_data_t *track, midi_event_t *event) static void ScheduleTrack(opl_track_data_t *track); +// Restart a song from the beginning. + +static void RestartSong(void) +{ + unsigned int i; + + running_tracks = num_tracks; + + for (i=0; ievent_type == MIDI_EVENT_META + && event->data.meta.type == MIDI_META_END_OF_TRACK) + { + --running_tracks; + + // When all tracks have finished, restart the song. + + if (running_tracks <= 0 && song_looping) + { + RestartSong(); + } + + return; + } + // Reschedule the callback for the next event in the track. ScheduleTrack(track); @@ -1001,7 +1075,11 @@ static void I_OPL_PlaySong(void *handle, int looping) tracks = malloc(MIDI_NumTracks(file) * sizeof(opl_track_data_t)); - for (i=0; iheader.time_division; } +void MIDI_RestartIterator(midi_track_iter_t *iter) +{ + iter->position = 0; +} + #ifdef TEST static char *MIDI_EventTypeToString(midi_event_type_t event_type) diff --git a/src/midifile.h b/src/midifile.h index faef549c..4ee0ddb2 100644 --- a/src/midifile.h +++ b/src/midifile.h @@ -155,6 +155,10 @@ unsigned int MIDI_NumTracks(midi_file_t *file); midi_track_iter_t *MIDI_IterateTrack(midi_file_t *file, unsigned int track_num); +// Free an iterator. + +void MIDI_FreeIterator(midi_track_iter_t *iter); + // Get the time until the next MIDI event in a track. unsigned int MIDI_GetDeltaTime(midi_track_iter_t *iter); @@ -163,5 +167,9 @@ unsigned int MIDI_GetDeltaTime(midi_track_iter_t *iter); int MIDI_GetNextEvent(midi_track_iter_t *iter, midi_event_t **event); +// Reset an iterator to the beginning of a track. + +void MIDI_RestartIterator(midi_track_iter_t *iter); + #endif /* #ifndef MIDIFILE_H */ -- cgit v1.2.3 From d372d65b3de579fe40207a308c3f7c4bf3076c0a Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Mon, 7 Sep 2009 18:11:25 +0000 Subject: Implement pitch bend. Subversion-branch: /branches/opl-branch Subversion-revision: 1658 --- src/i_oplmusic.c | 262 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 213 insertions(+), 49 deletions(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 1ac502f5..e9fdb395 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -91,6 +91,11 @@ typedef struct // Volume level int volume; + + // Pitch bend value: + + int bend; + } opl_channel_data_t; // Data associated with a track that is currently playing. @@ -127,7 +132,12 @@ struct opl_voice_s // The channel currently using this voice. opl_channel_data_t *channel; - // The note that this voice is playing. + // The midi key that this voice is playing. + unsigned int key; + + // The note being played. This is normally the same as + // the key, but if the instrument is a fixed pitch + // instrument, it is different. unsigned int note; // The frequency value being used. @@ -153,18 +163,120 @@ static const int voice_operators[2][OPL_NUM_VOICES] = { // Frequency values to use for each note. -static const unsigned int note_frequencies[] = { - - // These frequencies are only used for the first seven - // MIDI note values: - - 0x158, 0x16d, 0x183, 0x19a, 0x1b2, 0x1cc, 0x1e7, - - // These frequencies are used repeatedly, cycling around - // for each octave: - - 0x204, 0x223, 0x244, 0x266, 0x28b, 0x2b1, - 0x2da, 0x306, 0x334, 0x365, 0x398, 0x3cf, +static const unsigned short frequency_curve[] = { + + 0x133, 0x133, 0x134, 0x134, 0x135, 0x136, 0x136, 0x137, // -1 + 0x137, 0x138, 0x138, 0x139, 0x139, 0x13a, 0x13b, 0x13b, + 0x13c, 0x13c, 0x13d, 0x13d, 0x13e, 0x13f, 0x13f, 0x140, + 0x140, 0x141, 0x142, 0x142, 0x143, 0x143, 0x144, 0x144, + + 0x145, 0x146, 0x146, 0x147, 0x147, 0x148, 0x149, 0x149, // -2 + 0x14a, 0x14a, 0x14b, 0x14c, 0x14c, 0x14d, 0x14d, 0x14e, + 0x14f, 0x14f, 0x150, 0x150, 0x151, 0x152, 0x152, 0x153, + 0x153, 0x154, 0x155, 0x155, 0x156, 0x157, 0x157, 0x158, + + // These are used for the first seven MIDI note values: + + 0x158, 0x159, 0x15a, 0x15a, 0x15b, 0x15b, 0x15c, 0x15d, // 0 + 0x15d, 0x15e, 0x15f, 0x15f, 0x160, 0x161, 0x161, 0x162, + 0x162, 0x163, 0x164, 0x164, 0x165, 0x166, 0x166, 0x167, + 0x168, 0x168, 0x169, 0x16a, 0x16a, 0x16b, 0x16c, 0x16c, + + 0x16d, 0x16e, 0x16e, 0x16f, 0x170, 0x170, 0x171, 0x172, // 1 + 0x172, 0x173, 0x174, 0x174, 0x175, 0x176, 0x176, 0x177, + 0x178, 0x178, 0x179, 0x17a, 0x17a, 0x17b, 0x17c, 0x17c, + 0x17d, 0x17e, 0x17e, 0x17f, 0x180, 0x181, 0x181, 0x182, + + 0x183, 0x183, 0x184, 0x185, 0x185, 0x186, 0x187, 0x188, // 2 + 0x188, 0x189, 0x18a, 0x18a, 0x18b, 0x18c, 0x18d, 0x18d, + 0x18e, 0x18f, 0x18f, 0x190, 0x191, 0x192, 0x192, 0x193, + 0x194, 0x194, 0x195, 0x196, 0x197, 0x197, 0x198, 0x199, + + 0x19a, 0x19a, 0x19b, 0x19c, 0x19d, 0x19d, 0x19e, 0x19f, // 3 + 0x1a0, 0x1a0, 0x1a1, 0x1a2, 0x1a3, 0x1a3, 0x1a4, 0x1a5, + 0x1a6, 0x1a6, 0x1a7, 0x1a8, 0x1a9, 0x1a9, 0x1aa, 0x1ab, + 0x1ac, 0x1ad, 0x1ad, 0x1ae, 0x1af, 0x1b0, 0x1b0, 0x1b1, + + 0x1b2, 0x1b3, 0x1b4, 0x1b4, 0x1b5, 0x1b6, 0x1b7, 0x1b8, // 4 + 0x1b8, 0x1b9, 0x1ba, 0x1bb, 0x1bc, 0x1bc, 0x1bd, 0x1be, + 0x1bf, 0x1c0, 0x1c0, 0x1c1, 0x1c2, 0x1c3, 0x1c4, 0x1c4, + 0x1c5, 0x1c6, 0x1c7, 0x1c8, 0x1c9, 0x1c9, 0x1ca, 0x1cb, + + 0x1cc, 0x1cd, 0x1ce, 0x1ce, 0x1cf, 0x1d0, 0x1d1, 0x1d2, // 5 + 0x1d3, 0x1d3, 0x1d4, 0x1d5, 0x1d6, 0x1d7, 0x1d8, 0x1d8, + 0x1d9, 0x1da, 0x1db, 0x1dc, 0x1dd, 0x1de, 0x1de, 0x1df, + 0x1e0, 0x1e1, 0x1e2, 0x1e3, 0x1e4, 0x1e5, 0x1e5, 0x1e6, + + 0x1e7, 0x1e8, 0x1e9, 0x1ea, 0x1eb, 0x1ec, 0x1ed, 0x1ed, // 6 + 0x1ee, 0x1ef, 0x1f0, 0x1f1, 0x1f2, 0x1f3, 0x1f4, 0x1f5, + 0x1f6, 0x1f6, 0x1f7, 0x1f8, 0x1f9, 0x1fa, 0x1fb, 0x1fc, + 0x1fd, 0x1fe, 0x1ff, 0x200, 0x201, 0x201, 0x202, 0x203, + + // First note of looped range used for all octaves: + + 0x204, 0x205, 0x206, 0x207, 0x208, 0x209, 0x20a, 0x20b, // 7 + 0x20c, 0x20d, 0x20e, 0x20f, 0x210, 0x210, 0x211, 0x212, + 0x213, 0x214, 0x215, 0x216, 0x217, 0x218, 0x219, 0x21a, + 0x21b, 0x21c, 0x21d, 0x21e, 0x21f, 0x220, 0x221, 0x222, + + 0x223, 0x224, 0x225, 0x226, 0x227, 0x228, 0x229, 0x22a, // 8 + 0x22b, 0x22c, 0x22d, 0x22e, 0x22f, 0x230, 0x231, 0x232, + 0x233, 0x234, 0x235, 0x236, 0x237, 0x238, 0x239, 0x23a, + 0x23b, 0x23c, 0x23d, 0x23e, 0x23f, 0x240, 0x241, 0x242, + + 0x244, 0x245, 0x246, 0x247, 0x248, 0x249, 0x24a, 0x24b, // 9 + 0x24c, 0x24d, 0x24e, 0x24f, 0x250, 0x251, 0x252, 0x253, + 0x254, 0x256, 0x257, 0x258, 0x259, 0x25a, 0x25b, 0x25c, + 0x25d, 0x25e, 0x25f, 0x260, 0x262, 0x263, 0x264, 0x265, + + 0x266, 0x267, 0x268, 0x269, 0x26a, 0x26c, 0x26d, 0x26e, // 10 + 0x26f, 0x270, 0x271, 0x272, 0x273, 0x275, 0x276, 0x277, + 0x278, 0x279, 0x27a, 0x27b, 0x27d, 0x27e, 0x27f, 0x280, + 0x281, 0x282, 0x284, 0x285, 0x286, 0x287, 0x288, 0x289, + + 0x28b, 0x28c, 0x28d, 0x28e, 0x28f, 0x290, 0x292, 0x293, // 11 + 0x294, 0x295, 0x296, 0x298, 0x299, 0x29a, 0x29b, 0x29c, + 0x29e, 0x29f, 0x2a0, 0x2a1, 0x2a2, 0x2a4, 0x2a5, 0x2a6, + 0x2a7, 0x2a9, 0x2aa, 0x2ab, 0x2ac, 0x2ae, 0x2af, 0x2b0, + + 0x2b1, 0x2b2, 0x2b4, 0x2b5, 0x2b6, 0x2b7, 0x2b9, 0x2ba, // 12 + 0x2bb, 0x2bd, 0x2be, 0x2bf, 0x2c0, 0x2c2, 0x2c3, 0x2c4, + 0x2c5, 0x2c7, 0x2c8, 0x2c9, 0x2cb, 0x2cc, 0x2cd, 0x2ce, + 0x2d0, 0x2d1, 0x2d2, 0x2d4, 0x2d5, 0x2d6, 0x2d8, 0x2d9, + + 0x2da, 0x2dc, 0x2dd, 0x2de, 0x2e0, 0x2e1, 0x2e2, 0x2e4, // 13 + 0x2e5, 0x2e6, 0x2e8, 0x2e9, 0x2ea, 0x2ec, 0x2ed, 0x2ee, + 0x2f0, 0x2f1, 0x2f2, 0x2f4, 0x2f5, 0x2f6, 0x2f8, 0x2f9, + 0x2fb, 0x2fc, 0x2fd, 0x2ff, 0x300, 0x302, 0x303, 0x304, + + 0x306, 0x307, 0x309, 0x30a, 0x30b, 0x30d, 0x30e, 0x310, // 14 + 0x311, 0x312, 0x314, 0x315, 0x317, 0x318, 0x31a, 0x31b, + 0x31c, 0x31e, 0x31f, 0x321, 0x322, 0x324, 0x325, 0x327, + 0x328, 0x329, 0x32b, 0x32c, 0x32e, 0x32f, 0x331, 0x332, + + 0x334, 0x335, 0x337, 0x338, 0x33a, 0x33b, 0x33d, 0x33e, // 15 + 0x340, 0x341, 0x343, 0x344, 0x346, 0x347, 0x349, 0x34a, + 0x34c, 0x34d, 0x34f, 0x350, 0x352, 0x353, 0x355, 0x357, + 0x358, 0x35a, 0x35b, 0x35d, 0x35e, 0x360, 0x361, 0x363, + + 0x365, 0x366, 0x368, 0x369, 0x36b, 0x36c, 0x36e, 0x370, // 16 + 0x371, 0x373, 0x374, 0x376, 0x378, 0x379, 0x37b, 0x37c, + 0x37e, 0x380, 0x381, 0x383, 0x384, 0x386, 0x388, 0x389, + 0x38b, 0x38d, 0x38e, 0x390, 0x392, 0x393, 0x395, 0x397, + + 0x398, 0x39a, 0x39c, 0x39d, 0x39f, 0x3a1, 0x3a2, 0x3a4, // 17 + 0x3a6, 0x3a7, 0x3a9, 0x3ab, 0x3ac, 0x3ae, 0x3b0, 0x3b1, + 0x3b3, 0x3b5, 0x3b7, 0x3b8, 0x3ba, 0x3bc, 0x3bd, 0x3bf, + 0x3c1, 0x3c3, 0x3c4, 0x3c6, 0x3c8, 0x3ca, 0x3cb, 0x3cd, + + // The last note has an incomplete range, and loops round back to + // the start. Note that the last value is actually a buffer overrun + // and does not fit with the other values. + + 0x3cf, 0x3d1, 0x3d2, 0x3d4, 0x3d6, 0x3d8, 0x3da, 0x3db, // 18 + 0x3dd, 0x3df, 0x3e1, 0x3e3, 0x3e4, 0x3e6, 0x3e8, 0x3ea, + 0x3ec, 0x3ed, 0x3ef, 0x3f1, 0x3f3, 0x3f5, 0x3f6, 0x3f8, + 0x3fa, 0x3fc, 0x3fe, 0x36c, }; // Mapping from MIDI volume level to OPL level value. @@ -501,7 +613,7 @@ static void SetOperatorVolume(genmidi_op_t *op, unsigned int volume, reg_volume = (op_volume * volume) / 128; reg_volume = (0x3f - reg_volume) | op->scale; - // Update the register, if necessary: + // Update the register, if neccessary: if (*current_volume != reg_volume) { @@ -673,13 +785,13 @@ static void I_OPL_SetMusicVolume(int volume) current_music_volume = volume; } -static opl_voice_t *FindVoiceForNote(opl_channel_data_t *channel, int note) +static opl_voice_t *FindVoiceForKey(opl_channel_data_t *channel, int key) { unsigned int i; for (i=0; iindex, voice->freq >> 8); } +// Get the frequency that we should be using for a voice. + static void NoteOffEvent(opl_track_data_t *track, midi_event_t *event) { opl_voice_t *voice; @@ -707,7 +821,7 @@ static void NoteOffEvent(opl_track_data_t *track, midi_event_t *event) // Find the voice being used to play the note. - voice = FindVoiceForNote(channel, event->data.channel.param1); + voice = FindVoiceForKey(channel, event->data.channel.param1); if (voice == NULL) { @@ -721,29 +835,47 @@ static void NoteOffEvent(opl_track_data_t *track, midi_event_t *event) ReleaseVoice(voice); } -// Given a MIDI note number, get the corresponding OPL -// frequency value to use. - -static unsigned int FrequencyForNote(unsigned int note) +static unsigned int FrequencyForVoice(opl_voice_t *voice) { + unsigned int freq_index; unsigned int octave; - unsigned int key_num; + unsigned int sub_index; - // The first seven frequencies in the frequencies array are used - // only for the first seven MIDI notes. After this, the frequency - // value loops around the same twelve notes, increasing the - // octave. + freq_index = 64 + 32 * voice->note + voice->channel->bend; - if (note < 7) + // The first 7 notes use the start of the table, while + // consecutive notes loop around the latter part. + + if (freq_index < 284) { - return note_frequencies[note]; + return frequency_curve[freq_index]; } - else + + sub_index = (freq_index - 284) % (12 * 32); + octave = (freq_index - 284) / (12 * 32); + + // Calculate the resulting register value to use for the frequency. + + return frequency_curve[sub_index + 284] | (octave << 10); +} + +// Update the frequency that a voice is programmed to use. + +static void UpdateVoiceFrequency(opl_voice_t *voice) +{ + unsigned int freq; + + // Calculate the frequency to use for this voice and update it + // if neccessary. + + freq = FrequencyForVoice(voice); + + if (voice->freq != freq) { - octave = (note - 7) / 12; - key_num = (note - 7) % 12; + WriteRegister(OPL_REGS_FREQ_1 + voice->index, freq & 0xff); + WriteRegister(OPL_REGS_FREQ_2 + voice->index, (freq >> 8) | 0x20); - return note_frequencies[key_num + 7] | (octave << 10); + voice->freq = freq; } } @@ -752,7 +884,7 @@ static void NoteOnEvent(opl_track_data_t *track, midi_event_t *event) genmidi_instr_t *instrument; opl_voice_t *voice; opl_channel_data_t *channel; - unsigned int note; + unsigned int key; unsigned int volume; printf("note on: channel %i, %i, %i\n", @@ -763,19 +895,19 @@ static void NoteOnEvent(opl_track_data_t *track, midi_event_t *event) // The channel. channel = &track->channels[event->data.channel.channel]; - note = event->data.channel.param1; + key = event->data.channel.param1; volume = event->data.channel.param2; - // Percussion channel (10) is treated differently to normal notes. + // Percussion channel (10) is treated differently. if (event->data.channel.channel == 9) { - if (note < 35 || note > 81) + if (key < 35 || key > 81) { return; } - instrument = &percussion_instrs[note - 35]; + instrument = &percussion_instrs[key - 35]; } else { @@ -793,7 +925,19 @@ static void NoteOnEvent(opl_track_data_t *track, midi_event_t *event) } voice->channel = channel; - voice->note = note; + voice->key = key; + + // Work out the note to use. This is normally the same as + // the key, unless it is a fixed pitch instrument. + + if ((instrument->flags & GENMIDI_FLAG_FIXED) != 0) + { + voice->note = instrument->fixed_note; + } + else + { + voice->note = key; + } // Program the voice with the instrument data: @@ -803,19 +947,10 @@ static void NoteOnEvent(opl_track_data_t *track, midi_event_t *event) SetVoiceVolume(voice, volume); - // Fixed pitch? - - if ((instrument->flags & GENMIDI_FLAG_FIXED) != 0) - { - note = instrument->fixed_note; - } - // Write the frequency value to turn the note on. - voice->freq = FrequencyForNote(note); - - WriteRegister(OPL_REGS_FREQ_1 + voice->index, voice->freq & 0xff); - WriteRegister(OPL_REGS_FREQ_2 + voice->index, (voice->freq >> 8) | 0x20); + voice->freq = 0; + UpdateVoiceFrequency(voice); } static void ProgramChangeEvent(opl_track_data_t *track, midi_event_t *event) @@ -877,6 +1012,30 @@ static void ControllerEvent(opl_track_data_t *track, midi_event_t *event) } } +// Process a pitch bend event. + +static void PitchBendEvent(opl_track_data_t *track, midi_event_t *event) +{ + opl_channel_data_t *channel; + unsigned int i; + + // Update the channel bend value. Only the MSB of the pitch bend + // value is considered: this is what Doom does. + + channel = &track->channels[event->data.channel.channel]; + channel->bend = event->data.channel.param2 - 64; + + // Update all voices for this channel. + + for (i=0; iinstrument = &main_instrs[0]; channel->volume = 127; + channel->bend = 0; } // Start a MIDI track playing: -- cgit v1.2.3 From e97bc5db814b844ef2e4ee92f8a683031853dadb Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Tue, 8 Sep 2009 17:56:21 +0000 Subject: Program two voices for double voice instruments. Subversion-branch: /branches/opl-branch Subversion-revision: 1660 --- src/i_oplmusic.c | 164 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 91 insertions(+), 73 deletions(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index e9fdb395..8ec47b80 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -49,7 +49,7 @@ #define GENMIDI_HEADER "#OPL_II#" #define GENMIDI_FLAG_FIXED 0x0001 /* fixed pitch */ -#define GENMIDI_FLAG_2VOICE 0x0002 /* double voice (OPL3) */ +#define GENMIDI_FLAG_2VOICE 0x0004 /* double voice (OPL3) */ typedef struct { @@ -76,8 +76,7 @@ typedef struct byte fine_tuning; byte fixed_note; - genmidi_voice_t opl2_voice; - genmidi_voice_t opl3_voice; + genmidi_voice_t voices[2]; } PACKEDATTR genmidi_instr_t; // Data associated with a channel of a track that is currently playing. @@ -129,6 +128,11 @@ struct opl_voice_s // Currently-loaded instrument data genmidi_instr_t *current_instr; + // The voice number in the instrument to use. + // This is normally set to zero; if this is a double voice + // instrument, it may be one. + unsigned int current_instr_voice; + // The channel currently using this voice. opl_channel_data_t *channel; @@ -555,20 +559,25 @@ static void LoadOperatorData(int operator, genmidi_op_t *data, // Set the instrument for a particular voice. -static void SetVoiceInstrument(opl_voice_t *voice, genmidi_instr_t *instr) +static void SetVoiceInstrument(opl_voice_t *voice, + genmidi_instr_t *instr, + unsigned int instr_voice) { genmidi_voice_t *data; unsigned int modulating; // Instrument already set for this channel? - if (voice->current_instr == instr) + if (voice->current_instr == instr + && voice->current_instr_voice == instr_voice) { return; } voice->current_instr = instr; - data = &instr->opl2_voice; + voice->current_instr_voice = instr_voice; + + data = &instr->voices[instr_voice]; // Are we usind modulated feedback mode? @@ -630,7 +639,7 @@ static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume) voice->note_volume = volume; - opl_voice = &voice->current_instr->opl2_voice; + opl_voice = &voice->current_instr->voices[voice->current_instr_voice]; // Multiply note volume and channel volume to get the actual volume. @@ -765,7 +774,7 @@ static boolean I_OPL_InitMusic(void) voice = GetFreeVoice(); instr_num = rand() % 100; - SetVoiceInstrument(voice, &main_instrs[instr_num].opl2_voice); + SetVoiceInstrument(voice, &main_instrs[instr_num], 0); OPL_SetCallback(0, TestCallback, voice); } @@ -785,32 +794,18 @@ static void I_OPL_SetMusicVolume(int volume) current_music_volume = volume; } -static opl_voice_t *FindVoiceForKey(opl_channel_data_t *channel, int key) -{ - unsigned int i; - - for (i=0; iindex, voice->freq >> 8); } // Get the frequency that we should be using for a voice. -static void NoteOffEvent(opl_track_data_t *track, midi_event_t *event) +static void KeyOffEvent(opl_track_data_t *track, midi_event_t *event) { - opl_voice_t *voice; opl_channel_data_t *channel; + unsigned int key; + unsigned int i; printf("note off: channel %i, %i, %i\n", event->data.channel.channel, @@ -818,21 +813,22 @@ static void NoteOffEvent(opl_track_data_t *track, midi_event_t *event) event->data.channel.param2); channel = &track->channels[event->data.channel.channel]; + key = event->data.channel.param1; - // Find the voice being used to play the note. - - voice = FindVoiceForKey(channel, event->data.channel.param1); + // Turn off voices being used to play this key. + // If it is a double voice instrument there will be two. - if (voice == NULL) + for (i=0; idata.channel.channel, - event->data.channel.param1, - event->data.channel.param2); - - // The channel. - - channel = &track->channels[event->data.channel.channel]; - key = event->data.channel.param1; - volume = event->data.channel.param2; - - // Percussion channel (10) is treated differently. - - if (event->data.channel.channel == 9) - { - if (key < 35 || key > 81) - { - return; - } - - instrument = &percussion_instrs[key - 35]; - } - else - { - instrument = channel->instrument; - } // Find a voice to use for this new note. @@ -920,7 +893,7 @@ static void NoteOnEvent(opl_track_data_t *track, midi_event_t *event) if (voice == NULL) { - printf("\tno free voice\n"); + printf("\tno free voice for voice %i of instrument\n", instrument_voice); return; } @@ -941,7 +914,7 @@ static void NoteOnEvent(opl_track_data_t *track, midi_event_t *event) // Program the voice with the instrument data: - SetVoiceInstrument(voice, instrument); + SetVoiceInstrument(voice, instrument, 0); // Set the volume level. @@ -953,6 +926,51 @@ static void NoteOnEvent(opl_track_data_t *track, midi_event_t *event) UpdateVoiceFrequency(voice); } +static void KeyOnEvent(opl_track_data_t *track, midi_event_t *event) +{ + genmidi_instr_t *instrument; + opl_channel_data_t *channel; + unsigned int key; + unsigned int volume; + + printf("note on: channel %i, %i, %i\n", + event->data.channel.channel, + event->data.channel.param1, + event->data.channel.param2); + + // The channel. + + channel = &track->channels[event->data.channel.channel]; + key = event->data.channel.param1; + volume = event->data.channel.param2; + + // Percussion channel (10) is treated differently. + + if (event->data.channel.channel == 9) + { + if (key < 35 || key > 81) + { + return; + } + + instrument = &percussion_instrs[key - 35]; + } + else + { + instrument = channel->instrument; + } + + // Find and program a voice for this instrument. If this + // is a double voice instrument, we must do this twice. + + VoiceKeyOn(channel, instrument, 0, key, volume); + + if ((instrument->flags & GENMIDI_FLAG_2VOICE) != 0) + { + VoiceKeyOn(channel, instrument, 1, key, volume); + } +} + static void ProgramChangeEvent(opl_track_data_t *track, midi_event_t *event) { int channel; @@ -1075,11 +1093,11 @@ static void ProcessEvent(opl_track_data_t *track, midi_event_t *event) switch (event->event_type) { case MIDI_EVENT_NOTE_OFF: - NoteOffEvent(track, event); + KeyOffEvent(track, event); break; case MIDI_EVENT_NOTE_ON: - NoteOnEvent(track, event); + KeyOnEvent(track, event); break; case MIDI_EVENT_CONTROLLER: @@ -1284,7 +1302,7 @@ static void I_OPL_StopSong(void) { if (voices[i].channel != NULL) { - VoiceNoteOff(&voices[i]); + VoiceKeyOff(&voices[i]); ReleaseVoice(&voices[i]); } } -- cgit v1.2.3 From ba1accec2394c6ee70c5c79a42827099ef20e638 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Tue, 8 Sep 2009 18:12:10 +0000 Subject: Set the correct instrument voice, instead of using the first voice for both. Subversion-branch: /branches/opl-branch Subversion-revision: 1661 --- src/i_oplmusic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 8ec47b80..643f67c5 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -914,7 +914,7 @@ static void VoiceKeyOn(opl_channel_data_t *channel, // Program the voice with the instrument data: - SetVoiceInstrument(voice, instrument, 0); + SetVoiceInstrument(voice, instrument, instrument_voice); // Set the volume level. -- cgit v1.2.3 From cc0cc9fb0c806f6533dca794b1e829a626323d8b Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Fri, 11 Sep 2009 16:54:06 +0000 Subject: Implement the fine tuning field, based on research. Subversion-branch: /branches/opl-branch Subversion-revision: 1664 --- src/i_oplmusic.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 643f67c5..8cb833a2 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -839,6 +839,14 @@ static unsigned int FrequencyForVoice(opl_voice_t *voice) freq_index = 64 + 32 * voice->note + voice->channel->bend; + // If this is the second voice of a double voice instrument, the + // frequency index can be adjusted by the fine tuning field. + + if (voice->current_instr_voice != 0) + { + freq_index += (voice->current_instr->fine_tuning / 2) - 64; + } + // The first 7 notes use the start of the table, while // consecutive notes loop around the latter part. -- cgit v1.2.3 From a6a747e0ab96c45abe1a563f278f3ebc05fdec55 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 12 Sep 2009 17:22:58 +0000 Subject: Discard an existing voice when no voices are available (based on research with Vanilla). Subversion-branch: /branches/opl-branch Subversion-revision: 1668 --- src/i_oplmusic.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 110 insertions(+), 3 deletions(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 8cb833a2..bed81109 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -154,7 +154,8 @@ struct opl_voice_s unsigned int carrier_volume; unsigned int modulator_volume; - // Next in freelist + // Next in linked list; a voice is always either in the + // free list or the allocated list. opl_voice_t *next; }; @@ -318,6 +319,7 @@ static genmidi_instr_t *percussion_instrs; static opl_voice_t voices[OPL_NUM_VOICES]; static opl_voice_t *voice_free_list; +static opl_voice_t *voice_alloced_list; // Track data for playing tracks: @@ -505,12 +507,42 @@ static opl_voice_t *GetFreeVoice(void) return NULL; } + // Remove from free list + result = voice_free_list; voice_free_list = voice_free_list->next; + // Add to allocated list + + result->next = voice_alloced_list; + voice_alloced_list = result; + return result; } +// Remove a voice from the allocated voices list. + +static void RemoveVoiceFromAllocedList(opl_voice_t *voice) +{ + opl_voice_t **rover; + + rover = &voice_alloced_list; + + // Search the list until we find the voice, then remove it. + + while (*rover != NULL) + { + if (*rover == voice) + { + *rover = voice->next; + voice->next = NULL; + break; + } + + rover = &(*rover)->next; + } +} + // Release a voice back to the freelist. static void ReleaseVoice(opl_voice_t *voice) @@ -520,6 +552,10 @@ static void ReleaseVoice(opl_voice_t *voice) voice->channel = NULL; voice->note = 0; + // Remove from alloced list. + + RemoveVoiceFromAllocedList(voice); + // Search to the end of the freelist (This is how Doom behaves!) rover = &voice_free_list; @@ -831,6 +867,66 @@ static void KeyOffEvent(opl_track_data_t *track, midi_event_t *event) } } +// When all voices are in use, we must discard an existing voice to +// play a new note. Find and free an existing voice. The channel +// passed to the function is the channel for the new note to be +// played. + +static opl_voice_t *ReplaceExistingVoice(opl_channel_data_t *channel) +{ + opl_voice_t *rover; + opl_voice_t *result; + + // Check the allocated voices, if we find an instrument that is + // of a lower priority to the new instrument, discard it. + // Priority is determined by MIDI instrument number; old + + result = NULL; + + for (rover = voice_alloced_list; rover != NULL; rover = rover->next) + { + if (rover->current_instr > channel->instrument) + { + result = rover; + break; + } + } + + // If we didn't find a voice, find an existing voice being used to + // play a note on the same channel, and use that. + + if (result == NULL) + { + for (rover = voice_alloced_list; rover != NULL; rover = rover->next) + { + if (rover->channel == channel) + { + result = rover; + break; + } + } + } + + // Still nothing found? Give up and just use the first voice in + // the list. + + if (result == NULL) + { + result = voice_alloced_list; + } + + // Stop playing this voice playing and release it back to the free + // list. + + VoiceKeyOff(result); + ReleaseVoice(result); + + // Re-allocate the voice again and return it. + + return GetFreeVoice(); +} + + static unsigned int FrequencyForVoice(opl_voice_t *voice) { unsigned int freq_index; @@ -899,10 +995,21 @@ static void VoiceKeyOn(opl_channel_data_t *channel, voice = GetFreeVoice(); + // If there are no more voices left, we must decide what to do. + // If this is the first voice of the instrument, free an existing + // voice and use that. Otherwise, if this is the second voice, + // it isn't as important; just discard it. + if (voice == NULL) { - printf("\tno free voice for voice %i of instrument\n", instrument_voice); - return; + if (instrument_voice == 0) + { + voice = ReplaceExistingVoice(channel); + } + else + { + return; + } } voice->channel = channel; -- cgit v1.2.3 From 6132a2a3e365ca36b7a1a7d721289763bac971dd Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 12 Sep 2009 21:13:08 +0000 Subject: Vanilla Doom plays d_introa, not d_intro. Subversion-branch: /branches/opl-branch Subversion-revision: 1670 --- src/d_main.c | 2 +- src/i_oplmusic.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index c59a8fb7..46dd19ac 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -521,7 +521,7 @@ void D_DoAdvanceDemo (void) if ( gamemode == commercial ) S_StartMusic(mus_dm2ttl); else - S_StartMusic (mus_intro); + S_StartMusic (mus_introa); break; case 1: G_DeferedPlayDemo(DEH_String("demo1")); diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index bed81109..a6c03f6f 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -1513,7 +1513,7 @@ static void *I_OPL_RegisterSong(void *data, int len) // remove file now - remove(filename); +// remove(filename); Z_Free(filename); -- cgit v1.2.3 From 2b40842eeca7386f6bfe9b1b39760c8a4d69ca71 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 12 Sep 2009 23:04:57 +0000 Subject: Remove separate volume calculations for non-feedback (separate operator) voices. Doom writes the same value to both operators. Subversion-branch: /branches/opl-branch Subversion-revision: 1671 --- src/i_oplmusic.c | 69 ++++++++++++++++++++++++-------------------------------- 1 file changed, 29 insertions(+), 40 deletions(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index a6c03f6f..ce255ff7 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -150,9 +150,8 @@ struct opl_voice_s // The volume of the note being played on this channel. unsigned int note_volume; - // The current volume that has been set for this channel. - unsigned int carrier_volume; - unsigned int modulator_volume; + // The current volume (register value) that has been set for this channel. + unsigned int reg_volume; // Next in linked list; a voice is always either in the // free list or the allocated list. @@ -636,42 +635,15 @@ static void SetVoiceInstrument(opl_voice_t *voice, // Hack to force a volume update. - voice->carrier_volume = 999; - voice->modulator_volume = 999; -} - -// Calculate the volume level to use for a given operator. - -static void SetOperatorVolume(genmidi_op_t *op, unsigned int volume, - unsigned int opnum, - unsigned int *current_volume) -{ - unsigned int op_volume; - unsigned int reg_volume; - - // The volume of each instrument can be controlled via GENMIDI: - - op_volume = 0x3f - op->level; - - // The volume value to use in the register: - - reg_volume = (op_volume * volume) / 128; - reg_volume = (0x3f - reg_volume) | op->scale; - - // Update the register, if neccessary: - - if (*current_volume != reg_volume) - { - *current_volume = reg_volume; - - WriteRegister(OPL_REGS_LEVEL + opnum, reg_volume); - } + voice->reg_volume = 999; } static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume) { genmidi_voice_t *opl_voice; unsigned int full_volume; + unsigned int op_volume; + unsigned int reg_volume; voice->note_volume = volume; @@ -682,16 +654,33 @@ static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume) full_volume = (volume_mapping_table[voice->note_volume] * volume_mapping_table[voice->channel->volume]) / 127; - SetOperatorVolume(&opl_voice->carrier, full_volume, - voice->op2, &voice->carrier_volume); + // The volume of each instrument can be controlled via GENMIDI: + + op_volume = 0x3f - opl_voice->carrier.level; - // If we are using non-modulated feedback mode, we must set the - // volume for both voices. + // The volume value to use in the register: - if ((opl_voice->feedback & 0x01) != 0) + reg_volume = (op_volume * full_volume) / 128; + reg_volume = (0x3f - reg_volume) | opl_voice->carrier.scale; + + // Update the volume register(s) if necessary. + + if (reg_volume != voice->reg_volume) { - SetOperatorVolume(&opl_voice->modulator, full_volume, - voice->op1, &voice->modulator_volume); + voice->reg_volume = reg_volume; + + WriteRegister(OPL_REGS_LEVEL + voice->op2, reg_volume); + + // If we are using non-modulated feedback mode, we must set the + // volume for both voices. + // Note that the same register volume value is written for + // both voices, always calculated from the carrier's level + // value. + + if ((opl_voice->feedback & 0x01) != 0) + { + WriteRegister(OPL_REGS_LEVEL + voice->op1, reg_volume); + } } } -- cgit v1.2.3 From 7e2eaa105e270586e81c04fdf61bcafd7c96a34c Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 13 Sep 2009 00:15:24 +0000 Subject: Change intro/introa fix to be more accurate: Doom uses d_intro, but transforms this to d_introa when using OPL playback (thanks entryway) Subversion-branch: /branches/opl-branch Subversion-revision: 1672 --- src/d_main.c | 2 +- src/s_sound.c | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/d_main.c b/src/d_main.c index 46dd19ac..c59a8fb7 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -521,7 +521,7 @@ void D_DoAdvanceDemo (void) if ( gamemode == commercial ) S_StartMusic(mus_dm2ttl); else - S_StartMusic (mus_introa); + S_StartMusic (mus_intro); break; case 1: G_DeferedPlayDemo(DEH_String("demo1")); diff --git a/src/s_sound.c b/src/s_sound.c index f038e9cd..9e70ec73 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -804,6 +804,15 @@ void S_ChangeMusic(int musicnum, int looping) char namebuf[9]; void *handle; + // The Doom IWAD file has two versions of the intro music: d_intro + // and d_introa. The latter is used for OPL playback. + + if (musicnum == mus_intro && (snd_musicdevice == SNDDEVICE_ADLIB + || snd_musicdevice == SNDDEVICE_SB)) + { + musicnum = mus_introa; + } + if (musicnum <= mus_None || musicnum >= NUMMUSIC) { I_Error("Bad music number %d", musicnum); -- cgit v1.2.3 From 47282d2ca9ae99fb7bfa27408158df0544ac11b3 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Thu, 17 Sep 2009 20:20:36 +0000 Subject: Add OPL TODO list. Subversion-branch: /branches/opl-branch Subversion-revision: 1675 --- OPL-TODO | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 OPL-TODO diff --git a/OPL-TODO b/OPL-TODO new file mode 100644 index 00000000..594a2325 --- /dev/null +++ b/OPL-TODO @@ -0,0 +1,22 @@ + +opl-branch TODO list + +Needs research: + + * Some instruments have pitch offset down by one octave (eg. #36) + - what determines this? + * Strategy when no more voices are available is still wrong + * Scale levels don't exactly match Vanilla (off-by-one) + +Bad MIDIs: + + * deca.wad MAP01 + * gothicdm MAP05 + * Hell Revealed + +Other tasks: + + * Menu volume control + * Pause music + * Add option to select MIDI type in setup tool + -- cgit v1.2.3 From 13314576f8d5d8eec8bf413ca4b49255b9e1a6a2 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 19 Sep 2009 20:42:45 +0000 Subject: Add octave offset table, offset notes on specific instruments down by one octave, as per research. Subversion-branch: /branches/opl-branch Subversion-revision: 1678 --- src/i_oplmusic.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index ce255ff7..09d2ae42 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -304,6 +304,31 @@ static const unsigned int volume_mapping_table[] = { 124, 124, 125, 125, 126, 126, 127, 127 }; +// For octave offset table: + +static const unsigned int octave_offset_table[2][128] = { + { + 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, // 0-15 + 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31 + 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, // 32-47 + 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, // 48-63 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, // 64-79 + 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, // 80-95 + 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, // 96-111 + 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, // 112-127 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0-15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31 + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, // 32-47 + 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, // 48-63 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 64-79 + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80-95 + 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 96-111 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 112-127 + } +}; + static boolean music_initialised = false; //static boolean musicpaused = false; @@ -832,10 +857,12 @@ static void KeyOffEvent(opl_track_data_t *track, midi_event_t *event) unsigned int key; unsigned int i; +/* printf("note off: channel %i, %i, %i\n", event->data.channel.channel, event->data.channel.param1, event->data.channel.param2); +*/ channel = &track->channels[event->data.channel.channel]; key = event->data.channel.param1; @@ -921,8 +948,25 @@ static unsigned int FrequencyForVoice(opl_voice_t *voice) unsigned int freq_index; unsigned int octave; unsigned int sub_index; + unsigned int instr_num; + unsigned int note; + + note = voice->note; + + // What instrument number is this? + // Certain instruments have all notes offset down by one octave. + // Use the octave offset table to work out if this voice should + // be offset. + + instr_num = voice->current_instr - main_instrs; + + if (instr_num < 128 && note >= 12 + && octave_offset_table[voice->current_instr_voice][instr_num]) + { + note -= 12; + } - freq_index = 64 + 32 * voice->note + voice->channel->bend; + freq_index = 64 + 32 * note + voice->channel->bend; // If this is the second voice of a double voice instrument, the // frequency index can be adjusted by the fine tuning field. @@ -1037,10 +1081,12 @@ static void KeyOnEvent(opl_track_data_t *track, midi_event_t *event) unsigned int key; unsigned int volume; +/* printf("note on: channel %i, %i, %i\n", event->data.channel.channel, event->data.channel.param1, event->data.channel.param2); +*/ // The channel. @@ -1113,10 +1159,12 @@ static void ControllerEvent(opl_track_data_t *track, midi_event_t *event) unsigned int param; opl_channel_data_t *channel; +/* printf("change controller: channel %i, %i, %i\n", event->data.channel.channel, event->data.channel.param1, event->data.channel.param2); +*/ channel = &track->channels[event->data.channel.channel]; controller = event->data.channel.param1; -- cgit v1.2.3 From dd03cbbe2b688eac26011ac0ea0ada4047e5beb0 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 20 Sep 2009 20:47:17 +0000 Subject: Use the base note offset field to offset notes, not a fixed lookup table of instruments to offset. Subversion-branch: /branches/opl-branch Subversion-revision: 1680 --- src/i_oplmusic.c | 42 +++++------------------------------------- 1 file changed, 5 insertions(+), 37 deletions(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 09d2ae42..de126c63 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -34,6 +34,7 @@ #include "mus2mid.h" #include "deh_main.h" +#include "i_swap.h" #include "m_misc.h" #include "s_sound.h" #include "w_wad.h" @@ -304,31 +305,6 @@ static const unsigned int volume_mapping_table[] = { 124, 124, 125, 125, 126, 126, 127, 127 }; -// For octave offset table: - -static const unsigned int octave_offset_table[2][128] = { - { - 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, // 0-15 - 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31 - 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, // 32-47 - 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, // 48-63 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, // 64-79 - 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, // 80-95 - 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, // 96-111 - 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, // 112-127 - }, - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0-15 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31 - 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, // 32-47 - 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, // 48-63 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 64-79 - 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80-95 - 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 96-111 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 112-127 - } -}; - static boolean music_initialised = false; //static boolean musicpaused = false; @@ -945,26 +921,18 @@ static opl_voice_t *ReplaceExistingVoice(opl_channel_data_t *channel) static unsigned int FrequencyForVoice(opl_voice_t *voice) { + genmidi_voice_t *gm_voice; unsigned int freq_index; unsigned int octave; unsigned int sub_index; - unsigned int instr_num; unsigned int note; note = voice->note; - // What instrument number is this? - // Certain instruments have all notes offset down by one octave. - // Use the octave offset table to work out if this voice should - // be offset. - - instr_num = voice->current_instr - main_instrs; + // Apply note offset: - if (instr_num < 128 && note >= 12 - && octave_offset_table[voice->current_instr_voice][instr_num]) - { - note -= 12; - } + gm_voice = &voice->current_instr->voices[voice->current_instr_voice]; + note += (signed short) SHORT(gm_voice->base_note_offset); freq_index = 64 + 32 * note + voice->channel->bend; -- cgit v1.2.3 From b600e190fd4593faed8c14da776c991e99e8c86c Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 20 Sep 2009 20:52:03 +0000 Subject: Update OPL-TODO. Subversion-branch: /branches/opl-branch Subversion-revision: 1682 --- OPL-TODO | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/OPL-TODO b/OPL-TODO index 594a2325..8ca6a4b5 100644 --- a/OPL-TODO +++ b/OPL-TODO @@ -3,8 +3,6 @@ opl-branch TODO list Needs research: - * Some instruments have pitch offset down by one octave (eg. #36) - - what determines this? * Strategy when no more voices are available is still wrong * Scale levels don't exactly match Vanilla (off-by-one) @@ -12,6 +10,7 @@ Bad MIDIs: * deca.wad MAP01 * gothicdm MAP05 + * tnt.wad MAP30 * Hell Revealed Other tasks: -- cgit v1.2.3 From f894f24bcd63dc8b2a0604a9b659dd95aebbb384 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 20 Sep 2009 21:19:37 +0000 Subject: Avoid possible overflow due to base note offset. Subversion-branch: /branches/opl-branch Subversion-revision: 1683 --- src/i_oplmusic.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index de126c63..aa079614 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -934,6 +934,13 @@ static unsigned int FrequencyForVoice(opl_voice_t *voice) gm_voice = &voice->current_instr->voices[voice->current_instr_voice]; note += (signed short) SHORT(gm_voice->base_note_offset); + // Avoid possible overflow due to base note offset: + + if (note > 0x7f) + { + note = voice->note; + } + freq_index = 64 + 32 * note + voice->channel->bend; // If this is the second voice of a double voice instrument, the -- cgit v1.2.3 From fcf4a7241a742e7f3d8189c40ad852d48c674612 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 20 Sep 2009 21:21:30 +0000 Subject: Remove old test code. Subversion-branch: /branches/opl-branch Subversion-revision: 1684 --- src/i_oplmusic.c | 47 ----------------------------------------------- 1 file changed, 47 deletions(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index aa079614..b8e93534 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -43,8 +43,6 @@ #include "opl.h" #include "midifile.h" -//#define TEST - #define MAXMIDLENGTH (96 * 1024) #define GENMIDI_NUM_INSTRS 128 @@ -716,9 +714,6 @@ static void I_OPL_ShutdownMusic(void) { if (music_initialised) { -#ifdef TEST - InitRegisters(); -#endif OPL_Shutdown(); // Release GENMIDI lump @@ -729,30 +724,6 @@ static void I_OPL_ShutdownMusic(void) } } -#ifdef TEST -static void TestCallback(void *arg) -{ - opl_voice_t *voice = arg; - int note; - int wait_time; - - // Set level: - WriteRegister(OPL_REGS_LEVEL + voice->op2, 0); - - // Note off: - - WriteRegister(OPL_REGS_FREQ_2 + voice->index, 0x00); - // Note on: - - note = (rand() % (0x2ae - 0x16b)) + 0x16b; - WriteRegister(OPL_REGS_FREQ_1 + voice->index, note & 0xff); - WriteRegister(OPL_REGS_FREQ_2 + voice->index, 0x30 + (note >> 8)); - - wait_time = (rand() % 700) + 50; - OPL_SetCallback(wait_time, TestCallback, arg); -} -#endif - // Initialise music subsystem static boolean I_OPL_InitMusic(void) @@ -789,24 +760,6 @@ static boolean I_OPL_InitMusic(void) init_stage_reg_writes = false; -#ifdef TEST - { - int i; - opl_voice_t *voice; - int instr_num; - - for (i=0; i<3; ++i) - { - voice = GetFreeVoice(); - instr_num = rand() % 100; - - SetVoiceInstrument(voice, &main_instrs[instr_num], 0); - - OPL_SetCallback(0, TestCallback, voice); - } - } -#endif - music_initialised = true; return true; -- cgit v1.2.3 From 4b598f3d40e34b5d786c9236f891fbe2337c1b66 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 20 Sep 2009 21:25:59 +0000 Subject: Implement volume control. Subversion-branch: /branches/opl-branch Subversion-revision: 1685 --- OPL-TODO | 1 - src/i_oplmusic.c | 16 +++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/OPL-TODO b/OPL-TODO index 8ca6a4b5..7b9be01c 100644 --- a/OPL-TODO +++ b/OPL-TODO @@ -15,7 +15,6 @@ Bad MIDIs: Other tasks: - * Menu volume control * Pause music * Add option to select MIDI type in setup tool diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index b8e93534..4ac52754 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -651,7 +651,8 @@ static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume) // Multiply note volume and channel volume to get the actual volume. full_volume = (volume_mapping_table[voice->note_volume] - * volume_mapping_table[voice->channel->volume]) / 127; + * volume_mapping_table[voice->channel->volume] + * volume_mapping_table[current_music_volume]) / (127 * 127); // The volume of each instrument can be controlled via GENMIDI: @@ -769,8 +770,21 @@ static boolean I_OPL_InitMusic(void) static void I_OPL_SetMusicVolume(int volume) { + unsigned int i; + // Internal state variable. + current_music_volume = volume; + + // Update the volume of all voices. + + for (i=0; iset_paused_func(paused); + } +} + diff --git a/opl/opl.h b/opl/opl.h index a0998404..f5b93a64 100644 --- a/opl/opl.h +++ b/opl/opl.h @@ -96,5 +96,9 @@ void OPL_Unlock(void); void OPL_Delay(unsigned int ms); +// Pause the OPL callbacks. + +void OPL_SetPaused(int paused); + #endif diff --git a/opl/opl_internal.h b/opl/opl_internal.h index 384b96f8..78cbe7b2 100644 --- a/opl/opl_internal.h +++ b/opl/opl_internal.h @@ -39,6 +39,7 @@ typedef void (*opl_set_callback_func)(unsigned int ms, typedef void (*opl_clear_callbacks_func)(void); typedef void (*opl_lock_func)(void); typedef void (*opl_unlock_func)(void); +typedef void (*opl_set_paused_func)(int paused); typedef struct { @@ -52,6 +53,7 @@ typedef struct opl_clear_callbacks_func clear_callbacks_func; opl_lock_func lock_func; opl_unlock_func unlock_func; + opl_set_paused_func set_paused_func; } opl_driver_t; #endif /* #ifndef OPL_INTERNAL_H */ diff --git a/opl/opl_linux.c b/opl/opl_linux.c index 8a61dbf7..089192c9 100644 --- a/opl/opl_linux.c +++ b/opl/opl_linux.c @@ -94,7 +94,8 @@ opl_driver_t opl_linux_driver = OPL_Timer_SetCallback, OPL_Timer_ClearCallbacks, OPL_Timer_Lock, - OPL_Timer_Unlock + OPL_Timer_Unlock, + OPL_Timer_SetPaused }; #endif /* #ifdef HAVE_IOPERM */ diff --git a/opl/opl_sdl.c b/opl/opl_sdl.c index ffbe6820..e38f9f6e 100644 --- a/opl/opl_sdl.c +++ b/opl/opl_sdl.c @@ -60,6 +60,15 @@ static SDL_mutex *callback_queue_mutex = NULL; static int current_time; +// If non-zero, playback is currently paused. + +static int opl_sdl_paused; + +// Time offset (in samples) due to the fact that callbacks +// were previously paused. + +static unsigned int pause_offset; + // OPL software emulator structure. static FM_OPL *opl_emulator = NULL; @@ -96,11 +105,16 @@ static void AdvanceTime(unsigned int nsamples) current_time += nsamples; + if (opl_sdl_paused) + { + pause_offset += nsamples; + } + // Are there callbacks to invoke now? Keep invoking them // until there are none more left. while (!OPL_Queue_IsEmpty(callback_queue) - && current_time >= OPL_Queue_Peek(callback_queue)) + && current_time >= OPL_Queue_Peek(callback_queue) + pause_offset) { // Pop the callback from the queue to invoke it. @@ -180,13 +194,13 @@ static void OPL_Mix_Callback(void *udata, // the callback queue must be invoked. We can then fill the // buffer with this many samples. - if (OPL_Queue_IsEmpty(callback_queue)) + if (opl_sdl_paused || OPL_Queue_IsEmpty(callback_queue)) { nsamples = buffer_len - filled; } else { - next_callback_time = OPL_Queue_Peek(callback_queue); + next_callback_time = OPL_Queue_Peek(callback_queue) + pause_offset; nsamples = next_callback_time - current_time; @@ -260,7 +274,7 @@ static void TimerHandler(int channel, double interval_seconds) SDL_LockMutex(callback_queue_mutex); OPL_Queue_Push(callback_queue, TimerOver, (void *) channel, - current_time + interval_samples); + current_time - pause_offset + interval_samples); SDL_UnlockMutex(callback_queue_mutex); } @@ -297,6 +311,9 @@ static int OPL_SDL_Init(unsigned int port_base) sdl_was_initialised = 0; } + opl_sdl_paused = 0; + pause_offset = 0; + // Queue structure of callbacks to invoke. callback_queue = OPL_Queue_Create(); @@ -370,7 +387,7 @@ static void OPL_SDL_SetCallback(unsigned int ms, { SDL_LockMutex(callback_queue_mutex); OPL_Queue_Push(callback_queue, callback, data, - current_time + (ms * mixing_freq) / 1000); + current_time - pause_offset + (ms * mixing_freq) / 1000); SDL_UnlockMutex(callback_queue_mutex); } @@ -391,6 +408,11 @@ static void OPL_SDL_Unlock(void) SDL_UnlockMutex(callback_mutex); } +static void OPL_SDL_SetPaused(int paused) +{ + opl_sdl_paused = paused; +} + opl_driver_t opl_sdl_driver = { "SDL", @@ -401,6 +423,7 @@ opl_driver_t opl_sdl_driver = OPL_SDL_SetCallback, OPL_SDL_ClearCallbacks, OPL_SDL_Lock, - OPL_SDL_Unlock + OPL_SDL_Unlock, + OPL_SDL_SetPaused }; diff --git a/opl/opl_timer.c b/opl/opl_timer.c index e254a5e2..35b2092f 100644 --- a/opl/opl_timer.c +++ b/opl/opl_timer.c @@ -41,6 +41,15 @@ static SDL_Thread *timer_thread = NULL; static thread_state_t timer_thread_state; static int current_time; +// If non-zero, callbacks are currently paused. + +static int opl_timer_paused; + +// Offset in milliseconds to adjust time due to the fact that playback +// was paused. + +static unsigned int pause_offset = 0; + // Queue of callbacks waiting to be invoked. // The callback queue mutex is held while the callback queue structure // or current_time is being accessed. @@ -59,6 +68,17 @@ static SDL_mutex *timer_mutex; static int CallbackWaiting(unsigned int *next_time) { + // If paused, just wait in 50ms increments until unpaused. + // Update pause_offset so after we unpause, the callback + // times will be right. + + if (opl_timer_paused) + { + *next_time = current_time + 50; + pause_offset += 50; + return 0; + } + // If there are no queued callbacks, sleep for 50ms at a time // until a callback is added. @@ -72,7 +92,7 @@ static int CallbackWaiting(unsigned int *next_time) // If the time for the callback has not yet arrived, // we must sleep until the callback time. - *next_time = OPL_Queue_Peek(callback_queue); + *next_time = OPL_Queue_Peek(callback_queue) + pause_offset; return *next_time <= current_time; } @@ -169,6 +189,8 @@ int OPL_Timer_StartThread(void) timer_thread_state = THREAD_STATE_RUNNING; current_time = SDL_GetTicks(); + opl_timer_paused = 0; + pause_offset = 0; timer_thread = SDL_CreateThread(ThreadFunction, NULL); @@ -198,7 +220,8 @@ void OPL_Timer_StopThread(void) void OPL_Timer_SetCallback(unsigned int ms, opl_callback_t callback, void *data) { SDL_LockMutex(callback_queue_mutex); - OPL_Queue_Push(callback_queue, callback, data, current_time + ms); + OPL_Queue_Push(callback_queue, callback, data, + current_time + ms - pause_offset); SDL_UnlockMutex(callback_queue_mutex); } @@ -219,3 +242,10 @@ void OPL_Timer_Unlock(void) SDL_UnlockMutex(timer_mutex); } +void OPL_Timer_SetPaused(int paused) +{ + SDL_LockMutex(callback_queue_mutex); + opl_timer_paused = paused; + SDL_UnlockMutex(callback_queue_mutex); +} + diff --git a/opl/opl_timer.h b/opl/opl_timer.h index e8657a90..f03fc499 100644 --- a/opl/opl_timer.h +++ b/opl/opl_timer.h @@ -36,6 +36,7 @@ void OPL_Timer_SetCallback(unsigned int ms, void OPL_Timer_ClearCallbacks(void); void OPL_Timer_Lock(void); void OPL_Timer_Unlock(void); +void OPL_Timer_SetPaused(int paused); #endif /* #ifndef OPL_TIMER_H */ diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 4ac52754..7bfdc04d 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -1363,10 +1363,28 @@ static void I_OPL_PlaySong(void *handle, int looping) static void I_OPL_PauseSong(void) { + unsigned int i; + if (!music_initialised) { return; } + + // Pause OPL callbacks. + + OPL_SetPaused(1); + + // Turn off all main instrument voices (not percussion). + // This is what Vanilla does. + + for (i=0; i +#include +#include +#include + +#include +#include +#include + +#include "opl.h" +#include "opl_internal.h" +#include "opl_timer.h" + +static unsigned int opl_port_base; + +static int OPL_OpenBSD_Init(unsigned int port_base) +{ + // Try to get permissions: + + if (i386_iopl(1) < 0) + { + fprintf(stderr, "Failed to get raise I/O privilege level: " + "check that you are running as root.\n"); + return 0; + } + + opl_port_base = port_base; + + // Start callback thread + + if (!OPL_Timer_StartThread()) + { + i386_iopl(0); + return 0; + } + + return 1; +} + +static void OPL_OpenBSD_Shutdown(void) +{ + // Stop callback thread + + OPL_Timer_StopThread(); + + // Release I/O port permissions: + + i386_iopl(0); +} + +static unsigned int OPL_OpenBSD_PortRead(opl_port_t port) +{ + return inb(opl_port_base + port); +} + +static void OPL_OpenBSD_PortWrite(opl_port_t port, unsigned int value) +{ + outb(opl_port_base + port, value); +} + +opl_driver_t opl_openbsd_driver = +{ + "OpenBSD", + OPL_OpenBSD_Init, + OPL_OpenBSD_Shutdown, + OPL_OpenBSD_PortRead, + OPL_OpenBSD_PortWrite, + OPL_Timer_SetCallback, + OPL_Timer_ClearCallbacks, + OPL_Timer_Lock, + OPL_Timer_Unlock, + OPL_Timer_SetPaused +}; + +#endif /* #ifdef HAVE_IOPERM */ + -- cgit v1.2.3 From 808b78153c3d3bfe1fb692106323731d17e49489 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 26 Sep 2009 20:01:58 +0000 Subject: Don't use snd_mport to control OPL base I/O port; Vanilla doesn't do this. Subversion-branch: /branches/opl-branch Subversion-revision: 1691 --- src/i_oplmusic.c | 4 ++-- src/m_config.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 7bfdc04d..dd62076d 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -336,7 +336,7 @@ static boolean init_stage_reg_writes = false; // Configuration file variable, containing the port number for the // adlib chip. -int snd_mport = 0x388; +int opl_io_port = 0x388; static unsigned int GetStatus(void) { @@ -729,7 +729,7 @@ static void I_OPL_ShutdownMusic(void) static boolean I_OPL_InitMusic(void) { - if (!OPL_Init(snd_mport)) + if (!OPL_Init(opl_io_port)) { return false; } diff --git a/src/m_config.c b/src/m_config.c index 0d0faea0..4f789845 100644 --- a/src/m_config.c +++ b/src/m_config.c @@ -184,7 +184,6 @@ extern int vanilla_demo_limit; extern int snd_musicdevice; extern int snd_sfxdevice; extern int snd_samplerate; -extern int snd_mport; // controls whether to use libsamplerate for sample rate conversions @@ -197,6 +196,7 @@ extern int use_libsamplerate; static int snd_sbport = 0; static int snd_sbirq = 0; static int snd_sbdma = 0; +static int snd_mport = 0; typedef enum { -- cgit v1.2.3 From dce2c95f05b8f5ed734d1a1b75ccd7bfb2260557 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 26 Sep 2009 23:52:41 +0000 Subject: Move register read/write code into OPL library. Detect OPL in the library code, so that we fall back to software emulation if we have port access but an OPL is not detected. Fix detection of ioperm in configure. Subversion-branch: /branches/opl-branch Subversion-revision: 1692 --- configure.in | 2 +- opl/examples/droplay.c | 31 ------- opl/opl.c | 244 +++++++++++++++++++++++++++++++++++++++++++++++-- opl/opl.h | 31 ++++++- opl/opl_obsd.c | 2 +- src/i_oplmusic.c | 178 +++--------------------------------- 6 files changed, 282 insertions(+), 206 deletions(-) diff --git a/configure.in b/configure.in index 17ec9dc4..d8bad56e 100644 --- a/configure.in +++ b/configure.in @@ -70,7 +70,7 @@ AC_SDL_MAIN_WORKAROUND([ AC_CHECK_LIB(samplerate, src_new) AC_CHECK_HEADERS([linux/kd.h dev/isa/spkrio.h dev/speaker/speaker.h]) - AC_CHECK_FUNCS(mmap sched_setaffinity) + AC_CHECK_FUNCS(mmap sched_setaffinity ioperm) # OpenBSD I/O i386 library for I/O port access. diff --git a/opl/examples/droplay.c b/opl/examples/droplay.c index d53a427b..36f5c3c0 100644 --- a/opl/examples/droplay.c +++ b/opl/examples/droplay.c @@ -73,30 +73,6 @@ void ClearAllRegs(void) } } -// Detect an OPL chip. - -int DetectOPL(void) -{ - int val1, val2; - - WriteReg(OPL_REG_TIMER_CTRL, 0x60); - WriteReg(OPL_REG_TIMER_CTRL, 0x80); - - val1 = OPL_ReadPort(OPL_REGISTER_PORT) & 0xe0; - - WriteReg(OPL_REG_TIMER1, 0xff); - WriteReg(OPL_REG_TIMER_CTRL, 0x21); - - OPL_Delay(1); - - val2 = OPL_ReadPort(OPL_REGISTER_PORT) & 0xe0; - - WriteReg(OPL_REG_TIMER_CTRL, 0x60); - WriteReg(OPL_REG_TIMER_CTRL, 0x80); - - return val1 == 0 && val2 == 0xc0; -} - void Init(void) { if (SDL_Init(SDL_INIT_TIMER) < 0) @@ -110,12 +86,6 @@ void Init(void) fprintf(stderr, "Unable to initialise OPL layer\n"); exit(-1); } - - if (!DetectOPL()) - { - fprintf(stderr, "Adlib not detected\n"); - exit(-1); - } } void Shutdown(void) @@ -236,7 +206,6 @@ int main(int argc, char *argv[]) } Init(); - ClearAllRegs(); PlayFile(argv[1]); diff --git a/opl/opl.c b/opl/opl.c index f28cd63f..00c9b04a 100644 --- a/opl/opl.c +++ b/opl/opl.c @@ -1,4 +1,4 @@ -// Emacs style mode select -*- C++ -*- +// Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // Copyright(C) 2009 Simon Howard @@ -28,6 +28,10 @@ #include #include +#ifdef _WIN32_WCE +#include "libc_wince.h" +#endif + #include "SDL.h" #include "opl.h" @@ -56,25 +60,113 @@ static opl_driver_t *drivers[] = }; static opl_driver_t *driver = NULL; +static int init_stage_reg_writes = 1; -int OPL_Init(unsigned int port_base) +// +// Init/shutdown code. +// + +// Initialize the specified driver and detect an OPL chip. Returns +// true if an OPL is detected. + +static int InitDriver(opl_driver_t *_driver, unsigned int port_base) { - int i; + // Initialize the driver. + + if (!_driver->init_func(port_base)) + { + return 0; + } + + // The driver was initialized okay, so we now have somewhere + // to write to. It doesn't mean there's an OPL chip there, + // though. Perform the detection sequence to make sure. + // (it's done twice, like how Doom does it). + + driver = _driver; + init_stage_reg_writes = 1; + + if (!OPL_Detect() || !OPL_Detect()) + { + printf("OPL_Init: No OPL detected using '%s' driver.\n", _driver->name); + _driver->shutdown_func(); + driver = NULL; + return 0; + } - // Try drivers until we find a working one: + // Initialize all registers. + + OPL_InitRegisters(); + + init_stage_reg_writes = 0; + + printf("OPL_Init: Using driver '%s'.\n", driver->name); + + return 1; +} + +// Find a driver automatically by trying each in the list. + +static int AutoSelectDriver(unsigned int port_base) +{ + int i; for (i=0; drivers[i] != NULL; ++i) { - if (drivers[i]->init_func(port_base)) + if (InitDriver(drivers[i], port_base)) { - driver = drivers[i]; return 1; } } + printf("OPL_Init: Failed to find a working driver.\n"); + return 0; } +// Initialize the OPL library. Returns true if initialized +// successfully. + +int OPL_Init(unsigned int port_base) +{ + char *driver_name; + int i; + + driver_name = getenv("OPL_DRIVER"); + + if (driver_name != NULL) + { + // Search the list until we find the driver with this name. + + for (i=0; drivers[i] != NULL; ++i) + { + if (!strcmp(driver_name, drivers[i]->name)) + { + if (InitDriver(drivers[i], port_base)) + { + return 1; + } + else + { + printf("OPL_Init: Failed to initialize " + "driver: '%s'.\n", driver_name); + return 0; + } + } + } + + printf("OPL_Init: unknown driver: '%s'.\n", driver_name); + + return 0; + } + else + { + return AutoSelectDriver(port_base); + } +} + +// Shut down the OPL library. + void OPL_Shutdown(void) { if (driver != NULL) @@ -115,6 +207,146 @@ unsigned int OPL_ReadPort(opl_port_t port) } } +// +// Higher-level functions, based on the lower-level functions above +// (register write, etc). +// + +unsigned int OPL_ReadStatus(void) +{ + return OPL_ReadPort(OPL_REGISTER_PORT); +} + +// Write an OPL register value + +void OPL_WriteRegister(int reg, int value) +{ + int i; + + OPL_WritePort(OPL_REGISTER_PORT, reg); + + // For timing, read the register port six times after writing the + // register number to cause the appropriate delay + + for (i=0; i<6; ++i) + { + // An oddity of the Doom OPL code: at startup initialisation, + // the spacing here is performed by reading from the register + // port; after initialisation, the data port is read, instead. + + if (init_stage_reg_writes) + { + OPL_ReadPort(OPL_REGISTER_PORT); + } + else + { + OPL_ReadPort(OPL_DATA_PORT); + } + } + + OPL_WritePort(OPL_DATA_PORT, value); + + // Read the register port 24 times after writing the value to + // cause the appropriate delay + + for (i=0; i<24; ++i) + { + OPL_ReadStatus(); + } +} + +// Detect the presence of an OPL chip + +int OPL_Detect(void) +{ + int result1, result2; + int i; + + // Reset both timers: + OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60); + + // Enable interrupts: + OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80); + + // Read status + result1 = OPL_ReadStatus(); + + // Set timer: + OPL_WriteRegister(OPL_REG_TIMER1, 0xff); + + // Start timer 1: + OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x21); + + // Wait for 80 microseconds + // This is how Doom does it: + + for (i=0; i<200; ++i) + { + OPL_ReadStatus(); + } + + OPL_Delay(1); + + // Read status + result2 = OPL_ReadStatus(); + + // Reset both timers: + OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60); + + // Enable interrupts: + OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80); + + return (result1 & 0xe0) == 0x00 + && (result2 & 0xe0) == 0xc0; +} + +// Initialize registers on startup + +void OPL_InitRegisters(void) +{ + int r; + + // Initialize level registers + + for (r=OPL_REGS_LEVEL; r <= OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r) + { + OPL_WriteRegister(r, 0x3f); + } + + // Initialize other registers + // These two loops write to registers that actually don't exist, + // but this is what Doom does ... + // Similarly, the <= is also intenational. + + for (r=OPL_REGS_ATTACK; r <= OPL_REGS_WAVEFORM + OPL_NUM_OPERATORS; ++r) + { + OPL_WriteRegister(r, 0x00); + } + + // More registers ... + + for (r=1; r < OPL_REGS_LEVEL; ++r) + { + OPL_WriteRegister(r, 0x00); + } + + // Re-initialize the low registers: + + // Reset both timers and enable interrupts: + OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60); + OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80); + + // "Allow FM chips to control the waveform of each operator": + OPL_WriteRegister(OPL_REG_WAVEFORM_ENABLE, 0x20); + + // Keyboard split point on (?) + OPL_WriteRegister(OPL_REG_FM_MODE, 0x40); +} + +// +// Timer functions. +// + void OPL_SetCallback(unsigned int ms, opl_callback_t callback, void *data) { if (driver != NULL) diff --git a/opl/opl.h b/opl/opl.h index f5b93a64..9f5d0a9f 100644 --- a/opl/opl.h +++ b/opl/opl.h @@ -58,7 +58,11 @@ typedef enum #define OPL_REGS_FREQ_2 0xB0 #define OPL_REGS_FEEDBACK 0xC0 -// Initialise the OPL subsystem. +// +// Low-level functions. +// + +// Initialize the OPL subsystem. int OPL_Init(unsigned int port_base); @@ -74,6 +78,31 @@ void OPL_WritePort(opl_port_t port, unsigned int value); unsigned int OPL_ReadPort(opl_port_t port); +// +// Higher-level functions. +// + +// Read the cuurrent status byte of the OPL chip. + +unsigned int OPL_ReadStatus(void); + +// Write to an OPL register. + +void OPL_WriteRegister(int reg, int value); + +// Perform a detection sequence to determine that an +// OPL chip is present. + +int OPL_Detect(void); + +// Initialize all registers, performed on startup. + +void OPL_InitRegisters(void); + +// +// Timer callback functions. +// + // Set a timer callback. After the specified number of milliseconds // have elapsed, the callback will be invoked. diff --git a/opl/opl_obsd.c b/opl/opl_obsd.c index a3a22ab8..82d9186d 100644 --- a/opl/opl_obsd.c +++ b/opl/opl_obsd.c @@ -101,5 +101,5 @@ opl_driver_t opl_openbsd_driver = OPL_Timer_SetPaused }; -#endif /* #ifdef HAVE_IOPERM */ +#endif /* #ifdef HAVE_LIBI386 */ diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index dd62076d..e9e722e4 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -326,149 +326,11 @@ static unsigned int num_tracks; static unsigned int running_tracks = 0; static boolean song_looping; -// In the initialisation stage, register writes are spaced by reading -// from the register port (0). After initialisation, spacing is -// peformed by reading from the data port instead. I have no idea -// why. - -static boolean init_stage_reg_writes = false; - // Configuration file variable, containing the port number for the // adlib chip. int opl_io_port = 0x388; -static unsigned int GetStatus(void) -{ - return OPL_ReadPort(OPL_REGISTER_PORT); -} - -// Write an OPL register value - -static void WriteRegister(int reg, int value) -{ - int i; - - OPL_WritePort(OPL_REGISTER_PORT, reg); - - // For timing, read the register port six times after writing the - // register number to cause the appropriate delay - - for (i=0; i<6; ++i) - { - // An oddity of the Doom OPL code: at startup initialisation, - // the spacing here is performed by reading from the register - // port; after initialisation, the data port is read, instead. - - if (init_stage_reg_writes) - { - OPL_ReadPort(OPL_REGISTER_PORT); - } - else - { - OPL_ReadPort(OPL_DATA_PORT); - } - } - - OPL_WritePort(OPL_DATA_PORT, value); - - // Read the register port 25 times after writing the value to - // cause the appropriate delay - - for (i=0; i<24; ++i) - { - GetStatus(); - } -} - -// Detect the presence of an OPL chip - -static boolean DetectOPL(void) -{ - int result1, result2; - int i; - - // Reset both timers: - WriteRegister(OPL_REG_TIMER_CTRL, 0x60); - - // Enable interrupts: - WriteRegister(OPL_REG_TIMER_CTRL, 0x80); - - // Read status - result1 = GetStatus(); - - // Set timer: - WriteRegister(OPL_REG_TIMER1, 0xff); - - // Start timer 1: - WriteRegister(OPL_REG_TIMER_CTRL, 0x21); - - // Wait for 80 microseconds - // This is how Doom does it: - - for (i=0; i<200; ++i) - { - GetStatus(); - } - - OPL_Delay(1); - - // Read status - result2 = GetStatus(); - - // Reset both timers: - WriteRegister(OPL_REG_TIMER_CTRL, 0x60); - - // Enable interrupts: - WriteRegister(OPL_REG_TIMER_CTRL, 0x80); - - return (result1 & 0xe0) == 0x00 - && (result2 & 0xe0) == 0xc0; -} - -// Initialise registers on startup - -static void InitRegisters(void) -{ - int r; - - // Initialise level registers - - for (r=OPL_REGS_LEVEL; r <= OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r) - { - WriteRegister(r, 0x3f); - } - - // Initialise other registers - // These two loops write to registers that actually don't exist, - // but this is what Doom does ... - // Similarly, the <= is also intenational. - - for (r=OPL_REGS_ATTACK; r <= OPL_REGS_WAVEFORM + OPL_NUM_OPERATORS; ++r) - { - WriteRegister(r, 0x00); - } - - // More registers ... - - for (r=1; r < OPL_REGS_LEVEL; ++r) - { - WriteRegister(r, 0x00); - } - - // Re-initialise the low registers: - - // Reset both timers and enable interrupts: - WriteRegister(OPL_REG_TIMER_CTRL, 0x60); - WriteRegister(OPL_REG_TIMER_CTRL, 0x80); - - // "Allow FM chips to control the waveform of each operator": - WriteRegister(OPL_REG_WAVEFORM_ENABLE, 0x20); - - // Keyboard split point on (?) - WriteRegister(OPL_REG_FM_MODE, 0x40); -} - // Load instrument table from GENMIDI lump: static boolean LoadInstrumentTable(void) @@ -584,11 +446,11 @@ static void LoadOperatorData(int operator, genmidi_op_t *data, level |= 0x3f; } - WriteRegister(OPL_REGS_LEVEL + operator, level); - WriteRegister(OPL_REGS_TREMOLO + operator, data->tremolo); - WriteRegister(OPL_REGS_ATTACK + operator, data->attack); - WriteRegister(OPL_REGS_SUSTAIN + operator, data->sustain); - WriteRegister(OPL_REGS_WAVEFORM + operator, data->waveform); + OPL_WriteRegister(OPL_REGS_LEVEL + operator, level); + OPL_WriteRegister(OPL_REGS_TREMOLO + operator, data->tremolo); + OPL_WriteRegister(OPL_REGS_ATTACK + operator, data->attack); + OPL_WriteRegister(OPL_REGS_SUSTAIN + operator, data->sustain); + OPL_WriteRegister(OPL_REGS_WAVEFORM + operator, data->waveform); } // Set the instrument for a particular voice. @@ -629,8 +491,8 @@ static void SetVoiceInstrument(opl_voice_t *voice, // two operators. Turn on bits in the upper nybble; I think this // is for OPL3, where it turns on channel A/B. - WriteRegister(OPL_REGS_FEEDBACK + voice->index, - data->feedback | 0x30); + OPL_WriteRegister(OPL_REGS_FEEDBACK + voice->index, + data->feedback | 0x30); // Hack to force a volume update. @@ -669,7 +531,7 @@ static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume) { voice->reg_volume = reg_volume; - WriteRegister(OPL_REGS_LEVEL + voice->op2, reg_volume); + OPL_WriteRegister(OPL_REGS_LEVEL + voice->op2, reg_volume); // If we are using non-modulated feedback mode, we must set the // volume for both voices. @@ -679,7 +541,7 @@ static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume) if ((opl_voice->feedback & 0x01) != 0) { - WriteRegister(OPL_REGS_LEVEL + voice->op1, reg_volume); + OPL_WriteRegister(OPL_REGS_LEVEL + voice->op1, reg_volume); } } } @@ -730,18 +592,8 @@ static void I_OPL_ShutdownMusic(void) static boolean I_OPL_InitMusic(void) { if (!OPL_Init(opl_io_port)) - { - return false; - } - - init_stage_reg_writes = true; - - // Doom does the detection sequence twice, for some reason: - - if (!DetectOPL() || !DetectOPL()) { printf("Dude. The Adlib isn't responding.\n"); - OPL_Shutdown(); return false; } @@ -753,14 +605,8 @@ static boolean I_OPL_InitMusic(void) return false; } - InitRegisters(); InitVoices(); - // Now that initialisation has finished, switch the - // register writing mode: - - init_stage_reg_writes = false; - music_initialised = true; return true; @@ -789,7 +635,7 @@ static void I_OPL_SetMusicVolume(int volume) static void VoiceKeyOff(opl_voice_t *voice) { - WriteRegister(OPL_REGS_FREQ_2 + voice->index, voice->freq >> 8); + OPL_WriteRegister(OPL_REGS_FREQ_2 + voice->index, voice->freq >> 8); } // Get the frequency that we should be using for a voice. @@ -947,8 +793,8 @@ static void UpdateVoiceFrequency(opl_voice_t *voice) if (voice->freq != freq) { - WriteRegister(OPL_REGS_FREQ_1 + voice->index, freq & 0xff); - WriteRegister(OPL_REGS_FREQ_2 + voice->index, (freq >> 8) | 0x20); + OPL_WriteRegister(OPL_REGS_FREQ_1 + voice->index, freq & 0xff); + OPL_WriteRegister(OPL_REGS_FREQ_2 + voice->index, (freq >> 8) | 0x20); voice->freq = freq; } -- cgit v1.2.3 From 43f160000c540b129733cbea11a492d2598758e6 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 27 Sep 2009 00:08:19 +0000 Subject: Add documentation for OPL_DRIVER environment variable. Subversion-branch: /branches/opl-branch Subversion-revision: 1693 --- man/manpage.template | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/man/manpage.template b/man/manpage.template index 052ccb63..41f06dc0 100644 --- a/man/manpage.template +++ b/man/manpage.template @@ -28,6 +28,17 @@ specifies a PC speaker driver to use for sound effect playback. Valid options are "Linux" for the Linux console mode driver, "BSD" for the NetBSD/OpenBSD PC speaker driver, and "SDL" for SDL-based emulated PC speaker playback (using the digital output). +.TP +\fBOPL_DRIVER\fR +When using OPL MIDI playback, this environment variable specifies an +OPL backend driver to use. Valid options are "SDL" for an SDL-based +software emulated OPL chip, "Linux" for the Linux hardware OPL driver, +and "OpenBSD" for the OpenBSD/NetBSD hardware OPL driver. + +Generally speaking, a real hardware OPL chip sounds better than software +emulation; however, modern machines do not often include one. If +present, it may still require extra work to set up and elevated +security privileges to access. .SH FILES .TP \fB$HOME/.chocolate-doom/default.cfg\fR -- cgit v1.2.3 From de66c6b9672e4a97af96938517a1d954f32966ec Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 27 Sep 2009 00:40:55 +0000 Subject: Set privilege level to 3, not 1. Subversion-branch: /branches/opl-branch Subversion-revision: 1694 --- opl/opl_obsd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opl/opl_obsd.c b/opl/opl_obsd.c index 82d9186d..05574333 100644 --- a/opl/opl_obsd.c +++ b/opl/opl_obsd.c @@ -46,7 +46,7 @@ static int OPL_OpenBSD_Init(unsigned int port_base) { // Try to get permissions: - if (i386_iopl(1) < 0) + if (i386_iopl(3) < 0) { fprintf(stderr, "Failed to get raise I/O privilege level: " "check that you are running as root.\n"); -- cgit v1.2.3 From 614351cebaaf2b1a372c78639d2ede7d1d7091fc Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Mon, 28 Sep 2009 20:44:20 +0000 Subject: Change music enable/disable control in setup tool to a dropdown list, to allow MIDI playback type to be selected. Subversion-branch: /branches/opl-branch Subversion-revision: 1695 --- OPL-TODO | 2 +- setup/sound.c | 74 ++++++++++++++++++++++++++++++++++++++++------------------- 2 files changed, 51 insertions(+), 25 deletions(-) diff --git a/OPL-TODO b/OPL-TODO index 3def4e36..39e5b131 100644 --- a/OPL-TODO +++ b/OPL-TODO @@ -8,6 +8,7 @@ Needs research: Bad MIDIs: + * doom2.wad MAP01 * deca.wad MAP01 * gothicdm MAP05 * tnt.wad MAP30 @@ -15,6 +16,5 @@ Bad MIDIs: Other tasks: - * Add option to select MIDI type in setup tool * DMXOPTIONS opl3/phase option support. diff --git a/setup/sound.c b/setup/sound.c index 72414c83..92a6cbae 100644 --- a/setup/sound.c +++ b/setup/sound.c @@ -49,6 +49,14 @@ typedef enum NUM_SFXMODES } sfxmode_t; +typedef enum +{ + MUSMODE_DISABLED, + MUSMODE_OPL, + MUSMODE_NATIVE, + NUM_MUSMODES +} musmode_t; + static char *sfxmode_strings[] = { "Disabled", @@ -56,14 +64,14 @@ static char *sfxmode_strings[] = "Digital", }; -// Disable MIDI music on OSX: there are problems with the native -// MIDI code in SDL_mixer. +static char *musmode_strings[] = +{ + "Disabled", + "OPL (Adlib/SB)", + "Native MIDI" +}; -#ifdef __MACOSX__ -#define DEFAULT_MUSIC_DEVICE SNDDEVICE_NONE -#else #define DEFAULT_MUSIC_DEVICE SNDDEVICE_SB -#endif int snd_sfxdevice = SNDDEVICE_SB; int numChannels = 8; @@ -77,7 +85,7 @@ int snd_samplerate = 22050; int use_libsamplerate = 0; static int snd_sfxmode; -static int snd_musicenabled; +static int snd_musmode; static void UpdateSndDevices(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(data)) { @@ -93,14 +101,18 @@ static void UpdateSndDevices(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(data)) snd_sfxdevice = SNDDEVICE_SB; break; } - - if (snd_musicenabled) - { - snd_musicdevice = SNDDEVICE_SB; - } - else + + switch (snd_musmode) { - snd_musicdevice = SNDDEVICE_NONE; + case MUSMODE_DISABLED: + snd_musicdevice = SNDDEVICE_NONE; + break; + case MUSMODE_OPL: + snd_musicdevice = SNDDEVICE_SB; + break; + case MUSMODE_NATIVE: + snd_musicdevice = SNDDEVICE_GENMIDI; + break; } } @@ -110,7 +122,7 @@ void ConfigSound(void) txt_table_t *sfx_table; txt_table_t *music_table; txt_dropdown_list_t *sfx_mode_control; - txt_checkbox_t *music_enabled_control; + txt_dropdown_list_t *mus_mode_control; if (snd_sfxdevice == SNDDEVICE_PCSPEAKER) { @@ -124,8 +136,21 @@ void ConfigSound(void) { snd_sfxmode = SFXMODE_DISABLED; } - - snd_musicenabled = snd_musicdevice != SNDDEVICE_NONE; + + if (snd_musicdevice == SNDDEVICE_GENMIDI) + { + snd_musmode = MUSMODE_NATIVE; + } + else if (snd_musicdevice == SNDDEVICE_SB + || snd_musicdevice == SNDDEVICE_ADLIB + || snd_musicdevice == SNDDEVICE_AWE32) + { + snd_musmode = MUSMODE_OPL; + } + else + { + snd_musmode = MUSMODE_DISABLED; + } window = TXT_NewWindow("Sound configuration"); @@ -133,12 +158,10 @@ void ConfigSound(void) TXT_NewSeparator("Sound effects"), sfx_table = TXT_NewTable(2), TXT_NewSeparator("Music"), - music_enabled_control = TXT_NewCheckBox("Music enabled", - &snd_musicenabled), music_table = TXT_NewTable(2), NULL); - TXT_SetColumnWidths(sfx_table, 20, 5); + TXT_SetColumnWidths(sfx_table, 20, 14); TXT_AddWidgets(sfx_table, TXT_NewLabel("Sound effects"), @@ -151,17 +174,20 @@ void ConfigSound(void) TXT_NewSpinControl(&sfxVolume, 0, 15), NULL); - TXT_SetColumnWidths(music_table, 20, 5); + TXT_SetColumnWidths(music_table, 20, 14); TXT_AddWidgets(music_table, + TXT_NewLabel("Music playback"), + mus_mode_control = TXT_NewDropdownList(&snd_musmode, + musmode_strings, + NUM_MUSMODES), TXT_NewLabel("Music volume"), TXT_NewSpinControl(&musicVolume, 0, 15), NULL); - TXT_SignalConnect(sfx_mode_control, "changed", + TXT_SignalConnect(sfx_mode_control, "changed", UpdateSndDevices, NULL); - TXT_SignalConnect(music_enabled_control, "changed", + TXT_SignalConnect(mus_mode_control, "changed", UpdateSndDevices, NULL); - } -- cgit v1.2.3 From d484bfaf001faeb48fee28d046149108ab52a394 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Wed, 30 Sep 2009 00:11:44 +0000 Subject: Add Win9x native OPL driver (untested). Subversion-branch: /branches/opl-branch Subversion-revision: 1696 --- opl/Makefile.am | 5 ++- opl/opl.c | 6 +++ opl/opl_win9x.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 opl/opl_win9x.c diff --git a/opl/Makefile.am b/opl/Makefile.am index 9a757fdb..8bbed9f0 100644 --- a/opl/Makefile.am +++ b/opl/Makefile.am @@ -8,10 +8,11 @@ noinst_LIBRARIES=libopl.a libopl_a_SOURCES = \ opl_internal.h \ opl.c opl.h \ - opl_obsd.c opl_obsd.h \ opl_linux.c \ - opl_sdl.c \ + opl_obsd.c \ opl_queue.c opl_queue.h \ + opl_sdl.c \ opl_timer.c opl_timer.h \ + opl_win9x.c \ fmopl.c fmopl.h diff --git a/opl/opl.c b/opl/opl.c index 00c9b04a..749c19d1 100644 --- a/opl/opl.c +++ b/opl/opl.c @@ -45,6 +45,9 @@ extern opl_driver_t opl_linux_driver; #ifdef HAVE_LIBI386 extern opl_driver_t opl_openbsd_driver; #endif +#ifdef _WIN32 +extern opl_driver_t opl_win9x_driver; +#endif extern opl_driver_t opl_sdl_driver; static opl_driver_t *drivers[] = @@ -54,6 +57,9 @@ static opl_driver_t *drivers[] = #endif #ifdef HAVE_LIBI386 &opl_openbsd_driver, +#endif +#ifdef _WIN32 + &opl_win9x_driver, #endif &opl_sdl_driver, NULL diff --git a/opl/opl_win9x.c b/opl/opl_win9x.c new file mode 100644 index 00000000..04555760 --- /dev/null +++ b/opl/opl_win9x.c @@ -0,0 +1,137 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// OPL Win9x native interface. +// +//----------------------------------------------------------------------------- + +#include "config.h" + +#ifdef _WIN32 + +#define WIN32_LEAN_AND_MEAN +#include + +#include "opl.h" +#include "opl_internal.h" +#include "opl_timer.h" + +static unsigned int opl_port_base; + +// MingW? + +#if defined(__GNUC__) && defined(__i386__) + +static unsigned int OPL_Win9x_PortRead(opl_port_t port) +{ + unsigned char result; + + __asm__ volatile ( + "movl %1, %%edx\n" + "inb %%dx, %%al\n" + "movb %%al, %0" + : "=m" (result) + : "r" (opl_port_base + port) + : "edx", "al", "memory" + ); + + return result; +} + +static void OPL_Win9x_PortWrite(opl_port_t port, unsigned int value) +{ + __asm__ volatile ( + "movl %0, %%edx\n" + "movb %1, %%al\n" + "outb %%al, %%dx" + : + : "r" (opl_port_base + port), "r" ((unsigned char) value) + : "edx", "al" + ); +} + +// TODO: MSVC version +// #elif defined(_MSC_VER) && defined(_M_IX6) ... + +#else + +// Not x86, or don't know how to do port R/W on this compiler. + +#define NO_PORT_RW + +static unsigned int OPL_Win9x_PortRead(opl_port_t port) +{ + return 0; +} + +static void OPL_Win9x_PortWrite(opl_port_t port, unsigned int value) +{ +} + +#endif + +static int OPL_Win9x_Init(unsigned int port_base) +{ +#ifndef NO_PORT_RW + + OSVERSIONINFO version_info; + + // Check that this is a Windows 9x series OS: + + GetVersionEx(&version_info); + + if (version_info.dwPlatformId == 1) + { + opl_port_base = port_base; + + // Start callback thread + + return OPL_Timer_StartThread(); + } + +#endif + + return 0; +} + +static void OPL_Win9x_Shutdown(void) +{ + // Stop callback thread + + OPL_Timer_StopThread(); +} + +opl_driver_t opl_win9x_driver = +{ + "Win9x", + OPL_Win9x_Init, + OPL_Win9x_Shutdown, + OPL_Win9x_PortRead, + OPL_Win9x_PortWrite, + OPL_Timer_SetCallback, + OPL_Timer_ClearCallbacks, + OPL_Timer_Lock, + OPL_Timer_Unlock, + OPL_Timer_SetPaused +}; + +#endif /* #ifdef _WIN32 */ + -- cgit v1.2.3 From b8b755758cfa1e297332e5edf8e9e166ddf327ac Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Wed, 30 Sep 2009 01:13:18 +0000 Subject: Initialize dwOSVersionInfoSize before calling GetVersionEx(). Subversion-branch: /branches/opl-branch Subversion-revision: 1697 --- opl/opl_win9x.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/opl/opl_win9x.c b/opl/opl_win9x.c index 04555760..ff527b3e 100644 --- a/opl/opl_win9x.c +++ b/opl/opl_win9x.c @@ -96,6 +96,9 @@ static int OPL_Win9x_Init(unsigned int port_base) // Check that this is a Windows 9x series OS: + memset(&version_info, 0, sizeof(version_info)); + version_info.dwOSVersionInfoSize = sizeof(version_info); + GetVersionEx(&version_info); if (version_info.dwPlatformId == 1) -- cgit v1.2.3 From 4a70f989d2aacabffc2f02017704de042be7418a Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Thu, 1 Oct 2009 00:08:48 +0000 Subject: Convert to American English spellings. Subversion-branch: /branches/opl-branch Subversion-revision: 1700 --- opl/opl.c | 4 ++-- opl/opl_sdl.c | 18 +++++++++--------- src/i_oplmusic.c | 30 +++++++++++++++--------------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/opl/opl.c b/opl/opl.c index 749c19d1..bf999d47 100644 --- a/opl/opl.c +++ b/opl/opl.c @@ -236,9 +236,9 @@ void OPL_WriteRegister(int reg, int value) for (i=0; i<6; ++i) { - // An oddity of the Doom OPL code: at startup initialisation, + // An oddity of the Doom OPL code: at startup initialization, // the spacing here is performed by reading from the register - // port; after initialisation, the data port is read, instead. + // port; after initialization, the data port is read, instead. if (init_stage_reg_writes) { diff --git a/opl/opl_sdl.c b/opl/opl_sdl.c index e38f9f6e..40430546 100644 --- a/opl/opl_sdl.c +++ b/opl/opl_sdl.c @@ -79,11 +79,11 @@ static int16_t *mix_buffer = NULL; // SDL parameters. -static int sdl_was_initialised = 0; +static int sdl_was_initialized = 0; static int mixing_freq, mixing_channels; static Uint16 mixing_format; -static int SDLIsInitialised(void) +static int SDLIsInitialized(void) { int freq, channels; Uint16 format; @@ -227,13 +227,13 @@ static void OPL_SDL_Shutdown(void) { Mix_SetPostMix(NULL, NULL); - if (sdl_was_initialised) + if (sdl_was_initialized) { Mix_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); OPL_Queue_Destroy(callback_queue); free(mix_buffer); - sdl_was_initialised = 0; + sdl_was_initialized = 0; } if (opl_emulator != NULL) @@ -281,9 +281,9 @@ static void TimerHandler(int channel, double interval_seconds) static int OPL_SDL_Init(unsigned int port_base) { // Check if SDL_mixer has been opened already - // If not, we must initialise it now + // If not, we must initialize it now - if (!SDLIsInitialised()) + if (!SDLIsInitialized()) { if (SDL_Init(SDL_INIT_AUDIO) < 0) { @@ -304,11 +304,11 @@ static int OPL_SDL_Init(unsigned int port_base) // When this module shuts down, it has the responsibility to // shut down SDL. - sdl_was_initialised = 1; + sdl_was_initialized = 1; } else { - sdl_was_initialised = 0; + sdl_was_initialized = 0; } opl_sdl_paused = 0; @@ -345,7 +345,7 @@ static int OPL_SDL_Init(unsigned int port_base) if (opl_emulator == NULL) { - fprintf(stderr, "Failed to initialise software OPL emulator!\n"); + fprintf(stderr, "Failed to initialize software OPL emulator!\n"); OPL_SDL_Shutdown(); return 0; } diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index e9e722e4..35ba046d 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -303,7 +303,7 @@ static const unsigned int volume_mapping_table[] = { 124, 124, 125, 125, 126, 126, 127, 127 }; -static boolean music_initialised = false; +static boolean music_initialized = false; //static boolean musicpaused = false; static int current_music_volume; @@ -546,7 +546,7 @@ static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume) } } -// Initialise the voice table and freelist +// Initialize the voice table and freelist static void InitVoices(void) { @@ -556,7 +556,7 @@ static void InitVoices(void) voice_free_list = NULL; - // Initialise each voice. + // Initialize each voice. for (i=0; i 0; +} + +// Shutdown music + +static void I_OPL_ShutdownMusic(void) +{ + if (music_initialized) + { + // Stop currently-playing track, if there is one: + + I_OPL_StopSong(); + + OPL_Shutdown(); + + // Release GENMIDI lump + + W_ReleaseLumpName("GENMIDI"); + + music_initialized = false; + } +} + +// Initialize music subsystem + +static boolean I_OPL_InitMusic(void) +{ + if (!OPL_Init(opl_io_port)) + { + printf("Dude. The Adlib isn't responding.\n"); + return false; + } + + // Load instruments from GENMIDI lump: + + if (!LoadInstrumentTable()) + { + OPL_Shutdown(); + return false; + } + + InitVoices(); + + tracks = NULL; + num_tracks = 0; + music_initialized = true; + + return true; } static snddevice_t music_opl_devices[] = -- cgit v1.2.3 From 073e710f685a10c2084f6c04fbc4fe7c87530d93 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 3 Oct 2009 22:43:07 +0000 Subject: Use Mix_HookMusic rather than Mix_SetPostMix for OPL emulation, to avoid conflict with PC speaker emulation. Subversion-branch: /branches/opl-branch Subversion-revision: 1706 --- opl/opl_sdl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/opl/opl_sdl.c b/opl/opl_sdl.c index 40430546..4a2e1ff8 100644 --- a/opl/opl_sdl.c +++ b/opl/opl_sdl.c @@ -160,8 +160,8 @@ static void FillBuffer(int16_t *buffer, unsigned int nsamples) for (i=0; i -#include -#include -#include +#if defined(HAVE_LIBI386) #include #include #include +#define set_iopl i386_iopl + +#elif defined(HAVE_LIBAMD64) + +#include +#include +#include +#define set_iopl amd64_iopl + +#else +#define NO_OBSD_DRIVER +#endif + +// If the above succeeded, proceed with the rest. + +#ifndef NO_OBSD_DRIVER + +#include +#include +#include +#include #include "opl.h" #include "opl_internal.h" @@ -46,7 +66,7 @@ static int OPL_OpenBSD_Init(unsigned int port_base) { // Try to get permissions: - if (i386_iopl(3) < 0) + if (set_iopl(3) < 0) { fprintf(stderr, "Failed to get raise I/O privilege level: " "check that you are running as root.\n"); @@ -59,7 +79,7 @@ static int OPL_OpenBSD_Init(unsigned int port_base) if (!OPL_Timer_StartThread()) { - i386_iopl(0); + set_iopl(0); return 0; } @@ -74,7 +94,7 @@ static void OPL_OpenBSD_Shutdown(void) // Release I/O port permissions: - i386_iopl(0); + set_iopl(0); } static unsigned int OPL_OpenBSD_PortRead(opl_port_t port) @@ -101,5 +121,5 @@ opl_driver_t opl_openbsd_driver = OPL_Timer_SetPaused }; -#endif /* #ifdef HAVE_LIBI386 */ +#endif /* #ifndef NO_OBSD_DRIVER */ -- cgit v1.2.3 From 63788d629ee842495836bca69bf68fe16e807ad1 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 4 Oct 2009 16:45:32 +0000 Subject: Add README file for setting up hardware OPL. Subversion-branch: /branches/opl-branch Subversion-revision: 1708 --- README.OPL | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 README.OPL diff --git a/README.OPL b/README.OPL new file mode 100644 index 00000000..2b837c4c --- /dev/null +++ b/README.OPL @@ -0,0 +1,100 @@ +== Chocolate Doom hardware OPL support notes == + +Chocolate Doom is able to play MIDI music as it sounds in Vanilla Doom +with an OPL chip (as found in the Yamaha Adlib card, the Sound Blaster +and its clones). Most modern computers do not include an OPL chip any +more, as CPUs are fast enough to do decent software MIDI synthesis. +For this reason, a software OPL emulator is included as a substitute. + +However, no software emulator sounds exactly like a real (hardware) +OPL chip, so if you do have a sound card with hardware OPL, here's how +to configure Chocolate Doom to use it. + +=== Sound cards with OPL chips === + +If you have an ISA sound card, it almost certainly includes an OPL +chip. Modern computers don't have slots for ISA cards though, so you +must be running a pretty old machine. + +If you have a PCI sound card, you probably don't have an OPL chip. +However, there are some exceptions to this. The following cards are +known to include "legacy" OPL support: + + * C-Media CMI8738 (*) + * Forte Media FM801 + * Cards based on the Yamaha YMF724 (*) + +Other cards that apparently have OPL support but have not been tested: + + * S3 SonicVibes + * AZTech PCI 168 (AZT 3328 chipset) + * ESS Solo-1 sound cards (ES1938, ES1946, ES1969 chipset) + * Conexant Riptide Audio/Modem combo cards + * Cards based on the Crystal Semiconductors CS4281 + * Cards based on the Avance Logic ALS300 + * Cards based on the Avance Logic ALS4000 + +If you desperately want hardware OPL music, you may be able to find +one of these cards for sale cheap on eBay. + +For the cards listed above with (*) next to them, OPL support is +disabled by default and must be explictly enabled in software. + +If your machine is not a PC, you don't have an OPL chip, and you will +have to use the software OPL. + +=== Operating System support === + +If you're certain that you have a sound card with hardware OPL, you +may need to take extra steps to configure your operating system to +allow access to it. To do hardware OPL, Chocolate Doom must access +the chip directly, which is usually not possible in modern operating +systems unless you are running as the superuser (root/Administrator). + +=== Windows 9x === + +If you're running Windows 95, 98 or Me, there is no need to configure +anything. Windows allows direct access to the OPL chip. You can +confirm that hardware OPL is working by checking for this message in +stdout.txt: + + OPL_Init: Using driver 'Win9x'. + +=== Windows NT (including 2000, XP and later) === + +If you're running an NT-based system, it is not possible to directly +access the OPL chip, even when running as Administrator. Fortunately, +it is possible to use the third-party "PortTalk" driver: + + http://www.beyondlogic.org/porttalk/porttalk.htm + +TODO - the NT driver hasn't actually been written yet.. + +=== Linux === + +If you are using a system based on the Linux kernel, you can access +the OPL chip directly, but you must be running as root. You can +confirm that hardware OPL is working, by checking for this message on +startup: + + OPL_Init: Using driver 'Linux'. + +If you are using one of the PCI cards in the list above with a (*) +next to it, you may need to manually enable FM legacy support. Add +the following to your /etc/modprobe.conf file to do this: + + options snd-ymfpci fm_port=0x388 + options snd-cmipci fm_port=0x388 + +=== OpenBSD/NetBSD === + +You must be running as root to access the hardware OPL directly. You +can confirm that hadware OPL is working by checking for this message +on startup: + + OPL_Init: Using driver 'OpenBSD'. + +=== FreeBSD === + +There is no native OPL backend for FreeBSD yet. Sorry! + -- cgit v1.2.3 From c70eec36650e81025da97b2d716d446943cf5818 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 10 Oct 2009 22:23:24 +0000 Subject: Add README.OPL to dist. Subversion-branch: /branches/opl-branch Subversion-revision: 1713 --- Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.am b/Makefile.am index 991bf198..01c3c2e2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -38,6 +38,7 @@ EXTRA_DIST= \ config.h \ CMDLINE \ HACKING \ + README.OPL \ TODO \ BUGS -- cgit v1.2.3 From 56a6af426972ed2a334ae7001d19854aca964a75 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 11 Oct 2009 21:47:07 +0000 Subject: Don't apply base note offset if the instrument is a fixed note instrument. This should stop the ugly bleeping from the electric snare on E1M2. Subversion-branch: /branches/opl-branch Subversion-revision: 1715 --- src/i_oplmusic.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index cd331897..d32a163e 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -701,10 +701,15 @@ static unsigned int FrequencyForVoice(opl_voice_t *voice) note = voice->note; - // Apply note offset: + // Apply note offset. + // Don't apply offset if the instrument is a fixed note instrument. gm_voice = &voice->current_instr->voices[voice->current_instr_voice]; - note += (signed short) SHORT(gm_voice->base_note_offset); + + if ((voice->current_instr->flags & GENMIDI_FLAG_FIXED) == 0) + { + note += (signed short) SHORT(gm_voice->base_note_offset); + } // Avoid possible overflow due to base note offset: -- cgit v1.2.3 From 58f71d21b021bddcb24ac2b2b9736bf821221bd1 Mon Sep 17 00:00:00 2001 From: Russell Rice Date: Mon, 12 Oct 2009 23:43:08 +0000 Subject: - Add opl win9x driver to codeblocks project Subversion-branch: /branches/opl-branch Subversion-revision: 1716 --- codeblocks/game.cbp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/codeblocks/game.cbp b/codeblocks/game.cbp index ab6350eb..374936dc 100644 --- a/codeblocks/game.cbp +++ b/codeblocks/game.cbp @@ -79,6 +79,9 @@ + + -- cgit v1.2.3 From e30325c40f6ea482862745db0f4555e513f2952e Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 17 Oct 2009 22:36:15 +0000 Subject: Add OPL library API function to set software emulation sample rate, and set from snd_samplerate in the configuration file. Subversion-branch: /branches/opl-branch Subversion-revision: 1723 --- opl/opl.c | 9 +++++++++ opl/opl.h | 4 ++++ opl/opl_internal.h | 4 ++++ opl/opl_sdl.c | 19 +++++++++++++++---- src/i_oplmusic.c | 2 ++ 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/opl/opl.c b/opl/opl.c index 8e57647e..2c8fd692 100644 --- a/opl/opl.c +++ b/opl/opl.c @@ -68,6 +68,8 @@ static opl_driver_t *drivers[] = static opl_driver_t *driver = NULL; static int init_stage_reg_writes = 1; +unsigned int opl_sample_rate = 22050; + // // Init/shutdown code. // @@ -182,6 +184,13 @@ void OPL_Shutdown(void) } } +// Set the sample rate used for software OPL emulation. + +void OPL_SetSampleRate(unsigned int rate) +{ + opl_sample_rate = rate; +} + void OPL_WritePort(opl_port_t port, unsigned int value) { if (driver != NULL) diff --git a/opl/opl.h b/opl/opl.h index 9f5d0a9f..04d3cf27 100644 --- a/opl/opl.h +++ b/opl/opl.h @@ -70,6 +70,10 @@ int OPL_Init(unsigned int port_base); void OPL_Shutdown(void); +// Set the sample rate used for software emulation. + +void OPL_SetSampleRate(unsigned int rate); + // Write to one of the OPL I/O ports: void OPL_WritePort(opl_port_t port, unsigned int value); diff --git a/opl/opl_internal.h b/opl/opl_internal.h index 78cbe7b2..4a46b060 100644 --- a/opl/opl_internal.h +++ b/opl/opl_internal.h @@ -56,5 +56,9 @@ typedef struct opl_set_paused_func set_paused_func; } opl_driver_t; +// Sample rate to use when doing software emulation. + +extern unsigned int opl_sample_rate; + #endif /* #ifndef OPL_INTERNAL_H */ diff --git a/opl/opl_sdl.c b/opl/opl_sdl.c index 4a2e1ff8..2eb8288f 100644 --- a/opl/opl_sdl.c +++ b/opl/opl_sdl.c @@ -40,9 +40,6 @@ #include "opl_queue.h" -// TODO: -#define opl_sample_rate 22050 - // When the callback mutex is locked using OPL_Lock, callback functions // are not invoked. @@ -278,6 +275,20 @@ static void TimerHandler(int channel, double interval_seconds) SDL_UnlockMutex(callback_queue_mutex); } +static unsigned int GetSliceSize(void) +{ + unsigned int slicesize; + + slicesize = 1024 * (opl_sample_rate / 11025); + + if (slicesize <= 1024) + { + slicesize = 1024; + } + + return slicesize; +} + static int OPL_SDL_Init(unsigned int port_base) { // Check if SDL_mixer has been opened already @@ -291,7 +302,7 @@ static int OPL_SDL_Init(unsigned int port_base) return 0; } - if (Mix_OpenAudio(opl_sample_rate, AUDIO_S16SYS, 2, 1024) < 0) + if (Mix_OpenAudio(opl_sample_rate, AUDIO_S16SYS, 2, GetSliceSize()) < 0) { fprintf(stderr, "Error initialising SDL_mixer: %s\n", Mix_GetError()); diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index d32a163e..474877d4 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -1372,6 +1372,8 @@ static void I_OPL_ShutdownMusic(void) static boolean I_OPL_InitMusic(void) { + OPL_SetSampleRate(snd_samplerate); + if (!OPL_Init(opl_io_port)) { printf("Dude. The Adlib isn't responding.\n"); -- cgit v1.2.3 From a8e79308562fbcea7a39ed1846329959cbf343b3 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 17 Oct 2009 22:45:22 +0000 Subject: Change GetSliceSize() to always return a power of two. Subversion-branch: /branches/opl-branch Subversion-revision: 1724 --- opl/opl_sdl.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/opl/opl_sdl.c b/opl/opl_sdl.c index 2eb8288f..1963d5cd 100644 --- a/opl/opl_sdl.c +++ b/opl/opl_sdl.c @@ -40,6 +40,8 @@ #include "opl_queue.h" +#define MAX_SOUND_SLICE_TIME 100 /* ms */ + // When the callback mutex is locked using OPL_Lock, callback functions // are not invoked. @@ -277,16 +279,26 @@ static void TimerHandler(int channel, double interval_seconds) static unsigned int GetSliceSize(void) { - unsigned int slicesize; + int limit; + int n; + + limit = (opl_sample_rate * MAX_SOUND_SLICE_TIME) / 1000; - slicesize = 1024 * (opl_sample_rate / 11025); + // Try all powers of two, not exceeding the limit. - if (slicesize <= 1024) + for (n=0;; ++n) { - slicesize = 1024; + // 2^n <= limit < 2^n+1 ? + + if ((1 << (n + 1)) > limit) + { + return (1 << n); + } } - return slicesize; + // Should never happen? + + return 1024; } static int OPL_SDL_Init(unsigned int port_base) -- cgit v1.2.3 From 14bcdd1008db070bc4a12be73e3f692c1990966e Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 25 Oct 2009 17:07:55 +0000 Subject: Emulate odd octave 7 behavior of Vanilla Doom. Subversion-branch: /branches/opl-branch Subversion-revision: 1725 --- src/i_oplmusic.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 474877d4..89e73f1a 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -739,6 +739,26 @@ static unsigned int FrequencyForVoice(opl_voice_t *voice) sub_index = (freq_index - 284) % (12 * 32); octave = (freq_index - 284) / (12 * 32); + // Once the seventh octave is reached, things break down. + // We can only go up to octave 7 as a maximum anyway (the OPL + // register only has three bits for octave number), but for the + // notes in octave 7, the first five bits have octave=7, the + // following notes have octave=6. This 7/6 pattern repeats in + // following octaves (which are technically impossible to + // represent anyway). + + if (octave >= 7) + { + if (sub_index < 5) + { + octave = 7; + } + else + { + octave = 6; + } + } + // Calculate the resulting register value to use for the frequency. return frequency_curve[sub_index + 284] | (octave << 10); -- cgit v1.2.3 From 299e1c5abf804e2e249c4f77b82fb7949a3f9d7b Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 31 Oct 2009 16:47:49 +0000 Subject: When replacing an existing voice, discard voices that are the second voice of a two voice instrument. Don't discard instruments from lower numbered MIDI channels for higher numbered MIDI channels. Subversion-branch: /branches/opl-branch Subversion-revision: 1727 --- OPL-TODO | 1 - src/i_oplmusic.c | 20 ++++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/OPL-TODO b/OPL-TODO index 39e5b131..57d315a0 100644 --- a/OPL-TODO +++ b/OPL-TODO @@ -9,7 +9,6 @@ Needs research: Bad MIDIs: * doom2.wad MAP01 - * deca.wad MAP01 * gothicdm MAP05 * tnt.wad MAP30 * Alien Vendetta (title screen, MAP01, etc) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 89e73f1a..9501d51d 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -631,6 +631,16 @@ static void KeyOffEvent(opl_track_data_t *track, midi_event_t *event) } } +// Compare the priorities of channels, returning either -1, 0 or 1. + +static int CompareChannelPriorities(opl_channel_data_t *chan1, + opl_channel_data_t *chan2) +{ + // TODO ... + + return 1; +} + // When all voices are in use, we must discard an existing voice to // play a new note. Find and free an existing voice. The channel // passed to the function is the channel for the new note to be @@ -643,13 +653,19 @@ static opl_voice_t *ReplaceExistingVoice(opl_channel_data_t *channel) // Check the allocated voices, if we find an instrument that is // of a lower priority to the new instrument, discard it. - // Priority is determined by MIDI instrument number; old + // If a voice is being used to play the second voice of an instrument, + // use that, as second voices are non-essential. + // Lower numbered MIDI channels implicitly have a higher priority + // than higher-numbered channels, eg. MIDI channel 1 is never + // discarded for MIDI channel 2. result = NULL; for (rover = voice_alloced_list; rover != NULL; rover = rover->next) { - if (rover->current_instr > channel->instrument) + if (rover->current_instr_voice != 0 + || (rover->channel > channel + && CompareChannelPriorities(channel, rover->channel) > 0)) { result = rover; break; -- cgit v1.2.3 From b42f8f4ff9a282c61c7f578bea13c84e845675c3 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Fri, 5 Feb 2010 03:23:33 +0000 Subject: Add README.OPL to list of documentation files to distribute. Subversion-branch: /branches/opl-branch Subversion-revision: 1842 --- pkg/config.make.in | 1 + rpm.spec.in | 1 + 2 files changed, 2 insertions(+) diff --git a/pkg/config.make.in b/pkg/config.make.in index d8d72c60..6bda7471 100644 --- a/pkg/config.make.in +++ b/pkg/config.make.in @@ -19,6 +19,7 @@ PACKAGE_VERSION = @PACKAGE_VERSION@ # Documentation files to distribute with packages. DOC_FILES = README \ + README.OPL \ COPYING \ ChangeLog \ NEWS \ diff --git a/rpm.spec.in b/rpm.spec.in index 1b7e90c7..58142c1b 100644 --- a/rpm.spec.in +++ b/rpm.spec.in @@ -52,6 +52,7 @@ rm -rf $RPM_BUILD_ROOT %doc NEWS %doc AUTHORS %doc README +%doc README.OPL %doc COPYING %doc CMDLINE %doc BUGS -- cgit v1.2.3 From ff7fff78ec179f401a60b839eb522e08a9f5b5ca Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Fri, 5 Feb 2010 03:27:14 +0000 Subject: Only distribute README.OPL in the Windows package, don't bother including it in the Mac OS X package. Subversion-branch: /branches/opl-branch Subversion-revision: 1843 --- pkg/config.make.in | 1 - pkg/win32/GNUmakefile | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/config.make.in b/pkg/config.make.in index 6bda7471..d8d72c60 100644 --- a/pkg/config.make.in +++ b/pkg/config.make.in @@ -19,7 +19,6 @@ PACKAGE_VERSION = @PACKAGE_VERSION@ # Documentation files to distribute with packages. DOC_FILES = README \ - README.OPL \ COPYING \ ChangeLog \ NEWS \ diff --git a/pkg/win32/GNUmakefile b/pkg/win32/GNUmakefile index 626f1845..31968795 100644 --- a/pkg/win32/GNUmakefile +++ b/pkg/win32/GNUmakefile @@ -11,6 +11,8 @@ DLL_FILES=$(TOPLEVEL)/src/SDL.dll \ $(TOPLEVEL)/src/SDL_mixer.dll \ $(TOPLEVEL)/src/SDL_net.dll +DOC_FILES += README.OPL + ZIP=$(PACKAGE_TARNAME)-$(PACKAGE_VERSION)-win32.zip $(ZIP) : staging -- cgit v1.2.3 From cdacf59acecd944f4a573b3e112c0c43b052f975 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Fri, 5 Feb 2010 03:27:58 +0000 Subject: Add a hint message about permissions if unable to get I/O permissions for hardware OPL access. Subversion-branch: /branches/opl-branch Subversion-revision: 1844 --- opl/opl_linux.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/opl/opl_linux.c b/opl/opl_linux.c index 089192c9..319686b8 100644 --- a/opl/opl_linux.c +++ b/opl/opl_linux.c @@ -47,6 +47,14 @@ static int OPL_Linux_Init(unsigned int port_base) { fprintf(stderr, "Failed to get I/O port permissions for 0x%x: %s\n", port_base, strerror(errno)); + + if (errno == EPERM) + { + fprintf(stderr, + "\tYou may need to run the program as root in order\n" + "\tto acquire I/O port permissions for OPL MIDI playback.\n"); + } + return 0; } -- cgit v1.2.3 From c33c1fa07680ef07540fb1c48a323bdc816e3e77 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 6 Feb 2010 15:54:13 +0000 Subject: Fix Heretic/Hexen "noise" sound debug cheats. Subversion-branch: /branches/raven-branch Subversion-revision: 1850 --- src/heretic/s_sound.c | 12 ++++++++++-- src/hexen/s_sound.c | 12 ++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/heretic/s_sound.c b/src/heretic/s_sound.c index bdd24594..be4ae0e3 100644 --- a/src/heretic/s_sound.c +++ b/src/heretic/s_sound.c @@ -550,8 +550,16 @@ void S_GetChannelInfo(SoundInfo_t * s) c->priority = channel[i].priority; c->name = S_sfx[c->id].name; c->mo = channel[i].mo; - c->distance = P_AproxDistance(c->mo->x - viewx, c->mo->y - viewy) - >> FRACBITS; + + if (c->mo != NULL) + { + c->distance = P_AproxDistance(c->mo->x - viewx, c->mo->y - viewy) + >> FRACBITS; + } + else + { + c->distance = 0; + } } } diff --git a/src/hexen/s_sound.c b/src/hexen/s_sound.c index fdd01ba5..74774fb3 100644 --- a/src/hexen/s_sound.c +++ b/src/hexen/s_sound.c @@ -787,8 +787,16 @@ void S_GetChannelInfo(SoundInfo_t * s) c->priority = Channel[i].priority; c->name = S_sfx[c->id].name; c->mo = Channel[i].mo; - c->distance = P_AproxDistance(c->mo->x - viewx, c->mo->y - viewy) - >> FRACBITS; + + if (c->mo != NULL) + { + c->distance = P_AproxDistance(c->mo->x - viewx, c->mo->y - viewy) + >> FRACBITS; + } + else + { + c->distance = 0; + } } } -- cgit v1.2.3 From abb5f33199e6fe036d068b5a5f59e58e1505eb8e Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 7 Feb 2010 00:59:56 +0000 Subject: Add back unused phoenix rod object, frames, and dummy action pointer. These were in Heretic 1.0 but were later removed. Also add comments to info.h noting fire-death frames that were added Heretic 1.0. This is necessary for HHE support. Subversion-branch: /branches/raven-branch Subversion-revision: 1854 --- src/heretic/info.c | 35 +++++++++++++++++++++++++++++++++++ src/heretic/info.h | 8 ++++++-- src/heretic/p_pspr.c | 11 +++++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/heretic/info.c b/src/heretic/info.c index e566222c..4848ede9 100644 --- a/src/heretic/info.c +++ b/src/heretic/info.c @@ -95,6 +95,7 @@ void A_InitPhoenixPL2(); void A_FirePhoenixPL2(); void A_ShutdownPhoenixPL2(); void A_PhoenixPuff(); +void A_RemovedPhoenixPuff(); void A_FlameEnd(); void A_FloatPuff(); void A_FireCrossbowPL1(); @@ -663,6 +664,9 @@ state_t states[NUMSTATES] = { {SPR_FX08, 32773, 4, NULL, S_PHOENIXFXI1_7, 0, 0}, // S_PHOENIXFXI1_6 {SPR_FX08, 32774, 4, NULL, S_PHOENIXFXI1_8, 0, 0}, // S_PHOENIXFXI1_7 {SPR_FX08, 32775, 4, NULL, S_NULL, 0, 0}, // S_PHOENIXFXI1_8 + {SPR_FX08, 32776, 8, NULL, S_PHOENIXFXIX_1, 0, 0 }, // S_PHOENIXFXIX_1 + {SPR_FX08, 32777, 8, A_RemovedPhoenixFunc, S_PHOENIXFXIX_2, 0, 0 }, // S_PHOENIXFXIX_2 + {SPR_FX08, 32778, 8, NULL, S_NULL, 0, 0 }, // S_PHOENIXFXIX_3 {SPR_FX04, 1, 4, NULL, S_PHOENIXPUFF2, 0, 0}, // S_PHOENIXPUFF1 {SPR_FX04, 2, 4, NULL, S_PHOENIXPUFF3, 0, 0}, // S_PHOENIXPUFF2 {SPR_FX04, 3, 4, NULL, S_PHOENIXPUFF4, 0, 0}, // S_PHOENIXPUFF3 @@ -3727,6 +3731,37 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = { MF2_THRUGHOST | MF2_NOTELEPORT // flags2 }, + // The following thing is present in the mobjinfo table from Heretic 1.0, + // but not in Heretic 1.3 (ie. it was removed). It has been re-inserted + // here to support HHE patches. + + { // MT_PHOENIXFX_REMOVED + -1, // doomednum + S_PHOENIXFXIX_1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // crashstate + S_PHOENIXFXIX_3, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 2 * FRACUNIT, // radius + 4 * FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags + MF2_NOTELEPORT // flags2 + }, + { // MT_PHOENIXPUFF -1, // doomednum S_PHOENIXPUFF1, // spawnstate diff --git a/src/heretic/info.h b/src/heretic/info.h index f9581298..16b6aedc 100644 --- a/src/heretic/info.h +++ b/src/heretic/info.h @@ -653,6 +653,9 @@ typedef enum S_PHOENIXFXI1_6, S_PHOENIXFXI1_7, S_PHOENIXFXI1_8, + S_PHOENIXFXIX_1, // [ States in Heretic 1.0 that were removed + S_PHOENIXFXIX_2, + S_PHOENIXFXIX_3, // ] S_PHOENIXPUFF1, S_PHOENIXPUFF2, S_PHOENIXPUFF3, @@ -773,8 +776,8 @@ typedef enum S_PLAY_FDTH16, S_PLAY_FDTH17, S_PLAY_FDTH18, - S_PLAY_FDTH19, - S_PLAY_FDTH20, + S_PLAY_FDTH19, // < These two frames were not present in the Heretic + S_PLAY_FDTH20, // < 1.0 executable (fire death animation was extended) S_BLOODYSKULL1, S_BLOODYSKULL2, S_BLOODYSKULL3, @@ -1470,6 +1473,7 @@ typedef enum MT_GOLDWANDPUFF2, MT_WPHOENIXROD, MT_PHOENIXFX1, + MT_PHOENIXFX_REMOVED, // In Heretic 1.0, but removed. MT_PHOENIXPUFF, MT_PHOENIXFX2, MT_MISC15, diff --git a/src/heretic/p_pspr.c b/src/heretic/p_pspr.c index 7f9660ac..bd47c9a1 100644 --- a/src/heretic/p_pspr.c +++ b/src/heretic/p_pspr.c @@ -1650,6 +1650,17 @@ void A_PhoenixPuff(mobj_t * actor) puff->momz = 0; } +// +// This function was present in the Heretic 1.0 executable for the +// removed "secondary phoenix flash" object (MT_PHOENIXFX_REMOVED). +// The purpose of this object is unknown, as is this function. +// + +void A_RemovedPhoenixFunc(mobj_t *actor) +{ + I_Error("Action function invoked for removed Phoenix action!"); +} + //---------------------------------------------------------------------------- // // PROC A_InitPhoenixPL2 -- cgit v1.2.3 From b786d601aeaaa20167f408be56f7e8d13ecc2f8c Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 7 Feb 2010 02:04:12 +0000 Subject: Oops. Subversion-branch: /branches/raven-branch Subversion-revision: 1855 --- src/heretic/info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/heretic/info.c b/src/heretic/info.c index 4848ede9..7149220e 100644 --- a/src/heretic/info.c +++ b/src/heretic/info.c @@ -95,7 +95,7 @@ void A_InitPhoenixPL2(); void A_FirePhoenixPL2(); void A_ShutdownPhoenixPL2(); void A_PhoenixPuff(); -void A_RemovedPhoenixPuff(); +void A_RemovedPhoenixFunc(); void A_FlameEnd(); void A_FloatPuff(); void A_FireCrossbowPL1(); -- cgit v1.2.3 From e22d6a0d26b08e2e7c69972fed1cb96f300ef523 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 7 Feb 2010 02:59:25 +0000 Subject: Refactor dehacked structure mapping code to support string fields in addition to integer fields. Subversion-branch: /branches/raven-branch Subversion-revision: 1856 --- src/deh_mapping.c | 140 +++++++++++++++++++++++++++++++++++++++++------------- src/deh_mapping.h | 22 +++++++-- 2 files changed, 124 insertions(+), 38 deletions(-) diff --git a/src/deh_mapping.c b/src/deh_mapping.c index b215b128..f061c298 100644 --- a/src/deh_mapping.c +++ b/src/deh_mapping.c @@ -34,12 +34,9 @@ #include "i_system.h" #include "deh_mapping.h" -// -// Set the value of a particular field in a structure by name -// - -boolean DEH_SetMapping(deh_context_t *context, deh_mapping_t *mapping, - void *structptr, char *name, int value) +static deh_mapping_entry_t *GetMappingEntryByName(deh_context_t *context, + deh_mapping_t *mapping, + char *name) { int i; @@ -49,44 +46,121 @@ boolean DEH_SetMapping(deh_context_t *context, deh_mapping_t *mapping, if (!strcasecmp(entry->name, name)) { - void *location; - if (entry->location == NULL) { DEH_Warning(context, "Field '%s' is unsupported", name); - return false; + return NULL; } - location = (uint8_t *)structptr + ((uint8_t *)entry->location - (uint8_t *)mapping->base); - - // printf("Setting %p::%s to %i (%i bytes)\n", - // structptr, name, value, entry->size); - - switch (entry->size) - { - case 1: - * ((uint8_t *) location) = value; - break; - case 2: - * ((uint16_t *) location) = value; - break; - case 4: - * ((uint32_t *) location) = value; - break; - default: - DEH_Error(context, "Unknown field type for '%s' (BUG)", name); - return false; - } - - return true; + return entry; } } - // field with this name not found + // Not found. DEH_Warning(context, "Field named '%s' not found", name); - return false; + return NULL; +} + +// +// Get the location of the specified field in the specified structure. +// + +static void *GetStructField(void *structptr, + deh_mapping_t *mapping, + deh_mapping_entry_t *entry) +{ + unsigned int offset; + + offset = (uint8_t *)entry->location - (uint8_t *)mapping->base; + + return (uint8_t *)structptr + offset; +} + +// +// Set the value of a particular field in a structure by name +// + +boolean DEH_SetMapping(deh_context_t *context, deh_mapping_t *mapping, + void *structptr, char *name, int value) +{ + deh_mapping_entry_t *entry; + void *location; + + entry = GetMappingEntryByName(context, mapping, name); + + if (entry == NULL) + { + return false; + } + + // Sanity check: + + if (entry->is_string) + { + DEH_Error(context, "Tried to set '%s' as integer (BUG)", name); + return false; + } + + location = GetStructField(structptr, mapping, entry); + + // printf("Setting %p::%s to %i (%i bytes)\n", + // structptr, name, value, entry->size); + + // Set field content based on its type: + + switch (entry->size) + { + case 1: + * ((uint8_t *) location) = value; + break; + case 2: + * ((uint16_t *) location) = value; + break; + case 4: + * ((uint32_t *) location) = value; + break; + default: + DEH_Error(context, "Unknown field type for '%s' (BUG)", name); + return false; + } + + return true; +} + +// +// Set the value of a string field in a structure by name +// + +boolean DEH_SetStringMapping(deh_context_t *context, deh_mapping_t *mapping, + void *structptr, char *name, char *value) +{ + deh_mapping_entry_t *entry; + void *location; + + entry = GetMappingEntryByName(context, mapping, name); + + if (entry == NULL) + { + return false; + } + + // Sanity check: + + if (!entry->is_string) + { + DEH_Error(context, "Tried to set '%s' as string (BUG)", name); + return false; + } + + location = GetStructField(structptr, mapping, entry); + + // Copy value into field: + + strncpy(location, value, entry->size); + + return true; } void DEH_StructMD5Sum(md5_context_t *context, deh_mapping_t *mapping, diff --git a/src/deh_mapping.h b/src/deh_mapping.h index 4862dec9..129ddcfd 100644 --- a/src/deh_mapping.h +++ b/src/deh_mapping.h @@ -42,17 +42,23 @@ #define DEH_MAPPING(deh_name, fieldname) \ {deh_name, &deh_mapping_base.fieldname, \ - sizeof(deh_mapping_base.fieldname)}, + sizeof(deh_mapping_base.fieldname), \ + false}, + +#define DEH_MAPPING_STRING(deh_name, fieldname) \ + {deh_name, &deh_mapping_base.fieldname, \ + sizeof(deh_mapping_base.fieldname), \ + true}, #define DEH_UNSUPPORTED_MAPPING(deh_name) \ - {deh_name, NULL, -1}, - + {deh_name, NULL, -1, false}, + #define DEH_END_MAPPING \ {NULL, NULL, -1} \ } \ }; - + #define MAX_MAPPING_ENTRIES 32 @@ -73,6 +79,10 @@ struct deh_mapping_entry_s // field size int size; + + // if true, this is a string value. + + boolean is_string; }; struct deh_mapping_s @@ -81,8 +91,10 @@ struct deh_mapping_s deh_mapping_entry_t entries[MAX_MAPPING_ENTRIES]; }; -boolean DEH_SetMapping(deh_context_t *context, deh_mapping_t *mapping, +boolean DEH_SetMapping(deh_context_t *context, deh_mapping_t *mapping, void *structptr, char *name, int value); +boolean DEH_SetStringMapping(deh_context_t *context, deh_mapping_t *mapping, + void *structptr, char *name, char *value); void DEH_StructMD5Sum(md5_context_t *context, deh_mapping_t *mapping, void *structptr); -- cgit v1.2.3 From f04325260a5a95ca4c60c3b57762e48beedc381e Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 7 Feb 2010 03:02:01 +0000 Subject: Move sound structure and ammo definitions into headers. Add #ifndef guards around info.h. Subversion-branch: /branches/raven-branch Subversion-revision: 1857 --- src/heretic/doomdef.h | 1 + src/heretic/info.h | 7 ++++++- src/heretic/p_inter.c | 2 +- src/heretic/s_sound.c | 5 ++--- src/heretic/sounds.h | 5 +++++ 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/heretic/doomdef.h b/src/heretic/doomdef.h index 3f976c3d..aa1d0c0d 100644 --- a/src/heretic/doomdef.h +++ b/src/heretic/doomdef.h @@ -581,6 +581,7 @@ extern boolean singletics; // debug flag to cancel adaptiveness extern boolean DebugSound; // debug flag for displaying sound info extern int maxammo[NUMAMMO]; +extern int GetWeaponAmmo[NUMWEAPONS]; extern boolean demoplayback; extern int skytexture; diff --git a/src/heretic/info.h b/src/heretic/info.h index 16b6aedc..236826cb 100644 --- a/src/heretic/info.h +++ b/src/heretic/info.h @@ -21,7 +21,9 @@ // 02111-1307, USA. // //----------------------------------------------------------------------------- -// generated by multigen + +#ifndef HERETIC_INFO_H +#define HERETIC_INFO_H typedef enum { @@ -1579,3 +1581,6 @@ typedef struct } mobjinfo_t; extern mobjinfo_t mobjinfo[NUMMOBJTYPES]; + +#endif /* #ifndef HERETIC_INFO_H */ + diff --git a/src/heretic/p_inter.c b/src/heretic/p_inter.c index fba6d215..3587a95d 100644 --- a/src/heretic/p_inter.c +++ b/src/heretic/p_inter.c @@ -54,7 +54,7 @@ int maxammo[NUMAMMO] = { 150 // mace }; -static int GetWeaponAmmo[NUMWEAPONS] = { +int GetWeaponAmmo[NUMWEAPONS] = { 0, // staff 25, // gold wand 10, // crossbow diff --git a/src/heretic/s_sound.c b/src/heretic/s_sound.c index be4ae0e3..8e0be9f1 100644 --- a/src/heretic/s_sound.c +++ b/src/heretic/s_sound.c @@ -33,6 +33,8 @@ #include "r_local.h" #include "p_local.h" +#include "sounds.h" + #include "w_wad.h" #include "z_zone.h" @@ -55,9 +57,6 @@ int mus_lumpnum; void *mus_sndptr; byte *soundCurve; -extern sfxinfo_t S_sfx[]; -extern musicinfo_t S_music[]; - int snd_MaxVolume = 10; int snd_MusicVolume = 10; int snd_Channels = 16; diff --git a/src/heretic/sounds.h b/src/heretic/sounds.h index 707185c0..e50a6ad6 100644 --- a/src/heretic/sounds.h +++ b/src/heretic/sounds.h @@ -27,6 +27,8 @@ #ifndef __SOUNDSH__ #define __SOUNDSH__ +#include "i_sound.h" + #define MAX_SND_DIST 1600 #define MAX_CHANNELS 16 @@ -291,4 +293,7 @@ typedef enum NUMSFX } sfxenum_t; +extern sfxinfo_t S_sfx[]; +extern musicinfo_t S_music[]; + #endif -- cgit v1.2.3 From 6a92bc54be4081c8821ca110947133b5f1f8516b Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 7 Feb 2010 03:04:29 +0000 Subject: Initial code for HHE patch support. Subversion-branch: /branches/raven-branch Subversion-revision: 1858 --- src/Makefile.am | 4 +- src/heretic/Makefile.am | 11 +++- src/heretic/deh_ammo.c | 122 ++++++++++++++++++++++++++++++++++++++ src/heretic/deh_frame.c | 131 +++++++++++++++++++++++++++++++++++++++++ src/heretic/deh_htic.c | 85 +++++++++++++++++++++++++++ src/heretic/deh_htic.h | 45 ++++++++++++++ src/heretic/deh_sound.c | 118 +++++++++++++++++++++++++++++++++++++ src/heretic/deh_thing.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++ src/heretic/deh_weapon.c | 130 +++++++++++++++++++++++++++++++++++++++++ 9 files changed, 792 insertions(+), 3 deletions(-) create mode 100644 src/heretic/deh_ammo.c create mode 100644 src/heretic/deh_frame.c create mode 100644 src/heretic/deh_htic.c create mode 100644 src/heretic/deh_htic.h create mode 100644 src/heretic/deh_sound.c create mode 100644 src/heretic/deh_thing.c create mode 100644 src/heretic/deh_weapon.c diff --git a/src/Makefile.am b/src/Makefile.am index 9e550e15..eeabeb5b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -144,9 +144,9 @@ endif @PROGRAM_PREFIX@doom_LDADD = doom/libdoom.a $(EXTRA_LIBS) if HAVE_WINDRES -@PROGRAM_PREFIX@heretic_SOURCES=$(SOURCE_FILES) resource.rc +@PROGRAM_PREFIX@heretic_SOURCES=$(SOURCE_FILES_WITH_DEH) resource.rc else -@PROGRAM_PREFIX@heretic_SOURCES=$(SOURCE_FILES) +@PROGRAM_PREFIX@heretic_SOURCES=$(SOURCE_FILES_WITH_DEH) endif @PROGRAM_PREFIX@heretic_LDADD = heretic/libheretic.a $(EXTRA_LIBS) diff --git a/src/heretic/Makefile.am b/src/heretic/Makefile.am index 2a2f8245..3fdd3ff7 100644 --- a/src/heretic/Makefile.am +++ b/src/heretic/Makefile.am @@ -55,5 +55,14 @@ EXTRA_DIST= \ i_sound.c \ i_ibm.c -libheretic_a_SOURCES=$(SOURCE_FILES) +FEATURE_DEHACKED_SOURCE_FILES = \ +deh_ammo.c \ +deh_frame.c \ +deh_htic.c \ +deh_sound.c \ +deh_thing.c \ +deh_weapon.c + +libheretic_a_SOURCES=$(SOURCE_FILES) \ + $(FEATURE_DEHACKED_SOURCE_FILES) diff --git a/src/heretic/deh_ammo.c b/src/heretic/deh_ammo.c new file mode 100644 index 00000000..fe86c757 --- /dev/null +++ b/src/heretic/deh_ammo.c @@ -0,0 +1,122 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2005 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +//----------------------------------------------------------------------------- +// +// Parses "Ammo" sections in dehacked files +// +//----------------------------------------------------------------------------- + +#include +#include +#include + +#include "doomdef.h" +#include "doomtype.h" +#include "deh_defs.h" +#include "deh_io.h" +#include "deh_main.h" +#include "p_local.h" + +static void *DEH_AmmoStart(deh_context_t *context, char *line) +{ + int ammo_number = 0; + + if (sscanf(line, "Ammo %i", &ammo_number) != 1) + { + DEH_Warning(context, "Parse error on section start"); + return NULL; + } + + if (ammo_number < 0 || ammo_number >= NUMAMMO) + { + DEH_Warning(context, "Invalid ammo number: %i", ammo_number); + return NULL; + } + + return &maxammo[ammo_number]; +} + +static void DEH_AmmoParseLine(deh_context_t *context, char *line, void *tag) +{ + char *variable_name, *value; + int ivalue; + int ammo_number; + + if (tag == NULL) + return; + + ammo_number = ((int *) tag) - maxammo; + + // Parse the assignment + + if (!DEH_ParseAssignment(line, &variable_name, &value)) + { + // Failed to parse + + DEH_Warning(context, "Failed to parse assignment"); + return; + } + + ivalue = atoi(value); + + if (!strcasecmp(variable_name, "Per ammo")) + { + // Heretic doesn't have a "per clip" ammo array, instead + // it is per weapon. However, the weapon number lines + // up with the ammo number if we add one. + + GetWeaponAmmo[ammo_number + 1] = ivalue; + } + else if (!strcasecmp(variable_name, "Max ammo")) + { + maxammo[ammo_number] = ivalue; + } + else + { + DEH_Warning(context, "Field named '%s' not found", variable_name); + } +} + +static void DEH_AmmoMD5Hash(md5_context_t *context) +{ + int i; + + for (i=0; i +#include + +#include "doomtype.h" +#include "info.h" + +#include "deh_defs.h" +#include "deh_io.h" +#include "deh_main.h" +#include "deh_mapping.h" +#include "deh_htic.h" + +DEH_BEGIN_MAPPING(state_mapping, state_t) + DEH_MAPPING("Sprite number", sprite) + DEH_MAPPING("Sprite subnumber", frame) + DEH_MAPPING("Duration", tics) + DEH_MAPPING("Next frame", nextstate) + DEH_MAPPING("Unknown 1", misc1) + DEH_MAPPING("Unknown 2", misc2) + DEH_UNSUPPORTED_MAPPING("Action pointer") +DEH_END_MAPPING + +static void *DEH_FrameStart(deh_context_t *context, char *line) +{ + int frame_number = 0; + int mapped_frame_number; + state_t *state; + + if (sscanf(line, "Frame %i", &frame_number) != 1) + { + DEH_Warning(context, "Parse error on section start"); + return NULL; + } + + // Map the HHE frame number (which assumes a Heretic 1.0 state table) + // to the internal frame number (which is is the Heretic 1.3 state table): + + mapped_frame_number = DEH_MapHereticFrameNumber(frame_number); + + if (mapped_frame_number < 0 || mapped_frame_number >= DEH_HERETIC_NUMSTATES) + { + DEH_Warning(context, "Invalid frame number: %i", frame_number); + return NULL; + } + + state = &states[mapped_frame_number]; + + return state; +} + +static void DEH_FrameParseLine(deh_context_t *context, char *line, void *tag) +{ + state_t *state; + char *variable_name, *value; + int ivalue; + + if (tag == NULL) + return; + + state = (state_t *) tag; + + // Parse the assignment + + if (!DEH_ParseAssignment(line, &variable_name, &value)) + { + // Failed to parse + + DEH_Warning(context, "Failed to parse assignment"); + return; + } + + // all values are integers + + ivalue = atoi(value); + + // "Next frame" numbers need to undergo mapping. + + if (!strcasecmp(variable_name, "Next frame")) + { + ivalue = DEH_MapHereticFrameNumber(ivalue); + } + + DEH_SetMapping(context, &state_mapping, state, variable_name, ivalue); +} + +static void DEH_FrameMD5Sum(md5_context_t *context) +{ + int i; + + for (i=0; i +#include "deh_defs.h" +#include "deh_main.h" +#include "info.h" + +char *deh_signatures[] = +{ + "Patch File for HHE v1.1", + NULL +}; + +// deh_ammo.c: +extern deh_section_t deh_section_ammo; +// deh_frame.c: +extern deh_section_t deh_section_frame; +// deh_ptr.c: +extern deh_section_t deh_section_pointer; +// deh_sound.c +extern deh_section_t deh_section_sound; +// deh_text.c: +extern deh_section_t deh_section_text; +// deh_thing.c: +extern deh_section_t deh_section_thing; +// deh_weapon.c: +extern deh_section_t deh_section_weapon; + +// +// List of section types: +// + +deh_section_t *deh_section_types[] = +{ + &deh_section_ammo, + &deh_section_frame, +// &deh_section_pointer, TODO + &deh_section_sound, +// &deh_section_text, TODO + &deh_section_thing, + &deh_section_weapon, + NULL +}; + +// HHE only worked with Heretic 1.0 and unfortunately was never updated +// to support Heretic 1.3. Between Heretic 1.0 and 1.3, two new frames +// were added to the "states" table, to extend the "flame death" +// animation displayed when the player is killed by fire. Therefore, +// we must map the HHE frame numbers (which assume a Heretic 1.0 frame +// table) to corresponding indexes for the Heretic 1.3 frame table. + +int DEH_MapHereticFrameNumber(int frame) +{ + if (frame >= S_PLAY_FDTH19) + { + frame = (frame - S_PLAY_FDTH19) + S_BLOODYSKULL1; + } + + return frame; +} + diff --git a/src/heretic/deh_htic.h b/src/heretic/deh_htic.h new file mode 100644 index 00000000..fefcf818 --- /dev/null +++ b/src/heretic/deh_htic.h @@ -0,0 +1,45 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2010 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +//----------------------------------------------------------------------------- +// +// Common header for Heretic dehacked (HHE) support. +// +//----------------------------------------------------------------------------- + +#ifndef DEH_HTIC_H +#define DEH_HTIC_H + +#include "info.h" + +// HHE doesn't know about the last two states in the state table, so +// these are considered invalid. + +#define DEH_HERETIC_NUMSTATES (NUMSTATES - 2) + +// It also doesn't know about the last two things in the mobjinfo table +// (which correspond to the states above) + +#define DEH_HERETIC_NUMMOBJTYPES (NUMMOBJTYPES - 2) + +int DEH_MapHereticFrameNumber(int frame); + +#endif /* #ifndef DEH_HTIC_H */ + diff --git a/src/heretic/deh_sound.c b/src/heretic/deh_sound.c new file mode 100644 index 00000000..d1f266dd --- /dev/null +++ b/src/heretic/deh_sound.c @@ -0,0 +1,118 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2005 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +//----------------------------------------------------------------------------- +// +// Parses "Sound" sections in dehacked files +// +//----------------------------------------------------------------------------- + +#include +#include + +#include "doomfeatures.h" +#include "doomtype.h" +#include "deh_defs.h" +#include "deh_main.h" +#include "deh_mapping.h" + +#include "doomdef.h" +#include "i_sound.h" + +#include "sounds.h" + +DEH_BEGIN_MAPPING(sound_mapping, sfxinfo_t) + DEH_MAPPING_STRING("Name", name) + DEH_UNSUPPORTED_MAPPING("Special") + DEH_MAPPING("Value", priority) + DEH_MAPPING("Unknown 1", usefulness) + DEH_UNSUPPORTED_MAPPING("Unknown 2") + DEH_UNSUPPORTED_MAPPING("Unknown 3") + DEH_MAPPING("One/Two", numchannels) +DEH_END_MAPPING + +static void *DEH_SoundStart(deh_context_t *context, char *line) +{ + int sound_number = 0; + + if (sscanf(line, "Sound %i", &sound_number) != 1) + { + DEH_Warning(context, "Parse error on section start"); + return NULL; + } + + if (sound_number < 0 || sound_number >= NUMSFX) + { + DEH_Warning(context, "Invalid sound number: %i", sound_number); + return NULL; + } + + if (sound_number >= DEH_VANILLA_NUMSFX) + { + DEH_Warning(context, "Attempt to modify SFX %i. This will cause " + "problems in Vanilla dehacked.", sound_number); + } + + return &S_sfx[sound_number]; +} + +static void DEH_SoundParseLine(deh_context_t *context, char *line, void *tag) +{ + sfxinfo_t *sfx; + char *variable_name, *value; + + if (tag == NULL) + return; + + sfx = (sfxinfo_t *) tag; + + // Parse the assignment + + if (!DEH_ParseAssignment(line, &variable_name, &value)) + { + // Failed to parse + DEH_Warning(context, "Failed to parse assignment"); + return; + } + + // Set the field value: + + if (!strcasecmp(variable_name, "Name")) + { + DEH_SetStringMapping(context, &sound_mapping, sfx, + variable_name, value); + } + else + { + DEH_SetMapping(context, &sound_mapping, sfx, + variable_name, atoi(value)); + } +} + +deh_section_t deh_section_sound = +{ + "Sound", + NULL, + DEH_SoundStart, + DEH_SoundParseLine, + NULL, + NULL, +}; + diff --git a/src/heretic/deh_thing.c b/src/heretic/deh_thing.c new file mode 100644 index 00000000..fe94f3b8 --- /dev/null +++ b/src/heretic/deh_thing.c @@ -0,0 +1,149 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2005 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +//----------------------------------------------------------------------------- +// +// Parses "Thing" sections in dehacked files +// +//----------------------------------------------------------------------------- + +#include +#include + +#include "doomtype.h" + +#include "deh_defs.h" +#include "deh_main.h" +#include "deh_mapping.h" +#include "deh_htic.h" + +#include "info.h" + +DEH_BEGIN_MAPPING(thing_mapping, mobjinfo_t) + DEH_MAPPING("ID #", doomednum) + DEH_MAPPING("Initial frame", spawnstate) + DEH_MAPPING("Hit points", spawnhealth) + DEH_MAPPING("First moving frame", seestate) + DEH_MAPPING("Alert sound", seesound) + DEH_MAPPING("Reaction time", reactiontime) + DEH_MAPPING("Attack sound", attacksound) + DEH_MAPPING("Injury frame", painstate) + DEH_MAPPING("Pain chance", painchance) + DEH_MAPPING("Pain sound", painsound) + DEH_MAPPING("Close attack frame", meleestate) + DEH_MAPPING("Far attack frame", missilestate) + DEH_MAPPING("Burning frame", crashstate) + DEH_MAPPING("Death frame", deathstate) + DEH_MAPPING("Exploding frame", xdeathstate) + DEH_MAPPING("Death sound", deathsound) + DEH_MAPPING("Speed", speed) + DEH_MAPPING("Width", radius) + DEH_MAPPING("Height", height) + DEH_MAPPING("Mass", mass) + DEH_MAPPING("Missile damage", damage) + DEH_MAPPING("Action sound", activesound) + DEH_MAPPING("Bits 1", flags) + DEH_MAPPING("Bits 2", flags2) +DEH_END_MAPPING + +static void *DEH_ThingStart(deh_context_t *context, char *line) +{ + int thing_number = 0; + mobjinfo_t *mobj; + + if (sscanf(line, "Thing %i", &thing_number) != 1) + { + DEH_Warning(context, "Parse error on section start"); + return NULL; + } + + // HHE thing numbers are indexed from 1 + --thing_number; + + if (thing_number < 0 || thing_number >= DEH_HERETIC_NUMMOBJTYPES) + { + DEH_Warning(context, "Invalid thing number: %i", thing_number); + return NULL; + } + + mobj = &mobjinfo[thing_number]; + + return mobj; +} + +static void DEH_ThingParseLine(deh_context_t *context, char *line, void *tag) +{ + mobjinfo_t *mobj; + char *variable_name, *value; + int ivalue; + + if (tag == NULL) + return; + + mobj = (mobjinfo_t *) tag; + + // Parse the assignment + + if (!DEH_ParseAssignment(line, &variable_name, &value)) + { + // Failed to parse + + DEH_Warning(context, "Failed to parse assignment"); + return; + } + + // all values are integers + + ivalue = atoi(value); + + // If the value to be set is a frame, the frame number must + // undergo transformation from a Heretic 1.0 index to a + // Heretic 1.3 index. + + if (strstr(variable_name, "frame") != NULL) + { + ivalue = DEH_MapHereticFrameNumber(ivalue); + } + + // Set the field value + + DEH_SetMapping(context, &thing_mapping, mobj, variable_name, ivalue); +} + +static void DEH_ThingMD5Sum(md5_context_t *context) +{ + int i; + + for (i=0; i +#include +#include + +#include "doomtype.h" + +#include "doomdef.h" + +#include "deh_defs.h" +#include "deh_main.h" +#include "deh_mapping.h" +#include "deh_htic.h" + +DEH_BEGIN_MAPPING(weapon_mapping, weaponinfo_t) + DEH_MAPPING("Ammo type", ammo) + DEH_MAPPING("Deselect frame", upstate) + DEH_MAPPING("Select frame", downstate) + DEH_MAPPING("Bobbing frame", readystate) + DEH_MAPPING("Shooting frame", atkstate) + DEH_MAPPING("Firing frame", holdatkstate) + DEH_MAPPING("Unknown frame", flashstate) +DEH_END_MAPPING + +static void *DEH_WeaponStart(deh_context_t *context, char *line) +{ + int weapon_number = 0; + + if (sscanf(line, "Weapon %i", &weapon_number) != 1) + { + DEH_Warning(context, "Parse error on section start"); + return NULL; + } + + if (weapon_number < 0 || weapon_number >= NUMWEAPONS * 2) + { + DEH_Warning(context, "Invalid weapon number: %i", weapon_number); + return NULL; + } + + // Because of the tome of power, we have two levels of weapons: + + if (weapon_number < NUMWEAPONS) + { + return &wpnlev1info[weapon_number]; + } + else + { + return &wpnlev2info[weapon_number - NUMWEAPONS]; + } +} + +static void DEH_WeaponParseLine(deh_context_t *context, char *line, void *tag) +{ + char *variable_name, *value; + weaponinfo_t *weapon; + int ivalue; + + if (tag == NULL) + return; + + weapon = (weaponinfo_t *) tag; + + if (!DEH_ParseAssignment(line, &variable_name, &value)) + { + // Failed to parse + + DEH_Warning(context, "Failed to parse assignment"); + return; + } + + ivalue = atoi(value); + + // If this is a frame field, we need to map from Heretic 1.0 frame + // numbers to Heretic 1.3 frame numbers. + + if (strstr(variable_name, "frame") != NULL) + { + ivalue = DEH_MapHereticFrameNumber(ivalue); + } + + DEH_SetMapping(context, &weapon_mapping, weapon, variable_name, ivalue); +} + +static void DEH_WeaponMD5Sum(md5_context_t *context) +{ + int i; + + for (i=0; i #include "doomtype.h" +#include "m_misc.h" #include "deh_defs.h" #include "deh_main.h" @@ -117,7 +118,7 @@ static void DEH_ThingParseLine(deh_context_t *context, char *line, void *tag) // undergo transformation from a Heretic 1.0 index to a // Heretic 1.3 index. - if (strstr(variable_name, "frame") != NULL) + if (M_StrCaseStr(variable_name, "frame") != NULL) { ivalue = DEH_MapHereticFrameNumber(ivalue); } diff --git a/src/heretic/deh_weapon.c b/src/heretic/deh_weapon.c index 1daae766..28a90c68 100644 --- a/src/heretic/deh_weapon.c +++ b/src/heretic/deh_weapon.c @@ -29,6 +29,7 @@ #include #include "doomtype.h" +#include "m_misc.h" #include "doomdef.h" @@ -99,7 +100,7 @@ static void DEH_WeaponParseLine(deh_context_t *context, char *line, void *tag) // If this is a frame field, we need to map from Heretic 1.0 frame // numbers to Heretic 1.3 frame numbers. - if (strstr(variable_name, "frame") != NULL) + if (M_StrCaseStr(variable_name, "frame") != NULL) { ivalue = DEH_MapHereticFrameNumber(ivalue); } diff --git a/src/m_misc.c b/src/m_misc.c index 5847f1a2..9d3144b2 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -255,3 +255,29 @@ void M_ForceUppercase(char *text) } } +// +// M_StrCaseStr +// +// Case-insensitive version of strstr() +// + +char *M_StrCaseStr(char *haystack, char *needle) +{ + unsigned int needle_len; + unsigned int len; + unsigned int i; + + needle_len = strlen(needle); + len = strlen(haystack) - needle_len; + + for (i = 0; i <= len; ++i) + { + if (!strncasecmp(haystack + i, needle, needle_len)) + { + return haystack + i; + } + } + + return NULL; +} + diff --git a/src/m_misc.h b/src/m_misc.h index c92ddde8..c6be6ccb 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -42,7 +42,7 @@ long M_FileLength(FILE *handle); boolean M_StrToInt(const char *str, int *result); void M_ExtractFileBase(char *path, char *dest); void M_ForceUppercase(char *text); - +char *M_StrCaseStr(char *haystack, char *needle); #endif -- cgit v1.2.3 From b83a1ed02c6f22ebbced208b02bb59ff94bed941 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Mon, 8 Feb 2010 19:13:34 +0000 Subject: Add HHE 1.0 header signature to list of accepted signatures. Subversion-branch: /branches/raven-branch Subversion-revision: 1862 --- src/heretic/deh_htic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/heretic/deh_htic.c b/src/heretic/deh_htic.c index 11e73075..ca1c6d5c 100644 --- a/src/heretic/deh_htic.c +++ b/src/heretic/deh_htic.c @@ -31,6 +31,7 @@ char *deh_signatures[] = { + "Patch File for HHE v1.0", "Patch File for HHE v1.1", NULL }; -- cgit v1.2.3 From c09b386e1f3cb3518b22c440d6fb7c081ff3aa0b Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Mon, 8 Feb 2010 19:15:32 +0000 Subject: Only call InitializeSections() when a dehacked patch is actually loaded. Subversion-branch: /branches/raven-branch Subversion-revision: 1863 --- src/deh_main.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/deh_main.c b/src/deh_main.c index 3f0a6f29..75934087 100644 --- a/src/deh_main.c +++ b/src/deh_main.c @@ -39,6 +39,8 @@ extern deh_section_t *deh_section_types[]; extern char *deh_signatures[]; +static boolean deh_initialized = false; + // If true, we can do long string replacements. boolean deh_allow_long_strings = false; @@ -322,6 +324,12 @@ int DEH_LoadFile(char *filename) { deh_context_t *context; + if (!deh_initialized) + { + InitializeSections(); + deh_initialized = true; + } + printf(" loading %s\n", filename); context = DEH_OpenFile(filename); @@ -346,8 +354,6 @@ void DEH_Init(void) char *filename; int p; - InitializeSections(); - //! // @category mod // -- cgit v1.2.3 From 9769464ecf7ae3c92c628deed8164b61f567923a Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Mon, 8 Feb 2010 19:16:27 +0000 Subject: Call DEH_Init on Heretic startup so that patches will be loaded. Subversion-branch: /branches/raven-branch Subversion-revision: 1864 --- src/heretic/d_main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/heretic/d_main.c b/src/heretic/d_main.c index 10f5fd3e..ddcbca20 100644 --- a/src/heretic/d_main.c +++ b/src/heretic/d_main.c @@ -33,6 +33,7 @@ #include "config.h" #include "ct_chat.h" #include "doomdef.h" +#include "deh_main.h" #include "d_iwad.h" #include "i_endoom.h" #include "i_joystick.h" @@ -890,6 +891,11 @@ void D_DoomMain(void) printf("Z_Init: Init zone memory allocation daemon.\n"); Z_Init(); +#ifdef FEATURE_DEHACKED + printf("DEH_Init: Init Dehacked support.\n"); + DEH_Init(); +#endif + printf("W_Init: Init WADfiles.\n"); iwadfile = D_FindIWAD(IWAD_MASK_HERETIC, &gamemission); -- cgit v1.2.3 From 08e0b1471b357ae22a632d1752919d973db8983e Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Mon, 8 Feb 2010 19:18:48 +0000 Subject: Move action function prototypes in info.c into separate p_action.h header. Add table of action functions along with their location in the Heretic 1.0 executable, so that the "Action pointer" frame property can be set. Subversion-branch: /branches/raven-branch Subversion-revision: 1865 --- src/heretic/Makefile.am | 1 + src/heretic/deh_frame.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++-- src/heretic/info.c | 129 +--------------------------------- src/heretic/p_action.h | 160 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 338 insertions(+), 133 deletions(-) create mode 100644 src/heretic/p_action.h diff --git a/src/heretic/Makefile.am b/src/heretic/Makefile.am index 3fdd3ff7..081ca989 100644 --- a/src/heretic/Makefile.am +++ b/src/heretic/Makefile.am @@ -20,6 +20,7 @@ info.c info.h \ in_lude.c \ m_random.c m_random.h \ mn_menu.c \ + p_action.h \ p_ceilng.c \ p_doors.c \ p_enemy.c \ diff --git a/src/heretic/deh_frame.c b/src/heretic/deh_frame.c index 7dfb5aab..6ec62ae7 100644 --- a/src/heretic/deh_frame.c +++ b/src/heretic/deh_frame.c @@ -36,6 +36,145 @@ #include "deh_mapping.h" #include "deh_htic.h" +#include "p_action.h" + +// Offsets of action pointers within the Heretic 1.0 executable. +// (Seriously Greg, was this really necessary? What was wrong with the +// "copying action pointer from another frame" technique used in dehacked?) + +static const struct +{ + int offset; + void (*func)(); +} action_ptrs[] = { + { 0, NULL }, + { 69200, A_Look }, + { 69328, A_Chase }, + { 69872, A_FaceTarget }, + { 69984, A_Pain }, + { 70016, A_DripBlood }, + { 70160, A_KnightAttack }, + { 70304, A_ImpExplode }, + { 70480, A_BeastPuff }, + { 70592, A_ImpMeAttack }, + { 70672, A_ImpMsAttack }, + { 70880, A_ImpMsAttack2 }, + { 70976, A_ImpDeath }, + { 71024, A_ImpXDeath1 }, + { 71072, A_ImpXDeath2 }, + { 71376, A_ChicAttack }, + { 71456, A_ChicLook }, + { 71488, A_ChicChase }, + { 71520, A_ChicPain }, + { 71568, A_Feathers }, + { 71808, A_MummyAttack }, + { 71920, A_MummyAttack2 }, + { 72016, A_MummyFX1Seek }, + { 72048, A_MummySoul }, + { 72096, A_Sor1Pain }, + { 72144, A_Sor1Chase }, + { 72192, A_Srcr1Attack }, + { 72480, A_SorcererRise }, + { 72816, A_Srcr2Decide }, + { 72896, A_Srcr2Attack }, + { 73120, A_BlueSpark }, + { 73232, A_GenWizard }, + { 73392, A_Sor2DthInit }, + { 73424, A_Sor2DthLoop }, + { 73456, A_SorZap }, + { 73488, A_SorRise }, + { 73520, A_SorDSph }, + { 73552, A_SorDExp }, + { 73584, A_SorDBon }, + { 73616, A_SorSightSnd }, + { 73648, A_MinotaurAtk1 }, + { 73760, A_MinotaurDecide }, + { 74032, A_MinotaurCharge }, + { 74112, A_MinotaurAtk2 }, + { 74352, A_MinotaurAtk3 }, + { 74528, A_MntrFloorFire }, + { 74640, A_BeastAttack }, + { 74752, A_HeadAttack }, + { 75168, A_WhirlwindSeek }, + { 75328, A_HeadIceImpact }, + { 75488, A_HeadFireGrow }, + { 75632, A_SnakeAttack }, + { 75712, A_SnakeAttack2 }, + { 75792, A_ClinkAttack }, + { 75872, A_GhostOff }, + { 75888, A_WizAtk1 }, + { 75920, A_WizAtk2 }, + { 75952, A_WizAtk3 }, + { 76144, A_Scream }, + { 76400, A_NoBlocking }, + { 76784, A_Explode }, + { 76896, A_PodPain }, + { 77056, A_RemovePod }, + { 77104, A_MakePod }, + { 77344, A_BossDeath }, + { 77472, A_ESound }, + { 77520, A_SpawnTeleGlitter }, + { 77600, A_SpawnTeleGlitter2 }, + { 77680, A_AccTeleGlitter }, + { 77728, A_InitKeyGizmo }, + { 77824, A_VolcanoSet }, + { 77856, A_VolcanoBlast }, + { 78080, A_VolcBallImpact }, + { 78288, A_SkullPop }, + { 78448, A_CheckSkullFloor }, + { 78480, A_CheckSkullDone }, + { 78512, A_FreeTargMobj }, + { 78608, A_AddPlayerCorpse }, + { 78704, A_FlameSnd }, + { 78736, A_HideThing }, + { 78752, A_UnHideThing }, + { 81952, A_RestoreArtifact }, + { 82048, A_RestoreSpecialThing1 }, + { 82128, A_RestoreSpecialThing2 }, + { 108432, A_ContMobjSound }, + { 111168, A_WeaponReady }, + { 111568, A_BeakReady }, + { 111696, A_ReFire }, + { 111760, A_Lower }, + { 111856, A_BeakRaise }, + { 111920, A_Raise }, + { 112272, A_BeakAttackPL1 }, + { 112448, A_BeakAttackPL2 }, + { 112640, A_StaffAttackPL1 }, + { 112784, A_StaffAttackPL2 }, + { 112928, A_FireBlasterPL1 }, + { 113072, A_FireBlasterPL2 }, + { 113152, A_FireGoldWandPL1 }, + { 113296, A_FireGoldWandPL2 }, + { 113760, A_FireMacePL1 }, + { 113904, A_MacePL1Check }, + { 114032, A_MaceBallImpact }, + { 114192, A_MaceBallImpact2 }, + { 114624, A_FireMacePL2 }, + { 114752, A_DeathBallImpact }, + { 115088, A_SpawnRippers }, + { 115232, A_FireCrossbowPL1 }, + { 115312, A_FireCrossbowPL2 }, + { 115456, A_BoltSpark }, + { 115568, A_FireSkullRodPL1 }, + { 115648, A_FireSkullRodPL2 }, + { 115776, A_SkullRodPL2Seek }, + { 115808, A_AddPlayerRain }, + { 115984, A_SkullRodStorm }, + { 116272, A_RainImpact }, + { 116336, A_HideInCeiling }, + { 116368, A_FirePhoenixPL1 }, + { 116480, A_RemovedPhoenixFunc }, + { 116496, A_PhoenixPuff }, + { 116720, A_InitPhoenixPL2 }, + { 116736, A_FirePhoenixPL2 }, + { 117104, A_ShutdownPhoenixPL2 }, + { 117120, A_FlameEnd }, + { 117152, A_FloatPuff }, + { 117184, A_GauntletAttack }, + { 117648, A_Light0 } +}; + DEH_BEGIN_MAPPING(state_mapping, state_t) DEH_MAPPING("Sprite number", sprite) DEH_MAPPING("Sprite subnumber", frame) @@ -43,7 +182,6 @@ DEH_BEGIN_MAPPING(state_mapping, state_t) DEH_MAPPING("Next frame", nextstate) DEH_MAPPING("Unknown 1", misc1) DEH_MAPPING("Unknown 2", misc2) - DEH_UNSUPPORTED_MAPPING("Action pointer") DEH_END_MAPPING // When a HHE patch is first loaded, we must apply a small change @@ -87,6 +225,22 @@ static void *DEH_FrameStart(deh_context_t *context, char *line) return state; } +static boolean GetActionPointerForOffset(int offset, void **result) +{ + int i; + + for (i=0; iaction = func; } + else + { + // "Next frame" numbers need to undergo mapping. - DEH_SetMapping(context, &state_mapping, state, variable_name, ivalue); + if (!strcasecmp(variable_name, "Next frame")) + { + ivalue = DEH_MapHereticFrameNumber(ivalue); + } + + DEH_SetMapping(context, &state_mapping, state, variable_name, ivalue); + } } static void DEH_FrameMD5Sum(md5_context_t *context) diff --git a/src/heretic/info.c b/src/heretic/info.c index 7149220e..b6dd921f 100644 --- a/src/heretic/info.c +++ b/src/heretic/info.c @@ -22,7 +22,7 @@ // //----------------------------------------------------------------------------- #include "doomdef.h" -// generated by multigen +#include "p_action.h" char *sprnames[] = { "IMPX","ACLO","PTN1","SHLD","SHD2","BAGH","SPMP","INVS","PTN2","SOAR", @@ -41,133 +41,6 @@ char *sprnames[] = { NULL }; -void A_FreeTargMobj(); -void A_RestoreSpecialThing1(); -void A_RestoreSpecialThing2(); -void A_HideThing(); -void A_UnHideThing(); -void A_RestoreArtifact(); -void A_Scream(); -void A_Explode(); -void A_PodPain(); -void A_RemovePod(); -void A_MakePod(); -void A_InitKeyGizmo(); -void A_VolcanoSet(); -void A_VolcanoBlast(); -void A_BeastPuff(); -void A_VolcBallImpact(); -void A_SpawnTeleGlitter(); -void A_SpawnTeleGlitter2(); -void A_AccTeleGlitter(); -void A_Light0(); -void A_WeaponReady(); -void A_Lower(); -void A_Raise(); -void A_StaffAttackPL1(); -void A_ReFire(); -void A_StaffAttackPL2(); -void A_BeakReady(); -void A_BeakRaise(); -void A_BeakAttackPL1(); -void A_BeakAttackPL2(); -void A_GauntletAttack(); -void A_FireBlasterPL1(); -void A_FireBlasterPL2(); -void A_SpawnRippers(); -void A_FireMacePL1(); -void A_FireMacePL2(); -void A_MacePL1Check(); -void A_MaceBallImpact(); -void A_MaceBallImpact2(); -void A_DeathBallImpact(); -void A_FireSkullRodPL1(); -void A_FireSkullRodPL2(); -void A_SkullRodPL2Seek(); -void A_AddPlayerRain(); -void A_HideInCeiling(); -void A_SkullRodStorm(); -void A_RainImpact(); -void A_FireGoldWandPL1(); -void A_FireGoldWandPL2(); -void A_FirePhoenixPL1(); -void A_InitPhoenixPL2(); -void A_FirePhoenixPL2(); -void A_ShutdownPhoenixPL2(); -void A_PhoenixPuff(); -void A_RemovedPhoenixFunc(); -void A_FlameEnd(); -void A_FloatPuff(); -void A_FireCrossbowPL1(); -void A_FireCrossbowPL2(); -void A_BoltSpark(); -void A_Pain(); -void A_NoBlocking(); -void A_AddPlayerCorpse(); -void A_SkullPop(); -void A_FlameSnd(); -void A_CheckBurnGone(); -void A_CheckSkullFloor(); -void A_CheckSkullDone(); -void A_Feathers(); -void A_ChicLook(); -void A_ChicChase(); -void A_ChicPain(); -void A_FaceTarget(); -void A_ChicAttack(); -void A_Look(); -void A_Chase(); -void A_MummyAttack(); -void A_MummyAttack2(); -void A_MummySoul(); -void A_ContMobjSound(); -void A_MummyFX1Seek(); -void A_BeastAttack(); -void A_SnakeAttack(); -void A_SnakeAttack2(); -void A_HeadAttack(); -void A_BossDeath(); -void A_HeadIceImpact(); -void A_HeadFireGrow(); -void A_WhirlwindSeek(); -void A_ClinkAttack(); -void A_WizAtk1(); -void A_WizAtk2(); -void A_WizAtk3(); -void A_GhostOff(); -void A_ImpMeAttack(); -void A_ImpMsAttack(); -void A_ImpMsAttack2(); -void A_ImpDeath(); -void A_ImpXDeath1(); -void A_ImpXDeath2(); -void A_ImpExplode(); -void A_KnightAttack(); -void A_DripBlood(); -void A_Sor1Chase(); -void A_Sor1Pain(); -void A_Srcr1Attack(); -void A_SorZap(); -void A_SorcererRise(); -void A_SorRise(); -void A_SorSightSnd(); -void A_Srcr2Decide(); -void A_Srcr2Attack(); -void A_Sor2DthInit(); -void A_SorDSph(); -void A_Sor2DthLoop(); -void A_SorDExp(); -void A_SorDBon(); -void A_BlueSpark(); -void A_GenWizard(); -void A_MinotaurAtk1(); -void A_MinotaurDecide(); -void A_MinotaurAtk2(); -void A_MinotaurAtk3(); -void A_MinotaurCharge(); -void A_MntrFloorFire(); -void A_ESound(); - state_t states[NUMSTATES] = { {SPR_IMPX, 0, -1, NULL, S_NULL, 0, 0}, // S_NULL {SPR_ACLO, 4, 1050, A_FreeTargMobj, S_NULL, 0, 0}, // S_FREETARGMOBJ diff --git a/src/heretic/p_action.h b/src/heretic/p_action.h new file mode 100644 index 00000000..8d8e383c --- /dev/null +++ b/src/heretic/p_action.h @@ -0,0 +1,160 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 1993-1996 Id Software, Inc. +// Copyright(C) 1993-2008 Raven Software +// Copyright(C) 2008 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +//----------------------------------------------------------------------------- +// +// External definitions for action pointer functions. +// +//----------------------------------------------------------------------------- + +#ifndef HERETIC_P_ACTION_H +#define HERETIC_P_ACTION_H + +void A_FreeTargMobj(); +void A_RestoreSpecialThing1(); +void A_RestoreSpecialThing2(); +void A_HideThing(); +void A_UnHideThing(); +void A_RestoreArtifact(); +void A_Scream(); +void A_Explode(); +void A_PodPain(); +void A_RemovePod(); +void A_MakePod(); +void A_InitKeyGizmo(); +void A_VolcanoSet(); +void A_VolcanoBlast(); +void A_BeastPuff(); +void A_VolcBallImpact(); +void A_SpawnTeleGlitter(); +void A_SpawnTeleGlitter2(); +void A_AccTeleGlitter(); +void A_Light0(); +void A_WeaponReady(); +void A_Lower(); +void A_Raise(); +void A_StaffAttackPL1(); +void A_ReFire(); +void A_StaffAttackPL2(); +void A_BeakReady(); +void A_BeakRaise(); +void A_BeakAttackPL1(); +void A_BeakAttackPL2(); +void A_GauntletAttack(); +void A_FireBlasterPL1(); +void A_FireBlasterPL2(); +void A_SpawnRippers(); +void A_FireMacePL1(); +void A_FireMacePL2(); +void A_MacePL1Check(); +void A_MaceBallImpact(); +void A_MaceBallImpact2(); +void A_DeathBallImpact(); +void A_FireSkullRodPL1(); +void A_FireSkullRodPL2(); +void A_SkullRodPL2Seek(); +void A_AddPlayerRain(); +void A_HideInCeiling(); +void A_SkullRodStorm(); +void A_RainImpact(); +void A_FireGoldWandPL1(); +void A_FireGoldWandPL2(); +void A_FirePhoenixPL1(); +void A_InitPhoenixPL2(); +void A_FirePhoenixPL2(); +void A_ShutdownPhoenixPL2(); +void A_PhoenixPuff(); +void A_RemovedPhoenixFunc(); +void A_FlameEnd(); +void A_FloatPuff(); +void A_FireCrossbowPL1(); +void A_FireCrossbowPL2(); +void A_BoltSpark(); +void A_Pain(); +void A_NoBlocking(); +void A_AddPlayerCorpse(); +void A_SkullPop(); +void A_FlameSnd(); +void A_CheckBurnGone(); +void A_CheckSkullFloor(); +void A_CheckSkullDone(); +void A_Feathers(); +void A_ChicLook(); +void A_ChicChase(); +void A_ChicPain(); +void A_FaceTarget(); +void A_ChicAttack(); +void A_Look(); +void A_Chase(); +void A_MummyAttack(); +void A_MummyAttack2(); +void A_MummySoul(); +void A_ContMobjSound(); +void A_MummyFX1Seek(); +void A_BeastAttack(); +void A_SnakeAttack(); +void A_SnakeAttack2(); +void A_HeadAttack(); +void A_BossDeath(); +void A_HeadIceImpact(); +void A_HeadFireGrow(); +void A_WhirlwindSeek(); +void A_ClinkAttack(); +void A_WizAtk1(); +void A_WizAtk2(); +void A_WizAtk3(); +void A_GhostOff(); +void A_ImpMeAttack(); +void A_ImpMsAttack(); +void A_ImpMsAttack2(); +void A_ImpDeath(); +void A_ImpXDeath1(); +void A_ImpXDeath2(); +void A_ImpExplode(); +void A_KnightAttack(); +void A_DripBlood(); +void A_Sor1Chase(); +void A_Sor1Pain(); +void A_Srcr1Attack(); +void A_SorZap(); +void A_SorcererRise(); +void A_SorRise(); +void A_SorSightSnd(); +void A_Srcr2Decide(); +void A_Srcr2Attack(); +void A_Sor2DthInit(); +void A_SorDSph(); +void A_Sor2DthLoop(); +void A_SorDExp(); +void A_SorDBon(); +void A_BlueSpark(); +void A_GenWizard(); +void A_MinotaurAtk1(); +void A_MinotaurDecide(); +void A_MinotaurAtk2(); +void A_MinotaurAtk3(); +void A_MinotaurCharge(); +void A_MntrFloorFire(); +void A_ESound(); + +#endif /* #ifndef HERETIC_P_ACTION_H */ + -- cgit v1.2.3 From 56412813c70ba44a6f6a74cee5bf460ca6a31402 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Mon, 8 Feb 2010 21:14:30 +0000 Subject: Add Heretic implementation of HHE "Text" section, add DEH_String() around appropriate strings to allow string replacements. Subversion-branch: /branches/raven-branch Subversion-revision: 1867 --- src/heretic/Makefile.am | 1 + src/heretic/am_map.c | 7 +- src/heretic/ct_chat.c | 11 +- src/heretic/d_main.c | 72 ++-- src/heretic/deh_htext.c | 903 ++++++++++++++++++++++++++++++++++++++++++++++++ src/heretic/deh_htic.c | 6 +- src/heretic/f_finale.c | 23 +- src/heretic/g_game.c | 13 +- src/heretic/in_lude.c | 67 ++-- src/heretic/mn_menu.c | 52 +-- src/heretic/p_spec.c | 40 +-- src/heretic/p_switch.c | 5 +- src/heretic/p_user.c | 3 +- src/heretic/r_data.c | 38 +- src/heretic/r_draw.c | 49 +-- src/heretic/r_plane.c | 3 +- src/heretic/r_things.c | 14 +- src/heretic/sb_bar.c | 120 +++---- 18 files changed, 1184 insertions(+), 243 deletions(-) create mode 100644 src/heretic/deh_htext.c diff --git a/src/heretic/Makefile.am b/src/heretic/Makefile.am index 081ca989..e56ee806 100644 --- a/src/heretic/Makefile.am +++ b/src/heretic/Makefile.am @@ -59,6 +59,7 @@ i_ibm.c FEATURE_DEHACKED_SOURCE_FILES = \ deh_ammo.c \ deh_frame.c \ +deh_htext.c \ deh_htic.c \ deh_sound.c \ deh_thing.c \ diff --git a/src/heretic/am_map.c b/src/heretic/am_map.c index 99a8e206..aae30cc9 100644 --- a/src/heretic/am_map.c +++ b/src/heretic/am_map.c @@ -27,6 +27,7 @@ #include #include "doomdef.h" +#include "deh_str.h" #include "i_video.h" #include "m_controls.h" #include "p_local.h" @@ -408,7 +409,7 @@ void AM_loadPics(void) sprintf(namebuf, "AMMNUM%d", i); marknums[i] = W_CacheLumpName(namebuf, PU_STATIC); }*/ - maplump = W_CacheLumpName("AUTOPAGE", PU_STATIC); + maplump = W_CacheLumpName(DEH_String("AUTOPAGE"), PU_STATIC); } /*void AM_unloadPics(void) @@ -1473,6 +1474,7 @@ void AM_drawCrosshair(int color) void AM_Drawer(void) { + char *level_name; int numepisodes; if (!automapactive) @@ -1505,7 +1507,8 @@ void AM_Drawer(void) if (gameepisode <= numepisodes && gamemap < 10) { - MN_DrTextA(LevelNames[(gameepisode - 1) * 9 + gamemap - 1], 20, 145); + level_name = LevelNames[(gameepisode - 1) * 9 + gamemap - 1]; + MN_DrTextA(DEH_String(level_name), 20, 145); } // I_Update(); // V_MarkRect(f_x, f_y, f_w, f_h); diff --git a/src/heretic/ct_chat.c b/src/heretic/ct_chat.c index 75fa6035..958d1fff 100644 --- a/src/heretic/ct_chat.c +++ b/src/heretic/ct_chat.c @@ -29,6 +29,7 @@ #include #include "doomdef.h" #include "doomkeys.h" +#include "deh_str.h" #include "p_local.h" #include "s_sound.h" #include "v_video.h" @@ -115,7 +116,7 @@ void CT_Init(void) memset(plr_lastmsg[i], 0, MESSAGESIZE); memset(chat_msg[i], 0, MESSAGESIZE); } - FontABaseLump = W_GetNumForName("FONTA_S") + 1; + FontABaseLump = W_GetNumForName(DEH_String("FONTA_S")) + 1; return; } @@ -300,7 +301,7 @@ void CT_Ticker(void) CT_AddChar(i, 0); // set the end of message character if (numplayers > 2) { - strcpy(plr_lastmsg[i], CT_FromPlrText[i]); + strcpy(plr_lastmsg[i], DEH_String(CT_FromPlrText[i])); strcat(plr_lastmsg[i], chat_msg[i]); } else @@ -320,13 +321,13 @@ void CT_Ticker(void) if (numplayers > 1) { P_SetMessage(&players[consoleplayer], - "-MESSAGE SENT-", true); + DEH_String("-MESSAGE SENT-"), true); S_StartSound(NULL, sfx_chat); } else { P_SetMessage(&players[consoleplayer], - "THERE ARE NO OTHER PLAYERS IN THE GAME!", + DEH_String("THERE ARE NO OTHER PLAYERS IN THE GAME!"), true); S_StartSound(NULL, sfx_chat); } @@ -376,7 +377,7 @@ void CT_Drawer(void) x += patch->width; } } - V_DrawPatch(x, 10, W_CacheLumpName("FONTA59", PU_CACHE)); + V_DrawPatch(x, 10, W_CacheLumpName(DEH_String("FONTA59"), PU_CACHE)); BorderTopRefresh = true; UpdateState |= I_MESSAGES; } diff --git a/src/heretic/d_main.c b/src/heretic/d_main.c index ddcbca20..52e4239f 100644 --- a/src/heretic/d_main.c +++ b/src/heretic/d_main.c @@ -185,12 +185,12 @@ void D_Display(void) { if (!netgame) { - V_DrawPatch(160, viewwindowy + 5, W_CacheLumpName("PAUSED", + V_DrawPatch(160, viewwindowy + 5, W_CacheLumpName(DEH_String("PAUSED"), PU_CACHE)); } else { - V_DrawPatch(160, 70, W_CacheLumpName("PAUSED", PU_CACHE)); + V_DrawPatch(160, 70, W_CacheLumpName(DEH_String("PAUSED"), PU_CACHE)); } } // Handle player messages @@ -316,7 +316,7 @@ void D_PageDrawer(void) V_DrawRawScreen(W_CacheLumpName(pagename, PU_CACHE)); if (demosequence == 1) { - V_DrawPatch(4, 160, W_CacheLumpName("ADVISOR", PU_CACHE)); + V_DrawPatch(4, 160, W_CacheLumpName(DEH_String("ADVISOR"), PU_CACHE)); } UpdateState |= I_FULLSCRN; } @@ -348,45 +348,45 @@ void D_DoAdvanceDemo(void) case 0: pagetic = 210; gamestate = GS_DEMOSCREEN; - pagename = "TITLE"; + pagename = DEH_String("TITLE"); S_StartSong(mus_titl, false); break; case 1: pagetic = 140; gamestate = GS_DEMOSCREEN; - pagename = "TITLE"; + pagename = DEH_String("TITLE"); break; case 2: BorderNeedRefresh = true; UpdateState |= I_FULLSCRN; - G_DeferedPlayDemo("demo1"); + G_DeferedPlayDemo(DEH_String("demo1")); break; case 3: pagetic = 200; gamestate = GS_DEMOSCREEN; - pagename = "CREDIT"; + pagename = DEH_String("CREDIT"); break; case 4: BorderNeedRefresh = true; UpdateState |= I_FULLSCRN; - G_DeferedPlayDemo("demo2"); + G_DeferedPlayDemo(DEH_String("demo2")); break; case 5: pagetic = 200; gamestate = GS_DEMOSCREEN; if (gamemode == shareware) { - pagename = "ORDER"; + pagename = DEH_String("ORDER"); } else { - pagename = "CREDIT"; + pagename = DEH_String("CREDIT"); } break; case 6: BorderNeedRefresh = true; UpdateState |= I_FULLSCRN; - G_DeferedPlayDemo("demo3"); + G_DeferedPlayDemo(DEH_String("demo3")); break; } } @@ -639,7 +639,7 @@ void initStartup(void) // Blit main screen textScreen = TXT_GetScreenData(); - loading = W_CacheLumpName("LOADING", PU_CACHE); + loading = W_CacheLumpName(DEH_String("LOADING"), PU_CACHE); memcpy(textScreen, loading, 4000); // Print version string @@ -699,7 +699,7 @@ void tprintf(char *msg, int initflag) // haleyjd: moved up, removed WATCOMC code void CleanExit(void) { - printf("Exited from HERETIC.\n"); + printf(DEH_String("Exited from HERETIC.\n")); exit(1); } @@ -783,7 +783,7 @@ static void D_Endoom(void) return; } - endoom_data = W_CacheLumpName("ENDTEXT", PU_STATIC); + endoom_data = W_CacheLumpName(DEH_String("ENDTEXT"), PU_STATIC); I_Endoom(endoom_data); } @@ -848,7 +848,7 @@ void D_DoomMain(void) // // init subsystems // - printf("V_Init: allocate screens.\n"); + printf(DEH_String("V_Init: allocate screens.\n")); V_Init(); // Check for -CDROM @@ -873,7 +873,7 @@ void D_DoomMain(void) if (cdrom) { - M_SetConfigDir("c:\\heretic.cd\\"); + M_SetConfigDir(DEH_String("c:\\heretic.cd")); } else { @@ -881,14 +881,14 @@ void D_DoomMain(void) } // Load defaults before initing other systems - printf("M_LoadDefaults: Load system defaults.\n"); + printf(DEH_String("M_LoadDefaults: Load system defaults.\n")); D_BindVariables(); M_SetConfigFilenames("heretic.cfg", PROGRAM_PREFIX "heretic.cfg"); M_LoadDefaults(); I_AtExit(M_SaveDefaults, false); - printf("Z_Init: Init zone memory allocation daemon.\n"); + printf(DEH_String("Z_Init: Init zone memory allocation daemon.\n")); Z_Init(); #ifdef FEATURE_DEHACKED @@ -896,7 +896,7 @@ void D_DoomMain(void) DEH_Init(); #endif - printf("W_Init: Init WADfiles.\n"); + printf(DEH_String("W_Init: Init WADfiles.\n")); iwadfile = D_FindIWAD(IWAD_MASK_HERETIC, &gamemission); @@ -933,12 +933,12 @@ void D_DoomMain(void) } if (p && p < myargc - 1) { - sprintf(file, "%s.lmp", myargv[p + 1]); + sprintf(file, DEH_String("%s.lmp"), myargv[p + 1]); D_AddFile(file); - printf("Playing demo %s.lmp.\n", myargv[p + 1]); + printf(DEH_String("Playing demo %s.lmp.\n"), myargv[p + 1]); } - if (W_CheckNumForName("E2M1") == -1) + if (W_CheckNumForName(DEH_String("E2M1")) == -1) { gamemode = shareware; gamedescription = "Heretic (shareware)"; @@ -966,54 +966,54 @@ void D_DoomMain(void) // smsg[0] = 0; if (deathmatch) - status("DeathMatch..."); + status(DEH_String("DeathMatch...")); if (nomonsters) - status("No Monsters..."); + status(DEH_String("No Monsters...")); if (respawnparm) - status("Respawning..."); + status(DEH_String("Respawning...")); if (autostart) { char temp[64]; - sprintf(temp, "Warp to Episode %d, Map %d, Skill %d ", + sprintf(temp, DEH_String("Warp to Episode %d, Map %d, Skill %d "), startepisode, startmap, startskill + 1); status(temp); } wadprintf(); // print the added wadfiles - tprintf("MN_Init: Init menu system.\n", 1); + tprintf(DEH_String("MN_Init: Init menu system.\n"), 1); MN_Init(); CT_Init(); - tprintf("R_Init: Init Heretic refresh daemon.", 1); - hprintf("Loading graphics"); + tprintf(DEH_String("R_Init: Init Heretic refresh daemon."), 1); + hprintf(DEH_String("Loading graphics")); R_Init(); tprintf("\n", 0); - tprintf("P_Init: Init Playloop state.\n", 1); - hprintf("Init game engine."); + tprintf(DEH_String("P_Init: Init Playloop state.\n"), 1); + hprintf(DEH_String("Init game engine.")); P_Init(); IncThermo(); - tprintf("I_Init: Setting up machine state.\n", 1); + tprintf(DEH_String("I_Init: Setting up machine state.\n"), 1); I_CheckIsScreensaver(); I_InitTimer(); I_InitJoystick(); IncThermo(); - tprintf("S_Init: Setting up sound.\n", 1); + tprintf(DEH_String("S_Init: Setting up sound.\n"), 1); S_Init(); //IO_StartupTimer(); S_Start(); - tprintf("D_CheckNetGame: Checking network game status.\n", 1); - hprintf("Checking network game status."); + tprintf(DEH_String("D_CheckNetGame: Checking network game status.\n"), 1); + hprintf(DEH_String("Checking network game status.")); D_CheckNetGame(); IncThermo(); // haleyjd: removed WATCOMC - tprintf("SB_Init: Loading patches.\n", 1); + tprintf(DEH_String("SB_Init: Loading patches.\n"), 1); SB_Init(); IncThermo(); diff --git a/src/heretic/deh_htext.c b/src/heretic/deh_htext.c new file mode 100644 index 00000000..9075e610 --- /dev/null +++ b/src/heretic/deh_htext.c @@ -0,0 +1,903 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2005-2010 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +//----------------------------------------------------------------------------- +// +// Parses Text substitution sections in dehacked files +// +//----------------------------------------------------------------------------- + +#include +#include + +#include "doomtype.h" +#include "dstrings.h" + +#include "z_zone.h" + +#include "deh_defs.h" +#include "deh_io.h" +#include "deh_main.h" + +// +// Ok, Greg, the action pointers thing was bad enough, but this really +// takes the biscuit. Why does HHE's text replacement address strings +// by offset??!! The dehacked way was much nicer, why change it? +// +// Anyway, this is the Heretic 1.0 string table, which can be seen in +// HHE. This is the string and its offset from the start of the +// text section. Not all the strings are here; this is a redacted list +// that contains just the strings we support. +// + +static const struct +{ + unsigned int offset; + char *string; +} +string_table[] = +{ + { 0, NULL }, + { 4, NULL }, + { 64, NULL }, + { 104, NULL }, + { 160, NULL }, + { 200, NULL }, + { 220, NULL }, + { 228, "PLAYPAL" }, + { 236, NULL }, + { 244, NULL }, + { 252, NULL }, + { 272, NULL }, + { 288, NULL }, + { 296, NULL }, + { 316, NULL }, + { 332, NULL }, + { 372, NULL }, + { 436, NULL }, + { 504, NULL }, + { 536, NULL }, + { 544, NULL }, + { 560, NULL }, + { 576, NULL }, + { 584, "ENDTEXT" }, + { 592, NULL }, + { 612, NULL }, + { 640, NULL }, + { 664, NULL }, + { 712, NULL }, + { 744, NULL }, + { 764, NULL }, + { 808, NULL }, + { 820, NULL }, + { 828, NULL }, + { 840, NULL }, + { 876, NULL }, + { 884, NULL }, + { 908, NULL }, + { 952, NULL }, + { 992, NULL }, + { 1028, NULL }, + { 1036, NULL }, + { 1048, NULL }, + { 1088, NULL }, + { 1128, NULL }, + { 1160, NULL }, + { 1192, NULL }, + { 1212, NULL }, + { 1240, "E1M1: THE DOCKS" }, + { 1260, "E1M2: THE DUNGEONS" }, + { 1280, "E1M3: THE GATEHOUSE" }, + { 1304, "E1M4: THE GUARD TOWER" }, + { 1328, "E1M5: THE CITADEL" }, + { 1348, "E1M6: THE CATHEDRAL" }, + { 1372, "E1M7: THE CRYPTS" }, + { 1392, "E1M8: HELL'S MAW" }, + { 1412, "E1M9: THE GRAVEYARD" }, + { 1436, "E2M1: THE CRATER" }, + { 1456, "E2M2: THE LAVA PITS" }, + { 1480, "E2M3: THE RIVER OF FIRE" }, + { 1508, "E2M4: THE ICE GROTTO" }, + { 1532, "E2M5: THE CATACOMBS" }, + { 1556, "E2M6: THE LABYRINTH" }, + { 1580, "E2M7: THE GREAT HALL" }, + { 1604, "E2M8: THE PORTALS OF CHAOS" }, + { 1632, "E2M9: THE GLACIER" }, + { 1652, "E3M1: THE STOREHOUSE" }, + { 1676, "E3M2: THE CESSPOOL" }, + { 1696, "E3M3: THE CONFLUENCE" }, + { 1720, "E3M4: THE AZURE FORTRESS" }, + { 1748, "E3M5: THE OPHIDIAN LAIR" }, + { 1776, "E3M6: THE HALLS OF FEAR" }, + { 1804, "E3M7: THE CHASM" }, + { 1824, "E3M8: D'SPARIL'S KEEP" }, + { 1848, "E3M9: THE AQUIFER" }, + { 1868, "AUTOPAGE" }, + { 1880, "FOLLOW MODE ON" }, + { 1896, "FOLLOW MODE OFF" }, + { 1912, "GREEN: " }, + { 1936, "YELLOW: " }, + { 1948, "RED: " }, + { 1956, "BLUE: " }, + { 1964, "FONTA_S" }, + { 1972, "-MESSAGE SENT-" }, + { 1988, "THERE ARE NO OTHER PLAYERS IN THE GAME!" }, + { 2028, "FONTA59" }, + { 2036, "PAUSED" }, + { 2044, NULL }, + { 2056, NULL }, + { 2072, "ADVISOR" }, + { 2080, "TITLE" }, + { 2088, "demo1" }, + { 2096, "CREDIT" }, + { 2104, "demo2" }, + { 2112, "ORDER" }, + { 2120, "demo3" }, + { 2128, NULL }, + { 2140, NULL }, + { 2168, "hticsav%c.hsg" }, + { 2184, "heretic.wad" }, + { 2196, NULL }, + { 2212, NULL }, + { 2228, NULL }, + { 2240, "heretic.cfg" }, + { 2252, NULL }, + { 2264, NULL }, + { 2284, NULL }, + { 2304, "Exited from HERETIC.\n" }, + { 2328, NULL }, + { 2340, NULL }, + { 2352, NULL }, + { 2364, NULL }, + { 2372, NULL }, + { 2388, NULL }, + { 2404, NULL }, + { 2412, "c:\\heretic.cd" }, + { 2428, NULL }, + { 2436, NULL }, + { 2444, NULL }, + { 2464, NULL }, + { 2496, NULL }, + { 2508, NULL }, + { 2520, NULL }, + { 2528, "Playing demo %s.lmp.\n" }, + { 2552, NULL }, + { 2564, NULL }, + { 2572, NULL }, + { 2584, NULL }, + { 2592, "V_Init: allocate screens.\n" }, + { 2620, "M_LoadDefaults: Load system defaults.\n" }, + { 2660, "Z_Init: Init zone memory allocation daemon.\n" }, + { 2708, "W_Init: Init WADfiles.\n" }, + { 2732, "E2M1" }, + { 2740, "LOADING" }, + { 2748, "DeathMatch..." }, + { 2764, "No Monsters..." }, + { 2780, "Respawning..." }, + { 2796, "Warp to Episode %d, Map %d, Skill %d " }, + { 2836, "MN_Init: Init menu system.\n" }, + { 2864, "R_Init: Init Heretic refresh daemon." }, + { 2904, "Loading graphics" }, + { 2924, "P_Init: Init Playloop state." }, + { 2956, "Init game engine." }, + { 2976, "I_Init: Setting up machine state.\n" }, + { 3012, "D_CheckNetGame: Checking network game status.\n" }, + { 3060, "Checking network game status." }, + { 3092, "SB_Init: Loading patches.\n" }, + { 3120, NULL }, + { 3128, NULL }, + { 3140, NULL }, + { 3184, NULL }, + { 3220, NULL }, + { 3256, NULL }, + { 3280, NULL }, + { 3304, NULL }, + { 3320, NULL }, + { 3352, "PLAYER 1 HAS LEFT THE GAME" }, + { 3380, NULL }, + { 3432, NULL }, + { 3464, NULL }, + { 3508, NULL }, + { 3548, NULL }, + { 3600, NULL }, + { 3624, NULL }, + { 3664, NULL }, + { 3696, NULL }, + { 3736, "NET GAME" }, + { 3748, "SAVE GAME" }, + { 3760, "Only %i deathmatch spots, 4 required" }, + { 3800, "version %i" }, + { 3812, NULL }, + { 3828, NULL }, + { 3856, NULL }, + { 3872, NULL }, + { 3896, "GAME SAVED" }, + { 3908, "SKY1" }, + { 3916, "SKY2" }, + { 3924, "SKY3" }, + { 3932, NULL }, + { 3940, NULL }, + { 3976, NULL }, + { 3996, NULL }, + { 4016, E1TEXT }, + { 4536, E2TEXT }, + { 5068, E3TEXT }, + { 5632, "FLOOR25" }, + { 5640, "FLATHUH1" }, + { 5652, "FLTWAWA2" }, + { 5664, "FONTA_S" }, + { 5672, "FINAL1" }, + { 5680, "FINAL2" }, + { 5688, "E2PAL" }, + { 5696, "E2END" }, + { 5704, "PLAYPAL" }, + { 5712, "ORDER" }, + { 5720, "IMPX" }, + { 5728, "ACLO" }, + { 5736, "PTN1" }, + { 5744, "SHLD" }, + { 5752, "SHD2" }, + { 5760, "BAGH" }, + { 5768, "SPMP" }, + { 5776, "INVS" }, + { 5784, "PTN2" }, + { 5792, "SOAR" }, + { 5800, "INVU" }, + { 5808, "PWBK" }, + { 5816, "EGGC" }, + { 5824, "EGGM" }, + { 5832, "FX01" }, + { 5840, "SPHL" }, + { 5848, "TRCH" }, + { 5856, "FBMB" }, + { 5864, "XPL1" }, + { 5872, "ATLP" }, + { 5880, "PPOD" }, + { 5888, "AMG1" }, + { 5896, "SPSH" }, + { 5904, "LVAS" }, + { 5912, "SLDG" }, + { 5920, "SKH1" }, + { 5928, "SKH2" }, + { 5936, "SKH3" }, + { 5944, "SKH4" }, + { 5952, "CHDL" }, + { 5960, "SRTC" }, + { 5968, "SMPL" }, + { 5976, "STGS" }, + { 5984, "STGL" }, + { 5992, "STCS" }, + { 6000, "STCL" }, + { 6008, "KFR1" }, + { 6016, "BARL" }, + { 6024, "BRPL" }, + { 6032, "MOS1" }, + { 6040, "MOS2" }, + { 6048, "WTRH" }, + { 6056, "HCOR" }, + { 6064, "KGZ1" }, + { 6072, "KGZB" }, + { 6080, "KGZG" }, + { 6088, "KGZY" }, + { 6096, "VLCO" }, + { 6104, "VFBL" }, + { 6112, "VTFB" }, + { 6120, "SFFI" }, + { 6128, "TGLT" }, + { 6136, "TELE" }, + { 6144, "STFF" }, + { 6152, "PUF3" }, + { 6160, "PUF4" }, + { 6168, "BEAK" }, + { 6176, "WGNT" }, + { 6184, "GAUN" }, + { 6192, "PUF1" }, + { 6200, "WBLS" }, + { 6208, "BLSR" }, + { 6216, "FX18" }, + { 6224, "FX17" }, + { 6232, "WMCE" }, + { 6240, "MACE" }, + { 6248, "FX02" }, + { 6256, "WSKL" }, + { 6264, "HROD" }, + { 6272, "FX00" }, + { 6280, "FX20" }, + { 6288, "FX21" }, + { 6296, "FX22" }, + { 6304, "FX23" }, + { 6312, "GWND" }, + { 6320, "PUF2" }, + { 6328, "WPHX" }, + { 6336, "PHNX" }, + { 6344, "FX04" }, + { 6352, "FX08" }, + { 6360, "FX09" }, + { 6368, "WBOW" }, + { 6376, "CRBW" }, + { 6384, "FX03" }, + { 6392, "BLOD" }, + { 6400, "PLAY" }, + { 6408, "FDTH" }, + { 6416, "BSKL" }, + { 6424, "CHKN" }, + { 6432, "MUMM" }, + { 6440, "FX15" }, + { 6448, "BEAS" }, + { 6456, "FRB1" }, + { 6464, "SNKE" }, + { 6472, "SNFX" }, + { 6480, "HEAD" }, + { 6488, "FX05" }, + { 6496, "FX06" }, + { 6504, "FX07" }, + { 6512, "CLNK" }, + { 6520, "WZRD" }, + { 6528, "FX11" }, + { 6536, "FX10" }, + { 6544, "KNIG" }, + { 6552, "SPAX" }, + { 6560, "RAXE" }, + { 6568, "SRCR" }, + { 6576, "FX14" }, + { 6584, "SOR2" }, + { 6592, "SDTH" }, + { 6600, "FX16" }, + { 6608, "MNTR" }, + { 6616, "FX12" }, + { 6624, "FX13" }, + { 6632, "AKYY" }, + { 6640, "BKYY" }, + { 6648, "CKYY" }, + { 6656, "AMG2" }, + { 6664, "AMM1" }, + { 6672, "AMM2" }, + { 6680, "AMC1" }, + { 6688, "AMC2" }, + { 6696, "AMS1" }, + { 6704, "AMS2" }, + { 6712, "AMP1" }, + { 6720, "AMP2" }, + { 6728, "AMB1" }, + { 6736, "AMB2" }, + { 6768, "PLAYPAL" }, + { 6776, "MAPE1" }, + { 6784, "MAPE2" }, + { 6792, "MAPE3" }, + { 6800, "IN_X" }, + { 6808, "IN_YAH" }, + { 6816, "FONTB16" }, + { 6824, "FONTB_S" }, + { 6832, "FONTB13" }, + { 6840, "FONTB15" }, + { 6848, "FONTB05" }, + { 6856, "FACEA0" }, + { 6864, "FACEB0" }, + { 6872, NULL }, + { 6896, NULL }, + { 6940, "FLOOR16" }, + { 6948, "FINISHED" }, + { 6960, "NOW ENTERING:" }, + { 6976, "KILLS" }, + { 6984, "ITEMS" }, + { 6992, "SECRETS" }, + { 7000, "TIME" }, + { 7008, "BONUS" }, + { 7016, "SECRET" }, + { 7024, "TOTAL" }, + { 7032, "VICTIMS" }, + { 7044, "NEW GAME" }, + { 7056, "OPTIONS" }, + { 7064, "GAME FILES" }, + { 7076, "INFO" }, + { 7084, "QUIT GAME" }, + { 7096, "CITY OF THE DAMNED" }, + { 7116, "HELL'S MAW" }, + { 7128, "THE DOME OF D'SPARIL" }, + { 7152, "LOAD GAME" }, + { 7164, "SAVE GAME" }, + { 7176, "THOU NEEDETH A WET-NURSE" }, + { 7204, "YELLOWBELLIES-R-US" }, + { 7224, "BRINGEST THEM ONETH" }, + { 7244, "THOU ART A SMITE-MEISTER" }, + { 7272, "BLACK PLAGUE POSSESSES THEE" }, + { 7300, "END GAME" }, + { 7312, "MESSAGES : " }, + { 7324, "MOUSE SENSITIVITY" }, + { 7344, "MORE..." }, + { 7352, "SCREEN SIZE" }, + { 7364, "SFX VOLUME" }, + { 7376, "MUSIC VOLUME" }, + { 7392, "M_SKL00" }, + { 7400, "FONTA_S" }, + { 7408, "FONTB_S" }, + { 7416, "ARE YOU SURE YOU WANT TO QUIT?" }, + { 7448, "ARE YOU SURE YOU WANT TO END THE GAME?" }, + { 7488, "DO YOU WANT TO QUICKSAVE THE GAME NAMED" }, + { 7528, "DO YOU WANT TO QUICKLOAD THE GAME NAMED" }, + { 7572, "M_SLCTR1" }, + { 7584, "M_SLCTR2" }, + { 7596, "M_HTIC" }, + { 7604, NULL }, + { 7632, "hticsav%d.hsg" }, + { 7652, "M_FSLOT" }, + { 7668, "MESSAGES ON" }, + { 7680, "MESSAGES OFF" }, + { 7696, "YOU CAN'T START A NEW GAME FROM WITHIN A NETGAME!" }, + { 7748, "ONLY AVAILABLE IN THE REGISTERED VERSION" }, + { 7792, "PLAYPAL" }, + { 7800, "QUICKSAVING...." }, + { 7816, "QUICKLOADING...." }, + { 7836, "CHOOSE A QUICKSAVE SLOT" }, + { 7860, "CHOOSE A QUICKLOAD SLOT" }, + { 7884, "TITLE" }, + { 7892, "M_SLDLT" }, + { 7900, "M_SLDMD1" }, + { 7912, "M_SLDMD2" }, + { 7924, "M_SLDRT" }, + { 7932, "M_SLDKB" }, + { 7940, NULL }, + { 7968, NULL }, + { 7992, NULL }, + { 8020, NULL }, + { 8028, NULL }, + { 8056, NULL }, + { 8076, NULL }, + { 8088, NULL }, + { 8104, NULL }, + { 8116, NULL }, + { 8128, NULL }, + { 8136, NULL }, + { 8148, NULL }, + { 8164, NULL }, + { 8180, NULL }, + { 8192, NULL }, + { 8204, NULL }, + { 8220, NULL }, + { 8232, NULL }, + { 8248, NULL }, + { 8264, NULL }, + { 8276, NULL }, + { 8292, NULL }, + { 8308, NULL }, + { 8320, NULL }, + { 8328, NULL }, + { 8340, NULL }, + { 8352, NULL }, + { 8364, NULL }, + { 8376, NULL }, + { 8392, NULL }, + { 8408, NULL }, + { 8424, NULL }, + { 8436, NULL }, + { 8448, NULL }, + { 8460, NULL }, + { 8472, NULL }, + { 8488, NULL }, + { 8504, NULL }, + { 8520, NULL }, + { 8536, NULL }, + { 8548, NULL }, + { 8560, NULL }, + { 8572, NULL }, + { 8584, NULL }, + { 8596, NULL }, + { 8612, NULL }, + { 8624, NULL }, + { 8648, NULL }, + { 8660, NULL }, + { 8668, NULL }, + { 8680, NULL }, + { 8708, NULL }, + { 8720, NULL }, + { 8728, NULL }, + { 8740, NULL }, + { 8752, NULL }, + { 8764, NULL }, + { 8788, NULL }, + { 8800, NULL }, + { 8812, NULL }, + { 8824, NULL }, + { 8848, NULL }, + { 8880, NULL }, + { 8888, NULL }, + { 8896, NULL }, + { 8916, NULL }, + { 8968, "HRTIC00.pcx" }, + { 8980, NULL }, + { 9016, "SCREEN SHOT" }, + { 9028, "YOU NEED A BLUE KEY TO OPEN THIS DOOR" }, + { 9068, "YOU NEED A YELLOW KEY TO OPEN THIS DOOR" }, + { 9108, "YOU NEED A GREEN KEY TO OPEN THIS DOOR" }, + { 9148, NULL }, + { 9172, NULL }, + { 9220, NULL }, + { 9244, "CRYSTAL VIAL" }, + { 9260, "SILVER SHIELD" }, + { 9276, "ENCHANTED SHIELD" }, + { 9296, "BAG OF HOLDING" }, + { 9312, "MAP SCROLL" }, + { 9324, "BLUE KEY" }, + { 9336, "YELLOW KEY" }, + { 9348, "GREEN KEY" }, + { 9360, "QUARTZ FLASK" }, + { 9376, "WINGS OF WRATH" }, + { 9392, "RING OF INVINCIBILITY" }, + { 9416, "TOME OF POWER" }, + { 9432, "SHADOWSPHERE" }, + { 9448, "MORPH OVUM" }, + { 9460, "MYSTIC URN" }, + { 9472, "TORCH" }, + { 9480, "TIME BOMB OF THE ANCIENTS" }, + { 9508, "CHAOS DEVICE" }, + { 9524, "WAND CRYSTAL" }, + { 9540, "CRYSTAL GEODE" }, + { 9556, "MACE SPHERES" }, + { 9572, "PILE OF MACE SPHERES" }, + { 9596, "ETHEREAL ARROWS" }, + { 9612, "QUIVER OF ETHEREAL ARROWS" }, + { 9640, "CLAW ORB" }, + { 9652, "ENERGY ORB" }, + { 9664, "LESSER RUNES" }, + { 9680, "GREATER RUNES" }, + { 9696, "FLAME ORB" }, + { 9708, "INFERNO ORB" }, + { 9720, "FIREMACE" }, + { 9732, "ETHEREAL CROSSBOW" }, + { 9752, "DRAGON CLAW" }, + { 9764, "HELLSTAFF" }, + { 9776, "PHOENIX ROD" }, + { 9788, "GAUNTLETS OF THE NECROMANCER" }, + { 9820, NULL }, + { 9860, NULL }, + { 9892, NULL }, + { 9940, NULL }, + { 9972, NULL }, + { 10012, NULL }, + { 10052, NULL }, + { 10080, NULL }, + { 10088, "FLTWAWA1" }, + { 10100, "FLTFLWW1" }, + { 10112, "FLTLAVA1" }, + { 10124, "FLATHUH1" }, + { 10136, "FLTSLUD1" }, + { 10152, NULL }, + { 10192, NULL }, + { 10236, NULL }, + { 10248, NULL }, + { 10284, NULL }, + { 10320, NULL }, + { 10360, NULL }, + { 10392, NULL }, + { 10444, "PLAYPAL" }, + { 10452, NULL }, + { 10488, NULL }, + { 10508, NULL }, + { 10556, NULL }, + { 10596, "PNAMES" }, + { 10604, "TEXTURE1" }, + { 10616, "TEXTURE2" }, + { 10628, "S_END" }, + { 10636, "S_START" }, + { 10644, NULL }, + { 10684, NULL }, + { 10728, "F_START" }, + { 10736, "F_END" }, + { 10744, "COLORMAP" }, + { 10756, "\nR_InitTextures " }, + { 10776, "R_InitFlats\n" }, + { 10792, "R_InitSpriteLumps " }, + { 10812, NULL }, + { 10844, NULL }, + { 10880, NULL }, + { 10912, NULL }, + { 10948, "TINTTAB" }, + { 10956, NULL }, + { 10984, "FLOOR04" }, + { 10992, "FLAT513" }, + { 11000, "bordt" }, + { 11008, "bordb" }, + { 11016, "bordl" }, + { 11024, "bordr" }, + { 11032, "bordtl" }, + { 11040, "bordtr" }, + { 11048, "bordbr" }, + { 11056, "bordbl" }, + { 11064, "R_InitData " }, + { 11076, "R_InitPointToAngle\n" }, + { 11096, "R_InitTables " }, + { 11112, "R_InitPlanes\n" }, + { 11128, "R_InitLightTables " }, + { 11148, "R_InitSkyMap\n" }, + { 11164, "F_SKY1" }, + { 11172, NULL }, + { 11200, NULL }, + { 11232, NULL }, + { 11272, NULL }, + { 11312, NULL }, + { 11348, NULL }, + { 11380, NULL }, + { 11404, NULL }, + { 11436, NULL }, + { 11492, NULL }, + { 11548, NULL }, + { 11616, NULL }, + { 11684, NULL }, + { 11748, NULL }, + { 11792, NULL }, + { 11840, NULL }, + { 11896, NULL }, + { 11936, NULL }, + { 11980, NULL }, + { 12028, NULL }, + { 12072, NULL }, + { 12120, "LTFACE" }, + { 12128, "RTFACE" }, + { 12136, "BARBACK" }, + { 12144, "INVBAR" }, + { 12152, "CHAIN" }, + { 12160, "STATBAR" }, + { 12168, "LIFEBAR" }, + { 12176, "LIFEGEM2" }, + { 12188, "LIFEGEM0" }, + { 12200, "LTFCTOP" }, + { 12208, "RTFCTOP" }, + { 12216, "ARTIBOX" }, + { 12224, "SELECTBOX" }, + { 12236, "INVGEML1" }, + { 12248, "INVGEML2" }, + { 12260, "INVGEMR1" }, + { 12272, "INVGEMR2" }, + { 12284, "BLACKSQ" }, + { 12292, "ARMCLEAR" }, + { 12304, "CHAINBACK" }, + { 12320, "NEGNUM" }, + { 12328, "FONTB16" }, + { 12336, "SMALLIN0" }, + { 12348, "PLAYPAL" }, + { 12356, "SPINBK0" }, + { 12364, "SPFLY0" }, + { 12372, "LAME" }, + { 12408, "NAME" }, + { 12416, "MO.T" }, + { 12424, "MO.X" }, + { 12432, "MO.Y" }, + { 12448, "DIST" }, + { 12456, "------" }, + { 12472, "GOD1" }, + { 12480, "GOD2" }, + { 12488, "useartia" }, + { 12500, "ykeyicon" }, + { 12512, "gkeyicon" }, + { 12524, "bkeyicon" }, + { 12536, "GOD MODE ON" }, + { 12548, "GOD MODE OFF" }, + { 12564, "NO CLIPPING ON" }, + { 12580, "NO CLIPPING OFF" }, + { 12596, "ALL WEAPONS" }, + { 12608, "POWER OFF" }, + { 12620, "POWER ON" }, + { 12632, "FULL HEALTH" }, + { 12644, "ALL KEYS" }, + { 12656, "SOUND DEBUG ON" }, + { 12672, "SOUND DEBUG OFF" }, + { 12688, "TICKER ON" }, + { 12700, "TICKER OFF" }, + { 12712, "CHOOSE AN ARTIFACT ( A - J )" }, + { 12744, "HOW MANY ( 1 - 9 )" }, + { 12764, "YOU GOT IT" }, + { 12776, "BAD INPUT" }, + { 12788, "LEVEL WARP" }, + { 12800, "CHICKEN OFF" }, + { 12812, "CHICKEN ON" }, + { 12824, "MASSACRE" }, + { 12836, "CHEATER - YOU DON'T DESERVE WEAPONS" }, + { 12872, "TRYING TO CHEAT, EH? NOW YOU DIE!" }, + { 12908, "Bad V_DrawPatch" }, + { 12924, NULL }, + { 12960, NULL }, + { 12968, NULL }, + { 12976, NULL }, + { 13020, NULL }, + { 13048, NULL }, + { 13076, NULL }, + { 13104, NULL }, + { 13136, NULL }, + { 13168, NULL }, + { 13196, NULL }, + { 13240, NULL }, + { 13272, NULL }, + { 13296, NULL }, + { 13312, NULL }, + { 13324, NULL }, + { 13364, NULL }, + { 13408, NULL }, + { 13460, NULL }, + { 13492, NULL }, + { 13516, NULL }, + { 13560, NULL }, + { 13612, NULL }, + { 13664, NULL }, + { 13700, NULL }, + { 13744, NULL }, + { 13796, NULL }, + { 13848, NULL }, + { 13884, NULL }, + { 13940, NULL }, + { 13996, NULL }, + { 14040, NULL }, + { 14084, NULL }, + { 14140, NULL }, + { 14148, NULL }, + { 14164, NULL }, + { 14184, NULL }, + { 14192, NULL }, + { 14212, NULL }, + { 14256, NULL }, + { 14272, NULL }, + { 14284, NULL }, + { 14300, NULL }, + { 14312, NULL }, + { 14324, NULL }, + { 14348, NULL }, + { 14360, NULL }, + { 14372, NULL }, + { 14380, NULL }, + { 14392, NULL }, + { 14432, NULL }, + { 14444, NULL }, + { 14472, NULL }, + { 14496, NULL }, + { 14516, NULL }, + { 14536, NULL }, + { 14548, NULL }, + { 14560, NULL }, + { 14572, NULL }, + { 14580, NULL }, + { 14588, NULL }, + { 14596, NULL }, + { 14604, NULL }, + { 14612, NULL }, + { 14620, NULL }, + { 14636, NULL }, + { 14660, NULL }, + { 14704, NULL }, + { 14748, NULL }, + { 14760, NULL }, + { 14768, NULL }, +}; + +static boolean GetStringByOffset(unsigned int offset, char **result) +{ + int i; + + for (i=0; i MaxStringLength(strlen(orig_text))) + { + DEH_Error(context, "Replacement string is longer than the maximum " + "possible in heretic.exe"); + } + else + { + // Success. + + DEH_AddStringReplacement(orig_text, repl_text); + + return NULL; + } + + // Failure. + + Z_Free(repl_text); + + return NULL; +} + +static void DEH_TextParseLine(deh_context_t *context, char *line, void *tag) +{ + // not used +} + +deh_section_t deh_section_heretic_text = +{ + "Text", + NULL, + DEH_TextStart, + DEH_TextParseLine, + NULL, + NULL, +}; + diff --git a/src/heretic/deh_htic.c b/src/heretic/deh_htic.c index ca1c6d5c..59c426ef 100644 --- a/src/heretic/deh_htic.c +++ b/src/heretic/deh_htic.c @@ -44,8 +44,8 @@ extern deh_section_t deh_section_frame; extern deh_section_t deh_section_pointer; // deh_sound.c extern deh_section_t deh_section_sound; -// deh_text.c: -extern deh_section_t deh_section_text; +// deh_htext.c: +extern deh_section_t deh_section_heretic_text; // deh_thing.c: extern deh_section_t deh_section_thing; // deh_weapon.c: @@ -61,7 +61,7 @@ deh_section_t *deh_section_types[] = &deh_section_frame, // &deh_section_pointer, TODO &deh_section_sound, -// &deh_section_text, TODO + &deh_section_heretic_text, &deh_section_thing, &deh_section_weapon, NULL diff --git a/src/heretic/f_finale.c b/src/heretic/f_finale.c index 03806214..27b33e96 100644 --- a/src/heretic/f_finale.c +++ b/src/heretic/f_finale.c @@ -26,6 +26,7 @@ #include #include "doomdef.h" +#include "deh_str.h" #include "i_swap.h" #include "i_video.h" #include "s_sound.h" @@ -72,30 +73,30 @@ void F_StartFinale(void) switch (gameepisode) { case 1: - finaleflat = "FLOOR25"; + finaleflat = DEH_String("FLOOR25"); finaletext = e1text; break; case 2: - finaleflat = "FLATHUH1"; + finaleflat = DEH_String("FLATHUH1"); finaletext = e2text; break; case 3: - finaleflat = "FLTWAWA2"; + finaleflat = DEH_String("FLTWAWA2"); finaletext = e3text; break; case 4: - finaleflat = "FLOOR28"; + finaleflat = DEH_String("FLOOR28"); finaletext = e4text; break; case 5: - finaleflat = "FLOOR08"; + finaleflat = DEH_String("FLOOR08"); finaletext = e5text; break; } finalestage = 0; finalecount = 0; - FontABaseLump = W_GetNumForName("FONTA_S") + 1; + FontABaseLump = W_GetNumForName(DEH_String("FONTA_S")) + 1; // S_ChangeMusic(mus_victor, true); S_StartSong(mus_cptd, true); @@ -277,8 +278,8 @@ void F_DemonScroll(void) { return; } - p1 = W_CacheLumpName("FINAL1", PU_LEVEL); - p2 = W_CacheLumpName("FINAL2", PU_LEVEL); + p1 = W_CacheLumpName(DEH_String("FINAL1"), PU_LEVEL); + p2 = W_CacheLumpName(DEH_String("FINAL2"), PU_LEVEL); if (finalecount < 70) { memcpy(I_VideoBuffer, p1, SCREENHEIGHT * SCREENWIDTH); @@ -319,8 +320,8 @@ void F_DrawUnderwater(void) { underwawa = true; memset((byte *) 0xa0000, 0, SCREENWIDTH * SCREENHEIGHT); - I_SetPalette(W_CacheLumpName("E2PAL", PU_CACHE)); - V_DrawRawScreen(W_CacheLumpName("E2END", PU_CACHE)); + I_SetPalette(W_CacheLumpName(DEH_String("E2PAL"), PU_CACHE)); + V_DrawRawScreen(W_CacheLumpName(DEH_String("E2END"), PU_CACHE)); } paused = false; MenuActive = false; @@ -328,7 +329,7 @@ void F_DrawUnderwater(void) break; case 2: - V_DrawRawScreen(W_CacheLumpName("TITLE", PU_CACHE)); + V_DrawRawScreen(W_CacheLumpName(DEH_String("TITLE"), PU_CACHE)); //D_StartTitle(); // go to intro/demo mode. } } diff --git a/src/heretic/g_game.c b/src/heretic/g_game.c index a256b63b..4d1e63c3 100644 --- a/src/heretic/g_game.c +++ b/src/heretic/g_game.c @@ -28,6 +28,7 @@ #include #include "doomdef.h" #include "doomkeys.h" +#include "deh_str.h" #include "i_timer.h" #include "i_system.h" #include "m_controls.h" @@ -862,11 +863,11 @@ void G_Ticker(void) { if (netgame) { - strcpy(savedescription, "NET GAME"); + strcpy(savedescription, DEH_String("NET GAME")); } else { - strcpy(savedescription, "SAVE GAME"); + strcpy(savedescription, DEH_String("SAVE GAME")); } } savegameslot = @@ -1320,7 +1321,7 @@ void G_DoLoadGame(void) save_p = savebuffer + SAVESTRINGSIZE; // Skip the description field memset(vcheck, 0, sizeof(vcheck)); - sprintf(vcheck, "version %i", HERETIC_VERSION); + sprintf(vcheck, DEH_String("version %i"), HERETIC_VERSION); if (strcmp((char *) save_p, vcheck) != 0) { // Bad version return; @@ -1449,11 +1450,11 @@ void G_InitNew(skill_t skill, int episode, int map) // Set the sky map if (episode > 5) { - skytexture = R_TextureNumForName("SKY1"); + skytexture = R_TextureNumForName(DEH_String("SKY1")); } else { - skytexture = R_TextureNumForName(skyLumpNames[episode - 1]); + skytexture = R_TextureNumForName(DEH_String(skyLumpNames[episode - 1])); } // @@ -1694,7 +1695,7 @@ void G_DoSaveGame(void) SV_Open(name); SV_Write(description, SAVESTRINGSIZE); memset(verString, 0, sizeof(verString)); - sprintf(verString, "version %i", HERETIC_VERSION); + sprintf(verString, DEH_String("version %i"), HERETIC_VERSION); SV_Write(verString, VERSIONSIZE); SV_WriteByte(gameskill); SV_WriteByte(gameepisode); diff --git a/src/heretic/in_lude.c b/src/heretic/in_lude.c index 33b75956..084cd5c3 100644 --- a/src/heretic/in_lude.c +++ b/src/heretic/in_lude.c @@ -30,6 +30,7 @@ */ #include "doomdef.h" +#include "deh_str.h" #include "s_sound.h" #include "i_system.h" #include "i_video.h" @@ -161,7 +162,7 @@ extern void AM_Stop(void); void IN_Start(void) { - I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE)); + I_SetPalette(W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE)); IN_LoadPics(); IN_InitStats(); intermission = true; @@ -308,26 +309,26 @@ static void IN_LoadUnloadPics(void (*callback)(char *lumpname, switch (gameepisode) { case 1: - callback("MAPE1", 0, &patchINTERPIC); + callback(DEH_String("MAPE1"), 0, &patchINTERPIC); break; case 2: - callback("MAPE2", 0, &patchINTERPIC); + callback(DEH_String("MAPE2"), 0, &patchINTERPIC); break; case 3: - callback("MAPE3", 0, &patchINTERPIC); + callback(DEH_String("MAPE3"), 0, &patchINTERPIC); break; default: break; } - callback("IN_X", 0, &patchBEENTHERE); - callback("IN_YAH", 0, &patchGOINGTHERE); - callback("FONTB13", 0, &FontBNegative); + callback(DEH_String("IN_X"), 0, &patchBEENTHERE); + callback(DEH_String("IN_YAH"), 0, &patchGOINGTHERE); + callback(DEH_String("FONTB13"), 0, &FontBNegative); - callback("FONTB15", 0, &FontBSlash); - callback("FONTB05", 0, &FontBPercent); + callback(DEH_String("FONTB15"), 0, &FontBSlash); + callback(DEH_String("FONTB05"), 0, &FontBPercent); - FontBLumpBase = W_GetNumForName("FONTB16"); + FontBLumpBase = W_GetNumForName(DEH_String("FONTB16")); for (i = 0; i < 10; i++) { @@ -355,9 +356,9 @@ static void LoadLumpCallback(char *lumpname, int lumpnum, patch_t **ptr) void IN_LoadPics(void) { - FontBLump = W_GetNumForName("FONTB_S") + 1; - patchFaceOkayBase = W_GetNumForName("FACEA0"); - patchFaceDeadBase = W_GetNumForName("FACEB0"); + FontBLump = W_GetNumForName(DEH_String("FONTB_S")) + 1; + patchFaceOkayBase = W_GetNumForName(DEH_String("FACEA0")); + patchFaceDeadBase = W_GetNumForName(DEH_String("FACEB0")); IN_LoadUnloadPics(LoadLumpCallback); } @@ -580,7 +581,7 @@ void IN_DrawStatBack(void) byte *src; byte *dest; - src = W_CacheLumpName("FLOOR16", PU_CACHE); + src = W_CacheLumpName(DEH_String("FLOOR16"), PU_CACHE); dest = I_VideoBuffer; for (y = 0; y < SCREENHEIGHT; y++) @@ -612,8 +613,8 @@ void IN_DrawOldLevel(void) x = 160 - MN_TextBWidth(LevelNames[(gameepisode - 1) * 9 + prevmap - 1] + 7) / 2; IN_DrTextB(LevelNames[(gameepisode - 1) * 9 + prevmap - 1] + 7, x, 3); - x = 160 - MN_TextAWidth("FINISHED") / 2; - MN_DrTextA("FINISHED", x, 25); + x = 160 - MN_TextAWidth(DEH_String("FINISHED")) / 2; + MN_DrTextA(DEH_String("FINISHED"), x, 25); if (prevmap == 9) { @@ -660,8 +661,8 @@ void IN_DrawYAH(void) int i; int x; - x = 160 - MN_TextAWidth("NOW ENTERING:") / 2; - MN_DrTextA("NOW ENTERING:", x, 10); + x = 160 - MN_TextAWidth(DEH_String("NOW ENTERING:")) / 2; + MN_DrTextA(DEH_String("NOW ENTERING:"), x, 10); x = 160 - MN_TextBWidth(LevelNames[(gameepisode - 1) * 9 + gamemap - 1] + 7) / 2; IN_DrTextB(LevelNames[(gameepisode - 1) * 9 + gamemap - 1] + 7, x, 20); @@ -698,15 +699,15 @@ void IN_DrawSingleStats(void) int x; static int sounds; - IN_DrTextB("KILLS", 50, 65); - IN_DrTextB("ITEMS", 50, 90); - IN_DrTextB("SECRETS", 50, 115); + IN_DrTextB(DEH_String("KILLS"), 50, 65); + IN_DrTextB(DEH_String("ITEMS"), 50, 90); + IN_DrTextB(DEH_String("SECRETS"), 50, 115); x = 160 - MN_TextBWidth(LevelNames[(gameepisode - 1) * 9 + prevmap - 1] + 7) / 2; IN_DrTextB(LevelNames[(gameepisode - 1) * 9 + prevmap - 1] + 7, x, 3); - x = 160 - MN_TextAWidth("FINISHED") / 2; - MN_DrTextA("FINISHED", x, 25); + x = 160 - MN_TextAWidth(DEH_String("FINISHED")) / 2; + MN_DrTextA(DEH_String("FINISHED"), x, 25); if (intertime < 30) { @@ -757,13 +758,13 @@ void IN_DrawSingleStats(void) if (gamemode != retail || gameepisode <= 3) { - IN_DrTextB("TIME", 85, 160); + IN_DrTextB(DEH_String("TIME"), 85, 160); IN_DrawTime(155, 160, hours, minutes, seconds); } else { - x = 160 - MN_TextAWidth("NOW ENTERING:") / 2; - MN_DrTextA("NOW ENTERING:", x, 160); + x = 160 - MN_TextAWidth(DEH_String("NOW ENTERING:")) / 2; + MN_DrTextA(DEH_String("NOW ENTERING:"), x, 160); x = 160 - MN_TextBWidth(LevelNames[(gameepisode - 1) * 9 + gamemap - 1] + 7) / 2; @@ -787,14 +788,14 @@ void IN_DrawCoopStats(void) static int sounds; - IN_DrTextB("KILLS", 95, 35); - IN_DrTextB("BONUS", 155, 35); - IN_DrTextB("SECRET", 232, 35); + IN_DrTextB(DEH_String("KILLS"), 95, 35); + IN_DrTextB(DEH_String("BONUS"), 155, 35); + IN_DrTextB(DEH_String("SECRET"), 232, 35); x = 160 - MN_TextBWidth(LevelNames[(gameepisode - 1) * 9 + prevmap - 1] + 7) / 2; IN_DrTextB(LevelNames[(gameepisode - 1) * 9 + prevmap - 1] + 7, x, 3); - x = 160 - MN_TextAWidth("FINISHED") / 2; - MN_DrTextA("FINISHED", x, 25); + x = 160 - MN_TextAWidth(DEH_String("FINISHED")) / 2; + MN_DrTextA(DEH_String("FINISHED"), x, 25); ypos = 50; for (i = 0; i < MAXPLAYERS; i++) @@ -845,8 +846,8 @@ void IN_DrawDMStats(void) xpos = 90; ypos = 55; - IN_DrTextB("TOTAL", 265, 30); - MN_DrTextA("VICTIMS", 140, 8); + IN_DrTextB(DEH_String("TOTAL"), 265, 30); + MN_DrTextA(DEH_String("VICTIMS"), 140, 8); for (i = 0; i < 7; i++) { MN_DrTextA(KillersText[i], 10, 80 + 9 * i); diff --git a/src/heretic/mn_menu.c b/src/heretic/mn_menu.c index 33024a96..2b380234 100644 --- a/src/heretic/mn_menu.c +++ b/src/heretic/mn_menu.c @@ -25,6 +25,8 @@ // MN_menu.c #include + +#include "deh_str.h" #include "doomdef.h" #include "doomkeys.h" #include "i_system.h" @@ -73,7 +75,7 @@ typedef struct { ItemType_t type; char *text; - boolean(*func) (int option); + boolean(*func) (int option); int option; MenuType_t menu; } MenuItem_t; @@ -305,7 +307,7 @@ void MN_Init(void) InitFonts(); MenuActive = false; messageson = true; - SkullBaseLump = W_GetNumForName("M_SKL00"); + SkullBaseLump = W_GetNumForName(DEH_String("M_SKL00")); if (gamemode == retail) { // Add episodes 4 and 5 to the menu @@ -322,8 +324,8 @@ void MN_Init(void) static void InitFonts(void) { - FontABaseLump = W_GetNumForName("FONTA_S") + 1; - FontBBaseLump = W_GetNumForName("FONTB_S") + 1; + FontABaseLump = W_GetNumForName(DEH_String("FONTA_S")) + 1; + FontBBaseLump = W_GetNumForName(DEH_String("FONTB_S")) + 1; } //--------------------------------------------------------------------------- @@ -476,14 +478,16 @@ void MN_Drawer(void) int x; int y; MenuItem_t *item; + char *message; char *selName; if (MenuActive == false) { if (askforquit) { - MN_DrTextA(QuitEndMsg[typeofask - 1], 160 - - MN_TextAWidth(QuitEndMsg[typeofask - 1]) / 2, 80); + message = DEH_String(QuitEndMsg[typeofask - 1]); + + MN_DrTextA(message, 160 - MN_TextAWidth(message) / 2, 80); if (typeofask == 3) { MN_DrTextA(SlotText[quicksave - 1], 160 - @@ -525,13 +529,13 @@ void MN_Drawer(void) { if (item->type != ITT_EMPTY && item->text) { - MN_DrTextB(item->text, x, y); + MN_DrTextB(DEH_String(item->text), x, y); } y += ITEM_HEIGHT; item++; } y = CurrentMenu->y + (CurrentItPos * ITEM_HEIGHT) + SELECTOR_YOFFSET; - selName = MenuTime & 16 ? "M_SLCTR1" : "M_SLCTR2"; + selName = DEH_String(MenuTime & 16 ? "M_SLCTR1" : "M_SLCTR2"); V_DrawPatch(x + SELECTOR_XOFFSET, y, W_CacheLumpName(selName, PU_CACHE)); } @@ -548,7 +552,7 @@ static void DrawMainMenu(void) int frame; frame = (MenuTime / 3) % 18; - V_DrawPatch(88, 0, W_CacheLumpName("M_HTIC", PU_CACHE)); + V_DrawPatch(88, 0, W_CacheLumpName(DEH_String("M_HTIC"), PU_CACHE)); V_DrawPatch(40, 10, W_CacheLumpNum(SkullBaseLump + (17 - frame), PU_CACHE)); V_DrawPatch(232, 10, W_CacheLumpNum(SkullBaseLump + frame, PU_CACHE)); @@ -597,7 +601,11 @@ static void DrawFilesMenu(void) static void DrawLoadMenu(void) { - MN_DrTextB("LOAD GAME", 160 - MN_TextBWidth("LOAD GAME") / 2, 10); + char *title; + + title = DEH_String("LOAD GAME"); + + MN_DrTextB(title, 160 - MN_TextBWidth(title) / 2, 10); if (!slottextloaded) { MN_LoadSlotText(); @@ -613,7 +621,11 @@ static void DrawLoadMenu(void) static void DrawSaveMenu(void) { - MN_DrTextB("SAVE GAME", 160 - MN_TextBWidth("SAVE GAME") / 2, 10); + char *title; + + title = DEH_String("SAVE GAME"); + + MN_DrTextB(title, 160 - MN_TextBWidth(title) / 2, 10); if (!slottextloaded) { MN_LoadSlotText(); @@ -675,7 +687,7 @@ static void DrawFileSlots(Menu_t * menu) y = menu->y; for (i = 0; i < 6; i++) { - V_DrawPatch(x, y, W_CacheLumpName("M_FSLOT", PU_CACHE)); + V_DrawPatch(x, y, W_CacheLumpName(DEH_String("M_FSLOT"), PU_CACHE)); if (SlotStatus[i]) { MN_DrTextA(SlotText[i], x + 5, y + 5); @@ -796,11 +808,11 @@ static boolean SCMessages(int option) messageson ^= 1; if (messageson) { - P_SetMessage(&players[consoleplayer], "MESSAGES ON", true); + P_SetMessage(&players[consoleplayer], DEH_String("MESSAGES ON"), true); } else { - P_SetMessage(&players[consoleplayer], "MESSAGES OFF", true); + P_SetMessage(&players[consoleplayer], DEH_String("MESSAGES OFF"), true); } S_StartSound(NULL, sfx_chat); return true; @@ -1460,7 +1472,7 @@ boolean MN_Responder(event_t * event) if (CurrentMenu->items[i].text) { if (toupper(charTyped) - == toupper(CurrentMenu->items[i].text[0])) + == toupper(DEH_String(CurrentMenu->items[i].text)[0])) { CurrentItPos = i; return (true); @@ -1628,13 +1640,13 @@ static void DrawSlider(Menu_t * menu, int item, int width, int slot) x = menu->x + 24; y = menu->y + 2 + (item * ITEM_HEIGHT); - V_DrawPatch(x - 32, y, W_CacheLumpName("M_SLDLT", PU_CACHE)); + V_DrawPatch(x - 32, y, W_CacheLumpName(DEH_String("M_SLDLT"), PU_CACHE)); for (x2 = x, count = width; count--; x2 += 8) { - V_DrawPatch(x2, y, W_CacheLumpName(count & 1 ? "M_SLDMD1" - : "M_SLDMD2", PU_CACHE)); + V_DrawPatch(x2, y, W_CacheLumpName(DEH_String(count & 1 ? "M_SLDMD1" + : "M_SLDMD2"), PU_CACHE)); } - V_DrawPatch(x2, y, W_CacheLumpName("M_SLDRT", PU_CACHE)); + V_DrawPatch(x2, y, W_CacheLumpName(DEH_String("M_SLDRT"), PU_CACHE)); V_DrawPatch(x + 4 + slot * 8, y + 7, - W_CacheLumpName("M_SLDKB", PU_CACHE)); + W_CacheLumpName(DEH_String("M_SLDKB"), PU_CACHE)); } diff --git a/src/heretic/p_spec.c b/src/heretic/p_spec.c index 6227237b..49c067fa 100644 --- a/src/heretic/p_spec.c +++ b/src/heretic/p_spec.c @@ -25,6 +25,7 @@ // P_Spec.c #include "doomdef.h" +#include "deh_str.h" #include "i_system.h" #include "i_timer.h" #include "m_random.h" @@ -204,18 +205,12 @@ struct int type; } TerrainTypeDefs[] = { - { - "FLTWAWA1", FLOOR_WATER}, - { - "FLTFLWW1", FLOOR_WATER}, - { - "FLTLAVA1", FLOOR_LAVA}, - { - "FLATHUH1", FLOOR_LAVA}, - { - "FLTSLUD1", FLOOR_SLUDGE}, - { - "END", -1} + { "FLTWAWA1", FLOOR_WATER }, + { "FLTFLWW1", FLOOR_WATER }, + { "FLTLAVA1", FLOOR_LAVA }, + { "FLATHUH1", FLOOR_LAVA }, + { "FLTSLUD1", FLOOR_SLUDGE }, + { "END", -1 } }; mobj_t LavaInflictor; @@ -266,35 +261,40 @@ void P_InitTerrainTypes(void) void P_InitPicAnims(void) { + char *startname; + char *endname; int i; lastanim = anims; for (i = 0; animdefs[i].istexture != -1; i++) { + startname = DEH_String(animdefs[i].startname); + endname = DEH_String(animdefs[i].endname); + if (animdefs[i].istexture) { // Texture animation - if (R_CheckTextureNumForName(animdefs[i].startname) == -1) + if (R_CheckTextureNumForName(startname) == -1) { // Texture doesn't exist continue; } - lastanim->picnum = R_TextureNumForName(animdefs[i].endname); - lastanim->basepic = R_TextureNumForName(animdefs[i].startname); + lastanim->picnum = R_TextureNumForName(endname); + lastanim->basepic = R_TextureNumForName(startname); } else { // Flat animation - if (W_CheckNumForName(animdefs[i].startname) == -1) + if (W_CheckNumForName(startname) == -1) { // Flat doesn't exist continue; } - lastanim->picnum = R_FlatNumForName(animdefs[i].endname); - lastanim->basepic = R_FlatNumForName(animdefs[i].startname); + lastanim->picnum = R_FlatNumForName(endname); + lastanim->basepic = R_FlatNumForName(startname); } lastanim->istexture = animdefs[i].istexture; lastanim->numpics = lastanim->picnum - lastanim->basepic + 1; if (lastanim->numpics < 2) { I_Error("P_InitPicAnims: bad cycle from %s to %s", - animdefs[i].startname, animdefs[i].endname); + startname, endname); } lastanim->speed = animdefs[i].speed; lastanim++; @@ -1132,7 +1132,7 @@ void P_SpawnSpecials(void) int episode; episode = 1; - if (W_CheckNumForName("texture2") >= 0) + if (W_CheckNumForName(DEH_String("texture2")) >= 0) episode = 2; // diff --git a/src/heretic/p_switch.c b/src/heretic/p_switch.c index cef6a74b..2ec758b6 100644 --- a/src/heretic/p_switch.c +++ b/src/heretic/p_switch.c @@ -23,6 +23,7 @@ //----------------------------------------------------------------------------- #include "doomdef.h" +#include "deh_str.h" #include "i_system.h" #include "p_local.h" #include "s_sound.h" @@ -129,9 +130,9 @@ void P_InitSwitchList(void) if (alphSwitchList[i].episode <= episode) { switchlist[index++] = - R_TextureNumForName(alphSwitchList[i].name1); + R_TextureNumForName(DEH_String(alphSwitchList[i].name1)); switchlist[index++] = - R_TextureNumForName(alphSwitchList[i].name2); + R_TextureNumForName(DEH_String(alphSwitchList[i].name2)); } } } diff --git a/src/heretic/p_user.c b/src/heretic/p_user.c index 63368bab..16dbed49 100644 --- a/src/heretic/p_user.c +++ b/src/heretic/p_user.c @@ -27,6 +27,7 @@ #include #include "doomdef.h" +#include "deh_str.h" #include "m_random.h" #include "p_local.h" #include "s_sound.h" @@ -394,7 +395,7 @@ void P_DeathThink(player_t * player) { if (player == &players[consoleplayer]) { - I_SetPalette((byte *) W_CacheLumpName("PLAYPAL", PU_CACHE)); + I_SetPalette(W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE)); inv_ptr = 0; curpos = 0; newtorch = 0; diff --git a/src/heretic/r_data.c b/src/heretic/r_data.c index ee005248..b55e6dfc 100644 --- a/src/heretic/r_data.c +++ b/src/heretic/r_data.c @@ -316,12 +316,17 @@ void R_InitTextures(void) int offset, maxoff, maxoff2; int numtextures1, numtextures2; int *directory; + char *texture1, *texture2, *pnames; + + texture1 = DEH_String("TEXTURE1"); + texture2 = DEH_String("TEXTURE2"); + pnames = DEH_String("PNAMES"); // // load the patch names from pnames.lmp // name[8] = 0; - names = W_CacheLumpName("PNAMES", PU_STATIC); + names = W_CacheLumpName(pnames, PU_STATIC); nummappatches = LONG(*((int *) names)); name_p = names + 4; patchlookup = Z_Malloc(nummappatches * sizeof(*patchlookup), PU_STATIC, NULL); @@ -330,21 +335,21 @@ void R_InitTextures(void) strncpy(name, name_p + i * 8, 8); patchlookup[i] = W_CheckNumForName(name); } - W_ReleaseLumpName("PNAMES"); + W_ReleaseLumpName(pnames); // // load the map texture definitions from textures.lmp // - maptex = maptex1 = W_CacheLumpName("TEXTURE1", PU_STATIC); + maptex = maptex1 = W_CacheLumpName(texture1, PU_STATIC); numtextures1 = LONG(*maptex); - maxoff = W_LumpLength(W_GetNumForName("TEXTURE1")); + maxoff = W_LumpLength(W_GetNumForName(texture1)); directory = maptex + 1; - if (W_CheckNumForName("TEXTURE2") != -1) + if (W_CheckNumForName(texture2) != -1) { - maptex2 = W_CacheLumpName("TEXTURE2", PU_STATIC); + maptex2 = W_CacheLumpName(texture2, PU_STATIC); numtextures2 = LONG(*maptex2); - maxoff2 = W_LumpLength(W_GetNumForName("TEXTURE2")); + maxoff2 = W_LumpLength(W_GetNumForName(texture2)); } else { @@ -358,8 +363,11 @@ void R_InitTextures(void) // Init the startup thermometer at this point... // { + int start, end; int spramount; - spramount = W_GetNumForName("S_END") - W_GetNumForName("S_START") + 1; + start = W_GetNumForName(DEH_String("S_START")); + end = W_GetNumForName(DEH_String("S_END")); + spramount = end - start + 1; InitThermo(spramount + numtextures + 6); } @@ -427,10 +435,10 @@ void R_InitTextures(void) Z_Free(patchlookup); - W_ReleaseLumpName("TEXTURE1"); + W_ReleaseLumpName(texture1); if (maptex2) { - W_ReleaseLumpName("TEXTURE2"); + W_ReleaseLumpName(texture2); } // @@ -463,8 +471,8 @@ void R_InitFlats(void) { int i; - firstflat = W_GetNumForName("F_START") + 1; - lastflat = W_GetNumForName("F_END") - 1; + firstflat = W_GetNumForName(DEH_String("F_START")) + 1; + lastflat = W_GetNumForName(DEH_String("F_END")) - 1; numflats = lastflat - firstflat + 1; // translation table for global animation @@ -489,8 +497,8 @@ void R_InitSpriteLumps(void) int i; patch_t *patch; - firstspritelump = W_GetNumForName("S_START") + 1; - lastspritelump = W_GetNumForName("S_END") - 1; + firstspritelump = W_GetNumForName(DEH_String("S_START")) + 1; + lastspritelump = W_GetNumForName(DEH_String("S_END")) - 1; numspritelumps = lastspritelump - firstspritelump + 1; spritewidth = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0); spriteoffset = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0); @@ -527,7 +535,7 @@ void R_InitColormaps(void) // load in the light tables // 256 byte align tables // - lump = W_GetNumForName("COLORMAP"); + lump = W_GetNumForName(DEH_String("COLORMAP")); length = W_LumpLength(lump); colormaps = Z_Malloc(length, PU_STATIC, 0); W_ReadLump(lump, colormaps); diff --git a/src/heretic/r_draw.c b/src/heretic/r_draw.c index 5a20b50a..88653df1 100644 --- a/src/heretic/r_draw.c +++ b/src/heretic/r_draw.c @@ -24,6 +24,7 @@ // R_draw.c #include "doomdef.h" +#include "deh_str.h" #include "r_local.h" #include "i_video.h" #include "v_video.h" @@ -386,11 +387,11 @@ void R_DrawViewBorder(void) if (gamemode == shareware) { - src = W_CacheLumpName("FLOOR04", PU_CACHE); + src = W_CacheLumpName(DEH_String("FLOOR04"), PU_CACHE); } else { - src = W_CacheLumpName("FLAT513", PU_CACHE); + src = W_CacheLumpName(DEH_String("FLAT513"), PU_CACHE); } dest = I_VideoBuffer; @@ -409,24 +410,26 @@ void R_DrawViewBorder(void) } for (x = viewwindowx; x < viewwindowx + viewwidth; x += 16) { - V_DrawPatch(x, viewwindowy - 4, W_CacheLumpName("bordt", PU_CACHE)); - V_DrawPatch(x, viewwindowy + viewheight, W_CacheLumpName("bordb", - PU_CACHE)); + V_DrawPatch(x, viewwindowy - 4, + W_CacheLumpName(DEH_String("bordt"), PU_CACHE)); + V_DrawPatch(x, viewwindowy + viewheight, + W_CacheLumpName(DEH_String("bordb"), PU_CACHE)); } for (y = viewwindowy; y < viewwindowy + viewheight; y += 16) { - V_DrawPatch(viewwindowx - 4, y, W_CacheLumpName("bordl", PU_CACHE)); - V_DrawPatch(viewwindowx + viewwidth, y, W_CacheLumpName("bordr", - PU_CACHE)); + V_DrawPatch(viewwindowx - 4, y, + W_CacheLumpName(DEH_String("bordl"), PU_CACHE)); + V_DrawPatch(viewwindowx + viewwidth, y, + W_CacheLumpName(DEH_String("bordr"), PU_CACHE)); } - V_DrawPatch(viewwindowx - 4, viewwindowy - 4, W_CacheLumpName("bordtl", - PU_CACHE)); + V_DrawPatch(viewwindowx - 4, viewwindowy - 4, + W_CacheLumpName(DEH_String("bordtl"), PU_CACHE)); V_DrawPatch(viewwindowx + viewwidth, viewwindowy - 4, - W_CacheLumpName("bordtr", PU_CACHE)); + W_CacheLumpName(DEH_String("bordtr"), PU_CACHE)); V_DrawPatch(viewwindowx + viewwidth, viewwindowy + viewheight, - W_CacheLumpName("bordbr", PU_CACHE)); + W_CacheLumpName(DEH_String("bordbr"), PU_CACHE)); V_DrawPatch(viewwindowx - 4, viewwindowy + viewheight, - W_CacheLumpName("bordbl", PU_CACHE)); + W_CacheLumpName(DEH_String("bordbl"), PU_CACHE)); } /* @@ -450,11 +453,11 @@ void R_DrawTopBorder(void) if (gamemode == shareware) { - src = W_CacheLumpName("FLOOR04", PU_CACHE); + src = W_CacheLumpName(DEH_String("FLOOR04"), PU_CACHE); } else { - src = W_CacheLumpName("FLAT513", PU_CACHE); + src = W_CacheLumpName(DEH_String("FLAT513"), PU_CACHE); } dest = I_VideoBuffer; @@ -476,20 +479,20 @@ void R_DrawTopBorder(void) for (x = viewwindowx; x < viewwindowx + viewwidth; x += 16) { V_DrawPatch(x, viewwindowy - 4, - W_CacheLumpName("bordt", PU_CACHE)); + W_CacheLumpName(DEH_String("bordt"), PU_CACHE)); } - V_DrawPatch(viewwindowx - 4, viewwindowy, W_CacheLumpName("bordl", - PU_CACHE)); + V_DrawPatch(viewwindowx - 4, viewwindowy, + W_CacheLumpName(DEH_String("bordl"), PU_CACHE)); V_DrawPatch(viewwindowx + viewwidth, viewwindowy, - W_CacheLumpName("bordr", PU_CACHE)); + W_CacheLumpName(DEH_String("bordr"), PU_CACHE)); V_DrawPatch(viewwindowx - 4, viewwindowy + 16, - W_CacheLumpName("bordl", PU_CACHE)); + W_CacheLumpName(DEH_String("bordl"), PU_CACHE)); V_DrawPatch(viewwindowx + viewwidth, viewwindowy + 16, - W_CacheLumpName("bordr", PU_CACHE)); + W_CacheLumpName(DEH_String("bordr"), PU_CACHE)); V_DrawPatch(viewwindowx - 4, viewwindowy - 4, - W_CacheLumpName("bordtl", PU_CACHE)); + W_CacheLumpName(DEH_String("bordtl"), PU_CACHE)); V_DrawPatch(viewwindowx + viewwidth, viewwindowy - 4, - W_CacheLumpName("bordtr", PU_CACHE)); + W_CacheLumpName(DEH_String("bordtr"), PU_CACHE)); } } diff --git a/src/heretic/r_plane.c b/src/heretic/r_plane.c index e35e2931..cc86b118 100644 --- a/src/heretic/r_plane.c +++ b/src/heretic/r_plane.c @@ -25,6 +25,7 @@ #include #include "doomdef.h" +#include "deh_str.h" #include "i_system.h" #include "r_local.h" @@ -90,7 +91,7 @@ fixed_t cachedystep[SCREENHEIGHT]; void R_InitSkyMap(void) { - skyflatnum = R_FlatNumForName("F_SKY1"); + skyflatnum = R_FlatNumForName(DEH_String("F_SKY1")); skytexturemid = 200 * FRACUNIT; skyiscale = FRACUNIT; } diff --git a/src/heretic/r_things.c b/src/heretic/r_things.c index 6302303e..e5ff4b4b 100644 --- a/src/heretic/r_things.c +++ b/src/heretic/r_things.c @@ -25,6 +25,7 @@ #include #include #include "doomdef.h" +#include "deh_str.h" #include "i_swap.h" #include "i_system.h" #include "r_local.h" @@ -154,7 +155,7 @@ void R_InstallSpriteLump(int lump, unsigned frame, unsigned rotation, void R_InitSpriteDefs(char **namelist) { char **check; - int i, l, intname, frame, rotation; + int i, l, frame, rotation; int start, end; // count the number of sprite names @@ -176,17 +177,16 @@ void R_InitSpriteDefs(char **namelist) // Just compare 4 characters as ints for (i = 0; i < numsprites; i++) { - spritename = namelist[i]; + spritename = DEH_String(namelist[i]); memset(sprtemp, -1, sizeof(sprtemp)); maxframe = -1; - intname = *(int *) namelist[i]; // // scan the lumps, filling in the frames for whatever is found // for (l = start + 1; l < end; l++) - if (*(int *) lumpinfo[l].name == intname) + if (!strncasecmp(lumpinfo[l].name, spritename, 4)) { frame = lumpinfo[l].name[4] - 'A'; rotation = lumpinfo[l].name[5] - '0'; @@ -209,7 +209,7 @@ void R_InitSpriteDefs(char **namelist) if (gamemode == shareware) continue; I_Error("R_InitSprites: No lumps found for sprite %s", - namelist[i]); + spritename); } maxframe++; @@ -219,7 +219,7 @@ void R_InitSpriteDefs(char **namelist) { case -1: // no rotations were found for that frame at all I_Error("R_InitSprites: No patches found for %s frame %c", - namelist[i], frame + 'A'); + spritename, frame + 'A'); case 0: // only the first rotation is needed break; @@ -228,7 +228,7 @@ void R_InitSpriteDefs(char **namelist) if (sprtemp[frame].lump[rotation] == -1) I_Error ("R_InitSprites: Sprite %s frame %c is missing rotations", - namelist[i], frame + 'A'); + spritename, frame + 'A'); } } diff --git a/src/heretic/sb_bar.c b/src/heretic/sb_bar.c index 77bd40c2..3a90c000 100644 --- a/src/heretic/sb_bar.c +++ b/src/heretic/sb_bar.c @@ -25,6 +25,7 @@ // SB_bar.c #include "doomdef.h" +#include "deh_str.h" #include "i_video.h" #include "m_cheat.h" #include "m_misc.h" @@ -196,53 +197,53 @@ void SB_Init(void) int i; int startLump; - PatchLTFACE = W_CacheLumpName("LTFACE", PU_STATIC); - PatchRTFACE = W_CacheLumpName("RTFACE", PU_STATIC); - PatchBARBACK = W_CacheLumpName("BARBACK", PU_STATIC); - PatchINVBAR = W_CacheLumpName("INVBAR", PU_STATIC); - PatchCHAIN = W_CacheLumpName("CHAIN", PU_STATIC); + PatchLTFACE = W_CacheLumpName(DEH_String("LTFACE"), PU_STATIC); + PatchRTFACE = W_CacheLumpName(DEH_String("RTFACE"), PU_STATIC); + PatchBARBACK = W_CacheLumpName(DEH_String("BARBACK"), PU_STATIC); + PatchINVBAR = W_CacheLumpName(DEH_String("INVBAR"), PU_STATIC); + PatchCHAIN = W_CacheLumpName(DEH_String("CHAIN"), PU_STATIC); if (deathmatch) { - PatchSTATBAR = W_CacheLumpName("STATBAR", PU_STATIC); + PatchSTATBAR = W_CacheLumpName(DEH_String("STATBAR"), PU_STATIC); } else { - PatchSTATBAR = W_CacheLumpName("LIFEBAR", PU_STATIC); + PatchSTATBAR = W_CacheLumpName(DEH_String("LIFEBAR"), PU_STATIC); } if (!netgame) { // single player game uses red life gem - PatchLIFEGEM = W_CacheLumpName("LIFEGEM2", PU_STATIC); + PatchLIFEGEM = W_CacheLumpName(DEH_String("LIFEGEM2"), PU_STATIC); } else { - PatchLIFEGEM = W_CacheLumpNum(W_GetNumForName("LIFEGEM0") + PatchLIFEGEM = W_CacheLumpNum(W_GetNumForName(DEH_String("LIFEGEM0")) + consoleplayer, PU_STATIC); } - PatchLTFCTOP = W_CacheLumpName("LTFCTOP", PU_STATIC); - PatchRTFCTOP = W_CacheLumpName("RTFCTOP", PU_STATIC); - PatchSELECTBOX = W_CacheLumpName("SELECTBOX", PU_STATIC); - PatchINVLFGEM1 = W_CacheLumpName("INVGEML1", PU_STATIC); - PatchINVLFGEM2 = W_CacheLumpName("INVGEML2", PU_STATIC); - PatchINVRTGEM1 = W_CacheLumpName("INVGEMR1", PU_STATIC); - PatchINVRTGEM2 = W_CacheLumpName("INVGEMR2", PU_STATIC); - PatchBLACKSQ = W_CacheLumpName("BLACKSQ", PU_STATIC); - PatchARMCLEAR = W_CacheLumpName("ARMCLEAR", PU_STATIC); - PatchCHAINBACK = W_CacheLumpName("CHAINBACK", PU_STATIC); - startLump = W_GetNumForName("IN0"); + PatchLTFCTOP = W_CacheLumpName(DEH_String("LTFCTOP"), PU_STATIC); + PatchRTFCTOP = W_CacheLumpName(DEH_String("RTFCTOP"), PU_STATIC); + PatchSELECTBOX = W_CacheLumpName(DEH_String("SELECTBOX"), PU_STATIC); + PatchINVLFGEM1 = W_CacheLumpName(DEH_String("INVGEML1"), PU_STATIC); + PatchINVLFGEM2 = W_CacheLumpName(DEH_String("INVGEML2"), PU_STATIC); + PatchINVRTGEM1 = W_CacheLumpName(DEH_String("INVGEMR1"), PU_STATIC); + PatchINVRTGEM2 = W_CacheLumpName(DEH_String("INVGEMR2"), PU_STATIC); + PatchBLACKSQ = W_CacheLumpName(DEH_String("BLACKSQ"), PU_STATIC); + PatchARMCLEAR = W_CacheLumpName(DEH_String("ARMCLEAR"), PU_STATIC); + PatchCHAINBACK = W_CacheLumpName(DEH_String("CHAINBACK"), PU_STATIC); + startLump = W_GetNumForName(DEH_String("IN0")); for (i = 0; i < 10; i++) { PatchINumbers[i] = W_CacheLumpNum(startLump + i, PU_STATIC); } - PatchNEGATIVE = W_CacheLumpName("NEGNUM", PU_STATIC); - FontBNumBase = W_GetNumForName("FONTB16"); - startLump = W_GetNumForName("SMALLIN0"); + PatchNEGATIVE = W_CacheLumpName(DEH_String("NEGNUM"), PU_STATIC); + FontBNumBase = W_GetNumForName(DEH_String("FONTB16")); + startLump = W_GetNumForName(DEH_String("SMALLIN0")); for (i = 0; i < 10; i++) { PatchSmNumbers[i] = W_CacheLumpNum(startLump + i, PU_STATIC); } - playpalette = W_GetNumForName("PLAYPAL"); - spinbooklump = W_GetNumForName("SPINBK0"); - spinflylump = W_GetNumForName("SPFLY0"); + playpalette = W_GetNumForName(DEH_String("PLAYPAL")); + spinbooklump = W_GetNumForName(DEH_String("SPINBK0")); + spinflylump = W_GetNumForName(DEH_String("SPFLY0")); } //--------------------------------------------------------------------------- @@ -311,7 +312,7 @@ static void DrINumber(signed int val, int x, int y) { if (val < -9) { - V_DrawPatch(x + 1, y + 1, W_CacheLumpName("LAME", PU_CACHE)); + V_DrawPatch(x + 1, y + 1, W_CacheLumpName(DEH_String("LAME"), PU_CACHE)); } else { @@ -458,7 +459,7 @@ static void DrawSoundInfo(void) if (leveltime & 16) { - MN_DrTextA("*** SOUND DEBUG INFO ***", xPos[0], 20); + MN_DrTextA(DEH_String("*** SOUND DEBUG INFO ***"), xPos[0], 20); } S_GetChannelInfo(&s); if (s.channelCount == 0) @@ -466,13 +467,13 @@ static void DrawSoundInfo(void) return; } x = 0; - MN_DrTextA("NAME", xPos[x++], 30); - MN_DrTextA("MO.T", xPos[x++], 30); - MN_DrTextA("MO.X", xPos[x++], 30); - MN_DrTextA("MO.Y", xPos[x++], 30); - MN_DrTextA("ID", xPos[x++], 30); - MN_DrTextA("PRI", xPos[x++], 30); - MN_DrTextA("DIST", xPos[x++], 30); + MN_DrTextA(DEH_String("NAME"), xPos[x++], 30); + MN_DrTextA(DEH_String("MO.T"), xPos[x++], 30); + MN_DrTextA(DEH_String("MO.X"), xPos[x++], 30); + MN_DrTextA(DEH_String("MO.Y"), xPos[x++], 30); + MN_DrTextA(DEH_String("ID"), xPos[x++], 30); + MN_DrTextA(DEH_String("PRI"), xPos[x++], 30); + MN_DrTextA(DEH_String("DIST"), xPos[x++], 30); for (i = 0; i < s.channelCount; i++) { c = &s.chan[i]; @@ -480,7 +481,7 @@ static void DrawSoundInfo(void) y = 40 + i * 10; if (c->mo == NULL) { // Channel is unused - MN_DrTextA("------", xPos[0], y); + MN_DrTextA(DEH_String("------"), xPos[0], y); continue; } sprintf(text, "%s", c->name); @@ -570,8 +571,10 @@ void SB_Drawer(void) V_DrawPatch(0, 158, PatchBARBACK); if (players[consoleplayer].cheats & CF_GODMODE) { - V_DrawPatch(16, 167, W_CacheLumpName("GOD1", PU_CACHE)); - V_DrawPatch(287, 167, W_CacheLumpName("GOD2", PU_CACHE)); + V_DrawPatch(16, 167, + W_CacheLumpName(DEH_String("GOD1"), PU_CACHE)); + V_DrawPatch(287, 167, + W_CacheLumpName(DEH_String("GOD2"), PU_CACHE)); } oldhealth = -1; } @@ -776,8 +779,10 @@ void DrawMainBar(void) if (ArtifactFlash) { V_DrawPatch(180, 161, PatchBLACKSQ); - V_DrawPatch(182, 161, W_CacheLumpNum(W_GetNumForName("useartia") - + ArtifactFlash - 1, PU_CACHE)); + + temp = W_GetNumForName(DEH_String("useartia")) + ArtifactFlash - 1; + + V_DrawPatch(182, 161, W_CacheLumpNum(temp, PU_CACHE)); ArtifactFlash--; oldarti = -1; // so that the correct artifact fills in after the flash UpdateState |= I_STATBAR; @@ -789,7 +794,7 @@ void DrawMainBar(void) if (CPlayer->readyArtifact > 0) { V_DrawPatch(179, 160, - W_CacheLumpName(patcharti[CPlayer->readyArtifact], + W_CacheLumpName(DEH_String(patcharti[CPlayer->readyArtifact]), PU_CACHE)); DrSmallNumber(CPlayer->inventory[inv_ptr].count, 201, 182); } @@ -839,15 +844,15 @@ void DrawMainBar(void) { if (CPlayer->keys[key_yellow]) { - V_DrawPatch(153, 164, W_CacheLumpName("ykeyicon", PU_CACHE)); + V_DrawPatch(153, 164, W_CacheLumpName(DEH_String("ykeyicon"), PU_CACHE)); } if (CPlayer->keys[key_green]) { - V_DrawPatch(153, 172, W_CacheLumpName("gkeyicon", PU_CACHE)); + V_DrawPatch(153, 172, W_CacheLumpName(DEH_String("gkeyicon"), PU_CACHE)); } if (CPlayer->keys[key_blue]) { - V_DrawPatch(153, 180, W_CacheLumpName("bkeyicon", PU_CACHE)); + V_DrawPatch(153, 180, W_CacheLumpName(DEH_String("bkeyicon"), PU_CACHE)); } oldkeys = playerkeys; UpdateState |= I_STATBAR; @@ -861,7 +866,7 @@ void DrawMainBar(void) { DrINumber(temp, 109, 162); V_DrawPatch(111, 172, - W_CacheLumpName(ammopic[CPlayer->readyweapon - 1], + W_CacheLumpName(DEH_String(ammopic[CPlayer->readyweapon - 1]), PU_CACHE)); } oldammo = temp; @@ -887,6 +892,7 @@ void DrawMainBar(void) void DrawInventoryBar(void) { + char *patch; int i; int x; @@ -899,10 +905,9 @@ void DrawInventoryBar(void) if (CPlayer->inventorySlotNum > x + i && CPlayer->inventory[x + i].type != arti_none) { - V_DrawPatch(50 + i * 31, 160, - W_CacheLumpName(patcharti - [CPlayer->inventory[x + i].type], - PU_CACHE)); + patch = DEH_String(patcharti[CPlayer->inventory[x + i].type]); + + V_DrawPatch(50 + i * 31, 160, W_CacheLumpName(patch, PU_CACHE)); DrSmallNumber(CPlayer->inventory[x + i].count, 69 + i * 31, 182); } } @@ -921,6 +926,7 @@ void DrawInventoryBar(void) void DrawFullScreenStuff(void) { + char *patch; int i; int x; int temp; @@ -950,10 +956,9 @@ void DrawFullScreenStuff(void) { if (CPlayer->readyArtifact > 0) { - V_DrawTLPatch(286, 170, W_CacheLumpName("ARTIBOX", PU_CACHE)); - V_DrawPatch(286, 170, - W_CacheLumpName(patcharti[CPlayer->readyArtifact], - PU_CACHE)); + patch = DEH_String(patcharti[CPlayer->readyArtifact]); + V_DrawTLPatch(286, 170, W_CacheLumpName(DEH_String("ARTIBOX"), PU_CACHE)); + V_DrawPatch(286, 170, W_CacheLumpName(patch, PU_CACHE)); DrSmallNumber(CPlayer->inventory[inv_ptr].count, 307, 192); } } @@ -962,15 +967,14 @@ void DrawFullScreenStuff(void) x = inv_ptr - curpos; for (i = 0; i < 7; i++) { - V_DrawTLPatch(50 + i * 31, 168, W_CacheLumpName("ARTIBOX", - PU_CACHE)); + V_DrawTLPatch(50 + i * 31, 168, + W_CacheLumpName(DEH_String("ARTIBOX"), PU_CACHE)); if (CPlayer->inventorySlotNum > x + i && CPlayer->inventory[x + i].type != arti_none) { + patch = DEH_String(patcharti[CPlayer->inventory[x + i].type]); V_DrawPatch(50 + i * 31, 168, - W_CacheLumpName(patcharti - [CPlayer->inventory[x + i].type], - PU_CACHE)); + W_CacheLumpName(patch, PU_CACHE)); DrSmallNumber(CPlayer->inventory[x + i].count, 69 + i * 31, 190); } -- cgit v1.2.3 From c4fe7aa8b6962edf79e0ccec469970618a92b0ef Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Wed, 10 Feb 2010 20:21:21 +0000 Subject: Bump version number, update ChangeLog and NEWS. Subversion-branch: /trunk/chocolate-doom Subversion-revision: 1869 --- ChangeLog | 809 ++++++++++++++++++++++++++++++++++++++++++++++++ NEWS | 4 +- codeblocks/config.h | 6 +- codeblocks/game-res.rc | 10 +- codeblocks/setup-res.rc | 8 +- configure.in | 2 +- msvc/config.h | 6 +- msvc/win32.rc | 10 +- 8 files changed, 833 insertions(+), 22 deletions(-) diff --git a/ChangeLog b/ChangeLog index a370d4bd..a5bd7228 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,812 @@ +2010-01-31 18:21:50 fraggle + + Change Windows resource file to use PACKAGE_COPYRIGHT and + PACKAGE_LICENSE macros. + +2010-01-30 16:14:04 fraggle + + Change directory to home directory before launching the game, so that + recorded demos go somewhere sensible. + +2010-01-30 16:04:24 fraggle + + Set launch button as default button, so that it is possible to launch + the game by pressing return. + +2010-01-30 00:37:17 fraggle + + Rename mus2mid functions to be consistent with coding standard. + +2010-01-29 23:28:35 fraggle + + Remove unused PACKAGE_LONGDESC. + +2010-01-29 19:17:56 fraggle + + When doing a MUS to MID conversion, allocate MIDI channels so that the + lowest-numbered MIDI channels are used before higher-numbered ones. + Fixes ear-piercing whistle sound in the MAP05 music when playing with + timidity and EAWPATS (thanks entryway / HackNeyed). + +2010-01-29 03:55:20 fraggle + + Fix indentation/style etc. in mus2mid.c. + +2010-01-27 19:16:26 fraggle + + Add tags files to svn:ignore properties. + +2010-01-26 19:21:18 fraggle + + Minor fix of British spelling -> American. + +2010-01-26 19:18:18 fraggle + + Fix glass hack windows where a linedef is flagged as two sided but has + only one side. Fixes WADs such as OTTAWAU.WAD (thanks Never_Again). + +2010-01-23 23:06:45 fraggle + + Add menu item to launcher to open a terminal window that can be used + to start the game. Add missing 'edit' menu. Set svn:ignore property + for osx directory. + +2010-01-18 19:40:50 fraggle + + Fix package source URL. + +2010-01-18 19:29:48 fraggle + + Tweak package description slightly. + +2010-01-18 19:14:54 fraggle + + Define project short description, copyright, maintainer and URL in + configure.in. Use these for the Info-gnustep.plist file. Add + generated .spec file for building RPM packages. + +2010-01-17 16:58:37 fraggle + + Update NEWS. + +2010-01-17 16:31:03 fraggle + + Restore the original cursor when shutting down video code, this should + hopefully fix the problem with the mouse cursor disappearing when + exiting on Win9x (thanks Janizdreg). + +2010-01-16 19:20:11 fraggle + + Update TODO file. + +2010-01-15 19:29:28 fraggle + + Don't open the configuration window when the launcher is first run; + display an error message if the user tries to launch the game without + an IWAD selected. + +2010-01-15 19:14:02 fraggle + + Fix GNUstep info panel. + +2010-01-15 18:51:35 fraggle + + Center the launcher window and config window on startup. + +2010-01-15 18:40:37 fraggle + + Add wadfile.png for GNUstep build. + +2010-01-15 18:13:05 fraggle + + Extend osx makefile to allow building of a working GNUstep + application, for testing purposes. Add GNUstep version of Info.plist, + remove app-skeleton directory and move contents up. + +2010-01-14 18:47:03 fraggle + + In Chex Quest, use the radiation suit colormap instead of the red + colormaps that are usually used when taking damage (or using the + berserk pack). This matches the Vanilla chex.exe behavior (thanks + Fuzztooth). + +2010-01-12 20:16:25 fraggle + + Oops. + +2010-01-12 20:15:34 fraggle + + Strip executables when building Windows CE packages. + +2010-01-12 20:14:11 fraggle + + Rearrange order of Makefile generation to alphabetical order. + +2010-01-12 20:12:56 fraggle + + Move Makefile definitions for CC and STRIP into config.make, use + versions from autoconf. + +2010-01-12 20:09:54 fraggle + + Set main menu title based on package name, not fixed string. + +2010-01-12 20:09:01 fraggle + + Place commercial level name strings consecutively in the same array, + so that overflowing the end of one list accesses the start of the + next. This trick is used by pl2.wad for its MAP33 secret level. + +2010-01-12 01:32:24 fraggle + + Add missing connection for plutonia.wad set button. + +2010-01-12 01:20:48 fraggle + + Add document icon file and use for file associations. + +2010-01-11 19:10:42 fraggle + + Insert new files into the command line at the correct location, + allowing multiple files to be opened at once. + +2010-01-11 01:35:04 fraggle + + When launching a file from the finder, add the new file into the + command line at the appropriate position in the command line string. + +2010-01-10 20:46:15 fraggle + + Change "@executable_path@" to "@executable_path" + +2010-01-10 18:48:21 fraggle + + Install docs with a single cp, rather than using a for loop. + +2010-01-10 18:42:35 fraggle + + Recursively copy library dependencies into destination package. + Identify libraries to be installed based on the path in which they are + located, rather than whether there is "libSDL" in the name. Use + install_name_tool to change the search path so that the system looks + for libraries in @executable_path@ rather than their location on the + machine upon which the program was built. + +2010-01-09 21:06:31 fraggle + + Clear existing arguments when adding a file. + +2010-01-09 20:42:30 fraggle + + Add file to command line when opened; add link from AppController to + LauncherManager. + +2010-01-09 18:54:04 fraggle + + Initial code to identify file type by extension and add file to + command line. + +2010-01-09 18:38:48 fraggle + + Hook in AppController as delegate for application, add file + associations to property list file. + +2010-01-05 17:20:58 fraggle + + Add "clean" target to package makefiles. + +2010-01-05 15:52:12 fraggle + + Move config.make up to pkg/ directory. Use static makefiles to + generate all packages, rather than dynamically generated makefiles. + Add pkg/osx to dist. Make OS X staging directory depend on top level + documentation files. Generate CMDLINE as part of standard build if it + is not already present. Set svn:ignore properties. + +2010-01-04 22:53:44 fraggle + + Fix single space error when listing libraries. + +2010-01-04 22:45:45 fraggle + + Copy binaries into app dir along with libraries. + +2010-01-04 22:24:48 fraggle + + Include documentation files in package. + +2010-01-04 22:19:53 fraggle + + Fix GNUstep build. + +2010-01-04 22:11:11 fraggle + + Generate Info.plist and config.make in configure and remove temporary + versions. Include config.h from top level. + +2010-01-04 22:01:32 fraggle + + Import OS X launcher code to trunk. + +2010-01-03 03:49:11 fraggle + + Add quotes around $@ in autogen script (thanks exp[x]) + +2009-12-28 20:57:20 fraggle + + When recording low resolution (non-longtics) Vanilla demos, carry + forward the error from angleturn caused by the reduced resolution, so + that consecutive errors can accumulate, possibly making turning + slightly smoother. + +2009-12-27 01:42:13 fraggle + + Oops. + +2009-12-27 00:11:18 fraggle + + Allow DOOMWADDIR/DOOMWADPATH to contain the complete path to IWAD + files, as well as directories in which to search for IWAD files. + +2009-12-18 22:11:06 fraggle + + Fix poor quality application icons seen when the game is running. Add + back 8-bit icon files alongside files including both 8-bit and high + quality 32-bit versions. Use the high quality icon files for resource + files includes, and the low quality ones for in-game SDL. + +2009-12-18 21:11:32 fraggle + + Update generated source files containing icon data. + +2009-12-18 21:10:35 fraggle + + Make ExecuteCommand() under Unix return a failure when the executable + cannot be executed. + +2009-12-14 20:57:04 fraggle + + Use GetModuleFileNameW to get the (Unicode) path to the Doom + executable. This hopefully fixes problems with Unicode directory + names. + +2009-12-14 18:54:25 fraggle + + Add Chocolate Doom/setup icons with scaled versions for various + different icon sizes (thanks MikeRS). + +2009-12-12 01:20:49 fraggle + + Fix textscreen black border bug. + +2009-12-09 02:40:39 fraggle + + Fix the setup tool on Windows Vista/7 to not prompt for elevated + permissions and to disable the "Program Compatibility Assistant" + (thanks hobbs and MikeRS). + +2009-11-29 22:50:17 fraggle + + Add other missing files to dist. + +2009-11-29 22:25:51 fraggle + + Include .lvimrc in dist. + +2009-11-21 03:56:59 fraggle + + Add Makefile to build Win32 packages. + +2009-11-21 02:05:56 fraggle + + Use execvp() rather than execv(), to look up Doom binary in the PATH + if necessary. + +2009-11-21 00:40:58 fraggle + + Apply configuration file invalid key setting fix to setup code. + +2009-11-21 00:38:16 fraggle + + Don't crash if key settings are set in a configuration file that are + out of range (thanks entryway). + +2009-11-21 00:24:59 fraggle + + Fix crash with chocolate-setup under Windows (thanks Janizdreg). + +2009-11-19 21:49:13 fraggle + + Rework the OS X MIDI disabling code, as SDL_mixer 1.2.11 fixes the + crash. Check and disable MIDI by default if using an older version of + SDL on OS X. + +2009-11-19 21:07:31 fraggle + + Make chocolate-setup use its own location in the filesystem to find + the location of the chocolate-doom executable. Remove INSTALL_DIR + define. + +2009-11-05 19:57:55 fraggle + + Perform bounds checking on values passed to TXT_UpdateScreenArea() to + avoid crashes. + +2009-10-26 19:28:12 fraggle + + Initial hacks for compiling under SDL 1.3. + +2009-10-17 21:13:54 fraggle + + Fix error in last change. + +2009-10-17 20:39:37 fraggle + + Use M_StrToInt() when processing values passed with -spechit, so that + hex values can be specified. + +2009-10-17 20:29:46 fraggle + + Import donut overrun emulation code from PrBoom+ (Thanks entryway). + +2009-10-16 19:10:30 fraggle + + Fix compilation under MSVC (thanks entryway). + +2009-10-10 23:58:25 fraggle + + Rename pkg/wince/Makefile to pkg/wince/GNUmakefile (it uses GNU + extensions). + +2009-10-10 22:46:14 fraggle + + Add pkg directory to make dist. + +2009-10-10 02:02:58 fraggle + + Don't crash when using the donut special type and the joining linedef + is one sided (thanks Alexander Waldmann). + +2009-10-05 21:25:53 fraggle + + Fix desync in ep1-0500.lmp on 64-bit (thanks exp(x)). + +2009-10-05 00:38:14 fraggle + + Provide pointer to STARTUPINFO structure when calling CreateProcessW, + to stop crash under normal Windows (not CE) when launching Doom from + the setup tools (thanks Janizdreg). + +2009-10-01 20:08:21 fraggle + + Oops. + +2009-10-01 02:04:00 fraggle + + Oops. + +2009-10-01 00:07:03 fraggle + + Change British English spellings to American English, for consistency. + +2009-09-20 16:27:40 fraggle + + Use "const char" in libtextscreen where appropriate (thanks entryway). + +2009-09-11 22:56:47 fraggle + + Add (lack of) copyright notice for SDL workaround. + +2009-09-07 20:43:04 fraggle + + Fix compilation under MacOS X. + +2009-09-06 19:15:52 fraggle + + Fixes for MSVC compile (thanks entryway). + +2009-08-28 00:27:47 fraggle + + Allow PGUP/PGDN to scroll up and down in scroll panes (thanks + LionsPhil). + +2009-07-20 23:27:59 fraggle + + Remove redundant variable assignment (thanks Quasar/Yagisan) + +2009-07-20 01:37:41 fraggle + + Save and display the loading disk icon as a fixed 16x16 square, from + an image drawn at the bottom right corner of the screen. This seems + to be the same as how Vanilla behaves, and fixes chook3.wad, that uses + an STDISK replacement with an offset that pushes the image to the + left. + +2009-07-13 23:43:06 fraggle + + Add stdio.h include to fix MSVC build (thanks Kaiser) + +2009-07-12 17:47:12 fraggle + + Fix compile with libsamplerate. + +2009-07-12 15:00:50 fraggle + + On Windows CE, use the Windows API to find the amount of available + memory, so that at least two megabytes are always left available to + the OS. + +2009-07-11 12:15:32 fraggle + + Add missing item to NEWS. + +2009-07-07 20:46:55 fraggle + + Update NEWS. + +2009-07-07 20:38:00 fraggle + + Fix launching of the game from the setup tool in Windows CE. + +2009-06-21 20:33:35 fraggle + + Add Makefile for building CAB files, dependency calculation. + +2009-06-21 20:19:43 fraggle + + Use correct filename for SDL_net DLL. + +2009-06-21 20:03:38 fraggle + + Remove temporary files after generating CAB file. + +2009-06-20 23:13:44 fraggle + + Add script to generate Windows CE install package. + +2009-06-16 20:47:13 fraggle + + Automatically allocate a smaller zone size if it was not possible to + allocate the default 16 MiB. + +2009-06-13 18:10:18 fraggle + + Don't post zero key events. + +2009-06-12 20:07:55 fraggle + + On Windows CE systems without a keyboard, patch the default settings + to use hardware keys. + +2009-06-12 18:58:42 fraggle + + Remove debug messages. + +2009-06-12 18:35:39 fraggle + + Set the USER environment variable based on the owner information from + the registry. + +2009-06-12 18:34:27 fraggle + + Always grab input on Windows CE. + +2009-06-11 22:34:36 fraggle + + Include libc_wince.a in chocolate-server build. + +2009-06-11 20:41:20 fraggle + + Grab the input in setup when reading a new key binding, so that + Windows CE buttons are read properly. Map buttons to PC function + keys. + +2009-06-11 19:19:05 fraggle + + Include libc_wince.h on Windows CE. + +2009-06-11 19:18:12 fraggle + + Declare getenv/putenv on Windows CE for recent SDL versions that do + not declare it. + +2009-06-10 20:03:08 fraggle + + Add key bindings for pause, message refresh. + +2009-06-08 20:26:29 fraggle + + Remove debugging code. + +2009-06-08 19:15:57 fraggle + + Use SDL's getenv/putenv implementation, and populate at startup. + +2009-06-08 00:41:10 fraggle + + Use CreateFileW instead of OpenFile (doesn't exist on Windows CE) + +2009-06-07 20:08:08 fraggle + + Fix header includes (thanks exp[x]) + +2009-06-07 19:18:02 fraggle + + Don't add DirectX/Windib selector on Windows CE. + +2009-06-07 18:53:25 fraggle + + Use home dir to store configuration and savegames under Windows CE. + +2009-06-07 18:33:19 fraggle + + Fix setup tool display configuration dialog when fullscreen is not + supported. + +2009-06-07 18:10:05 fraggle + + Make auto-adjust code switch to windowed mode if no fullscreen modes + are available. + +2009-06-07 17:41:46 fraggle + + Catch errors when initialising SDL. Use the small textscreen font by + default on Windows CE if no fullscreen modes are available. + +2009-06-07 17:39:08 fraggle + + Add missing SDL_thread include. + +2009-06-07 17:35:43 fraggle + + Don't try to use the SDL DirectX driver under Windows CE. + +2009-06-07 16:21:41 fraggle + + Fix setup tool compile on Windows CE. + +2009-06-07 16:15:40 fraggle + + Remove call to setbuf. + +2009-06-07 15:35:27 fraggle + + Add IWAD search dirs for Windows CE. + +2009-06-07 15:20:46 fraggle + + Exit with an error on failure to allocate zone memory. + +2009-06-07 03:10:21 fraggle + + Use MessageBoxW instead of MessageBox (doesn't exist on Windows CE) + +2009-06-07 02:59:49 fraggle + + Add README file for Windows CE library. + +2009-06-07 02:56:21 fraggle + + Detect Windows CE target and build/include libc_wince files as + necessary. + +2009-06-07 02:50:47 rtc_marine + + - Update textscreen codeblocks project to include txt_scrollpane.* and + txt_smallfont.h + +2009-06-07 02:33:58 fraggle + + Include libc_wince.h when on Windows CE. + +2009-06-07 02:32:15 fraggle + + Add CPU affinity function for Windows CE. + +2009-06-07 02:27:58 fraggle + + Add libc_wince.h header, and EISDIR error value. + +2009-06-07 02:27:30 fraggle + + Use GetUserNameExW, not GetUserName (doesn't exist on WinCE) + +2009-06-07 02:26:45 fraggle + + Fix compile with FEATURE_MULTIPLAYER disabled. + +2009-06-07 02:24:40 fraggle + + Fix compile with FEATURE_SOUND disabled. + +2009-06-07 01:56:23 fraggle + + Add Windows CE implementations of some ANSI C functions that are + missing. + +2009-06-06 22:13:44 fraggle + + Don't check for Steam/CD installer versions on Windows CE. + +2009-06-05 17:58:48 fraggle + + Add key binding variables for automap and weapon keys. + +2009-06-04 00:37:02 fraggle + + Increase height of menu bindings dialog. + +2009-06-04 00:35:05 fraggle + + Use newer keyboard bindings dialog layout from raven-branch. + +2009-06-04 00:20:37 fraggle + + Add unique key groups for menu navigation and shortcuts. + +2009-06-04 00:20:06 fraggle + + Use key for confirming menu messages, not typed char. + +2009-06-03 21:45:54 fraggle + + Add dialog to setup tool for editing menu shortcuts. + +2009-06-03 21:18:04 fraggle + + Add config file variables to increase/decrease screen size. + +2009-06-03 20:59:26 fraggle + + Fix shortcut keys for menu items. + +2009-06-03 20:55:50 fraggle + + Add configuration file entries for menu key bindings. + +2009-06-03 20:37:19 fraggle + + Add key_ variables for the keys used to control the menu. + +2009-05-26 23:14:24 fraggle + + Fix tags for functions using TXT_UNCAST_ARG. + +2009-05-26 22:13:18 fraggle + + Set appropriate vim 'tags' variable for ctags files. + +2009-05-21 20:18:38 fraggle + + Set display settings window position based on screen dimensions, + rather than hard coding position. + +2009-05-19 18:07:49 fraggle + + Fix manpage documentation for DOOMWADPATH (thanks MikeRS) + +2009-05-18 19:30:49 fraggle + + Fix A_BossDeath behavior in v1.9 emulation mode (thanks entryway) + +2009-05-17 14:54:19 fraggle + + Always use an SDL buffer size that is a power of two. Reduce buffer + size to 70ms. + +2009-05-12 19:03:20 fraggle + + Add option to "join game" dialog in setup tool to autojoin a LAN game. + +2009-05-12 19:01:27 fraggle + + Make txt_inputboxes emit a "changed" signal when their value is + changed. + +2009-05-07 22:59:38 fraggle + + Calculate SDL buffer size automatically based on sample rate. + +2009-05-05 01:00:53 fraggle + + Better ASCII chart. + +2009-05-05 00:46:27 fraggle + + Minor smallfont fixups. + +2009-05-01 22:05:57 fraggle + + Add copyright headers to textscreen examples. + +2009-04-26 17:59:08 fraggle + + More smallfont fixups. + +2009-04-23 20:58:11 fraggle + + Fix up some extended ASCII characters. + +2009-04-23 19:19:52 fraggle + + Oops. + +2009-04-23 19:18:43 fraggle + + Add small textscreen font for low resolution displays, based on the + Atari-Small font by Tom Fine. + +2009-03-15 14:44:23 fraggle + + Fix clipped sounds when using libsamplerate (thanks David Flater) + +2009-03-14 15:28:41 fraggle + + Add check to allow sched_setaffinity code to work on older versions of + libc. + +2009-03-12 18:55:27 fraggle + + Define INVALID_SET_FILE_POINTER if it is not defined, to fix + compilation under MSVC6 (thanks Quasar) + +2009-03-08 22:51:25 fraggle + + Add "make doc" target to run Doxygen, and add a Doxyfile. Add @file + tags to start of header files so that Doxygen will process them. + +2009-03-07 00:35:08 fraggle + + Add documentation for high-level txt_desktop.h functions. + +2009-03-07 00:24:45 fraggle + + Add documentation for high-level textscreen functions. + +2009-03-06 20:01:32 fraggle + + Fix signed/unsigned conversion warning. + +2009-03-03 19:26:20 fraggle + + Look up SetProcessAffinityMask function at runtime, so that the + program should work under Win9x again. + +2009-01-30 23:53:47 fraggle + + Fix layout of widgets within scroll panes. Scroll scroll panes in + response to keyboard events. + +2009-01-29 23:26:03 fraggle + + Shrink text box slightly. + +2009-01-29 23:00:14 fraggle + + Allow clicking within scroll bars to set position. + +2009-01-29 22:54:13 fraggle + + Add scrollable pane widget to textscreen library. + +2009-01-17 14:05:31 fraggle + + Fix '-mmap' command line parameter. + +2009-01-07 22:05:13 fraggle + + Create the ~/.chocolate-doom/savegames directory on startup if it does + not exist. + +2009-01-07 21:51:37 fraggle + + Replace -nommap with -mmap; do not use mmap()ed file access by + default. Fixes Plutonia 2, and several other minor things. + +2008-12-10 20:25:05 fraggle + + Bump version to 1.2.1, update NEWS and ChangeLog. + 2008-12-10 20:20:10 fraggle Fix crash when playing Doom 1 levels. diff --git a/NEWS b/NEWS index ac12bded..31baedf0 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -... +1.3.0 (2010-02-10): * Chocolate Doom now runs on Windows Mobile/Windows CE! * It is possible to rebind most/all of the keys that control the @@ -35,6 +35,8 @@ * When recording shorttics demos, errors caused by the reduced turning resolution are carried forward, possibly making turning smoother. + * The source tarball can now be used to build an RPM package: + rpmbuild -tb chocolate-doom-VER.tar.gz Compatibility: * The A_BossDeath behavior in v1.9 emulation mode was fixed diff --git a/codeblocks/config.h b/codeblocks/config.h index fe1bde8f..70a4a742 100644 --- a/codeblocks/config.h +++ b/codeblocks/config.h @@ -9,19 +9,19 @@ #define PACKAGE_NAME "Chocolate Doom" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "Chocolate Doom 1.2.1" +#define PACKAGE_STRING "Chocolate Doom 1.3.0" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "chocolate-doom" /* Define to the version of this package. */ -#define PACKAGE_VERSION "1.2.1" +#define PACKAGE_VERSION "1.3.0" /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Version number of package */ -#define VERSION "1.2.1" +#define VERSION "1.3.0" /* Define to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel and VAX). */ diff --git a/codeblocks/game-res.rc b/codeblocks/game-res.rc index 4a9e289b..98e58491 100644 --- a/codeblocks/game-res.rc +++ b/codeblocks/game-res.rc @@ -1,21 +1,21 @@ 1 ICON "../data/doom.ico" 1 VERSIONINFO -PRODUCTVERSION 1,2,1,0 -FILEVERSION 1,2,1,0 +PRODUCTVERSION 1,3,0,0 +FILEVERSION 1,3,0,0 FILETYPE 1 { BLOCK "StringFileInfo" { BLOCK "040904E4" { - VALUE "FileVersion", "1.2.1" - VALUE "FileDescription", "1.2.1" + VALUE "FileVersion", "1.3.0" + VALUE "FileDescription", "1.3.0" VALUE "InternalName", "Chocolate-Doom" VALUE "CompanyName", "Chocolate-Doom" VALUE "LegalCopyright", "GNU General Public License" VALUE "ProductName", "Chocolate-Doom" - VALUE "ProductVersion", "1.2.1" + VALUE "ProductVersion", "1.3.0" } } } diff --git a/codeblocks/setup-res.rc b/codeblocks/setup-res.rc index fe791088..906658f3 100644 --- a/codeblocks/setup-res.rc +++ b/codeblocks/setup-res.rc @@ -3,21 +3,21 @@ CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "setup-manifest.xml" 1 VERSIONINFO -PRODUCTVERSION 1,2,1,0 -FILEVERSION 1,2,1,0 +PRODUCTVERSION 1,3,0,0 +FILEVERSION 1,3,0,0 FILETYPE 1 { BLOCK "StringFileInfo" { BLOCK "040904E4" { - VALUE "FileVersion", "1.2.1" + VALUE "FileVersion", "1.3.0" VALUE "FileDescription", "Chocolate-Doom Setup" VALUE "InternalName", "chocolate-setup" VALUE "CompanyName", "fraggle@gmail.com" VALUE "LegalCopyright", "GNU General Public License" VALUE "ProductName", "Chocolate-Doom Setup" - VALUE "ProductVersion", "1.2.1" + VALUE "ProductVersion", "1.3.0" } } } diff --git a/configure.in b/configure.in index 9d09739b..53039e5e 100644 --- a/configure.in +++ b/configure.in @@ -1,4 +1,4 @@ -AC_INIT(Chocolate Doom, 1.2.1, fraggle@gmail.com, chocolate-doom) +AC_INIT(Chocolate Doom, 1.3.0, fraggle@gmail.com, chocolate-doom) PACKAGE_SHORTDESC="Conservative Doom source port" PACKAGE_COPYRIGHT="Copyright (C) 1993-2010" diff --git a/msvc/config.h b/msvc/config.h index 7880c3c1..2dbf8c11 100644 --- a/msvc/config.h +++ b/msvc/config.h @@ -11,16 +11,16 @@ #define PACKAGE_NAME "Chocolate Doom" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "Chocolate Doom 1.2.1" +#define PACKAGE_STRING "Chocolate Doom 1.3.0" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "chocolate-doom" /* Define to the version of this package. */ -#define PACKAGE_VERSION "1.2.1" +#define PACKAGE_VERSION "1.3.0" /* Version number of package */ -#define VERSION "1.2.1" +#define VERSION "1.3.0" /* Define to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel and VAX). */ diff --git a/msvc/win32.rc b/msvc/win32.rc index b9bc06a2..72bb7802 100644 --- a/msvc/win32.rc +++ b/msvc/win32.rc @@ -32,21 +32,21 @@ #endif 1 VERSIONINFO -PRODUCTVERSION 1,2,1,0 -FILEVERSION 1,2,1,0 +PRODUCTVERSION 1,3,0,0 +FILEVERSION 1,3,0,0 FILETYPE 1 BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" BEGIN - VALUE "FileVersion", "1.2.1" - VALUE "FileDescription", "Chocolate Doom 1.2.1" + VALUE "FileVersion", "1.3.0" + VALUE "FileDescription", "Chocolate Doom 1.3.0" VALUE "InternalName", "chocolate-doom" VALUE "CompanyName", "fraggle@gmail.com" VALUE "LegalCopyright", "GNU General Public License" VALUE "ProductName", "Chocolate Doom" - VALUE "ProductVersion", "1.2.1" + VALUE "ProductVersion", "1.3.0" END END END -- cgit v1.2.3 From 06b97d2d116b622bc067b245f81b2857767d598e Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Fri, 26 Feb 2010 21:07:59 +0000 Subject: Add OPL hardware playback support for Windows NT-based systems. Subversion-branch: /branches/opl-branch Subversion-revision: 1871 --- OPL-TODO | 1 + README.OPL | 8 +- opl/Makefile.am | 3 +- opl/ioperm_sys.c | 255 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ opl/ioperm_sys.h | 36 ++++++++ opl/opl.c | 11 ++- opl/opl_win32.c | 172 +++++++++++++++++++++++++++++++++++++ opl/opl_win9x.c | 140 ------------------------------ 8 files changed, 480 insertions(+), 146 deletions(-) create mode 100644 opl/ioperm_sys.c create mode 100644 opl/ioperm_sys.h create mode 100644 opl/opl_win32.c delete mode 100644 opl/opl_win9x.c diff --git a/OPL-TODO b/OPL-TODO index 57d315a0..6252bc2a 100644 --- a/OPL-TODO +++ b/OPL-TODO @@ -15,5 +15,6 @@ Bad MIDIs: Other tasks: + * Get a better software OPL emulator * DMXOPTIONS opl3/phase option support. diff --git a/README.OPL b/README.OPL index 2b837c4c..1746dd88 100644 --- a/README.OPL +++ b/README.OPL @@ -64,11 +64,13 @@ stdout.txt: If you're running an NT-based system, it is not possible to directly access the OPL chip, even when running as Administrator. Fortunately, -it is possible to use the third-party "PortTalk" driver: +it is possible to use the "ioperm.sys" driver developed for Cygwin: - http://www.beyondlogic.org/porttalk/porttalk.htm + http://openwince.sourceforge.net/ioperm/ -TODO - the NT driver hasn't actually been written yet.. +It is not necessary to have Cygwin installed to use this. Copy the +ioperm.sys file into the same directory as the Chocolate Doom +executable and it should be automatically loaded. === Linux === diff --git a/opl/Makefile.am b/opl/Makefile.am index 8bbed9f0..d099b875 100644 --- a/opl/Makefile.am +++ b/opl/Makefile.am @@ -13,6 +13,7 @@ libopl_a_SOURCES = \ opl_queue.c opl_queue.h \ opl_sdl.c \ opl_timer.c opl_timer.h \ - opl_win9x.c \ + opl_win32.c \ + ioperm_sys.c ioperm_sys.h \ fmopl.c fmopl.h diff --git a/opl/ioperm_sys.c b/opl/ioperm_sys.c new file mode 100644 index 00000000..37512b63 --- /dev/null +++ b/opl/ioperm_sys.c @@ -0,0 +1,255 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2002, 2003 Marcel Telka +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// Interface to the ioperm.sys driver, based on code from the +// Cygwin ioperm library. +// +//----------------------------------------------------------------------------- + +#ifdef _WIN32 + +#include + +#define WIN32_LEAN_AND_MEAN +#include +#include + +#include + +#define IOPERM_FILE "\\\\.\\ioperm" + +#define IOCTL_IOPERM \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0xA00, METHOD_BUFFERED, FILE_ANY_ACCESS) + +struct ioperm_data +{ + unsigned long from; + unsigned long num; + int turn_on; +}; + +static SC_HANDLE scm = NULL; +static SC_HANDLE svc = NULL; +static int service_was_created = 0; +static int service_was_started = 0; + +int IOperm_EnablePortRange(unsigned int from, unsigned int num, int turn_on) +{ + HANDLE h; + struct ioperm_data ioperm_data; + DWORD BytesReturned; + BOOL r; + + h = CreateFile(IOPERM_FILE, GENERIC_READ, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (h == INVALID_HANDLE_VALUE) + { + errno = ENODEV; + return -1; + } + + ioperm_data.from = from; + ioperm_data.num = num; + ioperm_data.turn_on = turn_on; + + r = DeviceIoControl(h, IOCTL_IOPERM, + &ioperm_data, sizeof ioperm_data, + NULL, 0, + &BytesReturned, NULL); + + if (!r) + { + errno = EPERM; + } + + CloseHandle(h); + + return r != 0; +} + +// Load ioperm.sys driver. +// Returns 1 for success, 0 for failure. +// Remember to call IOperm_UninstallDriver to uninstall the driver later. + +int IOperm_InstallDriver(void) +{ + int error; + int result = 1; + + scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + + if (scm == NULL) + { + error = GetLastError(); + fprintf(stderr, "IOperm_InstallDriver: OpenSCManager failed (%i)\n", + error); + return 0; + } + + svc = CreateService(scm, + TEXT("ioperm"), + TEXT("I/O port access driver"), + SERVICE_ALL_ACCESS, + SERVICE_KERNEL_DRIVER, + SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, + "ioperm.sys", + NULL, + NULL, + NULL, + NULL, + NULL); + + if (svc == NULL) + { + error = GetLastError(); + + if (error != ERROR_SERVICE_EXISTS) + { + fprintf(stderr, + "IOperm_InstallDriver: Failed to create service (%i)\n", + error); + } + else + { + svc = OpenService(scm, TEXT("ioperm"), SERVICE_ALL_ACCESS); + + if (svc == NULL) + { + error = GetLastError(); + + fprintf(stderr, + "IOperm_InstallDriver: Failed to open service (%i)\n", + error); + } + } + + if (svc == NULL) + { + CloseServiceHandle(scm); + return 0; + } + } + else + { + service_was_created = 1; + } + + if (!StartService(svc, 0, NULL)) + { + error = GetLastError(); + + if (error != ERROR_SERVICE_ALREADY_RUNNING) + { + fprintf(stderr, "IOperm_InstallDriver: Failed to start service (%i)\n", + error); + result = 0; + } + else + { + printf("IOperm_InstallDriver: ioperm driver already running\n"); + } + } + else + { + printf("IOperm_InstallDriver: ioperm driver installed\n"); + service_was_started = 1; + } + + if (result == 0) + { + CloseServiceHandle(svc); + CloseServiceHandle(scm); + } + + return result; +} + +int IOperm_UninstallDriver(void) +{ + SERVICE_STATUS stat; + int result = 1; + int error; + + // If we started the service, stop it. + + if (service_was_started) + { + if (!ControlService(svc, SERVICE_CONTROL_STOP, &stat)) + { + error = GetLastError(); + + if (error == ERROR_SERVICE_NOT_ACTIVE) + { + fprintf(stderr, + "IOperm_UninstallDriver: Service not active? (%i)\n", + error); + } + else + { + fprintf(stderr, + "IOperm_UninstallDriver: Failed to stop service (%i)\n", + error); + result = 0; + } + } + } + + // If we created the service, delete it. + + if (service_was_created) + { + if (!DeleteService(svc)) + { + error = GetLastError(); + + fprintf(stderr, + "IOperm_UninstallDriver: DeleteService failed (%i)\n", + error); + + result = 0; + } + } + + // Close handles. + + if (svc != NULL) + { + CloseServiceHandle(svc); + svc = NULL; + } + + if (scm != NULL) + { + CloseServiceHandle(scm); + scm = NULL; + } + + service_was_created = 0; + service_was_started = 0; + + return result; +} + +#endif /* #ifndef _WIN32 */ + diff --git a/opl/ioperm_sys.h b/opl/ioperm_sys.h new file mode 100644 index 00000000..faf17bf3 --- /dev/null +++ b/opl/ioperm_sys.h @@ -0,0 +1,36 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2002, 2003 Marcel Telka +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// Interface to the ioperm.sys driver, based on code from the +// Cygwin ioperm library. +// +//----------------------------------------------------------------------------- + +#ifndef IOPERM_SYS_H +#define IOPERM_SYS_H + +int IOperm_EnablePortRange(unsigned int from, unsigned int num, int turn_on); +int IOperm_InstallDriver(void); +int IOperm_UninstallDriver(void); + +#endif /* #ifndef IOPERM_SYS_H */ + diff --git a/opl/opl.c b/opl/opl.c index 2c8fd692..9e674530 100644 --- a/opl/opl.c +++ b/opl/opl.c @@ -46,7 +46,7 @@ extern opl_driver_t opl_linux_driver; extern opl_driver_t opl_openbsd_driver; #endif #ifdef _WIN32 -extern opl_driver_t opl_win9x_driver; +extern opl_driver_t opl_win32_driver; #endif extern opl_driver_t opl_sdl_driver; @@ -59,7 +59,7 @@ static opl_driver_t *drivers[] = &opl_openbsd_driver, #endif #ifdef _WIN32 - &opl_win9x_driver, + &opl_win32_driver, #endif &opl_sdl_driver, NULL @@ -197,6 +197,7 @@ void OPL_WritePort(opl_port_t port, unsigned int value) { #ifdef OPL_DEBUG_TRACE printf("OPL_write: %i, %x\n", port, value); + fflush(stdout); #endif driver->write_port_func(port, value); } @@ -208,10 +209,16 @@ unsigned int OPL_ReadPort(opl_port_t port) { unsigned int result; +#ifdef OPL_DEBUG_TRACE + printf("OPL_read: %i...\n", port); + fflush(stdout); +#endif + result = driver->read_port_func(port); #ifdef OPL_DEBUG_TRACE printf("OPL_read: %i -> %x\n", port, result); + fflush(stdout); #endif return result; diff --git a/opl/opl_win32.c b/opl/opl_win32.c new file mode 100644 index 00000000..29df3643 --- /dev/null +++ b/opl/opl_win32.c @@ -0,0 +1,172 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// OPL Win32 native interface. +// +//----------------------------------------------------------------------------- + +#include "config.h" + +#ifdef _WIN32 + +#include + +#define WIN32_LEAN_AND_MEAN +#include + +#include "opl.h" +#include "opl_internal.h" +#include "opl_timer.h" + +#include "ioperm_sys.h" + +static unsigned int opl_port_base; + +// MingW? + +#if defined(__GNUC__) && defined(__i386__) + +static unsigned int OPL_Win32_PortRead(opl_port_t port) +{ + unsigned char result; + + __asm__ volatile ( + "movl %1, %%edx\n" + "inb %%dx, %%al\n" + "movb %%al, %0" + : "=m" (result) + : "r" (opl_port_base + port) + : "edx", "al", "memory" + ); + + return result; +} + +static void OPL_Win32_PortWrite(opl_port_t port, unsigned int value) +{ + __asm__ volatile ( + "movl %0, %%edx\n" + "movb %1, %%al\n" + "outb %%al, %%dx" + : + : "r" (opl_port_base + port), "r" ((unsigned char) value) + : "edx", "al" + ); +} + +// TODO: MSVC version +// #elif defined(_MSC_VER) && defined(_M_IX6) ... + +#else + +// Not x86, or don't know how to do port R/W on this compiler. + +#define NO_PORT_RW + +static unsigned int OPL_Win32_PortRead(opl_port_t port) +{ + return 0; +} + +static void OPL_Win32_PortWrite(opl_port_t port, unsigned int value) +{ +} + +#endif + +static int OPL_Win32_Init(unsigned int port_base) +{ +#ifndef NO_PORT_RW + + OSVERSIONINFO version_info; + + opl_port_base = port_base; + + // Check the OS version. + + memset(&version_info, 0, sizeof(version_info)); + version_info.dwOSVersionInfoSize = sizeof(version_info); + + GetVersionEx(&version_info); + + // On NT-based systems, we must acquire I/O port permissions + // using the ioperm.sys driver. + + if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT) + { + // Install driver. + + if (!IOperm_InstallDriver()) + { + return 0; + } + + // Open port range. + + if (!IOperm_EnablePortRange(opl_port_base, 2, 1)) + { + IOperm_UninstallDriver(); + return 0; + } + } + + // Start callback thread + + if (!OPL_Timer_StartThread()) + { + IOperm_UninstallDriver(); + return 0; + } + + return 1; + +#endif + + return 0; +} + +static void OPL_Win32_Shutdown(void) +{ + // Stop callback thread + + OPL_Timer_StopThread(); + + // Unload IOperm library. + + IOperm_UninstallDriver(); +} + +opl_driver_t opl_win32_driver = +{ + "Win32", + OPL_Win32_Init, + OPL_Win32_Shutdown, + OPL_Win32_PortRead, + OPL_Win32_PortWrite, + OPL_Timer_SetCallback, + OPL_Timer_ClearCallbacks, + OPL_Timer_Lock, + OPL_Timer_Unlock, + OPL_Timer_SetPaused +}; + +#endif /* #ifdef _WIN32 */ + diff --git a/opl/opl_win9x.c b/opl/opl_win9x.c deleted file mode 100644 index ff527b3e..00000000 --- a/opl/opl_win9x.c +++ /dev/null @@ -1,140 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// Copyright(C) 2009 Simon Howard -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -// 02111-1307, USA. -// -// DESCRIPTION: -// OPL Win9x native interface. -// -//----------------------------------------------------------------------------- - -#include "config.h" - -#ifdef _WIN32 - -#define WIN32_LEAN_AND_MEAN -#include - -#include "opl.h" -#include "opl_internal.h" -#include "opl_timer.h" - -static unsigned int opl_port_base; - -// MingW? - -#if defined(__GNUC__) && defined(__i386__) - -static unsigned int OPL_Win9x_PortRead(opl_port_t port) -{ - unsigned char result; - - __asm__ volatile ( - "movl %1, %%edx\n" - "inb %%dx, %%al\n" - "movb %%al, %0" - : "=m" (result) - : "r" (opl_port_base + port) - : "edx", "al", "memory" - ); - - return result; -} - -static void OPL_Win9x_PortWrite(opl_port_t port, unsigned int value) -{ - __asm__ volatile ( - "movl %0, %%edx\n" - "movb %1, %%al\n" - "outb %%al, %%dx" - : - : "r" (opl_port_base + port), "r" ((unsigned char) value) - : "edx", "al" - ); -} - -// TODO: MSVC version -// #elif defined(_MSC_VER) && defined(_M_IX6) ... - -#else - -// Not x86, or don't know how to do port R/W on this compiler. - -#define NO_PORT_RW - -static unsigned int OPL_Win9x_PortRead(opl_port_t port) -{ - return 0; -} - -static void OPL_Win9x_PortWrite(opl_port_t port, unsigned int value) -{ -} - -#endif - -static int OPL_Win9x_Init(unsigned int port_base) -{ -#ifndef NO_PORT_RW - - OSVERSIONINFO version_info; - - // Check that this is a Windows 9x series OS: - - memset(&version_info, 0, sizeof(version_info)); - version_info.dwOSVersionInfoSize = sizeof(version_info); - - GetVersionEx(&version_info); - - if (version_info.dwPlatformId == 1) - { - opl_port_base = port_base; - - // Start callback thread - - return OPL_Timer_StartThread(); - } - -#endif - - return 0; -} - -static void OPL_Win9x_Shutdown(void) -{ - // Stop callback thread - - OPL_Timer_StopThread(); -} - -opl_driver_t opl_win9x_driver = -{ - "Win9x", - OPL_Win9x_Init, - OPL_Win9x_Shutdown, - OPL_Win9x_PortRead, - OPL_Win9x_PortWrite, - OPL_Timer_SetCallback, - OPL_Timer_ClearCallbacks, - OPL_Timer_Lock, - OPL_Timer_Unlock, - OPL_Timer_SetPaused -}; - -#endif /* #ifdef _WIN32 */ - -- cgit v1.2.3 From 5f2cfb50d1632b015817f442b507b36c83301332 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Fri, 26 Feb 2010 21:32:41 +0000 Subject: Minor documentation fixups. Subversion-branch: /branches/opl-branch Subversion-revision: 1872 --- README.OPL | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.OPL b/README.OPL index 1746dd88..07699232 100644 --- a/README.OPL +++ b/README.OPL @@ -58,7 +58,7 @@ anything. Windows allows direct access to the OPL chip. You can confirm that hardware OPL is working by checking for this message in stdout.txt: - OPL_Init: Using driver 'Win9x'. + OPL_Init: Using driver 'Win32'. === Windows NT (including 2000, XP and later) === @@ -72,6 +72,11 @@ It is not necessary to have Cygwin installed to use this. Copy the ioperm.sys file into the same directory as the Chocolate Doom executable and it should be automatically loaded. +You can confirm that hardware OPL is working by checking for this +message in stdout.txt: + + OPL_Init: Using driver 'Win32'. + === Linux === If you are using a system based on the Linux kernel, you can access -- cgit v1.2.3 From 1398c8aed38b0d42e7081410ae1710858d335f7e Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 27 Feb 2010 17:48:25 +0000 Subject: Fix race condition with condition variable freed before it is signaled. Subversion-branch: /branches/opl-branch Subversion-revision: 1873 --- opl/opl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/opl/opl.c b/opl/opl.c index 9e674530..6d0e16db 100644 --- a/opl/opl.c +++ b/opl/opl.c @@ -415,9 +415,10 @@ static void DelayCallback(void *_delay_data) SDL_LockMutex(delay_data->mutex); delay_data->finished = 1; - SDL_UnlockMutex(delay_data->mutex); SDL_CondSignal(delay_data->cond); + + SDL_UnlockMutex(delay_data->mutex); } void OPL_Delay(unsigned int ms) -- cgit v1.2.3 From 77ea97c6277cc7261e332deec5d48fbddd88821a Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 27 Feb 2010 18:57:46 +0000 Subject: When loading driver, pass the full path to the ioperm.sys file. Subversion-branch: /branches/opl-branch Subversion-revision: 1874 --- opl/ioperm_sys.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/opl/ioperm_sys.c b/opl/ioperm_sys.c index 37512b63..4c06d97a 100644 --- a/opl/ioperm_sys.c +++ b/opl/ioperm_sys.c @@ -93,6 +93,7 @@ int IOperm_EnablePortRange(unsigned int from, unsigned int num, int turn_on) int IOperm_InstallDriver(void) { + wchar_t driver_path[MAX_PATH]; int error; int result = 1; @@ -106,19 +107,25 @@ int IOperm_InstallDriver(void) return 0; } - svc = CreateService(scm, - TEXT("ioperm"), - TEXT("I/O port access driver"), - SERVICE_ALL_ACCESS, - SERVICE_KERNEL_DRIVER, - SERVICE_AUTO_START, - SERVICE_ERROR_NORMAL, - "ioperm.sys", - NULL, - NULL, - NULL, - NULL, - NULL); + // Get the full path to the driver file. + + GetFullPathNameW(L"ioperm.sys", MAX_PATH, driver_path, NULL); + + // Create the service. + + svc = CreateServiceW(scm, + L"ioperm", + L"ioperm support for Cygwin driver", + SERVICE_ALL_ACCESS, + SERVICE_KERNEL_DRIVER, + SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, + driver_path, + NULL, + NULL, + NULL, + NULL, + NULL); if (svc == NULL) { @@ -229,6 +236,10 @@ int IOperm_UninstallDriver(void) result = 0; } + else + { + printf("IOperm_InstallDriver: ioperm driver uninstalled.\n"); + } } // Close handles. -- cgit v1.2.3 From 92c9d5c388ab91ade416539438fb0b8da9cd50bb Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 27 Feb 2010 19:11:24 +0000 Subject: Use wide-character versions of Win32 API functions. Clean up properly if it was not possible to start the ioperm service. Subversion-branch: /branches/opl-branch Subversion-revision: 1875 --- opl/ioperm_sys.c | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/opl/ioperm_sys.c b/opl/ioperm_sys.c index 4c06d97a..0e1ecfd5 100644 --- a/opl/ioperm_sys.c +++ b/opl/ioperm_sys.c @@ -35,7 +35,9 @@ #include -#define IOPERM_FILE "\\\\.\\ioperm" +#include "ioperm_sys.h" + +#define IOPERM_FILE L"\\\\.\\ioperm" #define IOCTL_IOPERM \ CTL_CODE(FILE_DEVICE_UNKNOWN, 0xA00, METHOD_BUFFERED, FILE_ANY_ACCESS) @@ -59,8 +61,8 @@ int IOperm_EnablePortRange(unsigned int from, unsigned int num, int turn_on) DWORD BytesReturned; BOOL r; - h = CreateFile(IOPERM_FILE, GENERIC_READ, 0, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + h = CreateFileW(IOPERM_FILE, GENERIC_READ, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (h == INVALID_HANDLE_VALUE) { @@ -97,12 +99,12 @@ int IOperm_InstallDriver(void) int error; int result = 1; - scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (scm == NULL) { error = GetLastError(); - fprintf(stderr, "IOperm_InstallDriver: OpenSCManager failed (%i)\n", + fprintf(stderr, "IOperm_InstallDriver: OpenSCManager failed (%i).\n", error); return 0; } @@ -134,19 +136,19 @@ int IOperm_InstallDriver(void) if (error != ERROR_SERVICE_EXISTS) { fprintf(stderr, - "IOperm_InstallDriver: Failed to create service (%i)\n", + "IOperm_InstallDriver: Failed to create service (%i).\n", error); } else { - svc = OpenService(scm, TEXT("ioperm"), SERVICE_ALL_ACCESS); + svc = OpenServiceW(scm, L"ioperm", SERVICE_ALL_ACCESS); if (svc == NULL) { error = GetLastError(); fprintf(stderr, - "IOperm_InstallDriver: Failed to open service (%i)\n", + "IOperm_InstallDriver: Failed to open service (%i).\n", error); } } @@ -162,31 +164,37 @@ int IOperm_InstallDriver(void) service_was_created = 1; } - if (!StartService(svc, 0, NULL)) + // Start the service. If the service already existed, it might have + // already been running as well. + + if (!StartServiceW(svc, 0, NULL)) { error = GetLastError(); if (error != ERROR_SERVICE_ALREADY_RUNNING) { - fprintf(stderr, "IOperm_InstallDriver: Failed to start service (%i)\n", + fprintf(stderr, "IOperm_InstallDriver: Failed to start service (%i).\n", error); + result = 0; } else { - printf("IOperm_InstallDriver: ioperm driver already running\n"); + printf("IOperm_InstallDriver: ioperm driver already running.\n"); } } else { - printf("IOperm_InstallDriver: ioperm driver installed\n"); + printf("IOperm_InstallDriver: ioperm driver installed.\n"); service_was_started = 1; } + // If we failed to start the driver running, we need to clean up + // before finishing. + if (result == 0) { - CloseServiceHandle(svc); - CloseServiceHandle(scm); + IOperm_UninstallDriver(); } return result; @@ -215,7 +223,7 @@ int IOperm_UninstallDriver(void) else { fprintf(stderr, - "IOperm_UninstallDriver: Failed to stop service (%i)\n", + "IOperm_UninstallDriver: Failed to stop service (%i).\n", error); result = 0; } @@ -231,14 +239,14 @@ int IOperm_UninstallDriver(void) error = GetLastError(); fprintf(stderr, - "IOperm_UninstallDriver: DeleteService failed (%i)\n", + "IOperm_UninstallDriver: DeleteService failed (%i).\n", error); result = 0; } - else + else if (service_was_started) { - printf("IOperm_InstallDriver: ioperm driver uninstalled.\n"); + printf("IOperm_UnInstallDriver: ioperm driver uninstalled.\n"); } } -- cgit v1.2.3 From 662ad8cbcdc389b41693fbe2fcf567275d3408e7 Mon Sep 17 00:00:00 2001 From: Russell Rice Date: Thu, 4 Mar 2010 01:36:32 +0000 Subject: - Fix codeblocks project build Subversion-branch: /branches/opl-branch Subversion-revision: 1876 --- codeblocks/game.cbp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/codeblocks/game.cbp b/codeblocks/game.cbp index 374936dc..d6313bdf 100644 --- a/codeblocks/game.cbp +++ b/codeblocks/game.cbp @@ -60,6 +60,10 @@ + + + @@ -79,7 +83,7 @@ - + -- cgit v1.2.3 From b9e18229624500d6d2a6112a5c00882d7b7051de Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Mon, 8 Mar 2010 00:51:00 +0000 Subject: Load advapi32.dll pointers dynamically at runtime. This should fix any potential problems with that library not existing on Win9x. Subversion-branch: /branches/opl-branch Subversion-revision: 1877 --- opl/ioperm_sys.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 108 insertions(+), 21 deletions(-) diff --git a/opl/ioperm_sys.c b/opl/ioperm_sys.c index 0e1ecfd5..8f50bcd3 100644 --- a/opl/ioperm_sys.c +++ b/opl/ioperm_sys.c @@ -49,11 +49,93 @@ struct ioperm_data int turn_on; }; +// Function pointers for advapi32.dll. This DLL does not exist on +// Windows 9x, so they are dynamically loaded from the DLL at runtime. + +static SC_HANDLE WINAPI (*MyOpenSCManagerW)(wchar_t *lpMachineName, + wchar_t *lpDatabaseName, + DWORD dwDesiredAccess) = NULL; +static SC_HANDLE WINAPI (*MyCreateServiceW)(SC_HANDLE hSCManager, + wchar_t *lpServiceName, + wchar_t *lpDisplayName, + DWORD dwDesiredAccess, + DWORD dwServiceType, + DWORD dwStartType, + DWORD dwErrorControl, + wchar_t *lpBinaryPathName, + wchar_t *lpLoadOrderGroup, + LPDWORD lpdwTagId, + wchar_t *lpDependencies, + wchar_t *lpServiceStartName, + wchar_t *lpPassword); +static SC_HANDLE WINAPI (*MyOpenServiceW)(SC_HANDLE hSCManager, + wchar_t *lpServiceName, + DWORD dwDesiredAccess); +static BOOL WINAPI (*MyStartServiceW)(SC_HANDLE hService, + DWORD dwNumServiceArgs, + wchar_t **lpServiceArgVectors); +static BOOL WINAPI (*MyControlService)(SC_HANDLE hService, + DWORD dwControl, + LPSERVICE_STATUS lpServiceStatus); +static BOOL WINAPI (*MyCloseServiceHandle)(SC_HANDLE hSCObject); +static BOOL WINAPI (*MyDeleteService)(SC_HANDLE hService); + +static struct +{ + char *name; + void **fn; +} dll_functions[] = { + { "OpenSCManagerW", (void **) &MyOpenSCManagerW }, + { "CreateServiceW", (void **) &MyCreateServiceW }, + { "OpenServiceW", (void **) &MyOpenServiceW }, + { "StartServiceW", (void **) &MyStartServiceW }, + { "ControlService", (void **) &MyControlService }, + { "CloseServiceHandle", (void **) &MyCloseServiceHandle }, + { "DeleteService", (void **) &MyDeleteService }, +}; + +// Globals + static SC_HANDLE scm = NULL; static SC_HANDLE svc = NULL; static int service_was_created = 0; static int service_was_started = 0; +static int LoadLibraryPointers(void) +{ + HMODULE dll; + int i; + + // Already loaded? + + if (MyOpenSCManagerW != NULL) + { + return 1; + } + + dll = LoadLibraryW(L"advapi32.dll"); + + if (dll == NULL) + { + fprintf(stderr, "LoadLibraryPointers: Failed to open advapi32.dll\n"); + return 0; + } + + for (i = 0; i < sizeof(dll_functions) / sizeof(*dll_functions); ++i) + { + *dll_functions[i].fn = GetProcAddress(dll, dll_functions[i].name); + + if (*dll_functions[i].fn == NULL) + { + fprintf(stderr, "LoadLibraryPointers: Failed to get address " + "for '%s'\n", dll_functions[i].name); + return 0; + } + } + + return 1; +} + int IOperm_EnablePortRange(unsigned int from, unsigned int num, int turn_on) { HANDLE h; @@ -99,7 +181,12 @@ int IOperm_InstallDriver(void) int error; int result = 1; - scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (!LoadLibraryPointers()) + { + return 0; + } + + scm = MyOpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (scm == NULL) { @@ -115,19 +202,19 @@ int IOperm_InstallDriver(void) // Create the service. - svc = CreateServiceW(scm, - L"ioperm", - L"ioperm support for Cygwin driver", - SERVICE_ALL_ACCESS, - SERVICE_KERNEL_DRIVER, - SERVICE_AUTO_START, - SERVICE_ERROR_NORMAL, - driver_path, - NULL, - NULL, - NULL, - NULL, - NULL); + svc = MyCreateServiceW(scm, + L"ioperm", + L"ioperm support for Cygwin driver", + SERVICE_ALL_ACCESS, + SERVICE_KERNEL_DRIVER, + SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, + driver_path, + NULL, + NULL, + NULL, + NULL, + NULL); if (svc == NULL) { @@ -141,7 +228,7 @@ int IOperm_InstallDriver(void) } else { - svc = OpenServiceW(scm, L"ioperm", SERVICE_ALL_ACCESS); + svc = MyOpenServiceW(scm, L"ioperm", SERVICE_ALL_ACCESS); if (svc == NULL) { @@ -155,7 +242,7 @@ int IOperm_InstallDriver(void) if (svc == NULL) { - CloseServiceHandle(scm); + MyCloseServiceHandle(scm); return 0; } } @@ -167,7 +254,7 @@ int IOperm_InstallDriver(void) // Start the service. If the service already existed, it might have // already been running as well. - if (!StartServiceW(svc, 0, NULL)) + if (!MyStartServiceW(svc, 0, NULL)) { error = GetLastError(); @@ -210,7 +297,7 @@ int IOperm_UninstallDriver(void) if (service_was_started) { - if (!ControlService(svc, SERVICE_CONTROL_STOP, &stat)) + if (!MyControlService(svc, SERVICE_CONTROL_STOP, &stat)) { error = GetLastError(); @@ -234,7 +321,7 @@ int IOperm_UninstallDriver(void) if (service_was_created) { - if (!DeleteService(svc)) + if (!MyDeleteService(svc)) { error = GetLastError(); @@ -254,13 +341,13 @@ int IOperm_UninstallDriver(void) if (svc != NULL) { - CloseServiceHandle(svc); + MyCloseServiceHandle(svc); svc = NULL; } if (scm != NULL) { - CloseServiceHandle(scm); + MyCloseServiceHandle(scm); scm = NULL; } -- cgit v1.2.3 From 2f3b2889abdcb219ae0d27e14f233ca76bf42dec Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Mon, 8 Mar 2010 18:50:29 +0000 Subject: Use native MIDI music by default. Subversion-branch: /trunk/chocolate-doom Subversion-revision: 1879 --- setup/sound.c | 2 +- src/s_sound.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup/sound.c b/setup/sound.c index 0ca95b08..f7a9c6d4 100644 --- a/setup/sound.c +++ b/setup/sound.c @@ -61,7 +61,7 @@ int snd_sfxdevice = SNDDEVICE_SB; int numChannels = 8; int sfxVolume = 15; -int snd_musicdevice = SNDDEVICE_SB; +int snd_musicdevice = SNDDEVICE_GENMIDI; int musicVolume = 15; int snd_samplerate = 22050; diff --git a/src/s_sound.c b/src/s_sound.c index 6c18f65e..7ae7559a 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -120,7 +120,7 @@ static musicinfo_t *mus_playing = NULL; int numChannels = 8; -int snd_musicdevice = SNDDEVICE_SB; +int snd_musicdevice = SNDDEVICE_GENMIDI; int snd_sfxdevice = SNDDEVICE_SB; // Sound modules -- cgit v1.2.3 From 695b64ce292e94a808797bfcaf2ed3f9823ad577 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Mon, 8 Mar 2010 18:52:59 +0000 Subject: Add OPL-TODO to dist, set svn:ignore properties. Subversion-branch: /trunk/chocolate-doom Subversion-revision: 1880 --- Makefile.am | 1 + opl/.gitignore | 2 ++ opl/examples/.gitignore | 4 +++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 13e8bddb..66cf0983 100644 --- a/Makefile.am +++ b/Makefile.am @@ -43,6 +43,7 @@ EXTRA_DIST= \ CMDLINE \ HACKING \ README.OPL \ + OPL-TODO \ TODO \ BUGS \ rpm.spec diff --git a/opl/.gitignore b/opl/.gitignore index e64379dc..8aac4e77 100644 --- a/opl/.gitignore +++ b/opl/.gitignore @@ -3,3 +3,5 @@ Makefile .deps libopl.a *.rc +tags +TAGS diff --git a/opl/examples/.gitignore b/opl/examples/.gitignore index 49bb1af8..4d589c73 100644 --- a/opl/examples/.gitignore +++ b/opl/examples/.gitignore @@ -2,4 +2,6 @@ Makefile.in Makefile .deps droplay - +*.exe +tags +TAGS -- cgit v1.2.3 From d76bed743c72bfccf5cbbc18dcd9cdb1e75935b3 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 18 Apr 2010 14:30:06 +0000 Subject: Add some DEH_String() calls where appropriate. Subversion-branch: /branches/raven-branch Subversion-revision: 1894 --- src/heretic/in_lude.c | 6 +++--- src/heretic/mn_menu.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/heretic/in_lude.c b/src/heretic/in_lude.c index 084cd5c3..3c382814 100644 --- a/src/heretic/in_lude.c +++ b/src/heretic/in_lude.c @@ -850,7 +850,7 @@ void IN_DrawDMStats(void) MN_DrTextA(DEH_String("VICTIMS"), 140, 8); for (i = 0; i < 7; i++) { - MN_DrTextA(KillersText[i], 10, 80 + 9 * i); + MN_DrTextA(DEH_String(KillersText[i]), 10, 80 + 9 * i); } if (intertime < 20) { @@ -941,7 +941,7 @@ void IN_DrawTime(int x, int y, int h, int m, int s) if (h) { IN_DrawNumber(h, x, y, 2); - IN_DrTextB(":", x + 26, y); + IN_DrTextB(DEH_String(":"), x + 26, y); } x += 34; if (m || h) @@ -951,7 +951,7 @@ void IN_DrawTime(int x, int y, int h, int m, int s) x += 34; if (s) { - IN_DrTextB(":", x - 8, y); + IN_DrTextB(DEH_String(":"), x - 8, y); IN_DrawNumber(s, x, y, 2); } } diff --git a/src/heretic/mn_menu.c b/src/heretic/mn_menu.c index 2b380234..955e0124 100644 --- a/src/heretic/mn_menu.c +++ b/src/heretic/mn_menu.c @@ -492,14 +492,14 @@ void MN_Drawer(void) { MN_DrTextA(SlotText[quicksave - 1], 160 - MN_TextAWidth(SlotText[quicksave - 1]) / 2, 90); - MN_DrTextA("?", 160 + + MN_DrTextA(DEH_String("?"), 160 + MN_TextAWidth(SlotText[quicksave - 1]) / 2, 90); } if (typeofask == 4) { MN_DrTextA(SlotText[quickload - 1], 160 - MN_TextAWidth(SlotText[quickload - 1]) / 2, 90); - MN_DrTextA("?", 160 + + MN_DrTextA(DEH_String("?"), 160 + MN_TextAWidth(SlotText[quickload - 1]) / 2, 90); } UpdateState |= I_FULLSCRN; @@ -706,11 +706,11 @@ static void DrawOptionsMenu(void) { if (messageson) { - MN_DrTextB("ON", 196, 50); + MN_DrTextB(DEH_String("ON"), 196, 50); } else { - MN_DrTextB("OFF", 196, 50); + MN_DrTextB(DEH_String("OFF"), 196, 50); } DrawSlider(&OptionsMenu, 3, 10, mouseSensitivity); } -- cgit v1.2.3 From 53ba7baf5191bb16ccfaa2aafd81f11cb210ecca Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 18 Apr 2010 14:51:28 +0000 Subject: Add deh_hhe_version variable to specify version of executable used to generate HHE patch. Refine DEH_MapHereticFrameNumber based on patch version. Subversion-branch: /branches/raven-branch Subversion-revision: 1895 --- src/heretic/deh_htic.c | 38 +++++++++++++++++++++++++++++--------- src/heretic/deh_htic.h | 13 +++++++++++++ 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/heretic/deh_htic.c b/src/heretic/deh_htic.c index 59c426ef..ff5d02ac 100644 --- a/src/heretic/deh_htic.c +++ b/src/heretic/deh_htic.c @@ -27,6 +27,7 @@ #include #include "deh_defs.h" #include "deh_main.h" +#include "deh_htic.h" #include "info.h" char *deh_signatures[] = @@ -36,6 +37,10 @@ char *deh_signatures[] = NULL }; +// Version number for patches. + +deh_hhe_version_t deh_hhe_version = deh_hhe_1_0; + // deh_ammo.c: extern deh_section_t deh_section_ammo; // deh_frame.c: @@ -67,18 +72,33 @@ deh_section_t *deh_section_types[] = NULL }; -// HHE only worked with Heretic 1.0 and unfortunately was never updated -// to support Heretic 1.3. Between Heretic 1.0 and 1.3, two new frames -// were added to the "states" table, to extend the "flame death" -// animation displayed when the player is killed by fire. Therefore, -// we must map the HHE frame numbers (which assume a Heretic 1.0 frame -// table) to corresponding indexes for the Heretic 1.3 frame table. - int DEH_MapHereticFrameNumber(int frame) { - if (frame >= S_PLAY_FDTH19) + if (deh_hhe_version < deh_hhe_1_0) { - frame = (frame - S_PLAY_FDTH19) + S_BLOODYSKULL1; + // Between Heretic 1.0 and 1.2, two new frames + // were added to the "states" table, to extend the "flame death" + // animation displayed when the player is killed by fire. Therefore, + // we must map Heretic 1.0 frame numbers to corresponding indexes + // for our state table. + + if (frame >= S_PLAY_FDTH19) + { + frame = (frame - S_PLAY_FDTH19) + S_BLOODYSKULL1; + } + } + else + { + // After Heretic 1.2, three unused frames were removed from the + // states table, unused phoenix rod frames. Our state table includes + // these missing states for backwards compatibility. We must therefore + // adjust frame numbers for v1.2/v1.3 to corresponding indexes for + // our state table. + + if (frame >= S_PHOENIXFXIX_1) + { + frame = (frame - S_PHOENIXFXIX_1) + S_PHOENIXPUFF1; + } } return frame; diff --git a/src/heretic/deh_htic.h b/src/heretic/deh_htic.h index fefcf818..199ad2fe 100644 --- a/src/heretic/deh_htic.h +++ b/src/heretic/deh_htic.h @@ -29,6 +29,17 @@ #include "info.h" +// HHE executable version. Loading HHE patches is (unfortunately) +// dependent on the version of the Heretic executable used to make them. + +typedef enum +{ + deh_hhe_1_0, + deh_hhe_1_2, + deh_hhe_1_3, + deh_hhe_num_versions +} deh_hhe_version_t; + // HHE doesn't know about the last two states in the state table, so // these are considered invalid. @@ -41,5 +52,7 @@ int DEH_MapHereticFrameNumber(int frame); +extern deh_hhe_version_t deh_hhe_version; + #endif /* #ifndef DEH_HTIC_H */ -- cgit v1.2.3 From bc4a39b2fe3f0883a7826fa998de6ea9825efada Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 18 Apr 2010 14:52:43 +0000 Subject: Add new action pointer lookup table, and find offset based on patch executable version. Subversion-branch: /branches/raven-branch Subversion-revision: 1897 --- src/heretic/deh_frame.c | 270 ++++++++++++++++++++++++------------------------ 1 file changed, 136 insertions(+), 134 deletions(-) diff --git a/src/heretic/deh_frame.c b/src/heretic/deh_frame.c index 6ec62ae7..8d9a8fdf 100644 --- a/src/heretic/deh_frame.c +++ b/src/heretic/deh_frame.c @@ -38,141 +38,143 @@ #include "p_action.h" +typedef struct +{ + int offsets[deh_hhe_num_versions]; + void (*func)(); +} hhe_action_pointer_t; + // Offsets of action pointers within the Heretic 1.0 executable. // (Seriously Greg, was this really necessary? What was wrong with the // "copying action pointer from another frame" technique used in dehacked?) -static const struct -{ - int offset; - void (*func)(); -} action_ptrs[] = { - { 0, NULL }, - { 69200, A_Look }, - { 69328, A_Chase }, - { 69872, A_FaceTarget }, - { 69984, A_Pain }, - { 70016, A_DripBlood }, - { 70160, A_KnightAttack }, - { 70304, A_ImpExplode }, - { 70480, A_BeastPuff }, - { 70592, A_ImpMeAttack }, - { 70672, A_ImpMsAttack }, - { 70880, A_ImpMsAttack2 }, - { 70976, A_ImpDeath }, - { 71024, A_ImpXDeath1 }, - { 71072, A_ImpXDeath2 }, - { 71376, A_ChicAttack }, - { 71456, A_ChicLook }, - { 71488, A_ChicChase }, - { 71520, A_ChicPain }, - { 71568, A_Feathers }, - { 71808, A_MummyAttack }, - { 71920, A_MummyAttack2 }, - { 72016, A_MummyFX1Seek }, - { 72048, A_MummySoul }, - { 72096, A_Sor1Pain }, - { 72144, A_Sor1Chase }, - { 72192, A_Srcr1Attack }, - { 72480, A_SorcererRise }, - { 72816, A_Srcr2Decide }, - { 72896, A_Srcr2Attack }, - { 73120, A_BlueSpark }, - { 73232, A_GenWizard }, - { 73392, A_Sor2DthInit }, - { 73424, A_Sor2DthLoop }, - { 73456, A_SorZap }, - { 73488, A_SorRise }, - { 73520, A_SorDSph }, - { 73552, A_SorDExp }, - { 73584, A_SorDBon }, - { 73616, A_SorSightSnd }, - { 73648, A_MinotaurAtk1 }, - { 73760, A_MinotaurDecide }, - { 74032, A_MinotaurCharge }, - { 74112, A_MinotaurAtk2 }, - { 74352, A_MinotaurAtk3 }, - { 74528, A_MntrFloorFire }, - { 74640, A_BeastAttack }, - { 74752, A_HeadAttack }, - { 75168, A_WhirlwindSeek }, - { 75328, A_HeadIceImpact }, - { 75488, A_HeadFireGrow }, - { 75632, A_SnakeAttack }, - { 75712, A_SnakeAttack2 }, - { 75792, A_ClinkAttack }, - { 75872, A_GhostOff }, - { 75888, A_WizAtk1 }, - { 75920, A_WizAtk2 }, - { 75952, A_WizAtk3 }, - { 76144, A_Scream }, - { 76400, A_NoBlocking }, - { 76784, A_Explode }, - { 76896, A_PodPain }, - { 77056, A_RemovePod }, - { 77104, A_MakePod }, - { 77344, A_BossDeath }, - { 77472, A_ESound }, - { 77520, A_SpawnTeleGlitter }, - { 77600, A_SpawnTeleGlitter2 }, - { 77680, A_AccTeleGlitter }, - { 77728, A_InitKeyGizmo }, - { 77824, A_VolcanoSet }, - { 77856, A_VolcanoBlast }, - { 78080, A_VolcBallImpact }, - { 78288, A_SkullPop }, - { 78448, A_CheckSkullFloor }, - { 78480, A_CheckSkullDone }, - { 78512, A_FreeTargMobj }, - { 78608, A_AddPlayerCorpse }, - { 78704, A_FlameSnd }, - { 78736, A_HideThing }, - { 78752, A_UnHideThing }, - { 81952, A_RestoreArtifact }, - { 82048, A_RestoreSpecialThing1 }, - { 82128, A_RestoreSpecialThing2 }, - { 108432, A_ContMobjSound }, - { 111168, A_WeaponReady }, - { 111568, A_BeakReady }, - { 111696, A_ReFire }, - { 111760, A_Lower }, - { 111856, A_BeakRaise }, - { 111920, A_Raise }, - { 112272, A_BeakAttackPL1 }, - { 112448, A_BeakAttackPL2 }, - { 112640, A_StaffAttackPL1 }, - { 112784, A_StaffAttackPL2 }, - { 112928, A_FireBlasterPL1 }, - { 113072, A_FireBlasterPL2 }, - { 113152, A_FireGoldWandPL1 }, - { 113296, A_FireGoldWandPL2 }, - { 113760, A_FireMacePL1 }, - { 113904, A_MacePL1Check }, - { 114032, A_MaceBallImpact }, - { 114192, A_MaceBallImpact2 }, - { 114624, A_FireMacePL2 }, - { 114752, A_DeathBallImpact }, - { 115088, A_SpawnRippers }, - { 115232, A_FireCrossbowPL1 }, - { 115312, A_FireCrossbowPL2 }, - { 115456, A_BoltSpark }, - { 115568, A_FireSkullRodPL1 }, - { 115648, A_FireSkullRodPL2 }, - { 115776, A_SkullRodPL2Seek }, - { 115808, A_AddPlayerRain }, - { 115984, A_SkullRodStorm }, - { 116272, A_RainImpact }, - { 116336, A_HideInCeiling }, - { 116368, A_FirePhoenixPL1 }, - { 116480, A_RemovedPhoenixFunc }, - { 116496, A_PhoenixPuff }, - { 116720, A_InitPhoenixPL2 }, - { 116736, A_FirePhoenixPL2 }, - { 117104, A_ShutdownPhoenixPL2 }, - { 117120, A_FlameEnd }, - { 117152, A_FloatPuff }, - { 117184, A_GauntletAttack }, - { 117648, A_Light0 } +static hhe_action_pointer_t action_pointers[] = { + { { 77680, 80144, 80208 }, A_AccTeleGlitter }, + { { 78608, 81104, 81168 }, A_AddPlayerCorpse }, + { { 115808, 118000, 118240 }, A_AddPlayerRain }, + { { 112272, 114480, 114720 }, A_BeakAttackPL1 }, + { { 112448, 114656, 114896 }, A_BeakAttackPL2 }, + { { 111856, 114176, 114416 }, A_BeakRaise }, + { { 111568, 113888, 114128 }, A_BeakReady }, + { { 74640, 77120, 77184 }, A_BeastAttack }, + { { 70480, 72992, 73056 }, A_BeastPuff }, + { { 73120, 75600, 75664 }, A_BlueSpark }, + { { 115456, 117648, 117888 }, A_BoltSpark }, + { { 77344, 79808, 79872 }, A_BossDeath }, + { { 69328, 71856, 71920 }, A_Chase }, + { { 0, 80976, 81040 }, A_CheckBurnGone }, + { { 78480, 80944, 81008 }, A_CheckSkullDone }, + { { 78448, 80912, 80976 }, A_CheckSkullFloor }, + { { 71376, 73888, 73952 }, A_ChicAttack }, + { { 71488, 74000, 74064 }, A_ChicChase }, + { { 71456, 73968, 74032 }, A_ChicLook }, + { { 71520, 74032, 74096 }, A_ChicPain }, + { { 75792, 78208, 78272 }, A_ClinkAttack }, + { { 108432, 110816, 111056 }, A_ContMobjSound }, + { { 114752, 116944, 117184 }, A_DeathBallImpact }, + { { 70016, 72528, 72592 }, A_DripBlood }, + { { 77472, 79936, 80000 }, A_ESound }, + { { 76784, 79248, 79312 }, A_Explode }, + { { 69872, 72400, 72464 }, A_FaceTarget }, + { { 71568, 74080, 74144 }, A_Feathers }, + { { 112928, 115136, 115376 }, A_FireBlasterPL1 }, + { { 113072, 115280, 115520 }, A_FireBlasterPL2 }, + { { 115232, 117424, 117664 }, A_FireCrossbowPL1 }, + { { 115312, 117504, 117744 }, A_FireCrossbowPL2 }, + { { 113152, 115360, 115600 }, A_FireGoldWandPL1 }, + { { 113296, 115504, 115744 }, A_FireGoldWandPL2 }, + { { 113760, 115968, 116208 }, A_FireMacePL1 }, + { { 114624, 116816, 117056 }, A_FireMacePL2 }, + { { 116368, 118544, 118784 }, A_FirePhoenixPL1 }, + { { 116736, 118896, 119136 }, A_FirePhoenixPL2 }, + { { 115568, 117760, 118000 }, A_FireSkullRodPL1 }, + { { 115648, 117840, 118080 }, A_FireSkullRodPL2 }, + { { 117120, 119280, 119520 }, A_FlameEnd }, + { { 78704, 81200, 81264 }, A_FlameSnd }, + { { 117152, 119312, 119552 }, A_FloatPuff }, + { { 78512, 81008, 81072 }, A_FreeTargMobj }, + { { 117184, 119344, 119584 }, A_GauntletAttack }, + { { 73232, 75712, 75776 }, A_GenWizard }, + { { 75872, 78304, 78368 }, A_GhostOff }, + { { 74752, 77232, 77296 }, A_HeadAttack }, + { { 75488, 77984, 78048 }, A_HeadFireGrow }, + { { 75328, 77824, 77888 }, A_HeadIceImpact }, + { { 116336, 118512, 118752 }, A_HideInCeiling }, + { { 78736, 81232, 81296 }, A_HideThing }, + { { 70976, 73488, 73552 }, A_ImpDeath }, + { { 70304, 72816, 72880 }, A_ImpExplode }, + { { 70592, 73104, 73168 }, A_ImpMeAttack }, + { { 70672, 73184, 73248 }, A_ImpMsAttack }, + { { 70880, 73392, 73456 }, A_ImpMsAttack2 }, + { { 71024, 73536, 73600 }, A_ImpXDeath1 }, + { { 71072, 73584, 73648 }, A_ImpXDeath2 }, + { { 77728, 80192, 80256 }, A_InitKeyGizmo }, + { { 116720, 118880, 119120 }, A_InitPhoenixPL2 }, + { { 70160, 72672, 72736 }, A_KnightAttack }, + { { 117648, 119824, 120064 }, A_Light0 }, + { { 69200, 71728, 71792 }, A_Look }, + { { 111760, 114080, 114320 }, A_Lower }, + { { 114032, 116224, 116464 }, A_MaceBallImpact }, + { { 114192, 116384, 116624 }, A_MaceBallImpact2 }, + { { 113904, 116112, 116352 }, A_MacePL1Check }, + { { 77104, 79568, 79632 }, A_MakePod }, + { { 73648, 76128, 76192 }, A_MinotaurAtk1 }, + { { 74112, 76592, 76656 }, A_MinotaurAtk2 }, + { { 74352, 76832, 76896 }, A_MinotaurAtk3 }, + { { 74032, 76512, 76576 }, A_MinotaurCharge }, + { { 73760, 76240, 76304 }, A_MinotaurDecide }, + { { 74528, 77008, 77072 }, A_MntrFloorFire }, + { { 71808, 74288, 74352 }, A_MummyAttack }, + { { 71920, 74400, 74464 }, A_MummyAttack2 }, + { { 72016, 74496, 74560 }, A_MummyFX1Seek }, + { { 72048, 74528, 74592 }, A_MummySoul }, + { { 76400, 78832, 78896 }, A_NoBlocking }, + { { 69984, 72496, 72560 }, A_Pain }, + { { 116496, 118656, 118896 }, A_PhoenixPuff }, + { { 76896, 79360, 79424 }, A_PodPain }, + { { 116272, 118448, 118688 }, A_RainImpact }, + { { 111920, 114240, 114480 }, A_Raise }, + { { 111696, 114016, 114256 }, A_ReFire }, + { { 77056, 79520, 79584 }, A_RemovePod }, + { { 116480, 0, 0 }, A_RemovedPhoenixFunc }, + { { 81952, 84464, 84528 }, A_RestoreArtifact }, + { { 82048, 84544, 84608 }, A_RestoreSpecialThing1 }, + { { 82128, 84592, 84656 }, A_RestoreSpecialThing2 }, + { { 76144, 78576, 78640 }, A_Scream }, + { { 117104, 119264, 119504 }, A_ShutdownPhoenixPL2 }, + { { 78288, 80752, 80816 }, A_SkullPop }, + { { 115776, 117968, 118208 }, A_SkullRodPL2Seek }, + { { 115984, 118176, 118416 }, A_SkullRodStorm }, + { { 75632, 78048, 78112 }, A_SnakeAttack }, + { { 75712, 78128, 78192 }, A_SnakeAttack2 }, + { { 72144, 74624, 74688 }, A_Sor1Chase }, + { { 72096, 74576, 74640 }, A_Sor1Pain }, + { { 73392, 75872, 75936 }, A_Sor2DthInit }, + { { 73424, 75904, 75968 }, A_Sor2DthLoop }, + { { 73584, 76064, 76128 }, A_SorDBon }, + { { 73552, 76032, 76096 }, A_SorDExp }, + { { 73520, 76000, 76064 }, A_SorDSph }, + { { 73488, 75968, 76032 }, A_SorRise }, + { { 73616, 76096, 76160 }, A_SorSightSnd }, + { { 73456, 75936, 76000 }, A_SorZap }, + { { 72480, 74960, 75024 }, A_SorcererRise }, + { { 115088, 117280, 117520 }, A_SpawnRippers }, + { { 77520, 79984, 80048 }, A_SpawnTeleGlitter }, + { { 77600, 80064, 80128 }, A_SpawnTeleGlitter2 }, + { { 72192, 74672, 74736 }, A_Srcr1Attack }, + { { 72896, 75376, 75440 }, A_Srcr2Attack }, + { { 72816, 75296, 75360 }, A_Srcr2Decide }, + { { 112640, 114848, 115088 }, A_StaffAttackPL1 }, + { { 112784, 114992, 115232 }, A_StaffAttackPL2 }, + { { 78752, 81248, 81312 }, A_UnHideThing }, + { { 78080, 80544, 80608 }, A_VolcBallImpact }, + { { 77856, 80320, 80384 }, A_VolcanoBlast }, + { { 77824, 80288, 80352 }, A_VolcanoSet }, + { { 111168, 113488, 113728 }, A_WeaponReady }, + { { 75168, 77664, 77728 }, A_WhirlwindSeek }, + { { 75888, 78320, 78384 }, A_WizAtk1 }, + { { 75920, 78352, 78416 }, A_WizAtk2 }, + { { 75952, 78384, 78448 }, A_WizAtk3 }, }; DEH_BEGIN_MAPPING(state_mapping, state_t) @@ -229,11 +231,11 @@ static boolean GetActionPointerForOffset(int offset, void **result) { int i; - for (i=0; i= 0; ++i) + { + if ((unsigned int) string_list[i] == offset) + { + return true; + } + } + + return false; +} + static boolean GetStringByOffset(unsigned int offset, char **result) { int i; - for (i=0; ikeys[key_blue]) { - P_SetMessage(player, TXT_NEEDBLUEKEY, false); + P_SetMessage(player, DEH_String(TXT_NEEDBLUEKEY), false); S_StartSound(NULL, sfx_plroof); return; } @@ -245,7 +246,7 @@ void EV_VerticalDoor(line_t * line, mobj_t * thing) } if (!player->keys[key_yellow]) { - P_SetMessage(player, TXT_NEEDYELLOWKEY, false); + P_SetMessage(player, DEH_String(TXT_NEEDYELLOWKEY), false); S_StartSound(NULL, sfx_plroof); return; } @@ -258,7 +259,7 @@ void EV_VerticalDoor(line_t * line, mobj_t * thing) } if (!player->keys[key_green]) { - P_SetMessage(player, TXT_NEEDGREENKEY, false); + P_SetMessage(player, DEH_String(TXT_NEEDGREENKEY), false); S_StartSound(NULL, sfx_plroof); return; } diff --git a/src/heretic/p_inter.c b/src/heretic/p_inter.c index 3587a95d..afdb37f2 100644 --- a/src/heretic/p_inter.c +++ b/src/heretic/p_inter.c @@ -25,6 +25,7 @@ // P_inter.c #include "doomdef.h" +#include "deh_str.h" #include "i_system.h" #include "i_timer.h" #include "m_random.h" @@ -580,21 +581,21 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher) { return; } - P_SetMessage(player, TXT_ITEMHEALTH, false); + P_SetMessage(player, DEH_String(TXT_ITEMHEALTH), false); break; case SPR_SHLD: // Item_Shield1 if (!P_GiveArmor(player, 1)) { return; } - P_SetMessage(player, TXT_ITEMSHIELD1, false); + P_SetMessage(player, DEH_String(TXT_ITEMSHIELD1), false); break; case SPR_SHD2: // Item_Shield2 if (!P_GiveArmor(player, 2)) { return; } - P_SetMessage(player, TXT_ITEMSHIELD2, false); + P_SetMessage(player, DEH_String(TXT_ITEMSHIELD2), false); break; case SPR_BAGH: // Item_BagOfHolding if (!player->backpack) @@ -610,21 +611,21 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher) P_GiveAmmo(player, am_crossbow, AMMO_CBOW_WIMPY); P_GiveAmmo(player, am_skullrod, AMMO_SKRD_WIMPY); P_GiveAmmo(player, am_phoenixrod, AMMO_PHRD_WIMPY); - P_SetMessage(player, TXT_ITEMBAGOFHOLDING, false); + P_SetMessage(player, DEH_String(TXT_ITEMBAGOFHOLDING), false); break; case SPR_SPMP: // Item_SuperMap if (!P_GivePower(player, pw_allmap)) { return; } - P_SetMessage(player, TXT_ITEMSUPERMAP, false); + P_SetMessage(player, DEH_String(TXT_ITEMSUPERMAP), false); break; // Keys case SPR_BKYY: // Key_Blue if (!player->keys[key_blue]) { - P_SetMessage(player, TXT_GOTBLUEKEY, false); + P_SetMessage(player, DEH_String(TXT_GOTBLUEKEY), false); } P_GiveKey(player, key_blue); sound = sfx_keyup; @@ -636,7 +637,7 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher) case SPR_CKYY: // Key_Yellow if (!player->keys[key_yellow]) { - P_SetMessage(player, TXT_GOTYELLOWKEY, false); + P_SetMessage(player, DEH_String(TXT_GOTYELLOWKEY), false); } sound = sfx_keyup; P_GiveKey(player, key_yellow); @@ -648,7 +649,7 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher) case SPR_AKYY: // Key_Green if (!player->keys[key_green]) { - P_SetMessage(player, TXT_GOTGREENKEY, false); + P_SetMessage(player, DEH_String(TXT_GOTGREENKEY), false); } sound = sfx_keyup; P_GiveKey(player, key_green); @@ -662,70 +663,70 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher) case SPR_PTN2: // Arti_HealingPotion if (P_GiveArtifact(player, arti_health, special)) { - P_SetMessage(player, TXT_ARTIHEALTH, false); + P_SetMessage(player, DEH_String(TXT_ARTIHEALTH), false); P_SetDormantArtifact(special); } return; case SPR_SOAR: // Arti_Fly if (P_GiveArtifact(player, arti_fly, special)) { - P_SetMessage(player, TXT_ARTIFLY, false); + P_SetMessage(player, DEH_String(TXT_ARTIFLY), false); P_SetDormantArtifact(special); } return; case SPR_INVU: // Arti_Invulnerability if (P_GiveArtifact(player, arti_invulnerability, special)) { - P_SetMessage(player, TXT_ARTIINVULNERABILITY, false); + P_SetMessage(player, DEH_String(TXT_ARTIINVULNERABILITY), false); P_SetDormantArtifact(special); } return; case SPR_PWBK: // Arti_TomeOfPower if (P_GiveArtifact(player, arti_tomeofpower, special)) { - P_SetMessage(player, TXT_ARTITOMEOFPOWER, false); + P_SetMessage(player, DEH_String(TXT_ARTITOMEOFPOWER), false); P_SetDormantArtifact(special); } return; case SPR_INVS: // Arti_Invisibility if (P_GiveArtifact(player, arti_invisibility, special)) { - P_SetMessage(player, TXT_ARTIINVISIBILITY, false); + P_SetMessage(player, DEH_String(TXT_ARTIINVISIBILITY), false); P_SetDormantArtifact(special); } return; case SPR_EGGC: // Arti_Egg if (P_GiveArtifact(player, arti_egg, special)) { - P_SetMessage(player, TXT_ARTIEGG, false); + P_SetMessage(player, DEH_String(TXT_ARTIEGG), false); P_SetDormantArtifact(special); } return; case SPR_SPHL: // Arti_SuperHealth if (P_GiveArtifact(player, arti_superhealth, special)) { - P_SetMessage(player, TXT_ARTISUPERHEALTH, false); + P_SetMessage(player, DEH_String(TXT_ARTISUPERHEALTH), false); P_SetDormantArtifact(special); } return; case SPR_TRCH: // Arti_Torch if (P_GiveArtifact(player, arti_torch, special)) { - P_SetMessage(player, TXT_ARTITORCH, false); + P_SetMessage(player, DEH_String(TXT_ARTITORCH), false); P_SetDormantArtifact(special); } return; case SPR_FBMB: // Arti_FireBomb if (P_GiveArtifact(player, arti_firebomb, special)) { - P_SetMessage(player, TXT_ARTIFIREBOMB, false); + P_SetMessage(player, DEH_String(TXT_ARTIFIREBOMB), false); P_SetDormantArtifact(special); } return; case SPR_ATLP: // Arti_Teleport if (P_GiveArtifact(player, arti_teleport, special)) { - P_SetMessage(player, TXT_ARTITELEPORT, false); + P_SetMessage(player, DEH_String(TXT_ARTITELEPORT), false); P_SetDormantArtifact(special); } return; @@ -736,84 +737,84 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher) { return; } - P_SetMessage(player, TXT_AMMOGOLDWAND1, false); + P_SetMessage(player, DEH_String(TXT_AMMOGOLDWAND1), false); break; case SPR_AMG2: // Ammo_GoldWandHefty if (!P_GiveAmmo(player, am_goldwand, special->health)) { return; } - P_SetMessage(player, TXT_AMMOGOLDWAND2, false); + P_SetMessage(player, DEH_String(TXT_AMMOGOLDWAND2), false); break; case SPR_AMM1: // Ammo_MaceWimpy if (!P_GiveAmmo(player, am_mace, special->health)) { return; } - P_SetMessage(player, TXT_AMMOMACE1, false); + P_SetMessage(player, DEH_String(TXT_AMMOMACE1), false); break; case SPR_AMM2: // Ammo_MaceHefty if (!P_GiveAmmo(player, am_mace, special->health)) { return; } - P_SetMessage(player, TXT_AMMOMACE2, false); + P_SetMessage(player, DEH_String(TXT_AMMOMACE2), false); break; case SPR_AMC1: // Ammo_CrossbowWimpy if (!P_GiveAmmo(player, am_crossbow, special->health)) { return; } - P_SetMessage(player, TXT_AMMOCROSSBOW1, false); + P_SetMessage(player, DEH_String(TXT_AMMOCROSSBOW1), false); break; case SPR_AMC2: // Ammo_CrossbowHefty if (!P_GiveAmmo(player, am_crossbow, special->health)) { return; } - P_SetMessage(player, TXT_AMMOCROSSBOW2, false); + P_SetMessage(player, DEH_String(TXT_AMMOCROSSBOW2), false); break; case SPR_AMB1: // Ammo_BlasterWimpy if (!P_GiveAmmo(player, am_blaster, special->health)) { return; } - P_SetMessage(player, TXT_AMMOBLASTER1, false); + P_SetMessage(player, DEH_String(TXT_AMMOBLASTER1), false); break; case SPR_AMB2: // Ammo_BlasterHefty if (!P_GiveAmmo(player, am_blaster, special->health)) { return; } - P_SetMessage(player, TXT_AMMOBLASTER2, false); + P_SetMessage(player, DEH_String(TXT_AMMOBLASTER2), false); break; case SPR_AMS1: // Ammo_SkullRodWimpy if (!P_GiveAmmo(player, am_skullrod, special->health)) { return; } - P_SetMessage(player, TXT_AMMOSKULLROD1, false); + P_SetMessage(player, DEH_String(TXT_AMMOSKULLROD1), false); break; case SPR_AMS2: // Ammo_SkullRodHefty if (!P_GiveAmmo(player, am_skullrod, special->health)) { return; } - P_SetMessage(player, TXT_AMMOSKULLROD2, false); + P_SetMessage(player, DEH_String(TXT_AMMOSKULLROD2), false); break; case SPR_AMP1: // Ammo_PhoenixRodWimpy if (!P_GiveAmmo(player, am_phoenixrod, special->health)) { return; } - P_SetMessage(player, TXT_AMMOPHOENIXROD1, false); + P_SetMessage(player, DEH_String(TXT_AMMOPHOENIXROD1), false); break; case SPR_AMP2: // Ammo_PhoenixRodHefty if (!P_GiveAmmo(player, am_phoenixrod, special->health)) { return; } - P_SetMessage(player, TXT_AMMOPHOENIXROD2, false); + P_SetMessage(player, DEH_String(TXT_AMMOPHOENIXROD2), false); break; // Weapons @@ -822,7 +823,7 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher) { return; } - P_SetMessage(player, TXT_WPNMACE, false); + P_SetMessage(player, DEH_String(TXT_WPNMACE), false); sound = sfx_wpnup; break; case SPR_WBOW: // Weapon_Crossbow @@ -830,7 +831,7 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher) { return; } - P_SetMessage(player, TXT_WPNCROSSBOW, false); + P_SetMessage(player, DEH_String(TXT_WPNCROSSBOW), false); sound = sfx_wpnup; break; case SPR_WBLS: // Weapon_Blaster @@ -838,7 +839,7 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher) { return; } - P_SetMessage(player, TXT_WPNBLASTER, false); + P_SetMessage(player, DEH_String(TXT_WPNBLASTER), false); sound = sfx_wpnup; break; case SPR_WSKL: // Weapon_SkullRod @@ -846,7 +847,7 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher) { return; } - P_SetMessage(player, TXT_WPNSKULLROD, false); + P_SetMessage(player, DEH_String(TXT_WPNSKULLROD), false); sound = sfx_wpnup; break; case SPR_WPHX: // Weapon_PhoenixRod @@ -854,7 +855,7 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher) { return; } - P_SetMessage(player, TXT_WPNPHOENIXROD, false); + P_SetMessage(player, DEH_String(TXT_WPNPHOENIXROD), false); sound = sfx_wpnup; break; case SPR_WGNT: // Weapon_Gauntlets @@ -862,7 +863,7 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher) { return; } - P_SetMessage(player, TXT_WPNGAUNTLETS, false); + P_SetMessage(player, DEH_String(TXT_WPNGAUNTLETS), false); sound = sfx_wpnup; break; default: diff --git a/src/heretic/sb_bar.c b/src/heretic/sb_bar.c index 3a90c000..51bd3b9c 100644 --- a/src/heretic/sb_bar.c +++ b/src/heretic/sb_bar.c @@ -1055,11 +1055,11 @@ static void CheatGodFunc(player_t * player, Cheat_t * cheat) player->cheats ^= CF_GODMODE; if (player->cheats & CF_GODMODE) { - P_SetMessage(player, TXT_CHEATGODON, false); + P_SetMessage(player, DEH_String(TXT_CHEATGODON), false); } else { - P_SetMessage(player, TXT_CHEATGODOFF, false); + P_SetMessage(player, DEH_String(TXT_CHEATGODOFF), false); } SB_state = -1; } @@ -1069,11 +1069,11 @@ static void CheatNoClipFunc(player_t * player, Cheat_t * cheat) player->cheats ^= CF_NOCLIP; if (player->cheats & CF_NOCLIP) { - P_SetMessage(player, TXT_CHEATNOCLIPON, false); + P_SetMessage(player, DEH_String(TXT_CHEATNOCLIPON), false); } else { - P_SetMessage(player, TXT_CHEATNOCLIPOFF, false); + P_SetMessage(player, DEH_String(TXT_CHEATNOCLIPOFF), false); } } @@ -1106,7 +1106,7 @@ static void CheatWeaponsFunc(player_t * player, Cheat_t * cheat) { player->ammo[i] = player->maxammo[i]; } - P_SetMessage(player, TXT_CHEATWEAPONS, false); + P_SetMessage(player, DEH_String(TXT_CHEATWEAPONS), false); } static void CheatPowerFunc(player_t * player, Cheat_t * cheat) @@ -1114,12 +1114,12 @@ static void CheatPowerFunc(player_t * player, Cheat_t * cheat) if (player->powers[pw_weaponlevel2]) { player->powers[pw_weaponlevel2] = 0; - P_SetMessage(player, TXT_CHEATPOWEROFF, false); + P_SetMessage(player, DEH_String(TXT_CHEATPOWEROFF), false); } else { P_UseArtifact(player, arti_tomeofpower); - P_SetMessage(player, TXT_CHEATPOWERON, false); + P_SetMessage(player, DEH_String(TXT_CHEATPOWERON), false); } } @@ -1133,7 +1133,7 @@ static void CheatHealthFunc(player_t * player, Cheat_t * cheat) { player->health = player->mo->health = MAXHEALTH; } - P_SetMessage(player, TXT_CHEATHEALTH, false); + P_SetMessage(player, DEH_String(TXT_CHEATHEALTH), false); } static void CheatKeysFunc(player_t * player, Cheat_t * cheat) @@ -1144,7 +1144,7 @@ static void CheatKeysFunc(player_t * player, Cheat_t * cheat) player->keys[key_green] = true; player->keys[key_blue] = true; playerkeys = 7; // Key refresh flags - P_SetMessage(player, TXT_CHEATKEYS, false); + P_SetMessage(player, DEH_String(TXT_CHEATKEYS), false); } static void CheatSoundFunc(player_t * player, Cheat_t * cheat) @@ -1152,11 +1152,11 @@ static void CheatSoundFunc(player_t * player, Cheat_t * cheat) DebugSound = !DebugSound; if (DebugSound) { - P_SetMessage(player, TXT_CHEATSOUNDON, false); + P_SetMessage(player, DEH_String(TXT_CHEATSOUNDON), false); } else { - P_SetMessage(player, TXT_CHEATSOUNDOFF, false); + P_SetMessage(player, DEH_String(TXT_CHEATSOUNDOFF), false); } } @@ -1165,11 +1165,11 @@ static void CheatTickerFunc(player_t * player, Cheat_t * cheat) DisplayTicker = !DisplayTicker; if (DisplayTicker) { - P_SetMessage(player, TXT_CHEATTICKERON, false); + P_SetMessage(player, DEH_String(TXT_CHEATTICKERON), false); } else { - P_SetMessage(player, TXT_CHEATTICKEROFF, false); + P_SetMessage(player, DEH_String(TXT_CHEATTICKEROFF), false); } I_DisplayFPSDots(DisplayTicker); @@ -1177,12 +1177,12 @@ static void CheatTickerFunc(player_t * player, Cheat_t * cheat) static void CheatArtifact1Func(player_t * player, Cheat_t * cheat) { - P_SetMessage(player, TXT_CHEATARTIFACTS1, false); + P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTS1), false); } static void CheatArtifact2Func(player_t * player, Cheat_t * cheat) { - P_SetMessage(player, TXT_CHEATARTIFACTS2, false); + P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTS2), false); } static void CheatArtifact3Func(player_t * player, Cheat_t * cheat) @@ -1210,7 +1210,7 @@ static void CheatArtifact3Func(player_t * player, Cheat_t * cheat) P_GiveArtifact(player, i, NULL); } } - P_SetMessage(player, TXT_CHEATARTIFACTS3, false); + P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTS3), false); } else if (type > arti_none && type < NUMARTIFACTS && count > 0 && count < 10) @@ -1218,18 +1218,18 @@ static void CheatArtifact3Func(player_t * player, Cheat_t * cheat) if (gamemode == shareware && (type == arti_superhealth || type == arti_teleport)) { - P_SetMessage(player, TXT_CHEATARTIFACTSFAIL, false); + P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTSFAIL), false); return; } for (i = 0; i < count; i++) { P_GiveArtifact(player, type, NULL); } - P_SetMessage(player, TXT_CHEATARTIFACTS3, false); + P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTS3), false); } else { // Bad input - P_SetMessage(player, TXT_CHEATARTIFACTSFAIL, false); + P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTSFAIL), false); } } @@ -1246,7 +1246,7 @@ static void CheatWarpFunc(player_t * player, Cheat_t * cheat) if (D_ValidEpisodeMap(gamemission, gamemode, episode, map)) { G_DeferedInitNew(gameskill, episode, map); - P_SetMessage(player, TXT_CHEATWARP, false); + P_SetMessage(player, DEH_String(TXT_CHEATWARP), false); } } @@ -1258,19 +1258,19 @@ static void CheatChickenFunc(player_t * player, Cheat_t * cheat) { if (P_UndoPlayerChicken(player)) { - P_SetMessage(player, TXT_CHEATCHICKENOFF, false); + P_SetMessage(player, DEH_String(TXT_CHEATCHICKENOFF), false); } } else if (P_ChickenMorphPlayer(player)) { - P_SetMessage(player, TXT_CHEATCHICKENON, false); + P_SetMessage(player, DEH_String(TXT_CHEATCHICKENON), false); } } static void CheatMassacreFunc(player_t * player, Cheat_t * cheat) { P_Massacre(); - P_SetMessage(player, TXT_CHEATMASSACRE, false); + P_SetMessage(player, DEH_String(TXT_CHEATMASSACRE), false); } static void CheatIDKFAFunc(player_t * player, Cheat_t * cheat) @@ -1285,11 +1285,11 @@ static void CheatIDKFAFunc(player_t * player, Cheat_t * cheat) player->weaponowned[i] = false; } player->pendingweapon = wp_staff; - P_SetMessage(player, TXT_CHEATIDKFA, true); + P_SetMessage(player, DEH_String(TXT_CHEATIDKFA), true); } static void CheatIDDQDFunc(player_t * player, Cheat_t * cheat) { P_DamageMobj(player->mo, NULL, player->mo, 10000); - P_SetMessage(player, TXT_CHEATIDDQD, true); + P_SetMessage(player, DEH_String(TXT_CHEATIDDQD), true); } -- cgit v1.2.3 From 8069d8a0fdf30516cb6eff6d6a9e2990061f95c1 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 18 Apr 2010 16:40:14 +0000 Subject: Add -hhever command line parameter to select patch version number. Subversion-branch: /branches/raven-branch Subversion-revision: 1902 --- src/heretic/deh_frame.c | 11 ++------ src/heretic/deh_htic.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++- src/heretic/deh_htic.h | 1 + 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/src/heretic/deh_frame.c b/src/heretic/deh_frame.c index 8d9a8fdf..6d7f6c3b 100644 --- a/src/heretic/deh_frame.c +++ b/src/heretic/deh_frame.c @@ -186,17 +186,10 @@ DEH_BEGIN_MAPPING(state_mapping, state_t) DEH_MAPPING("Unknown 2", misc2) DEH_END_MAPPING -// When a HHE patch is first loaded, we must apply a small change -// to the states[] table. The table was changed between 1.0 and -// 1.3 to add two extra frames to the player "burning death" -// animation. -// If we are using an HHE patch, the table must behave like the -// Heretic 1.0 table. We must therefore change the table to cut -// these out again. - static void DEH_FrameInit(void) { - states[S_PLAY_FDTH18].nextstate = S_NULL; + // Bit of a hack here: + DEH_HereticInit(); } static void *DEH_FrameStart(deh_context_t *context, char *line) diff --git a/src/heretic/deh_htic.c b/src/heretic/deh_htic.c index ff5d02ac..40fa5765 100644 --- a/src/heretic/deh_htic.c +++ b/src/heretic/deh_htic.c @@ -24,11 +24,15 @@ // //----------------------------------------------------------------------------- +#include #include +#include + #include "deh_defs.h" #include "deh_main.h" #include "deh_htic.h" #include "info.h" +#include "m_argv.h" char *deh_signatures[] = { @@ -37,6 +41,11 @@ char *deh_signatures[] = NULL }; +static char *hhe_versions[] = +{ + "1.0", "1.2", "1.3" +}; + // Version number for patches. deh_hhe_version_t deh_hhe_version = deh_hhe_1_0; @@ -72,9 +81,65 @@ deh_section_t *deh_section_types[] = NULL }; +static void SetHHEVersionByName(char *name) +{ + int i; + + for (i=0; i + // + // Select the Heretic version number that was used to generate the + // HHE patch to be loaded. Patches for each of the Vanilla + // Heretic versions (1.0, 1.2, 1.3) can be loaded, but the correct + // version number must be specified. + + i = M_CheckParm("-hhever"); + + if (i > 0) + { + SetHHEVersionByName(myargv[i + 1]); + } + + // For v1.0 patches, we must apply a slight change to the states[] + // table. The table was changed between 1.0 and 1.3 to add two extra + // frames to the player "burning death" animation. + // + // If we are using a v1.0 patch, we must change the table to cut + // these out again. + + if (deh_hhe_version < deh_hhe_1_2) + { + states[S_PLAY_FDTH18].nextstate = S_NULL; + } +} + int DEH_MapHereticFrameNumber(int frame) { - if (deh_hhe_version < deh_hhe_1_0) + if (deh_hhe_version < deh_hhe_1_2) { // Between Heretic 1.0 and 1.2, two new frames // were added to the "states" table, to extend the "flame death" diff --git a/src/heretic/deh_htic.h b/src/heretic/deh_htic.h index 199ad2fe..f006c149 100644 --- a/src/heretic/deh_htic.h +++ b/src/heretic/deh_htic.h @@ -50,6 +50,7 @@ typedef enum #define DEH_HERETIC_NUMMOBJTYPES (NUMMOBJTYPES - 2) +void DEH_HereticInit(void); int DEH_MapHereticFrameNumber(int frame); extern deh_hhe_version_t deh_hhe_version; -- cgit v1.2.3 From 38c31575d255e1b2ff71f9de2c4d16664e8fb1e9 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 18 Apr 2010 17:15:20 +0000 Subject: Add WAD merging command line options to Heretic. Subversion-branch: /branches/raven-branch Subversion-revision: 1903 --- src/heretic/d_main.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/src/heretic/d_main.c b/src/heretic/d_main.c index 52e4239f..5fa2d5f9 100644 --- a/src/heretic/d_main.c +++ b/src/heretic/d_main.c @@ -46,6 +46,7 @@ #include "m_controls.h" #include "p_local.h" #include "s_sound.h" +#include "w_merge.h" #include "v_video.h" #define STARTUP_WINDOW_X 17 @@ -908,6 +909,132 @@ void D_DoomMain(void) D_AddFile(iwadfile); +#ifdef FEATURE_WAD_MERGE + + // Merged PWADs are loaded first, because they are supposed to be + // modified IWADs. + + //! + // @arg + // @category mod + // + // Simulates the behavior of deutex's -merge option, merging a PWAD + // into the main IWAD. Multiple files may be specified. + // + + p = M_CheckParm("-merge"); + + if (p > 0) + { + for (p = p + 1; p + // @category mod + // + // Simulates the behavior of NWT's -merge option. Multiple files + // may be specified. + + p = M_CheckParm("-nwtmerge"); + + if (p > 0) + { + for (p = p + 1; p + // @category mod + // + // Simulates the behavior of NWT's -af option, merging flats into + // the main IWAD directory. Multiple files may be specified. + // + + p = M_CheckParm("-af"); + + if (p > 0) + { + for (p = p + 1; p + // @category mod + // + // Simulates the behavior of NWT's -as option, merging sprites + // into the main IWAD directory. Multiple files may be specified. + // + + p = M_CheckParm("-as"); + + if (p > 0) + { + for (p = p + 1; p + // @category mod + // + // Equivalent to "-af -as ". + // + + p = M_CheckParm("-aa"); + + if (p > 0) + { + for (p = p + 1; p