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