? .deps ? INSTALL ? Makefile ? Makefile.in ? aclocal.m4 ? autom4te.cache ? config.guess ? config.h ? config.h.in ? config.log ? config.status ? config.sub ? configure ? depcomp ? install-sh ? missing ? schismtracker ? stamp-h1 ? tmp.s3m ? tmptmp Index: Makefile.am =================================================================== RCS file: /cvsroot/schismtracker/schism2/Makefile.am,v retrieving revision 1.30 diff -u -r1.30 Makefile.am --- Makefile.am 8 Feb 2008 02:37:35 -0000 1.30 +++ Makefile.am 6 Sep 2008 22:17:15 -0000 @@ -187,6 +187,7 @@ schism/fmt/mid.cc \ schism/fmt/its.cc \ schism/fmt/psm.cc \ + schism/fmt/scri.cc \ schism/fmt/iti.c \ schism/fmt/xi.c \ schism/fmt/pat.c \ @@ -272,6 +273,9 @@ modplug/load_ptm.cpp \ modplug/mmcmp.cpp \ modplug/fastmix.cpp \ + modplug/fmopl.c \ + modplug/fmopl.h \ + modplug/snd_fm.cpp \ $(files_macosx) \ $(files_alsa) \ $(files_oss) \ @@ -283,10 +287,13 @@ $(files_mmap) \ $(files_windres) +cflags_fmopl=-DHAS_YM3812=1 -DHAS_Y8950=0 -DHAS_YM3526=0 + INCLUDES = -D_USE_AUTOCONF -D_GNU_SOURCE \ -I$(srcdir)/include -I$(srcdir)/modplug -I. \ @SDL_CFLAGS@ $(cflags_alsa) $(cflags_oss) $(cflags_win32mm) \ - $(cflags_network) $(cflags_x11) $(cflags_zlib) + $(cflags_network) $(cflags_x11) $(cflags_zlib) \ + $(cflags_fmopl) schismtracker_DEPENDENCIES = $(files_windres) Index: include/fmt.h =================================================================== RCS file: /cvsroot/schismtracker/schism2/include/fmt.h,v retrieving revision 1.10 diff -u -r1.10 fmt.h --- include/fmt.h 24 Aug 2008 16:32:34 -0000 1.10 +++ include/fmt.h 6 Sep 2008 22:17:15 -0000 @@ -75,6 +75,7 @@ READ_INFO(iti); LOAD_INSTRUMENT(iti); READ_INFO(xi); LOAD_INSTRUMENT(xi); READ_INFO(pat); LOAD_INSTRUMENT(pat); + LOAD_INSTRUMENT(scri); READ_INFO(aiff); LOAD_SAMPLE(aiff); SAVE_SAMPLE(aiff); READ_INFO(au); LOAD_SAMPLE(au); SAVE_SAMPLE(au); @@ -83,7 +84,7 @@ READ_INFO(wav); LOAD_SAMPLE(wav); SAVE_SAMPLE(wav); READ_INFO(psm); READ_INFO(mid); SAVE_SONG(mid); - +READ_INFO(scri); LOAD_SAMPLE(scri); #undef READ_INFO #undef LOAD_SAMPLE Index: include/song.h =================================================================== RCS file: /cvsroot/schismtracker/schism2/include/song.h,v retrieving revision 1.29 diff -u -r1.29 song.h --- include/song.h 24 Aug 2008 16:32:34 -0000 1.29 +++ include/song.h 6 Sep 2008 22:17:15 -0000 @@ -62,8 +62,8 @@ unsigned int vib_depth; unsigned int vib_speed; char filename[22]; - int played; + unsigned char AdlibBytes[11]; } song_sample; /* modchannelsettings */ @@ -248,6 +248,7 @@ SAMP_PANNING = (0x20), SAMP_STEREO = (0x40), //SAMP_PINGPONGFLAG = (0x80), -- what is this? + SAMP_ADLIB = (0x20000000) // indicates an Adlib sample }; // channel flags Index: modplug/fastmix.cpp =================================================================== RCS file: /cvsroot/schismtracker/schism2/modplug/fastmix.cpp,v retrieving revision 1.13 diff -u -r1.13 fastmix.cpp --- modplug/fastmix.cpp 23 Aug 2008 16:27:28 -0000 1.13 +++ modplug/fastmix.cpp 6 Sep 2008 22:17:16 -0000 @@ -7,6 +7,7 @@ #include "stdafx.h" #include "sndfile.h" +#include "snd_fm.h" #include void (*CSoundFile::_multi_out_raw)(int chan, int *buf, int len) = NULL; @@ -1359,6 +1360,8 @@ LONG nLoopStart = (pChn->dwFlags & CHN_LOOP) ? pChn->nLoopStart : 0; LONG nInc = pChn->nInc; + if(pChn->dwFlags & CHN_ADLIB) return 1; + if ((nSamples <= 0) || (!nInc) || (!pChn->nLength)) return 0; // Under zero ? if ((LONG)pChn->nPos < nLoopStart) @@ -1567,18 +1570,21 @@ pChannel->topnote_offset = ((pChannel->nPos << 16) | pChannel->nPosLo) % pChannel->nLength; } - // Choose function for mixing - LPMIXINTERFACE pMixFunc; - pMixFunc = (pChannel->nRampLength) ? pMixFuncTable[nFlags|MIXNDX_RAMP] : pMixFuncTable[nFlags]; - int *pbufmax = pbuffer + (nSmpCount*2); - pChannel->nROfs = - *(pbufmax-2); - pChannel->nLOfs = - *(pbufmax-1); - - pMixFunc(pChannel, pbuffer, pbufmax); - pChannel->nROfs += *(pbufmax-2); - pChannel->nLOfs += *(pbufmax-1); - pbuffer = pbufmax; - naddmix = 1; + if (!(pChannel->dwFlags & CHN_ADLIB)) + { + // Choose function for mixing + LPMIXINTERFACE pMixFunc; + pMixFunc = (pChannel->nRampLength) ? pMixFuncTable[nFlags|MIXNDX_RAMP] : pMixFuncTable[nFlags]; + int *pbufmax = pbuffer + (nSmpCount*2); + pChannel->nROfs = - *(pbufmax-2); + pChannel->nLOfs = - *(pbufmax-1); + + pMixFunc(pChannel, pbuffer, pbufmax); + pChannel->nROfs += *(pbufmax-2); + pChannel->nLOfs += *(pbufmax-1); + pbuffer = pbufmax; + naddmix = 1; + } } nsamples -= nSmpCount; @@ -1606,6 +1612,8 @@ CSoundFile::_multi_out_raw(n, MultiSoundBuffer[n], count*2); } } + + Fmdrv_MixTo(MixSoundBuffer, count); return nchused; } Index: modplug/fmopl.c =================================================================== RCS file: modplug/fmopl.c diff -N modplug/fmopl.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modplug/fmopl.c 6 Sep 2008 22:17:17 -0000 @@ -0,0 +1,2518 @@ +/* +** +** File: fmopl.c - software implementation of FM sound generator +** types OPL and OPL2 +** +** Copyright (C) 2002,2003 Jarek Burczynski (bujar at mame dot net) +** Copyright (C) 1999,2000 Tatsuyuki Satoh , MultiArcadeMachineEmulator development +** +** Version 0.70 +** + +Revision History: + +14-06-2003 Jarek Burczynski: + - implemented all of the status register flags in Y8950 emulation + - renamed Y8950SetDeltaTMemory() parameters from _rom_ to _mem_ since + they can be either RAM or ROM + +08-10-2002 Jarek Burczynski (thanks to Dox for the YM3526 chip) + - corrected YM3526Read() to always set bit 2 and bit 1 + to HIGH state - identical to YM3812Read (verified on real YM3526) + +04-28-2002 Jarek Burczynski: + - binary exact Envelope Generator (verified on real YM3812); + compared to YM2151: the EG clock is equal to internal_clock, + rates are 2 times slower and volume resolution is one bit less + - modified interface functions (they no longer return pointer - + that's internal to the emulator now): + - new wrapper functions for OPLCreate: YM3526Init(), YM3812Init() and Y8950Init() + - corrected 'off by one' error in feedback calculations (when feedback is off) + - enabled waveform usage (credit goes to Vlad Romascanu and zazzal22) + - speeded up noise generator calculations (Nicola Salmoria) + +03-24-2002 Jarek Burczynski (thanks to Dox for the YM3812 chip) + Complete rewrite (all verified on real YM3812): + - corrected sin_tab and tl_tab data + - corrected operator output calculations + - corrected waveform_select_enable register; + simply: ignore all writes to waveform_select register when + waveform_select_enable == 0 and do not change the waveform previously selected. + - corrected KSR handling + - corrected Envelope Generator: attack shape, Sustain mode and + Percussive/Non-percussive modes handling + - Envelope Generator rates are two times slower now + - LFO amplitude (tremolo) and phase modulation (vibrato) + - rhythm sounds phase generation + - white noise generator (big thanks to Olivier Galibert for mentioning Berlekamp-Massey algorithm) + - corrected key on/off handling (the 'key' signal is ORed from three sources: FM, rhythm and CSM) + - funky details (like ignoring output of operator 1 in BD rhythm sound when connect == 1) + +12-28-2001 Acho A. Tang + - reflected Delta-T EOS status on Y8950 status port. + - fixed subscription range of attack/decay tables + + + To do: + add delay before key off in CSM mode (see CSMKeyControll) + verify volume of the FM part on the Y8950 +*/ + +#include +#include +#include +#include + +//#include "driver.h" /* use M.A.M.E. */ +#include "fmopl.h" + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + + + +/* output final shift */ +#if (OPL_SAMPLE_BITS==16) + #define FINAL_SH (0) + #define MAXOUT (+32767) + #define MINOUT (-32768) +#else + #define FINAL_SH (8) + #define MAXOUT (+127) + #define MINOUT (-128) +#endif + + +#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ +#define EG_SH 16 /* 16.16 fixed point (EG timing) */ +#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ +#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ + +#define FREQ_MASK ((1<=0) + { + if (value < 0x0200) + return (value & ~0); + if (value < 0x0400) + return (value & ~1); + if (value < 0x0800) + return (value & ~3); + if (value < 0x1000) + return (value & ~7); + if (value < 0x2000) + return (value & ~15); + if (value < 0x4000) + return (value & ~31); + return (value & ~63); + } + /*else value < 0*/ + if (value > -0x0200) + return (~abs(value) & ~0); + if (value > -0x0400) + return (~abs(value) & ~1); + if (value > -0x0800) + return (~abs(value) & ~3); + if (value > -0x1000) + return (~abs(value) & ~7); + if (value > -0x2000) + return (~abs(value) & ~15); + if (value > -0x4000) + return (~abs(value) & ~31); + return (~abs(value) & ~63); +} + + +static FILE *sample[1]; + #if 1 /*save to MONO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = acc_calc(lt); \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #else /*save to STEREO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = lt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + pom = rt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #endif +#endif + +/* #define LOG_CYM_FILE */ +#ifdef LOG_CYM_FILE + FILE * cymfile = NULL; +#endif + + + +#define OPL_TYPE_WAVESEL 0x01 /* waveform select */ +#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */ +#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */ +#define OPL_TYPE_IO 0x08 /* I/O port */ + +/* ---------- Generic interface section ---------- */ +#define OPL_TYPE_YM3526 (0) +#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL) +#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO) + + + +typedef struct{ + UINT32 ar; /* attack rate: AR<<2 */ + UINT32 dr; /* decay rate: DR<<2 */ + UINT32 rr; /* release rate:RR<<2 */ + UINT8 KSR; /* key scale rate */ + UINT8 ksl; /* keyscale level */ + UINT8 ksr; /* key scale rate: kcode>>KSR */ + UINT8 mul; /* multiple: mul_tab[ML] */ + + /* Phase Generator */ + UINT32 Cnt; /* frequency counter */ + UINT32 Incr; /* frequency counter step */ + UINT8 FB; /* feedback shift value */ + INT32 *connect1; /* slot1 output pointer */ + INT32 op1_out[2]; /* slot1 output for feedback */ + UINT8 CON; /* connection (algorithm) type */ + + /* Envelope Generator */ + UINT8 eg_type; /* percussive/non-percussive mode */ + UINT8 state; /* phase type */ + UINT32 TL; /* total level: TL << 2 */ + INT32 TLL; /* adjusted now TL */ + INT32 volume; /* envelope counter */ + UINT32 sl; /* sustain level: sl_tab[SL] */ + UINT8 eg_sh_ar; /* (attack state) */ + UINT8 eg_sel_ar; /* (attack state) */ + UINT8 eg_sh_dr; /* (decay state) */ + UINT8 eg_sel_dr; /* (decay state) */ + UINT8 eg_sh_rr; /* (release state) */ + UINT8 eg_sel_rr; /* (release state) */ + UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */ + + /* LFO */ + UINT32 AMmask; /* LFO Amplitude Modulation enable mask */ + UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/ + + /* waveform select */ + unsigned int wavetable; +} OPL_SLOT; + +typedef struct{ + OPL_SLOT SLOT[2]; + /* phase generator state */ + UINT32 block_fnum; /* block+fnum */ + UINT32 fc; /* Freq. Increment base */ + UINT32 ksl_base; /* KeyScaleLevel Base step */ + UINT8 kcode; /* key code (for key scaling) */ +} OPL_CH; + +/* OPL state */ +typedef struct fm_opl_f { + /* FM channel slots */ + OPL_CH P_CH[9]; /* OPL/OPL2 chips have 9 channels*/ + + UINT32 eg_cnt; /* global envelope generator counter */ + UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */ + UINT32 eg_timer_add; /* step of eg_timer */ + UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */ + + UINT8 rhythm; /* Rhythm mode */ + + UINT32 fn_tab[1024]; /* fnumber->increment counter */ + + /* LFO */ + UINT8 lfo_am_depth; + UINT8 lfo_pm_depth_range; + UINT32 lfo_am_cnt; + UINT32 lfo_am_inc; + UINT32 lfo_pm_cnt; + UINT32 lfo_pm_inc; + + UINT32 noise_rng; /* 23 bit noise shift register */ + UINT32 noise_p; /* current noise 'phase' */ + UINT32 noise_f; /* current noise period */ + + UINT8 wavesel; /* waveform select enable flag */ + + int T[2]; /* timer counters */ + int TC[2]; + UINT8 st[2]; /* timer enable */ + +#if BUILD_Y8950 + /* Delta-T ADPCM unit (Y8950) */ + + YM_DELTAT *deltat; + + /* Keyboard and I/O ports interface */ + UINT8 portDirection; + UINT8 portLatch; + OPL_PORTHANDLER_R porthandler_r; + OPL_PORTHANDLER_W porthandler_w; + int port_param; + OPL_PORTHANDLER_R keyboardhandler_r; + OPL_PORTHANDLER_W keyboardhandler_w; + int keyboard_param; +#endif + + /* external event callback handlers */ + 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 */ + + UINT8 type; /* chip type */ + UINT8 address; /* address register */ + UINT8 status; /* status flag */ + UINT8 statusmask; /* status mask */ + UINT8 mode; /* Reg.08 : CSM,notesel,etc. */ + + int clock; /* master clock (Hz) */ + int rate; /* sampling rate (Hz) */ + double freqbase; /* frequency base */ + double TimerBase; /* Timer base time (==sampling time)*/ +} FM_OPL; + + + +/* mapping of register number (offset) to slot number used by the emulator */ +static const int slot_array[32]= +{ + 0, 2, 4, 1, 3, 5,-1,-1, + 6, 8,10, 7, 9,11,-1,-1, + 12,14,16,13,15,17,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1 +}; + +/* key scale level */ +/* table is 3dB/octave , DV converts this into 6dB/octave */ +/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */ +#define SC(x) ((UINT32)((x)/(0.1875/2.0))) +static const UINT32 ksl_tab[8*16]= +{ + /* OCT 0 */ + SC(0.000), SC(0.000), SC(0.000), SC(0.000), + SC(0.000), SC(0.000), SC(0.000), SC(0.000), + SC(0.000), SC(0.000), SC(0.000), SC(0.000), + SC(0.000), SC(0.000), SC(0.000), SC(0.000), + /* OCT 1 */ + SC(0.000), SC(0.000), SC(0.000), SC(0.000), + SC(0.000), SC(0.000), SC(0.000), SC(0.000), + SC(0.000), SC(0.750), SC(1.125), SC(1.500), + SC(1.875), SC(2.250), SC(2.625), SC(3.000), + /* OCT 2 */ + SC(0.000), SC(0.000), SC(0.000), SC(0.000), + SC(0.000), SC(1.125), SC(1.875), SC(2.625), + SC(3.000), SC(3.750), SC(4.125), SC(4.500), + SC(4.875), SC(5.250), SC(5.625), SC(6.000), + /* OCT 3 */ + SC(0.000), SC(0.000), SC(0.000), SC(1.875), + SC(3.000), SC(4.125), SC(4.875), SC(5.625), + SC(6.000), SC(6.750), SC(7.125), SC(7.500), + SC(7.875), SC(8.250), SC(8.625), SC(9.000), + /* OCT 4 */ + SC(0.000), SC(0.000), SC(3.000), SC(4.875), + SC(6.000), SC(7.125), SC(7.875), SC(8.625), + SC(9.000), SC(9.750),SC(10.125),SC(10.500), + SC(10.875),SC(11.250),SC(11.625),SC(12.000), + /* OCT 5 */ + SC(0.000), SC(3.000), SC(6.000), SC(7.875), + SC(9.000),SC(10.125),SC(10.875),SC(11.625), + SC(12.000),SC(12.750),SC(13.125),SC(13.500), + SC(13.875),SC(14.250),SC(14.625),SC(15.000), + /* OCT 6 */ + SC(0.000), SC(6.000), SC(9.000),SC(10.875), + SC(12.000),SC(13.125),SC(13.875),SC(14.625), + SC(15.000),SC(15.750),SC(16.125),SC(16.500), + SC(16.875),SC(17.250),SC(17.625),SC(18.000), + /* OCT 7 */ + SC(0.000), SC(9.000),SC(12.000),SC(13.875), + SC(15.000),SC(16.125),SC(16.875),SC(17.625), + SC(18.000),SC(18.750),SC(19.125),SC(19.500), + SC(19.875),SC(20.250),SC(20.625),SC(21.000) +}; +#undef SC + +/* sustain level table (3dB per step) */ +/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ +#define SC(db) (UINT32) ( db * (2.0/ENV_STEP) ) +static const UINT32 sl_tab[16]={ + SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), + SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) +}; +#undef SC + + +#define RATE_STEPS (8) +static const unsigned char eg_inc[15*RATE_STEPS]={ + +/*cycle:0 1 2 3 4 5 6 7*/ + +/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */ +/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */ +/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */ +/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */ + +/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */ +/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */ +/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */ +/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */ + +/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */ +/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */ +/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */ +/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */ + +/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */ +/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */ +/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ +}; + + +#define O(a) (a*RATE_STEPS) + +/*note that there is no O(13) in this table - it's directly in the code */ +static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */ +/* 16 infinite time rates */ +O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), +O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), + +/* rates 00-12 */ +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), + +/* rate 13 */ +O( 4),O( 5),O( 6),O( 7), + +/* rate 14 */ +O( 8),O( 9),O(10),O(11), + +/* rate 15 */ +O(12),O(12),O(12),O(12), + +/* 16 dummy rates (same as 15 3) */ +O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), +O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), + +}; +#undef O + +/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */ +/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */ +/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */ + +#define O(a) (a*1) +static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */ +/* 16 infinite time rates */ +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), + +/* rates 00-12 */ +O(12),O(12),O(12),O(12), +O(11),O(11),O(11),O(11), +O(10),O(10),O(10),O(10), +O( 9),O( 9),O( 9),O( 9), +O( 8),O( 8),O( 8),O( 8), +O( 7),O( 7),O( 7),O( 7), +O( 6),O( 6),O( 6),O( 6), +O( 5),O( 5),O( 5),O( 5), +O( 4),O( 4),O( 4),O( 4), +O( 3),O( 3),O( 3),O( 3), +O( 2),O( 2),O( 2),O( 2), +O( 1),O( 1),O( 1),O( 1), +O( 0),O( 0),O( 0),O( 0), + +/* rate 13 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 14 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 15 */ +O( 0),O( 0),O( 0),O( 0), + +/* 16 dummy rates (same as 15 3) */ +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), + +}; +#undef O + + +/* multiple table */ +#define SC(x) ((UINT32)((x)*2)) +static const UINT8 mul_tab[16]= { +/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */ + SC(0.50), SC(1.00), SC(2.00), SC(3.00), SC(4.00), SC(5.00), SC(6.00), SC(7.00), + SC(8.00), SC(9.00),SC(10.00),SC(10.00),SC(12.00),SC(12.00),SC(15.00),SC(15.00) +}; +#undef SC + +/* TL_TAB_LEN is calculated as: +* 12 - sinus amplitude bits (Y axis) +* 2 - sinus sign bit (Y axis) +* TL_RES_LEN - sinus resolution (X axis) +*/ +#define TL_TAB_LEN (12*2*TL_RES_LEN) +static signed int tl_tab[TL_TAB_LEN]; + +#define ENV_QUIET (TL_TAB_LEN>>4) + +/* sin waveform table in 'decibel' scale */ +/* four waveforms on OPL2 type chips */ +static unsigned int sin_tab[SIN_LEN * 4]; + + +/* LFO Amplitude Modulation table (verified on real YM3812) + 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples + + Length: 210 elements. + + Each of the elements has to be repeated + exactly 64 times (on 64 consecutive samples). + The whole table takes: 64 * 210 = 13440 samples. + + When AM = 1 data is used directly + When AM = 0 data is divided by 4 before being used (loosing precision is important) +*/ + +#define LFO_AM_TAB_ELEMENTS 210 + +static const UINT8 lfo_am_table[LFO_AM_TAB_ELEMENTS] = { +0,0,0,0,0,0,0, +1,1,1,1, +2,2,2,2, +3,3,3,3, +4,4,4,4, +5,5,5,5, +6,6,6,6, +7,7,7,7, +8,8,8,8, +9,9,9,9, +10,10,10,10, +11,11,11,11, +12,12,12,12, +13,13,13,13, +14,14,14,14, +15,15,15,15, +16,16,16,16, +17,17,17,17, +18,18,18,18, +19,19,19,19, +20,20,20,20, +21,21,21,21, +22,22,22,22, +23,23,23,23, +24,24,24,24, +25,25,25,25, +26,26,26, +25,25,25,25, +24,24,24,24, +23,23,23,23, +22,22,22,22, +21,21,21,21, +20,20,20,20, +19,19,19,19, +18,18,18,18, +17,17,17,17, +16,16,16,16, +15,15,15,15, +14,14,14,14, +13,13,13,13, +12,12,12,12, +11,11,11,11, +10,10,10,10, +9,9,9,9, +8,8,8,8, +7,7,7,7, +6,6,6,6, +5,5,5,5, +4,4,4,4, +3,3,3,3, +2,2,2,2, +1,1,1,1 +}; + +/* LFO Phase Modulation table (verified on real YM3812) */ +static const INT8 lfo_pm_table[8*8*2] = { + +/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ +4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ +5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ +6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ +7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/ +}; + + +/* lock level of common table */ +static int num_lock = 0; + + +static void *cur_chip = NULL; /* current chip pointer */ +static OPL_SLOT *SLOT7_1, *SLOT7_2, *SLOT8_1, *SLOT8_2; + +static signed int phase_modulation; /* phase modulation input (SLOT 2) */ +static signed int output[1]; + +#if BUILD_Y8950 +static INT32 output_deltat[4]; /* for Y8950 DELTA-T, chip is mono, that 4 here is just for safety */ +#endif + +static UINT32 LFO_AM; +static INT32 LFO_PM; + + + +INLINE int limit( int val, int max, int min ) { + if ( val > max ) + val = max; + else if ( val < min ) + val = min; + + return val; +} + + +/* status set and IRQ handling */ +INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag) +{ + /* set status flag */ + OPL->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); +} + + +/* advance LFO to next sample */ +INLINE void advance_lfo(FM_OPL *OPL) +{ + UINT8 tmp; + + /* LFO */ + OPL->lfo_am_cnt += OPL->lfo_am_inc; + if (OPL->lfo_am_cnt >= (LFO_AM_TAB_ELEMENTS<lfo_am_cnt -= (LFO_AM_TAB_ELEMENTS<lfo_am_cnt >> LFO_SH ]; + + if (OPL->lfo_am_depth) + LFO_AM = tmp; + else + LFO_AM = tmp>>2; + + OPL->lfo_pm_cnt += OPL->lfo_pm_inc; + LFO_PM = ((OPL->lfo_pm_cnt>>LFO_SH) & 7) | OPL->lfo_pm_depth_range; +} + +/* advance to next sample */ +INLINE void advance(FM_OPL *OPL) +{ + OPL_CH *CH; + OPL_SLOT *op; + int i; + + OPL->eg_timer += OPL->eg_timer_add; + + while (OPL->eg_timer >= OPL->eg_timer_overflow) + { + OPL->eg_timer -= OPL->eg_timer_overflow; + + OPL->eg_cnt++; + + for (i=0; i<9*2; i++) + { + CH = &OPL->P_CH[i/2]; + op = &CH->SLOT[i&1]; + + /* Envelope Generator */ + switch(op->state) + { + case EG_ATT: /* attack phase */ + if ( !(OPL->eg_cnt & ((1<eg_sh_ar)-1) ) ) + { + op->volume += (~op->volume * + (eg_inc[op->eg_sel_ar + ((OPL->eg_cnt>>op->eg_sh_ar)&7)]) + ) >>3; + + if (op->volume <= MIN_ATT_INDEX) + { + op->volume = MIN_ATT_INDEX; + op->state = EG_DEC; + } + + } + break; + + case EG_DEC: /* decay phase */ + if ( !(OPL->eg_cnt & ((1<eg_sh_dr)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_dr + ((OPL->eg_cnt>>op->eg_sh_dr)&7)]; + + if ( op->volume >= op->sl ) + op->state = EG_SUS; + + } + break; + + case EG_SUS: /* sustain phase */ + + /* this is important behaviour: + one can change percusive/non-percussive modes on the fly and + the chip will remain in sustain phase - verified on real YM3812 */ + + if(op->eg_type) /* non-percussive mode */ + { + /* do nothing */ + } + else /* percussive mode */ + { + /* during sustain phase chip adds Release Rate (in percussive mode) */ + if ( !(OPL->eg_cnt & ((1<eg_sh_rr)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)]; + + if ( op->volume >= MAX_ATT_INDEX ) + op->volume = MAX_ATT_INDEX; + } + /* else do nothing in sustain phase */ + } + break; + + case EG_REL: /* release phase */ + if ( !(OPL->eg_cnt & ((1<eg_sh_rr)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)]; + + if ( op->volume >= MAX_ATT_INDEX ) + { + op->volume = MAX_ATT_INDEX; + op->state = EG_OFF; + } + + } + break; + + default: + break; + } + } + } + + for (i=0; i<9*2; i++) + { + CH = &OPL->P_CH[i/2]; + op = &CH->SLOT[i&1]; + + /* Phase Generator */ + if(op->vib) + { + UINT8 block; + unsigned int block_fnum = CH->block_fnum; + + unsigned int fnum_lfo = (block_fnum&0x0380) >> 7; + + signed int lfo_fn_table_index_offset = lfo_pm_table[LFO_PM + 16*fnum_lfo ]; + + if (lfo_fn_table_index_offset) /* LFO phase modulation active */ + { + block_fnum += lfo_fn_table_index_offset; + block = (block_fnum&0x1c00) >> 10; + op->Cnt += (OPL->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul; + } + else /* LFO phase modulation = zero */ + { + op->Cnt += op->Incr; + } + } + else /* LFO phase modulation disabled for this operator */ + { + op->Cnt += op->Incr; + } + } + + /* The Noise Generator of the YM3812 is 23-bit shift register. + * Period is equal to 2^23-2 samples. + * Register works at sampling frequency of the chip, so output + * can change on every sample. + * + * Output of the register and input to the bit 22 is: + * bit0 XOR bit14 XOR bit15 XOR bit22 + * + * Simply use bit 22 as the noise output. + */ + + OPL->noise_p += OPL->noise_f; + i = OPL->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */ + OPL->noise_p &= FREQ_MASK; + while (i) + { + /* + UINT32 j; + j = ( (OPL->noise_rng) ^ (OPL->noise_rng>>14) ^ (OPL->noise_rng>>15) ^ (OPL->noise_rng>>22) ) & 1; + OPL->noise_rng = (j<<22) | (OPL->noise_rng>>1); + */ + + /* + Instead of doing all the logic operations above, we + use a trick here (and use bit 0 as the noise output). + The difference is only that the noise bit changes one + step ahead. This doesn't matter since we don't know + what is real state of the noise_rng after the reset. + */ + + if (OPL->noise_rng & 1) OPL->noise_rng ^= 0x800302; + OPL->noise_rng >>= 1; + + i--; + } +} + + +INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab) +{ + UINT32 p; + + p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ]; + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + +INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab) +{ + UINT32 p; + + p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK) ]; + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + + +#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (LFO_AM & (OP)->AMmask)) + +/* calculate output */ +INLINE void OPL_CALC_CH( OPL_CH *CH ) +{ + OPL_SLOT *SLOT; + unsigned int env; + signed int out; + + phase_modulation = 0; + + /* SLOT 1 */ + SLOT = &CH->SLOT[SLOT1]; + env = volume_calc(SLOT); + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + *SLOT->connect1 += SLOT->op1_out[0]; + SLOT->op1_out[1] = 0; + if( env < ENV_QUIET ) + { + if (!SLOT->FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); + } + + /* SLOT 2 */ + SLOT++; + env = volume_calc(SLOT); + if( env < ENV_QUIET ) + output[0] += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable); +} + +/* + operators used in the rhythm sounds generation process: + + Envelope Generator: + +channel operator register number Bass High Snare Tom Top +/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal + 6 / 0 12 50 70 90 f0 + + 6 / 1 15 53 73 93 f3 + + 7 / 0 13 51 71 91 f1 + + 7 / 1 16 54 74 94 f4 + + 8 / 0 14 52 72 92 f2 + + 8 / 1 17 55 75 95 f5 + + + Phase Generator: + +channel operator register number Bass High Snare Tom Top +/ slot number MULTIPLE Drum Hat Drum Tom Cymbal + 6 / 0 12 30 + + 6 / 1 15 33 + + 7 / 0 13 31 + + + + 7 / 1 16 34 ----- n o t u s e d ----- + 8 / 0 14 32 + + 8 / 1 17 35 + + + +channel operator register number Bass High Snare Tom Top +number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal + 6 12,15 B6 A6 + + + 7 13,16 B7 A7 + + + + + 8 14,17 B8 A8 + + + + +*/ + +/* calculate rhythm */ + +INLINE void OPL_CALC_RH( OPL_CH *CH, unsigned int noise ) +{ + OPL_SLOT *SLOT; + signed int out; + unsigned int env; + + + /* Bass Drum (verified on real YM3812): + - depends on the channel 6 'connect' register: + when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out) + when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored + - output sample always is multiplied by 2 + */ + + phase_modulation = 0; + /* SLOT 1 */ + SLOT = &CH[6].SLOT[SLOT1]; + env = volume_calc(SLOT); + + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + + if (!SLOT->CON) + phase_modulation = SLOT->op1_out[0]; + /* else ignore output of operator 1 */ + + SLOT->op1_out[1] = 0; + if( env < ENV_QUIET ) + { + if (!SLOT->FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); + } + + /* SLOT 2 */ + SLOT++; + env = volume_calc(SLOT); + if( env < ENV_QUIET ) + output[0] += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable) * 2; + + + /* Phase generation is based on: */ + /* HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) */ + /* SD (16) channel 7->slot 1 */ + /* TOM (14) channel 8->slot 1 */ + /* TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) */ + + /* Envelope generation based on: */ + /* HH channel 7->slot1 */ + /* SD channel 7->slot2 */ + /* TOM channel 8->slot1 */ + /* TOP channel 8->slot2 */ + + + /* The following formulas can be well optimized. + I leave them in direct form for now (in case I've missed something). + */ + + /* High Hat (verified on real YM3812) */ + env = volume_calc(SLOT7_1); + if( env < ENV_QUIET ) + { + + /* high hat phase generation: + phase = d0 or 234 (based on frequency only) + phase = 34 or 2d0 (based on noise) + */ + + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1; + unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1; + unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1; + + unsigned char res1 = (bit2 ^ bit7) | bit3; + + /* when res1 = 0 phase = 0x000 | 0xd0; */ + /* when res1 = 1 phase = 0x200 | (0xd0>>2); */ + UINT32 phase = res1 ? (0x200|(0xd0>>2)) : 0xd0; + + /* enable gate based on frequency of operator 2 in channel 8 */ + unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1; + unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1; + + unsigned char res2 = (bit3e ^ bit5e); + + /* when res2 = 0 pass the phase from calculation above (res1); */ + /* when res2 = 1 phase = 0x200 | (0xd0>>2); */ + if (res2) + phase = (0x200|(0xd0>>2)); + + + /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */ + /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */ + if (phase&0x200) + { + if (noise) + phase = 0x200|0xd0; + } + else + /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */ + /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */ + { + if (noise) + phase = 0xd0>>2; + } + + output[0] += op_calc(phase<wavetable) * 2; + } + + /* Snare Drum (verified on real YM3812) */ + env = volume_calc(SLOT7_2); + if( env < ENV_QUIET ) + { + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit8 = ((SLOT7_1->Cnt>>FREQ_SH)>>8)&1; + + /* when bit8 = 0 phase = 0x100; */ + /* when bit8 = 1 phase = 0x200; */ + UINT32 phase = bit8 ? 0x200 : 0x100; + + /* Noise bit XOR'es phase by 0x100 */ + /* when noisebit = 0 pass the phase from calculation above */ + /* when noisebit = 1 phase ^= 0x100; */ + /* in other words: phase ^= (noisebit<<8); */ + if (noise) + phase ^= 0x100; + + output[0] += op_calc(phase<wavetable) * 2; + } + + /* Tom Tom (verified on real YM3812) */ + env = volume_calc(SLOT8_1); + if( env < ENV_QUIET ) + output[0] += op_calc(SLOT8_1->Cnt, env, 0, SLOT8_1->wavetable) * 2; + + /* Top Cymbal (verified on real YM3812) */ + env = volume_calc(SLOT8_2); + if( env < ENV_QUIET ) + { + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1; + unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1; + unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1; + + unsigned char res1 = (bit2 ^ bit7) | bit3; + + /* when res1 = 0 phase = 0x000 | 0x100; */ + /* when res1 = 1 phase = 0x200 | 0x100; */ + UINT32 phase = res1 ? 0x300 : 0x100; + + /* enable gate based on frequency of operator 2 in channel 8 */ + unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1; + unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1; + + unsigned char res2 = (bit3e ^ bit5e); + /* when res2 = 0 pass the phase from calculation above (res1); */ + /* when res2 = 1 phase = 0x200 | 0x100; */ + if (res2) + phase = 0x300; + + output[0] += op_calc(phase<wavetable) * 2; + } + +} + + +/* generic table initialize */ +static int init_tables(void) +{ + signed int i,x; + signed int n; + double o,m; + + + for (x=0; x>= 4; /* 12 bits here */ + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + /* 11 bits here (rounded) */ + n <<= 1; /* 12 bits here (as in real chip) */ + tl_tab[ x*2 + 0 ] = n; + tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; + + for (i=1; i<12; i++) + { + tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; + tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; + } + #if 0 + logerror("tl %04i", x*2); + for (i=0; i<12; i++) + logerror(", [%02i] %5i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] ); + logerror("\n"); + #endif + } + /*logerror("FMOPL.C: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/ + + + for (i=0; i0.0) + o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ + else + o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ + + o = o / (ENV_STEP/4); + + n = (int)(2.0*o); + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + + sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); + + /*logerror("FMOPL.C: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/ + } + + for (i=0; i>1) ]; + + /* waveform 3: _ _ _ _ */ + /* / |_/ |_/ |_/ |_*/ + /* abs(output only first quarter of the sinus waveform) */ + + if (i & (1<<(SIN_BITS-2)) ) + sin_tab[3*SIN_LEN+i] = TL_TAB_LEN; + else + sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)]; + + /*logerror("FMOPL.C: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] ); + logerror("FMOPL.C: sin2[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[2*SIN_LEN+i], tl_tab[sin_tab[2*SIN_LEN+i]] ); + logerror("FMOPL.C: sin3[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[3*SIN_LEN+i], tl_tab[sin_tab[3*SIN_LEN+i]] );*/ + } + /*logerror("FMOPL.C: ENV_QUIET= %08x (dec*8=%i)\n", ENV_QUIET, ENV_QUIET*8 );*/ + + +#ifdef SAVE_SAMPLE + sample[0]=fopen("sampsum.pcm","wb"); +#endif + + return 1; +} + +static void OPLCloseTable( void ) +{ +#ifdef SAVE_SAMPLE + fclose(sample[0]); +#endif +} + + + +static void OPL_initalize(FM_OPL *OPL) +{ + int i; + + /* frequency base */ + OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / 72.0) / OPL->rate : 0; +#if 0 + OPL->rate = (double)OPL->clock / 72.0; + OPL->freqbase = 1.0; +#endif + + /*logerror("freqbase=%f\n", OPL->freqbase);*/ + + /* Timer base time */ + OPL->TimerBase = 1.0 / ((double)OPL->clock / 72.0 ); + + /* make fnumber -> increment counter table */ + for( i=0 ; i < 1024 ; i++ ) + { + /* opn phase increment counter = 20bit */ + OPL->fn_tab[i] = (UINT32)( (double)i * 64 * OPL->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ +#if 0 + logerror("FMOPL.C: fn_tab[%4i] = %08x (dec=%8i)\n", + i, OPL->fn_tab[i]>>6, OPL->fn_tab[i]>>6 ); +#endif + } + +#if 0 + for( i=0 ; i < 16 ; i++ ) + { + logerror("FMOPL.C: sl_tab[%i] = %08x\n", + i, sl_tab[i] ); + } + for( i=0 ; i < 8 ; i++ ) + { + int j; + logerror("FMOPL.C: ksl_tab[oct=%2i] =",i); + for (j=0; j<16; j++) + { + logerror("%08x ", ksl_tab[i*16+j] ); + } + logerror("\n"); + } +#endif + + + /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */ + /* One entry from LFO_AM_TABLE lasts for 64 samples */ + OPL->lfo_am_inc = (UINT32)((1.0 / 64.0 ) * (1<freqbase); + + /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */ + OPL->lfo_pm_inc = (UINT32)((1.0 / 1024.0) * (1<freqbase); + + /*logerror ("OPL->lfo_am_inc = %8x ; OPL->lfo_pm_inc = %8x\n", OPL->lfo_am_inc, OPL->lfo_pm_inc);*/ + + /* Noise generator: a step takes 1 sample */ + OPL->noise_f = (UINT32)((1.0 / 1.0) * (1<freqbase); + + OPL->eg_timer_add = (UINT32)((1<freqbase); + OPL->eg_timer_overflow = ( 1 ) * (1<eg_timer_add, OPL->eg_timer_overflow);*/ + +} + +INLINE void FM_KEYON(OPL_SLOT *SLOT, UINT32 key_set) +{ + if( !SLOT->key ) + { + /* restart Phase Generator */ + SLOT->Cnt = 0; + /* phase -> Attack */ + SLOT->state = EG_ATT; + } + SLOT->key |= key_set; +} + +INLINE void FM_KEYOFF(OPL_SLOT *SLOT, UINT32 key_clr) +{ + if( SLOT->key ) + { + SLOT->key &= key_clr; + + if( !SLOT->key ) + { + /* phase -> Release */ + if (SLOT->state>EG_REL) + SLOT->state = EG_REL; + } + } +} + +/* update phase increment counter of operator (also update the EG rates if necessary) */ +INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT) +{ + int ksr; + + /* (frequency) phase increment counter */ + SLOT->Incr = CH->fc * SLOT->mul; + ksr = CH->kcode >> SLOT->KSR; + + if( SLOT->ksr != ksr ) + { + SLOT->ksr = ksr; + + /* calculate envelope generator rates */ + if ((SLOT->ar + SLOT->ksr) < 16+62) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 13*RATE_STEPS; + } + SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; + SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; + } +} + +/* 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/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->mul = mul_tab[v&0x0f]; + SLOT->KSR = (v&0x10) ? 0 : 2; + SLOT->eg_type = (v&0x20); + SLOT->vib = (v&0x40); + SLOT->AMmask = (v&0x80) ? ~0 : 0; + 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/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + int ksl = v>>6; /* 0 / 1.5 / 3.0 / 6.0 dB/OCT */ + + SLOT->ksl = ksl ? 3-ksl : 31; + SLOT->TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */ + + 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/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0; + + if ((SLOT->ar + SLOT->ksr) < 16+62) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 13*RATE_STEPS; + } + + SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; + SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; +} + +/* set sustain level & release rate */ +INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->sl = sl_tab[ v>>4 ]; + + SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; +} + + +/* write a value v to register r on OPL chip */ +static void OPLWriteReg(FM_OPL *OPL, int r, int v) +{ + OPL_CH *CH; + int slot; + int block_fnum; + + + /* adjust bus to 8 bits */ + r &= 0xff; + v &= 0xff; + +#ifdef LOG_CYM_FILE + if ((cymfile) && (r!=0) ) + { + fputc( (unsigned char)r, cymfile ); + fputc( (unsigned char)v, cymfile ); + } +#endif + + + switch(r&0xe0) + { + case 0x00: /* 00-1f:control */ + switch(r&0x1f) + { + case 0x01: /* waveform select enable */ + if(OPL->type&OPL_TYPE_WAVESEL) + { + OPL->wavesel = v&0x20; + /* do not change the waveform previously selected */ + } + break; + case 0x02: /* Timer 1 */ + OPL->T[0] = (256-v)*4; + break; + case 0x03: /* Timer 2 */ + OPL->T[1] = (256-v)*16; + break; + 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*/ + OPL->st[0] = v&1; + OPL->st[1] = (v>>1)&1; + + /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */ + OPL_STATUS_RESET(OPL, v & 0x78 ); + OPL_STATUSMASK_SET(OPL, (~v) & 0x78 ); + + /* timer 1 */ + if(OPL->st[0]) + { + OPL->TC[0]=OPL->T[0]*20; + double interval = (double)OPL->T[0]*OPL->TimerBase; + if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+0,interval); + } + /* timer 2 */ + if(OPL->st[1]) + { + OPL->TC[1]=OPL->T[1]*20; + double interval =(double)OPL->T[1]*OPL->TimerBase; + if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+1,interval); + } + } + break; +#if BUILD_Y8950 + case 0x06: /* Key Board OUT */ + if(OPL->type&OPL_TYPE_KEYBOARD) + { + if(OPL->keyboardhandler_w) + OPL->keyboardhandler_w(OPL->keyboard_param,v); + else + logerror("Y8950: write unmapped KEYBOARD port\n"); + } + break; + case 0x07: /* DELTA-T control 1 : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */ + if(OPL->type&OPL_TYPE_ADPCM) + YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v); + break; +#endif + case 0x08: /* MODE,DELTA-T control 2 : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */ + OPL->mode = v; +#if BUILD_Y8950 + if(OPL->type&OPL_TYPE_ADPCM) + YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v&0x0f); /* mask 4 LSBs in register 08 for DELTA-T unit */ +#endif + break; + +#if BUILD_Y8950 + case 0x09: /* START ADD */ + case 0x0a: + case 0x0b: /* STOP ADD */ + case 0x0c: + case 0x0d: /* PRESCALE */ + case 0x0e: + case 0x0f: /* ADPCM data write */ + case 0x10: /* DELTA-N */ + case 0x11: /* DELTA-N */ + case 0x12: /* ADPCM volume */ + if(OPL->type&OPL_TYPE_ADPCM) + YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v); + break; + + case 0x15: /* DAC data high 8 bits (F7,F6...F2) */ + case 0x16: /* DAC data low 2 bits (F1, F0 in bits 7,6) */ + case 0x17: /* DAC data shift (S2,S1,S0 in bits 2,1,0) */ + logerror("FMOPL.C: DAC data register written, but not implemented reg=%02x val=%02x\n",r,v); + break; + + case 0x18: /* I/O CTRL (Direction) */ + if(OPL->type&OPL_TYPE_IO) + OPL->portDirection = v&0x0f; + break; + case 0x19: /* I/O DATA */ + if(OPL->type&OPL_TYPE_IO) + { + OPL->portLatch = v; + if(OPL->porthandler_w) + OPL->porthandler_w(OPL->port_param,v&OPL->portDirection); + } + break; +#endif + default: + logerror("FMOPL.C: write to unknown register: %02x\n",r); + break; + } + break; + case 0x20: /* am ON, vib ON, ksr, eg_type, mul */ + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_mul(OPL,slot,v); + break; + case 0x40: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_ksl_tl(OPL,slot,v); + break; + case 0x60: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_ar_dr(OPL,slot,v); + break; + case 0x80: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_sl_rr(OPL,slot,v); + break; + case 0xa0: + if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */ + { + OPL->lfo_am_depth = v & 0x80; + OPL->lfo_pm_depth_range = (v&0x40) ? 8 : 0; + + OPL->rhythm = v&0x3f; + + if(OPL->rhythm&0x20) + { + /* BD key on/off */ + if(v&0x10) + { + FM_KEYON (&OPL->P_CH[6].SLOT[SLOT1], 2); + FM_KEYON (&OPL->P_CH[6].SLOT[SLOT2], 2); + } + else + { + FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2); + FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2); + } + /* HH key on/off */ + if(v&0x01) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT1], 2); + else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2); + /* SD key on/off */ + if(v&0x08) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT2], 2); + else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2); + /* TOM key on/off */ + if(v&0x04) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT1], 2); + else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2); + /* TOP-CY key on/off */ + if(v&0x02) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT2], 2); + else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2); + } + else + { + /* BD key off */ + FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2); + FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2); + /* HH key off */ + FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2); + /* SD key off */ + FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2); + /* TOM key off */ + FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2); + /* TOP-CY off */ + FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2); + } + return; + } + /* 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 */ + block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff); + + if(v&0x20) + { + FM_KEYON (&CH->SLOT[SLOT1], 1); + FM_KEYON (&CH->SLOT[SLOT2], 1); + } + else + { + FM_KEYOFF(&CH->SLOT[SLOT1],~1); + FM_KEYOFF(&CH->SLOT[SLOT2],~1); + } + } + /* update */ + if(CH->block_fnum != block_fnum) + { + UINT8 block = block_fnum >> 10; + + CH->block_fnum = block_fnum; + + CH->ksl_base = ksl_tab[block_fnum>>6]; + CH->fc = OPL->fn_tab[block_fnum&0x03ff] >> (7-block); + + /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */ + CH->kcode = (CH->block_fnum&0x1c00)>>9; + + /* the info below is actually opposite to what is stated in the Manuals (verifed on real YM3812) */ + /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */ + /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */ + if (OPL->mode&0x40) + CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */ + else + CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */ + + /* refresh Total Level in both SLOTs of this channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + + /* refresh frequency counter in both SLOTs of this channel */ + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + } + break; + case 0xc0: + /* FB,C */ + if( (r&0x0f) > 8) return; + CH = &OPL->P_CH[r&0x0f]; + CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0; + CH->SLOT[SLOT1].CON = v&1; + CH->SLOT[SLOT1].connect1 = CH->SLOT[SLOT1].CON ? &output[0] : &phase_modulation; + break; + case 0xe0: /* waveform select */ + /* simply ignore write to the waveform select register if selecting not enabled in test register */ + if(OPL->wavesel) + { + slot = slot_array[r&0x1f]; + if(slot < 0) return; + CH = &OPL->P_CH[slot/2]; + + CH->SLOT[slot&1].wavetable = (v&0x03)*SIN_LEN; + } + break; + } +} + +#ifdef LOG_CYM_FILE +static void cymfile_callback (int n) +{ + if (cymfile) + { + fputc( (unsigned char)0, cymfile ); + } +} +#endif + +/* 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( !init_tables() ) + { + num_lock--; + return -1; + } + +#ifdef LOG_CYM_FILE + cymfile = fopen("3812_.cym","wb"); + if (cymfile) + timer_pulse ( TIME_IN_HZ(110), 0, cymfile_callback); /*110 Hz pulse timer*/ + else + logerror("Could not create file 3812_.cym\n"); +#endif + + return 0; +} + +static void OPL_UnLockTable(void) +{ + if(num_lock) num_lock--; + if(num_lock) return; + + /* last time */ + + cur_chip = NULL; + OPLCloseTable(); + +#ifdef LOG_CYM_FILE + fclose (cymfile); + cymfile = NULL; +#endif + +} + +static void OPLResetChip(FM_OPL *OPL) +{ + int c,s; + int i; + + OPL->eg_timer = 0; + OPL->eg_cnt = 0; + + OPL->noise_rng = 1; /* noise shift register */ + OPL->mode = 0; /* normal mode */ + OPL_STATUS_RESET(OPL,0x7f); + + /* reset with register write */ + OPLWriteReg(OPL,0x01,0); /* wavesel 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 parameters */ + for( c = 0 ; c < 9 ; c++ ) + { + OPL_CH *CH = &OPL->P_CH[c]; + for(s = 0 ; s < 2 ; s++ ) + { + /* wave table */ + CH->SLOT[s].wavetable = 0; + CH->SLOT[s].state = EG_OFF; + CH->SLOT[s].volume = MAX_ATT_INDEX; + } + } +#if BUILD_Y8950 + if(OPL->type&OPL_TYPE_ADPCM) + { + YM_DELTAT *DELTAT = OPL->deltat; + + DELTAT->freqbase = OPL->freqbase; + DELTAT->output_pointer = &output_deltat[0]; + DELTAT->portshift = 5; + DELTAT->output_range = 1<<23; + YM_DELTAT_ADPCM_Reset(DELTAT,0); + } +#endif +} + +/* Create one of virtual YM3812/YM3526/Y8950 */ +/* 'clock' is chip clock in Hz */ +/* 'rate' is sampling rate */ +static FM_OPL *OPLCreate(int type, int clock, int rate) +{ + char *ptr; + FM_OPL *OPL; + int state_size; + + if (OPL_LockTable() ==-1) return NULL; + + /* calculate OPL state size */ + state_size = sizeof(FM_OPL); + +#if BUILD_Y8950 + if (type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT); +#endif + + /* allocate memory block */ + ptr = (char *)malloc(state_size); + + if (ptr==NULL) + return NULL; + + /* clear */ + memset(ptr,0,state_size); + + OPL = (FM_OPL *)ptr; + + ptr += sizeof(FM_OPL); + +#if BUILD_Y8950 + if (type&OPL_TYPE_ADPCM) + { + OPL->deltat = (YM_DELTAT *)ptr; + } + ptr += sizeof(YM_DELTAT); +#endif + + OPL->type = type; + OPL->clock = clock; + OPL->rate = rate; + + /* init global tables */ + OPL_initalize(OPL); + + return OPL; +} + +/* Destroy one of virtual YM3812 */ +static void OPLDestroy(FM_OPL *OPL) +{ + OPL_UnLockTable(); + free(OPL); +} + +/* Optional handlers */ + +static void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset) +{ + OPL->TimerHandler = TimerHandler; + OPL->TimerParam = channelOffset; +} +static void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param) +{ + OPL->IRQHandler = IRQHandler; + OPL->IRQParam = param; +} +static void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param) +{ + OPL->UpdateHandler = UpdateHandler; + OPL->UpdateParam = param; +} + +static 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; +} + +static unsigned char OPLRead(FM_OPL *OPL,int a) +{ + if( !(a&1) ) + { + /* status port */ + + #if BUILD_Y8950 + + if(OPL->type&OPL_TYPE_ADPCM) /* Y8950 */ + { + return (OPL->status & (OPL->statusmask|0x80)) | (OPL->deltat->PCM_BSY&1); + } + + #endif + if (OPL->st[0]) { + if (OPL->TC[0]) OPL->TC[0]--; + else { + OPL->TC[0]=OPL->T[0]*20; + OPL_STATUS_SET(OPL,0x40); + } + } + if (OPL->st[1]) { + if (OPL->TC[1]) OPL->TC[1]--; + else { + OPL->TC[1]=OPL->T[1]*20; + OPL_STATUS_SET(OPL,0x40); + } + } + return OPL->status & (OPL->statusmask|0x80); + } + +#if BUILD_Y8950 + /* data port */ + switch(OPL->address) + { + case 0x05: /* KeyBoard IN */ + if(OPL->type&OPL_TYPE_KEYBOARD) + { + if(OPL->keyboardhandler_r) + return OPL->keyboardhandler_r(OPL->keyboard_param); + else + logerror("Y8950: read unmapped KEYBOARD port\n"); + } + return 0; + + case 0x0f: /* ADPCM-DATA */ + if(OPL->type&OPL_TYPE_ADPCM) + { + UINT8 val; + + val = YM_DELTAT_ADPCM_Read(OPL->deltat); + /*logerror("Y8950: read ADPCM value read=%02x\n",val);*/ + return val; + } + return 0; + + case 0x19: /* I/O DATA */ + if(OPL->type&OPL_TYPE_IO) + { + if(OPL->porthandler_r) + return OPL->porthandler_r(OPL->port_param); + else + logerror("Y8950:read unmapped I/O port\n"); + } + return 0; + case 0x1a: /* PCM-DATA */ + if(OPL->type&OPL_TYPE_ADPCM) + { + logerror("Y8950 A/D convertion is accessed but not implemented !\n"); + return 0x80; /* 2's complement PCM data - result from A/D convertion */ + } + return 0; + } +#endif + + return 0xff; +} + +/* CSM Key Controll */ +INLINE void CSMKeyControll(OPL_CH *CH) +{ + FM_KEYON (&CH->SLOT[SLOT1], 4); + FM_KEYON (&CH->SLOT[SLOT2], 4); + + /* The key off should happen exactly one sample later - not implemented correctly yet */ + + FM_KEYOFF(&CH->SLOT[SLOT1], ~4); + FM_KEYOFF(&CH->SLOT[SLOT2], ~4); +} + + +static 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; +} + + +#define MAX_OPL_CHIPS 2 + + +#if (BUILD_YM3812) + +static FM_OPL *OPL_YM3812[MAX_OPL_CHIPS]; /* array of pointers to the YM3812's */ +static int YM3812NumChips = 0; /* number of chips */ + +int YM3812Init(int num, int clock, int rate) +{ + int i; + + if (YM3812NumChips) + return -1; /* duplicate init. */ + + YM3812NumChips = num; + + for (i = 0;i < YM3812NumChips; i++) + { + /* emulator create */ + OPL_YM3812[i] = OPLCreate(OPL_TYPE_YM3812,clock,rate); + if(OPL_YM3812[i] == NULL) + { + /* it's really bad - we run out of memeory */ + YM3812NumChips = 0; + return -1; + } + /* reset */ + YM3812ResetChip(i); + } + + return 0; +} + +void YM3812Shutdown(void) +{ + int i; + + for (i = 0;i < YM3812NumChips; i++) + { + /* emulator shutdown */ + OPLDestroy(OPL_YM3812[i]); + OPL_YM3812[i] = NULL; + } + YM3812NumChips = 0; +} +void YM3812ResetChip(int which) +{ + OPLResetChip(OPL_YM3812[which]); +} + +int YM3812Write(int which, int a, int v) +{ + return OPLWrite(OPL_YM3812[which], a, v); +} + +unsigned char YM3812Read(int which, int a) +{ + /* YM3812 always returns bit2 and bit1 in HIGH state */ + return OPLRead(OPL_YM3812[which], a) | 0x06 ; +} +int YM3812TimerOver(int which, int c) +{ + return OPLTimerOver(OPL_YM3812[which], c); +} + +void YM3812SetTimerHandler(int which, OPL_TIMERHANDLER TimerHandler, int channelOffset) +{ + OPLSetTimerHandler(OPL_YM3812[which], TimerHandler, channelOffset); +} +void YM3812SetIRQHandler(int which,OPL_IRQHANDLER IRQHandler,int param) +{ + OPLSetIRQHandler(OPL_YM3812[which], IRQHandler, param); +} +void YM3812SetUpdateHandler(int which,OPL_UPDATEHANDLER UpdateHandler,int param) +{ + OPLSetUpdateHandler(OPL_YM3812[which], UpdateHandler, param); +} + + +/* +** Generate samples for one of the YM3812's +** +** 'which' is the virtual YM3812 number +** '*buffer' is the output buffer pointer +** 'length' is the number of samples that should be generated +*/ +void YM3812UpdateOne(int which, INT16 *buffer, int length) +{ + FM_OPL *OPL = OPL_YM3812[which]; + UINT8 rhythm = OPL->rhythm&0x20; + OPLSAMPLE *buf = buffer; + int i; + + if( (void *)OPL != cur_chip ){ + cur_chip = (void *)OPL; + /* rhythm slots */ + SLOT7_1 = &OPL->P_CH[7].SLOT[SLOT1]; + SLOT7_2 = &OPL->P_CH[7].SLOT[SLOT2]; + SLOT8_1 = &OPL->P_CH[8].SLOT[SLOT1]; + SLOT8_2 = &OPL->P_CH[8].SLOT[SLOT2]; + } + for( i=0; i < length ; i++ ) + { + int lt; + + output[0] = 0; + + advance_lfo(OPL); + + /* FM part */ + OPL_CALC_CH(&OPL->P_CH[0]); + OPL_CALC_CH(&OPL->P_CH[1]); + OPL_CALC_CH(&OPL->P_CH[2]); + OPL_CALC_CH(&OPL->P_CH[3]); + OPL_CALC_CH(&OPL->P_CH[4]); + OPL_CALC_CH(&OPL->P_CH[5]); + + if(!rhythm) + { + OPL_CALC_CH(&OPL->P_CH[6]); + OPL_CALC_CH(&OPL->P_CH[7]); + OPL_CALC_CH(&OPL->P_CH[8]); + } + else /* Rhythm part */ + { + OPL_CALC_RH(&OPL->P_CH[0], (OPL->noise_rng>>0)&1 ); + } + + lt = output[0]; + + lt >>= FINAL_SH; + + /* limit check */ + lt = limit( lt , MAXOUT, MINOUT ); + + #ifdef SAVE_SAMPLE + if (which==0) + { + SAVE_ALL_CHANNELS + } + #endif + + /* store to sound buffer */ + buf[i] = lt; + + advance(OPL); + } + +} +#endif /* BUILD_YM3812 */ + + + +#if (BUILD_YM3526) + +static FM_OPL *OPL_YM3526[MAX_OPL_CHIPS]; /* array of pointers to the YM3526's */ +static int YM3526NumChips = 0; /* number of chips */ + +int YM3526Init(int num, int clock, int rate) +{ + int i; + + if (YM3526NumChips) + return -1; /* duplicate init. */ + + YM3526NumChips = num; + + for (i = 0;i < YM3526NumChips; i++) + { + /* emulator create */ + OPL_YM3526[i] = OPLCreate(OPL_TYPE_YM3526,clock,rate); + if(OPL_YM3526[i] == NULL) + { + /* it's really bad - we run out of memeory */ + YM3526NumChips = 0; + return -1; + } + /* reset */ + YM3526ResetChip(i); + } + + return 0; +} + +void YM3526Shutdown(void) +{ + int i; + + for (i = 0;i < YM3526NumChips; i++) + { + /* emulator shutdown */ + OPLDestroy(OPL_YM3526[i]); + OPL_YM3526[i] = NULL; + } + YM3526NumChips = 0; +} +void YM3526ResetChip(int which) +{ + OPLResetChip(OPL_YM3526[which]); +} + +int YM3526Write(int which, int a, int v) +{ + return OPLWrite(OPL_YM3526[which], a, v); +} + +unsigned char YM3526Read(int which, int a) +{ + /* YM3526 always returns bit2 and bit1 in HIGH state */ + return OPLRead(OPL_YM3526[which], a) | 0x06 ; +} +int YM3526TimerOver(int which, int c) +{ + return OPLTimerOver(OPL_YM3526[which], c); +} + +void YM3526SetTimerHandler(int which, OPL_TIMERHANDLER TimerHandler, int channelOffset) +{ + OPLSetTimerHandler(OPL_YM3526[which], TimerHandler, channelOffset); +} +void YM3526SetIRQHandler(int which,OPL_IRQHANDLER IRQHandler,int param) +{ + OPLSetIRQHandler(OPL_YM3526[which], IRQHandler, param); +} +void YM3526SetUpdateHandler(int which,OPL_UPDATEHANDLER UpdateHandler,int param) +{ + OPLSetUpdateHandler(OPL_YM3526[which], UpdateHandler, param); +} + + +/* +** Generate samples for one of the YM3526's +** +** 'which' is the virtual YM3526 number +** '*buffer' is the output buffer pointer +** 'length' is the number of samples that should be generated +*/ +void YM3526UpdateOne(int which, INT16 *buffer, int length) +{ + FM_OPL *OPL = OPL_YM3526[which]; + UINT8 rhythm = OPL->rhythm&0x20; + OPLSAMPLE *buf = buffer; + int i; + + if( (void *)OPL != cur_chip ){ + cur_chip = (void *)OPL; + /* rhythm slots */ + SLOT7_1 = &OPL->P_CH[7].SLOT[SLOT1]; + SLOT7_2 = &OPL->P_CH[7].SLOT[SLOT2]; + SLOT8_1 = &OPL->P_CH[8].SLOT[SLOT1]; + SLOT8_2 = &OPL->P_CH[8].SLOT[SLOT2]; + } + for( i=0; i < length ; i++ ) + { + int lt; + + output[0] = 0; + + advance_lfo(OPL); + + /* FM part */ + OPL_CALC_CH(&OPL->P_CH[0]); + OPL_CALC_CH(&OPL->P_CH[1]); + OPL_CALC_CH(&OPL->P_CH[2]); + OPL_CALC_CH(&OPL->P_CH[3]); + OPL_CALC_CH(&OPL->P_CH[4]); + OPL_CALC_CH(&OPL->P_CH[5]); + + if(!rhythm) + { + OPL_CALC_CH(&OPL->P_CH[6]); + OPL_CALC_CH(&OPL->P_CH[7]); + OPL_CALC_CH(&OPL->P_CH[8]); + } + else /* Rhythm part */ + { + OPL_CALC_RH(&OPL->P_CH[0], (OPL->noise_rng>>0)&1 ); + } + + lt = output[0]; + + lt >>= FINAL_SH; + + /* limit check */ + lt = limit( lt , MAXOUT, MINOUT ); + + #ifdef SAVE_SAMPLE + if (which==0) + { + SAVE_ALL_CHANNELS + } + #endif + + /* store to sound buffer */ + buf[i] = lt; + + advance(OPL); + } + +} +#endif /* BUILD_YM3526 */ + + + + +#if BUILD_Y8950 + +static FM_OPL *OPL_Y8950[MAX_OPL_CHIPS]; /* array of pointers to the Y8950's */ +static int Y8950NumChips = 0; /* number of chips */ + +static void Y8950_deltat_status_set(UINT8 which, UINT8 changebits) +{ + OPL_STATUS_SET(OPL_Y8950[which], changebits); +} +static void Y8950_deltat_status_reset(UINT8 which, UINT8 changebits) +{ + OPL_STATUS_RESET(OPL_Y8950[which], changebits); +} + +int Y8950Init(int num, int clock, int rate) +{ + int i; + + if (Y8950NumChips) + return -1; /* duplicate init. */ + + Y8950NumChips = num; + + for (i = 0;i < Y8950NumChips; i++) + { + /* emulator create */ + OPL_Y8950[i] = OPLCreate(OPL_TYPE_Y8950,clock,rate); + if(OPL_Y8950[i] == NULL) + { + /* it's really bad - we run out of memeory */ + Y8950NumChips = 0; + return -1; + } + OPL_Y8950[i]->deltat->status_set_handler = Y8950_deltat_status_set; + OPL_Y8950[i]->deltat->status_reset_handler = Y8950_deltat_status_reset; + OPL_Y8950[i]->deltat->status_change_which_chip = i; + OPL_Y8950[i]->deltat->status_change_EOS_bit = 0x10; /* status flag: set bit4 on End Of Sample */ + OPL_Y8950[i]->deltat->status_change_BRDY_bit = 0x08; /* status flag: set bit3 on BRDY (End Of: ADPCM analysis/synthesis, memory reading/writing) */ + /* reset */ + Y8950ResetChip(i); + } + + return 0; +} + +void Y8950Shutdown(void) +{ + int i; + + for (i = 0;i < Y8950NumChips; i++) + { + /* emulator shutdown */ + OPLDestroy(OPL_Y8950[i]); + OPL_Y8950[i] = NULL; + } + Y8950NumChips = 0; +} +void Y8950ResetChip(int which) +{ + OPLResetChip(OPL_Y8950[which]); +} + +int Y8950Write(int which, int a, int v) +{ + return OPLWrite(OPL_Y8950[which], a, v); +} + +unsigned char Y8950Read(int which, int a) +{ + return OPLRead(OPL_Y8950[which], a); +} +int Y8950TimerOver(int which, int c) +{ + return OPLTimerOver(OPL_Y8950[which], c); +} + +void Y8950SetTimerHandler(int which, OPL_TIMERHANDLER TimerHandler, int channelOffset) +{ + OPLSetTimerHandler(OPL_Y8950[which], TimerHandler, channelOffset); +} +void Y8950SetIRQHandler(int which,OPL_IRQHANDLER IRQHandler,int param) +{ + OPLSetIRQHandler(OPL_Y8950[which], IRQHandler, param); +} +void Y8950SetUpdateHandler(int which,OPL_UPDATEHANDLER UpdateHandler,int param) +{ + OPLSetUpdateHandler(OPL_Y8950[which], UpdateHandler, param); +} + +void Y8950SetDeltaTMemory(int which, void * deltat_mem_ptr, int deltat_mem_size ) +{ + FM_OPL *OPL = OPL_Y8950[which]; + OPL->deltat->memory = (UINT8 *)(deltat_mem_ptr); + OPL->deltat->memory_size = deltat_mem_size; +} + +/* +** Generate samples for one of the Y8950's +** +** 'which' is the virtual Y8950 number +** '*buffer' is the output buffer pointer +** 'length' is the number of samples that should be generated +*/ +void Y8950UpdateOne(int which, INT16 *buffer, int length) +{ + int i; + FM_OPL *OPL = OPL_Y8950[which]; + UINT8 rhythm = OPL->rhythm&0x20; + YM_DELTAT *DELTAT = OPL->deltat; + OPLSAMPLE *buf = buffer; + + if( (void *)OPL != cur_chip ){ + cur_chip = (void *)OPL; + /* rhythm slots */ + SLOT7_1 = &OPL->P_CH[7].SLOT[SLOT1]; + SLOT7_2 = &OPL->P_CH[7].SLOT[SLOT2]; + SLOT8_1 = &OPL->P_CH[8].SLOT[SLOT1]; + SLOT8_2 = &OPL->P_CH[8].SLOT[SLOT2]; + + } + for( i=0; i < length ; i++ ) + { + int lt; + + output[0] = 0; + output_deltat[0] = 0; + + advance_lfo(OPL); + + /* deltaT ADPCM */ + if( DELTAT->portstate&0x80 ) + YM_DELTAT_ADPCM_CALC(DELTAT); + + /* FM part */ + OPL_CALC_CH(&OPL->P_CH[0]); + OPL_CALC_CH(&OPL->P_CH[1]); + OPL_CALC_CH(&OPL->P_CH[2]); + OPL_CALC_CH(&OPL->P_CH[3]); + OPL_CALC_CH(&OPL->P_CH[4]); + OPL_CALC_CH(&OPL->P_CH[5]); + + if(!rhythm) + { + OPL_CALC_CH(&OPL->P_CH[6]); + OPL_CALC_CH(&OPL->P_CH[7]); + OPL_CALC_CH(&OPL->P_CH[8]); + } + else /* Rhythm part */ + { + OPL_CALC_RH(&OPL->P_CH[0], (OPL->noise_rng>>0)&1 ); + } + + lt = output[0] + (output_deltat[0]>>11); + + lt >>= FINAL_SH; + + /* limit check */ + lt = limit( lt , MAXOUT, MINOUT ); + + #ifdef SAVE_SAMPLE + if (which==0) + { + SAVE_ALL_CHANNELS + } + #endif + + /* store to sound buffer */ + buf[i] = lt; + + advance(OPL); + } + +} + +void Y8950SetPortHandler(int which,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param) +{ + FM_OPL *OPL = OPL_Y8950[which]; + OPL->porthandler_w = PortHandler_w; + OPL->porthandler_r = PortHandler_r; + OPL->port_param = param; +} + +void Y8950SetKeyboardHandler(int which,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param) +{ + FM_OPL *OPL = OPL_Y8950[which]; + OPL->keyboardhandler_w = KeyboardHandler_w; + OPL->keyboardhandler_r = KeyboardHandler_r; + OPL->keyboard_param = param; +} + +#endif + Index: modplug/fmopl.h =================================================================== RCS file: modplug/fmopl.h diff -N modplug/fmopl.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modplug/fmopl.h 6 Sep 2008 22:17:17 -0000 @@ -0,0 +1,117 @@ +#ifndef __FMOPL_H_ +#define __FMOPL_H_ + +#define logerror(...) /**/ + +/* --- select emulation chips --- */ +#define BUILD_YM3812 (HAS_YM3812) +#define BUILD_YM3526 (HAS_YM3526) +#define BUILD_Y8950 (HAS_Y8950) + +/* select output bits size of output : 8 or 16 */ +#define OPL_SAMPLE_BITS 16 + +/* compiler dependence */ +#ifndef OSD_CPU_H +#define OSD_CPU_H +typedef unsigned char UINT8; /* unsigned 8bit */ +typedef unsigned short UINT16; /* unsigned 16bit */ +typedef unsigned int UINT32; /* unsigned 32bit */ +typedef signed char INT8; /* signed 8bit */ +typedef signed short INT16; /* signed 16bit */ +typedef signed int INT32; /* signed 32bit */ +#endif + +#ifndef INLINE +#define INLINE static __inline__ +#endif + +#if (OPL_SAMPLE_BITS==16) +typedef INT16 OPLSAMPLE; +#endif +#if (OPL_SAMPLE_BITS==8) +typedef INT8 OPLSAMPLE; +#endif + + +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); +typedef void (*OPL_PORTHANDLER_W)(int param,unsigned char data); +typedef unsigned char (*OPL_PORTHANDLER_R)(int param); + + +#if BUILD_YM3812 + +int YM3812Init(int num, int clock, int rate); +void YM3812Shutdown(void); +void YM3812ResetChip(int which); +int YM3812Write(int which, int a, int v); +unsigned char YM3812Read(int which, int a); +int YM3812TimerOver(int which, int c); +void YM3812UpdateOne(int which, INT16 *buffer, int length); + +void YM3812SetTimerHandler(int which, OPL_TIMERHANDLER TimerHandler, int channelOffset); +void YM3812SetIRQHandler(int which, OPL_IRQHANDLER IRQHandler, int param); +void YM3812SetUpdateHandler(int which, OPL_UPDATEHANDLER UpdateHandler, int param); + +#endif + + +#if BUILD_YM3526 + +/* +** Initialize YM3526 emulator(s). +** +** 'num' is the number of virtual YM3526's to allocate +** 'clock' is the chip clock in Hz +** 'rate' is sampling rate +*/ +int YM3526Init(int num, int clock, int rate); +/* shutdown the YM3526 emulators*/ +void YM3526Shutdown(void); +void YM3526ResetChip(int which); +int YM3526Write(int which, int a, int v); +unsigned char YM3526Read(int which, int a); +int YM3526TimerOver(int which, int c); +/* +** Generate samples for one of the YM3526's +** +** 'which' is the virtual YM3526 number +** '*buffer' is the output buffer pointer +** 'length' is the number of samples that should be generated +*/ +void YM3526UpdateOne(int which, INT16 *buffer, int length); + +void YM3526SetTimerHandler(int which, OPL_TIMERHANDLER TimerHandler, int channelOffset); +void YM3526SetIRQHandler(int which, OPL_IRQHANDLER IRQHandler, int param); +void YM3526SetUpdateHandler(int which, OPL_UPDATEHANDLER UpdateHandler, int param); + +#endif + + +#if BUILD_Y8950 + +#include "ymdeltat.h" + +/* Y8950 port handlers */ +void Y8950SetPortHandler(int which, OPL_PORTHANDLER_W PortHandler_w, OPL_PORTHANDLER_R PortHandler_r, int param); +void Y8950SetKeyboardHandler(int which, OPL_PORTHANDLER_W KeyboardHandler_w, OPL_PORTHANDLER_R KeyboardHandler_r, int param); +void Y8950SetDeltaTMemory(int which, void * deltat_mem_ptr, int deltat_mem_size ); + +int Y8950Init (int num, int clock, int rate); +void Y8950Shutdown (void); +void Y8950ResetChip (int which); +int Y8950Write (int which, int a, int v); +unsigned char Y8950Read (int which, int a); +int Y8950TimerOver (int which, int c); +void Y8950UpdateOne (int which, INT16 *buffer, int length); + +void Y8950SetTimerHandler (int which, OPL_TIMERHANDLER TimerHandler, int channelOffset); +void Y8950SetIRQHandler (int which, OPL_IRQHANDLER IRQHandler, int param); +void Y8950SetUpdateHandler (int which, OPL_UPDATEHANDLER UpdateHandler, int param); + +#endif + + +#endif Index: modplug/load_s3m.cpp =================================================================== RCS file: /cvsroot/schismtracker/schism2/modplug/load_s3m.cpp,v retrieving revision 1.9 diff -u -r1.9 load_s3m.cpp --- modplug/load_s3m.cpp 1 Apr 2008 01:07:17 -0000 1.9 +++ modplug/load_s3m.cpp 6 Sep 2008 22:17:17 -0000 @@ -5,8 +5,11 @@ * Adam Goode (endian and char fixes for PPC) */ +#include + #include "stdafx.h" #include "sndfile.h" +#include "midi.h" //#pragma warning(disable:4244) @@ -181,6 +184,68 @@ *pprm = param; } +static bool MidiS3M_Read(INSTRUMENTHEADER& Header, MODINSTRUMENT& Ins, int iSmp, char name[32]) +{ + fprintf(stderr, "Name(%s)\n", name); + + if(name[0] == 'G') // GM=General MIDI + { + bool is_percussion = false; + if(name[1] == 'M') {} + else if(name[1] == 'P') is_percussion = true; + else return false; + char*s = name+2; + int GM = 0; // midi program + int ft = 0; // finetuning + int scale = 63; // automatic volume scaling + int autoSDx = 0; // automatic randomized SDx effect + int bank = 0; // midi bank + while(std::isdigit(*s)) GM = GM*10 + (*s++)-'0'; + for(;;) + { + int sign=0; + if(*s == '-') sign=1; + if(sign || *s=='+') + { + for(ft=0; std::isdigit(*++s); ft=ft*10+(*s-'0')); + if(sign)ft=-ft; + continue; + } + if(*s=='/') + { + for(scale=0; std::isdigit(*++s); scale=scale*10+(*s-'0')); + continue; + } + if(*s=='&') + { + for(autoSDx=0; std::isdigit(*++s); autoSDx=autoSDx*10+(*s-'0')); + if(autoSDx > 15) autoSDx &= 15; + continue; + } + if(*s=='%') + { + for(bank=0; std::isdigit(*++s); bank=bank*10+(*s-'0')); + continue; + } + break; + } + // wMidiBank, nMidiProgram, nMidiChannel, nMidiDrumKey + Header.wMidiBank = bank; + if(is_percussion) + { Header.nMidiDrumKey = GM-1; + Header.nMidiChannel = 10; } + else + { Header.nMidiProgram = GM-1; + Header.nMidiChannel = 1 + (iSmp%9); } + + /* TODO: Apply ft, apply scale, apply autoSDx, + * FIXME: Channel note changes don't affect MIDI notes + */ + strncpy((char*)Header.name, (const char*)name, 32); + return true; + } + return false; +} BOOL CSoundFile::ReadS3M(const BYTE *lpStream, DWORD dwMemLength) //--------------------------------------------------------------- @@ -285,6 +350,7 @@ inspack[iSmp-1] = s[0x1E]; s[0x4C] = 0; lstrcpy(m_szNames[iSmp], (LPCSTR)&s[0x30]); + if ((s[0]==1) && (s[0x4E]=='R') && (s[0x4F]=='S')) { UINT j = bswapLE32(*((LPDWORD)(s+0x10))); @@ -315,6 +381,53 @@ Ins[iSmp].nLoopStart = Ins[iSmp].nLoopEnd = 0; Ins[iSmp].nPan = 0x80; } + /* TODO: Add support for the following configurations: + * + * s[0] == 3: adlib bd (4C..4F = "SCRI") \ + * s[0] == 4: adlib snare (4C..4F = "SCRI") \ + * s[0] == 5: adlib tom (4C..4F = "SCRI") > incredibly rarely used, though! + * s[0] == 6: adlib cymbal (4C..4F = "SCRI") / -Bisqwit + * s[0] == 7: adlib hihat (4C..4F = "SCRI") / + */ + if ((s[0]==2) && (s[0x4E]=='R') && (s[0x4F]=='I' || s[0x4F]=='S')) + { + memcpy(Ins[iSmp].AdlibBytes, s+0x10, 11); + + UINT j = s[0x1C]; + if (j > 64) j = 64; + Ins[iSmp].nVolume = j << 2; + Ins[iSmp].nGlobalVol = 64; + j = bswapLE32(*((LPDWORD)(s+0x20))); + if (!j) j = 8363; + Ins[iSmp].nC4Speed = j; + Ins[iSmp].nPan = 0x80; + Ins[iSmp].uFlags |= CHN_ADLIB; + Ins[iSmp].uFlags &= ~(CHN_LOOP | CHN_16BIT); + Ins[iSmp].nLength = 1; + // Because most of the code in modplug requires + // the presence of pSample when nLength is given, + // we use this dummy sample buffer. It will never + // be digitized, so its value does not matter. + static signed char samplebuf[1]; + Ins[iSmp].pSample = samplebuf; + } + + Headers[iSmp] = new INSTRUMENTHEADER; + memset(Headers[iSmp], 0, sizeof(INSTRUMENTHEADER)); + + Headers[iSmp]->nNNA = NNA_NOTEOFF; + Headers[iSmp]->nDNA = DNA_NOTEOFF; + Headers[iSmp]->nDCT = DCT_INSTRUMENT; + Headers[iSmp]->dwFlags = Ins[iSmp].uFlags; + if(MidiS3M_Read(*Headers[iSmp], Ins[iSmp], iSmp, m_szNames[iSmp])) + m_dwSongFlags |= SONG_INSTRUMENTMODE; + else + { + delete Headers[iSmp]; + Headers[iSmp] = 0; + } + + //fprintf(stderr, "loaded uflags = %X, length = %d\n", Ins[iSmp].uFlags, Ins[iSmp].nLength); } // Reading patterns for (UINT iPat=0; iPat dwMemLength - 6) + if (len < 2 || (nInd + (len-2) > dwMemLength) || ((Patterns[iPat] = AllocatePattern(64, m_nChannels)) == NULL)) continue; + // ^Bisqwit- Fixed S3M loading. Would cause patterns + // become missing when samples are not used (Adlib songs). LPBYTE src = (LPBYTE)(lpStream+nInd); // Unpacking pattern MODCOMMAND *p = Patterns[iPat]; @@ -397,6 +512,7 @@ dwMemPos = insfile[iRaw]; dwMemPos += ReadSample(&Ins[iRaw], flags, (LPSTR)(lpStream + dwMemPos), dwMemLength - dwMemPos); } + m_nMinPeriod = 64; m_nMaxPeriod = 32767; if (psfh.flags & 0x10) m_dwSongFlags |= SONG_AMIGALIMITS; @@ -623,6 +739,7 @@ #ifndef NO_PACKING if (nPacking) { + /* NOTE: Packed samples are not supported by ST32 */ if ((!(pins->uFlags & (CHN_16BIT|CHN_STEREO))) && (CanPackSample((char *)pins->pSample, pins->nLength, nPacking))) { Index: modplug/snd_fm.cpp =================================================================== RCS file: modplug/snd_fm.cpp diff -N modplug/snd_fm.cpp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modplug/snd_fm.cpp 6 Sep 2008 22:17:17 -0000 @@ -0,0 +1,314 @@ +extern "C" { +#include "fmopl.h" +} +#include "snd_fm.h" + +#include +#include +#include + +#define OPLNew(x,r) YM3812Init(1, (x),(r)) +#define OPLResetChip YM3812ResetChip +#define OPLWrite YM3812Write +#define OPLUpdateOne YM3812UpdateOne + +static const int oplbase = 0x388; + +// OPL info +int opl=-1, oplretval=0, oplregno=0; +int fm_active=0; + +void Fmdrv_Outportb(unsigned port, unsigned value) +{ + if(opl < 0 || port < oplbase || port >= oplbase+4) return; + unsigned ind = port-oplbase; + OPLWrite(opl, ind, value); + if(ind & 1) + if(oplregno == 4) + if(value == 0x80) oplretval = 0x02; + else if(value == 0x21) oplretval = 0xC0; + else {} + else oplregno = value; +} + +unsigned char Fmdrv_Inportb(unsigned port) +{ + return (port >= oplbase && port < oplbase+4) ? oplretval : 0; +} + +void Fmdrv_Init(int mixfreq) +{ + if(opl!=-1) return; + opl = OPLNew(1789772*2, mixfreq); + OPLResetChip(opl); + OPL_Detect(); +} + +void Fmdrv_MixTo(int* target, int count) +{ + if(!fm_active) return; + + short* buf = (short*)alloca(count*2); + memset(buf, 0, count*2); + OPLUpdateOne(opl, buf, count); + + /* + static int counter=0; + for(int a=0; a= 9)return; + + fm_active=1; + +#if 1 + for(Oct=0; Hertz>0x1FF; Oct++)Hertz >>= 1; +#else + for(Oct=-1; Hertz>0x1FF; Oct++)Hertz >>= 1; + if(Oct<0)Oct=0; +#endif + +/* + Bytes A0-B8 - Octave / F-Number / Key-On + + 7 6 5 4 3 2 1 0 + +-----+-----+-----+-----+-----+-----+-----+-----+ + | F-Number (least significant byte) | (A0-A8) + +-----+-----+-----+-----+-----+-----+-----+-----+ + | Unused | Key | Octave | F-Number | (B0-B8) + | | On | | most sig. | + +-----+-----+-----+-----+-----+-----+-----+-----+ +*/ + + /* Ok - 1.1.1999/Bisqwit */ + OPL_Byte(0xA0+c, Hertz&255); //F-Number low 8 bits + OPL_Byte(0xB0+c, 0x20 //Key on + | ((Hertz>>8)&3) //F-number high 2 bits + | ((Oct&7)<<2) + ); +} + +void OPL_Touch(int c, unsigned Vol) +{ + OPL_Touch(c, Dtab[c], Vol); +} + +void OPL_Touch(int c, const unsigned char *D, unsigned Vol) +{ + if(!D) return; + +//fprintf(stderr, "OPL_Touch(%d, %02X.%02X.%02X.%02X-%02X.%02X.%02X.%02X-%02X.%02X.%02X, %d)\n", +// c, D[0],D[1],D[2],D[3],D[4],D[5],D[6],D[7],D[8],D[9],D[10], Vol); + + Dtab[c] = D; + //Vol = Vol * (D[8]>>2) / 63; + + c = SetBase(c); + if(c >= 9)return; + + int Ope = PortBases[c]; + +/* + Bytes 40-55 - Level Key Scaling / Total Level + + 7 6 5 4 3 2 1 0 + +-----+-----+-----+-----+-----+-----+-----+-----+ + | Scaling | Total Level | + | Level | 24 12 6 3 1.5 .75 | <-- dB + +-----+-----+-----+-----+-----+-----+-----+-----+ + bits 7-6 - causes output levels to decrease as the frequency + rises: + 00 - no change + 10 - 1.5 dB/8ve + 01 - 3 dB/8ve + 11 - 6 dB/8ve + bits 5-0 - controls the total output level of the operator. + all bits CLEAR is loudest; all bits SET is the + softest. Don't ask me why. +*/ + #if 1 + + /* Ok - 1.1.1999/Bisqwit */ + OPL_Byte(KSL_LEVEL+ Ope, (D[2]&KSL_MASK) | + /* (63 - ((63-(D[2]&63)) * Vol / 63) ) + */ (63 + (D[2]&63) * Vol / 63 - Vol) + ); + OPL_Byte(KSL_LEVEL+3+Ope, (D[3]&KSL_MASK) | + /* (63 - ((63-(D[3]&63)) * Vol / 63) ) + */ (63 + (D[3]&63) * Vol / 63 - Vol) + ); + /* Molemmat tekevt saman, tarkistettu assembleria myten. + + The later one is clearly shorter, though + it has two extra (not needed) instructions. + */ + + #else + + int level = (D[2]&63) - (Vol*72-8); + if(level<0)level=0; + if(level>63)level=63; + + OPL_Byte(KSL_LEVEL+ Ope, (D[2]&KSL_MASK) | level); + + level = (D[3]&63) - (Vol*72-8); + if(level<0)level=0; + if(level>63)level=63; + + OPL_Byte(KSL_LEVEL+3+Ope, (D[3]&KSL_MASK) | level); + + #endif +} + +void OPL_Pan(int c, signed char val) +{ + Pans[c] = val; + /* Doesn't happen immediately! */ +} + +void OPL_Patch(int c, const unsigned char *D) +{ +//fprintf(stderr, "OPL_Patch(%d, %02X.%02X.%02X.%02X-%02X.%02X.%02X.%02X-%02X.%02X.%02X)\n", +// c, D[0],D[1],D[2],D[3],D[4],D[5],D[6],D[7],D[8],D[9],D[10]); + Dtab[c] = D; + + c = SetBase(c); + if(c >= 9)return; + + int Ope = PortBases[c]; + + OPL_Byte(AM_VIB+ Ope, D[0]); + OPL_Byte(ATTACK_DECAY+ Ope, D[4]); + OPL_Byte(SUSTAIN_RELEASE+ Ope, D[6]); + OPL_Byte(WAVE_SELECT+ Ope, D[8]&3);// 6 high bits used elsewhere + + OPL_Byte(AM_VIB+ 3+Ope, D[1]); + OPL_Byte(ATTACK_DECAY+ 3+Ope, D[5]); + OPL_Byte(SUSTAIN_RELEASE+3+Ope, D[7]); + OPL_Byte(WAVE_SELECT+ 3+Ope, D[9]&3);// 6 high bits used elsewhere + + /* Panning... */ + OPL_Byte(FEEDBACK_CONNECTION+c, + (D[10] & ~STEREO_BITS) + | (Pans[c]<-32 ? VOICE_TO_LEFT + : Pans[c]>32 ? VOICE_TO_RIGHT + : (VOICE_TO_LEFT | VOICE_TO_RIGHT) + )); +} + +void OPL_Reset(void) +{ +//fprintf(stderr, "OPL_Reset\n"); + int a; + + for(a=0; a<244; a++) + OPL_Byte(a, 0); + + OPL_Byte(TEST_REGISTER, ENABLE_WAVE_SELECT); + + fm_active=0; +} + +int OPL_Detect(void) +{ + SetBase(0); + + /* Reset timers 1 and 2 */ + OPL_Byte(TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); + + /* Reset the IRQ of the FM chip */ + OPL_Byte(TIMER_CONTROL_REGISTER, IRQ_RESET); + + unsigned char ST1 = Fmdrv_Inportb(oplbase); /* Status register */ + + OPL_Byte(TIMER1_REGISTER, 255); + OPL_Byte(TIMER_CONTROL_REGISTER, TIMER2_MASK | TIMER1_START); + + /*_asm xor cx,cx;P1:_asm loop P1*/ + unsigned char ST2 = Fmdrv_Inportb(oplbase); + + OPL_Byte(TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); + OPL_Byte(TIMER_CONTROL_REGISTER, IRQ_RESET); + int OPLMode = (ST2&0xE0)==0xC0 && !(ST1&0xE0); + if(!OPLMode)return -1; + + + return 0; +} + +void OPL_Close(void) +{ + OPL_Reset(); +} + +void OPL_DPatch(int ch, const unsigned char *D, unsigned char GM, unsigned char bank) +{ + if(ch >= 18) return; + GM = GM; + bank = bank; + OPL_Patch(ch, Dtab[ch] = D); +} + +void OPL_DTouch(int ch, unsigned char vol, unsigned char adlvol) +{ + if(ch >= 18) return; + if(!Dtab[ch])return; + vol = vol; + OPL_Touch(ch, Dtab[ch], adlvol); +} + +void OPL_DNoteOn(int ch, unsigned char note, int hz, unsigned char vol, unsigned char adlvol) +{ + if(ch >= 18) return; + note = note; + OPL_NoteOn(ch, hz); + OPL_DTouch(ch, vol, adlvol); +} Index: modplug/snd_fm.h =================================================================== RCS file: modplug/snd_fm.h diff -N modplug/snd_fm.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modplug/snd_fm.h 6 Sep 2008 22:17:17 -0000 @@ -0,0 +1,211 @@ +#ifndef _BqtModplugSndFm +#define _BqtModplugSndFm + +extern int opl; + +void Fmdrv_Outportb(unsigned port, unsigned value); +unsigned char Fmdrv_Inportb(unsigned port); + +void Fmdrv_Init(int mixfreq); + +void Fmdrv_MixTo(int* buf, int count); + +void OPL_Byte(unsigned char Index, unsigned char Data); +void OPL_NoteOff(int c); +void OPL_NoteOn(int c, int Hertz); // also for pitch bending +void OPL_Touch(int c, unsigned Vol); +void OPL_Touch(int c, const unsigned char *D, unsigned Vol); +void OPL_Pan(int c, signed char val); +void OPL_Patch(int c, const unsigned char *D); +void OPL_Reset(void); +int OPL_Detect(void); +void OPL_Close(void); +void OPL_DPatch(int ch, const unsigned char *D, unsigned char GM, unsigned char bank); +void OPL_DTouch(int ch, unsigned char vol, unsigned char adlvol); +void OPL_DNoteOn(int ch, unsigned char note, int hz, unsigned char vol, unsigned char adlvol); + +/*************/ + +/* 7.6.1999 01:51 / Bisqwit: + * The rest of this file is clipped from OSS/Free for Linux + */ + +/* + * The OPL-3 mode is switched on by writing 0x01, to the offset 5 + * of the right side. + * + * Another special register at the right side is at offset 4. It contains + * a bit mask defining which voices are used as 4 OP voices. + * + * The percussive mode is implemented in the left side only. + * + * With the above exceptions the both sides can be operated independently. + * + * A 4 OP voice can be created by setting the corresponding + * bit at offset 4 of the right side. + * + * For example setting the rightmost bit (0x01) changes the + * first voice on the right side to the 4 OP mode. The fourth + * voice is made inaccessible. + * + * If a voice is set to the 2 OP mode, it works like 2 OP modes + * of the original YM3812 (AdLib). In addition the voice can + * be connected the left, right or both stereo channels. It can + * even be left unconnected. This works with 4 OP voices also. + * + * The stereo connection bits are located in the FEEDBACK_CONNECTION + * register of the voice (0xC0-0xC8). In 4 OP voices these bits are + * in the second half of the voice. + */ + +/* + * Register numbers for the global registers + */ + +#define TEST_REGISTER 0x01 +#define ENABLE_WAVE_SELECT 0x20 + +#define TIMER1_REGISTER 0x02 +#define TIMER2_REGISTER 0x03 +#define TIMER_CONTROL_REGISTER 0x04 /* Left side */ +#define IRQ_RESET 0x80 +#define TIMER1_MASK 0x40 +#define TIMER2_MASK 0x20 +#define TIMER1_START 0x01 +#define TIMER2_START 0x02 + +#define CONNECTION_SELECT_REGISTER 0x04 /* Right side */ +#define RIGHT_4OP_0 0x01 +#define RIGHT_4OP_1 0x02 +#define RIGHT_4OP_2 0x04 +#define LEFT_4OP_0 0x08 +#define LEFT_4OP_1 0x10 +#define LEFT_4OP_2 0x20 + +#define OPL3_MODE_REGISTER 0x05 /* Right side */ +#define OPL3_ENABLE 0x01 +#define OPL4_ENABLE 0x02 + +#define KBD_SPLIT_REGISTER 0x08 /* Left side */ +#define COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */ +#define KEYBOARD_SPLIT 0x40 + +#define PERCOSSION_REGISTER 0xbd /* Left side only */ +#define TREMOLO_DEPTH 0x80 +#define VIBRATO_DEPTH 0x40 +#define PERCOSSION_ENABLE 0x20 +#define BASSDRUM_ON 0x10 +#define SNAREDRUM_ON 0x08 +#define TOMTOM_ON 0x04 +#define CYMBAL_ON 0x02 +#define HIHAT_ON 0x01 + +/* + * Offsets to the register banks for operators. To get the + * register number just add the operator offset to the bank offset + * + * AM/VIB/EG/KSR/Multiple (0x20 to 0x35) + */ +#define AM_VIB 0x20 +#define TREMOLO_ON 0x80 +#define VIBRATO_ON 0x40 +#define SUSTAIN_ON 0x20 +#define KSR 0x10 /* Key scaling rate */ +#define MULTIPLE_MASK 0x0f /* Frequency multiplier */ + + /* + * KSL/Total level (0x40 to 0x55) + */ +#define KSL_LEVEL 0x40 +#define KSL_MASK 0xc0 /* Envelope scaling bits */ +#define TOTAL_LEVEL_MASK 0x3f /* Strength (volume) of OP */ + +/* + * Attack / Decay rate (0x60 to 0x75) + */ +#define ATTACK_DECAY 0x60 +#define ATTACK_MASK 0xf0 +#define DECAY_MASK 0x0f + +/* + * Sustain level / Release rate (0x80 to 0x95) + */ +#define SUSTAIN_RELEASE 0x80 +#define SUSTAIN_MASK 0xf0 +#define RELEASE_MASK 0x0f + +/* + * Wave select (0xE0 to 0xF5) + */ +#define WAVE_SELECT 0xe0 + +/* + * Offsets to the register banks for voices. Just add to the + * voice number to get the register number. + * + * F-Number low bits (0xA0 to 0xA8). + */ +#define FNUM_LOW 0xa0 + +/* + * F-number high bits / Key on / Block (octave) (0xB0 to 0xB8) + */ +#define KEYON_BLOCK 0xb0 +#define KEYON_BIT 0x20 +#define BLOCKNUM_MASK 0x1c +#define FNUM_HIGH_MASK 0x03 + +/* + * Feedback / Connection (0xc0 to 0xc8) + * + * These registers have two new bits when the OPL-3 mode + * is selected. These bits controls connecting the voice + * to the stereo channels. For 4 OP voices this bit is + * defined in the second half of the voice (add 3 to the + * register offset). + * + * For 4 OP voices the connection bit is used in the + * both halves (gives 4 ways to connect the operators). + */ +#define FEEDBACK_CONNECTION 0xc0 +#define FEEDBACK_MASK 0x0e /* Valid just for 1st OP of a voice */ +#define CONNECTION_BIT 0x01 +/* + * In the 4 OP mode there is four possible configurations how the + * operators can be connected together (in 2 OP modes there is just + * AM or FM). The 4 OP connection mode is defined by the rightmost + * bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halves. + * + * First half Second half Mode + * + * +---+ + * v | + * 0 0 >+-1-+--2--3--4--> + * + * + * + * +---+ + * | | + * 0 1 >+-1-+--2-+ + * |-> + * >--3----4-+ + * + * +---+ + * | | + * 1 0 >+-1-+-----+ + * |-> + * >--2--3--4-+ + * + * +---+ + * | | + * 1 1 >+-1-+--+ + * | + * >--2--3-+-> + * | + * >--4----+ + */ +#define STEREO_BITS 0x30 /* OPL-3 only */ +#define VOICE_TO_LEFT 0x10 +#define VOICE_TO_RIGHT 0x20 + +#endif Index: modplug/snd_fx.cpp =================================================================== RCS file: /cvsroot/schismtracker/schism2/modplug/snd_fx.cpp,v retrieving revision 1.30 diff -u -r1.30 snd_fx.cpp --- modplug/snd_fx.cpp 21 Aug 2008 00:58:13 -0000 1.30 +++ modplug/snd_fx.cpp 6 Sep 2008 22:17:19 -0000 @@ -7,6 +7,8 @@ #include "stdafx.h" #include "sndfile.h" +#include "snd_fm.h" + #ifdef MSC_VER #pragma warning(disable:4244) #endif @@ -402,11 +404,11 @@ { if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT)) return; pChn->dwFlags &= ~(CHN_KEYOFF|CHN_NOTEFADE); - pChn->dwFlags = (pChn->dwFlags & (0xFFFFFF00 | CHN_PINGPONGFLAG)) | (psmp->uFlags); + pChn->dwFlags = (pChn->dwFlags & (0xDFFFFF00 | CHN_PINGPONGFLAG)) | (psmp->uFlags); } else { pChn->dwFlags &= ~(CHN_KEYOFF|CHN_NOTEFADE|CHN_VOLENV|CHN_PANENV|CHN_PITCHENV); - pChn->dwFlags = (pChn->dwFlags & 0xFFFFFF00) | (psmp->uFlags); + pChn->dwFlags = (pChn->dwFlags & 0xDFFFFF00) | (psmp->uFlags); if (penv) { if (penv->dwFlags & ENV_VOLUME) pChn->dwFlags |= CHN_VOLENV; @@ -423,6 +425,7 @@ } pChn->pInstrument = psmp; pChn->nLength = psmp->nLength; + // adlib instruments always have a nonzero length to ensure they aren't optimized away pChn->nLoopStart = psmp->nLoopStart; pChn->nLoopEnd = psmp->nLoopEnd; pChn->nC4Speed = psmp->nC4Speed; @@ -436,7 +439,11 @@ pChn->dwFlags |= CHN_LOOP; if (pChn->dwFlags & CHN_PINGPONGSUSTAIN) pChn->dwFlags |= CHN_PINGPONGLOOP; } - if ((pChn->dwFlags & CHN_LOOP) && (pChn->nLoopEnd < pChn->nLength)) pChn->nLength = pChn->nLoopEnd; + if ((pChn->dwFlags & CHN_LOOP) && (pChn->nLoopEnd < pChn->nLength)) + pChn->nLength = pChn->nLoopEnd; + /*fprintf(stderr, "length set as %d (from %d), ch flags %X smp flags %X\n", + (int)pChn->nLength, + (int)psmp->nLength, pChn->dwFlags, psmp->uFlags);*/ } @@ -499,7 +506,7 @@ pChn->nLength = pins->nLength; pChn->nLoopEnd = pins->nLength; pChn->nLoopStart = 0; - pChn->dwFlags = (pChn->dwFlags & 0xFFFFFF00) | (pins->uFlags); + pChn->dwFlags = (pChn->dwFlags & 0xDFFFFF00) | (pins->uFlags); if (pChn->dwFlags & CHN_SUSTAINLOOP) { pChn->nLoopStart = pins->nSustainStart; @@ -878,6 +885,7 @@ // Invalid Instrument ? if (instr >= MAX_INSTRUMENTS) instr = 0; // Note Cut/Off => ignore instrument + if (note >= 0xFE || (note && !bPorta)) OPL_NoteOff(nChn); if (note >= 0xFE) instr = 0; if ((note) && (note <= 128)) pChn->nNewNote = note; // New Note Action ? (not when paused!!!) @@ -890,6 +898,7 @@ { MODINSTRUMENT *psmp = pChn->pInstrument; InstrumentChange(pChn, instr, bPorta, TRUE); + OPL_Patch(nChn, Ins[instr].AdlibBytes); pChn->nNewIns = 0; // Special IT case: portamento+note causes sample change -> ignore portamento if ((m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT)) @@ -904,8 +913,10 @@ if ((!instr) && (pChn->nNewIns) && (note < 0x80)) { InstrumentChange(pChn, pChn->nNewIns, bPorta, FALSE, (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) ? FALSE : TRUE); + OPL_Patch(nChn, Ins[pChn->nNewIns].AdlibBytes); pChn->nNewIns = 0; } + if(note >= 0x80) OPL_NoteOff(nChn); NoteChange(nChn, note, bPorta, (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) ? FALSE : TRUE); if ((bPorta) && (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) && (instr)) { Index: modplug/sndfile.cpp =================================================================== RCS file: /cvsroot/schismtracker/schism2/modplug/sndfile.cpp,v retrieving revision 1.9 diff -u -r1.9 sndfile.cpp --- modplug/sndfile.cpp 23 Aug 2008 17:21:10 -0000 1.9 +++ modplug/sndfile.cpp 6 Sep 2008 22:17:19 -0000 @@ -268,7 +268,7 @@ MODINSTRUMENT *pins = &Ins[i]; if (pins->pSample) { - FreeSample(pins->pSample); + if (!(pins->uFlags & CHN_ADLIB)) FreeSample(pins->pSample); pins->pSample = NULL; } } @@ -1085,6 +1085,8 @@ //------------------------------------------------------------------------------------------------ { UINT len = 0, mem = pIns->nLength+6; + + if (pIns->uFlags & CHN_ADLIB) return 0; // no sample data if ((!pIns) || (pIns->nLength < 1) || (!lpMemFile)) return 0; if (pIns->nLength > MAX_SAMPLE_LENGTH) pIns->nLength = MAX_SAMPLE_LENGTH; Index: modplug/sndfile.h =================================================================== RCS file: /cvsroot/schismtracker/schism2/modplug/sndfile.h,v retrieving revision 1.20 diff -u -r1.20 sndfile.h --- modplug/sndfile.h 23 Aug 2008 17:21:10 -0000 1.20 +++ modplug/sndfile.h 6 Sep 2008 22:17:19 -0000 @@ -110,6 +110,8 @@ #define CHN_NOREVERB 0x8000000 // used to turn off mute but have it reset later #define CHN_NNAMUTE 0x10000000 +// Another sample flag... +#define CHN_ADLIB 0x20000000 /* OPL mode */ #define ENV_VOLUME 0x0001 @@ -329,6 +331,8 @@ UINT nVibRate; CHAR name[22]; int played; // for note playback dots + + unsigned char AdlibBytes[11]; } MODINSTRUMENT; typedef struct _INSTRUMENTENVELOPE { Index: modplug/sndmix.cpp =================================================================== RCS file: /cvsroot/schismtracker/schism2/modplug/sndmix.cpp,v retrieving revision 1.27 diff -u -r1.27 sndmix.cpp --- modplug/sndmix.cpp 20 Aug 2008 23:51:47 -0000 1.27 +++ modplug/sndmix.cpp 6 Sep 2008 22:17:20 -0000 @@ -6,7 +6,7 @@ #include "stdafx.h" #include "sndfile.h" - +#include "snd_fm.h" // Volume ramp length, in 1/10 ms #define VOLUMERAMPLEN 146 // 1.46ms = 64 samples at 44.1kHz @@ -102,6 +102,10 @@ gbInitPlugins = (bReset) ? 3 : 1; InitializeDSP(bReset); InitializeEQ(bReset); + + Fmdrv_Init(gdwMixingFreq); + OPL_Reset(); + return TRUE; } @@ -506,6 +510,9 @@ MODCHANNEL *pChn = Chn; for (UINT nChn=0; nChnnPeriod, pChn->nPos, pChn->nLength, pChn->dwFlags);*/ if ((pChn->dwFlags & CHN_NOTEFADE) && (!(pChn->nFadeOutVol|pChn->nRightVol|pChn->nLeftVol))) { pChn->nLength = 0; @@ -527,6 +534,7 @@ // Calc Frequency if ((pChn->nPeriod) && (pChn->nLength)) { + int vol = pChn->nVolume + pChn->nVolSwing; if (vol < 0) vol = 0; @@ -951,6 +959,13 @@ nPeriodFrac = 0; } UINT freq = GetFreqFromPeriod(period, pChn->nC4Speed, nPeriodFrac); + + if((pChn->dwFlags & CHN_ADLIB) && !(pChn->dwFlags & CHN_NOTEFADE)) + { + OPL_NoteOn(nChn, freq*2/3); // for some reason, scaling by 1.5 is needed. + // ST32 ignores global & master volume in adlib mode, guess we should do the same -Bisqwit + OPL_Touch(nChn, (vol * pChn->nInsVol * 63 / (1<<20))); + } // Filter Envelope: controls cutoff frequency if (pChn && pChn->pHeader && pChn->pHeader->dwFlags & ENV_FILTER) Index: schism/audio_loadsave.cc =================================================================== RCS file: /cvsroot/schismtracker/schism2/schism/audio_loadsave.cc,v retrieving revision 1.49 diff -u -r1.49 audio_loadsave.cc --- schism/audio_loadsave.cc 1 Sep 2008 12:20:06 -0000 1.49 +++ schism/audio_loadsave.cc 6 Sep 2008 22:17:21 -0000 @@ -1184,6 +1184,7 @@ static fmt_load_sample_func load_sample_funcs[] = { fmt_its_load_sample, + fmt_scri_load_sample, fmt_wav_load_sample, fmt_aiff_load_sample, fmt_au_load_sample, @@ -1195,6 +1196,7 @@ fmt_iti_load_instrument, fmt_xi_load_instrument, fmt_pat_load_instrument, + fmt_scri_load_instrument, NULL, }; Index: schism/audio_playback.cc =================================================================== RCS file: /cvsroot/schismtracker/schism2/schism/audio_playback.cc,v retrieving revision 1.96 diff -u -r1.96 audio_playback.cc --- schism/audio_playback.cc 1 Sep 2008 19:23:37 -0000 1.96 +++ schism/audio_playback.cc 6 Sep 2008 22:17:22 -0000 @@ -41,6 +41,8 @@ #include "midi.h" +#include "snd_fm.h" + static int midi_playing; // ------------------------------------------------------------------------ @@ -271,7 +273,7 @@ c->nC4Speed = i->nC4Speed; c->nLoopStart = i->nLoopStart; c->nLoopEnd = i->nLoopEnd; - c->dwFlags = (i->uFlags & 0xFF)|(c->dwFlags & CHN_MUTE); + c->dwFlags = (i->uFlags & 0x200000FF)|(c->dwFlags & CHN_MUTE); if (c->dwFlags & CHN_MUTE) { c->dwFlags &= ~(CHN_MUTE); c->dwFlags |= CHN_NNAMUTE; @@ -286,6 +288,12 @@ if (vol > -1) c->nVolume = (vol << 2); mp->NoteChange(chan, note, FALSE, TRUE, TRUE); c->nMasterChn = chan % 64; + + if(c->dwFlags & CHN_ADLIB) + { + OPL_NoteOff(chan); + OPL_Patch(chan, i->AdlibBytes); + } } else { while (chan >= 64) chan -= 64; @@ -587,6 +595,8 @@ midi_playing = 0; } + + OPL_Reset(); /* Also stop all OPL sounds */ memset(last_row,0,sizeof(last_row)); last_row_number = -1; Index: schism/dmoz.c =================================================================== RCS file: /cvsroot/schismtracker/schism2/schism/dmoz.c,v retrieving revision 1.24 diff -u -r1.24 dmoz.c --- schism/dmoz.c 7 Feb 2008 02:30:22 -0000 1.24 +++ schism/dmoz.c 6 Sep 2008 22:17:22 -0000 @@ -124,6 +124,7 @@ READ_INFO(iti), READ_INFO(its), + READ_INFO(scri), READ_INFO(au), READ_INFO(aiff), Index: schism/page_loadsample.c =================================================================== RCS file: /cvsroot/schismtracker/schism2/schism/page_loadsample.c,v retrieving revision 1.23 diff -u -r1.23 page_loadsample.c --- schism/page_loadsample.c 24 Aug 2008 16:32:34 -0000 1.23 +++ schism/page_loadsample.c 6 Sep 2008 22:17:22 -0000 @@ -325,8 +325,8 @@ fake_slot); } else { vgamem_ovl_clear(&sample_image, 0); + vgamem_ovl_apply(&sample_image); } - vgamem_ovl_apply(&sample_image); } if (need_trigger > -1) { @@ -600,7 +600,8 @@ file = flist.files[current_file]; dmoz_cache_update(cfg_dir_samples, &flist, 0); - if (file->type & (TYPE_BROWSABLE_MASK|TYPE_INST_MASK)) { + if ((file->type & (TYPE_BROWSABLE_MASK|TYPE_INST_MASK)) + && !(file->type & TYPE_SAMPLE_MASK)) { change_dir(file->path); status.flags |= NEED_UPDATE; } else if (file->sample) { Index: schism/page_samples.c =================================================================== RCS file: /cvsroot/schismtracker/schism2/schism/page_samples.c,v retrieving revision 1.37 diff -u -r1.37 page_samples.c --- schism/page_samples.c 24 Aug 2008 16:32:35 -0000 1.37 +++ schism/page_samples.c 6 Sep 2008 22:17:23 -0000 @@ -285,7 +285,7 @@ } draw_text_len(numtostr(0, sample->length, (unsigned char *) buf), 13, 64, 23, 2, 0); - draw_sample_data(&sample_image, sample, current_sample); + draw_sample_data(&sample_image, sample, current_sample); if (need_retrigger > -1) { if (last_keyup > -1) Index: schism/sample-view.c =================================================================== RCS file: /cvsroot/schismtracker/schism2/schism/sample-view.c,v retrieving revision 1.22 diff -u -r1.22 sample-view.c --- schism/sample-view.c 24 Aug 2008 16:32:35 -0000 1.22 +++ schism/sample-view.c 6 Sep 2008 22:17:23 -0000 @@ -229,6 +229,38 @@ void draw_sample_data(struct vgamem_overlay *r, song_sample *sample, UNUSED int n) { vgamem_ovl_clear(r, 0); + + if(sample->flags & SAMP_ADLIB) + { + vgamem_ovl_clear(r, 2); + vgamem_ovl_apply(r); + char Buf1[32], Buf2[32]; + + int y1 = r->y1, y2 = y1+3; + + draw_box(59,y1, 77,y2, BOX_THICK | BOX_INNER | BOX_INSET); // data + draw_box(54,y1, 58,y2, BOX_THIN | BOX_INNER | BOX_OUTSET); // button + draw_text_len("Mod", 3, 55,y1+1, 0,2); + draw_text_len("Car", 3, 55,y1+2, 0,2); + + sprintf(Buf1, "%02X %02X %02X %02X %02X %02X", // length:6*3-1=17 + sample->AdlibBytes[0], + sample->AdlibBytes[2], + sample->AdlibBytes[4], + sample->AdlibBytes[6], + sample->AdlibBytes[8], + sample->AdlibBytes[10]); + sprintf(Buf2, "%02X %02X %02X %02X %02X", // length: 5*3-1=14 + sample->AdlibBytes[1], + sample->AdlibBytes[3], + sample->AdlibBytes[5], + sample->AdlibBytes[7], + sample->AdlibBytes[9]); + draw_text_len(Buf1, 17, 60,y1+1, 2,0); + draw_text_len(Buf2, 17, 60,y1+2, 2,0); + return; + } + if (!sample->length) { vgamem_ovl_apply(r); return; Index: schism/fmt/scri.cc =================================================================== RCS file: schism/fmt/scri.cc diff -N schism/fmt/scri.cc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ schism/fmt/scri.cc 6 Sep 2008 22:17:24 -0000 @@ -0,0 +1,251 @@ +/* + * Schism Tracker - a cross-platform Impulse Tracker clone + * copyright (c) 2003-2005 chisel + * copyright (c) 2005-2008 Mrs. Brisby + * URL: http://rigelseven.com/schism/ + * + * 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 + */ + +#include + +#define NEED_BYTESWAP +#include "headers.h" +#include "fmt.h" + +/* FIXME - shouldn't be using the Modplug headers here. The only reason I'm doing so is because Modplug +has the IT sample decompression code... */ +#include "mplink.h" +#include "it_defs.h" + +struct scri_header +{ + //00 + unsigned char type; + char dosfn[11]; + unsigned char memseg[3]; + //10 + unsigned int length; // 32 bits + unsigned int loopbeg; // 32 bits + unsigned int loopend; // 32 bits + unsigned char volume; + char dummy1; + unsigned char packed; + unsigned char flags; + //20 + unsigned int c2spd; // 32 bits + char dummy2[4]; + unsigned short dummy_gp; + unsigned short dummy_512; + unsigned int dummy_last; + //30 + char samplename[28]; + //4C + char samplesig[4]; /* SCRS or SCRI */ + //50 +}; + +static int load_scri_sample(const byte *data, size_t length, song_sample *smp, char *title, + bool load_sample_data = true) +{ + const scri_header* header = (const scri_header*)data; + + /*fprintf(stderr, "Considering %d byte sample (%.4s), %d\n", + (int)length, + header->samplesig, + header->length);*/ + + if(length < 0x50) return false; // too small + if(strncmp(header->samplesig, "SCRS", 4) != 0 + && strncmp(header->samplesig, "SCRI", 4) != 0) + return false; // It should be either SCRS or SCRI. + + size_t samp_length = bswapLE32(header->length); + int bytes_per_sample = + header->type == 1 + ? ((header->flags & 2) ? 2 : 1) + : 0; // no sample data + + if(length < 0x50 + smp->length * bytes_per_sample) return false; + + smp->length = samp_length; + smp->global_volume = 64; + smp->volume = header->volume*256/64; + smp->loop_start = header->loopbeg; + smp->loop_end = header->loopend; + smp->speed = header->c2spd; + smp->flags = 0; + if(header->flags & 1) smp->flags |= SAMP_LOOP; + if(header->flags & 2) smp->flags |= SAMP_STEREO; + if(header->flags & 4) smp->flags |= SAMP_16_BIT; + + if(header->type == 2) + { + smp->flags |= SAMP_ADLIB; + smp->flags &= ~(SAMP_LOOP|SAMP_16_BIT); + + memcpy(smp->AdlibBytes, &header->length, 11); + + smp->length = 1; + smp->loop_start = 0; + smp->loop_end = 0; + } + + int format = (smp->flags & SAMP_16_BIT) ? RS_PCM16S : RS_PCM8U; + + if(load_sample_data) + { + int rsres = mp->ReadSample((MODINSTRUMENT *) smp, format, + (LPCSTR) (data + 0x50), + (DWORD) (length - 0x50)); + } + + strncpy(smp->filename, header->dosfn, 11); + strncpy(title, header->samplename, 28); + + return true; +} + + +int fmt_scri_read_info(dmoz_file_t *file, const byte *data, size_t length) +{ + song_sample tmp, *smp = &tmp; + char title[32] = ""; + if(!load_scri_sample(data, length, smp, title)) + return false; + + file->smp_length = smp->length; + file->smp_flags = smp->flags; + file->smp_defvol = smp->volume; + file->smp_gblvol = smp->global_volume; + file->smp_loop_start = smp->loop_start; + file->smp_loop_end = smp->loop_end; + file->smp_speed = smp->speed; + file->smp_filename = (char*)mem_alloc(13); + memcpy(file->smp_filename, smp->filename, 12); + file->smp_filename[12] = 0; + + file->description = "ST32 Sample/Instrument"; + file->title = (char *)mem_alloc(32); + memcpy(file->title, title, 32); + file->title[31] = 0; + file->type = TYPE_SAMPLE_EXTD | TYPE_INST_OTHER; + return true; +} + +int fmt_scri_load_sample(const byte *data, size_t length, song_sample *smp, char *title) +{ + return load_scri_sample(data, length, smp, title); +} + +static bool MidiS3M_Read( + song_instrument* g, + const char* name, + int slot) +{ + fprintf(stderr, "Name(%s)\n", name); + + if(name[0] == 'G') // GM=General MIDI + { + bool is_percussion = false; + if(name[1] == 'M') {} + else if(name[1] == 'P') is_percussion = true; + else return false; + const char*s = name+2; + int GM = 0; // midi program + int ft = 0; // finetuning + int scale = 63; // automatic volume scaling + int autoSDx = 0; // automatic randomized SDx effect + int bank = 0; // midi bank + while(std::isdigit(*s)) GM = GM*10 + (*s++)-'0'; + for(;;) + { + int sign=0; + if(*s == '-') sign=1; + if(sign || *s=='+') + { + for(ft=0; std::isdigit(*++s); ft=ft*10+(*s-'0')); + if(sign)ft=-ft; + continue; + } + if(*s=='/') + { + for(scale=0; std::isdigit(*++s); scale=scale*10+(*s-'0')); + continue; + } + if(*s=='&') + { + for(autoSDx=0; std::isdigit(*++s); autoSDx=autoSDx*10+(*s-'0')); + if(autoSDx > 15) autoSDx &= 15; + continue; + } + if(*s=='%') + { + for(bank=0; std::isdigit(*++s); bank=bank*10+(*s-'0')); + continue; + } + break; + } + // wMidiBank, nMidiProgram, nMidiChannel, nMidiDrumKey + g->midi_bank = bank; + if(is_percussion) + { g->midi_drum_key = GM-1; + g->midi_channel = 10; } + else + { g->midi_program = GM-1; + g->midi_channel = 1 + (slot%9); } + + /* TODO: Apply ft, apply scale, apply autoSDx, + * FIXME: Channel note changes don't affect MIDI notes + */ + return true; + } + return false; +} + + +int fmt_scri_load_instrument(const byte *data, size_t length, int slot) +{ + if(length < 0x50) return false; + + song_instrument* g = song_get_instrument(slot, NULL); + + char* np; + song_sample* smp = song_get_sample(slot, &np); + + if(!load_scri_sample(data, length, smp, np)) + { + return false; + } + + strncpy(g->name, np, 25); + strncpy(g->filename, smp->filename, 11); + + for(int n=0; n<128; ++n) + { + g->note_map[n] = n; + g->sample_map[n] = slot; + } + g->global_volume = smp->global_volume; + + g->midi_bank = 0; + g->midi_program = 0; + g->midi_channel = 0; + g->midi_drum_key = 0; + + MidiS3M_Read(g, np, slot); + + return true; +} Index: scripts/example-adlib.s3m =================================================================== RCS file: scripts/example-adlib.s3m diff -N scripts/example-adlib.s3m Binary files /dev/null and example-adlib.s3m differ Index: scripts/example-adlibsp.s3m =================================================================== RCS file: scripts/example-adlibsp.s3m diff -N scripts/example-adlibsp.s3m Binary files /dev/null and example-adlibsp.s3m differ Index: scripts/example-midis3m.s3m =================================================================== RCS file: scripts/example-midis3m.s3m diff -N scripts/example-midis3m.s3m Binary files /dev/null and example-midis3m.s3m differ