mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-22 20:21:26 +00:00
- 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)
This commit is contained in:
parent
298e465e22
commit
3a5afd1418
18 changed files with 846 additions and 456 deletions
|
@ -1,3 +1,24 @@
|
|||
April 2, 2008
|
||||
- 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.
|
||||
|
||||
April 1, 2008
|
||||
- Added support for DRO playback and dual-chip RAW playback.
|
||||
|
||||
March 30, 2008
|
||||
- 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.
|
||||
|
||||
March 30, 2008 (Changes by Graf Zahl)
|
||||
- Changed: When the screen is being deleted the 'screen' variable should be
|
||||
set to NULL before performing the delete. Otherwise, in some abnormal
|
||||
|
@ -5904,7 +5925,7 @@ February 9, 2005
|
|||
took me a while to figure out the problem, ZDoom can now play raw OPL songs.
|
||||
|
||||
February 7, 2005
|
||||
- Added the writeopl command to write a song in RDosPlay raw OPL format. It's usage is:
|
||||
- Added the writeopl command to write a song in RDosPlay raw OPL format. Its usage is:
|
||||
writeopl [songname] <filename>
|
||||
If the currently playing song is a MUS song being played through the OPL emulation,
|
||||
then you just need to specify the filename. Otherwise, you need to provide the
|
||||
|
|
|
@ -1266,7 +1266,6 @@ static menu_t SoundMenu =
|
|||
*
|
||||
*=======================================*/
|
||||
|
||||
EXTERN_CVAR (Bool, opl_enable)
|
||||
EXTERN_CVAR (Bool, opl_onechip)
|
||||
|
||||
static menuitem_t AdvSoundItems[] =
|
||||
|
|
|
@ -75,17 +75,6 @@ Revision History:
|
|||
#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) */
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
#include "zstring.h"
|
||||
|
||||
/* select output bits size of output : 8 or 16 */
|
||||
#define OPL_SAMPLE_BITS 16
|
||||
// Multiplying OPL_SAMPLE_RATE by ADLIB_CLOCK_MUL gives the number
|
||||
// Adlib clocks per second, as used by the RAWADATA file format.
|
||||
|
||||
/* compiler dependence */
|
||||
#ifndef OSD_CPU_H
|
||||
|
@ -17,13 +17,6 @@ typedef signed short INT16; /* signed 16bit */
|
|||
typedef signed int INT32; /* signed 32bit */
|
||||
#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);
|
||||
|
|
|
@ -1,124 +0,0 @@
|
|||
/*
|
||||
* Name: MUS Playing kernel
|
||||
* Project: MUS File Player Library
|
||||
* Version: 1.70
|
||||
* Author: Vladimir Arnost (QA-Software)
|
||||
* Last revision: Oct-28-1995
|
||||
* 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
|
||||
* Some minor changes to improve sound quality. Tried to add
|
||||
* stereo sound capabilities, but failed to -- my SB Pro refuses
|
||||
* to switch to stereo mode.
|
||||
* Aug-13-1994 V1.20 V.Arnost
|
||||
* Stereo sound fixed. Now works also with Sound Blaster Pro II
|
||||
* (chip OPL3 -- gives 18 "stereo" (ahem) channels).
|
||||
* Changed code to handle properly notes without volume.
|
||||
* (Uses previous volume on given channel.)
|
||||
* Added cyclic channel usage to avoid annoying clicking noise.
|
||||
* Aug-17-1994 V1.30 V.Arnost
|
||||
* Completely rewritten time synchronization. Now the player runs
|
||||
* on IRQ 8 (RTC Clock - 1024 Hz).
|
||||
* Aug-28-1994 V1.40 V.Arnost
|
||||
* Added Adlib and SB Pro II detection.
|
||||
* Fixed bug that caused high part of 32-bit registers (EAX,EBX...)
|
||||
* to be corrupted.
|
||||
* Oct-30-1994 V1.50 V.Arnost
|
||||
* Tidied up the source code
|
||||
* Added C key - invoke COMMAND.COM
|
||||
* Added RTC timer interrupt flag check (0000:04A0)
|
||||
* Added BLASTER environment variable parsing
|
||||
* FIRST PUBLIC RELEASE
|
||||
* Apr-16-1995 V1.60 V.Arnost
|
||||
* Moved into separate source file MUSLIB.C
|
||||
* May-01-1995 V1.61 V.Arnost
|
||||
* Added system timer (IRQ 0) support
|
||||
* Jul-12-1995 V1.62 V.Arnost
|
||||
* OPL2/OPL3-specific code moved to module MLOPL.C
|
||||
* Module MUSLIB.C renamed to MLKERNEL.C
|
||||
* Aug-04-1995 V1.63 V.Arnost
|
||||
* Fixed stack-related bug occuring in big-code models in Watcom C
|
||||
* Aug-16-1995 V1.64 V.Arnost
|
||||
* Stack size changed from 256 to 512 words because of stack
|
||||
* underflows caused by AWE32 driver
|
||||
* Aug-28-1995 V1.65 V.Arnost
|
||||
* Fixed a serious bug that caused the player to generate an
|
||||
* exception in AWE32 driver under DOS/4GW: Register ES contained
|
||||
* garbage instead of DGROUP. The compiler-generated prolog of
|
||||
* interrupt handlers doesn't set ES register at all, thus any
|
||||
* STOS/MOVS/SCAS/CMPS instruction used within the int. handler
|
||||
* crashes the program.
|
||||
* Oct-28-1995 V1.70 V.Arnost
|
||||
* System-specific timer code moved separate modules
|
||||
*/
|
||||
|
||||
#include "muslib.h"
|
||||
|
||||
|
||||
char MLversion[] = "MUS Lib V"MLVERSIONSTR;
|
||||
char MLcopyright[] = "Copyright (c) 1994-1996 QA-Software";
|
||||
|
||||
/* Program */
|
||||
int musicBlock::playTick()
|
||||
{
|
||||
int delay = 0;
|
||||
|
||||
while (delay == 0)
|
||||
{
|
||||
uchar data = *score++;
|
||||
uchar command = (data >> 4) & 7;
|
||||
uchar channel = data & 0x0F;
|
||||
uchar last = data & 0x80;
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case 0: // release note
|
||||
playingcount--;
|
||||
OPLreleaseNote(channel, *score++);
|
||||
break;
|
||||
case 1: { // play note
|
||||
uchar note = *score++;
|
||||
playingcount++;
|
||||
if (note & 0x80) // note with volume
|
||||
OPLplayNote(channel, note & 0x7F, *score++);
|
||||
else
|
||||
OPLplayNote(channel, note, -1);
|
||||
} break;
|
||||
case 2: // pitch wheel
|
||||
// MUS pitch wheel is 8 bits, but MIDI is 14
|
||||
OPLpitchWheel(channel, *score++ << (14 - 8));
|
||||
break;
|
||||
case 3: // system event (valueless controller)
|
||||
OPLchangeControl(channel, *score++, 0);
|
||||
break;
|
||||
case 4: { // change control
|
||||
uchar ctrl = *score++;
|
||||
uchar value = *score++;
|
||||
OPLchangeControl(channel, ctrl, value);
|
||||
} break;
|
||||
case 6: // end
|
||||
return 0;
|
||||
case 5: // ???
|
||||
case 7: // ???
|
||||
break;
|
||||
}
|
||||
if (last)
|
||||
{
|
||||
uchar t;
|
||||
|
||||
do
|
||||
{
|
||||
t = *score++;
|
||||
delay = (delay << 7) | (t & 127);
|
||||
} while (t & 128);
|
||||
}
|
||||
}
|
||||
return delay;
|
||||
}
|
|
@ -44,6 +44,18 @@
|
|||
#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);
|
||||
|
@ -106,64 +118,73 @@ static WORD frequencies[] =
|
|||
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, 0x388
|
||||
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 freq, uint octave, uint keyon)
|
||||
void OPLio::OPLwriteFreq(uint channel, uint note, uint pitch, uint keyon)
|
||||
{
|
||||
int i;
|
||||
int j = (freq<<5) + octave;
|
||||
i = 0;
|
||||
int octave = 0;
|
||||
int j = (note << 5) + pitch;
|
||||
|
||||
if (j < 0)
|
||||
{
|
||||
j = 0;
|
||||
}
|
||||
else if (j >= 0x11C)
|
||||
else if (j >= 284)
|
||||
{
|
||||
j -= 0x11C;
|
||||
i = j / 0x180;
|
||||
if (i > 7)
|
||||
j -= 284;
|
||||
octave = j / (32*12);
|
||||
if (octave > 7)
|
||||
{
|
||||
i = 7;
|
||||
octave = 7;
|
||||
}
|
||||
j = (j % 0x180) + 0x11C;
|
||||
j = (j % (32*12)) + 284;
|
||||
}
|
||||
i = frequencies[j] | (i << 10);
|
||||
int i = frequencies[j] | (octave << 10);
|
||||
|
||||
OPLwriteValue (0xA0, channel, (BYTE)i);
|
||||
OPLwriteValue (0xB0, channel, (BYTE)(i>>8)|(keyon<<5));
|
||||
|
@ -277,20 +298,12 @@ void OPLio::OPLshutup(void)
|
|||
/*
|
||||
* Initialize hardware upon startup
|
||||
*/
|
||||
int OPLio::OPLinit(uint numchips, uint rate)
|
||||
int OPLio::OPLinit(uint numchips)
|
||||
{
|
||||
if (!YM3812Init (numchips, 3579545, rate))
|
||||
if (!YM3812Init (numchips, 3579545, int(OPL_SAMPLE_RATE)))
|
||||
{
|
||||
uint i;
|
||||
|
||||
OPLchannels = OPL2CHANNELS*numchips;
|
||||
for (i = 0; i < numchips; ++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();
|
||||
OPLchannels = OPL2CHANNELS * numchips;
|
||||
OPLwriteInitState();
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
|
@ -299,6 +312,17 @@ int OPLio::OPLinit(uint numchips, uint rate)
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "doomdef.h"
|
||||
#include "m_swap.h"
|
||||
#include "w_wad.h"
|
||||
#include "fmopl.h"
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
|
@ -105,7 +106,7 @@ OPLMIDIDevice::~OPLMIDIDevice()
|
|||
|
||||
int OPLMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata)
|
||||
{
|
||||
if (io == NULL || io->OPLinit(TwoChips + 1, uint(OPL_SAMPLE_RATE)))
|
||||
if (io == NULL || io->OPLinit(TwoChips + 1))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
@ -209,6 +210,7 @@ int OPLMIDIDevice::SetTimeDiv(int timediv)
|
|||
void OPLMIDIDevice::CalcTickRate()
|
||||
{
|
||||
SamplesPerTick = OPL_SAMPLE_RATE / (1000000.0 / Tempo) / Division;
|
||||
io->SetClockRate(SamplesPerTick);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
372
src/oplsynth/music_opldumper_mididevice.cpp
Normal file
372
src/oplsynth/music_opldumper_mididevice.cpp
Normal file
|
@ -0,0 +1,372 @@
|
|||
/*
|
||||
** music_opl_mididevice.cpp
|
||||
** Writes raw OPL commands from the emulated OPL MIDI output to disk.
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2008 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
// HEADER FILES ------------------------------------------------------------
|
||||
|
||||
#include "i_musicinterns.h"
|
||||
#include "templates.h"
|
||||
#include "doomdef.h"
|
||||
#include "m_swap.h"
|
||||
#include "w_wad.h"
|
||||
#include "fmopl.h"
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
// TYPES -------------------------------------------------------------------
|
||||
|
||||
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
||||
|
||||
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
||||
|
||||
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
||||
|
||||
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
||||
|
||||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||
|
||||
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLDumperMIDIDevice Constructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
OPLDumperMIDIDevice::OPLDumperMIDIDevice(const char *filename)
|
||||
{
|
||||
// Replace the standard OPL device with a disk writer.
|
||||
delete io;
|
||||
io = new DiskWriterIO(filename);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLDumperMIDIDevice Destructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
OPLDumperMIDIDevice::~OPLDumperMIDIDevice()
|
||||
{
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLDumperMIDIDevice :: Resume
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int OPLDumperMIDIDevice::Resume()
|
||||
{
|
||||
int time;
|
||||
|
||||
time = PlayTick();
|
||||
while (time != 0)
|
||||
{
|
||||
io->WriteDelay(time);
|
||||
time = PlayTick();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLDumperMIDIDevice :: Stop
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void OPLDumperMIDIDevice::Stop()
|
||||
{
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DiskWriterIO Constructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
DiskWriterIO::DiskWriterIO(const char *filename)
|
||||
: Filename(filename)
|
||||
{
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DiskWriterIO Destructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
DiskWriterIO::~DiskWriterIO()
|
||||
{
|
||||
OPLdeinit();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DiskWriterIO :: OPLinit
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int DiskWriterIO::OPLinit(uint numchips)
|
||||
{
|
||||
// If the file extension is unknown or not present, the default format
|
||||
// is RAW. Otherwise, you can use DRO.
|
||||
if (Filename.Len() < 5 || stricmp(&Filename[Filename.Len() - 4], ".dro") != 0)
|
||||
{
|
||||
Format = FMT_RDOS;
|
||||
}
|
||||
else
|
||||
{
|
||||
Format = FMT_DOSBOX;
|
||||
}
|
||||
File = fopen(Filename, "wb");
|
||||
if (File == NULL)
|
||||
{
|
||||
Printf("Could not open %s for writing.\n", Filename.GetChars());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (Format == FMT_RDOS)
|
||||
{
|
||||
fwrite("RAWADATA\0", 1, 10, File);
|
||||
NeedClockRate = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
fwrite("DBRAWOPL"
|
||||
"\0\0" // Minor version number
|
||||
"\1\0" // Major version number
|
||||
"\0\0\0\0" // Total milliseconds
|
||||
"\0\0\0", // Total data
|
||||
1, 20, File);
|
||||
if (numchips == 1)
|
||||
{
|
||||
fwrite("\0\0\0", 1, 4, File); // Single OPL-2
|
||||
}
|
||||
else
|
||||
{
|
||||
fwrite("\2\0\0", 1, 4, File); // Dual OPL-2
|
||||
}
|
||||
NeedClockRate = false;
|
||||
}
|
||||
|
||||
TimePerTick = 0;
|
||||
TickMul = 1;
|
||||
CurTime = 0;
|
||||
CurIntTime = 0;
|
||||
CurChip = 0;
|
||||
OPLchannels = OPL2CHANNELS * numchips;
|
||||
OPLwriteInitState();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DiskWriterIO :: OPLdeinit
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DiskWriterIO::OPLdeinit()
|
||||
{
|
||||
if (File != NULL)
|
||||
{
|
||||
if (Format == FMT_RDOS)
|
||||
{
|
||||
WORD endmark = 65535;
|
||||
fwrite(&endmark, 2, 1, File);
|
||||
}
|
||||
else
|
||||
{
|
||||
long where_am_i = ftell(File);
|
||||
DWORD len[2];
|
||||
|
||||
fseek(File, 12, SEEK_SET);
|
||||
len[0] = LittleLong(CurIntTime);
|
||||
len[1] = LittleLong(where_am_i - 24);
|
||||
fwrite(len, 4, 2, File);
|
||||
}
|
||||
fclose(File);
|
||||
File = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DiskWriterIO :: OPLwriteReg
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DiskWriterIO::OPLwriteReg(int which, uint reg, uchar data)
|
||||
{
|
||||
SetChip(which);
|
||||
if (Format == FMT_RDOS)
|
||||
{
|
||||
if (reg != 0 && reg != 2 && (reg != 255 || data != 255))
|
||||
{
|
||||
BYTE cmd[2] = { data, reg };
|
||||
fwrite(cmd, 1, 2, File);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BYTE cmd[3] = { 4, reg, data };
|
||||
fwrite (cmd + (reg > 4), 1, 3 - (reg > 4), File);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DiskWriterIO :: SetChip
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DiskWriterIO :: SetChip(int chipnum)
|
||||
{
|
||||
assert(chipnum == 0 || chipnum == 1);
|
||||
|
||||
if (chipnum != CurChip)
|
||||
{
|
||||
CurChip = chipnum;
|
||||
if (Format == FMT_RDOS)
|
||||
{
|
||||
BYTE switcher[2] = { chipnum + 1, 2 };
|
||||
fwrite(switcher, 1, 2, File);
|
||||
}
|
||||
else
|
||||
{
|
||||
BYTE switcher = chipnum + 2;
|
||||
fwrite(&switcher, 1, 1, File);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DiskWriterIO :: SetClockRate
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DiskWriterIO::SetClockRate(double samples_per_tick)
|
||||
{
|
||||
TimePerTick = samples_per_tick / OPL_SAMPLE_RATE * 1000.0;
|
||||
|
||||
if (Format == FMT_RDOS)
|
||||
{
|
||||
double clock_rate;
|
||||
int clock_mul;
|
||||
WORD clock_word;
|
||||
|
||||
clock_rate = samples_per_tick * ADLIB_CLOCK_MUL;
|
||||
clock_mul = 1;
|
||||
|
||||
// The RDos raw format's clock rate is stored in a word. Therefore,
|
||||
// the longest tick that can be stored is only ~55 ms.
|
||||
while (clock_rate / clock_mul + 0.5 > 65535.0)
|
||||
{
|
||||
clock_mul++;
|
||||
}
|
||||
clock_word = WORD(clock_rate / clock_mul + 0.5);
|
||||
|
||||
if (NeedClockRate)
|
||||
{ // Set the initial clock rate.
|
||||
clock_word = LittleShort(clock_word);
|
||||
fseek(File, 8, SEEK_SET);
|
||||
fwrite(&clock_word, 2, 1, File);
|
||||
fseek(File, 0, SEEK_END);
|
||||
NeedClockRate = false;
|
||||
}
|
||||
else
|
||||
{ // Change the clock rate in the middle of the song.
|
||||
BYTE clock_change[4] = { 0, 2, clock_word & 255, clock_word >> 8 };
|
||||
fwrite(clock_change, 1, 4, File);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DiskWriterIO :: WriteDelay
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DiskWriterIO :: WriteDelay(int ticks)
|
||||
{
|
||||
if (ticks <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Format == FMT_RDOS)
|
||||
{ // RDos raw has very precise delays but isn't very efficient at
|
||||
// storing long delays.
|
||||
BYTE delay[2];
|
||||
|
||||
ticks *= TickMul;
|
||||
delay[1] = 0;
|
||||
while (ticks > 255)
|
||||
{
|
||||
ticks -= 255;
|
||||
delay[0] = 255;
|
||||
fwrite(delay, 1, 2, File);
|
||||
}
|
||||
delay[0] = BYTE(ticks);
|
||||
fwrite(delay, 1, 2, File);
|
||||
}
|
||||
else
|
||||
{ // DosBox only has millisecond-precise delays.
|
||||
int delay;
|
||||
|
||||
CurTime += TimePerTick * ticks;
|
||||
delay = int(CurTime + 0.5) - CurIntTime;
|
||||
CurIntTime += delay;
|
||||
while (delay > 65536)
|
||||
{
|
||||
BYTE cmd[3] = { 1, 255, 255 };
|
||||
fwrite(cmd, 1, 2, File);
|
||||
delay -= 65536;
|
||||
}
|
||||
delay--;
|
||||
if (delay <= 255)
|
||||
{
|
||||
BYTE cmd[2] = { 0, BYTE(delay) };
|
||||
fwrite(cmd, 1, 2, File);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(delay <= 65535);
|
||||
BYTE cmd[3] = { 1, BYTE(delay & 255), BYTE(delay >> 8) };
|
||||
fwrite(cmd, 1, 3, File);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -130,7 +130,7 @@ struct OP2instrEntry {
|
|||
};
|
||||
|
||||
#define FL_FIXED_PITCH 0x0001 // note has fixed pitch (see below)
|
||||
#define FL_UNKNOWN 0x0002 // ??? (used in instrument #65 only)
|
||||
#define FL_UNKNOWN 0x0002 // ??? (used in instrument #65 only)
|
||||
#define FL_DOUBLE_VOICE 0x0004 // use two voices instead of one
|
||||
|
||||
|
||||
|
@ -160,7 +160,7 @@ struct OPLdata {
|
|||
};
|
||||
|
||||
struct OPLio {
|
||||
virtual ~OPLio() {}
|
||||
virtual ~OPLio();
|
||||
|
||||
void OPLwriteChannel(uint regbase, uint channel, uchar data1, uchar data2);
|
||||
void OPLwriteValue(uint regbase, uint channel, uchar value);
|
||||
|
@ -171,14 +171,43 @@ struct OPLio {
|
|||
void OPLwritePan(uint channel, struct OPL2instrument *instr, int pan);
|
||||
void OPLwriteInstrument(uint channel, struct OPL2instrument *instr);
|
||||
void OPLshutup(void);
|
||||
void OPLwriteInitState();
|
||||
|
||||
virtual int OPLinit(uint numchips, uint rate);
|
||||
virtual int OPLinit(uint numchips);
|
||||
virtual void OPLdeinit(void);
|
||||
virtual void OPLwriteReg(int which, uint reg, uchar data);
|
||||
virtual void SetClockRate(double samples_per_tick);
|
||||
virtual void WriteDelay(int ticks);
|
||||
|
||||
uint OPLchannels;
|
||||
};
|
||||
|
||||
struct DiskWriterIO : public OPLio
|
||||
{
|
||||
DiskWriterIO(const char *filename);
|
||||
~DiskWriterIO();
|
||||
|
||||
int OPLinit(uint numchips);
|
||||
void OPLdeinit();
|
||||
void OPLwriteReg(int which, uint reg, uchar data);
|
||||
void SetClockRate(double samples_per_tick);
|
||||
void WriteDelay(int ticks);
|
||||
|
||||
void SetChip(int chipnum);
|
||||
|
||||
FILE *File;
|
||||
FString Filename;
|
||||
int Format;
|
||||
bool NeedClockRate;
|
||||
double TimePerTick; // In milliseconds
|
||||
double CurTime;
|
||||
int CurIntTime;
|
||||
int TickMul;
|
||||
int CurChip;
|
||||
|
||||
enum { FMT_RDOS, FMT_DOSBOX };
|
||||
};
|
||||
|
||||
struct musicBlock {
|
||||
musicBlock();
|
||||
~musicBlock();
|
||||
|
@ -193,8 +222,6 @@ struct musicBlock {
|
|||
|
||||
ulong MLtime;
|
||||
|
||||
int playTick();
|
||||
|
||||
void OPLplayNote(uint channel, uchar note, int volume);
|
||||
void OPLreleaseNote(uint channel, uchar note);
|
||||
void OPLpitchWheel(uint channel, int pitch);
|
||||
|
@ -254,4 +281,7 @@ enum MUSctrl {
|
|||
ctrlResetCtrls
|
||||
};
|
||||
|
||||
#define OPL_SAMPLE_RATE 49716.0
|
||||
#define ADLIB_CLOCK_MUL 24.0
|
||||
|
||||
#endif // __MUSLIB_H_
|
||||
|
|
|
@ -79,7 +79,7 @@ void OPLmusicBlock::ResetChips ()
|
|||
TwoChips = !opl_onechip;
|
||||
Serialize();
|
||||
io->OPLdeinit ();
|
||||
io->OPLinit (TwoChips + 1, uint(OPL_SAMPLE_RATE));
|
||||
io->OPLinit (TwoChips + 1);
|
||||
Unserialize();
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ void OPLmusicBlock::Restart()
|
|||
playingcount = 0;
|
||||
}
|
||||
|
||||
OPLmusicFile::OPLmusicFile (FILE *file, char * musiccache, int len, int maxSamples)
|
||||
OPLmusicFile::OPLmusicFile (FILE *file, char *musiccache, int len)
|
||||
: ScoreLen (len)
|
||||
{
|
||||
if (io == NULL)
|
||||
|
@ -115,29 +115,15 @@ OPLmusicFile::OPLmusicFile (FILE *file, char * musiccache, int len, int maxSampl
|
|||
memcpy(scoredata, &musiccache[0], len);
|
||||
}
|
||||
|
||||
if (io->OPLinit (TwoChips + 1, uint(OPL_SAMPLE_RATE)))
|
||||
if (io->OPLinit (TwoChips + 1))
|
||||
{
|
||||
delete[] scoredata;
|
||||
scoredata = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for MUS format
|
||||
if (*(DWORD *)scoredata == MAKE_ID('M','U','S',0x1a))
|
||||
{
|
||||
FWadLump data = Wads.OpenLumpName ("GENMIDI");
|
||||
if (0 != OPLloadBank (data))
|
||||
{
|
||||
delete[] scoredata;
|
||||
scoredata = NULL;
|
||||
return;
|
||||
}
|
||||
BlockForStats = this;
|
||||
SamplesPerTick = OPL_SAMPLE_RATE / 140.0;
|
||||
RawPlayer = NotRaw;
|
||||
}
|
||||
// Check for RDosPlay raw OPL format
|
||||
else if (((DWORD *)scoredata)[0] == MAKE_ID('R','A','W','A') &&
|
||||
if (((DWORD *)scoredata)[0] == MAKE_ID('R','A','W','A') &&
|
||||
((DWORD *)scoredata)[1] == MAKE_ID('D','A','T','A'))
|
||||
{
|
||||
RawPlayer = RDosPlay;
|
||||
|
@ -145,7 +131,16 @@ OPLmusicFile::OPLmusicFile (FILE *file, char * musiccache, int len, int maxSampl
|
|||
{ // A clock speed of 0 is bad
|
||||
*(WORD *)(scoredata + 8) = 0xFFFF;
|
||||
}
|
||||
SamplesPerTick = OPL_SAMPLE_RATE * LittleShort(*(WORD *)(scoredata + 8)) / 1193180.0;
|
||||
SamplesPerTick = LittleShort(*(WORD *)(scoredata + 8)) / ADLIB_CLOCK_MUL;
|
||||
}
|
||||
// Check for DosBox OPL dump
|
||||
else if (((DWORD *)scoredata)[0] == MAKE_ID('D','B','R','A') &&
|
||||
((DWORD *)scoredata)[1] == MAKE_ID('W','O','P','L') &&
|
||||
((DWORD *)scoredata)[2] == MAKE_ID(0,0,1,0))
|
||||
{
|
||||
RawPlayer = DosBox;
|
||||
SamplesPerTick = OPL_SAMPLE_RATE / 1000;
|
||||
ScoreLen = MIN<int>(len - 24, LittleLong(((DWORD *)scoredata)[4]));
|
||||
}
|
||||
// Check for modified IMF format (includes a header)
|
||||
else if (((DWORD *)scoredata)[0] == MAKE_ID('A','D','L','I') &&
|
||||
|
@ -202,14 +197,16 @@ void OPLmusicFile::SetLooping (bool loop)
|
|||
void OPLmusicFile::Restart ()
|
||||
{
|
||||
OPLmusicBlock::Restart();
|
||||
if (RawPlayer == NotRaw)
|
||||
{
|
||||
score = scoredata + ((MUSheader *)scoredata)->scoreStart;
|
||||
}
|
||||
else if (RawPlayer == RDosPlay)
|
||||
WhichChip = 0;
|
||||
if (RawPlayer == RDosPlay)
|
||||
{
|
||||
score = scoredata + 10;
|
||||
SamplesPerTick = OPL_SAMPLE_RATE * LittleShort(*(WORD *)(scoredata + 8)) / 1193180.0;
|
||||
SamplesPerTick = LittleShort(*(WORD *)(scoredata + 8)) / ADLIB_CLOCK_MUL;
|
||||
}
|
||||
else if (RawPlayer == DosBox)
|
||||
{
|
||||
score = scoredata + 24;
|
||||
SamplesPerTick = OPL_SAMPLE_RATE / 1000;
|
||||
}
|
||||
else if (RawPlayer == IMF)
|
||||
{
|
||||
|
@ -226,6 +223,7 @@ void OPLmusicFile::Restart ()
|
|||
score += 4; // Skip song length
|
||||
}
|
||||
}
|
||||
io->SetClockRate(SamplesPerTick);
|
||||
}
|
||||
|
||||
bool OPLmusicBlock::ServiceStream (void *buff, int numbytes)
|
||||
|
@ -289,6 +287,7 @@ bool OPLmusicBlock::ServiceStream (void *buff, int numbytes)
|
|||
else
|
||||
{
|
||||
prevEnded = false;
|
||||
io->WriteDelay(next);
|
||||
NextTickIn += SamplesPerTick * next;
|
||||
assert (NextTickIn >= 0);
|
||||
MLtime += next;
|
||||
|
@ -303,11 +302,7 @@ int OPLmusicFile::PlayTick ()
|
|||
{
|
||||
BYTE reg, data;
|
||||
|
||||
if (RawPlayer == NotRaw)
|
||||
{
|
||||
return playTick ();
|
||||
}
|
||||
else if (RawPlayer == RDosPlay)
|
||||
if (RawPlayer == RDosPlay)
|
||||
{
|
||||
while (score < scoredata + ScoreLen)
|
||||
{
|
||||
|
@ -325,9 +320,18 @@ int OPLmusicFile::PlayTick ()
|
|||
case 2: // Speed change or OPL3 switch
|
||||
if (data == 0)
|
||||
{
|
||||
SamplesPerTick = OPL_SAMPLE_RATE * LittleShort(*(WORD *)(score)) / 1193180.0;
|
||||
SamplesPerTick = LittleShort(*(WORD *)(score)) / ADLIB_CLOCK_MUL;
|
||||
io->SetClockRate(SamplesPerTick);
|
||||
score += 2;
|
||||
}
|
||||
else if (data == 1)
|
||||
{
|
||||
WhichChip = 0;
|
||||
}
|
||||
else if (data == 2)
|
||||
{
|
||||
WhichChip = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xFF: // End of song
|
||||
|
@ -338,11 +342,55 @@ int OPLmusicFile::PlayTick ()
|
|||
break;
|
||||
|
||||
default: // It's something to stuff into the OPL chip
|
||||
io->OPLwriteReg (0, reg, data);
|
||||
if (WhichChip == 0 || TwoChips)
|
||||
{
|
||||
io->OPLwriteReg(WhichChip, reg, data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (RawPlayer == DosBox)
|
||||
{
|
||||
while (score < scoredata + ScoreLen)
|
||||
{
|
||||
reg = *score++;
|
||||
|
||||
if (reg == 4)
|
||||
{
|
||||
reg = *score++;
|
||||
data = *score++;
|
||||
}
|
||||
else if (reg == 0)
|
||||
{ // One-byte delay
|
||||
return *score++ + 1;
|
||||
}
|
||||
else if (reg == 1)
|
||||
{ // Two-byte delay
|
||||
int delay = score[0] + (score[1] << 8) + 1;
|
||||
score += 2;
|
||||
return delay;
|
||||
}
|
||||
else if (reg == 2)
|
||||
{ // Select OPL chip 0
|
||||
WhichChip = 0;
|
||||
continue;
|
||||
}
|
||||
else if (reg == 3)
|
||||
{ // Select OPL chip 1
|
||||
WhichChip = 1;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
data = *score++;
|
||||
}
|
||||
if (WhichChip == 0 || TwoChips)
|
||||
{
|
||||
io->OPLwriteReg(WhichChip, reg, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (RawPlayer == IMF)
|
||||
{
|
||||
WORD delay = 0;
|
||||
|
@ -398,161 +446,33 @@ ADD_STAT (opl)
|
|||
}
|
||||
}
|
||||
|
||||
struct DiskWriterIO : public OPLio
|
||||
OPLmusicFile::OPLmusicFile(const OPLmusicFile *source, const char *filename)
|
||||
{
|
||||
DiskWriterIO () : File(NULL) {}
|
||||
virtual ~DiskWriterIO () { if (File != NULL) fclose (File); }
|
||||
int OPLinit(const char *filename);
|
||||
virtual void OPLwriteReg(int which, uint reg, uchar data);
|
||||
|
||||
FILE *File;
|
||||
bool RawFormat;
|
||||
};
|
||||
|
||||
class OPLmusicWriter : public musicBlock
|
||||
{
|
||||
public:
|
||||
OPLmusicWriter (const char *songname, const char *filename);
|
||||
~OPLmusicWriter ();
|
||||
void Go ();
|
||||
|
||||
bool SharingData;
|
||||
FILE *File;
|
||||
};
|
||||
|
||||
OPLmusicWriter::OPLmusicWriter (const char *songname, const char *filename)
|
||||
{
|
||||
io = NULL;
|
||||
SharingData = true;
|
||||
if (songname == NULL)
|
||||
ScoreLen = source->ScoreLen;
|
||||
scoredata = new BYTE[ScoreLen];
|
||||
memcpy(scoredata, source->scoredata, ScoreLen);
|
||||
SamplesPerTick = source->SamplesPerTick;
|
||||
RawPlayer = source->RawPlayer;
|
||||
score = source->score;
|
||||
TwoChips = source->TwoChips;
|
||||
WhichChip = 0;
|
||||
if (io != NULL)
|
||||
{
|
||||
if (BlockForStats == NULL)
|
||||
{
|
||||
Printf ("Not currently playing an OPL song.\n");
|
||||
return;
|
||||
}
|
||||
scoredata = BlockForStats->scoredata;
|
||||
OPLinstruments = BlockForStats->OPLinstruments;
|
||||
}
|
||||
else
|
||||
{
|
||||
SharingData = false;
|
||||
int lumpnum = Wads.CheckNumForName (songname, ns_music);
|
||||
if (lumpnum == -1)
|
||||
{
|
||||
Printf ("Song %s is unknown.\n", songname);
|
||||
return;
|
||||
}
|
||||
FWadLump song = Wads.OpenLumpNum (lumpnum);
|
||||
scoredata = new BYTE [song.GetLength ()];
|
||||
song.Read (scoredata, song.GetLength());
|
||||
FWadLump genmidi = Wads.OpenLumpName ("GENMIDI");
|
||||
OPLloadBank (genmidi);
|
||||
}
|
||||
io = new DiskWriterIO ();
|
||||
if (((DiskWriterIO *)io)->OPLinit (filename) == 0)
|
||||
{
|
||||
OPLplayMusic (127);
|
||||
score = scoredata + ((MUSheader *)scoredata)->scoreStart;
|
||||
Go ();
|
||||
delete io;
|
||||
}
|
||||
io = new DiskWriterIO(filename);
|
||||
io->OPLinit(TwoChips);
|
||||
Restart();
|
||||
}
|
||||
|
||||
OPLmusicWriter::~OPLmusicWriter ()
|
||||
void OPLmusicFile::Dump()
|
||||
{
|
||||
if (io != NULL) delete io;
|
||||
if (!SharingData)
|
||||
int time;
|
||||
|
||||
time = PlayTick();
|
||||
while (time != 0)
|
||||
{
|
||||
delete scoredata;
|
||||
}
|
||||
else
|
||||
{
|
||||
OPLinstruments = NULL;
|
||||
io->WriteDelay(time);
|
||||
time = PlayTick();
|
||||
}
|
||||
}
|
||||
|
||||
void OPLmusicWriter::Go ()
|
||||
{
|
||||
int next;
|
||||
|
||||
while ((next = playTick()) != 0)
|
||||
{
|
||||
MLtime += next;
|
||||
while (next > 255)
|
||||
{
|
||||
io->OPLwriteReg (10, 0, 255);
|
||||
next -= 255;
|
||||
}
|
||||
io->OPLwriteReg (10, 0, next);
|
||||
}
|
||||
io->OPLwriteReg (10, 0xFF, 0xFF);
|
||||
}
|
||||
|
||||
int DiskWriterIO::OPLinit (const char *filename)
|
||||
{
|
||||
int numchips;
|
||||
//size_t namelen;
|
||||
|
||||
// If the file extension is unknown or not present, the default format
|
||||
// is RAW. Otherwise, you can use DRO. But not right now. The DRO format
|
||||
// is still in a state of flux, so I don't want the hassle.
|
||||
//namelen = strlen (filename);
|
||||
RawFormat = 1; //(namelen < 5 || stricmp (filename + namelen - 4, ".dro") != 0);
|
||||
File = fopen (filename, "wb");
|
||||
if (File == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (RawFormat)
|
||||
{
|
||||
fwrite ("RAWADATA", 1, 8, File);
|
||||
WORD clock = LittleShort(17045/2);
|
||||
fwrite (&clock, 2, 1, File);
|
||||
numchips = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
numchips = 2;
|
||||
}
|
||||
|
||||
OPLchannels = OPL2CHANNELS*numchips;
|
||||
for (int i = 0; i < numchips; ++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();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DiskWriterIO::OPLwriteReg(int which, uint reg, uchar data)
|
||||
{
|
||||
if (which == 10 || (reg != 0 && reg != 2 && reg != 0xFF))
|
||||
{
|
||||
struct { BYTE data, reg; } out = { data, reg };
|
||||
fwrite (&out, 2, 1, File);
|
||||
}
|
||||
else
|
||||
{
|
||||
reg = reg;
|
||||
}
|
||||
}
|
||||
|
||||
CCMD (writeopl)
|
||||
{
|
||||
if (argv.argc() == 2)
|
||||
{
|
||||
OPLmusicWriter writer (NULL, argv[1]);
|
||||
}
|
||||
else if (argv.argc() == 3)
|
||||
{
|
||||
OPLmusicWriter writer (argv[1], argv[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf ("Usage: writeopl [songname] <filename>");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
#include "muslib.h"
|
||||
#include "files.h"
|
||||
|
||||
#define OPL_SAMPLE_RATE 49716.0
|
||||
|
||||
class OPLmusicBlock : public musicBlock
|
||||
{
|
||||
public:
|
||||
|
@ -43,16 +41,20 @@ protected:
|
|||
class OPLmusicFile : public OPLmusicBlock
|
||||
{
|
||||
public:
|
||||
OPLmusicFile(FILE *file, char *musiccache, int len, int maxSamples);
|
||||
OPLmusicFile(FILE *file, char *musiccache, int len);
|
||||
OPLmusicFile(const OPLmusicFile *source, const char *filename);
|
||||
~OPLmusicFile();
|
||||
|
||||
bool IsValid() const;
|
||||
void SetLooping(bool loop);
|
||||
void Restart();
|
||||
void Dump();
|
||||
|
||||
protected:
|
||||
OPLmusicFile() {}
|
||||
int PlayTick();
|
||||
|
||||
enum { NotRaw, RDosPlay, IMF } RawPlayer;
|
||||
enum { RDosPlay, IMF, DosBox } RawPlayer;
|
||||
int ScoreLen;
|
||||
int WhichChip;
|
||||
};
|
||||
|
|
|
@ -147,6 +147,11 @@ FString MusInfo::GetStats()
|
|||
return "No stats available for this song";
|
||||
}
|
||||
|
||||
MusInfo *MusInfo::GetOPLDumper(const char *filename)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void I_InitMusic (void)
|
||||
{
|
||||
static bool setatterm = false;
|
||||
|
@ -304,7 +309,6 @@ void *I_RegisterSong (const char *filename, char *musiccache, int offset, int le
|
|||
/* MUS are played as:
|
||||
- OPL:
|
||||
- if explicitly selected by $mididevice
|
||||
- when opl_enable is true and no midi device is set for the song
|
||||
- when snd_mididevice is -3 and no midi device is set for the song
|
||||
|
||||
Timidity:
|
||||
|
@ -321,9 +325,9 @@ void *I_RegisterSong (const char *filename, char *musiccache, int offset, int le
|
|||
- when snd_mididevice is >= 0 and no midi device is set for the song
|
||||
- as fallback when both OPL and Timidity failed and snd_mididevice is >= 0
|
||||
*/
|
||||
if (((opl_enable || snd_mididevice == -3) && device == MDEV_DEFAULT) || device == MDEV_OPL)
|
||||
if ((snd_mididevice == -3 && device == MDEV_DEFAULT) || device == MDEV_OPL)
|
||||
{
|
||||
info = new OPLMUSSong (file, musiccache, len);
|
||||
info = new MUSSong2 (file, musiccache, len, true);
|
||||
}
|
||||
else if (device == MDEV_TIMIDITY || (device == MDEV_DEFAULT && snd_mididevice == -2))
|
||||
{
|
||||
|
@ -369,7 +373,7 @@ void *I_RegisterSong (const char *filename, char *musiccache, int offset, int le
|
|||
#ifdef _WIN32
|
||||
if (info == NULL)
|
||||
{
|
||||
info = new MUSSong2 (file, musiccache, len);
|
||||
info = new MUSSong2 (file, musiccache, len, false);
|
||||
}
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
@ -384,7 +388,6 @@ void *I_RegisterSong (const char *filename, char *musiccache, int offset, int le
|
|||
/* MIDI are played as:
|
||||
OPL:
|
||||
- if explicitly selected by $mididevice
|
||||
- when opl_enable is true and no midi device is set for the song
|
||||
- when snd_mididevice is -3 and no midi device is set for the song
|
||||
|
||||
Timidity:
|
||||
|
@ -422,8 +425,11 @@ void *I_RegisterSong (const char *filename, char *musiccache, int offset, int le
|
|||
}
|
||||
#endif // _WIN32
|
||||
}
|
||||
// Check for RDosPlay raw OPL format
|
||||
else if (id == MAKE_ID('R','A','W','A') && len >= 12)
|
||||
// Check for various raw OPL formats
|
||||
else if (len >= 12 &&
|
||||
(id == MAKE_ID('R','A','W','A') || // Rdos Raw OPL
|
||||
id == MAKE_ID('D','B','R','A') || // DosBox Raw OPL
|
||||
id == MAKE_ID('A','D','L','I'))) // Martin Fernandez's modified IMF
|
||||
{
|
||||
DWORD fullsig[2];
|
||||
|
||||
|
@ -441,30 +447,9 @@ void *I_RegisterSong (const char *filename, char *musiccache, int offset, int le
|
|||
memcpy(fullsig, musiccache, 8);
|
||||
}
|
||||
|
||||
if (fullsig[1] == MAKE_ID('D','A','T','A'))
|
||||
{
|
||||
info = new OPLMUSSong (file, musiccache, len);
|
||||
}
|
||||
}
|
||||
// Check for Martin Fernandez's modified IMF format
|
||||
else if (id == MAKE_ID('A','D','L','I'))
|
||||
{
|
||||
char fullhead[6];
|
||||
|
||||
if (file != NULL)
|
||||
{
|
||||
if (fread (fullhead, 1, 6, file) != 6)
|
||||
{
|
||||
fclose (file);
|
||||
return 0;
|
||||
}
|
||||
fseek (file, -6, SEEK_CUR);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(fullhead, musiccache, 6);
|
||||
}
|
||||
if (fullhead[4] == 'B' && fullhead[5] == 1)
|
||||
if ((fullsig[0] == MAKE_ID('R','A','W','A') && fullsig[1] == MAKE_ID('D','A','T','A')) ||
|
||||
(fullsig[0] == MAKE_ID('D','B','R','A') && fullsig[1] == MAKE_ID('W','O','P','L')) ||
|
||||
(fullsig[0] == MAKE_ID('A','D','L','I') && (fullsig[1] & MAKE_ID(255,255,0,0)) == MAKE_ID('B',1,0,0)))
|
||||
{
|
||||
info = new OPLMUSSong (file, musiccache, len);
|
||||
}
|
||||
|
@ -597,3 +582,40 @@ ADD_STAT(music)
|
|||
}
|
||||
return "No song playing";
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// CCMD writeopl
|
||||
//
|
||||
// If the current song can be played with OPL instruments, dump it to
|
||||
// the specified file on disk.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
CCMD (writeopl)
|
||||
{
|
||||
if (argv.argc() == 2)
|
||||
{
|
||||
if (currSong == NULL)
|
||||
{
|
||||
Printf ("No song is currently playing.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
MusInfo *dumper = currSong->GetOPLDumper(argv[1]);
|
||||
if (dumper == NULL)
|
||||
{
|
||||
Printf ("Current song cannot be saved as OPL data.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
dumper->Play(false);
|
||||
delete dumper;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf ("Usage: writeopl <filename>");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ public:
|
|||
virtual bool SetPosition (int order);
|
||||
virtual void Update();
|
||||
virtual FString GetStats();
|
||||
virtual MusInfo *GetOPLDumper(const char *filename);
|
||||
|
||||
enum EState
|
||||
{
|
||||
|
@ -152,7 +153,7 @@ protected:
|
|||
|
||||
// OPL implementation of a MIDI output device -------------------------------
|
||||
|
||||
class OPLMIDIDevice : public MIDIDevice, OPLmusicBlock
|
||||
class OPLMIDIDevice : public MIDIDevice, protected OPLmusicBlock
|
||||
{
|
||||
public:
|
||||
OPLMIDIDevice();
|
||||
|
@ -191,6 +192,17 @@ protected:
|
|||
DWORD Position;
|
||||
};
|
||||
|
||||
// OPL dumper implementation of a MIDI output device ------------------------
|
||||
|
||||
class OPLDumperMIDIDevice : public OPLMIDIDevice
|
||||
{
|
||||
public:
|
||||
OPLDumperMIDIDevice(const char *filename);
|
||||
~OPLDumperMIDIDevice();
|
||||
int Resume();
|
||||
void Stop();
|
||||
};
|
||||
|
||||
// Base class for streaming MUS and MIDI files ------------------------------
|
||||
|
||||
class MIDIStreamer : public MusInfo
|
||||
|
@ -210,13 +222,15 @@ public:
|
|||
void Update();
|
||||
|
||||
protected:
|
||||
static void Callback(unsigned int uMsg, void *userdata, DWORD dwParam1, DWORD dwParam2);
|
||||
MIDIStreamer(const char *dumpname);
|
||||
|
||||
void OutputVolume (DWORD volume);
|
||||
int FillBuffer(int buffer_num, int max_events, DWORD max_time);
|
||||
bool ServiceEvent();
|
||||
int VolumeControllerChange(int channel, int volume);
|
||||
|
||||
static void Callback(unsigned int uMsg, void *userdata, DWORD dwParam1, DWORD dwParam2);
|
||||
|
||||
// Virtuals for subclasses to override
|
||||
virtual void CheckCaps();
|
||||
virtual void DoInitialSetup() = 0;
|
||||
|
@ -261,6 +275,7 @@ protected:
|
|||
DWORD Volume;
|
||||
bool UseOPLDevice;
|
||||
bool CallbackIsThreaded;
|
||||
FString DumpFilename;
|
||||
};
|
||||
|
||||
// MUS file played with a MIDI stream ---------------------------------------
|
||||
|
@ -268,10 +283,14 @@ protected:
|
|||
class MUSSong2 : public MIDIStreamer
|
||||
{
|
||||
public:
|
||||
MUSSong2 (FILE *file, char *musiccache, int length);
|
||||
~MUSSong2 ();
|
||||
MUSSong2(FILE *file, char *musiccache, int length, bool opl);
|
||||
~MUSSong2();
|
||||
|
||||
MusInfo *GetOPLDumper(const char *filename);
|
||||
|
||||
protected:
|
||||
MUSSong2(const MUSSong2 *original, const char *filename); //OPL dump constructor
|
||||
|
||||
void DoInitialSetup();
|
||||
void DoRestart();
|
||||
bool CheckDone();
|
||||
|
@ -288,10 +307,14 @@ protected:
|
|||
class MIDISong2 : public MIDIStreamer
|
||||
{
|
||||
public:
|
||||
MIDISong2 (FILE *file, char *musiccache, int length, bool opl);
|
||||
~MIDISong2 ();
|
||||
MIDISong2(FILE *file, char *musiccache, int length, bool opl);
|
||||
~MIDISong2();
|
||||
|
||||
MusInfo *GetOPLDumper(const char *filename);
|
||||
|
||||
protected:
|
||||
MIDISong2(const MIDISong2 *original, const char *filename); // OPL dump constructor
|
||||
|
||||
void CheckCaps();
|
||||
void DoInitialSetup();
|
||||
void DoRestart();
|
||||
|
@ -307,6 +330,7 @@ protected:
|
|||
void SetTempo(int new_tempo);
|
||||
|
||||
BYTE *MusHeader;
|
||||
int SongLen;
|
||||
TrackInfo *Tracks;
|
||||
TrackInfo *TrackDue;
|
||||
int NumTracks;
|
||||
|
@ -381,19 +405,29 @@ protected:
|
|||
class OPLMUSSong : public StreamSong
|
||||
{
|
||||
public:
|
||||
OPLMUSSong (FILE *file, char * musiccache, int length);
|
||||
OPLMUSSong (FILE *file, char *musiccache, int length);
|
||||
~OPLMUSSong ();
|
||||
void Play (bool looping);
|
||||
bool IsPlaying ();
|
||||
bool IsValid () const;
|
||||
void ResetChips ();
|
||||
MusInfo *GetOPLDumper(const char *filename);
|
||||
|
||||
protected:
|
||||
OPLMUSSong(const OPLMUSSong *original, const char *filename); // OPL dump constructor
|
||||
|
||||
static bool FillStream (SoundStream *stream, void *buff, int len, void *userdata);
|
||||
|
||||
OPLmusicFile *Music;
|
||||
};
|
||||
|
||||
class OPLMUSDumper : public OPLMUSSong
|
||||
{
|
||||
public:
|
||||
OPLMUSDumper(const OPLMUSSong *original, const char *filename);
|
||||
void Play(bool looping);
|
||||
};
|
||||
|
||||
// CD track/disk played through the multimedia system -----------------------
|
||||
|
||||
class CDSong : public MusInfo
|
||||
|
@ -430,4 +464,3 @@ extern MusInfo *currSong;
|
|||
extern int nomusic;
|
||||
|
||||
EXTERN_CVAR (Float, snd_musicvolume)
|
||||
EXTERN_CVAR (Bool, opl_enable)
|
||||
|
|
|
@ -44,8 +44,6 @@
|
|||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
#define MAX_TIME (1000000/20) // Send out 1/20 of a sec of events at a time.
|
||||
|
||||
// Used by SendCommand to check for unexpected end-of-track conditions.
|
||||
#define CHECK_FINISHED \
|
||||
if (track->TrackP >= track->MaxTrackP) \
|
||||
|
@ -115,6 +113,7 @@ MIDISong2::MIDISong2 (FILE *file, char *musiccache, int len, bool opl)
|
|||
}
|
||||
#endif
|
||||
MusHeader = new BYTE[len];
|
||||
SongLen = len;
|
||||
if (file != NULL)
|
||||
{
|
||||
if (fread(MusHeader, 1, len, file) != (size_t)len)
|
||||
|
@ -754,3 +753,43 @@ void MIDISong2::SetTempo(int new_tempo)
|
|||
Tempo = new_tempo;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// MIDISong2 :: GetOPLDumper
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
MusInfo *MIDISong2::GetOPLDumper(const char *filename)
|
||||
{
|
||||
return new MIDISong2(this, filename);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// MIDISong2 OPL Dumping Constructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
MIDISong2::MIDISong2(const MIDISong2 *original, const char *filename)
|
||||
: MIDIStreamer(filename)
|
||||
{
|
||||
SongLen = original->SongLen;
|
||||
MusHeader = new BYTE[original->SongLen];
|
||||
memcpy(MusHeader, original->MusHeader, original->SongLen);
|
||||
Format = original->Format;
|
||||
NumTracks = original->NumTracks;
|
||||
DesignationMask = 0;
|
||||
Division = original->Division;
|
||||
Tempo = InitialTempo = original->InitialTempo;
|
||||
Tracks = new TrackInfo[NumTracks];
|
||||
for (int i = 0; i < NumTracks; ++i)
|
||||
{
|
||||
TrackInfo *newtrack = &Tracks[i];
|
||||
const TrackInfo *oldtrack = &original->Tracks[i];
|
||||
|
||||
newtrack->TrackBegin = MusHeader + (oldtrack->TrackBegin - original->MusHeader);
|
||||
newtrack->TrackP = 0;
|
||||
newtrack->MaxTrackP = oldtrack->MaxTrackP;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,25 @@ MIDIStreamer::MIDIStreamer(bool opl)
|
|||
#endif
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// MIDIStreamer OPL Dumping Constructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
MIDIStreamer::MIDIStreamer(const char *dumpname)
|
||||
: MIDI(0), DumpFilename(dumpname),
|
||||
#ifdef _WIN32
|
||||
PlayerThread(0), ExitEvent(0), BufferDoneEvent(0),
|
||||
#endif
|
||||
Division(0), InitialTempo(500000), UseOPLDevice(true)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
BufferDoneEvent = NULL;
|
||||
ExitEvent = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// MIDIStreamer Destructor
|
||||
|
@ -163,7 +182,7 @@ void MIDIStreamer::CheckCaps()
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void MIDIStreamer::Play (bool looping)
|
||||
void MIDIStreamer::Play(bool looping)
|
||||
{
|
||||
DWORD tid;
|
||||
|
||||
|
@ -175,6 +194,11 @@ void MIDIStreamer::Play (bool looping)
|
|||
InitialPlayback = true;
|
||||
|
||||
assert(MIDI == NULL);
|
||||
if (DumpFilename.IsNotEmpty())
|
||||
{
|
||||
MIDI = new OPLDumperMIDIDevice(DumpFilename);
|
||||
}
|
||||
else
|
||||
#ifdef _WIN32
|
||||
if (!UseOPLDevice)
|
||||
{
|
||||
|
@ -188,7 +212,7 @@ void MIDIStreamer::Play (bool looping)
|
|||
|
||||
#ifndef _WIN32
|
||||
assert(MIDI->NeedThreadedCallback() == false);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (0 != MIDI->Open(Callback, this))
|
||||
{
|
||||
|
@ -281,7 +305,7 @@ void MIDIStreamer::Play (bool looping)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void MIDIStreamer::Pause ()
|
||||
void MIDIStreamer::Pause()
|
||||
{
|
||||
if (m_Status == STATE_Playing)
|
||||
{
|
||||
|
@ -302,7 +326,7 @@ void MIDIStreamer::Pause ()
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void MIDIStreamer::Resume ()
|
||||
void MIDIStreamer::Resume()
|
||||
{
|
||||
if (m_Status == STATE_Paused)
|
||||
{
|
||||
|
@ -322,7 +346,7 @@ void MIDIStreamer::Resume ()
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void MIDIStreamer::Stop ()
|
||||
void MIDIStreamer::Stop()
|
||||
{
|
||||
EndQueued = 2;
|
||||
#ifdef _WIN32
|
||||
|
@ -355,7 +379,7 @@ void MIDIStreamer::Stop ()
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
bool MIDIStreamer::IsPlaying ()
|
||||
bool MIDIStreamer::IsPlaying()
|
||||
{
|
||||
return m_Status != STATE_Stopped;
|
||||
}
|
||||
|
@ -369,7 +393,7 @@ bool MIDIStreamer::IsPlaying ()
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void MIDIStreamer::MusicVolumeChanged ()
|
||||
void MIDIStreamer::MusicVolumeChanged()
|
||||
{
|
||||
if (MIDI->FakeVolume())
|
||||
{
|
||||
|
@ -676,7 +700,7 @@ int MIDIStreamer::FillBuffer(int buffer_num, int max_events, DWORD max_time)
|
|||
|
||||
//==========================================================================
|
||||
//
|
||||
// MIDIDevice constructor and desctructor stubs.
|
||||
// MIDIDevice stubs.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
|
|
|
@ -31,8 +31,6 @@
|
|||
**---------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
// HEADER FILES ------------------------------------------------------------
|
||||
|
||||
#include "i_musicinterns.h"
|
||||
|
@ -88,8 +86,8 @@ static const BYTE CtrlTranslate[15] =
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
MUSSong2::MUSSong2 (FILE *file, char *musiccache, int len)
|
||||
: MIDIStreamer(false), MusHeader(0), MusBuffer(0)
|
||||
MUSSong2::MUSSong2 (FILE *file, char *musiccache, int len, bool opl)
|
||||
: MIDIStreamer(opl), MusHeader(0), MusBuffer(0)
|
||||
{
|
||||
if (ExitEvent == NULL)
|
||||
{
|
||||
|
@ -304,4 +302,32 @@ end:
|
|||
}
|
||||
return events;
|
||||
}
|
||||
#endif
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// MUSSong2 :: GetOPLDumper
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
MusInfo *MUSSong2::GetOPLDumper(const char *filename)
|
||||
{
|
||||
return new MUSSong2(this, filename);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// MUSSong2 OPL Dumping Constructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
MUSSong2::MUSSong2(const MUSSong2 *original, const char *filename)
|
||||
: MIDIStreamer(filename)
|
||||
{
|
||||
int songstart = LittleShort(original->MusHeader->SongStart);
|
||||
MaxMusP = original->MaxMusP;
|
||||
MusHeader = (MUSHeader *)new BYTE[songstart + MaxMusP];
|
||||
memcpy(MusHeader, original->MusHeader, songstart + MaxMusP);
|
||||
MusBuffer = (BYTE *)MusHeader + songstart;
|
||||
Division = 140;
|
||||
InitialTempo = 1000000;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#include "i_musicinterns.h"
|
||||
|
||||
CVAR (Bool, opl_enable, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
|
||||
static bool OPL_Active;
|
||||
|
||||
CUSTOM_CVAR (Bool, opl_onechip, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
|
@ -12,12 +10,11 @@ CUSTOM_CVAR (Bool, opl_onechip, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
OPLMUSSong::OPLMUSSong (FILE *file, char *musiccache, int len)
|
||||
{
|
||||
int samples = int(OPL_SAMPLE_RATE / 14);
|
||||
|
||||
Music = new OPLmusicFile (file, musiccache, len, samples);
|
||||
Music = new OPLmusicFile (file, musiccache, len);
|
||||
|
||||
m_Stream = GSnd->CreateStream (FillStream, samples*4,
|
||||
SoundStream::Mono | SoundStream::Float, int(OPL_SAMPLE_RATE), this);
|
||||
|
@ -63,7 +60,7 @@ void OPLMUSSong::Play (bool looping)
|
|||
Music->SetLooping (looping);
|
||||
Music->Restart ();
|
||||
|
||||
if (m_Stream->Play (true, snd_musicvolume, false))
|
||||
if (m_Stream == NULL || m_Stream->Play (true, snd_musicvolume, false))
|
||||
{
|
||||
m_Status = STATE_Playing;
|
||||
}
|
||||
|
@ -74,3 +71,24 @@ bool OPLMUSSong::FillStream (SoundStream *stream, void *buff, int len, void *use
|
|||
OPLMUSSong *song = (OPLMUSSong *)userdata;
|
||||
return song->Music->ServiceStream (buff, len);
|
||||
}
|
||||
|
||||
MusInfo *OPLMUSSong::GetOPLDumper(const char *filename)
|
||||
{
|
||||
return new OPLMUSDumper(this, filename);
|
||||
}
|
||||
|
||||
OPLMUSSong::OPLMUSSong(const OPLMUSSong *original, const char *filename)
|
||||
{
|
||||
Music = new OPLmusicFile(original->Music, filename);
|
||||
m_Stream = NULL;
|
||||
}
|
||||
|
||||
OPLMUSDumper::OPLMUSDumper(const OPLMUSSong *original, const char *filename)
|
||||
: OPLMUSSong(original, filename)
|
||||
{
|
||||
}
|
||||
|
||||
void OPLMUSDumper::Play(bool looping)
|
||||
{
|
||||
Music->Dump();
|
||||
}
|
||||
|
|
|
@ -2844,6 +2844,10 @@
|
|||
RelativePath=".\src\oplsynth\music_opl_mididevice.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\oplsynth\music_opldumper_mididevice.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="src\sound\music_spc.cpp"
|
||||
>
|
||||
|
@ -2887,10 +2891,6 @@
|
|||
RelativePath=".\src\oplsynth\fmopl.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\oplsynth\mlkernel.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\oplsynth\mlopl.cpp"
|
||||
>
|
||||
|
|
Loading…
Reference in a new issue