/* Extended Module Player * Copyright (C) 1997-2001 Claudio Matsuoka and Hipolito Carraro Jr * * This file is part of the Extended Module Player and is distributed * under the terms of the GNU General Public License. See doc/COPYING * for more information. * * $Id: synth.c,v 1.9 2001/01/13 11:54:30 claudio Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "xmp.h" #include "xmpi.h" #include "driver.h" #include "fmopl.h" /* * ------+-----------------------------------+-----------------------+ * offset| SBI data format | YM3812 base port * ------+-----------------------------------+-----------------------+ * 0 Modulator Sound Characteristics 20 * 1 Carrier Sound Characteristics * Bit 7: AM Modulation (am) * Bit 6: Vibrato (vibrato) * Bit 5: Sustaining Sound * Bit 4: Envelop Scaling * Bits 3-0: Frequency Multiplier * ------+-----------------------------------+-----------------------+ * 2 Modulator Scaling/Output Level 40 * 3 Carrier Scaling/Output Level * Bits 7-6: Level Scaling * Bits 5-0: Output Level * ------+-----------------------------------+-----------------------+ * 4 Modulator Attack/Decay 60 * 5 Carrier Attack/Decay * Bits 7-4: Attack Rate * Bits 3-0: Decay Rate * ------+-----------------------------------+-----------------------+ * 6 Modulator Sustain/Release 80 * 7 Carrier Sustain/Release * Bits 7-4: Sustain Level * Bits 3-0: Release Rate * ------+-----------------------------------+-----------------------+ * 8 Modulator Wave Select E0 * 9 Carrier Wave Select * Bits 7-2: All bits clear * Bits 1-0: Wave Select * ------+-----------------------------------+-----------------------+ * 10 Feedback/Connection C0 * Bits 7-4: All bits clear * Bits 3-1: Modulator Feedback * Bit 0: Connection * ------+-----------------------------------+-----------------------+ */ static int register_base[11] = { 0x20, 0x20, 0x40, 0x40, 0x60, 0x60, 0x80, 0x80, 0xe0, 0xe0, 0xc0 }; static int register_offset[2][9] = { /* Channel 1 2 3 4 5 6 7 8 9 */ /* Operator 1 */ { 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12 }, /* Operator 2 */ { 0x03, 0x04, 0x05, 0x0B, 0x0C, 0x0D, 0x13, 0x14, 0x15 } }; /* * In octave 4, the F-number values for the chromatic scale and their * corresponding frequencies would be: * * F Number Frequency Note * 16B 277.2 C# * 181 293.7 D * 198 311.1 D# * 1B0 329.6 E * 1CA 349.2 F * 1E5 370.0 F# * 202 392.0 G * 220 415.3 G# * 241 440.0 A * 263 466.2 A# * 287 493.9 B * 2AE 523.3 C */ static int ym3812_note[] = { 0x157, 0x16b, 0x181, 0x198, 0x1b0, 0x1ca, 0x1e5, 0x202, 0x220, 0x241, 0x263, 0x287, 0x2ae }; static FM_OPL *ym3812; #define NUM_SYNTH_CHANNEL 9 static int voc2ch[NUM_SYNTH_CHANNEL]; #undef DEBUG_ADLIB #ifdef DEBUG_ADLIB #include #include #define DELAY(x) do { int i; for (i = 0; i < x; i++) inb (0x388); } while (0) #endif /* * After writing to the register port, you must wait twelve cycles before * sending the data; after writing the data, eighty-four cycles must elapse * before any other sound card operation may be performed. * * The most accurate method of producing the delay is to read the register * port six times after writing to the register port, and read the register * port thirty-five times after writing to the data port. */ static inline int opl_write (FM_OPL *opl, int a, int v) { #ifdef DEBUG_ADLIB outb (a, 0x388); DELAY (5); outb (v, 0x389); DELAY (35); return 0; #else OPLWrite (opl, 0x388, a); return OPLWrite (opl, 0x389, v); #endif } static inline uint8 opl_read (FM_OPL *opl, int a) { #ifdef DEBUG_ADLIB int x; outb (a, 0x388); DELAY (5); x = inb (0x389); DELAY (35); return x; #else OPLWrite (opl, 0x388, a); return OPLRead (opl, 0x389); #endif } static int synth_getchannel (int c) { int i, free = -1; for (c++, i = 0; i < NUM_SYNTH_CHANNEL; i++) { if (voc2ch[i] == c) { return i; } if (voc2ch[i] == 0) { free = i; break; } } if (free != -1) voc2ch[free] = c; return free; } void synth_chreset () { int i; for (i = NUM_SYNTH_CHANNEL; i--;) voc2ch[i] = 0; } void synth_setpatch (int c, uint8 *data) { int i, x; if ((c = synth_getchannel (c)) < 0) return; for (i = 0; i < 10; i++) opl_write (ym3812, register_base[i] + register_offset[i % 2][c], data[i]); opl_write (ym3812, register_base[10] + c, data[10]); x = opl_read (ym3812, 0xb0 + c); opl_write (ym3812, 0xb0 + c, x & ~0x20); } /* * Bytes A0-B8 - Octave / F-Number / Key-On * * 7 6 5 4 3 2 1 0 * +-----+-----+-----+-----+-----+-----+-----+-----+ * | F-Number (least significant byte) | (A0-A8) * | | * +-----+-----+-----+-----+-----+-----+-----+-----+ * * 7 6 5 4 3 2 1 0 * +-----+-----+-----+-----+-----+-----+-----+-----+ * | Unused | Key | Octave | F-Number | (B0-B8) * | | On | | most sig. | * +-----+-----+-----+-----+-----+-----+-----+-----+ * * bit 5 - Channel is voiced when set, silent when clear. * bits 4-2 - Octave (0-7). 0 is lowest, 7 is highest. * bits 1-0 - Most significant bits of F-number. */ void synth_setnote (int c, int note, int bend) { int n, f, o; if ((c = synth_getchannel (c)) < 0) return; n = note % 12; f = ym3812_note[n] + (ym3812_note[n + 1] - ym3812_note[n]) * bend / 100; o = note / 12 - 1; if (o < 0) o = 0; opl_write (ym3812, 0xa0 + c, f & 0xff); opl_write (ym3812, 0xb0 + c, 0x20 | ((o << 2) & 0x1c) | ((f >> 8) & 0x03)); } int synth_init (int freq) { #ifdef DEBUG_ADLIB ioperm (0x388, 2, 1); #endif ym3812 = OPLCreate (OPL_TYPE_IO, 3600000, freq); synth_chreset (); return ym3812 != NULL; } int synth_reset () { #ifdef DEBUG_ADLIB int i; for (i = 0; i < 9; i++) opl_write (ym3812, 0xb0 + i, 0); #else OPLResetChip (ym3812); #endif synth_chreset (); return 0; } int synth_deinit () { synth_reset (); OPLDestroy (ym3812); return 0; } void synth_mixer (int* tmp_bk, int count, int vl, int vr, int itpt_inc) { if (!tmp_bk) return; YM3812UpdateOne (ym3812, tmp_bk, count, itpt_inc, vl / 2, vr / 2); }