ScummVM API documentation
towns_pc98_fmsynth.h
1/* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#ifndef TOWNS_PC98_FMSYNTH_H
23#define TOWNS_PC98_FMSYNTH_H
24
25#include "audio/audiostream.h"
26#include "audio/mixer.h"
27#include "common/mutex.h"
28#include "common/func.h"
29#include "common/array.h"
30
31#ifdef __DS__
32/* This disables the rhythm channel when emulating the PC-98 type 86 sound card.
33 * The only purpose is code size reduction for certain backends.
34 * At the moment the only games which make use of the rhythm channel are the
35 * (very rare) PC-98 versions of Legend of Kyrandia 2 and Lands of Lore. Music will
36 * still be okay, just missing a couple of rhythm instruments.
37 */
38#define DISABLE_PC98_RHYTHM_CHANNEL
39#endif
40
41/* Experimental code for emulation of the chip's busy flag wait cycle.
42 * Explanation:
43 * Before attempting a port write a client application would usually read the chip's
44 * busy flag and remain in a loop until the flag is cleared. This does not work with
45 * an emulator that is on the same thread as the client code (the busy flag will never
46 * clear). Instead, I emulate a wait cycle by withholding (enqueueing) incoming register
47 * writes for the duration of the wait cycle.
48 * For now I have disabled this with an #ifdef since I haven't seen any impact on the
49 * sound.
50 */
51//#define ENABLE_SNDTOWNS98_WAITCYCLES
52
53class TownsPC98_FmSynthOperator;
54class TownsPC98_FmSynthSquareWaveSource;
55#ifndef DISABLE_PC98_RHYTHM_CHANNEL
56class TownsPC98_FmSynthPercussionSource;
57#endif
58
59enum EnvelopeState {
60 kEnvReady = 0,
61 kEnvAttacking,
62 kEnvDecaying,
63 kEnvSustaining,
64 kEnvReleasing
65};
66
68public:
69 enum EmuType {
70 kTypeTowns = 0,
71 kType26 = 1,
72 kType86 = 2
73 };
74
75 TownsPC98_FmSynth(Audio::Mixer *mixer, EmuType type);
76 virtual ~TownsPC98_FmSynth();
77
78 virtual bool init();
79 virtual void reset();
80
81 void writeReg(uint8 part, uint8 regAddress, uint8 value);
82 uint8 readReg(uint8 part, uint8 regAddress);
83
84 // AudioStream interface
85 int readBuffer(int16 *buffer, const int numSamples) override;
86 bool isStereo() const override;
87 bool endOfData() const override;
88 int getRate() const override;
89
90protected:
91 void deinit();
92
93 // Implement this in your inherited class if your driver generates
94 // additional output that has to be inserted into the buffer.
95 virtual void nextTickEx(int32 *buffer, uint32 bufferSize) {}
96
97 virtual void timerCallbackA() = 0;
98 virtual void timerCallbackB() = 0;
99
100 // The audio driver can store and apply two different volume settings
101 // (usually for music and sound effects). The channel mask will determine
102 // which channels get effected by which setting. The first bits will be
103 // the normal fm channels, the next bits the ssg channels and the final
104 // bit the rhythm channel.
105 void setVolumeIntern(int volA, int volB);
106 void setVolumeChannelMasks(int channelMaskA, int channelMaskB);
107
108 // This allows to balance out the fm/ssg levels.
109 void setLevelSSG(int vol);
110
111 const int _numChan;
112 const int _numSSG;
113 const bool _hasPercussion;
114
115 Common::Mutex &_mutex;
116 int _mixerThreadLockCounter;
117
118private:
119 void generateTables();
120 void writeRegInternal(uint8 part, uint8 regAddress, uint8 value);
121 void nextTick(int32 *buffer, uint32 bufferSize);
122
123#ifdef ENABLE_SNDTOWNS98_WAITCYCLES
124 void startWaitCycle();
125#endif
126
127 struct ChanInternal {
128 ChanInternal() = default;
129 ChanInternal(const ChanInternal &) = delete;
130 ChanInternal(ChanInternal &&) = delete;
131 ChanInternal &operator=(const ChanInternal &) = delete;
132 ChanInternal &operator=(ChanInternal &&) = delete;
133 ~ChanInternal();
134
135 void ampModSensitivity(uint32 value) {
136 ampModSvty = (1 << (3 - value)) - (((value >> 1) & 1) | (value & 1));
137 }
138 void frqModSensitivity(uint32 value) {
139 frqModSvty = value << 5;
140 }
141 void fbClear() {
142 feedbuf[0] = feedbuf[1] = feedbuf[2] = 0;
143 }
144
145 bool enableLeft = false;
146 bool enableRight = false;
147 bool updateEnvelopeParameters = false;
148 int32 feedbuf[3] = {};
149 uint8 algorithm = 0u;
150
151 uint32 ampModSvty = 0u;
152 uint32 frqModSvty = 0u;
153
154 TownsPC98_FmSynthOperator *opr[4] = {};
155 };
156
157 TownsPC98_FmSynthSquareWaveSource *_ssg;
158#ifndef DISABLE_PC98_RHYTHM_CHANNEL
159 TownsPC98_FmSynthPercussionSource *_prc;
160#endif
161 ChanInternal *_chanInternal;
162
163 uint8 *_oprRates;
164 uint8 *_oprRateshift;
165 uint8 *_oprAttackDecay;
166 uint32 *_oprFrq;
167 uint32 *_oprSinTbl;
168 int32 *_oprLevelOut;
169 int32 *_oprDetune;
170
172 ChipTimerProc *_timerProcIdle;
173 ChipTimerProc *_timerProcA;
174 ChipTimerProc *_timerProcB;
175 void idleTimerCallback() {}
176
177 struct ChipTimer {
178 bool enabled;
179 uint16 value;
180
181 int32 smpTillCb;
182 uint32 smpTillCbRem;
183 int32 smpPerCb;
184 uint32 smpPerCbRem;
185
186 ChipTimerProc *cb;
187 };
188
189 ChipTimer _timers[2];
190
191 int _volMaskA, _volMaskB;
192 uint16 _volumeA, _volumeB;
193
194 int32 *_renderBuffer;
195 int _renderBufferSize;
196 int _numPending;
197 int _offsPending;
198 int _rateScale;
199 int _outRateMult;
200 int _rateConvCnt;
201 float _predSmpCount;
202 const int _internalRate;
203 const int _outputRate;
204
205#ifdef ENABLE_SNDTOWNS98_WAITCYCLES
206 int _waitCycleRemainder;
207 const int _samplesPerWaitCycle;
208
209 struct RegEntry {
210 RegEntry(uint8 p, uint8 r, uint8 v) : part(p), reg(r), val(v) {}
211 uint8 part;
212 uint8 reg;
213 uint8 val;
214 };
215
216 Common::Array<RegEntry> _waitCycleElapsedWrites;
217#endif
218
219 uint8 _registers[255][2];
220
221 Audio::Mixer *_mixer;
222 Audio::SoundHandle _soundHandle;
223
224#ifndef DISABLE_PC98_RHYTHM_CHANNEL
225 static const uint8 _percussionData[];
226#endif
227 static const uint32 _adtStat[];
228 static const uint8 _detSrc[];
229 static const int _ssgTables[];
230
231 bool _ready;
232};
233
234#endif
Definition: audiostream.h:50
Definition: mixer.h:70
Definition: mixer.h:49
Definition: array.h:52
Definition: func.h:389
Definition: mutex.h:67
Definition: towns_pc98_fmsynth.h:67
bool isStereo() const override
int getRate() const override
bool endOfData() const override
int readBuffer(int16 *buffer, const int numSamples) override