1
0
mirror of synced 2024-12-18 10:45:52 +01:00
lindbergh-loader/src/libsegaapi/segaapi.c

997 lines
28 KiB
C
Raw Normal View History

2022-07-28 13:43:10 +02:00
/*
Segaapi audio library emulator
Parts stolen from Sega, teknogods and jayfoxrox
Modified by doozer in 2022 to work with Outrun 2 SP under modern Linux
2022-07-28 13:59:04 +02:00
https://www.openal.org/documentation/
https://github.com/teknogods/OpenSegaAPI/blob/master/Opensegaapi/src/opensegaapi.cpp
https://web.archive.org/web/20070218003259/http://www.devmaster.net/articles.php?catID=6
2022-07-28 13:43:10 +02:00
*/
2022-07-28 19:54:00 +02:00
#define TSF_IMPLEMENTATION
2022-07-28 13:43:10 +02:00
#include <AL/al.h>
#include <AL/alc.h>
#include <AL/alext.h>
#include <AL/alut.h>
2022-07-28 19:54:00 +02:00
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
2022-07-28 13:43:10 +02:00
2022-07-28 19:54:00 +02:00
#include "segaapi.h"
2022-07-28 13:43:10 +02:00
#include "segadef.h"
#include "segaeax.h"
#include "tsf.h"
//#define DEBUG_SAMPLE
#define DEBUG_OUTPUT
//#define DUMP_WAV
//#define DUMP_BUFFER
// outrun2 will complain if these aren't present
const GUID EAX_NULL_GUID;
// DEFINE_GUID(EAX_NULL_GUID, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
// DEFINE_GUID(EAXPROPERTYID_EAX40_FXSlot2, 0x883b431d, 0xf6f0, 0x3746, 0x91, 0x9f, 0x60, 0xe7, 0xe0, 0x6b, 0x5e, 0xdd);
const GUID EAX_FREQUENCYSHIFTER_EFFECT;
const GUID EAX_ECHO_EFFECT;
const GUID EAX_REVERB_EFFECT;
const GUID EAX_EQUALIZER_EFFECT;
const GUID EAX_DISTORTION_EFFECT;
const GUID EAX_AGCCOMPRESSOR_EFFECT;
const GUID EAX_PITCHSHIFTER_EFFECT;
const GUID EAX_FLANGER_EFFECT;
const GUID EAX_VOCALMORPHER_EFFECT;
const GUID EAX_AUTOWAH_EFFECT;
const GUID EAX_RINGMODULATOR_EFFECT;
const GUID EAX_CHORUS_EFFECT;
const GUID EAXPROPERTYID_EAX40_FXSlot0;
const GUID EAXPROPERTYID_EAX40_FXSlot1;
const GUID EAXPROPERTYID_EAX40_FXSlot2;
const GUID EAXPROPERTYID_EAX40_FXSlot3;
typedef struct
{
2022-10-05 15:20:36 +02:00
// SEGA API Parts
2022-07-28 13:43:10 +02:00
void *userData;
HAWOSEGABUFFERCALLBACK callback;
bool synthesizer;
bool loop;
unsigned int channels;
unsigned int startLoop;
unsigned int endLoop;
unsigned int endOffset;
unsigned int sampleRate;
unsigned int sampleFormat;
uint8_t *data;
size_t size;
bool playing;
bool paused;
2022-10-05 15:20:36 +02:00
// OpenAL Parts
2022-07-28 13:43:10 +02:00
ALuint alBuffer;
ALuint alSource;
2022-10-05 15:20:36 +02:00
// TinySoundFont Parts
2022-07-28 13:43:10 +02:00
tsf *synth;
struct tsf_region *region;
} segaapiContext_t;
#ifdef DEBUG_OUTPUT
void dbgPrint(const char *format, ...)
{
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
printf("\n");
}
#else
void dbgPrint(const char *format, ...)
{
return;
}
#endif
ALsizei FramesToBytes(ALsizei size, ALenum channels, ALenum type)
{
2022-07-28 13:59:04 +02:00
switch (channels)
{
case AL_MONO_SOFT:
size *= 1;
break;
case AL_STEREO_SOFT:
size *= 2;
break;
case AL_REAR_SOFT:
size *= 2;
break;
case AL_QUAD_SOFT:
size *= 4;
break;
case AL_5POINT1_SOFT:
size *= 6;
break;
case AL_6POINT1_SOFT:
size *= 7;
break;
case AL_7POINT1_SOFT:
size *= 8;
break;
}
2022-07-28 13:43:10 +02:00
2022-07-28 13:59:04 +02:00
switch (type)
{
case AL_BYTE_SOFT:
size *= sizeof(ALbyte);
break;
case AL_UNSIGNED_BYTE_SOFT:
size *= sizeof(ALubyte);
break;
case AL_SHORT_SOFT:
size *= sizeof(ALshort);
break;
case AL_UNSIGNED_SHORT_SOFT:
size *= sizeof(ALushort);
break;
case AL_INT_SOFT:
size *= sizeof(ALint);
break;
case AL_UNSIGNED_INT_SOFT:
size *= sizeof(ALuint);
break;
case AL_FLOAT_SOFT:
size *= sizeof(ALfloat);
break;
case AL_DOUBLE_SOFT:
size *= sizeof(ALdouble);
break;
}
return size;
}
2022-07-28 13:43:10 +02:00
static unsigned int bufferSampleSize(segaapiContext_t *context)
2022-10-05 15:20:36 +02:00
{
2022-07-28 13:43:10 +02:00
return context->channels * ((context->sampleFormat == HASF_SIGNED_16PCM) ? 2 : 1);
}
static void updateBufferLoop(segaapiContext_t *context)
2022-10-05 15:20:36 +02:00
{
2022-07-28 13:43:10 +02:00
if (context == NULL)
return;
2022-10-05 15:20:36 +02:00
2022-07-28 13:43:10 +02:00
unsigned int sampleSize = bufferSampleSize(context);
alSourcei(context->alSource, AL_BUFFER, AL_NONE);
2022-10-05 15:20:36 +02:00
2022-07-28 13:43:10 +02:00
/*
FIXME: Re-enable, only crashed before - so fix this too..
ALint loopPoints[] = { buffer->startLoop / sampleSize, buffer->endLoop / sampleSize };
alBufferiv(buffer->alBuffer,AL_LOOP_POINTS_SOFT,loopPoints);
CHECK();
*/
2022-08-02 13:12:27 +02:00
}
AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data, ALuint samplerate, ALenum internalformat)
{
2022-10-05 15:20:36 +02:00
2022-08-02 13:12:27 +02:00
ALsizei FrameSize = FramesToBytes(samples, channels, type);
offset *= FrameSize;
alBufferData(buffer + offset, internalformat, data, FrameSize, samplerate);
2022-07-28 13:43:10 +02:00
}
static void updateBufferData(segaapiContext_t *context, unsigned int offset, size_t length)
{
ALenum alFormat = -1;
ALenum alChannels = -1;
ALenum alType;
switch (context->sampleFormat)
{
case HASF_UNSIGNED_8PCM: /* Unsigned (offset 128) 8-bit PCM */
alType = AL_BYTE_SOFT;
switch (context->channels)
{
case 1:
alFormat = AL_MONO8_SOFT;
alChannels = AL_MONO_SOFT;
break;
case 2:
alFormat = AL_STEREO8_SOFT;
alChannels = AL_STEREO_SOFT;
break;
default:
break;
}
break;
case HASF_SIGNED_16PCM: /* Signed 16-bit PCM */
alType = AL_SHORT_SOFT;
switch (context->channels)
{
case 1:
alFormat = AL_MONO16_SOFT;
alChannels = AL_MONO_SOFT;
break;
case 2:
alFormat = AL_STEREO16_SOFT;
alChannels = AL_STEREO_SOFT;
break;
default:
break;
}
default:
break;
}
2022-10-05 15:20:36 +02:00
2022-07-28 13:43:10 +02:00
if (alFormat == -1)
{
dbgPrint("Unknown format! 0x%X with %u channels!\n", context->sampleFormat, context->channels);
2022-10-05 15:20:36 +02:00
abort();
2022-07-28 13:43:10 +02:00
}
ALint position;
alGetSourcei(context->alSource, AL_SAMPLE_OFFSET, &position); // TODO: Patch if looping is active
ALint unsafe[2];
alGetSourceiv(context->alSource, AL_BYTE_RW_OFFSETS_SOFT, unsafe); // AL_BYTE_OFFSET
2022-10-05 15:20:36 +02:00
// We should update the playing buffer
// OpenAL doesn't want to let us do this!!
2022-07-28 13:43:10 +02:00
if (offset != -1)
{
2022-10-05 15:20:36 +02:00
// alBufferSubSamplesSOFT(context->alBuffer, offset / bufferSampleSize(context), length / bufferSampleSize(context), alChannels, alType, &context->data[offset], context->sampleRate, alFormat);
2022-07-28 13:43:10 +02:00
dbgPrint("Soft update in buffer %X at %u (%u bytes) - buffer playing at %u, unsafe region is %u to %u\n", (uintptr_t)context, offset, length, position, unsafe[0], unsafe[1]);
2022-10-05 15:20:36 +02:00
ALint position;
alGetSourcei(context->alSource, AL_BYTE_OFFSET, &position);
2022-07-28 13:43:10 +02:00
alSourcei(context->alSource, AL_BUFFER, AL_NONE);
2022-10-05 15:20:36 +02:00
alBufferData(context->alBuffer, alFormat, context->data, FramesToBytes(context->size / bufferSampleSize(context), alChannels, alType), context->sampleRate);
2022-07-28 13:43:10 +02:00
alSourcei(context->alSource, AL_BUFFER, context->alBuffer);
2022-10-05 15:20:36 +02:00
alSourcei(context->alSource, AL_BYTE_OFFSET, position);
2022-07-28 13:43:10 +02:00
2022-10-05 15:20:36 +02:00
return;
2022-07-28 13:43:10 +02:00
}
2022-10-05 15:20:36 +02:00
// Clear the buffer and write again
alSourcei(context->alSource, AL_BUFFER, AL_NONE);
alBufferData(context->alBuffer, alFormat, context->data, FramesToBytes(context->size / bufferSampleSize(context), alChannels, alType), context->sampleRate);
alSourcei(context->alSource, AL_BUFFER, context->alBuffer);
// updateBufferLoop(context);
dbgPrint("Hard update in buffer %X (%u bytes) - buffer playing at %u, unsafe region is %u to %u\n", (uintptr_t)context, context->size, position, unsafe[0], unsafe[1]);
2022-07-28 13:43:10 +02:00
}
static void resetBuffer(segaapiContext_t *context)
{ // printf("%s %d\n", __func__, __LINE__);
// * - Send Routing
// * - for 1 channel buffer, channel is routed to Front-Left and Front-Right.
// * - for 2 channel buffer, channel 0 is routed Front-Left, channel 1 is routed Front-Right
// * - Send Levels are set to 0 (infinite attenuation)
// * - Channel Volume is set to 0xFFFFFFFF (no attenuation)
// * - No notification.
// * - StartLoopOffset is set to 0.
context->startLoop = 0;
// * - EndLoopOffset and EndOffset are set to pConfig->mapdata.dwSize.
context->endOffset = context->size;
context->endLoop = context->size;
// * - No loop.
context->loop = false;
context->paused = false;
tsf *res = (tsf *)TSF_MALLOC(sizeof(tsf));
TSF_MEMSET(res, 0, sizeof(tsf));
res->presetNum = 0;
res->outSampleRate = context->sampleRate;
context->synth = res;
struct tsf_region *region = malloc(sizeof(struct tsf_region));
memset(region, 0, sizeof(struct tsf_region));
tsf_region_clear(region, 0);
region->ampenv.delay = 0;
region->ampenv.hold = 300.0f;
region->ampenv.attack = 0;
region->ampenv.decay = 0;
region->ampenv.release = 0;
region->ampenv.sustain = 0;
context->region = region;
// * - Buffer is in the stop state.
// * - Play position is set to 0.
updateBufferData(context, -1, -1);
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_Play(void *hHandle)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_Play() 0x%x", hHandle);
2022-10-05 15:20:36 +02:00
2022-07-28 13:43:10 +02:00
segaapiContext_t *context = hHandle;
if (context == NULL)
return SEGAERR_BAD_PARAM;
alSourcei(context->alSource, AL_LOOPING, context->loop ? AL_TRUE : AL_FALSE);
alSourcei(context->alSource, AL_BUFFER, context->alBuffer);
alSourcePlay(context->alSource);
return SEGA_SUCCESS;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_Pause(void *hHandle)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_Pause() 0x%x", hHandle);
segaapiContext_t *context = hHandle;
if (context == NULL)
return SEGAERR_BAD_PARAM;
alSourcePause(context->alSource);
return SEGA_SUCCESS;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_Stop(void *hHandle)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_Stop() 0x%x", hHandle);
segaapiContext_t *context = hHandle;
if (context == NULL)
return SEGAERR_BAD_PARAM;
alSourceStop(context->alSource);
return SEGA_SUCCESS;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_PlayWithSetup(void *hHandle)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_PlayWithSetup() 0x%x", hHandle);
segaapiContext_t *context = hHandle;
if (context == NULL)
return SEGAERR_BAD_PARAM;
alSourcei(context->alSource, AL_LOOPING, context->loop ? AL_TRUE : AL_FALSE);
alSourcei(context->alSource, AL_BUFFER, context->alBuffer);
alSourcePlay(context->alSource);
return SEGAERR_UNSUPPORTED;
}
2022-08-02 13:12:27 +02:00
PlaybackStatus SEGAAPI_GetPlaybackStatus(void *hHandle)
2022-07-28 13:43:10 +02:00
{
ALint state;
dbgPrint("SEGAAPI_GetPlaybackStatus() 0x%x", hHandle);
segaapiContext_t *context = hHandle;
if (context == NULL)
2022-07-28 13:59:04 +02:00
return PLAYBACK_STATUS_INVALID;
2022-07-28 13:43:10 +02:00
alGetSourcei(context->alSource, AL_SOURCE_STATE, &state);
switch (state)
{
case AL_PLAYING:
2022-07-28 13:59:04 +02:00
return PLAYBACK_STATUS_ACTIVE;
2022-07-28 13:43:10 +02:00
case AL_PAUSED:
2022-07-28 13:59:04 +02:00
return PLAYBACK_STATUS_PAUSE;
2022-07-28 13:43:10 +02:00
case AL_INITIAL:
case AL_STOPPED:
2022-07-28 13:59:04 +02:00
return PLAYBACK_STATUS_STOP;
2022-07-28 13:43:10 +02:00
default:
2022-07-28 13:59:04 +02:00
return PLAYBACK_STATUS_INVALID;
2022-07-28 13:43:10 +02:00
}
2022-07-28 13:59:04 +02:00
return PLAYBACK_STATUS_INVALID;
2022-07-28 13:43:10 +02:00
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_SetFormat(void *hHandle, HAWOSEFORMAT *pFormat)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_SetFormat() 0x%x", hHandle);
return SEGAERR_UNSUPPORTED;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_GetFormat(void *hHandle, HAWOSEFORMAT *pFormat)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_GetFormat() 0x%x", hHandle);
return SEGAERR_UNSUPPORTED;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_SetSampleRate(void *hHandle, unsigned int dwSampleRate)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_SetSampleRate() 0x%x 0x%x", hHandle, dwSampleRate);
2022-10-05 15:20:36 +02:00
2022-07-28 13:43:10 +02:00
if (hHandle == NULL)
return SEGAERR_BAD_HANDLE;
2022-10-05 15:20:36 +02:00
2022-07-28 13:43:10 +02:00
segaapiContext_t *context = hHandle;
context->sampleRate = dwSampleRate;
updateBufferData(context, -1, -1);
return SEGA_SUCCESS;
}
2022-08-02 13:12:27 +02:00
unsigned int SEGAAPI_GetSampleRate(void *hHandle)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_GetSampleRate() 0x%x", hHandle);
return SEGAERR_UNSUPPORTED;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_SetPriority(void *hHandle, unsigned int dwPriority)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_SetPriority() 0x%x 0x%x", hHandle, dwPriority);
return SEGAERR_UNSUPPORTED;
}
2022-08-02 13:12:27 +02:00
unsigned int SEGAAPI_GetPriority(void *hHandle)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_GetPriority() 0x%x", hHandle);
return SEGAERR_UNSUPPORTED;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_SetUserData(void *hHandle, void *hUserData)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_SetUserData() 0x%x 0x%x", hHandle, hUserData);
if (hHandle == NULL)
{ // Not sure if this is correct here, but ABC currently tries to call this with a null pointer..
dbgPrint("SEGAAPI_SetUserData() SEGAERR_BAD_HANDLE");
return SEGAERR_BAD_HANDLE;
}
segaapiContext_t *context = hHandle;
context->userData = hUserData;
dbgPrint("SEGAAPI_SetUserData() complete");
return SEGA_SUCCESS;
}
2022-08-02 13:12:27 +02:00
void *SEGAAPI_GetUserData(void *hHandle)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_GetPriority() 0x%x", hHandle);
if (hHandle == NULL)
return NULL;
segaapiContext_t *context = hHandle;
return context->userData;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_SetSendRouting(void *hHandle, unsigned int dwChannel, unsigned int dwSend, HAROUTING dwDest)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_SetSendRouting() 0x%x 0x%x 0x%x 0x%x", hHandle, dwChannel, dwSend, dwDest);
return SEGA_SUCCESS;
}
2022-08-02 13:12:27 +02:00
HAROUTING SEGAAPI_GetSendRouting(void *hHandle, unsigned int dwChannel, unsigned int dwSend)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_GetSendRouting() 0x%x 0x%x 0x%x", hHandle, dwChannel, dwSend);
return HA_UNUSED_PORT;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_SetSendLevel(void *hHandle, unsigned int dwChannel, unsigned int dwSend, unsigned int dwLevel)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_SetSendLevel() 0x%x 0x%x 0x%x 0x%x", hHandle, dwChannel, dwSend, dwLevel);
return SEGA_SUCCESS;
}
2022-08-02 13:12:27 +02:00
unsigned int SEGAAPI_GetSendLevel(void *hHandle, unsigned int dwChannel, unsigned int dwSend)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_GetSendLevel() 0x%x 0x%x 0x%x", hHandle, dwChannel, dwSend);
return 0;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_SetChannelVolume(void *hHandle, unsigned int dwChannel, unsigned int dwVolume)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_SetChannelVolume() 0x%x 0x%x 0x%x", hHandle, dwChannel, dwVolume);
return SEGAERR_UNSUPPORTED;
}
2022-08-02 13:12:27 +02:00
unsigned int SEGAAPI_GetChannelVolume(void *hHandle, unsigned int dwChannel)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_GetChannelVolume() 0x%x 0x%x", hHandle, dwChannel);
return 0;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_SetPlaybackPosition(void *hHandle, unsigned int dwPlaybackPos)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_SetPlaybackPosition() 0x%x 0x%x", hHandle, dwPlaybackPos);
segaapiContext_t *context = hHandle;
alSourcei(context->alSource, AL_BYTE_OFFSET, dwPlaybackPos);
return SEGA_SUCCESS;
}
2022-08-02 13:12:27 +02:00
unsigned int SEGAAPI_GetPlaybackPosition(void *hHandle)
2022-07-28 13:43:10 +02:00
{
ALint position;
dbgPrint("SEGAAPI_GetPlaybackPosition() 0x%x", hHandle);
segaapiContext_t *context = hHandle;
alGetSourcei(context->alSource, AL_BYTE_OFFSET, &position);
return position;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_SetNotificationFrequency(void *hHandle, unsigned int dwFrameCount)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_SetNotificationFrequency() 0x%x 0x%x", hHandle, dwFrameCount);
return SEGAERR_UNSUPPORTED;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_SetNotificationPoint(void *hHandle, unsigned int dwBufferOffset)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_SetNotificationPoint() 0x%x 0x%x", hHandle, dwBufferOffset);
return SEGAERR_UNSUPPORTED;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_ClearNotificationPoint(void *hHandle, unsigned int dwBufferOffset)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_ClearNotificationPoint() 0x%x 0x%x", hHandle, dwBufferOffset);
return SEGAERR_UNSUPPORTED;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_SetStartLoopOffset(void *hHandle, unsigned int dwOffset)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_SetStartLoopOffset() 0x%x 0x%x", hHandle, dwOffset);
if (hHandle == NULL)
{ // Not sure if this is correct here, but ABC currently tries to call this with a null pointer..
return SEGAERR_BAD_HANDLE;
}
segaapiContext_t *context = hHandle;
context->startLoop = dwOffset;
updateBufferLoop(context);
return SEGA_SUCCESS;
}
2022-08-02 13:12:27 +02:00
unsigned int SEGAAPI_GetStartLoopOffset(void *hHandle)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_GetStartLoopOffset() 0x%x", hHandle);
return 0;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_SetEndLoopOffset(void *hHandle, unsigned int dwOffset)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_SetEndLoopOffset() 0x%x 0x%x", hHandle, dwOffset);
if (hHandle == NULL)
{ // Not sure if this is correct here, but ABC currently tries to call this with a null pointer..
return SEGAERR_BAD_HANDLE;
}
segaapiContext_t *context = hHandle;
context->endLoop = dwOffset;
updateBufferLoop(context);
return SEGA_SUCCESS;
}
2022-08-02 13:12:27 +02:00
unsigned int SEGAAPI_GetEndLoopOffset(void *hHandle)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_GetEndLoopOffset() 0x%x", hHandle);
return 0;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_SetEndOffset(void *hHandle, unsigned int dwOffset)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_SetEndOffset() 0x%x 0x%x", hHandle, dwOffset);
return SEGAERR_UNSUPPORTED;
}
2022-08-02 13:12:27 +02:00
unsigned int SEGAAPI_GetEndOffset(void *hHandle)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_GetEndOffset() 0x%x", hHandle);
return 0;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_SetLoopState(void *hHandle, int bDoContinuousLooping)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_SetLoopState() 0x%x 0x%x", hHandle, bDoContinuousLooping);
segaapiContext_t *context = hHandle;
context->loop = bDoContinuousLooping;
alSourcei(context->alSource, AL_LOOPING, context->loop ? AL_TRUE : AL_FALSE);
return SEGA_SUCCESS;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_GetLoopState(void *hHandle)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_GetLoopState() 0x%x", hHandle);
return 0;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_UpdateBuffer(void *hHandle, unsigned int dwStartOffset, unsigned int dwLength)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_UpdateBuffer() 0x%x 0x%x 0x%x", hHandle, dwStartOffset, dwLength);
if (hHandle == NULL)
{
dbgPrint("SEGAAPI_UpdateBuffer() SEGAERR_BAD_HANDLE");
return SEGAERR_BAD_HANDLE;
}
segaapiContext_t *context = hHandle;
updateBufferData(context, dwStartOffset, dwLength);
return SEGA_SUCCESS;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_SetSynthParam(void *hHandle, HASYNTHPARAMSEXT param, int lPARWValue)
2022-07-28 13:43:10 +02:00
{
float volume;
float semiTones;
float freqRatio;
dbgPrint("SEGAAPI_SetSynthParam() 0x%x 0x%x 0x%x", hHandle, param, lPARWValue);
segaapiContext_t *context = hHandle;
2022-07-28 13:59:04 +02:00
2022-07-28 13:43:10 +02:00
if (context == NULL)
return SEGAERR_BAD_PARAM;
enum
{
StartAddrsOffset,
EndAddrsOffset,
StartloopAddrsOffset,
EndloopAddrsOffset,
StartAddrsCoarseOffset,
ModLfoToPitch,
VibLfoToPitch,
ModEnvToPitch,
InitialFilterFc,
InitialFilterQ,
ModLfoToFilterFc,
ModEnvToFilterFc,
EndAddrsCoarseOffset,
ModLfoToVolume,
Unused1,
ChorusEffectsSend,
ReverbEffectsSend,
Pan,
Unused2,
Unused3,
Unused4,
DelayModLFO,
FreqModLFO,
DelayVibLFO,
FreqVibLFO,
DelayModEnv,
AttackModEnv,
HoldModEnv,
DecayModEnv,
SustainModEnv,
ReleaseModEnv,
KeynumToModEnvHold,
KeynumToModEnvDecay,
DelayVolEnv,
AttackVolEnv,
HoldVolEnv,
DecayVolEnv,
SustainVolEnv,
ReleaseVolEnv,
KeynumToVolEnvHold,
KeynumToVolEnvDecay,
Instrument,
Reserved1,
KeyRange,
VelRange,
StartloopAddrsCoarseOffset,
Keynum,
Velocity,
InitialAttenuation,
Reserved2,
EndloopAddrsCoarseOffset,
CoarseTune,
FineTune,
SampleID,
SampleModes,
Reserved3,
ScaleTuning,
ExclusiveClass,
OverridingRootKey,
Unused5,
EndOper
};
int mapping[26] = {
InitialAttenuation, ///< 0, 0x00, initialAttenuation
FineTune, ///< 1, 0x01, fineTune + coarseTune * 100
InitialFilterFc, ///< 2, 0x02, initialFilterFc
InitialFilterQ, ///< 3, 0x03, initialFilterQ
DelayVolEnv, ///< 4, 0x04, delayVolEnv
AttackVolEnv, ///< 5, 0x05, attackVolEnv
HoldVolEnv, ///< 6, 0x06, holdVolEnv
DecayVolEnv, ///< 7, 0x07, decayVolEnv
SustainVolEnv, ///< 8, 0x08, sustainVolEnv
ReleaseVolEnv, ///< 9, 0x09, releaseVolEnv
DelayModEnv, ///< 10, 0x0A, delayModEnv
AttackModEnv, ///< 11, 0x0B, attackModEnv
HoldModEnv, ///< 12, 0x0C, holdModEnv
DecayModEnv, ///< 13, 0x0D, decayModEnv
SustainModEnv, ///< 14, 0x0E, sustainModEnv
ReleaseModEnv, ///< 15, 0x0F, releaseModEnv
DelayModLFO, ///< 16, 0x10, delayModLFO
FreqModLFO, ///< 17, 0x11, freqModLFO
DelayVibLFO, ///< 18, 0x12, delayVibLFO
FreqVibLFO, ///< 19, 0x13, freqVibLFO
ModLfoToPitch, ///< 20, 0x14, modLfoToPitch
VibLfoToPitch, ///< 21, 0x15, vibLfoToPitch
ModLfoToFilterFc, ///< 22, 0x16, modLfoToFilterFc
ModLfoToVolume, ///< 23, 0x17, modLfoToVolume
ModEnvToPitch, ///< 24, 0x18, modEnvToPitch
ModEnvToFilterFc ///< 25, 0x19, modEnvToFilterFc
};
int realParam = mapping[param];
switch (param)
{
case HAVP_ATTENUATION:
volume = tsf_decibelsToGain(0.0f - lPARWValue / 10.0f);
alListenerf(AL_GAIN, volume);
// buffer->xaVoice->SetVolume(volume);
dbgPrint("SEGAAPI_SetSynthParam() HAVP_ATTENUATION gain: %f dB: %d", volume, lPARWValue);
break;
case HAVP_PITCH:
semiTones = lPARWValue / 100.0f;
// freqRatio = XAudio2SemitonesToFrequencyRatio(semiTones);
// http://www-personal.umich.edu/~bazald/l/api/_x_audio2_8h_source.html
freqRatio = powf(2.0f, semiTones / 12.0f);
// buffer->xaVoice->SetFrequencyRatio(freqRatio);
alSourcef(context->alSource, AL_PITCH, freqRatio);
dbgPrint("SEGAAPI_SetSynthParam() HAVP_PITCH hHandle: %08X semitones: %f freqRatio: %f", hHandle, semiTones, freqRatio);
break;
default:
dbgPrint("SEGAAPI_SetSynthParam() unsupported param: 0x%x", param);
}
return SEGAERR_UNSUPPORTED;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_GetSynthParam(void *hHandle, HASYNTHPARAMSEXT param)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_GetSynthParam() 0x%x 0x%x", hHandle, param);
return 0;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_SetSynthParamMultiple(void *hHandle, unsigned int dwNumParams, SynthParamSet *pSynthParams)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_SetSynthParamMultiple() 0x%x 0x%x 0x%x", hHandle, dwNumParams, pSynthParams);
segaapiContext_t *context = hHandle;
if (context == NULL)
return SEGAERR_BAD_PARAM;
for (int i = 0; i < dwNumParams; i++)
{
SEGAAPI_SetSynthParam(hHandle, pSynthParams[i].param, pSynthParams[i].lPARWValue);
}
return SEGAERR_UNSUPPORTED;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_GetSynthParamMultiple(void *hHandle, unsigned int dwNumParams, SynthParamSet *pSynthParams)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_GetSynthParamMultiple() 0x%x 0x%x 0x%x", hHandle, dwNumParams, pSynthParams);
return SEGAERR_UNSUPPORTED;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_SetReleaseState(void *hHandle, int bSet)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_SetReleaseState() 0x%x 0x%x", hHandle, bSet);
return SEGAERR_UNSUPPORTED;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_CreateBuffer(HAWOSEBUFFERCONFIG *pConfig, HAWOSEGABUFFERCALLBACK pCallback, unsigned int dwFlags, void **phHandle)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_CreateBuffer() 0x%x 0x%x 0x%x 0x%x", pConfig, pCallback, dwFlags, phHandle);
if ((phHandle == NULL) || (pConfig == NULL))
{
dbgPrint("SEGAAPI_CreateBuffer() SEGAERR_BAD_POINTER");
return SEGAERR_BAD_POINTER;
}
2022-10-05 15:20:36 +02:00
2022-07-28 13:43:10 +02:00
segaapiContext_t *context = malloc(sizeof(segaapiContext_t));
if (context == NULL)
{
dbgPrint("SEGAAPI_CreateBuffer() SEGAERR_OUT_OF_MEMORY");
return SEGAERR_OUT_OF_MEMORY;
}
2022-10-05 15:20:36 +02:00
2022-07-28 13:43:10 +02:00
// dbgPrint("SEGAAPI_CreateBuffer() allocated %i bytes",sizeof(segaapiContext_t));
context->playing = false;
context->callback = pCallback;
context->synthesizer = dwFlags & HABUF_SYNTH_BUFFER;
context->sampleRate = pConfig->dwSampleRate;
context->sampleFormat = pConfig->dwSampleFormat;
context->channels = pConfig->byNumChans;
context->userData = pConfig->hUserData;
context->size = pConfig->mapData.dwSize;
pConfig->mapData.dwOffset = 0;
// can't have all 3 types at once - sanity check
if ((dwFlags & 0x06) == 0x06)
{
dbgPrint("SEGAAPI_CreateBuffer() SEGAERR_BAD_PARAM");
free(context);
return SEGAERR_BAD_PARAM;
}
// indiate that caller allocate memory
if (dwFlags & HABUF_ALLOC_USER_MEM)
{
context->data = pConfig->mapData.hBufferHdr;
dbgPrint("SEGAAPI_CreateBuffer() user memory 0x%x", context->data);
}
// reuse memory
else if (dwFlags & HABUF_USE_MAPPED_MEM)
{
context->data = pConfig->mapData.hBufferHdr;
if (context->data == NULL)
{
// null pointer, allocate memory
context->data = malloc(context->size);
if (context->data == NULL)
{
dbgPrint("SEGAAPI_CreateBuffer() SEGAERR_OUT_OF_MEMORY");
return SEGAERR_OUT_OF_MEMORY;
}
dbgPrint("SEGAAPI_CreateBuffer() bad pointer, allocated %i data bytes", context->size);
}
else
dbgPrint("SEGAAPI_CreateBuffer() reusing memory 0x%x", context->data);
}
// Allocate new buffer (caller will fill it later)
else
{
context->data = malloc(context->size);
if (context->data == NULL)
{
dbgPrint("SEGAAPI_CreateBuffer() SEGAERR_OUT_OF_MEMORY");
return SEGAERR_OUT_OF_MEMORY;
}
dbgPrint("SEGAAPI_CreateBuffer() allocated %i data bytes", context->size);
}
#ifdef DEBUG_SAMPLE
dbgPrint("SEGAAPI_CreateBuffer() dwPriority = 0x%08X; // The priority with which the voices should be allocated. This is used when voices need to be ripped off.", pConfig->dwPriority);
dbgPrint("SEGAAPI_CreateBuffer() dwSampleRate = %u; // The sample rate the voice desires", pConfig->dwSampleRate);
dbgPrint("SEGAAPI_CreateBuffer() dwSampleFormat = 0x%08X; // The sample format the voice will use", pConfig->dwSampleFormat);
dbgPrint("SEGAAPI_CreateBuffer() byNumChans = 0x%08X; // The number of samples in the sample frame. (1 = mono, 2 = stereo).", pConfig->byNumChans);
dbgPrint("SEGAAPI_CreateBuffer() dwReserved = 0x%08X; // Reserved field", pConfig->dwReserved);
dbgPrint("SEGAAPI_CreateBuffer() hUserData = 0x%08X; // User data", (uintptr_t)pConfig->hUserData);
dbgPrint("SEGAAPI_CreateBuffer() mapData { // The sample memory mapping for the buffer.");
dbgPrint("SEGAAPI_CreateBuffer() dwSize = 0x%08X; // Supply by caller. Size (in bytes) of the valid sample data", pConfig->mapData.dwSize);
dbgPrint("SEGAAPI_CreateBuffer() dwOffset = 0x%08X; // Return by driver. Offset of buffer where the the first valid sample should be written to", pConfig->mapData.dwOffset);
dbgPrint("SEGAAPI_CreateBuffer() hBufferHdr = 0x%08X; // Memory address that user-space application can access, or mapped memory handle.", (uintptr_t)pConfig->mapData.hBufferHdr);
dbgPrint("SEGAAPI_CreateBuffer() }");
#endif
pConfig->mapData.hBufferHdr = context->data;
alGenBuffers(1, &context->alBuffer);
alGenSources(1, &context->alSource);
/*
TODO:
* HABUF_ALLOC_USER_MEM bit when set indicates caller allocate sound data memory buffer.
* HABUF_USE_MAPPED_MEM
Can't be used at the same time!!!
*/
if (context->synthesizer)
{
dbgPrint("SEGAAPI_CreateBuffer() !!! Doesn't support synth buffers yet!");
// https://stackoverflow.com/questions/44157238/can-i-produce-a-synthetic-sound-using-openal
}
resetBuffer(context);
*phHandle = context;
return SEGA_SUCCESS;
}
2022-08-02 13:12:27 +02:00
int SEGAAPI_DestroyBuffer(void *hHandle)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_DestroyBuffer() 0x%x", hHandle);
if (hHandle == NULL)
return SEGAERR_BAD_PARAM;
free(hHandle);
return SEGA_SUCCESS;
}
2022-07-28 19:54:00 +02:00
int SEGAAPI_SetGlobalEAXProperty(GUID *guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_SetGlobalEAXProperty() 0x%x 0x%x 0x%x 0x%x", guid, ulProperty, pData, ulDataSize);
return 0;
}
2022-07-28 19:54:00 +02:00
int SEGAAPI_GetGlobalEAXProperty(GUID *guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_GetGlobalEAXProperty() 0x%x 0x%x 0x%x 0x%x", guid, ulProperty, pData, ulDataSize);
return 0;
}
2022-07-28 19:54:00 +02:00
int SEGAAPI_SetSPDIFOutChannelStatus(unsigned int dwChannelStatus, unsigned int dwExtChannelStatus)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_SetSPDIFOutChannelStatus() 0x%x 0x%x", dwChannelStatus, dwExtChannelStatus);
return SEGAERR_UNSUPPORTED;
}
2022-07-28 19:54:00 +02:00
int SEGAAPI_GetSPDIFOutChannelStatus(unsigned int *pdwChannelStatus, unsigned int *pdwExtChannelStatus)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_GetSPDIFOutChannelStatus() 0x%x 0x%x", pdwChannelStatus, pdwExtChannelStatus);
return SEGAERR_UNSUPPORTED;
}
2022-07-28 19:54:00 +02:00
int SEGAAPI_SetSPDIFOutSampleRate(HASPDIFOUTRATE dwSamplingRate)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_SetSPDIFOutSampleRate() 0x%x", dwSamplingRate);
return SEGAERR_UNSUPPORTED;
}
HASPDIFOUTRATE SEGAAPI_GetSPDIFOutSampleRate(void)
{
dbgPrint("SEGAAPI_GetSPDIFOutSampleRate()");
return HASPDIFOUT_48KHZ;
}
2022-07-28 19:54:00 +02:00
int SEGAAPI_SetSPDIFOutChannelRouting(unsigned int dwChannel, HAROUTING dwSource)
2022-07-28 13:43:10 +02:00
{
switch (dwChannel)
{
case 0: // left
dbgPrint("SEGAAPI_SetSPDIFOutChannelRouting() dwChannel = LEFT; dwSource = 0x%x", dwSource);
break;
case 1: // right
dbgPrint("SEGAAPI_SetSPDIFOutChannelRouting() dwChannel = RIGHT; dwSource = 0x%x", dwSource);
break;
default:
dbgPrint("SEGAAPI_SetSPDIFOutChannelRouting() dwChannel = UNKNOWN; dwSource = 0x%x", dwSource);
break;
}
return SEGAERR_UNSUPPORTED;
}
2022-07-28 19:54:00 +02:00
HAROUTING SEGAAPI_GetSPDIFOutChannelRouting(unsigned int dwChannel)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_GetSPDIFOutChannelRouting() 0x%x", dwChannel);
return HA_UNUSED_PORT;
}
2022-07-28 19:54:00 +02:00
int SEGAAPI_SetIOVolume(HAPHYSICALIO dwPhysIO, unsigned int dwVolume)
2022-07-28 13:43:10 +02:00
{
// float v = (dwVolume >> 16) & 0xffff;
dbgPrint("SEGAAPI_SetIOVolume() 0x%x 0x%x", dwPhysIO, dwVolume);
// alListenerf(AL_GAIN, v);
return SEGA_SUCCESS;
}
2022-07-28 19:54:00 +02:00
unsigned int SEGAAPI_GetIOVolume(HAPHYSICALIO dwPhysIO)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_GetIOVolume() 0x%x", dwPhysIO);
return 0xffffffff;
}
2022-07-28 19:54:00 +02:00
void SEGAAPI_SetLastStatus(int LastStatus)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_SetLastStatus() 0x%x", LastStatus);
return;
}
2022-07-28 19:54:00 +02:00
int SEGAAPI_GetLastStatus(void)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_GetLastStatus()");
return SEGA_SUCCESS;
}
2022-07-28 19:54:00 +02:00
int SEGAAPI_Reset(void)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_Reset()");
return SEGA_SUCCESS;
}
2022-07-28 19:54:00 +02:00
int SEGAAPI_Init(void)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_Init()");
2022-10-05 15:20:36 +02:00
if (alutInit(NULL, NULL) == AL_FALSE)
2022-07-28 13:43:10 +02:00
{
2022-10-05 15:20:36 +02:00
dbgPrint("SEGAAPI_Init() alutInit() failed!");
2022-07-28 13:43:10 +02:00
return SEGAERR_FAIL;
}
SEGAAPI_SetGlobalEAXProperty((GUID *)&EAXPROPERTYID_EAX40_FXSlot2, 0, (void *)&EAX_NULL_GUID, 16);
return SEGA_SUCCESS;
}
2022-07-28 19:54:00 +02:00
int SEGAAPI_Exit(void)
2022-07-28 13:43:10 +02:00
{
dbgPrint("SEGAAPI_Exit()");
alutExit();
return SEGA_SUCCESS;
}