File: OPL.h

package info (click to toggle)
libopenmpt 0.8.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 9,844 kB
  • sloc: cpp: 129,441; sh: 4,695; ansic: 1,107; makefile: 712
file content (135 lines) | stat: -rw-r--r-- 4,347 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
 * OPL.h
 * -----
 * Purpose: Translate data coming from OpenMPT's mixer into OPL commands to be sent to the Opal emulator.
 * Notes  : (currently none)
 * Authors: OpenMPT Devs
 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
 */

#pragma once

#include "openmpt/all/BuildSettings.hpp"

#include "Snd_defs.h"

OPENMPT_NAMESPACE_BEGIN

class Opal;

class OPL
{
public:
	enum OPLRegisters : uint8
	{
		// Operators (combine with result of OperatorToRegister)
		AM_VIB          = 0x20,  // AM / VIB / EG / KSR / Multiple (0x20 to 0x35)
		KSL_LEVEL       = 0x40,  // KSL / Total level (0x40 to 0x55)
		ATTACK_DECAY    = 0x60,  // Attack rate / Decay rate (0x60 to 0x75)
		SUSTAIN_RELEASE = 0x80,  // Sustain level / Release rate (0x80 to 0x95)
		WAVE_SELECT     = 0xE0,  // Wave select (0xE0 to 0xF5)

		// Channels (combine with result of ChannelToRegister)
		FNUM_LOW            = 0xA0,  // F-number low bits (0xA0 to 0xA8)
		KEYON_BLOCK         = 0xB0,  // F-number high bits / Key on / Block (octave) (0xB0 to 0xB8)
		FEEDBACK_CONNECTION = 0xC0,  // Feedback / Connection (0xC0 to 0xC8)

		TREMOLO_VIBRATO_DEPTH = 0xBD,  // Tremolo Depth / Vibrato Depth / Percussion Mode / BD/SD/TT/CY/HH Key-On
	};

	enum OPLValues : uint8
	{
		// AM_VIB
		TREMOLO_ON       = 0x80,
		VIBRATO_ON       = 0x40,
		SUSTAIN_ON       = 0x20,
		KSR              = 0x10,  // Key scaling rate
		MULTIPLE_MASK    = 0x0F,  // Frequency multiplier

		// KSL_LEVEL
		KSL_MASK         = 0xC0,  // Envelope scaling bits
		TOTAL_LEVEL_MASK = 0x3F,  // Strength (volume) of OP

		// ATTACK_DECAY
		ATTACK_MASK      = 0xF0,
		DECAY_MASK       = 0x0F,

		// SUSTAIN_RELEASE
		SUSTAIN_MASK     = 0xF0,
		RELEASE_MASK     = 0x0F,

		// KEYON_BLOCK
		KEYON_BIT        = 0x20,

		// FEEDBACK_CONNECTION
		FEEDBACK_MASK    = 0x0E,  // Valid just for first OP of a voice
		CONNECTION_BIT   = 0x01,
		VOICE_TO_LEFT    = 0x10,
		VOICE_TO_RIGHT   = 0x20,
		STEREO_BITS      = VOICE_TO_LEFT | VOICE_TO_RIGHT,
	};

	using Register = uint16;
	using Value = uint8;

	class IRegisterLogger
	{
	public:
		virtual void Port(CHANNELINDEX c, Register reg, Value value) = 0;
		virtual void MoveChannel(CHANNELINDEX from, CHANNELINDEX to) = 0;
		virtual ~IRegisterLogger() {}
	};

	explicit OPL(uint32 sampleRate);
	explicit OPL(IRegisterLogger &logger);
	~OPL();

	void Initialize(uint32 sampleRate);
	void Mix(int32 *buffer, size_t count, uint32 volumeFactorQ16);

	void NoteOff(CHANNELINDEX c);
	void NoteCut(CHANNELINDEX c, bool unassign = true);
	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);
	bool IsActive(CHANNELINDEX c) const { return GetVoice(c) != OPL_CHANNEL_INVALID; }
	void MoveChannel(CHANNELINDEX from, CHANNELINDEX to);
	void Reset();

	// A list of all registers for channels and operators if oplCh == 0xFF, otherwise all registers for the given channel and its operators
	static std::vector<Register> AllVoiceRegisters(uint8 oplCh = 0xFF);
	// Returns voice for given register, or 0xFF if it's not a voice-specific register
	static uint8 RegisterToVoice(Register reg);
	// Returns register without any voice offset, so as if it was triggering the first voice
	static Register StripVoiceFromRegister(Register reg);

protected:
	static Register ChannelToRegister(uint8 oplCh);
	static Register OperatorToRegister(uint8 oplCh);
	static uint8 CalcVolume(uint8 trackerVol, uint8 kslVolume);
	uint8 GetVoice(CHANNELINDEX c) const;
	uint8 AllocateVoice(CHANNELINDEX c);
	void Port(CHANNELINDEX c, Register reg, Value value);

	enum
	{
		OPL_CHANNELS = 18,       // 9 for OPL2 or 18 for OPL3
		OPL_CHANNEL_CUT = 0x80,  // Indicates that the channel has been cut and used as a hint to re-use the channel for the same tracker channel if possible
		OPL_CHANNEL_MASK = 0x7F,
		OPL_CHANNEL_INVALID = 0xFF,
		OPL_BASERATE = 49716,
	};

	std::unique_ptr<Opal> m_opl;
	IRegisterLogger *m_logger = nullptr;

	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