/****************************************************************************
*
* init.c
*
* Copyright (c) 1991-1992 Microsoft Corporation. All Rights Reserved.
*
***************************************************************************/
#include <windows.h>
#include <mmsystem.h>
#include <mmddk.h>
#define NOSTR /* to avoid redefining the strings */
#include "adlib.h"
/***************************************************************************
internal function prototypes
***************************************************************************/
void FAR SoundWarmInit(void);
static int NEAR SoundColdInit(void);
/*static void NEAR SoundWarmInit(void); */
static int NEAR BoardInstalled(void);
static void NEAR SetMode(BYTE mode);
static void NEAR SetGParam(BYTE amD, BYTE vibD, BYTE nSel);
static void NEAR Set3812(BYTE state);
static void NEAR InitSlotVolume(void);
static void NEAR InitFNums(void);
static void NEAR SoundChut(BYTE voice);
static void NEAR SetPitchRange(WORD pR);
static void NEAR SetFNum(NPWORD fNumVec, int num, int den);
static long NEAR CalcPremFNum(int numDeltaDemiTon, int denDeltaDemiTon);
static int NEAR PASCAL LoadPatches(void);
static int NEAR PASCAL LoadDrumPatches(void);
/*************************************************************************
public data
*************************************************************************/
WORD wPort = DEF_PORT; /* address of sound chip */
BOOL fEnabled; /* are we enabled? */
TIMBRE patches[MAXPATCH]; /* patch data */
DRUMPATCH drumpatch[NUMDRUMNOTES]; /* drum kit data */
#ifdef DEBUG
WORD wDebugLevel; /* debug level */
#endif
/*************************************************************************
strings
*************************************************************************/
#define BCODE _based(_segname("_CODE"))
/*non-localized strings */
char BCODE aszDriverName[] = "adlib.drv";
char BCODE aszProductName[] = "Ad Lib";
char BCODE aszAdlibDelay[] = "WriteDelay";
char BCODE aszSystemIni[] = "system.ini";
#ifdef DEBUG
char BCODE aszAdlib[] = "adlib";
char BCODE aszMMDebug[] = "mmdebug";
#endif
/***************************************************************************
local data
***************************************************************************/
static HANDLE ghInstance; /* our global instance */
static BOOL fInit; /* have we initialized yet? */
/* format of drumkit.bin file */
typedef struct drumfilepatch_tag {
BYTE key; /* the key to map */
BYTE patch; /* the patch to use */
BYTE note; /* the note to play */
} DRUMFILEPATCH, *NPDRUMFILEPATCH, FAR *LPDRUMFILEPATCH;
/***************************************************************************
public functions
***************************************************************************/
/****************************************************************************
* @doc INTERNAL
*
* @api int | SoundColdInit | Must be called for start-up initialization.
*
* @rdesc Returns a nonzero value if the board is installed and zero otherwise.
***************************************************************************/
static int NEAR SoundColdInit(void)
{
int hardware;
D1("SoundColdInit");
if (hardware = BoardInstalled())
SoundWarmInit();
return hardware;
}
/****************************************************************************
* @doc INTERNAL
*
* @api void | SoundWarmInit | Initializes the chip in melodic mode (mode == 0).
*
* @rdesc There is no return value.
***************************************************************************/
void FAR SoundWarmInit(void)
{
BYTE i;
D1("SoundWarmInit");
SetGParam(0, 0, 0); /* init global parameters */
InitSlotVolume(); /* sets volume of each slot to MAXVOLUME */
InitFNums(); /* initializes frequency shift table to no shift */
for (i = 0 ; i <= 8; i++)
SoundChut(i); /* set frequencies of voices 0 - 8 to 0 */
SetMode(1); /* percussion mode (melodic mode == 0) */
SetPitchRange(2); /* GMM pitch range is 2 semitones */
#if 1
Set3812(1); /* sets wave-select parameter */
#else
Set3812(0); /* DOES NOT set wave-select parameter */
#endif
}
/****************************************************************************
* @doc INTERNAL
*
* @api int | BoardInstalled | Checks to see if the board is installed.
*
* @rdesc Returns a nonzero value if the board is installed and zero otherwise.
***************************************************************************/
static int NEAR BoardInstalled(void)
{
BYTE t1, t2, i;
D1("BoardInstalled");
SndOutput(4, 0x60); /* mask T1 & T2 */
SndOutput(4, 0x80); /* reset IRQ */
t1 = inport(); /* read status register */
SndOutput(2, 0xff); /* set timer - 1 latch */
SndOutput(4, 0x21); /* unmask & start T1 */
for (i = 0; i < 200; i++) { /* 100 uSec delay for timer - 1 overflow */
#ifdef STUPID
inport();
#else
t2 = i; /* a delay of some sort... */
#endif
}
t2 = inport(); /* read status register */
SndOutput(4, 0x60);
SndOutput(4, 0x80);
return (t1 & 0xE0) == 0 && (t2 & 0xE0) == 0xC0;
}
/****************************************************************************
* @doc INTERNAL
*
* @api void | SetMode | Puts the chip in melodic mode (mode == 0), or in
* percussive mode (mode != 0).
*
* @parm BYTE | mode | Specifies which mode to put the chip into.
*
* @rdesc There is no return value.
***************************************************************************/
static void NEAR SetMode(BYTE mode)
{
if (mode) {
SetFreq(TOM, TOM_PITCH, 0); /* set frequency of TOM voice */
SetFreq(SD, SD_PITCH, 0); /* set frequency of SD voice */
}
fPercussion = mode;
percBits = 0; /* initialize control bits of percussive voices */
}
/****************************************************************************
* @doc INTERNAL
*
* @api void | SetGParam | Sets the 3 global parameters AmDepth, VibDepth
* and NoteSel. The change takes place immediately.
*
* @parm BYTE | amD | The new AmDepth parameter.
*
* @parm BYTE | vibD | The new VibDepth parameter.
*
* @parm BYTE | nSel | The new NoteSel parameter.
*
* @rdesc There is no return value.
***************************************************************************/
static void NEAR SetGParam(BYTE amD, BYTE vibD, BYTE nSel)
{
amDepth = amD;
vibDepth = vibD;
noteSel = nSel;
SndSAmVibRhythm();
SndSNoteSel();
}
/****************************************************************************
* @doc INTERNAL
*
* @api void | Set3812 | Enables (state != 0) or disables (state == 0) the
* wave-select parameters.
*
* @parm BYTE | state | Indicates whether to enable or disable the wave-select
* parameters.
*
* @comm If you do not want to use the wave-select parameters, call this
* function with a value of 0 AFTER calling SoundColdInit() or
* SoundWarmInit().
*
* @rdesc There is no return value.
***************************************************************************/
static void NEAR Set3812(BYTE state)
{
BYTE i;
D1("Set3812");
/* set waveform for each of the 18 slots to sine wave */
for (i = 0; i < 18; i++)
SndOutput((BYTE)(0xE0 | offsetSlot[i]), 0);
/* enable/disable the wave-select parameters */
modeWaveSel = (BYTE)(state ? 0x20 : 0);
SndOutput(1, modeWaveSel);
}
#if 0 /* never used */
static void NEAR InitSlotParams(void);
/****************************************************************************
* @doc INTERNAL
*
* @api void | InitSlotParams | In melodic mode, this function initializes all
* voices to electric-pianos. In percussive mode, it initializes the 6
* melodic voices to electric-pianos and the 5 percussive voices to their
* default timbres.
*
* @comm This function is pointless because the timbre of each voice gets
* set as soon as the voice is allocated, so it's commented out.
*
* @rdesc There is no return value.
***************************************************************************/
static void NEAR InitSlotParams(void)
{
/* definition of default melodic(electric piano) and percussive voices: */
static BYTE pianoOpr0[] = { 1, 1, 3, 15, 5, 0, 1, 3, 15, 0, 0, 0, 1, 0 };
static BYTE pianoOpr1[] = { 0, 1, 1, 15, 7, 0, 2, 4, 0, 0, 0, 1, 0, 0 };
static BYTE bdOpr0[] = { 0, 0, 0, 10, 4, 0, 8, 12, 11, 0, 0, 0, 1, 0 };
static BYTE bdOpr1[] = { 0, 0, 0, 13, 4, 0, 6, 15, 0, 0, 0, 0, 1, 0 };
static BYTE sdOpr[] = { 0, 12, 0, 15, 11, 0, 8, 5, 0, 0, 0, 0, 0, 0 };
static BYTE tomOpr[] = { 0, 4, 0, 15, 11, 0, 7, 5, 0, 0, 0, 0, 0, 0 };
static BYTE cymbOpr[] = { 0, 1, 0, 15, 11, 0, 5, 5, 0, 0, 0, 0, 0, 0 };
static BYTE hhOpr[] = { 0, 1, 0, 15, 11, 0, 7, 5, 0, 0, 0, 0, 0, 0 };
BYTE i;
for (i = 0; i < 18; i++)
if (operSlot[i])
SetSlotParam(i, pianoOpr1, 0);
else
SetSlotParam(i, pianoOpr0, 0);
if (fPercussion) {
SetSlotParam(12, bdOpr0, 0);
SetSlotParam(15, bdOpr1, 0);
SetSlotParam(16, sdOpr, 0);
SetSlotParam(14, tomOpr, 0);
SetSlotParam(17, cymbOpr, 0);
SetSlotParam(13, hhOpr, 0);
}
}
#endif
/****************************************************************************
* @doc INTERNAL
*
* @api void | InitSlotVolume | Sets the volume values in the <t slotRelVolume>
* array to MAXVOLUME.
*
* @rdesc There is no return value.
***************************************************************************/
static void NEAR InitSlotVolume(void)
{
int i;
for (i = 0; i < 18; i++)
slotRelVolume[i] = MAXVOLUME;
}
/****************************************************************************
* @doc INTERNAL
*
* @api void | InitFNums | Initializes all lines of the frequency table
* (the <p fNumNotes> array). Each line represents 12 half-tones shifted
* by (n / NR_STEP_PITCH), where 'n' is the line number and ranges from
* 1 to NR_STEP_PITCH.
*
* @rdesc There is no return value.
***************************************************************************/
static void NEAR InitFNums(void)
{
WORD i, j, k;
WORD num; /* numerator */
WORD numStep; /* step value for numerator */
WORD row; /* row in the frequency table */
/* calculate each row in the fNumNotes table */
numStep = 100 / NR_STEP_PITCH;
for (num = row = 0; row < NR_STEP_PITCH; row++, num += numStep)
SetFNum(fNumNotes[row], num, 100);
/* fNumFreqPtr has an element for each voice, pointing to the */
/* appropriate row in the fNumNotes table. They're all initialized */
/* to the first row, which represents no pitch shift. */
for (i = 0; i < 11; i++) {
fNumFreqPtr[i] = fNumNotes[0];
halfToneOffset[i] = 0;
}
/* just for optimization */
for (i = 0, k = 0; i < 8; i++)
for (j = 0; j < 12; j++, k++) {
noteDIV12[k] = (BYTE)i;
noteMOD12[k] = (BYTE)j;
}
}
/****************************************************************************
* @doc INTERNAL
*
* @api void | SoundChut | Sets the frequency of voice <p voice> to 0 Hz.
*
* @parm BYTE | voice | Specifies which voice to set.
*
* @rdesc There is no return value.
***************************************************************************/
static void NEAR SoundChut(BYTE voice)
{
D1("SoundChut");
SndOutput((BYTE)(0xA0 | voice), 0);
SndOutput((BYTE)(0xB0 | voice), 0);
}
/****************************************************************************
* @doc INTERNAL
*
* @api void | SetPitchRange | This routine changes the global pitch bend
* range value.
*
* @parm WORD | pR | The new pitch bend range.
*
* @comm The value can be from 1 to 12 (in half-tones). For example, the
* value 12 means that the pitch bend will range from -12 (pitchBend == 0,
* see <f xSetVoicePitch>) to +12 (pitchBend == 0x3fff) half-tones. The
* change will be effective as of the next call to <f xSetVoicePitch>.
*
* @rdesc There is no return value.
***************************************************************************/
static void NEAR SetPitchRange(WORD pR)
{
if (pR > 12)
pR = 12;
if (pR < 1)
pR = 1;
pitchRangeStep = pR * NR_STEP_PITCH;
}
/****************************************************************************
* @doc INTERNAL
*
* @api void | SetFNum | Initializes a line in the frequency table with
* shifted frequency values. The values are shifted a fraction (num/den)
* of a half-tone.
*
* @parm NPWORD | fNumVec | The line from the frequency table.
*
* @parm int | num | Numerator.
*
* @parm int | den | Denominator.
*
* @xref CalcPremFNum
*
* @rdesc There is no return value.
***************************************************************************/
static void NEAR SetFNum(NPWORD fNumVec, int num, int den)
{
int i;
long val;
*fNumVec++ = (WORD)((4 + (val = CalcPremFNum(num, den))) >> 3);
for (i = 1; i < 12; i++) {
val *= 106;
*fNumVec++ = (WORD)((4 + (val /= 100)) >> 3);
}
}
/****************************************************************************
* @doc INTERNAL
*
* @api long | CalcPremFNum | Calculates some magic number that is used in
* setting the values in the <p fNumNotes> table.
*
* @parm int | numDeltaDemiTon | Numerator (-100 to +100).
*
* @parm int | denDeltaDemiTon | Denominator (1 to 100).
*
* @comm If the numerator (numDeltaDemiTon) is positive, the frequency is
* increased; if negative, it is decreased. The function calculates:
* f8 = Fb(1 + 0.06 num /den) (where Fb = 26044 * 2 / 25)
* fNum8 = f8 * 65536 * 72 / 3.58e6
*
* @rdesc Returns fNum8, which is the binary value of the frequency 260.44 (C)
* shifted by +/- <p numdeltaDemiTon> / <p denDeltaDemiTon> * 8.
***************************************************************************/
static long NEAR CalcPremFNum(int numDeltaDemiTon, int denDeltaDemiTon)
{
long f8;
long fNum8;
long d100;
d100 = denDeltaDemiTon * 100;
f8 = (d100 + 6 * numDeltaDemiTon) * (26044L * 2L);
f8 /= d100 * 25;
fNum8 = f8 * 16384;
fNum8 *= 9L;
fNum8 /= 179L * 625L;
return fNum8;
}
/****************************************************************************
* @doc INTERNAL
*
* @api int | LoadPatches | Reads the patch set from the BANK resource and
* builds the <p patches> array.
*
* @rdesc Returns the number of patches loaded, or 0 if an error occurs.
***************************************************************************/
static int NEAR PASCAL LoadPatches(void)
{
HANDLE hResInfo;
HANDLE hResData;
LPSTR lpRes;
int iPatches;
DWORD dwOffset;
DWORD dwResSize;
LPTIMBRE lpBankTimbre;
LPTIMBRE lpPatchTimbre;
LPBANKHDR lpBankHdr;
/* find resource and get its size */
hResInfo = FindResource(ghInstance, MAKEINTRESOURCE(DEFAULTBANK), MAKEINTRESOURCE(RT_BANK));
if (!hResInfo) {
D1("Default bank resource not found");
return 0;
}
dwResSize = (DWORD)SizeofResource(ghInstance, hResInfo);
/* load and lock resource */
hResData = LoadResource(ghInstance, hResInfo);
if (!hResData) {
D1("Bank resource not loaded");
return 0;
}
lpRes = LockResource(hResData);
if (!lpRes) {
D1("Bank resource not locked");
return 0;
}
/* read the bank resource, loading patches as we find them */
D1("loading patches");
lpBankHdr = (LPBANKHDR)lpRes;
dwOffset = lpBankHdr->offsetTimbre; /* point to first one */
for (iPatches = 0; iPatches < MAXPATCH; iPatches++) {
lpBankTimbre = (LPTIMBRE)(lpRes + dwOffset);
lpPatchTimbre = &patches[iPatches];
*lpPatchTimbre = *lpBankTimbre;
dwOffset += sizeof(TIMBRE);
if (dwOffset + sizeof(TIMBRE) > dwResSize) {
D1("Attempt to read past end of bank resource");
break;
}
}
UnlockResource(hResData);
FreeResource(hResData);
return iPatches;
}
/****************************************************************************
* @doc INTERNAL
*
* @api int | LoadDrumPatches | Reads the drum kit patch set from the
* DRUMKIT resource and builds the <p drumpatch> array.
*
* @comm Each entry of the <t drumpatch> array (representing a key number
* from the "drum patch") consists of a patch number and note number
* from some other patch.
*
* @rdesc Returns the number of patches loaded, or 0 if an error occurs.
***************************************************************************/
static int NEAR PASCAL LoadDrumPatches(void)
{
HANDLE hResInfo;
HANDLE hResData;
LPSTR lpRes;
int iPatches;
int key;
DWORD dwOffset;
DWORD dwResSize;
LPDRUMFILEPATCH lpResPatch;
/* find resource and get its size */
hResInfo = FindResource(ghInstance, MAKEINTRESOURCE(DEFAULTDRUMKIT), MAKEINTRESOURCE(RT_DRUMKIT));
if (!hResInfo) {
D1("Default drum resource not found");
return 0;
}
dwResSize = (DWORD)SizeofResource(ghInstance, hResInfo);
/* load and lock resource */
hResData = LoadResource(ghInstance, hResInfo);
if (!hResData) {
D1("Drum resource not loaded");
return 0;
}
lpRes = LockResource(hResData);
if (!lpRes) {
D1("Drum resource not locked");
return 0;
}
/* read the drum resource, loading patches as we find them */
D1("reading drum data");
dwOffset = 0;
for (iPatches = 0; iPatches < NUMDRUMNOTES; iPatches++) {
lpResPatch = (LPDRUMFILEPATCH)(lpRes + dwOffset);
key = lpResPatch->key;
if ((key >= FIRSTDRUMNOTE) && (key <= LASTDRUMNOTE)) {
drumpatch[key - FIRSTDRUMNOTE].patch = lpResPatch->patch;
drumpatch[key - FIRSTDRUMNOTE].note = lpResPatch->note;
}
else {
D1("Drum patch key out of range");
}
dwOffset += sizeof(DRUMFILEPATCH);
if (dwOffset + sizeof(DRUMFILEPATCH) > dwResSize) {
D1("Attempt to read past end of drum resource");
break;
}
}
UnlockResource(hResData);
FreeResource(hResData);
return iPatches;
}
/***************************************************************************
public functions
***************************************************************************/
/****************************************************************************
* @doc INTERNAL
*
* @api BOOL | Enable | Enables the card. If we haven't yet enabled in
* this session, it will do a cold restart of the card and load the
* patches; otherwise it will do a warm restart.
*
* @rdesc Returns TRUE if successful and false otherwise.
***************************************************************************/
BOOL NEAR PASCAL Enable(void)
{
D1("Enable");
if (vadlibdAcquireAdLibSynth()) {
D1("AdLib could NOT be aquired for ENABLE!!!");
return FALSE;
}
if (!fInit) { /* if we haven't initialized yet */
if (!SoundColdInit()) /* if we can't find a card */
return FALSE; /* keep fInit set to FALSE */
if (!LoadPatches()) /* load the melodic patches */
return FALSE;
if (!LoadDrumPatches()) /* load the drum kit information */
return FALSE;
}
else { /* we've already initialized */
SoundWarmInit(); /* so do a warm restart */
}
fInit = TRUE; /* say that we have initialized once */
fEnabled = TRUE; /* say that we're enabled */
if (vadlibdReleaseAdLibSynth())
D1("AdLib could NOT be RELEASED for ENABLE!!! VERY GOOFY!!");
return TRUE;
}
/****************************************************************************
* @doc INTERNAL
*
* @api void | Disable | Since this function is called either when a
* Windows session ends or when we switch to a DOS box (in 286 mode),
* we'll reset the card in preparation for someone else to use it.
*
* @rdesc There is no return value.
***************************************************************************/
void NEAR PASCAL Disable(void)
{
D1("Disable");
if (fInit) { /* if we have a card */
if (vadlibdAcquireAdLibSynth())
D1("AdLib could NOT be aquired for DISABLE!!!");
SoundWarmInit(); /* reset card to be good */
if (vadlibdReleaseAdLibSynth())
D1("AdLib could NOT be RELEASED for DISABLE!!!");
}
fEnabled = FALSE; /* say that we're not enabled */
}
/****************************************************************************
* @doc INTERNAL
*
* @api int | LibMain | Library initialization code.
*
* @parm HANDLE | hInstance | Our instance handle.
*
* @parm WORD | wHeapSize | The heap size from the .def file.
*
* @parm LPSTR | lpCmdLine | The command line.
*
* @rdesc Returns 1 if the initialization was successful and 0 otherwise.
***************************************************************************/
#define DEF286WRITEDELAY 8 /* 25MHz 286 */
#define DEF386WRITEDELAY 14 /* 50MHz 386 */
#define DEF486WRITEDELAY 41 /* 50MHz 486 */
int NEAR PASCAL LibMain(HANDLE hInstance, WORD wHeapSize, LPSTR lpCmdLine)
{
extern WORD wWriteDelay;
DWORD dwWinFlags;
#ifdef DEBUG
/* get debug level - default is 0 */
wDebugLevel = GetProfileInt(aszMMDebug, aszAdlib, 0);
#endif
D1("LibMain");
ghInstance = hInstance; /* save our instance */
dwWinFlags = GetWinFlags();
if (dwWinFlags & WF_CPU286)
wWriteDelay = DEF286WRITEDELAY;
else if (dwWinFlags & WF_CPU386)
wWriteDelay = DEF386WRITEDELAY;
else if (dwWinFlags & WF_CPU486)
wWriteDelay = DEF486WRITEDELAY;
vadlibdGetEntryPoint();
return 1; /* exit ok */
}
Detected encoding: ASCII (7 bit) | 2
|