Remove all OPL synths from the project.

This commit is contained in:
nashmuhandes 2016-04-27 21:34:23 +08:00
parent e74517ff37
commit 4b5c73b675
28 changed files with 14 additions and 9359 deletions

View file

@ -547,8 +547,6 @@ file( GLOB HEADER_FILES
g_strife/*.h
intermission/*.h
menu/*.h
oplsynth/*.h
oplsynth/dosbox/*.h
posix/*.h
posix/cocoa/*.h
posix/sdl/*.h
@ -691,12 +689,6 @@ set( FASTMATH_PCH_SOURCES
menu/playermenu.cpp
menu/readthis.cpp
menu/videomenu.cpp
oplsynth/fmopl.cpp
oplsynth/mlopl.cpp
oplsynth/mlopl_io.cpp
oplsynth/dosbox/opl.cpp
oplsynth/OPL3.cpp
oplsynth/nukedopl3.cpp
timidity/common.cpp
timidity/instrum.cpp
timidity/instrum_dls.cpp
@ -720,9 +712,6 @@ set( FASTMATH_PCH_SOURCES
# Enable fast math for some sources
set( FASTMATH_SOURCES
${FASTMATH_PCH_SOURCES}
oplsynth/music_opldumper_mididevice.cpp
oplsynth/music_opl_mididevice.cpp
oplsynth/opl_mus_player.cpp
sound/commonsound.cpp
sound/i_music.cpp
sound/i_sound.cpp
@ -737,7 +726,6 @@ set( FASTMATH_SOURCES
sound/music_midistream.cpp
sound/music_midi_base.cpp
sound/music_midi_timidity.cpp
sound/music_mus_opl.cpp
sound/music_stream.cpp
sound/music_fluidsynth_mididevice.cpp
sound/music_softsynth_mididevice.cpp
@ -1113,7 +1101,6 @@ include_directories( .
g_raven
g_strife
g_shared
oplsynth
sound
textures
thingdef
@ -1172,10 +1159,6 @@ if( NOT WIN32 )
COMMAND chmod +x ${CMAKE_CURRENT_BINARY_DIR}/link-make
COMMAND /bin/sh -c ${CMAKE_CURRENT_BINARY_DIR}/link-make )
endif()
if( CMAKE_COMPILER_IS_GNUCXX )
# GCC misoptimizes this file
set_source_files_properties( oplsynth/fmopl.cpp PROPERTIES COMPILE_FLAGS "-fno-tree-dominator-opts -fno-tree-fre" )
endif()
if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
# Need to enable intrinsics for this file.
if( SSE_MATTERS )
@ -1190,8 +1173,6 @@ if( APPLE )
endif()
source_group("Audio Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/.+")
source_group("Audio Files\\OPL Synth" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/oplsynth/.+")
source_group("Audio Files\\OPL Synth\\DOSBox" FILES oplsynth/dosbox/opl.cpp oplsynth/dosbox/opl.h)
source_group("Audio Files\\Timidity\\Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/timidity/.+\\.h$")
source_group("Audio Files\\Timidity\\Source" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/timidity/.+\\.cpp$")
source_group("Audio Files\\WildMidi\\Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/wildmidi/.+\\.h$")

File diff suppressed because it is too large Load diff

View file

@ -1,28 +0,0 @@
/*
* Name: General Use Types Definitions -- Header Include file
* Version: 1.24
* Author: Vladimir Arnost (QA-Software)
* Last revision: Sep-4-1995
* Compiler: Borland C++ 3.1, Watcom C/C++ 10.0
*
*/
#ifndef __DEFTYPES_H_
#define __DEFTYPES_H_
/* Global type declarations */
#include "doomtype.h"
/* machine dependent types */
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long ulong;
typedef signed char schar;
typedef signed short sshort;
typedef signed int sint;
typedef signed long slong;
#endif // __DEFTYPES_H_

File diff suppressed because it is too large Load diff

View file

@ -1,231 +0,0 @@
/*
* Copyright (C) 2002-2011 The DOSBox Team
* OPL2/OPL3 emulation library
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* Originally based on ADLIBEMU.C, an AdLib/OPL2 emulation library by Ken Silverman
* Copyright (C) 1998-2001 Ken Silverman
* Ken Silverman's official web site: "http://www.advsys.net/ken"
*/
#define fltype double
/*
define Bits, Bitu, Bit32s, Bit32u, Bit16s, Bit16u, Bit8s, Bit8u here
*/
/*
#include <stdint.h>
typedef uintptr_t Bitu;
typedef intptr_t Bits;
typedef uint32_t Bit32u;
typedef int32_t Bit32s;
typedef uint16_t Bit16u;
typedef int16_t Bit16s;
typedef uint8_t Bit8u;
typedef int8_t Bit8s;
*/
/*
define attribution that inlines/forces inlining of a function (optional)
*/
#define OPL_INLINE inline
#undef NUM_CHANNELS
#if defined(OPLTYPE_IS_OPL3)
#define NUM_CHANNELS 18
#else
#define NUM_CHANNELS 9
#endif
#define MAXOPERATORS (NUM_CHANNELS*2)
#define FL05 ((fltype)0.5)
#define FL2 ((fltype)2.0)
#define PI ((fltype)3.1415926535897932384626433832795)
#define FIXEDPT 0x10000 // fixed-point calculations using 16+16
#define FIXEDPT_LFO 0x1000000 // fixed-point calculations using 8+24
#define WAVEPREC 1024 // waveform precision (10 bits)
#define INTFREQU ((fltype)(14318180.0 / 288.0)) // clocking of the chip
#define OF_TYPE_ATT 0
#define OF_TYPE_DEC 1
#define OF_TYPE_REL 2
#define OF_TYPE_SUS 3
#define OF_TYPE_SUS_NOKEEP 4
#define OF_TYPE_OFF 5
#define ARC_CONTROL 0x00
#define ARC_TVS_KSR_MUL 0x20
#define ARC_KSL_OUTLEV 0x40
#define ARC_ATTR_DECR 0x60
#define ARC_SUSL_RELR 0x80
#define ARC_FREQ_NUM 0xa0
#define ARC_KON_BNUM 0xb0
#define ARC_PERC_MODE 0xbd
#define ARC_FEEDBACK 0xc0
#define ARC_WAVE_SEL 0xe0
#define ARC_SECONDSET 0x100 // second operator set for OPL3
#define OP_ACT_OFF 0x00
#define OP_ACT_NORMAL 0x01 // regular channel activated (bitmasked)
#define OP_ACT_PERC 0x02 // percussion channel activated (bitmasked)
#define BLOCKBUF_SIZE 512
// vibrato constants
#define VIBTAB_SIZE 8
#define VIBFAC 70/50000 // no braces, integer mul/div
// tremolo constants and table
#define TREMTAB_SIZE 53
#define TREM_FREQ ((fltype)(3.7)) // tremolo at 3.7hz
/* operator struct definition
For OPL2 all 9 channels consist of two operators each, carrier and modulator.
Channel x has operators x as modulator and operators (9+x) as carrier.
For OPL3 all 18 channels consist either of two operators (2op mode) or four
operators (4op mode) which is determined through register4 of the second
adlib register set.
Only the channels 0,1,2 (first set) and 9,10,11 (second set) can act as
4op channels. The two additional operators for a channel y come from the
2op channel y+3 so the operatorss y, (9+y), y+3, (9+y)+3 make up a 4op
channel.
*/
typedef struct operator_struct {
Bit32s cval, lastcval; // current output/last output (used for feedback)
Bit32u tcount, wfpos, tinc; // time (position in waveform) and time increment
fltype amp, step_amp; // and amplification (envelope)
fltype vol; // volume
fltype sustain_level; // sustain level
Bit32s mfbi; // feedback amount
fltype a0, a1, a2, a3; // attack rate function coefficients
fltype decaymul, releasemul; // decay/release rate functions
Bit32u op_state; // current state of operator (attack/decay/sustain/release/off)
Bit32u toff;
Bit32s freq_high; // highest three bits of the frequency, used for vibrato calculations
Bit16s* cur_wform; // start of selected waveform
Bit32u cur_wmask; // mask for selected waveform
Bit32u act_state; // activity state (regular, percussion)
bool sus_keep; // keep sustain level when decay finished
bool vibrato,tremolo; // vibrato/tremolo enable bits
// variables used to provide non-continuous envelopes
Bit32u generator_pos; // for non-standard sample rates we need to determine how many samples have passed
Bits cur_env_step; // current (standardized) sample position
Bits env_step_a,env_step_d,env_step_r; // number of std samples of one step (for attack/decay/release mode)
Bit8u step_skip_pos_a; // position of 8-cyclic step skipping (always 2^x to check against mask)
Bits env_step_skip_a; // bitmask that determines if a step is skipped (respective bit is zero then)
#if defined(OPLTYPE_IS_OPL3)
bool is_4op,is_4op_attached; // base of a 4op channel/part of a 4op channel
float left_pan,right_pan; // opl3 stereo panning amount
#endif
} op_type;
// per-chip variables
class DBOPL : public OPLEmul
{
private:
Bitu chip_num;
op_type op[MAXOPERATORS];
Bits int_samplerate;
Bit8u status;
Bit32u opl_index;
#if defined(OPLTYPE_IS_OPL3)
Bit8u adlibreg[512]; // adlib register set (including second set)
Bit8u wave_sel[44]; // waveform selection
#else
Bit8u adlibreg[256]; // adlib register set
Bit8u wave_sel[22]; // waveform selection
#endif
fltype recipsamp; // inverse of sampling rate
// vibrato/tremolo tables
Bit32s vib_table[VIBTAB_SIZE];
Bit32s trem_table[TREMTAB_SIZE*2];
Bit32s vibval_const[BLOCKBUF_SIZE];
Bit32s tremval_const[BLOCKBUF_SIZE];
// vibrato value tables (used per-operator)
Bit32s vibval_var1[BLOCKBUF_SIZE];
Bit32s vibval_var2[BLOCKBUF_SIZE];
// vibrato/trmolo value table pointers
Bit32s *vibval1, *vibval2, *vibval3, *vibval4;
Bit32s *tremval1, *tremval2, *tremval3, *tremval4;
// calculated frequency multiplication values (depend on sampling rate)
fltype frqmul[16];
// vibrato/tremolo increment/counter
Bit32u vibtab_pos;
Bit32u vibtab_add;
Bit32u tremtab_pos;
Bit32u tremtab_add;
// Enable full MIDI panning; disable OPL3 panning
bool FullPan;
// enable an operator
void enable_operator(Bitu regbase, op_type* op_pt, Bit32u act_type);
void disable_operator(op_type* op_pt, Bit32u act_type);
// functions to change parameters of an operator
void change_frequency(Bitu chanbase, Bitu regbase, op_type* op_pt);
void change_attackrate(Bitu regbase, op_type* op_pt);
void change_decayrate(Bitu regbase, op_type* op_pt);
void change_releaserate(Bitu regbase, op_type* op_pt);
void change_sustainlevel(Bitu regbase, op_type* op_pt);
void change_waveform(Bitu regbase, op_type* op_pt);
void change_keepsustain(Bitu regbase, op_type* op_pt);
void change_vibrato(Bitu regbase, op_type* op_pt);
void change_feedback(Bitu chanbase, op_type* op_pt);
// general functions
public:
void Reset();
void Update(float* sndptr, int numsamples);
void WriteReg(int idx, int val);
void SetPanning(int c, float left, float right);
DBOPL(bool stereo);
};
static Bit32u generator_add; // should be a chip parameter

File diff suppressed because it is too large Load diff

View file

@ -1,485 +0,0 @@
/*
* Name: OPL2/OPL3 Music driver
* Project: MUS File Player Library
* Version: 1.67
* Author: Vladimir Arnost (QA-Software)
* Last revision: May-2-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
* 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-28-1994 V1.40 V.Arnost
* Added Adlib and SB Pro II detection.
* Apr-16-1995 V1.60 V.Arnost
* Moved into separate source file MUSLIB.C
* Jul-12-1995 V1.62 V.Arnost
* Module created and source code copied from MUSLIB.C
* Aug-08-1995 V1.63 V.Arnost
* Modified to follow changes in MLOPL_IO.C
* Aug-13-1995 V1.64 V.Arnost
* Added OPLsendMIDI() function
* Sep-8-1995 V1.65 V.Arnost
* Added sustain pedal support.
* Improved pause/unpause functions. Now all notes are made
* silent (even released notes, which haven't sounded off yet).
* Mar-1-1996 V1.66 V.Arnost
* Cleaned up the source
* May-2-1996 V1.67 V.Arnost
* Added modulation wheel (vibrato) support
*/
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <io.h>
#endif
#include "muslib.h"
#include "files.h"
#include "c_cvars.h"
#define MOD_MIN 40 /* vibrato threshold */
//#define HIGHEST_NOTE 102
#define HIGHEST_NOTE 127
musicBlock::musicBlock ()
{
memset (this, 0, sizeof(*this));
}
musicBlock::~musicBlock ()
{
if (OPLinstruments != NULL) free(OPLinstruments);
}
void musicBlock::writeFrequency(uint slot, uint note, int pitch, uint keyOn)
{
io->OPLwriteFreq (slot, note, pitch, keyOn);
}
void musicBlock::writeModulation(uint slot, struct OPL2instrument *instr, int state)
{
if (state)
state = 0x40; /* enable Frequency Vibrato */
io->OPLwriteChannel(0x20, slot,
(instr->feedback & 1) ? (instr->trem_vibr_1 | state) : instr->trem_vibr_1,
instr->trem_vibr_2 | state);
}
uint musicBlock::calcVolume(uint channelVolume, uint channelExpression, uint noteVolume)
{
noteVolume = ((ulong)channelVolume * channelExpression * noteVolume) / (127*127);
if (noteVolume > 127)
return 127;
else
return noteVolume;
}
int musicBlock::occupyChannel(uint slot, uint channel,
int note, int volume, struct OP2instrEntry *instrument, uchar secondary)
{
struct OPL2instrument *instr;
struct channelEntry *ch = &channels[slot];
ch->channel = channel;
ch->note = note;
ch->flags = secondary ? CH_SECONDARY : 0;
if (driverdata.channelModulation[channel] >= MOD_MIN)
ch->flags |= CH_VIBRATO;
ch->time = MLtime;
if (volume == -1)
volume = driverdata.channelLastVolume[channel];
else
driverdata.channelLastVolume[channel] = volume;
ch->realvolume = calcVolume(driverdata.channelVolume[channel],
driverdata.channelExpression[channel], ch->volume = volume);
if (instrument->flags & FL_FIXED_PITCH)
note = instrument->note;
else if (channel == PERCUSSION)
note = 60; // C-5
if (secondary && (instrument->flags & FL_DOUBLE_VOICE))
ch->finetune = (instrument->finetune - 0x80) >> 1;
else
ch->finetune = 0;
ch->pitch = ch->finetune + driverdata.channelPitch[channel];
if (secondary)
instr = &instrument->instr[1];
else
instr = &instrument->instr[0];
ch->instr = instr;
if (channel != PERCUSSION && !(instrument->flags & FL_FIXED_PITCH))
{
if ( (note += instr->basenote) < 0)
while ((note += 12) < 0) {}
else if (note > HIGHEST_NOTE)
while ((note -= 12) > HIGHEST_NOTE) {}
}
ch->realnote = note;
io->OPLwriteInstrument(slot, instr);
if (ch->flags & CH_VIBRATO)
writeModulation(slot, instr, 1);
io->OPLwritePan(slot, instr, driverdata.channelPan[channel]);
io->OPLwriteVolume(slot, instr, ch->realvolume);
writeFrequency(slot, note, ch->pitch, 1);
return slot;
}
int musicBlock::releaseChannel(uint slot, uint killed)
{
struct channelEntry *ch = &channels[slot];
writeFrequency(slot, ch->realnote, ch->pitch, 0);
ch->channel |= CH_FREE;
ch->time = MLtime;
ch->flags = CH_FREE;
if (killed)
{
io->OPLwriteChannel(0x80, slot, 0x0F, 0x0F); // release rate - fastest
io->OPLwriteChannel(0x40, slot, 0x3F, 0x3F); // no volume
}
return slot;
}
int musicBlock::releaseSustain(uint channel)
{
uint i;
uint id = channel;
for(i = 0; i < io->OPLchannels; i++)
{
if (channels[i].channel == id && channels[i].flags & CH_SUSTAIN)
releaseChannel(i, 0);
}
return 0;
}
int musicBlock::findFreeChannel(uint flag, uint channel, uchar note)
{
uint i;
ulong bestfit = 0;
uint bestvoice = 0;
for (i = 0; i < io->OPLchannels; ++i)
{
ulong magic;
magic = ((channels[i].flags & CH_FREE) << 24) |
((channels[i].note == note &&
channels[i].channel == channel) << 30) |
((channels[i].flags & CH_SUSTAIN) << 28) |
((MLtime - channels[i].time) & 0x1fffffff);
if (magic > bestfit)
{
bestfit = magic;
bestvoice = i;
}
}
if ((flag & 1) && !(bestfit & 0x80000000))
{ // No free channels good enough
return -1;
}
releaseChannel (bestvoice, 1);
return bestvoice;
}
struct OP2instrEntry *musicBlock::getInstrument(uint channel, uchar note)
{
uint instrnumber;
if (channel == PERCUSSION)
{
if (note < 35 || note > 81)
return NULL; /* wrong percussion number */
instrnumber = note + (128-35);
}
else
{
instrnumber = driverdata.channelInstr[channel];
}
if (OPLinstruments)
return &OPLinstruments[instrnumber];
else
return NULL;
}
// code 1: play note
CVAR (Bool, opl_singlevoice, 0, 0)
void musicBlock::OPLplayNote(uint channel, uchar note, int volume)
{
int i;
struct OP2instrEntry *instr;
if (volume == 0)
{
OPLreleaseNote (channel, note);
return;
}
if ( (instr = getInstrument(channel, note)) == NULL )
return;
if ( (i = findFreeChannel((channel == PERCUSSION) ? 2 : 0, channel, note)) != -1)
{
occupyChannel(i, channel, note, volume, instr, 0);
if ((instr->flags & FL_DOUBLE_VOICE) && !opl_singlevoice)
{
if ( (i = findFreeChannel((channel == PERCUSSION) ? 3 : 1, channel, note)) != -1)
occupyChannel(i, channel, note, volume, instr, 1);
}
}
}
// code 0: release note
void musicBlock::OPLreleaseNote(uint channel, uchar note)
{
uint i;
uint id = channel;
uint sustain = driverdata.channelSustain[channel];
for(i = 0; i < io->OPLchannels; i++)
{
if (channels[i].channel == id && channels[i].note == note)
{
if (sustain < 0x40)
releaseChannel(i, 0);
else
channels[i].flags |= CH_SUSTAIN;
}
}
}
// code 2: change pitch wheel (bender)
void musicBlock::OPLpitchWheel(uint channel, int pitch)
{
uint i;
uint id = channel;
// Convert pitch from 14-bit to 7-bit, then scale it, since the player
// code only understands sensitivities of 2 semitones.
pitch = (pitch - 8192) * driverdata.channelPitchSens[channel] / (200 * 128) + 64;
driverdata.channelPitch[channel] = pitch;
for(i = 0; i < io->OPLchannels; i++)
{
struct channelEntry *ch = &channels[i];
if (ch->channel == id)
{
ch->time = MLtime;
ch->pitch = ch->finetune + pitch;
writeFrequency(i, ch->realnote, ch->pitch, 1);
}
}
}
// code 4: change control
void musicBlock::OPLchangeControl(uint channel, uchar controller, int value)
{
uint i;
uint id = channel;
switch (controller)
{
case ctrlPatch: /* change instrument */
OPLprogramChange(channel, value);
break;
case ctrlModulation:
driverdata.channelModulation[channel] = value;
for(i = 0; i < io->OPLchannels; i++)
{
struct channelEntry *ch = &channels[i];
if (ch->channel == id)
{
uchar flags = ch->flags;
ch->time = MLtime;
if (value >= MOD_MIN)
{
ch->flags |= CH_VIBRATO;
if (ch->flags != flags)
writeModulation(i, ch->instr, 1);
} else {
ch->flags &= ~CH_VIBRATO;
if (ch->flags != flags)
writeModulation(i, ch->instr, 0);
}
}
}
break;
case ctrlVolume: /* change volume */
driverdata.channelVolume[channel] = value;
/* fall-through */
case ctrlExpression: /* change expression */
if (controller == ctrlExpression)
{
driverdata.channelExpression[channel] = value;
}
for(i = 0; i < io->OPLchannels; i++)
{
struct channelEntry *ch = &channels[i];
if (ch->channel == id)
{
ch->time = MLtime;
ch->realvolume = calcVolume(driverdata.channelVolume[channel],
driverdata.channelExpression[channel], ch->volume);
io->OPLwriteVolume(i, ch->instr, ch->realvolume);
}
}
break;
case ctrlPan: /* change pan (balance) */
driverdata.channelPan[channel] = value -= 64;
for(i = 0; i < io->OPLchannels; i++)
{
struct channelEntry *ch = &channels[i];
if (ch->channel == id)
{
ch->time = MLtime;
io->OPLwritePan(i, ch->instr, value);
}
}
break;
case ctrlSustainPedal: /* change sustain pedal (hold) */
driverdata.channelSustain[channel] = value;
if (value < 0x40)
releaseSustain(channel);
break;
case ctrlNotesOff: /* turn off all notes that are not sustained */
for (i = 0; i < io->OPLchannels; ++i)
{
if (channels[i].channel == id)
{
if (driverdata.channelSustain[id] < 0x40)
releaseChannel(i, 0);
else
channels[i].flags |= CH_SUSTAIN;
}
}
break;
case ctrlSoundsOff: /* release all notes for this channel */
for (i = 0; i < io->OPLchannels; ++i)
{
if (channels[i].channel == id)
{
releaseChannel(i, 0);
}
}
break;
case ctrlRPNHi:
driverdata.channelRPN[id] = (driverdata.channelRPN[id] & 0x007F) | (value << 7);
break;
case ctrlRPNLo:
driverdata.channelRPN[id] = (driverdata.channelRPN[id] & 0x3F80) | value;
break;
case ctrlNRPNLo:
case ctrlNRPNHi:
driverdata.channelRPN[id] = 0x3FFF;
break;
case ctrlDataEntryHi:
if (driverdata.channelRPN[id] == 0)
{
driverdata.channelPitchSens[id] = value * 100 + (driverdata.channelPitchSens[id] % 100);
}
break;
case ctrlDataEntryLo:
if (driverdata.channelRPN[id] == 0)
{
driverdata.channelPitchSens[id] = value + (driverdata.channelPitchSens[id] / 100) * 100;
}
break;
}
}
void musicBlock::OPLresetControllers(uint chan, int vol)
{
driverdata.channelVolume[chan] = vol;
driverdata.channelExpression[chan] = 127;
driverdata.channelSustain[chan] = 0;
driverdata.channelLastVolume[chan] = 64;
driverdata.channelPitch[chan] = 64;
driverdata.channelRPN[chan] = 0x3fff;
driverdata.channelPitchSens[chan] = 200;
}
void musicBlock::OPLprogramChange(uint channel, int value)
{
driverdata.channelInstr[channel] = value;
}
void musicBlock::OPLplayMusic(int vol)
{
uint i;
for (i = 0; i < CHANNELS; i++)
{
OPLresetControllers(i, vol);
}
}
void musicBlock::OPLstopMusic()
{
uint i;
for(i = 0; i < io->OPLchannels; i++)
if (!(channels[i].flags & CH_FREE))
releaseChannel(i, 1);
}
int musicBlock::OPLloadBank (FileReader &data)
{
static const uchar masterhdr[8] = { '#','O','P','L','_','I','I','#' };
struct OP2instrEntry *instruments;
uchar filehdr[8];
data.Read (filehdr, 8);
if (memcmp(filehdr, masterhdr, 8))
return -2; /* bad instrument file */
if ( (instruments = (struct OP2instrEntry *)calloc(OP2INSTRCOUNT, OP2INSTRSIZE)) == NULL)
return -3; /* not enough memory */
data.Read (instruments, OP2INSTRSIZE * OP2INSTRCOUNT);
if (OPLinstruments != NULL)
{
free(OPLinstruments);
}
OPLinstruments = instruments;
#if 0
for (int i = 0; i < 175; ++i)
{
Printf ("%3d.%-33s%3d %3d %3d %d\n", i,
(BYTE *)data+6308+i*32,
OPLinstruments[i].instr[0].basenote,
OPLinstruments[i].instr[1].basenote,
OPLinstruments[i].note,
OPLinstruments[i].flags);
}
#endif
return 0;
}

View file

@ -1,380 +0,0 @@
/*
* 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
*/
#include <math.h>
#ifdef _WIN32
#include <dos.h>
#include <conio.h>
#endif
#include "muslib.h"
#include "opl.h"
#include "c_cvars.h"
const double HALF_PI = (M_PI*0.5);
EXTERN_CVAR(Int, opl_core)
extern int current_opl_core;
OPLio::~OPLio()
{
}
void OPLio::SetClockRate(double samples_per_tick)
{
}
void OPLio::WriteDelay(int ticks)
{
}
void OPLio::OPLwriteReg(int which, uint reg, uchar data)
{
if (IsOPL3)
{
reg |= (which & 1) << 8;
which >>= 1;
}
if (chips[which] != NULL)
{
chips[which]->WriteReg(reg, 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[OPL2CHANNELS] = {
0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12};
uint which = channel / OPL2CHANNELS;
uint reg = regbase + op_num[channel % OPL2CHANNELS];
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)
{
uint which = channel / OPL2CHANNELS;
uint reg = regbase + (channel % OPL2CHANNELS);
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);
// Set real panning if we're using emulated chips.
int chanper = IsOPL3 ? OPL3CHANNELS : OPL2CHANNELS;
int which = channel / chanper;
if (chips[which] != NULL)
{
// This is the MIDI-recommended pan formula. 0 and 1 are
// both hard left so that 64 can be perfectly center.
// (Note that the 'pan' passed to this function is the
// MIDI pan position, subtracted by 64.)
double level = (pan <= -63) ? 0 : (pan + 64 - 1) / 126.0;
chips[which]->SetPanning(channel % chanper,
(float)cos(HALF_PI * level), (float)sin(HALF_PI * level));
}
}
}
/*
* 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, bool stereo, bool initopl3)
{
assert(numchips >= 1 && numchips <= countof(chips));
uint i;
IsOPL3 = (current_opl_core == 1 || current_opl_core == 2 || current_opl_core == 3);
memset(chips, 0, sizeof(chips));
if (IsOPL3)
{
numchips = (numchips + 1) >> 1;
}
for (i = 0; i < numchips; ++i)
{
OPLEmul *chip = IsOPL3 ? (current_opl_core == 1 ? DBOPLCreate(stereo) : (current_opl_core == 2 ? JavaOPLCreate(stereo) : NukedOPL3Create(stereo))) : YM3812Create(stereo);
if (chip == NULL)
{
break;
}
chips[i] = chip;
}
NumChips = i;
OPLchannels = i * (IsOPL3 ? OPL3CHANNELS : OPL2CHANNELS);
OPLwriteInitState(initopl3);
return i;
}
void OPLio::OPLwriteInitState(bool initopl3)
{
for (uint i = 0; i < NumChips; ++i)
{
int chip = i << (int)IsOPL3;
if (IsOPL3 && initopl3)
{
OPLwriteReg(chip, 0x105, 0x01); // enable YMF262/OPL3 mode
OPLwriteReg(chip, 0x104, 0x00); // disable 4-operator mode
}
OPLwriteReg(chip, 0x01, 0x20); // enable Waveform Select
OPLwriteReg(chip, 0x0B, 0x40); // turn off CSW mode
OPLwriteReg(chip, 0xBD, 0x00); // set vibrato/tremolo depth to low, set melodic mode
}
OPLshutup();
}
/*
* Deinitialize hardware before shutdown
*/
void OPLio::OPLdeinit(void)
{
for (size_t i = 0; i < countof(chips); ++i)
{
if (chips[i] != NULL)
{
delete chips[i];
chips[i] = NULL;
}
}
}

View file

@ -1,313 +0,0 @@
/*
** music_opl_mididevice.cpp
** Provides an emulated OPL implementation of a MIDI output device.
**
**---------------------------------------------------------------------------
** 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.
**---------------------------------------------------------------------------
**
** Uses Vladimir Arnost's MUS player library.
*/
// HEADER FILES ------------------------------------------------------------
#include "i_musicinterns.h"
#include "templates.h"
#include "doomdef.h"
#include "m_swap.h"
#include "w_wad.h"
#include "v_text.h"
#include "i_system.h"
#include "opl.h"
// MACROS ------------------------------------------------------------------
#if defined(_DEBUG) && defined(_WIN32) && defined(_MSC_VER)
#define DEBUGOUT(m,c,s,t) \
{ char foo[128]; mysnprintf(foo, countof(foo), m, c, s, t); I_DebugPrint(foo); }
#else
#define DEBUGOUT(m,c,s,t)
#endif
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
void OPL_SetCore(const char *args);
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
EXTERN_CVAR(Int, opl_numchips)
// PRIVATE DATA DEFINITIONS ------------------------------------------------
// PUBLIC DATA DEFINITIONS -------------------------------------------------
CVAR(Bool, opl_fullpan, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
// CODE --------------------------------------------------------------------
//==========================================================================
//
// OPLMIDIDevice Contructor
//
//==========================================================================
OPLMIDIDevice::OPLMIDIDevice(const char *args)
{
OPL_SetCore(args);
FullPan = opl_fullpan;
FWadLump data = Wads.OpenLumpName("GENMIDI");
OPLloadBank(data);
SampleRate = (int)OPL_SAMPLE_RATE;
}
//==========================================================================
//
// OPLMIDIDevice :: Open
//
// Returns 0 on success.
//
//==========================================================================
int OPLMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata)
{
if (io == NULL || 0 == (NumChips = io->OPLinit(opl_numchips, FullPan, true)))
{
return 1;
}
int ret = OpenStream(14, (FullPan || io->IsOPL3) ? 0 : SoundStream::Mono, callback, userdata);
if (ret == 0)
{
OPLstopMusic();
OPLplayMusic(100);
DEBUGOUT("========= New song started ==========\n", 0, 0, 0);
}
return ret;
}
//==========================================================================
//
// OPLMIDIDevice :: Close
//
//==========================================================================
void OPLMIDIDevice::Close()
{
SoftSynthMIDIDevice::Close();
io->OPLdeinit();
}
//==========================================================================
//
// OPLMIDIDevice :: GetTechnology
//
//==========================================================================
int OPLMIDIDevice::GetTechnology() const
{
return MOD_FMSYNTH;
}
//==========================================================================
//
// OPLMIDIDevice :: CalcTickRate
//
// Tempo is the number of microseconds per quarter note.
// Division is the number of ticks per quarter note.
//
//==========================================================================
void OPLMIDIDevice::CalcTickRate()
{
SoftSynthMIDIDevice::CalcTickRate();
io->SetClockRate(OPLmusicBlock::SamplesPerTick = SoftSynthMIDIDevice::SamplesPerTick);
}
//==========================================================================
//
// OPLMIDIDevice :: PlayTick
//
// We derive from two base classes that both define PlayTick(), so we need
// to be unambiguous about which one to use.
//
//==========================================================================
int OPLMIDIDevice::PlayTick()
{
return SoftSynthMIDIDevice::PlayTick();
}
//==========================================================================
//
// OPLMIDIDevice :: HandleEvent
//
// Processes a normal MIDI event.
//
//==========================================================================
void OPLMIDIDevice::HandleEvent(int status, int parm1, int parm2)
{
int command = status & 0xF0;
int channel = status & 0x0F;
// Swap channels 9 and 15, because their roles are reversed
// in MUS and MIDI formats.
if (channel == 9)
{
channel = 15;
}
else if (channel == 15)
{
channel = 9;
}
switch (command)
{
case MIDI_NOTEOFF:
playingcount--;
OPLreleaseNote(channel, parm1);
break;
case MIDI_NOTEON:
playingcount++;
OPLplayNote(channel, parm1, parm2);
break;
case MIDI_POLYPRESS:
DEBUGOUT("Unhandled note aftertouch: Channel %d, note %d, value %d\n", channel, parm1, parm2);
break;
case MIDI_CTRLCHANGE:
switch (parm1)
{
case 0: OPLchangeControl(channel, ctrlBank, parm2); break;
case 1: OPLchangeControl(channel, ctrlModulation, parm2); break;
case 6: OPLchangeControl(channel, ctrlDataEntryHi, parm2); break;
case 7: OPLchangeControl(channel, ctrlVolume, parm2); break;
case 10: OPLchangeControl(channel, ctrlPan, parm2); break;
case 11: OPLchangeControl(channel, ctrlExpression, parm2); break;
case 38: OPLchangeControl(channel, ctrlDataEntryLo, parm2); break;
case 64: OPLchangeControl(channel, ctrlSustainPedal, parm2); break;
case 67: OPLchangeControl(channel, ctrlSoftPedal, parm2); break;
case 91: OPLchangeControl(channel, ctrlReverb, parm2); break;
case 93: OPLchangeControl(channel, ctrlChorus, parm2); break;
case 98: OPLchangeControl(channel, ctrlNRPNLo, parm2); break;
case 99: OPLchangeControl(channel, ctrlNRPNHi, parm2); break;
case 100: OPLchangeControl(channel, ctrlRPNLo, parm2); break;
case 101: OPLchangeControl(channel, ctrlRPNHi, parm2); break;
case 120: OPLchangeControl(channel, ctrlSoundsOff, parm2); break;
case 121: OPLresetControllers(channel, 100); break;
case 123: OPLchangeControl(channel, ctrlNotesOff, parm2); break;
case 126: OPLchangeControl(channel, ctrlMono, parm2); break;
case 127: OPLchangeControl(channel, ctrlPoly, parm2); break;
default:
DEBUGOUT("Unhandled controller: Channel %d, controller %d, value %d\n", channel, parm1, parm2);
break;
}
break;
case MIDI_PRGMCHANGE:
OPLprogramChange(channel, parm1);
break;
case MIDI_CHANPRESS:
DEBUGOUT("Unhandled channel aftertouch: Channel %d, value %d\n", channel, parm1, 0);
break;
case MIDI_PITCHBEND:
OPLpitchWheel(channel, parm1 | (parm2 << 7));
break;
}
}
//==========================================================================
//
// OPLMIDIDevice :: HandleLongEvent
//
//==========================================================================
void OPLMIDIDevice::HandleLongEvent(const BYTE *data, int len)
{
}
//==========================================================================
//
// OPLMIDIDevice :: ComputeOutput
//
// We override ServiceStream, so this function is never actually called.
//
//==========================================================================
void OPLMIDIDevice::ComputeOutput(float *buffer, int len)
{
}
//==========================================================================
//
// OPLMIDIDevice :: ServiceStream
//
//==========================================================================
bool OPLMIDIDevice::ServiceStream(void *buff, int numbytes)
{
return OPLmusicBlock::ServiceStream(buff, numbytes);
}
//==========================================================================
//
// OPLMIDIDevice :: GetStats
//
//==========================================================================
FString OPLMIDIDevice::GetStats()
{
FString out;
char star[3] = { TEXTCOLOR_ESCAPE, 'A', '*' };
for (uint i = 0; i < io->OPLchannels; ++i)
{
if (channels[i].flags & CH_FREE)
{
star[1] = CR_BRICK + 'A';
}
else if (channels[i].flags & CH_SUSTAIN)
{
star[1] = CR_ORANGE + 'A';
}
else if (channels[i].flags & CH_SECONDARY)
{
star[1] = CR_BLUE + 'A';
}
else
{
star[1] = CR_GREEN + 'A';
}
out.AppendCStrPart (star, 3);
}
return out;
}

View file

@ -1,387 +0,0 @@
/*
** 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 "opl.h"
// MACROS ------------------------------------------------------------------
// TYPES -------------------------------------------------------------------
class OPLDump : public OPLEmul
{
public:
OPLDump(FILE *file) : File(file), TimePerTick(0), CurTime(0),
CurIntTime(0), TickMul(1), CurChip(0) {}
// If we're doing things right, these should never be reset.
virtual void Reset() { assert(0); }
// Update() is only used for getting waveform data, which dumps don't do.
virtual void Update(float *buffer, int length) { assert(0); }
// OPL dumps don't pan beyond what OPL3 is capable of (which is
// already written using registers from the original data).
virtual void SetPanning(int c, float left, float right) {}
// Only for the OPL dumpers, not the emulators
virtual void SetClockRate(double samples_per_tick) {}
virtual void WriteDelay(int ticks) = 0;
protected:
FILE *File;
double TimePerTick; // in milliseconds
double CurTime;
int CurIntTime;
int TickMul;
BYTE CurChip;
};
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
// PRIVATE DATA DEFINITIONS ------------------------------------------------
// PUBLIC DATA DEFINITIONS -------------------------------------------------
// CODE --------------------------------------------------------------------
class OPL_RDOSdump : public OPLDump
{
public:
OPL_RDOSdump(FILE *file) : OPLDump(file)
{
assert(File != NULL);
fwrite("RAWADATA\0", 1, 10, File);
NeedClockRate = true;
}
virtual ~OPL_RDOSdump()
{
if (File != NULL)
{
WORD endmark = 0xFFFF;
fwrite(&endmark, 2, 1, File);
fclose(File);
}
}
virtual void WriteReg(int reg, int v)
{
assert(File != NULL);
BYTE chipnum = reg >> 8;
if (chipnum != CurChip)
{
BYTE switcher[2] = { (BYTE)(chipnum + 1), 2 };
fwrite(switcher, 1, 2, File);
}
reg &= 255;
if (reg != 0 && reg != 2 && (reg != 255 || v != 255))
{
BYTE cmd[2] = { BYTE(v), BYTE(reg) };
fwrite(cmd, 1, 2, File);
}
}
virtual void SetClockRate(double samples_per_tick)
{
TimePerTick = samples_per_tick / OPL_SAMPLE_RATE * 1000.0;
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, BYTE(clock_word & 255), BYTE(clock_word >> 8) };
fwrite(clock_change, 1, 4, File);
}
}
virtual void WriteDelay(int ticks)
{
if (ticks > 0)
{ // 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);
}
}
protected:
bool NeedClockRate;
};
class OPL_DOSBOXdump : public OPLDump
{
public:
OPL_DOSBOXdump(FILE *file, bool dual) : OPLDump(file), Dual(dual)
{
assert(File != NULL);
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);
char type[4] = { (char)(Dual * 2), 0, 0, 0 }; // Single or dual OPL-2
fwrite(type, 1, 4, File);
}
virtual ~OPL_DOSBOXdump()
{
if (File != NULL)
{
long where_am_i = ftell(File);
DWORD len[2];
fseek(File, 12, SEEK_SET);
len[0] = LittleLong(CurIntTime);
len[1] = LittleLong(DWORD(where_am_i - 24));
fwrite(len, 4, 2, File);
fclose(File);
}
}
virtual void WriteReg(int reg, int v)
{
assert(File != NULL);
BYTE chipnum = reg >> 8;
if (chipnum != CurChip)
{
CurChip = chipnum;
fputc(chipnum + 2, File);
}
reg &= 255;
BYTE cmd[3] = { 4, BYTE(reg), BYTE(v) };
fwrite (cmd + (reg > 4), 1, 3 - (reg > 4), File);
}
virtual void WriteDelay(int ticks)
{
if (ticks > 0)
{ // 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);
}
}
}
protected:
bool Dual;
};
//==========================================================================
//
// OPLDumperMIDIDevice Constructor
//
//==========================================================================
OPLDumperMIDIDevice::OPLDumperMIDIDevice(const char *filename)
: OPLMIDIDevice(NULL)
{
// 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, bool, bool initopl3)
{
FILE *file = fopen(Filename, "wb");
if (file == NULL)
{
Printf("Could not open %s for writing.\n", Filename.GetChars());
return 0;
}
numchips = clamp(numchips, 1u, 2u);
memset(chips, 0, sizeof(chips));
// 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)
{
chips[0] = new OPL_RDOSdump(file);
}
else
{
chips[0] = new OPL_DOSBOXdump(file, numchips > 1);
}
OPLchannels = OPL2CHANNELS * numchips;
NumChips = numchips;
IsOPL3 = numchips > 1;
OPLwriteInitState(initopl3);
return numchips;
}
//==========================================================================
//
// DiskWriterIO :: SetClockRate
//
//==========================================================================
void DiskWriterIO::SetClockRate(double samples_per_tick)
{
static_cast<OPLDump *>(chips[0])->SetClockRate(samples_per_tick);
}
//==========================================================================
//
// DiskWriterIO :: WriteDelay
//
//==========================================================================
void DiskWriterIO :: WriteDelay(int ticks)
{
static_cast<OPLDump *>(chips[0])->WriteDelay(ticks);
}

View file

@ -1,285 +0,0 @@
/*
* Name: Main header include file
* Project: MUS File Player Library
* Version: 1.75
* Author: Vladimir Arnost (QA-Software)
* Last revision: Mar-9-1996
* Compiler: Borland C++ 3.1, Watcom C/C++ 10.0
*
*/
/* From muslib175.zip/README.1ST:
1.1 - Disclaimer of Warranties
------------------------------
#ifdef LAWYER
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
#else
Use this software at your own risk.
#endif
1.2 - Terms of Use
------------------
This library may be used in any freeware or shareware product free of
charge. The product may not be sold for profit (except for shareware) and
should be freely available to the public. It would be nice of you if you
credited me in your product and notified me if you use this library.
If you want to use this library in a commercial product, contact me
and we will make an agreement. It is a violation of the law to make money
of this product without prior signing an agreement and paying a license fee.
This licence will allow its holder to sell any products based on MUSLib,
royalty-free. There is no need to buy separate licences for different
products once the licence fee is paid.
1.3 - Contacting the Author
---------------------------
Internet (address valid probably until the end of year 1998):
xarnos00@dcse.fee.vutbr.cz
FIDO:
2:423/36.2
Snail-mail:
Vladimir Arnost
Ceska 921
Chrudim 4
537 01
CZECH REPUBLIC
Voice-mail (Czech language only, not recommended; weekends only):
+42-455-2154
*/
#ifndef __MUSLIB_H_
#define __MUSLIB_H_
#ifndef __DEFTYPES_H_
#include "deftypes.h"
#endif
class FileReader;
/* Global Definitions */
#define MLVERSION 0x0175
#define MLVERSIONSTR "1.75"
extern char MLversion[];
extern char MLcopyright[];
#define CHANNELS 16 // total channels 0..CHANNELS-1
#define PERCUSSION 15 // percussion channel
/* MUS file header structure */
struct MUSheader {
char ID[4]; // identifier "MUS" 0x1A
WORD scoreLen; // score length
WORD scoreStart; // score start
WORD channels; // primary channels
WORD sec_channels; // secondary channels (??)
WORD instrCnt; // used instrument count
WORD dummy;
// WORD instruments[...]; // table of used instruments
};
/* OPL2 instrument */
struct OPL2instrument {
/*00*/ BYTE trem_vibr_1; /* OP 1: tremolo/vibrato/sustain/KSR/multi */
/*01*/ BYTE att_dec_1; /* OP 1: attack rate/decay rate */
/*02*/ BYTE sust_rel_1; /* OP 1: sustain level/release rate */
/*03*/ BYTE wave_1; /* OP 1: waveform select */
/*04*/ BYTE scale_1; /* OP 1: key scale level */
/*05*/ BYTE level_1; /* OP 1: output level */
/*06*/ BYTE feedback; /* feedback/AM-FM (both operators) */
/*07*/ BYTE trem_vibr_2; /* OP 2: tremolo/vibrato/sustain/KSR/multi */
/*08*/ BYTE att_dec_2; /* OP 2: attack rate/decay rate */
/*09*/ BYTE sust_rel_2; /* OP 2: sustain level/release rate */
/*0A*/ BYTE wave_2; /* OP 2: waveform select */
/*0B*/ BYTE scale_2; /* OP 2: key scale level */
/*0C*/ BYTE level_2; /* OP 2: output level */
/*0D*/ BYTE unused;
/*0E*/ sshort basenote; /* base note offset */
};
/* OP2 instrument file entry */
struct OP2instrEntry {
/*00*/ WORD flags; // see FL_xxx below
/*02*/ BYTE finetune; // finetune value for 2-voice sounds
/*03*/ BYTE note; // note # for fixed instruments
/*04*/ struct OPL2instrument instr[2]; // instruments
};
#define FL_FIXED_PITCH 0x0001 // note has fixed pitch (see below)
#define FL_UNKNOWN 0x0002 // ??? (used in instrument #65 only)
#define FL_DOUBLE_VOICE 0x0004 // use two voices instead of one
#define OP2INSTRSIZE sizeof(struct OP2instrEntry) // instrument size (36 bytes)
#define OP2INSTRCOUNT (128 + 81-35+1) // instrument count
/* From MLOPL_IO.CPP */
#define OPL2CHANNELS 9
#define OPL3CHANNELS 18
#define MAXOPL2CHIPS 8
#define MAXCHANNELS (OPL2CHANNELS * MAXOPL2CHIPS)
/* Channel Flags: */
#define CH_SECONDARY 0x01
#define CH_SUSTAIN 0x02
#define CH_VIBRATO 0x04 /* set if modulation >= MOD_MIN */
#define CH_FREE 0x80
struct OPLdata {
uint channelInstr[CHANNELS]; // instrument #
uchar channelVolume[CHANNELS]; // volume
uchar channelLastVolume[CHANNELS]; // last volume
schar channelPan[CHANNELS]; // pan, 0=normal
schar channelPitch[CHANNELS]; // pitch wheel, 64=normal
uchar channelSustain[CHANNELS]; // sustain pedal value
uchar channelModulation[CHANNELS]; // modulation pot value
ushort channelPitchSens[CHANNELS]; // pitch sensitivity, 2=default
ushort channelRPN[CHANNELS]; // RPN number for data entry
uchar channelExpression[CHANNELS]; // expression
};
struct OPLio {
virtual ~OPLio();
void OPLwriteChannel(uint regbase, uint channel, uchar data1, uchar data2);
void OPLwriteValue(uint regbase, uint channel, uchar value);
void OPLwriteFreq(uint channel, uint freq, uint octave, uint keyon);
uint OPLconvertVolume(uint data, uint volume);
uint OPLpanVolume(uint volume, int pan);
void OPLwriteVolume(uint channel, struct OPL2instrument *instr, uint volume);
void OPLwritePan(uint channel, struct OPL2instrument *instr, int pan);
void OPLwriteInstrument(uint channel, struct OPL2instrument *instr);
void OPLshutup(void);
void OPLwriteInitState(bool initopl3);
virtual int OPLinit(uint numchips, bool stereo=false, bool initopl3=false);
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);
class OPLEmul *chips[MAXOPL2CHIPS];
uint OPLchannels;
uint NumChips;
bool IsOPL3;
};
struct DiskWriterIO : public OPLio
{
DiskWriterIO(const char *filename);
~DiskWriterIO();
int OPLinit(uint numchips, bool notused, bool initopl3);
void SetClockRate(double samples_per_tick);
void WriteDelay(int ticks);
FString Filename;
};
struct musicBlock {
musicBlock();
~musicBlock();
BYTE *score;
BYTE *scoredata;
int playingcount;
OPLdata driverdata;
OPLio *io;
struct OP2instrEntry *OPLinstruments;
ulong MLtime;
void OPLplayNote(uint channel, uchar note, int volume);
void OPLreleaseNote(uint channel, uchar note);
void OPLpitchWheel(uint channel, int pitch);
void OPLchangeControl(uint channel, uchar controller, int value);
void OPLprogramChange(uint channel, int value);
void OPLresetControllers(uint channel, int vol);
void OPLplayMusic(int vol);
void OPLstopMusic();
int OPLloadBank (FileReader &data);
protected:
/* OPL channel (voice) data */
struct channelEntry {
uchar channel; /* MUS channel number */
uchar note; /* note number */
uchar flags; /* see CH_xxx below */
uchar realnote; /* adjusted note number */
schar finetune; /* frequency fine-tune */
sint pitch; /* pitch-wheel value */
uint volume; /* note volume */
uint realvolume; /* adjusted note volume */
struct OPL2instrument *instr; /* current instrument */
ulong time; /* note start time */
} channels[MAXCHANNELS];
void writeFrequency(uint slot, uint note, int pitch, uint keyOn);
void writeModulation(uint slot, struct OPL2instrument *instr, int state);
uint calcVolume(uint channelVolume, uint channelExpression, uint noteVolume);
int occupyChannel(uint slot, uint channel,
int note, int volume, struct OP2instrEntry *instrument, uchar secondary);
int releaseChannel(uint slot, uint killed);
int releaseSustain(uint channel);
int findFreeChannel(uint flag, uint channel, uchar note);
struct OP2instrEntry *getInstrument(uint channel, uchar note);
friend class Stat_opl;
};
enum MUSctrl {
ctrlPatch = 0,
ctrlBank,
ctrlModulation,
ctrlVolume,
ctrlPan,
ctrlExpression,
ctrlReverb,
ctrlChorus,
ctrlSustainPedal,
ctrlSoftPedal,
ctrlRPNHi,
ctrlRPNLo,
ctrlNRPNHi,
ctrlNRPNLo,
ctrlDataEntryHi,
ctrlDataEntryLo,
ctrlSoundsOff,
ctrlNotesOff,
ctrlMono,
ctrlPoly,
};
#define ADLIB_CLOCK_MUL 24.0
#endif // __MUSLIB_H_

File diff suppressed because it is too large Load diff

View file

@ -1,235 +0,0 @@
/*
* Copyright (C) 2013-2015 Nuke.YKT(Alexey Khokholov)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
Nuked Yamaha YMF262(aka OPL3) emulator.
Thanks:
MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh):
Feedback and Rhythm part calculation information.
forums.submarine.org.uk(carbon14, opl3):
Tremolo and phase generator calculation information.
OPLx decapsulated(Matthew Gambrell, Olli Niemitalo):
OPL2 ROMs.
*/
//version 1.6
#include "opl.h"
#include "muslib.h"
typedef uintptr_t Bitu;
typedef intptr_t Bits;
typedef DWORD Bit32u;
typedef SDWORD Bit32s;
typedef WORD Bit16u;
typedef SWORD Bit16s;
typedef BYTE Bit8u;
typedef SBYTE Bit8s;
// Channel types
enum {
ch_2op = 0,
ch_4op = 1,
ch_4op2 = 2,
ch_drum = 3
};
// Envelope key types
enum {
egk_norm = 0x01,
egk_drum = 0x02
};
//
// logsin table
//
static const Bit16u logsinrom[256] = {
0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365,
0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261,
0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd,
0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166,
0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118,
0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db,
0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9,
0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081,
0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060,
0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045,
0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f,
0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e,
0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011,
0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007,
0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002,
0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000
};
//
// exp table
//
static const Bit16u exprom[256] = {
0x000, 0x003, 0x006, 0x008, 0x00b, 0x00e, 0x011, 0x014, 0x016, 0x019, 0x01c, 0x01f, 0x022, 0x025, 0x028, 0x02a,
0x02d, 0x030, 0x033, 0x036, 0x039, 0x03c, 0x03f, 0x042, 0x045, 0x048, 0x04b, 0x04e, 0x051, 0x054, 0x057, 0x05a,
0x05d, 0x060, 0x063, 0x066, 0x069, 0x06c, 0x06f, 0x072, 0x075, 0x078, 0x07b, 0x07e, 0x082, 0x085, 0x088, 0x08b,
0x08e, 0x091, 0x094, 0x098, 0x09b, 0x09e, 0x0a1, 0x0a4, 0x0a8, 0x0ab, 0x0ae, 0x0b1, 0x0b5, 0x0b8, 0x0bb, 0x0be,
0x0c2, 0x0c5, 0x0c8, 0x0cc, 0x0cf, 0x0d2, 0x0d6, 0x0d9, 0x0dc, 0x0e0, 0x0e3, 0x0e7, 0x0ea, 0x0ed, 0x0f1, 0x0f4,
0x0f8, 0x0fb, 0x0ff, 0x102, 0x106, 0x109, 0x10c, 0x110, 0x114, 0x117, 0x11b, 0x11e, 0x122, 0x125, 0x129, 0x12c,
0x130, 0x134, 0x137, 0x13b, 0x13e, 0x142, 0x146, 0x149, 0x14d, 0x151, 0x154, 0x158, 0x15c, 0x160, 0x163, 0x167,
0x16b, 0x16f, 0x172, 0x176, 0x17a, 0x17e, 0x181, 0x185, 0x189, 0x18d, 0x191, 0x195, 0x199, 0x19c, 0x1a0, 0x1a4,
0x1a8, 0x1ac, 0x1b0, 0x1b4, 0x1b8, 0x1bc, 0x1c0, 0x1c4, 0x1c8, 0x1cc, 0x1d0, 0x1d4, 0x1d8, 0x1dc, 0x1e0, 0x1e4,
0x1e8, 0x1ec, 0x1f0, 0x1f5, 0x1f9, 0x1fd, 0x201, 0x205, 0x209, 0x20e, 0x212, 0x216, 0x21a, 0x21e, 0x223, 0x227,
0x22b, 0x230, 0x234, 0x238, 0x23c, 0x241, 0x245, 0x249, 0x24e, 0x252, 0x257, 0x25b, 0x25f, 0x264, 0x268, 0x26d,
0x271, 0x276, 0x27a, 0x27f, 0x283, 0x288, 0x28c, 0x291, 0x295, 0x29a, 0x29e, 0x2a3, 0x2a8, 0x2ac, 0x2b1, 0x2b5,
0x2ba, 0x2bf, 0x2c4, 0x2c8, 0x2cd, 0x2d2, 0x2d6, 0x2db, 0x2e0, 0x2e5, 0x2e9, 0x2ee, 0x2f3, 0x2f8, 0x2fd, 0x302,
0x306, 0x30b, 0x310, 0x315, 0x31a, 0x31f, 0x324, 0x329, 0x32e, 0x333, 0x338, 0x33d, 0x342, 0x347, 0x34c, 0x351,
0x356, 0x35b, 0x360, 0x365, 0x36a, 0x370, 0x375, 0x37a, 0x37f, 0x384, 0x38a, 0x38f, 0x394, 0x399, 0x39f, 0x3a4,
0x3a9, 0x3ae, 0x3b4, 0x3b9, 0x3bf, 0x3c4, 0x3c9, 0x3cf, 0x3d4, 0x3da, 0x3df, 0x3e4, 0x3ea, 0x3ef, 0x3f5, 0x3fa
};
//
// freq mult table multiplied by 2
//
// 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15
//
static const Bit8u mt[16] = { 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 };
//
// ksl table
//
static const Bit8u kslrom[16] = { 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64 };
static const Bit8u kslshift[4] = { 8, 1, 2, 0 };
//
// LFO vibrato
//
static const Bit8u vib_table[8] = { 3, 1, 0, 1, 3, 1, 0, 1 };
static const Bit8s vibsgn_table[8] = { 1, 1, 1, 1, -1, -1, -1, -1 };
//
// envelope generator constants
//
static const Bit8u eg_incstep[3][4][8] = {
{ { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 } },
{ { 0, 1, 0, 1, 0, 1, 0, 1 }, { 0, 1, 0, 1, 1, 1, 0, 1 }, { 0, 1, 1, 1, 0, 1, 1, 1 }, { 0, 1, 1, 1, 1, 1, 1, 1 } },
{ { 1, 1, 1, 1, 1, 1, 1, 1 }, { 2, 2, 1, 1, 1, 1, 1, 1 }, { 2, 2, 1, 1, 2, 2, 1, 1 }, { 2, 2, 2, 2, 2, 2, 1, 1 } }
};
static const Bit8u eg_incdesc[16] = {
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2
};
static const Bit8s eg_incsh[16] = {
0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, -1, -2
};
//
// address decoding
//
static const Bit8s ad_slot[0x20] = { 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
static const Bit8u ch_slot[18] = { 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 };
struct opl_chip;
struct opl_slot;
struct opl_channel;
struct opl_slot {
opl_channel *channel;
opl_chip *chip;
Bit16s out;
Bit16s fbmod;
Bit16s *mod;
Bit16s prout[2];
Bit16s eg_rout;
Bit16s eg_out;
Bit8u eg_inc;
Bit8u eg_gen;
Bit8u eg_rate;
Bit8u eg_ksl;
Bit8u *trem;
Bit8u reg_vib;
Bit8u reg_type;
Bit8u reg_ksr;
Bit8u reg_mult;
Bit8u reg_ksl;
Bit8u reg_tl;
Bit8u reg_ar;
Bit8u reg_dr;
Bit8u reg_sl;
Bit8u reg_rr;
Bit8u reg_wf;
Bit8u key;
Bit32u pg_phase;
};
struct opl_channel {
opl_slot *slots[2];
opl_channel *pair;
opl_chip *chip;
Bit16s *out[4];
Bit8u chtype;
Bit16u f_num;
Bit8u block;
Bit8u fb;
Bit8u con;
Bit8u alg;
Bit8u ksv;
Bit16u cha, chb;
float fcha, fchb;
};
struct opl_chip {
opl_channel channel[18];
opl_slot slot[36];
Bit16u timer;
Bit8u newm;
Bit8u nts;
Bit8u dvb;
Bit8u dam;
Bit8u rhy;
Bit8u vibpos;
Bit8u tremval;
Bit8u tremtval;
Bit8u tremdir;
Bit32u noise;
Bit16s zeromod;
Bit32s mixbuff[2];
Bit8u FullPan;
};
class NukedOPL3 : public OPLEmul {
private:
opl_chip opl3;
bool FullPan;
public:
void Reset();
void Update(float* sndptr, int numsamples);
void WriteReg(int reg, int v);
void SetPanning(int c, float left, float right);
NukedOPL3(bool stereo);
};

View file

@ -1,29 +0,0 @@
#ifndef OPL_H
#define OPL_H
#include "zstring.h"
// Abstract base class for OPL emulators
class OPLEmul
{
public:
OPLEmul() {}
virtual ~OPLEmul() {}
virtual void Reset() = 0;
virtual void WriteReg(int reg, int v) = 0;
virtual void Update(float *buffer, int length) = 0;
virtual void SetPanning(int c, float left, float right) = 0;
};
OPLEmul *YM3812Create(bool stereo);
OPLEmul *DBOPLCreate(bool stereo);
OPLEmul *JavaOPLCreate(bool stereo);
OPLEmul *NukedOPL3Create(bool stereo);
#define OPL_SAMPLE_RATE 49716.0
#define CENTER_PANNING_POWER 0.70710678118 /* [RH] volume at center for EQP */
#endif

View file

@ -1,541 +0,0 @@
#ifdef _WIN32
#include <io.h>
#endif
#include <string.h>
#include <assert.h>
#include <math.h>
#include "opl_mus_player.h"
#include "doomtype.h"
#include "opl.h"
#include "w_wad.h"
#include "templates.h"
#include "c_cvars.h"
#include "i_system.h"
#include "stats.h"
#define IMF_RATE 700.0
EXTERN_CVAR (Int, opl_numchips)
OPLmusicBlock::OPLmusicBlock()
{
scoredata = NULL;
NextTickIn = 0;
LastOffset = 0;
NumChips = MIN(*opl_numchips, 2);
Looping = false;
FullPan = false;
io = NULL;
io = new OPLio;
}
OPLmusicBlock::~OPLmusicBlock()
{
delete io;
}
void OPLmusicBlock::ResetChips ()
{
ChipAccess.Enter();
io->OPLdeinit ();
NumChips = io->OPLinit(MIN(*opl_numchips, 2), FullPan);
ChipAccess.Leave();
}
void OPLmusicBlock::Restart()
{
OPLstopMusic ();
OPLplayMusic (127);
MLtime = 0;
playingcount = 0;
LastOffset = 0;
}
OPLmusicFile::OPLmusicFile (FileReader *reader)
: ScoreLen (reader->GetLength())
{
if (io == NULL)
{
return;
}
scoredata = new BYTE[ScoreLen];
if (reader->Read(scoredata, ScoreLen) != ScoreLen)
{
fail: delete[] scoredata;
scoredata = NULL;
return;
}
if (0 == (NumChips = io->OPLinit(NumChips)))
{
goto fail;
}
// Check for RDosPlay raw OPL format
if (((DWORD *)scoredata)[0] == MAKE_ID('R','A','W','A') &&
((DWORD *)scoredata)[1] == MAKE_ID('D','A','T','A'))
{
RawPlayer = RDosPlay;
if (*(WORD *)(scoredata + 8) == 0)
{ // A clock speed of 0 is bad
*(WORD *)(scoredata + 8) = 0xFFFF;
}
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'))
{
if (LittleShort(((WORD *)scoredata)[5]) == 1)
{
RawPlayer = DosBox1;
SamplesPerTick = OPL_SAMPLE_RATE / 1000;
ScoreLen = MIN<int>(ScoreLen - 24, LittleLong(((DWORD *)scoredata)[4])) + 24;
}
else if (((DWORD *)scoredata)[2] == MAKE_ID(2,0,0,0))
{
bool okay = true;
if (scoredata[21] != 0)
{
Printf("Unsupported DOSBox Raw OPL format %d\n", scoredata[20]);
okay = false;
}
if (scoredata[22] != 0)
{
Printf("Unsupported DOSBox Raw OPL compression %d\n", scoredata[21]);
okay = false;
}
if (!okay)
goto fail;
RawPlayer = DosBox2;
SamplesPerTick = OPL_SAMPLE_RATE / 1000;
int headersize = 0x1A + scoredata[0x19];
ScoreLen = MIN<int>(ScoreLen - headersize, LittleLong(((DWORD *)scoredata)[3]) * 2) + headersize;
}
else
{
Printf("Unsupported DOSBox Raw OPL version %d.%d\n", LittleShort(((WORD *)scoredata)[4]), LittleShort(((WORD *)scoredata)[5]));
goto fail;
}
}
// Check for modified IMF format (includes a header)
else if (((DWORD *)scoredata)[0] == MAKE_ID('A','D','L','I') &&
scoredata[4] == 'B' && scoredata[5] == 1)
{
int songlen;
BYTE *max = scoredata + ScoreLen;
RawPlayer = IMF;
SamplesPerTick = OPL_SAMPLE_RATE / IMF_RATE;
score = scoredata + 6;
// Skip track and game name
for (int i = 2; i != 0; --i)
{
while (score < max && *score++ != '\0') {}
}
if (score < max) score++; // Skip unknown byte
if (score + 8 > max)
{ // Not enough room left for song data
delete[] scoredata;
scoredata = NULL;
return;
}
songlen = LittleLong(*(DWORD *)score);
if (songlen != 0 && (songlen +=4) < ScoreLen - (score - scoredata))
{
ScoreLen = songlen + int(score - scoredata);
}
}
else
{
goto fail;
}
Restart ();
}
OPLmusicFile::~OPLmusicFile ()
{
if (scoredata != NULL)
{
io->OPLdeinit ();
delete[] scoredata;
scoredata = NULL;
}
}
bool OPLmusicFile::IsValid () const
{
return scoredata != NULL;
}
void OPLmusicFile::SetLooping (bool loop)
{
Looping = loop;
}
void OPLmusicFile::Restart ()
{
OPLmusicBlock::Restart();
WhichChip = 0;
switch (RawPlayer)
{
case RDosPlay:
score = scoredata + 10;
SamplesPerTick = LittleShort(*(WORD *)(scoredata + 8)) / ADLIB_CLOCK_MUL;
break;
case DosBox1:
score = scoredata + 24;
SamplesPerTick = OPL_SAMPLE_RATE / 1000;
break;
case DosBox2:
score = scoredata + 0x1A + scoredata[0x19];
SamplesPerTick = OPL_SAMPLE_RATE / 1000;
break;
case IMF:
score = scoredata + 6;
// Skip track and game name
for (int i = 2; i != 0; --i)
{
while (*score++ != '\0') {}
}
score++; // Skip unknown byte
if (*(DWORD *)score != 0)
{
score += 4; // Skip song length
}
break;
}
io->SetClockRate(SamplesPerTick);
}
bool OPLmusicBlock::ServiceStream (void *buff, int numbytes)
{
float *samples1 = (float *)buff;
int stereoshift = (int)(FullPan | io->IsOPL3);
int numsamples = numbytes / (sizeof(float) << stereoshift);
bool prevEnded = false;
bool res = true;
memset(buff, 0, numbytes);
ChipAccess.Enter();
while (numsamples > 0)
{
double ticky = NextTickIn;
int tick_in = int(NextTickIn);
int samplesleft = MIN(numsamples, tick_in);
size_t i;
if (samplesleft > 0)
{
for (i = 0; i < io->NumChips; ++i)
{
io->chips[i]->Update(samples1, samplesleft);
}
OffsetSamples(samples1, samplesleft << stereoshift);
assert(NextTickIn == ticky);
NextTickIn -= samplesleft;
assert (NextTickIn >= 0);
numsamples -= samplesleft;
samples1 += samplesleft << stereoshift;
}
if (NextTickIn < 1)
{
int next = PlayTick();
assert(next >= 0);
if (next == 0)
{ // end of song
if (!Looping || prevEnded)
{
if (numsamples > 0)
{
for (i = 0; i < io->NumChips; ++i)
{
io->chips[i]->Update(samples1, numsamples);
}
OffsetSamples(samples1, numsamples << stereoshift);
}
res = false;
break;
}
else
{
// Avoid infinite loops from songs that do nothing but end
prevEnded = true;
Restart ();
}
}
else
{
prevEnded = false;
io->WriteDelay(next);
NextTickIn += SamplesPerTick * next;
assert (NextTickIn >= 0);
MLtime += next;
}
}
}
ChipAccess.Leave();
return res;
}
void OPLmusicBlock::OffsetSamples(float *buff, int count)
{
// Three out of four of the OPL waveforms are non-negative. Depending on
// timbre selection, this can cause the output waveform to tend toward
// very large positive values. Heretic's music is particularly bad for
// this. This function attempts to compensate by offseting the sample
// data back to around the [-1.0, 1.0] range.
double max = -1e10, min = 1e10, offset, step;
int i, ramp, largest_at = 0;
// Find max and min values for this segment of the waveform.
for (i = 0; i < count; ++i)
{
if (buff[i] > max)
{
max = buff[i];
largest_at = i;
}
if (buff[i] < min)
{
min = buff[i];
largest_at = i;
}
}
// Prefer to keep the offset at 0, even if it means a little clipping.
if (LastOffset == 0 && min >= -1.1 && max <= 1.1)
{
offset = 0;
}
else
{
offset = (max + min) / 2;
// If the new offset is close to 0, make it 0 to avoid making another
// full loop through the sample data.
if (fabs(offset) < 1/256.0)
{
offset = 0;
}
}
// Ramp the offset change so there aren't any abrupt clicks in the output.
// If the ramp is too short, it can sound scratchy. cblood2.mid is
// particularly unforgiving of short ramps.
if (count >= 512)
{
ramp = 512;
step = (offset - LastOffset) / 512;
}
else
{
ramp = MIN(count, MAX(196, largest_at));
step = (offset - LastOffset) / ramp;
}
offset = LastOffset;
i = 0;
if (step != 0)
{
for (; i < ramp; ++i)
{
buff[i] = float(buff[i] - offset);
offset += step;
}
}
if (offset != 0)
{
for (; i < count; ++i)
{
buff[i] = float(buff[i] - offset);
}
}
LastOffset = float(offset);
}
int OPLmusicFile::PlayTick ()
{
BYTE reg, data;
WORD delay;
switch (RawPlayer)
{
case RDosPlay:
while (score < scoredata + ScoreLen)
{
data = *score++;
reg = *score++;
switch (reg)
{
case 0: // Delay
if (data != 0)
{
return data;
}
break;
case 2: // Speed change or OPL3 switch
if (data == 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
if (data == 0xFF)
{
return 0;
}
break;
default: // It's something to stuff into the OPL chip
io->OPLwriteReg(WhichChip, reg, data);
break;
}
}
break;
case DosBox1:
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++;
}
io->OPLwriteReg(WhichChip, reg, data);
}
break;
case DosBox2:
{
BYTE *to_reg = scoredata + 0x1A;
BYTE to_reg_size = scoredata[0x19];
BYTE short_delay_code = scoredata[0x17];
BYTE long_delay_code = scoredata[0x18];
while (score < scoredata + ScoreLen)
{
BYTE code = *score++;
data = *score++;
// Which OPL chip to write to is encoded in the high bit of the code value.
int which = !!(code & 0x80);
code &= 0x7F;
if (code == short_delay_code)
{
return data + 1;
}
else if (code == long_delay_code)
{
return (data + 1) << 8;
}
else if (code < to_reg_size)
{
io->OPLwriteReg(which, to_reg[code], data);
}
}
}
break;
case IMF:
delay = 0;
while (delay == 0 && score + 4 - scoredata <= ScoreLen)
{
if (*(DWORD *)score == 0xFFFFFFFF)
{ // This is a special value that means to end the song.
return 0;
}
reg = score[0];
data = score[1];
delay = LittleShort(((WORD *)score)[1]);
score += 4;
io->OPLwriteReg (0, reg, data);
}
return delay;
}
return 0;
}
/*
ADD_STAT (opl)
{
return YM3812GetVoiceString ();
}
*/
OPLmusicFile::OPLmusicFile(const OPLmusicFile *source, const char *filename)
{
ScoreLen = source->ScoreLen;
scoredata = new BYTE[ScoreLen];
memcpy(scoredata, source->scoredata, ScoreLen);
SamplesPerTick = source->SamplesPerTick;
RawPlayer = source->RawPlayer;
score = source->score;
NumChips = source->NumChips;
WhichChip = 0;
if (io != NULL)
{
delete io;
}
io = new DiskWriterIO(filename);
NumChips = io->OPLinit(NumChips);
Restart();
}
void OPLmusicFile::Dump()
{
int time;
time = PlayTick();
while (time != 0)
{
io->WriteDelay(time);
time = PlayTick();
}
}

View file

@ -1,48 +0,0 @@
#include "critsec.h"
#include "muslib.h"
class OPLmusicBlock : public musicBlock
{
public:
OPLmusicBlock();
virtual ~OPLmusicBlock();
bool ServiceStream(void *buff, int numbytes);
void ResetChips();
virtual void Restart();
protected:
virtual int PlayTick() = 0;
void OffsetSamples(float *buff, int count);
double NextTickIn;
double SamplesPerTick;
int NumChips;
bool Looping;
double LastOffset;
bool FullPan;
FCriticalSection ChipAccess;
};
class OPLmusicFile : public OPLmusicBlock
{
public:
OPLmusicFile(FileReader *reader);
OPLmusicFile(const OPLmusicFile *source, const char *filename);
virtual ~OPLmusicFile();
bool IsValid() const;
void SetLooping(bool loop);
void Restart();
void Dump();
protected:
OPLmusicFile() {}
int PlayTick();
enum { RDosPlay, IMF, DosBox1, DosBox2 } RawPlayer;
int ScoreLen;
int WhichChip;
};

View file

@ -389,7 +389,7 @@ enum EMidiDevice
{
MDEV_DEFAULT = -1,
MDEV_MMAPI = 0,
MDEV_OPL = 1,
MDEV_OPL = 1, // unused
MDEV_SNDSYS = 2,
MDEV_TIMIDITY = 3,
MDEV_FLUIDSYNTH = 4,

View file

@ -295,11 +295,6 @@ FString MusInfo::GetStats()
return "No stats available for this song";
}
MusInfo *MusInfo::GetOPLDumper(const char *filename)
{
return NULL;
}
MusInfo *MusInfo::GetWaveDumper(const char *filename, int rate)
{
return NULL;
@ -474,7 +469,9 @@ retry_as_sndsys:
(id[0] == MAKE_ID('D','B','R','A') && id[1] == MAKE_ID('W','O','P','L')) || // DosBox Raw OPL
(id[0] == MAKE_ID('A','D','L','I') && *((BYTE *)id + 4) == 'B')) // Martin Fernandez's modified IMF
{
info = new OPLMUSSong (*reader, device != NULL? device->args.GetChars() : "");
//These are no longer supported.
delete reader;
return 0;
}
// Check for game music
else if ((fmt = GME_CheckFormat(id[0])) != NULL && fmt[0] != '\0')
@ -707,43 +704,6 @@ 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, 0); // FIXME: Remember subsong.
delete dumper;
}
}
}
else
{
Printf ("Usage: writeopl <filename>\n");
}
}
//==========================================================================
//
// CCMD writewave

View file

@ -78,7 +78,6 @@ public:
virtual bool SetSubsong (int subsong);
virtual void Update();
virtual FString GetStats();
virtual MusInfo *GetOPLDumper(const char *filename);
virtual MusInfo *GetWaveDumper(const char *filename, int rate);
virtual void FluidSettingInt(const char *setting, int value); // FluidSynth settings
virtual void FluidSettingNum(const char *setting, double value); // "

View file

@ -17,7 +17,7 @@
#define TRUE 1
#endif
#include "tempfiles.h"
#include "oplsynth/opl_mus_player.h"
#include "critsec.h"
#include "c_cvars.h"
#include "mus2midi.h"
#include "i_sound.h"
@ -265,37 +265,6 @@ protected:
virtual void ComputeOutput(float *buffer, int len) = 0;
};
// OPL implementation of a MIDI output device -------------------------------
class OPLMIDIDevice : public SoftSynthMIDIDevice, protected OPLmusicBlock
{
public:
OPLMIDIDevice(const char *args);
int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata);
void Close();
int GetTechnology() const;
FString GetStats();
protected:
void CalcTickRate();
int PlayTick();
void HandleEvent(int status, int parm1, int parm2);
void HandleLongEvent(const BYTE *data, int len);
void ComputeOutput(float *buffer, int len);
bool ServiceStream(void *buff, int numbytes);
};
// OPL dumper implementation of a MIDI output device ------------------------
class OPLDumperMIDIDevice : public OPLMIDIDevice
{
public:
OPLDumperMIDIDevice(const char *filename);
~OPLDumperMIDIDevice();
int Resume();
void Stop();
};
// Internal TiMidity MIDI device --------------------------------------------
namespace Timidity { struct Renderer; }
@ -528,7 +497,6 @@ public:
MUSSong2(FileReader &reader, EMidiDevice type, const char *args);
~MUSSong2();
MusInfo *GetOPLDumper(const char *filename);
MusInfo *GetWaveDumper(const char *filename, int rate);
protected:
@ -554,7 +522,6 @@ public:
MIDISong2(FileReader &reader, EMidiDevice type, const char *args);
~MIDISong2();
MusInfo *GetOPLDumper(const char *filename);
MusInfo *GetWaveDumper(const char *filename, int rate);
protected:
@ -611,7 +578,6 @@ public:
HMISong(FileReader &reader, EMidiDevice type, const char *args);
~HMISong();
MusInfo *GetOPLDumper(const char *filename);
MusInfo *GetWaveDumper(const char *filename, int rate);
protected:
@ -654,7 +620,6 @@ public:
XMISong(FileReader &reader, EMidiDevice type, const char *args);
~XMISong();
MusInfo *GetOPLDumper(const char *filename);
MusInfo *GetWaveDumper(const char *filename, int rate);
protected:
@ -709,34 +674,6 @@ protected:
SoundStream *m_Stream;
};
// MUS file played by a software OPL2 synth and streamed through the sound system
class OPLMUSSong : public StreamSong
{
public:
OPLMUSSong (FileReader &reader, const char *args);
~OPLMUSSong ();
void Play (bool looping, int subsong);
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, int);
};
// CD track/disk played through the multimedia system -----------------------
class CDSong : public MusInfo

View file

@ -382,11 +382,7 @@ void HMISong::SetupForHMP(int len)
void HMISong::CheckCaps(int tech)
{
// What's the equivalent HMI device for our technology?
if (tech == MOD_FMSYNTH)
{
tech = HMI_DEV_OPL3;
}
else if (tech == MOD_MIDIPORT)
if (tech == MOD_MIDIPORT)
{
tech = HMI_DEV_MPU401;
}
@ -1020,17 +1016,6 @@ HMISong::TrackInfo *HMISong::FindNextDue ()
}
//==========================================================================
//
// HMISong :: GetOPLDumper
//
//==========================================================================
MusInfo *HMISong::GetOPLDumper(const char *filename)
{
return new HMISong(this, filename, MDEV_OPL);
}
//==========================================================================
//
// HMISong :: GetWaveDumper

View file

@ -29,7 +29,7 @@ static void AddDefaultMidiDevices(FOptionValues *opt)
#endif
pair[p].Text = "GUS";
pair[p].Value = -4.0;
pair[p+1].Text = "OPL Synth Emulation";
pair[p+1].Text = "OPL [DISABLED]";
pair[p+1].Value = -3.0;
pair[p+2].Text = "TiMidity++";
pair[p+2].Value = -2.0;
@ -67,7 +67,8 @@ static void MIDIDeviceChanged(int newdev)
#ifdef _WIN32
UINT mididevice;
CUSTOM_CVAR (Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
// default to device 0 on Windows
CUSTOM_CVAR (Int, snd_mididevice, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
{
if (!nummididevicesset)
return;
@ -173,7 +174,7 @@ CCMD (snd_listmididevices)
PrintMidiDevice (-5, "FluidSynth", MOD_SWSYNTH, 0);
#endif
PrintMidiDevice (-4, "Gravis Ultrasound Emulation", MOD_SWSYNTH, 0);
PrintMidiDevice (-3, "Emulated OPL FM Synth", MOD_FMSYNTH, 0);
PrintMidiDevice (-3, "OPL [DISABLED]", MOD_FMSYNTH, 0);
PrintMidiDevice (-2, "TiMidity++", MOD_SWSYNTH, 0);
PrintMidiDevice (-1, "Sound System", 0, 0);
if (nummididevices != 0)
@ -219,7 +220,7 @@ CCMD (snd_listmididevices)
Printf("%s-5. FluidSynth\n", -5 == snd_mididevice ? TEXTCOLOR_BOLD : "");
#endif
Printf("%s-4. Gravis Ultrasound Emulation\n", -4 == snd_mididevice ? TEXTCOLOR_BOLD : "");
Printf("%s-3. Emulated OPL FM Synth\n", -3 == snd_mididevice ? TEXTCOLOR_BOLD : "");
Printf("%s-3. OPL [DISABLED]\n", -3 == snd_mididevice ? TEXTCOLOR_BOLD : "");
Printf("%s-2. TiMidity++\n", -2 == snd_mididevice ? TEXTCOLOR_BOLD : "");
Printf("%s-1. Sound System\n", -1 == snd_mididevice ? TEXTCOLOR_BOLD : "");
}

View file

@ -207,10 +207,6 @@ void MIDIStreamer::CheckCaps(int tech)
EMidiDevice MIDIStreamer::SelectMIDIDevice(EMidiDevice device)
{
/* MIDI are played as:
- OPL:
- if explicitly selected by $mididevice
- when snd_mididevice is -3 and no midi device is set for the song
- Timidity:
- if explicitly selected by $mididevice
- when snd_mididevice is -2 and no midi device is set for the song
@ -218,12 +214,12 @@ EMidiDevice MIDIStreamer::SelectMIDIDevice(EMidiDevice device)
- Sound System:
- if explicitly selected by $mididevice
- when snd_mididevice is -1 and no midi device is set for the song
- as fallback when both OPL and Timidity failed unless snd_mididevice is >= 0
- as fallback when Timidity has failed unless snd_mididevice is >= 0
- MMAPI (Win32 only):
- if explicitly selected by $mididevice (non-Win32 redirects this to Sound System)
- 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
- as fallback when Timidity has failed and snd_mididevice is >= 0
*/
// Choose the type of MIDI device we want.
@ -278,18 +274,6 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const
case MDEV_GUS:
return new TimidityMIDIDevice(Args);
case MDEV_OPL:
try
{
return new OPLMIDIDevice(Args);
}
catch (CRecoverableError &err)
{
// The creation of an OPL MIDI device can abort with an error if no GENMIDI lump can be found.
Printf("Unable to create OPL MIDI device: %s\nFalling back to Sound System playback", err.GetMessage());
return new SndSysMIDIDevice;
}
case MDEV_TIMIDITY:
return new TimidityPPMIDIDevice(Args);
@ -323,11 +307,7 @@ void MIDIStreamer::Play(bool looping, int subsong)
devtype = SelectMIDIDevice(DeviceType);
if (DumpFilename.IsNotEmpty())
{
if (devtype == MDEV_OPL)
{
MIDI = new OPLDumperMIDIDevice(DumpFilename);
}
else if (devtype == MDEV_GUS)
if (devtype == MDEV_GUS)
{
MIDI = new TimidityWaveWriterMIDIDevice(DumpFilename, 0);
}

View file

@ -376,17 +376,6 @@ end:
return events;
}
//==========================================================================
//
// MUSSong2 :: GetOPLDumper
//
//==========================================================================
MusInfo *MUSSong2::GetOPLDumper(const char *filename)
{
return new MUSSong2(this, filename, MDEV_OPL);
}
//==========================================================================
//
// MUSSong2 :: GetWaveDumper

View file

@ -1,115 +0,0 @@
#include "i_musicinterns.h"
#include "oplsynth/muslib.h"
#include "oplsynth/opl.h"
static bool OPL_Active;
CUSTOM_CVAR (Int, opl_numchips, 2, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
{
if (*self <= 0)
{
self = 1;
}
else if (*self > MAXOPL2CHIPS)
{
self = MAXOPL2CHIPS;
}
else if (OPL_Active && currSong != NULL)
{
static_cast<OPLMUSSong *>(currSong)->ResetChips ();
}
}
CVAR(Int, opl_core, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
int current_opl_core;
// Get OPL core override from $mididevice
void OPL_SetCore(const char *args)
{
current_opl_core = opl_core;
if (args != NULL && *args >= '0' && *args < '4') current_opl_core = *args - '0';
}
OPLMUSSong::OPLMUSSong (FileReader &reader, const char *args)
{
int samples = int(OPL_SAMPLE_RATE / 14);
OPL_SetCore(args);
Music = new OPLmusicFile (&reader);
m_Stream = GSnd->CreateStream (FillStream, samples*4,
(current_opl_core == 0 ? SoundStream::Mono : 0) | SoundStream::Float, int(OPL_SAMPLE_RATE), this);
if (m_Stream == NULL)
{
Printf (PRINT_BOLD, "Could not create music stream.\n");
delete Music;
return;
}
OPL_Active = true;
}
OPLMUSSong::~OPLMUSSong ()
{
OPL_Active = false;
Stop ();
if (Music != NULL)
{
delete Music;
}
}
bool OPLMUSSong::IsValid () const
{
return m_Stream != NULL;
}
void OPLMUSSong::ResetChips ()
{
Music->ResetChips ();
}
bool OPLMUSSong::IsPlaying ()
{
return m_Status == STATE_Playing;
}
void OPLMUSSong::Play (bool looping, int subsong)
{
m_Status = STATE_Stopped;
m_Looping = looping;
Music->SetLooping (looping);
Music->Restart ();
if (m_Stream == NULL || m_Stream->Play (true, snd_musicvolume))
{
m_Status = STATE_Playing;
}
}
bool OPLMUSSong::FillStream (SoundStream *stream, void *buff, int len, void *userdata)
{
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, int)
{
Music->Dump();
}

View file

@ -813,17 +813,6 @@ MIDISong2::TrackInfo *MIDISong2::FindNextDue ()
}
//==========================================================================
//
// MIDISong2 :: GetOPLDumper
//
//==========================================================================
MusInfo *MIDISong2::GetOPLDumper(const char *filename)
{
return new MIDISong2(this, filename, MDEV_OPL);
}
//==========================================================================
//
// MIDISong2 :: GetWaveDumper

View file

@ -704,17 +704,6 @@ XMISong::EventSource XMISong::FindNextDue()
}
//==========================================================================
//
// XMISong :: GetOPLDumper
//
//==========================================================================
MusInfo *XMISong::GetOPLDumper(const char *filename)
{
return new XMISong(this, filename, MDEV_OPL);
}
//==========================================================================
//
// XMISong :: GetWaveDumper

View file

@ -1600,11 +1600,6 @@ OptionMenu AdvSoundOptions
Title "$ADVSNDMNU_TITLE"
Option "$ADVSNDMNU_SAMPLERATE", "snd_samplerate", "SampleRates"
StaticText " "
StaticText "$ADVSNDMNU_OPLSYNTHESIS", 1
Slider "$ADVSNDMNU_OPLNUMCHIPS", "opl_numchips", 1, 8, 1, 0
Option "$ADVSNDMNU_OPLFULLPAN", "opl_fullpan", "OnOff"
Option "$ADVSNDMNU_OPLCORES", "opl_core", "OplCores"
StaticText " "
StaticText "$ADVSNDMNU_GUSEMULATION", 1
TextField "$ADVSNDMNU_GUSCONFIG", "midi_config"
Slider "$ADVSNDMNU_MIDIVOICES", "midi_voices", 16, 256, 4, 0