/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * Low-level functions for the AWE32 driver. * * By George Foot. * * Information taken primarily from "AWE32/EMU8000 Programmer's Guide" * (AEPG) by Dave Rossum. The AEPG is part of the AWE32 Developers' * Information Pack (ADIP) from Creative Labs. * * I/O port verification technique taken from "The Un-official Sound * Blaster AWE32 Programming Guide" by Vince Vu a.k.a. Judge Dredd * * Miscellaneous guidance taken from Takashi Iwai's Linux AWE32 driver * * See readme.txt for copyright information. */ #include #include "allegro.h" #include "emu8k.h" #ifndef ALLEGRO_DOS #error something is wrong with the makefile #endif /****************************************************************************\ * * * EMU8000.H : EMU8000 init arrays ( for hardware level programming only ) * * * * (C) Copyright Creative Technology Ltd. 1992-96. All rights reserved * * worldwide. * * * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY * * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE * * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR * * PURPOSE. * * * \****************************************************************************/ static unsigned short init1_1[32] = { 0x03ff, 0x0030, 0x07ff, 0x0130, 0x0bff, 0x0230, 0x0fff, 0x0330, 0x13ff, 0x0430, 0x17ff, 0x0530, 0x1bff, 0x0630, 0x1fff, 0x0730, 0x23ff, 0x0830, 0x27ff, 0x0930, 0x2bff, 0x0a30, 0x2fff, 0x0b30, 0x33ff, 0x0c30, 0x37ff, 0x0d30, 0x3bff, 0x0e30, 0x3fff, 0x0f30 }; static unsigned short init1_2[32] = { 0x43ff, 0x0030, 0x47ff, 0x0130, 0x4bff, 0x0230, 0x4fff, 0x0330, 0x53ff, 0x0430, 0x57ff, 0x0530, 0x5bff, 0x0630, 0x5fff, 0x0730, 0x63ff, 0x0830, 0x67ff, 0x0930, 0x6bff, 0x0a30, 0x6fff, 0x0b30, 0x73ff, 0x0c30, 0x77ff, 0x0d30, 0x7bff, 0x0e30, 0x7fff, 0x0f30 }; static unsigned short init1_3[32] = { 0x83ff, 0x0030, 0x87ff, 0x0130, 0x8bff, 0x0230, 0x8fff, 0x0330, 0x93ff, 0x0430, 0x97ff, 0x0530, 0x9bff, 0x0630, 0x9fff, 0x0730, 0xa3ff, 0x0830, 0xa7ff, 0x0930, 0xabff, 0x0a30, 0xafff, 0x0b30, 0xb3ff, 0x0c30, 0xb7ff, 0x0d30, 0xbbff, 0x0e30, 0xbfff, 0x0f30 }; static unsigned short init1_4[32] = { 0xc3ff, 0x0030, 0xc7ff, 0x0130, 0xcbff, 0x0230, 0xcfff, 0x0330, 0xd3ff, 0x0430, 0xd7ff, 0x0530, 0xdbff, 0x0630, 0xdfff, 0x0730, 0xe3ff, 0x0830, 0xe7ff, 0x0930, 0xebff, 0x0a30, 0xefff, 0x0b30, 0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30 }; static unsigned short init2_1[32] = { 0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330, 0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730, 0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30, 0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30 }; static unsigned short init2_2[32] = { 0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330, 0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730, 0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30, 0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30 }; static unsigned short init2_3[32] = { 0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330, 0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730, 0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30, 0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30 }; static unsigned short init2_4[32] = { 0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330, 0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730, 0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30, 0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30 }; static unsigned short init3_1[32] = { 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254, 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234, 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224 }; static unsigned short init3_2[32] = { 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254, 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264, 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294, 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3 }; static unsigned short init3_3[32] = { 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287, 0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7, 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386, 0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55 }; static unsigned short init3_4[32] = { 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308, 0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F, 0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319, 0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570 }; static unsigned short init4_1[32] = { 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254, 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234, 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224 }; static unsigned short init4_2[32] = { 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254, 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264, 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294, 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3 }; static unsigned short init4_3[32] = { 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287, 0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7, 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386, 0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55 }; static unsigned short init4_4[32] = { 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308, 0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F, 0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319, 0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570 }; /*** (end of init arrays) ***/ #define MIN_LOOP_LEN 2 int _emu8k_baseport = 0; int _emu8k_numchannels = 32; /* Functions to deal with the AWE32's I/O ports */ static INLINE void write_word(int reg, int channel, int port, int data) { int out_port = _emu8k_baseport; switch (port) { case 0: out_port += 0x0000; break; case 1: out_port += 0x0400; break; case 2: out_port += 0x0402; break; case 3: out_port += 0x0800; break; default: return; } outportw(_emu8k_baseport + 0x802, (reg << 5) + (channel & 0x1f)); outportw(out_port, data); } static INLINE unsigned int read_word(int reg, int channel, int port) { int in_port = _emu8k_baseport; switch (port) { case 0: in_port += 0x0000; break; case 1: in_port += 0x0400; break; case 2: in_port += 0x0402; break; case 3: in_port += 0x0800; break; default: return 0; } outportw(_emu8k_baseport + 0x802, (reg << 5) + (channel & 0x1f)); return inportw(in_port); } static INLINE void write_dword(int reg, int channel, int port, unsigned int data) { int out_port = _emu8k_baseport; switch (port) { case 0: out_port += 0x0000; break; case 1: out_port += 0x0400; break; case 2: out_port += 0x0402; break; case 3: out_port += 0x0800; break; default: return; } outportw(_emu8k_baseport + 0x802, (reg << 5) + (channel & 0x1f)); outportw(out_port, data & 0xffff); outportw(out_port + 2, (data >> 16) & 0xffff); } static INLINE unsigned int read_dword(int reg, int channel, int port) { int in_port, a, b; in_port = _emu8k_baseport; switch (port) { case 0: in_port += 0x0000; break; case 1: in_port += 0x0400; break; case 2: in_port += 0x0402; break; case 3: in_port += 0x0800; break; default: return 0; } outportw(_emu8k_baseport + 0x802, (reg << 5) + (channel & 0x1f)); a = inportw(in_port); b = inportw(in_port + 2); return ((b << 16) + a); } /* write_*: * Functions to write information to the AWE32's registers * (abbreviated as in the EPG). */ static INLINE void write_CPF(int channel, int i) { write_dword(0, channel, 0, i); } static INLINE void write_PTRX(int channel, int i) { write_dword(1, channel, 0, i); } static INLINE void write_CVCF(int channel, int i) { write_dword(2, channel, 0, i); } static INLINE void write_VTFT(int channel, int i) { write_dword(3, channel, 0, i); } static INLINE void write_PSST(int channel, int i) { write_dword(6, channel, 0, i); } static INLINE void write_CSL(int channel, int i) { write_dword(7, channel, 0, i); } static INLINE void write_CCCA(int channel, int i) { write_dword(0, channel, 1, i); } static INLINE void write_HWCF4(int i) { write_dword(1, 9, 1, i); } static INLINE void write_HWCF5(int i) { write_dword(1, 10, 1, i); } static INLINE void write_HWCF6(int i) { write_dword(1, 13, 1, i); } static INLINE void write_SMALR(int i) { write_dword(1, 20, 1, i); } static INLINE void write_SMARR(int i) { write_dword(1, 21, 1, i); } static INLINE void write_SMALW(int i) { write_dword(1, 22, 1, i); } static INLINE void write_SMARW(int i) { write_dword(1, 23, 1, i); } /* static INLINE void write_SMLD(int i) { write_word(1, 26, 1, i); } */ /* static INLINE void write_SMRD(int i) { write_word(1, 26, 2, i); } */ /* static INLINE void write_WC(int i) { write_word(1, 27, 2, i); } */ static INLINE void write_HWCF1(int i) { write_word(1, 29, 1, i); } static INLINE void write_HWCF2(int i) { write_word(1, 30, 1, i); } static INLINE void write_HWCF3(int i) { write_word(1, 31, 1, i); } static INLINE void write_INIT1(int channel, int i) { write_word(2, channel, 1, i); } /* `channel' is really `element' here */ static INLINE void write_INIT2(int channel, int i) { write_word(2, channel, 2, i); } static INLINE void write_INIT3(int channel, int i) { write_word(3, channel, 1, i); } static INLINE void write_INIT4(int channel, int i) { write_word(3, channel, 2, i); } static INLINE void write_ENVVOL(int channel, int i) { write_word(4, channel, 1, i); } static INLINE void write_DCYSUSV(int channel, int i) { write_word(5, channel, 1, i); } static INLINE void write_ENVVAL(int channel, int i) { write_word(6, channel, 1, i); } static INLINE void write_DCYSUS(int channel, int i) { write_word(7, channel, 1, i); } static INLINE void write_ATKHLDV(int channel, int i) { write_word(4, channel, 2, i); } static INLINE void write_LFO1VAL(int channel, int i) { write_word(5, channel, 2, i); } static INLINE void write_ATKHLD(int channel, int i) { write_word(6, channel, 2, i); } static INLINE void write_LFO2VAL(int channel, int i) { write_word(7, channel, 2, i); } static INLINE void write_IP(int channel, int i) { write_word(0, channel, 3, i); } static INLINE void write_IFATN(int channel, int i) { write_word(1, channel, 3, i); } static INLINE void write_PEFE(int channel, int i) { write_word(2, channel, 3, i); } static INLINE void write_FMMOD(int channel, int i) { write_word(3, channel, 3, i); } static INLINE void write_TREMFRQ(int channel, int i) { write_word(4, channel, 3, i); } static INLINE void write_FM2FRQ2(int channel, int i) { write_word(5, channel, 3, i); } /* read_*: * Functions to read information from the AWE32's registers * (abbreviated as in the AEPG). */ /* static INLINE int read_CPF(int channel) { return read_dword(0, channel, 0); } */ /* static INLINE int read_PTRX(int channel) { return read_dword(1, channel, 0); } */ /* static INLINE int read_CVCF(int channel) { return read_dword(2, channel, 0); } */ /* static INLINE int read_VTFT(int channel) { return read_dword(3, channel, 0); } */ static INLINE int read_PSST(int channel) { return read_dword(6, channel, 0); } /* static INLINE int read_CSL(int channel) { return read_dword(7, channel, 0); } */ /* static INLINE int read_CCCA(int channel) { return read_dword(0, channel, 1); } */ /* static INLINE int read_HWCF4(void) { return read_dword(1, 9, 1); } */ /* static INLINE int read_HWCF5(void) { return read_dword(1, 10, 1); } */ /* static INLINE int read_HWCF6(void) { return read_dword(1, 13, 1); } */ /* static INLINE int read_SMALR(void) { return read_dword(1, 20, 1); } */ /* static INLINE int read_SMARR(void) { return read_dword(1, 21, 1); } */ /* static INLINE int read_SMALW(void) { return read_dword(1, 22, 1); } */ /* static INLINE int read_SMARW(void) { return read_dword(1, 23, 1); } */ /* static INLINE int read_SMLD(void) { return read_word(1, 26, 1); } */ /* static INLINE int read_SMRD(void) { return read_word(1, 26, 2); } */ /* static INLINE int read_WC(void) { return read_word(1, 27, 2); } */ /* static INLINE int read_HWCF1(void) { return read_word(1, 29, 1); } */ /* static INLINE int read_HWCF2(void) { return read_word(1, 30, 1); } */ /* static INLINE int read_HWCF3(void) { return read_word(1, 31, 1); } */ /* static INLINE int read_INIT1(void) { return read_word(2, 0, 1); } */ /* static INLINE int read_INIT2(void) { return read_word(2, 0, 2); } */ /* static INLINE int read_INIT3(void) { return read_word(3, 0, 1); } */ /* static INLINE int read_INIT4(void) { return read_word(3, 0, 2); } */ /* static INLINE int read_ENVVOL(int channel) { return read_word(4, channel, 1); } */ /* static INLINE int read_DCYSUSV(int channel) { return read_word(5, channel, 1); } */ /* static INLINE int read_ENVVAL(int channel) { return read_word(6, channel, 1); } */ /* static INLINE int read_DCYSUS(int channel) { return read_word(7, channel, 1); } */ /* static INLINE int read_ATKHLDV(int channel) { return read_word(4, channel, 2); } */ /* static INLINE int read_LFO1VAL(int channel) { return read_word(5, channel, 2); } */ /* static INLINE int read_ATKHLD(int channel) { return read_word(6, channel, 2); } */ /* static INLINE int read_LFO2VAL(int channel) { return read_word(7, channel, 2); } */ /* static INLINE int read_IP(int channel) { return read_word(0, channel, 3); } */ static INLINE int read_IFATN(int channel) { return read_word(1, channel, 3); } /* static INLINE int read_PEFE(int channel) { return read_word(2, channel, 3); } */ /* static INLINE int read_FMMOD(int channel) { return read_word(3, channel, 3); } */ /* static INLINE int read_TREMFRQ(int channel) { return read_word(4, channel, 3); } */ /* static INLINE int read_FM2FRQ2(int channel) { return read_word(5, channel, 3); } */ /* wait_*: * Functions to wait for DMA streams to be ready. */ /* static INLINE void wait_LR(void) { while (read_SMALR() & 0x80000000) ; } */ /* static INLINE void wait_LW(void) { while (read_SMALW() & 0x80000000) ; } */ /* static INLINE void wait_RR(void) { while (read_SMARR() & 0x80000000) ; } */ /* static INLINE void wait_RW(void) { while (read_SMARW() & 0x80000000) ; } */ /* write_init_arrays: * Writes the given set of initialisation arrays to the card. */ static void write_init_arrays(unsigned short *init1, unsigned short *init2, unsigned short *init3, unsigned short *init4) { int i; for (i = 0; i < 32; i++) write_INIT1(i, init1[i]); for (i = 0; i < 32; i++) write_INIT2(i, init2[i]); for (i = 0; i < 32; i++) write_INIT3(i, init3[i]); for (i = 0; i < 32; i++) write_INIT4(i, init4[i]); } /* emu8k_init: * Initialise the synthesiser. See AEPG chapter 4. */ void emu8k_init(void) { int channel; write_HWCF1(0x0059); write_HWCF2(0x0020); for (channel=0; channel<_emu8k_numchannels; channel++) write_DCYSUSV(channel, 0x0080); for (channel=0; channel<_emu8k_numchannels; channel++) { write_ENVVOL(channel, 0); write_ENVVAL(channel, 0); write_DCYSUS(channel, 0); write_ATKHLDV(channel, 0); write_LFO1VAL(channel, 0); write_ATKHLD(channel, 0); write_LFO2VAL(channel, 0); write_IP(channel, 0); write_IFATN(channel, 0); write_PEFE(channel, 0); write_FMMOD(channel, 0); write_TREMFRQ(channel, 0); write_FM2FRQ2(channel, 0); write_PTRX(channel, 0); write_VTFT(channel, 0); write_PSST(channel, 0); write_CSL(channel, 0); write_CCCA(channel, 0); } for (channel=0; channel<_emu8k_numchannels; channel++) { write_CPF(channel, 0); write_CVCF(channel, 0); } write_SMALR(0); write_SMARR(0); write_SMALW(0); write_SMARW(0); write_init_arrays(init1_1, init1_2, init1_3, init1_4); rest(25); /* wait 24 msec + 1 for luck */ write_init_arrays(init2_1, init2_2, init2_3, init2_4); write_init_arrays(init3_1, init3_2, init3_3, init3_4); write_HWCF4(0); write_HWCF5(0x00000083); write_HWCF6(0x00008000); write_init_arrays(init4_1, init4_2, init4_3, init4_4); write_HWCF3(0x0004); } /* emu8k_startsound: * Start a sound on a channel. */ void emu8k_startsound(int channel, struct envparms_t *envparms) { int psst = 0, csl = 0; write_DCYSUSV(channel, 0x0080); write_VTFT(channel, 0); write_CVCF(channel, 0); write_PTRX(channel, 0); write_CPF(channel, 0); write_ENVVOL(channel, envparms->envvol); write_ENVVAL(channel, envparms->envval); write_DCYSUS(channel, envparms->dcysus); write_ATKHLDV(channel, envparms->atkhldv); write_LFO1VAL(channel, envparms->lfo1val); write_ATKHLD(channel, envparms->atkhld); write_LFO2VAL(channel, envparms->lfo2val); write_IP(channel, envparms->ip); write_IFATN(channel, envparms->ifatn); write_PEFE(channel, envparms->pefe); write_FMMOD(channel, envparms->fmmod); write_TREMFRQ(channel, envparms->tremfrq); write_FM2FRQ2(channel, envparms->fm2frq2); switch (envparms->smpmode & 3) { case 0: /* no looping */ case 2: /* invalid; should be read as `no looping' */ psst = (envparms->psst & 0xff000000) /* copy pan verbatim; mask out loop start */ + (envparms->sampend) /* set loop start to near sample end */ - MIN_LOOP_LEN; csl = (envparms->csl & 0xff000000) /* copy chorus verbatim; mask out loop end */ + (envparms->sampend) /* set loop end to sample end */ + MIN_LOOP_LEN; break; case 1: /* loop indefinitely */ case 3: /* loop until key released, then continue */ psst = envparms->psst; /* leave the loop as it stands */ csl = envparms->csl; break; } write_PSST(channel, psst); write_CSL(channel, csl); write_CCCA(channel, envparms->ccca); write_VTFT(channel, 0x0000FFFF); write_CVCF(channel, 0x0000FFFF); write_DCYSUSV(channel, envparms->dcysusv); write_PTRX(channel, envparms->ptrx); write_CPF(channel, 0x40000000); } END_OF_STATIC_FUNCTION(emu8k_startsound); /* emu8k_releasesound: * End a sound on a channel by starting its release phase. */ void emu8k_releasesound(int channel, struct envparms_t *envparms) { if (envparms->smpmode == 3) { /* If sound should stop looping and continue, do so */ write_CSL(channel, (envparms->chorus << 24) + envparms->sampend + MIN_LOOP_LEN); write_PSST(channel, (envparms->pan << 24) + envparms->sampend - MIN_LOOP_LEN); } write_DCYSUSV(channel, 0x8000 + envparms->volrel); /* Start volume envelope release phase */ write_DCYSUS(channel, 0x8000 + envparms->modrel); /* Start modulation envelope release phase */ } END_OF_STATIC_FUNCTION(emu8k_releasesound); /* emu8k_modulate_*: * Modulation functions - changing a channel's sound in various ways. */ void emu8k_modulate_atten(int channel, int atten) { int ifatn; ifatn = read_IFATN(channel); write_IFATN(channel, (ifatn & 0xff00) + (atten & 0xff)); } END_OF_STATIC_FUNCTION(emu8k_modulate_atten); void emu8k_modulate_ip(int channel, int ip) { write_IP(channel, ip); } END_OF_STATIC_FUNCTION(emu8k_modulate_ip); void emu8k_modulate_pan(int channel, int pan) { int psst; psst = read_PSST(channel); write_PSST(channel, (pan << 24) + (psst & 0x00ffffff)); } END_OF_STATIC_FUNCTION(emu8k_modulate_pan); /* emu8k_terminatesound: * End a sound on a channel immediately (may produce an audible click). */ void emu8k_terminatesound(int channel) { write_DCYSUSV(channel, 0x0080); write_VTFT(channel, 0x0000FFFF); write_CVCF(channel, 0x0000FFFF); } END_OF_STATIC_FUNCTION(emu8k_terminatesound); /* emu8k_detect: * Locate the EMU8000. This tries to extract the base port from the E section * of the BLASTER environment variable, and then does some test reads to check * that there is an EMU8000 there. */ int emu8k_detect(void) { char *envvar; if (!(envvar = getenv("BLASTER"))) { ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("BLASTER environment variable not set")); return 0; } _emu8k_baseport = 0; while (*envvar) { if (*envvar == 'E') _emu8k_baseport = strtol(envvar + 1, NULL, 16); while ((*envvar != ' ') && (*envvar != 0)) envvar++; if (*envvar) envvar++; } if (!_emu8k_baseport) { ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("BLASTER environment variable has no E section")); return 0; } uszprintf(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("AWE32 detection failed on port 0x%04x"), _emu8k_baseport); if ((read_word(7, 0, 3) & 0x000f) != 0x000c) return 0; if ((read_word(1, 29, 1) & 0x007e) != 0x0058) return 0; if ((read_word(1, 30, 1) & 0x0003) != 0x0003) return 0; usetc(allegro_error, 0); return 1; } /* emu_*: * Functions to convert SoundFont information to EMU8000 parameters. */ static INLINE unsigned short emu_delay(int x) { int a = 0x8000 - pow(2, x / 1200.0) / 0.000725; return (a < 0) ? 0 : ((a > 0x8000) ? 0x8000 : a); } static INLINE unsigned char emu_attack(int x) { /* * AEPG doesn't specify exact conversion here; I'm using what Takashi * Iwai used for his Linux driver */ int a, msec; msec = pow(2, x / 1200.0) * 1000; if (msec == 0) return 0x7f; a = 11878 / msec; return (a < 1) ? 1 : ((a > 0x7f) ? 0x7f : a); } static INLINE unsigned char emu_hold(int x) { int a = 0x7f - (int)(pow(2, x / 1200.0) * 0x7f / 11.68); return (a < 0) ? 0 : ((a > 0x7f) ? 0x7f : a); } static INLINE unsigned char emu_decay(int x) { /* * AEPG doesn't specify exact conversion here; I'm using an adaptation of * what Takashi Iwai used for his Linux driver */ int a, msec; msec = pow(2, x / 1200.0) * 1000; if (msec == 0) return 0x7f; a = 0x7f - 54.8 * log10(msec / 23.04); /* Takashi's */ // a = 47349/(msec+349); /* mine */ return (a < 1) ? 1 : ((a > 0x7f) ? 0x7f : a); } static INLINE unsigned char emu_sustain(int x) { /* This is not the same as in Takashi Iwai's Linux driver, but I think I'm right */ int a = 0x7f - x / 7.5; return (a < 0) ? 0 : ((a > 0x7f) ? 0x7f : a); } static INLINE unsigned char emu_release(int x) { return emu_decay(x); } static INLINE unsigned char emu_mod(int x, int peak_deviation) { int a = x * 0x80 / peak_deviation; return (a < -0x80) ? 0x80 : ((a > 0x7f) ? 0x7f : a); } static INLINE unsigned char emu_freq(int x) { int a = (8.176 * pow(2, x / 1200.0) + 0.032) / 0.042; return (a < 0) ? 0 : ((a > 0xff) ? 0xff : a); } static INLINE unsigned char emu_reverb(int x) { int a = (x * 0xff) / 1000; return (a < 0) ? 0 : ((a > 0xff) ? 0xff : a); } static INLINE unsigned char emu_chorus(int x) { int a = (x * 0xff) / 1000; return (a < 0) ? 0 : ((a > 0xff) ? 0xff : a); } static INLINE unsigned char emu_pan(int x) { int a = 0x7f - (x * 0xff) / 1000; return (a < 8) ? 8 : ((a > 0xf8) ? 0xf8 : a); } static INLINE unsigned char emu_filterQ(int x) { int a = (x / 15); return (a < 0) ? 0 : ((a > 15) ? 15 : a); } static INLINE unsigned int emu_address(int base, int offset, int coarse_offset) { return base + offset + coarse_offset * 32 * 1024; } /* static INLINE unsigned short emu_pitch(int key, int rootkey, int scale, int coarse, int fine, int initial) { int a = (((key - rootkey) * scale + coarse * 100 + fine - initial) * 0x1000) / 1200 + 0xE000; return (a < 0) ? 0 : ((a > 0xFFFF) ? 0xFFFF : a); } */ static INLINE unsigned char emu_filter(int x) { int a = (x - 4721) / 25; return (a < 0) ? 0 : ((a > 0xff) ? 0xff : a); } static INLINE unsigned char emu_atten(int x) { int a = x / 3.75; return (a < 0) ? 0 : ((a > 0xff) ? 0xff : a); } /* emu8k_createenvelope: * Converts a set of SoundFont generators to equivalent EMU8000 register * settings, for passing to the sound playing functions above. */ envparms_t *emu8k_createenvelope(generators_t sfgen) { envparms_t *envelope = (envparms_t *) _lock_malloc(sizeof(envparms_t)); if (envelope) { envelope->envvol = emu_delay(sfgen[sfgen_delayModEnv]); envelope->envval = emu_delay(sfgen[sfgen_delayVolEnv]); envelope->modsust = (emu_sustain(sfgen[sfgen_sustainModEnv]) << 8); envelope->modrel = (emu_release(sfgen[sfgen_releaseModEnv]) << 0); envelope->dcysus = (0 << 15) // we're programming the decay, not the release + envelope->modsust + (0 << 7) // unused + (emu_decay(sfgen[sfgen_decayModEnv]) << 0); envelope->atkhldv = (0 << 15) // 0 otherwise it won't attack + (emu_hold(sfgen[sfgen_holdVolEnv]) << 8) + (0 << 7) // unused + (emu_attack(sfgen[sfgen_attackVolEnv]) << 0); envelope->lfo1val = emu_delay(sfgen[sfgen_delayModLFO]); envelope->atkhld = (0 << 15) // 0 otherwise it won't attack + (emu_hold(sfgen[sfgen_holdModEnv]) << 8) + (0 << 7) // unused + (emu_attack(sfgen[sfgen_attackModEnv]) << 0); envelope->lfo2val = emu_delay(sfgen[sfgen_delayVibLFO]); envelope->pitch = 0xe000; envelope->ip = envelope->pitch; envelope->filter = (emu_filter(sfgen[sfgen_initialFilterFc]) << 8); envelope->atten = (emu_atten(sfgen[sfgen_initialAttenuation]) << 0); envelope->ifatn = envelope->filter + envelope->atten; envelope->pefe = (emu_mod(sfgen[sfgen_modEnvToPitch], 1200) << 8) + (emu_mod(sfgen[sfgen_modEnvToFilterFc], 7200) << 0); envelope->fmmod = (emu_mod(sfgen[sfgen_modLfoToPitch], 1200) << 8) + (emu_mod(sfgen[sfgen_modLfoToFilterFc], 3600) << 0); envelope->tremfrq = (emu_mod(sfgen[sfgen_modLfoToVolume], 120) << 8) + (emu_freq(sfgen[sfgen_freqModLFO]) << 0); envelope->fm2frq2 = (emu_mod(sfgen[sfgen_vibLfoToPitch], 1200) << 8) + (emu_freq(sfgen[sfgen_freqVibLFO]) << 0); envelope->volsust = (emu_sustain(sfgen[sfgen_sustainVolEnv]) << 8); envelope->volrel = (emu_release(sfgen[sfgen_releaseVolEnv]) << 0); envelope->dcysusv = (0 << 15) // we're programming decay not release + envelope->volsust + (0 << 7) // turn on envelope engine + (emu_decay(sfgen[sfgen_decayVolEnv]) << 0); envelope->ptrx = (0x4000 << 16) // ??? I thought the top word wasn't for me to use + (emu_reverb(sfgen[sfgen_reverbEffectsSend]) << 8) + (0 << 0); // unused envelope->pan = emu_pan(sfgen[sfgen_pan]); envelope->loopst = emu_address(sfgen[gfgen_startloopAddrs], sfgen[sfgen_startloopAddrsOffset], sfgen[sfgen_startloopAddrsCoarseOffset]); envelope->sampend = emu_address(sfgen[gfgen_endAddrs], sfgen[sfgen_endAddrsOffset], sfgen[sfgen_endAddrsCoarseOffset]); envelope->psst = (envelope->pan << 24) + (envelope->loopst); envelope->chorus = emu_chorus(sfgen[sfgen_chorusEffectsSend]); envelope->csl = (envelope->chorus << 24) + (emu_address(sfgen[gfgen_endloopAddrs], sfgen[sfgen_endloopAddrsOffset], sfgen[sfgen_endloopAddrsCoarseOffset]) << 0); envelope->ccca = (emu_filterQ(sfgen[sfgen_initialFilterQ]) << 28) + (0 << 24) // DMA control bits + (emu_address(sfgen[gfgen_startAddrs], sfgen[sfgen_startAddrsOffset], sfgen[sfgen_startAddrsCoarseOffset]) << 0); envelope->rootkey = (sfgen[sfgen_overridingRootKey] != -1) ? sfgen[sfgen_overridingRootKey] : 69; envelope->ipbase = ((sfgen[sfgen_coarseTune] * 100 + sfgen[sfgen_fineTune] - envelope->rootkey * sfgen[sfgen_scaleTuning]) * 0x1000) / 1200 + 0xE000; envelope->ipscale = sfgen[sfgen_scaleTuning]; envelope->minkey = sfgen[sfgen_keyRange] & 0xff; envelope->maxkey = (sfgen[sfgen_keyRange] >> 8) & 0xff; envelope->minvel = sfgen[sfgen_velRange] & 0xff; envelope->maxvel = (sfgen[sfgen_velRange] >> 8) & 0xff; envelope->key = -1; envelope->vel = -1; envelope->exc = sfgen[sfgen_exclusiveClass]; envelope->keyMEH = 0; envelope->keyMED = 0; envelope->keyVEH = 0; envelope->keyVED = 0; envelope->smpmode = sfgen[sfgen_sampleModes]; } return envelope; } /* emu8k_destroyenvelope: * Destroys an envelope created by emu8k_createenvelope. */ void emu8k_destroyenvelope(envparms_t * env) { free(env); } /* emu8k_lock: * Locks all data and functions which might be called in an interrupt context. */ void emu8k_lock(void) { LOCK_VARIABLE(_emu8k_baseport); LOCK_VARIABLE(_emu8k_numchannels); LOCK_FUNCTION(emu8k_startsound); LOCK_FUNCTION(emu8k_releasesound); LOCK_FUNCTION(emu8k_modulate_atten); LOCK_FUNCTION(emu8k_modulate_ip); LOCK_FUNCTION(emu8k_modulate_pan); LOCK_FUNCTION(emu8k_terminatesound); }