mirror of
https://github.com/libretro/ppsspp.git
synced 2024-11-29 11:20:40 +00:00
Merge pull request #5509 from unknownbrackets/sas-minor
Improve pitch handling in sas
This commit is contained in:
commit
106656f51e
@ -45,6 +45,7 @@ enum {
|
||||
ERROR_SAS_BAD_ADDRESS = 0x80420005,
|
||||
ERROR_SAS_INVALID_VOICE = 0x80420010,
|
||||
ERROR_SAS_INVALID_NOISE_FREQ = 0x80420011,
|
||||
ERROR_SAS_INVALID_PITCH = 0x80420012,
|
||||
ERROR_SAS_INVALID_ADSR_CURVE_MODE = 0x80420013,
|
||||
ERROR_SAS_INVALID_PARAMETER = 0x80420014,
|
||||
ERROR_SAS_INVALID_LOOP_POS = 0x80420015,
|
||||
@ -263,23 +264,17 @@ u32 sceSasSetVolume(u32 core, int voiceNum, int leftVol, int rightVol, int effec
|
||||
}
|
||||
|
||||
u32 sceSasSetPitch(u32 core, int voiceNum, int pitch) {
|
||||
DEBUG_LOG(SCESAS, "sceSasSetPitch(%08x, %i, %i)", core, voiceNum, pitch);
|
||||
|
||||
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
|
||||
WARN_LOG(SCESAS, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
|
||||
return ERROR_SAS_INVALID_VOICE;
|
||||
}
|
||||
|
||||
SasVoice &v = sas->voices[voiceNum];
|
||||
// Clamp pitch
|
||||
if (pitch < PSP_SAS_PITCH_MIN) {
|
||||
WARN_LOG(SCESAS, "sceSasSetPitch: bad pitch %i, clamping to %i", pitch, PSP_SAS_PITCH_MIN);
|
||||
pitch = PSP_SAS_PITCH_MIN;
|
||||
} else if (pitch > PSP_SAS_PITCH_MAX) {
|
||||
WARN_LOG(SCESAS, "sceSasSetPitch: bad pitch %i, clamping to %i", pitch, PSP_SAS_PITCH_MAX);
|
||||
pitch = PSP_SAS_PITCH_MAX;
|
||||
if (pitch < PSP_SAS_PITCH_MIN || pitch > PSP_SAS_PITCH_MAX) {
|
||||
WARN_LOG(SCESAS, "sceSasSetPitch(%08x, %i, %i): bad pitch", core, voiceNum, pitch);
|
||||
return ERROR_SAS_INVALID_PITCH;
|
||||
}
|
||||
|
||||
DEBUG_LOG(SCESAS, "sceSasSetPitch(%08x, %i, %i)", core, voiceNum, pitch);
|
||||
SasVoice &v = sas->voices[voiceNum];
|
||||
v.pitch = pitch;
|
||||
v.ChangedParams(false);
|
||||
return 0;
|
||||
|
@ -368,16 +368,7 @@ void SasVoice::ReadSamples(s16 *output, int numSamples) {
|
||||
// Read N samples into the resample buffer. Could do either PCM or VAG here.
|
||||
switch (type) {
|
||||
case VOICETYPE_VAG:
|
||||
{
|
||||
vag.GetSamples(output, numSamples);
|
||||
if (vag.End()) {
|
||||
// NOTICE_LOG(SCESAS, "Hit end of VAG audio");
|
||||
playing = false;
|
||||
on = false; // ??
|
||||
// TODO: Should this remain on somehow or just hit rock bottom immediately?
|
||||
envelope.End();
|
||||
}
|
||||
}
|
||||
vag.GetSamples(output, numSamples);
|
||||
break;
|
||||
case VOICETYPE_PCM:
|
||||
{
|
||||
@ -428,8 +419,7 @@ void SasVoice::ReadSamples(s16 *output, int numSamples) {
|
||||
bool SasVoice::HaveSamplesEnded() {
|
||||
switch (type) {
|
||||
case VOICETYPE_VAG:
|
||||
// TODO: Is it here, or before the samples are processed?
|
||||
return false;
|
||||
return vag.End();
|
||||
|
||||
case VOICETYPE_PCM:
|
||||
return pcmIndex >= pcmSize;
|
||||
@ -462,15 +452,17 @@ void SasInstance::MixVoice(SasVoice &voice) {
|
||||
// Actually this is not entirely correct - we need to get one extra sample, and store it
|
||||
// for the next time around. A little complicated...
|
||||
// But for now, see Smoothness HACKERY below :P
|
||||
u32 numSamples = (voice.sampleFrac + grainSize * voice.pitch) / PSP_SAS_PITCH_BASE;
|
||||
u32 numSamples = ((u32)voice.sampleFrac + (u32)grainSize * (u32)voice.pitch) >> PSP_SAS_PITCH_BASE_SHIFT;
|
||||
if ((int)numSamples > grainSize * 4) {
|
||||
ERROR_LOG(SASMIX, "numSamples too large, clamping: %i vs %i", numSamples, grainSize * 4);
|
||||
numSamples = grainSize * 4;
|
||||
}
|
||||
|
||||
// This feels a bit hacky. The first 32 samples after a keyon are 0s.
|
||||
const bool ignorePitch = voice.type == VOICETYPE_PCM && voice.pitch > PSP_SAS_PITCH_BASE;
|
||||
if (voice.envelope.NeedsKeyOn()) {
|
||||
voice.ReadSamples(resampleBuffer + 2 + 32, numSamples - 32);
|
||||
int delay = ignorePitch ? 32 : (32 * (u32)voice.pitch) >> PSP_SAS_PITCH_BASE_SHIFT;
|
||||
voice.ReadSamples(resampleBuffer + 2 + delay, numSamples - delay);
|
||||
} else {
|
||||
voice.ReadSamples(resampleBuffer + 2, numSamples);
|
||||
}
|
||||
@ -482,42 +474,14 @@ void SasInstance::MixVoice(SasVoice &voice) {
|
||||
voice.resampleHist[0] = resampleBuffer[2 + numSamples - 2];
|
||||
voice.resampleHist[1] = resampleBuffer[2 + numSamples - 1];
|
||||
|
||||
// Resample to the correct pitch, writing exactly "grainSize" samples.
|
||||
// This is a HORRIBLE resampler by the way.
|
||||
// TODO: Special case no-resample case (and 2x and 0.5x) for speed, it's not uncommon
|
||||
|
||||
u32 sampleFrac = voice.sampleFrac;
|
||||
// We need to shift by 12 anyway, so combine that with the volume shift.
|
||||
int volumeShift = (12 + MAX_CONFIG_VOLUME - g_Config.iSFXVolume);
|
||||
if (volumeShift < 0) volumeShift = 0;
|
||||
for (int i = 0; i < grainSize; i++) {
|
||||
// For now: nearest neighbour, not even using the resample history at all.
|
||||
int sample = resampleBuffer[sampleFrac / PSP_SAS_PITCH_BASE + 2];
|
||||
sampleFrac += voice.pitch;
|
||||
|
||||
// The maximum envelope height (PSP_SAS_ENVELOPE_HEIGHT_MAX) is (1 << 30) - 1.
|
||||
// Reduce it to 14 bits, by shifting off 15. Round up by adding (1 << 14) first.
|
||||
int envelopeValue = voice.envelope.GetHeight();
|
||||
envelopeValue = (envelopeValue + (1 << 14)) >> 15;
|
||||
|
||||
// We just scale by the envelope before we scale by volumes.
|
||||
// Again, we round up by adding (1 << 14) first (*after* multiplying.)
|
||||
sample = ((sample * envelopeValue) + (1 << 14)) >> 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 ) >> volumeShift; // Max = 16 and Min = 12(default)
|
||||
mixBuffer[i * 2 + 1] += (sample * voice.volumeRight) >> volumeShift; // Max = 16 and Min = 12(default)
|
||||
sendBuffer[i * 2] += sample * voice.volumeLeftSend >> 12;
|
||||
sendBuffer[i * 2 + 1] += sample * voice.volumeRightSend >> 12;
|
||||
voice.envelope.Step();
|
||||
}
|
||||
|
||||
voice.sampleFrac = sampleFrac;
|
||||
// Let's hope grainSize is a power of 2.
|
||||
//voice.sampleFrac &= grainSize * PSP_SAS_PITCH_BASE - 1;
|
||||
voice.sampleFrac -= numSamples * PSP_SAS_PITCH_BASE;
|
||||
// Let's try to optimize the easy case where we don't need to resample at all.
|
||||
if (voice.sampleFrac == 0 && (voice.pitch == PSP_SAS_PITCH_BASE || ignorePitch))
|
||||
MixSamplesOptimal(voice);
|
||||
// Half pitch is also quite common.
|
||||
else if (voice.pitch == PSP_SAS_PITCH_BASE / 2)
|
||||
MixSamplesHalfPitch(voice);
|
||||
else
|
||||
MixSamples(voice);
|
||||
|
||||
if (voice.HaveSamplesEnded())
|
||||
voice.envelope.End();
|
||||
@ -530,6 +494,83 @@ void SasInstance::MixVoice(SasVoice &voice) {
|
||||
}
|
||||
}
|
||||
|
||||
void SasInstance::MixSamples(SasVoice &voice) {
|
||||
// Resample to the correct pitch, writing exactly "grainSize" samples.
|
||||
// This is a poor resampler by the way.
|
||||
u32 sampleFrac = voice.sampleFrac;
|
||||
|
||||
// We need to shift by 12 anyway, so combine that with the volume shift.
|
||||
u8 volumeShift = 12;
|
||||
if (g_Config.iSFXVolume >= 0 && g_Config.iSFXVolume < MAX_CONFIG_VOLUME)
|
||||
volumeShift += MAX_CONFIG_VOLUME - g_Config.iSFXVolume;
|
||||
|
||||
const int offset = sampleFrac == 0 ? 2 : 1;
|
||||
for (int i = 0; i < grainSize; i++) {
|
||||
const int readIndex = sampleFrac >> PSP_SAS_PITCH_BASE_SHIFT;
|
||||
const int readFrac = sampleFrac & (PSP_SAS_PITCH_BASE - 1);
|
||||
int sample1 = resampleBuffer[readIndex + offset];
|
||||
int sample2 = resampleBuffer[readIndex + 1 + offset];
|
||||
int sample = (sample1 * (PSP_SAS_PITCH_BASE - readFrac) + sample2 * readFrac) / PSP_SAS_PITCH_BASE;
|
||||
sampleFrac += voice.pitch;
|
||||
|
||||
MixSample(voice, i, sample, volumeShift);
|
||||
}
|
||||
|
||||
voice.sampleFrac = sampleFrac & (PSP_SAS_PITCH_BASE - 1);
|
||||
}
|
||||
|
||||
void SasInstance::MixSamplesHalfPitch(SasVoice &voice) {
|
||||
// We need to shift by 12 anyway, so combine that with the volume shift.
|
||||
u8 volumeShift = 12;
|
||||
if (g_Config.iSFXVolume >= 0 && g_Config.iSFXVolume < MAX_CONFIG_VOLUME)
|
||||
volumeShift += MAX_CONFIG_VOLUME - g_Config.iSFXVolume;
|
||||
|
||||
int readIndex2 = voice.sampleFrac == 0 ? 0 : -1;
|
||||
for (int i = 0; i < grainSize; i++) {
|
||||
int sample1 = resampleBuffer[(readIndex2 >> 1) + 2];
|
||||
int sample2 = resampleBuffer[(readIndex2 >> 1) + 1 + 2];
|
||||
int sample = readIndex2 & 1 ? ((sample1 + sample2) >> 1) : sample1;
|
||||
++readIndex2;
|
||||
|
||||
MixSample(voice, i, sample, volumeShift);
|
||||
}
|
||||
|
||||
voice.sampleFrac = readIndex2 & 1 ? PSP_SAS_PITCH_BASE / 2 : 0;
|
||||
}
|
||||
|
||||
void SasInstance::MixSamplesOptimal(SasVoice &voice) {
|
||||
// We need to shift by 12 anyway, so combine that with the volume shift.
|
||||
u8 volumeShift = 12;
|
||||
if (g_Config.iSFXVolume >= 0 && g_Config.iSFXVolume < MAX_CONFIG_VOLUME)
|
||||
volumeShift += MAX_CONFIG_VOLUME - g_Config.iSFXVolume;
|
||||
|
||||
int readIndex = 2;
|
||||
for (int i = 0; i < grainSize; i++) {
|
||||
int sample = resampleBuffer[readIndex++];
|
||||
MixSample(voice, i, sample, volumeShift);
|
||||
}
|
||||
}
|
||||
|
||||
inline void SasInstance::MixSample(SasVoice &voice, int i, int sample, u8 volumeShift) {
|
||||
// The maximum envelope height (PSP_SAS_ENVELOPE_HEIGHT_MAX) is (1 << 30) - 1.
|
||||
// Reduce it to 14 bits, by shifting off 15. Round up by adding (1 << 14) first.
|
||||
int envelopeValue = voice.envelope.GetHeight();
|
||||
envelopeValue = (envelopeValue + (1 << 14)) >> 15;
|
||||
|
||||
// We just scale by the envelope before we scale by volumes.
|
||||
// Again, we round up by adding (1 << 14) first (*after* multiplying.)
|
||||
sample = ((sample * envelopeValue) + (1 << 14)) >> 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 ) >> volumeShift; // Max = 16 and Min = 12(default)
|
||||
mixBuffer[i * 2 + 1] += (sample * voice.volumeRight) >> volumeShift; // Max = 16 and Min = 12(default)
|
||||
sendBuffer[i * 2] += sample * voice.volumeLeftSend >> 12;
|
||||
sendBuffer[i * 2 + 1] += sample * voice.volumeRightSend >> 12;
|
||||
voice.envelope.Step();
|
||||
}
|
||||
|
||||
void SasInstance::Mix(u32 outAddr, u32 inAddr, int leftVol, int rightVol) {
|
||||
int voicesPlayingCount = 0;
|
||||
|
||||
@ -648,6 +689,7 @@ void SasVoice::KeyOn() {
|
||||
playing = true;
|
||||
on = true;
|
||||
paused = false;
|
||||
sampleFrac = 0;
|
||||
}
|
||||
|
||||
void SasVoice::KeyOff() {
|
||||
|
@ -29,8 +29,9 @@
|
||||
enum {
|
||||
PSP_SAS_VOICES_MAX = 32,
|
||||
|
||||
PSP_SAS_PITCH_MIN = 1,
|
||||
PSP_SAS_PITCH_MIN = 0x0000,
|
||||
PSP_SAS_PITCH_BASE = 0x1000,
|
||||
PSP_SAS_PITCH_BASE_SHIFT = 12,
|
||||
PSP_SAS_PITCH_MAX = 0x4000,
|
||||
|
||||
PSP_SAS_VOL_MAX = 0x1000,
|
||||
@ -291,5 +292,10 @@ public:
|
||||
WaveformEffect waveformEffect;
|
||||
|
||||
private:
|
||||
void MixSamples(SasVoice &voice);
|
||||
void MixSamplesHalfPitch(SasVoice &voice);
|
||||
void MixSamplesOptimal(SasVoice &voice);
|
||||
void MixSample(SasVoice &voice, int i, int sample, u8 volumeShift);
|
||||
|
||||
int grainSize;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user