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 // SAS is a software mixing engine that runs on the Media Engine CPU. We just HLE it.
19 // This is a very rough implementation that needs lots of work.
20 //
21 // This file just contains the API, the real stuff is in HW/SasAudio.cpp/h.
22 //
23 // JPCSP is, as it often is, a pretty good reference although I didn't actually use it much yet:
24 // http://code.google.com/p/jpcsp/source/browse/trunk/src/jpcsp/HLE/modules150/sceSasCore.java
25 //
26 // This should be multithreaded and improved at some point. Some discussion here:
27 // https://github.com/hrydgard/ppsspp/issues/1078
28 
29 #include <cstdlib>
30 #include <functional>
31 #include <thread>
32 #include <mutex>
33 #include <condition_variable>
34 
35 #include "Common/Profiler/Profiler.h"
36 #include "Common/Thread/ThreadUtil.h"
37 #include "Common/Serialize/SerializeFuncs.h"
38 #include "Common/Log.h"
39 #include "Core/Config.h"
40 #include "Core/CoreTiming.h"
41 #include "Core/HLE/HLE.h"
42 #include "Core/HLE/FunctionWrappers.h"
43 #include "Core/MIPS/MIPS.h"
44 #include "Core/HW/SasAudio.h"
45 #include "Core/MemMap.h"
46 #include "Core/Reporting.h"
47 
48 #include "Core/HLE/sceSas.h"
49 #include "Core/HLE/sceKernel.h"
50 #include "Core/HLE/sceKernelThread.h"
51 
52 enum {
53 	ERROR_SAS_INVALID_GRAIN           = 0x80420001,
54 	ERROR_SAS_INVALID_MAX_VOICES      = 0x80420002,
55 	ERROR_SAS_INVALID_OUTPUT_MODE     = 0x80420003,
56 	ERROR_SAS_INVALID_SAMPLE_RATE     = 0x80420004,
57 	ERROR_SAS_BAD_ADDRESS             = 0x80420005,
58 	ERROR_SAS_INVALID_VOICE           = 0x80420010,
59 	ERROR_SAS_INVALID_NOISE_FREQ      = 0x80420011,
60 	ERROR_SAS_INVALID_PITCH           = 0x80420012,
61 	ERROR_SAS_INVALID_ADSR_CURVE_MODE = 0x80420013,
62 	ERROR_SAS_INVALID_PARAMETER       = 0x80420014,
63 	ERROR_SAS_INVALID_LOOP_POS        = 0x80420015,
64 	ERROR_SAS_VOICE_PAUSED            = 0x80420016,
65 	ERROR_SAS_INVALID_VOLUME          = 0x80420018,
66 	ERROR_SAS_INVALID_ADSR_RATE       = 0x80420019,
67 	ERROR_SAS_INVALID_PCM_SIZE        = 0x8042001A,
68 	ERROR_SAS_REV_INVALID_TYPE        = 0x80420020,
69 	ERROR_SAS_REV_INVALID_FEEDBACK    = 0x80420021,
70 	ERROR_SAS_REV_INVALID_DELAY       = 0x80420022,
71 	ERROR_SAS_REV_INVALID_VOLUME      = 0x80420023,
72 	ERROR_SAS_BUSY                    = 0x80420030,
73 	ERROR_SAS_ATRAC3_ALREADY_SET      = 0x80420040,
74 	ERROR_SAS_ATRAC3_NOT_SET          = 0x80420041,
75 	ERROR_SAS_NOT_INIT                = 0x80420100,
76 };
77 
78 // TODO - allow more than one, associating each with one Core pointer (passed in to all the functions)
79 // No known games use more than one instance of Sas though.
80 static SasInstance *sas = NULL;
81 
82 enum SasThreadState {
83 	DISABLED,
84 	READY,
85 	QUEUED,
86 };
87 struct SasThreadParams {
88 	u32 outAddr;
89 	u32 inAddr;
90 	int leftVol;
91 	int rightVol;
92 };
93 
94 static std::thread *sasThread;
95 static std::mutex sasWakeMutex;
96 static std::mutex sasDoneMutex;
97 static std::condition_variable sasWake;
98 static std::condition_variable sasDone;
99 static volatile int sasThreadState = SasThreadState::DISABLED;
100 static SasThreadParams sasThreadParams;
101 static int sasMixEvent = -1;
102 
__SasThread()103 int __SasThread() {
104 	SetCurrentThreadName("SAS");
105 
106 	std::unique_lock<std::mutex> guard(sasWakeMutex);
107 	while (sasThreadState != SasThreadState::DISABLED) {
108 		sasWake.wait(guard);
109 		if (sasThreadState == SasThreadState::QUEUED) {
110 			sas->Mix(sasThreadParams.outAddr, sasThreadParams.inAddr, sasThreadParams.leftVol, sasThreadParams.rightVol);
111 
112 			std::lock_guard<std::mutex> doneGuard(sasDoneMutex);
113 			sasThreadState = SasThreadState::READY;
114 			sasDone.notify_one();
115 		}
116 	}
117 	return 0;
118 }
119 
__SasDrain()120 static void __SasDrain() {
121 	std::unique_lock<std::mutex> guard(sasDoneMutex);
122 	while (sasThreadState == SasThreadState::QUEUED)
123 		sasDone.wait(guard);
124 }
125 
__SasEnqueueMix(u32 outAddr,u32 inAddr=0,int leftVol=0,int rightVol=0)126 static void __SasEnqueueMix(u32 outAddr, u32 inAddr = 0, int leftVol = 0, int rightVol = 0) {
127 	if (sasThreadState == SasThreadState::DISABLED) {
128 		// No thread, call it immediately.
129 		sas->Mix(outAddr, inAddr, leftVol, rightVol);
130 		return;
131 	}
132 
133 	if (sasThreadState == SasThreadState::QUEUED) {
134 		// Wait for the queue to drain.
135 		__SasDrain();
136 	}
137 
138 	// We're safe to write, since it can't be processing now anymore.
139 	// No other thread enqueues.
140 	sasThreadParams.outAddr = outAddr;
141 	sasThreadParams.inAddr = inAddr;
142 	sasThreadParams.leftVol = leftVol;
143 	sasThreadParams.rightVol = rightVol;
144 
145 	// And now, notify.
146 	sasWakeMutex.lock();
147 	sasThreadState = SasThreadState::QUEUED;
148 	sasWake.notify_one();
149 	sasWakeMutex.unlock();
150 }
151 
__SasDisableThread()152 static void __SasDisableThread() {
153 	if (sasThreadState != SasThreadState::DISABLED) {
154 		sasWakeMutex.lock();
155 		sasThreadState = SasThreadState::DISABLED;
156 		sasWake.notify_one();
157 		sasWakeMutex.unlock();
158 		sasThread->join();
159 		delete sasThread;
160 		sasThread = nullptr;
161 	}
162 }
163 
sasMixFinish(u64 userdata,int cycleslate)164 static void sasMixFinish(u64 userdata, int cycleslate) {
165 	PROFILE_THIS_SCOPE("mixer");
166 
167 	u32 error;
168 	SceUID threadID = (SceUID)userdata;
169 	SceUID verify = __KernelGetWaitID(threadID, WAITTYPE_HLEDELAY, error);
170 	u64 result = __KernelGetWaitValue(threadID, error);
171 
172 	if (error == 0 && verify == 1) {
173 		// Wait until it's actually complete before waking the thread.
174 		__SasDrain();
175 
176 		__KernelResumeThreadFromWait(threadID, result);
177 		__KernelReSchedule("woke from sas mix");
178 	} else {
179 		WARN_LOG(HLE, "Someone else woke up SAS-blocked thread?");
180 	}
181 }
182 
__SasInit()183 void __SasInit() {
184 	sas = new SasInstance();
185 
186 	sasMixEvent = CoreTiming::RegisterEvent("SasMix", sasMixFinish);
187 
188 	if (g_Config.bSeparateSASThread) {
189 		sasThreadState = SasThreadState::READY;
190 		sasThread = new std::thread(__SasThread);
191 	} else {
192 		sasThreadState = SasThreadState::DISABLED;
193 	}
194 }
195 
__SasDoState(PointerWrap & p)196 void __SasDoState(PointerWrap &p) {
197 	auto s = p.Section("sceSas", 1, 2);
198 	if (!s)
199 		return;
200 
201 	if (sasThreadState == SasThreadState::QUEUED) {
202 		// Wait for the queue to drain.  Don't want to save the wrong stuff.
203 		__SasDrain();
204 	}
205 
206 	DoClass(p, sas);
207 
208 	if (s >= 2) {
209 		Do(p, sasMixEvent);
210 	} else {
211 		sasMixEvent = -1;
212 		__SasDisableThread();
213 	}
214 
215 	CoreTiming::RestoreRegisterEvent(sasMixEvent, "SasMix", sasMixFinish);
216 }
217 
__SasShutdown()218 void __SasShutdown() {
219 	__SasDisableThread();
220 
221 	delete sas;
222 	sas = 0;
223 }
224 
225 
sceSasInit(u32 core,u32 grainSize,u32 maxVoices,u32 outputMode,u32 sampleRate)226 static u32 sceSasInit(u32 core, u32 grainSize, u32 maxVoices, u32 outputMode, u32 sampleRate) {
227 	if (!Memory::IsValidAddress(core) || (core & 0x3F) != 0) {
228 		ERROR_LOG_REPORT(SCESAS, "sceSasInit(%08x, %i, %i, %i, %i): bad core address", core, grainSize, maxVoices, outputMode, sampleRate);
229 		return ERROR_SAS_BAD_ADDRESS;
230 	}
231 	if (maxVoices == 0 || maxVoices > PSP_SAS_VOICES_MAX) {
232 		ERROR_LOG_REPORT(SCESAS, "sceSasInit(%08x, %i, %i, %i, %i): bad max voices", core, grainSize, maxVoices, outputMode, sampleRate);
233 		return ERROR_SAS_INVALID_MAX_VOICES;
234 	}
235 	if (grainSize < 0x40 || grainSize > 0x800 || (grainSize & 0x1F) != 0) {
236 		ERROR_LOG_REPORT(SCESAS, "sceSasInit(%08x, %i, %i, %i, %i): bad grain size", core, grainSize, maxVoices, outputMode, sampleRate);
237 		return ERROR_SAS_INVALID_GRAIN;
238 	}
239 	if (outputMode != 0 && outputMode != 1) {
240 		ERROR_LOG_REPORT(SCESAS, "sceSasInit(%08x, %i, %i, %i, %i): bad output mode", core, grainSize, maxVoices, outputMode, sampleRate);
241 		return ERROR_SAS_INVALID_OUTPUT_MODE;
242 	}
243 	if (sampleRate != 44100) {
244 		ERROR_LOG_REPORT(SCESAS, "sceSasInit(%08x, %i, %i, %i, %i): bad sample rate", core, grainSize, maxVoices, outputMode, sampleRate);
245 		return ERROR_SAS_INVALID_SAMPLE_RATE;
246 	}
247 	INFO_LOG(SCESAS, "sceSasInit(%08x, %i, %i, %i, %i)", core, grainSize, maxVoices, outputMode, sampleRate);
248 
249 	sas->SetGrainSize(grainSize);
250 	// Seems like maxVoices is actually ignored for all intents and purposes.
251 	sas->maxVoices = PSP_SAS_VOICES_MAX;
252 	sas->outputMode = outputMode;
253 	for (int i = 0; i < sas->maxVoices; i++) {
254 		sas->voices[i].sampleRate = sampleRate;
255 		sas->voices[i].playing = false;
256 		sas->voices[i].loop = false;
257 	}
258 	return 0;
259 }
260 
sceSasGetEndFlag(u32 core)261 static u32 sceSasGetEndFlag(u32 core) {
262 	u32 endFlag = 0;
263 	__SasDrain();
264 	for (int i = 0; i < sas->maxVoices; i++) {
265 		if (!sas->voices[i].playing)
266 			endFlag |= (1 << i);
267 	}
268 
269 	VERBOSE_LOG(SCESAS, "%08x=sceSasGetEndFlag(%08x)", endFlag, core);
270 	return endFlag;
271 }
272 
delaySasResult(int result)273 static int delaySasResult(int result) {
274 	const int usec = sas->EstimateMixUs();
275 
276 	// No event, fall back.
277 	if (sasMixEvent == -1) {
278 		return hleDelayResult(result, "sas core", usec);
279 	}
280 
281 	CoreTiming::ScheduleEvent(usToCycles(usec), sasMixEvent, __KernelGetCurThread());
282 	__KernelWaitCurThread(WAITTYPE_HLEDELAY, 1, result, 0, false, "sas core");
283 	return result;
284 }
285 
286 // Runs the mixer
_sceSasCore(u32 core,u32 outAddr)287 static u32 _sceSasCore(u32 core, u32 outAddr) {
288 	PROFILE_THIS_SCOPE("mixer");
289 
290 	if (!Memory::IsValidAddress(outAddr)) {
291 		return hleReportError(SCESAS, ERROR_SAS_INVALID_PARAMETER, "invalid address");
292 	}
293 	if (!__KernelIsDispatchEnabled()) {
294 		return hleLogError(SCESAS, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch disabled");
295 	}
296 
297 	__SasEnqueueMix(outAddr);
298 
299 	return hleLogSuccessI(SCESAS, delaySasResult(0));
300 }
301 
302 // Another way of running the mixer, the inoutAddr should be both input and output
_sceSasCoreWithMix(u32 core,u32 inoutAddr,int leftVolume,int rightVolume)303 static u32 _sceSasCoreWithMix(u32 core, u32 inoutAddr, int leftVolume, int rightVolume) {
304 	PROFILE_THIS_SCOPE("mixer");
305 
306 	if (!Memory::IsValidAddress(inoutAddr)) {
307 		return hleReportError(SCESAS, ERROR_SAS_INVALID_PARAMETER, "invalid address");
308 	}
309 	if (sas->outputMode == PSP_SAS_OUTPUTMODE_RAW) {
310 		return hleReportError(SCESAS, 0x80000004, "unsupported outputMode");
311 	}
312 	if (!__KernelIsDispatchEnabled()) {
313 		return hleLogError(SCESAS, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch disabled");
314 	}
315 
316 	__SasEnqueueMix(inoutAddr, inoutAddr, leftVolume, rightVolume);
317 
318 	return hleLogSuccessI(SCESAS, delaySasResult(0));
319 }
320 
sceSasSetVoice(u32 core,int voiceNum,u32 vagAddr,int size,int loop)321 static u32 sceSasSetVoice(u32 core, int voiceNum, u32 vagAddr, int size, int loop) {
322 	if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)	{
323 		return hleLogWarning(SCESAS, ERROR_SAS_INVALID_VOICE, "invalid voicenum");
324 	}
325 
326 	if (size == 0 || ((u32)size & 0xF) != 0) {
327 		if (size == 0) {
328 			DEBUG_LOG(SCESAS, "%s: invalid size %d", __FUNCTION__, size);
329 		} else {
330 			WARN_LOG(SCESAS, "%s: invalid size %d", __FUNCTION__, size);
331 		}
332 		return ERROR_SAS_INVALID_PARAMETER;
333 	}
334 	if (loop != 0 && loop != 1) {
335 		WARN_LOG_REPORT(SCESAS, "%s: invalid loop mode %d", __FUNCTION__, loop);
336 		return ERROR_SAS_INVALID_LOOP_POS;
337 	}
338 
339 	if (!Memory::IsValidAddress(vagAddr)) {
340 		ERROR_LOG(SCESAS, "%s: Ignoring invalid VAG audio address %08x", __FUNCTION__, vagAddr);
341 		return 0;
342 	}
343 
344 	__SasDrain();
345 	SasVoice &v = sas->voices[voiceNum];
346 	if (v.type == VOICETYPE_ATRAC3) {
347 		return hleLogError(SCESAS, ERROR_SAS_ATRAC3_ALREADY_SET, "voice is already ATRAC3");
348 	}
349 
350 	if (size < 0) {
351 		// POSSIBLE HACK
352 		// SetVoice with negative sizes returns OK (0) unlike SetVoicePCM, but should not
353 		// play any audio, it seems. So let's bail and not do anything.
354 		// Needs more rigorous testing perhaps, but this fixes issue https://github.com/hrydgard/ppsspp/issues/5652
355 		// while being fairly low risk to other games.
356 		size = 0;
357 		DEBUG_LOG(SCESAS, "sceSasSetVoice(%08x, %i, %08x, %i, %i) : HACK: Negative size changed to 0", core, voiceNum, vagAddr, size, loop);
358 	} else {
359 		DEBUG_LOG(SCESAS, "sceSasSetVoice(%08x, %i, %08x, %i, %i)", core, voiceNum, vagAddr, size, loop);
360 	}
361 
362 	u32 prevVagAddr = v.vagAddr;
363 	v.type = VOICETYPE_VAG;
364 	v.vagAddr = vagAddr;  // Real VAG header is 0x30 bytes behind the vagAddr
365 	v.vagSize = size;
366 	v.loop = loop ? true : false;
367 	v.ChangedParams(vagAddr == prevVagAddr);
368 	return 0;
369 }
370 
sceSasSetVoicePCM(u32 core,int voiceNum,u32 pcmAddr,int size,int loopPos)371 static u32 sceSasSetVoicePCM(u32 core, int voiceNum, u32 pcmAddr, int size, int loopPos) {
372 	if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
373 		return hleLogWarning(SCESAS, ERROR_SAS_INVALID_VOICE, "invalid voicenum");
374 	}
375 	if (size <= 0 || size > 0x10000) {
376 		WARN_LOG(SCESAS, "%s: invalid size %d", __FUNCTION__, size);
377 		return ERROR_SAS_INVALID_PCM_SIZE;
378 	}
379 	if (loopPos >= size) {
380 		ERROR_LOG_REPORT(SCESAS, "sceSasSetVoicePCM(%08x, %i, %08x, %i, %i): bad loop pos", core, voiceNum, pcmAddr, size, loopPos);
381 		return ERROR_SAS_INVALID_LOOP_POS;
382 	}
383 	if (!Memory::IsValidAddress(pcmAddr)) {
384 		ERROR_LOG(SCESAS, "Ignoring invalid PCM audio address %08x", pcmAddr);
385 		return 0;
386 	}
387 
388 	__SasDrain();
389 	SasVoice &v = sas->voices[voiceNum];
390 	if (v.type == VOICETYPE_ATRAC3) {
391 		return hleLogError(SCESAS, ERROR_SAS_ATRAC3_ALREADY_SET, "voice is already ATRAC3");
392 	}
393 
394 	DEBUG_LOG(SCESAS, "sceSasSetVoicePCM(%08x, %i, %08x, %i, %i)", core, voiceNum, pcmAddr, size, loopPos);
395 
396 	u32 prevPcmAddr = v.pcmAddr;
397 	v.type = VOICETYPE_PCM;
398 	v.pcmAddr = pcmAddr;
399 	v.pcmSize = size;
400 	v.pcmIndex = 0;
401 	v.pcmLoopPos = loopPos >= 0 ? loopPos : 0;
402 	v.loop = loopPos >= 0 ? true : false;
403 	v.playing = true;
404 	v.ChangedParams(pcmAddr == prevPcmAddr);
405 	return 0;
406 }
407 
sceSasGetPauseFlag(u32 core)408 static u32 sceSasGetPauseFlag(u32 core) {
409 	u32 pauseFlag = 0;
410 	__SasDrain();
411 	for (int i = 0; i < sas->maxVoices; i++) {
412 		if (sas->voices[i].paused)
413 			pauseFlag |= (1 << i);
414 	}
415 
416 	DEBUG_LOG(SCESAS, "sceSasGetPauseFlag(%08x)", pauseFlag);
417 	return pauseFlag;
418 }
419 
sceSasSetPause(u32 core,u32 voicebit,int pause)420 static u32 sceSasSetPause(u32 core, u32 voicebit, int pause) {
421 	DEBUG_LOG(SCESAS, "sceSasSetPause(%08x, %08x, %i)", core, voicebit, pause);
422 
423 	__SasDrain();
424 	for (int i = 0; voicebit != 0; i++, voicebit >>= 1) {
425 		if (i < PSP_SAS_VOICES_MAX && i >= 0) {
426 			if ((voicebit & 1) != 0)
427 				sas->voices[i].paused = pause ? true : false;
428 		}
429 	}
430 
431 	return 0;
432 }
433 
sceSasSetVolume(u32 core,int voiceNum,int leftVol,int rightVol,int effectLeftVol,int effectRightVol)434 static u32 sceSasSetVolume(u32 core, int voiceNum, int leftVol, int rightVol, int effectLeftVol, int effectRightVol) {
435 	DEBUG_LOG(SCESAS, "sceSasSetVolume(%08x, %i, %i, %i, %i, %i)", core, voiceNum, leftVol, rightVol, effectLeftVol, effectRightVol);
436 
437 	if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)	{
438 		WARN_LOG(SCESAS, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
439 		return ERROR_SAS_INVALID_VOICE;
440 	}
441 	bool overVolume = abs(leftVol) > PSP_SAS_VOL_MAX || abs(rightVol) > PSP_SAS_VOL_MAX;
442 	overVolume = overVolume || abs(effectLeftVol) > PSP_SAS_VOL_MAX || abs(effectRightVol) > PSP_SAS_VOL_MAX;
443 	if (overVolume)
444 		return ERROR_SAS_INVALID_VOLUME;
445 
446 	__SasDrain();
447 	SasVoice &v = sas->voices[voiceNum];
448 	v.volumeLeft = leftVol;
449 	v.volumeRight = rightVol;
450 	v.effectLeft = effectLeftVol;
451 	v.effectRight = effectRightVol;
452 	return 0;
453 }
454 
sceSasSetPitch(u32 core,int voiceNum,int pitch)455 static u32 sceSasSetPitch(u32 core, int voiceNum, int pitch) {
456 	if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)	{
457 		WARN_LOG(SCESAS, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
458 		return ERROR_SAS_INVALID_VOICE;
459 	}
460 	if (pitch < PSP_SAS_PITCH_MIN || pitch > PSP_SAS_PITCH_MAX) {
461 		WARN_LOG(SCESAS, "sceSasSetPitch(%08x, %i, %i): bad pitch", core, voiceNum, pitch);
462 		return ERROR_SAS_INVALID_PITCH;
463 	}
464 
465 	DEBUG_LOG(SCESAS, "sceSasSetPitch(%08x, %i, %i)", core, voiceNum, pitch);
466 	__SasDrain();
467 	SasVoice &v = sas->voices[voiceNum];
468 	v.pitch = pitch;
469 	v.ChangedParams(false);
470 	return 0;
471 }
472 
sceSasSetKeyOn(u32 core,int voiceNum)473 static u32 sceSasSetKeyOn(u32 core, int voiceNum) {
474 	DEBUG_LOG(SCESAS, "sceSasSetKeyOn(%08x, %i)", core, voiceNum);
475 
476 	if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)	{
477 		WARN_LOG(SCESAS, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
478 		return ERROR_SAS_INVALID_VOICE;
479 	}
480 
481 	__SasDrain();
482 	if (sas->voices[voiceNum].paused || sas->voices[voiceNum].on) {
483 		return ERROR_SAS_VOICE_PAUSED;
484 	}
485 
486 	SasVoice &v = sas->voices[voiceNum];
487 	v.KeyOn();
488 	return 0;
489 }
490 
491 // sceSasSetKeyOff can be used to start sounds, that just sound during the Release phase!
sceSasSetKeyOff(u32 core,int voiceNum)492 static u32 sceSasSetKeyOff(u32 core, int voiceNum) {
493 	if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
494 		WARN_LOG(SCESAS, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
495 		return ERROR_SAS_INVALID_VOICE;
496 	} else {
497 		DEBUG_LOG(SCESAS, "sceSasSetKeyOff(%08x, %i)", core, voiceNum);
498 
499 		__SasDrain();
500 		if (sas->voices[voiceNum].paused || !sas->voices[voiceNum].on) {
501 			return ERROR_SAS_VOICE_PAUSED;
502 		}
503 
504 		SasVoice &v = sas->voices[voiceNum];
505 		v.KeyOff();
506 		return 0;
507 	}
508 }
509 
sceSasSetNoise(u32 core,int voiceNum,int freq)510 static u32 sceSasSetNoise(u32 core, int voiceNum, int freq) {
511 	if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)	{
512 		WARN_LOG(SCESAS, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
513 		return ERROR_SAS_INVALID_VOICE;
514 	}
515 	if (freq < 0 || freq >= 64) {
516 		DEBUG_LOG(SCESAS, "sceSasSetNoise(%08x, %i, %i)", core, voiceNum, freq);
517 		return ERROR_SAS_INVALID_NOISE_FREQ;
518 	}
519 
520 	DEBUG_LOG(SCESAS, "sceSasSetNoise(%08x, %i, %i)", core, voiceNum, freq);
521 
522 	__SasDrain();
523 	SasVoice &v = sas->voices[voiceNum];
524 	v.type = VOICETYPE_NOISE;
525 	v.noiseFreq = freq;
526 	v.ChangedParams(true);
527 	return 0;
528 }
529 
sceSasSetSL(u32 core,int voiceNum,int level)530 static u32 sceSasSetSL(u32 core, int voiceNum, int level) {
531 	if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)	{
532 		WARN_LOG(SCESAS, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
533 		return ERROR_SAS_INVALID_VOICE;
534 	}
535 
536 	DEBUG_LOG(SCESAS, "sceSasSetSL(%08x, %i, %08x)", core, voiceNum, level);
537 	__SasDrain();
538 	SasVoice &v = sas->voices[voiceNum];
539 	v.envelope.sustainLevel = level;
540 	return 0;
541 }
542 
sceSasSetADSR(u32 core,int voiceNum,int flag,int a,int d,int s,int r)543 static u32 sceSasSetADSR(u32 core, int voiceNum, int flag, int a, int d, int s, int r) {
544 	if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)	{
545 		WARN_LOG(SCESAS, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
546 		return ERROR_SAS_INVALID_VOICE;
547 	}
548 	// Create a mask like flag for the invalid values.
549 	int invalid = (a < 0 ? 0x1 : 0) | (d < 0 ? 0x2 : 0) | (s < 0 ? 0x4 : 0) | (r < 0 ? 0x8 : 0);
550 	if (invalid & flag) {
551 		WARN_LOG_REPORT(SCESAS, "sceSasSetADSR(%08x, %i, %i, %08x, %08x, %08x, %08x): invalid value", core, voiceNum, flag, a, d, s, r);
552 		return ERROR_SAS_INVALID_ADSR_RATE;
553 	}
554 
555 	DEBUG_LOG(SCESAS, "0=sceSasSetADSR(%08x, %i, %i, %08x, %08x, %08x, %08x)", core, voiceNum, flag, a, d, s, r);
556 
557 	__SasDrain();
558 	SasVoice &v = sas->voices[voiceNum];
559 	if ((flag & 0x1) != 0) v.envelope.attackRate  = a;
560 	if ((flag & 0x2) != 0) v.envelope.decayRate   = d;
561 	if ((flag & 0x4) != 0) v.envelope.sustainRate = s;
562 	if ((flag & 0x8) != 0) v.envelope.releaseRate = r;
563 	return 0;
564 }
565 
sceSasSetADSRMode(u32 core,int voiceNum,int flag,int a,int d,int s,int r)566 static u32 sceSasSetADSRMode(u32 core, int voiceNum, int flag, int a, int d, int s, int r) {
567 	if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)	{
568 		WARN_LOG(SCESAS, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
569 		return ERROR_SAS_INVALID_VOICE;
570 	}
571 
572 	// Probably by accident (?), the PSP ignores the top bit of these values.
573 	a = a & ~0x80000000;
574 	d = d & ~0x80000000;
575 	s = s & ~0x80000000;
576 	r = r & ~0x80000000;
577 
578 	// This will look like the update flag for the invalid modes.
579 	int invalid = 0;
580 	if (a > 5 || (a & 1) != 0) {
581 		invalid |= 0x1;
582 	}
583 	if (d > 5 || (d & 1) != 1) {
584 		invalid |= 0x2;
585 	}
586 	if (s > 5) {
587 		invalid |= 0x4;
588 	}
589 	if (r > 5 || (r & 1) != 1) {
590 		invalid |= 0x8;
591 	}
592 	if (invalid & flag) {
593 		if (a == 5 && d == 5 && s == 5 && r == 5) {
594 			// Some games do this right at init.  It seems to fail even on a PSP, but let's not report it.
595 			DEBUG_LOG(SCESAS, "sceSasSetADSRMode(%08x, %i, %i, %08x, %08x, %08x, %08x): invalid modes", core, voiceNum, flag, a, d, s, r);
596 		} else {
597 			WARN_LOG_REPORT(SCESAS, "sceSasSetADSRMode(%08x, %i, %i, %08x, %08x, %08x, %08x): invalid modes", core, voiceNum, flag, a, d, s, r);
598 		}
599 		return ERROR_SAS_INVALID_ADSR_CURVE_MODE;
600 	}
601 
602 	DEBUG_LOG(SCESAS, "sceSasSetADSRMode(%08x, %i, %i, %08x, %08x, %08x, %08x)", core, voiceNum, flag, a, d, s, r);
603 	__SasDrain();
604 	SasVoice &v = sas->voices[voiceNum];
605 	if ((flag & 0x1) != 0) v.envelope.attackType  = a;
606 	if ((flag & 0x2) != 0) v.envelope.decayType   = d;
607 	if ((flag & 0x4) != 0) v.envelope.sustainType = s;
608 	if ((flag & 0x8) != 0) v.envelope.releaseType = r;
609 	return 0;
610 }
611 
612 
sceSasSetSimpleADSR(u32 core,int voiceNum,u32 ADSREnv1,u32 ADSREnv2)613 static u32 sceSasSetSimpleADSR(u32 core, int voiceNum, u32 ADSREnv1, u32 ADSREnv2) {
614 	if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)	{
615 		WARN_LOG(SCESAS, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
616 		return ERROR_SAS_INVALID_VOICE;
617 	}
618 	// This bit could be related to decay type or systain type, but gives an error if you try to set it.
619 	if ((ADSREnv2 >> 13) & 1) {
620 		WARN_LOG_REPORT(SCESAS, "sceSasSetSimpleADSR(%08x, %d, %04x, %04x): Invalid ADSREnv2", core, voiceNum, ADSREnv1, ADSREnv2);
621 		return ERROR_SAS_INVALID_ADSR_CURVE_MODE;
622 	}
623 
624 	DEBUG_LOG(SCESAS, "sasSetSimpleADSR(%08x, %i, %08x, %08x)", core, voiceNum, ADSREnv1, ADSREnv2);
625 
626 	__SasDrain();
627 	SasVoice &v = sas->voices[voiceNum];
628 	v.envelope.SetSimpleEnvelope(ADSREnv1 & 0xFFFF, ADSREnv2 & 0xFFFF);
629 	return 0;
630 }
631 
sceSasGetEnvelopeHeight(u32 core,int voiceNum)632 static u32 sceSasGetEnvelopeHeight(u32 core, int voiceNum) {
633 	if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)	{
634 		ERROR_LOG(SCESAS, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
635 		return ERROR_SAS_INVALID_VOICE;
636 	}
637 
638 	__SasDrain();
639 	SasVoice &v = sas->voices[voiceNum];
640 	int height = v.envelope.GetHeight();
641 	DEBUG_LOG(SCESAS, "%i = sceSasGetEnvelopeHeight(%08x, %i)", height, core, voiceNum);
642 	return height;
643 }
644 
sceSasRevType(u32 core,int type)645 static u32 sceSasRevType(u32 core, int type) {
646 	if (type < PSP_SAS_EFFECT_TYPE_OFF || type > PSP_SAS_EFFECT_TYPE_MAX) {
647 		return hleLogError(SCESAS, ERROR_SAS_REV_INVALID_TYPE, "invalid type");
648 	}
649 
650 	__SasDrain();
651 	sas->SetWaveformEffectType(type);
652 	return hleLogSuccessI(SCESAS, 0);
653 }
654 
sceSasRevParam(u32 core,int delay,int feedback)655 static u32 sceSasRevParam(u32 core, int delay, int feedback) {
656 	if (delay < 0 || delay >= 128) {
657 		return hleLogError(SCESAS, ERROR_SAS_REV_INVALID_DELAY, "invalid delay value");
658 	}
659 	if (feedback < 0 || feedback >= 128) {
660 		return hleLogError(SCESAS, ERROR_SAS_REV_INVALID_FEEDBACK, "invalid feedback value");
661 	}
662 
663 	__SasDrain();
664 	sas->waveformEffect.delay = delay;
665 	sas->waveformEffect.feedback = feedback;
666 	return hleLogSuccessI(SCESAS, 0);
667 }
668 
sceSasRevEVOL(u32 core,u32 lv,u32 rv)669 static u32 sceSasRevEVOL(u32 core, u32 lv, u32 rv) {
670 	if (lv > 0x1000 || rv > 0x1000) {
671 		return hleReportDebug(SCESAS, ERROR_SAS_REV_INVALID_VOLUME, "invalid volume");
672 	}
673 
674 	__SasDrain();
675 	sas->waveformEffect.leftVol = lv;
676 	sas->waveformEffect.rightVol = rv;
677 	return hleLogSuccessI(SCESAS, 0);
678 }
679 
sceSasRevVON(u32 core,int dry,int wet)680 static u32 sceSasRevVON(u32 core, int dry, int wet) {
681 	__SasDrain();
682 	sas->waveformEffect.isDryOn = dry != 0;
683 	sas->waveformEffect.isWetOn = wet != 0;
684 	return hleLogSuccessI(SCESAS, 0);
685 }
686 
sceSasGetGrain(u32 core)687 static u32 sceSasGetGrain(u32 core) {
688 	DEBUG_LOG(SCESAS, "sceSasGetGrain(%08x)", core);
689 	return sas->GetGrainSize();
690 }
691 
sceSasSetGrain(u32 core,int grain)692 static u32 sceSasSetGrain(u32 core, int grain) {
693 	INFO_LOG(SCESAS, "sceSasSetGrain(%08x, %i)", core, grain);
694 	__SasDrain();
695 	sas->SetGrainSize(grain);
696 	return 0;
697 }
698 
sceSasGetOutputMode(u32 core)699 static u32 sceSasGetOutputMode(u32 core) {
700 	DEBUG_LOG(SCESAS, "sceSasGetOutputMode(%08x)", core);
701 	return sas->outputMode;
702 }
703 
sceSasSetOutputMode(u32 core,u32 outputMode)704 static u32 sceSasSetOutputMode(u32 core, u32 outputMode) {
705 	if (outputMode != 0 && outputMode != 1) {
706 		ERROR_LOG_REPORT(SCESAS, "sceSasSetOutputMode(%08x, %i): bad output mode", core, outputMode);
707 		return ERROR_SAS_INVALID_OUTPUT_MODE;
708 	}
709 	DEBUG_LOG(SCESAS, "sceSasSetOutputMode(%08x, %i)", core, outputMode);
710 	__SasDrain();
711 	sas->outputMode = outputMode;
712 
713 	return 0;
714 }
715 
sceSasGetAllEnvelopeHeights(u32 core,u32 heightsAddr)716 static u32 sceSasGetAllEnvelopeHeights(u32 core, u32 heightsAddr) {
717 	DEBUG_LOG(SCESAS, "sceSasGetAllEnvelopeHeights(%08x, %i)", core, heightsAddr);
718 
719 	if (!Memory::IsValidAddress(heightsAddr)) {
720 		return ERROR_SAS_INVALID_PARAMETER;
721 	}
722 
723 	__SasDrain();
724 	for (int i = 0; i < PSP_SAS_VOICES_MAX; i++) {
725 		int voiceHeight = sas->voices[i].envelope.GetHeight();
726 		Memory::Write_U32(voiceHeight, heightsAddr + i * 4);
727 	}
728 
729 	return 0;
730 }
731 
sceSasSetTriangularWave(u32 sasCore,int voice,int unknown)732 static u32 sceSasSetTriangularWave(u32 sasCore, int voice, int unknown) {
733 	ERROR_LOG_REPORT(SCESAS, "UNIMPL sceSasSetTriangularWave(%08x, %i, %i)", sasCore, voice, unknown);
734 	return 0;
735 }
736 
sceSasSetSteepWave(u32 sasCore,int voice,int unknown)737 static u32 sceSasSetSteepWave(u32 sasCore, int voice, int unknown) {
738 	ERROR_LOG_REPORT(SCESAS, "UNIMPL sceSasSetSteepWave(%08x, %i, %i)", sasCore, voice, unknown);
739 	return 0;
740 }
741 
__sceSasSetVoiceATRAC3(u32 core,int voiceNum,u32 atrac3Context)742 static u32 __sceSasSetVoiceATRAC3(u32 core, int voiceNum, u32 atrac3Context) {
743 	if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
744 		return hleLogWarning(SCESAS, ERROR_SAS_INVALID_VOICE, "invalid voicenum");
745 	}
746 
747 	__SasDrain();
748 	SasVoice &v = sas->voices[voiceNum];
749 	if (v.type == VOICETYPE_ATRAC3) {
750 		return hleLogError(SCESAS, ERROR_SAS_ATRAC3_ALREADY_SET, "voice is already ATRAC3");
751 	}
752 	v.type = VOICETYPE_ATRAC3;
753 	v.loop = false;
754 	v.playing = true;
755 	v.atrac3.setContext(atrac3Context);
756 	Memory::Write_U32(atrac3Context, core + 56 * voiceNum + 20);
757 
758 	return hleLogSuccessI(SCESAS, 0);
759 }
760 
__sceSasConcatenateATRAC3(u32 core,int voiceNum,u32 atrac3DataAddr,int atrac3DataLength)761 static u32 __sceSasConcatenateATRAC3(u32 core, int voiceNum, u32 atrac3DataAddr, int atrac3DataLength) {
762 	if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
763 		return hleLogWarning(SCESAS, ERROR_SAS_INVALID_VOICE, "invalid voicenum");
764 	}
765 
766 	DEBUG_LOG_REPORT(SCESAS, "__sceSasConcatenateATRAC3(%08x, %i, %08x, %i)", core, voiceNum, atrac3DataAddr, atrac3DataLength);
767 	__SasDrain();
768 	SasVoice &v = sas->voices[voiceNum];
769 	if (Memory::IsValidAddress(atrac3DataAddr))
770 		v.atrac3.addStreamData(atrac3DataAddr, atrac3DataLength);
771 	return 0;
772 }
773 
__sceSasUnsetATRAC3(u32 core,int voiceNum)774 static u32 __sceSasUnsetATRAC3(u32 core, int voiceNum) {
775 	if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
776 		return hleLogWarning(SCESAS, ERROR_SAS_INVALID_VOICE, "invalid voicenum");
777 	}
778 
779 	__SasDrain();
780 	SasVoice &v = sas->voices[voiceNum];
781 	if (v.type != VOICETYPE_ATRAC3) {
782 		return hleLogError(SCESAS, ERROR_SAS_ATRAC3_NOT_SET, "voice is not ATRAC3");
783 	}
784 	v.type = VOICETYPE_OFF;
785 	v.playing = false;
786 	v.on = false;
787 	// This unpauses.  Some games, like Sol Trigger, depend on this.
788 	v.paused = false;
789 	Memory::Write_U32(0, core + 56 * voiceNum + 20);
790 
791 	return hleLogSuccessI(SCESAS, 0);
792 }
793 
__SasGetDebugStats(char * stats,size_t bufsize)794 void __SasGetDebugStats(char *stats, size_t bufsize) {
795 	if (sas) {
796 		sas->GetDebugText(stats, bufsize);
797 	} else {
798 		snprintf(stats, bufsize, "Sas not initialized");
799 	}
800 }
801 
802 const HLEFunction sceSasCore[] =
803 {
804 	{0X42778A9F, &WrapU_UUUUU<sceSasInit>,               "__sceSasInit",                  'x', "xxxxx"  },
805 	{0XA3589D81, &WrapU_UU<_sceSasCore>,                 "__sceSasCore",                  'x', "xx"     },
806 	{0X50A14DFC, &WrapU_UUII<_sceSasCoreWithMix>,        "__sceSasCoreWithMix",           'x', "xxii"   },
807 	{0X68A46B95, &WrapU_U<sceSasGetEndFlag>,             "__sceSasGetEndFlag",            'x', "x"      },
808 	{0X440CA7D8, &WrapU_UIIIII<sceSasSetVolume>,         "__sceSasSetVolume",             'x', "xiiiii" },
809 	{0XAD84D37F, &WrapU_UII<sceSasSetPitch>,             "__sceSasSetPitch",              'x', "xii"    },
810 	{0X99944089, &WrapU_UIUII<sceSasSetVoice>,           "__sceSasSetVoice",              'x', "xixii"  },
811 	{0XB7660A23, &WrapU_UII<sceSasSetNoise>,             "__sceSasSetNoise",              'x', "xii"    },
812 	{0X019B25EB, &WrapU_UIIIIII<sceSasSetADSR>,          "__sceSasSetADSR",               'x', "xiiiiii"},
813 	{0X9EC3676A, &WrapU_UIIIIII<sceSasSetADSRMode>,      "__sceSasSetADSRmode",           'x', "xiiiiii"},
814 	{0X5F9529F6, &WrapU_UII<sceSasSetSL>,                "__sceSasSetSL",                 'x', "xii"    },
815 	{0X74AE582A, &WrapU_UI<sceSasGetEnvelopeHeight>,     "__sceSasGetEnvelopeHeight",     'x', "xi"     },
816 	{0XCBCD4F79, &WrapU_UIUU<sceSasSetSimpleADSR>,       "__sceSasSetSimpleADSR",         'x', "xixx"   },
817 	{0XA0CF2FA4, &WrapU_UI<sceSasSetKeyOff>,             "__sceSasSetKeyOff",             'x', "xi"     },
818 	{0X76F01ACA, &WrapU_UI<sceSasSetKeyOn>,              "__sceSasSetKeyOn",              'x', "xi"     },
819 	{0XF983B186, &WrapU_UII<sceSasRevVON>,               "__sceSasRevVON",                'x', "xii"    },
820 	{0XD5A229C9, &WrapU_UUU<sceSasRevEVOL>,              "__sceSasRevEVOL",               'x', "xxx"    },
821 	{0X33D4AB37, &WrapU_UI<sceSasRevType>,               "__sceSasRevType",               'x', "xi"     },
822 	{0X267A6DD2, &WrapU_UII<sceSasRevParam>,             "__sceSasRevParam",              'x', "xii"    },
823 	{0X2C8E6AB3, &WrapU_U<sceSasGetPauseFlag>,           "__sceSasGetPauseFlag",          'x', "x"      },
824 	{0X787D04D5, &WrapU_UUI<sceSasSetPause>,             "__sceSasSetPause",              'x', "xxi"    },
825 	{0XA232CBE6, &WrapU_UII<sceSasSetTriangularWave>,    "__sceSasSetTrianglarWave",      'x', "xii"    }, // Typo.
826 	{0XD5EBBBCD, &WrapU_UII<sceSasSetSteepWave>,         "__sceSasSetSteepWave",          'x', "xii"    },
827 	{0XBD11B7C2, &WrapU_U<sceSasGetGrain>,               "__sceSasGetGrain",              'x', "x"      },
828 	{0XD1E0A01E, &WrapU_UI<sceSasSetGrain>,              "__sceSasSetGrain",              'x', "xi"     },
829 	{0XE175EF66, &WrapU_U<sceSasGetOutputMode>,          "__sceSasGetOutputmode",         'x', "x"      },
830 	{0XE855BF76, &WrapU_UU<sceSasSetOutputMode>,         "__sceSasSetOutputmode",         'x', "xx"     },
831 	{0X07F58C24, &WrapU_UU<sceSasGetAllEnvelopeHeights>, "__sceSasGetAllEnvelopeHeights", 'x', "xx"     },
832 	{0XE1CD9561, &WrapU_UIUII<sceSasSetVoicePCM>,        "__sceSasSetVoicePCM",           'x', "xixii"  },
833 	{0X4AA9EAD6, &WrapU_UIU<__sceSasSetVoiceATRAC3>,     "__sceSasSetVoiceATRAC3",        'x', "xix"    },
834 	{0X7497EA85, &WrapU_UIUI<__sceSasConcatenateATRAC3>, "__sceSasConcatenateATRAC3",     'x', "xixi"   },
835 	{0XF6107F00, &WrapU_UI<__sceSasUnsetATRAC3>,         "__sceSasUnsetATRAC3",           'x', "xi"     },
836 };
837 
Register_sceSasCore()838 void Register_sceSasCore()
839 {
840 	RegisterModule("sceSasCore", ARRAY_SIZE(sceSasCore), sceSasCore);
841 }
842 
843