gzdoom-gles/src/sound/music_spc.cpp

264 lines
5.6 KiB
C++
Raw Normal View History

#include "i_musicinterns.h"
#include "templates.h"
#include "c_cvars.h"
#include "doomdef.h"
struct XID6Tag
{
BYTE ID;
BYTE Type;
WORD Value;
};
EXTERN_CVAR (Int, snd_samplerate)
CVAR (Int, spc_amp, 30, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, spc_8bit, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, spc_stereo, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, spc_lowpass, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, spc_surround, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, spc_oldsamples, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, spc_noecho, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CUSTOM_CVAR (Int, spc_quality, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
{
if (spc_quality < 0)
{
spc_quality = 0;
}
else if (spc_quality > 3)
{
spc_quality = 3;
}
}
CUSTOM_CVAR (Int, spc_frequency, 32000, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
{
if (spc_frequency < 8000)
{
spc_frequency = 8000;
}
else if (spc_frequency > 65535)
{
spc_frequency = 65535;
}
}
SPCSong::SPCSong (FILE *iofile, int len)
{
FileReader file (iofile, len);
if (!LoadEmu ())
{
return;
}
// No sense in using a higher frequency than the final output
int freq = MIN (*spc_frequency, *snd_samplerate);
Is8Bit = spc_8bit;
Stereo = spc_stereo;
m_Stream = GSnd->CreateStream (FillStream, 16384,
(Stereo ? 0 : SoundStream::Mono) |
(Is8Bit ? SoundStream::Bits8 : 0),
freq, this);
if (m_Stream == NULL)
{
Printf (PRINT_BOLD, "Could not create music stream.\n");
CloseEmu ();
return;
}
ResetAPU (spc_amp);
SetAPUOpt (~0, Stereo + 1, Is8Bit ? 8 : 16, freq, spc_quality,
(spc_lowpass ? 1 : 0) | (spc_oldsamples ? 2 : 0) | (spc_surround ? 4 : 0) | (spc_noecho ? 16 : 0));
BYTE spcfile[66048];
file.Read (spcfile, 66048);
if (LoadSPCFile != NULL)
{
LoadSPCFile (spcfile);
}
else
{
void *apuram;
BYTE *extraram;
void *dsp;
GetAPUData (&apuram, &extraram, NULL, NULL, &dsp, NULL, NULL, NULL);
memcpy (apuram, spcfile + 0x100, 65536);
memcpy (dsp, spcfile + 0x10100, 128);
memcpy (extraram, spcfile + 0x101c0, 64);
FixAPU (spcfile[37]+spcfile[38]*256, spcfile[39], spcfile[41], spcfile[40], spcfile[42], spcfile[43]);
}
// Search for amplification tag in extended ID666 info
if (len > 66056)
{
DWORD id;
file.Read (&id, 4);
if (id == MAKE_ID('x','i','d','6'))
{
DWORD size;
file >> size;
DWORD pos = 66056;
while (pos < size)
{
XID6Tag tag;
file.Read (&tag, 4);
if (tag.Type == 0)
{
// Don't care about these
}
else
{
if (pos + LittleShort(tag.Value) <= size)
{
if (tag.Type == 4 && tag.ID == 0x36)
{
DWORD amp;
file >> amp;
if (APUVersion < 98)
{
amp >>= 12;
}
SetDSPAmp (amp);
break;
}
}
file.Seek (LittleShort(tag.Value), SEEK_CUR);
}
}
}
}
}
SPCSong::~SPCSong ()
{
Stop ();
CloseEmu ();
}
bool SPCSong::IsValid () const
{
return HandleAPU != NULL;
}
bool SPCSong::IsPlaying ()
{
return m_Status == STATE_Playing;
}
void SPCSong::Play (bool looping)
{
m_Status = STATE_Stopped;
m_Looping = true;
if (m_Stream->Play (snd_musicvolume))
{
m_Status = STATE_Playing;
}
}
bool SPCSong::FillStream (SoundStream *stream, void *buff, int len, void *userdata)
{
SPCSong *song = (SPCSong *)userdata;
song->EmuAPU (buff, len >> (song->Stereo + !song->Is8Bit), 1);
if (song->Is8Bit)
{
BYTE *bytebuff = (BYTE *)buff;
for (int i = 0; i < len; ++i)
{
bytebuff[i] -= 128;
}
}
return true;
}
bool SPCSong::LoadEmu ()
{
APUVersion = 0;
HandleAPU = LoadLibraryA ("snesapu.dll");
if (HandleAPU == NULL)
{
Printf ("Could not load snesapu.dll\n");
return false;
}
SNESAPUInfo = (SNESAPUInfo_TYPE)GetProcAddress (HandleAPU, "SNESAPUInfo");
if (SNESAPUInfo == NULL)
{
Printf ("This snesapu.dll is too old.\n");
}
else
{
DWORD ver, min, opt;
SNESAPUInfo (&ver, &min, &opt);
if ((min & 0xffff00) >= 0x8500 && (min & 0xffff00) < 0x9800)
{
APUVersion = 85;
}
else if ((min & 0xffff00) == 0x9800)
{
APUVersion = 98;
}
else if ((min & 0xffff00) == 0x11000)
{
APUVersion = 110;
}
else
{
char letters[4];
letters[0] = (char)ver; letters[1] = 0;
letters[2] = (char)min; letters[3] = 0;
Printf ("This snesapu.dll is too new.\nIt is version %lx.%02lx%s and"
"is backward compatible with DLL version %lx.%02lx%s.\n"
"ZDoom is only known to support DLL versions 0.95 - 2.0\n",
(ver>>16) & 255, (ver>>8) & 255, letters,
(min>>16) & 255, (min>>8) & 255, letters+2);
}
if (APUVersion != 0)
{
if (!(GetAPUData = (GetAPUData_TYPE)GetProcAddress (HandleAPU, "GetAPUData")) ||
!(ResetAPU = (ResetAPU_TYPE)GetProcAddress (HandleAPU, "ResetAPU")) ||
!(SetDSPAmp = (SetDSPAmp_TYPE)GetProcAddress (HandleAPU, "SetDSPAmp")) ||
!(FixAPU = (FixAPU_TYPE)GetProcAddress (HandleAPU, "FixAPU")) ||
!(SetAPUOpt = (SetAPUOpt_TYPE)GetProcAddress (HandleAPU, "SetAPUOpt")) ||
!(EmuAPU = (EmuAPU_TYPE)GetProcAddress (HandleAPU, "EmuAPU")))
{
Printf ("Snesapu.dll is missing some functions.\n");
APUVersion = 0;
}
LoadSPCFile = (LoadSPCFile_TYPE)GetProcAddress (HandleAPU, "LoadSPCFile");
}
}
if (APUVersion == 0)
{
FreeLibrary (HandleAPU);
HandleAPU = NULL;
return false;
}
return true;
}
void SPCSong::CloseEmu ()
{
if (HandleAPU != NULL)
{
FreeLibrary (HandleAPU);
HandleAPU = NULL;
}
}