raze-gles/source/audiolib/src/xmp.cpp
Christoph Oelckers 2cbe211e7c - transitioned project to CMake and deleted most of the old build system.
The EDuke32 and RedNukem frontends are working, Blood isn't yet.

Notes:

many of the CMake variables and its output still refer to zdoom. Before changing that I wanted to make sure to be able to commit something that works.
support code for Windows XP has been entirely removed. On Windows this will only target Vista and up.
the crc32.h header had to be renamed to deconflict from zlib.
several Windows API calls were changed to call the A-versions directly. Weirdly enough there were places that defined their parameters as T types but in a non-working way.
removed some remaining editor files and support for the native software rendering only Windows backend.
in a few simple cases, replaced 'char' with 'uint8_t'. The code as-is depends on chars being unsigned which is non-portable. This needs to be carefully reviewed.
2019-09-22 23:15:46 +02:00

343 lines
8.4 KiB
C++

#include "compat.h"
#ifdef HAVE_XMP
#include "pitch.h"
#include "multivoc.h"
#include "_multivc.h"
#include "libxmp-lite/xmp.h"
typedef struct {
void * ptr;
VoiceNode *owner;
size_t length;
xmp_context context;
int time;
} xmp_data;
int32_t MV_GetXMPPosition(VoiceNode *voice)
{
auto xmpd = (xmp_data *)voice->rawdataptr;
return xmpd->time;
}
void MV_SetXMPPosition(VoiceNode *voice, int32_t position)
{
auto xmpd = (xmp_data *)voice->rawdataptr;
xmp_seek_time(xmpd->context, position);
}
static playbackstatus MV_GetNextXMPBlock(VoiceNode *voice)
{
auto xmpd = (xmp_data *)voice->rawdataptr;
struct xmp_frame_info mi;
if (xmp_play_frame(xmpd->context) != 0)
{
if (voice->LoopSize > 0)
{
xmp_restart_module(xmpd->context);
if (xmp_play_frame(xmpd->context) != 0)
return NoMoreData;
}
else
return NoMoreData;
}
xmp_get_frame_info(xmpd->context, &mi);
xmpd->time = mi.time;
uint32_t const samples = mi.buffer_size / (2 * (16/8)); // since 2-channel, 16-bit is hardcoded
// uint32_t const samples = mi.buffer_size / (voice->channels * (voice->bits / 8));
voice->sound = (char const *)mi.buffer;
voice->length = samples << 16;
voice->position = 0;
voice->BlockLength = 0;
MV_SetVoiceMixMode(voice);
return KeepPlaying;
}
int32_t MV_PlayXMP3D(char *ptr, uint32_t length, int32_t loophow, int32_t pitchoffset, int32_t angle, int32_t distance, int32_t priority, float volume, intptr_t callbackval)
{
int32_t left;
int32_t right;
int32_t mid;
int32_t vol;
int32_t status;
if (!MV_Installed)
{
MV_SetErrorCode(MV_NotInstalled);
return MV_Error;
}
if (distance < 0)
{
distance = -distance;
angle += MV_NUMPANPOSITIONS / 2;
}
vol = MIX_VOLUME(distance);
// Ensure angle is within 0 - 127
angle &= MV_MAXPANPOSITION;
left = MV_PanTable[angle][vol].left;
right = MV_PanTable[angle][vol].right;
mid = max( 0, 255 - distance );
status = MV_PlayXMP(ptr, length, loophow, -1, pitchoffset, mid, left, right, priority, volume, callbackval);
return status;
}
int32_t MV_PlayXMP(char *ptr, uint32_t length, int32_t loopstart, int32_t loopend, int32_t pitchoffset, int32_t vol, int32_t left, int32_t right, int32_t priority, float volume, intptr_t callbackval)
{
VoiceNode *voice;
xmp_data * xmpd = 0;
int retval;
UNREFERENCED_PARAMETER(loopend);
if (!MV_Installed)
{
MV_SetErrorCode(MV_NotInstalled);
return MV_Error;
}
xmpd = (xmp_data *)Xcalloc(1, sizeof(xmp_data));
if (!xmpd)
{
MV_SetErrorCode(MV_InvalidFile);
return MV_Error;
}
xmpd->ptr = ptr;
xmpd->length = length;
if ((xmpd->context = xmp_create_context()) == NULL)
{
Xfree(xmpd);
MV_SetErrorCode(MV_InvalidFile);
return MV_Error;
}
if ((retval = xmp_load_module_from_memory(xmpd->context, ptr, length)) != 0)
{
Xfree(xmpd);
MV_Printf("MV_PlayXMP: xmp_load_module_from_memory failed (%i)\n", retval);
MV_SetErrorCode(MV_InvalidFile);
return MV_Error;
}
// Request a voice from the voice pool
voice = MV_AllocVoice(priority);
if (voice == NULL)
{
xmp_release_module(xmpd->context);
xmp_free_context(xmpd->context);
Xfree(xmpd);
MV_SetErrorCode(MV_NoVoices);
return MV_Error;
}
xmpd->owner = voice;
voice->length = 0;
voice->sound = 0;
voice->wavetype = FMT_XMP;
voice->rawdataptr = (void*)xmpd;
voice->GetSound = MV_GetNextXMPBlock;
voice->LoopCount = 0;
voice->BlockLength = 0;
voice->PitchScale = PITCH_GetScale(pitchoffset);
voice->next = NULL;
voice->prev = NULL;
voice->priority = priority;
voice->callbackval = callbackval;
voice->bits = 16;
voice->channels = 2;
voice->SamplingRate = MV_MixRate;
voice->Paused = FALSE;
voice->LoopStart = 0;
voice->LoopEnd = 0;
voice->LoopSize = loopstart >= 0 ? 1 : 0;
xmp_start_player(xmpd->context, MV_MixRate, 0);
xmp_set_player(xmpd->context, XMP_PLAYER_INTERP, XMP_INTERP_SPLINE);
// CODEDUP multivoc.c MV_SetVoicePitch
voice->RateScale = (voice->SamplingRate * voice->PitchScale) / MV_MixRate;
voice->FixedPointBufferSize = (voice->RateScale * MV_MIXBUFFERSIZE) - voice->RateScale;
MV_SetVoiceMixMode(voice);
MV_SetVoiceVolume(voice, vol, left, right, volume);
MV_PlayVoice(voice);
return voice->handle;
}
void MV_ReleaseXMPVoice(VoiceNode * voice)
{
auto xmpd = (xmp_data *) voice->rawdataptr;
if (voice->wavetype != FMT_XMP)
return;
voice->rawdataptr = 0;
voice->length = 0;
voice->sound = nullptr;
xmp_end_player(xmpd->context);
xmp_release_module(xmpd->context);
xmp_free_context(xmpd->context);
Xfree(xmpd);
}
#else
#include "_multivc.h"
static char const NoXMP[] = "MV_PlayXMP: libxmp-lite support not included in this binary.\n";
int32_t MV_PlayXMP(char *ptr, uint32_t ptrlength, int32_t loopstart, int32_t loopend, int32_t pitchoffset, int32_t vol,
int32_t left, int32_t right, int32_t priority, float volume, intptr_t callbackval)
{
UNREFERENCED_PARAMETER(ptr);
UNREFERENCED_PARAMETER(ptrlength);
UNREFERENCED_PARAMETER(loopstart);
UNREFERENCED_PARAMETER(loopend);
UNREFERENCED_PARAMETER(pitchoffset);
UNREFERENCED_PARAMETER(vol);
UNREFERENCED_PARAMETER(left);
UNREFERENCED_PARAMETER(right);
UNREFERENCED_PARAMETER(priority);
UNREFERENCED_PARAMETER(volume);
UNREFERENCED_PARAMETER(callbackval);
MV_Printf(NoXMP);
return -1;
}
int32_t MV_PlayXMP3D(char *ptr, uint32_t ptrlength, int32_t loophow, int32_t pitchoffset, int32_t angle,
int32_t distance, int32_t priority, float volume, intptr_t callbackval)
{
UNREFERENCED_PARAMETER(ptr);
UNREFERENCED_PARAMETER(ptrlength);
UNREFERENCED_PARAMETER(loophow);
UNREFERENCED_PARAMETER(pitchoffset);
UNREFERENCED_PARAMETER(angle);
UNREFERENCED_PARAMETER(distance);
UNREFERENCED_PARAMETER(priority);
UNREFERENCED_PARAMETER(volume);
UNREFERENCED_PARAMETER(callbackval);
MV_Printf(NoXMP);
return -1;
}
#endif
// KEEPINSYNC libxmp-lite/src/*_load.c
static int it_test_memory(char const *ptr, uint32_t ptrlength)
{
static char const it_magic[] = "IMPM";
if (ptrlength < sizeof(it_magic)-1 ||
memcmp(ptr, it_magic, sizeof(it_magic)-1))
return -1;
return 0;
}
static int mod_test_memory(char const *ptr, uint32_t ptrlength)
{
if (ptrlength < 1084)
return -1;
char const * const buf = ptr + 1080;
if (!strncmp(buf + 2, "CH", 2) && isdigit((int)buf[0]) && isdigit((int)buf[1]))
{
int i = (buf[0] - '0') * 10 + buf[1] - '0';
if (i > 0 && i <= 32)
return 0;
}
if (!strncmp(buf + 1, "CHN", 3) && isdigit((int)*buf))
{
if (*buf >= '0' && *buf <= '9')
return 0;
}
if (!memcmp(buf, "M.K.", 4))
return 0;
return -1;
}
static int s3m_test_memory(char const *ptr, uint32_t ptrlength)
{
static char const s3m_magic[] = "SCRM";
#define s3m_magic_offset 44
if (ptrlength < s3m_magic_offset + sizeof(s3m_magic)-1 ||
memcmp(ptr + s3m_magic_offset, s3m_magic, sizeof(s3m_magic)-1) ||
ptr[29] != 0x10)
return -1;
return 0;
}
static int xm_test_memory(char const *ptr, uint32_t ptrlength)
{
static char const xm_magic[] = "Extended Module: ";
if (ptrlength < sizeof(xm_magic)-1 ||
memcmp(ptr, xm_magic, sizeof(xm_magic)-1))
return -1;
return 0;
}
static int mtm_test_memory(char const *ptr, uint32_t ptrlength)
{
static char const mtm_magic[] = "MTM\x10";
if (ptrlength < sizeof(mtm_magic)-1 ||
memcmp(ptr, mtm_magic, sizeof(mtm_magic)-1))
return -1;
return 0;
}
int MV_IdentifyXMP(char const *ptr, uint32_t ptrlength)
{
static decltype(mod_test_memory) * const module_test_functions[] =
{
it_test_memory,
mod_test_memory,
s3m_test_memory,
xm_test_memory,
mtm_test_memory,
};
for (auto const test_module : module_test_functions)
{
if (test_module(ptr, ptrlength) == 0)
return 1;
}
return 0;
}