mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2025-02-22 11:11:25 +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
|
g_strife/*.h
|
||||||
intermission/*.h
|
intermission/*.h
|
||||||
menu/*.h
|
menu/*.h
|
||||||
oplsynth/*.h
|
|
||||||
oplsynth/dosbox/*.h
|
|
||||||
posix/*.h
|
posix/*.h
|
||||||
posix/cocoa/*.h
|
posix/cocoa/*.h
|
||||||
posix/sdl/*.h
|
posix/sdl/*.h
|
||||||
|
@ -691,12 +689,6 @@ set( FASTMATH_PCH_SOURCES
|
||||||
menu/playermenu.cpp
|
menu/playermenu.cpp
|
||||||
menu/readthis.cpp
|
menu/readthis.cpp
|
||||||
menu/videomenu.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/common.cpp
|
||||||
timidity/instrum.cpp
|
timidity/instrum.cpp
|
||||||
timidity/instrum_dls.cpp
|
timidity/instrum_dls.cpp
|
||||||
|
@ -720,9 +712,6 @@ set( FASTMATH_PCH_SOURCES
|
||||||
# Enable fast math for some sources
|
# Enable fast math for some sources
|
||||||
set( FASTMATH_SOURCES
|
set( FASTMATH_SOURCES
|
||||||
${FASTMATH_PCH_SOURCES}
|
${FASTMATH_PCH_SOURCES}
|
||||||
oplsynth/music_opldumper_mididevice.cpp
|
|
||||||
oplsynth/music_opl_mididevice.cpp
|
|
||||||
oplsynth/opl_mus_player.cpp
|
|
||||||
sound/commonsound.cpp
|
sound/commonsound.cpp
|
||||||
sound/i_music.cpp
|
sound/i_music.cpp
|
||||||
sound/i_sound.cpp
|
sound/i_sound.cpp
|
||||||
|
@ -737,7 +726,6 @@ set( FASTMATH_SOURCES
|
||||||
sound/music_midistream.cpp
|
sound/music_midistream.cpp
|
||||||
sound/music_midi_base.cpp
|
sound/music_midi_base.cpp
|
||||||
sound/music_midi_timidity.cpp
|
sound/music_midi_timidity.cpp
|
||||||
sound/music_mus_opl.cpp
|
|
||||||
sound/music_stream.cpp
|
sound/music_stream.cpp
|
||||||
sound/music_fluidsynth_mididevice.cpp
|
sound/music_fluidsynth_mididevice.cpp
|
||||||
sound/music_softsynth_mididevice.cpp
|
sound/music_softsynth_mididevice.cpp
|
||||||
|
@ -1113,7 +1101,6 @@ include_directories( .
|
||||||
g_raven
|
g_raven
|
||||||
g_strife
|
g_strife
|
||||||
g_shared
|
g_shared
|
||||||
oplsynth
|
|
||||||
sound
|
sound
|
||||||
textures
|
textures
|
||||||
thingdef
|
thingdef
|
||||||
|
@ -1172,10 +1159,6 @@ if( NOT WIN32 )
|
||||||
COMMAND chmod +x ${CMAKE_CURRENT_BINARY_DIR}/link-make
|
COMMAND chmod +x ${CMAKE_CURRENT_BINARY_DIR}/link-make
|
||||||
COMMAND /bin/sh -c ${CMAKE_CURRENT_BINARY_DIR}/link-make )
|
COMMAND /bin/sh -c ${CMAKE_CURRENT_BINARY_DIR}/link-make )
|
||||||
endif()
|
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 )
|
if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
|
||||||
# Need to enable intrinsics for this file.
|
# Need to enable intrinsics for this file.
|
||||||
if( SSE_MATTERS )
|
if( SSE_MATTERS )
|
||||||
|
@ -1190,8 +1173,6 @@ if( APPLE )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
source_group("Audio Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/.+")
|
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\\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\\Timidity\\Source" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/timidity/.+\\.cpp$")
|
||||||
source_group("Audio Files\\WildMidi\\Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/wildmidi/.+\\.h$")
|
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_DEFAULT = -1,
|
||||||
MDEV_MMAPI = 0,
|
MDEV_MMAPI = 0,
|
||||||
MDEV_OPL = 1,
|
MDEV_OPL = 1, // unused
|
||||||
MDEV_SNDSYS = 2,
|
MDEV_SNDSYS = 2,
|
||||||
MDEV_TIMIDITY = 3,
|
MDEV_TIMIDITY = 3,
|
||||||
MDEV_FLUIDSYNTH = 4,
|
MDEV_FLUIDSYNTH = 4,
|
||||||
|
|
|
@ -295,11 +295,6 @@ FString MusInfo::GetStats()
|
||||||
return "No stats available for this song";
|
return "No stats available for this song";
|
||||||
}
|
}
|
||||||
|
|
||||||
MusInfo *MusInfo::GetOPLDumper(const char *filename)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
MusInfo *MusInfo::GetWaveDumper(const char *filename, int rate)
|
MusInfo *MusInfo::GetWaveDumper(const char *filename, int rate)
|
||||||
{
|
{
|
||||||
return NULL;
|
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('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
|
(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
|
// Check for game music
|
||||||
else if ((fmt = GME_CheckFormat(id[0])) != NULL && fmt[0] != '\0')
|
else if ((fmt = GME_CheckFormat(id[0])) != NULL && fmt[0] != '\0')
|
||||||
|
@ -707,43 +704,6 @@ ADD_STAT(music)
|
||||||
return "No song playing";
|
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
|
// CCMD writewave
|
||||||
|
|
|
@ -78,7 +78,6 @@ public:
|
||||||
virtual bool SetSubsong (int subsong);
|
virtual bool SetSubsong (int subsong);
|
||||||
virtual void Update();
|
virtual void Update();
|
||||||
virtual FString GetStats();
|
virtual FString GetStats();
|
||||||
virtual MusInfo *GetOPLDumper(const char *filename);
|
|
||||||
virtual MusInfo *GetWaveDumper(const char *filename, int rate);
|
virtual MusInfo *GetWaveDumper(const char *filename, int rate);
|
||||||
virtual void FluidSettingInt(const char *setting, int value); // FluidSynth settings
|
virtual void FluidSettingInt(const char *setting, int value); // FluidSynth settings
|
||||||
virtual void FluidSettingNum(const char *setting, double value); // "
|
virtual void FluidSettingNum(const char *setting, double value); // "
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#define TRUE 1
|
#define TRUE 1
|
||||||
#endif
|
#endif
|
||||||
#include "tempfiles.h"
|
#include "tempfiles.h"
|
||||||
#include "oplsynth/opl_mus_player.h"
|
#include "critsec.h"
|
||||||
#include "c_cvars.h"
|
#include "c_cvars.h"
|
||||||
#include "mus2midi.h"
|
#include "mus2midi.h"
|
||||||
#include "i_sound.h"
|
#include "i_sound.h"
|
||||||
|
@ -265,37 +265,6 @@ protected:
|
||||||
virtual void ComputeOutput(float *buffer, int len) = 0;
|
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 --------------------------------------------
|
// Internal TiMidity MIDI device --------------------------------------------
|
||||||
|
|
||||||
namespace Timidity { struct Renderer; }
|
namespace Timidity { struct Renderer; }
|
||||||
|
@ -528,7 +497,6 @@ public:
|
||||||
MUSSong2(FileReader &reader, EMidiDevice type, const char *args);
|
MUSSong2(FileReader &reader, EMidiDevice type, const char *args);
|
||||||
~MUSSong2();
|
~MUSSong2();
|
||||||
|
|
||||||
MusInfo *GetOPLDumper(const char *filename);
|
|
||||||
MusInfo *GetWaveDumper(const char *filename, int rate);
|
MusInfo *GetWaveDumper(const char *filename, int rate);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -554,7 +522,6 @@ public:
|
||||||
MIDISong2(FileReader &reader, EMidiDevice type, const char *args);
|
MIDISong2(FileReader &reader, EMidiDevice type, const char *args);
|
||||||
~MIDISong2();
|
~MIDISong2();
|
||||||
|
|
||||||
MusInfo *GetOPLDumper(const char *filename);
|
|
||||||
MusInfo *GetWaveDumper(const char *filename, int rate);
|
MusInfo *GetWaveDumper(const char *filename, int rate);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -611,7 +578,6 @@ public:
|
||||||
HMISong(FileReader &reader, EMidiDevice type, const char *args);
|
HMISong(FileReader &reader, EMidiDevice type, const char *args);
|
||||||
~HMISong();
|
~HMISong();
|
||||||
|
|
||||||
MusInfo *GetOPLDumper(const char *filename);
|
|
||||||
MusInfo *GetWaveDumper(const char *filename, int rate);
|
MusInfo *GetWaveDumper(const char *filename, int rate);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -654,7 +620,6 @@ public:
|
||||||
XMISong(FileReader &reader, EMidiDevice type, const char *args);
|
XMISong(FileReader &reader, EMidiDevice type, const char *args);
|
||||||
~XMISong();
|
~XMISong();
|
||||||
|
|
||||||
MusInfo *GetOPLDumper(const char *filename);
|
|
||||||
MusInfo *GetWaveDumper(const char *filename, int rate);
|
MusInfo *GetWaveDumper(const char *filename, int rate);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -709,34 +674,6 @@ protected:
|
||||||
SoundStream *m_Stream;
|
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 -----------------------
|
// CD track/disk played through the multimedia system -----------------------
|
||||||
|
|
||||||
class CDSong : public MusInfo
|
class CDSong : public MusInfo
|
||||||
|
|
|
@ -382,11 +382,7 @@ void HMISong::SetupForHMP(int len)
|
||||||
void HMISong::CheckCaps(int tech)
|
void HMISong::CheckCaps(int tech)
|
||||||
{
|
{
|
||||||
// What's the equivalent HMI device for our technology?
|
// What's the equivalent HMI device for our technology?
|
||||||
if (tech == MOD_FMSYNTH)
|
if (tech == MOD_MIDIPORT)
|
||||||
{
|
|
||||||
tech = HMI_DEV_OPL3;
|
|
||||||
}
|
|
||||||
else if (tech == MOD_MIDIPORT)
|
|
||||||
{
|
{
|
||||||
tech = HMI_DEV_MPU401;
|
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
|
// HMISong :: GetWaveDumper
|
||||||
|
|
|
@ -29,7 +29,7 @@ static void AddDefaultMidiDevices(FOptionValues *opt)
|
||||||
#endif
|
#endif
|
||||||
pair[p].Text = "GUS";
|
pair[p].Text = "GUS";
|
||||||
pair[p].Value = -4.0;
|
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+1].Value = -3.0;
|
||||||
pair[p+2].Text = "TiMidity++";
|
pair[p+2].Text = "TiMidity++";
|
||||||
pair[p+2].Value = -2.0;
|
pair[p+2].Value = -2.0;
|
||||||
|
@ -67,7 +67,8 @@ static void MIDIDeviceChanged(int newdev)
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
UINT mididevice;
|
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)
|
if (!nummididevicesset)
|
||||||
return;
|
return;
|
||||||
|
@ -173,7 +174,7 @@ CCMD (snd_listmididevices)
|
||||||
PrintMidiDevice (-5, "FluidSynth", MOD_SWSYNTH, 0);
|
PrintMidiDevice (-5, "FluidSynth", MOD_SWSYNTH, 0);
|
||||||
#endif
|
#endif
|
||||||
PrintMidiDevice (-4, "Gravis Ultrasound Emulation", MOD_SWSYNTH, 0);
|
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 (-2, "TiMidity++", MOD_SWSYNTH, 0);
|
||||||
PrintMidiDevice (-1, "Sound System", 0, 0);
|
PrintMidiDevice (-1, "Sound System", 0, 0);
|
||||||
if (nummididevices != 0)
|
if (nummididevices != 0)
|
||||||
|
@ -219,7 +220,7 @@ CCMD (snd_listmididevices)
|
||||||
Printf("%s-5. FluidSynth\n", -5 == snd_mididevice ? TEXTCOLOR_BOLD : "");
|
Printf("%s-5. FluidSynth\n", -5 == snd_mididevice ? TEXTCOLOR_BOLD : "");
|
||||||
#endif
|
#endif
|
||||||
Printf("%s-4. Gravis Ultrasound Emulation\n", -4 == snd_mididevice ? TEXTCOLOR_BOLD : "");
|
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-2. TiMidity++\n", -2 == snd_mididevice ? TEXTCOLOR_BOLD : "");
|
||||||
Printf("%s-1. Sound System\n", -1 == 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)
|
EMidiDevice MIDIStreamer::SelectMIDIDevice(EMidiDevice device)
|
||||||
{
|
{
|
||||||
/* MIDI are played as:
|
/* 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:
|
- Timidity:
|
||||||
- if explicitly selected by $mididevice
|
- if explicitly selected by $mididevice
|
||||||
- when snd_mididevice is -2 and no midi device is set for the song
|
- 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:
|
- Sound System:
|
||||||
- if explicitly selected by $mididevice
|
- if explicitly selected by $mididevice
|
||||||
- when snd_mididevice is -1 and no midi device is set for the song
|
- 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):
|
- MMAPI (Win32 only):
|
||||||
- if explicitly selected by $mididevice (non-Win32 redirects this to Sound System)
|
- 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
|
- 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.
|
// Choose the type of MIDI device we want.
|
||||||
|
@ -278,18 +274,6 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const
|
||||||
case MDEV_GUS:
|
case MDEV_GUS:
|
||||||
return new TimidityMIDIDevice(Args);
|
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:
|
case MDEV_TIMIDITY:
|
||||||
return new TimidityPPMIDIDevice(Args);
|
return new TimidityPPMIDIDevice(Args);
|
||||||
|
|
||||||
|
@ -323,11 +307,7 @@ void MIDIStreamer::Play(bool looping, int subsong)
|
||||||
devtype = SelectMIDIDevice(DeviceType);
|
devtype = SelectMIDIDevice(DeviceType);
|
||||||
if (DumpFilename.IsNotEmpty())
|
if (DumpFilename.IsNotEmpty())
|
||||||
{
|
{
|
||||||
if (devtype == MDEV_OPL)
|
if (devtype == MDEV_GUS)
|
||||||
{
|
|
||||||
MIDI = new OPLDumperMIDIDevice(DumpFilename);
|
|
||||||
}
|
|
||||||
else if (devtype == MDEV_GUS)
|
|
||||||
{
|
{
|
||||||
MIDI = new TimidityWaveWriterMIDIDevice(DumpFilename, 0);
|
MIDI = new TimidityWaveWriterMIDIDevice(DumpFilename, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -376,17 +376,6 @@ end:
|
||||||
return events;
|
return events;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// MUSSong2 :: GetOPLDumper
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
MusInfo *MUSSong2::GetOPLDumper(const char *filename)
|
|
||||||
{
|
|
||||||
return new MUSSong2(this, filename, MDEV_OPL);
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// MUSSong2 :: GetWaveDumper
|
// 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
|
// 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
|
// XMISong :: GetWaveDumper
|
||||||
|
|
|
@ -1600,11 +1600,6 @@ OptionMenu AdvSoundOptions
|
||||||
Title "$ADVSNDMNU_TITLE"
|
Title "$ADVSNDMNU_TITLE"
|
||||||
Option "$ADVSNDMNU_SAMPLERATE", "snd_samplerate", "SampleRates"
|
Option "$ADVSNDMNU_SAMPLERATE", "snd_samplerate", "SampleRates"
|
||||||
StaticText " "
|
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
|
StaticText "$ADVSNDMNU_GUSEMULATION", 1
|
||||||
TextField "$ADVSNDMNU_GUSCONFIG", "midi_config"
|
TextField "$ADVSNDMNU_GUSCONFIG", "midi_config"
|
||||||
Slider "$ADVSNDMNU_MIDIVOICES", "midi_voices", 16, 256, 4, 0
|
Slider "$ADVSNDMNU_MIDIVOICES", "midi_voices", 16, 256, 4, 0
|
||||||
|
|
Loading…
Reference in a new issue