mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Lots of various work on sceSas. ADSR envelopes, correct looping, etc etc... Still far from perfect but better.
This commit is contained in:
parent
5d11bc7c3c
commit
1400237d0e
8 changed files with 447 additions and 143 deletions
|
@ -45,6 +45,7 @@ enum LOG_TYPE {
|
|||
INTC,
|
||||
MEMMAP,
|
||||
SOUND,
|
||||
SAS,
|
||||
HLE,
|
||||
TIMER,
|
||||
VIDEO,
|
||||
|
|
|
@ -56,6 +56,7 @@ LogManager::LogManager()
|
|||
m_Log[LogTypes::INTC] = new LogContainer("INTC", "Interrupts");
|
||||
m_Log[LogTypes::MEMMAP] = new LogContainer("MM", "Memory Map");
|
||||
m_Log[LogTypes::SOUND] = new LogContainer("SND", "Sound");
|
||||
m_Log[LogTypes::SAS] = new LogContainer("SAS", "Sound Mixer (Sas)");
|
||||
m_Log[LogTypes::HLE] = new LogContainer("HLE", "HLE");
|
||||
m_Log[LogTypes::TIMER] = new LogContainer("TMR", "Timer");
|
||||
m_Log[LogTypes::VIDEO] = new LogContainer("VID", "Video");
|
||||
|
|
|
@ -486,19 +486,19 @@ void Idle(int maxIdle)
|
|||
if (maxIdle != 0 && cyclesDown > maxIdle)
|
||||
cyclesDown = maxIdle;
|
||||
|
||||
if (first && cyclesDown > 0)
|
||||
{
|
||||
int cyclesExecuted = slicelength - downcount;
|
||||
int cyclesNextEvent = (int) (first->time - globalTimer);
|
||||
if (first && cyclesDown > 0)
|
||||
{
|
||||
int cyclesExecuted = slicelength - downcount;
|
||||
int cyclesNextEvent = (int) (first->time - globalTimer);
|
||||
|
||||
if (cyclesNextEvent < cyclesExecuted + cyclesDown)
|
||||
{
|
||||
cyclesDown = cyclesNextEvent - cyclesExecuted;
|
||||
// Now, now... no time machines, please.
|
||||
if (cyclesDown < 0)
|
||||
cyclesDown = 0;
|
||||
}
|
||||
}
|
||||
if (cyclesNextEvent < cyclesExecuted + cyclesDown)
|
||||
{
|
||||
cyclesDown = cyclesNextEvent - cyclesExecuted;
|
||||
// Now, now... no time machines, please.
|
||||
if (cyclesDown < 0)
|
||||
cyclesDown = 0;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_LOG(CPU, "Idle for %i cycles! (%f ms)", cyclesDown, cyclesDown / (float)(CPU_HZ * 0.001f));
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ void PSPSaveDialog::Init(int paramAddr)
|
|||
case SCE_UTILITY_SAVEDATA_TYPE_LIST:
|
||||
display = DS_NONE;
|
||||
break;
|
||||
case SCE_UTILITY_SAVEDATA_TYPE_DELETE: // This run on PSP display a list of all save on the PSP. Weird.
|
||||
case SCE_UTILITY_SAVEDATA_TYPE_DELETE: // This run on PSP display a list of all save on the PSP. Weird. (Not really, it's to let you free up space)
|
||||
default:
|
||||
{
|
||||
ERROR_LOG(HLE, "Load/Save function %d not coded. Title: %s Save: %s File: %s", param.GetPspParam()->mode, param.GetGameName(param.GetPspParam()).c_str(), param.GetGameName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str());
|
||||
|
|
|
@ -436,7 +436,6 @@ struct DeviceSize {
|
|||
};
|
||||
|
||||
u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr, int outLen) {
|
||||
|
||||
if (strcmp(name, "emulator:")) {
|
||||
DEBUG_LOG(HLE,"sceIoDevctl(\"%s\", %08x, %08x, %i, %08x, %i)", name, cmd,argAddr,argLen,outPtr,outLen);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
// SAS is a software mixing engine that runs on the Media Engine CPU. We just HLE it.
|
||||
// This is a very rough implementation that needs lots of work.
|
||||
//
|
||||
// This file just contains the API, the real stuff is in HW/SasAudio.cpp/h.
|
||||
//
|
||||
// JPCSP is, as it often is, a pretty good reference although I didn't actually use it much yet:
|
||||
// http://code.google.com/p/jpcsp/source/browse/trunk/src/jpcsp/HLE/modules150/sceSasCore.java
|
||||
|
||||
|
@ -30,6 +32,15 @@
|
|||
#include "sceSas.h"
|
||||
#include "sceKernel.h"
|
||||
|
||||
enum {
|
||||
ERROR_SAS_INVALID_VOICE = 0x80420010,
|
||||
ERROR_SAS_INVALID_ADSR_CURVE_MODE = 0x80420013,
|
||||
ERROR_SAS_INVALID_PARAMETER = 0x80420014,
|
||||
ERROR_SAS_VOICE_PAUSED = 0x80420016,
|
||||
ERROR_SAS_INVALID_SIZE = 0x8042001A,
|
||||
ERROR_SAS_BUSY = 0x80420030,
|
||||
ERROR_SAS_NOT_INIT = 0x80420100,
|
||||
};
|
||||
|
||||
// TODO - allow more than one, associating each with one Core pointer (passed in to all the functions)
|
||||
// No known games use more than one instance of Sas though.
|
||||
|
@ -65,6 +76,9 @@ u32 sceSasGetEndFlag(u32 core)
|
|||
u32 _sceSasCore(u32 core, u32 outAddr)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasCore(, %08x) (grain: %i samples)", outAddr, sas.GetGrainSize());
|
||||
if (!Memory::IsValidAddress(outAddr)) {
|
||||
return ERROR_SAS_INVALID_PARAMETER;
|
||||
}
|
||||
Memory::Memset(outAddr, 0, sas.GetGrainSize() * 2 * 2);
|
||||
sas.Mix(outAddr);
|
||||
return 0;
|
||||
|
@ -74,6 +88,9 @@ u32 _sceSasCore(u32 core, u32 outAddr)
|
|||
u32 _sceSasCoreWithMix(u32 core, u32 outAddr)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasCoreWithMix(, %08x) (grain: %i samples)", outAddr, sas.GetGrainSize());
|
||||
if (!Memory::IsValidAddress(outAddr)) {
|
||||
return ERROR_SAS_INVALID_PARAMETER;
|
||||
}
|
||||
sas.Mix(outAddr);
|
||||
return 0;
|
||||
}
|
||||
|
@ -86,7 +103,7 @@ u32 sceSasSetVoice(u32 core, int voiceNum, u32 vagAddr, int size, int loop)
|
|||
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)
|
||||
{
|
||||
WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
|
||||
return 0;
|
||||
return ERROR_SAS_INVALID_VOICE;
|
||||
}
|
||||
|
||||
//Real VAG header is 0x30 bytes behind the vagAddr
|
||||
|
@ -94,11 +111,22 @@ u32 sceSasSetVoice(u32 core, int voiceNum, u32 vagAddr, int size, int loop)
|
|||
v.type = VOICETYPE_VAG;
|
||||
v.vagAddr = vagAddr;
|
||||
v.vagSize = size;
|
||||
v.loop = loop;
|
||||
v.playing = false;
|
||||
v.loop = loop ? true : false;
|
||||
v.ChangedParams();
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 sceSasGetPauseFlag(u32 core)
|
||||
{
|
||||
u32 pauseFlag = 0;
|
||||
for (int i = 0; i < sas.maxVoices; i++) {
|
||||
if (sas.voices[i].paused)
|
||||
pauseFlag |= 1 << i;
|
||||
}
|
||||
DEBUG_LOG(HLE,"%08x=sceSasGetPauseFlag()", pauseFlag);
|
||||
return pauseFlag;
|
||||
}
|
||||
|
||||
u32 sceSasSetPause(u32 core, int voicebit, int pause)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasSetPause(core=%08x, voicebit=%08x, pause=%i)", core, voicebit, pause);
|
||||
|
@ -107,11 +135,11 @@ u32 sceSasSetPause(u32 core, int voicebit, int pause)
|
|||
if (i < PSP_SAS_VOICES_MAX && i >= 0)
|
||||
{
|
||||
if ((voicebit & 1) != 0)
|
||||
sas.voices[i].setPaused = pause;
|
||||
sas.voices[i].paused = pause ? true : false;
|
||||
}
|
||||
// TODO: Correct error code? Mimana crashes otherwise.
|
||||
else
|
||||
return -1;
|
||||
return ERROR_SAS_INVALID_VOICE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -124,7 +152,7 @@ u32 sceSasSetVolume(u32 core, int voiceNum, int l, int r, int el, int er)
|
|||
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)
|
||||
{
|
||||
WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
|
||||
return 0;
|
||||
return ERROR_SAS_INVALID_VOICE;
|
||||
}
|
||||
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
|
@ -140,7 +168,7 @@ u32 sceSasSetPitch(u32 core, int voiceNum, int pitch)
|
|||
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)
|
||||
{
|
||||
WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
|
||||
return 0;
|
||||
return ERROR_SAS_INVALID_VOICE;
|
||||
}
|
||||
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
|
@ -155,16 +183,14 @@ u32 sceSasSetKeyOn(u32 core, int voiceNum)
|
|||
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)
|
||||
{
|
||||
WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
|
||||
return 0;
|
||||
return ERROR_SAS_INVALID_VOICE;
|
||||
}
|
||||
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
v.vag.Start(Memory::GetPointer(v.vagAddr));
|
||||
v.playing = true;
|
||||
v.KeyOn();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: We really need ADSR work:
|
||||
// sceSasSetKeyOff can be used to start sounds, that just sound during the Release phase!
|
||||
u32 sceSasSetKeyOff(u32 core, int voiceNum)
|
||||
{
|
||||
|
@ -173,11 +199,11 @@ u32 sceSasSetKeyOff(u32 core, int voiceNum)
|
|||
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)
|
||||
{
|
||||
WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
|
||||
return 0;
|
||||
return ERROR_SAS_INVALID_VOICE;
|
||||
}
|
||||
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
v.playing = false; // not right! Should directly enter Release envelope stage instead!
|
||||
v.KeyOff();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -188,7 +214,7 @@ u32 sceSasSetNoise(u32 core, int voiceNum, int freq)
|
|||
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)
|
||||
{
|
||||
WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
|
||||
return 0;
|
||||
return ERROR_SAS_INVALID_VOICE;
|
||||
}
|
||||
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
|
@ -204,7 +230,7 @@ u32 sceSasSetSL(u32 core, int voiceNum, int level)
|
|||
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)
|
||||
{
|
||||
WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
|
||||
return 0;
|
||||
return ERROR_SAS_INVALID_VOICE;
|
||||
}
|
||||
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
|
@ -219,7 +245,7 @@ u32 sceSasSetADSR(u32 core, int voiceNum,int flag ,int a, int d, int s, int r)
|
|||
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)
|
||||
{
|
||||
WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
|
||||
return 0;
|
||||
return ERROR_SAS_INVALID_VOICE;
|
||||
}
|
||||
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
|
@ -237,7 +263,7 @@ u32 sceSasSetADSRMode(u32 core, int voiceNum,int flag ,int a, int d, int s, int
|
|||
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)
|
||||
{
|
||||
WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
|
||||
return 0;
|
||||
return ERROR_SAS_INVALID_VOICE;
|
||||
}
|
||||
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
|
@ -264,43 +290,34 @@ u32 sceSasGetEnvelopeHeight(u32 core, u32 voiceNum)
|
|||
{
|
||||
DEBUG_LOG(HLE,"UNIMPL 0=sceSasGetEnvelopeHeight(core=%08x, voicenum=%i)", core, voiceNum);
|
||||
}
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)
|
||||
{
|
||||
WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
|
||||
return ERROR_SAS_INVALID_VOICE;
|
||||
}
|
||||
|
||||
return v.playing ? 0x3fffffff : 0;
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
return v.envelope.GetHeight();
|
||||
}
|
||||
|
||||
u32 sceSasRevType(u32 core, int type)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasRevType(core=%08x, type=%i)", core, type);
|
||||
|
||||
sas.waveformEffect.type=type;
|
||||
sas.waveformEffect.type = type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 sceSasRevParam(u32 core, int delay, int feedback)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasRevParam(core=%08x, delay=%i, feedback=%i)", core, delay, feedback);
|
||||
|
||||
sas.waveformEffect.delay = delay;
|
||||
sas.waveformEffect.feedback = feedback;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 sceSasGetPauseFlag(u32 core)
|
||||
{
|
||||
u32 PauseFlag = 0;
|
||||
for (int i = 0; i < sas.maxVoices; i++) {
|
||||
if (!sas.voices[i].playing)
|
||||
PauseFlag |= 1 << i;
|
||||
}
|
||||
DEBUG_LOG(HLE,"%08x=sceSasGetPauseFlag()", PauseFlag);
|
||||
return PauseFlag;
|
||||
}
|
||||
|
||||
u32 sceSasRevEVOL(u32 core, int lv, int rv)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasRevEVOL(core=%08x, leftVolume=%i, rightVolume=%i)", core, lv, rv);
|
||||
|
||||
sas.waveformEffect.leftVol = lv;
|
||||
sas.waveformEffect.rightVol = rv;
|
||||
return 0;
|
||||
|
@ -309,7 +326,6 @@ u32 sceSasRevEVOL(u32 core, int lv, int rv)
|
|||
u32 sceSasRevVON(u32 core, int dry, int wet)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasRevVON(core=%08x, dry=%i, wet=%i)", core, dry, wet);
|
||||
|
||||
sas.waveformEffect.isDryOn = (dry > 0);
|
||||
sas.waveformEffect.isWetOn = (wet > 0);
|
||||
return 0;
|
||||
|
@ -345,9 +361,10 @@ u32 sceSasSetVoicePCM(u32 core, int voiceNum, u32 pcmAddr, int size, int loop)
|
|||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasSetVoicePCM(core=%08x, voicenum=%i, pcmAddr=%08x, size=%i, loop=%i)",core, voiceNum, pcmAddr, size, loop);
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
v.type = VOICETYPE_PCM;
|
||||
v.pcmAddr = pcmAddr;
|
||||
v.pcmSize = size;
|
||||
v.loop = loop;
|
||||
v.loop = loop ? true : false;
|
||||
v.playing = true;
|
||||
return 0;
|
||||
}
|
||||
|
@ -357,22 +374,34 @@ u32 sceSasGetAllEnvelopeHeights(u32 core, u32 heightsAddr)
|
|||
DEBUG_LOG(HLE,"0=sceSasGetAllEnvelopeHeights(core=%08x, heightsAddr=%i)", core, heightsAddr);
|
||||
if (Memory::IsValidAddress(heightsAddr)) {
|
||||
for (int i = 0; i < sas.length; i++) {
|
||||
int voiceHeight = sas.voices[i].envelope.height;
|
||||
int voiceHeight = sas.voices[i].envelope.GetHeight();
|
||||
Memory::Write_U32(voiceHeight, heightsAddr + i * 4);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 sceSasSetTriangularWave(u32 sasCore, int voice, int unknown)
|
||||
{
|
||||
ERROR_LOG(HLE,"UNIMPL 0=sceSasSetTriangularWave(core=%08x, voice=%i, unknown=%i)", sasCore, voice, unknown);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 sceSasSetSteepWave(u32 sasCore, int voice, int unknown)
|
||||
{
|
||||
ERROR_LOG(HLE,"UNIMPL 0=sceSasSetSteepWave(core=%08x, voice=%i, unknown=%i)", sasCore, voice, unknown);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const HLEFunction sceSasCore[] =
|
||||
{
|
||||
{0x42778a9f, WrapU_UUUUU<sceSasInit>, "__sceSasInit"}, // (SceUID * sasCore, int grain, int maxVoices, int outputMode, int sampleRate)
|
||||
{0xa3589d81, WrapU_UU<_sceSasCore>, "__sceSasCore"},
|
||||
{0x50a14dfc, WrapU_UU<_sceSasCoreWithMix>, "__sceSasCoreWithMix"}, // Process and mix into buffer (int sasCore, int sasInOut, int leftVolume, int rightVolume)
|
||||
{0x68a46b95, WrapU_U<sceSasGetEndFlag>, "__sceSasGetEndFlag"}, // int sasCore
|
||||
{0x68a46b95, WrapU_U<sceSasGetEndFlag>, "__sceSasGetEndFlag"},
|
||||
{0x440ca7d8, WrapU_UIIIII<sceSasSetVolume>, "__sceSasSetVolume"},
|
||||
{0xad84d37f, WrapU_UII<sceSasSetPitch>, "__sceSasSetPitch"},
|
||||
{0x99944089, WrapU_UIUII<sceSasSetVoice>, "__sceSasSetVoice"}, // (int sasCore, int voice, int vagAddr, int size, int loopmode)
|
||||
{0x99944089, WrapU_UIUII<sceSasSetVoice>, "__sceSasSetVoice"},
|
||||
{0xb7660a23, WrapU_UII<sceSasSetNoise>, "__sceSasSetNoise"},
|
||||
{0x019b25eb, WrapU_UIIIIII<sceSasSetADSR>, "__sceSasSetADSR"},
|
||||
{0x9ec3676a, WrapU_UIIIIII<sceSasSetADSRMode>, "__sceSasSetADSRmode"},
|
||||
|
@ -380,20 +409,20 @@ const HLEFunction sceSasCore[] =
|
|||
{0x74ae582a, WrapU_UU<sceSasGetEnvelopeHeight>, "__sceSasGetEnvelopeHeight"},
|
||||
{0xcbcd4f79, WrapU_UUUU<sceSasSetSimpleADSR>, "__sceSasSetSimpleADSR"},
|
||||
{0xa0cf2fa4, WrapU_UI<sceSasSetKeyOff>, "__sceSasSetKeyOff"},
|
||||
{0x76f01aca, WrapU_UI<sceSasSetKeyOn>, "__sceSasSetKeyOn"}, // (int sasCore, int voice)
|
||||
{0xf983b186, WrapU_UII<sceSasRevVON>, "__sceSasRevVON"}, // int sasCore, int dry, int wet
|
||||
{0xd5a229c9, WrapU_UII<sceSasRevEVOL>, "__sceSasRevEVOL"}, // (int sasCore, int leftVol, int rightVol) // effect volume
|
||||
{0x33d4ab37, WrapU_UI<sceSasRevType>, "__sceSasRevType"}, // (int sasCore, int type)
|
||||
{0x267a6dd2, WrapU_UII<sceSasRevParam>, "__sceSasRevParam"}, // (int sasCore, int delay, int feedback)
|
||||
{0x2c8e6ab3, WrapU_U<sceSasGetPauseFlag>, "__sceSasGetPauseFlag"}, // int sasCore
|
||||
{0x76f01aca, WrapU_UI<sceSasSetKeyOn>, "__sceSasSetKeyOn"},
|
||||
{0xf983b186, WrapU_UII<sceSasRevVON>, "__sceSasRevVON"},
|
||||
{0xd5a229c9, WrapU_UII<sceSasRevEVOL>, "__sceSasRevEVOL"}, // effect volume
|
||||
{0x33d4ab37, WrapU_UI<sceSasRevType>, "__sceSasRevType"},
|
||||
{0x267a6dd2, WrapU_UII<sceSasRevParam>, "__sceSasRevParam"},
|
||||
{0x2c8e6ab3, WrapU_U<sceSasGetPauseFlag>, "__sceSasGetPauseFlag"},
|
||||
{0x787d04d5, WrapU_UII<sceSasSetPause>, "__sceSasSetPause"},
|
||||
{0xa232cbe6, 0, "__sceSasSetTriangularWave"}, // (int sasCore, int voice, int unknown)
|
||||
{0xd5ebbbcd, 0, "__sceSasSetSteepWave"}, // (int sasCore, int voice, int unknown) // square wave?
|
||||
{0xa232cbe6, WrapU_UII<sceSasSetTriangularWave>, "__sceSasSetTriangularWave"},
|
||||
{0xd5ebbbcd, WrapU_UII<sceSasSetSteepWave>, "__sceSasSetSteepWave"},
|
||||
{0xBD11B7C2, WrapU_U<sceSasGetGrain>, "__sceSasGetGrain"},
|
||||
{0xd1e0a01e, WrapU_UI<sceSasSetGrain>, "__sceSasSetGrain"},
|
||||
{0xe175ef66, WrapU_U<sceSasGetOutputMode>, "__sceSasGetOutputmode"},
|
||||
{0xe855bf76, WrapU_UU<sceSasSetOutputMode>, "__sceSasSetOutputmode"},
|
||||
{0x07f58c24, WrapU_UU<sceSasGetAllEnvelopeHeights>, "__sceSasGetAllEnvelopeHeights"}, // (int sasCore, int heightAddr) 32-bit heights, 0-0x40000000
|
||||
{0x07f58c24, WrapU_UU<sceSasGetAllEnvelopeHeights>, "__sceSasGetAllEnvelopeHeights"},
|
||||
{0xE1CD9561, WrapU_UIUII<sceSasSetVoicePCM>, "__sceSasSetVoicePCM"},
|
||||
};
|
||||
|
||||
|
|
|
@ -27,15 +27,21 @@ static const double f[5][2] =
|
|||
{ 98.0 / 64.0, -55.0 / 64.0 },
|
||||
{ 122.0 / 64.0, -60.0 / 64.0 } };
|
||||
|
||||
void VagDecoder::Start(u8 *data)
|
||||
void VagDecoder::Start(u8 *data, bool loopEnabled)
|
||||
{
|
||||
loopEnabled_ = loopEnabled;
|
||||
loopAtNextBlock_ = false;
|
||||
loopStartBlock_ = 0;
|
||||
end_ = false;
|
||||
data_ = data;
|
||||
read_ = data;
|
||||
curSample = 28;
|
||||
curBlock_ = -1;
|
||||
s_1 = 0.0; // per block?
|
||||
s_2 = 0.0;
|
||||
}
|
||||
|
||||
bool VagDecoder::Decode()
|
||||
bool VagDecoder::DecodeBlock()
|
||||
{
|
||||
int predict_nr = GetByte();
|
||||
int shift_factor = predict_nr & 0xf;
|
||||
|
@ -46,6 +52,12 @@ bool VagDecoder::Decode()
|
|||
end_ = true;
|
||||
return false;
|
||||
}
|
||||
else if (flags == 6) {
|
||||
loopStartBlock_ = curBlock_;
|
||||
}
|
||||
else if (flags == 3 && loopEnabled_) {
|
||||
loopAtNextBlock_ = true;
|
||||
}
|
||||
for (int i = 0; i < 28; i += 2)
|
||||
{
|
||||
int d = GetByte();
|
||||
|
@ -65,9 +77,29 @@ bool VagDecoder::Decode()
|
|||
s_1 = samples[i];
|
||||
}
|
||||
curSample = 0;
|
||||
curBlock_++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void VagDecoder::GetSamples(s16 *outSamples, int numSamples) {
|
||||
if (end_) {
|
||||
memset(outSamples, 0, numSamples * sizeof(int));
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < numSamples; i++) {
|
||||
if (curSample == 28) {
|
||||
if (loopAtNextBlock_) {
|
||||
read_ = data_ + 16 * loopStartBlock_;
|
||||
curBlock_ = loopStartBlock_;
|
||||
s_1 = 0.0;
|
||||
s_2 = 0.0;
|
||||
}
|
||||
DecodeBlock();
|
||||
}
|
||||
outSamples[i] = end_ ? 0 : samples[curSample++];
|
||||
}
|
||||
}
|
||||
|
||||
// http://code.google.com/p/jpcsp/source/browse/trunk/src/jpcsp/HLE/modules150/sceSasCore.java
|
||||
|
||||
int simpleRate(int n) {
|
||||
|
@ -105,7 +137,7 @@ static int getSustainType(int bitfield2) {
|
|||
case 4: return PSP_SAS_ADSR_CURVE_MODE_LINEAR_BENT;
|
||||
case 6: return PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE;
|
||||
}
|
||||
ERROR_LOG(HLE,"sasSetSimpleADSR,ERROR_SAS_INVALID_ADSR_CURVE_MODE");
|
||||
ERROR_LOG(SAS,"sasSetSimpleADSR,ERROR_SAS_INVALID_ADSR_CURVE_MODE");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -128,8 +160,7 @@ static int getSustainLevel(int bitfield1) {
|
|||
return ((bitfield1 & 0x000F) + 1) << 26;
|
||||
}
|
||||
|
||||
void ADSREnvelope::SetSimpleEnvelope(u32 ADSREnv1, u32 ADSREnv2)
|
||||
{
|
||||
void ADSREnvelope::SetSimpleEnvelope(u32 ADSREnv1, u32 ADSREnv2) {
|
||||
attackRate = getAttackRate(ADSREnv1);
|
||||
attackType = getAttackType(ADSREnv1);
|
||||
decayRate = getDecayRate(ADSREnv1);
|
||||
|
@ -141,12 +172,40 @@ void ADSREnvelope::SetSimpleEnvelope(u32 ADSREnv1, u32 ADSREnv2)
|
|||
sustainLevel = getSustainLevel(ADSREnv1);
|
||||
}
|
||||
|
||||
void SasInstance::Mix(u32 outAddr)
|
||||
{
|
||||
SasInstance::SasInstance() : mixBuffer(0), sendBuffer(0), resampleBuffer(0), grainSize(0) {
|
||||
}
|
||||
|
||||
SasInstance::~SasInstance() {
|
||||
delete [] mixBuffer;
|
||||
delete [] sendBuffer;
|
||||
delete [] resampleBuffer;
|
||||
}
|
||||
|
||||
void SasInstance::SetGrainSize(int newGrainSize) {
|
||||
if (mixBuffer)
|
||||
delete [] mixBuffer;
|
||||
if (sendBuffer)
|
||||
delete [] sendBuffer;
|
||||
mixBuffer = new s32[newGrainSize * 2];
|
||||
sendBuffer = new s32[newGrainSize * 2];
|
||||
memset(mixBuffer, 0, sizeof(int) * newGrainSize * 2);
|
||||
memset(sendBuffer, 0, sizeof(int) * newGrainSize * 2);
|
||||
grainSize = newGrainSize;
|
||||
if (resampleBuffer)
|
||||
delete [] resampleBuffer;
|
||||
|
||||
// 2 samples padding at the start, that's where we copy the two last samples from the channel
|
||||
// so that we can do bicubic resampling if necessary.
|
||||
resampleBuffer = new s16[grainSize * 4 + 2];
|
||||
}
|
||||
|
||||
void SasInstance::Mix(u32 outAddr) {
|
||||
int voicesPlayingCount = 0;
|
||||
for (int v = 0; v < PSP_SAS_VOICES_MAX; v++) {
|
||||
SasVoice &voice = voices[v];
|
||||
if (!voice.playing)
|
||||
if (!voice.playing || voice.paused)
|
||||
continue;
|
||||
voicesPlayingCount++;
|
||||
|
||||
// TODO: Special case no-resample case for speed
|
||||
|
||||
|
@ -161,37 +220,44 @@ void SasInstance::Mix(u32 outAddr)
|
|||
int numSamples = lastSample - curSample;
|
||||
|
||||
// Read N samples into the resample buffer. Could do either PCM or VAG here.
|
||||
|
||||
for (int i = 0; i < numSamples; i++) {
|
||||
int sample = voice.vag.GetSample();
|
||||
if (voice.samplePos >= voice.vagSize || voice.vag.End()) {
|
||||
if (voice.loop) {
|
||||
voice.Loop();
|
||||
} else {
|
||||
voice.playing = false;
|
||||
// TODO: clear rest of buffer
|
||||
memset(resampleBuffer, 0, (numSamples - i) * sizeof(resampleBuffer[0]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
resampleBuffer[i + 2] = sample;
|
||||
voice.vag.GetSamples(resampleBuffer + 2, numSamples);
|
||||
if (voice.vag.End()) {
|
||||
// NOTICE_LOG(SAS, "Hit end of VAG audio");
|
||||
voice.playing = false;
|
||||
voice.on = false; // ??
|
||||
}
|
||||
|
||||
// Save resample history
|
||||
voice.resampleHist[0] = resampleBuffer[numSamples - 2];
|
||||
voice.resampleHist[1] = resampleBuffer[numSamples - 1];
|
||||
voice.resampleHist[0] = resampleBuffer[2 + numSamples - 2];
|
||||
voice.resampleHist[1] = resampleBuffer[2 + numSamples - 1];
|
||||
|
||||
// Resample to the correct pitch, writing exactly "grainSize" samples.
|
||||
|
||||
int bufferPos = (voice.samplePos & (PSP_SAS_PITCH_BASE - 1)) + 2 * PSP_SAS_PITCH_BASE;
|
||||
u32 bufferPos = (voice.samplePos & (PSP_SAS_PITCH_BASE - 1)) + 2 * PSP_SAS_PITCH_BASE;
|
||||
for (int i = 0; i < grainSize; i++) {
|
||||
// For now: nearest neighbour, not even using the resample history at all.
|
||||
int sample = resampleBuffer[bufferPos / PSP_SAS_PITCH_BASE];
|
||||
bufferPos += voice.pitch;
|
||||
|
||||
// Reduce envelope to 15 bits, rounding down.
|
||||
int envelopeValue = voice.envelope.GetHeight();
|
||||
envelopeValue = ((envelopeValue >> 15) + 1) >> 1;
|
||||
|
||||
// We just scale by the envelope before we scale by volumes.
|
||||
sample = sample * envelopeValue >> 15;
|
||||
|
||||
// We mix into this 32-bit temp buffer and clip in a second loop
|
||||
// Ideally, the shift right should be there too but for now I'm concerned about
|
||||
// not overflowing.
|
||||
mixBuffer[i * 2] += sample * voice.volumeLeft >> 15;
|
||||
mixBuffer[i * 2 + 1] += sample * voice.volumeRight >> 15;
|
||||
sendBuffer[i * 2] += sample * voice.volumeLeftSend >> 15;
|
||||
sendBuffer[i * 2 + 1] += sample * voice.volumeRightSend >> 15;
|
||||
voice.envelope.Step();
|
||||
}
|
||||
if (voice.envelope.HasEnded())
|
||||
{
|
||||
// NOTICE_LOG(SAS, "Hit end of envelope");
|
||||
voice.playing = false;
|
||||
}
|
||||
}
|
||||
else if (voice.pcmAddr != 0) {
|
||||
|
@ -202,27 +268,217 @@ void SasInstance::Mix(u32 outAddr)
|
|||
}
|
||||
}
|
||||
|
||||
//if (voicesPlayingCount)
|
||||
// NOTICE_LOG(SAS, "Sas mixed %i voices", voicesPlayingCount);
|
||||
// Okay, apply effects processing to the Send buffer alone here.
|
||||
// Reverb, echo, what have you.
|
||||
// TODO
|
||||
|
||||
// Alright, all voices mixed. Let's convert and clip, and at the same time, wipe mixBuffer for next time. Could also dither.
|
||||
for (int i = 0; i < grainSize * 2; i++) {
|
||||
s16 *out = (s16 *)Memory::GetPointer(outAddr);
|
||||
int sample = mixBuffer[i];
|
||||
mixBuffer[i] = 0;
|
||||
if (sample > 32767) out[i] = 32767;
|
||||
else if (sample < -32768) out[i] = -32768;
|
||||
else out[i] = sample;
|
||||
for (int i = 0; i < grainSize; i++) {
|
||||
int sampleL = mixBuffer[i * 2] + sendBuffer[i * 2];
|
||||
int sampleR = mixBuffer[i * 2 + 1] + sendBuffer[i * 2 + 1];
|
||||
mixBuffer[i * 2] = 0;
|
||||
mixBuffer[i * 2 + 1] = 0;
|
||||
s16 outL, outR;
|
||||
if (sampleL > 32767) outL = 32767;
|
||||
else if (sampleL < -32768) outL = -32768;
|
||||
else outL = sampleL;
|
||||
if (sampleR > 32767) outR = 32767;
|
||||
else if (sampleR < -32768) outR = -32768;
|
||||
else outR = sampleR;
|
||||
Memory::WriteUnchecked_U16(outL, outAddr + i * 2 * 2);
|
||||
Memory::WriteUnchecked_U16(outR, outAddr + i * 2 * 2 + 2);
|
||||
}
|
||||
}
|
||||
|
||||
void SasVoice::Reset()
|
||||
{
|
||||
void SasVoice::Reset() {
|
||||
resampleHist[0] = 0;
|
||||
resampleHist[1] = 0;
|
||||
}
|
||||
|
||||
void SasVoice::Loop()
|
||||
{
|
||||
if (vagAddr) {
|
||||
vag.Start(Memory::GetPointer(vagAddr));
|
||||
samplePos = 0;
|
||||
void SasVoice::KeyOn() {
|
||||
on = true;
|
||||
playing = true;
|
||||
paused = false;
|
||||
envelope.KeyOn();
|
||||
switch (type) {
|
||||
case VOICETYPE_VAG:
|
||||
vag.Start(Memory::GetPointer(vagAddr), loop);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SasVoice::KeyOff() {
|
||||
on = false;
|
||||
envelope.KeyOff();
|
||||
}
|
||||
|
||||
void SasVoice::ChangedParams() {
|
||||
if (!playing && on) {
|
||||
playing = true;
|
||||
}
|
||||
// TODO: restart VAG somehow
|
||||
}
|
||||
|
||||
// This is horribly stolen from JPCSP.
|
||||
// Need to find a real solution.
|
||||
static const short expCurve[] = {
|
||||
0x0000, 0x0380, 0x06E4, 0x0A2D, 0x0D5B, 0x1072, 0x136F, 0x1653,
|
||||
0x1921, 0x1BD9, 0x1E7B, 0x2106, 0x237F, 0x25E4, 0x2835, 0x2A73,
|
||||
0x2CA0, 0x2EBB, 0x30C6, 0x32C0, 0x34AB, 0x3686, 0x3852, 0x3A10,
|
||||
0x3BC0, 0x3D63, 0x3EF7, 0x4081, 0x41FC, 0x436E, 0x44D3, 0x462B,
|
||||
0x477B, 0x48BF, 0x49FA, 0x4B2B, 0x4C51, 0x4D70, 0x4E84, 0x4F90,
|
||||
0x5095, 0x5191, 0x5284, 0x5370, 0x5455, 0x5534, 0x5609, 0x56D9,
|
||||
0x57A3, 0x5867, 0x5924, 0x59DB, 0x5A8C, 0x5B39, 0x5BE0, 0x5C81,
|
||||
0x5D1C, 0x5DB5, 0x5E48, 0x5ED5, 0x5F60, 0x5FE5, 0x6066, 0x60E2,
|
||||
0x615D, 0x61D2, 0x6244, 0x62B2, 0x631D, 0x6384, 0x63E8, 0x644A,
|
||||
0x64A8, 0x6503, 0x655B, 0x65B1, 0x6605, 0x6653, 0x66A2, 0x66ED,
|
||||
0x6737, 0x677D, 0x67C1, 0x6804, 0x6844, 0x6882, 0x68BF, 0x68F9,
|
||||
0x6932, 0x6969, 0x699D, 0x69D2, 0x6A03, 0x6A34, 0x6A63, 0x6A8F,
|
||||
0x6ABC, 0x6AE6, 0x6B0E, 0x6B37, 0x6B5D, 0x6B84, 0x6BA7, 0x6BCB,
|
||||
0x6BED, 0x6C0E, 0x6C2D, 0x6C4D, 0x6C6B, 0x6C88, 0x6CA4, 0x6CBF,
|
||||
0x6CD9, 0x6CF3, 0x6D0C, 0x6D24, 0x6D3B, 0x6D52, 0x6D68, 0x6D7D,
|
||||
0x6D91, 0x6DA6, 0x6DB9, 0x6DCA, 0x6DDE, 0x6DEF, 0x6DFF, 0x6E10,
|
||||
0x6E20, 0x6E30, 0x6E3E, 0x6E4C, 0x6E5A, 0x6E68, 0x6E76, 0x6E82,
|
||||
0x6E8E, 0x6E9B, 0x6EA5, 0x6EB1, 0x6EBC, 0x6EC6, 0x6ED1, 0x6EDB,
|
||||
0x6EE4, 0x6EED, 0x6EF6, 0x6EFE, 0x6F07, 0x6F10, 0x6F17, 0x6F20,
|
||||
0x6F27, 0x6F2E, 0x6F35, 0x6F3C, 0x6F43, 0x6F48, 0x6F4F, 0x6F54,
|
||||
0x6F5B, 0x6F60, 0x6F66, 0x6F6B, 0x6F70, 0x6F74, 0x6F79, 0x6F7E,
|
||||
0x6F82, 0x6F87, 0x6F8A, 0x6F90, 0x6F93, 0x6F97, 0x6F9A, 0x6F9E,
|
||||
0x6FA1, 0x6FA5, 0x6FA8, 0x6FAC, 0x6FAD, 0x6FB1, 0x6FB4, 0x6FB6,
|
||||
0x6FBA, 0x6FBB, 0x6FBF, 0x6FC1, 0x6FC4, 0x6FC6, 0x6FC8, 0x6FC9,
|
||||
0x6FCD, 0x6FCF, 0x6FD0, 0x6FD2, 0x6FD4, 0x6FD6, 0x6FD7, 0x6FD9,
|
||||
0x6FDB, 0x6FDD, 0x6FDE, 0x6FDE, 0x6FE0, 0x6FE2, 0x6FE4, 0x6FE5,
|
||||
0x6FE5, 0x6FE7, 0x6FE9, 0x6FE9, 0x6FEB, 0x6FEC, 0x6FEC, 0x6FEE,
|
||||
0x6FEE, 0x6FF0, 0x6FF0, 0x6FF2, 0x6FF2, 0x6FF3, 0x6FF3, 0x6FF5,
|
||||
0x6FF5, 0x6FF7, 0x6FF7, 0x6FF7, 0x6FF9, 0x6FF9, 0x6FF9, 0x6FFA,
|
||||
0x6FFA, 0x6FFA, 0x6FFC, 0x6FFC, 0x6FFC, 0x6FFE, 0x6FFE, 0x6FFE,
|
||||
0x7000
|
||||
};
|
||||
|
||||
static int durationFromRate(int rate)
|
||||
{
|
||||
if (rate == 0) {
|
||||
return PSP_SAS_ENVELOPE_FREQ_MAX;
|
||||
} else {
|
||||
// From experimental tests on a PSP:
|
||||
// rate=0x7FFFFFFF => duration=0x10
|
||||
// rate=0x3FFFFFFF => duration=0x22
|
||||
// rate=0x1FFFFFFF => duration=0x44
|
||||
// rate=0x0FFFFFFF => duration=0x81
|
||||
// rate=0x07FFFFFF => duration=0xF1
|
||||
// rate=0x03FFFFFF => duration=0x1B9
|
||||
//
|
||||
// The correct curve model is still unknown.
|
||||
// We use the following approximation:
|
||||
// duration = 0x7FFFFFFF / rate * 0x10
|
||||
return PSP_SAS_ENVELOPE_FREQ_MAX / rate * 0x10;
|
||||
}
|
||||
}
|
||||
|
||||
const short expCurveReference = 0x7000;
|
||||
|
||||
static int getExpCurveAt(int index, int duration) {
|
||||
const short curveLength = sizeof(expCurve) / sizeof(short);
|
||||
|
||||
float curveIndex = (index * curveLength) / (float) duration;
|
||||
int curveIndex1 = (int) curveIndex;
|
||||
int curveIndex2 = curveIndex1 + 1;
|
||||
float curveIndexFraction = curveIndex - curveIndex1;
|
||||
|
||||
if (curveIndex1 < 0) {
|
||||
return expCurve[0];
|
||||
} else if (curveIndex2 >= curveLength || curveIndex2 < 0) {
|
||||
return expCurve[curveLength - 1];
|
||||
}
|
||||
|
||||
float sample = expCurve[curveIndex1] * (1.f - curveIndexFraction) + expCurve[curveIndex2] * curveIndexFraction;
|
||||
return (short)(sample);
|
||||
}
|
||||
|
||||
void ADSREnvelope::WalkCurve(int rate, int type) {
|
||||
short expFactor;
|
||||
int duration;
|
||||
switch (type) {
|
||||
case PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE:
|
||||
height_ += rate;
|
||||
break;
|
||||
|
||||
case PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE:
|
||||
height_ -= rate;
|
||||
break;
|
||||
|
||||
case PSP_SAS_ADSR_CURVE_MODE_LINEAR_BENT:
|
||||
if (height_ < (s64)PSP_SAS_ENVELOPE_HEIGHT_MAX * 3 / 4) {
|
||||
height_ += rate;
|
||||
} else {
|
||||
height_ += rate / 4;
|
||||
}
|
||||
break;
|
||||
|
||||
case PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE:
|
||||
// NOTICE_LOG(SAS, "UNIMPL EXP DECR");
|
||||
duration = durationFromRate(rate);
|
||||
expFactor = getExpCurveAt(steps_, duration);
|
||||
height_ = (s64)expFactor * PSP_SAS_ENVELOPE_HEIGHT_MAX / expCurveReference;
|
||||
height_ = PSP_SAS_ENVELOPE_HEIGHT_MAX - height_;
|
||||
break;
|
||||
|
||||
case PSP_SAS_ADSR_CURVE_MODE_EXPONENT_INCREASE:
|
||||
duration = durationFromRate(rate);
|
||||
expFactor = getExpCurveAt(steps_, duration);
|
||||
height_ = (s64)expFactor * PSP_SAS_ENVELOPE_HEIGHT_MAX / expCurveReference;
|
||||
break;
|
||||
|
||||
case PSP_SAS_ADSR_CURVE_MODE_DIRECT:
|
||||
height_ = rate; // Simple :)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ADSREnvelope::SetState(ADSRState state) {
|
||||
steps_ = 0;
|
||||
state_ = state;
|
||||
}
|
||||
|
||||
void ADSREnvelope::Step() {
|
||||
switch (state_) {
|
||||
case STATE_ATTACK:
|
||||
WalkCurve(attackRate, attackType);
|
||||
if (height_ > PSP_SAS_ENVELOPE_HEIGHT_MAX || height_ < 0)
|
||||
SetState(STATE_DECAY);
|
||||
break;
|
||||
case STATE_DECAY:
|
||||
WalkCurve(decayRate, decayType);
|
||||
if (height_ > PSP_SAS_ENVELOPE_HEIGHT_MAX || height_ < sustainLevel)
|
||||
SetState(STATE_SUSTAIN);
|
||||
break;
|
||||
case STATE_SUSTAIN:
|
||||
WalkCurve(sustainRate, sustainType);
|
||||
break;
|
||||
case STATE_RELEASE:
|
||||
WalkCurve(releaseRate, releaseType);
|
||||
if (height_ <= 0) {
|
||||
height_ = 0;
|
||||
SetState(STATE_OFF);
|
||||
}
|
||||
break;
|
||||
case STATE_OFF:
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
steps_++;
|
||||
}
|
||||
|
||||
void ADSREnvelope::KeyOn() {
|
||||
SetState(STATE_ATTACK);
|
||||
height_ = 0;
|
||||
}
|
||||
|
||||
void ADSREnvelope::KeyOff() {
|
||||
SetState(STATE_RELEASE);
|
||||
height_ = sustainLevel;
|
||||
}
|
|
@ -44,6 +44,9 @@ enum {
|
|||
PSP_SAS_ADSR_DECAY = 2,
|
||||
PSP_SAS_ADSR_SUSTAIN = 4,
|
||||
PSP_SAS_ADSR_RELEASE = 8,
|
||||
|
||||
PSP_SAS_ENVELOPE_HEIGHT_MAX = 0x40000000,
|
||||
PSP_SAS_ENVELOPE_FREQ_MAX = 0x7FFFFFFF,
|
||||
};
|
||||
|
||||
struct WaveformEffect
|
||||
|
@ -62,6 +65,8 @@ enum VoiceType {
|
|||
VOICETYPE_PCM = 1,
|
||||
VOICETYPE_NOISE = 2,
|
||||
VOICETYPE_ATRAC3 = 3,
|
||||
VOICETYPE_TRIWAVE = 4, // are these used? there are functions for them (sceSetTriangularWave)
|
||||
VOICETYPE_PULSEWAVE = 5,
|
||||
};
|
||||
|
||||
// VAG is a Sony ADPCM audio compression format, which goes all the way back to the PSX.
|
||||
|
@ -71,24 +76,15 @@ enum VoiceType {
|
|||
class VagDecoder
|
||||
{
|
||||
public:
|
||||
void Start(u8 *data);
|
||||
void Start(u8 *data, bool loopEnabled);
|
||||
|
||||
int GetSample()
|
||||
{
|
||||
if (end_)
|
||||
return 0;
|
||||
if (curSample == 28)
|
||||
Decode();
|
||||
if (end_)
|
||||
return 0;
|
||||
return samples[curSample++];
|
||||
}
|
||||
void GetSamples(s16 *outSamples, int numSamples);
|
||||
|
||||
bool Decode();
|
||||
bool DecodeBlock();
|
||||
bool End() const { return end_; }
|
||||
|
||||
u8 GetByte() {
|
||||
return *data_++;
|
||||
return *read_++;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -96,21 +92,38 @@ private:
|
|||
int curSample;
|
||||
|
||||
u8 *data_;
|
||||
u8 *read_;
|
||||
int curBlock_;
|
||||
int loopStartBlock_;
|
||||
|
||||
// rolling state. start at 0, should probably reset to 0 on loops?
|
||||
double s_1;
|
||||
double s_2;
|
||||
|
||||
bool loopEnabled_;
|
||||
bool loopAtNextBlock_;
|
||||
bool end_;
|
||||
};
|
||||
|
||||
|
||||
// Max height: 0x40000000 I think
|
||||
class ADSREnvelope
|
||||
{
|
||||
public:
|
||||
void SetSimpleEnvelope(u32 ADSREnv1, u32 ADSREnv2);
|
||||
|
||||
void WalkCurve(int rate, int type);
|
||||
|
||||
void KeyOn();
|
||||
void KeyOff();
|
||||
|
||||
void Step();
|
||||
|
||||
int GetHeight() const {
|
||||
return height_ > PSP_SAS_ENVELOPE_HEIGHT_MAX ? PSP_SAS_ENVELOPE_HEIGHT_MAX : height_;
|
||||
}
|
||||
bool HasEnded() const {
|
||||
return state_ == STATE_OFF;
|
||||
}
|
||||
int attackRate;
|
||||
int decayRate;
|
||||
int sustainRate;
|
||||
|
@ -120,7 +133,20 @@ public:
|
|||
int sustainType;
|
||||
int sustainLevel;
|
||||
int releaseType;
|
||||
int height;
|
||||
|
||||
private:
|
||||
enum ADSRState {
|
||||
STATE_ATTACK,
|
||||
STATE_DECAY,
|
||||
STATE_SUSTAIN,
|
||||
STATE_RELEASE,
|
||||
STATE_OFF,
|
||||
};
|
||||
void SetState(ADSRState state);
|
||||
|
||||
ADSRState state_;
|
||||
int steps_;
|
||||
s64 height_; // s64 to avoid having to care about overflow when calculatimg. TODO: this should be fine as s32
|
||||
};
|
||||
|
||||
// A SAS voice.
|
||||
|
@ -129,6 +155,9 @@ public:
|
|||
struct SasVoice
|
||||
{
|
||||
bool playing;
|
||||
bool paused; // a voice can be playing AND paused. In that case, it won't play.
|
||||
bool on; // key-on, key-off.
|
||||
|
||||
VoiceType type;
|
||||
|
||||
u32 vagAddr;
|
||||
|
@ -136,9 +165,10 @@ struct SasVoice
|
|||
u32 pcmAddr;
|
||||
int pcmSize;
|
||||
int sampleRate;
|
||||
|
||||
int samplePos;
|
||||
int pitch;
|
||||
int loop;
|
||||
bool loop;
|
||||
|
||||
int noiseFreq;
|
||||
|
||||
|
@ -147,11 +177,13 @@ struct SasVoice
|
|||
int volumeLeftSend; // volume to "Send" (audio-lingo) to the effects processing engine, like reverb
|
||||
int volumeRightSend;
|
||||
|
||||
int setPaused;
|
||||
|
||||
void Loop();
|
||||
void Reset();
|
||||
|
||||
void KeyOn();
|
||||
void KeyOff();
|
||||
|
||||
void ChangedParams();
|
||||
|
||||
s16 resampleHist[2];
|
||||
|
||||
ADSREnvelope envelope;
|
||||
|
@ -162,28 +194,10 @@ struct SasVoice
|
|||
class SasInstance
|
||||
{
|
||||
public:
|
||||
SasInstance() : mixBuffer(0), resampleBuffer(0), grainSize(0) {}
|
||||
~SasInstance() {
|
||||
delete [] mixBuffer;
|
||||
delete [] resampleBuffer;
|
||||
}
|
||||
SasVoice voices[PSP_SAS_VOICES_MAX];
|
||||
WaveformEffect waveformEffect;
|
||||
|
||||
void SetGrainSize(int newGrainSize) {
|
||||
if (mixBuffer)
|
||||
delete [] mixBuffer;
|
||||
mixBuffer = new s32[newGrainSize * 2];
|
||||
memset(mixBuffer, 0, sizeof(int) * newGrainSize * 2);
|
||||
grainSize = newGrainSize;
|
||||
if (resampleBuffer)
|
||||
delete [] resampleBuffer;
|
||||
|
||||
// 2 samples padding at the start, that's where we copy the two last samples from the channel
|
||||
// so that we can do bicubic resampling if necessary.
|
||||
resampleBuffer = new s16[grainSize * 4 + 2];
|
||||
}
|
||||
SasInstance();
|
||||
~SasInstance();
|
||||
|
||||
void SetGrainSize(int newGrainSize);
|
||||
int GetGrainSize() const { return grainSize; }
|
||||
|
||||
int maxVoices;
|
||||
|
@ -191,11 +205,15 @@ public:
|
|||
int outputMode;
|
||||
int length;
|
||||
|
||||
s16 *resampleBuffer;
|
||||
int *mixBuffer;
|
||||
int *sendBuffer;
|
||||
s16 *resampleBuffer;
|
||||
|
||||
void Mix(u32 outAddr);
|
||||
|
||||
SasVoice voices[PSP_SAS_VOICES_MAX];
|
||||
WaveformEffect waveformEffect;
|
||||
|
||||
private:
|
||||
int grainSize;
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue