ScummVM API documentation
asound.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 MADS_DRAGONSPHERE_ASOUND_H
23 #define MADS_DRAGONSPHERE_ASOUND_H
24 
25 #include "audio/fmopl.h"
26 #include "common/mutex.h"
27 #include "common/queue.h"
28 #include "common/util.h"
29 #include "mads/core/sound_manager.h"
30 
31 namespace MADS {
32 namespace Dragonsphere {
33 
34 #define ADLIB_CHANNEL_COUNT 9
35 
36 struct AdlibChannel {
37  static bool _isDisabled;
38 
39  // ---- byte fields (offsets 0x00 - 0x13) --------------------------------
40 
41  uint8 _activeCount = 0; // 0x00 duration tick countdown; 0 = channel idle
42  uint8 _pitchBend = 0; // 0x01 signed pitch-bend offset applied by writePitchBend
43  uint8 _volumeFadeStep = 0; // 0x02 signed per-period volume/velocity delta (0xFF = fade out)
44  uint8 _vibratoDepth = 0; // 0x03 signed LFO depth; negated each period to oscillate
45  uint8 _note = 0; // 0x04 MIDI note number for current event
46  uint8 _sampleIndex = 0; // 0x05 index into _samples[] selecting the OPL patch
47  uint8 _volume = 0; // 0x06 channel volume (0-127)
48  uint8 _noteOffset = 0; // 0x07 subtracted from _activeCount to shorten note duration
49  uint8 _durationOverride = 0; // 0x08 when non-zero, overrides the stream's duration byte
50  uint8 _keyOnDelay = 0; // 0x09 countdown before the OPL key-on bit is cleared (gate time)
51  uint8 _fadePeriodCounter = 0; // 0x0A counts down to 0 before applying _volumeFadeStep
52  uint8 _fadePeriodReload = 0; // 0x0B reload value for _fadePeriodCounter
53  uint8 _arpPeriodCounter = 0; // 0x0C arpeggio tick counter (reloaded from _arpPeriodReload)
54  uint8 _vibPeriodCounter = 0; // 0x0D LFO tick counter; when it reaches 0 a vibrato step fires
55  uint8 _vibPeriodReload = 0; // 0x0E reload value for _vibPeriodCounter
56  uint8 _patchAttenuation = 0; // 0x0F per-note attenuation offset added on top of the patch TL
57  uint8 _velocity = 0; // 0x10 note velocity (0-127); used together with _volume for TL
58  uint8 _writeVolumePending = 0; // 0x11 non-zero -> call sub_11856 (secondary freq write) this tick
59  uint8 _arpPeriodReload = 0; // 0x12 arpeggio period reload value (set by opcode 9)
60  uint8 _arpCounterReload = 0; // 0x13 arpeggio counter reload (decremented; 0xFF = infinite)
61 
62  // ---- pointer fields (offsets 0x14 - 0x1F, word-sized in original) ----
63 
64  byte *_loopStartPtr = nullptr; // 0x14 start of the current loop body
65  byte *_pSrc = nullptr; // 0x16 current read pointer into the sound-data stream
66  byte *_innerLoopPtr = nullptr; // 0x18 inner-loop restart address
67  byte *_outerLoopPtr = nullptr; // 0x1A outer-loop restart address
68  byte *_soundData = nullptr; // 0x1C base address of this channel's sound-data block
69  byte *_branchTargetPtr = nullptr; // 0x1E target of the most recent branch/jump opcode
70 
71  // ---- word counter fields (offsets 0x20 - 0x29) ----------------------
72 
73  uint16 _innerLoopCount = 0; // 0x20 remaining inner-loop iterations (0 = infinite until opcode)
74  uint16 _outerLoopCount = 0; // 0x22 remaining outer-loop iterations (0 = infinite until opcode)
75  uint16 _noiseFreqMask = 0; // 0x24 AND mask applied to the random number in noise mode
76  uint16 _freqAccum = 0; // 0x26 frequency sweep accumulator (base frequency + swept offset)
77  uint16 _freqStep = 0; // 0x28 per-tick increment added to _freqAccum during a sweep
78 
79  // ---- sweep / pause state (offsets 0x2A - 0x2D) ----------------------
80 
81  // 0x2A-0x2B Last computed OPL total-level bytes written by writeVolume.
82  // lo byte (0x2A) = carrier TL nibble; hi byte (0x2B) = modulator TL nibble.
83  // Written at the end of asound_writeVolume so that command7 (resume) can
84  // restore operator levels without a full recalculation.
85  uint8 _cachedCarrierTL = 0; // 0x2A cached carrier total-level (6-bit, 0 = loudest)
86  uint8 _savedFreqSweep = 0; // 0x2B copy of _freqSweepCounter saved by command6 (pause)
87  uint8 _freqSweepCounter = 0; // 0x2C countdown for frequency-sweep ticks; 0 = sweep done
88  uint8 _savedSweepCounter = 0; // 0x2D second save slot: _freqSweepCounter is mirrored here
89  // by command6/7 so command7 can restore the live value
90 
91 // ---- tuning / control (offsets 0x2E - 0x31) ------------------------
92 
93  uint8 _transpose = 0; // 0x2E semitone offset added when looking up _semitoneFreqTable
94  uint8 _octaveTranspose = 0; // 0x2F octave shift: added to _note before octave calculation
95  uint8 _pendingStop = 0; // 0x30 0xFF = channel is fading out and will go idle when silent
96  uint8 _vibratoMode = 0; // 0x31 0xFF = one-shot vibrato (clamp & stop); else = continuous
97 
102  void reset();
103 
109  void load(byte *soundData);
110 
116  void setPtr2(byte *ptr);
117 
123  void enable();
124 
131  void processChannelFade();
132 };
133 
134 // ---------------------------------------------------------------------------
135 // AdlibSample (unchanged from Return of the Phantom, 0x16 bytes)
136 // ---------------------------------------------------------------------------
137 struct AdlibSample {
138  uint8 _attackRate = 0;
139  uint8 _decayRate = 0;
140  uint8 _sustainLevel = 0;
141  uint8 _releaseRate = 0;
142  uint8 _egTyp = 0;
143  uint8 _ksr = 0;
144  uint8 _totalLevel = 0;
145  uint8 _scalingLevel = 0;
146  uint8 _waveformSelect = 0;
147  uint8 _freqMultiple = 0;
148  uint8 _feedback = 0;
149  uint8 _ampMod = 0;
150  uint8 _vib = 0;
151  uint8 _alg = 0;
152  uint8 _freqSweepInit = 0; // initial _freqSweepCounter value; 0 = immediate key-on
153  uint8 _reserved = 0;
154  uint16 _freqMask = 0; // loaded into channel _noiseFreqMask
155  uint16 _freqBase = 0; // loaded into channel _freqAccum
156  uint16 _outerLoopPtr = 0; // loaded into channel _freqStep
157 
158  AdlibSample() {
159  }
161 };
162 
163 // ---------------------------------------------------------------------------
164 // ASound - Dragonsphere Adlib sound driver base class
165 //
166 // Command dispatch table layout (from off_11A14 / off_11A26 / off_11A2E /
167 // funcs_12251 / off_11A64 in the disassembly):
168 //
169 // Table 1 off_11A14 commands 0- 8 (max=8, base=0, 9 entries)
170 // Table 2 off_11A26 commands 16-19 (max=0x13, base=0x10, 4 entries)
171 // command16 = background-music dispatcher (calls command18)
172 // command17 = play specific piece (loads 7 channels direct)
173 // command18 = re-entrant music launcher (reads word_12370 to pick a sub-command)
174 // command19 = no-op (asound_command98)
175 // Table 3 off_11A2E commands 24-32 (max=0x20, base=0x18, 9 entries)
176 // Table 4 funcs_12251 commands 32-49 (max=0x31, base=0x20, 18 entries)
177 // Includes asound_command32-48 (music pieces / SFX loaders)
178 // and asound_command98 (no-op) in the last slot.
179 // Table 5 off_11A64 commands 64-101 (max=0x65, base=0x40, 38 entries)
180 // Includes asound_command64-101 (single-shot SFX loaders via
181 // findFreeChannel / findFreeChannelFull) and two no-ops.
182 //
183 // The driver also exposes:
184 // asound_command90 / 91 - two-voice SFX (findFreeChannelFull x2)
185 // asound_command95 - four-voice music piece (findFreeChannel x4)
186 // sub_11F98 - two-voice SFX (findFreeChannelFull x2)
187 //
188 // word_12370 tracks the "current music index" used by command18 to select
189 // which music-piece loader to call.
190 // ---------------------------------------------------------------------------
191 class ASound : public SoundDriver {
192 protected:
194  typedef void (ASound::*CallbackFunction)();
195 
196 private:
197  OPL::OPL *_opl = OPL::Config::create();
198 
199  // ---- callback / tick state ------------------------------------------
200  uint16 _callbackCounter = 0; // per-tick countdown
201  uint16 _callbackPeriod = 0; // reload value; 0 = callback disabled
202  // Pointer to the deferred sound-loader called when the
203  // counter fires (stored as uint16 in the original; typed as a member
204  // function pointer in the C++ port).
205  CallbackFunction _callbackFnPtr = nullptr;
206 
207  // ---- active-channel context (set by pollAllChannels) ----------------
208  AdlibChannel *_activeChannelPtr = nullptr;
209  uint8 _activeChannelNumber = 0;
210  uint16 _activeChannelReg = 0; // 0xA0+ch register for the active channel
211  uint16 _currentOpBase = 0; // operator register base for the active channel
212 
213  // ---- per-frame working state ----------------------------------------
214  AdlibSample *_samplePtr = nullptr; // patch being loaded/written
215  byte *pSrc = nullptr; // current read pointer (mirrors channel _pSrc)
216  int16 _pollResult = 0;
217  int16 _resultFlag = 0;
218  uint16 _randomSeed = 0x4D2;
219 
220  // ---- driver-wide flags ----------------------------------------------
221  uint16 _isDisabled = 0; // non-zero while the engine is paused (command6)
222  uint8 _findChannelMode = 0; // 0=full search, 1=ch0-5 only, 2=ch6-8 then pending
223 
224  // ---- per-channel sweep shadows (for channel 5 special-casing) -------
225  // Not present as separate globals in Dragonsphere; the channel
226  // struct fields _savedFreqSweep / _savedSweepCounter handle this.
227 
228  // ---- misc globals ---------------------------------------------------
229  uint8 _anySweepActive = 0; // set to 1 if any channel has _freqSweepCounter > 0
230  int _frameNumber2 = 0; // secondary frame counter incremented every update
231 
232  // ---- script / sequencer registers -----------------------------------
233  uint8 _scriptVars[32] = {}; // byte_16A10: 32 general-purpose script registers
234 
235  // ---- music-index tracker (word_12370) --------------------------------
236  // Tracks which music piece was last launched by command18.
237  uint16 _musicIndex = 0;
238 
239  // ---- tempo / sequencer state (from opcodes3 group 2 handlers) --------
240  uint8 _musicOnlyFlag = 0; // byte_12393: 1=music-only check, 0=all channels
241  uint16 _tempoFineStep = 0; // word_124F2: fine tempo step (opcode A6)
242  uint16 _tempoCoarseStep = 0; // word_124F0: coarse tempo step (opcode A7)
243  uint16 _tempoPeriod = 0; // word_124EE: tempo period in ticks (opcode A8)
244  uint8 _tempoEnabled = 0; // non-zero when tempo tick is active (opcode A8)
245  uint16 _tempoTickCounter = 0; // countdown for tempo callback
246  uint16 _tempoShift = 0; // word_124F4: tempo shift (opcode A9)
247 
248  // =========================================================================
249  // Private helpers
250  // =========================================================================
251 
253  void onTimer();
254 
256  void clearCallback();
257  void resetCallback() {
258  clearCallback();
259  }
260 
267  void adlib_channelOff(uint8 portIndex);
268 
287  void writeVolume();
288 
294  void writeFrequency();
295 
300  void writePitchBend();
301 
308  void writeArpeggio();
309 
314  void updateOctave();
315 
321  void noteOn();
322 
328  void writeSampleRegs();
329 
335  void loadSample();
336 
342  void update();
343 
350  void update1(int channelIndex);
351 
356  void setFrequency(uint8 voice, uint16 freq);
357 
363  void noise_inner(int channelIndex);
364 
368  void updateAllChannels();
369 
374  uint16 readWord_impl();
375 
381  void tickCallback();
382 
383 protected:
384  // ---- nine AdlibChannel instances ------------------------------------
385  AdlibChannel _channel0, _channel1, _channel2;
386  AdlibChannel _channel3, _channel4, _channel5;
387  AdlibChannel _channel6, _channel7, _channel8;
388  AdlibChannel *_channels[ADLIB_CHANNEL_COUNT] = {
389  &_channel0, &_channel1, &_channel2,
390  &_channel3, &_channel4, &_channel5,
391  &_channel6, &_channel7, &_channel8
392  };
393 
394  uint8 _adlibPorts[0x100] = { 0 };
396 
397 protected:
398  // =========================================================================
399  // Protected helpers (used by ASound1–ASound9)
400  // =========================================================================
401 
407  int isMusicChannelsActive();
408 
413  int isAnyChannelActive();
414 
421  void scheduleCallback(CallbackFunction fn) { _callbackFnPtr = fn; }
422 
427  void resetCallbackTimer(uint16 period) {
428  _callbackFnPtr = nullptr;
429  _callbackCounter = period;
430  _callbackPeriod = period;
431  }
432 
437  void resetCallbackTimerEx(uint16 counter, uint16 period) {
438  _callbackFnPtr = nullptr;
439  _callbackCounter = counter;
440  _callbackPeriod = period;
441  }
442 
444  void setMusicIndex(uint16 idx) { _musicIndex = idx; }
446  uint16 getMusicIndex() const { return _musicIndex; }
447 
449  void setScriptVar(int idx, uint8 val) { _scriptVars[idx] = val; }
454  void write(uint8 reg, uint8 value);
455 
457  uint16 getRandomNumber();
458 
465  void adlib_channelOn(uint8 portIndex);
466 
468  void signalSoundPlaying();
469 
474  void pollAllChannels();
475 
494  void pollActiveChannel();
495 
497  bool isSoundActive(byte *ptr) const;
498 
504  void findFreeChannel(byte *soundData);
505 
511  void findFreeChannelFull(byte *soundData);
512 
514  byte *loadData(int offset) {
515  return &_soundData[offset];
516  }
517 
518 protected:
519  // =========================================================================
520  // Core commands (0-8) - identical in purpose to Return of the Phantom
521  // =========================================================================
522 
531  int command0();
532 
537  int command1();
538 
544  int command2();
545 
550  int command3();
551 
556  int command4();
557 
563  int command5();
564 
572  int command6();
573 
580  int command7();
581 
588  int command8();
589 
594  virtual void callFunction(uint16 offset);
595 
596  // =========================================================================
597  // Music-index launcher (called via command18)
598  // =========================================================================
599 
607  int command18();
608 
609 public:
613  static void validate(bool isDemo);
614 
615 public:
623  ASound(Audio::Mixer *mixer, const Common::Path &filename,
624  int dataOffset, int dataSize);
625 
626  ~ASound() override {
627  delete _opl;
628  }
629 
631  virtual int stop() override;
632 
634  int poll() override;
635 
640  void noise() override;
641 
646  void playSound(int offset);
647 
648  void setVolume(int volume) override {
649  // TODO: implement if needed
650  }
651 };
652 
653 } // namespace Dragonsphere
654 } // namespace MADS
655 
656 #endif
void scheduleCallback(CallbackFunction fn)
Definition: asound.h:421
void setVolume(int volume) override
Definition: asound.h:648
Definition: array.h:52
Definition: asound.h:191
Definition: path.h:52
Definition: stream.h:745
void setScriptVar(int idx, uint8 val)
Definition: asound.h:449
Definition: asound.h:36
static OPL * create(DriverId driver, OplType type)
Definition: mixer.h:70
Definition: asound.h:137
byte * loadData(int offset)
Definition: asound.h:514
Definition: sound_manager.h:38
Definition: anim_timer.h:27
void load(byte *soundData)
void resetCallbackTimerEx(uint16 counter, uint16 period)
Definition: asound.h:437
void setMusicIndex(uint16 idx)
Definition: asound.h:444
uint16 getMusicIndex() const
Definition: asound.h:446
void resetCallbackTimer(uint16 period)
Definition: asound.h:427
Definition: fmopl.h:116