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 
53 class TownsPC98_FmSynthOperator;
54 class TownsPC98_FmSynthSquareWaveSource;
55 #ifndef DISABLE_PC98_RHYTHM_CHANNEL
56 class TownsPC98_FmSynthPercussionSource;
57 #endif
58 
59 enum EnvelopeState {
60  kEnvReady = 0,
61  kEnvAttacking,
62  kEnvDecaying,
63  kEnvSustaining,
64  kEnvReleasing
65 };
66 
68 public:
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);
86  bool isStereo() const;
87  bool endOfData() const;
88  int getRate() const;
89 
90 protected:
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 
118 private:
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: array.h:52
Definition: func.h:389
Definition: mixer.h:49
Definition: mixer.h:70
int getRate() const
Definition: mutex.h:67
Definition: audiostream.h:50
int readBuffer(int16 *buffer, const int numSamples)
Definition: towns_pc98_fmsynth.h:67
bool isStereo() const
bool endOfData() const