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();
129  ~ChanInternal();
130 
131  void ampModSensitivity(uint32 value) {
132  ampModSvty = (1 << (3 - value)) - (((value >> 1) & 1) | (value & 1));
133  }
134  void frqModSensitivity(uint32 value) {
135  frqModSvty = value << 5;
136  }
137  void fbClear() {
138  feedbuf[0] = feedbuf[1] = feedbuf[2] = 0;
139  }
140 
141  bool enableLeft;
142  bool enableRight;
143  bool updateEnvelopeParameters;
144  int32 feedbuf[3];
145  uint8 algorithm;
146 
147  uint32 ampModSvty;
148  uint32 frqModSvty;
149 
150  TownsPC98_FmSynthOperator *opr[4];
151  };
152 
153  TownsPC98_FmSynthSquareWaveSource *_ssg;
154 #ifndef DISABLE_PC98_RHYTHM_CHANNEL
155  TownsPC98_FmSynthPercussionSource *_prc;
156 #endif
157  ChanInternal *_chanInternal;
158 
159  uint8 *_oprRates;
160  uint8 *_oprRateshift;
161  uint8 *_oprAttackDecay;
162  uint32 *_oprFrq;
163  uint32 *_oprSinTbl;
164  int32 *_oprLevelOut;
165  int32 *_oprDetune;
166 
168  ChipTimerProc *_timerProcIdle;
169  ChipTimerProc *_timerProcA;
170  ChipTimerProc *_timerProcB;
171  void idleTimerCallback() {}
172 
173  struct ChipTimer {
174  bool enabled;
175  uint16 value;
176 
177  int32 smpTillCb;
178  uint32 smpTillCbRem;
179  int32 smpPerCb;
180  uint32 smpPerCbRem;
181 
182  ChipTimerProc *cb;
183  };
184 
185  ChipTimer _timers[2];
186 
187  int _volMaskA, _volMaskB;
188  uint16 _volumeA, _volumeB;
189 
190  int32 *_renderBuffer;
191  int _renderBufferSize;
192  int _numPending;
193  int _offsPending;
194  int _rateScale;
195  int _outRateMult;
196  int _rateConvCnt;
197  float _predSmpCount;
198  const int _internalRate;
199  const int _outputRate;
200 
201 #ifdef ENABLE_SNDTOWNS98_WAITCYCLES
202  int _waitCycleRemainder;
203  const int _samplesPerWaitCycle;
204 
205  struct RegEntry {
206  RegEntry(uint8 p, uint8 r, uint8 v) : part(p), reg(r), val(v) {}
207  uint8 part;
208  uint8 reg;
209  uint8 val;
210  };
211 
212  Common::Array<RegEntry> _waitCycleElapsedWrites;
213 #endif
214 
215  uint8 _registers[255][2];
216 
217  Audio::Mixer *_mixer;
218  Audio::SoundHandle _soundHandle;
219 
220 #ifndef DISABLE_PC98_RHYTHM_CHANNEL
221  static const uint8 _percussionData[];
222 #endif
223  static const uint32 _adtStat[];
224  static const uint8 _detSrc[];
225  static const int _ssgTables[];
226 
227  bool _ready;
228 };
229 
230 #endif
Definition: array.h:52
Definition: func.h:389
Definition: mixer.h:49
Definition: mixer.h:59
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