ScummVM API documentation
player_apple2.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 SCUMM_PLAYERS_PLAYER_APPLEII_H
23 #define SCUMM_PLAYERS_PLAYER_APPLEII_H
24 
25 #include "common/mutex.h"
26 #include "common/scummsys.h"
27 #include "scumm/music.h"
28 #include "audio/audiostream.h"
29 #include "audio/mixer.h"
30 
31 namespace Scumm {
32 
33 class ScummEngine;
34 
35 /*
36  * Optimized for use with periodical read/write phases when the buffer
37  * is filled in a write phase and completely read in a read phase.
38  * The growing strategy is optimized for repeated small (e.g. 2 bytes)
39  * single writes resulting in large buffers
40  * (avg.: 4KB, max: 18KB @ 16bit/22.050kHz (MM sound21)).
41  */
42 class SampleBuffer {
43 public:
44  SampleBuffer() : _data(0) {
45  clear();
46  }
47 
48  ~SampleBuffer() {
49  free(_data);
50  }
51 
52  void clear() {
53  free(_data);
54  _data = 0;
55  _capacity = 0;
56  _writePos = 0;
57  _readPos = 0;
58  }
59 
60  void ensureFree(uint32 needed) {
61  // if data was read completely, reset read/write pos to front
62  if ((_writePos != 0) && (_writePos == _readPos)) {
63  _writePos = 0;
64  _readPos = 0;
65  }
66 
67  // check for enough space at end of buffer
68  uint32 freeEndCnt = _capacity - _writePos;
69  if (needed <= freeEndCnt)
70  return;
71 
72  uint32 avail = availableSize();
73 
74  // check for enough space at beginning and end of buffer
75  if (needed <= _readPos + freeEndCnt) {
76  // move unread data to front of buffer
77  memmove(_data, _data + _readPos, avail);
78  _writePos = avail;
79  _readPos = 0;
80  } else { // needs a grow
81  byte *old_data = _data;
82  uint32 new_len = avail + needed;
83 
84  _capacity = new_len + 2048;
85  _data = (byte *)malloc(_capacity);
86 
87  if (old_data) {
88  // copy old unread data to front of new buffer
89  memcpy(_data, old_data + _readPos, avail);
90  free(old_data);
91  _writePos = avail;
92  _readPos = 0;
93  }
94  }
95  }
96 
97  uint32 availableSize() const {
98  if (_readPos >= _writePos)
99  return 0;
100  return _writePos - _readPos;
101  }
102 
103  uint32 write(const void *dataPtr, uint32 dataSize) {
104  ensureFree(dataSize);
105  memcpy(_data + _writePos, dataPtr, dataSize);
106  _writePos += dataSize;
107  return dataSize;
108  }
109 
110  uint32 read(byte *dataPtr, uint32 dataSize) {
111  uint32 avail = availableSize();
112  if (avail == 0)
113  return 0;
114  if (dataSize > avail)
115  dataSize = avail;
116  memcpy(dataPtr, _data + _readPos, dataSize);
117  _readPos += dataSize;
118  return dataSize;
119  }
120 
121 private:
122  uint32 _writePos;
123  uint32 _readPos;
124  uint32 _capacity;
125  byte *_data;
126 };
127 
128 // CPU_CLOCK according to AppleWin
129 static const double APPLEII_CPU_CLOCK = 1020484.5; // ~ 1.02 MHz
130 
131 /*
132  * Converts the 1-bit speaker state values into audio samples.
133  * This is done by aggregation of the speaker states at each
134  * CPU cycle in a sampling period into an audio sample.
135  */
137 private:
138  void addSampleToBuffer(int sample) {
139  int16 value = sample * _volume / _maxVolume;
140  _buffer.write(&value, sizeof(value));
141  }
142 
143 public:
144  SampleConverter() :
145  _cyclesPerSampleFP(0),
146  _missingCyclesFP(0),
147  _sampleCyclesSumFP(0),
148  _volume(_maxVolume)
149  {}
150 
151  ~SampleConverter() {}
152 
153  void reset() {
154  _missingCyclesFP = 0;
155  _sampleCyclesSumFP = 0;
156  _buffer.clear();
157  }
158 
159  uint32 availableSize() const {
160  return _buffer.availableSize();
161  }
162 
163  void setMusicVolume(int vol) {
164  assert(vol >= 0 && vol <= _maxVolume);
165  _volume = vol;
166  }
167 
168  void setSampleRate(int rate) {
169  /* ~46 CPU cycles per sample @ 22.05kHz */
170  _cyclesPerSampleFP = int(APPLEII_CPU_CLOCK * (1 << PREC_SHIFT) / rate);
171  reset();
172  }
173 
174  void addCycles(byte level, const int cycles) {
175  /* convert to fixed precision floats */
176  int cyclesFP = cycles << PREC_SHIFT;
177 
178  // step 1: if cycles are left from the last call, process them first
179  if (_missingCyclesFP > 0) {
180  int n = (_missingCyclesFP < cyclesFP) ? _missingCyclesFP : cyclesFP;
181  if (level)
182  _sampleCyclesSumFP += n;
183  cyclesFP -= n;
184  _missingCyclesFP -= n;
185  if (_missingCyclesFP == 0) {
186  addSampleToBuffer(2*32767 * _sampleCyclesSumFP / _cyclesPerSampleFP - 32767);
187  } else {
188  return;
189  }
190  }
191 
192  _sampleCyclesSumFP = 0;
193 
194  // step 2: process blocks of cycles fitting into a whole sample
195  while (cyclesFP >= _cyclesPerSampleFP) {
196  addSampleToBuffer(level ? 32767 : -32767);
197  cyclesFP -= _cyclesPerSampleFP;
198  }
199 
200  // step 3: remember cycles left for next call
201  if (cyclesFP > 0) {
202  _missingCyclesFP = _cyclesPerSampleFP - cyclesFP;
203  if (level)
204  _sampleCyclesSumFP = cyclesFP;
205  }
206  }
207 
208  uint32 readSamples(void *buffer, int numSamples) {
209  return _buffer.read((byte *)buffer, numSamples * 2) / 2;
210  }
211 
212 private:
213  static const int PREC_SHIFT = 7;
214 
215 private:
216  int _cyclesPerSampleFP; /* (fixed precision) */
217  int _missingCyclesFP; /* (fixed precision) */
218  int _sampleCyclesSumFP; /* (fixed precision) */
219  int _volume; /* 0 - 256 */
220  static const int _maxVolume = 256;
221  SampleBuffer _buffer;
222 };
223 
224 class Player_AppleII;
225 
227 public:
229  virtual ~AppleII_SoundFunction() {}
230  virtual void init(Player_AppleII *player, const byte *params) = 0;
231  /* returns true if finished */
232  virtual bool update() = 0;
233 protected:
234  Player_AppleII *_player = nullptr;
235 };
236 
238 public:
239  Player_AppleII(ScummEngine *scumm, Audio::Mixer *mixer);
240  ~Player_AppleII() override;
241 
242  void setMusicVolume(int vol) override { _sampleConverter.setMusicVolume(vol); }
243  void setSampleRate(int rate) {
244  _sampleRate = rate;
245  _sampleConverter.setSampleRate(rate);
246  }
247  void startSound(int sound) override;
248  void stopSound(int sound) override;
249  void stopAllSounds() override;
250  int getSoundStatus(int sound) const override;
251  int getMusicTimer() override;
252 
253  // AudioStream API
254  int readBuffer(int16 *buffer, const int numSamples) override;
255  bool isStereo() const override { return false; }
256  bool endOfData() const override { return false; }
257  int getRate() const override { return _sampleRate; }
258 
259 public:
260  void speakerToggle();
261  void generateSamples(int cycles);
262  void wait(int interval, int count);
263 
264 private:
265  // sound number
266  int _soundNr = 0;
267  // type of sound
268  int _type = 0;
269  // number of loops left
270  int _loop = 0;
271  // global sound param list
272  const byte *_params = nullptr;
273  // speaker toggle state (0 / 1)
274  byte _speakerState = 0;
275  // sound function
276  AppleII_SoundFunction *_soundFunc;
277  // cycle to sample converter
278  SampleConverter _sampleConverter;
279 
280  ScummEngine *_vm;
281  Audio::Mixer *_mixer;
282  Audio::SoundHandle _soundHandle;
283  int _sampleRate = 0;
284  Common::Mutex _mutex;
285 
286  void resetState();
287  bool updateSound();
288 };
289 
290 } // End of namespace Scumm
291 
292 #endif
Definition: player_apple2.h:237
bool isStereo() const override
Definition: player_apple2.h:255
bool endOfData() const override
Definition: player_apple2.h:256
Definition: music.h:40
Definition: scumm.h:519
Definition: mixer.h:49
Definition: mixer.h:59
void setMusicVolume(int vol) override
Definition: player_apple2.h:242
Definition: player_apple2.h:42
Definition: mutex.h:67
Definition: player_apple2.h:226
Definition: audiostream.h:50
int getRate() const override
Definition: player_apple2.h:257
Definition: player_apple2.h:136
Definition: actor.h:30