#pragma once
#include "BuildSettings.h"
#include "Snd_defs.h"
class Opal;
OPENMPT_NAMESPACE_BEGIN
class OPL
{
public:
enum OPLRegisters : uint8
{
AM_VIB = 0x20, KSL_LEVEL = 0x40, ATTACK_DECAY = 0x60, SUSTAIN_RELEASE = 0x80, WAVE_SELECT = 0xE0,
FNUM_LOW = 0xA0, KEYON_BLOCK = 0xB0, FEEDBACK_CONNECTION = 0xC0, };
enum OPLValues : uint8
{
TREMOLO_ON = 0x80,
VIBRATO_ON = 0x40,
SUSTAIN_ON = 0x20,
KSR = 0x10, MULTIPLE_MASK = 0x0F,
KSL_MASK = 0xC0, TOTAL_LEVEL_MASK = 0x3F,
ATTACK_MASK = 0xF0,
DECAY_MASK = 0x0F,
SUSTAIN_MASK = 0xF0,
RELEASE_MASK = 0x0F,
KEYON_BIT = 0x20,
FEEDBACK_MASK = 0x0E, CONNECTION_BIT = 0x01,
VOICE_TO_LEFT = 0x10,
VOICE_TO_RIGHT = 0x20,
STEREO_BITS = VOICE_TO_LEFT | VOICE_TO_RIGHT,
};
OPL();
~OPL();
void Initialize(uint32 samplerate);
void Mix(int32 *buffer, size_t count, uint32 volumeFactorQ16);
void NoteOff(CHANNELINDEX c);
void NoteCut(CHANNELINDEX c);
void Frequency(CHANNELINDEX c, uint32 milliHertz, bool keyOff, bool beatingOscillators);
void Volume(CHANNELINDEX c, uint8 vol, bool applyToModulator);
int8 Pan(CHANNELINDEX c, int32 pan);
void Patch(CHANNELINDEX c, const OPLPatch &patch);
void Reset();
bool IsActive(CHANNELINDEX c) { return GetVoice(c) != OPL_CHANNEL_INVALID; }
void MoveChannel(CHANNELINDEX from, CHANNELINDEX to);
protected:
static uint16 ChannelToRegister(uint8 oplCh);
static uint16 OperatorToRegister(uint8 oplCh);
static uint8 CalcVolume(uint8 trackerVol, uint8 kslVolume);
uint8 GetVoice(CHANNELINDEX c) const;
uint8 AllocateVoice(CHANNELINDEX c);
enum
{
OPL_CHANNELS = 18, OPL_CHANNEL_INVALID = 0xFF,
OPL_BASERATE = 49716,
};
std::unique_ptr<Opal> m_opl;
std::array<uint8, OPL_CHANNELS> m_KeyOnBlock;
std::array<CHANNELINDEX, OPL_CHANNELS> m_OPLtoChan;
std::array<uint8, MAX_CHANNELS> m_ChanToOPL;
std::array<OPLPatch, OPL_CHANNELS> m_Patches;
bool m_isActive = false;
};
OPENMPT_NAMESPACE_END