mirror of
https://github.com/libretro/ppsspp.git
synced 2025-01-18 22:53:08 +00:00
sceSas cleanup
This commit is contained in:
parent
a86ef2c7d8
commit
15b7c1c96c
@ -727,6 +727,8 @@ add_library(${CoreLibName} ${CoreLinkType}
|
||||
Core/HLE/sceVaudio.h
|
||||
Core/HW/MemoryStick.cpp
|
||||
Core/HW/MemoryStick.h
|
||||
Core/HW/SasAudio.cpp
|
||||
Core/HW/SasAudio.h
|
||||
Core/Host.cpp
|
||||
Core/Host.h
|
||||
Core/Loaders.cpp
|
||||
|
@ -63,6 +63,7 @@ set(SRCS
|
||||
HLE/scesupPreAcc.cpp
|
||||
HLE/sceVaudio.cpp
|
||||
HW/MemoryStick.cpp
|
||||
HW/SasAudio.cpp
|
||||
FileSystems/BlockDevices.cpp
|
||||
FileSystems/ISOFileSystem.cpp
|
||||
FileSystems/DirectoryFileSystem.cpp
|
||||
|
@ -175,6 +175,7 @@
|
||||
<ClCompile Include="HLE\__sceAudio.cpp" />
|
||||
<ClCompile Include="Host.cpp" />
|
||||
<ClCompile Include="HW\MemoryStick.cpp" />
|
||||
<ClCompile Include="HW\SasAudio.cpp" />
|
||||
<ClCompile Include="Loaders.cpp" />
|
||||
<ClCompile Include="MemMap.cpp" />
|
||||
<ClCompile Include="MemmapFunctions.cpp" />
|
||||
@ -320,6 +321,7 @@
|
||||
<ClInclude Include="HLE\sceVaudio.h" />
|
||||
<ClInclude Include="HLE\__sceAudio.h" />
|
||||
<ClInclude Include="Host.h" />
|
||||
<ClInclude Include="HW\SasAudio.h" />
|
||||
<ClInclude Include="HW\MemoryStick.h" />
|
||||
<ClInclude Include="Loaders.h" />
|
||||
<ClInclude Include="MemMap.h" />
|
||||
|
@ -342,6 +342,9 @@
|
||||
<ClCompile Include="Dialog\PSPOskDialog.cpp">
|
||||
<Filter>Dialog</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\SasAudio.cpp">
|
||||
<Filter>HW</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ELF\ElfReader.h">
|
||||
@ -629,6 +632,9 @@
|
||||
<ClInclude Include="Dialog\PSPOskDialog.h">
|
||||
<Filter>Dialog</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\SasAudio.h">
|
||||
<Filter>HW</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="CMakeLists.txt" />
|
||||
|
@ -232,7 +232,7 @@ void PSPOskDialog::Update()
|
||||
Memory::Write_U16(0x0030,oskData.outtextPtr+12);
|
||||
oskData.outtextlength = 6;
|
||||
oskParams.base.result= 0;
|
||||
oskData.result = SceUtilityOskResult::PSP_UTILITY_OSK_RESULT_CHANGED;
|
||||
oskData.result = PSP_UTILITY_OSK_RESULT_CHANGED;
|
||||
Memory::WriteStruct(oskParams.SceUtilityOskDataPtr, &oskData);
|
||||
Memory::WriteStruct(oskParamsAddr, &oskParams);
|
||||
}
|
||||
|
@ -164,6 +164,11 @@ template<int func(u32, const char *)> void WrapI_UC() {
|
||||
RETURN(retval);
|
||||
}
|
||||
|
||||
template<u32 func(u32, int , int , int, int, int)> void WrapU_UIIIII() {
|
||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5));
|
||||
RETURN(retval);
|
||||
}
|
||||
|
||||
template<u32 func(u32, int , int , int, int, int, int)> void WrapU_UIIIIII() {
|
||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5), PARAM(6));
|
||||
RETURN(retval);
|
||||
@ -178,6 +183,11 @@ template<void func(u32, int, u32, int, int)> void WrapV_UIUII() {
|
||||
func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
||||
}
|
||||
|
||||
template<u32 func(u32, int, u32, int, int)> void WrapU_UIUII() {
|
||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
||||
RETURN(retval);
|
||||
}
|
||||
|
||||
template<u32 func(u32, int, int)> void WrapU_UII() {
|
||||
u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
|
||||
RETURN(retval);
|
||||
|
@ -22,211 +22,25 @@
|
||||
// http://code.google.com/p/jpcsp/source/browse/trunk/src/jpcsp/HLE/modules150/sceSasCore.java
|
||||
|
||||
#include "base/basictypes.h"
|
||||
|
||||
#include "Log.h"
|
||||
#include "HLE.h"
|
||||
#include "../MIPS/MIPS.h"
|
||||
#include "../HW/SasAudio.h"
|
||||
|
||||
#include "sceSas.h"
|
||||
#include "sceKernel.h"
|
||||
|
||||
static const int PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE=0;
|
||||
static const int PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE=1;
|
||||
static const int PSP_SAS_ADSR_CURVE_MODE_LINEAR_BENT=2;
|
||||
static const int PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE=3;
|
||||
static const int PSP_SAS_ADSR_CURVE_MODE_EXPONENT_INCREASE=4;
|
||||
static const int PSP_SAS_ADSR_CURVE_MODE_DIRECT= 5;
|
||||
|
||||
static const int PSP_SAS_ADSR_ATTACK=1;
|
||||
static const int PSP_SAS_ADSR_DECAY=2;
|
||||
static const int PSP_SAS_ADSR_SUSTAIN=4;
|
||||
static const int PSP_SAS_ADSR_RELEASE=8;
|
||||
|
||||
|
||||
struct WaveformEffect
|
||||
{
|
||||
int type;
|
||||
int delay;
|
||||
int feedback ;
|
||||
int leftVol;
|
||||
int rightVol;
|
||||
int isDryOn;
|
||||
int isWetOn;
|
||||
};
|
||||
|
||||
static const double f[5][2] =
|
||||
{ { 0.0, 0.0 },
|
||||
{ 60.0 / 64.0, 0.0 },
|
||||
{ 115.0 / 64.0, -52.0 / 64.0 },
|
||||
{ 98.0 / 64.0, -55.0 / 64.0 },
|
||||
{ 122.0 / 64.0, -60.0 / 64.0 } };
|
||||
|
||||
// VAG is a Sony ADPCM audio compression format, which goes all the way back to the PSX.
|
||||
// It compresses 28 16-bit samples into a block of 16 bytes.
|
||||
// TODO: Get rid of the doubles, making sure it does not impact sound quality.
|
||||
// Doubles are pretty fast on Android devices these days though.
|
||||
class VagDecoder
|
||||
{
|
||||
public:
|
||||
void Start(u8 *data)
|
||||
{
|
||||
data_ = data;
|
||||
curSample = 28;
|
||||
s_1 = 0.0; // per block?
|
||||
s_2 = 0.0;
|
||||
}
|
||||
|
||||
int GetSample()
|
||||
{
|
||||
if (end_)
|
||||
return 0;
|
||||
if (curSample == 28)
|
||||
Decode();
|
||||
if (end_)
|
||||
return 0;
|
||||
return samples[curSample++];
|
||||
}
|
||||
|
||||
bool Decode();
|
||||
bool End() const { return end_; }
|
||||
|
||||
u8 GetByte() {
|
||||
return *data_++;
|
||||
}
|
||||
|
||||
private:
|
||||
double samples[28];
|
||||
int curSample;
|
||||
|
||||
u8 *data_;
|
||||
|
||||
// rolling state. start at 0, should probably reset to 0 on loops?
|
||||
double s_1;
|
||||
double s_2;
|
||||
|
||||
bool end_;
|
||||
};
|
||||
|
||||
bool VagDecoder::Decode()
|
||||
{
|
||||
int predict_nr = GetByte();
|
||||
int shift_factor = predict_nr & 0xf;
|
||||
predict_nr >>= 4;
|
||||
int flags = GetByte();
|
||||
if (flags == 7)
|
||||
{
|
||||
end_ = true;
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < 28; i += 2)
|
||||
{
|
||||
int d = GetByte();
|
||||
int s = (d & 0xf) << 12;
|
||||
if (s & 0x8000)
|
||||
s |= 0xffff0000;
|
||||
samples[i] = (double)(s >> shift_factor);
|
||||
s = (d & 0xf0) << 8;
|
||||
if (s & 0x8000)
|
||||
s |= 0xffff0000;
|
||||
samples[i + 1] = (double)(s >> shift_factor);
|
||||
}
|
||||
for (int i = 0; i < 28; i++)
|
||||
{
|
||||
samples[i] = samples[i] + s_1 * f[predict_nr][0] + s_2 * f[predict_nr][1];
|
||||
s_2 = s_1;
|
||||
s_1 = samples[i];
|
||||
}
|
||||
curSample = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// A SAS voice.
|
||||
// TODO: Look into pre-decoding the VAG samples on SetVoice instead of decoding them on the fly.
|
||||
// It's not very likely that games encode VAG dynamically.
|
||||
struct Voice
|
||||
{
|
||||
u32 vagAddr;
|
||||
u32 pcmAddr;
|
||||
int samplePos;
|
||||
int size;
|
||||
int loop;
|
||||
int freq; //units?
|
||||
int volumeLeft;
|
||||
int volumeRight;
|
||||
int volumeLeftSend; // volume to "Send" (audio-lingo) to the effects processing engine, like reverb
|
||||
int volumeRightSend;
|
||||
int attackRate;
|
||||
int decayRate;
|
||||
int sustainRate;
|
||||
int releaseRate;
|
||||
int attackType;
|
||||
int decayType;
|
||||
int sustainType;
|
||||
int sustainLevel;
|
||||
int releaseType;
|
||||
int pitch;
|
||||
int setPaused;
|
||||
int height;
|
||||
bool playing;
|
||||
|
||||
VagDecoder vag;
|
||||
};
|
||||
|
||||
class SasInstance
|
||||
{
|
||||
public:
|
||||
enum { NUM_VOICES = 32 };
|
||||
Voice voices[NUM_VOICES];
|
||||
WaveformEffect waveformEffect;
|
||||
int grainSize;
|
||||
int maxVoices;
|
||||
int sampleRate;
|
||||
int outputMode;
|
||||
int length;
|
||||
|
||||
void mix(u32 outAddr);
|
||||
};
|
||||
|
||||
// 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.
|
||||
SasInstance sas;
|
||||
|
||||
// TODO: Make deterministic, by adding staging buffers that we pump out on a fixed CoreTiming-scheduled interval.
|
||||
|
||||
void SasInstance::mix(u32 outAddr)
|
||||
{
|
||||
s16 *out = (s16 *)Memory::GetPointer(outAddr);
|
||||
// Don't need to memset, done by the caller.
|
||||
for (int v = 0; v < NUM_VOICES; v++) // sas.maxVoices?
|
||||
{
|
||||
Voice &voice = sas.voices[v];
|
||||
|
||||
if (voice.playing && voice.vagAddr != 0)
|
||||
{
|
||||
for (int i = 0; i < sas.grainSize; i++)
|
||||
{
|
||||
int sample = voice.vag.GetSample();
|
||||
voice.samplePos++;
|
||||
if (voice.samplePos >= voice.size || voice.vag.End())
|
||||
{
|
||||
voice.playing = false;
|
||||
break;
|
||||
}
|
||||
int l = sample;
|
||||
int r = sample; //* (voice.volumeLeft >> 16), r = sample * (voice.volumeRight >> 16);
|
||||
|
||||
// TODO: should mix into a temporary 32-bit buffer and then clip down
|
||||
out[i * 2] += l;
|
||||
out[i * 2 + 1] += r;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 sceSasInit(u32 core, u32 grainSize, u32 maxVoices, u32 outputMode, u32 sampleRate)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasInit()");
|
||||
DEBUG_LOG(HLE,"0=sceSasInit(%08x, %i, maxVoices=%i, %i, %i)", core, grainSize, maxVoices, outputMode, sampleRate);
|
||||
memset(&sas, 0, sizeof(sas));
|
||||
sas.grainSize = grainSize;
|
||||
sas.SetGrainSize(grainSize);
|
||||
sas.maxVoices = maxVoices;
|
||||
sas.sampleRate = sampleRate;
|
||||
sas.outputMode = outputMode;
|
||||
@ -248,41 +62,41 @@ u32 sceSasGetEndFlag(u32 core)
|
||||
}
|
||||
|
||||
// Runs the mixer
|
||||
void _sceSasCore(u32 core, u32 outAddr)
|
||||
u32 _sceSasCore(u32 core, u32 outAddr)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasCore(, %08x) (grain: %i samples)", outAddr, sas.grainSize);
|
||||
Memory::Memset(outAddr, 0, sas.grainSize * 2 * 2);
|
||||
sas.mix(outAddr);
|
||||
RETURN(0);
|
||||
DEBUG_LOG(HLE,"0=sceSasCore(, %08x) (grain: %i samples)", outAddr, sas.GetGrainSize());
|
||||
Memory::Memset(outAddr, 0, sas.GetGrainSize() * 2 * 2);
|
||||
sas.Mix(outAddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Another way of running the mixer, what was the difference again?
|
||||
void _sceSasCoreWithMix(u32 core, u32 outAddr)
|
||||
u32 _sceSasCoreWithMix(u32 core, u32 outAddr)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasCoreWithMix(, %08x)", outAddr);
|
||||
sas.mix(outAddr);
|
||||
RETURN(0);
|
||||
DEBUG_LOG(HLE,"0=sceSasCoreWithMix(, %08x) (grain: %i samples)", outAddr, sas.GetGrainSize());
|
||||
sas.Mix(outAddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sceSasSetVoice(u32 core, int voiceNum, u32 vagAddr, int size, int loop)
|
||||
u32 sceSasSetVoice(u32 core, int voiceNum, u32 vagAddr, int size, int loop)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasSetVoice(core=%08x, voicenum=%i, vag=%08x, size=%i, loop=%i)",
|
||||
core, voiceNum, vagAddr, size, loop);
|
||||
|
||||
if (voiceNum > SasInstance::NUM_VOICES || voiceNum < 0)
|
||||
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)
|
||||
{
|
||||
WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
|
||||
RETURN(0);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Real VAG header is 0x30 bytes behind the vagAddr
|
||||
Voice &v = sas.voices[voiceNum];
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
v.type = VOICETYPE_VAG;
|
||||
v.vagAddr = vagAddr;
|
||||
v.size = size;
|
||||
v.vagSize = size;
|
||||
v.loop = loop;
|
||||
v.playing = false;
|
||||
RETURN(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 sceSasSetPause(u32 core, int voicebit, int pause)
|
||||
@ -290,10 +104,10 @@ u32 sceSasSetPause(u32 core, int voicebit, int pause)
|
||||
DEBUG_LOG(HLE,"0=sceSasSetPause(core=%08x, voicebit=%08x, pause=%i)", core, voicebit, pause);
|
||||
for (int i = 0; voicebit != 0; i++, voicebit >>= 1)
|
||||
{
|
||||
if (i < SasInstance::NUM_VOICES && i >= 0)
|
||||
if (i < PSP_SAS_VOICES_MAX && i >= 0)
|
||||
{
|
||||
if ((voicebit & 1) != 0)
|
||||
sas.voices[i].setPaused=pause;
|
||||
sas.voices[i].setPaused = pause;
|
||||
}
|
||||
// TODO: Correct error code? Mimana crashes otherwise.
|
||||
else
|
||||
@ -303,86 +117,83 @@ u32 sceSasSetPause(u32 core, int voicebit, int pause)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sceSasSetVolume(u32 core, int voiceNum, int l, int r, int el, int er)
|
||||
u32 sceSasSetVolume(u32 core, int voiceNum, int l, int r, int el, int er)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasSetVolume(core=%08x, voiceNum=%i, l=%i, r=%i, el=%i, er=%i", core, voiceNum, l, r, el, er);
|
||||
|
||||
if (voiceNum > SasInstance::NUM_VOICES || voiceNum < 0)
|
||||
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)
|
||||
{
|
||||
WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
|
||||
RETURN(0);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Voice &v = sas.voices[voiceNum];
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
v.volumeLeft = l;
|
||||
v.volumeRight = r;
|
||||
RETURN(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sceSasSetPitch(u32 core, int voiceNum, int pitch)
|
||||
u32 sceSasSetPitch(u32 core, int voiceNum, int pitch)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasSetPitch(core=%08x, voiceNum=%i, pitch=%i)", core, voiceNum, pitch);
|
||||
|
||||
if (voiceNum > SasInstance::NUM_VOICES || voiceNum < 0)
|
||||
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)
|
||||
{
|
||||
WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
|
||||
RETURN(0);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Voice &v = sas.voices[voiceNum];
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
v.pitch = pitch;
|
||||
RETURN(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sceSasSetKeyOn(u32 core, int voiceNum)
|
||||
u32 sceSasSetKeyOn(u32 core, int voiceNum)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasSetKeyOn(core=%08x, voiceNum=%i)", core, voiceNum);
|
||||
|
||||
if (voiceNum > SasInstance::NUM_VOICES || voiceNum < 0)
|
||||
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)
|
||||
{
|
||||
WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
|
||||
RETURN(0);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Voice &v = sas.voices[voiceNum];
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
v.vag.Start(Memory::GetPointer(v.vagAddr));
|
||||
v.playing = true;
|
||||
RETURN(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: We really need ADSR work:
|
||||
// sceSasSetKeyOff can be used to start sounds, that just sound during the Release phase!
|
||||
void sceSasSetKeyOff(u32 core, int voiceNum)
|
||||
u32 sceSasSetKeyOff(u32 core, int voiceNum)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasSetKeyOff(core=%08x, voiceNum=%i)", core, voiceNum);
|
||||
|
||||
if (voiceNum > SasInstance::NUM_VOICES || voiceNum < 0)
|
||||
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)
|
||||
{
|
||||
WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
|
||||
RETURN(0);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Voice &v = sas.voices[voiceNum];
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
v.playing = false; // not right! Should directly enter Release envelope stage instead!
|
||||
RETURN(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 sceSasSetNoise(u32 core, int voiceNum, int freq)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasSetNoise(core=%08x, voiceNum=%i, freq=%i)", core, voiceNum, freq);
|
||||
|
||||
if (voiceNum > SasInstance::NUM_VOICES || voiceNum < 0)
|
||||
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)
|
||||
{
|
||||
WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Voice &v = sas.voices[voiceNum];
|
||||
v.freq = freq;
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
v.type = VOICETYPE_NOISE;
|
||||
v.noiseFreq = freq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -390,14 +201,14 @@ u32 sceSasSetSL(u32 core, int voiceNum, int level)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasSetSL(core=%08x, voiceNum=%i, level=%i)", core, voiceNum, level);
|
||||
|
||||
if (voiceNum > SasInstance::NUM_VOICES || voiceNum < 0)
|
||||
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)
|
||||
{
|
||||
WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Voice &v = sas.voices[voiceNum];
|
||||
v.sustainLevel = level;
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
v.envelope.sustainLevel = level;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -405,17 +216,17 @@ u32 sceSasSetADSR(u32 core, int voiceNum,int flag ,int a, int d, int s, int r)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasSetADSR(core=%08x, voicenum=%i, flag=%i, a=%08x, d=%08x, s=%08x, r=%08x)",core, voiceNum, flag, a,d,s,r)
|
||||
|
||||
if (voiceNum > SasInstance::NUM_VOICES || voiceNum < 0)
|
||||
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)
|
||||
{
|
||||
WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Voice &v = sas.voices[voiceNum];
|
||||
if ((flag & 0x1) != 0) v.attackRate = a;
|
||||
if ((flag & 0x2) != 0) v.decayRate = d;
|
||||
if ((flag & 0x4) != 0) v.sustainRate = s;
|
||||
if ((flag & 0x8) != 0) v.releaseRate = r;
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
if ((flag & 0x1) != 0) v.envelope.attackRate = a;
|
||||
if ((flag & 0x2) != 0) v.envelope.decayRate = d;
|
||||
if ((flag & 0x4) != 0) v.envelope.sustainRate = s;
|
||||
if ((flag & 0x8) != 0) v.envelope.releaseRate = r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -423,95 +234,26 @@ u32 sceSasSetADSRMode(u32 core, int voiceNum,int flag ,int a, int d, int s, int
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasSetADSRMode(core=%08x, voicenum=%i, flag=%i, a=%08x, d=%08x, s=%08x, r=%08x)",core, voiceNum, flag, a,d,s,r)
|
||||
|
||||
if (voiceNum > SasInstance::NUM_VOICES || voiceNum < 0)
|
||||
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0)
|
||||
{
|
||||
WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Voice &v = sas.voices[voiceNum];
|
||||
if ((flag & 0x1) != 0) v.attackType = a;
|
||||
if ((flag & 0x2) != 0) v.decayType = d;
|
||||
if ((flag & 0x4) != 0) v.sustainType = s;
|
||||
if ((flag & 0x8) != 0) v.releaseType = r;
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
if ((flag & 0x1) != 0) v.envelope.attackType = a;
|
||||
if ((flag & 0x2) != 0) v.envelope.decayType = d;
|
||||
if ((flag & 0x4) != 0) v.envelope.sustainType = s;
|
||||
if ((flag & 0x8) != 0) v.envelope.releaseType = r;
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
// http://code.google.com/p/jpcsp/source/browse/trunk/src/jpcsp/HLE/modules150/sceSasCore.java
|
||||
|
||||
int simpleRate(int n) {
|
||||
n &= 0x7F;
|
||||
if (n == 0x7F) {
|
||||
return 0;
|
||||
}
|
||||
int rate = ((7 - (n & 0x3)) << 26) >> (n >> 2);
|
||||
if (rate == 0) {
|
||||
return 1;
|
||||
}
|
||||
return rate;
|
||||
}
|
||||
|
||||
int attackRate(int bitfield1) {
|
||||
return simpleRate(bitfield1 >> 8);
|
||||
}
|
||||
|
||||
int attackType(int bitfield1) {
|
||||
return (bitfield1 & 0x8000) == 0 ? PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE : PSP_SAS_ADSR_CURVE_MODE_LINEAR_BENT;
|
||||
}
|
||||
|
||||
int decayRate(int bitfield1) {
|
||||
return 0x80000000 >> ((bitfield1 >> 4) & 0x000F);
|
||||
}
|
||||
|
||||
int sustainRate(int bitfield2) {
|
||||
return simpleRate(bitfield2 >> 6);
|
||||
}
|
||||
|
||||
int sustainType(int bitfield2) {
|
||||
switch (bitfield2 >> 13) {
|
||||
case 0: return PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE;
|
||||
case 2: return PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE;
|
||||
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");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int releaseType(int bitfield2) {
|
||||
return (bitfield2 & 0x0020) == 0 ? PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE : PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE;
|
||||
}
|
||||
|
||||
int releaseRate(int bitfield2) {
|
||||
int n = bitfield2 & 0x001F;
|
||||
if (n == 31) {
|
||||
return 0;
|
||||
}
|
||||
if (releaseType(bitfield2) == PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE) {
|
||||
return (0x40000000 >> (n + 2));
|
||||
}
|
||||
return (0x8000000 >> n);
|
||||
}
|
||||
|
||||
int sustainLevel(int bitfield1) {
|
||||
return ((bitfield1 & 0x000F) + 1) << 26;
|
||||
}
|
||||
|
||||
u32 sceSasSetSimpleADSR(u32 core, u32 voiceNum, u32 ADSREnv1, u32 ADSREnv2)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sasSetSimpleADSR(%08x, %i, %08x, %08x)", core, voiceNum, ADSREnv1, ADSREnv2);
|
||||
ADSREnv1 &= 0xFFFF;
|
||||
ADSREnv2 &= 0xFFFF;
|
||||
Voice &v = sas.voices[voiceNum];
|
||||
v.attackRate = attackRate(ADSREnv1);
|
||||
v.attackType = attackType(ADSREnv1);
|
||||
v.decayRate = decayRate(ADSREnv1);
|
||||
v.decayType = PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE;
|
||||
v.sustainRate = sustainRate(ADSREnv2);
|
||||
v.sustainType = sustainType(ADSREnv2);
|
||||
v.releaseRate = releaseRate(ADSREnv2);
|
||||
v.releaseType = releaseType(ADSREnv2);
|
||||
v.sustainLevel = sustainLevel(ADSREnv1);
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
v.envelope.SetSimpleEnvelope(ADSREnv1 & 0xFFFF, ADSREnv2 & 0xFFFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -522,26 +264,26 @@ u32 sceSasGetEnvelopeHeight(u32 core, u32 voiceNum)
|
||||
{
|
||||
DEBUG_LOG(HLE,"UNIMPL 0=sceSasGetEnvelopeHeight(core=%08x, voicenum=%i)", core, voiceNum);
|
||||
}
|
||||
Voice &v = sas.voices[voiceNum];
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
|
||||
return v.playing ? 0x3fffffff : 0;
|
||||
}
|
||||
|
||||
void sceSasRevType(u32 core, int type)
|
||||
u32 sceSasRevType(u32 core, int type)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasRevType(core=%08x, type=%i)", core, type);
|
||||
|
||||
sas.waveformEffect.type=type;
|
||||
RETURN(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sceSasRevParam(u32 core, int delay, int feedback)
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 sceSasGetPauseFlag(u32 core)
|
||||
@ -555,35 +297,34 @@ u32 sceSasGetPauseFlag(u32 core)
|
||||
return PauseFlag;
|
||||
}
|
||||
|
||||
void sceSasRevEVOL(u32 core, int lv, int rv)
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sceSasRevVON(u32 core, int dry, int wet)
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 sceSasGetGrain(u32 core)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasGetGrain(core=%08x)", core);
|
||||
return sas.grainSize;
|
||||
|
||||
return sas.GetGrainSize();
|
||||
}
|
||||
|
||||
u32 sceSasSetGrain(u32 core, int grain)
|
||||
{
|
||||
DEBUG_LOG(HLE,"0=sceSasSetGrain(core=%08x, grain=%i)", core, grain);
|
||||
sas.grainSize = grain;
|
||||
sas.SetGrainSize(grain);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -600,15 +341,15 @@ u32 sceSasSetOutputMode(u32 core, u32 outputMode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sceSasSetVoicePCM(u32 core, int voiceNum, u32 pcmAddr, int size, int loop)
|
||||
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);
|
||||
Voice &v = sas.voices[voiceNum];
|
||||
SasVoice &v = sas.voices[voiceNum];
|
||||
v.pcmAddr = pcmAddr;
|
||||
v.size = size;
|
||||
v.pcmSize = size;
|
||||
v.loop = loop;
|
||||
v.playing = true;
|
||||
RETURN(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 sceSasGetAllEnvelopeHeights(u32 core, u32 heightsAddr)
|
||||
@ -616,7 +357,7 @@ 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].height;
|
||||
int voiceHeight = sas.voices[i].envelope.height;
|
||||
Memory::Write_U32(voiceHeight, heightsAddr + i * 4);
|
||||
}
|
||||
}
|
||||
@ -626,24 +367,24 @@ u32 sceSasGetAllEnvelopeHeights(u32 core, u32 heightsAddr)
|
||||
const HLEFunction sceSasCore[] =
|
||||
{
|
||||
{0x42778a9f, WrapU_UUUUU<sceSasInit>, "__sceSasInit"}, // (SceUID * sasCore, int grain, int maxVoices, int outputMode, int sampleRate)
|
||||
{0xa3589d81, WrapV_UU<_sceSasCore>, "__sceSasCore"},
|
||||
{0x50a14dfc, WrapV_UU<_sceSasCoreWithMix>, "__sceSasCoreWithMix"}, // Process and mix into buffer (int sasCore, int sasInOut, int leftVolume, int rightVolume)
|
||||
{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
|
||||
{0x440ca7d8, WrapV_UIIIII<sceSasSetVolume>, "__sceSasSetVolume"},
|
||||
{0xad84d37f, WrapV_UII<sceSasSetPitch>, "__sceSasSetPitch"},
|
||||
{0x99944089, WrapV_UIUII<sceSasSetVoice>, "__sceSasSetVoice"}, // (int sasCore, int voice, int vagAddr, int size, int loopmode)
|
||||
{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)
|
||||
{0xb7660a23, WrapU_UII<sceSasSetNoise>, "__sceSasSetNoise"},
|
||||
{0x019b25eb, WrapU_UIIIIII<sceSasSetADSR>, "__sceSasSetADSR"},
|
||||
{0x9ec3676a, WrapU_UIIIIII<sceSasSetADSRMode>, "__sceSasSetADSRmode"},
|
||||
{0x5f9529f6, WrapU_UII<sceSasSetSL>, "__sceSasSetSL"},
|
||||
{0x74ae582a, WrapU_UU<sceSasGetEnvelopeHeight>, "__sceSasGetEnvelopeHeight"},
|
||||
{0xcbcd4f79, WrapU_UUUU<sceSasSetSimpleADSR>, "__sceSasSetSimpleADSR"},
|
||||
{0xa0cf2fa4, WrapV_UI<sceSasSetKeyOff>, "__sceSasSetKeyOff"},
|
||||
{0x76f01aca, WrapV_UI<sceSasSetKeyOn>, "__sceSasSetKeyOn"}, // (int sasCore, int voice)
|
||||
{0xf983b186, WrapV_UII<sceSasRevVON>, "__sceSasRevVON"}, // int sasCore, int dry, int wet
|
||||
{0xd5a229c9, WrapV_UII<sceSasRevEVOL>, "__sceSasRevEVOL"}, // (int sasCore, int leftVol, int rightVol) // effect volume
|
||||
{0x33d4ab37, WrapV_UI<sceSasRevType>, "__sceSasRevType"}, // (int sasCore, int type)
|
||||
{0x267a6dd2, WrapV_UII<sceSasRevParam>, "__sceSasRevParam"}, // (int sasCore, int delay, int feedback)
|
||||
{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
|
||||
{0x787d04d5, WrapU_UII<sceSasSetPause>, "__sceSasSetPause"},
|
||||
{0xa232cbe6, 0, "__sceSasSetTriangularWave"}, // (int sasCore, int voice, int unknown)
|
||||
@ -653,7 +394,7 @@ const HLEFunction sceSasCore[] =
|
||||
{0xe175ef66, WrapU_U<sceSasGetOutputMode>, "__sceSasGetOutputmode"},
|
||||
{0xe855bf76, WrapU_UU<sceSasSetOutputMode>, "__sceSasSetOutputmode"},
|
||||
{0x07f58c24, WrapU_UU<sceSasGetAllEnvelopeHeights>, "__sceSasGetAllEnvelopeHeights"}, // (int sasCore, int heightAddr) 32-bit heights, 0-0x40000000
|
||||
{0xE1CD9561, WrapV_UIUII<sceSasSetVoicePCM>, "__sceSasSetVoicePCM"},
|
||||
{0xE1CD9561, WrapU_UIUII<sceSasSetVoicePCM>, "__sceSasSetVoicePCM"},
|
||||
};
|
||||
|
||||
void Register_sceSasCore()
|
||||
|
188
Core/HW/SasAudio.cpp
Normal file
188
Core/HW/SasAudio.cpp
Normal file
@ -0,0 +1,188 @@
|
||||
// Copyright (c) 2012- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "../Globals.h"
|
||||
#include "../MemMap.h"
|
||||
#include "SasAudio.h"
|
||||
|
||||
static const double f[5][2] =
|
||||
{ { 0.0, 0.0 },
|
||||
{ 60.0 / 64.0, 0.0 },
|
||||
{ 115.0 / 64.0, -52.0 / 64.0 },
|
||||
{ 98.0 / 64.0, -55.0 / 64.0 },
|
||||
{ 122.0 / 64.0, -60.0 / 64.0 } };
|
||||
|
||||
void VagDecoder::Start(u8 *data)
|
||||
{
|
||||
data_ = data;
|
||||
curSample = 28;
|
||||
s_1 = 0.0; // per block?
|
||||
s_2 = 0.0;
|
||||
}
|
||||
|
||||
bool VagDecoder::Decode()
|
||||
{
|
||||
int predict_nr = GetByte();
|
||||
int shift_factor = predict_nr & 0xf;
|
||||
predict_nr >>= 4;
|
||||
int flags = GetByte();
|
||||
if (flags == 7)
|
||||
{
|
||||
end_ = true;
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < 28; i += 2)
|
||||
{
|
||||
int d = GetByte();
|
||||
int s = (d & 0xf) << 12;
|
||||
if (s & 0x8000)
|
||||
s |= 0xffff0000;
|
||||
samples[i] = (double)(s >> shift_factor);
|
||||
s = (d & 0xf0) << 8;
|
||||
if (s & 0x8000)
|
||||
s |= 0xffff0000;
|
||||
samples[i + 1] = (double)(s >> shift_factor);
|
||||
}
|
||||
for (int i = 0; i < 28; i++)
|
||||
{
|
||||
samples[i] = samples[i] + s_1 * f[predict_nr][0] + s_2 * f[predict_nr][1];
|
||||
s_2 = s_1;
|
||||
s_1 = samples[i];
|
||||
}
|
||||
curSample = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// http://code.google.com/p/jpcsp/source/browse/trunk/src/jpcsp/HLE/modules150/sceSasCore.java
|
||||
|
||||
int simpleRate(int n) {
|
||||
n &= 0x7F;
|
||||
if (n == 0x7F) {
|
||||
return 0;
|
||||
}
|
||||
int rate = ((7 - (n & 0x3)) << 26) >> (n >> 2);
|
||||
if (rate == 0) {
|
||||
return 1;
|
||||
}
|
||||
return rate;
|
||||
}
|
||||
|
||||
static int getAttackRate(int bitfield1) {
|
||||
return simpleRate(bitfield1 >> 8);
|
||||
}
|
||||
|
||||
static int getAttackType(int bitfield1) {
|
||||
return (bitfield1 & 0x8000) == 0 ? PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE : PSP_SAS_ADSR_CURVE_MODE_LINEAR_BENT;
|
||||
}
|
||||
|
||||
static int getDecayRate(int bitfield1) {
|
||||
return 0x80000000 >> ((bitfield1 >> 4) & 0x000F);
|
||||
}
|
||||
|
||||
static int getSustainRate(int bitfield2) {
|
||||
return simpleRate(bitfield2 >> 6);
|
||||
}
|
||||
|
||||
static int getSustainType(int bitfield2) {
|
||||
switch (bitfield2 >> 13) {
|
||||
case 0: return PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE;
|
||||
case 2: return PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE;
|
||||
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");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getReleaseType(int bitfield2) {
|
||||
return (bitfield2 & 0x0020) == 0 ? PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE : PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE;
|
||||
}
|
||||
|
||||
static int getReleaseRate(int bitfield2) {
|
||||
int n = bitfield2 & 0x001F;
|
||||
if (n == 31) {
|
||||
return 0;
|
||||
}
|
||||
if (getReleaseType(bitfield2) == PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE) {
|
||||
return (0x40000000 >> (n + 2));
|
||||
}
|
||||
return (0x8000000 >> n);
|
||||
}
|
||||
|
||||
static int getSustainLevel(int bitfield1) {
|
||||
return ((bitfield1 & 0x000F) + 1) << 26;
|
||||
}
|
||||
|
||||
void ADSREnvelope::SetSimpleEnvelope(u32 ADSREnv1, u32 ADSREnv2)
|
||||
{
|
||||
attackRate = getAttackRate(ADSREnv1);
|
||||
attackType = getAttackType(ADSREnv1);
|
||||
decayRate = getDecayRate(ADSREnv1);
|
||||
decayType = PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE;
|
||||
sustainRate = getSustainRate(ADSREnv2);
|
||||
sustainType = getSustainType(ADSREnv2);
|
||||
releaseRate = getReleaseRate(ADSREnv2);
|
||||
releaseType = getReleaseType(ADSREnv2);
|
||||
sustainLevel = getSustainLevel(ADSREnv1);
|
||||
}
|
||||
|
||||
void SasInstance::Mix(u32 outAddr)
|
||||
{
|
||||
for (int v = 0; v < PSP_SAS_VOICES_MAX; v++) {
|
||||
SasVoice &voice = voices[v];
|
||||
if (!voice.playing)
|
||||
continue;
|
||||
|
||||
if (voice.vagAddr != 0) {
|
||||
for (int i = 0; i < grainSize; i++) {
|
||||
int sample = voice.vag.GetSample();
|
||||
voice.samplePos++;
|
||||
if (voice.samplePos >= voice.vagSize || voice.vag.End()) {
|
||||
if (voice.loop) {
|
||||
voice.vag.Loop();
|
||||
} else {
|
||||
voice.playing = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
int l = sample * voice.volumeLeft >> 15;
|
||||
int r = sample * voice.volumeRight >> 15;
|
||||
|
||||
// We mix into this 32-bit temp buffer and clip in a second loop
|
||||
mixBuffer[i * 2] += l;
|
||||
mixBuffer[i * 2 + 1] += r;
|
||||
}
|
||||
}
|
||||
else if (voice.pcmAddr != 0) {
|
||||
// PCM mixing should be easy, can probably share code with VAG if we predecode those
|
||||
}
|
||||
else if (voice.noiseFreq != 0) {
|
||||
// Generate noise?
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
192
Core/HW/SasAudio.h
Normal file
192
Core/HW/SasAudio.h
Normal file
@ -0,0 +1,192 @@
|
||||
// Copyright (c) 2012- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
|
||||
|
||||
// This is not really hardware, it's a software audio mixer running on the Media Engine.
|
||||
// From the perspective of a PSP app though, it might as well be.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Globals.h"
|
||||
|
||||
enum {
|
||||
PSP_SAS_VOICES_MAX = 32,
|
||||
|
||||
PSP_SAS_PITCH_MIN = 1,
|
||||
PSP_SAS_PITCH_BASE = 0x1000,
|
||||
PSP_SAS_PITCH_MAX = 0x4000,
|
||||
|
||||
PSP_SAS_VOL_MAX = 0x1000,
|
||||
|
||||
PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE = 0,
|
||||
PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE = 1,
|
||||
PSP_SAS_ADSR_CURVE_MODE_LINEAR_BENT = 2,
|
||||
PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE = 3,
|
||||
PSP_SAS_ADSR_CURVE_MODE_EXPONENT_INCREASE = 4,
|
||||
PSP_SAS_ADSR_CURVE_MODE_DIRECT = 5,
|
||||
|
||||
PSP_SAS_ADSR_ATTACK = 1,
|
||||
PSP_SAS_ADSR_DECAY = 2,
|
||||
PSP_SAS_ADSR_SUSTAIN = 4,
|
||||
PSP_SAS_ADSR_RELEASE = 8,
|
||||
};
|
||||
|
||||
struct WaveformEffect
|
||||
{
|
||||
int type;
|
||||
int delay;
|
||||
int feedback;
|
||||
int leftVol;
|
||||
int rightVol;
|
||||
int isDryOn;
|
||||
int isWetOn;
|
||||
};
|
||||
|
||||
enum VoiceType {
|
||||
VOICETYPE_VAG = 0, // default
|
||||
VOICETYPE_PCM = 1,
|
||||
VOICETYPE_NOISE = 2,
|
||||
VOICETYPE_ATRAC3 = 3,
|
||||
};
|
||||
|
||||
// VAG is a Sony ADPCM audio compression format, which goes all the way back to the PSX.
|
||||
// It compresses 28 16-bit samples into a block of 16 bytes.
|
||||
// TODO: Get rid of the doubles, making sure it does not impact sound quality.
|
||||
// Doubles are pretty fast on Android devices these days though.
|
||||
class VagDecoder
|
||||
{
|
||||
public:
|
||||
void Start(u8 *data);
|
||||
|
||||
int GetSample()
|
||||
{
|
||||
if (end_)
|
||||
return 0;
|
||||
if (curSample == 28)
|
||||
Decode();
|
||||
if (end_)
|
||||
return 0;
|
||||
return samples[curSample++];
|
||||
}
|
||||
|
||||
bool Decode();
|
||||
bool End() const { return end_; }
|
||||
|
||||
u8 GetByte() {
|
||||
return *data_++;
|
||||
}
|
||||
|
||||
void Loop() {
|
||||
curSample = 0;
|
||||
s_1 = 0;
|
||||
s_2 = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
double samples[28];
|
||||
int curSample;
|
||||
|
||||
u8 *data_;
|
||||
|
||||
// rolling state. start at 0, should probably reset to 0 on loops?
|
||||
double s_1;
|
||||
double s_2;
|
||||
|
||||
bool end_;
|
||||
};
|
||||
|
||||
|
||||
// Max height: 0x40000000 I think
|
||||
class ADSREnvelope
|
||||
{
|
||||
public:
|
||||
void SetSimpleEnvelope(u32 ADSREnv1, u32 ADSREnv2);
|
||||
|
||||
int attackRate;
|
||||
int decayRate;
|
||||
int sustainRate;
|
||||
int releaseRate;
|
||||
int attackType;
|
||||
int decayType;
|
||||
int sustainType;
|
||||
int sustainLevel;
|
||||
int releaseType;
|
||||
int height;
|
||||
};
|
||||
|
||||
// A SAS voice.
|
||||
// TODO: Look into pre-decoding the VAG samples on SetVoice instead of decoding them on the fly.
|
||||
// It's not very likely that games encode VAG dynamically.
|
||||
struct SasVoice
|
||||
{
|
||||
bool playing;
|
||||
VoiceType type;
|
||||
|
||||
u32 vagAddr;
|
||||
int vagSize;
|
||||
u32 pcmAddr;
|
||||
int pcmSize;
|
||||
int sampleRate;
|
||||
int samplePos;
|
||||
int pitch;
|
||||
int loop;
|
||||
|
||||
int noiseFreq;
|
||||
|
||||
int volumeLeft;
|
||||
int volumeRight;
|
||||
int volumeLeftSend; // volume to "Send" (audio-lingo) to the effects processing engine, like reverb
|
||||
int volumeRightSend;
|
||||
|
||||
int setPaused;
|
||||
|
||||
ADSREnvelope envelope;
|
||||
|
||||
VagDecoder vag;
|
||||
};
|
||||
|
||||
class SasInstance
|
||||
{
|
||||
public:
|
||||
SasInstance() : mixBuffer(0), grainSize(256) {}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int GetGrainSize() const { return grainSize; }
|
||||
|
||||
int maxVoices;
|
||||
int sampleRate;
|
||||
int outputMode;
|
||||
int length;
|
||||
|
||||
int *mixBuffer;
|
||||
|
||||
void Mix(u32 outAddr);
|
||||
|
||||
private:
|
||||
int grainSize;
|
||||
};
|
@ -83,6 +83,7 @@ LOCAL_SRC_FILES := \
|
||||
$(SRC)/Core/ELF/PrxDecrypter.cpp \
|
||||
$(SRC)/Core/ELF/ParamSFO.cpp \
|
||||
$(SRC)/Core/HW/MemoryStick.cpp \
|
||||
$(SRC)/Core/HW/SasAudio.cpp \
|
||||
$(SRC)/Core/Core.cpp \
|
||||
$(SRC)/Core/Config.cpp \
|
||||
$(SRC)/Core/CoreTiming.cpp \
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit b41feca84bd700770791b17bde2d0a7bf910538d
|
||||
Subproject commit 700e0df918512a0fc4105c23ce2198fbfbefb5ae
|
Loading…
x
Reference in New Issue
Block a user