qzdoom-gpl/src/oplsynth/mlopl_io.cpp
Randy Heit 3a5afd1418 - Added support for dumping from RAW/DRO/IMF files, so now anything that
can be played as OPL can also be dumped.
- Removed the opl_enable cvar, since OPL playback is now selectable as just
  another MIDI device.
- Added support for DRO playback and dual-chip RAW playback.
- Removed MUS support from OPLMUSSong, since using the OPLMIDIDevice with
  MUSSong2 works just as well. There are still lots of leftover bits in
  the class that should probably be removed at some point, too.
- Added dual-chip dumping support for the RAW format.
- Added DosBox Raw OPL (.DRO) dumping support. For whatever reason,
  in_adlib calculates the song length for this format wrong, even though
  the exact length is stored right in the header. (But in_adlib seems buggy
  in general; too bad it's the only Windows version of Adplug that seems to
  exist.)
- Rewrote the OPL dumper to work with MIDI as well as MUS.


SVN r872 (trunk)
2008-04-03 02:31:39 +00:00

332 lines
12 KiB
C++

/*
* Name: Low-level OPL2/OPL3 I/O interface
* Project: MUS File Player Library
* Version: 1.64
* Author: Vladimir Arnost (QA-Software)
* Last revision: Mar-1-1996
* Compiler: Borland C++ 3.1, Watcom C/C++ 10.0
*
*/
/*
* Revision History:
*
* Aug-8-1994 V1.00 V.Arnost
* Written from scratch
* Aug-9-1994 V1.10 V.Arnost
* Added stereo capabilities
* Aug-13-1994 V1.20 V.Arnost
* Stereo capabilities made functional
* Aug-24-1994 V1.30 V.Arnost
* Added Adlib and SB Pro II detection
* Oct-30-1994 V1.40 V.Arnost
* Added BLASTER variable parsing
* Apr-14-1995 V1.50 V.Arnost
* Some declarations moved from adlib.h to deftypes.h
* Jul-22-1995 V1.60 V.Arnost
* Ported to Watcom C
* Simplified WriteChannel() and WriteValue()
* Jul-24-1995 V1.61 V.Arnost
* DetectBlaster() moved to MLMISC.C
* Aug-8-1995 V1.62 V.Arnost
* Module renamed to MLOPL_IO.C and functions renamed to OPLxxx
* Mixer-related functions moved to module MLSBMIX.C
* Sep-8-1995 V1.63 V.Arnost
* OPLwriteReg() routine sped up on OPL3 cards
* Mar-1-1996 V1.64 V.Arnost
* Cleaned up the source
*/
#ifdef _WIN32
#include <dos.h>
#include <conio.h>
#endif
#include "muslib.h"
#include "fmopl.h"
OPLio::~OPLio()
{
}
void OPLio::SetClockRate(double samples_per_tick)
{
}
void OPLio::WriteDelay(int ticks)
{
}
void OPLio::OPLwriteReg(int which, uint reg, uchar data)
{
YM3812Write (which, 0, reg);
YM3812Write (which, 1, data);
}
/*
* Write to an operator pair. To be used for register bases of 0x20, 0x40,
* 0x60, 0x80 and 0xE0.
*/
void OPLio::OPLwriteChannel(uint regbase, uint channel, uchar data1, uchar data2)
{
static const uint op_num[] = {
0x000, 0x001, 0x002, 0x008, 0x009, 0x00A, 0x010, 0x011, 0x012,
0x100, 0x101, 0x102, 0x108, 0x109, 0x10A, 0x110, 0x111, 0x112};
uint reg = regbase+op_num[channel];
uint which = reg>>8;
OPLwriteReg (which, reg, data1);
OPLwriteReg (which, reg+3, data2);
}
/*
* Write to channel a single value. To be used for register bases of
* 0xA0, 0xB0 and 0xC0.
*/
void OPLio::OPLwriteValue(uint regbase, uint channel, uchar value)
{
static const uint reg_num[] = {
0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, 0x008,
0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108};
uint reg = regbase+reg_num[channel];
uint which = reg>>8;
OPLwriteReg (which, reg, value);
}
static WORD frequencies[] =
{
0x133, 0x133, 0x134, 0x134, 0x135, 0x136, 0x136, 0x137, 0x137, 0x138, 0x138, 0x139,
0x139, 0x13a, 0x13b, 0x13b, 0x13c, 0x13c, 0x13d, 0x13d, 0x13e, 0x13f, 0x13f, 0x140,
0x140, 0x141, 0x142, 0x142, 0x143, 0x143, 0x144, 0x144, 0x145, 0x146, 0x146, 0x147,
0x147, 0x148, 0x149, 0x149, 0x14a, 0x14a, 0x14b, 0x14c, 0x14c, 0x14d, 0x14d, 0x14e,
0x14f, 0x14f, 0x150, 0x150, 0x151, 0x152, 0x152, 0x153, 0x153, 0x154, 0x155, 0x155,
0x156, 0x157, 0x157, 0x158, 0x158, 0x159, 0x15a, 0x15a, 0x15b, 0x15b, 0x15c, 0x15d,
0x15d, 0x15e, 0x15f, 0x15f, 0x160, 0x161, 0x161, 0x162, 0x162, 0x163, 0x164, 0x164,
0x165, 0x166, 0x166, 0x167, 0x168, 0x168, 0x169, 0x16a, 0x16a, 0x16b, 0x16c, 0x16c,
0x16d, 0x16e, 0x16e, 0x16f, 0x170, 0x170, 0x171, 0x172, 0x172, 0x173, 0x174, 0x174,
0x175, 0x176, 0x176, 0x177, 0x178, 0x178, 0x179, 0x17a, 0x17a, 0x17b, 0x17c, 0x17c,
0x17d, 0x17e, 0x17e, 0x17f, 0x180, 0x181, 0x181, 0x182, 0x183, 0x183, 0x184, 0x185,
0x185, 0x186, 0x187, 0x188, 0x188, 0x189, 0x18a, 0x18a, 0x18b, 0x18c, 0x18d, 0x18d,
0x18e, 0x18f, 0x18f, 0x190, 0x191, 0x192, 0x192, 0x193, 0x194, 0x194, 0x195, 0x196,
0x197, 0x197, 0x198, 0x199, 0x19a, 0x19a, 0x19b, 0x19c, 0x19d, 0x19d, 0x19e, 0x19f,
0x1a0, 0x1a0, 0x1a1, 0x1a2, 0x1a3, 0x1a3, 0x1a4, 0x1a5, 0x1a6, 0x1a6, 0x1a7, 0x1a8,
0x1a9, 0x1a9, 0x1aa, 0x1ab, 0x1ac, 0x1ad, 0x1ad, 0x1ae, 0x1af, 0x1b0, 0x1b0, 0x1b1,
0x1b2, 0x1b3, 0x1b4, 0x1b4, 0x1b5, 0x1b6, 0x1b7, 0x1b8, 0x1b8, 0x1b9, 0x1ba, 0x1bb,
0x1bc, 0x1bc, 0x1bd, 0x1be, 0x1bf, 0x1c0, 0x1c0, 0x1c1, 0x1c2, 0x1c3, 0x1c4, 0x1c4,
0x1c5, 0x1c6, 0x1c7, 0x1c8, 0x1c9, 0x1c9, 0x1ca, 0x1cb, 0x1cc, 0x1cd, 0x1ce, 0x1ce,
0x1cf, 0x1d0, 0x1d1, 0x1d2, 0x1d3, 0x1d3, 0x1d4, 0x1d5, 0x1d6, 0x1d7, 0x1d8, 0x1d8,
0x1d9, 0x1da, 0x1db, 0x1dc, 0x1dd, 0x1de, 0x1de, 0x1df, 0x1e0, 0x1e1, 0x1e2, 0x1e3,
0x1e4, 0x1e5, 0x1e5, 0x1e6, 0x1e7, 0x1e8, 0x1e9, 0x1ea, 0x1eb, 0x1ec, 0x1ed, 0x1ed,
0x1ee, 0x1ef, 0x1f0, 0x1f1, 0x1f2, 0x1f3, 0x1f4, 0x1f5, 0x1f6, 0x1f6, 0x1f7, 0x1f8,
0x1f9, 0x1fa, 0x1fb, 0x1fc, 0x1fd, 0x1fe, 0x1ff, 0x200,
0x201, 0x201, 0x202, 0x203, 0x204, 0x205, 0x206, 0x207, 0x208, 0x209, 0x20a, 0x20b, 0x20c, 0x20d, 0x20e, 0x20f,
0x210, 0x210, 0x211, 0x212, 0x213, 0x214, 0x215, 0x216, 0x217, 0x218, 0x219, 0x21a, 0x21b, 0x21c, 0x21d, 0x21e,
0x21f, 0x220, 0x221, 0x222, 0x223, 0x224, 0x225, 0x226, 0x227, 0x228, 0x229, 0x22a, 0x22b, 0x22c, 0x22d, 0x22e,
0x22f, 0x230, 0x231, 0x232, 0x233, 0x234, 0x235, 0x236, 0x237, 0x238, 0x239, 0x23a, 0x23b, 0x23c, 0x23d, 0x23e,
0x23f, 0x240, 0x241, 0x242, 0x244, 0x245, 0x246, 0x247, 0x248, 0x249, 0x24a, 0x24b, 0x24c, 0x24d, 0x24e, 0x24f,
0x250, 0x251, 0x252, 0x253, 0x254, 0x256, 0x257, 0x258, 0x259, 0x25a, 0x25b, 0x25c, 0x25d, 0x25e, 0x25f, 0x260,
0x262, 0x263, 0x264, 0x265, 0x266, 0x267, 0x268, 0x269, 0x26a, 0x26c, 0x26d, 0x26e, 0x26f, 0x270, 0x271, 0x272,
0x273, 0x275, 0x276, 0x277, 0x278, 0x279, 0x27a, 0x27b, 0x27d, 0x27e, 0x27f, 0x280, 0x281, 0x282, 0x284, 0x285,
0x286, 0x287, 0x288, 0x289, 0x28b, 0x28c, 0x28d, 0x28e, 0x28f, 0x290, 0x292, 0x293, 0x294, 0x295, 0x296, 0x298,
0x299, 0x29a, 0x29b, 0x29c, 0x29e, 0x29f, 0x2a0, 0x2a1, 0x2a2, 0x2a4, 0x2a5, 0x2a6, 0x2a7, 0x2a9, 0x2aa, 0x2ab,
0x2ac, 0x2ae, 0x2af, 0x2b0, 0x2b1, 0x2b2, 0x2b4, 0x2b5, 0x2b6, 0x2b7, 0x2b9, 0x2ba, 0x2bb, 0x2bd, 0x2be, 0x2bf,
0x2c0, 0x2c2, 0x2c3, 0x2c4, 0x2c5, 0x2c7, 0x2c8, 0x2c9, 0x2cb, 0x2cc, 0x2cd, 0x2ce, 0x2d0, 0x2d1, 0x2d2, 0x2d4,
0x2d5, 0x2d6, 0x2d8, 0x2d9, 0x2da, 0x2dc, 0x2dd, 0x2de, 0x2e0, 0x2e1, 0x2e2, 0x2e4, 0x2e5, 0x2e6, 0x2e8, 0x2e9,
0x2ea, 0x2ec, 0x2ed, 0x2ee, 0x2f0, 0x2f1, 0x2f2, 0x2f4, 0x2f5, 0x2f6, 0x2f8, 0x2f9, 0x2fb, 0x2fc, 0x2fd, 0x2ff,
0x300, 0x302, 0x303, 0x304, 0x306, 0x307, 0x309, 0x30a, 0x30b, 0x30d, 0x30e, 0x310, 0x311, 0x312, 0x314, 0x315,
0x317, 0x318, 0x31a, 0x31b, 0x31c, 0x31e, 0x31f, 0x321, 0x322, 0x324, 0x325, 0x327, 0x328, 0x329, 0x32b, 0x32c,
0x32e, 0x32f, 0x331, 0x332, 0x334, 0x335, 0x337, 0x338, 0x33a, 0x33b, 0x33d, 0x33e, 0x340, 0x341, 0x343, 0x344,
0x346, 0x347, 0x349, 0x34a, 0x34c, 0x34d, 0x34f, 0x350, 0x352, 0x353, 0x355, 0x357, 0x358, 0x35a, 0x35b, 0x35d,
0x35e, 0x360, 0x361, 0x363, 0x365, 0x366, 0x368, 0x369, 0x36b, 0x36c, 0x36e, 0x370, 0x371, 0x373, 0x374, 0x376,
0x378, 0x379, 0x37b, 0x37c, 0x37e, 0x380, 0x381, 0x383, 0x384, 0x386, 0x388, 0x389, 0x38b, 0x38d, 0x38e, 0x390,
0x392, 0x393, 0x395, 0x397, 0x398, 0x39a, 0x39c, 0x39d, 0x39f, 0x3a1, 0x3a2, 0x3a4, 0x3a6, 0x3a7, 0x3a9, 0x3ab,
0x3ac, 0x3ae, 0x3b0, 0x3b1, 0x3b3, 0x3b5, 0x3b7, 0x3b8, 0x3ba, 0x3bc, 0x3bd, 0x3bf, 0x3c1, 0x3c3, 0x3c4, 0x3c6,
0x3c8, 0x3ca, 0x3cb, 0x3cd, 0x3cf, 0x3d1, 0x3d2, 0x3d4, 0x3d6, 0x3d8, 0x3da, 0x3db, 0x3dd, 0x3df, 0x3e1, 0x3e3,
0x3e4, 0x3e6, 0x3e8, 0x3ea, 0x3ec, 0x3ed, 0x3ef, 0x3f1, 0x3f3, 0x3f5, 0x3f6, 0x3f8, 0x3fa, 0x3fc, 0x3fe, 0x36c
};
/*
* Write frequency/octave/keyon data to a channel
* [RH] This is totally different from the original MUS library code
* but matches exactly what DMX does. I haven't a clue why there are 284
* special bytes at the beginning of the table for the first few notes.
* That last byte in the table doesn't look right, either, but that's what
* it really is.
*/
void OPLio::OPLwriteFreq(uint channel, uint note, uint pitch, uint keyon)
{
int octave = 0;
int j = (note << 5) + pitch;
if (j < 0)
{
j = 0;
}
else if (j >= 284)
{
j -= 284;
octave = j / (32*12);
if (octave > 7)
{
octave = 7;
}
j = (j % (32*12)) + 284;
}
int i = frequencies[j] | (octave << 10);
OPLwriteValue (0xA0, channel, (BYTE)i);
OPLwriteValue (0xB0, channel, (BYTE)(i>>8)|(keyon<<5));
}
/*
* Adjust volume value (register 0x40)
*/
inline uint OPLio::OPLconvertVolume(uint data, uint volume)
{
static uchar volumetable[128] = {
0, 1, 3, 5, 6, 8, 10, 11,
13, 14, 16, 17, 19, 20, 22, 23,
25, 26, 27, 29, 30, 32, 33, 34,
36, 37, 39, 41, 43, 45, 47, 49,
50, 52, 54, 55, 57, 59, 60, 61,
63, 64, 66, 67, 68, 69, 71, 72,
73, 74, 75, 76, 77, 79, 80, 81,
82, 83, 84, 84, 85, 86, 87, 88,
89, 90, 91, 92, 92, 93, 94, 95,
96, 96, 97, 98, 99, 99, 100, 101,
101, 102, 103, 103, 104, 105, 105, 106,
107, 107, 108, 109, 109, 110, 110, 111,
112, 112, 113, 113, 114, 114, 115, 115,
116, 117, 117, 118, 118, 119, 119, 120,
120, 121, 121, 122, 122, 123, 123, 123,
124, 124, 125, 125, 126, 126, 127, 127};
return 0x3F - (((0x3F - data) *
(uint)volumetable[volume <= 127 ? volume : 127]) >> 7);
}
uint OPLio::OPLpanVolume(uint volume, int pan)
{
if (pan >= 0)
return volume;
else
return (volume * (pan + 64)) / 64;
}
/*
* Write volume data to a channel
*/
void OPLio::OPLwriteVolume(uint channel, struct OPL2instrument *instr, uint volume)
{
if (instr != 0)
{
OPLwriteChannel(0x40, channel, ((instr->feedback & 1) ?
OPLconvertVolume(instr->level_1, volume) : instr->level_1) | instr->scale_1,
OPLconvertVolume(instr->level_2, volume) | instr->scale_2);
}
}
/*
* Write pan (balance) data to a channel
*/
void OPLio::OPLwritePan(uint channel, struct OPL2instrument *instr, int pan)
{
if (instr != 0)
{
uchar bits;
if (pan < -36) bits = 0x10; // left
else if (pan > 36) bits = 0x20; // right
else bits = 0x30; // both
OPLwriteValue(0xC0, channel, instr->feedback | bits);
}
}
/*
* Write an instrument to a channel
*
* Instrument layout:
*
* Operator1 Operator2 Descr.
* data[0] data[7] reg. 0x20 - tremolo/vibrato/sustain/KSR/multi
* data[1] data[8] reg. 0x60 - attack rate/decay rate
* data[2] data[9] reg. 0x80 - sustain level/release rate
* data[3] data[10] reg. 0xE0 - waveform select
* data[4] data[11] reg. 0x40 - key scale level
* data[5] data[12] reg. 0x40 - output level (bottom 6 bits only)
* data[6] reg. 0xC0 - feedback/AM-FM (both operators)
*/
void OPLio::OPLwriteInstrument(uint channel, struct OPL2instrument *instr)
{
OPLwriteChannel(0x40, channel, 0x3F, 0x3F); // no volume
OPLwriteChannel(0x20, channel, instr->trem_vibr_1, instr->trem_vibr_2);
OPLwriteChannel(0x60, channel, instr->att_dec_1, instr->att_dec_2);
OPLwriteChannel(0x80, channel, instr->sust_rel_1, instr->sust_rel_2);
OPLwriteChannel(0xE0, channel, instr->wave_1, instr->wave_2);
OPLwriteValue (0xC0, channel, instr->feedback | 0x30);
}
/*
* Stop all sounds
*/
void OPLio::OPLshutup(void)
{
uint i;
for(i = 0; i < OPLchannels; i++)
{
OPLwriteChannel(0x40, i, 0x3F, 0x3F); // turn off volume
OPLwriteChannel(0x60, i, 0xFF, 0xFF); // the fastest attack, decay
OPLwriteChannel(0x80, i, 0x0F, 0x0F); // ... and release
OPLwriteValue(0xB0, i, 0); // KEY-OFF
}
}
/*
* Initialize hardware upon startup
*/
int OPLio::OPLinit(uint numchips)
{
if (!YM3812Init (numchips, 3579545, int(OPL_SAMPLE_RATE)))
{
OPLchannels = OPL2CHANNELS * numchips;
OPLwriteInitState();
return 0;
}
else
{
return -1;
}
}
void OPLio::OPLwriteInitState()
{
for (uint i = 0; i < OPLchannels / OPL2CHANNELS; ++i)
{
OPLwriteReg (i, 0x01, 0x20); // enable Waveform Select
OPLwriteReg (i, 0x0B, 0x40); // turn off CSW mode
OPLwriteReg (i, 0xBD, 0x00); // set vibrato/tremolo depth to low, set melodic mode
}
OPLshutup();
}
/*
* Deinitialize hardware before shutdown
*/
void OPLio::OPLdeinit(void)
{
YM3812Shutdown ();
}