daedalus/Source/SysPSP/HLEAudio/AudioPluginPSP.cpp
2023-04-26 12:23:31 +10:00

341 lines
No EOL
7.5 KiB
C++

/*
Copyright (C) 2003 Azimer
Copyright (C) 2001,2006 StrmnNrmn
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; either version 2
of the License, or (at your option) any later version.
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 for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//
// N.B. This source code is derived from Azimer's Audio plugin (v0.55?)
// and modified by StrmnNrmn to work with Daedalus PSP. Thanks Azimer!
// Drop me a line if you get chance :)
//
#include "BuildOptions.h"
#include "Base/Types.h"
#include <stdio.h>
#include <new>
#include <pspkernel.h>
#include <pspaudiolib.h>
#include <pspaudio.h>
#include "Config/ConfigOptions.h"
#include "Core/CPU.h"
#include "Core/Interrupt.h"
#include "Core/Memory.h"
#include "Core/ROM.h"
#include "Core/RSP_HLE.h"
#include "Debug/DBGConsole.h"
#include "HLEAudio/HLEAudioInternal.h"
#include "HLEAudio/AudioBuffer.h"
#include "HLEAudio/AudioPlugin.h"
// #include "SysPSP/Utility/JobManager.h"
#include "SysPSP/Utility/CacheUtil.h"
// #include "SysPSP/Utility/JobManager.h"
#include "Core/FramerateLimiter.h"
#include "System/Thread.h"
#ifdef DAEDALUS_PSP_USE_ME
#include "SysPSP/PRX/MediaEngine/me.h"
#include "SysPSP/Utility/ModulePSP.h"
bool gLoadedMediaEnginePRX {false};
volatile me_struct *mei;
bool InitialiseMediaEngine()
{
if ( gLoadedMediaEnginePRX == false)
{
if( CModule::Load("Plugins/mediaengine.prx") < 0 ) return false;
mei = (volatile struct me_struct *)malloc_64(sizeof(struct me_struct));
mei = (volatile struct me_struct *)(make_uncached_ptr(mei));
sceKernelDcacheWritebackInvalidateAll();
if (InitME(mei) == 0)
{
gLoadedMediaEnginePRX = true;
return true;
}
else
{
#ifdef DAEDALUS_DEBUG_CONSOLE
printf(" Couldn't initialize MediaEngine Instance\n");
#endif
return false;
}
}
else
{
printf("Media Engine already Initialised\n");
}
}
#endif
#define RSP_AUDIO_INTR_CYCLES 1
extern u32 gSoundSync;
static const u32 kOutputFrequency = 44100;
static const u32 MAX_OUTPUT_FREQUENCY = kOutputFrequency * 4;
static bool audio_open = false;
// Large kAudioBufferSize creates huge delay on sound //Corn
static const u32 kAudioBufferSize = 1024 * 2; // OSX uses a circular buffer length, 1024 * 1024
class AudioPluginPSP : public CAudioPlugin
{
public:
AudioPluginPSP();
virtual ~AudioPluginPSP();
virtual bool StartEmulation();
virtual void StopEmulation();
virtual void DacrateChanged( int system_type );
virtual void LenChanged();
virtual u32 ReadLength() {return 0;}
virtual EProcessResult ProcessAList();
//virtual void SetFrequency(u32 frequency);
virtual void AddBuffer( u8 * start, u32 length);
virtual void FillBuffer( Sample * buffer, u32 num_samples);
virtual void StopAudio();
virtual void StartAudio();
public:
CAudioBuffer * mAudioBufferUncached;
private:
CAudioBuffer * mAudioBuffer;
bool mKeepRunning;
bool mExitAudioThread;
u32 mFrequency;
s32 mAudioThread;
s32 mSemaphore;
// u32 mBufferLenMs;
};
static AudioPluginPSP * ac;
void AudioPluginPSP::FillBuffer(Sample * buffer, u32 num_samples)
{
sceKernelWaitSema( mSemaphore, 1, nullptr );
mAudioBufferUncached->Drain( buffer, num_samples );
sceKernelSignalSema( mSemaphore, 1 );
}
EAudioPluginMode gAudioPluginEnabled( APM_DISABLED );
AudioPluginPSP::AudioPluginPSP()
:mKeepRunning (false)
//: mAudioBuffer( kAudioBufferSize )
, mFrequency( 44100 )
, mSemaphore( sceKernelCreateSema( "AudioPluginPSP", 0, 1, 1, nullptr ) )
//, mAudioThread ( kInvalidThreadHandle )
//, mKeepRunning( false )
//, mBufferLenMs ( 0 )
{
// Allocate audio buffer with malloc_64 to avoid cached/uncached aliasing
void * mem = malloc_64( sizeof( CAudioBuffer ) );
mAudioBuffer = new( mem ) CAudioBuffer( kAudioBufferSize );
mAudioBufferUncached = (CAudioBuffer*)make_uncached_ptr(mem);
// Ideally we could just invalidate this range?
dcache_wbinv_range_unaligned( mAudioBuffer, mAudioBuffer+sizeof( CAudioBuffer ) );
#ifdef DAEDALUS_PSP_USE_ME
InitialiseMediaEngine();
#endif
}
AudioPluginPSP::~AudioPluginPSP( )
{
mAudioBuffer->~CAudioBuffer();
free(mAudioBuffer);
sceKernelDeleteSema(mSemaphore);
pspAudioEnd();
}
bool AudioPluginPSP::StartEmulation()
{
return true;
}
void AudioPluginPSP::StopEmulation()
{
Audio_Reset();
StopAudio();
sceKernelDeleteSema(mSemaphore);
pspAudioEndPre();
sceKernelDelayThread(100000);
pspAudioEnd();
}
void AudioPluginPSP::DacrateChanged( int system_type )
{
u32 clock = (system_type == ST_NTSC) ? VI_NTSC_CLOCK : VI_PAL_CLOCK;
u32 dacrate = Memory_AI_GetRegister(AI_DACRATE_REG);
u32 frequency = clock / (dacrate + 1);
#ifdef DAEDALUS_DEBUG_CONSOLE
DBGConsole_Msg(0, "Audio frequency: %d", frequency);
#endif
mFrequency = frequency;
}
void AudioPluginPSP::LenChanged()
{
if( gAudioPluginEnabled > APM_DISABLED )
{
u32 address = Memory_AI_GetRegister(AI_DRAM_ADDR_REG) & 0xFFFFFF;
u32 length = Memory_AI_GetRegister(AI_LEN_REG);
AddBuffer( g_pu8RamBase + address, length );
}
else
{
StopAudio();
}
}
EProcessResult AudioPluginPSP::ProcessAList()
{
Memory_SP_SetRegisterBits(SP_STATUS_REG, SP_STATUS_HALT);
EProcessResult result = PR_NOT_STARTED;
switch( gAudioPluginEnabled )
{
case APM_DISABLED:
result = PR_COMPLETED;
break;
case APM_ENABLED_ASYNC:
{
sceKernelDcacheWritebackInvalidateAll();
if(BeginME( mei, (int)&Audio_Ucode, (int)NULL, -1, NULL, -1, NULL) < 0){
Audio_Ucode();
result = PR_COMPLETED;
break;
}
}
result = PR_COMPLETED;
break;
case APM_ENABLED_SYNC:
Audio_Ucode();
result = PR_COMPLETED;
break;
}
return result;
}
void audioCallback( void * buf, unsigned int length, void * userdata )
{
AudioPluginPSP * ac( reinterpret_cast< AudioPluginPSP * >( userdata ) );
ac->FillBuffer( reinterpret_cast< Sample * >( buf ), length );
}
void AudioPluginPSP::StartAudio()
{
if (mKeepRunning)
return;
mKeepRunning = true;
ac = this;
pspAudioInit();
pspAudioSetChannelCallback( 0, audioCallback, this );
// Everything OK
audio_open = true;
}
void AudioPluginPSP::AddBuffer( u8 *start, u32 length )
{
if (length == 0)
return;
if (!mKeepRunning)
StartAudio();
u32 num_samples = length / sizeof( Sample );
switch( gAudioPluginEnabled )
{
case APM_DISABLED:
break;
case APM_ENABLED_ASYNC:
{
mAudioBufferUncached->AddSamples( reinterpret_cast< const Sample * >( start ), num_samples, mFrequency, kOutputFrequency );
}
break;
case APM_ENABLED_SYNC:
{
mAudioBufferUncached->AddSamples( reinterpret_cast< const Sample * >( start ), num_samples, mFrequency, kOutputFrequency );
}
break;
}
/*
u32 remaining_samples = mAudioBuffer.GetNumBufferedSamples();
mBufferLenMs = (1000 * remaining_samples) / kOutputFrequency);
float ms = (float) num_samples * 1000.f / (float)mFrequency;
#ifdef DAEDALUS_DEBUG_CONSOLE
DPF_AUDIO("Queuing %d samples @%dHz - %.2fms - bufferlen now %d\n", num_samples, mFrequency, ms, mBufferLenMs);
#endif
*/
}
void AudioPluginPSP::StopAudio()
{
if (!mKeepRunning)
return;
mKeepRunning = false;
audio_open = false;
}
std::unique_ptr<CAudioPlugin> CreateAudioPlugin()
{
return std::make_unique<AudioPluginPSP>();
}