- removed the OPL synth because parts of it are GPLv3 and even under the best circumstances not compatible with the license mix here.

This commit is contained in:
Christoph Oelckers 2020-01-02 19:18:30 +01:00
parent 4571ba74c2
commit e43d2c10c0
27 changed files with 4 additions and 9426 deletions

View file

@ -381,7 +381,6 @@ set( LZMA_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/lzma/C" )
set( GDTOA_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/gdtoa" )
set( ZMUSIC_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zmusic" )
set( TIMIDITYPP_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/timidityplus" )
set( OPLSYNTH_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/oplsynth" )
set( GME_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/game-music-emu" )
if( NOT CMAKE_CROSSCOMPILING )
@ -403,7 +402,6 @@ endif()
add_subdirectory( libraries/lzma )
add_subdirectory( libraries/dumb )
add_subdirectory( libraries/game-music-emu)
add_subdirectory( libraries/oplsynth )
add_subdirectory( libraries/timidityplus )
add_subdirectory( libraries/zmusic )
add_subdirectory( tools )

View file

@ -1,26 +0,0 @@
cmake_minimum_required( VERSION 2.8.7 )
use_fast_math()
require_stricmp()
require_strnicmp()
if( ZD_CMAKE_COMPILER_IS_GNUC_COMPATIBLE )
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-unused-parameter -fomit-frame-pointer" )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11" )
endif()
include_directories( oplsynth )
file( GLOB HEADER_FILES
oplsynth/*.h
)
add_library( oplsynth STATIC
fmopl.cpp
musicblock.cpp
nukedopl3.cpp
opl_mus_player.cpp
OPL3.cpp
oplio.cpp
dosbox/opl.cpp
)
target_link_libraries( oplsynth )

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,230 +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:
op_type op[MAXOPERATORS];
Bits int_samplerate;
Bit8u status;
Bit32u opl_index;
#if defined(OPLTYPE_IS_OPL3)
Bit8u adlibreg[512]; // adlib register set (including second set)
Bit8u wave_sel[44]; // waveform selection
#else
Bit8u adlibreg[256]; // adlib register set
Bit8u wave_sel[22]; // waveform selection
#endif
fltype recipsamp; // inverse of sampling rate
// vibrato/tremolo tables
Bit32s vib_table[VIBTAB_SIZE];
Bit32s trem_table[TREMTAB_SIZE*2];
Bit32s vibval_const[BLOCKBUF_SIZE];
Bit32s tremval_const[BLOCKBUF_SIZE];
// vibrato value tables (used per-operator)
Bit32s vibval_var1[BLOCKBUF_SIZE];
Bit32s vibval_var2[BLOCKBUF_SIZE];
// vibrato/trmolo value table pointers
Bit32s *vibval1, *vibval2, *vibval3, *vibval4;
Bit32s *tremval1, *tremval2, *tremval3, *tremval4;
// calculated frequency multiplication values (depend on sampling rate)
fltype frqmul[16];
// vibrato/tremolo increment/counter
Bit32u vibtab_pos;
Bit32u vibtab_add;
Bit32u tremtab_pos;
Bit32u tremtab_add;
// Enable full MIDI panning; disable OPL3 panning
bool FullPan;
// enable an operator
void enable_operator(Bitu regbase, op_type* op_pt, Bit32u act_type);
void disable_operator(op_type* op_pt, Bit32u act_type);
// functions to change parameters of an operator
void change_frequency(Bitu chanbase, Bitu regbase, op_type* op_pt);
void change_attackrate(Bitu regbase, op_type* op_pt);
void change_decayrate(Bitu regbase, op_type* op_pt);
void change_releaserate(Bitu regbase, op_type* op_pt);
void change_sustainlevel(Bitu regbase, op_type* op_pt);
void change_waveform(Bitu regbase, op_type* op_pt);
void change_keepsustain(Bitu regbase, op_type* op_pt);
void change_vibrato(Bitu regbase, op_type* op_pt);
void change_feedback(Bitu chanbase, op_type* op_pt);
// general functions
public:
void Reset();
void Update(float* sndptr, int numsamples);
void WriteReg(int idx, int val);
void SetPanning(int c, float left, float right);
DBOPL(bool stereo);
};
static Bit32u generator_add; // should be a chip parameter

File diff suppressed because it is too large Load diff

View file

@ -1,498 +0,0 @@
//-----------------------------------------------------------------------------
//
// Copyright 2002-2016 Randy Heit
// Copyright 2005-2014 Simon Howard
// Copyright 2017 Christoph Oelckers
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//-----------------------------------------------------------------------------
//
// This is mostly a reimplementation of the interface provided by
// MusLib based on Chocolate-Doom's OPL player, although the
// interface has been cleaned up a bit to be more consistent and readable.
//
//
#include <stdlib.h>
#include <string.h>
#include "musicblock.h"
musicBlock::musicBlock ()
{
memset (this, 0, sizeof(*this));
for(auto &oplchannel : oplchannels) oplchannel.Panning = 64; // default to center panning.
for(auto &voice : voices) voice.index = ~0u; // mark all free.
}
musicBlock::~musicBlock ()
{
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
int musicBlock::releaseVoice(uint32_t slot, uint32_t killed)
{
struct OPLVoice *ch = &voices[slot];
io->WriteFrequency(slot, ch->note, ch->pitch, 0);
ch->index = ~0u;
ch->sustained = false;
if (!killed) ch->timestamp = ++timeCounter;
if (killed) io->MuteChannel(slot);
return slot;
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
int musicBlock::findFreeVoice()
{
// We want to prefer the least recently freed voice, as more recently
// freed voices can still play a tone from their release state.
// Sustained voices are replaced when there are no free voices.
uint32_t min_value = ~0u;
int result = -1;
for (uint32_t i = 0; i < io->NumChannels; ++i)
{
uint32_t voice_value = voices[i].timestamp + (voices[i].sustained ? (1 << 31) : 0);
if ((voices[i].index == ~0u || voices[i].sustained) && (voice_value < min_value))
{
min_value = voice_value;
result = i;
}
}
if (result >= 0)
{
releaseVoice(result, 1);
}
return result;
}
//----------------------------------------------------------------------------
//
// When all voices are in use, we must discard an existing voice to
// play a new note. Find and free an existing voice. The channel
// passed to the function is the channel for the new note to be
// played.
//
//----------------------------------------------------------------------------
int musicBlock::replaceExistingVoice()
{
// Check the allocated voices, if we find an instrument that is
// of a lower priority to the new instrument, discard it.
// If a voice is being used to play the second voice of an instrument,
// use that, as second voices are non-essential.
// Lower numbered MIDI channels implicitly have a higher priority
// than higher-numbered channels, eg. MIDI channel 1 is never
// discarded for MIDI channel 2.
int result = 0;
for (uint32_t i = 0; i < io->NumChannels; ++i)
{
if (voices[i].current_instr_voice == &voices[i].current_instr->voices[1] ||
voices[i].index >= voices[result].index)
{
result = i;
}
}
releaseVoice(result, 1);
return result;
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void musicBlock::voiceKeyOn(uint32_t slot, uint32_t channo, GenMidiInstrument *instrument, uint32_t instrument_voice, uint32_t key, uint32_t volume)
{
struct OPLVoice *voice = &voices[slot];
auto &channel = oplchannels[channo];
GenMidiVoice *gmvoice;
voice->index = channo;
voice->key = key;
// Program the voice with the instrument data:
voice->current_instr = instrument;
gmvoice = voice->current_instr_voice = &instrument->voices[instrument_voice];
io->WriteInstrument(slot,gmvoice, channel.Vibrato);
io->WritePan(slot, gmvoice, channel.Panning);
// Set the volume level.
voice->note_volume = volume;
io->WriteVolume(slot, gmvoice, channel.Volume, channel.Expression, volume);
// Write the frequency value to turn the note on.
// Work out the note to use. This is normally the same as
// the key, unless it is a fixed pitch instrument.
int note;
if (instrument->flags & GENMIDI_FLAG_FIXED) note = instrument->fixed_note;
else if (channo == CHAN_PERCUSSION) note = 60;
else note = key;
// If this is the second voice of a double voice instrument, the
// frequency index can be adjusted by the fine tuning field.
voice->fine_tuning = (instrument_voice != 0) ? (voice->current_instr->fine_tuning / 2) - 64 : 0;
voice->pitch = voice->fine_tuning + channel.Pitch;
if (!(instrument->flags & GENMIDI_FLAG_FIXED) && channo != CHAN_PERCUSSION)
{
note += gmvoice->base_note_offset;
}
// Avoid possible overflow due to base note offset:
while (note < 0)
{
note += 12;
}
while (note > HIGHEST_NOTE)
{
note -= 12;
}
voice->note = note;
io->WriteFrequency(slot, note, voice->pitch, 1);
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
bool opl_singlevoice;
void musicBlock::noteOn(uint32_t channel, uint8_t key, int volume)
{
if (volume <= 0)
{
noteOff(channel, key);
return;
}
GenMidiInstrument *instrument;
// Percussion channel is treated differently.
if (channel == CHAN_PERCUSSION)
{
if (key < GENMIDI_FIST_PERCUSSION || key >= GENMIDI_FIST_PERCUSSION + GENMIDI_NUM_PERCUSSION)
{
return;
}
instrument = &OPLinstruments[key + (GENMIDI_NUM_INSTRS - GENMIDI_FIST_PERCUSSION)];
}
else
{
auto inst = oplchannels[channel].Instrument;
if (inst >= GENMIDI_NUM_TOTAL) return; // better safe than sorry.
instrument = &OPLinstruments[inst];
}
bool double_voice = ((instrument->flags) & GENMIDI_FLAG_2VOICE) && !opl_singlevoice;
int i = findFreeVoice();
if (i < 0) i = replaceExistingVoice();
if (i >= 0)
{
voiceKeyOn(i, channel, instrument, 0, key, volume);
if (double_voice)
{
i = findFreeVoice();
if (i >= 0)
{
voiceKeyOn(i, channel, instrument, 1, key, volume);
}
}
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void musicBlock::noteOff(uint32_t id, uint8_t note)
{
uint32_t sustain = oplchannels[id].Sustain;
for(uint32_t i = 0; i < io->NumChannels; i++)
{
if (voices[i].index == id && voices[i].key == note)
{
if (sustain >= MIN_SUSTAIN)
{
voices[i].sustained = true;
voices[i].timestamp = ++timeCounter;
}
else releaseVoice(i, 0);
}
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void musicBlock::changePitch(uint32_t id, int val1, int val2)
{
// Convert pitch from 14-bit to 7-bit, then scale it, since the player
// code only understands sensitivities of 2 semitones.
int pitch = ((val1 | (val2 << 7)) - 8192) * oplchannels[id].PitchSensitivity / (200 * 128) + 64;
oplchannels[id].Pitch = pitch;
for(uint32_t i = 0; i < io->NumChannels; i++)
{
auto &ch = voices[i];
if (ch.index == id)
{
ch.pitch = ch.fine_tuning + pitch;
io->WriteFrequency(i, ch.note, ch.pitch, 1);
}
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void musicBlock::changeModulation(uint32_t id, int value)
{
bool vibrato = (value >= VIBRATO_THRESHOLD);
oplchannels[id].Vibrato = vibrato;
for (uint32_t i = 0; i < io->NumChannels; i++)
{
auto &ch = voices[i];
if (ch.index == id)
{
io->WriteTremolo(i, ch.current_instr_voice, vibrato);
}
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void musicBlock::changeSustain(uint32_t id, int value)
{
oplchannels[id].Sustain = value;
if (value < MIN_SUSTAIN)
{
for (uint32_t i = 0; i < io->NumChannels; i++)
{
if (voices[i].index == id && voices[i].sustained)
releaseVoice(i, 0);
}
}
}
//----------------------------------------------------------------------------
//
// Change volume or expression.
// Since both go to the same register, one function can handle both.
//
//----------------------------------------------------------------------------
void musicBlock::changeVolume(uint32_t id, int value, bool expression)
{
auto &chan = oplchannels[id];
if (!expression) chan.Volume = value;
else chan.Expression = value;
for (uint32_t i = 0; i < io->NumChannels; i++)
{
auto &ch = voices[i];
if (ch.index == id)
{
io->WriteVolume(i, ch.current_instr_voice, chan.Volume, chan.Expression, ch.note_volume);
}
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void musicBlock::changePanning(uint32_t id, int value)
{
oplchannels[id].Panning = value;
for(uint32_t i = 0; i < io->NumChannels; i++)
{
auto &ch = voices[i];
if (ch.index == id)
{
io->WritePan(i, ch.current_instr_voice, value);
}
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void musicBlock::notesOff(uint32_t id, int value)
{
for (uint32_t i = 0; i < io->NumChannels; ++i)
{
if (voices[i].index == id)
{
if (oplchannels[id].Sustain >= MIN_SUSTAIN)
{
voices[i].sustained = true;
voices[i].timestamp = ++timeCounter;
}
else releaseVoice(i, 0);
}
}
}
//----------------------------------------------------------------------------
//
// release all notes for this channel
//
//----------------------------------------------------------------------------
void musicBlock::allNotesOff(uint32_t id, int value)
{
for (uint32_t i = 0; i < io->NumChannels; ++i)
{
if (voices[i].index == id)
{
releaseVoice(i, 0);
}
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void musicBlock::changeExtended(uint32_t id, uint8_t controller, int value)
{
switch (controller)
{
case ctrlRPNHi:
oplchannels[id].RPN = (oplchannels[id].RPN & 0x007F) | (value << 7);
break;
case ctrlRPNLo:
oplchannels[id].RPN = (oplchannels[id].RPN & 0x3F80) | value;
break;
case ctrlNRPNLo:
case ctrlNRPNHi:
oplchannels[id].RPN = 0x3FFF;
break;
case ctrlDataEntryHi:
if (oplchannels[id].RPN == 0)
{
oplchannels[id].PitchSensitivity = value * 100 + (oplchannels[id].PitchSensitivity % 100);
}
break;
case ctrlDataEntryLo:
if (oplchannels[id].RPN == 0)
{
oplchannels[id].PitchSensitivity = value + (oplchannels[id].PitchSensitivity / 100) * 100;
}
break;
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void musicBlock::resetControllers(uint32_t chan, int vol)
{
auto &channel = oplchannels[chan];
channel.Volume = vol;
channel.Expression = 127;
channel.Sustain = 0;
channel.Pitch = 64;
channel.RPN = 0x3fff;
channel.PitchSensitivity = 200;
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void musicBlock::programChange(uint32_t channel, int value)
{
oplchannels[channel].Instrument = value;
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void musicBlock::resetAllControllers(int vol)
{
uint32_t i;
for (i = 0; i < NUM_CHANNELS; i++)
{
resetControllers(i, vol);
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void musicBlock::stopAllVoices()
{
for (uint32_t i = 0; i < io->NumChannels; i++)
{
if (voices[i].index != ~0u) releaseVoice(i, 1);
voices[i].timestamp = 0;
}
timeCounter = 0;
}

File diff suppressed because it is too large Load diff

View file

@ -1,527 +0,0 @@
/*
** opl_mus_player.cpp
**
**---------------------------------------------------------------------------
** Copyright 1999-2016 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.
**---------------------------------------------------------------------------
**
*/
#ifdef _WIN32
#include <io.h>
#endif
#include <string.h>
#include <assert.h>
#include <math.h>
#include <algorithm>
#include "opl_mus_player.h"
#include "opl.h"
#include "o_swap.h"
#define IMF_RATE 700.0
OPLmusicBlock::OPLmusicBlock(int core, int numchips)
{
currentCore = core;
scoredata = NULL;
NextTickIn = 0;
LastOffset = 0;
NumChips = std::min(numchips, 2);
Looping = false;
FullPan = false;
io = NULL;
io = new OPLio;
}
OPLmusicBlock::~OPLmusicBlock()
{
delete io;
}
void OPLmusicBlock::ResetChips (int numchips)
{
io->Reset ();
NumChips = io->Init(currentCore, std::min(numchips, 2), FullPan, false);
}
void OPLmusicBlock::Restart()
{
stopAllVoices ();
resetAllControllers (127);
playingcount = 0;
LastOffset = 0;
}
OPLmusicFile::OPLmusicFile (const void *data, size_t length, int core, int numchips, const char *&errormessage)
: OPLmusicBlock(core, numchips), ScoreLen ((int)length)
{
static char errorbuffer[80];
errormessage = nullptr;
if (io == nullptr)
{
return;
}
scoredata = new uint8_t[ScoreLen];
memcpy(scoredata, data, length);
if (0 == (NumChips = io->Init(core, NumChips, false, false)))
{
goto fail;
}
// Check for RDosPlay raw OPL format
if (!memcmp(scoredata, "RAWADATA", 8))
{
RawPlayer = RDosPlay;
if (*(uint16_t *)(scoredata + 8) == 0)
{ // A clock speed of 0 is bad
*(uint16_t *)(scoredata + 8) = 0xFFFF;
}
SamplesPerTick = LittleShort(*(uint16_t *)(scoredata + 8)) / ADLIB_CLOCK_MUL;
}
// Check for DosBox OPL dump
else if (!memcmp(scoredata, "DBRAWOPL", 8))
{
if (LittleShort(((uint16_t *)scoredata)[5]) == 1)
{
RawPlayer = DosBox1;
SamplesPerTick = OPL_SAMPLE_RATE / 1000;
ScoreLen = std::min<int>(ScoreLen - 24, LittleLong(((uint32_t *)scoredata)[4])) + 24;
}
else if (LittleLong(((uint32_t *)scoredata)[2]) == 2)
{
bool okay = true;
if (scoredata[21] != 0)
{
snprintf(errorbuffer, 80, "Unsupported DOSBox Raw OPL format %d\n", scoredata[20]);
errormessage = errorbuffer;
okay = false;
}
if (scoredata[22] != 0)
{
snprintf(errorbuffer, 80, "Unsupported DOSBox Raw OPL compression %d\n", scoredata[21]);
errormessage = errorbuffer;
okay = false;
}
if (!okay)
goto fail;
RawPlayer = DosBox2;
SamplesPerTick = OPL_SAMPLE_RATE / 1000;
int headersize = 0x1A + scoredata[0x19];
ScoreLen = std::min<int>(ScoreLen - headersize, LittleLong(((uint32_t *)scoredata)[3]) * 2) + headersize;
}
else
{
snprintf(errorbuffer, 80, "Unsupported DOSBox Raw OPL version %d.%d\n", LittleShort(((uint16_t *)scoredata)[4]), LittleShort(((uint16_t *)scoredata)[5]));
errormessage = errorbuffer;
goto fail;
}
}
// Check for modified IMF format (includes a header)
else if (!memcmp(scoredata, "ADLIB\1", 6))
{
int songlen;
uint8_t *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
goto fail;
}
songlen = LittleLong(*(uint32_t *)score);
if (songlen != 0 && (songlen +=4) < ScoreLen - (score - scoredata))
{
ScoreLen = songlen + int(score - scoredata);
}
}
else
{
errormessage = "Unknown OPL format";
goto fail;
}
Restart ();
return;
fail:
delete[] scoredata;
scoredata = nullptr;
return;
}
OPLmusicFile::~OPLmusicFile ()
{
if (scoredata != NULL)
{
io->Reset ();
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(*(uint16_t *)(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 (*(uint32_t *)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);
while (numsamples > 0)
{
int tick_in = int(NextTickIn);
int samplesleft = std::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);
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);
}
}
}
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 = std::min(count, std::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 ()
{
uint8_t reg, data;
uint16_t 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(*(uint16_t *)(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->WriteRegister(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->WriteRegister(WhichChip, reg, data);
}
break;
case DosBox2:
{
uint8_t *to_reg = scoredata + 0x1A;
uint8_t to_reg_size = scoredata[0x19];
uint8_t short_delay_code = scoredata[0x17];
uint8_t long_delay_code = scoredata[0x18];
while (score < scoredata + ScoreLen)
{
uint8_t 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->WriteRegister(which, to_reg[code], data);
}
}
}
break;
case IMF:
delay = 0;
while (delay == 0 && score + 4 - scoredata <= ScoreLen)
{
if (*(uint32_t *)score == 0xFFFFFFFF)
{ // This is a special value that means to end the song.
return 0;
}
reg = score[0];
data = score[1];
delay = LittleShort(((uint16_t *)score)[1]);
score += 4;
io->WriteRegister (0, reg, data);
}
return delay;
}
return 0;
}

View file

@ -1,485 +0,0 @@
//-----------------------------------------------------------------------------
//
// Copyright 2002-2016 Randy Heit
// Copyright 2005-2014 Simon Howard
// Copyright 2017 Christoph Oelckers
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//-----------------------------------------------------------------------------
//
// OPL IO interface. Partly built from the non-MusLib code in the old version
// plus some additions from Chocolate Doom.
//
#include <math.h>
#include <assert.h>
#include <algorithm>
#include <string.h>
#include "genmidi.h"
#include "oplio.h"
#include "opl.h"
const double HALF_PI = (3.14159265358979323846 * 0.5);
OPLio::~OPLio()
{
}
void OPLio::SetClockRate(double samples_per_tick)
{
}
void OPLio::WriteDelay(int ticks)
{
}
//----------------------------------------------------------------------------
//
// Initialize OPL emulator
//
//----------------------------------------------------------------------------
int OPLio::Init(int core, uint32_t numchips, bool stereo, bool initopl3)
{
assert(numchips >= 1 && numchips <= OPL_NUM_VOICES);
uint32_t i;
IsOPL3 = (core == 1 || core == 2 || core == 3);
memset(chips, 0, sizeof(chips));
if (IsOPL3)
{
numchips = (numchips + 1) >> 1;
}
for (i = 0; i < numchips; ++i)
{
OPLEmul *chip = IsOPL3 ? (core == 1 ? DBOPLCreate(stereo) : (core == 2 ? JavaOPLCreate(stereo) : NukedOPL3Create(stereo))) : YM3812Create(stereo);
if (chip == NULL)
{
break;
}
chips[i] = chip;
}
NumChips = i;
NumChannels = i * (IsOPL3 ? OPL3_NUM_VOICES : OPL_NUM_VOICES);
WriteInitState(initopl3);
return i;
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void OPLio::WriteInitState(bool initopl3)
{
for (uint32_t k = 0; k < NumChips; ++k)
{
int chip = k << (int)IsOPL3;
if (IsOPL3 && initopl3)
{
WriteRegister(chip, OPL_REG_OPL3_ENABLE, 1);
WriteRegister(chip, OPL_REG_4OPMODE_ENABLE, 0);
}
WriteRegister(chip, OPL_REG_WAVEFORM_ENABLE, WAVEFORM_ENABLED);
WriteRegister(chip, OPL_REG_PERCUSSION_MODE, 0); // should be the default, but cannot verify for some of the cores.
}
// Reset all channels.
for (uint32_t k = 0; k < NumChannels; k++)
{
MuteChannel(k);
WriteValue(OPL_REGS_FREQ_2, k, 0);
}
}
//----------------------------------------------------------------------------
//
// Deinitialize emulator before shutdown
//
//----------------------------------------------------------------------------
void OPLio::Reset(void)
{
for (auto &c : chips)
{
if (c != nullptr)
{
delete c;
c = nullptr;
}
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void OPLio::WriteRegister(int chipnum, uint32_t reg, uint8_t data)
{
if (IsOPL3)
{
reg |= (chipnum & 1) << 8;
chipnum >>= 1;
}
if (chips[chipnum] != nullptr)
{
chips[chipnum]->WriteReg(reg, data);
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void OPLio::WriteValue(uint32_t regbase, uint32_t channel, uint8_t value)
{
WriteRegister (channel / OPL_NUM_VOICES, regbase + (channel % OPL_NUM_VOICES), value);
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
static const int voice_operators[OPL_NUM_VOICES] = { 0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12 };
void OPLio::WriteOperator(uint32_t regbase, uint32_t channel, int index, uint8_t data2)
{
WriteRegister(channel / OPL_NUM_VOICES, regbase + voice_operators[channel % OPL_NUM_VOICES] + 3*index, data2);
}
//----------------------------------------------------------------------------
//
// 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.
//
//----------------------------------------------------------------------------
static const uint16_t frequencies[] = { // (this is the table from Chocolate Doom, which contains the same values as ZDoom's original one but is better formatted.
0x133, 0x133, 0x134, 0x134, 0x135, 0x136, 0x136, 0x137, // -1
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, // -2
0x14a, 0x14a, 0x14b, 0x14c, 0x14c, 0x14d, 0x14d, 0x14e,
0x14f, 0x14f, 0x150, 0x150, 0x151, 0x152, 0x152, 0x153,
0x153, 0x154, 0x155, 0x155, 0x156, 0x157, 0x157, 0x158,
// These are used for the first seven MIDI note values:
0x158, 0x159, 0x15a, 0x15a, 0x15b, 0x15b, 0x15c, 0x15d, // 0
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, // 1
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, // 2
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, // 3
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, // 4
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, // 5
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, // 6
0x1ee, 0x1ef, 0x1f0, 0x1f1, 0x1f2, 0x1f3, 0x1f4, 0x1f5,
0x1f6, 0x1f6, 0x1f7, 0x1f8, 0x1f9, 0x1fa, 0x1fb, 0x1fc,
0x1fd, 0x1fe, 0x1ff, 0x200, 0x201, 0x201, 0x202, 0x203,
// First note of looped range used for all octaves:
0x204, 0x205, 0x206, 0x207, 0x208, 0x209, 0x20a, 0x20b, // 7
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, // 8
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, // 9
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, // 10
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, // 11
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, // 12
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, // 13
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, // 14
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, // 15
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, // 16
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, // 17
0x3a6, 0x3a7, 0x3a9, 0x3ab, 0x3ac, 0x3ae, 0x3b0, 0x3b1,
0x3b3, 0x3b5, 0x3b7, 0x3b8, 0x3ba, 0x3bc, 0x3bd, 0x3bf,
0x3c1, 0x3c3, 0x3c4, 0x3c6, 0x3c8, 0x3ca, 0x3cb, 0x3cd,
// The last note has an incomplete range, and loops round back to
// the start. Note that the last value is actually a buffer overrun
// and does not fit with the other values.
0x3cf, 0x3d1, 0x3d2, 0x3d4, 0x3d6, 0x3d8, 0x3da, 0x3db, // 18
0x3dd, 0x3df, 0x3e1, 0x3e3, 0x3e4, 0x3e6, 0x3e8, 0x3ea,
0x3ec, 0x3ed, 0x3ef, 0x3f1, 0x3f3, 0x3f5, 0x3f6, 0x3f8,
0x3fa, 0x3fc, 0x3fe, 0x36c,
};
void OPLio::WriteFrequency(uint32_t channel, uint32_t note, uint32_t pitch, uint32_t 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);
WriteValue (OPL_REGS_FREQ_1, channel, (uint8_t)i);
WriteValue (OPL_REGS_FREQ_2, channel, (uint8_t)(i>>8)|(keyon<<5));
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
static uint8_t 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};
void OPLio::WriteVolume(uint32_t channel, struct GenMidiVoice *voice, uint32_t vol1, uint32_t vol2, uint32_t vol3)
{
if (voice != nullptr)
{
uint32_t full_volume = volumetable[std::min<uint32_t>(127, (uint32_t)((uint64_t)vol1*vol2*vol3) / (127 * 127))];
int reg_volume2 = ((0x3f - voice->carrier.level) * full_volume) / 128;
reg_volume2 = (0x3f - reg_volume2) | voice->carrier.scale;
WriteOperator(OPL_REGS_LEVEL, channel, 1, reg_volume2);
int reg_volume1;
if (voice->feedback & 0x01)
{
// Chocolate Doom says:
// If we are using non-modulated feedback mode, we must set the
// volume for both voices.
// Note that the same register volume value is written for
// both voices, always calculated from the carrier's level
// value.
// But Muslib does it differently than the comment above states. Which one is correct?
reg_volume1 = ((0x3f - voice->modulator.level) * full_volume) / 128;
reg_volume1 = (0x3f - reg_volume1) | voice->modulator.scale;
}
else
{
reg_volume1 = voice->modulator.level | voice->modulator.scale;
}
WriteOperator(OPL_REGS_LEVEL, channel, 0,reg_volume1);
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void OPLio::WritePan(uint32_t channel, struct GenMidiVoice *voice, int pan)
{
if (voice != 0)
{
WriteValue(OPL_REGS_FEEDBACK, channel, voice->feedback | (pan >= 28 ? 0x20 : 0) | (pan <= 100 ? 0x10 : 0));
// Set real panning if we're using emulated chips.
int chanper = IsOPL3 ? OPL3_NUM_VOICES : OPL_NUM_VOICES;
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.
double level = (pan <= 1) ? 0 : (pan - 1) / 126.0;
chips[which]->SetPanning(channel % chanper,
(float)cos(HALF_PI * level), (float)sin(HALF_PI * level));
}
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void OPLio::WriteTremolo(uint32_t channel, struct GenMidiVoice *voice, bool vibrato)
{
int val1 = voice->modulator.tremolo, val2 = voice->carrier.tremolo;
if (vibrato)
{
if (voice->feedback & 1) val1 |= 0x40;
val2 |= 0x40;
}
WriteOperator(OPL_REGS_TREMOLO, channel, 1, val2);
WriteOperator(OPL_REGS_TREMOLO, channel, 0, val1);
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void OPLio::MuteChannel(uint32_t channel)
{
WriteOperator(OPL_REGS_LEVEL, channel, 1, NO_VOLUME);
WriteOperator(OPL_REGS_ATTACK, channel, 1, MAX_ATTACK_DECAY);
WriteOperator(OPL_REGS_SUSTAIN, channel, 1, NO_SUSTAIN_MAX_RELEASE);
WriteOperator(OPL_REGS_LEVEL, channel, 0, NO_VOLUME);
WriteOperator(OPL_REGS_ATTACK, channel, 0, MAX_ATTACK_DECAY);
WriteOperator(OPL_REGS_SUSTAIN, channel, 0, NO_SUSTAIN_MAX_RELEASE);
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void OPLio::LoadOperatorData(uint32_t channel, int op_index, genmidi_op_t *data, bool max_level, bool vibrato)
{
// The scale and level fields must be combined for the level register.
// For the carrier wave we always set the maximum level.
int level = data->scale;
if (max_level) level |= 0x3f;
else level |= data->level;
int tremolo = data->tremolo;
if (vibrato) tremolo |= 0x40;
WriteOperator(OPL_REGS_LEVEL, channel, op_index, level);
WriteOperator(OPL_REGS_TREMOLO, channel, op_index, tremolo);
WriteOperator(OPL_REGS_ATTACK, channel, op_index, data->attack);
WriteOperator(OPL_REGS_SUSTAIN, channel, op_index, data->sustain);
WriteOperator(OPL_REGS_WAVEFORM, channel, op_index, data->waveform);
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void OPLio::WriteInstrument(uint32_t channel, struct GenMidiVoice *voice, bool vibrato)
{
bool modulating = (voice->feedback & 0x01) == 0;
// Doom loads the second operator first, then the first.
// The carrier is set to minimum volume until the voice volume
// is set later. If we are not using modulating mode, we must set both to minimum volume.
LoadOperatorData(channel, 1, &voice->carrier, true, vibrato);
LoadOperatorData(channel, 0, &voice->modulator, !modulating, vibrato && modulating);
// The feedback register is written by the calling code.
}

View file

@ -1,60 +0,0 @@
#pragma once
#include <stdint.h>
#if defined(__GNUC__)
// With versions of GCC newer than 4.2, it appears it was determined that the
// cost of an unaligned pointer on PPC was high enough to add padding to the
// end of packed structs. For whatever reason __packed__ and pragma pack are
// handled differently in this regard. Note that this only needs to be applied
// to types which are used in arrays or sizeof is needed. This also prevents
// code from taking references to the struct members.
#define FORCE_PACKED __attribute__((__packed__))
#else
#define FORCE_PACKED
#endif
#pragma pack(push, 1)
struct genmidi_op_t
{
uint8_t tremolo;
uint8_t attack;
uint8_t sustain;
uint8_t waveform;
uint8_t scale;
uint8_t level;
} FORCE_PACKED;
struct GenMidiVoice
{
genmidi_op_t modulator;
uint8_t feedback;
genmidi_op_t carrier;
uint8_t unused;
int16_t base_note_offset;
} FORCE_PACKED;
struct GenMidiInstrument
{
uint16_t flags;
uint8_t fine_tuning;
uint8_t fixed_note;
GenMidiVoice voices[2];
} FORCE_PACKED;
#pragma pack(pop)
enum
{
GENMIDI_FLAG_FIXED = 0x0001, /* fixed pitch */
GENMIDI_FLAG_2VOICE = 0x0004 /* double voice (OPL3) */
};
enum
{
GENMIDI_NUM_INSTRS = 128,
GENMIDI_FIST_PERCUSSION = 35,
GENMIDI_NUM_PERCUSSION = 47,
GENMIDI_NUM_TOTAL = GENMIDI_NUM_INSTRS + GENMIDI_NUM_PERCUSSION
};

View file

@ -1,66 +0,0 @@
#pragma once
#include <stdint.h>
#include "genmidi.h"
#include "oplio.h"
struct OPLVoice
{
unsigned int index; // Index of this voice, or -1 if not in use.
unsigned int key; // The midi key that this voice is playing.
unsigned int note; // The note being played. This is normally the same as the key, but if the instrument is a fixed pitch instrument, it is different.
unsigned int note_volume; // The volume of the note being played on this channel.
GenMidiInstrument *current_instr; // Currently-loaded instrument data
GenMidiVoice *current_instr_voice;// The voice number in the instrument to use. This is normally set to the instrument's first voice; if this is a double voice instrument, it may be the second one
bool sustained;
int8_t fine_tuning;
int pitch;
uint32_t timestamp;
};
struct musicBlock {
musicBlock();
~musicBlock();
OPLChannel oplchannels[NUM_CHANNELS];
OPLio *io;
uint32_t timeCounter;
struct GenMidiInstrument OPLinstruments[GENMIDI_NUM_TOTAL];
void changeModulation(uint32_t id, int value);
void changeSustain(uint32_t id, int value);
void changeVolume(uint32_t id, int value, bool expression);
void changePanning(uint32_t id, int value);
void notesOff(uint32_t id, int value);
void allNotesOff(uint32_t id, int value);
void changeExtended(uint32_t channel, uint8_t controller, int value);
void resetControllers(uint32_t channel, int vol);
void programChange(uint32_t channel, int value);
void resetAllControllers(int vol);
void changePitch(uint32_t channel, int val1, int val2);
void noteOn(uint32_t channel, uint8_t note, int volume);
void noteOff(uint32_t channel, uint8_t note);
void stopAllVoices();
protected:
OPLVoice voices[NUM_VOICES];
int findFreeVoice();
int replaceExistingVoice();
void voiceKeyOn(uint32_t slot, uint32_t channo, GenMidiInstrument *instrument, uint32_t instrument_voice, uint32_t, uint32_t volume);
int releaseVoice(uint32_t slot, uint32_t killed);
friend class Stat_opl;
};
enum ExtCtrl {
ctrlRPNHi,
ctrlRPNLo,
ctrlNRPNHi,
ctrlNRPNLo,
ctrlDataEntryHi,
ctrlDataEntryLo,
};

View file

@ -1,240 +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 "musicblock.h"
namespace NukedOPL3
{
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;
// 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);
};
} // namespace NukedOPL3

View file

@ -1,255 +0,0 @@
//
// DESCRIPTION:
// Endianess handling, swapping 16bit and 32bit.
//
//-----------------------------------------------------------------------------
#ifndef __M_SWAP_H__
#define __M_SWAP_H__
#include <stdlib.h>
// Endianess handling.
// WAD files are stored little endian.
#ifdef __APPLE__
#include <libkern/OSByteOrder.h>
inline short LittleShort(short x)
{
return (short)OSSwapLittleToHostInt16((uint16_t)x);
}
inline unsigned short LittleShort(unsigned short x)
{
return OSSwapLittleToHostInt16(x);
}
inline short LittleShort(int x)
{
return OSSwapLittleToHostInt16((uint16_t)x);
}
inline unsigned short LittleShort(unsigned int x)
{
return OSSwapLittleToHostInt16((uint16_t)x);
}
inline int LittleLong(int x)
{
return OSSwapLittleToHostInt32((uint32_t)x);
}
inline unsigned int LittleLong(unsigned int x)
{
return OSSwapLittleToHostInt32(x);
}
inline short BigShort(short x)
{
return (short)OSSwapBigToHostInt16((uint16_t)x);
}
inline unsigned short BigShort(unsigned short x)
{
return OSSwapBigToHostInt16(x);
}
inline int BigLong(int x)
{
return OSSwapBigToHostInt32((uint32_t)x);
}
inline unsigned int BigLong(unsigned int x)
{
return OSSwapBigToHostInt32(x);
}
#elif defined __BIG_ENDIAN__
// Swap 16bit, that is, MSB and LSB byte.
// No masking with 0xFF should be necessary.
inline short LittleShort (short x)
{
return (short)((((unsigned short)x)>>8) | (((unsigned short)x)<<8));
}
inline unsigned short LittleShort (unsigned short x)
{
return (unsigned short)((x>>8) | (x<<8));
}
inline short LittleShort (int x)
{
return LittleShort((short)x);
}
inline unsigned short LittleShort (unsigned int x)
{
return LittleShort((unsigned short)x);
}
// Swapping 32bit.
inline unsigned int LittleLong (unsigned int x)
{
return (unsigned int)(
(x>>24)
| ((x>>8) & 0xff00)
| ((x<<8) & 0xff0000)
| (x<<24));
}
inline int LittleLong (int x)
{
return (int)(
(((unsigned int)x)>>24)
| ((((unsigned int)x)>>8) & 0xff00)
| ((((unsigned int)x)<<8) & 0xff0000)
| (((unsigned int)x)<<24));
}
inline short BigShort(short x)
{
return x;
}
inline unsigned short BigShort(unsigned short x)
{
return x;
}
inline unsigned int BigLong(unsigned int x)
{
return x;
}
inline int BigLong(int x)
{
return x;
}
#else
inline short LittleShort(short x)
{
return x;
}
inline unsigned short LittleShort(unsigned short x)
{
return x;
}
inline unsigned int LittleLong(unsigned int x)
{
return x;
}
inline int LittleLong(int x)
{
return x;
}
#ifdef _MSC_VER
inline short BigShort(short x)
{
return (short)_byteswap_ushort((unsigned short)x);
}
inline unsigned short BigShort(unsigned short x)
{
return _byteswap_ushort(x);
}
inline int BigLong(int x)
{
return (int)_byteswap_ulong((unsigned long)x);
}
inline unsigned int BigLong(unsigned int x)
{
return (unsigned int)_byteswap_ulong((unsigned long)x);
}
#pragma warning (default: 4035)
#else
inline short BigShort (short x)
{
return (short)((((unsigned short)x)>>8) | (((unsigned short)x)<<8));
}
inline unsigned short BigShort (unsigned short x)
{
return (unsigned short)((x>>8) | (x<<8));
}
inline unsigned int BigLong (unsigned int x)
{
return (unsigned int)(
(x>>24)
| ((x>>8) & 0xff00)
| ((x<<8) & 0xff0000)
| (x<<24));
}
inline int BigLong (int x)
{
return (int)(
(((unsigned int)x)>>24)
| ((((unsigned int)x)>>8) & 0xff00)
| ((((unsigned int)x)<<8) & 0xff0000)
| (((unsigned int)x)<<24));
}
#endif
#endif // __BIG_ENDIAN__
// These may be destructive so they should create errors
unsigned long BigLong(unsigned long) = delete;
long BigLong(long) = delete;
unsigned long LittleLong(unsigned long) = delete;
long LittleLong(long) = delete;
// Data accessors, since some data is highly likely to be unaligned.
#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__)
inline int GetShort(const unsigned char *foo)
{
return *(const short *)foo;
}
inline int GetInt(const unsigned char *foo)
{
return *(const int *)foo;
}
#else
inline int GetShort(const unsigned char *foo)
{
return short(foo[0] | (foo[1] << 8));
}
inline int GetInt(const unsigned char *foo)
{
return int(foo[0] | (foo[1] << 8) | (foo[2] << 16) | (foo[3] << 24));
}
#endif
inline int GetBigInt(const unsigned char *foo)
{
return int((foo[0] << 24) | (foo[1] << 16) | (foo[2] << 8) | foo[3]);
}
#ifdef __BIG_ENDIAN__
inline int GetNativeInt(const unsigned char *foo)
{
return GetBigInt(foo);
}
#else
inline int GetNativeInt(const unsigned char *foo)
{
return GetInt(foo);
}
#endif
#endif // __M_SWAP_H__

View file

@ -1,29 +0,0 @@
#ifndef OPL_H
#define OPL_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 */
#define ADLIB_CLOCK_MUL 24.0
#endif

View file

@ -1,238 +0,0 @@
// ====================================================================================================================
// ====================================================================================================================
// xs_Float.h
//
// Source: "Know Your FPU: Fixing Floating Fast"
// http://www.stereopsis.com/sree/fpu2006.html
//
// xs_CRoundToInt: Round toward nearest, but ties round toward even (just like FISTP)
// xs_ToInt: Round toward zero, just like the C (int) cast
// xs_FloorToInt: Round down
// xs_CeilToInt: Round up
// xs_RoundToInt: Round toward nearest, but ties round up
// ====================================================================================================================
// ====================================================================================================================
#ifndef _xs_FLOAT_H_
#define _xs_FLOAT_H_
#include <stdint.h>
// ====================================================================================================================
// Defines
// ====================================================================================================================
#ifndef _xs_DEFAULT_CONVERSION
#define _xs_DEFAULT_CONVERSION 0
#endif //_xs_DEFAULT_CONVERSION
#if __BIG_ENDIAN__
#define _xs_iexp_ 0
#define _xs_iman_ 1
#else
#define _xs_iexp_ 1 //intel is little endian
#define _xs_iman_ 0
#endif //BigEndian_
#ifdef __GNUC__
#define finline inline
#else
#define finline __forceinline
#endif
typedef double real64;
union _xs_doubleints
{
real64 val;
uint32_t ival[2];
};
#if 0
#define _xs_doublecopysgn(a,b) ((int32_t*)&a)[_xs_iexp_]&=~(((int32_t*)&b)[_xs_iexp_]&0x80000000)
#define _xs_doubleisnegative(a) ((((int32_t*)&a)[_xs_iexp_])|0x80000000)
#endif
// ====================================================================================================================
// Constants
// ====================================================================================================================
const real64 _xs_doublemagic = real64 (6755399441055744.0); //2^52 * 1.5, uses limited precisicion to floor
const real64 _xs_doublemagicdelta = (1.5e-8); //almost .5f = .5f + 1e^(number of exp bit)
const real64 _xs_doublemagicroundeps = (.5f-_xs_doublemagicdelta); //almost .5f = .5f - 1e^(number of exp bit)
// ====================================================================================================================
// Prototypes
// ====================================================================================================================
static int32_t xs_CRoundToInt (real64 val, real64 dmr = _xs_doublemagic);
static int32_t xs_ToInt (real64 val, real64 dme = -_xs_doublemagicroundeps);
static int32_t xs_FloorToInt (real64 val, real64 dme = _xs_doublemagicroundeps);
static int32_t xs_CeilToInt (real64 val, real64 dme = _xs_doublemagicroundeps);
static int32_t xs_RoundToInt (real64 val);
//int32_t versions
finline static int32_t xs_CRoundToInt (int32_t val) {return val;}
finline static int32_t xs_ToInt (int32_t val) {return val;}
// ====================================================================================================================
// Fix Class
// ====================================================================================================================
template <int32_t N> class xs_Fix
{
public:
typedef int32_t Fix;
// ====================================================================================================================
// Basic Conversion from Numbers
// ====================================================================================================================
finline static Fix ToFix (int32_t val) {return val<<N;}
finline static Fix ToFix (real64 val) {return xs_ConvertToFixed(val);}
// ====================================================================================================================
// Basic Conversion to Numbers
// ====================================================================================================================
finline static real64 ToReal (Fix f) {return real64(f)/real64(1<<N);}
finline static int32_t ToInt (Fix f) {return f>>N;}
protected:
// ====================================================================================================================
// Helper function - mainly to preserve _xs_DEFAULT_CONVERSION
// ====================================================================================================================
finline static int32_t xs_ConvertToFixed (real64 val)
{
#if _xs_DEFAULT_CONVERSION==0
return xs_CRoundToInt(val, _xs_doublemagic/(1<<N));
#else
return (long)((val)*(1<<N));
#endif
}
};
finline static int32_t xs_ToFixed(int32_t n, real64 val)
{
#if _xs_DEFAULT_CONVERSION==0
return xs_CRoundToInt(val, _xs_doublemagic/(1<<n));
#else
return (long)((val)*(1<<N));
#endif
}
// ====================================================================================================================
// ====================================================================================================================
// Inline implementation
// ====================================================================================================================
// ====================================================================================================================
finline static int32_t xs_CRoundToInt(real64 val, real64 dmr)
{
#if _xs_DEFAULT_CONVERSION==0
_xs_doubleints uval;
uval.val = val + dmr;
return uval.ival[_xs_iman_];
#else
return int32_t(floor(val+.5));
#endif
}
// ====================================================================================================================
finline static int32_t xs_ToInt(real64 val, real64 dme)
{
/* unused - something else I tried...
_xs_doublecopysgn(dme,val);
return xs_CRoundToInt(val+dme);
return 0;
*/
#if _MSC_VER >= 1400
// VC++ 2005's standard cast is a little bit faster than this
// magic number code. (Which is pretty amazing!) SSE has the
// fastest C-style float->int conversion, but unfortunately,
// checking for SSE support every time you need to do a
// conversion completely negates its performance advantage.
return int32_t(val);
#else
#if _xs_DEFAULT_CONVERSION==0
return (val<0) ? xs_CRoundToInt(val-dme) :
xs_CRoundToInt(val+dme);
#else
return int32_t(val);
#endif
#endif
}
// ====================================================================================================================
finline static int32_t xs_FloorToInt(real64 val, real64 dme)
{
#if _xs_DEFAULT_CONVERSION==0
return xs_CRoundToInt (val - dme);
#else
return floor(val);
#endif
}
// ====================================================================================================================
finline static int32_t xs_CeilToInt(real64 val, real64 dme)
{
#if _xs_DEFAULT_CONVERSION==0
return xs_CRoundToInt (val + dme);
#else
return ceil(val);
#endif
}
// ====================================================================================================================
finline static int32_t xs_RoundToInt(real64 val)
{
#if _xs_DEFAULT_CONVERSION==0
// Yes, it is important that two fadds be generated, so you cannot override the dmr
// passed to xs_CRoundToInt with _xs_doublemagic + _xs_doublemagicdelta. If you do,
// you'll end up with Banker's Rounding again.
return xs_CRoundToInt (val + _xs_doublemagicdelta);
#else
return floor(val+.5);
#endif
}
// ====================================================================================================================
// ====================================================================================================================
// Unsigned variants
// ====================================================================================================================
// ====================================================================================================================
finline static uint32_t xs_CRoundToUInt(real64 val)
{
return (uint32_t)xs_CRoundToInt(val);
}
finline static uint32_t xs_FloorToUInt(real64 val)
{
return (uint32_t)xs_FloorToInt(val);
}
finline static uint32_t xs_CeilToUInt(real64 val)
{
return (uint32_t)xs_CeilToInt(val);
}
finline static uint32_t xs_RoundToUInt(real64 val)
{
return (uint32_t)xs_RoundToInt(val);
}
// ====================================================================================================================
// ====================================================================================================================
#endif // _xs_FLOAT_H_

View file

@ -1,51 +0,0 @@
#pragma once
#include <mutex>
#include <vector>
#include <string>
#include "musicblock.h"
class OPLmusicBlock : public musicBlock
{
public:
OPLmusicBlock(int core, int numchips);
virtual ~OPLmusicBlock();
bool ServiceStream(void *buff, int numbytes);
void ResetChips(int numchips);
virtual void Restart();
protected:
virtual int PlayTick() = 0;
void OffsetSamples(float *buff, int count);
uint8_t *score;
uint8_t *scoredata;
double NextTickIn;
double SamplesPerTick;
double LastOffset;
int playingcount;
int NumChips;
int currentCore;
bool Looping;
bool FullPan;
};
class OPLmusicFile : public OPLmusicBlock
{
public:
OPLmusicFile(const void *data, size_t length, int core, int numchips, const char *&errormessage);
virtual ~OPLmusicFile();
bool IsValid() const;
void SetLooping(bool loop);
void Restart();
protected:
OPLmusicFile(int core, int numchips) : OPLmusicBlock(core, numchips) {}
int PlayTick();
enum { RDosPlay, IMF, DosBox1, DosBox2 } RawPlayer;
int ScoreLen;
int WhichChip;
};

View file

@ -1,99 +0,0 @@
#pragma once
enum
{
// Operator registers (21 of each):
OPL_REGS_TREMOLO = 0x20,
OPL_REGS_LEVEL = 0x40,
OPL_REGS_ATTACK = 0x60,
OPL_REGS_SUSTAIN = 0x80,
OPL_REGS_WAVEFORM = 0xE0,
// Voice registers (9 of each):
OPL_REGS_FREQ_1 = 0xA0,
OPL_REGS_FREQ_2 = 0xB0,
OPL_REGS_FEEDBACK = 0xC0,
};
enum
{
OPL_REG_WAVEFORM_ENABLE = 0x01,
OPL_REG_TIMER1 = 0x02,
OPL_REG_TIMER2 = 0x03,
OPL_REG_TIMER_CTRL = 0x04,
OPL_REG_FM_MODE = 0x08,
OPL_REG_PERCUSSION_MODE = 0xBD,
OPL_REG_OPL3_ENABLE = 0x105,
OPL_REG_4OPMODE_ENABLE = 0x104,
};
enum
{
NO_VOLUME = 0x3f,
MAX_ATTACK_DECAY = 0xff,
NO_SUSTAIN_MAX_RELEASE = 0xf,
WAVEFORM_ENABLED = 0x20
};
enum
{
OPL_NUM_VOICES = 9,
OPL3_NUM_VOICES = 18,
MAXOPL2CHIPS = 8,
NUM_VOICES = (OPL_NUM_VOICES * MAXOPL2CHIPS),
NUM_CHANNELS = 16,
CHAN_PERCUSSION = 15,
VIBRATO_THRESHOLD = 40,
MIN_SUSTAIN = 0x40,
HIGHEST_NOTE = 127,
};
struct GenMidiVoice;
struct genmidi_op_t;
struct OPLio
{
virtual ~OPLio();
void WriteOperator(uint32_t regbase, uint32_t channel, int index, uint8_t data2);
void LoadOperatorData(uint32_t channel, int op_index, genmidi_op_t *op_data, bool maxlevel, bool vibrato);
void WriteValue(uint32_t regbase, uint32_t channel, uint8_t value);
void WriteFrequency(uint32_t channel, uint32_t freq, uint32_t octave, uint32_t keyon);
void WriteVolume(uint32_t channel, GenMidiVoice *voice, uint32_t v1, uint32_t v2, uint32_t v3);
void WritePan(uint32_t channel, GenMidiVoice *voice, int pan);
void WriteTremolo(uint32_t channel, GenMidiVoice *voice, bool vibrato);
void WriteInstrument(uint32_t channel, GenMidiVoice *voice, bool vibrato);
void WriteInitState(bool opl3);
void MuteChannel(uint32_t chan);
void StopPlayback();
virtual int Init(int core, uint32_t numchips, bool stereo, bool initopl3);
virtual void Reset();
virtual void WriteRegister(int which, uint32_t reg, uint8_t data);
virtual void SetClockRate(double samples_per_tick);
virtual void WriteDelay(int ticks);
class OPLEmul *chips[OPL_NUM_VOICES];
uint32_t NumChannels;
uint32_t NumChips;
bool IsOPL3;
};
struct OPLChannel
{
uint32_t Instrument;
uint8_t Volume;
uint8_t Panning;
int8_t Pitch;
uint8_t Sustain;
bool Vibrato;
uint8_t Expression;
uint16_t PitchSensitivity;
uint16_t RPN;
};

View file

@ -41,7 +41,7 @@ else()
endif()
include_directories( "${CMAKE_CURRENT_SOURCE_DIR}/../libraries/dumb/include" "${ZLIB_INCLUDE_DIR}" "${TIMIDITYPP_INCLUDE_DIR}" "${OPLSYNTH_INCLUDE_DIR}" "${GME_INCLUDE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" )
include_directories( "${CMAKE_CURRENT_SOURCE_DIR}/../libraries/dumb/include" "${ZLIB_INCLUDE_DIR}" "${TIMIDITYPP_INCLUDE_DIR}" "${GME_INCLUDE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" )
if (WIN32)
set( PLAT_SOURCES
@ -65,7 +65,6 @@ add_library( zmusic STATIC
${HEADER_FILES}
i_module.cpp
mididevices/music_base_mididevice.cpp
mididevices/music_opl_mididevice.cpp
mididevices/music_timiditypp_mididevice.cpp
mididevices/music_fluidsynth_mididevice.cpp
mididevices/music_softsynth_mididevice.cpp
@ -78,7 +77,6 @@ add_library( zmusic STATIC
streamsources/music_dumb.cpp
streamsources/music_gme.cpp
streamsources/music_libsndfile.cpp
streamsources/music_opl.cpp
streamsources/music_xa.cpp
musicformats/music_stream.cpp
musicformats/music_midi.cpp
@ -90,7 +88,7 @@ add_library( zmusic STATIC
zmusic/zmusic.cpp
${PLAT_SOURCES}
)
target_link_libraries( zmusic dumb gme oplsynth timidityplus)
target_link_libraries( zmusic dumb gme timidityplus)
if( NOT DYN_SNDFILE AND SNDFILE_FOUND )
include_directories( "${SNDFILE_INCLUDE_DIRS}" )

View file

@ -1,326 +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.
**---------------------------------------------------------------------------
**
**
*/
// HEADER FILES ------------------------------------------------------------
#include "mididevice.h"
#include "zmusic/mus2midi.h"
#include "oplsynth/opl.h"
#include "oplsynth/opl_mus_player.h"
// MACROS ------------------------------------------------------------------
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
// PRIVATE DATA DEFINITIONS ------------------------------------------------
OPLConfig oplConfig;
// OPL implementation of a MIDI output device -------------------------------
class OPLMIDIDevice : public SoftSynthMIDIDevice, protected OPLmusicBlock
{
public:
OPLMIDIDevice(int core);
int OpenRenderer() override;
void Close() override;
int GetTechnology() const override;
std::string GetStats() override;
protected:
void CalcTickRate() override;
int PlayTick() override;
void HandleEvent(int status, int parm1, int parm2) override;
void HandleLongEvent(const uint8_t *data, int len) override;
void ComputeOutput(float *buffer, int len) override;
bool ServiceStream(void *buff, int numbytes) override;
int GetDeviceType() const override { return MDEV_OPL; }
};
// PUBLIC DATA DEFINITIONS -------------------------------------------------
// CODE --------------------------------------------------------------------
//==========================================================================
//
// OPLMIDIDevice Contructor
//
//==========================================================================
OPLMIDIDevice::OPLMIDIDevice(int core)
: SoftSynthMIDIDevice((int)OPL_SAMPLE_RATE), OPLmusicBlock(core, oplConfig.numchips)
{
FullPan = oplConfig.fullpan;
memcpy(OPLinstruments, oplConfig.OPLinstruments, sizeof(OPLinstruments));
StreamBlockSize = 14;
}
//==========================================================================
//
// OPLMIDIDevice :: Open
//
// Returns 0 on success.
//
//==========================================================================
int OPLMIDIDevice::OpenRenderer()
{
if (io == NULL || 0 == (NumChips = io->Init(currentCore, NumChips, FullPan, true)))
{
return 1;
}
isMono = !FullPan && !io->IsOPL3;
stopAllVoices();
resetAllControllers(100);
return 0;
}
//==========================================================================
//
// OPLMIDIDevice :: Close
//
//==========================================================================
void OPLMIDIDevice::Close()
{
SoftSynthMIDIDevice::Close();
io->Reset();
}
//==========================================================================
//
// OPLMIDIDevice :: GetTechnology
//
//==========================================================================
int OPLMIDIDevice::GetTechnology() const
{
return MIDIDEV_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 voices 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--;
noteOff(channel, parm1);
break;
case MIDI_NOTEON:
playingcount++;
noteOn(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)
{
// some controllers here get passed on but are not handled by the player.
//case 0: changeBank(channel, parm2); break;
case 1: changeModulation(channel, parm2); break;
case 6: changeExtended(channel, ctrlDataEntryHi, parm2); break;
case 7: changeVolume(channel, parm2, false); break;
case 10: changePanning(channel, parm2); break;
case 11: changeVolume(channel, parm2, true); break;
case 38: changeExtended(channel, ctrlDataEntryLo, parm2); break;
case 64: changeSustain(channel, parm2); break;
//case 67: changeSoftPedal(channel, parm2); break;
//case 91: changeReverb(channel, parm2); break;
//case 93: changeChorus(channel, parm2); break;
case 98: changeExtended(channel, ctrlNRPNLo, parm2); break;
case 99: changeExtended(channel, ctrlNRPNHi, parm2); break;
case 100: changeExtended(channel, ctrlRPNLo, parm2); break;
case 101: changeExtended(channel, ctrlRPNHi, parm2); break;
case 120: allNotesOff(channel, parm2); break;
case 121: resetControllers(channel, 100); break;
case 123: notesOff(channel, parm2); break;
//case 126: changeMono(channel, parm2); break;
//case 127: changePoly(channel, parm2); break;
default:
//DEBUGOUT("Unhandled controller: Channel %d, controller %d, value %d\n", channel, parm1, parm2);
break;
}
break;
case MIDI_PRGMCHANGE:
programChange(channel, parm1);
break;
case MIDI_CHANPRESS:
//DEBUGOUT("Unhandled channel aftertouch: Channel %d, value %d\n", channel, parm1, 0);
break;
case MIDI_PITCHBEND:
changePitch(channel, parm1, parm2);
break;
}
}
//==========================================================================
//
// OPLMIDIDevice :: HandleLongEvent
//
//==========================================================================
void OPLMIDIDevice::HandleLongEvent(const uint8_t *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
//
//==========================================================================
std::string OPLMIDIDevice::GetStats()
{
std::string out;
for (uint32_t i = 0; i < io->NumChannels; ++i)
{
char star = '*';
if (voices[i].index == ~0u)
{
star = '/';
}
else if (voices[i].sustained)
{
star = '-';
}
else if (voices[i].current_instr_voice == &voices[i].current_instr->voices[1])
{
star = '\\';
}
else
{
star = '+';
}
out += star;
}
return out;
}
MIDIDevice* CreateOplMIDIDevice(const char *Args)
{
if (!oplConfig.genmidiset) throw std::runtime_error("Cannot play OPL without GENMIDI data");
int core = oplConfig.core;
if (Args != NULL && *Args >= '0' && *Args < '4') core = *Args - '0';
return new OPLMIDIDevice(core);
}

View file

@ -195,21 +195,6 @@ bool MIDIStreamer::IsValid() const
EMidiDevice MIDIStreamer::SelectMIDIDevice(EMidiDevice device)
{
/* MIDI are played as:
- OPL:
- if explicitly selected by $mididevice
- when snd_mididevice is -3 and no midi device is set for the song
- Timidity:
- if explicitly selected by $mididevice
- when snd_mididevice is -2 and no midi device is set for the song
- MMAPI (Win32 only):
- if explicitly selected by $mididevice (non-Win32 redirects this to Sound System)
- when snd_mididevice is >= 0 and no midi device is set for the song
- as fallback when both OPL and Timidity failed and snd_mididevice is >= 0
*/
// Choose the type of MIDI device we want.
if (device != MDEV_DEFAULT)
{
@ -220,7 +205,6 @@ EMidiDevice MIDIStreamer::SelectMIDIDevice(EMidiDevice device)
case -1: return MDEV_SNDSYS;
case -4:
case -2: return MDEV_TIMIDITY;
case -3: return MDEV_OPL;
case -5: return MDEV_FLUIDSYNTH;
default:
#ifdef _WIN32
@ -267,10 +251,6 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype, int samplerate)
break;
#endif // HAVE_FLUIDSYNTH
case MDEV_OPL:
dev = CreateOplMIDIDevice(Args.c_str());
break;
case MDEV_TIMIDITY:
dev = CreateTimidityPPMIDIDevice(Args.c_str(), samplerate);
break;
@ -291,7 +271,6 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype, int samplerate)
#ifdef _WIN32
else if (!checked[MDEV_MMAPI]) devtype = MDEV_MMAPI;
#endif
else if (!checked[MDEV_OPL]) devtype = MDEV_OPL;
if (devtype == MDEV_DEFAULT)
{
@ -304,7 +283,7 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype, int samplerate)
{
static const char *devnames[] = {
"Windows Default",
"OPL",
"",
"",
"Timidity++",
"FluidSynth",

View file

@ -1,151 +0,0 @@
/*
** music_opl.cpp
** Plays raw OPL formats
**
**---------------------------------------------------------------------------
** Copyright 1998-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.
**---------------------------------------------------------------------------
*/
#include "streamsource.h"
#include "oplsynth/opl.h"
#include "oplsynth/opl_mus_player.h"
#include "../../libraries/music_common/fileio.h"
#include "zmusic/midiconfig.h"
//==========================================================================
//
// OPL file played by a software OPL2 synth and streamed through the sound system
//
//==========================================================================
class OPLMUSSong : public StreamSource
{
public:
OPLMUSSong (MusicIO::FileInterface *reader, OPLConfig *config);
~OPLMUSSong ();
bool Start() override;
void ChangeSettingInt(const char *name, int value) override;
SoundStreamInfo GetFormat() override;
protected:
bool GetData(void *buffer, size_t len) override;
OPLmusicFile *Music;
int current_opl_core;
};
//==========================================================================
//
//
//
//==========================================================================
OPLMUSSong::OPLMUSSong(MusicIO::FileInterface* reader, OPLConfig* config)
{
const char* error = nullptr;
reader->seek(0, SEEK_END);
auto fs = reader->tell();
reader->seek(0, SEEK_SET);
std::vector<uint8_t> data(fs);
reader->read(data.data(), (int)data.size());
Music = new OPLmusicFile(data.data(), data.size(), config->core, config->numchips, error);
if (error)
{
delete Music;
throw std::runtime_error(error);
}
}
//==========================================================================
//
//
//
//==========================================================================
SoundStreamInfo OPLMUSSong::GetFormat()
{
int samples = int(OPL_SAMPLE_RATE / 14);
return { samples * 4, int(OPL_SAMPLE_RATE), current_opl_core == 0? 1:2 };
}
//==========================================================================
//
//
//
//==========================================================================
OPLMUSSong::~OPLMUSSong ()
{
if (Music != NULL)
{
delete Music;
}
}
//==========================================================================
//
//
//
//==========================================================================
void OPLMUSSong::ChangeSettingInt(const char * name, int val)
{
if (!strcmp(name, "opl.numchips"))
Music->ResetChips (val);
}
//==========================================================================
//
//
//
//==========================================================================
bool OPLMUSSong::Start()
{
Music->SetLooping (m_Looping);
Music->Restart ();
return true;
}
//==========================================================================
//
//
//
//==========================================================================
bool OPLMUSSong::GetData(void *buffer, size_t len)
{
return Music->ServiceStream(buffer, int(len)) ? len : 0;
}
StreamSource *OPL_OpenSong(MusicIO::FileInterface* reader, OPLConfig *config)
{
return new OPLMUSSong(reader, config);
}

View file

@ -33,7 +33,6 @@
*/
#include <algorithm>
#include "timiditypp/timidity.h"
#include "oplsynth/oplio.h"
#include "../../libraries/dumb/include/dumb.h"
#include "zmusic.h"
@ -61,8 +60,6 @@ void ZMusic_SetCallbacks(const Callbacks* cb)
void ZMusic_SetGenMidi(const uint8_t* data)
{
memcpy(oplConfig.OPLinstruments, data, 175 * 36);
oplConfig.genmidiset = true;
}
template<class valtype>
@ -224,28 +221,6 @@ bool ChangeMusicSetting(ZMusic::EIntConfigKey key, MusInfo *currSong, int value,
ChangeAndReturn(fluidConfig.fluid_chorus_type, value, pRealValue);
return false;
case opl_numchips:
if (value <= 0)
value = 1;
else if (value > MAXOPL2CHIPS)
value = MAXOPL2CHIPS;
if (currSong != NULL)
currSong->ChangeSettingInt("opl.numchips", value);
ChangeAndReturn(oplConfig.numchips, value, pRealValue);
return false;
case opl_core:
if (value < 0) value = 0;
else if (value > 3) value = 3;
ChangeAndReturn(oplConfig.core, value, pRealValue);
return devType() == MDEV_OPL;
case opl_fullpan:
ChangeAndReturn(oplConfig.fullpan, value, pRealValue);
return false;
case timidity_modulation_wheel:
ChangeVarSync(TimidityPlus::timidity_modulation_wheel, value);
if (pRealValue) *pRealValue = value;

View file

@ -56,7 +56,6 @@
#define GZIP_FCOMMENT 16
class MIDIDevice;
class OPLmusicFile;
class StreamSource;
class MusInfo;
@ -230,14 +229,7 @@ MusInfo *ZMusic_OpenSong (MusicIO::FileInterface *reader, EMidiDevice device, co
// Check for various raw OPL formats
else
{
if (
(id[0] == MAKE_ID('R', 'A', 'W', 'A') && id[1] == MAKE_ID('D', 'A', 'T', 'A')) || // Rdos 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') && *((uint8_t*)id + 4) == 'B')) // Martin Fernandez's modified IMF
{
streamsource = OPL_OpenSong(reader, &oplConfig);
}
else if ((id[0] == MAKE_ID('R', 'I', 'F', 'F') && id[2] == MAKE_ID('C', 'D', 'X', 'A')))
if ((id[0] == MAKE_ID('R', 'I', 'F', 'F') && id[2] == MAKE_ID('C', 'D', 'X', 'A')))
{
streamsource = XA_OpenSong(reader); // this takes over the reader.
reader = nullptr; // We do not own this anymore.

View file

@ -961,7 +961,6 @@ 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( APPLE )

View file

@ -215,29 +215,6 @@ static MusicIO::SoundFontReaderInterface* mus_openSoundFont(const char* sfname,
}
//==========================================================================
//
// Pass some basic working data to the music backend
// We do this once at startup for everything available.
//
//==========================================================================
static void SetupGenMidi()
{
// The OPL renderer should not care about where this comes from.
// Note: No I_Error here - this needs to be consistent with the rest of the music code.
auto lump = fileSystem.FindFile("engine/genmidi.op2");
if (lump < 0)
{
Printf("No GENMIDI lump found. OPL playback not available.");
return;
}
auto data = fileSystem.OpenFileReader(lump);
auto genmidi = data.Read();
if (genmidi.Size() < 8 + 175 * 36 || memcmp(genmidi.Data(), "#OPL_II#", 8)) return;
ZMusic_SetGenMidi(genmidi.Data()+8);
}
//==========================================================================
//
@ -268,7 +245,6 @@ void Mus_Init(void)
//callbacks.DumbVorbisDecode = dumb_decode_vorbis_;
ZMusic_SetCallbacks(&callbacks);
SetupGenMidi();
timerSetCallback(S_UpdateMusic);
}

Binary file not shown.