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
31namespace Scumm {
32
33class 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 */
43public:
44 SampleBuffer() : _data(0) {
45 clear();
46 }
47
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
121private:
122 uint32 _writePos;
123 uint32 _readPos;
124 uint32 _capacity;
125 byte *_data;
126};
127
128// CPU_CLOCK according to AppleWin
129static 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 */
137private:
138 void addSampleToBuffer(int sample) {
139 int16 value = sample * _volume / _maxVolume;
140 _buffer.write(&value, sizeof(value));
141 }
142
143public:
145 _cyclesPerSampleFP(0),
146 _missingCyclesFP(0),
147 _sampleCyclesSumFP(0),
148 _volume(_maxVolume)
149 {}
150
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
212private:
213 static const int PREC_SHIFT = 7;
214
215private:
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
224class Player_AppleII;
225
227public:
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;
233protected:
234 Player_AppleII *_player = nullptr;
235};
236
238public:
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
259public:
260 void speakerToggle();
261 void generateSamples(int cycles);
262 void wait(int interval, int count);
263
264private:
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: audiostream.h:50
Definition: mixer.h:70
Definition: mixer.h:49
Definition: mutex.h:67
Definition: player_apple2.h:226
Definition: music.h:40
Definition: player_apple2.h:237
void setMusicVolume(int vol) override
Definition: player_apple2.h:242
void startSound(int sound) override
int getRate() const override
Definition: player_apple2.h:257
int readBuffer(int16 *buffer, const int numSamples) override
void stopAllSounds() override
int getSoundStatus(int sound) const override
bool endOfData() const override
Definition: player_apple2.h:256
bool isStereo() const override
Definition: player_apple2.h:255
int getMusicTimer() override
void stopSound(int sound) override
Definition: player_apple2.h:42
Definition: player_apple2.h:136
Definition: scumm.h:536
Definition: actor.h:30