Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/HLE/__sceAudio.cpp
3186 views
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
#include <atomic>
19
#include <mutex>
20
#include <algorithm>
21
22
#include "Common/Common.h"
23
#include "Common/File/Path.h"
24
#include "Common/Serialize/Serializer.h"
25
#include "Common/Serialize/SerializeFuncs.h"
26
#include "Common/Data/Collections/FixedSizeQueue.h"
27
#include "Common/System/System.h"
28
#include "Common/Math/SIMDHeaders.h"
29
#include "Common/StringUtils.h"
30
31
#include "Core/Config.h"
32
#include "Core/CoreTiming.h"
33
#include "Core/MemMapHelpers.h"
34
#include "Core/Reporting.h"
35
#include "Core/System.h"
36
#include "Core/WaveFile.h"
37
#include "Core/ELF/ParamSFO.h"
38
#include "Core/HLE/sceKernelTime.h"
39
#include "Core/HLE/ErrorCodes.h"
40
#include "Core/HLE/__sceAudio.h"
41
#include "Core/HLE/sceAudio.h"
42
#include "Core/HLE/sceKernel.h"
43
#include "Core/HLE/sceKernelThread.h"
44
#include "Core/Util/AudioFormat.h"
45
46
// Should be used to lock anything related to the outAudioQueue.
47
// atomic locks are used on the lock. TODO: make this lock-free
48
std::atomic_flag atomicLock_;
49
50
// We copy samples as they are written into this simple ring buffer.
51
// Might try something more efficient later.
52
FixedSizeQueue<s16, 32768 * 8> chanSampleQueues[PSP_AUDIO_CHANNEL_MAX + 1];
53
54
int eventAudioUpdate = -1;
55
56
// TODO: This is now useless and should be removed. Just scared of breaking states.
57
int eventHostAudioUpdate = -1;
58
59
int mixFrequency = 44100;
60
int srcFrequency = 0;
61
62
const int hwSampleRate = 44100;
63
const int hwBlockSize = 64;
64
65
static int audioIntervalCycles;
66
static int audioHostIntervalCycles;
67
68
static s32 *mixBuffer;
69
static s16 *clampedMixBuffer;
70
#ifndef MOBILE_DEVICE
71
WaveFileWriter g_wave_writer;
72
static bool m_logAudio;
73
#endif
74
75
// High and low watermarks, basically. For perfect emulation, the correct values are 0 and 1, respectively.
76
// TODO: Tweak. Hm, there aren't actually even used currently...
77
static int chanQueueMaxSizeFactor;
78
static int chanQueueMinSizeFactor;
79
80
static void hleAudioUpdate(u64 userdata, int cyclesLate) {
81
// Schedule the next cycle first. __AudioUpdate() may consume cycles.
82
CoreTiming::ScheduleEvent(audioIntervalCycles - cyclesLate, eventAudioUpdate, 0);
83
84
__AudioUpdate();
85
}
86
87
static void hleHostAudioUpdate(u64 userdata, int cyclesLate) {
88
CoreTiming::ScheduleEvent(audioHostIntervalCycles - cyclesLate, eventHostAudioUpdate, 0);
89
}
90
91
static void __AudioCPUMHzChange() {
92
audioIntervalCycles = (int)(usToCycles(1000000ULL) * hwBlockSize / hwSampleRate);
93
94
// Soon to be removed.
95
audioHostIntervalCycles = (int)(usToCycles(1000000ULL) * 512 / hwSampleRate);
96
}
97
98
void __AudioInit() {
99
System_AudioResetStatCounters();
100
mixFrequency = 44100;
101
srcFrequency = 0;
102
103
chanQueueMaxSizeFactor = 2;
104
chanQueueMinSizeFactor = 1;
105
106
__AudioCPUMHzChange();
107
108
eventAudioUpdate = CoreTiming::RegisterEvent("AudioUpdate", &hleAudioUpdate);
109
eventHostAudioUpdate = CoreTiming::RegisterEvent("AudioUpdateHost", &hleHostAudioUpdate);
110
111
CoreTiming::ScheduleEvent(audioIntervalCycles, eventAudioUpdate, 0);
112
CoreTiming::ScheduleEvent(audioHostIntervalCycles, eventHostAudioUpdate, 0);
113
for (u32 i = 0; i < PSP_AUDIO_CHANNEL_MAX + 1; i++) {
114
g_audioChans[i].index = i;
115
g_audioChans[i].clear();
116
}
117
118
mixBuffer = new s32[hwBlockSize * 2];
119
clampedMixBuffer = new s16[hwBlockSize * 2];
120
memset(mixBuffer, 0, hwBlockSize * 2 * sizeof(s32));
121
122
System_AudioClear();
123
CoreTiming::RegisterMHzChangeCallback(&__AudioCPUMHzChange);
124
}
125
126
void __AudioDoState(PointerWrap &p) {
127
auto s = p.Section("sceAudio", 1, 2);
128
if (!s)
129
return;
130
131
Do(p, eventAudioUpdate);
132
CoreTiming::RestoreRegisterEvent(eventAudioUpdate, "AudioUpdate", &hleAudioUpdate);
133
Do(p, eventHostAudioUpdate);
134
CoreTiming::RestoreRegisterEvent(eventHostAudioUpdate, "AudioUpdateHost", &hleHostAudioUpdate);
135
136
Do(p, mixFrequency);
137
if (s >= 2) {
138
Do(p, srcFrequency);
139
} else {
140
// Assume that it was actually the SRC channel frequency.
141
srcFrequency = mixFrequency;
142
mixFrequency = 44100;
143
}
144
145
if (s >= 2) {
146
// TODO: Next time we bump, get rid of this. It's kinda useless.
147
auto s = p.Section("resampler", 1);
148
if (p.mode == p.MODE_READ) {
149
System_AudioClear();
150
}
151
} else {
152
// Only to preserve the previous file format. Might cause a slight audio glitch on upgrades?
153
FixedSizeQueue<s16, 512 * 16> outAudioQueue;
154
outAudioQueue.DoState(p);
155
156
System_AudioClear();
157
}
158
159
int chanCount = ARRAY_SIZE(g_audioChans);
160
Do(p, chanCount);
161
if (chanCount != ARRAY_SIZE(g_audioChans))
162
{
163
ERROR_LOG(Log::sceAudio, "Savestate failure: different number of audio channels.");
164
p.SetError(p.ERROR_FAILURE);
165
return;
166
}
167
for (int i = 0; i < chanCount; ++i) {
168
g_audioChans[i].index = i;
169
g_audioChans[i].DoState(p);
170
}
171
172
__AudioCPUMHzChange();
173
}
174
175
void __AudioShutdown() {
176
delete [] mixBuffer;
177
delete [] clampedMixBuffer;
178
179
mixBuffer = 0;
180
for (u32 i = 0; i < PSP_AUDIO_CHANNEL_MAX + 1; i++) {
181
g_audioChans[i].index = i;
182
g_audioChans[i].clear();
183
}
184
185
#ifndef MOBILE_DEVICE
186
if (g_Config.bDumpAudio) {
187
__StopLogAudio();
188
}
189
#endif
190
}
191
192
u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking) {
193
u32 ret = chan.sampleCount;
194
195
if (chan.sampleAddress == 0) {
196
// For some reason, multichannel audio lies and returns the sample count here.
197
if (chanNum == PSP_AUDIO_CHANNEL_SRC || chanNum == PSP_AUDIO_CHANNEL_OUTPUT2) {
198
ret = 0;
199
}
200
}
201
202
// If there's anything on the queue at all, it should be busy, but we try to be a bit lax.
203
//if (chanSampleQueues[chanNum].size() > chan.sampleCount * 2 * chanQueueMaxSizeFactor || chan.sampleAddress == 0) {
204
if (chanSampleQueues[chanNum].size() > 0) {
205
if (blocking) {
206
// TODO: Regular multichannel audio seems to block for 64 samples less? Or enqueue the first 64 sync?
207
int blockSamples = (int)chanSampleQueues[chanNum].size() / 2 / chanQueueMinSizeFactor;
208
209
if (__KernelIsDispatchEnabled()) {
210
AudioChannelWaitInfo waitInfo = {__KernelGetCurThread(), blockSamples};
211
chan.waitingThreads.push_back(waitInfo);
212
// Also remember the value to return in the waitValue.
213
__KernelWaitCurThread(WAITTYPE_AUDIOCHANNEL, (SceUID)chanNum + 1, ret, 0, false, "blocking audio");
214
} else {
215
// TODO: Maybe we shouldn't take this audio after all?
216
ret = SCE_KERNEL_ERROR_CAN_NOT_WAIT;
217
}
218
219
// Fall through to the sample queueing, don't want to lose the samples even though
220
// we're getting full. The PSP would enqueue after blocking.
221
} else {
222
// Non-blocking doesn't even enqueue, but it's not commonly used.
223
return SCE_ERROR_AUDIO_CHANNEL_BUSY;
224
}
225
}
226
227
if (chan.sampleAddress == 0) {
228
return ret;
229
}
230
231
// NOTE: The below is WRONG! See issue #20095.
232
//
233
// What we should be queueing here is just the sampleAddress and sampleCount. Then when dequeuing is when we should
234
// read the actual data.
235
236
int leftVol = chan.leftVolume;
237
int rightVol = chan.rightVolume;
238
239
if (leftVol == (1 << 15) && rightVol == (1 << 15) && chan.format == PSP_AUDIO_FORMAT_STEREO && IS_LITTLE_ENDIAN) {
240
// TODO: Add mono->stereo conversion to this path.
241
242
// Good news: the volume (1 << 15), specifically, doesn't affect the values at all.
243
// We can just do a direct memory copy.
244
const u32 totalSamples = chan.sampleCount * (chan.format == PSP_AUDIO_FORMAT_STEREO ? 2 : 1);
245
s16 *buf1 = 0, *buf2 = 0;
246
size_t sz1, sz2;
247
chanSampleQueues[chanNum].pushPointers(totalSamples, &buf1, &sz1, &buf2, &sz2);
248
249
if (Memory::IsValidAddress(chan.sampleAddress + (totalSamples - 1) * sizeof(s16_le))) {
250
Memory::Memcpy(buf1, chan.sampleAddress, (u32)sz1 * sizeof(s16));
251
if (buf2)
252
Memory::Memcpy(buf2, chan.sampleAddress + (u32)sz1 * sizeof(s16), (u32)sz2 * sizeof(s16));
253
}
254
} else {
255
// Remember that maximum volume allowed is 0xFFFFF so left shift is no issue.
256
// This way we can optimally shift by 16.
257
leftVol <<=1;
258
rightVol <<=1;
259
260
if (chan.format == PSP_AUDIO_FORMAT_STEREO) {
261
const u32 totalSamples = chan.sampleCount * 2;
262
263
s16_le *sampleData = (s16_le *) Memory::GetPointer(chan.sampleAddress);
264
265
// Walking a pointer for speed. But let's make sure we wouldn't trip on an invalid ptr.
266
if (Memory::IsValidAddress(chan.sampleAddress + (totalSamples - 1) * sizeof(s16_le))) {
267
s16 *buf1 = 0, *buf2 = 0;
268
size_t sz1, sz2;
269
chanSampleQueues[chanNum].pushPointers(totalSamples, &buf1, &sz1, &buf2, &sz2);
270
AdjustVolumeBlock(buf1, sampleData, sz1, leftVol, rightVol);
271
if (buf2) {
272
AdjustVolumeBlock(buf2, sampleData + sz1, sz2, leftVol, rightVol);
273
}
274
}
275
} else if (chan.format == PSP_AUDIO_FORMAT_MONO) {
276
// Rare, so unoptimized. Expands to stereo.
277
for (u32 i = 0; i < chan.sampleCount; i++) {
278
s16 sample = (s16)Memory::Read_U16(chan.sampleAddress + 2 * i);
279
chanSampleQueues[chanNum].push(ApplySampleVolume(sample, leftVol));
280
chanSampleQueues[chanNum].push(ApplySampleVolume(sample, rightVol));
281
}
282
}
283
}
284
return ret;
285
}
286
287
void __AudioWakeThreads(AudioChannel &chan, int result, int step) {
288
u32 error;
289
bool wokeThreads = false;
290
for (size_t w = 0; w < chan.waitingThreads.size(); ++w) {
291
AudioChannelWaitInfo &waitInfo = chan.waitingThreads[w];
292
waitInfo.numSamples -= step;
293
294
// If it's done (there will still be samples on queue) and actually still waiting, wake it up.
295
u32 waitID = __KernelGetWaitID(waitInfo.threadID, WAITTYPE_AUDIOCHANNEL, error);
296
if (waitInfo.numSamples <= 0 && waitID != 0) {
297
// DEBUG_LOG(Log::sceAudio, "Woke thread %i for some buffer filling", waitingThread);
298
u32 ret = result == 0 ? __KernelGetWaitValue(waitInfo.threadID, error) : SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED;
299
__KernelResumeThreadFromWait(waitInfo.threadID, ret);
300
wokeThreads = true;
301
302
chan.waitingThreads.erase(chan.waitingThreads.begin() + w--);
303
}
304
// This means the thread stopped waiting, so stop trying to wake it.
305
else if (waitID == 0)
306
chan.waitingThreads.erase(chan.waitingThreads.begin() + w--);
307
}
308
309
if (wokeThreads) {
310
__KernelReSchedule("audio drain");
311
}
312
}
313
314
void __AudioWakeThreads(AudioChannel &chan, int result) {
315
__AudioWakeThreads(chan, result, 0x7FFFFFFF);
316
}
317
318
void __AudioSetOutputFrequency(int freq) {
319
if (freq != 44100) {
320
WARN_LOG_REPORT(Log::sceAudio, "Switching audio frequency to %i", freq);
321
} else {
322
DEBUG_LOG(Log::sceAudio, "Switching audio frequency to %i", freq);
323
}
324
mixFrequency = freq;
325
}
326
327
void __AudioSetSRCFrequency(int freq) {
328
srcFrequency = freq;
329
}
330
331
// Mix samples from the various audio channels into a single sample queue, managed by the backend implementation.
332
void __AudioUpdate(bool resetRecording) {
333
// AUDIO throttle doesn't really work on the PSP since the mixing intervals are so closely tied
334
// to the CPU. Much better to throttle the frame rate on frame display and just throw away audio
335
// if the buffer somehow gets full.
336
bool firstChannel = true;
337
const int16_t srcBufferSize = hwBlockSize * 2;
338
int16_t srcBuffer[srcBufferSize];
339
340
for (u32 i = 0; i < PSP_AUDIO_CHANNEL_MAX + 1; i++) {
341
if (!g_audioChans[i].reserved) {
342
continue;
343
}
344
345
__AudioWakeThreads(g_audioChans[i], 0, hwBlockSize);
346
347
if (!chanSampleQueues[i].size()) {
348
continue;
349
}
350
351
bool needsResample = i == PSP_AUDIO_CHANNEL_SRC && srcFrequency != 0 && srcFrequency != mixFrequency;
352
size_t sz = needsResample ? (srcBufferSize * srcFrequency) / mixFrequency : srcBufferSize;
353
if (sz > chanSampleQueues[i].size()) {
354
ERROR_LOG(Log::sceAudio, "Channel %i buffer underrun at %i of %i", i, (int)chanSampleQueues[i].size() / 2, (int)sz / 2);
355
}
356
357
const s16 *buf1 = 0, *buf2 = 0;
358
size_t sz1, sz2;
359
360
chanSampleQueues[i].popPointers(sz, &buf1, &sz1, &buf2, &sz2);
361
362
// We do this check as the very last thing before mixing, to maximize compatibility.
363
if (g_audioChans[i].mute) {
364
continue;
365
}
366
367
if (needsResample) {
368
auto read = [&](size_t i) {
369
if (i < sz1)
370
return buf1[i];
371
if (i < sz1 + sz2)
372
return buf2[i - sz1];
373
if (buf2)
374
return buf2[sz2 - 1];
375
return buf1[sz1 - 1];
376
};
377
378
// TODO: This is terrible, since it's doing it by small chunk and discarding frac.
379
const uint32_t ratio = (uint32_t)(65536.0 * srcFrequency / (double)mixFrequency);
380
uint32_t frac = 0;
381
size_t readIndex = 0;
382
for (size_t outIndex = 0; readIndex < sz && outIndex < srcBufferSize; outIndex += 2) {
383
size_t readIndex2 = readIndex + 2;
384
int16_t l1 = read(readIndex);
385
int16_t r1 = read(readIndex + 1);
386
int16_t l2 = read(readIndex2);
387
int16_t r2 = read(readIndex2 + 1);
388
int sampleL = ((l1 << 16) + (l2 - l1) * (uint16_t)frac) >> 16;
389
int sampleR = ((r1 << 16) + (r2 - r1) * (uint16_t)frac) >> 16;
390
srcBuffer[outIndex] = sampleL;
391
srcBuffer[outIndex + 1] = sampleR;
392
frac += ratio;
393
readIndex += 2 * (uint16_t)(frac >> 16);
394
frac &= 0xffff;
395
}
396
397
buf1 = srcBuffer;
398
sz1 = srcBufferSize;
399
buf2 = nullptr;
400
sz2 = 0;
401
}
402
403
if (firstChannel) {
404
for (size_t s = 0; s < sz1; s++)
405
mixBuffer[s] = buf1[s];
406
if (buf2) {
407
for (size_t s = 0; s < sz2; s++)
408
mixBuffer[s + sz1] = buf2[s];
409
}
410
firstChannel = false;
411
} else {
412
// Surprisingly hard to SIMD efficiently on SSE2 due to lack of 16-to-32-bit sign extension. NEON should be straight-forward though, and SSE4.1 can do it nicely.
413
// Actually, the cmple/pack trick should work fine...
414
for (size_t s = 0; s < sz1; s++)
415
mixBuffer[s] += buf1[s];
416
if (buf2) {
417
for (size_t s = 0; s < sz2; s++)
418
mixBuffer[s + sz1] += buf2[s];
419
}
420
}
421
}
422
423
if (firstChannel) {
424
// Nothing was written above, let's memset.
425
memset(mixBuffer, 0, hwBlockSize * 2 * sizeof(s32));
426
}
427
428
if (g_Config.bEnableSound) {
429
float multiplier = Volume100ToMultiplier(std::clamp(g_Config.iGameVolume, 0, VOLUMEHI_FULL));
430
if (PSP_CoreParameter().fpsLimit != FPSLimit::NORMAL || PSP_CoreParameter().fastForward) {
431
if (g_Config.iAltSpeedVolume != -1) {
432
// Multiply in the alt speed volume instead of replacing like before.
433
multiplier *= Volume100ToMultiplier(g_Config.iAltSpeedVolume);
434
}
435
}
436
437
System_AudioPushSamples(mixBuffer, hwBlockSize, multiplier);
438
439
#ifndef MOBILE_DEVICE
440
if (g_Config.bSaveLoadResetsAVdumping && resetRecording) {
441
__StopLogAudio();
442
std::string discID = g_paramSFO.GetDiscID();
443
Path audio_file_name = GetSysDirectory(DIRECTORY_AUDIO) / StringFromFormat("%s_%s.wav", discID.c_str(), KernelTimeNowFormatted().c_str()).c_str();
444
INFO_LOG(Log::Common, "Restarted audio recording to: %s", audio_file_name.c_str());
445
if (!File::Exists(GetSysDirectory(DIRECTORY_AUDIO)))
446
File::CreateDir(GetSysDirectory(DIRECTORY_AUDIO));
447
File::CreateEmptyFile(audio_file_name);
448
__StartLogAudio(audio_file_name);
449
}
450
if (!m_logAudio) {
451
if (g_Config.bDumpAudio) {
452
// Use gameID_EmulatedTimestamp for filename
453
std::string discID = g_paramSFO.GetDiscID();
454
Path audio_file_name = GetSysDirectory(DIRECTORY_AUDIO) / StringFromFormat("%s_%s.wav", discID.c_str(), KernelTimeNowFormatted().c_str());
455
INFO_LOG(Log::Common,"Recording audio to: %s", audio_file_name.c_str());
456
// Create the path just in case it doesn't exist
457
if (!File::Exists(GetSysDirectory(DIRECTORY_AUDIO)))
458
File::CreateDir(GetSysDirectory(DIRECTORY_AUDIO));
459
File::CreateEmptyFile(audio_file_name);
460
__StartLogAudio(audio_file_name);
461
}
462
} else {
463
if (g_Config.bDumpAudio) {
464
for (int i = 0; i < hwBlockSize * 2; i++) {
465
clampedMixBuffer[i] = clamp_s16(mixBuffer[i]);
466
}
467
g_wave_writer.AddStereoSamples(clampedMixBuffer, hwBlockSize);
468
} else {
469
__StopLogAudio();
470
}
471
}
472
#endif
473
}
474
}
475
476
#ifndef MOBILE_DEVICE
477
void __StartLogAudio(const Path& filename) {
478
if (!m_logAudio) {
479
m_logAudio = true;
480
g_wave_writer.Start(filename, 44100);
481
g_wave_writer.SetSkipSilence(false);
482
NOTICE_LOG(Log::sceAudio, "Starting Audio logging");
483
} else {
484
WARN_LOG(Log::sceAudio, "Audio logging has already been started");
485
}
486
}
487
488
void __StopLogAudio() {
489
if (m_logAudio) {
490
m_logAudio = false;
491
g_wave_writer.Stop();
492
NOTICE_LOG(Log::sceAudio, "Stopping Audio logging");
493
} else {
494
WARN_LOG(Log::sceAudio, "Audio logging has already been stopped");
495
}
496
}
497
#endif
498
499
void WAVDump::Reset() {
500
__AudioUpdate(true);
501
}
502
503