Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audio SPU emulation (duckstation) #1601

Open
wants to merge 84 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
d8309d9
get audio panning working
sean-horton Apr 18, 2022
5def8d7
revert accidental change of prev code
sean-horton Apr 18, 2022
c2cbbb1
stubs for alternate audio
sean-horton Oct 2, 2022
f2713c0
parse vab
sean-horton Oct 2, 2022
527e26e
loading and parsing audio stuff
sean-horton Oct 4, 2022
97ddf3e
audio is somewhat working in testing
sean-horton Oct 5, 2022
5bd0017
Kind of working in AO and AE now
sean-horton Oct 5, 2022
77e7b2b
cleanup some build testing I had with SDL_Mixer
sean-horton Oct 5, 2022
13e7b87
thread performance
sean-horton Oct 5, 2022
99fa9a4
Merge branch 'master' of github.com:sean-horton/alive_reversing into …
sean-horton Oct 5, 2022
1784d0c
enable the other sound stuff
sean-horton Oct 6, 2022
d2b8947
I think songs are playing right
sean-horton Oct 7, 2022
fa928ea
pitches working
sean-horton Oct 7, 2022
aadf3a5
sfx panning
sean-horton Oct 7, 2022
29e1b3c
more audio updates
sean-horton Oct 8, 2022
c3ed07f
long time since I visited, commit what I have
sean-horton Feb 18, 2023
b319132
openal checkpoint
sean-horton Feb 20, 2023
da53611
sequencer checkpoint - trying something new
sean-horton Mar 4, 2023
12cd95b
another checkpoint - ticks are 44100hz...
sean-horton Mar 4, 2023
fb0c2ca
playback performance
sean-horton Mar 4, 2023
0dfd867
going to try lock free
sean-horton Mar 5, 2023
b420d62
another checkpoint
sean-horton Mar 7, 2023
140e854
another checkpoint
sean-horton Mar 7, 2023
62bf87b
midi bend kind of working
sean-horton Mar 7, 2023
f1359e2
not much better
sean-horton Mar 8, 2023
546f403
checkpoint again
sean-horton Mar 18, 2023
6e23095
working gauss table?
sean-horton Mar 31, 2023
f325d3a
cleanup before reverb
sean-horton Mar 31, 2023
c54e49b
reverb checkpoint
sean-horton Mar 31, 2023
acf6ad8
working reverb
sean-horton Mar 31, 2023
59e8722
change volume calculations
sean-horton Apr 1, 2023
707c9e8
fix loop popping?
sean-horton Apr 1, 2023
dec841d
start of some cleanup
sean-horton Apr 1, 2023
0ff1daf
formatting
sean-horton Apr 2, 2023
3a357e7
lower mix volume
sean-horton Apr 2, 2023
263b106
wrong voice may pitch bend from sequence
sean-horton Apr 2, 2023
ffb94d3
remove open-al from project
sean-horton Apr 2, 2023
639af8d
big refactor for mutex locks on spu
sean-horton Apr 2, 2023
4755899
missed config option
sean-horton Apr 2, 2023
eaccbfd
missed cmake option
sean-horton Apr 2, 2023
d31fdc0
always try and get a voice
sean-horton Apr 3, 2023
c6126ec
cleanup project files
sean-horton Apr 3, 2023
a8af51e
sequences did not say they ended
sean-horton Apr 3, 2023
e8e23a9
save some CPU cycles
sean-horton Apr 3, 2023
df9d454
cleanup + fix seq note off
sean-horton Apr 3, 2023
006cf9e
AE sample looping
sean-horton Apr 3, 2023
50472c4
store sequence volume on voice - seq could reset
sean-horton Apr 4, 2023
d787222
change voice volumes if seq volume changes
sean-horton Apr 4, 2023
3853674
Make sure END_TRACK occurs on beat
sean-horton Apr 4, 2023
562b5b2
proper volume - but samples mesed up?
sean-horton Apr 6, 2023
59ccb46
clamp audio before SDL - add comments
sean-horton Apr 7, 2023
fc85acf
Merge branch 'master' of github.com:sean-horton/alive_reversing into …
sean-horton Apr 7, 2023
d3ecafd
Missed a case when playing sequences
sean-horton Apr 7, 2023
6498697
allocate voices based on priority
sean-horton Apr 7, 2023
afc3a7c
simplify logic
sean-horton Apr 7, 2023
6473937
fix note/pitch frequency calculation
sean-horton Apr 23, 2023
aad7e20
fix pitch shift
sean-horton Apr 25, 2023
6b2e4e4
add lookup tables to fix notes
sean-horton Apr 25, 2023
954908d
remove unneeded table entries
sean-horton Apr 26, 2023
6acb744
Was looking at wrong level....
sean-horton Apr 26, 2023
1dd882b
applying audio fixes
sean-horton Apr 26, 2023
1b2391c
and another one
sean-horton Apr 27, 2023
a7680cb
and another one...
sean-horton Apr 28, 2023
c702c8f
and another one.
sean-horton Apr 28, 2023
b7d88fa
and another one
sean-horton Apr 28, 2023
070ce32
last one, but some are broken still
sean-horton Apr 28, 2023
65233c1
use remaining lookup tables
sean-horton Apr 28, 2023
d00aa57
add comment
sean-horton Apr 29, 2023
63a05f9
RF escape audio is all messed up
sean-horton Apr 29, 2023
16f0133
whistle fixes
sean-horton Apr 29, 2023
cecffc8
fix pitches?
sean-horton May 20, 2023
ad6cef7
add comment for removed item from table
sean-horton May 20, 2023
9016f9e
cleanup
sean-horton May 20, 2023
ca27e1e
pitch can go lower
sean-horton May 20, 2023
26429d9
Change volume division
sean-horton May 21, 2023
ea16ada
checkpoint incorporated new panning
sean-horton Jun 8, 2024
5ab3534
better panning + minor refactor
sean-horton Jun 9, 2024
211847d
Code review points
sean-horton Jun 9, 2024
35b59e6
Fix memory leak causing crash
sean-horton Jun 10, 2024
2ce1391
Fix memcpy of audio block
sean-horton Jun 11, 2024
c347886
Interpolation is now an interface
sean-horton Jun 13, 2024
d5b2a5c
Get Illeprih's reverb working
sean-horton Jun 14, 2024
768eb27
Allow easier swap out of implementations
sean-horton Jun 14, 2024
0275cc4
Add end of lines to files
sean-horton Jun 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
228 changes: 227 additions & 1 deletion Source/AliveLibAE/Sound/Midi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
#include "PsxSpuApi.hpp"
#include "AmbientSound.hpp"


#if !AUDIO_SPU_EMULATION
EXPORT void CC SFX_SetPitch_4CA510(const SfxDefinition* pSfx, s32 channelsBits, s16 pitch);

const s32 kSeqTableSizeAE = 144;
Expand Down Expand Up @@ -867,3 +867,229 @@ EXPORT void CC SND_Restart_4CB0E0()

// Next -> SND_Init_Ambiance_4CB480, SND_Reset_Ambiance_4CB4B0, Start_Sounds_for_TLV_4CB530, Start_Slig_sounds_4CB980
// Stop_slig_sounds_4CBA70 Path::Start_Sounds_For_Objects_In_Camera_4CBAF0, Start_Sounds_For_Objects_In_Near_Cameras_4CBB60
#else

#include "../../AliveLibCommon/audio/MidiPlayer.hpp"

class AEResourceProvider : public psx::ResourceProvider
{
public:
psx::ResourceData* readFile(char_type* name)
{
LvlFileRecord* fileRecord = sLvlArchive_5BC520.Find_File_Record_433160(name);
s32 size = fileRecord->field_10_num_sectors << 11;
u8* data = new u8[size];
sLvlArchive_5BC520.Read_File_4330A0(fileRecord, data);

psx::ResourceData* resource = new psx::ResourceData();
resource->data = data;
resource->size = size;
return resource;
}

psx::ResourceData* readSeq(const char_type* fileName, const char_type* sequenceName)
{
psx::ResourceData* resource = new psx::ResourceData();
resource->data = 0;
resource->size = 0;

if (!sequenceName || sequenceName == nullptr)
{
return resource;
}

// TODO - maybe this load should be stored in memory for faster access?
// Need to check underlying implementation
if (mLoadedFile != fileName)
{
mLoadedFile = fileName;
ResourceManager::LoadResourceFile_49C170(fileName, nullptr);
}

s32 hash = ResourceManager::SEQ_HashName_49BE30(sequenceName);
u8** ppSeq = ResourceManager::GetLoadedResource_49C2A0(ResourceManager::Resource_Seq, hash, 1, 1);
if (!ppSeq)
{
return resource;
}

u32 size = ResourceManager::Get_Header_49C410(ppSeq)->field_0_size;
u8* data = new u8[size];
memcpy(data, *ppSeq, size);
resource->data = data;
resource->size = size;
ResourceManager::FreeResource_49C330(ppSeq);
return resource;
}

s32 sequenceCount()
{
return 144;
}

private:
const char_type* mLoadedFile = NULL;
};

/*
AE is different than AO. AE reads sample data from a separate
sounds.dat file whereas AO reads it inline of the vab body.
*/
class AESoundSampleParser : public psx::SoundSampleParser
{
public:
std::vector<psx::Sample*> parseSamples(psx::VabHeader* vabHeader, u8* ppVabBody)
{
std::vector<psx::Sample*> samples;
std::ifstream soundDatFile;
soundDatFile.open("sounds.dat", std::ios::binary);
if (!soundDatFile.is_open())
{
throw std::invalid_argument("Could not find sounds.dat");
}
soundDatFile.seekg(0, std::ios::end);
std::streamsize fileSize = soundDatFile.tellg();
soundDatFile.seekg(0, std::ios::beg);
char* soundData = new char[(s32) fileSize + 1]; // Plus one, just in case interpolating tries to go that one byte further!

if (!soundDatFile.read(soundData, fileSize))
{
throw std::invalid_argument("Could not read sounds.dat");
}

int pos = 0;
for (s32 i = 0; i < vabHeader->field_16_num_vags; i++)
{
u32 size = *reinterpret_cast<u32*>(&ppVabBody[pos]);
pos += sizeof(u32);

u32 sampleRate = *reinterpret_cast<u32*>(&ppVabBody[pos]);
pos += sizeof(u32);

// In AO the body is at this part.
// IN AE instead it's an offset in sounds.dat
u32 offset = *reinterpret_cast<u32*>(&ppVabBody[pos]);
pos += sizeof(u32);

// Read data from sounds.dat
u8* data = new u8[size];
memcpy(data, &soundData[offset], size);

psx::Sample* sample = new psx::Sample();
sample->m_SampleBuffer = reinterpret_cast<s16*>(data);
sample->i_SampleSize = size / 2;
sample->sampleRate = 8000; // non standard? Doesn't use sampleRate field?
sample->loop = sampleRate > 44100; // non-standard?
samples.push_back(sample);
}

soundDatFile.close();
delete[] soundData;

return samples;
}

void applyFix(char_type* headerName, s32 vag, s32 vagOffset, SPU::Sample* sample)
{
// TODO - need to fix AE vag attrs similar to what is down for AO
// The below lines are to get around compiler warnings of unused parameters
headerName;
vag;
vagOffset;
sample;
}
};

psx::MidiPlayer* player = new psx::MidiPlayer(new AEResourceProvider(), new AESoundSampleParser());

EXPORT void CC SND_StopAll_4CB060()
{
MusicController::EnableMusic_47FE10(FALSE);
BackgroundMusic::Stop_4CB000();
player->SND_StopAll();
}

EXPORT void CC SND_Init_4CA1F0()
{
player->SND_Init();
}

EXPORT void CC SND_Shutdown_4CA280()
{
player->SND_Shutdown();
}

EXPORT void CC SND_Stop_Channels_Mask_4CA810(u32 bitMask)
{
player->SND_Stop_Channels_Mask(bitMask);
}

EXPORT void SND_Reset_4C9FB0()
{
player->SND_Reset();
}

EXPORT void CC SND_Load_VABS_4CA350(SoundBlockInfo* pSoundBlockInfo, s32 reverb)
{
player->SND_Load_VABS(reinterpret_cast<psx::SoundBlockInfo*>(pSoundBlockInfo), reverb);
}

EXPORT s32 CC SND_4CA5D0(s32 program, s32 vabId, s32 note, s16 vol, s16 min, s16 max)
{
return player->SND(program, vabId, note, vol, min, max);
}

EXPORT void CC SND_Restart_4CB0E0()
{
MusicController::EnableMusic_47FE10(TRUE);
BackgroundMusic::Play_4CB030();
Start_Sounds_For_Objects_In_Near_Cameras_4CBB60();
player->SND_Restart();
}

EXPORT void CC SND_Load_Seqs_4CAED0(OpenSeqHandle* pSeqTable, const char_type* bsqFileName)
{
player->SND_Load_Seqs(reinterpret_cast<psx::OpenSeqHandle*>(pSeqTable), bsqFileName);
}

EXPORT void CC SND_SEQ_Stop_4CAE60(u16 idx)
{
player->SND_SEQ_Stop(idx);
}

EXPORT s8 CC SND_Seq_Table_Valid_4CAFE0()
{
return player->SND_Seq_Table_Valid();
}

EXPORT s16 CC SND_SEQ_PlaySeq_4CA960(u16 idx, s16 repeatCount, s16 bDontStop)
{
return player->SND_SEQ_PlaySeq(idx, repeatCount, bDontStop);
}

EXPORT void CC SND_SEQ_SetVol_4CAD20(s32 idx, s16 volLeft, s16 volRight)
{
player->SND_SEQ_SetVol(idx, volLeft, volRight);
}

EXPORT s16 CC SND_SEQ_Play_4CAB10(u16 idx, s16 repeatCount, s16 volLeft, s16 volRight)
{
return player->SND_SEQ_Play(idx, repeatCount, volLeft, volRight);
}

EXPORT s32 CC SND_SsIsEos_DeInlined_4CACD0(u16 idx)
{
return player->SND_SsIsEos_DeInlined(idx);
}

EXPORT s32 CC SFX_SfxDefinition_Play_4CA700(const SfxDefinition* sfxDef, s16 volLeft, s16 volRight, s16 pitch_min, s16 pitch_max)
{
return player->SFX_SfxDefinition_Play(reinterpret_cast<psx::SfxDefinition*>(const_cast<SfxDefinition*>(sfxDef)), volLeft, volRight, pitch_min, pitch_max);
}

EXPORT s32 CC SFX_SfxDefinition_Play_4CA420(const SfxDefinition* sfxDef, s16 volume, s16 pitch_min, s16 pitch_max)
{
return player->SFX_SfxDefinition_Play(reinterpret_cast<psx::SfxDefinition*>(const_cast<SfxDefinition*>(sfxDef)), volume, pitch_min, pitch_max);
}

#endif
7 changes: 7 additions & 0 deletions Source/AliveLibAE/Sound/Midi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@ class IMidiVars
virtual s16 LoadResourceFile(const char_type* pFileName, Camera* pCamera) = 0;
};

#if !AUDIO_SPU_EMULATION
EXPORT IMidiVars* GetMidiVars();
EXPORT void SetMidiApiVars(IMidiVars* pVars);


using TReclaimMemoryFn = void(CC*)(u32);
using TLoadResourceFileFn = s16(CC*)(const s8*, Camera*);
using TGetLoadedResourceFn = u8**(CC*) (u32, u32, u16, u16);
Expand All @@ -58,6 +60,7 @@ EXPORT void SND_Restart_SetCallBack(TSNDRestart cb);
EXPORT void CC SND_Load_Seqs_Impl(OpenSeqHandle* pSeqTable, const char_type* bsqFileName);

EXPORT void SND_Stop_All_Seqs_4CA850();
#endif

EXPORT void CC SND_StopAll_4CB060();
EXPORT void CC SND_Init_4CA1F0();
Expand All @@ -75,6 +78,10 @@ EXPORT s32 CC SND_SsIsEos_DeInlined_4CACD0(u16 idx);
EXPORT s32 CC SFX_SfxDefinition_Play_4CA700(const SfxDefinition* sfxDef, s16 volLeft, s16 volRight, s16 pitch_min, s16 pitch_max);
EXPORT s32 CC SFX_SfxDefinition_Play_4CA420(const SfxDefinition* sfxDef, s16 volume, s16 pitch_min, s16 pitch_max);

// required additional?
EXPORT s32 CC SND_4CA5D0(s32 program, s32 vabId, s32 note, s16 vol, s16 min, s16 max);
EXPORT void CC SND_Restart_4CB0E0();

enum SeqId
{
MainMenuAmbient_0 = 0,
Expand Down
4 changes: 3 additions & 1 deletion Source/AliveLibAE/Sound/PsxSpuApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,9 @@ EXPORT s32 CC MIDI_PlayMidiNote_4FCB30(s32 vabId, s32 program, s32 note, s32 lef
{
MIDI_Wait_4FCE50();
}


// reuse a field that is never accessed for passing the samples pan
GetSpuApiVars()->sSoundEntryTable16().table[vabId][pVagIter->field_10_vag].field_1F = pVagIter->field_11_pad;
GetSoundAPI().SND_PlayEx(
&gSpuVars->sSoundEntryTable16().table[vabId][pVagIter->field_10_vag],
panLeft,
Expand Down
31 changes: 31 additions & 0 deletions Source/AliveLibAE/Sound/Sound.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,39 @@ EXPORT s32 CC SND_PlayEx_4EF740(const SoundEntry* pSnd, s32 panLeft, s32 panRigh
pDSoundBuffer->SetFrequency(freqHz);
pDSoundBuffer->SetVolume(sVolumeTable_BBBD38[panRightConverted]);

// OLD PAN
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the new panning is superior, no need to keep the original bad one in, should be removed instead, with all vars that it exclusively uses

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds panning to the existing SDL audio rendering and is unrelated to the SPU emulation path. Since the existing SDL implementation is mono it's superior in the sense that it adds panning. I'm mentioning in case you don't want to modify the existing audio renderer otherwise I can remove the old panning.

const s32 panConverted = (DSBPAN_RIGHT * (panLeft2 - panRight)) / 127; // From PSX pan range to DSound pan range
pDSoundBuffer->SetPan(-panConverted); // Fix Inverted Stereo
// OLD PAN END

// NEW PAN
// If the sample is panned we must use the pan of the sample.
// It seems all sfx are center and only music samples are panned.
s8 samplePan = pSnd->field_1F;
if (samplePan < 64)
{
panRight = ((s32) (panRight * (samplePan / 64.0)));
}
else if (samplePan > 64)
{
panLeft = ((s32) ((panLeft * (64 - (samplePan - 64))) / 64.0));
}

// convert the 0-64-127 pan value into a value
// that makes sense for SDL
s32 sdlPan = 0;
if (panRight < panLeft)
{
double p = (double) panRight / panLeft * DSBPAN_RIGHT;
sdlPan = -(DSBPAN_RIGHT - ((s32) p));
}
else if (panRight > panLeft)
{
double p = (double) panLeft / panRight * DSBPAN_RIGHT;
sdlPan = DSBPAN_RIGHT - ((s32) p);
}
pDSoundBuffer->SetPan(sdlPan);
// NEW PAN END

if (playFlags & DSBPLAY_LOOPING)
{
Expand Down
Loading