CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/HW/SasAudio.h
Views: 1401
1
// Copyright (c) 2012- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
19
20
// This is not really hardware, it's a software audio mixer running on the Media Engine.
21
// From the perspective of a PSP app though, it might as well be.
22
23
#pragma once
24
25
#include "Common/CommonTypes.h"
26
#include "Core/HW/BufferQueue.h"
27
#include "Core/HW/SasReverb.h"
28
29
class PointerWrap;
30
31
// General constants.
32
enum {
33
PSP_SAS_VOICES_MAX = 32,
34
PSP_SAS_VOL_MAX = 0x1000,
35
PSP_SAS_MAX_GRAIN = 2048, // Matches the max value of the parameter to sceSasInit
36
PSP_SAS_PITCH_MIN = 0x0000,
37
PSP_SAS_PITCH_BASE = 0x1000,
38
PSP_SAS_PITCH_MASK = 0xFFF,
39
PSP_SAS_PITCH_BASE_SHIFT = 12,
40
PSP_SAS_PITCH_MAX = 0x4000,
41
PSP_SAS_ENVELOPE_HEIGHT_MAX = 0x40000000,
42
PSP_SAS_ENVELOPE_FREQ_MAX = 0x7FFFFFFF,
43
};
44
45
// The type of these are baked into savestates.
46
enum SasADSRCurveMode : int {
47
PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE = 0,
48
PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE = 1,
49
PSP_SAS_ADSR_CURVE_MODE_LINEAR_BENT = 2,
50
PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE = 3,
51
PSP_SAS_ADSR_CURVE_MODE_EXPONENT_INCREASE = 4,
52
PSP_SAS_ADSR_CURVE_MODE_DIRECT = 5,
53
};
54
55
enum {
56
PSP_SAS_ADSR_ATTACK = 1,
57
PSP_SAS_ADSR_DECAY = 2,
58
PSP_SAS_ADSR_SUSTAIN = 4,
59
PSP_SAS_ADSR_RELEASE = 8,
60
};
61
62
enum SasEffectType {
63
PSP_SAS_EFFECT_TYPE_OFF = -1,
64
PSP_SAS_EFFECT_TYPE_ROOM = 0,
65
PSP_SAS_EFFECT_TYPE_STUDIO_SMALL = 1,
66
PSP_SAS_EFFECT_TYPE_STUDIO_MEDIUM = 2,
67
PSP_SAS_EFFECT_TYPE_STUDIO_LARGE = 3,
68
PSP_SAS_EFFECT_TYPE_HALL = 4,
69
PSP_SAS_EFFECT_TYPE_SPACE = 5,
70
PSP_SAS_EFFECT_TYPE_ECHO = 6,
71
PSP_SAS_EFFECT_TYPE_DELAY = 7,
72
PSP_SAS_EFFECT_TYPE_PIPE = 8,
73
PSP_SAS_EFFECT_TYPE_MAX = 8,
74
};
75
76
enum SasOutputMode {
77
PSP_SAS_OUTPUTMODE_MIXED = 0,
78
PSP_SAS_OUTPUTMODE_RAW = 1,
79
};
80
81
struct WaveformEffect {
82
int type;
83
int delay;
84
int feedback;
85
int leftVol;
86
int rightVol;
87
int isDryOn;
88
int isWetOn;
89
};
90
91
enum VoiceType {
92
VOICETYPE_OFF,
93
VOICETYPE_VAG, // default
94
VOICETYPE_NOISE,
95
VOICETYPE_TRIWAVE, // are these used? there are functions for them (sceSetTriangularWave)
96
VOICETYPE_PULSEWAVE,
97
VOICETYPE_PCM,
98
VOICETYPE_ATRAC3,
99
};
100
101
// VAG is a Sony ADPCM audio compression format, which goes all the way back to the PSX.
102
// It compresses 28 16-bit samples into a block of 16 bytes.
103
class VagDecoder {
104
public:
105
VagDecoder() : data_(0), read_(0), end_(true) {
106
memset(samples, 0, sizeof(samples));
107
}
108
void Start(u32 dataPtr, u32 vagSize, bool loopEnabled);
109
110
void GetSamples(s16 *outSamples, int numSamples);
111
112
void DecodeBlock(const u8 *&readp);
113
bool End() const { return end_; }
114
115
void DoState(PointerWrap &p);
116
117
u32 GetReadPtr() const { return read_; }
118
119
private:
120
s16 samples[28];
121
int curSample = 0;
122
123
u32 data_ = 0;
124
u32 read_ = 0;
125
int curBlock_ = -1;
126
int loopStartBlock_ = -1;
127
int numBlocks_ = 0;
128
129
// rolling state. start at 0, should probably reset to 0 on loops?
130
int s_1 = 0;
131
int s_2 = 0;
132
133
bool loopEnabled_ = false;
134
bool loopAtNextBlock_ = false;
135
bool end_ = false;
136
};
137
138
class SasAtrac3 {
139
public:
140
SasAtrac3() : contextAddr_(0), atracID_(-1), sampleQueue_(0), end_(false) {}
141
~SasAtrac3() { delete sampleQueue_; }
142
int setContext(u32 context);
143
void getNextSamples(s16 *outbuf, int wantedSamples);
144
int addStreamData(u32 bufPtr, u32 addbytes);
145
void DoState(PointerWrap &p);
146
bool End() const {
147
return end_;
148
}
149
150
private:
151
u32 contextAddr_;
152
int atracID_;
153
BufferQueue *sampleQueue_;
154
bool end_;
155
};
156
157
class ADSREnvelope {
158
public:
159
void SetSimpleEnvelope(u32 ADSREnv1, u32 ADSREnv2);
160
void SetEnvelope(int flag, int a, int d, int s, int r);
161
void SetRate(int flag, int a, int d, int s, int r);
162
void SetSustainLevel(int sl) {
163
sustainLevel = sl;
164
}
165
166
void WalkCurve(int type, int rate);
167
168
void KeyOn();
169
void KeyOff();
170
void End();
171
172
inline void Step();
173
174
int GetHeight() const {
175
return (int)(height_ > (s64)PSP_SAS_ENVELOPE_HEIGHT_MAX ? PSP_SAS_ENVELOPE_HEIGHT_MAX : height_);
176
}
177
bool NeedsKeyOn() const {
178
return state_ == STATE_KEYON;
179
}
180
bool HasEnded() const {
181
return state_ == STATE_OFF;
182
}
183
184
void DoState(PointerWrap &p);
185
186
int attackRate = 0;
187
int decayRate = 0;
188
int sustainRate = 0;
189
int sustainLevel = 0;
190
int releaseRate = 0;
191
192
SasADSRCurveMode attackType = PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE;
193
SasADSRCurveMode decayType = PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE;
194
SasADSRCurveMode sustainType = PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE;
195
SasADSRCurveMode releaseType = PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE;
196
197
private:
198
// Actual PSP values.
199
enum ADSRState {
200
// Okay, this one isn't a real value but it might be.
201
STATE_KEYON_STEP = -42,
202
203
STATE_KEYON = -2,
204
STATE_OFF = -1,
205
STATE_ATTACK = 0,
206
STATE_DECAY = 1,
207
STATE_SUSTAIN = 2,
208
STATE_RELEASE = 3,
209
};
210
void SetState(ADSRState state);
211
212
ADSRState state_ = STATE_OFF;
213
s64 height_ = 0; // s64 to avoid having to care about overflow when calculating. TODO: this should be fine as s32
214
};
215
216
// A SAS voice.
217
// TODO: Look into pre-decoding the VAG samples on SetVoice instead of decoding them on the fly.
218
// It's not very likely that games encode VAG dynamically.
219
struct SasVoice {
220
SasVoice()
221
: playing(false),
222
paused(false),
223
on(false),
224
type(VOICETYPE_OFF),
225
vagAddr(0),
226
vagSize(0),
227
pcmAddr(0),
228
pcmSize(0),
229
pcmIndex(0),
230
pcmLoopPos(0),
231
sampleRate(44100),
232
sampleFrac(0),
233
pitch(PSP_SAS_PITCH_BASE),
234
loop(false),
235
noiseFreq(0),
236
volumeLeft(PSP_SAS_VOL_MAX),
237
volumeRight(PSP_SAS_VOL_MAX),
238
effectLeft(PSP_SAS_VOL_MAX),
239
effectRight(PSP_SAS_VOL_MAX) {
240
memset(resampleHist, 0, sizeof(resampleHist));
241
}
242
243
void Reset();
244
void KeyOn();
245
void KeyOff();
246
247
void DoState(PointerWrap &p);
248
249
void ReadSamples(s16 *output, int numSamples);
250
bool HaveSamplesEnded() const;
251
252
// For debugging.
253
u32 GetReadAddress() const {
254
if (type == VOICETYPE_VAG) {
255
return vag.GetReadPtr();
256
} else {
257
return 0; // TODO.
258
}
259
}
260
261
bool playing;
262
bool paused; // a voice can be playing AND paused. In that case, it won't play.
263
bool on; // key-on, key-off.
264
265
VoiceType type;
266
267
u32 vagAddr;
268
u32 vagSize;
269
u32 pcmAddr;
270
int pcmSize;
271
int pcmIndex;
272
int pcmLoopPos;
273
int sampleRate;
274
275
uint32_t sampleFrac;
276
int pitch;
277
bool loop;
278
279
int noiseFreq;
280
281
int volumeLeft;
282
int volumeRight;
283
284
// volume to "Send" (audio-lingo) to the effects processing engine, like reverb
285
int effectLeft;
286
int effectRight;
287
s16 resampleHist[2];
288
289
ADSREnvelope envelope;
290
291
// TODO: Union these two?
292
VagDecoder vag;
293
SasAtrac3 atrac3;
294
};
295
296
class SasInstance {
297
public:
298
SasInstance();
299
~SasInstance();
300
301
void ClearGrainSize();
302
void SetGrainSize(int newGrainSize);
303
int GetGrainSize() const { return grainSize; }
304
int EstimateMixUs();
305
306
int maxVoices = PSP_SAS_VOICES_MAX;
307
int sampleRate = 44100;
308
int outputMode = PSP_SAS_OUTPUTMODE_MIXED;
309
310
int *mixBuffer = nullptr;
311
int *sendBuffer = nullptr;
312
s16 *sendBufferDownsampled = nullptr;
313
s16 *sendBufferProcessed = nullptr;
314
315
FILE *audioDump = nullptr;
316
317
void Mix(u32 outAddr, u32 inAddr = 0, int leftVol = 0, int rightVol = 0);
318
void MixVoice(SasVoice &voice);
319
320
// Applies reverb to send buffer, according to waveformEffect.
321
void ApplyWaveformEffect();
322
void SetWaveformEffectType(int type);
323
void WriteMixedOutput(s16 *outp, const s16 *inp, int leftVol, int rightVol);
324
325
void GetDebugText(char *text, size_t bufsize);
326
327
void DoState(PointerWrap &p);
328
329
SasVoice voices[PSP_SAS_VOICES_MAX];
330
WaveformEffect waveformEffect;
331
332
private:
333
SasReverb reverb_;
334
int grainSize = 0;
335
int16_t mixTemp_[PSP_SAS_MAX_GRAIN * 4 + 2 + 16]; // some extra margin for very high pitches.
336
};
337
338
const char *ADSRCurveModeAsString(SasADSRCurveMode mode);
339
340