mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2025-02-20 18:32:06 +00:00
Remove all OPL synths from the project.
This commit is contained in:
parent
e74517ff37
commit
4b5c73b675
28 changed files with 14 additions and 9359 deletions
|
@ -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
|
@ -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
|
@ -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
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
@ -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);
|
||||
};
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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); // "
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 : "");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -376,17 +376,6 @@ end:
|
|||
return events;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// MUSSong2 :: GetOPLDumper
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
MusInfo *MUSSong2::GetOPLDumper(const char *filename)
|
||||
{
|
||||
return new MUSSong2(this, filename, MDEV_OPL);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// MUSSong2 :: GetWaveDumper
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue